summaryrefslogtreecommitdiffstats
path: root/generic/ttk
diff options
context:
space:
mode:
Diffstat (limited to 'generic/ttk')
-rw-r--r--generic/ttk/ttk.decls150
-rw-r--r--generic/ttk/ttkBlink.c166
-rw-r--r--generic/ttk/ttkButton.c853
-rw-r--r--generic/ttk/ttkCache.c350
-rw-r--r--generic/ttk/ttkClamTheme.c971
-rw-r--r--generic/ttk/ttkClassicTheme.c513
-rw-r--r--generic/ttk/ttkDecls.h272
-rw-r--r--generic/ttk/ttkDefaultTheme.c1130
-rw-r--r--generic/ttk/ttkElements.c1281
-rw-r--r--generic/ttk/ttkEntry.c2048
-rw-r--r--generic/ttk/ttkFrame.c654
-rw-r--r--generic/ttk/ttkGenStubs.tcl921
-rw-r--r--generic/ttk/ttkImage.c415
-rw-r--r--generic/ttk/ttkInit.c282
-rw-r--r--generic/ttk/ttkLabel.c693
-rw-r--r--generic/ttk/ttkLayout.c1252
-rw-r--r--generic/ttk/ttkManager.c552
-rw-r--r--generic/ttk/ttkManager.h92
-rw-r--r--generic/ttk/ttkNotebook.c1413
-rw-r--r--generic/ttk/ttkPanedwindow.c975
-rw-r--r--generic/ttk/ttkProgress.c545
-rw-r--r--generic/ttk/ttkScale.c515
-rw-r--r--generic/ttk/ttkScroll.c252
-rw-r--r--generic/ttk/ttkScrollbar.c345
-rw-r--r--generic/ttk/ttkSeparator.c136
-rw-r--r--generic/ttk/ttkSquare.c301
-rw-r--r--generic/ttk/ttkState.c273
-rw-r--r--generic/ttk/ttkStubInit.c61
-rw-r--r--generic/ttk/ttkStubLib.c74
-rw-r--r--generic/ttk/ttkTagSet.c306
-rw-r--r--generic/ttk/ttkTheme.c1737
-rw-r--r--generic/ttk/ttkTheme.h444
-rw-r--r--generic/ttk/ttkThemeInt.h42
-rw-r--r--generic/ttk/ttkTrace.c190
-rw-r--r--generic/ttk/ttkTrack.c183
-rw-r--r--generic/ttk/ttkTreeview.c3442
-rw-r--r--generic/ttk/ttkWidget.c789
-rw-r--r--generic/ttk/ttkWidget.h273
38 files changed, 24891 insertions, 0 deletions
diff --git a/generic/ttk/ttk.decls b/generic/ttk/ttk.decls
new file mode 100644
index 0000000..8b2b50b
--- /dev/null
+++ b/generic/ttk/ttk.decls
@@ -0,0 +1,150 @@
+library ttk
+interface ttk
+epoch 0
+scspec TTKAPI
+
+declare 0 current {
+ Ttk_Theme Ttk_GetTheme(Tcl_Interp *interp, const char *name);
+}
+declare 1 current {
+ Ttk_Theme Ttk_GetDefaultTheme(Tcl_Interp *interp);
+}
+declare 2 current {
+ Ttk_Theme Ttk_GetCurrentTheme(Tcl_Interp *interp);
+}
+declare 3 current {
+ Ttk_Theme Ttk_CreateTheme(
+ Tcl_Interp *interp, const char *name, Ttk_Theme parent);
+}
+declare 4 current {
+ void Ttk_RegisterCleanup(
+ Tcl_Interp *interp, void *deleteData, Ttk_CleanupProc *cleanupProc);
+}
+
+declare 5 current {
+ int Ttk_RegisterElementSpec(
+ Ttk_Theme theme,
+ const char *elementName,
+ Ttk_ElementSpec *elementSpec,
+ void *clientData);
+}
+
+declare 6 current {
+ Ttk_ElementClass *Ttk_RegisterElement(
+ Tcl_Interp *interp,
+ Ttk_Theme theme,
+ const char *elementName,
+ Ttk_ElementSpec *elementSpec,
+ void *clientData);
+}
+
+declare 7 current {
+ int Ttk_RegisterElementFactory(
+ Tcl_Interp *interp,
+ const char *name,
+ Ttk_ElementFactory factoryProc,
+ void *clientData);
+}
+
+declare 8 current {
+ void Ttk_RegisterLayout(
+ Ttk_Theme theme, const char *className, Ttk_LayoutSpec layoutSpec);
+}
+
+#
+# State maps.
+#
+declare 10 current {
+ int Ttk_GetStateSpecFromObj(
+ Tcl_Interp *interp, Tcl_Obj *objPtr, Ttk_StateSpec *spec_rtn);
+}
+declare 11 current {
+ Tcl_Obj *Ttk_NewStateSpecObj(
+ unsigned int onbits, unsigned int offbits);
+}
+declare 12 current {
+ Ttk_StateMap Ttk_GetStateMapFromObj(
+ Tcl_Interp *interp, Tcl_Obj *objPtr);
+}
+declare 13 current {
+ Tcl_Obj *Ttk_StateMapLookup(
+ Tcl_Interp *interp, Ttk_StateMap map, Ttk_State state);
+}
+declare 14 current {
+ int Ttk_StateTableLookup(
+ Ttk_StateTable map[], Ttk_State state);
+}
+
+
+#
+# Low-level geometry utilities.
+#
+declare 20 current {
+ int Ttk_GetPaddingFromObj(
+ Tcl_Interp *interp,
+ Tk_Window tkwin,
+ Tcl_Obj *objPtr,
+ Ttk_Padding *pad_rtn);
+}
+declare 21 current {
+ int Ttk_GetBorderFromObj(
+ Tcl_Interp *interp,
+ Tcl_Obj *objPtr,
+ Ttk_Padding *pad_rtn);
+}
+declare 22 current {
+ int Ttk_GetStickyFromObj(
+ Tcl_Interp *interp, Tcl_Obj *objPtr, Ttk_Sticky *sticky_rtn);
+}
+declare 23 current {
+ Ttk_Padding Ttk_MakePadding(
+ short l, short t, short r, short b);
+}
+declare 24 current {
+ Ttk_Padding Ttk_UniformPadding(
+ short borderWidth);
+}
+declare 25 current {
+ Ttk_Padding Ttk_AddPadding(Ttk_Padding pad1, Ttk_Padding pad2);
+}
+declare 26 current {
+ Ttk_Padding Ttk_RelievePadding(
+ Ttk_Padding padding, int relief, int n);
+}
+declare 27 current {
+ Ttk_Box Ttk_MakeBox(int x, int y, int width, int height);
+}
+declare 28 current {
+ int Ttk_BoxContains(Ttk_Box box, int x, int y);
+}
+declare 29 current {
+ Ttk_Box Ttk_PackBox(Ttk_Box *cavity, int w, int h, Ttk_Side side);
+}
+declare 30 current {
+ Ttk_Box Ttk_StickBox(Ttk_Box parcel, int w, int h, Ttk_Sticky sticky);
+}
+declare 31 current {
+ Ttk_Box Ttk_AnchorBox(Ttk_Box parcel, int w, int h, Tk_Anchor anchor);
+}
+declare 32 current {
+ Ttk_Box Ttk_PadBox(Ttk_Box b, Ttk_Padding p);
+}
+declare 33 current {
+ Ttk_Box Ttk_ExpandBox(Ttk_Box b, Ttk_Padding p);
+}
+declare 34 current {
+ Ttk_Box Ttk_PlaceBox(
+ Ttk_Box *cavity, int w, int h, Ttk_Side side, Ttk_Sticky sticky);
+}
+declare 35 current {
+ Tcl_Obj *Ttk_NewBoxObj(Ttk_Box box);
+}
+
+#
+# Utilities.
+#
+declare 40 current {
+ int Ttk_GetOrientFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, int *orient);
+}
+
+
diff --git a/generic/ttk/ttkBlink.c b/generic/ttk/ttkBlink.c
new file mode 100644
index 0000000..7e46fac
--- /dev/null
+++ b/generic/ttk/ttkBlink.c
@@ -0,0 +1,166 @@
+/*
+ * Copyright 2004, Joe English.
+ *
+ * Usage:
+ * TtkBlinkCursor(corePtr), usually called in a widget's Init hook,
+ * arranges to periodically toggle the corePtr->flags CURSOR_ON bit
+ * on and off (and schedule a redisplay) whenever the widget has focus.
+ *
+ * Note: Widgets may have additional logic to decide whether
+ * to display the cursor or not (e.g., readonly or disabled states);
+ * TtkBlinkCursor() does not account for this.
+ *
+ * TODO:
+ * Add script-level access to configure application-wide blink rate.
+ */
+
+#include <tk.h>
+#include "ttkTheme.h"
+#include "ttkWidget.h"
+
+#define DEF_CURSOR_ON_TIME 600 /* milliseconds */
+#define DEF_CURSOR_OFF_TIME 300 /* milliseconds */
+
+/* Interp-specific data for tracking cursors:
+ */
+typedef struct
+{
+ WidgetCore *owner; /* Widget that currently has cursor */
+ Tcl_TimerToken timer; /* Blink timer */
+ int onTime; /* #milliseconds to blink cursor on */
+ int offTime; /* #milliseconds to blink cursor off */
+} CursorManager;
+
+/* CursorManagerDeleteProc --
+ * InterpDeleteProc for cursor manager.
+ */
+static void CursorManagerDeleteProc(ClientData clientData, Tcl_Interp *interp)
+{
+ CursorManager *cm = (CursorManager*)clientData;
+ if (cm->timer) {
+ Tcl_DeleteTimerHandler(cm->timer);
+ }
+ ckfree(clientData);
+}
+
+/* GetCursorManager --
+ * Look up and create if necessary the interp's cursor manager.
+ */
+static CursorManager *GetCursorManager(Tcl_Interp *interp)
+{
+ static const char *cm_key = "ttk::CursorManager";
+ CursorManager *cm = (CursorManager *) Tcl_GetAssocData(interp, cm_key,0);
+
+ if (!cm) {
+ cm = (CursorManager*)ckalloc(sizeof(*cm));
+ cm->timer = 0;
+ cm->owner = 0;
+ cm->onTime = DEF_CURSOR_ON_TIME;
+ cm->offTime = DEF_CURSOR_OFF_TIME;
+ Tcl_SetAssocData(interp,cm_key,CursorManagerDeleteProc,(ClientData)cm);
+ }
+ return cm;
+}
+
+/* CursorBlinkProc --
+ * Timer handler to blink the insert cursor on and off.
+ */
+static void
+CursorBlinkProc(ClientData clientData)
+{
+ CursorManager *cm = (CursorManager*)clientData;
+ int blinkTime;
+
+ if (cm->owner->flags & CURSOR_ON) {
+ cm->owner->flags &= ~CURSOR_ON;
+ blinkTime = cm->offTime;
+ } else {
+ cm->owner->flags |= CURSOR_ON;
+ blinkTime = cm->onTime;
+ }
+ cm->timer = Tcl_CreateTimerHandler(blinkTime, CursorBlinkProc, clientData);
+ TtkRedisplayWidget(cm->owner);
+}
+
+/* LoseCursor --
+ * Turn cursor off, disable blink timer.
+ */
+static void LoseCursor(CursorManager *cm, WidgetCore *corePtr)
+{
+ if (corePtr->flags & CURSOR_ON) {
+ corePtr->flags &= ~CURSOR_ON;
+ TtkRedisplayWidget(corePtr);
+ }
+ if (cm->owner == corePtr) {
+ cm->owner = NULL;
+ }
+ if (cm->timer) {
+ Tcl_DeleteTimerHandler(cm->timer);
+ cm->timer = 0;
+ }
+}
+
+/* ClaimCursor --
+ * Claim ownership of the insert cursor and blink on.
+ */
+static void ClaimCursor(CursorManager *cm, WidgetCore *corePtr)
+{
+ if (cm->owner == corePtr)
+ return;
+ if (cm->owner)
+ LoseCursor(cm, cm->owner);
+
+ corePtr->flags |= CURSOR_ON;
+ TtkRedisplayWidget(corePtr);
+
+ cm->owner = corePtr;
+ cm->timer = Tcl_CreateTimerHandler(cm->onTime, CursorBlinkProc, cm);
+}
+
+/*
+ * CursorEventProc --
+ * Event handler for FocusIn and FocusOut events;
+ * claim/lose ownership of the insert cursor when the widget
+ * acquires/loses keyboard focus.
+ */
+
+#define CursorEventMask (FocusChangeMask|StructureNotifyMask)
+#define RealFocusEvent(d) \
+ (d == NotifyInferior || d == NotifyAncestor || d == NotifyNonlinear)
+
+static void
+CursorEventProc(ClientData clientData, XEvent *eventPtr)
+{
+ WidgetCore *corePtr = (WidgetCore *)clientData;
+ CursorManager *cm = GetCursorManager(corePtr->interp);
+
+ switch (eventPtr->type) {
+ case DestroyNotify:
+ if (cm->owner == corePtr)
+ LoseCursor(cm, corePtr);
+ Tk_DeleteEventHandler(
+ corePtr->tkwin, CursorEventMask, CursorEventProc, clientData);
+ break;
+ case FocusIn:
+ if (RealFocusEvent(eventPtr->xfocus.detail))
+ ClaimCursor(cm, corePtr);
+ break;
+ case FocusOut:
+ if (RealFocusEvent(eventPtr->xfocus.detail))
+ LoseCursor(cm, corePtr);
+ break;
+ }
+}
+
+/*
+ * TtkBlinkCursor (main routine) --
+ * Arrange to blink the cursor on and off whenever the
+ * widget has focus.
+ */
+void TtkBlinkCursor(WidgetCore *corePtr)
+{
+ Tk_CreateEventHandler(
+ corePtr->tkwin, CursorEventMask, CursorEventProc, corePtr);
+}
+
+/*EOF*/
diff --git a/generic/ttk/ttkButton.c b/generic/ttk/ttkButton.c
new file mode 100644
index 0000000..2954184
--- /dev/null
+++ b/generic/ttk/ttkButton.c
@@ -0,0 +1,853 @@
+/*
+ * Copyright (c) 2003, Joe English
+ *
+ * label, button, checkbutton, radiobutton, and menubutton widgets.
+ */
+
+#include <string.h>
+#include <tk.h>
+#include "ttkTheme.h"
+#include "ttkWidget.h"
+
+/* Bit fields for OptionSpec mask field:
+ */
+#define STATE_CHANGED (0x100) /* -state option changed */
+#define DEFAULTSTATE_CHANGED (0x200) /* -default option changed */
+
+/*------------------------------------------------------------------------
+ * +++ Base resources for labels, buttons, checkbuttons, etc:
+ */
+typedef struct
+{
+ /*
+ * Text element resources:
+ */
+ Tcl_Obj *textObj;
+ Tcl_Obj *textVariableObj;
+ Tcl_Obj *underlineObj;
+ Tcl_Obj *widthObj;
+
+ Ttk_TraceHandle *textVariableTrace;
+ Ttk_ImageSpec *imageSpec;
+
+ /*
+ * Image element resources:
+ */
+ Tcl_Obj *imageObj;
+
+ /*
+ * Compound label/image resources:
+ */
+ Tcl_Obj *compoundObj;
+ Tcl_Obj *paddingObj;
+
+ /*
+ * Compatibility/legacy options:
+ */
+ Tcl_Obj *stateObj;
+
+} BasePart;
+
+typedef struct
+{
+ WidgetCore core;
+ BasePart base;
+} Base;
+
+static Tk_OptionSpec BaseOptionSpecs[] =
+{
+ {TK_OPTION_STRING, "-text", "text", "Text", "",
+ Tk_Offset(Base,base.textObj), -1,
+ 0,0,GEOMETRY_CHANGED },
+ {TK_OPTION_STRING, "-textvariable", "textVariable", "Variable", "",
+ Tk_Offset(Base,base.textVariableObj), -1,
+ TK_OPTION_NULL_OK,0,GEOMETRY_CHANGED },
+ {TK_OPTION_INT, "-underline", "underline", "Underline",
+ "-1", Tk_Offset(Base,base.underlineObj), -1,
+ 0,0,0 },
+ /* SB: OPTION_INT, see <<NOTE-NULLOPTIONS>> */
+ {TK_OPTION_STRING, "-width", "width", "Width",
+ NULL, Tk_Offset(Base,base.widthObj), -1,
+ TK_OPTION_NULL_OK,0,GEOMETRY_CHANGED },
+
+ /*
+ * Image options
+ */
+ {TK_OPTION_STRING, "-image", "image", "Image", NULL/*default*/,
+ Tk_Offset(Base,base.imageObj), -1,
+ TK_OPTION_NULL_OK,0,GEOMETRY_CHANGED },
+
+ /*
+ * Compound base/image options
+ */
+ {TK_OPTION_STRING_TABLE, "-compound", "compound", "Compound",
+ "none", Tk_Offset(Base,base.compoundObj), -1,
+ 0,(ClientData)ttkCompoundStrings,GEOMETRY_CHANGED },
+ {TK_OPTION_STRING, "-padding", "padding", "Pad",
+ NULL, Tk_Offset(Base,base.paddingObj), -1,
+ TK_OPTION_NULL_OK,0,GEOMETRY_CHANGED},
+
+ /*
+ * Compatibility/legacy options
+ */
+ {TK_OPTION_STRING, "-state", "state", "State",
+ "normal", Tk_Offset(Base,base.stateObj), -1,
+ 0,0,STATE_CHANGED },
+
+ WIDGET_INHERIT_OPTIONS(ttkCoreOptionSpecs)
+};
+
+/*
+ * Variable trace procedure for -textvariable option:
+ */
+static void TextVariableChanged(void *clientData, const char *value)
+{
+ Base *basePtr = clientData;
+ Tcl_Obj *newText;
+
+ if (WidgetDestroyed(&basePtr->core)) {
+ return;
+ }
+
+ newText = value ? Tcl_NewStringObj(value, -1) : Tcl_NewStringObj("", 0);
+
+ Tcl_IncrRefCount(newText);
+ Tcl_DecrRefCount(basePtr->base.textObj);
+ basePtr->base.textObj = newText;
+
+ TtkResizeWidget(&basePtr->core);
+}
+
+static void
+BaseInitialize(Tcl_Interp *interp, void *recordPtr)
+{
+ Base *basePtr = recordPtr;
+ basePtr->base.textVariableTrace = 0;
+ basePtr->base.imageSpec = NULL;
+}
+
+static void
+BaseCleanup(void *recordPtr)
+{
+ Base *basePtr = recordPtr;
+ if (basePtr->base.textVariableTrace)
+ Ttk_UntraceVariable(basePtr->base.textVariableTrace);
+ if (basePtr->base.imageSpec)
+ TtkFreeImageSpec(basePtr->base.imageSpec);
+}
+
+static int BaseConfigure(Tcl_Interp *interp, void *recordPtr, int mask)
+{
+ Base *basePtr = recordPtr;
+ Tcl_Obj *textVarName = basePtr->base.textVariableObj;
+ Ttk_TraceHandle *vt = 0;
+ Ttk_ImageSpec *imageSpec = NULL;
+
+ if (textVarName != NULL && *Tcl_GetString(textVarName) != '\0') {
+ vt = Ttk_TraceVariable(interp,textVarName,TextVariableChanged,basePtr);
+ if (!vt) return TCL_ERROR;
+ }
+
+ if (basePtr->base.imageObj) {
+ imageSpec = TtkGetImageSpec(
+ interp, basePtr->core.tkwin, basePtr->base.imageObj);
+ if (!imageSpec) {
+ goto error;
+ }
+ }
+
+ if (TtkCoreConfigure(interp, recordPtr, mask) != TCL_OK) {
+error:
+ if (imageSpec) TtkFreeImageSpec(imageSpec);
+ if (vt) Ttk_UntraceVariable(vt);
+ return TCL_ERROR;
+ }
+
+ if (basePtr->base.textVariableTrace) {
+ Ttk_UntraceVariable(basePtr->base.textVariableTrace);
+ }
+ basePtr->base.textVariableTrace = vt;
+
+ if (basePtr->base.imageSpec) {
+ TtkFreeImageSpec(basePtr->base.imageSpec);
+ }
+ basePtr->base.imageSpec = imageSpec;
+
+ if (mask & STATE_CHANGED) {
+ TtkCheckStateOption(&basePtr->core, basePtr->base.stateObj);
+ }
+
+ return TCL_OK;
+}
+
+static int
+BasePostConfigure(Tcl_Interp *interp, void *recordPtr, int mask)
+{
+ Base *basePtr = recordPtr;
+ int status = TCL_OK;
+
+ if (basePtr->base.textVariableTrace) {
+ status = Ttk_FireTrace(basePtr->base.textVariableTrace);
+ }
+
+ return status;
+}
+
+/*------------------------------------------------------------------------
+ * +++ Label widget.
+ * Just a base widget that adds a few appearance-related options
+ */
+
+typedef struct
+{
+ Tcl_Obj *backgroundObj;
+ Tcl_Obj *foregroundObj;
+ Tcl_Obj *fontObj;
+ Tcl_Obj *borderWidthObj;
+ Tcl_Obj *reliefObj;
+ Tcl_Obj *anchorObj;
+ Tcl_Obj *justifyObj;
+ Tcl_Obj *wrapLengthObj;
+} LabelPart;
+
+typedef struct
+{
+ WidgetCore core;
+ BasePart base;
+ LabelPart label;
+} Label;
+
+static Tk_OptionSpec LabelOptionSpecs[] =
+{
+ {TK_OPTION_BORDER, "-background", "frameColor", "FrameColor",
+ NULL, Tk_Offset(Label,label.backgroundObj), -1,
+ TK_OPTION_NULL_OK,0,0 },
+ {TK_OPTION_COLOR, "-foreground", "textColor", "TextColor",
+ NULL, Tk_Offset(Label,label.foregroundObj), -1,
+ TK_OPTION_NULL_OK,0,0 },
+ {TK_OPTION_FONT, "-font", "font", "Font",
+ NULL, Tk_Offset(Label,label.fontObj), -1,
+ TK_OPTION_NULL_OK,0,GEOMETRY_CHANGED },
+ {TK_OPTION_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
+ NULL, Tk_Offset(Label,label.borderWidthObj), -1,
+ TK_OPTION_NULL_OK,0,GEOMETRY_CHANGED },
+ {TK_OPTION_RELIEF, "-relief", "relief", "Relief",
+ NULL, Tk_Offset(Label,label.reliefObj), -1,
+ TK_OPTION_NULL_OK,0,GEOMETRY_CHANGED },
+ {TK_OPTION_ANCHOR, "-anchor", "anchor", "Anchor",
+ NULL, Tk_Offset(Label,label.anchorObj), -1,
+ TK_OPTION_NULL_OK, 0, GEOMETRY_CHANGED},
+ {TK_OPTION_JUSTIFY, "-justify", "justify", "Justify",
+ NULL, Tk_Offset(Label, label.justifyObj), -1,
+ TK_OPTION_NULL_OK,0,GEOMETRY_CHANGED },
+ {TK_OPTION_PIXELS, "-wraplength", "wrapLength", "WrapLength",
+ NULL, Tk_Offset(Label, label.wrapLengthObj), -1,
+ TK_OPTION_NULL_OK,0,GEOMETRY_CHANGED /*SB: SIZE_CHANGED*/ },
+
+ WIDGET_TAKEFOCUS_FALSE,
+ WIDGET_INHERIT_OPTIONS(BaseOptionSpecs)
+};
+
+static const Ttk_Ensemble LabelCommands[] = {
+ { "configure", TtkWidgetConfigureCommand,0 },
+ { "cget", TtkWidgetCgetCommand,0 },
+ { "instate", TtkWidgetInstateCommand,0 },
+ { "state", TtkWidgetStateCommand,0 },
+ { "identify", TtkWidgetIdentifyCommand,0 },
+ { 0,0,0 }
+};
+
+static WidgetSpec LabelWidgetSpec =
+{
+ "TLabel", /* className */
+ sizeof(Label), /* recordSize */
+ LabelOptionSpecs, /* optionSpecs */
+ LabelCommands, /* subcommands */
+ BaseInitialize, /* initializeProc */
+ BaseCleanup, /* cleanupProc */
+ BaseConfigure, /* configureProc */
+ BasePostConfigure, /* postConfigureProc */
+ TtkWidgetGetLayout, /* getLayoutProc */
+ TtkWidgetSize, /* sizeProc */
+ TtkWidgetDoLayout, /* layoutProc */
+ TtkWidgetDisplay /* displayProc */
+};
+
+TTK_BEGIN_LAYOUT(LabelLayout)
+ TTK_GROUP("Label.border", TTK_FILL_BOTH|TTK_BORDER,
+ TTK_GROUP("Label.padding", TTK_FILL_BOTH|TTK_BORDER,
+ TTK_NODE("Label.label", TTK_FILL_BOTH)))
+TTK_END_LAYOUT
+
+/*------------------------------------------------------------------------
+ * +++ Button widget.
+ * Adds a new subcommand "invoke", and options "-command" and "-default"
+ */
+
+typedef struct
+{
+ Tcl_Obj *commandObj;
+ Tcl_Obj *defaultStateObj;
+} ButtonPart;
+
+typedef struct
+{
+ WidgetCore core;
+ BasePart base;
+ ButtonPart button;
+} Button;
+
+/*
+ * Option specifications:
+ */
+static Tk_OptionSpec ButtonOptionSpecs[] =
+{
+ {TK_OPTION_STRING, "-command", "command", "Command",
+ "", Tk_Offset(Button, button.commandObj), -1, 0,0,0},
+ {TK_OPTION_STRING_TABLE, "-default", "default", "Default",
+ "normal", Tk_Offset(Button, button.defaultStateObj), -1,
+ 0, (ClientData) ttkDefaultStrings, DEFAULTSTATE_CHANGED},
+
+ WIDGET_TAKEFOCUS_TRUE,
+ WIDGET_INHERIT_OPTIONS(BaseOptionSpecs)
+};
+
+static int ButtonConfigure(Tcl_Interp *interp, void *recordPtr, int mask)
+{
+ Button *buttonPtr = recordPtr;
+
+ if (BaseConfigure(interp, recordPtr, mask) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ /* Handle "-default" option:
+ */
+ if (mask & DEFAULTSTATE_CHANGED) {
+ int defaultState = TTK_BUTTON_DEFAULT_DISABLED;
+ Ttk_GetButtonDefaultStateFromObj(
+ NULL, buttonPtr->button.defaultStateObj, &defaultState);
+ if (defaultState == TTK_BUTTON_DEFAULT_ACTIVE) {
+ TtkWidgetChangeState(&buttonPtr->core, TTK_STATE_ALTERNATE, 0);
+ } else {
+ TtkWidgetChangeState(&buttonPtr->core, 0, TTK_STATE_ALTERNATE);
+ }
+ }
+ return TCL_OK;
+}
+
+/* $button invoke --
+ * Evaluate the button's -command.
+ */
+static int
+ButtonInvokeCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ Button *buttonPtr = recordPtr;
+ if (objc > 2) {
+ Tcl_WrongNumArgs(interp, 1, objv, "invoke");
+ return TCL_ERROR;
+ }
+ if (buttonPtr->core.state & TTK_STATE_DISABLED) {
+ return TCL_OK;
+ }
+ return Tcl_EvalObjEx(interp, buttonPtr->button.commandObj, TCL_EVAL_GLOBAL);
+}
+
+static const Ttk_Ensemble ButtonCommands[] = {
+ { "configure", TtkWidgetConfigureCommand,0 },
+ { "cget", TtkWidgetCgetCommand,0 },
+ { "invoke", ButtonInvokeCommand,0 },
+ { "instate", TtkWidgetInstateCommand,0 },
+ { "state", TtkWidgetStateCommand,0 },
+ { "identify", TtkWidgetIdentifyCommand,0 },
+ { 0,0,0 }
+};
+
+static WidgetSpec ButtonWidgetSpec =
+{
+ "TButton", /* className */
+ sizeof(Button), /* recordSize */
+ ButtonOptionSpecs, /* optionSpecs */
+ ButtonCommands, /* subcommands */
+ BaseInitialize, /* initializeProc */
+ BaseCleanup, /* cleanupProc */
+ ButtonConfigure, /* configureProc */
+ BasePostConfigure, /* postConfigureProc */
+ TtkWidgetGetLayout, /* getLayoutProc */
+ TtkWidgetSize, /* sizeProc */
+ TtkWidgetDoLayout, /* layoutProc */
+ TtkWidgetDisplay /* displayProc */
+};
+
+TTK_BEGIN_LAYOUT(ButtonLayout)
+ TTK_GROUP("Button.border", TTK_FILL_BOTH|TTK_BORDER,
+ TTK_GROUP("Button.focus", TTK_FILL_BOTH,
+ TTK_GROUP("Button.padding", TTK_FILL_BOTH,
+ TTK_NODE("Button.label", TTK_FILL_BOTH))))
+TTK_END_LAYOUT
+
+/*------------------------------------------------------------------------
+ * +++ Checkbutton widget.
+ */
+typedef struct
+{
+ Tcl_Obj *variableObj;
+ Tcl_Obj *onValueObj;
+ Tcl_Obj *offValueObj;
+ Tcl_Obj *commandObj;
+
+ Ttk_TraceHandle *variableTrace;
+
+} CheckbuttonPart;
+
+typedef struct
+{
+ WidgetCore core;
+ BasePart base;
+ CheckbuttonPart checkbutton;
+} Checkbutton;
+
+/*
+ * Option specifications:
+ */
+static Tk_OptionSpec CheckbuttonOptionSpecs[] =
+{
+ {TK_OPTION_STRING, "-variable", "variable", "Variable",
+ "", Tk_Offset(Checkbutton, checkbutton.variableObj), -1,
+ TK_OPTION_DONT_SET_DEFAULT,0,0},
+ {TK_OPTION_STRING, "-onvalue", "onValue", "OnValue",
+ "1", Tk_Offset(Checkbutton, checkbutton.onValueObj), -1,
+ 0,0,0},
+ {TK_OPTION_STRING, "-offvalue", "offValue", "OffValue",
+ "0", Tk_Offset(Checkbutton, checkbutton.offValueObj), -1,
+ 0,0,0},
+ {TK_OPTION_STRING, "-command", "command", "Command",
+ "", Tk_Offset(Checkbutton, checkbutton.commandObj), -1,
+ 0,0,0},
+
+ WIDGET_TAKEFOCUS_TRUE,
+ WIDGET_INHERIT_OPTIONS(BaseOptionSpecs)
+};
+
+/*
+ * Variable trace procedure for checkbutton -variable option
+ */
+static void CheckbuttonVariableChanged(void *clientData, const char *value)
+{
+ Checkbutton *checkPtr = clientData;
+
+ if (WidgetDestroyed(&checkPtr->core)) {
+ return;
+ }
+
+ if (!value) {
+ TtkWidgetChangeState(&checkPtr->core, TTK_STATE_ALTERNATE, 0);
+ return;
+ }
+ /* else */
+ TtkWidgetChangeState(&checkPtr->core, 0, TTK_STATE_ALTERNATE);
+ if (!strcmp(value, Tcl_GetString(checkPtr->checkbutton.onValueObj))) {
+ TtkWidgetChangeState(&checkPtr->core, TTK_STATE_SELECTED, 0);
+ } else {
+ TtkWidgetChangeState(&checkPtr->core, 0, TTK_STATE_SELECTED);
+ }
+}
+
+static void
+CheckbuttonInitialize(Tcl_Interp *interp, void *recordPtr)
+{
+ Checkbutton *checkPtr = recordPtr;
+ Tcl_Obj *variableObj;
+
+ /* default -variable is the widget name:
+ */
+ variableObj = Tcl_NewStringObj(Tk_PathName(checkPtr->core.tkwin), -1);
+ Tcl_IncrRefCount(variableObj);
+ checkPtr->checkbutton.variableObj = variableObj;
+ BaseInitialize(interp, recordPtr);
+}
+
+static void
+CheckbuttonCleanup(void *recordPtr)
+{
+ Checkbutton *checkPtr = recordPtr;
+ Ttk_UntraceVariable(checkPtr->checkbutton.variableTrace);
+ checkPtr->checkbutton.variableTrace = 0;
+ BaseCleanup(recordPtr);
+}
+
+static int
+CheckbuttonConfigure(Tcl_Interp *interp, void *recordPtr, int mask)
+{
+ Checkbutton *checkPtr = recordPtr;
+ Ttk_TraceHandle *vt = Ttk_TraceVariable(
+ interp, checkPtr->checkbutton.variableObj,
+ CheckbuttonVariableChanged, checkPtr);
+
+ if (!vt) {
+ return TCL_ERROR;
+ }
+
+ if (BaseConfigure(interp, recordPtr, mask) != TCL_OK){
+ Ttk_UntraceVariable(vt);
+ return TCL_ERROR;
+ }
+
+ Ttk_UntraceVariable(checkPtr->checkbutton.variableTrace);
+ checkPtr->checkbutton.variableTrace = vt;
+
+ return TCL_OK;
+}
+
+static int
+CheckbuttonPostConfigure(Tcl_Interp *interp, void *recordPtr, int mask)
+{
+ Checkbutton *checkPtr = recordPtr;
+ int status = TCL_OK;
+
+ if (checkPtr->checkbutton.variableTrace)
+ status = Ttk_FireTrace(checkPtr->checkbutton.variableTrace);
+ if (status == TCL_OK && !WidgetDestroyed(&checkPtr->core))
+ status = BasePostConfigure(interp, recordPtr, mask);
+ return status;
+}
+
+/*
+ * Checkbutton 'invoke' subcommand:
+ * Toggles the checkbutton state.
+ */
+static int
+CheckbuttonInvokeCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ Checkbutton *checkPtr = recordPtr;
+ WidgetCore *corePtr = &checkPtr->core;
+ Tcl_Obj *newValue;
+
+ if (objc > 2) {
+ Tcl_WrongNumArgs(interp, 1, objv, "invoke");
+ return TCL_ERROR;
+ }
+ if (corePtr->state & TTK_STATE_DISABLED)
+ return TCL_OK;
+
+ /*
+ * Toggle the selected state.
+ */
+ if (corePtr->state & TTK_STATE_SELECTED)
+ newValue = checkPtr->checkbutton.offValueObj;
+ else
+ newValue = checkPtr->checkbutton.onValueObj;
+
+ if (Tcl_ObjSetVar2(interp,
+ checkPtr->checkbutton.variableObj, NULL, newValue,
+ TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG)
+ == NULL)
+ return TCL_ERROR;
+
+ if (WidgetDestroyed(corePtr))
+ return TCL_ERROR;
+
+ return Tcl_EvalObjEx(interp,
+ checkPtr->checkbutton.commandObj, TCL_EVAL_GLOBAL);
+}
+
+static const Ttk_Ensemble CheckbuttonCommands[] = {
+ { "configure", TtkWidgetConfigureCommand,0 },
+ { "cget", TtkWidgetCgetCommand,0 },
+ { "invoke", CheckbuttonInvokeCommand,0 },
+ { "instate", TtkWidgetInstateCommand,0 },
+ { "state", TtkWidgetStateCommand,0 },
+ { "identify", TtkWidgetIdentifyCommand,0 },
+ /* MISSING: select, deselect, toggle */
+ { 0,0,0 }
+};
+
+static WidgetSpec CheckbuttonWidgetSpec =
+{
+ "TCheckbutton", /* className */
+ sizeof(Checkbutton), /* recordSize */
+ CheckbuttonOptionSpecs, /* optionSpecs */
+ CheckbuttonCommands, /* subcommands */
+ CheckbuttonInitialize, /* initializeProc */
+ CheckbuttonCleanup, /* cleanupProc */
+ CheckbuttonConfigure, /* configureProc */
+ CheckbuttonPostConfigure, /* postConfigureProc */
+ TtkWidgetGetLayout, /* getLayoutProc */
+ TtkWidgetSize, /* sizeProc */
+ TtkWidgetDoLayout, /* layoutProc */
+ TtkWidgetDisplay /* displayProc */
+};
+
+TTK_BEGIN_LAYOUT(CheckbuttonLayout)
+ TTK_GROUP("Checkbutton.padding", TTK_FILL_BOTH,
+ TTK_NODE("Checkbutton.indicator", TTK_PACK_LEFT)
+ TTK_GROUP("Checkbutton.focus", TTK_PACK_LEFT | TTK_STICK_W,
+ TTK_NODE("Checkbutton.label", TTK_FILL_BOTH)))
+TTK_END_LAYOUT
+
+/*------------------------------------------------------------------------
+ * +++ Radiobutton widget.
+ */
+
+typedef struct
+{
+ Tcl_Obj *variableObj;
+ Tcl_Obj *valueObj;
+ Tcl_Obj *commandObj;
+
+ Ttk_TraceHandle *variableTrace;
+
+} RadiobuttonPart;
+
+typedef struct
+{
+ WidgetCore core;
+ BasePart base;
+ RadiobuttonPart radiobutton;
+} Radiobutton;
+
+/*
+ * Option specifications:
+ */
+static Tk_OptionSpec RadiobuttonOptionSpecs[] =
+{
+ {TK_OPTION_STRING, "-variable", "variable", "Variable",
+ "::selectedButton", Tk_Offset(Radiobutton, radiobutton.variableObj),-1,
+ 0,0,0},
+ {TK_OPTION_STRING, "-value", "Value", "Value",
+ "1", Tk_Offset(Radiobutton, radiobutton.valueObj), -1,
+ 0,0,0},
+ {TK_OPTION_STRING, "-command", "command", "Command",
+ "", Tk_Offset(Radiobutton, radiobutton.commandObj), -1,
+ 0,0,0},
+
+ WIDGET_TAKEFOCUS_TRUE,
+ WIDGET_INHERIT_OPTIONS(BaseOptionSpecs)
+};
+
+/*
+ * Variable trace procedure for radiobuttons.
+ */
+static void
+RadiobuttonVariableChanged(void *clientData, const char *value)
+{
+ Radiobutton *radioPtr = clientData;
+
+ if (WidgetDestroyed(&radioPtr->core)) {
+ return;
+ }
+
+ if (!value) {
+ TtkWidgetChangeState(&radioPtr->core, TTK_STATE_ALTERNATE, 0);
+ return;
+ }
+ /* else */
+ TtkWidgetChangeState(&radioPtr->core, 0, TTK_STATE_ALTERNATE);
+ if (!strcmp(value, Tcl_GetString(radioPtr->radiobutton.valueObj))) {
+ TtkWidgetChangeState(&radioPtr->core, TTK_STATE_SELECTED, 0);
+ } else {
+ TtkWidgetChangeState(&radioPtr->core, 0, TTK_STATE_SELECTED);
+ }
+}
+
+static void
+RadiobuttonCleanup(void *recordPtr)
+{
+ Radiobutton *radioPtr = recordPtr;
+ Ttk_UntraceVariable(radioPtr->radiobutton.variableTrace);
+ radioPtr->radiobutton.variableTrace = 0;
+ BaseCleanup(recordPtr);
+}
+
+static int
+RadiobuttonConfigure(Tcl_Interp *interp, void *recordPtr, int mask)
+{
+ Radiobutton *radioPtr = recordPtr;
+ Ttk_TraceHandle *vt = Ttk_TraceVariable(
+ interp, radioPtr->radiobutton.variableObj,
+ RadiobuttonVariableChanged, radioPtr);
+
+ if (!vt) {
+ return TCL_ERROR;
+ }
+
+ if (BaseConfigure(interp, recordPtr, mask) != TCL_OK) {
+ Ttk_UntraceVariable(vt);
+ return TCL_ERROR;
+ }
+
+ Ttk_UntraceVariable(radioPtr->radiobutton.variableTrace);
+ radioPtr->radiobutton.variableTrace = vt;
+
+ return TCL_OK;
+}
+
+static int
+RadiobuttonPostConfigure(Tcl_Interp *interp, void *recordPtr, int mask)
+{
+ Radiobutton *radioPtr = recordPtr;
+ int status = TCL_OK;
+
+ if (radioPtr->radiobutton.variableTrace)
+ status = Ttk_FireTrace(radioPtr->radiobutton.variableTrace);
+ if (status == TCL_OK && !WidgetDestroyed(&radioPtr->core))
+ status = BasePostConfigure(interp, recordPtr, mask);
+ return status;
+}
+
+/*
+ * Radiobutton 'invoke' subcommand:
+ * Sets the radiobutton -variable to the -value, evaluates the -command.
+ */
+static int
+RadiobuttonInvokeCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ Radiobutton *radioPtr = recordPtr;
+ WidgetCore *corePtr = &radioPtr->core;
+
+ if (objc > 2) {
+ Tcl_WrongNumArgs(interp, 1, objv, "invoke");
+ return TCL_ERROR;
+ }
+ if (corePtr->state & TTK_STATE_DISABLED)
+ return TCL_OK;
+
+ if (Tcl_ObjSetVar2(interp,
+ radioPtr->radiobutton.variableObj, NULL,
+ radioPtr->radiobutton.valueObj,
+ TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG)
+ == NULL)
+ return TCL_ERROR;
+
+ if (WidgetDestroyed(corePtr))
+ return TCL_ERROR;
+
+ return Tcl_EvalObjEx(interp,
+ radioPtr->radiobutton.commandObj, TCL_EVAL_GLOBAL);
+}
+
+static const Ttk_Ensemble RadiobuttonCommands[] = {
+ { "configure", TtkWidgetConfigureCommand,0 },
+ { "cget", TtkWidgetCgetCommand,0 },
+ { "invoke", RadiobuttonInvokeCommand,0 },
+ { "instate", TtkWidgetInstateCommand,0 },
+ { "state", TtkWidgetStateCommand,0 },
+ { "identify", TtkWidgetIdentifyCommand,0 },
+ /* MISSING: select, deselect */
+ { 0,0,0 }
+};
+
+static WidgetSpec RadiobuttonWidgetSpec =
+{
+ "TRadiobutton", /* className */
+ sizeof(Radiobutton), /* recordSize */
+ RadiobuttonOptionSpecs, /* optionSpecs */
+ RadiobuttonCommands, /* subcommands */
+ BaseInitialize, /* initializeProc */
+ RadiobuttonCleanup, /* cleanupProc */
+ RadiobuttonConfigure, /* configureProc */
+ RadiobuttonPostConfigure, /* postConfigureProc */
+ TtkWidgetGetLayout, /* getLayoutProc */
+ TtkWidgetSize, /* sizeProc */
+ TtkWidgetDoLayout, /* layoutProc */
+ TtkWidgetDisplay /* displayProc */
+};
+
+TTK_BEGIN_LAYOUT(RadiobuttonLayout)
+ TTK_GROUP("Radiobutton.padding", TTK_FILL_BOTH,
+ TTK_NODE("Radiobutton.indicator", TTK_PACK_LEFT)
+ TTK_GROUP("Radiobutton.focus", TTK_PACK_LEFT,
+ TTK_NODE("Radiobutton.label", TTK_FILL_BOTH)))
+TTK_END_LAYOUT
+
+/*------------------------------------------------------------------------
+ * +++ Menubutton widget.
+ */
+
+typedef struct
+{
+ Tcl_Obj *menuObj;
+ Tcl_Obj *directionObj;
+} MenubuttonPart;
+
+typedef struct
+{
+ WidgetCore core;
+ BasePart base;
+ MenubuttonPart menubutton;
+} Menubutton;
+
+/*
+ * Option specifications:
+ */
+static const char *const directionStrings[] = {
+ "above", "below", "left", "right", "flush", NULL
+};
+static Tk_OptionSpec MenubuttonOptionSpecs[] =
+{
+ {TK_OPTION_STRING, "-menu", "menu", "Menu",
+ "", Tk_Offset(Menubutton, menubutton.menuObj), -1, 0,0,0},
+ {TK_OPTION_STRING_TABLE, "-direction", "direction", "Direction",
+ "below", Tk_Offset(Menubutton, menubutton.directionObj), -1,
+ 0,(ClientData)directionStrings,GEOMETRY_CHANGED},
+
+ WIDGET_TAKEFOCUS_TRUE,
+ WIDGET_INHERIT_OPTIONS(BaseOptionSpecs)
+};
+
+static const Ttk_Ensemble MenubuttonCommands[] = {
+ { "configure", TtkWidgetConfigureCommand,0 },
+ { "cget", TtkWidgetCgetCommand,0 },
+ { "instate", TtkWidgetInstateCommand,0 },
+ { "state", TtkWidgetStateCommand,0 },
+ { "identify", TtkWidgetIdentifyCommand,0 },
+ { 0,0,0 }
+};
+
+static WidgetSpec MenubuttonWidgetSpec =
+{
+ "TMenubutton", /* className */
+ sizeof(Menubutton), /* recordSize */
+ MenubuttonOptionSpecs, /* optionSpecs */
+ MenubuttonCommands, /* subcommands */
+ BaseInitialize, /* initializeProc */
+ BaseCleanup, /* cleanupProc */
+ BaseConfigure, /* configureProc */
+ BasePostConfigure, /* postConfigureProc */
+ TtkWidgetGetLayout, /* getLayoutProc */
+ TtkWidgetSize, /* sizeProc */
+ TtkWidgetDoLayout, /* layoutProc */
+ TtkWidgetDisplay /* displayProc */
+};
+
+TTK_BEGIN_LAYOUT(MenubuttonLayout)
+ TTK_GROUP("Menubutton.border", TTK_FILL_BOTH,
+ TTK_GROUP("Menubutton.focus", TTK_FILL_BOTH,
+ TTK_NODE("Menubutton.indicator", TTK_PACK_RIGHT)
+ TTK_GROUP("Menubutton.padding", TTK_PACK_LEFT|TTK_EXPAND|TTK_FILL_X,
+ TTK_NODE("Menubutton.label", TTK_PACK_LEFT))))
+TTK_END_LAYOUT
+
+/*------------------------------------------------------------------------
+ * +++ Initialization.
+ */
+
+MODULE_SCOPE
+void TtkButton_Init(Tcl_Interp *interp)
+{
+ Ttk_Theme theme = Ttk_GetDefaultTheme(interp);
+
+ Ttk_RegisterLayout(theme, "TLabel", LabelLayout);
+ Ttk_RegisterLayout(theme, "TButton", ButtonLayout);
+ Ttk_RegisterLayout(theme, "TCheckbutton", CheckbuttonLayout);
+ Ttk_RegisterLayout(theme, "TRadiobutton", RadiobuttonLayout);
+ Ttk_RegisterLayout(theme, "TMenubutton", MenubuttonLayout);
+
+ RegisterWidget(interp, "ttk::label", &LabelWidgetSpec);
+ RegisterWidget(interp, "ttk::button", &ButtonWidgetSpec);
+ RegisterWidget(interp, "ttk::checkbutton", &CheckbuttonWidgetSpec);
+ RegisterWidget(interp, "ttk::radiobutton", &RadiobuttonWidgetSpec);
+ RegisterWidget(interp, "ttk::menubutton", &MenubuttonWidgetSpec);
+}
diff --git a/generic/ttk/ttkCache.c b/generic/ttk/ttkCache.c
new file mode 100644
index 0000000..e3aeaba
--- /dev/null
+++ b/generic/ttk/ttkCache.c
@@ -0,0 +1,350 @@
+/*
+ * Theme engine resource cache.
+ *
+ * Copyright (c) 2004, Joe English
+ *
+ * The problem:
+ *
+ * Tk maintains reference counts for fonts, colors, and images,
+ * and deallocates them when the reference count goes to zero.
+ * With the theme engine, resources are allocated right before
+ * drawing an element and released immediately after.
+ * This causes a severe performance penalty, and on PseudoColor
+ * visuals it causes colormap cycling as colormap entries are
+ * released and reused.
+ *
+ * Solution: Acquire fonts, colors, and objects from a
+ * resource cache instead of directly from Tk; the cache
+ * holds a semipermanent reference to the resource to keep
+ * it from being deallocated.
+ *
+ * The plumbing and control flow here is quite contorted;
+ * it would be better to address this problem in the core instead.
+ *
+ * @@@ BUGS/TODO: Need distinct caches for each combination
+ * of display, visual, and colormap.
+ *
+ * @@@ Colormap flashing on PseudoColor visuals is still possible,
+ * but this will be a transient effect.
+ */
+
+#include <stdio.h> /* for sprintf */
+#include <tk.h>
+#include "ttkTheme.h"
+
+struct Ttk_ResourceCache_ {
+ Tcl_Interp *interp; /* Interpreter for error reporting */
+ Tk_Window tkwin; /* Cache window. */
+ Tcl_HashTable fontTable; /* Entries: Tcl_Obj* holding FontObjs */
+ Tcl_HashTable colorTable; /* Entries: Tcl_Obj* holding ColorObjs */
+ Tcl_HashTable borderTable; /* Entries: Tcl_Obj* holding BorderObjs */
+ Tcl_HashTable imageTable; /* Entries: Tk_Images */
+
+ Tcl_HashTable namedColors; /* Entries: RGB values as Tcl_StringObjs */
+};
+
+/*
+ * Ttk_CreateResourceCache --
+ * Initialize a new resource cache.
+ */
+Ttk_ResourceCache Ttk_CreateResourceCache(Tcl_Interp *interp)
+{
+ Ttk_ResourceCache cache = (Ttk_ResourceCache)ckalloc(sizeof(*cache));
+
+ cache->tkwin = NULL; /* initialized later */
+ cache->interp = interp;
+ Tcl_InitHashTable(&cache->fontTable, TCL_STRING_KEYS);
+ Tcl_InitHashTable(&cache->colorTable, TCL_STRING_KEYS);
+ Tcl_InitHashTable(&cache->borderTable, TCL_STRING_KEYS);
+ Tcl_InitHashTable(&cache->imageTable, TCL_STRING_KEYS);
+ Tcl_InitHashTable(&cache->namedColors, TCL_STRING_KEYS);
+
+ return cache;
+}
+
+/*
+ * Ttk_ClearCache --
+ * Release references to all cached resources.
+ */
+static void Ttk_ClearCache(Ttk_ResourceCache cache)
+{
+ Tcl_HashSearch search;
+ Tcl_HashEntry *entryPtr;
+
+ /*
+ * Free fonts:
+ */
+ entryPtr = Tcl_FirstHashEntry(&cache->fontTable, &search);
+ while (entryPtr != NULL) {
+ Tcl_Obj *fontObj = Tcl_GetHashValue(entryPtr);
+ if (fontObj) {
+ Tk_FreeFontFromObj(cache->tkwin, fontObj);
+ Tcl_DecrRefCount(fontObj);
+ }
+ entryPtr = Tcl_NextHashEntry(&search);
+ }
+ Tcl_DeleteHashTable(&cache->fontTable);
+ Tcl_InitHashTable(&cache->fontTable, TCL_STRING_KEYS);
+
+ /*
+ * Free colors:
+ */
+ entryPtr = Tcl_FirstHashEntry(&cache->colorTable, &search);
+ while (entryPtr != NULL) {
+ Tcl_Obj *colorObj = Tcl_GetHashValue(entryPtr);
+ if (colorObj) {
+ Tk_FreeColorFromObj(cache->tkwin, colorObj);
+ Tcl_DecrRefCount(colorObj);
+ }
+ entryPtr = Tcl_NextHashEntry(&search);
+ }
+ Tcl_DeleteHashTable(&cache->colorTable);
+ Tcl_InitHashTable(&cache->colorTable, TCL_STRING_KEYS);
+
+ /*
+ * Free borders:
+ */
+ entryPtr = Tcl_FirstHashEntry(&cache->borderTable, &search);
+ while (entryPtr != NULL) {
+ Tcl_Obj *borderObj = Tcl_GetHashValue(entryPtr);
+ if (borderObj) {
+ Tk_Free3DBorderFromObj(cache->tkwin, borderObj);
+ Tcl_DecrRefCount(borderObj);
+ }
+ entryPtr = Tcl_NextHashEntry(&search);
+ }
+ Tcl_DeleteHashTable(&cache->borderTable);
+ Tcl_InitHashTable(&cache->borderTable, TCL_STRING_KEYS);
+
+ /*
+ * Free images:
+ */
+ entryPtr = Tcl_FirstHashEntry(&cache->imageTable, &search);
+ while (entryPtr != NULL) {
+ Tk_Image image = Tcl_GetHashValue(entryPtr);
+ if (image) {
+ Tk_FreeImage(image);
+ }
+ entryPtr = Tcl_NextHashEntry(&search);
+ }
+ Tcl_DeleteHashTable(&cache->imageTable);
+ Tcl_InitHashTable(&cache->imageTable, TCL_STRING_KEYS);
+
+ return;
+}
+
+/*
+ * Ttk_FreeResourceCache --
+ * Release references to all cached resources, delete the cache.
+ */
+
+void Ttk_FreeResourceCache(Ttk_ResourceCache cache)
+{
+ Tcl_HashEntry *entryPtr;
+ Tcl_HashSearch search;
+
+ Ttk_ClearCache(cache);
+
+ Tcl_DeleteHashTable(&cache->colorTable);
+ Tcl_DeleteHashTable(&cache->fontTable);
+ Tcl_DeleteHashTable(&cache->imageTable);
+
+ /*
+ * Free named colors:
+ */
+ entryPtr = Tcl_FirstHashEntry(&cache->namedColors, &search);
+ while (entryPtr != NULL) {
+ Tcl_Obj *colorNameObj = Tcl_GetHashValue(entryPtr);
+ Tcl_DecrRefCount(colorNameObj);
+ entryPtr = Tcl_NextHashEntry(&search);
+ }
+ Tcl_DeleteHashTable(&cache->namedColors);
+
+ ckfree((ClientData)cache);
+}
+
+/*
+ * CacheWinEventHandler --
+ * Detect when the cache window is destroyed, clear cache.
+ */
+static void CacheWinEventHandler(ClientData clientData, XEvent *eventPtr)
+{
+ Ttk_ResourceCache cache = clientData;
+
+ if (eventPtr->type != DestroyNotify) {
+ return;
+ }
+ Tk_DeleteEventHandler(cache->tkwin, StructureNotifyMask,
+ CacheWinEventHandler, clientData);
+ Ttk_ClearCache(cache);
+ cache->tkwin = NULL;
+}
+
+/*
+ * InitCacheWindow --
+ * Specify the cache window if not already set.
+ * @@@ SHOULD: use separate caches for each combination
+ * @@@ of display, visual, and colormap.
+ */
+static void InitCacheWindow(Ttk_ResourceCache cache, Tk_Window tkwin)
+{
+ if (cache->tkwin == NULL) {
+ cache->tkwin = tkwin;
+ Tk_CreateEventHandler(tkwin, StructureNotifyMask,
+ CacheWinEventHandler, cache);
+ }
+}
+
+/*
+ * Ttk_RegisterNamedColor --
+ * Specify an RGB triplet as a named color.
+ * Overrides any previous named color specification.
+ */
+void Ttk_RegisterNamedColor(
+ Ttk_ResourceCache cache,
+ const char *colorName,
+ XColor *colorPtr)
+{
+ int newEntry;
+ Tcl_HashEntry *entryPtr;
+ char nameBuf[14];
+ Tcl_Obj *colorNameObj;
+
+ sprintf(nameBuf, "#%04X%04X%04X",
+ colorPtr->red, colorPtr->green, colorPtr->blue);
+ colorNameObj = Tcl_NewStringObj(nameBuf, -1);
+ Tcl_IncrRefCount(colorNameObj);
+
+ entryPtr = Tcl_CreateHashEntry(&cache->namedColors, colorName, &newEntry);
+ if (!newEntry) {
+ Tcl_Obj *oldColor = Tcl_GetHashValue(entryPtr);
+ Tcl_DecrRefCount(oldColor);
+ }
+
+ Tcl_SetHashValue(entryPtr, colorNameObj);
+}
+
+/*
+ * CheckNamedColor(objPtr) --
+ * If objPtr is a registered color name, return a Tcl_Obj *
+ * containing the registered color value specification.
+ * Otherwise, return the input argument.
+ */
+static Tcl_Obj *CheckNamedColor(Ttk_ResourceCache cache, Tcl_Obj *objPtr)
+{
+ Tcl_HashEntry *entryPtr =
+ Tcl_FindHashEntry(&cache->namedColors, Tcl_GetString(objPtr));
+ if (entryPtr) { /* Use named color instead */
+ objPtr = Tcl_GetHashValue(entryPtr);
+ }
+ return objPtr;
+}
+
+/*
+ * Template for allocation routines:
+ */
+typedef void *(*Allocator)(Tcl_Interp *, Tk_Window, Tcl_Obj *);
+
+static Tcl_Obj *Ttk_Use(
+ Tcl_Interp *interp,
+ Tcl_HashTable *table,
+ Allocator allocate,
+ Tk_Window tkwin,
+ Tcl_Obj *objPtr)
+{
+ int newEntry;
+ Tcl_HashEntry *entryPtr =
+ Tcl_CreateHashEntry(table,Tcl_GetString(objPtr),&newEntry);
+ Tcl_Obj *cacheObj;
+
+ if (!newEntry) {
+ return Tcl_GetHashValue(entryPtr);
+ }
+
+ cacheObj = Tcl_DuplicateObj(objPtr);
+ Tcl_IncrRefCount(cacheObj);
+
+ if (allocate(interp, tkwin, cacheObj)) {
+ Tcl_SetHashValue(entryPtr, cacheObj);
+ return cacheObj;
+ } else {
+ Tcl_DecrRefCount(cacheObj);
+ Tcl_SetHashValue(entryPtr, NULL);
+ Tcl_BackgroundError(interp);
+ return NULL;
+ }
+}
+
+/*
+ * Ttk_UseFont --
+ * Acquire a font from the cache.
+ */
+Tcl_Obj *Ttk_UseFont(Ttk_ResourceCache cache, Tk_Window tkwin, Tcl_Obj *objPtr)
+{
+ InitCacheWindow(cache, tkwin);
+ return Ttk_Use(cache->interp,
+ &cache->fontTable,(Allocator)Tk_AllocFontFromObj, tkwin, objPtr);
+}
+
+/*
+ * Ttk_UseColor --
+ * Acquire a color from the cache.
+ */
+Tcl_Obj *Ttk_UseColor(Ttk_ResourceCache cache, Tk_Window tkwin, Tcl_Obj *objPtr)
+{
+ objPtr = CheckNamedColor(cache, objPtr);
+ InitCacheWindow(cache, tkwin);
+ return Ttk_Use(cache->interp,
+ &cache->colorTable,(Allocator)Tk_AllocColorFromObj, tkwin, objPtr);
+}
+
+/*
+ * Ttk_UseBorder --
+ * Acquire a Tk_3DBorder from the cache.
+ */
+Tcl_Obj *Ttk_UseBorder(
+ Ttk_ResourceCache cache, Tk_Window tkwin, Tcl_Obj *objPtr)
+{
+ objPtr = CheckNamedColor(cache, objPtr);
+ InitCacheWindow(cache, tkwin);
+ return Ttk_Use(cache->interp,
+ &cache->borderTable,(Allocator)Tk_Alloc3DBorderFromObj, tkwin, objPtr);
+}
+
+/* NullImageChanged --
+ * Tk_ImageChangedProc for Ttk_UseImage
+ */
+
+static void NullImageChanged(ClientData clientData,
+ int x, int y, int width, int height, int imageWidth, int imageHeight)
+{ /* No-op */ }
+
+/*
+ * Ttk_UseImage --
+ * Acquire a Tk_Image from the cache.
+ */
+Tk_Image Ttk_UseImage(Ttk_ResourceCache cache, Tk_Window tkwin, Tcl_Obj *objPtr)
+{
+ const char *imageName = Tcl_GetString(objPtr);
+ int newEntry;
+ Tcl_HashEntry *entryPtr =
+ Tcl_CreateHashEntry(&cache->imageTable,imageName,&newEntry);
+ Tk_Image image;
+
+ InitCacheWindow(cache, tkwin);
+
+ if (!newEntry) {
+ return Tcl_GetHashValue(entryPtr);
+ }
+
+ image = Tk_GetImage(cache->interp, tkwin, imageName, NullImageChanged,0);
+ Tcl_SetHashValue(entryPtr, image);
+
+ if (!image) {
+ Tcl_BackgroundError(cache->interp);
+ }
+
+ return image;
+}
+
+/*EOF*/
diff --git a/generic/ttk/ttkClamTheme.c b/generic/ttk/ttkClamTheme.c
new file mode 100644
index 0000000..572f630
--- /dev/null
+++ b/generic/ttk/ttkClamTheme.c
@@ -0,0 +1,971 @@
+/*
+ * Copyright (C) 2004 Joe English
+ *
+ * "clam" theme; inspired by the XFCE family of Gnome themes.
+ */
+
+#include <tk.h>
+#include "ttkTheme.h"
+
+/*
+ * Under windows, the Tk-provided XDrawLine and XDrawArc have an
+ * off-by-one error in the end point. This is especially apparent with this
+ * theme. Defining this macro as true handles this case.
+ */
+#if defined(WIN32) && !defined(WIN32_XDRAWLINE_HACK)
+# define WIN32_XDRAWLINE_HACK 1
+#else
+# define WIN32_XDRAWLINE_HACK 0
+#endif
+
+#define STR(x) StR(x)
+#define StR(x) #x
+
+#define SCROLLBAR_THICKNESS 14
+
+#define FRAME_COLOR "#dcdad5"
+#define LIGHT_COLOR "#ffffff"
+#define DARK_COLOR "#cfcdc8"
+#define DARKER_COLOR "#bab5ab"
+#define DARKEST_COLOR "#9e9a91"
+
+/*------------------------------------------------------------------------
+ * +++ Utilities.
+ */
+
+static GC Ttk_GCForColor(Tk_Window tkwin, Tcl_Obj* colorObj, Drawable d)
+{
+ GC gc = Tk_GCForColor(Tk_GetColorFromObj(tkwin, colorObj), d);
+
+#ifdef MAC_OSX_TK
+ /*
+ * Workaround for Tk bug under Aqua where the default line width is 0.
+ */
+ Display *display = Tk_Display(tkwin);
+ unsigned long mask = 0ul;
+ XGCValues gcValues;
+
+ gcValues.line_width = 1;
+ mask = GCLineWidth;
+
+ XChangeGC(display, gc, mask, &gcValues);
+#endif
+
+ return gc;
+}
+
+static void DrawSmoothBorder(
+ Tk_Window tkwin, Drawable d, Ttk_Box b,
+ Tcl_Obj *outerColorObj, Tcl_Obj *upperColorObj, Tcl_Obj *lowerColorObj)
+{
+ Display *display = Tk_Display(tkwin);
+ int x1 = b.x, x2 = b.x + b.width - 1;
+ int y1 = b.y, y2 = b.y + b.height - 1;
+ const int w = WIN32_XDRAWLINE_HACK;
+ GC gc;
+
+ if ( outerColorObj
+ && (gc=Ttk_GCForColor(tkwin,outerColorObj,d)))
+ {
+ XDrawLine(display,d,gc, x1+1,y1, x2-1+w,y1); /* N */
+ XDrawLine(display,d,gc, x1+1,y2, x2-1+w,y2); /* S */
+ XDrawLine(display,d,gc, x1,y1+1, x1,y2-1+w); /* E */
+ XDrawLine(display,d,gc, x2,y1+1, x2,y2-1+w); /* W */
+ }
+
+ if ( upperColorObj
+ && (gc=Ttk_GCForColor(tkwin,upperColorObj,d)))
+ {
+ XDrawLine(display,d,gc, x1+1,y1+1, x2-1+w,y1+1); /* N */
+ XDrawLine(display,d,gc, x1+1,y1+1, x1+1,y2-1); /* E */
+ }
+
+ if ( lowerColorObj
+ && (gc=Ttk_GCForColor(tkwin,lowerColorObj,d)))
+ {
+ XDrawLine(display,d,gc, x2-1,y2-1, x1+1-w,y2-1); /* S */
+ XDrawLine(display,d,gc, x2-1,y2-1, x2-1,y1+1-w); /* W */
+ }
+}
+
+static GC BackgroundGC(Tk_Window tkwin, Tcl_Obj *backgroundObj)
+{
+ Tk_3DBorder bd = Tk_Get3DBorderFromObj(tkwin, backgroundObj);
+ return Tk_3DBorderGC(tkwin, bd, TK_3D_FLAT_GC);
+}
+
+/*------------------------------------------------------------------------
+ * +++ Border element.
+ */
+
+typedef struct {
+ Tcl_Obj *borderColorObj;
+ Tcl_Obj *lightColorObj;
+ Tcl_Obj *darkColorObj;
+ Tcl_Obj *reliefObj;
+ Tcl_Obj *borderWidthObj; /* See <<NOTE-BORDERWIDTH>> */
+} BorderElement;
+
+static Ttk_ElementOptionSpec BorderElementOptions[] = {
+ { "-bordercolor", TK_OPTION_COLOR,
+ Tk_Offset(BorderElement,borderColorObj), DARKEST_COLOR },
+ { "-lightcolor", TK_OPTION_COLOR,
+ Tk_Offset(BorderElement,lightColorObj), LIGHT_COLOR },
+ { "-darkcolor", TK_OPTION_COLOR,
+ Tk_Offset(BorderElement,darkColorObj), DARK_COLOR },
+ { "-relief", TK_OPTION_RELIEF,
+ Tk_Offset(BorderElement,reliefObj), "flat" },
+ { "-borderwidth", TK_OPTION_PIXELS,
+ Tk_Offset(BorderElement,borderWidthObj), "2" },
+ { NULL, 0, 0, NULL }
+};
+
+/*
+ * <<NOTE-BORDERWIDTH>>: -borderwidth is only partially supported:
+ * in this theme, borders are always exactly 2 pixels thick.
+ * With -borderwidth 0, border is not drawn at all;
+ * otherwise a 2-pixel border is used. For -borderwidth > 2,
+ * the excess is used as padding.
+ */
+
+static void BorderElementSize(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+{
+ BorderElement *border = (BorderElement*)elementRecord;
+ int borderWidth = 2;
+ Tk_GetPixelsFromObj(NULL, tkwin, border->borderWidthObj, &borderWidth);
+ if (borderWidth == 1) ++borderWidth;
+ *paddingPtr = Ttk_UniformPadding((short)borderWidth);
+}
+
+static void BorderElementDraw(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ Drawable d, Ttk_Box b, unsigned state)
+{
+ BorderElement *border = elementRecord;
+ int relief = TK_RELIEF_FLAT;
+ int borderWidth = 2;
+ Tcl_Obj *outer = 0, *upper = 0, *lower = 0;
+
+ Tk_GetReliefFromObj(NULL, border->reliefObj, &relief);
+ Tk_GetPixelsFromObj(NULL, tkwin, border->borderWidthObj, &borderWidth);
+
+ if (borderWidth == 0) return;
+
+ switch (relief) {
+ case TK_RELIEF_GROOVE :
+ case TK_RELIEF_RIDGE :
+ case TK_RELIEF_RAISED :
+ outer = border->borderColorObj;
+ upper = border->lightColorObj;
+ lower = border->darkColorObj;
+ break;
+ case TK_RELIEF_SUNKEN :
+ outer = border->borderColorObj;
+ upper = border->darkColorObj;
+ lower = border->lightColorObj;
+ break;
+ case TK_RELIEF_FLAT :
+ outer = upper = lower = 0;
+ break;
+ case TK_RELIEF_SOLID :
+ outer = upper = lower = border->borderColorObj;
+ break;
+ }
+
+ DrawSmoothBorder(tkwin, d, b, outer, upper, lower);
+}
+
+static Ttk_ElementSpec BorderElementSpec = {
+ TK_STYLE_VERSION_2,
+ sizeof(BorderElement),
+ BorderElementOptions,
+ BorderElementSize,
+ BorderElementDraw
+};
+
+/*------------------------------------------------------------------------
+ * +++ Field element.
+ */
+
+typedef struct {
+ Tcl_Obj *borderColorObj;
+ Tcl_Obj *lightColorObj;
+ Tcl_Obj *darkColorObj;
+ Tcl_Obj *backgroundObj;
+} FieldElement;
+
+static Ttk_ElementOptionSpec FieldElementOptions[] = {
+ { "-bordercolor", TK_OPTION_COLOR,
+ Tk_Offset(FieldElement,borderColorObj), DARKEST_COLOR },
+ { "-lightcolor", TK_OPTION_COLOR,
+ Tk_Offset(FieldElement,lightColorObj), LIGHT_COLOR },
+ { "-darkcolor", TK_OPTION_COLOR,
+ Tk_Offset(FieldElement,darkColorObj), DARK_COLOR },
+ { "-fieldbackground", TK_OPTION_BORDER,
+ Tk_Offset(FieldElement,backgroundObj), "white" },
+ { NULL, 0, 0, NULL }
+};
+
+static void FieldElementSize(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+{
+ *paddingPtr = Ttk_UniformPadding(2);
+}
+
+static void FieldElementDraw(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ Drawable d, Ttk_Box b, unsigned state)
+{
+ FieldElement *field = elementRecord;
+ Tk_3DBorder bg = Tk_Get3DBorderFromObj(tkwin, field->backgroundObj);
+ Ttk_Box f = Ttk_PadBox(b, Ttk_UniformPadding(2));
+ Tcl_Obj *outer = field->borderColorObj,
+ *inner = field->lightColorObj;
+
+ DrawSmoothBorder(tkwin, d, b, outer, inner, inner);
+ Tk_Fill3DRectangle(
+ tkwin, d, bg, f.x, f.y, f.width, f.height, 0, TK_RELIEF_SUNKEN);
+}
+
+static Ttk_ElementSpec FieldElementSpec = {
+ TK_STYLE_VERSION_2,
+ sizeof(FieldElement),
+ FieldElementOptions,
+ FieldElementSize,
+ FieldElementDraw
+};
+
+/*
+ * Modified field element for comboboxes:
+ * Right edge is expanded to overlap the dropdown button.
+ */
+static void ComboboxFieldElementDraw(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ Drawable d, Ttk_Box b, unsigned state)
+{
+ FieldElement *field = elementRecord;
+ GC gc = Ttk_GCForColor(tkwin,field->borderColorObj,d);
+
+ ++b.width;
+ FieldElementDraw(clientData, elementRecord, tkwin, d, b, state);
+
+ XDrawLine(Tk_Display(tkwin), d, gc,
+ b.x + b.width - 1, b.y,
+ b.x + b.width - 1, b.y + b.height - 1 + WIN32_XDRAWLINE_HACK);
+}
+
+static Ttk_ElementSpec ComboboxFieldElementSpec = {
+ TK_STYLE_VERSION_2,
+ sizeof(FieldElement),
+ FieldElementOptions,
+ FieldElementSize,
+ ComboboxFieldElementDraw
+};
+
+/*------------------------------------------------------------------------
+ * +++ Indicator elements for check and radio buttons.
+ */
+
+typedef struct {
+ Tcl_Obj *sizeObj;
+ Tcl_Obj *marginObj;
+ Tcl_Obj *backgroundObj;
+ Tcl_Obj *foregroundObj;
+ Tcl_Obj *upperColorObj;
+ Tcl_Obj *lowerColorObj;
+} IndicatorElement;
+
+static Ttk_ElementOptionSpec IndicatorElementOptions[] = {
+ { "-indicatorsize", TK_OPTION_PIXELS,
+ Tk_Offset(IndicatorElement,sizeObj), "10" },
+ { "-indicatormargin", TK_OPTION_STRING,
+ Tk_Offset(IndicatorElement,marginObj), "1" },
+ { "-indicatorbackground", TK_OPTION_COLOR,
+ Tk_Offset(IndicatorElement,backgroundObj), "white" },
+ { "-indicatorforeground", TK_OPTION_COLOR,
+ Tk_Offset(IndicatorElement,foregroundObj), "black" },
+ { "-upperbordercolor", TK_OPTION_COLOR,
+ Tk_Offset(IndicatorElement,upperColorObj), DARKEST_COLOR },
+ { "-lowerbordercolor", TK_OPTION_COLOR,
+ Tk_Offset(IndicatorElement,lowerColorObj), DARK_COLOR },
+ { NULL, 0, 0, NULL }
+};
+
+static void IndicatorElementSize(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+{
+ IndicatorElement *indicator = elementRecord;
+ Ttk_Padding margins;
+ int size = 10;
+ Ttk_GetPaddingFromObj(NULL, tkwin, indicator->marginObj, &margins);
+ Tk_GetPixelsFromObj(NULL, tkwin, indicator->sizeObj, &size);
+ *widthPtr = size + Ttk_PaddingWidth(margins);
+ *heightPtr = size + Ttk_PaddingHeight(margins);
+}
+
+static void RadioIndicatorElementDraw(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ Drawable d, Ttk_Box b, unsigned state)
+{
+ IndicatorElement *indicator = elementRecord;
+ GC gcb=Ttk_GCForColor(tkwin,indicator->backgroundObj,d);
+ GC gcf=Ttk_GCForColor(tkwin,indicator->foregroundObj,d);
+ GC gcu=Ttk_GCForColor(tkwin,indicator->upperColorObj,d);
+ GC gcl=Ttk_GCForColor(tkwin,indicator->lowerColorObj,d);
+ Ttk_Padding padding;
+
+ Ttk_GetPaddingFromObj(NULL, tkwin, indicator->marginObj, &padding);
+ b = Ttk_PadBox(b, padding);
+
+ XFillArc(Tk_Display(tkwin),d,gcb, b.x,b.y,b.width,b.height, 0,360*64);
+ XDrawArc(Tk_Display(tkwin),d,gcl, b.x,b.y,b.width,b.height, 225*64,180*64);
+ XDrawArc(Tk_Display(tkwin),d,gcu, b.x,b.y,b.width,b.height, 45*64,180*64);
+
+ if (state & TTK_STATE_SELECTED) {
+ b = Ttk_PadBox(b,Ttk_UniformPadding(3));
+ XFillArc(Tk_Display(tkwin),d,gcf, b.x,b.y,b.width,b.height, 0,360*64);
+ XDrawArc(Tk_Display(tkwin),d,gcf, b.x,b.y,b.width,b.height, 0,360*64);
+#if WIN32_XDRAWLINE_HACK
+ XDrawArc(Tk_Display(tkwin),d,gcf, b.x,b.y,b.width,b.height, 300*64,360*64);
+#endif
+ }
+}
+
+static void CheckIndicatorElementDraw(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ Drawable d, Ttk_Box b, unsigned state)
+{
+ Display *display = Tk_Display(tkwin);
+ IndicatorElement *indicator = elementRecord;
+ GC gcb=Ttk_GCForColor(tkwin,indicator->backgroundObj,d);
+ GC gcf=Ttk_GCForColor(tkwin,indicator->foregroundObj,d);
+ GC gcu=Ttk_GCForColor(tkwin,indicator->upperColorObj,d);
+ GC gcl=Ttk_GCForColor(tkwin,indicator->lowerColorObj,d);
+ Ttk_Padding padding;
+ const int w = WIN32_XDRAWLINE_HACK;
+
+ Ttk_GetPaddingFromObj(NULL, tkwin, indicator->marginObj, &padding);
+ b = Ttk_PadBox(b, padding);
+
+ XFillRectangle(display,d,gcb, b.x,b.y,b.width,b.height);
+ XDrawLine(display,d,gcl,b.x,b.y+b.height,b.x+b.width+w,b.y+b.height);/*S*/
+ XDrawLine(display,d,gcl,b.x+b.width,b.y,b.x+b.width,b.y+b.height+w); /*E*/
+ XDrawLine(display,d,gcu,b.x,b.y, b.x,b.y+b.height+w); /*W*/
+ XDrawLine(display,d,gcu,b.x,b.y, b.x+b.width+w,b.y); /*N*/
+
+ if (state & TTK_STATE_SELECTED) {
+ int p,q,r,s;
+
+ b = Ttk_PadBox(b,Ttk_UniformPadding(2));
+ p = b.x, q = b.y, r = b.x+b.width, s = b.y+b.height;
+
+ r+=w, s+=w;
+ XDrawLine(display, d, gcf, p, q, r, s);
+ XDrawLine(display, d, gcf, p+1, q, r, s-1);
+ XDrawLine(display, d, gcf, p, q+1, r-1, s);
+
+ s-=w, q-=w;
+ XDrawLine(display, d, gcf, p, s, r, q);
+ XDrawLine(display, d, gcf, p+1, s, r, q+1);
+ XDrawLine(display, d, gcf, p, s-1, r-1, q);
+ }
+}
+
+static Ttk_ElementSpec RadioIndicatorElementSpec = {
+ TK_STYLE_VERSION_2,
+ sizeof(IndicatorElement),
+ IndicatorElementOptions,
+ IndicatorElementSize,
+ RadioIndicatorElementDraw
+};
+
+static Ttk_ElementSpec CheckIndicatorElementSpec = {
+ TK_STYLE_VERSION_2,
+ sizeof(IndicatorElement),
+ IndicatorElementOptions,
+ IndicatorElementSize,
+ CheckIndicatorElementDraw
+};
+
+#define MENUBUTTON_ARROW_SIZE 5
+
+typedef struct {
+ Tcl_Obj *sizeObj;
+ Tcl_Obj *colorObj;
+ Tcl_Obj *paddingObj;
+} MenuIndicatorElement;
+
+static Ttk_ElementOptionSpec MenuIndicatorElementOptions[] =
+{
+ { "-arrowsize", TK_OPTION_PIXELS,
+ Tk_Offset(MenuIndicatorElement,sizeObj),
+ STR(MENUBUTTON_ARROW_SIZE)},
+ { "-arrowcolor",TK_OPTION_COLOR,
+ Tk_Offset(MenuIndicatorElement,colorObj),
+ "black" },
+ { "-arrowpadding",TK_OPTION_STRING,
+ Tk_Offset(MenuIndicatorElement,paddingObj),
+ "3" },
+ { NULL, 0, 0, NULL }
+};
+
+static void MenuIndicatorElementSize(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+{
+ MenuIndicatorElement *indicator = elementRecord;
+ Ttk_Padding margins;
+ int size = MENUBUTTON_ARROW_SIZE;
+ Tk_GetPixelsFromObj(NULL, tkwin, indicator->sizeObj, &size);
+ Ttk_GetPaddingFromObj(NULL, tkwin, indicator->paddingObj, &margins);
+ TtkArrowSize(size, ARROW_DOWN, widthPtr, heightPtr);
+ *widthPtr += Ttk_PaddingWidth(margins);
+ *heightPtr += Ttk_PaddingHeight(margins);
+}
+
+static void MenuIndicatorElementDraw(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ Drawable d, Ttk_Box b, unsigned int state)
+{
+ MenuIndicatorElement *indicator = elementRecord;
+ XColor *arrowColor = Tk_GetColorFromObj(tkwin, indicator->colorObj);
+ GC gc = Tk_GCForColor(arrowColor, d);
+ int size = MENUBUTTON_ARROW_SIZE;
+ int width, height;
+
+ Tk_GetPixelsFromObj(NULL, tkwin, indicator->sizeObj, &size);
+
+ TtkArrowSize(size, ARROW_DOWN, &width, &height);
+ b = Ttk_StickBox(b, width, height, 0);
+ TtkFillArrow(Tk_Display(tkwin), d, gc, b, ARROW_DOWN);
+}
+
+static Ttk_ElementSpec MenuIndicatorElementSpec =
+{
+ TK_STYLE_VERSION_2,
+ sizeof(MenuIndicatorElement),
+ MenuIndicatorElementOptions,
+ MenuIndicatorElementSize,
+ MenuIndicatorElementDraw
+};
+
+/*------------------------------------------------------------------------
+ * +++ Grips.
+ *
+ * TODO: factor this with ThumbElementDraw
+ */
+
+static Ttk_Orient GripClientData[] = {
+ TTK_ORIENT_HORIZONTAL, TTK_ORIENT_VERTICAL
+};
+
+typedef struct {
+ Tcl_Obj *lightColorObj;
+ Tcl_Obj *borderColorObj;
+ Tcl_Obj *gripCountObj;
+} GripElement;
+
+static Ttk_ElementOptionSpec GripElementOptions[] = {
+ { "-lightcolor", TK_OPTION_COLOR,
+ Tk_Offset(GripElement,lightColorObj), LIGHT_COLOR },
+ { "-bordercolor", TK_OPTION_COLOR,
+ Tk_Offset(GripElement,borderColorObj), DARKEST_COLOR },
+ { "-gripcount", TK_OPTION_INT,
+ Tk_Offset(GripElement,gripCountObj), "5" },
+ { NULL, 0, 0, NULL }
+};
+
+static void GripElementSize(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+{
+ int horizontal = *((Ttk_Orient*)clientData) == TTK_ORIENT_HORIZONTAL;
+ GripElement *grip = elementRecord;
+ int gripCount = 0;
+
+ Tcl_GetIntFromObj(NULL, grip->gripCountObj, &gripCount);
+ if (horizontal) {
+ *widthPtr = 2*gripCount;
+ } else {
+ *heightPtr = 2*gripCount;
+ }
+}
+
+static void GripElementDraw(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ Drawable d, Ttk_Box b, unsigned state)
+{
+ const int w = WIN32_XDRAWLINE_HACK;
+ int horizontal = *((Ttk_Orient*)clientData) == TTK_ORIENT_HORIZONTAL;
+ GripElement *grip = elementRecord;
+ GC lightGC = Ttk_GCForColor(tkwin,grip->lightColorObj,d);
+ GC darkGC = Ttk_GCForColor(tkwin,grip->borderColorObj,d);
+ int gripPad = 1, gripCount = 0;
+ int i;
+
+ Tcl_GetIntFromObj(NULL, grip->gripCountObj, &gripCount);
+
+ if (horizontal) {
+ int x = b.x + b.width / 2 - gripCount;
+ int y1 = b.y + gripPad, y2 = b.y + b.height - gripPad - 1 + w;
+ for (i=0; i<gripCount; ++i) {
+ XDrawLine(Tk_Display(tkwin), d, darkGC, x,y1, x,y2); ++x;
+ XDrawLine(Tk_Display(tkwin), d, lightGC, x,y1, x,y2); ++x;
+ }
+ } else {
+ int y = b.y + b.height / 2 - gripCount;
+ int x1 = b.x + gripPad, x2 = b.x + b.width - gripPad - 1 + w;
+ for (i=0; i<gripCount; ++i) {
+ XDrawLine(Tk_Display(tkwin), d, darkGC, x1,y, x2,y); ++y;
+ XDrawLine(Tk_Display(tkwin), d, lightGC, x1,y, x2,y); ++y;
+ }
+ }
+}
+
+static Ttk_ElementSpec GripElementSpec = {
+ TK_STYLE_VERSION_2,
+ sizeof(GripElement),
+ GripElementOptions,
+ GripElementSize,
+ GripElementDraw
+};
+
+/*------------------------------------------------------------------------
+ * +++ Scrollbar elements: trough, arrows, thumb.
+ *
+ * Notice that the trough element has 0 internal padding;
+ * that way the thumb and arrow borders overlap the trough.
+ */
+
+typedef struct { /* Common element record for scrollbar elements */
+ Tcl_Obj *orientObj;
+ Tcl_Obj *backgroundObj;
+ Tcl_Obj *borderColorObj;
+ Tcl_Obj *troughColorObj;
+ Tcl_Obj *lightColorObj;
+ Tcl_Obj *darkColorObj;
+ Tcl_Obj *arrowColorObj;
+ Tcl_Obj *arrowSizeObj;
+ Tcl_Obj *gripCountObj;
+ Tcl_Obj *sliderlengthObj;
+} ScrollbarElement;
+
+static Ttk_ElementOptionSpec ScrollbarElementOptions[] = {
+ { "-orient", TK_OPTION_ANY,
+ Tk_Offset(ScrollbarElement, orientObj), "horizontal" },
+ { "-background", TK_OPTION_BORDER,
+ Tk_Offset(ScrollbarElement,backgroundObj), FRAME_COLOR },
+ { "-bordercolor", TK_OPTION_COLOR,
+ Tk_Offset(ScrollbarElement,borderColorObj), DARKEST_COLOR },
+ { "-troughcolor", TK_OPTION_COLOR,
+ Tk_Offset(ScrollbarElement,troughColorObj), DARKER_COLOR },
+ { "-lightcolor", TK_OPTION_COLOR,
+ Tk_Offset(ScrollbarElement,lightColorObj), LIGHT_COLOR },
+ { "-darkcolor", TK_OPTION_COLOR,
+ Tk_Offset(ScrollbarElement,darkColorObj), DARK_COLOR },
+ { "-arrowcolor", TK_OPTION_COLOR,
+ Tk_Offset(ScrollbarElement,arrowColorObj), "#000000" },
+ { "-arrowsize", TK_OPTION_PIXELS,
+ Tk_Offset(ScrollbarElement,arrowSizeObj), STR(SCROLLBAR_THICKNESS) },
+ { "-gripcount", TK_OPTION_INT,
+ Tk_Offset(ScrollbarElement,gripCountObj), "5" },
+ { "-sliderlength", TK_OPTION_INT,
+ Tk_Offset(ScrollbarElement,sliderlengthObj), "30" },
+ { NULL, 0, 0, NULL }
+};
+
+static void TroughElementDraw(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ Drawable d, Ttk_Box b, unsigned state)
+{
+ ScrollbarElement *sb = elementRecord;
+ GC gcb = Ttk_GCForColor(tkwin,sb->borderColorObj,d);
+ GC gct = Ttk_GCForColor(tkwin,sb->troughColorObj,d);
+ XFillRectangle(Tk_Display(tkwin), d, gct, b.x, b.y, b.width-1, b.height-1);
+ XDrawRectangle(Tk_Display(tkwin), d, gcb, b.x, b.y, b.width-1, b.height-1);
+}
+
+static Ttk_ElementSpec TroughElementSpec = {
+ TK_STYLE_VERSION_2,
+ sizeof(ScrollbarElement),
+ ScrollbarElementOptions,
+ TtkNullElementSize,
+ TroughElementDraw
+};
+
+static void ThumbElementSize(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+{
+ ScrollbarElement *sb = elementRecord;
+ int size = SCROLLBAR_THICKNESS;
+ Tcl_GetIntFromObj(NULL, sb->arrowSizeObj, &size);
+ *widthPtr = *heightPtr = size;
+}
+
+static void ThumbElementDraw(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ Drawable d, Ttk_Box b, unsigned state)
+{
+ ScrollbarElement *sb = elementRecord;
+ int gripCount = 0, orient = TTK_ORIENT_HORIZONTAL;
+ GC lightGC, darkGC;
+ int x1, y1, x2, y2, dx, dy, i;
+ const int w = WIN32_XDRAWLINE_HACK;
+
+ DrawSmoothBorder(tkwin, d, b,
+ sb->borderColorObj, sb->lightColorObj, sb->darkColorObj);
+ XFillRectangle(
+ Tk_Display(tkwin), d, BackgroundGC(tkwin, sb->backgroundObj),
+ b.x+2, b.y+2, b.width-4, b.height-4);
+
+ /*
+ * Draw grip:
+ */
+ Ttk_GetOrientFromObj(NULL, sb->orientObj, &orient);
+ Tcl_GetIntFromObj(NULL, sb->gripCountObj, &gripCount);
+ lightGC = Ttk_GCForColor(tkwin,sb->lightColorObj,d);
+ darkGC = Ttk_GCForColor(tkwin,sb->borderColorObj,d);
+
+ if (orient == TTK_ORIENT_HORIZONTAL) {
+ dx = 1; dy = 0;
+ x1 = x2 = b.x + b.width / 2 - gripCount;
+ y1 = b.y + 2;
+ y2 = b.y + b.height - 3 + w;
+ } else {
+ dx = 0; dy = 1;
+ y1 = y2 = b.y + b.height / 2 - gripCount;
+ x1 = b.x + 2;
+ x2 = b.x + b.width - 3 + w;
+ }
+
+ for (i=0; i<gripCount; ++i) {
+ XDrawLine(Tk_Display(tkwin), d, darkGC, x1,y1, x2,y2);
+ x1 += dx; x2 += dx; y1 += dy; y2 += dy;
+ XDrawLine(Tk_Display(tkwin), d, lightGC, x1,y1, x2,y2);
+ x1 += dx; x2 += dx; y1 += dy; y2 += dy;
+ }
+}
+
+static Ttk_ElementSpec ThumbElementSpec = {
+ TK_STYLE_VERSION_2,
+ sizeof(ScrollbarElement),
+ ScrollbarElementOptions,
+ ThumbElementSize,
+ ThumbElementDraw
+};
+
+/*------------------------------------------------------------------------
+ * +++ Slider element.
+ */
+static void SliderElementSize(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+{
+ ScrollbarElement *sb = elementRecord;
+ int length, thickness, orient;
+
+ length = thickness = SCROLLBAR_THICKNESS;
+ Ttk_GetOrientFromObj(NULL, sb->orientObj, &orient);
+ Tcl_GetIntFromObj(NULL, sb->arrowSizeObj, &thickness);
+ Tk_GetPixelsFromObj(NULL, tkwin, sb->sliderlengthObj, &length);
+ if (orient == TTK_ORIENT_VERTICAL) {
+ *heightPtr = length;
+ *widthPtr = thickness;
+ } else {
+ *heightPtr = thickness;
+ *widthPtr = length;
+ }
+
+}
+
+static Ttk_ElementSpec SliderElementSpec = {
+ TK_STYLE_VERSION_2,
+ sizeof(ScrollbarElement),
+ ScrollbarElementOptions,
+ SliderElementSize,
+ ThumbElementDraw
+};
+
+/*------------------------------------------------------------------------
+ * +++ Progress bar element
+ */
+static void PbarElementSize(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+{
+ SliderElementSize(clientData, elementRecord, tkwin,
+ widthPtr, heightPtr, paddingPtr);
+ *paddingPtr = Ttk_UniformPadding(2);
+ *widthPtr += 4;
+ *heightPtr += 4;
+}
+
+static void PbarElementDraw(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ Drawable d, Ttk_Box b, unsigned state)
+{
+ ScrollbarElement *sb = elementRecord;
+
+ b = Ttk_PadBox(b, Ttk_UniformPadding(2));
+ if (b.width > 4 && b.height > 4) {
+ DrawSmoothBorder(tkwin, d, b,
+ sb->borderColorObj, sb->lightColorObj, sb->darkColorObj);
+ XFillRectangle(Tk_Display(tkwin), d,
+ BackgroundGC(tkwin, sb->backgroundObj),
+ b.x+2, b.y+2, b.width-4, b.height-4);
+ }
+}
+
+static Ttk_ElementSpec PbarElementSpec = {
+ TK_STYLE_VERSION_2,
+ sizeof(ScrollbarElement),
+ ScrollbarElementOptions,
+ PbarElementSize,
+ PbarElementDraw
+};
+
+
+/*------------------------------------------------------------------------
+ * +++ Scrollbar arrows.
+ */
+static int ArrowElements[] = { ARROW_UP, ARROW_DOWN, ARROW_LEFT, ARROW_RIGHT };
+
+static void ArrowElementSize(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+{
+ ScrollbarElement *sb = elementRecord;
+ int size = SCROLLBAR_THICKNESS;
+ Tcl_GetIntFromObj(NULL, sb->arrowSizeObj, &size);
+ *widthPtr = *heightPtr = size;
+}
+
+static void ArrowElementDraw(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ Drawable d, Ttk_Box b, unsigned state)
+{
+ ArrowDirection dir = *(ArrowDirection*)clientData;
+ ScrollbarElement *sb = elementRecord;
+ GC gc = Ttk_GCForColor(tkwin,sb->arrowColorObj, d);
+ int h, cx, cy;
+
+ DrawSmoothBorder(tkwin, d, b,
+ sb->borderColorObj, sb->lightColorObj, sb->darkColorObj);
+
+ XFillRectangle(
+ Tk_Display(tkwin), d, BackgroundGC(tkwin, sb->backgroundObj),
+ b.x+2, b.y+2, b.width-4, b.height-4);
+
+ b = Ttk_PadBox(b, Ttk_UniformPadding(3));
+ h = b.width < b.height ? b.width : b.height;
+ TtkArrowSize(h/2, dir, &cx, &cy);
+ b = Ttk_AnchorBox(b, cx, cy, TK_ANCHOR_CENTER);
+
+ TtkFillArrow(Tk_Display(tkwin), d, gc, b, dir);
+}
+
+static Ttk_ElementSpec ArrowElementSpec = {
+ TK_STYLE_VERSION_2,
+ sizeof(ScrollbarElement),
+ ScrollbarElementOptions,
+ ArrowElementSize,
+ ArrowElementDraw
+};
+
+
+/*------------------------------------------------------------------------
+ * +++ Notebook elements.
+ *
+ * Note: Tabs, except for the rightmost, overlap the neighbor to
+ * their right by one pixel.
+ */
+
+typedef struct {
+ Tcl_Obj *backgroundObj;
+ Tcl_Obj *borderColorObj;
+ Tcl_Obj *lightColorObj;
+ Tcl_Obj *darkColorObj;
+} NotebookElement;
+
+static Ttk_ElementOptionSpec NotebookElementOptions[] = {
+ { "-background", TK_OPTION_BORDER,
+ Tk_Offset(NotebookElement,backgroundObj), FRAME_COLOR },
+ { "-bordercolor", TK_OPTION_COLOR,
+ Tk_Offset(NotebookElement,borderColorObj), DARKEST_COLOR },
+ { "-lightcolor", TK_OPTION_COLOR,
+ Tk_Offset(NotebookElement,lightColorObj), LIGHT_COLOR },
+ { "-darkcolor", TK_OPTION_COLOR,
+ Tk_Offset(NotebookElement,darkColorObj), DARK_COLOR },
+ { NULL, 0, 0, NULL }
+};
+
+static void TabElementSize(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+{
+ int borderWidth = 2;
+ paddingPtr->top = paddingPtr->left = paddingPtr->right = borderWidth;
+ paddingPtr->bottom = 0;
+}
+
+static void TabElementDraw(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ Drawable d, Ttk_Box b, unsigned int state)
+{
+ NotebookElement *tab = elementRecord;
+ Tk_3DBorder border = Tk_Get3DBorderFromObj(tkwin, tab->backgroundObj);
+ Display *display = Tk_Display(tkwin);
+ int borderWidth = 2, dh = 0;
+ int x1,y1,x2,y2;
+ GC gc;
+ const int w = WIN32_XDRAWLINE_HACK;
+
+ if (state & TTK_STATE_SELECTED) {
+ dh = borderWidth;
+ }
+
+ if (state & TTK_STATE_USER2) { /* Rightmost tab */
+ --b.width;
+ }
+
+ Tk_Fill3DRectangle(tkwin, d, border,
+ b.x+2, b.y+2, b.width-1, b.height-2+dh, borderWidth, TK_RELIEF_FLAT);
+
+ x1 = b.x, x2 = b.x + b.width;
+ y1 = b.y, y2 = b.y + b.height;
+
+
+ gc=Ttk_GCForColor(tkwin,tab->borderColorObj,d);
+ XDrawLine(display,d,gc, x1,y1+1, x1,y2+w);
+ XDrawLine(display,d,gc, x2,y1+1, x2,y2+w);
+ XDrawLine(display,d,gc, x1+1,y1, x2-1+w,y1);
+
+ gc=Ttk_GCForColor(tkwin,tab->lightColorObj,d);
+ XDrawLine(display,d,gc, x1+1,y1+1, x1+1,y2-1+dh+w);
+ XDrawLine(display,d,gc, x1+1,y1+1, x2-1+w,y1+1);
+}
+
+static Ttk_ElementSpec TabElementSpec =
+{
+ TK_STYLE_VERSION_2,
+ sizeof(NotebookElement),
+ NotebookElementOptions,
+ TabElementSize,
+ TabElementDraw
+};
+
+static void ClientElementSize(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+{
+ int borderWidth = 2;
+ *paddingPtr = Ttk_UniformPadding((short)borderWidth);
+}
+
+static void ClientElementDraw(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ Drawable d, Ttk_Box b, unsigned int state)
+{
+ NotebookElement *ce = elementRecord;
+ Tk_3DBorder border = Tk_Get3DBorderFromObj(tkwin, ce->backgroundObj);
+ int borderWidth = 2;
+
+ Tk_Fill3DRectangle(tkwin, d, border,
+ b.x, b.y, b.width, b.height, borderWidth,TK_RELIEF_FLAT);
+ DrawSmoothBorder(tkwin, d, b,
+ ce->borderColorObj, ce->lightColorObj, ce->darkColorObj);
+}
+
+static Ttk_ElementSpec ClientElementSpec =
+{
+ TK_STYLE_VERSION_2,
+ sizeof(NotebookElement),
+ NotebookElementOptions,
+ ClientElementSize,
+ ClientElementDraw
+};
+
+/*------------------------------------------------------------------------
+ * +++ Modified widget layouts.
+ */
+
+TTK_BEGIN_LAYOUT_TABLE(LayoutTable)
+
+TTK_LAYOUT("TCombobox",
+ TTK_NODE("Combobox.downarrow", TTK_PACK_RIGHT|TTK_FILL_Y)
+ TTK_GROUP("Combobox.field", TTK_PACK_LEFT|TTK_FILL_BOTH|TTK_EXPAND,
+ TTK_GROUP("Combobox.padding", TTK_FILL_BOTH,
+ TTK_NODE("Combobox.textarea", TTK_FILL_BOTH))))
+
+TTK_LAYOUT("Horizontal.Sash",
+ TTK_GROUP("Sash.hsash", TTK_FILL_BOTH,
+ TTK_NODE("Sash.hgrip", TTK_FILL_BOTH)))
+
+TTK_LAYOUT("Vertical.Sash",
+ TTK_GROUP("Sash.vsash", TTK_FILL_BOTH,
+ TTK_NODE("Sash.vgrip", TTK_FILL_BOTH)))
+
+TTK_END_LAYOUT_TABLE
+
+/*------------------------------------------------------------------------
+ * +++ Initialization.
+ */
+
+MODULE_SCOPE int
+TtkClamTheme_Init(Tcl_Interp *interp)
+{
+ Ttk_Theme theme = Ttk_CreateTheme(interp, "clam", 0);
+
+ if (!theme) {
+ return TCL_ERROR;
+ }
+
+ Ttk_RegisterElement(interp,
+ theme, "border", &BorderElementSpec, NULL);
+ Ttk_RegisterElement(interp,
+ theme, "field", &FieldElementSpec, NULL);
+ Ttk_RegisterElement(interp,
+ theme, "Combobox.field", &ComboboxFieldElementSpec, NULL);
+ Ttk_RegisterElement(interp,
+ theme, "trough", &TroughElementSpec, NULL);
+ Ttk_RegisterElement(interp,
+ theme, "thumb", &ThumbElementSpec, NULL);
+ Ttk_RegisterElement(interp,
+ theme, "uparrow", &ArrowElementSpec, &ArrowElements[0]);
+ Ttk_RegisterElement(interp,
+ theme, "downarrow", &ArrowElementSpec, &ArrowElements[1]);
+ Ttk_RegisterElement(interp,
+ theme, "leftarrow", &ArrowElementSpec, &ArrowElements[2]);
+ Ttk_RegisterElement(interp,
+ theme, "rightarrow", &ArrowElementSpec, &ArrowElements[3]);
+
+ Ttk_RegisterElement(interp,
+ theme, "Radiobutton.indicator", &RadioIndicatorElementSpec, NULL);
+ Ttk_RegisterElement(interp,
+ theme, "Checkbutton.indicator", &CheckIndicatorElementSpec, NULL);
+ Ttk_RegisterElement(interp,
+ theme, "Menubutton.indicator", &MenuIndicatorElementSpec, NULL);
+
+ Ttk_RegisterElement(interp, theme, "tab", &TabElementSpec, NULL);
+ Ttk_RegisterElement(interp, theme, "client", &ClientElementSpec, NULL);
+
+ Ttk_RegisterElement(interp, theme, "slider", &SliderElementSpec, NULL);
+ Ttk_RegisterElement(interp, theme, "bar", &PbarElementSpec, NULL);
+ Ttk_RegisterElement(interp, theme, "pbar", &PbarElementSpec, NULL);
+
+ Ttk_RegisterElement(interp, theme, "hgrip",
+ &GripElementSpec, &GripClientData[0]);
+ Ttk_RegisterElement(interp, theme, "vgrip",
+ &GripElementSpec, &GripClientData[1]);
+
+ Ttk_RegisterLayouts(theme, LayoutTable);
+
+ Tcl_PkgProvide(interp, "ttk::theme::clam", TTK_VERSION);
+
+ return TCL_OK;
+}
diff --git a/generic/ttk/ttkClassicTheme.c b/generic/ttk/ttkClassicTheme.c
new file mode 100644
index 0000000..2fbcd76
--- /dev/null
+++ b/generic/ttk/ttkClassicTheme.c
@@ -0,0 +1,513 @@
+/*
+ * Copyright (c) 2004, Joe English
+ *
+ * "classic" theme; implements the classic Motif-like Tk look.
+ *
+ */
+
+#include <tk.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include "ttkTheme.h"
+
+#define DEFAULT_BORDERWIDTH "2"
+#define DEFAULT_ARROW_SIZE "15"
+
+/*----------------------------------------------------------------------
+ * +++ Highlight element implementation.
+ * Draw a solid highlight border to indicate focus.
+ */
+
+typedef struct {
+ Tcl_Obj *highlightColorObj;
+ Tcl_Obj *highlightThicknessObj;
+} HighlightElement;
+
+static Ttk_ElementOptionSpec HighlightElementOptions[] = {
+ { "-highlightcolor",TK_OPTION_COLOR,
+ Tk_Offset(HighlightElement,highlightColorObj), DEFAULT_BACKGROUND },
+ { "-highlightthickness",TK_OPTION_PIXELS,
+ Tk_Offset(HighlightElement,highlightThicknessObj), "0" },
+ { NULL, 0, 0, NULL }
+};
+
+static void HighlightElementSize(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+{
+ HighlightElement *hl = elementRecord;
+ int highlightThickness = 0;
+
+ Tcl_GetIntFromObj(NULL,hl->highlightThicknessObj,&highlightThickness);
+ *paddingPtr = Ttk_UniformPadding((short)highlightThickness);
+}
+
+static void HighlightElementDraw(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ Drawable d, Ttk_Box b, unsigned int state)
+{
+ HighlightElement *hl = elementRecord;
+ int highlightThickness = 0;
+ XColor *highlightColor = Tk_GetColorFromObj(tkwin, hl->highlightColorObj);
+
+ Tcl_GetIntFromObj(NULL,hl->highlightThicknessObj,&highlightThickness);
+ if (highlightColor && highlightThickness > 0) {
+ GC gc = Tk_GCForColor(highlightColor, d);
+ Tk_DrawFocusHighlight(tkwin, gc, highlightThickness, d);
+ }
+}
+
+static Ttk_ElementSpec HighlightElementSpec =
+{
+ TK_STYLE_VERSION_2,
+ sizeof(HighlightElement),
+ HighlightElementOptions,
+ HighlightElementSize,
+ HighlightElementDraw
+};
+
+/*------------------------------------------------------------------------
+ * +++ Button Border element:
+ *
+ * The Motif-style button border on X11 consists of (from outside-in):
+ *
+ * + focus indicator (controlled by -highlightcolor and -highlightthickness),
+ * + default ring (if -default active; blank if -default normal)
+ * + shaded border (controlled by -background, -borderwidth, and -relief)
+ */
+
+typedef struct {
+ Tcl_Obj *borderObj;
+ Tcl_Obj *borderWidthObj;
+ Tcl_Obj *reliefObj;
+ Tcl_Obj *defaultStateObj;
+} ButtonBorderElement;
+
+static Ttk_ElementOptionSpec ButtonBorderElementOptions[] =
+{
+ { "-background", TK_OPTION_BORDER,
+ Tk_Offset(ButtonBorderElement,borderObj), DEFAULT_BACKGROUND },
+ { "-borderwidth", TK_OPTION_PIXELS,
+ Tk_Offset(ButtonBorderElement,borderWidthObj), DEFAULT_BORDERWIDTH },
+ { "-relief", TK_OPTION_RELIEF,
+ Tk_Offset(ButtonBorderElement,reliefObj), "flat" },
+ { "-default", TK_OPTION_ANY,
+ Tk_Offset(ButtonBorderElement,defaultStateObj), "disabled" },
+ { NULL, 0, 0, NULL }
+};
+
+static void ButtonBorderElementSize(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+{
+ ButtonBorderElement *bd = elementRecord;
+ int defaultState = TTK_BUTTON_DEFAULT_DISABLED;
+ int borderWidth = 0;
+
+ Tcl_GetIntFromObj(NULL, bd->borderWidthObj, &borderWidth);
+ Ttk_GetButtonDefaultStateFromObj(NULL, bd->defaultStateObj, &defaultState);
+
+ if (defaultState != TTK_BUTTON_DEFAULT_DISABLED) {
+ borderWidth += 5;
+ }
+ *paddingPtr = Ttk_UniformPadding((short)borderWidth);
+}
+
+/*
+ * (@@@ Note: ButtonBorderElement still still still buggy:
+ * padding for default ring is drawn in the wrong color
+ * when the button is active.)
+ */
+static void ButtonBorderElementDraw(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ Drawable d, Ttk_Box b, unsigned int state)
+{
+ ButtonBorderElement *bd = elementRecord;
+ Tk_3DBorder border = NULL;
+ int borderWidth = 1, relief = TK_RELIEF_FLAT;
+ int defaultState = TTK_BUTTON_DEFAULT_DISABLED;
+ int inset = 0;
+
+ /*
+ * Get option values.
+ */
+ border = Tk_Get3DBorderFromObj(tkwin, bd->borderObj);
+ Tcl_GetIntFromObj(NULL, bd->borderWidthObj, &borderWidth);
+ Tk_GetReliefFromObj(NULL, bd->reliefObj, &relief);
+ Ttk_GetButtonDefaultStateFromObj(NULL, bd->defaultStateObj, &defaultState);
+
+ /*
+ * Default ring:
+ */
+ switch (defaultState)
+ {
+ case TTK_BUTTON_DEFAULT_DISABLED :
+ break;
+ case TTK_BUTTON_DEFAULT_NORMAL :
+ inset += 5;
+ break;
+ case TTK_BUTTON_DEFAULT_ACTIVE :
+ Tk_Draw3DRectangle(tkwin, d, border,
+ b.x+inset, b.y+inset, b.width - 2*inset, b.height - 2*inset,
+ 2, TK_RELIEF_FLAT);
+ inset += 2;
+ Tk_Draw3DRectangle(tkwin, d, border,
+ b.x+inset, b.y+inset, b.width - 2*inset, b.height - 2*inset,
+ 1, TK_RELIEF_SUNKEN);
+ ++inset;
+ Tk_Draw3DRectangle(tkwin, d, border,
+ b.x+inset, b.y+inset, b.width - 2*inset, b.height - 2*inset,
+ 2, TK_RELIEF_FLAT);
+ inset += 2;
+ break;
+ }
+
+ /*
+ * 3-D border:
+ */
+ if (border && borderWidth > 0) {
+ Tk_Draw3DRectangle(tkwin, d, border,
+ b.x+inset, b.y+inset, b.width - 2*inset, b.height - 2*inset,
+ borderWidth,relief);
+ }
+}
+
+static Ttk_ElementSpec ButtonBorderElementSpec =
+{
+ TK_STYLE_VERSION_2,
+ sizeof(ButtonBorderElement),
+ ButtonBorderElementOptions,
+ ButtonBorderElementSize,
+ ButtonBorderElementDraw
+};
+
+/*----------------------------------------------------------------------
+ * +++ Arrow element(s).
+ *
+ * Draws a 3-D shaded triangle.
+ * clientData is an enum ArrowDirection pointer.
+ */
+
+static int ArrowElements[] = { ARROW_UP, ARROW_DOWN, ARROW_LEFT, ARROW_RIGHT };
+typedef struct
+{
+ Tcl_Obj *sizeObj;
+ Tcl_Obj *borderObj;
+ Tcl_Obj *borderWidthObj;
+ Tcl_Obj *reliefObj;
+} ArrowElement;
+
+static Ttk_ElementOptionSpec ArrowElementOptions[] =
+{
+ { "-arrowsize", TK_OPTION_PIXELS, Tk_Offset(ArrowElement,sizeObj),
+ DEFAULT_ARROW_SIZE },
+ { "-background", TK_OPTION_BORDER, Tk_Offset(ArrowElement,borderObj),
+ DEFAULT_BACKGROUND },
+ { "-borderwidth", TK_OPTION_PIXELS, Tk_Offset(ArrowElement,borderWidthObj),
+ DEFAULT_BORDERWIDTH },
+ { "-relief", TK_OPTION_RELIEF, Tk_Offset(ArrowElement,reliefObj),"raised" },
+ { NULL, 0, 0, NULL }
+};
+
+static void ArrowElementSize(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+{
+ ArrowElement *arrow = elementRecord;
+ int size = 12;
+
+ Tk_GetPixelsFromObj(NULL, tkwin, arrow->sizeObj, &size);
+ *widthPtr = *heightPtr = size;
+}
+
+static void ArrowElementDraw(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ Drawable d, Ttk_Box b, unsigned int state)
+{
+ int direction = *(int *)clientData;
+ ArrowElement *arrow = elementRecord;
+ Tk_3DBorder border = Tk_Get3DBorderFromObj(tkwin, arrow->borderObj);
+ int borderWidth = 2;
+ int relief = TK_RELIEF_RAISED;
+ int size = b.width < b.height ? b.width : b.height;
+ XPoint points[3];
+
+ Tk_GetPixelsFromObj(NULL, tkwin, arrow->borderWidthObj, &borderWidth);
+ Tk_GetReliefFromObj(NULL, arrow->reliefObj, &relief);
+
+
+ /*
+ * @@@ There are off-by-one pixel errors in the way these are drawn;
+ * @@@ need to take a look at Tk_Fill3DPolygon and X11 to find the
+ * @@@ exact rules.
+ */
+ switch (direction)
+ {
+ case ARROW_UP:
+ points[2].x = b.x; points[2].y = b.y + size;
+ points[1].x = b.x + size/2; points[1].y = b.y;
+ points[0].x = b.x + size; points[0].y = b.y + size;
+ break;
+ case ARROW_DOWN:
+ points[0].x = b.x; points[0].y = b.y;
+ points[1].x = b.x + size/2; points[1].y = b.y + size;
+ points[2].x = b.x + size; points[2].y = b.y;
+ break;
+ case ARROW_LEFT:
+ points[0].x = b.x; points[0].y = b.y + size / 2;
+ points[1].x = b.x + size; points[1].y = b.y + size;
+ points[2].x = b.x + size; points[2].y = b.y;
+ break;
+ case ARROW_RIGHT:
+ points[0].x = b.x + size; points[0].y = b.y + size / 2;
+ points[1].x = b.x; points[1].y = b.y;
+ points[2].x = b.x; points[2].y = b.y + size;
+ break;
+ }
+
+ Tk_Fill3DPolygon(tkwin, d, border, points, 3, borderWidth, relief);
+}
+
+static Ttk_ElementSpec ArrowElementSpec =
+{
+ TK_STYLE_VERSION_2,
+ sizeof(ArrowElement),
+ ArrowElementOptions,
+ ArrowElementSize,
+ ArrowElementDraw
+};
+
+
+/*------------------------------------------------------------------------
+ * +++ Sash element (for ttk::panedwindow)
+ *
+ * NOTES:
+ *
+ * panedwindows with -orient horizontal use vertical sashes, and vice versa.
+ *
+ * Interpretation of -sashrelief 'groove' and 'ridge' are
+ * swapped wrt. the core panedwindow, which (I think) has them backwards.
+ *
+ * Default -sashrelief is sunken; the core panedwindow has default
+ * -sashrelief raised, but that looks wrong to me.
+ */
+
+static Ttk_Orient SashClientData[] = {
+ TTK_ORIENT_HORIZONTAL, TTK_ORIENT_VERTICAL
+};
+
+typedef struct {
+ Tcl_Obj *borderObj; /* background color */
+ Tcl_Obj *sashReliefObj; /* sash relief */
+ Tcl_Obj *sashThicknessObj; /* overall thickness of sash */
+ Tcl_Obj *sashPadObj; /* padding on either side of handle */
+ Tcl_Obj *handleSizeObj; /* handle width and height */
+ Tcl_Obj *handlePadObj; /* handle's distance from edge */
+} SashElement;
+
+static Ttk_ElementOptionSpec SashOptions[] = {
+ { "-background", TK_OPTION_BORDER,
+ Tk_Offset(SashElement,borderObj), DEFAULT_BACKGROUND },
+ { "-sashrelief", TK_OPTION_RELIEF,
+ Tk_Offset(SashElement,sashReliefObj), "sunken" },
+ { "-sashthickness", TK_OPTION_PIXELS,
+ Tk_Offset(SashElement,sashThicknessObj), "6" },
+ { "-sashpad", TK_OPTION_PIXELS,
+ Tk_Offset(SashElement,sashPadObj), "2" },
+ { "-handlesize", TK_OPTION_PIXELS,
+ Tk_Offset(SashElement,handleSizeObj), "8" },
+ { "-handlepad", TK_OPTION_PIXELS,
+ Tk_Offset(SashElement,handlePadObj), "8" },
+ { NULL, 0, 0, NULL }
+};
+
+static void SashElementSize(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+{
+ SashElement *sash = elementRecord;
+ int sashPad = 2, sashThickness = 6, handleSize = 8;
+ int horizontal = *((Ttk_Orient*)clientData) == TTK_ORIENT_HORIZONTAL;
+
+ Tk_GetPixelsFromObj(NULL, tkwin, sash->sashThicknessObj, &sashThickness);
+ Tk_GetPixelsFromObj(NULL, tkwin, sash->handleSizeObj, &handleSize);
+ Tk_GetPixelsFromObj(NULL, tkwin, sash->sashPadObj, &sashPad);
+
+ if (sashThickness < handleSize + 2*sashPad)
+ sashThickness = handleSize + 2*sashPad;
+
+ if (horizontal)
+ *heightPtr = sashThickness;
+ else
+ *widthPtr = sashThickness;
+}
+
+static void SashElementDraw(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ Drawable d, Ttk_Box b, Ttk_State state)
+{
+ SashElement *sash = elementRecord;
+ Tk_3DBorder border = Tk_Get3DBorderFromObj(tkwin, sash->borderObj);
+ GC gc1,gc2;
+ int relief = TK_RELIEF_RAISED;
+ int handleSize = 8, handlePad = 8;
+ int horizontal = *((Ttk_Orient*)clientData) == TTK_ORIENT_HORIZONTAL;
+ Ttk_Box hb;
+
+ Tk_GetPixelsFromObj(NULL, tkwin, sash->handleSizeObj, &handleSize);
+ Tk_GetPixelsFromObj(NULL, tkwin, sash->handlePadObj, &handlePad);
+ Tk_GetReliefFromObj(NULL, sash->sashReliefObj, &relief);
+
+ switch (relief) {
+ case TK_RELIEF_RAISED: case TK_RELIEF_RIDGE:
+ gc1 = Tk_3DBorderGC(tkwin, border, TK_3D_LIGHT_GC);
+ gc2 = Tk_3DBorderGC(tkwin, border, TK_3D_DARK_GC);
+ break;
+ case TK_RELIEF_SUNKEN: case TK_RELIEF_GROOVE:
+ gc1 = Tk_3DBorderGC(tkwin, border, TK_3D_DARK_GC);
+ gc2 = Tk_3DBorderGC(tkwin, border, TK_3D_LIGHT_GC);
+ break;
+ case TK_RELIEF_SOLID:
+ gc1 = gc2 = Tk_3DBorderGC(tkwin, border, TK_3D_DARK_GC);
+ break;
+ case TK_RELIEF_FLAT:
+ default:
+ gc1 = gc2 = Tk_3DBorderGC(tkwin, border, TK_3D_FLAT_GC);
+ break;
+ }
+
+ /* Draw sash line:
+ */
+ if (horizontal) {
+ int y = b.y + b.height/2 - 1;
+ XDrawLine(Tk_Display(tkwin), d, gc1, b.x, y, b.x+b.width, y); ++y;
+ XDrawLine(Tk_Display(tkwin), d, gc2, b.x, y, b.x+b.width, y);
+ } else {
+ int x = b.x + b.width/2 - 1;
+ XDrawLine(Tk_Display(tkwin), d, gc1, x, b.y, x, b.y+b.height); ++x;
+ XDrawLine(Tk_Display(tkwin), d, gc2, x, b.y, x, b.y+b.height);
+ }
+
+ /* Draw handle:
+ */
+ if (handleSize >= 0) {
+ if (horizontal) {
+ hb = Ttk_StickBox(b, handleSize, handleSize, TTK_STICK_W);
+ hb.x += handlePad;
+ } else {
+ hb = Ttk_StickBox(b, handleSize, handleSize, TTK_STICK_N);
+ hb.y += handlePad;
+ }
+ Tk_Fill3DRectangle(tkwin, d, border,
+ hb.x, hb.y, hb.width, hb.height, 1, TK_RELIEF_RAISED);
+ }
+}
+
+static Ttk_ElementSpec SashElementSpec = {
+ TK_STYLE_VERSION_2,
+ sizeof(SashElement),
+ SashOptions,
+ SashElementSize,
+ SashElementDraw
+};
+
+/*------------------------------------------------------------------------
+ * +++ Widget layouts.
+ */
+
+TTK_BEGIN_LAYOUT_TABLE(LayoutTable)
+
+TTK_LAYOUT("TButton",
+ TTK_GROUP("Button.highlight", TTK_FILL_BOTH,
+ TTK_GROUP("Button.border", TTK_FILL_BOTH|TTK_BORDER,
+ TTK_GROUP("Button.padding", TTK_FILL_BOTH,
+ TTK_NODE("Button.label", TTK_FILL_BOTH)))))
+
+TTK_LAYOUT("TCheckbutton",
+ TTK_GROUP("Checkbutton.highlight", TTK_FILL_BOTH,
+ TTK_GROUP("Checkbutton.border", TTK_FILL_BOTH,
+ TTK_GROUP("Checkbutton.padding", TTK_FILL_BOTH,
+ TTK_NODE("Checkbutton.indicator", TTK_PACK_LEFT)
+ TTK_NODE("Checkbutton.label", TTK_PACK_LEFT|TTK_FILL_BOTH)))))
+
+TTK_LAYOUT("TRadiobutton",
+ TTK_GROUP("Radiobutton.highlight", TTK_FILL_BOTH,
+ TTK_GROUP("Radiobutton.border", TTK_FILL_BOTH,
+ TTK_GROUP("Radiobutton.padding", TTK_FILL_BOTH,
+ TTK_NODE("Radiobutton.indicator", TTK_PACK_LEFT)
+ TTK_NODE("Radiobutton.label", TTK_PACK_LEFT|TTK_FILL_BOTH)))))
+
+TTK_LAYOUT("TMenubutton",
+ TTK_GROUP("Menubutton.highlight", TTK_FILL_BOTH,
+ TTK_GROUP("Menubutton.border", TTK_FILL_BOTH,
+ TTK_NODE("Menubutton.indicator", TTK_PACK_RIGHT)
+ TTK_GROUP("Menubutton.padding", TTK_PACK_LEFT|TTK_EXPAND|TTK_FILL_X,
+ TTK_NODE("Menubutton.label", 0)))))
+
+/* "classic" entry, includes highlight border */
+TTK_LAYOUT("TEntry",
+ TTK_GROUP("Entry.highlight", TTK_FILL_BOTH,
+ TTK_GROUP("Entry.field", TTK_FILL_BOTH|TTK_BORDER,
+ TTK_GROUP("Entry.padding", TTK_FILL_BOTH,
+ TTK_NODE("Entry.textarea", TTK_FILL_BOTH)))))
+
+/* Notebook tabs -- omit focus ring */
+TTK_LAYOUT("Tab",
+ TTK_GROUP("Notebook.tab", TTK_FILL_BOTH,
+ TTK_GROUP("Notebook.padding", TTK_FILL_BOTH,
+ TTK_NODE("Notebook.label", TTK_FILL_BOTH))))
+
+TTK_END_LAYOUT_TABLE
+
+/* POSSIBLY: include Scale layouts w/focus border
+ */
+
+/*------------------------------------------------------------------------
+ * TtkClassicTheme_Init --
+ * Install classic theme.
+ */
+
+MODULE_SCOPE int TtkClassicTheme_Init(Tcl_Interp *interp)
+{
+ Ttk_Theme theme = Ttk_CreateTheme(interp, "classic", NULL);
+
+ if (!theme) {
+ return TCL_ERROR;
+ }
+
+ /*
+ * Register elements:
+ */
+ Ttk_RegisterElement(interp, theme, "highlight",
+ &HighlightElementSpec, NULL);
+
+ Ttk_RegisterElement(interp, theme, "Button.border",
+ &ButtonBorderElementSpec, NULL);
+
+ Ttk_RegisterElement(interp, theme, "uparrow",
+ &ArrowElementSpec, &ArrowElements[0]);
+ Ttk_RegisterElement(interp, theme, "downarrow",
+ &ArrowElementSpec, &ArrowElements[1]);
+ Ttk_RegisterElement(interp, theme, "leftarrow",
+ &ArrowElementSpec, &ArrowElements[2]);
+ Ttk_RegisterElement(interp, theme, "rightarrow",
+ &ArrowElementSpec, &ArrowElements[3]);
+ Ttk_RegisterElement(interp, theme, "arrow",
+ &ArrowElementSpec, &ArrowElements[0]);
+
+ Ttk_RegisterElement(interp, theme, "hsash",
+ &SashElementSpec, &SashClientData[0]);
+ Ttk_RegisterElement(interp, theme, "vsash",
+ &SashElementSpec, &SashClientData[1]);
+
+ /*
+ * Register layouts:
+ */
+ Ttk_RegisterLayouts(theme, LayoutTable);
+
+ Tcl_PkgProvide(interp, "ttk::theme::classic", TTK_VERSION);
+
+ return TCL_OK;
+}
+
+/*EOF*/
diff --git a/generic/ttk/ttkDecls.h b/generic/ttk/ttkDecls.h
new file mode 100644
index 0000000..ee679b7
--- /dev/null
+++ b/generic/ttk/ttkDecls.h
@@ -0,0 +1,272 @@
+/*
+ * This file is (mostly) automatically generated from ttk.decls.
+ */
+
+#ifndef _TTKDECLS
+#define _TTKDECLS
+
+#if defined(USE_TTK_STUBS)
+
+extern const char *TtkInitializeStubs(
+ Tcl_Interp *, const char *version, int epoch, int revision);
+#define Ttk_InitStubs(interp) TtkInitializeStubs( \
+ interp, TTK_VERSION, TTK_STUBS_EPOCH, TTK_STUBS_REVISION)
+#else
+
+#define Ttk_InitStubs(interp) Tcl_PkgRequire(interp, "Ttk", TTK_VERSION, 0)
+
+#endif
+
+
+/* !BEGIN!: Do not edit below this line. */
+
+#define TTK_STUBS_EPOCH 0
+#define TTK_STUBS_REVISION 31
+
+/*
+ * Exported function declarations:
+ */
+
+/* 0 */
+TTKAPI Ttk_Theme Ttk_GetTheme(Tcl_Interp *interp, CONST char *name);
+/* 1 */
+TTKAPI Ttk_Theme Ttk_GetDefaultTheme(Tcl_Interp *interp);
+/* 2 */
+TTKAPI Ttk_Theme Ttk_GetCurrentTheme(Tcl_Interp *interp);
+/* 3 */
+TTKAPI Ttk_Theme Ttk_CreateTheme(Tcl_Interp *interp, CONST char *name,
+ Ttk_Theme parent);
+/* 4 */
+TTKAPI void Ttk_RegisterCleanup(Tcl_Interp *interp,
+ VOID *deleteData,
+ Ttk_CleanupProc *cleanupProc);
+/* 5 */
+TTKAPI int Ttk_RegisterElementSpec(Ttk_Theme theme,
+ CONST char *elementName,
+ Ttk_ElementSpec *elementSpec,
+ VOID *clientData);
+/* 6 */
+TTKAPI Ttk_ElementClass * Ttk_RegisterElement(Tcl_Interp *interp,
+ Ttk_Theme theme, CONST char *elementName,
+ Ttk_ElementSpec *elementSpec,
+ VOID *clientData);
+/* 7 */
+TTKAPI int Ttk_RegisterElementFactory(Tcl_Interp *interp,
+ CONST char *name,
+ Ttk_ElementFactory factoryProc,
+ VOID *clientData);
+/* 8 */
+TTKAPI void Ttk_RegisterLayout(Ttk_Theme theme,
+ CONST char *className,
+ Ttk_LayoutSpec layoutSpec);
+/* Slot 9 is reserved */
+/* 10 */
+TTKAPI int Ttk_GetStateSpecFromObj(Tcl_Interp *interp,
+ Tcl_Obj *objPtr, Ttk_StateSpec *spec_rtn);
+/* 11 */
+TTKAPI Tcl_Obj * Ttk_NewStateSpecObj(unsigned int onbits,
+ unsigned int offbits);
+/* 12 */
+TTKAPI Ttk_StateMap Ttk_GetStateMapFromObj(Tcl_Interp *interp,
+ Tcl_Obj *objPtr);
+/* 13 */
+TTKAPI Tcl_Obj * Ttk_StateMapLookup(Tcl_Interp *interp,
+ Ttk_StateMap map, Ttk_State state);
+/* 14 */
+TTKAPI int Ttk_StateTableLookup(Ttk_StateTable map[],
+ Ttk_State state);
+/* Slot 15 is reserved */
+/* Slot 16 is reserved */
+/* Slot 17 is reserved */
+/* Slot 18 is reserved */
+/* Slot 19 is reserved */
+/* 20 */
+TTKAPI int Ttk_GetPaddingFromObj(Tcl_Interp *interp,
+ Tk_Window tkwin, Tcl_Obj *objPtr,
+ Ttk_Padding *pad_rtn);
+/* 21 */
+TTKAPI int Ttk_GetBorderFromObj(Tcl_Interp *interp,
+ Tcl_Obj *objPtr, Ttk_Padding *pad_rtn);
+/* 22 */
+TTKAPI int Ttk_GetStickyFromObj(Tcl_Interp *interp,
+ Tcl_Obj *objPtr, Ttk_Sticky *sticky_rtn);
+/* 23 */
+TTKAPI Ttk_Padding Ttk_MakePadding(short l, short t, short r, short b);
+/* 24 */
+TTKAPI Ttk_Padding Ttk_UniformPadding(short borderWidth);
+/* 25 */
+TTKAPI Ttk_Padding Ttk_AddPadding(Ttk_Padding pad1, Ttk_Padding pad2);
+/* 26 */
+TTKAPI Ttk_Padding Ttk_RelievePadding(Ttk_Padding padding, int relief,
+ int n);
+/* 27 */
+TTKAPI Ttk_Box Ttk_MakeBox(int x, int y, int width, int height);
+/* 28 */
+TTKAPI int Ttk_BoxContains(Ttk_Box box, int x, int y);
+/* 29 */
+TTKAPI Ttk_Box Ttk_PackBox(Ttk_Box *cavity, int w, int h,
+ Ttk_Side side);
+/* 30 */
+TTKAPI Ttk_Box Ttk_StickBox(Ttk_Box parcel, int w, int h,
+ Ttk_Sticky sticky);
+/* 31 */
+TTKAPI Ttk_Box Ttk_AnchorBox(Ttk_Box parcel, int w, int h,
+ Tk_Anchor anchor);
+/* 32 */
+TTKAPI Ttk_Box Ttk_PadBox(Ttk_Box b, Ttk_Padding p);
+/* 33 */
+TTKAPI Ttk_Box Ttk_ExpandBox(Ttk_Box b, Ttk_Padding p);
+/* 34 */
+TTKAPI Ttk_Box Ttk_PlaceBox(Ttk_Box *cavity, int w, int h,
+ Ttk_Side side, Ttk_Sticky sticky);
+/* 35 */
+TTKAPI Tcl_Obj * Ttk_NewBoxObj(Ttk_Box box);
+/* Slot 36 is reserved */
+/* Slot 37 is reserved */
+/* Slot 38 is reserved */
+/* Slot 39 is reserved */
+/* 40 */
+TTKAPI int Ttk_GetOrientFromObj(Tcl_Interp *interp,
+ Tcl_Obj *objPtr, int *orient);
+
+typedef struct TtkStubs {
+ int magic;
+ int epoch;
+ int revision;
+ const struct TtkStubHooks *hooks;
+
+ Ttk_Theme (*ttk_GetTheme) (Tcl_Interp *interp, CONST char *name); /* 0 */
+ Ttk_Theme (*ttk_GetDefaultTheme) (Tcl_Interp *interp); /* 1 */
+ Ttk_Theme (*ttk_GetCurrentTheme) (Tcl_Interp *interp); /* 2 */
+ Ttk_Theme (*ttk_CreateTheme) (Tcl_Interp *interp, CONST char *name, Ttk_Theme parent); /* 3 */
+ void (*ttk_RegisterCleanup) (Tcl_Interp *interp, VOID *deleteData, Ttk_CleanupProc *cleanupProc); /* 4 */
+ int (*ttk_RegisterElementSpec) (Ttk_Theme theme, CONST char *elementName, Ttk_ElementSpec *elementSpec, VOID *clientData); /* 5 */
+ Ttk_ElementClass * (*ttk_RegisterElement) (Tcl_Interp *interp, Ttk_Theme theme, CONST char *elementName, Ttk_ElementSpec *elementSpec, VOID *clientData); /* 6 */
+ int (*ttk_RegisterElementFactory) (Tcl_Interp *interp, CONST char *name, Ttk_ElementFactory factoryProc, VOID *clientData); /* 7 */
+ void (*ttk_RegisterLayout) (Ttk_Theme theme, CONST char *className, Ttk_LayoutSpec layoutSpec); /* 8 */
+ void (*reserved9)(void);
+ int (*ttk_GetStateSpecFromObj) (Tcl_Interp *interp, Tcl_Obj *objPtr, Ttk_StateSpec *spec_rtn); /* 10 */
+ Tcl_Obj * (*ttk_NewStateSpecObj) (unsigned int onbits, unsigned int offbits); /* 11 */
+ Ttk_StateMap (*ttk_GetStateMapFromObj) (Tcl_Interp *interp, Tcl_Obj *objPtr); /* 12 */
+ Tcl_Obj * (*ttk_StateMapLookup) (Tcl_Interp *interp, Ttk_StateMap map, Ttk_State state); /* 13 */
+ int (*ttk_StateTableLookup) (Ttk_StateTable map[], Ttk_State state); /* 14 */
+ void (*reserved15)(void);
+ void (*reserved16)(void);
+ void (*reserved17)(void);
+ void (*reserved18)(void);
+ void (*reserved19)(void);
+ int (*ttk_GetPaddingFromObj) (Tcl_Interp *interp, Tk_Window tkwin, Tcl_Obj *objPtr, Ttk_Padding *pad_rtn); /* 20 */
+ int (*ttk_GetBorderFromObj) (Tcl_Interp *interp, Tcl_Obj *objPtr, Ttk_Padding *pad_rtn); /* 21 */
+ int (*ttk_GetStickyFromObj) (Tcl_Interp *interp, Tcl_Obj *objPtr, Ttk_Sticky *sticky_rtn); /* 22 */
+ Ttk_Padding (*ttk_MakePadding) (short l, short t, short r, short b); /* 23 */
+ Ttk_Padding (*ttk_UniformPadding) (short borderWidth); /* 24 */
+ Ttk_Padding (*ttk_AddPadding) (Ttk_Padding pad1, Ttk_Padding pad2); /* 25 */
+ Ttk_Padding (*ttk_RelievePadding) (Ttk_Padding padding, int relief, int n); /* 26 */
+ Ttk_Box (*ttk_MakeBox) (int x, int y, int width, int height); /* 27 */
+ int (*ttk_BoxContains) (Ttk_Box box, int x, int y); /* 28 */
+ Ttk_Box (*ttk_PackBox) (Ttk_Box *cavity, int w, int h, Ttk_Side side); /* 29 */
+ Ttk_Box (*ttk_StickBox) (Ttk_Box parcel, int w, int h, Ttk_Sticky sticky); /* 30 */
+ Ttk_Box (*ttk_AnchorBox) (Ttk_Box parcel, int w, int h, Tk_Anchor anchor); /* 31 */
+ Ttk_Box (*ttk_PadBox) (Ttk_Box b, Ttk_Padding p); /* 32 */
+ Ttk_Box (*ttk_ExpandBox) (Ttk_Box b, Ttk_Padding p); /* 33 */
+ Ttk_Box (*ttk_PlaceBox) (Ttk_Box *cavity, int w, int h, Ttk_Side side, Ttk_Sticky sticky); /* 34 */
+ Tcl_Obj * (*ttk_NewBoxObj) (Ttk_Box box); /* 35 */
+ void (*reserved36)(void);
+ void (*reserved37)(void);
+ void (*reserved38)(void);
+ void (*reserved39)(void);
+ int (*ttk_GetOrientFromObj) (Tcl_Interp *interp, Tcl_Obj *objPtr, int *orient); /* 40 */
+} TtkStubs;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+extern const TtkStubs *ttkStubsPtr;
+#ifdef __cplusplus
+}
+#endif
+
+#if defined(USE_TTK_STUBS)
+
+/*
+ * Inline function declarations:
+ */
+
+#define Ttk_GetTheme \
+ (ttkStubsPtr->ttk_GetTheme) /* 0 */
+#define Ttk_GetDefaultTheme \
+ (ttkStubsPtr->ttk_GetDefaultTheme) /* 1 */
+#define Ttk_GetCurrentTheme \
+ (ttkStubsPtr->ttk_GetCurrentTheme) /* 2 */
+#define Ttk_CreateTheme \
+ (ttkStubsPtr->ttk_CreateTheme) /* 3 */
+#define Ttk_RegisterCleanup \
+ (ttkStubsPtr->ttk_RegisterCleanup) /* 4 */
+#define Ttk_RegisterElementSpec \
+ (ttkStubsPtr->ttk_RegisterElementSpec) /* 5 */
+#define Ttk_RegisterElement \
+ (ttkStubsPtr->ttk_RegisterElement) /* 6 */
+#define Ttk_RegisterElementFactory \
+ (ttkStubsPtr->ttk_RegisterElementFactory) /* 7 */
+#define Ttk_RegisterLayout \
+ (ttkStubsPtr->ttk_RegisterLayout) /* 8 */
+/* Slot 9 is reserved */
+#define Ttk_GetStateSpecFromObj \
+ (ttkStubsPtr->ttk_GetStateSpecFromObj) /* 10 */
+#define Ttk_NewStateSpecObj \
+ (ttkStubsPtr->ttk_NewStateSpecObj) /* 11 */
+#define Ttk_GetStateMapFromObj \
+ (ttkStubsPtr->ttk_GetStateMapFromObj) /* 12 */
+#define Ttk_StateMapLookup \
+ (ttkStubsPtr->ttk_StateMapLookup) /* 13 */
+#define Ttk_StateTableLookup \
+ (ttkStubsPtr->ttk_StateTableLookup) /* 14 */
+/* Slot 15 is reserved */
+/* Slot 16 is reserved */
+/* Slot 17 is reserved */
+/* Slot 18 is reserved */
+/* Slot 19 is reserved */
+#define Ttk_GetPaddingFromObj \
+ (ttkStubsPtr->ttk_GetPaddingFromObj) /* 20 */
+#define Ttk_GetBorderFromObj \
+ (ttkStubsPtr->ttk_GetBorderFromObj) /* 21 */
+#define Ttk_GetStickyFromObj \
+ (ttkStubsPtr->ttk_GetStickyFromObj) /* 22 */
+#define Ttk_MakePadding \
+ (ttkStubsPtr->ttk_MakePadding) /* 23 */
+#define Ttk_UniformPadding \
+ (ttkStubsPtr->ttk_UniformPadding) /* 24 */
+#define Ttk_AddPadding \
+ (ttkStubsPtr->ttk_AddPadding) /* 25 */
+#define Ttk_RelievePadding \
+ (ttkStubsPtr->ttk_RelievePadding) /* 26 */
+#define Ttk_MakeBox \
+ (ttkStubsPtr->ttk_MakeBox) /* 27 */
+#define Ttk_BoxContains \
+ (ttkStubsPtr->ttk_BoxContains) /* 28 */
+#define Ttk_PackBox \
+ (ttkStubsPtr->ttk_PackBox) /* 29 */
+#define Ttk_StickBox \
+ (ttkStubsPtr->ttk_StickBox) /* 30 */
+#define Ttk_AnchorBox \
+ (ttkStubsPtr->ttk_AnchorBox) /* 31 */
+#define Ttk_PadBox \
+ (ttkStubsPtr->ttk_PadBox) /* 32 */
+#define Ttk_ExpandBox \
+ (ttkStubsPtr->ttk_ExpandBox) /* 33 */
+#define Ttk_PlaceBox \
+ (ttkStubsPtr->ttk_PlaceBox) /* 34 */
+#define Ttk_NewBoxObj \
+ (ttkStubsPtr->ttk_NewBoxObj) /* 35 */
+/* Slot 36 is reserved */
+/* Slot 37 is reserved */
+/* Slot 38 is reserved */
+/* Slot 39 is reserved */
+#define Ttk_GetOrientFromObj \
+ (ttkStubsPtr->ttk_GetOrientFromObj) /* 40 */
+
+#endif /* defined(USE_TTK_STUBS) */
+
+/* !END!: Do not edit above this line. */
+
+#endif /* _TTKDECLS */
diff --git a/generic/ttk/ttkDefaultTheme.c b/generic/ttk/ttkDefaultTheme.c
new file mode 100644
index 0000000..d2deee8
--- /dev/null
+++ b/generic/ttk/ttkDefaultTheme.c
@@ -0,0 +1,1130 @@
+/*
+ * Copyright (c) 2003, Joe English
+ *
+ * Tk alternate theme, intended to match the MSUE and Gtk's (old) default theme
+ */
+
+#include <math.h>
+#include <string.h>
+
+#include <tkInt.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include "ttkTheme.h"
+
+#if defined(WIN32)
+static const int WIN32_XDRAWLINE_HACK = 1;
+#else
+static const int WIN32_XDRAWLINE_HACK = 0;
+#endif
+
+#define BORDERWIDTH 2
+#define SCROLLBAR_WIDTH 14
+#define MIN_THUMB_SIZE 8
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Helper routines for border drawing:
+ *
+ * NOTE: MSUE specifies a slightly different arrangement
+ * for button borders than for other elements; "shadowColors"
+ * is for button borders.
+ *
+ * Please excuse the gross misspelling "LITE" for "LIGHT",
+ * but it makes things line up nicer.
+ */
+
+enum BorderColor { FLAT = 1, LITE = 2, DARK = 3, BRDR = 4 };
+
+/* top-left outer, top-left inner, bottom-right inner, bottom-right outer */
+static int const shadowColors[6][4] = {
+ { FLAT, FLAT, FLAT, FLAT }, /* TK_RELIEF_FLAT = 0*/
+ { DARK, LITE, DARK, LITE }, /* TK_RELIEF_GROOVE = 1*/
+ { LITE, FLAT, DARK, BRDR }, /* TK_RELIEF_RAISED = 2*/
+ { LITE, DARK, LITE, DARK }, /* TK_RELIEF_RIDGE = 3*/
+ { BRDR, BRDR, BRDR, BRDR }, /* TK_RELIEF_SOLID = 4*/
+ { BRDR, DARK, FLAT, LITE } /* TK_RELIEF_SUNKEN = 5*/
+};
+
+/* top-left, bottom-right */
+static int const thinShadowColors[6][4] = {
+ { FLAT, FLAT }, /* TK_RELIEF_FLAT = 0*/
+ { DARK, LITE }, /* TK_RELIEF_GROOVE = 1*/
+ { LITE, DARK }, /* TK_RELIEF_RAISED = 2*/
+ { LITE, DARK }, /* TK_RELIEF_RIDGE = 3*/
+ { BRDR, BRDR }, /* TK_RELIEF_SOLID = 4*/
+ { DARK, LITE } /* TK_RELIEF_SUNKEN = 5*/
+};
+
+static void DrawCorner(
+ Tk_Window tkwin,
+ Drawable d,
+ Tk_3DBorder border, /* get most GCs from here... */
+ GC borderGC, /* "window border" color GC */
+ int x,int y, int width,int height, /* where to draw */
+ int corner, /* 0 => top left; 1 => bottom right */
+ enum BorderColor color)
+{
+ XPoint points[3];
+ GC gc;
+
+ --width; --height;
+ points[0].x = x; points[0].y = y+height;
+ points[1].x = x+width*corner; points[1].y = y+height*corner;
+ points[2].x = x+width; points[2].y = y;
+
+ if (color == BRDR)
+ gc = borderGC;
+ else
+ gc = Tk_3DBorderGC(tkwin, border, (int)color);
+
+ XDrawLines(Tk_Display(tkwin), d, gc, points, 3, CoordModeOrigin);
+}
+
+static void DrawBorder(
+ Tk_Window tkwin, Drawable d, Tk_3DBorder border, XColor *borderColor,
+ Ttk_Box b, int borderWidth, int relief)
+{
+ GC borderGC = Tk_GCForColor(borderColor, d);
+
+ switch (borderWidth) {
+ case 2: /* "thick" border */
+ DrawCorner(tkwin, d, border, borderGC,
+ b.x, b.y, b.width, b.height, 0,shadowColors[relief][0]);
+ DrawCorner(tkwin, d, border, borderGC,
+ b.x+1, b.y+1, b.width-2, b.height-2, 0,shadowColors[relief][1]);
+ DrawCorner(tkwin, d, border, borderGC,
+ b.x+1, b.y+1, b.width-2, b.height-2, 1,shadowColors[relief][2]);
+ DrawCorner(tkwin, d, border, borderGC,
+ b.x, b.y, b.width, b.height, 1,shadowColors[relief][3]);
+ break;
+ case 1: /* "thin" border */
+ DrawCorner(tkwin, d, border, borderGC,
+ b.x, b.y, b.width, b.height, 0, thinShadowColors[relief][0]);
+ DrawCorner(tkwin, d, border, borderGC,
+ b.x, b.y, b.width, b.height, 1, thinShadowColors[relief][1]);
+ break;
+ case 0: /* no border -- do nothing */
+ break;
+ default: /* Fall back to Motif-style borders: */
+ Tk_Draw3DRectangle(tkwin, d, border,
+ b.x, b.y, b.width, b.height, borderWidth,relief);
+ break;
+ }
+}
+
+/* Alternate shadow colors for entry fields:
+ * NOTE: FLAT color is normally white, and the LITE color is a darker shade.
+ */
+static int fieldShadowColors[4] = { DARK, BRDR, LITE, FLAT };
+
+static void DrawFieldBorder(
+ Tk_Window tkwin, Drawable d, Tk_3DBorder border, XColor *borderColor,
+ Ttk_Box b)
+{
+ GC borderGC = Tk_GCForColor(borderColor, d);
+ DrawCorner(tkwin, d, border, borderGC,
+ b.x, b.y, b.width, b.height, 0,fieldShadowColors[0]);
+ DrawCorner(tkwin, d, border, borderGC,
+ b.x+1, b.y+1, b.width-2, b.height-2, 0,fieldShadowColors[1]);
+ DrawCorner(tkwin, d, border, borderGC,
+ b.x+1, b.y+1, b.width-2, b.height-2, 1,fieldShadowColors[2]);
+ DrawCorner(tkwin, d, border, borderGC,
+ b.x, b.y, b.width, b.height, 1,fieldShadowColors[3]);
+ return;
+}
+
+/*
+ * ArrowPoints --
+ * Compute points of arrow polygon.
+ */
+static void ArrowPoints(Ttk_Box b, ArrowDirection dir, XPoint points[4])
+{
+ int cx, cy, h;
+
+ switch (dir) {
+ case ARROW_UP:
+ h = (b.width - 1)/2;
+ cx = b.x + h;
+ cy = b.y;
+ if (b.height <= h) h = b.height - 1;
+ points[0].x = cx; points[0].y = cy;
+ points[1].x = cx - h; points[1].y = cy + h;
+ points[2].x = cx + h; points[2].y = cy + h;
+ break;
+ case ARROW_DOWN:
+ h = (b.width - 1)/2;
+ cx = b.x + h;
+ cy = b.y + b.height - 1;
+ if (b.height <= h) h = b.height - 1;
+ points[0].x = cx; points[0].y = cy;
+ points[1].x = cx - h; points[1].y = cy - h;
+ points[2].x = cx + h; points[2].y = cy - h;
+ break;
+ case ARROW_LEFT:
+ h = (b.height - 1)/2;
+ cx = b.x;
+ cy = b.y + h;
+ if (b.width <= h) h = b.width - 1;
+ points[0].x = cx; points[0].y = cy;
+ points[1].x = cx + h; points[1].y = cy - h;
+ points[2].x = cx + h; points[2].y = cy + h;
+ break;
+ case ARROW_RIGHT:
+ h = (b.height - 1)/2;
+ cx = b.x + b.width - 1;
+ cy = b.y + h;
+ if (b.width <= h) h = b.width - 1;
+ points[0].x = cx; points[0].y = cy;
+ points[1].x = cx - h; points[1].y = cy - h;
+ points[2].x = cx - h; points[2].y = cy + h;
+ break;
+ }
+
+ points[3].x = points[0].x;
+ points[3].y = points[0].y;
+}
+
+/*public*/
+void TtkArrowSize(int h, ArrowDirection dir, int *widthPtr, int *heightPtr)
+{
+ switch (dir) {
+ case ARROW_UP:
+ case ARROW_DOWN: *widthPtr = 2*h+1; *heightPtr = h+1; break;
+ case ARROW_LEFT:
+ case ARROW_RIGHT: *widthPtr = h+1; *heightPtr = 2*h+1;
+ }
+}
+
+/*
+ * TtkDrawArrow, TtkFillArrow --
+ * Draw an arrow in the indicated direction inside the specified box.
+ */
+/*public*/
+void TtkFillArrow(
+ Display *display, Drawable d, GC gc, Ttk_Box b, ArrowDirection dir)
+{
+ XPoint points[4];
+ ArrowPoints(b, dir, points);
+ XFillPolygon(display, d, gc, points, 3, Convex, CoordModeOrigin);
+ XDrawLines(display, d, gc, points, 4, CoordModeOrigin);
+}
+
+/*public*/
+void TtkDrawArrow(
+ Display *display, Drawable d, GC gc, Ttk_Box b, ArrowDirection dir)
+{
+ XPoint points[4];
+ ArrowPoints(b, dir, points);
+ XDrawLines(display, d, gc, points, 4, CoordModeOrigin);
+}
+
+/*
+ *----------------------------------------------------------------------
+ * +++ Border element implementation.
+ *
+ * This border consists of (from outside-in):
+ *
+ * + a 1-pixel thick default indicator (defaultable widgets only)
+ * + 1- or 2- pixel shaded border (controlled by -background and -relief)
+ * + 1 pixel padding (???)
+ */
+
+typedef struct {
+ Tcl_Obj *borderObj;
+ Tcl_Obj *borderColorObj; /* Extra border color */
+ Tcl_Obj *borderWidthObj;
+ Tcl_Obj *reliefObj;
+ Tcl_Obj *defaultStateObj; /* for buttons */
+} BorderElement;
+
+static Ttk_ElementOptionSpec BorderElementOptions[] = {
+ { "-background", TK_OPTION_BORDER, Tk_Offset(BorderElement,borderObj),
+ DEFAULT_BACKGROUND },
+ { "-bordercolor",TK_OPTION_COLOR,
+ Tk_Offset(BorderElement,borderColorObj), "black" },
+ { "-default", TK_OPTION_ANY, Tk_Offset(BorderElement,defaultStateObj),
+ "disabled" },
+ { "-borderwidth",TK_OPTION_PIXELS,Tk_Offset(BorderElement,borderWidthObj),
+ STRINGIFY(BORDERWIDTH) },
+ { "-relief", TK_OPTION_RELIEF, Tk_Offset(BorderElement,reliefObj),
+ "flat" },
+ { NULL, 0, 0, NULL }
+};
+
+static void BorderElementSize(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+{
+ BorderElement *bd = elementRecord;
+ int borderWidth = 0;
+ int defaultState = TTK_BUTTON_DEFAULT_DISABLED;
+
+ Tcl_GetIntFromObj(NULL, bd->borderWidthObj, &borderWidth);
+ Ttk_GetButtonDefaultStateFromObj(NULL, bd->defaultStateObj, &defaultState);
+
+ if (defaultState != TTK_BUTTON_DEFAULT_DISABLED) {
+ ++borderWidth;
+ }
+
+ *paddingPtr = Ttk_UniformPadding((short)borderWidth);
+}
+
+static void BorderElementDraw(
+ void *clientData, void *elementRecord,
+ Tk_Window tkwin, Drawable d, Ttk_Box b, unsigned int state)
+{
+ BorderElement *bd = elementRecord;
+ Tk_3DBorder border = Tk_Get3DBorderFromObj(tkwin, bd->borderObj);
+ XColor *borderColor = Tk_GetColorFromObj(tkwin, bd->borderColorObj);
+ int borderWidth = 2;
+ int relief = TK_RELIEF_FLAT;
+ int defaultState = TTK_BUTTON_DEFAULT_DISABLED;
+
+ /*
+ * Get option values.
+ */
+ Tcl_GetIntFromObj(NULL, bd->borderWidthObj, &borderWidth);
+ Tk_GetReliefFromObj(NULL, bd->reliefObj, &relief);
+ Ttk_GetButtonDefaultStateFromObj(NULL, bd->defaultStateObj, &defaultState);
+
+ if (defaultState == TTK_BUTTON_DEFAULT_ACTIVE) {
+ GC gc = Tk_GCForColor(borderColor, d);
+ XDrawRectangle(Tk_Display(tkwin), d, gc,
+ b.x, b.y, b.width-1, b.height-1);
+ }
+ if (defaultState != TTK_BUTTON_DEFAULT_DISABLED) {
+ /* Space for default ring: */
+ b = Ttk_PadBox(b, Ttk_UniformPadding(1));
+ }
+
+ DrawBorder(tkwin, d, border, borderColor, b, borderWidth, relief);
+}
+
+static Ttk_ElementSpec BorderElementSpec = {
+ TK_STYLE_VERSION_2,
+ sizeof(BorderElement),
+ BorderElementOptions,
+ BorderElementSize,
+ BorderElementDraw
+};
+
+/*----------------------------------------------------------------------
+ * +++ Field element:
+ * Used for editable fields.
+ */
+typedef struct {
+ Tcl_Obj *borderObj;
+ Tcl_Obj *borderColorObj; /* Extra border color */
+} FieldElement;
+
+static Ttk_ElementOptionSpec FieldElementOptions[] = {
+ { "-fieldbackground", TK_OPTION_BORDER, Tk_Offset(FieldElement,borderObj),
+ "white" },
+ { "-bordercolor",TK_OPTION_COLOR, Tk_Offset(FieldElement,borderColorObj),
+ "black" },
+ { NULL, 0, 0, NULL }
+};
+
+static void FieldElementSize(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+{
+ *paddingPtr = Ttk_UniformPadding(2);
+}
+
+static void FieldElementDraw(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ Drawable d, Ttk_Box b, unsigned int state)
+{
+ FieldElement *field = elementRecord;
+ Tk_3DBorder border = Tk_Get3DBorderFromObj(tkwin, field->borderObj);
+ XColor *borderColor = Tk_GetColorFromObj(tkwin, field->borderColorObj);
+
+ Tk_Fill3DRectangle(
+ tkwin, d, border, b.x, b.y, b.width, b.height, 0, TK_RELIEF_SUNKEN);
+ DrawFieldBorder(tkwin, d, border, borderColor, b);
+}
+
+static Ttk_ElementSpec FieldElementSpec = {
+ TK_STYLE_VERSION_2,
+ sizeof(FieldElement),
+ FieldElementOptions,
+ FieldElementSize,
+ FieldElementDraw
+};
+
+/*------------------------------------------------------------------------
+ * Indicators --
+ *
+ * Code derived (probably incorrectly) from TIP 109 implementation,
+ * unix/tkUnixButton.c r 1.15.
+ */
+
+/*
+ * Indicator bitmap descriptor:
+ */
+typedef struct {
+ int width; /* Width of each image */
+ int height; /* Height of each image */
+ int nimages; /* #images / row */
+ const char *const *pixels; /* array[height] of char[width*nimage] */
+ Ttk_StateTable *map;/* used to look up image index by state */
+} IndicatorSpec;
+
+#if 0
+/*XPM*/
+static const char *const button_images[] = {
+ /* width height ncolors chars_per_pixel */
+ "52 13 8 1",
+ /* colors */
+ "A c #808000000000 s shadow",
+ "B c #000080800000 s highlight",
+ "C c #808080800000 s 3dlight",
+ "D c #000000008080 s window",
+ "E c #808000008080 s 3ddark",
+ "F c #000080808080 s frame",
+ "G c #000000000000 s foreground",
+ "H c #000080800000 s disabledfg",
+};
+#endif
+
+static Ttk_StateTable checkbutton_states[] = {
+ { 0, 0, TTK_STATE_SELECTED|TTK_STATE_DISABLED },
+ { 1, TTK_STATE_SELECTED, TTK_STATE_DISABLED },
+ { 2, TTK_STATE_DISABLED, TTK_STATE_SELECTED },
+ { 3, TTK_STATE_SELECTED|TTK_STATE_DISABLED, 0 },
+ { 0, 0, 0 }
+};
+
+static const char *const checkbutton_pixels[] = {
+ "AAAAAAAAAAAABAAAAAAAAAAAABAAAAAAAAAAAABAAAAAAAAAAAAB",
+ "AEEEEEEEEEECBAEEEEEEEEEECBAEEEEEEEEEECBAEEEEEEEEEECB",
+ "AEDDDDDDDDDCBAEDDDDDDDDDCBAEFFFFFFFFFCBAEFFFFFFFFFCB",
+ "AEDDDDDDDDDCBAEDDDDDDDGDCBAEFFFFFFFFFCBAEFFFFFFFHFCB",
+ "AEDDDDDDDDDCBAEDDDDDDGGDCBAEFFFFFFFFFCBAEFFFFFFHHFCB",
+ "AEDDDDDDDDDCBAEDGDDDGGGDCBAEFFFFFFFFFCBAEFHFFFHHHFCB",
+ "AEDDDDDDDDDCBAEDGGDGGGDDCBAEFFFFFFFFFCBAEFHHFHHHFFCB",
+ "AEDDDDDDDDDCBAEDGGGGGDDDCBAEFFFFFFFFFCBAEFHHHHHFFFCB",
+ "AEDDDDDDDDDCBAEDDGGGDDDDCBAEFFFFFFFFFCBAEFFHHHFFFFCB",
+ "AEDDDDDDDDDCBAEDDDGDDDDDCBAEFFFFFFFFFCBAEFFFHFFFFFCB",
+ "AEDDDDDDDDDCBAEDDDDDDDDDCBAEFFFFFFFFFCBAEFFFFFFFFFCB",
+ "ACCCCCCCCCCCBACCCCCCCCCCCBACCCCCCCCCCCBACCCCCCCCCCCB",
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
+};
+
+static IndicatorSpec checkbutton_spec = {
+ 13, 13, 4, /* width, height, nimages */
+ checkbutton_pixels,
+ checkbutton_states
+};
+
+static Ttk_StateTable radiobutton_states[] = {
+ { 0, 0, TTK_STATE_SELECTED|TTK_STATE_DISABLED },
+ { 1, TTK_STATE_SELECTED, TTK_STATE_DISABLED },
+ { 2, TTK_STATE_DISABLED, TTK_STATE_SELECTED },
+ { 3, TTK_STATE_SELECTED|TTK_STATE_DISABLED, 0 },
+ { 0, 0, 0 }
+};
+
+static const char *const radiobutton_pixels[] = {
+ "FFFFAAAAFFFFFFFFFAAAAFFFFFFFFFAAAAFFFFFFFFFAAAAFFFFF",
+ "FFAAEEEEAAFFFFFAAEEEEAAFFFFFAAEEEEAAFFFFFAAEEEEAAFFF",
+ "FAEEDDDDEEBFFFAEEDDDDEEBFFFAEEFFFFEEBFFFAEEFFFFEEBFF",
+ "FAEDDDDDDCBFFFAEDDDDDDCBFFFAEFFFFFFCBFFFAEFFFFFFCBFF",
+ "AEDDDDDDDDCBFAEDDDGGDDDCBFAEFFFFFFFFCBFAEFFFHHFFFCBF",
+ "AEDDDDDDDDCBFAEDDGGGGDDCBFAEFFFFFFFFCBFAEFFHHHHFFCBF",
+ "AEDDDDDDDDCBFAEDDGGGGDDCBFAEFFFFFFFFCBFAEFFHHHHFFCBF",
+ "AEDDDDDDDDCBFAEDDDGGDDDCBFAEFFFFFFFFCBFAEFFFHHFFFCBF",
+ "FAEDDDDDDCBFFFAEDDDDDDCBFFFAEFFFFFFCBFFFAEFFFFFFCBFF",
+ "FACCDDDDCCBFFFACCDDDDCCBFFFACCFFFFCCBFFFACCFFFFCCBFF",
+ "FFBBCCCCBBFFFFFBBCCCCBBFFFFFBBCCCCBBFFFFFBBCCCCBBFFF",
+ "FFFFBBBBFFFFFFFFFBBBBFFFFFFFFFBBBBFFFFFFFFFBBBBFFFFF",
+ "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
+};
+
+static IndicatorSpec radiobutton_spec = {
+ 13, 13, 4, /* width, height, nimages */
+ radiobutton_pixels,
+ radiobutton_states
+};
+
+typedef struct {
+ Tcl_Obj *backgroundObj;
+ Tcl_Obj *foregroundObj;
+ Tcl_Obj *colorObj;
+ Tcl_Obj *lightColorObj;
+ Tcl_Obj *shadeColorObj;
+ Tcl_Obj *borderColorObj;
+ Tcl_Obj *marginObj;
+} IndicatorElement;
+
+static Ttk_ElementOptionSpec IndicatorElementOptions[] = {
+ { "-background", TK_OPTION_COLOR,
+ Tk_Offset(IndicatorElement,backgroundObj), DEFAULT_BACKGROUND },
+ { "-foreground", TK_OPTION_COLOR,
+ Tk_Offset(IndicatorElement,foregroundObj), DEFAULT_FOREGROUND },
+ { "-indicatorcolor", TK_OPTION_COLOR,
+ Tk_Offset(IndicatorElement,colorObj), "#FFFFFF" },
+ { "-lightcolor", TK_OPTION_COLOR,
+ Tk_Offset(IndicatorElement,lightColorObj), "#DDDDDD" },
+ { "-shadecolor", TK_OPTION_COLOR,
+ Tk_Offset(IndicatorElement,shadeColorObj), "#888888" },
+ { "-bordercolor", TK_OPTION_COLOR,
+ Tk_Offset(IndicatorElement,borderColorObj), "black" },
+ { "-indicatormargin", TK_OPTION_STRING,
+ Tk_Offset(IndicatorElement,marginObj), "0 2 4 2" },
+ { NULL, 0, 0, NULL }
+};
+
+static void IndicatorElementSize(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+{
+ IndicatorSpec *spec = clientData;
+ IndicatorElement *indicator = elementRecord;
+ Ttk_Padding margins;
+ Ttk_GetPaddingFromObj(NULL, tkwin, indicator->marginObj, &margins);
+ *widthPtr = spec->width + Ttk_PaddingWidth(margins);
+ *heightPtr = spec->height + Ttk_PaddingHeight(margins);
+}
+
+static void IndicatorElementDraw(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ Drawable d, Ttk_Box b, unsigned int state)
+{
+ IndicatorSpec *spec = clientData;
+ IndicatorElement *indicator = elementRecord;
+ Display *display = Tk_Display(tkwin);
+ Ttk_Padding padding;
+ XColor *fgColor, *frameColor, *shadeColor, *indicatorColor, *borderColor;
+
+ int index, ix, iy;
+ XGCValues gcValues;
+ GC copyGC;
+ unsigned long imgColors[8];
+ XImage *img;
+
+ Ttk_GetPaddingFromObj(NULL, tkwin, indicator->marginObj, &padding);
+ b = Ttk_PadBox(b, padding);
+
+ if ( b.x < 0
+ || b.y < 0
+ || Tk_Width(tkwin) < b.x + spec->width
+ || Tk_Height(tkwin) < b.y + spec->height)
+ {
+ /* Oops! not enough room to display the image.
+ * Don't draw anything.
+ */
+ return;
+ }
+
+ /*
+ * Fill in imgColors palette:
+ *
+ * (SHOULD: take light and shade colors from the border object,
+ * but Tk doesn't provide easy access to these in the public API.)
+ */
+ fgColor = Tk_GetColorFromObj(tkwin, indicator->foregroundObj);
+ frameColor = Tk_GetColorFromObj(tkwin, indicator->backgroundObj);
+ shadeColor = Tk_GetColorFromObj(tkwin, indicator->shadeColorObj);
+ indicatorColor = Tk_GetColorFromObj(tkwin, indicator->colorObj);
+ borderColor = Tk_GetColorFromObj(tkwin, indicator->borderColorObj);
+
+ imgColors[0 /*A*/] = shadeColor->pixel;
+ imgColors[1 /*B*/] = indicatorColor->pixel;
+ imgColors[2 /*C*/] = frameColor->pixel;
+ imgColors[3 /*D*/] = indicatorColor->pixel;
+ imgColors[4 /*E*/] = borderColor->pixel;
+ imgColors[5 /*F*/] = frameColor->pixel;
+ imgColors[6 /*G*/] = fgColor->pixel;
+ imgColors[7 /*H*/] = fgColor->pixel;
+
+ /*
+ * Create a scratch buffer to store the image:
+ */
+ img = XGetImage(display,d, 0, 0,
+ (unsigned int)spec->width, (unsigned int)spec->height,
+ AllPlanes, ZPixmap);
+ if (img == NULL)
+ return;
+
+ /*
+ * Create the image, painting it into an XImage one pixel at a time.
+ */
+ index = Ttk_StateTableLookup(spec->map, state);
+ for (iy=0 ; iy<spec->height ; iy++) {
+ for (ix=0 ; ix<spec->width ; ix++) {
+ XPutPixel(img, ix, iy,
+ imgColors[spec->pixels[iy][index*spec->width+ix] - 'A'] );
+ }
+ }
+
+ /*
+ * Copy onto our target drawable surface.
+ */
+ memset(&gcValues, 0, sizeof(gcValues));
+ copyGC = Tk_GetGC(tkwin, 0, &gcValues);
+
+ TkPutImage(NULL, 0, display, d, copyGC, img, 0, 0, b.x, b.y,
+ spec->width, spec->height);
+
+ /*
+ * Tidy up.
+ */
+ Tk_FreeGC(display, copyGC);
+ XDestroyImage(img);
+}
+
+static Ttk_ElementSpec IndicatorElementSpec = {
+ TK_STYLE_VERSION_2,
+ sizeof(IndicatorElement),
+ IndicatorElementOptions,
+ IndicatorElementSize,
+ IndicatorElementDraw
+};
+
+/*----------------------------------------------------------------------
+ * +++ Arrow element(s).
+ *
+ * Draws a solid triangle, inside a box.
+ * clientData is an enum ArrowDirection pointer.
+ */
+
+static int ArrowElements[] = { ARROW_UP, ARROW_DOWN, ARROW_LEFT, ARROW_RIGHT };
+typedef struct {
+ Tcl_Obj *sizeObj;
+ Tcl_Obj *borderObj;
+ Tcl_Obj *borderColorObj; /* Extra color for borders */
+ Tcl_Obj *reliefObj;
+ Tcl_Obj *colorObj; /* Arrow color */
+} ArrowElement;
+
+static Ttk_ElementOptionSpec ArrowElementOptions[] = {
+ { "-arrowsize", TK_OPTION_PIXELS,
+ Tk_Offset(ArrowElement,sizeObj), STRINGIFY(SCROLLBAR_WIDTH) },
+ { "-background", TK_OPTION_BORDER,
+ Tk_Offset(ArrowElement,borderObj), DEFAULT_BACKGROUND },
+ { "-bordercolor", TK_OPTION_COLOR,
+ Tk_Offset(ArrowElement,borderColorObj), "black" },
+ { "-relief", TK_OPTION_RELIEF,
+ Tk_Offset(ArrowElement,reliefObj),"raised"},
+ { "-arrowcolor", TK_OPTION_COLOR,
+ Tk_Offset(ArrowElement,colorObj),"black"},
+ { NULL, 0, 0, NULL }
+};
+
+/*
+ * Note asymmetric padding:
+ * top/left padding is 1 less than bottom/right,
+ * since in this theme 2-pixel borders are asymmetric.
+ */
+static Ttk_Padding ArrowPadding = { 3,3,4,4 };
+
+static void ArrowElementSize(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+{
+ ArrowElement *arrow = elementRecord;
+ int direction = *(int *)clientData;
+ int width = SCROLLBAR_WIDTH;
+
+ Tk_GetPixelsFromObj(NULL, tkwin, arrow->sizeObj, &width);
+ width -= Ttk_PaddingWidth(ArrowPadding);
+ TtkArrowSize(width/2, direction, widthPtr, heightPtr);
+ *widthPtr += Ttk_PaddingWidth(ArrowPadding);
+ *heightPtr += Ttk_PaddingHeight(ArrowPadding);
+}
+
+static void ArrowElementDraw(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ Drawable d, Ttk_Box b, unsigned int state)
+{
+ int direction = *(int *)clientData;
+ ArrowElement *arrow = elementRecord;
+ Tk_3DBorder border = Tk_Get3DBorderFromObj(tkwin, arrow->borderObj);
+ XColor *borderColor = Tk_GetColorFromObj(tkwin, arrow->borderColorObj);
+ XColor *arrowColor = Tk_GetColorFromObj(tkwin, arrow->colorObj);
+ int relief = TK_RELIEF_RAISED;
+ int borderWidth = 2;
+
+ Tk_GetReliefFromObj(NULL, arrow->reliefObj, &relief);
+
+ Tk_Fill3DRectangle(
+ tkwin, d, border, b.x, b.y, b.width, b.height, 0, TK_RELIEF_FLAT);
+ DrawBorder(tkwin,d,border,borderColor,b,borderWidth,relief);
+
+ TtkFillArrow(Tk_Display(tkwin), d, Tk_GCForColor(arrowColor, d),
+ Ttk_PadBox(b, ArrowPadding), direction);
+}
+
+static Ttk_ElementSpec ArrowElementSpec = {
+ TK_STYLE_VERSION_2,
+ sizeof(ArrowElement),
+ ArrowElementOptions,
+ ArrowElementSize,
+ ArrowElementDraw
+};
+
+/*----------------------------------------------------------------------
+ * +++ Menubutton indicator:
+ * Draw an arrow in the direction where the menu will be posted.
+ */
+
+#define MENUBUTTON_ARROW_SIZE 5
+
+typedef struct {
+ Tcl_Obj *directionObj;
+ Tcl_Obj *sizeObj;
+ Tcl_Obj *colorObj;
+} MenubuttonArrowElement;
+
+static const char *directionStrings[] = { /* See also: button.c */
+ "above", "below", "left", "right", "flush", NULL
+};
+enum { POST_ABOVE, POST_BELOW, POST_LEFT, POST_RIGHT, POST_FLUSH };
+
+static Ttk_ElementOptionSpec MenubuttonArrowElementOptions[] = {
+ { "-direction", TK_OPTION_STRING,
+ Tk_Offset(MenubuttonArrowElement,directionObj), "below" },
+ { "-arrowsize", TK_OPTION_PIXELS,
+ Tk_Offset(MenubuttonArrowElement,sizeObj), STRINGIFY(MENUBUTTON_ARROW_SIZE)},
+ { "-arrowcolor",TK_OPTION_COLOR,
+ Tk_Offset(MenubuttonArrowElement,colorObj), "black"},
+ { NULL, 0, 0, NULL }
+};
+
+static Ttk_Padding MenubuttonArrowPadding = { 3, 0, 3, 0 };
+
+static void MenubuttonArrowElementSize(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+{
+ MenubuttonArrowElement *arrow = elementRecord;
+ int size = MENUBUTTON_ARROW_SIZE;
+ Tk_GetPixelsFromObj(NULL, tkwin, arrow->sizeObj, &size);
+ *widthPtr = *heightPtr = 2 * size + 1;
+ *widthPtr += Ttk_PaddingWidth(MenubuttonArrowPadding);
+ *heightPtr += Ttk_PaddingHeight(MenubuttonArrowPadding);
+}
+
+static void MenubuttonArrowElementDraw(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ Drawable d, Ttk_Box b, unsigned int state)
+{
+ MenubuttonArrowElement *arrow = elementRecord;
+ XColor *arrowColor = Tk_GetColorFromObj(tkwin, arrow->colorObj);
+ GC gc = Tk_GCForColor(arrowColor, d);
+ int size = MENUBUTTON_ARROW_SIZE;
+ int postDirection = POST_BELOW;
+ ArrowDirection arrowDirection = ARROW_DOWN;
+ int width = 0, height = 0;
+
+ Tk_GetPixelsFromObj(NULL, tkwin, arrow->sizeObj, &size);
+ Tcl_GetIndexFromObj(NULL, arrow->directionObj, directionStrings,
+ ""/*message*/, 0/*flags*/, &postDirection);
+
+ /* ... this might not be such a great idea ... */
+ switch (postDirection) {
+ case POST_ABOVE: arrowDirection = ARROW_UP; break;
+ case POST_BELOW: arrowDirection = ARROW_DOWN; break;
+ case POST_LEFT: arrowDirection = ARROW_LEFT; break;
+ case POST_RIGHT: arrowDirection = ARROW_RIGHT; break;
+ case POST_FLUSH: arrowDirection = ARROW_DOWN; break;
+ }
+
+ TtkArrowSize(size, arrowDirection, &width, &height);
+ b = Ttk_PadBox(b, MenubuttonArrowPadding);
+ b = Ttk_AnchorBox(b, width, height, TK_ANCHOR_CENTER);
+ TtkFillArrow(Tk_Display(tkwin), d, gc, b, arrowDirection);
+}
+
+static Ttk_ElementSpec MenubuttonArrowElementSpec = {
+ TK_STYLE_VERSION_2,
+ sizeof(MenubuttonArrowElement),
+ MenubuttonArrowElementOptions,
+ MenubuttonArrowElementSize,
+ MenubuttonArrowElementDraw
+};
+
+/*----------------------------------------------------------------------
+ * +++ Trough element
+ *
+ * Used in scrollbars and the scale.
+ *
+ * The -groovewidth option can be used to set the size of the short axis
+ * for the drawn area. This will not affect the geometry, but can be used
+ * to draw a thin centered trough inside the packet alloted. This is used
+ * to show a win32-style scale widget. Use -1 or a large number to use the
+ * full area (default).
+ *
+ */
+
+typedef struct {
+ Tcl_Obj *colorObj;
+ Tcl_Obj *borderWidthObj;
+ Tcl_Obj *reliefObj;
+ Tcl_Obj *grooveWidthObj;
+ Tcl_Obj *orientObj;
+} TroughElement;
+
+static Ttk_ElementOptionSpec TroughElementOptions[] = {
+ { "-orient", TK_OPTION_ANY,
+ Tk_Offset(TroughElement, orientObj), "horizontal" },
+ { "-troughborderwidth", TK_OPTION_PIXELS,
+ Tk_Offset(TroughElement,borderWidthObj), "1" },
+ { "-troughcolor", TK_OPTION_BORDER,
+ Tk_Offset(TroughElement,colorObj), DEFAULT_BACKGROUND },
+ { "-troughrelief",TK_OPTION_RELIEF,
+ Tk_Offset(TroughElement,reliefObj), "sunken" },
+ { "-groovewidth", TK_OPTION_PIXELS,
+ Tk_Offset(TroughElement,grooveWidthObj), "-1" },
+ { NULL, 0, 0, NULL }
+};
+
+static void TroughElementSize(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+{
+ TroughElement *troughPtr = elementRecord;
+ int borderWidth = 2, grooveWidth = 0;
+
+ Tk_GetPixelsFromObj(NULL, tkwin, troughPtr->borderWidthObj, &borderWidth);
+ Tk_GetPixelsFromObj(NULL, tkwin, troughPtr->grooveWidthObj, &grooveWidth);
+
+ if (grooveWidth <= 0) {
+ *paddingPtr = Ttk_UniformPadding((short)borderWidth);
+ }
+}
+
+static void TroughElementDraw(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ Drawable d, Ttk_Box b, unsigned int state)
+{
+ TroughElement *troughPtr = elementRecord;
+ Tk_3DBorder border = NULL;
+ int borderWidth = 2, relief = TK_RELIEF_SUNKEN, groove = -1, orient;
+
+ border = Tk_Get3DBorderFromObj(tkwin, troughPtr->colorObj);
+ Ttk_GetOrientFromObj(NULL, troughPtr->orientObj, &orient);
+ Tk_GetReliefFromObj(NULL, troughPtr->reliefObj, &relief);
+ Tk_GetPixelsFromObj(NULL, tkwin, troughPtr->borderWidthObj, &borderWidth);
+ Tk_GetPixelsFromObj(NULL, tkwin, troughPtr->grooveWidthObj, &groove);
+
+ if (groove != -1 && groove < b.height && groove < b.width) {
+ if (orient == TTK_ORIENT_HORIZONTAL) {
+ b.y = b.y + b.height/2 - groove/2;
+ b.height = groove;
+ } else {
+ b.x = b.x + b.width/2 - groove/2;
+ b.width = groove;
+ }
+ }
+
+ Tk_Fill3DRectangle(tkwin, d, border, b.x, b.y, b.width, b.height,
+ borderWidth, relief);
+}
+
+static Ttk_ElementSpec TroughElementSpec = {
+ TK_STYLE_VERSION_2,
+ sizeof(TroughElement),
+ TroughElementOptions,
+ TroughElementSize,
+ TroughElementDraw
+};
+
+/*
+ *----------------------------------------------------------------------
+ * +++ Thumb element.
+ */
+
+typedef struct {
+ Tcl_Obj *sizeObj;
+ Tcl_Obj *firstObj;
+ Tcl_Obj *lastObj;
+ Tcl_Obj *borderObj;
+ Tcl_Obj *borderColorObj;
+ Tcl_Obj *reliefObj;
+ Tcl_Obj *orientObj;
+} ThumbElement;
+
+static Ttk_ElementOptionSpec ThumbElementOptions[] = {
+ { "-width", TK_OPTION_PIXELS, Tk_Offset(ThumbElement,sizeObj),
+ STRINGIFY(SCROLLBAR_WIDTH) },
+ { "-background", TK_OPTION_BORDER, Tk_Offset(ThumbElement,borderObj),
+ DEFAULT_BACKGROUND },
+ { "-bordercolor", TK_OPTION_COLOR, Tk_Offset(ThumbElement,borderColorObj),
+ "black" },
+ { "-relief", TK_OPTION_RELIEF,Tk_Offset(ThumbElement,reliefObj),"raised" },
+ { "-orient", TK_OPTION_ANY,Tk_Offset(ThumbElement,orientObj),"horizontal"},
+ { NULL, 0, 0, NULL }
+};
+
+static void ThumbElementSize(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+{
+ ThumbElement *thumb = elementRecord;
+ int orient, size;
+ Tk_GetPixelsFromObj(NULL, tkwin, thumb->sizeObj, &size);
+ Ttk_GetOrientFromObj(NULL, thumb->orientObj, &orient);
+
+ if (orient == TTK_ORIENT_VERTICAL) {
+ *widthPtr = size;
+ *heightPtr = MIN_THUMB_SIZE;
+ } else {
+ *widthPtr = MIN_THUMB_SIZE;
+ *heightPtr = size;
+ }
+}
+
+static void ThumbElementDraw(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ Drawable d, Ttk_Box b, unsigned int state)
+{
+ ThumbElement *thumb = elementRecord;
+ Tk_3DBorder border = Tk_Get3DBorderFromObj(tkwin, thumb->borderObj);
+ XColor *borderColor = Tk_GetColorFromObj(tkwin, thumb->borderColorObj);
+ int relief = TK_RELIEF_RAISED;
+ int borderWidth = 2;
+
+ /*
+ * Don't draw the thumb if we are disabled.
+ * This makes it behave like Windows ... if that's what we want.
+ if (state & TTK_STATE_DISABLED)
+ return;
+ */
+
+ Tk_GetReliefFromObj(NULL, thumb->reliefObj, &relief);
+
+ Tk_Fill3DRectangle(
+ tkwin, d, border, b.x,b.y,b.width,b.height, 0, TK_RELIEF_FLAT);
+ DrawBorder(tkwin, d, border, borderColor, b, borderWidth, relief);
+}
+
+static Ttk_ElementSpec ThumbElementSpec = {
+ TK_STYLE_VERSION_2,
+ sizeof(ThumbElement),
+ ThumbElementOptions,
+ ThumbElementSize,
+ ThumbElementDraw
+};
+
+/*
+ *----------------------------------------------------------------------
+ * +++ Slider element.
+ *
+ * This is the moving part of the scale widget.
+ *
+ * The slider element is the thumb in the scale widget. This is drawn
+ * as an arrow-type element that can point up, down, left or right.
+ *
+ */
+
+typedef struct {
+ Tcl_Obj *lengthObj; /* Long axis dimension */
+ Tcl_Obj *thicknessObj; /* Short axis dimension */
+ Tcl_Obj *reliefObj; /* Relief for this object */
+ Tcl_Obj *borderObj; /* Border / background color */
+ Tcl_Obj *borderColorObj; /* Additional border color */
+ Tcl_Obj *borderWidthObj;
+ Tcl_Obj *orientObj; /* Orientation of overall slider */
+} SliderElement;
+
+static Ttk_ElementOptionSpec SliderElementOptions[] = {
+ { "-sliderlength", TK_OPTION_PIXELS, Tk_Offset(SliderElement,lengthObj),
+ "15" },
+ { "-sliderthickness",TK_OPTION_PIXELS,Tk_Offset(SliderElement,thicknessObj),
+ "15" },
+ { "-sliderrelief", TK_OPTION_RELIEF, Tk_Offset(SliderElement,reliefObj),
+ "raised" },
+ { "-borderwidth", TK_OPTION_PIXELS, Tk_Offset(SliderElement,borderWidthObj),
+ STRINGIFY(BORDERWIDTH) },
+ { "-background", TK_OPTION_BORDER, Tk_Offset(SliderElement,borderObj),
+ DEFAULT_BACKGROUND },
+ { "-bordercolor", TK_OPTION_COLOR, Tk_Offset(ThumbElement,borderColorObj),
+ "black" },
+ { "-orient", TK_OPTION_ANY, Tk_Offset(SliderElement,orientObj),
+ "horizontal" },
+ { NULL, 0, 0, NULL }
+};
+
+static void SliderElementSize(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+{
+ SliderElement *slider = elementRecord;
+ int orient, length, thickness, borderWidth;
+
+ Ttk_GetOrientFromObj(NULL, slider->orientObj, &orient);
+ Tk_GetPixelsFromObj(NULL, tkwin, slider->borderWidthObj, &borderWidth);
+ Tk_GetPixelsFromObj(NULL, tkwin, slider->lengthObj, &length);
+ Tk_GetPixelsFromObj(NULL, tkwin, slider->thicknessObj, &thickness);
+
+ switch (orient) {
+ case TTK_ORIENT_VERTICAL:
+ *widthPtr = thickness + (borderWidth *2);
+ *heightPtr = *widthPtr/2;
+ break;
+
+ case TTK_ORIENT_HORIZONTAL:
+ *heightPtr = thickness + (borderWidth *2);
+ *widthPtr = *heightPtr/2;
+ break;
+ }
+}
+
+static void SliderElementDraw(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ Drawable d, Ttk_Box b, unsigned int state)
+{
+ SliderElement *slider = elementRecord;
+ Tk_3DBorder border = Tk_Get3DBorderFromObj(tkwin, slider->borderObj);
+ XColor *borderColor = Tk_GetColorFromObj(tkwin, slider->borderColorObj);
+ int relief = TK_RELIEF_RAISED, borderWidth = 2;
+
+ Tk_GetPixelsFromObj(NULL, tkwin, slider->borderWidthObj, &borderWidth);
+ Tk_GetReliefFromObj(NULL, slider->reliefObj, &relief);
+
+ Tk_Fill3DRectangle(tkwin, d, border,
+ b.x, b.y, b.width, b.height,
+ borderWidth, TK_RELIEF_FLAT);
+ DrawBorder(tkwin, d, border, borderColor, b, borderWidth, relief);
+}
+
+static Ttk_ElementSpec SliderElementSpec = {
+ TK_STYLE_VERSION_2,
+ sizeof(SliderElement),
+ SliderElementOptions,
+ SliderElementSize,
+ SliderElementDraw
+};
+
+/*------------------------------------------------------------------------
+ * +++ Tree indicator element.
+ */
+
+#define TTK_STATE_OPEN TTK_STATE_USER1 /* XREF: treeview.c */
+#define TTK_STATE_LEAF TTK_STATE_USER2
+
+typedef struct {
+ Tcl_Obj *colorObj;
+ Tcl_Obj *marginObj;
+ Tcl_Obj *diameterObj;
+} TreeitemIndicator;
+
+static Ttk_ElementOptionSpec TreeitemIndicatorOptions[] = {
+ { "-foreground", TK_OPTION_COLOR,
+ Tk_Offset(TreeitemIndicator,colorObj), DEFAULT_FOREGROUND },
+ { "-diameter", TK_OPTION_PIXELS,
+ Tk_Offset(TreeitemIndicator,diameterObj), "9" },
+ { "-indicatormargins", TK_OPTION_STRING,
+ Tk_Offset(TreeitemIndicator,marginObj), "2 2 4 2" },
+ { NULL, 0, 0, NULL }
+};
+
+static void TreeitemIndicatorSize(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+{
+ TreeitemIndicator *indicator = elementRecord;
+ int diameter = 0;
+ Ttk_Padding margins;
+
+ Ttk_GetPaddingFromObj(NULL, tkwin, indicator->marginObj, &margins);
+ Tk_GetPixelsFromObj(NULL, tkwin, indicator->diameterObj, &diameter);
+ *widthPtr = diameter + Ttk_PaddingWidth(margins);
+ *heightPtr = diameter + Ttk_PaddingHeight(margins);
+}
+
+static void TreeitemIndicatorDraw(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ Drawable d, Ttk_Box b, Ttk_State state)
+{
+ TreeitemIndicator *indicator = elementRecord;
+ XColor *color = Tk_GetColorFromObj(tkwin, indicator->colorObj);
+ GC gc = Tk_GCForColor(color, d);
+ Ttk_Padding padding = Ttk_UniformPadding(0);
+ int w = WIN32_XDRAWLINE_HACK;
+ int cx, cy;
+
+ if (state & TTK_STATE_LEAF) {
+ /* don't draw anything ... */
+ return;
+ }
+
+ Ttk_GetPaddingFromObj(NULL,tkwin,indicator->marginObj,&padding);
+ b = Ttk_PadBox(b, padding);
+
+ XDrawRectangle(Tk_Display(tkwin), d, gc,
+ b.x, b.y, b.width - 1, b.height - 1);
+
+ cx = b.x + (b.width - 1) / 2;
+ cy = b.y + (b.height - 1) / 2;
+ XDrawLine(Tk_Display(tkwin), d, gc, b.x+2, cy, b.x+b.width-3+w, cy);
+
+ if (!(state & TTK_STATE_OPEN)) {
+ /* turn '-' into a '+' */
+ XDrawLine(Tk_Display(tkwin), d, gc, cx, b.y+2, cx, b.y+b.height-3+w);
+ }
+}
+
+static Ttk_ElementSpec TreeitemIndicatorElementSpec = {
+ TK_STYLE_VERSION_2,
+ sizeof(TreeitemIndicator),
+ TreeitemIndicatorOptions,
+ TreeitemIndicatorSize,
+ TreeitemIndicatorDraw
+};
+
+/*------------------------------------------------------------------------
+ * TtkAltTheme_Init --
+ * Install alternate theme.
+ */
+MODULE_SCOPE int TtkAltTheme_Init(Tcl_Interp *interp)
+{
+ Ttk_Theme theme = Ttk_CreateTheme(interp, "alt", NULL);
+
+ if (!theme) {
+ return TCL_ERROR;
+ }
+
+ Ttk_RegisterElement(interp, theme, "border", &BorderElementSpec, NULL);
+
+ Ttk_RegisterElement(interp, theme, "Checkbutton.indicator",
+ &IndicatorElementSpec, &checkbutton_spec);
+ Ttk_RegisterElement(interp, theme, "Radiobutton.indicator",
+ &IndicatorElementSpec, &radiobutton_spec);
+ Ttk_RegisterElement(interp, theme, "Menubutton.indicator",
+ &MenubuttonArrowElementSpec, NULL);
+
+ Ttk_RegisterElement(interp, theme, "field", &FieldElementSpec, NULL);
+
+ Ttk_RegisterElement(interp, theme, "trough", &TroughElementSpec, NULL);
+ Ttk_RegisterElement(interp, theme, "thumb", &ThumbElementSpec, NULL);
+ Ttk_RegisterElement(interp, theme, "slider", &SliderElementSpec, NULL);
+
+ Ttk_RegisterElement(interp, theme, "uparrow",
+ &ArrowElementSpec, &ArrowElements[0]);
+ Ttk_RegisterElement(interp, theme, "downarrow",
+ &ArrowElementSpec, &ArrowElements[1]);
+ Ttk_RegisterElement(interp, theme, "leftarrow",
+ &ArrowElementSpec, &ArrowElements[2]);
+ Ttk_RegisterElement(interp, theme, "rightarrow",
+ &ArrowElementSpec, &ArrowElements[3]);
+ Ttk_RegisterElement(interp, theme, "arrow",
+ &ArrowElementSpec, &ArrowElements[0]);
+
+ Ttk_RegisterElement(interp, theme, "arrow",
+ &ArrowElementSpec, &ArrowElements[0]);
+
+ Ttk_RegisterElement(interp, theme, "Treeitem.indicator",
+ &TreeitemIndicatorElementSpec, 0);
+
+ Tcl_PkgProvide(interp, "ttk::theme::alt", TTK_VERSION);
+
+ return TCL_OK;
+}
+
+/*EOF*/
diff --git a/generic/ttk/ttkElements.c b/generic/ttk/ttkElements.c
new file mode 100644
index 0000000..22af1d6
--- /dev/null
+++ b/generic/ttk/ttkElements.c
@@ -0,0 +1,1281 @@
+/*
+ * Copyright (c) 2003, Joe English
+ *
+ * Default implementation for themed elements.
+ *
+ */
+
+#include <tcl.h>
+#include <tk.h>
+#include <string.h>
+#include "ttkTheme.h"
+#include "ttkWidget.h"
+
+#define DEFAULT_BORDERWIDTH "2"
+#define DEFAULT_ARROW_SIZE "15"
+#define MIN_THUMB_SIZE 10
+
+/*----------------------------------------------------------------------
+ * +++ Null element. Does nothing; used as a stub.
+ * Null element methods, option table and element spec are public,
+ * and may be used in other engines.
+ */
+
+/* public */ Ttk_ElementOptionSpec TtkNullElementOptions[] = { { NULL, 0, 0, NULL } };
+
+/* public */ void
+TtkNullElementSize(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+{
+}
+
+/* public */ void
+TtkNullElementDraw(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ Drawable d, Ttk_Box b, unsigned int state)
+{
+}
+
+/* public */ Ttk_ElementSpec ttkNullElementSpec = {
+ TK_STYLE_VERSION_2,
+ sizeof(NullElement),
+ TtkNullElementOptions,
+ TtkNullElementSize,
+ TtkNullElementDraw
+};
+
+/*----------------------------------------------------------------------
+ * +++ Background and fill elements.
+ *
+ * The fill element fills its parcel with the background color.
+ * The background element ignores the parcel, and fills the entire window.
+ *
+ * Ttk_GetLayout() automatically includes a background element.
+ */
+
+typedef struct {
+ Tcl_Obj *backgroundObj;
+} BackgroundElement;
+
+static Ttk_ElementOptionSpec BackgroundElementOptions[] = {
+ { "-background", TK_OPTION_BORDER,
+ Tk_Offset(BackgroundElement,backgroundObj), DEFAULT_BACKGROUND },
+ { NULL, 0, 0, NULL }
+};
+
+static void FillElementDraw(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ Drawable d, Ttk_Box b, unsigned int state)
+{
+ BackgroundElement *bg = elementRecord;
+ Tk_3DBorder backgroundPtr = Tk_Get3DBorderFromObj(tkwin,bg->backgroundObj);
+
+ XFillRectangle(Tk_Display(tkwin), d,
+ Tk_3DBorderGC(tkwin, backgroundPtr, TK_3D_FLAT_GC),
+ b.x, b.y, b.width, b.height);
+}
+
+static void BackgroundElementDraw(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ Drawable d, Ttk_Box b, unsigned int state)
+{
+ FillElementDraw(
+ clientData, elementRecord, tkwin,
+ d, Ttk_WinBox(tkwin), state);
+}
+
+static Ttk_ElementSpec FillElementSpec = {
+ TK_STYLE_VERSION_2,
+ sizeof(BackgroundElement),
+ BackgroundElementOptions,
+ TtkNullElementSize,
+ FillElementDraw
+};
+
+static Ttk_ElementSpec BackgroundElementSpec = {
+ TK_STYLE_VERSION_2,
+ sizeof(BackgroundElement),
+ BackgroundElementOptions,
+ TtkNullElementSize,
+ BackgroundElementDraw
+};
+
+/*----------------------------------------------------------------------
+ * +++ Border element.
+ */
+
+typedef struct {
+ Tcl_Obj *borderObj;
+ Tcl_Obj *borderWidthObj;
+ Tcl_Obj *reliefObj;
+} BorderElement;
+
+static Ttk_ElementOptionSpec BorderElementOptions[] = {
+ { "-background", TK_OPTION_BORDER,
+ Tk_Offset(BorderElement,borderObj), DEFAULT_BACKGROUND },
+ { "-borderwidth", TK_OPTION_PIXELS,
+ Tk_Offset(BorderElement,borderWidthObj), DEFAULT_BORDERWIDTH },
+ { "-relief", TK_OPTION_RELIEF,
+ Tk_Offset(BorderElement,reliefObj), "flat" },
+ { NULL, 0, 0, NULL }
+};
+
+static void BorderElementSize(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+{
+ BorderElement *bd = elementRecord;
+ int borderWidth = 0;
+ Tcl_GetIntFromObj(NULL, bd->borderWidthObj, &borderWidth);
+ *paddingPtr = Ttk_UniformPadding((short)borderWidth);
+}
+
+static void BorderElementDraw(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ Drawable d, Ttk_Box b, unsigned int state)
+{
+ BorderElement *bd = elementRecord;
+ Tk_3DBorder border = NULL;
+ int borderWidth = 1, relief = TK_RELIEF_FLAT;
+
+ border = Tk_Get3DBorderFromObj(tkwin, bd->borderObj);
+ Tcl_GetIntFromObj(NULL, bd->borderWidthObj, &borderWidth);
+ Tk_GetReliefFromObj(NULL, bd->reliefObj, &relief);
+
+ if (border && borderWidth > 0 && relief != TK_RELIEF_FLAT) {
+ Tk_Draw3DRectangle(tkwin, d, border,
+ b.x, b.y, b.width, b.height, borderWidth,relief);
+ }
+}
+
+static Ttk_ElementSpec BorderElementSpec = {
+ TK_STYLE_VERSION_2,
+ sizeof(BorderElement),
+ BorderElementOptions,
+ BorderElementSize,
+ BorderElementDraw
+};
+
+/*----------------------------------------------------------------------
+ * +++ Field element.
+ * Used for editable fields.
+ */
+typedef struct {
+ Tcl_Obj *borderObj;
+ Tcl_Obj *borderWidthObj;
+} FieldElement;
+
+static Ttk_ElementOptionSpec FieldElementOptions[] = {
+ { "-fieldbackground", TK_OPTION_BORDER,
+ Tk_Offset(FieldElement,borderObj), "white" },
+ { "-borderwidth", TK_OPTION_PIXELS,
+ Tk_Offset(FieldElement,borderWidthObj), "2" },
+ { NULL, 0, 0, NULL }
+};
+
+static void FieldElementSize(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+{
+ FieldElement *field = elementRecord;
+ int borderWidth = 2;
+ Tk_GetPixelsFromObj(NULL, tkwin, field->borderWidthObj, &borderWidth);
+ *paddingPtr = Ttk_UniformPadding((short)borderWidth);
+}
+
+static void FieldElementDraw(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ Drawable d, Ttk_Box b, unsigned int state)
+{
+ FieldElement *field = elementRecord;
+ Tk_3DBorder border = Tk_Get3DBorderFromObj(tkwin, field->borderObj);
+ int borderWidth = 2;
+
+ Tk_GetPixelsFromObj(NULL, tkwin, field->borderWidthObj, &borderWidth);
+ Tk_Fill3DRectangle(tkwin, d, border,
+ b.x, b.y, b.width, b.height, borderWidth, TK_RELIEF_SUNKEN);
+}
+
+static Ttk_ElementSpec FieldElementSpec = {
+ TK_STYLE_VERSION_2,
+ sizeof(FieldElement),
+ FieldElementOptions,
+ FieldElementSize,
+ FieldElementDraw
+};
+
+/*
+ *----------------------------------------------------------------------
+ * +++ Padding element.
+ *
+ * This element has no visual representation, only geometry.
+ * It adds a (possibly non-uniform) internal border.
+ * In addition, if "-shiftrelief" is specified,
+ * adds additional pixels to shift child elements "in" or "out"
+ * depending on the -relief.
+ */
+
+typedef struct {
+ Tcl_Obj *paddingObj;
+ Tcl_Obj *reliefObj;
+ Tcl_Obj *shiftreliefObj;
+} PaddingElement;
+
+static Ttk_ElementOptionSpec PaddingElementOptions[] = {
+ { "-padding", TK_OPTION_STRING,
+ Tk_Offset(PaddingElement,paddingObj), "0" },
+ { "-relief", TK_OPTION_RELIEF,
+ Tk_Offset(PaddingElement,reliefObj), "flat" },
+ { "-shiftrelief", TK_OPTION_INT,
+ Tk_Offset(PaddingElement,shiftreliefObj), "0" },
+ { NULL, 0, 0, NULL }
+};
+
+static void PaddingElementSize(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+{
+ PaddingElement *padding = elementRecord;
+ int shiftRelief = 0;
+ int relief = TK_RELIEF_FLAT;
+ Ttk_Padding pad;
+
+ Tk_GetReliefFromObj(NULL, padding->reliefObj, &relief);
+ Tcl_GetIntFromObj(NULL, padding->shiftreliefObj, &shiftRelief);
+ Ttk_GetPaddingFromObj(NULL,tkwin,padding->paddingObj,&pad);
+ *paddingPtr = Ttk_RelievePadding(pad, relief, shiftRelief);
+}
+
+static Ttk_ElementSpec PaddingElementSpec = {
+ TK_STYLE_VERSION_2,
+ sizeof(PaddingElement),
+ PaddingElementOptions,
+ PaddingElementSize,
+ TtkNullElementDraw
+};
+
+/*----------------------------------------------------------------------
+ * +++ Focus ring element.
+ * Draws a dashed focus ring, if the widget has keyboard focus.
+ */
+typedef struct {
+ Tcl_Obj *focusColorObj;
+ Tcl_Obj *focusThicknessObj;
+} FocusElement;
+
+/*
+ * DrawFocusRing --
+ * Draw a dotted rectangle to indicate focus.
+ */
+static void DrawFocusRing(
+ Tk_Window tkwin, Drawable d, Tcl_Obj *colorObj, Ttk_Box b)
+{
+ XColor *color = Tk_GetColorFromObj(tkwin, colorObj);
+ unsigned long mask = 0UL;
+ XGCValues gcvalues;
+ GC gc;
+
+ gcvalues.foreground = color->pixel;
+ gcvalues.line_style = LineOnOffDash;
+ gcvalues.line_width = 1;
+ gcvalues.dashes = 1;
+ gcvalues.dash_offset = 1;
+ mask = GCForeground | GCLineStyle | GCDashList | GCDashOffset | GCLineWidth;
+
+ gc = Tk_GetGC(tkwin, mask, &gcvalues);
+ XDrawRectangle(Tk_Display(tkwin), d, gc, b.x, b.y, b.width-1, b.height-1);
+ Tk_FreeGC(Tk_Display(tkwin), gc);
+}
+
+static Ttk_ElementOptionSpec FocusElementOptions[] = {
+ { "-focuscolor",TK_OPTION_COLOR,
+ Tk_Offset(FocusElement,focusColorObj), "black" },
+ { "-focusthickness",TK_OPTION_PIXELS,
+ Tk_Offset(FocusElement,focusThicknessObj), "1" },
+ { NULL, 0, 0, NULL }
+};
+
+static void FocusElementSize(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+{
+ FocusElement *focus = elementRecord;
+ int focusThickness = 0;
+
+ Tcl_GetIntFromObj(NULL, focus->focusThicknessObj, &focusThickness);
+ *paddingPtr = Ttk_UniformPadding((short)focusThickness);
+}
+
+static void FocusElementDraw(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ Drawable d, Ttk_Box b, unsigned int state)
+{
+ FocusElement *focus = elementRecord;
+ int focusThickness = 0;
+
+ if (state & TTK_STATE_FOCUS) {
+ Tcl_GetIntFromObj(NULL,focus->focusThicknessObj,&focusThickness);
+ DrawFocusRing(tkwin, d, focus->focusColorObj, b);
+ }
+}
+
+static Ttk_ElementSpec FocusElementSpec = {
+ TK_STYLE_VERSION_2,
+ sizeof(FocusElement),
+ FocusElementOptions,
+ FocusElementSize,
+ FocusElementDraw
+};
+
+/*----------------------------------------------------------------------
+ * +++ Separator element.
+ * Just draws a horizontal or vertical bar.
+ * Three elements are defined: horizontal, vertical, and general;
+ * the general separator checks the "-orient" option.
+ */
+
+typedef struct {
+ Tcl_Obj *orientObj;
+ Tcl_Obj *borderObj;
+} SeparatorElement;
+
+static Ttk_ElementOptionSpec SeparatorElementOptions[] = {
+ { "-orient", TK_OPTION_ANY,
+ Tk_Offset(SeparatorElement, orientObj), "horizontal" },
+ { "-background", TK_OPTION_BORDER,
+ Tk_Offset(SeparatorElement,borderObj), DEFAULT_BACKGROUND },
+ { NULL, 0, 0, NULL }
+};
+
+static void SeparatorElementSize(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+{
+ *widthPtr = *heightPtr = 2;
+}
+
+static void HorizontalSeparatorElementDraw(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ Drawable d, Ttk_Box b, unsigned int state)
+{
+ SeparatorElement *separator = elementRecord;
+ Tk_3DBorder border = Tk_Get3DBorderFromObj(tkwin, separator->borderObj);
+ GC lightGC = Tk_3DBorderGC(tkwin, border, TK_3D_LIGHT_GC);
+ GC darkGC = Tk_3DBorderGC(tkwin, border, TK_3D_DARK_GC);
+
+ XDrawLine(Tk_Display(tkwin), d, darkGC, b.x, b.y, b.x + b.width, b.y);
+ XDrawLine(Tk_Display(tkwin), d, lightGC, b.x, b.y+1, b.x + b.width, b.y+1);
+}
+
+static void VerticalSeparatorElementDraw(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ Drawable d, Ttk_Box b, unsigned int state)
+{
+ SeparatorElement *separator = elementRecord;
+ Tk_3DBorder border = Tk_Get3DBorderFromObj(tkwin, separator->borderObj);
+ GC lightGC = Tk_3DBorderGC(tkwin, border, TK_3D_LIGHT_GC);
+ GC darkGC = Tk_3DBorderGC(tkwin, border, TK_3D_DARK_GC);
+
+ XDrawLine(Tk_Display(tkwin), d, darkGC, b.x, b.y, b.x, b.y + b.height);
+ XDrawLine(Tk_Display(tkwin), d, lightGC, b.x+1, b.y, b.x+1, b.y+b.height);
+}
+
+static void GeneralSeparatorElementDraw(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ Drawable d, Ttk_Box b, unsigned int state)
+{
+ SeparatorElement *separator = elementRecord;
+ int orient;
+ Ttk_GetOrientFromObj(NULL, separator->orientObj, &orient);
+ switch (orient) {
+ case TTK_ORIENT_HORIZONTAL:
+ HorizontalSeparatorElementDraw(
+ clientData, elementRecord, tkwin, d, b, state);
+ break;
+ case TTK_ORIENT_VERTICAL:
+ VerticalSeparatorElementDraw(
+ clientData, elementRecord, tkwin, d, b, state);
+ break;
+ }
+}
+
+static Ttk_ElementSpec HorizontalSeparatorElementSpec = {
+ TK_STYLE_VERSION_2,
+ sizeof(SeparatorElement),
+ SeparatorElementOptions,
+ SeparatorElementSize,
+ HorizontalSeparatorElementDraw
+};
+
+static Ttk_ElementSpec VerticalSeparatorElementSpec = {
+ TK_STYLE_VERSION_2,
+ sizeof(SeparatorElement),
+ SeparatorElementOptions,
+ SeparatorElementSize,
+ HorizontalSeparatorElementDraw
+};
+
+static Ttk_ElementSpec SeparatorElementSpec = {
+ TK_STYLE_VERSION_2,
+ sizeof(SeparatorElement),
+ SeparatorElementOptions,
+ SeparatorElementSize,
+ GeneralSeparatorElementDraw
+};
+
+/*----------------------------------------------------------------------
+ * +++ Sizegrip: lower-right corner grip handle for resizing window.
+ */
+
+typedef struct {
+ Tcl_Obj *backgroundObj;
+} SizegripElement;
+
+static Ttk_ElementOptionSpec SizegripOptions[] = {
+ { "-background", TK_OPTION_BORDER,
+ Tk_Offset(SizegripElement,backgroundObj), DEFAULT_BACKGROUND },
+ {0,0,0,0}
+};
+
+static void SizegripSize(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+{
+ int gripCount = 3, gripSpace = 2, gripThickness = 3;
+ *widthPtr = *heightPtr = gripCount * (gripSpace + gripThickness);
+}
+
+static void SizegripDraw(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ Drawable d, Ttk_Box b, Ttk_State state)
+{
+ SizegripElement *grip = elementRecord;
+ int gripCount = 3, gripSpace = 2;
+ Tk_3DBorder border = Tk_Get3DBorderFromObj(tkwin, grip->backgroundObj);
+ GC lightGC = Tk_3DBorderGC(tkwin, border, TK_3D_LIGHT_GC);
+ GC darkGC = Tk_3DBorderGC(tkwin, border, TK_3D_DARK_GC);
+ int x1 = b.x + b.width-1, y1 = b.y + b.height-1, x2 = x1, y2 = y1;
+
+ while (gripCount--) {
+ x1 -= gripSpace; y2 -= gripSpace;
+ XDrawLine(Tk_Display(tkwin), d, darkGC, x1,y1, x2,y2); --x1; --y2;
+ XDrawLine(Tk_Display(tkwin), d, darkGC, x1,y1, x2,y2); --x1; --y2;
+ XDrawLine(Tk_Display(tkwin), d, lightGC, x1,y1, x2,y2); --x1; --y2;
+ }
+}
+
+static Ttk_ElementSpec SizegripElementSpec = {
+ TK_STYLE_VERSION_2,
+ sizeof(SizegripElement),
+ SizegripOptions,
+ SizegripSize,
+ SizegripDraw
+};
+
+/*----------------------------------------------------------------------
+ * +++ Indicator element.
+ *
+ * Draws the on/off indicator for checkbuttons and radiobuttons.
+ *
+ * Draws a 3-D square (or diamond), raised if off, sunken if on.
+ *
+ * This is actually a regression from Tk 8.5 back to the ugly old Motif
+ * style; use "altTheme" for the newer, nicer version.
+ */
+
+typedef struct {
+ Tcl_Obj *backgroundObj;
+ Tcl_Obj *reliefObj;
+ Tcl_Obj *colorObj;
+ Tcl_Obj *diameterObj;
+ Tcl_Obj *marginObj;
+ Tcl_Obj *borderWidthObj;
+} IndicatorElement;
+
+static Ttk_ElementOptionSpec IndicatorElementOptions[] = {
+ { "-background", TK_OPTION_BORDER,
+ Tk_Offset(IndicatorElement,backgroundObj), DEFAULT_BACKGROUND },
+ { "-indicatorcolor", TK_OPTION_BORDER,
+ Tk_Offset(IndicatorElement,colorObj), DEFAULT_BACKGROUND },
+ { "-indicatorrelief", TK_OPTION_RELIEF,
+ Tk_Offset(IndicatorElement,reliefObj), "raised" },
+ { "-indicatordiameter", TK_OPTION_PIXELS,
+ Tk_Offset(IndicatorElement,diameterObj), "12" },
+ { "-indicatormargin", TK_OPTION_STRING,
+ Tk_Offset(IndicatorElement,marginObj), "0 2 4 2" },
+ { "-borderwidth", TK_OPTION_PIXELS,
+ Tk_Offset(IndicatorElement,borderWidthObj), DEFAULT_BORDERWIDTH },
+ { NULL, 0, 0, NULL }
+};
+
+/*
+ * Checkbutton indicators (default): 3-D square.
+ */
+static void SquareIndicatorElementSize(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+{
+ IndicatorElement *indicator = elementRecord;
+ Ttk_Padding margins;
+ int diameter = 0;
+ Ttk_GetPaddingFromObj(NULL, tkwin, indicator->marginObj, &margins);
+ Tk_GetPixelsFromObj(NULL, tkwin, indicator->diameterObj, &diameter);
+ *widthPtr = diameter + Ttk_PaddingWidth(margins);
+ *heightPtr = diameter + Ttk_PaddingHeight(margins);
+}
+
+static void SquareIndicatorElementDraw(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ Drawable d, Ttk_Box b, unsigned int state)
+{
+ IndicatorElement *indicator = elementRecord;
+ Tk_3DBorder border = 0, interior = 0;
+ int relief = TK_RELIEF_RAISED;
+ Ttk_Padding padding;
+ int borderWidth = 2;
+ int diameter;
+
+ interior = Tk_Get3DBorderFromObj(tkwin, indicator->colorObj);
+ border = Tk_Get3DBorderFromObj(tkwin, indicator->backgroundObj);
+ Tcl_GetIntFromObj(NULL,indicator->borderWidthObj,&borderWidth);
+ Tk_GetReliefFromObj(NULL,indicator->reliefObj,&relief);
+ Ttk_GetPaddingFromObj(NULL,tkwin,indicator->marginObj,&padding);
+
+ b = Ttk_PadBox(b, padding);
+
+ diameter = b.width < b.height ? b.width : b.height;
+ Tk_Fill3DRectangle(tkwin, d, interior, b.x, b.y,
+ diameter, diameter,borderWidth, TK_RELIEF_FLAT);
+ Tk_Draw3DRectangle(tkwin, d, border, b.x, b.y,
+ diameter, diameter, borderWidth, relief);
+}
+
+/*
+ * Radiobutton indicators: 3-D diamond.
+ */
+static void DiamondIndicatorElementSize(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+{
+ IndicatorElement *indicator = elementRecord;
+ Ttk_Padding margins;
+ int diameter = 0;
+ Ttk_GetPaddingFromObj(NULL, tkwin, indicator->marginObj, &margins);
+ Tk_GetPixelsFromObj(NULL, tkwin, indicator->diameterObj, &diameter);
+ *widthPtr = diameter + 3 + Ttk_PaddingWidth(margins);
+ *heightPtr = diameter + 3 + Ttk_PaddingHeight(margins);
+}
+
+static void DiamondIndicatorElementDraw(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ Drawable d, Ttk_Box b, unsigned int state)
+{
+ IndicatorElement *indicator = elementRecord;
+ Tk_3DBorder border = 0, interior = 0;
+ int borderWidth = 2;
+ int relief = TK_RELIEF_RAISED;
+ int diameter, radius;
+ XPoint points[4];
+ Ttk_Padding padding;
+
+ interior = Tk_Get3DBorderFromObj(tkwin, indicator->colorObj);
+ border = Tk_Get3DBorderFromObj(tkwin, indicator->backgroundObj);
+ Tcl_GetIntFromObj(NULL,indicator->borderWidthObj,&borderWidth);
+ Tk_GetReliefFromObj(NULL,indicator->reliefObj,&relief);
+ Ttk_GetPaddingFromObj(NULL,tkwin,indicator->marginObj,&padding);
+
+ b = Ttk_PadBox(b, padding);
+
+ diameter = b.width < b.height ? b.width : b.height;
+ radius = diameter / 2;
+
+ points[0].x = b.x;
+ points[0].y = b.y + radius;
+ points[1].x = b.x + radius;
+ points[1].y = b.y + 2*radius;
+ points[2].x = b.x + 2*radius;
+ points[2].y = b.y + radius;
+ points[3].x = b.x + radius;
+ points[3].y = b.y;
+
+ Tk_Fill3DPolygon(tkwin,d,interior,points,4,borderWidth,TK_RELIEF_FLAT);
+ Tk_Draw3DPolygon(tkwin,d,border,points,4,borderWidth,relief);
+}
+
+static Ttk_ElementSpec CheckbuttonIndicatorElementSpec = {
+ TK_STYLE_VERSION_2,
+ sizeof(IndicatorElement),
+ IndicatorElementOptions,
+ SquareIndicatorElementSize,
+ SquareIndicatorElementDraw
+};
+
+static Ttk_ElementSpec RadiobuttonIndicatorElementSpec = {
+ TK_STYLE_VERSION_2,
+ sizeof(IndicatorElement),
+ IndicatorElementOptions,
+ DiamondIndicatorElementSize,
+ DiamondIndicatorElementDraw
+};
+
+/*
+ *----------------------------------------------------------------------
+ * +++ Menubutton indicators.
+ *
+ * These aren't functional like radio/check indicators,
+ * they're just affordability indicators.
+ *
+ * Standard Tk sets the indicator size to 4.0 mm by 1.7 mm.
+ * I have no idea where these numbers came from.
+ */
+
+typedef struct {
+ Tcl_Obj *backgroundObj;
+ Tcl_Obj *widthObj;
+ Tcl_Obj *heightObj;
+ Tcl_Obj *borderWidthObj;
+ Tcl_Obj *reliefObj;
+ Tcl_Obj *marginObj;
+} MenuIndicatorElement;
+
+static Ttk_ElementOptionSpec MenuIndicatorElementOptions[] = {
+ { "-background", TK_OPTION_BORDER,
+ Tk_Offset(MenuIndicatorElement,backgroundObj), DEFAULT_BACKGROUND },
+ { "-indicatorwidth", TK_OPTION_PIXELS,
+ Tk_Offset(MenuIndicatorElement,widthObj), "4.0m" },
+ { "-indicatorheight", TK_OPTION_PIXELS,
+ Tk_Offset(MenuIndicatorElement,heightObj), "1.7m" },
+ { "-borderwidth", TK_OPTION_PIXELS,
+ Tk_Offset(MenuIndicatorElement,borderWidthObj), DEFAULT_BORDERWIDTH },
+ { "-indicatorrelief", TK_OPTION_RELIEF,
+ Tk_Offset(MenuIndicatorElement,reliefObj),"raised" },
+ { "-indicatormargin", TK_OPTION_STRING,
+ Tk_Offset(MenuIndicatorElement,marginObj), "5 0" },
+ { NULL, 0, 0, NULL }
+};
+
+static void MenuIndicatorElementSize(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+{
+ MenuIndicatorElement *mi = elementRecord;
+ Ttk_Padding margins;
+ Tk_GetPixelsFromObj(NULL, tkwin, mi->widthObj, widthPtr);
+ Tk_GetPixelsFromObj(NULL, tkwin, mi->heightObj, heightPtr);
+ Ttk_GetPaddingFromObj(NULL,tkwin,mi->marginObj, &margins);
+ *widthPtr += Ttk_PaddingWidth(margins);
+ *heightPtr += Ttk_PaddingHeight(margins);
+}
+
+static void MenuIndicatorElementDraw(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ Drawable d, Ttk_Box b, unsigned int state)
+{
+ MenuIndicatorElement *mi = elementRecord;
+ Tk_3DBorder border = Tk_Get3DBorderFromObj(tkwin, mi->backgroundObj);
+ Ttk_Padding margins;
+ int borderWidth = 2;
+
+ Ttk_GetPaddingFromObj(NULL,tkwin,mi->marginObj,&margins);
+ b = Ttk_PadBox(b, margins);
+ Tk_GetPixelsFromObj(NULL, tkwin, mi->borderWidthObj, &borderWidth);
+ Tk_Fill3DRectangle(tkwin, d, border, b.x, b.y, b.width, b.height,
+ borderWidth, TK_RELIEF_RAISED);
+}
+
+static Ttk_ElementSpec MenuIndicatorElementSpec = {
+ TK_STYLE_VERSION_2,
+ sizeof(MenuIndicatorElement),
+ MenuIndicatorElementOptions,
+ MenuIndicatorElementSize,
+ MenuIndicatorElementDraw
+};
+
+/*----------------------------------------------------------------------
+ * +++ Arrow elements.
+ *
+ * Draws a solid triangle inside a box.
+ * clientData is an enum ArrowDirection pointer.
+ */
+
+static int ArrowElements[] = { ARROW_UP, ARROW_DOWN, ARROW_LEFT, ARROW_RIGHT };
+typedef struct {
+ Tcl_Obj *borderObj;
+ Tcl_Obj *borderWidthObj;
+ Tcl_Obj *reliefObj;
+ Tcl_Obj *sizeObj;
+ Tcl_Obj *colorObj;
+} ArrowElement;
+
+static Ttk_ElementOptionSpec ArrowElementOptions[] = {
+ { "-background", TK_OPTION_BORDER,
+ Tk_Offset(ArrowElement,borderObj), DEFAULT_BACKGROUND },
+ { "-relief",TK_OPTION_RELIEF,
+ Tk_Offset(ArrowElement,reliefObj),"raised"},
+ { "-borderwidth", TK_OPTION_PIXELS,
+ Tk_Offset(ArrowElement,borderWidthObj), "1" },
+ { "-arrowcolor",TK_OPTION_COLOR,
+ Tk_Offset(ArrowElement,colorObj),"black"},
+ { "-arrowsize", TK_OPTION_PIXELS,
+ Tk_Offset(ArrowElement,sizeObj), "14" },
+ { NULL, 0, 0, NULL }
+};
+
+static Ttk_Padding ArrowMargins = { 3,3,3,3 };
+
+static void ArrowElementSize(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+{
+ ArrowElement *arrow = elementRecord;
+ int direction = *(int *)clientData;
+ int width = 14;
+
+ Tk_GetPixelsFromObj(NULL, tkwin, arrow->sizeObj, &width);
+ width -= Ttk_PaddingWidth(ArrowMargins);
+ TtkArrowSize(width/2, direction, widthPtr, heightPtr);
+ *widthPtr += Ttk_PaddingWidth(ArrowMargins);
+ *heightPtr += Ttk_PaddingWidth(ArrowMargins);
+}
+
+static void ArrowElementDraw(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ Drawable d, Ttk_Box b, unsigned int state)
+{
+ int direction = *(int *)clientData;
+ ArrowElement *arrow = elementRecord;
+ Tk_3DBorder border = Tk_Get3DBorderFromObj(tkwin, arrow->borderObj);
+ XColor *arrowColor = Tk_GetColorFromObj(tkwin, arrow->colorObj);
+ int relief = TK_RELIEF_RAISED;
+ int borderWidth = 1;
+
+ Tk_GetReliefFromObj(NULL, arrow->reliefObj, &relief);
+
+ Tk_Fill3DRectangle(
+ tkwin, d, border, b.x, b.y, b.width, b.height, borderWidth, relief);
+
+ TtkFillArrow(Tk_Display(tkwin), d, Tk_GCForColor(arrowColor, d),
+ Ttk_PadBox(b, ArrowMargins), direction);
+}
+
+static Ttk_ElementSpec ArrowElementSpec = {
+ TK_STYLE_VERSION_2,
+ sizeof(ArrowElement),
+ ArrowElementOptions,
+ ArrowElementSize,
+ ArrowElementDraw
+};
+
+/*----------------------------------------------------------------------
+ * +++ Trough element.
+ *
+ * Used in scrollbars and scales in place of "border".
+ */
+
+typedef struct {
+ Tcl_Obj *colorObj;
+ Tcl_Obj *borderWidthObj;
+ Tcl_Obj *reliefObj;
+} TroughElement;
+
+static Ttk_ElementOptionSpec TroughElementOptions[] = {
+ { "-borderwidth", TK_OPTION_PIXELS,
+ Tk_Offset(TroughElement,borderWidthObj), DEFAULT_BORDERWIDTH },
+ { "-troughcolor", TK_OPTION_BORDER,
+ Tk_Offset(TroughElement,colorObj), DEFAULT_BACKGROUND },
+ { "-troughrelief",TK_OPTION_RELIEF,
+ Tk_Offset(TroughElement,reliefObj), "sunken" },
+ { NULL, 0, 0, NULL }
+};
+
+static void TroughElementSize(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+{
+ TroughElement *troughPtr = elementRecord;
+ int borderWidth = 2;
+
+ Tk_GetPixelsFromObj(NULL, tkwin, troughPtr->borderWidthObj, &borderWidth);
+ *paddingPtr = Ttk_UniformPadding((short)borderWidth);
+}
+
+static void TroughElementDraw(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ Drawable d, Ttk_Box b, unsigned int state)
+{
+ TroughElement *troughPtr = elementRecord;
+ Tk_3DBorder border = NULL;
+ int borderWidth = 2, relief = TK_RELIEF_SUNKEN;
+
+ border = Tk_Get3DBorderFromObj(tkwin, troughPtr->colorObj);
+ Tk_GetReliefFromObj(NULL, troughPtr->reliefObj, &relief);
+ Tk_GetPixelsFromObj(NULL, tkwin, troughPtr->borderWidthObj, &borderWidth);
+
+ Tk_Fill3DRectangle(tkwin, d, border, b.x, b.y, b.width, b.height,
+ borderWidth, relief);
+}
+
+static Ttk_ElementSpec TroughElementSpec = {
+ TK_STYLE_VERSION_2,
+ sizeof(TroughElement),
+ TroughElementOptions,
+ TroughElementSize,
+ TroughElementDraw
+};
+
+/*
+ *----------------------------------------------------------------------
+ * +++ Thumb element.
+ *
+ * Used in scrollbars.
+ */
+
+typedef struct {
+ Tcl_Obj *orientObj;
+ Tcl_Obj *thicknessObj;
+ Tcl_Obj *reliefObj;
+ Tcl_Obj *borderObj;
+ Tcl_Obj *borderWidthObj;
+} ThumbElement;
+
+static Ttk_ElementOptionSpec ThumbElementOptions[] = {
+ { "-orient", TK_OPTION_ANY,
+ Tk_Offset(ThumbElement, orientObj), "horizontal" },
+ { "-width", TK_OPTION_PIXELS,
+ Tk_Offset(ThumbElement,thicknessObj), DEFAULT_ARROW_SIZE },
+ { "-relief", TK_OPTION_RELIEF,
+ Tk_Offset(ThumbElement,reliefObj), "raised" },
+ { "-background", TK_OPTION_BORDER,
+ Tk_Offset(ThumbElement,borderObj), DEFAULT_BACKGROUND },
+ { "-borderwidth", TK_OPTION_PIXELS,
+ Tk_Offset(ThumbElement,borderWidthObj), DEFAULT_BORDERWIDTH },
+ { NULL, 0, 0, NULL }
+};
+
+static void ThumbElementSize(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+{
+ ThumbElement *thumb = elementRecord;
+ int orient, thickness;
+
+ Tk_GetPixelsFromObj(NULL, tkwin, thumb->thicknessObj, &thickness);
+ Ttk_GetOrientFromObj(NULL, thumb->orientObj, &orient);
+
+ if (orient == TTK_ORIENT_VERTICAL) {
+ *widthPtr = thickness;
+ *heightPtr = MIN_THUMB_SIZE;
+ } else {
+ *widthPtr = MIN_THUMB_SIZE;
+ *heightPtr = thickness;
+ }
+}
+
+static void ThumbElementDraw(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ Drawable d, Ttk_Box b, unsigned int state)
+{
+ ThumbElement *thumb = elementRecord;
+ Tk_3DBorder border = Tk_Get3DBorderFromObj(tkwin, thumb->borderObj);
+ int borderWidth = 2, relief = TK_RELIEF_RAISED;
+
+ Tk_GetPixelsFromObj(NULL, tkwin, thumb->borderWidthObj, &borderWidth);
+ Tk_GetReliefFromObj(NULL, thumb->reliefObj, &relief);
+ Tk_Fill3DRectangle(tkwin, d, border, b.x, b.y, b.width, b.height,
+ borderWidth, relief);
+}
+
+static Ttk_ElementSpec ThumbElementSpec = {
+ TK_STYLE_VERSION_2,
+ sizeof(ThumbElement),
+ ThumbElementOptions,
+ ThumbElementSize,
+ ThumbElementDraw
+};
+
+/*
+ *----------------------------------------------------------------------
+ * +++ Slider element.
+ *
+ * This is the moving part of the scale widget. Drawn as a raised box.
+ */
+
+typedef struct {
+ Tcl_Obj *orientObj; /* orientation of overall slider */
+ Tcl_Obj *lengthObj; /* slider length */
+ Tcl_Obj *thicknessObj; /* slider thickness */
+ Tcl_Obj *reliefObj; /* the relief for this object */
+ Tcl_Obj *borderObj; /* the background color */
+ Tcl_Obj *borderWidthObj; /* the size of the border */
+} SliderElement;
+
+static Ttk_ElementOptionSpec SliderElementOptions[] = {
+ { "-sliderlength", TK_OPTION_PIXELS, Tk_Offset(SliderElement,lengthObj),
+ "30" },
+ { "-sliderthickness",TK_OPTION_PIXELS,Tk_Offset(SliderElement,thicknessObj),
+ "15" },
+ { "-sliderrelief", TK_OPTION_RELIEF, Tk_Offset(SliderElement,reliefObj),
+ "raised" },
+ { "-borderwidth", TK_OPTION_PIXELS, Tk_Offset(SliderElement,borderWidthObj),
+ DEFAULT_BORDERWIDTH },
+ { "-background", TK_OPTION_BORDER, Tk_Offset(SliderElement,borderObj),
+ DEFAULT_BACKGROUND },
+ { "-orient", TK_OPTION_ANY, Tk_Offset(SliderElement,orientObj),
+ "horizontal" },
+ { NULL, 0, 0, NULL }
+};
+
+static void SliderElementSize(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+{
+ SliderElement *slider = elementRecord;
+ int orient, length, thickness;
+
+ Ttk_GetOrientFromObj(NULL, slider->orientObj, &orient);
+ Tk_GetPixelsFromObj(NULL, tkwin, slider->lengthObj, &length);
+ Tk_GetPixelsFromObj(NULL, tkwin, slider->thicknessObj, &thickness);
+
+ switch (orient) {
+ case TTK_ORIENT_VERTICAL:
+ *widthPtr = thickness;
+ *heightPtr = length;
+ break;
+
+ case TTK_ORIENT_HORIZONTAL:
+ *widthPtr = length;
+ *heightPtr = thickness;
+ break;
+ }
+}
+
+static void SliderElementDraw(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ Drawable d, Ttk_Box b, unsigned int state)
+{
+ SliderElement *slider = elementRecord;
+ Tk_3DBorder border = NULL;
+ int relief = TK_RELIEF_RAISED, borderWidth = 2, orient;
+
+ border = Tk_Get3DBorderFromObj(tkwin, slider->borderObj);
+ Ttk_GetOrientFromObj(NULL, slider->orientObj, &orient);
+ Tk_GetPixelsFromObj(NULL, tkwin, slider->borderWidthObj, &borderWidth);
+ Tk_GetReliefFromObj(NULL, slider->reliefObj, &relief);
+
+ Tk_Fill3DRectangle(tkwin, d, border,
+ b.x, b.y, b.width, b.height,
+ borderWidth, relief);
+
+ if (relief != TK_RELIEF_FLAT) {
+ if (orient == TTK_ORIENT_HORIZONTAL) {
+ if (b.width > 4) {
+ b.x += b.width/2;
+ XDrawLine(Tk_Display(tkwin), d,
+ Tk_3DBorderGC(tkwin, border, TK_3D_DARK_GC),
+ b.x-1, b.y+borderWidth, b.x-1, b.y+b.height-borderWidth);
+ XDrawLine(Tk_Display(tkwin), d,
+ Tk_3DBorderGC(tkwin, border, TK_3D_LIGHT_GC),
+ b.x, b.y+borderWidth, b.x, b.y+b.height-borderWidth);
+ }
+ } else {
+ if (b.height > 4) {
+ b.y += b.height/2;
+ XDrawLine(Tk_Display(tkwin), d,
+ Tk_3DBorderGC(tkwin, border, TK_3D_DARK_GC),
+ b.x+borderWidth, b.y-1, b.x+b.width-borderWidth, b.y-1);
+ XDrawLine(Tk_Display(tkwin), d,
+ Tk_3DBorderGC(tkwin, border, TK_3D_LIGHT_GC),
+ b.x+borderWidth, b.y, b.x+b.width-borderWidth, b.y);
+ }
+ }
+ }
+}
+
+static Ttk_ElementSpec SliderElementSpec = {
+ TK_STYLE_VERSION_2,
+ sizeof(SliderElement),
+ SliderElementOptions,
+ SliderElementSize,
+ SliderElementDraw
+};
+
+/*------------------------------------------------------------------------
+ * +++ Progress bar element:
+ * Draws the moving part of the progress bar.
+ *
+ * -thickness specifies the size along the short axis of the bar.
+ * -length specifies the default size along the long axis;
+ * the bar will be this long in indeterminate mode.
+ */
+
+#define DEFAULT_PBAR_THICKNESS "15"
+#define DEFAULT_PBAR_LENGTH "30"
+
+typedef struct {
+ Tcl_Obj *orientObj; /* widget orientation */
+ Tcl_Obj *thicknessObj; /* the height/width of the bar */
+ Tcl_Obj *lengthObj; /* default width/height of the bar */
+ Tcl_Obj *reliefObj; /* border relief for this object */
+ Tcl_Obj *borderObj; /* background color */
+ Tcl_Obj *borderWidthObj; /* thickness of the border */
+} PbarElement;
+
+static Ttk_ElementOptionSpec PbarElementOptions[] = {
+ { "-orient", TK_OPTION_ANY, Tk_Offset(PbarElement,orientObj),
+ "horizontal" },
+ { "-thickness", TK_OPTION_PIXELS, Tk_Offset(PbarElement,thicknessObj),
+ DEFAULT_PBAR_THICKNESS },
+ { "-barsize", TK_OPTION_PIXELS, Tk_Offset(PbarElement,lengthObj),
+ DEFAULT_PBAR_LENGTH },
+ { "-pbarrelief", TK_OPTION_RELIEF, Tk_Offset(PbarElement,reliefObj),
+ "raised" },
+ { "-borderwidth", TK_OPTION_PIXELS, Tk_Offset(PbarElement,borderWidthObj),
+ DEFAULT_BORDERWIDTH },
+ { "-background", TK_OPTION_BORDER, Tk_Offset(PbarElement,borderObj),
+ DEFAULT_BACKGROUND },
+ { NULL, 0, 0, NULL }
+};
+
+static void PbarElementSize(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+{
+ PbarElement *pbar = elementRecord;
+ int orient, thickness = 15, length = 30, borderWidth = 2;
+
+ Ttk_GetOrientFromObj(NULL, pbar->orientObj, &orient);
+ Tk_GetPixelsFromObj(NULL, tkwin, pbar->thicknessObj, &thickness);
+ Tk_GetPixelsFromObj(NULL, tkwin, pbar->lengthObj, &length);
+ Tk_GetPixelsFromObj(NULL, tkwin, pbar->borderWidthObj, &borderWidth);
+
+ switch (orient) {
+ case TTK_ORIENT_HORIZONTAL:
+ *widthPtr = length + 2 * borderWidth;
+ *heightPtr = thickness + 2 * borderWidth;
+ break;
+ case TTK_ORIENT_VERTICAL:
+ *widthPtr = thickness + 2 * borderWidth;
+ *heightPtr = length + 2 * borderWidth;
+ break;
+ }
+}
+
+static void PbarElementDraw(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ Drawable d, Ttk_Box b, Ttk_State state)
+{
+ PbarElement *pbar = elementRecord;
+ Tk_3DBorder border = Tk_Get3DBorderFromObj(tkwin, pbar->borderObj);
+ int relief = TK_RELIEF_RAISED, borderWidth = 2;
+
+ Tk_GetPixelsFromObj(NULL, tkwin, pbar->borderWidthObj, &borderWidth);
+ Tk_GetReliefFromObj(NULL, pbar->reliefObj, &relief);
+
+ Tk_Fill3DRectangle(tkwin, d, border,
+ b.x, b.y, b.width, b.height,
+ borderWidth, relief);
+}
+
+static Ttk_ElementSpec PbarElementSpec = {
+ TK_STYLE_VERSION_2,
+ sizeof(PbarElement),
+ PbarElementOptions,
+ PbarElementSize,
+ PbarElementDraw
+};
+
+/*------------------------------------------------------------------------
+ * +++ Notebook tabs and client area.
+ */
+
+typedef struct {
+ Tcl_Obj *borderWidthObj;
+ Tcl_Obj *backgroundObj;
+} TabElement;
+
+static Ttk_ElementOptionSpec TabElementOptions[] = {
+ { "-borderwidth", TK_OPTION_PIXELS,
+ Tk_Offset(TabElement,borderWidthObj),"1" },
+ { "-background", TK_OPTION_BORDER,
+ Tk_Offset(TabElement,backgroundObj), DEFAULT_BACKGROUND },
+ {0,0,0,0}
+};
+
+static void TabElementSize(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+{
+ TabElement *tab = elementRecord;
+ int borderWidth = 1;
+ Tk_GetPixelsFromObj(0, tkwin, tab->borderWidthObj, &borderWidth);
+ paddingPtr->top = paddingPtr->left = paddingPtr->right = borderWidth;
+ paddingPtr->bottom = 0;
+}
+
+static void TabElementDraw(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ Drawable d, Ttk_Box b, unsigned int state)
+{
+ TabElement *tab = elementRecord;
+ Tk_3DBorder border = Tk_Get3DBorderFromObj(tkwin, tab->backgroundObj);
+ int borderWidth = 1;
+ int cut = 2;
+ XPoint pts[6];
+ int n = 0;
+
+ Tcl_GetIntFromObj(NULL, tab->borderWidthObj, &borderWidth);
+
+ if (state & TTK_STATE_SELECTED) {
+ /*
+ * Draw slightly outside of the allocated parcel,
+ * to overwrite the client area border.
+ */
+ b.height += borderWidth;
+ }
+
+ pts[n].x = b.x; pts[n].y = b.y + b.height - 1; ++n;
+ pts[n].x = b.x; pts[n].y = b.y + cut; ++n;
+ pts[n].x = b.x + cut; pts[n].y = b.y; ++n;
+ pts[n].x = b.x + b.width-1-cut; pts[n].y = b.y; ++n;
+ pts[n].x = b.x + b.width-1; pts[n].y = b.y + cut; ++n;
+ pts[n].x = b.x + b.width-1; pts[n].y = b.y + b.height; ++n;
+
+ XFillPolygon(Tk_Display(tkwin), d,
+ Tk_3DBorderGC(tkwin, border, TK_3D_FLAT_GC),
+ pts, 6, Convex, CoordModeOrigin);
+
+#ifndef WIN32
+ /*
+ * Account for whether XDrawLines draws endpoints by platform
+ */
+ --pts[5].y;
+#endif
+
+ while (borderWidth--) {
+ XDrawLines(Tk_Display(tkwin), d,
+ Tk_3DBorderGC(tkwin, border, TK_3D_LIGHT_GC),
+ pts, 4, CoordModeOrigin);
+ XDrawLines(Tk_Display(tkwin), d,
+ Tk_3DBorderGC(tkwin, border, TK_3D_DARK_GC),
+ pts+3, 3, CoordModeOrigin);
+ ++pts[0].x; ++pts[1].x; ++pts[2].x; --pts[4].x; --pts[5].x;
+ ++pts[2].y; ++pts[3].y;
+ }
+
+}
+
+static Ttk_ElementSpec TabElementSpec = {
+ TK_STYLE_VERSION_2,
+ sizeof(TabElement),
+ TabElementOptions,
+ TabElementSize,
+ TabElementDraw
+};
+
+/*
+ * Client area element:
+ * Uses same resources as tab element.
+ */
+typedef TabElement ClientElement;
+#define ClientElementOptions TabElementOptions
+
+static void ClientElementDraw(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ Drawable d, Ttk_Box b, unsigned int state)
+{
+ ClientElement *ce = elementRecord;
+ Tk_3DBorder border = Tk_Get3DBorderFromObj(tkwin, ce->backgroundObj);
+ int borderWidth = 1;
+
+ Tcl_GetIntFromObj(NULL, ce->borderWidthObj, &borderWidth);
+
+ Tk_Fill3DRectangle(tkwin, d, border,
+ b.x, b.y, b.width, b.height, borderWidth,TK_RELIEF_RAISED);
+}
+
+static void ClientElementSize(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+{
+ ClientElement *ce = elementRecord;
+ int borderWidth = 1;
+ Tk_GetPixelsFromObj(0, tkwin, ce->borderWidthObj, &borderWidth);
+ *paddingPtr = Ttk_UniformPadding((short)borderWidth);
+}
+
+static Ttk_ElementSpec ClientElementSpec = {
+ TK_STYLE_VERSION_2,
+ sizeof(ClientElement),
+ ClientElementOptions,
+ ClientElementSize,
+ ClientElementDraw
+};
+
+/*----------------------------------------------------------------------
+ * TtkElements_Init --
+ * Register default element implementations.
+ */
+
+MODULE_SCOPE
+void TtkElements_Init(Tcl_Interp *interp)
+{
+ Ttk_Theme theme = Ttk_GetDefaultTheme(interp);
+
+ /*
+ * Elements:
+ */
+ Ttk_RegisterElement(interp, theme, "background",
+ &BackgroundElementSpec,NULL);
+
+ Ttk_RegisterElement(interp, theme, "fill", &FillElementSpec, NULL);
+ Ttk_RegisterElement(interp, theme, "border", &BorderElementSpec, NULL);
+ Ttk_RegisterElement(interp, theme, "field", &FieldElementSpec, NULL);
+ Ttk_RegisterElement(interp, theme, "focus", &FocusElementSpec, NULL);
+
+ Ttk_RegisterElement(interp, theme, "padding", &PaddingElementSpec, NULL);
+
+ Ttk_RegisterElement(interp, theme, "Checkbutton.indicator",
+ &CheckbuttonIndicatorElementSpec, NULL);
+ Ttk_RegisterElement(interp, theme, "Radiobutton.indicator",
+ &RadiobuttonIndicatorElementSpec, NULL);
+ Ttk_RegisterElement(interp, theme, "Menubutton.indicator",
+ &MenuIndicatorElementSpec, NULL);
+
+ Ttk_RegisterElement(interp, theme, "indicator", &ttkNullElementSpec,NULL);
+
+ Ttk_RegisterElement(interp, theme, "uparrow",
+ &ArrowElementSpec, &ArrowElements[0]);
+ Ttk_RegisterElement(interp, theme, "downarrow",
+ &ArrowElementSpec, &ArrowElements[1]);
+ Ttk_RegisterElement(interp, theme, "leftarrow",
+ &ArrowElementSpec, &ArrowElements[2]);
+ Ttk_RegisterElement(interp, theme, "rightarrow",
+ &ArrowElementSpec, &ArrowElements[3]);
+ Ttk_RegisterElement(interp, theme, "arrow",
+ &ArrowElementSpec, &ArrowElements[0]);
+
+ Ttk_RegisterElement(interp, theme, "trough", &TroughElementSpec, NULL);
+ Ttk_RegisterElement(interp, theme, "thumb", &ThumbElementSpec, NULL);
+ Ttk_RegisterElement(interp, theme, "slider", &SliderElementSpec, NULL);
+ Ttk_RegisterElement(interp, theme, "pbar", &PbarElementSpec, NULL);
+
+ Ttk_RegisterElement(interp, theme, "separator",
+ &SeparatorElementSpec, NULL);
+ Ttk_RegisterElement(interp, theme, "hseparator",
+ &HorizontalSeparatorElementSpec, NULL);
+ Ttk_RegisterElement(interp, theme, "vseparator",
+ &VerticalSeparatorElementSpec, NULL);
+
+ Ttk_RegisterElement(interp, theme, "sizegrip", &SizegripElementSpec, NULL);
+
+ Ttk_RegisterElement(interp, theme, "tab", &TabElementSpec, NULL);
+ Ttk_RegisterElement(interp, theme, "client", &ClientElementSpec, NULL);
+
+ /*
+ * Register "default" as a user-loadable theme (for now):
+ */
+ Tcl_PkgProvide(interp, "ttk::theme::default", TTK_VERSION);
+}
+
+/*EOF*/
diff --git a/generic/ttk/ttkEntry.c b/generic/ttk/ttkEntry.c
new file mode 100644
index 0000000..5c280e4
--- /dev/null
+++ b/generic/ttk/ttkEntry.c
@@ -0,0 +1,2048 @@
+/*
+ * DERIVED FROM: tk/generic/tkEntry.c r1.35.
+ *
+ * Copyright (c) 1990-1994 The Regents of the University of California.
+ * Copyright (c) 1994-1997 Sun Microsystems, Inc.
+ * Copyright (c) 2000 Ajuba Solutions.
+ * Copyright (c) 2002 ActiveState Corporation.
+ * Copyright (c) 2004 Joe English
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <tkInt.h>
+#include <X11/Xatom.h>
+
+#include "ttkTheme.h"
+#include "ttkWidget.h"
+
+/*
+ * Extra bits for core.flags:
+ */
+#define GOT_SELECTION (WIDGET_USER_FLAG<<1)
+#define SYNCING_VARIABLE (WIDGET_USER_FLAG<<2)
+#define VALIDATING (WIDGET_USER_FLAG<<3)
+#define VALIDATION_SET_VALUE (WIDGET_USER_FLAG<<4)
+
+/*
+ * Definitions for -validate option values:
+ */
+typedef enum validateMode {
+ VMODE_ALL, VMODE_KEY, VMODE_FOCUS, VMODE_FOCUSIN, VMODE_FOCUSOUT, VMODE_NONE
+} VMODE;
+
+static const char *const validateStrings[] = {
+ "all", "key", "focus", "focusin", "focusout", "none", NULL
+};
+
+/*
+ * Validation reasons:
+ */
+typedef enum validateReason {
+ VALIDATE_INSERT, VALIDATE_DELETE,
+ VALIDATE_FOCUSIN, VALIDATE_FOCUSOUT,
+ VALIDATE_FORCED
+} VREASON;
+
+static const char *const validateReasonStrings[] = {
+ "key", "key", "focusin", "focusout", "forced", NULL
+};
+
+/*------------------------------------------------------------------------
+ * +++ Entry widget record.
+ *
+ * Dependencies:
+ *
+ * textVariableTrace : textVariableObj
+ *
+ * numBytes,numChars : string
+ * displayString : numChars, showChar
+ * layoutHeight,
+ * layoutWidth,
+ * textLayout : fontObj, displayString
+ * layoutX, layoutY : textLayout, justify, xscroll.first
+ *
+ * Invariants:
+ *
+ * 0 <= insertPos <= numChars
+ * 0 <= selectFirst < selectLast <= numChars || selectFirst == selectLast == -1
+ * displayString points to string if showChar == NULL,
+ * or to malloc'ed storage if showChar != NULL.
+ */
+
+/* Style parameters:
+ */
+typedef struct {
+ Tcl_Obj *foregroundObj; /* Foreground color for normal text */
+ Tcl_Obj *backgroundObj; /* Entry widget background color */
+ Tcl_Obj *selBorderObj; /* Border and background for selection */
+ Tcl_Obj *selBorderWidthObj; /* Width of selection border */
+ Tcl_Obj *selForegroundObj; /* Foreground color for selected text */
+ Tcl_Obj *insertColorObj; /* Color of insertion cursor */
+ Tcl_Obj *insertWidthObj; /* Insert cursor width */
+} EntryStyleData;
+
+typedef struct {
+ /*
+ * Internal state:
+ */
+ char *string; /* Storage for string (malloced) */
+ int numBytes; /* Length of string in bytes. */
+ int numChars; /* Length of string in characters. */
+
+ int insertPos; /* Insert index */
+ int selectFirst; /* Index of start of selection, or -1 */
+ int selectLast; /* Index of end of selection, or -1 */
+
+ Scrollable xscroll; /* Current scroll position */
+ ScrollHandle xscrollHandle;
+
+ /*
+ * Options managed by Tk_SetOptions:
+ */
+ Tcl_Obj *textVariableObj; /* Name of linked variable */
+ int exportSelection; /* Tie internal selection to X selection? */
+
+ VMODE validate; /* Validation mode */
+ char *validateCmd; /* Validation script template */
+ char *invalidCmd; /* Invalid callback script template */
+
+ char *showChar; /* Used to derive displayString */
+
+ Tcl_Obj *fontObj; /* Text font to use */
+ Tcl_Obj *widthObj; /* Desired width of window (in avgchars) */
+ Tk_Justify justify; /* Text justification */
+
+ EntryStyleData styleData; /* Display style data (widget options) */
+ EntryStyleData styleDefaults;/* Style defaults (fallback values) */
+
+ Tcl_Obj *stateObj; /* Compatibility option -- see CheckStateObj */
+
+ /*
+ * Derived resources:
+ */
+ Ttk_TraceHandle *textVariableTrace;
+
+ char *displayString; /* String to use when displaying */
+ Tk_TextLayout textLayout; /* Cached text layout information. */
+ int layoutWidth; /* textLayout width */
+ int layoutHeight; /* textLayout height */
+
+ int layoutX, layoutY; /* Origin for text layout. */
+
+} EntryPart;
+
+typedef struct {
+ WidgetCore core;
+ EntryPart entry;
+} Entry;
+
+/*
+ * Extra mask bits for Tk_SetOptions()
+ */
+#define STATE_CHANGED (0x100) /* -state option changed */
+#define TEXTVAR_CHANGED (0x200) /* -textvariable option changed */
+#define SCROLLCMD_CHANGED (0x400) /* -xscrollcommand option changed */
+
+/*
+ * Default option values:
+ */
+#define DEF_SELECT_BG "#000000"
+#define DEF_SELECT_FG "#ffffff"
+#define DEF_INSERT_BG "black"
+#define DEF_ENTRY_WIDTH "20"
+#define DEF_ENTRY_FONT "TkTextFont"
+#define DEF_LIST_HEIGHT "10"
+
+static Tk_OptionSpec EntryOptionSpecs[] = {
+ {TK_OPTION_BOOLEAN, "-exportselection", "exportSelection",
+ "ExportSelection", "1", -1, Tk_Offset(Entry, entry.exportSelection),
+ 0,0,0 },
+ {TK_OPTION_FONT, "-font", "font", "Font",
+ DEF_ENTRY_FONT, Tk_Offset(Entry, entry.fontObj),-1,
+ 0,0,GEOMETRY_CHANGED},
+ {TK_OPTION_STRING, "-invalidcommand", "invalidCommand", "InvalidCommand",
+ NULL, -1, Tk_Offset(Entry, entry.invalidCmd),
+ TK_OPTION_NULL_OK, 0, 0},
+ {TK_OPTION_JUSTIFY, "-justify", "justify", "Justify",
+ "left", -1, Tk_Offset(Entry, entry.justify),
+ 0, 0, GEOMETRY_CHANGED},
+ {TK_OPTION_STRING, "-show", "show", "Show",
+ NULL, -1, Tk_Offset(Entry, entry.showChar),
+ TK_OPTION_NULL_OK, 0, 0},
+ {TK_OPTION_STRING, "-state", "state", "State",
+ "normal", Tk_Offset(Entry, entry.stateObj), -1,
+ 0,0,STATE_CHANGED},
+ {TK_OPTION_STRING, "-textvariable", "textVariable", "Variable",
+ NULL, Tk_Offset(Entry, entry.textVariableObj), -1,
+ TK_OPTION_NULL_OK,0,TEXTVAR_CHANGED},
+ {TK_OPTION_STRING_TABLE, "-validate", "validate", "Validate",
+ "none", -1, Tk_Offset(Entry, entry.validate),
+ 0, (ClientData) validateStrings, 0},
+ {TK_OPTION_STRING, "-validatecommand", "validateCommand", "ValidateCommand",
+ NULL, -1, Tk_Offset(Entry, entry.validateCmd),
+ TK_OPTION_NULL_OK, 0, 0},
+ {TK_OPTION_INT, "-width", "width", "Width",
+ DEF_ENTRY_WIDTH, Tk_Offset(Entry, entry.widthObj), -1,
+ 0,0,GEOMETRY_CHANGED},
+ {TK_OPTION_STRING, "-xscrollcommand", "xScrollCommand", "ScrollCommand",
+ NULL, -1, Tk_Offset(Entry, entry.xscroll.scrollCmd),
+ TK_OPTION_NULL_OK, 0, SCROLLCMD_CHANGED},
+
+ /* EntryStyleData options:
+ */
+ {TK_OPTION_COLOR, "-foreground", "textColor", "TextColor",
+ NULL, Tk_Offset(Entry, entry.styleData.foregroundObj), -1,
+ TK_OPTION_NULL_OK,0,0},
+ {TK_OPTION_COLOR, "-background", "windowColor", "WindowColor",
+ NULL, Tk_Offset(Entry, entry.styleData.backgroundObj), -1,
+ TK_OPTION_NULL_OK,0,0},
+
+ WIDGET_TAKEFOCUS_TRUE,
+ WIDGET_INHERIT_OPTIONS(ttkCoreOptionSpecs)
+};
+
+/*------------------------------------------------------------------------
+ * +++ EntryStyleData management.
+ * This is still more awkward than it should be;
+ * it should be able to use the Element API instead.
+ */
+
+/* EntryInitStyleDefaults --
+ * Initialize EntryStyleData record to fallback values.
+ */
+static void EntryInitStyleDefaults(EntryStyleData *es)
+{
+#define INIT(member, value) \
+ es->member = Tcl_NewStringObj(value, -1); \
+ Tcl_IncrRefCount(es->member);
+ INIT(foregroundObj, DEFAULT_FOREGROUND)
+ INIT(selBorderObj, DEF_SELECT_BG)
+ INIT(selForegroundObj, DEF_SELECT_FG)
+ INIT(insertColorObj, DEFAULT_FOREGROUND)
+ INIT(selBorderWidthObj, "0")
+ INIT(insertWidthObj, "1")
+#undef INIT
+}
+
+static void EntryFreeStyleDefaults(EntryStyleData *es)
+{
+ Tcl_DecrRefCount(es->foregroundObj);
+ Tcl_DecrRefCount(es->selBorderObj);
+ Tcl_DecrRefCount(es->selForegroundObj);
+ Tcl_DecrRefCount(es->insertColorObj);
+ Tcl_DecrRefCount(es->selBorderWidthObj);
+ Tcl_DecrRefCount(es->insertWidthObj);
+}
+
+/*
+ * EntryInitStyleData --
+ * Look up style-specific data for an entry widget.
+ */
+static void EntryInitStyleData(Entry *entryPtr, EntryStyleData *es)
+{
+ Ttk_State state = entryPtr->core.state;
+ Ttk_ResourceCache cache = Ttk_GetResourceCache(entryPtr->core.interp);
+ Tk_Window tkwin = entryPtr->core.tkwin;
+ Tcl_Obj *tmp;
+
+ /* Initialize to fallback values:
+ */
+ *es = entryPtr->entry.styleDefaults;
+
+# define INIT(member, name) \
+ if ((tmp=Ttk_QueryOption(entryPtr->core.layout,name,state))) \
+ es->member=tmp;
+ INIT(foregroundObj, "-foreground");
+ INIT(selBorderObj, "-selectbackground")
+ INIT(selBorderWidthObj, "-selectborderwidth")
+ INIT(selForegroundObj, "-selectforeground")
+ INIT(insertColorObj, "-insertcolor")
+ INIT(insertWidthObj, "-insertwidth")
+#undef INIT
+
+ /* Reacquire color & border resources from resource cache.
+ */
+ es->foregroundObj = Ttk_UseColor(cache, tkwin, es->foregroundObj);
+ es->selForegroundObj = Ttk_UseColor(cache, tkwin, es->selForegroundObj);
+ es->insertColorObj = Ttk_UseColor(cache, tkwin, es->insertColorObj);
+ es->selBorderObj = Ttk_UseBorder(cache, tkwin, es->selBorderObj);
+}
+
+/*------------------------------------------------------------------------
+ * +++ Resource management.
+ */
+
+/* EntryDisplayString --
+ * Return a malloc'ed string consisting of 'numChars' copies
+ * of (the first character in the string) 'showChar'.
+ * Used to compute the displayString if -show is non-NULL.
+ */
+static char *EntryDisplayString(const char *showChar, int numChars)
+{
+ char *displayString, *p;
+ int size;
+ Tcl_UniChar ch;
+ char buf[TCL_UTF_MAX];
+
+ Tcl_UtfToUniChar(showChar, &ch);
+ size = Tcl_UniCharToUtf(ch, buf);
+ p = displayString = ckalloc(numChars * size + 1);
+
+ while (numChars--) {
+ p += Tcl_UniCharToUtf(ch, p);
+ }
+ *p = '\0';
+
+ return displayString;
+}
+
+/* EntryUpdateTextLayout --
+ * Recompute textLayout, layoutWidth, and layoutHeight
+ * from displayString and fontObj.
+ */
+static void EntryUpdateTextLayout(Entry *entryPtr)
+{
+ Tk_FreeTextLayout(entryPtr->entry.textLayout);
+ entryPtr->entry.textLayout = Tk_ComputeTextLayout(
+ Tk_GetFontFromObj(entryPtr->core.tkwin, entryPtr->entry.fontObj),
+ entryPtr->entry.displayString, entryPtr->entry.numChars,
+ 0/*wraplength*/, entryPtr->entry.justify, TK_IGNORE_NEWLINES,
+ &entryPtr->entry.layoutWidth, &entryPtr->entry.layoutHeight);
+}
+
+/* EntryEditable --
+ * Returns 1 if the entry widget accepts user changes, 0 otherwise
+ */
+static int
+EntryEditable(Entry *entryPtr)
+{
+ return !(entryPtr->core.state & (TTK_STATE_DISABLED|TTK_STATE_READONLY));
+}
+
+/*------------------------------------------------------------------------
+ * +++ Selection management.
+ */
+
+/* EntryFetchSelection --
+ * Selection handler for entry widgets.
+ */
+static int
+EntryFetchSelection(
+ ClientData clientData, int offset, char *buffer, int maxBytes)
+{
+ Entry *entryPtr = (Entry *) clientData;
+ size_t byteCount;
+ const char *string;
+ const char *selStart, *selEnd;
+
+ if (entryPtr->entry.selectFirst < 0 || !entryPtr->entry.exportSelection) {
+ return -1;
+ }
+ string = entryPtr->entry.displayString;
+
+ selStart = Tcl_UtfAtIndex(string, entryPtr->entry.selectFirst);
+ selEnd = Tcl_UtfAtIndex(selStart,
+ entryPtr->entry.selectLast - entryPtr->entry.selectFirst);
+ byteCount = selEnd - selStart - offset;
+ if (byteCount > (size_t)maxBytes) {
+ /* @@@POSSIBLE BUG: Can transfer partial UTF-8 sequences. Is this OK? */
+ byteCount = maxBytes;
+ }
+ if (byteCount <= 0) {
+ return 0;
+ }
+ memcpy(buffer, selStart + offset, byteCount);
+ buffer[byteCount] = '\0';
+ return byteCount;
+}
+
+/* EntryLostSelection --
+ * Tk_LostSelProc for Entry widgets; called when an entry
+ * loses ownership of the selection.
+ */
+static void EntryLostSelection(ClientData clientData)
+{
+ Entry *entryPtr = (Entry *) clientData;
+ entryPtr->core.flags &= ~GOT_SELECTION;
+ entryPtr->entry.selectFirst = entryPtr->entry.selectLast = -1;
+ TtkRedisplayWidget(&entryPtr->core);
+}
+
+/* EntryOwnSelection --
+ * Assert ownership of the PRIMARY selection,
+ * if -exportselection set and selection is present.
+ */
+static void EntryOwnSelection(Entry *entryPtr)
+{
+ if (entryPtr->entry.exportSelection
+ && !(entryPtr->core.flags & GOT_SELECTION)) {
+ Tk_OwnSelection(entryPtr->core.tkwin, XA_PRIMARY, EntryLostSelection,
+ (ClientData) entryPtr);
+ entryPtr->core.flags |= GOT_SELECTION;
+ }
+}
+
+/*------------------------------------------------------------------------
+ * +++ Validation.
+ */
+
+/* ExpandPercents --
+ * Expand an entry validation script template (-validatecommand
+ * or -invalidcommand).
+ */
+static void
+ExpandPercents(
+ Entry *entryPtr, /* Entry that needs validation. */
+ const char *template, /* Script template */
+ const char *new, /* Potential new value of entry string */
+ int index, /* index of insert/delete */
+ int count, /* #changed characters */
+ VREASON reason, /* Reason for change */
+ Tcl_DString *dsPtr) /* Result of %-substitutions */
+{
+ int spaceNeeded, cvtFlags;
+ int number, length;
+ const char *string;
+ int stringLength;
+ Tcl_UniChar ch;
+ char numStorage[2*TCL_INTEGER_SPACE];
+
+ while (*template) {
+ /* Find everything up to the next % character and append it
+ * to the result string.
+ */
+ string = Tcl_UtfFindFirst(template, '%');
+ if (string == NULL) {
+ /* No more %-sequences to expand.
+ * Copy the rest of the template.
+ */
+ Tcl_DStringAppend(dsPtr, template, -1);
+ return;
+ }
+ if (string != template) {
+ Tcl_DStringAppend(dsPtr, template, string - template);
+ template = string;
+ }
+
+ /* There's a percent sequence here. Process it.
+ */
+ ++template; /* skip over % */
+ if (*template != '\0') {
+ template += Tcl_UtfToUniChar(template, &ch);
+ } else {
+ ch = '%';
+ }
+
+ stringLength = -1;
+ switch (ch) {
+ case 'd': /* Type of call that caused validation */
+ if (reason == VALIDATE_INSERT) {
+ number = 1;
+ } else if (reason == VALIDATE_DELETE) {
+ number = 0;
+ } else {
+ number = -1;
+ }
+ sprintf(numStorage, "%d", number);
+ string = numStorage;
+ break;
+ case 'i': /* index of insert/delete */
+ sprintf(numStorage, "%d", index);
+ string = numStorage;
+ break;
+ case 'P': /* 'Peeked' new value of the string */
+ string = new;
+ break;
+ case 's': /* Current string value */
+ string = entryPtr->entry.string;
+ break;
+ case 'S': /* string to be inserted/deleted, if any */
+ if (reason == VALIDATE_INSERT) {
+ string = Tcl_UtfAtIndex(new, index);
+ stringLength = Tcl_UtfAtIndex(string, count) - string;
+ } else if (reason == VALIDATE_DELETE) {
+ string = Tcl_UtfAtIndex(entryPtr->entry.string, index);
+ stringLength = Tcl_UtfAtIndex(string, count) - string;
+ } else {
+ string = "";
+ stringLength = 0;
+ }
+ break;
+ case 'v': /* type of validation currently set */
+ string = validateStrings[entryPtr->entry.validate];
+ break;
+ case 'V': /* type of validation in effect */
+ string = validateReasonStrings[reason];
+ break;
+ case 'W': /* widget name */
+ string = Tk_PathName(entryPtr->core.tkwin);
+ break;
+ default:
+ length = Tcl_UniCharToUtf(ch, numStorage);
+ numStorage[length] = '\0';
+ string = numStorage;
+ break;
+ }
+
+ spaceNeeded = Tcl_ScanCountedElement(string, stringLength, &cvtFlags);
+ length = Tcl_DStringLength(dsPtr);
+ Tcl_DStringSetLength(dsPtr, length + spaceNeeded);
+ spaceNeeded = Tcl_ConvertCountedElement(string, stringLength,
+ Tcl_DStringValue(dsPtr) + length,
+ cvtFlags | TCL_DONT_USE_BRACES);
+ Tcl_DStringSetLength(dsPtr, length + spaceNeeded);
+ }
+}
+
+/* RunValidationScript --
+ * Build and evaluate an entry validation script.
+ * If the script raises an error, disable validation
+ * by setting '-validate none'
+ */
+static int RunValidationScript(
+ Tcl_Interp *interp, /* Interpreter to use */
+ Entry *entryPtr, /* Entry being validated */
+ const char *template, /* Script template */
+ const char *optionName, /* "-validatecommand", "-invalidcommand" */
+ const char *new, /* Potential new value of entry string */
+ int index, /* index of insert/delete */
+ int count, /* #changed characters */
+ VREASON reason) /* Reason for change */
+{
+ Tcl_DString script;
+ int code;
+
+ Tcl_DStringInit(&script);
+ ExpandPercents(entryPtr, template, new, index, count, reason, &script);
+ code = Tcl_EvalEx(interp,
+ Tcl_DStringValue(&script), Tcl_DStringLength(&script),
+ TCL_EVAL_GLOBAL);
+ Tcl_DStringFree(&script);
+ if (WidgetDestroyed(&entryPtr->core))
+ return TCL_ERROR;
+
+ if (code != TCL_OK && code != TCL_RETURN) {
+ Tcl_AddErrorInfo(interp, "\n\t(in ");
+ Tcl_AddErrorInfo(interp, optionName);
+ Tcl_AddErrorInfo(interp, " validation command executed by ");
+ Tcl_AddErrorInfo(interp, Tk_PathName(entryPtr->core.tkwin));
+ Tcl_AddErrorInfo(interp, ")");
+ entryPtr->entry.validate = VMODE_NONE;
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/* EntryNeedsValidation --
+ * Determine whether the specified VREASON should trigger validation
+ * in the current VMODE.
+ */
+static int EntryNeedsValidation(VMODE vmode, VREASON reason)
+{
+ return (reason == VALIDATE_FORCED)
+ || (vmode == VMODE_ALL)
+ || (reason == VALIDATE_FOCUSIN
+ && (vmode == VMODE_FOCUSIN || vmode == VMODE_FOCUS))
+ || (reason == VALIDATE_FOCUSOUT
+ && (vmode == VMODE_FOCUSOUT || vmode == VMODE_FOCUS))
+ || (reason == VALIDATE_INSERT && vmode == VMODE_KEY)
+ || (reason == VALIDATE_DELETE && vmode == VMODE_KEY)
+ ;
+}
+
+/* EntryValidateChange --
+ * Validate a proposed change to the entry widget's value if required.
+ * Call the -invalidcommand if validation fails.
+ *
+ * Returns:
+ * TCL_OK if the change is accepted
+ * TCL_BREAK if the change is rejected
+ * TCL_ERROR if any errors occured
+ *
+ * The change will be rejected if -validatecommand returns 0,
+ * or if -validatecommand or -invalidcommand modifies the value.
+ */
+static int
+EntryValidateChange(
+ Entry *entryPtr, /* Entry that needs validation. */
+ const char *newValue, /* Potential new value of entry string */
+ int index, /* index of insert/delete, -1 otherwise */
+ int count, /* #changed characters */
+ VREASON reason) /* Reason for change */
+{
+ Tcl_Interp *interp = entryPtr->core.interp;
+ VMODE vmode = entryPtr->entry.validate;
+ int code, change_ok;
+
+ if ( (entryPtr->entry.validateCmd == NULL)
+ || (entryPtr->core.flags & VALIDATING)
+ || !EntryNeedsValidation(vmode, reason) )
+ {
+ return TCL_OK;
+ }
+
+ entryPtr->core.flags |= VALIDATING;
+
+ /* Run -validatecommand and check return value:
+ */
+ code = RunValidationScript(interp, entryPtr,
+ entryPtr->entry.validateCmd, "-validatecommand",
+ newValue, index, count, reason);
+ if (code != TCL_OK) {
+ goto done;
+ }
+
+ code = Tcl_GetBooleanFromObj(interp,Tcl_GetObjResult(interp), &change_ok);
+ if (code != TCL_OK) {
+ entryPtr->entry.validate = VMODE_NONE; /* Disable validation */
+ Tcl_AddErrorInfo(interp,
+ "\n(validation command did not return valid boolean)");
+ goto done;
+ }
+
+ /* Run the -invalidcommand if validation failed:
+ */
+ if (!change_ok && entryPtr->entry.invalidCmd != NULL) {
+ code = RunValidationScript(interp, entryPtr,
+ entryPtr->entry.invalidCmd, "-invalidcommand",
+ newValue, index, count, reason);
+ if (code != TCL_OK) {
+ goto done;
+ }
+ }
+
+ /* Reject the pending change if validation failed
+ * or if a validation script changed the value.
+ */
+ if (!change_ok || (entryPtr->core.flags & VALIDATION_SET_VALUE)) {
+ code = TCL_BREAK;
+ }
+
+done:
+ entryPtr->core.flags &= ~(VALIDATING|VALIDATION_SET_VALUE);
+ return code;
+}
+
+/* EntryRevalidate --
+ * Revalidate the current value of an entry widget,
+ * update the TTK_STATE_INVALID bit.
+ *
+ * Returns:
+ * TCL_OK if valid, TCL_BREAK if invalid, TCL_ERROR on error.
+ */
+static int EntryRevalidate(Tcl_Interp *interp, Entry *entryPtr, VREASON reason)
+{
+ int code = EntryValidateChange(
+ entryPtr, entryPtr->entry.string, -1,0, reason);
+
+ if (code == TCL_BREAK) {
+ TtkWidgetChangeState(&entryPtr->core, TTK_STATE_INVALID, 0);
+ } else if (code == TCL_OK) {
+ TtkWidgetChangeState(&entryPtr->core, 0, TTK_STATE_INVALID);
+ }
+
+ return code;
+}
+
+/* EntryRevalidateBG --
+ * Revalidate in the background (called from event handler).
+ */
+static void EntryRevalidateBG(Entry *entryPtr, VREASON reason)
+{
+ Tcl_Interp *interp = entryPtr->core.interp;
+ if (EntryRevalidate(interp, entryPtr, reason) == TCL_ERROR) {
+ Tcl_BackgroundError(interp);
+ }
+}
+
+/*------------------------------------------------------------------------
+ * +++ Entry widget modification.
+ */
+
+/* AdjustIndex --
+ * Adjust index to account for insertion (nChars > 0)
+ * or deletion (nChars < 0) at specified index.
+ */
+static int AdjustIndex(int i0, int index, int nChars)
+{
+ if (i0 >= index) {
+ i0 += nChars;
+ if (i0 < index) { /* index was inside deleted range */
+ i0 = index;
+ }
+ }
+ return i0;
+}
+
+/* AdjustIndices --
+ * Adjust all internal entry indexes to account for change.
+ * Note that insertPos, and selectFirst have "right gravity",
+ * while leftIndex (=xscroll.first) and selectLast have "left gravity".
+ */
+static void AdjustIndices(Entry *entryPtr, int index, int nChars)
+{
+ EntryPart *e = &entryPtr->entry;
+ int g = nChars > 0; /* left gravity adjustment */
+
+ e->insertPos = AdjustIndex(e->insertPos, index, nChars);
+ e->selectFirst = AdjustIndex(e->selectFirst, index, nChars);
+ e->selectLast = AdjustIndex(e->selectLast, index+g, nChars);
+ e->xscroll.first= AdjustIndex(e->xscroll.first, index+g, nChars);
+
+ if (e->selectLast <= e->selectFirst)
+ e->selectFirst = e->selectLast = -1;
+}
+
+/* EntryStoreValue --
+ * Replace the contents of a text entry with a given value,
+ * recompute dependent resources, and schedule a redisplay.
+ *
+ * See also: EntrySetValue().
+ */
+static void
+EntryStoreValue(Entry *entryPtr, const char *value)
+{
+ size_t numBytes = strlen(value);
+ int numChars = Tcl_NumUtfChars(value, numBytes);
+
+ if (entryPtr->core.flags & VALIDATING)
+ entryPtr->core.flags |= VALIDATION_SET_VALUE;
+
+ /* Make sure all indices remain in bounds:
+ */
+ if (numChars < entryPtr->entry.numChars)
+ AdjustIndices(entryPtr, numChars, numChars - entryPtr->entry.numChars);
+
+ /* Free old value:
+ */
+ if (entryPtr->entry.displayString != entryPtr->entry.string)
+ ckfree(entryPtr->entry.displayString);
+ ckfree(entryPtr->entry.string);
+
+ /* Store new value:
+ */
+ entryPtr->entry.string = ckalloc(numBytes + 1);
+ strcpy(entryPtr->entry.string, value);
+ entryPtr->entry.numBytes = numBytes;
+ entryPtr->entry.numChars = numChars;
+
+ entryPtr->entry.displayString
+ = entryPtr->entry.showChar
+ ? EntryDisplayString(entryPtr->entry.showChar, numChars)
+ : entryPtr->entry.string
+ ;
+
+ /* Update layout, schedule redisplay:
+ */
+ EntryUpdateTextLayout(entryPtr);
+ TtkRedisplayWidget(&entryPtr->core);
+}
+
+/* EntrySetValue --
+ * Stores a new value in the entry widget and updates the
+ * linked -textvariable, if any. The write trace on the
+ * text variable is temporarily disabled; however, other
+ * write traces may change the value of the variable.
+ * If so, the widget is updated again with the new value.
+ *
+ * Returns:
+ * TCL_OK if successful, TCL_ERROR otherwise.
+ */
+static int EntrySetValue(Entry *entryPtr, const char *value)
+{
+ EntryStoreValue(entryPtr, value);
+
+ if (entryPtr->entry.textVariableObj) {
+ const char *textVarName =
+ Tcl_GetString(entryPtr->entry.textVariableObj);
+ if (textVarName && *textVarName) {
+ entryPtr->core.flags |= SYNCING_VARIABLE;
+ value = Tcl_SetVar(entryPtr->core.interp, textVarName,
+ value, TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG);
+ entryPtr->core.flags &= ~SYNCING_VARIABLE;
+ if (!value || WidgetDestroyed(&entryPtr->core)) {
+ return TCL_ERROR;
+ } else if (strcmp(value, entryPtr->entry.string) != 0) {
+ /* Some write trace has changed the variable value.
+ */
+ EntryStoreValue(entryPtr, value);
+ }
+ }
+ }
+
+ return TCL_OK;
+}
+
+/* EntryTextVariableTrace --
+ * Variable trace procedure for entry -textvariable
+ */
+static void EntryTextVariableTrace(void *recordPtr, const char *value)
+{
+ Entry *entryPtr = recordPtr;
+
+ if (WidgetDestroyed(&entryPtr->core)) {
+ return;
+ }
+
+ if (entryPtr->core.flags & SYNCING_VARIABLE) {
+ /* Trace was fired due to Tcl_SetVar call in EntrySetValue.
+ * Don't do anything.
+ */
+ return;
+ }
+
+ EntryStoreValue(entryPtr, value ? value : "");
+}
+
+/*------------------------------------------------------------------------
+ * +++ Insertion and deletion.
+ */
+
+/* InsertChars --
+ * Add new characters to an entry widget.
+ */
+static int
+InsertChars(
+ Entry *entryPtr, /* Entry that is to get the new elements. */
+ int index, /* Insert before this index */
+ const char *value) /* New characters to add */
+{
+ char *string = entryPtr->entry.string;
+ size_t byteIndex = Tcl_UtfAtIndex(string, index) - string;
+ size_t byteCount = strlen(value);
+ int charsAdded = Tcl_NumUtfChars(value, byteCount);
+ size_t newByteCount = entryPtr->entry.numBytes + byteCount + 1;
+ char *new;
+ int code;
+
+ if (byteCount == 0) {
+ return TCL_OK;
+ }
+
+ new = ckalloc(newByteCount);
+ memcpy(new, string, byteIndex);
+ strcpy(new + byteIndex, value);
+ strcpy(new + byteIndex + byteCount, string + byteIndex);
+
+ code = EntryValidateChange(
+ entryPtr, new, index, charsAdded, VALIDATE_INSERT);
+
+ if (code == TCL_OK) {
+ AdjustIndices(entryPtr, index, charsAdded);
+ code = EntrySetValue(entryPtr, new);
+ } else if (code == TCL_BREAK) {
+ code = TCL_OK;
+ }
+
+ ckfree(new);
+ return code;
+}
+
+/* DeleteChars --
+ * Remove one or more characters from an entry widget.
+ */
+static int
+DeleteChars(
+ Entry *entryPtr, /* Entry widget to modify. */
+ int index, /* Index of first character to delete. */
+ int count) /* How many characters to delete. */
+{
+ char *string = entryPtr->entry.string;
+ size_t byteIndex, byteCount, newByteCount;
+ char *new;
+ int code;
+
+ if (index < 0) {
+ index = 0;
+ }
+ if (count > entryPtr->entry.numChars - index) {
+ count = entryPtr->entry.numChars - index;
+ }
+ if (count <= 0) {
+ return TCL_OK;
+ }
+
+ byteIndex = Tcl_UtfAtIndex(string, index) - string;
+ byteCount = Tcl_UtfAtIndex(string+byteIndex, count) - (string+byteIndex);
+
+ newByteCount = entryPtr->entry.numBytes + 1 - byteCount;
+ new = ckalloc(newByteCount);
+ memcpy(new, string, byteIndex);
+ strcpy(new + byteIndex, string + byteIndex + byteCount);
+
+ code = EntryValidateChange(
+ entryPtr, new, index, count, VALIDATE_DELETE);
+
+ if (code == TCL_OK) {
+ AdjustIndices(entryPtr, index, -count);
+ code = EntrySetValue(entryPtr, new);
+ } else if (code == TCL_BREAK) {
+ code = TCL_OK;
+ }
+ ckfree(new);
+
+ return code;
+}
+
+/*------------------------------------------------------------------------
+ * +++ Event handler.
+ */
+
+/* EntryEventProc --
+ * Extra event handling for entry widgets:
+ * Triggers validation on FocusIn and FocusOut events.
+ */
+#define EntryEventMask (FocusChangeMask)
+static void
+EntryEventProc(ClientData clientData, XEvent *eventPtr)
+{
+ Entry *entryPtr = (Entry *) clientData;
+
+ Tcl_Preserve(clientData);
+ switch (eventPtr->type) {
+ case DestroyNotify:
+ Tk_DeleteEventHandler(entryPtr->core.tkwin,
+ EntryEventMask, EntryEventProc, clientData);
+ break;
+ case FocusIn:
+ EntryRevalidateBG(entryPtr, VALIDATE_FOCUSIN);
+ break;
+ case FocusOut:
+ EntryRevalidateBG(entryPtr, VALIDATE_FOCUSOUT);
+ break;
+ }
+ Tcl_Release(clientData);
+}
+
+/*------------------------------------------------------------------------
+ * +++ Initialization and cleanup.
+ */
+
+static void
+EntryInitialize(Tcl_Interp *interp, void *recordPtr)
+{
+ Entry *entryPtr = recordPtr;
+
+ Tk_CreateEventHandler(
+ entryPtr->core.tkwin, EntryEventMask, EntryEventProc, entryPtr);
+ Tk_CreateSelHandler(entryPtr->core.tkwin, XA_PRIMARY, XA_STRING,
+ EntryFetchSelection, (ClientData) entryPtr, XA_STRING);
+ TtkBlinkCursor(&entryPtr->core);
+
+ entryPtr->entry.string = ckalloc(1);
+ *entryPtr->entry.string = '\0';
+ entryPtr->entry.displayString = entryPtr->entry.string;
+ entryPtr->entry.textVariableTrace = 0;
+ entryPtr->entry.numBytes = entryPtr->entry.numChars = 0;
+
+ EntryInitStyleDefaults(&entryPtr->entry.styleDefaults);
+
+ entryPtr->entry.xscrollHandle =
+ TtkCreateScrollHandle(&entryPtr->core, &entryPtr->entry.xscroll);
+
+ entryPtr->entry.insertPos = 0;
+ entryPtr->entry.selectFirst = -1;
+ entryPtr->entry.selectLast = -1;
+}
+
+static void
+EntryCleanup(void *recordPtr)
+{
+ Entry *entryPtr = recordPtr;
+
+ if (entryPtr->entry.textVariableTrace)
+ Ttk_UntraceVariable(entryPtr->entry.textVariableTrace);
+
+ TtkFreeScrollHandle(entryPtr->entry.xscrollHandle);
+
+ EntryFreeStyleDefaults(&entryPtr->entry.styleDefaults);
+
+ Tk_DeleteSelHandler(entryPtr->core.tkwin, XA_PRIMARY, XA_STRING);
+
+ Tk_FreeTextLayout(entryPtr->entry.textLayout);
+ if (entryPtr->entry.displayString != entryPtr->entry.string)
+ ckfree(entryPtr->entry.displayString);
+ ckfree(entryPtr->entry.string);
+}
+
+/* EntryConfigure --
+ * Configure hook for Entry widgets.
+ */
+static int EntryConfigure(Tcl_Interp *interp, void *recordPtr, int mask)
+{
+ Entry *entryPtr = recordPtr;
+ Tcl_Obj *textVarName = entryPtr->entry.textVariableObj;
+ Ttk_TraceHandle *vt = 0;
+
+ if (mask & TEXTVAR_CHANGED) {
+ if (textVarName && *Tcl_GetString(textVarName)) {
+ vt = Ttk_TraceVariable(interp,
+ textVarName,EntryTextVariableTrace,entryPtr);
+ if (!vt) return TCL_ERROR;
+ }
+ }
+
+ if (TtkCoreConfigure(interp, recordPtr, mask) != TCL_OK) {
+ if (vt) Ttk_UntraceVariable(vt);
+ return TCL_ERROR;
+ }
+
+ /* Update derived resources:
+ */
+ if (mask & TEXTVAR_CHANGED) {
+ if (entryPtr->entry.textVariableTrace)
+ Ttk_UntraceVariable(entryPtr->entry.textVariableTrace);
+ entryPtr->entry.textVariableTrace = vt;
+ }
+
+ /* Claim the selection, in case we've suddenly started exporting it.
+ */
+ if (entryPtr->entry.exportSelection && entryPtr->entry.selectFirst != -1) {
+ EntryOwnSelection(entryPtr);
+ }
+
+ /* Handle -state compatibility option:
+ */
+ if (mask & STATE_CHANGED) {
+ TtkCheckStateOption(&entryPtr->core, entryPtr->entry.stateObj);
+ }
+
+ /* Force scrollbar update if needed:
+ */
+ if (mask & SCROLLCMD_CHANGED) {
+ TtkScrollbarUpdateRequired(entryPtr->entry.xscrollHandle);
+ }
+
+ /* Recompute the displayString, in case showChar changed:
+ */
+ if (entryPtr->entry.displayString != entryPtr->entry.string)
+ ckfree(entryPtr->entry.displayString);
+
+ entryPtr->entry.displayString
+ = entryPtr->entry.showChar
+ ? EntryDisplayString(entryPtr->entry.showChar, entryPtr->entry.numChars)
+ : entryPtr->entry.string
+ ;
+
+ /* Update textLayout:
+ */
+ EntryUpdateTextLayout(entryPtr);
+ return TCL_OK;
+}
+
+/* EntryPostConfigure --
+ * Post-configuration hook for entry widgets.
+ */
+static int EntryPostConfigure(Tcl_Interp *interp, void *recordPtr, int mask)
+{
+ Entry *entryPtr = recordPtr;
+ int status = TCL_OK;
+
+ if ((mask & TEXTVAR_CHANGED) && entryPtr->entry.textVariableTrace != NULL) {
+ status = Ttk_FireTrace(entryPtr->entry.textVariableTrace);
+ }
+
+ return status;
+}
+
+/*------------------------------------------------------------------------
+ * +++ Layout and display.
+ */
+
+/* EntryCharPosition --
+ * Return the X coordinate of the specified character index.
+ * Precondition: textLayout and layoutX up-to-date.
+ */
+static int
+EntryCharPosition(Entry *entryPtr, int index)
+{
+ int xPos;
+ Tk_CharBbox(entryPtr->entry.textLayout, index, &xPos, NULL, NULL, NULL);
+ return xPos + entryPtr->entry.layoutX;
+}
+
+/* EntryDoLayout --
+ * Layout hook for entry widgets.
+ *
+ * Determine position of textLayout based on xscroll.first, justify,
+ * and display area.
+ *
+ * Recalculates layoutX, layoutY, and rightIndex,
+ * and updates xscroll accordingly.
+ * May adjust xscroll.first to ensure the maximum #characters are onscreen.
+ */
+static void
+EntryDoLayout(void *recordPtr)
+{
+ Entry *entryPtr = recordPtr;
+ WidgetCore *corePtr = &entryPtr->core;
+ Tk_TextLayout textLayout = entryPtr->entry.textLayout;
+ int leftIndex = entryPtr->entry.xscroll.first;
+ int rightIndex;
+ Ttk_Box textarea;
+
+ Ttk_PlaceLayout(corePtr->layout,corePtr->state,Ttk_WinBox(corePtr->tkwin));
+ textarea = Ttk_ClientRegion(corePtr->layout, "textarea");
+
+ /* Center the text vertically within the available parcel:
+ */
+ entryPtr->entry.layoutY = textarea.y +
+ (textarea.height - entryPtr->entry.layoutHeight)/2;
+
+ /* Recompute where the leftmost character on the display will
+ * be drawn (layoutX) and adjust leftIndex if necessary.
+ */
+ if (entryPtr->entry.layoutWidth <= textarea.width) {
+ /* Everything fits. Set leftIndex to zero (no need to scroll),
+ * and compute layoutX based on -justify.
+ */
+ int extraSpace = textarea.width - entryPtr->entry.layoutWidth;
+ leftIndex = 0;
+ rightIndex = entryPtr->entry.numChars;
+ entryPtr->entry.layoutX = textarea.x;
+ if (entryPtr->entry.justify == TK_JUSTIFY_RIGHT) {
+ entryPtr->entry.layoutX += extraSpace;
+ } else if (entryPtr->entry.justify == TK_JUSTIFY_CENTER) {
+ entryPtr->entry.layoutX += extraSpace / 2;
+ }
+ } else {
+ /* The whole string doesn't fit in the window.
+ * Limit leftIndex to leave at most one character's worth
+ * of empty space on the right.
+ */
+ int overflow = entryPtr->entry.layoutWidth - textarea.width;
+ int maxLeftIndex = 1 + Tk_PointToChar(textLayout, overflow, 0);
+ int leftX;
+
+ if (leftIndex > maxLeftIndex) {
+ leftIndex = maxLeftIndex;
+ }
+
+ /* Compute layoutX and rightIndex.
+ * rightIndex is set to one past the last fully-visible character.
+ */
+ Tk_CharBbox(textLayout, leftIndex, &leftX, NULL, NULL, NULL);
+ rightIndex = Tk_PointToChar(textLayout, leftX + textarea.width, 0);
+ entryPtr->entry.layoutX = textarea.x - leftX;
+ }
+
+ TtkScrolled(entryPtr->entry.xscrollHandle,
+ leftIndex, rightIndex, entryPtr->entry.numChars);
+}
+
+/* EntryGetGC -- Helper routine.
+ * Get a GC using the specified foreground color and the entry's font.
+ * Result must be freed with Tk_FreeGC().
+ */
+static GC EntryGetGC(Entry *entryPtr, Tcl_Obj *colorObj, TkRegion clip)
+{
+ Tk_Window tkwin = entryPtr->core.tkwin;
+ Tk_Font font = Tk_GetFontFromObj(tkwin, entryPtr->entry.fontObj);
+ XColor *colorPtr;
+ unsigned long mask = 0ul;
+ XGCValues gcValues;
+ GC gc;
+
+ gcValues.line_width = 1; mask |= GCLineWidth;
+ gcValues.font = Tk_FontId(font); mask |= GCFont;
+ if (colorObj != 0 && (colorPtr=Tk_GetColorFromObj(tkwin,colorObj)) != 0) {
+ gcValues.foreground = colorPtr->pixel;
+ mask |= GCForeground;
+ }
+ gc = Tk_GetGC(entryPtr->core.tkwin, mask, &gcValues);
+ if (clip != None) {
+ TkSetRegion(Tk_Display(entryPtr->core.tkwin), gc, clip);
+ }
+ return gc;
+}
+
+/* EntryDisplay --
+ * Redraws the contents of an entry window.
+ */
+static void EntryDisplay(void *clientData, Drawable d)
+{
+ Entry *entryPtr = clientData;
+ Tk_Window tkwin = entryPtr->core.tkwin;
+ int leftIndex = entryPtr->entry.xscroll.first,
+ rightIndex = entryPtr->entry.xscroll.last + 1,
+ selFirst = entryPtr->entry.selectFirst,
+ selLast = entryPtr->entry.selectLast;
+ EntryStyleData es;
+ GC gc;
+ int showSelection, showCursor;
+ Ttk_Box textarea;
+ TkRegion clipRegion;
+ XRectangle rect;
+
+ EntryInitStyleData(entryPtr, &es);
+
+ textarea = Ttk_ClientRegion(entryPtr->core.layout, "textarea");
+ showCursor =
+ (entryPtr->core.flags & CURSOR_ON) != 0
+ && EntryEditable(entryPtr)
+ && entryPtr->entry.insertPos >= leftIndex
+ && entryPtr->entry.insertPos <= rightIndex
+ ;
+ showSelection =
+ (entryPtr->core.state & TTK_STATE_DISABLED) == 0
+ && selFirst > -1
+ && selLast > leftIndex
+ && selFirst <= rightIndex
+ ;
+
+ /* Adjust selection range to keep in display bounds.
+ */
+ if (showSelection) {
+ if (selFirst < leftIndex)
+ selFirst = leftIndex;
+ if (selLast > rightIndex)
+ selLast = rightIndex;
+ }
+
+ /* Draw widget background & border
+ */
+ Ttk_DrawLayout(entryPtr->core.layout, entryPtr->core.state, d);
+
+ /* Draw selection background
+ */
+ if (showSelection && es.selBorderObj) {
+ Tk_3DBorder selBorder = Tk_Get3DBorderFromObj(tkwin, es.selBorderObj);
+ int selStartX = EntryCharPosition(entryPtr, selFirst);
+ int selEndX = EntryCharPosition(entryPtr, selLast);
+ int borderWidth = 1;
+
+ Tcl_GetIntFromObj(NULL, es.selBorderWidthObj, &borderWidth);
+
+ if (selBorder) {
+ Tk_Fill3DRectangle(tkwin, d, selBorder,
+ selStartX - borderWidth, entryPtr->entry.layoutY - borderWidth,
+ selEndX - selStartX + 2*borderWidth,
+ entryPtr->entry.layoutHeight + 2*borderWidth,
+ borderWidth, TK_RELIEF_RAISED);
+ }
+ }
+
+ /* Initialize the clip region. Note that Xft does _not_ derive its
+ * clipping area from the GC, so we have to supply that by other means.
+ */
+
+ rect.x = textarea.x;
+ rect.y = textarea.y;
+ rect.width = textarea.width;
+ rect.height = textarea.height;
+ clipRegion = TkCreateRegion();
+ TkUnionRectWithRegion(&rect, clipRegion, clipRegion);
+#ifdef HAVE_XFT
+ TkUnixSetXftClipRegion(clipRegion);
+#endif
+
+ /* Draw cursor:
+ */
+ if (showCursor) {
+ int cursorX = EntryCharPosition(entryPtr, entryPtr->entry.insertPos),
+ cursorY = entryPtr->entry.layoutY,
+ cursorHeight = entryPtr->entry.layoutHeight,
+ cursorWidth = 1;
+
+ Tcl_GetIntFromObj(NULL,es.insertWidthObj,&cursorWidth);
+ if (cursorWidth <= 0) {
+ cursorWidth = 1;
+ }
+
+ /* @@@ should: maybe: SetCaretPos even when blinked off */
+ Tk_SetCaretPos(tkwin, cursorX, cursorY, cursorHeight);
+
+ gc = EntryGetGC(entryPtr, es.insertColorObj, clipRegion);
+ XFillRectangle(Tk_Display(tkwin), d, gc,
+ cursorX-cursorWidth/2, cursorY, cursorWidth, cursorHeight);
+ XSetClipMask(Tk_Display(tkwin), gc, None);
+ Tk_FreeGC(Tk_Display(tkwin), gc);
+ }
+
+ /* Draw the text:
+ */
+ gc = EntryGetGC(entryPtr, es.foregroundObj, clipRegion);
+ Tk_DrawTextLayout(
+ Tk_Display(tkwin), d, gc, entryPtr->entry.textLayout,
+ entryPtr->entry.layoutX, entryPtr->entry.layoutY,
+ leftIndex, rightIndex);
+ XSetClipMask(Tk_Display(tkwin), gc, None);
+ Tk_FreeGC(Tk_Display(tkwin), gc);
+
+ /* Overwrite the selected portion (if any) in the -selectforeground color:
+ */
+ if (showSelection) {
+ gc = EntryGetGC(entryPtr, es.selForegroundObj, clipRegion);
+ Tk_DrawTextLayout(
+ Tk_Display(tkwin), d, gc, entryPtr->entry.textLayout,
+ entryPtr->entry.layoutX, entryPtr->entry.layoutY,
+ selFirst, selLast);
+ XSetClipMask(Tk_Display(tkwin), gc, None);
+ Tk_FreeGC(Tk_Display(tkwin), gc);
+ }
+
+ /* Drop the region. Note that we have to manually remove the reference to
+ * it from the Xft guts (if they're being used).
+ */
+#ifdef HAVE_XFT
+ TkUnixSetXftClipRegion(None);
+#endif
+ TkDestroyRegion(clipRegion);
+}
+
+/*------------------------------------------------------------------------
+ * +++ Widget commands.
+ */
+
+/* EntryIndex --
+ * Parse an index into an entry and return either its value
+ * or an error.
+ *
+ * Results:
+ * A standard Tcl result. If all went well, then *indexPtr is
+ * filled in with the character index (into entryPtr) corresponding to
+ * string. The index value is guaranteed to lie between 0 and
+ * the number of characters in the string, inclusive. If an
+ * error occurs then an error message is left in the interp's result.
+ */
+static int
+EntryIndex(
+ Tcl_Interp *interp, /* For error messages. */
+ Entry *entryPtr, /* Entry widget to query */
+ Tcl_Obj *indexObj, /* Symbolic index name */
+ int *indexPtr) /* Return value */
+{
+# define EntryWidth(e) (Tk_Width(entryPtr->core.tkwin)) /* Not Right */
+ int length;
+ const char *string = Tcl_GetStringFromObj(indexObj, &length);
+
+ if (strncmp(string, "end", length) == 0) {
+ *indexPtr = entryPtr->entry.numChars;
+ } else if (strncmp(string, "insert", length) == 0) {
+ *indexPtr = entryPtr->entry.insertPos;
+ } else if (strncmp(string, "left", length) == 0) { /* for debugging */
+ *indexPtr = entryPtr->entry.xscroll.first;
+ } else if (strncmp(string, "right", length) == 0) { /* for debugging */
+ *indexPtr = entryPtr->entry.xscroll.last;
+ } else if (strncmp(string, "sel.", 4) == 0) {
+ if (entryPtr->entry.selectFirst < 0) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "selection isn't in widget ",
+ Tk_PathName(entryPtr->core.tkwin), NULL);
+ return TCL_ERROR;
+ }
+ if (strncmp(string, "sel.first", length) == 0) {
+ *indexPtr = entryPtr->entry.selectFirst;
+ } else if (strncmp(string, "sel.last", length) == 0) {
+ *indexPtr = entryPtr->entry.selectLast;
+ } else {
+ goto badIndex;
+ }
+ } else if (string[0] == '@') {
+ int roundUp = 0;
+ int maxWidth = EntryWidth(entryPtr);
+ int x;
+
+ if (Tcl_GetInt(interp, string + 1, &x) != TCL_OK) {
+ goto badIndex;
+ }
+ if (x > maxWidth) {
+ x = maxWidth;
+ roundUp = 1;
+ }
+ *indexPtr = Tk_PointToChar(entryPtr->entry.textLayout,
+ x - entryPtr->entry.layoutX, 0);
+
+ if (*indexPtr < entryPtr->entry.xscroll.first) {
+ *indexPtr = entryPtr->entry.xscroll.first;
+ }
+
+ /*
+ * Special trick: if the x-position was off-screen to the right,
+ * round the index up to refer to the character just after the
+ * last visible one on the screen. This is needed to enable the
+ * last character to be selected, for example.
+ */
+
+ if (roundUp && (*indexPtr < entryPtr->entry.numChars)) {
+ *indexPtr += 1;
+ }
+ } else {
+ if (Tcl_GetInt(interp, string, indexPtr) != TCL_OK) {
+ goto badIndex;
+ }
+ if (*indexPtr < 0) {
+ *indexPtr = 0;
+ } else if (*indexPtr > entryPtr->entry.numChars) {
+ *indexPtr = entryPtr->entry.numChars;
+ }
+ }
+ return TCL_OK;
+
+badIndex:
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "bad entry index \"", string, "\"", NULL);
+ return TCL_ERROR;
+}
+
+/* $entry bbox $index --
+ * Return the bounding box of the character at the specified index.
+ */
+static int
+EntryBBoxCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ Entry *entryPtr = recordPtr;
+ Ttk_Box b;
+ int index;
+
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "index");
+ return TCL_ERROR;
+ }
+ if (EntryIndex(interp, entryPtr, objv[2], &index) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if ((index == entryPtr->entry.numChars) && (index > 0)) {
+ index--;
+ }
+ Tk_CharBbox(entryPtr->entry.textLayout, index,
+ &b.x, &b.y, &b.width, &b.height);
+ b.x += entryPtr->entry.layoutX;
+ b.y += entryPtr->entry.layoutY;
+ Tcl_SetObjResult(interp, Ttk_NewBoxObj(b));
+ return TCL_OK;
+}
+
+/* $entry delete $from ?$to? --
+ * Delete the characters in the range [$from,$to).
+ * $to defaults to $from+1 if not specified.
+ */
+static int
+EntryDeleteCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ Entry *entryPtr = recordPtr;
+ int first, last;
+
+ if ((objc < 3) || (objc > 4)) {
+ Tcl_WrongNumArgs(interp, 2, objv, "firstIndex ?lastIndex?");
+ return TCL_ERROR;
+ }
+ if (EntryIndex(interp, entryPtr, objv[2], &first) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (objc == 3) {
+ last = first + 1;
+ } else if (EntryIndex(interp, entryPtr, objv[3], &last) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ if (last >= first && EntryEditable(entryPtr)) {
+ return DeleteChars(entryPtr, first, last - first);
+ }
+ return TCL_OK;
+}
+
+/* $entry get --
+ * Return the current value of the entry widget.
+ */
+static int
+EntryGetCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ Entry *entryPtr = recordPtr;
+ if (objc != 2) {
+ Tcl_WrongNumArgs(interp, 2, objv, NULL);
+ return TCL_ERROR;
+ }
+ Tcl_SetResult(interp, entryPtr->entry.string, TCL_VOLATILE);
+ return TCL_OK;
+}
+
+/* $entry icursor $index --
+ * Set the insert cursor position.
+ */
+static int
+EntryICursorCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ Entry *entryPtr = recordPtr;
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "pos");
+ return TCL_ERROR;
+ }
+ if (EntryIndex(interp, entryPtr, objv[2],
+ &entryPtr->entry.insertPos) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ TtkRedisplayWidget(&entryPtr->core);
+ return TCL_OK;
+}
+
+/* $entry index $index --
+ * Return numeric value (0..numChars) of the specified index.
+ */
+static int
+EntryIndexCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ Entry *entryPtr = recordPtr;
+ int index;
+
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "string");
+ return TCL_ERROR;
+ }
+ if (EntryIndex(interp, entryPtr, objv[2], &index) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(index));
+ return TCL_OK;
+}
+
+/* $entry insert $index $text --
+ * Insert $text after position $index.
+ * Silent no-op if the entry is disabled or read-only.
+ */
+static int
+EntryInsertCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ Entry *entryPtr = recordPtr;
+ int index;
+
+ if (objc != 4) {
+ Tcl_WrongNumArgs(interp, 2, objv, "index text");
+ return TCL_ERROR;
+ }
+ if (EntryIndex(interp, entryPtr, objv[2], &index) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (EntryEditable(entryPtr)) {
+ return InsertChars(entryPtr, index, Tcl_GetString(objv[3]));
+ }
+ return TCL_OK;
+}
+
+/* $entry selection clear --
+ * Clear selection.
+ */
+static int EntrySelectionClearCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ Entry *entryPtr = recordPtr;
+
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 3, objv, NULL);
+ return TCL_ERROR;
+ }
+ entryPtr->entry.selectFirst = entryPtr->entry.selectLast = -1;
+ TtkRedisplayWidget(&entryPtr->core);
+ return TCL_OK;
+}
+
+/* $entry selection present --
+ * Returns 1 if any characters are selected, 0 otherwise.
+ */
+static int EntrySelectionPresentCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ Entry *entryPtr = recordPtr;
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 3, objv, NULL);
+ return TCL_ERROR;
+ }
+ Tcl_SetObjResult(interp,
+ Tcl_NewBooleanObj(entryPtr->entry.selectFirst >= 0));
+ return TCL_OK;
+}
+
+/* $entry selection range $start $end --
+ * Explicitly set the selection range.
+ */
+static int EntrySelectionRangeCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ Entry *entryPtr = recordPtr;
+ int start, end;
+ if (objc != 5) {
+ Tcl_WrongNumArgs(interp, 3, objv, "start end");
+ return TCL_ERROR;
+ }
+ if ( EntryIndex(interp, entryPtr, objv[3], &start) != TCL_OK
+ || EntryIndex(interp, entryPtr, objv[4], &end) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (entryPtr->core.state & TTK_STATE_DISABLED) {
+ return TCL_OK;
+ }
+
+ if (start >= end) {
+ entryPtr->entry.selectFirst = entryPtr->entry.selectLast = -1;
+ } else {
+ entryPtr->entry.selectFirst = start;
+ entryPtr->entry.selectLast = end;
+ EntryOwnSelection(entryPtr);
+ }
+ TtkRedisplayWidget(&entryPtr->core);
+ return TCL_OK;
+}
+
+static const Ttk_Ensemble EntrySelectionCommands[] = {
+ { "clear", EntrySelectionClearCommand,0 },
+ { "present", EntrySelectionPresentCommand,0 },
+ { "range", EntrySelectionRangeCommand,0 },
+ { 0,0,0 }
+};
+
+/* $entry set $value
+ * Sets the value of an entry widget.
+ */
+static int EntrySetCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ Entry *entryPtr = recordPtr;
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "value");
+ return TCL_ERROR;
+ }
+ EntrySetValue(entryPtr, Tcl_GetString(objv[2]));
+ return TCL_OK;
+}
+
+/* $entry validate --
+ * Trigger forced validation. Returns 1/0 if validation succeeds/fails
+ * or error status from -validatecommand / -invalidcommand.
+ */
+static int EntryValidateCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ Entry *entryPtr = recordPtr;
+ int code;
+
+ if (objc != 2) {
+ Tcl_WrongNumArgs(interp, 2, objv, NULL);
+ return TCL_ERROR;
+ }
+
+ code = EntryRevalidate(interp, entryPtr, VALIDATE_FORCED);
+
+ if (code == TCL_ERROR)
+ return code;
+
+ Tcl_SetObjResult(interp, Tcl_NewBooleanObj(code == TCL_OK));
+ return TCL_OK;
+}
+
+/* $entry xview -- horizontal scrolling interface
+ */
+static int EntryXViewCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ Entry *entryPtr = recordPtr;
+ return TtkScrollviewCommand(interp, objc, objv, entryPtr->entry.xscrollHandle);
+}
+
+static const Ttk_Ensemble EntryCommands[] = {
+ { "bbox", EntryBBoxCommand,0 },
+ { "cget", TtkWidgetCgetCommand,0 },
+ { "configure", TtkWidgetConfigureCommand,0 },
+ { "delete", EntryDeleteCommand,0 },
+ { "get", EntryGetCommand,0 },
+ { "icursor", EntryICursorCommand,0 },
+ { "identify", TtkWidgetIdentifyCommand,0 },
+ { "index", EntryIndexCommand,0 },
+ { "insert", EntryInsertCommand,0 },
+ { "instate", TtkWidgetInstateCommand,0 },
+ { "selection", 0,EntrySelectionCommands },
+ { "state", TtkWidgetStateCommand,0 },
+ { "validate", EntryValidateCommand,0 },
+ { "xview", EntryXViewCommand,0 },
+ { 0,0,0 }
+};
+
+/*------------------------------------------------------------------------
+ * +++ Entry widget definition.
+ */
+
+static WidgetSpec EntryWidgetSpec = {
+ "TEntry", /* className */
+ sizeof(Entry), /* recordSize */
+ EntryOptionSpecs, /* optionSpecs */
+ EntryCommands, /* subcommands */
+ EntryInitialize, /* initializeProc */
+ EntryCleanup, /* cleanupProc */
+ EntryConfigure, /* configureProc */
+ EntryPostConfigure, /* postConfigureProc */
+ TtkWidgetGetLayout, /* getLayoutProc */
+ TtkWidgetSize, /* sizeProc */
+ EntryDoLayout, /* layoutProc */
+ EntryDisplay /* displayProc */
+};
+
+/*------------------------------------------------------------------------
+ * +++ Combobox widget record.
+ */
+
+typedef struct {
+ Tcl_Obj *postCommandObj;
+ Tcl_Obj *valuesObj;
+ Tcl_Obj *heightObj;
+ int currentIndex;
+} ComboboxPart;
+
+typedef struct {
+ WidgetCore core;
+ EntryPart entry;
+ ComboboxPart combobox;
+} Combobox;
+
+static Tk_OptionSpec ComboboxOptionSpecs[] = {
+ {TK_OPTION_STRING, "-height", "height", "Height",
+ DEF_LIST_HEIGHT, Tk_Offset(Combobox, combobox.heightObj), -1,
+ 0,0,0 },
+ {TK_OPTION_STRING, "-postcommand", "postCommand", "PostCommand",
+ "", Tk_Offset(Combobox, combobox.postCommandObj), -1,
+ 0,0,0 },
+ {TK_OPTION_STRING, "-values", "values", "Values",
+ "", Tk_Offset(Combobox, combobox.valuesObj), -1,
+ 0,0,0 },
+ WIDGET_INHERIT_OPTIONS(EntryOptionSpecs)
+};
+
+/* ComboboxInitialize --
+ * Initialization hook for combobox widgets.
+ */
+static void
+ComboboxInitialize(Tcl_Interp *interp, void *recordPtr)
+{
+ Combobox *cb = recordPtr;
+
+ cb->combobox.currentIndex = -1;
+ TtkTrackElementState(&cb->core);
+ EntryInitialize(interp, recordPtr);
+}
+
+/* ComboboxConfigure --
+ * Configuration hook for combobox widgets.
+ */
+static int
+ComboboxConfigure(Tcl_Interp *interp, void *recordPtr, int mask)
+{
+ Combobox *cbPtr = recordPtr;
+ int unused;
+
+ /* Make sure -values is a valid list:
+ */
+ if (Tcl_ListObjLength(interp,cbPtr->combobox.valuesObj,&unused) != TCL_OK)
+ return TCL_ERROR;
+
+ return EntryConfigure(interp, recordPtr, mask);
+}
+
+/* $cb current ?newIndex? -- get or set current index.
+ * Setting the current index updates the combobox value,
+ * but the value and -values may be changed independently
+ * of the index. Instead of trying to keep currentIndex
+ * in sync at all times, [$cb current] double-checks
+ */
+static int ComboboxCurrentCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ Combobox *cbPtr = recordPtr;
+ int currentIndex = cbPtr->combobox.currentIndex;
+ const char *currentValue = cbPtr->entry.string;
+ int nValues;
+ Tcl_Obj **values;
+
+ Tcl_ListObjGetElements(interp,cbPtr->combobox.valuesObj,&nValues,&values);
+
+ if (objc == 2) {
+ /* Check if currentIndex still valid:
+ */
+ if ( currentIndex < 0
+ || currentIndex >= nValues
+ || strcmp(currentValue,Tcl_GetString(values[currentIndex]))
+ )
+ {
+ /* Not valid. Check current value against each element in -values:
+ */
+ for (currentIndex = 0; currentIndex < nValues; ++currentIndex) {
+ if (!strcmp(currentValue,Tcl_GetString(values[currentIndex]))) {
+ break;
+ }
+ }
+ if (currentIndex >= nValues) {
+ /* Not found */
+ currentIndex = -1;
+ }
+ }
+ cbPtr->combobox.currentIndex = currentIndex;
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(currentIndex));
+ return TCL_OK;
+ } else if (objc == 3) {
+ if (Tcl_GetIntFromObj(interp, objv[2], &currentIndex) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (currentIndex < 0 || currentIndex >= nValues) {
+ Tcl_AppendResult(interp,
+ "Index ", Tcl_GetString(objv[2]), " out of range",
+ NULL);
+ return TCL_ERROR;
+ }
+
+ cbPtr->combobox.currentIndex = currentIndex;
+
+ return EntrySetValue(recordPtr, Tcl_GetString(values[currentIndex]));
+ } else {
+ Tcl_WrongNumArgs(interp, 2, objv, "?newIndex?");
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*------------------------------------------------------------------------
+ * +++ Combobox widget definition.
+ */
+static const Ttk_Ensemble ComboboxCommands[] = {
+ { "bbox", EntryBBoxCommand,0 },
+ { "cget", TtkWidgetCgetCommand,0 },
+ { "configure", TtkWidgetConfigureCommand,0 },
+ { "current", ComboboxCurrentCommand,0 },
+ { "delete", EntryDeleteCommand,0 },
+ { "get", EntryGetCommand,0 },
+ { "icursor", EntryICursorCommand,0 },
+ { "identify", TtkWidgetIdentifyCommand,0 },
+ { "index", EntryIndexCommand,0 },
+ { "insert", EntryInsertCommand,0 },
+ { "instate", TtkWidgetInstateCommand,0 },
+ { "selection", 0,EntrySelectionCommands },
+ { "state", TtkWidgetStateCommand,0 },
+ { "set", EntrySetCommand,0 },
+ { "validate", EntryValidateCommand,0 },
+ { "xview", EntryXViewCommand,0 },
+ { 0,0,0 }
+};
+
+static WidgetSpec ComboboxWidgetSpec = {
+ "TCombobox", /* className */
+ sizeof(Combobox), /* recordSize */
+ ComboboxOptionSpecs, /* optionSpecs */
+ ComboboxCommands, /* subcommands */
+ ComboboxInitialize, /* initializeProc */
+ EntryCleanup, /* cleanupProc */
+ ComboboxConfigure, /* configureProc */
+ EntryPostConfigure, /* postConfigureProc */
+ TtkWidgetGetLayout, /* getLayoutProc */
+ TtkWidgetSize, /* sizeProc */
+ EntryDoLayout, /* layoutProc */
+ EntryDisplay /* displayProc */
+};
+
+/*------------------------------------------------------------------------
+ * +++ Spinbox widget.
+ */
+
+typedef struct {
+ Tcl_Obj *valuesObj;
+
+ Tcl_Obj *fromObj;
+ Tcl_Obj *toObj;
+ Tcl_Obj *incrementObj;
+ Tcl_Obj *formatObj;
+
+ Tcl_Obj *wrapObj;
+ Tcl_Obj *commandObj;
+} SpinboxPart;
+
+typedef struct {
+ WidgetCore core;
+ EntryPart entry;
+ SpinboxPart spinbox;
+} Spinbox;
+
+static Tk_OptionSpec SpinboxOptionSpecs[] = {
+ {TK_OPTION_STRING, "-values", "values", "Values",
+ "", Tk_Offset(Spinbox, spinbox.valuesObj), -1,
+ 0,0,0 },
+
+ {TK_OPTION_DOUBLE, "-from", "from", "From",
+ "0", Tk_Offset(Spinbox,spinbox.fromObj), -1,
+ 0,0,0 },
+ {TK_OPTION_DOUBLE, "-to", "to", "To",
+ "0", Tk_Offset(Spinbox,spinbox.toObj), -1,
+ 0,0,0 },
+ {TK_OPTION_DOUBLE, "-increment", "increment", "Increment",
+ "1", Tk_Offset(Spinbox,spinbox.incrementObj), -1,
+ 0,0,0 },
+ {TK_OPTION_STRING, "-format", "format", "Format",
+ "", Tk_Offset(Spinbox, spinbox.formatObj), -1,
+ 0,0,0 },
+
+ {TK_OPTION_STRING, "-command", "command", "Command",
+ "", Tk_Offset(Spinbox, spinbox.commandObj), -1,
+ 0,0,0 },
+ {TK_OPTION_BOOLEAN, "-wrap", "wrap", "Wrap",
+ "0", Tk_Offset(Spinbox,spinbox.wrapObj), -1,
+ 0,0,0 },
+
+ WIDGET_INHERIT_OPTIONS(EntryOptionSpecs)
+};
+
+/* SpinboxInitialize --
+ * Initialization hook for spinbox widgets.
+ */
+static void
+SpinboxInitialize(Tcl_Interp *interp, void *recordPtr)
+{
+ Spinbox *sb = recordPtr;
+ TtkTrackElementState(&sb->core);
+ EntryInitialize(interp, recordPtr);
+}
+
+/* SpinboxConfigure --
+ * Configuration hook for spinbox widgets.
+ */
+static int
+SpinboxConfigure(Tcl_Interp *interp, void *recordPtr, int mask)
+{
+ Spinbox *sb = recordPtr;
+ int unused;
+
+ /* Make sure -values is a valid list:
+ */
+ if (Tcl_ListObjLength(interp,sb->spinbox.valuesObj,&unused) != TCL_OK)
+ return TCL_ERROR;
+
+ return EntryConfigure(interp, recordPtr, mask);
+}
+
+static const Ttk_Ensemble SpinboxCommands[] = {
+ { "bbox", EntryBBoxCommand,0 },
+ { "cget", TtkWidgetCgetCommand,0 },
+ { "configure", TtkWidgetConfigureCommand,0 },
+ { "delete", EntryDeleteCommand,0 },
+ { "get", EntryGetCommand,0 },
+ { "icursor", EntryICursorCommand,0 },
+ { "identify", TtkWidgetIdentifyCommand,0 },
+ { "index", EntryIndexCommand,0 },
+ { "insert", EntryInsertCommand,0 },
+ { "instate", TtkWidgetInstateCommand,0 },
+ { "selection", 0,EntrySelectionCommands },
+ { "state", TtkWidgetStateCommand,0 },
+ { "set", EntrySetCommand,0 },
+ { "validate", EntryValidateCommand,0 },
+ { "xview", EntryXViewCommand,0 },
+ { 0,0,0 }
+};
+
+static WidgetSpec SpinboxWidgetSpec = {
+ "TSpinbox", /* className */
+ sizeof(Spinbox), /* recordSize */
+ SpinboxOptionSpecs, /* optionSpecs */
+ SpinboxCommands, /* subcommands */
+ SpinboxInitialize, /* initializeProc */
+ EntryCleanup, /* cleanupProc */
+ SpinboxConfigure, /* configureProc */
+ EntryPostConfigure, /* postConfigureProc */
+ TtkWidgetGetLayout, /* getLayoutProc */
+ TtkWidgetSize, /* sizeProc */
+ EntryDoLayout, /* layoutProc */
+ EntryDisplay /* displayProc */
+};
+
+/*------------------------------------------------------------------------
+ * +++ Textarea element.
+ *
+ * Text display area for Entry widgets.
+ * Just computes requested size; display is handled by the widget itself.
+ */
+
+typedef struct {
+ Tcl_Obj *fontObj;
+ Tcl_Obj *widthObj;
+} TextareaElement;
+
+static Ttk_ElementOptionSpec TextareaElementOptions[] = {
+ { "-font", TK_OPTION_FONT,
+ Tk_Offset(TextareaElement,fontObj), DEF_ENTRY_FONT },
+ { "-width", TK_OPTION_INT,
+ Tk_Offset(TextareaElement,widthObj), "20" },
+ { NULL, 0, 0, NULL }
+};
+
+static void TextareaElementSize(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+{
+ TextareaElement *textarea = elementRecord;
+ Tk_Font font = Tk_GetFontFromObj(tkwin, textarea->fontObj);
+ int avgWidth = Tk_TextWidth(font, "0", 1);
+ Tk_FontMetrics fm;
+ int prefWidth = 1;
+
+ Tk_GetFontMetrics(font, &fm);
+ Tcl_GetIntFromObj(NULL, textarea->widthObj, &prefWidth);
+ if (prefWidth <= 0)
+ prefWidth = 1;
+
+ *heightPtr = fm.linespace;
+ *widthPtr = prefWidth * avgWidth;
+}
+
+static Ttk_ElementSpec TextareaElementSpec = {
+ TK_STYLE_VERSION_2,
+ sizeof(TextareaElement),
+ TextareaElementOptions,
+ TextareaElementSize,
+ TtkNullElementDraw
+};
+
+/*------------------------------------------------------------------------
+ * +++ Widget layouts.
+ */
+
+TTK_BEGIN_LAYOUT(EntryLayout)
+ TTK_GROUP("Entry.field", TTK_FILL_BOTH|TTK_BORDER,
+ TTK_GROUP("Entry.padding", TTK_FILL_BOTH,
+ TTK_NODE("Entry.textarea", TTK_FILL_BOTH)))
+TTK_END_LAYOUT
+
+TTK_BEGIN_LAYOUT(ComboboxLayout)
+ TTK_GROUP("Combobox.field", TTK_FILL_BOTH,
+ TTK_NODE("Combobox.downarrow", TTK_PACK_RIGHT|TTK_FILL_Y)
+ TTK_GROUP("Combobox.padding", TTK_FILL_BOTH|TTK_PACK_LEFT|TTK_EXPAND,
+ TTK_NODE("Combobox.textarea", TTK_FILL_BOTH)))
+TTK_END_LAYOUT
+
+TTK_BEGIN_LAYOUT(SpinboxLayout)
+ TTK_GROUP("Spinbox.field", TTK_PACK_TOP|TTK_FILL_X,
+ TTK_GROUP("null", TTK_PACK_RIGHT,
+ TTK_NODE("Spinbox.uparrow", TTK_PACK_TOP|TTK_STICK_E)
+ TTK_NODE("Spinbox.downarrow", TTK_PACK_BOTTOM|TTK_STICK_E))
+ TTK_GROUP("Spinbox.padding", TTK_FILL_BOTH,
+ TTK_NODE("Spinbox.textarea", TTK_FILL_BOTH)))
+TTK_END_LAYOUT
+
+/*------------------------------------------------------------------------
+ * +++ Initialization.
+ */
+MODULE_SCOPE
+void TtkEntry_Init(Tcl_Interp *interp)
+{
+ Ttk_Theme themePtr = Ttk_GetDefaultTheme(interp);
+
+ Ttk_RegisterElement(interp, themePtr, "textarea", &TextareaElementSpec, 0);
+
+ Ttk_RegisterLayout(themePtr, "TEntry", EntryLayout);
+ Ttk_RegisterLayout(themePtr, "TCombobox", ComboboxLayout);
+ Ttk_RegisterLayout(themePtr, "TSpinbox", SpinboxLayout);
+
+ RegisterWidget(interp, "ttk::entry", &EntryWidgetSpec);
+ RegisterWidget(interp, "ttk::combobox", &ComboboxWidgetSpec);
+ RegisterWidget(interp, "ttk::spinbox", &SpinboxWidgetSpec);
+}
+
+/*EOF*/
diff --git a/generic/ttk/ttkFrame.c b/generic/ttk/ttkFrame.c
new file mode 100644
index 0000000..7860024
--- /dev/null
+++ b/generic/ttk/ttkFrame.c
@@ -0,0 +1,654 @@
+/*
+ * Copyright (c) 2004, Joe English
+ *
+ * ttk::frame and ttk::labelframe widgets.
+ */
+
+#include <tk.h>
+
+#include "ttkTheme.h"
+#include "ttkWidget.h"
+#include "ttkManager.h"
+
+/* ======================================================================
+ * +++ Frame widget:
+ */
+
+typedef struct {
+ Tcl_Obj *borderWidthObj;
+ Tcl_Obj *paddingObj;
+ Tcl_Obj *reliefObj;
+ Tcl_Obj *widthObj;
+ Tcl_Obj *heightObj;
+} FramePart;
+
+typedef struct {
+ WidgetCore core;
+ FramePart frame;
+} Frame;
+
+static Tk_OptionSpec FrameOptionSpecs[] = {
+ {TK_OPTION_PIXELS, "-borderwidth", "borderWidth", "BorderWidth", NULL,
+ Tk_Offset(Frame,frame.borderWidthObj), -1,
+ TK_OPTION_NULL_OK,0,GEOMETRY_CHANGED },
+ {TK_OPTION_STRING, "-padding", "padding", "Pad", NULL,
+ Tk_Offset(Frame,frame.paddingObj), -1,
+ TK_OPTION_NULL_OK,0,GEOMETRY_CHANGED },
+ {TK_OPTION_RELIEF, "-relief", "relief", "Relief", NULL,
+ Tk_Offset(Frame,frame.reliefObj), -1,
+ TK_OPTION_NULL_OK,0,0 },
+ {TK_OPTION_PIXELS, "-width", "width", "Width", "0",
+ Tk_Offset(Frame,frame.widthObj), -1,
+ 0,0,GEOMETRY_CHANGED },
+ {TK_OPTION_PIXELS, "-height", "height", "Height", "0",
+ Tk_Offset(Frame,frame.heightObj), -1,
+ 0,0,GEOMETRY_CHANGED },
+
+ WIDGET_TAKEFOCUS_FALSE,
+ WIDGET_INHERIT_OPTIONS(ttkCoreOptionSpecs)
+};
+
+static const Ttk_Ensemble FrameCommands[] = {
+ { "configure", TtkWidgetConfigureCommand,0 },
+ { "cget", TtkWidgetCgetCommand,0 },
+ { "instate", TtkWidgetInstateCommand,0 },
+ { "state", TtkWidgetStateCommand,0 },
+ { "identify", TtkWidgetIdentifyCommand,0 },
+ { 0,0,0 }
+};
+
+/*
+ * FrameMargins --
+ * Compute internal margins for a frame widget.
+ * This includes the -borderWidth, plus any additional -padding.
+ */
+static Ttk_Padding FrameMargins(Frame *framePtr)
+{
+ Ttk_Padding margins = Ttk_UniformPadding(0);
+
+ /* Check -padding:
+ */
+ if (framePtr->frame.paddingObj) {
+ Ttk_GetPaddingFromObj(NULL,
+ framePtr->core.tkwin, framePtr->frame.paddingObj, &margins);
+ }
+
+ /* Add padding for border:
+ */
+ if (framePtr->frame.borderWidthObj) {
+ int border = 0;
+ Tk_GetPixelsFromObj(NULL,
+ framePtr->core.tkwin, framePtr->frame.borderWidthObj, &border);
+ margins = Ttk_AddPadding(margins, Ttk_UniformPadding((short)border));
+ }
+
+ return margins;
+}
+
+/* FrameSize procedure --
+ * The frame doesn't request a size of its own by default,
+ * but it does have an internal border. See also <<NOTE-SIZE>>
+ */
+static int FrameSize(void *recordPtr, int *widthPtr, int *heightPtr)
+{
+ Frame *framePtr = recordPtr;
+ Ttk_SetMargins(framePtr->core.tkwin, FrameMargins(framePtr));
+ return 0;
+}
+
+/*
+ * FrameConfigure -- configure hook.
+ * <<NOTE-SIZE>> Usually the size of a frame is controlled by
+ * a geometry manager (pack, grid); the -width and -height
+ * options are only effective if geometry propagation is turned
+ * off or if the [place] GM is used for child widgets.
+ *
+ * To avoid geometry blinking, we issue a geometry request
+ * in the Configure hook instead of the Size hook, and only
+ * if -width and/or -height is nonzero and one of them
+ * or the other size-related options (-borderwidth, -padding)
+ * has been changed.
+ */
+
+static int FrameConfigure(Tcl_Interp *interp, void *recordPtr, int mask)
+{
+ Frame *framePtr = recordPtr;
+ int width, height;
+
+ /*
+ * Make sure -padding resource, if present, is correct:
+ */
+ if (framePtr->frame.paddingObj) {
+ Ttk_Padding unused;
+ if (Ttk_GetPaddingFromObj(interp,
+ framePtr->core.tkwin,
+ framePtr->frame.paddingObj,
+ &unused) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+
+ /* See <<NOTE-SIZE>>
+ */
+ if ( TCL_OK != Tk_GetPixelsFromObj(
+ interp,framePtr->core.tkwin,framePtr->frame.widthObj,&width)
+ || TCL_OK != Tk_GetPixelsFromObj(
+ interp,framePtr->core.tkwin,framePtr->frame.heightObj,&height)
+ )
+ {
+ return TCL_ERROR;
+ }
+
+ if ((width > 0 || height > 0) && (mask & GEOMETRY_CHANGED)) {
+ Tk_GeometryRequest(framePtr->core.tkwin, width, height);
+ }
+
+ return TtkCoreConfigure(interp, recordPtr, mask);
+}
+
+static WidgetSpec FrameWidgetSpec = {
+ "TFrame", /* className */
+ sizeof(Frame), /* recordSize */
+ FrameOptionSpecs, /* optionSpecs */
+ FrameCommands, /* subcommands */
+ TtkNullInitialize, /* initializeProc */
+ TtkNullCleanup, /* cleanupProc */
+ FrameConfigure, /* configureProc */
+ TtkNullPostConfigure, /* postConfigureProc */
+ TtkWidgetGetLayout, /* getLayoutProc */
+ FrameSize, /* sizeProc */
+ TtkWidgetDoLayout, /* layoutProc */
+ TtkWidgetDisplay /* displayProc */
+};
+
+TTK_BEGIN_LAYOUT(FrameLayout)
+ TTK_NODE("Frame.border", TTK_FILL_BOTH)
+TTK_END_LAYOUT
+
+/* ======================================================================
+ * +++ Labelframe widget:
+ */
+
+#define DEFAULT_LABELINSET 8
+#define DEFAULT_BORDERWIDTH 2
+
+int TtkGetLabelAnchorFromObj(
+ Tcl_Interp *interp, Tcl_Obj *objPtr, Ttk_PositionSpec *anchorPtr)
+{
+ const char *string = Tcl_GetString(objPtr);
+ char c = *string++;
+ Ttk_PositionSpec flags = 0;
+
+ /* First character determines side:
+ */
+ switch (c) {
+ case 'w' : flags = TTK_PACK_LEFT; break;
+ case 'e' : flags = TTK_PACK_RIGHT; break;
+ case 'n' : flags = TTK_PACK_TOP; break;
+ case 's' : flags = TTK_PACK_BOTTOM; break;
+ default : goto error;
+ }
+
+ /* Remaining characters are as per -sticky:
+ */
+ while ((c = *string++) != '\0') {
+ switch (c) {
+ case 'w' : flags |= TTK_STICK_W; break;
+ case 'e' : flags |= TTK_STICK_E; break;
+ case 'n' : flags |= TTK_STICK_N; break;
+ case 's' : flags |= TTK_STICK_S; break;
+ default : goto error;
+ }
+ }
+
+ *anchorPtr = flags;
+ return TCL_OK;
+
+error:
+ if (interp) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp,
+ "Bad label anchor specification ", Tcl_GetString(objPtr),
+ NULL);
+ }
+ return TCL_ERROR;
+}
+
+/* LabelAnchorSide --
+ * Returns the side corresponding to a LabelAnchor value.
+ */
+static Ttk_Side LabelAnchorSide(Ttk_PositionSpec flags)
+{
+ if (flags & TTK_PACK_LEFT) return TTK_SIDE_LEFT;
+ else if (flags & TTK_PACK_RIGHT) return TTK_SIDE_RIGHT;
+ else if (flags & TTK_PACK_TOP) return TTK_SIDE_TOP;
+ else if (flags & TTK_PACK_BOTTOM) return TTK_SIDE_BOTTOM;
+ /*NOTREACHED*/
+ return TTK_SIDE_TOP;
+}
+
+/*
+ * Labelframe widget record:
+ */
+typedef struct {
+ Tcl_Obj *labelAnchorObj;
+ Tcl_Obj *textObj;
+ Tcl_Obj *underlineObj;
+ Tk_Window labelWidget;
+
+ Ttk_Manager *mgr;
+ Ttk_Layout labelLayout; /* Sublayout for label */
+ Ttk_Box labelParcel; /* Set in layoutProc */
+} LabelframePart;
+
+typedef struct {
+ WidgetCore core;
+ FramePart frame;
+ LabelframePart label;
+} Labelframe;
+
+#define LABELWIDGET_CHANGED 0x100
+
+static Tk_OptionSpec LabelframeOptionSpecs[] = {
+ {TK_OPTION_STRING, "-labelanchor", "labelAnchor", "LabelAnchor",
+ "nw", Tk_Offset(Labelframe, label.labelAnchorObj),-1,
+ 0,0,GEOMETRY_CHANGED},
+ {TK_OPTION_STRING, "-text", "text", "Text", "",
+ Tk_Offset(Labelframe,label.textObj), -1,
+ 0,0,GEOMETRY_CHANGED },
+ {TK_OPTION_INT, "-underline", "underline", "Underline",
+ "-1", Tk_Offset(Labelframe,label.underlineObj), -1,
+ 0,0,0 },
+ {TK_OPTION_WINDOW, "-labelwidget", "labelWidget", "LabelWidget", NULL,
+ -1, Tk_Offset(Labelframe,label.labelWidget),
+ TK_OPTION_NULL_OK,0,LABELWIDGET_CHANGED|GEOMETRY_CHANGED },
+
+ WIDGET_INHERIT_OPTIONS(FrameOptionSpecs)
+};
+
+/*
+ * Labelframe style parameters:
+ */
+typedef struct {
+ int borderWidth; /* border width */
+ Ttk_Padding padding; /* internal padding */
+ Ttk_PositionSpec labelAnchor; /* corner/side to place label */
+ Ttk_Padding labelMargins; /* extra space around label */
+ int labelOutside; /* true=>place label outside border */
+} LabelframeStyle;
+
+static void LabelframeStyleOptions(Labelframe *lf, LabelframeStyle *style)
+{
+ Ttk_Layout layout = lf->core.layout;
+ Tcl_Obj *objPtr;
+
+ style->borderWidth = DEFAULT_BORDERWIDTH;
+ style->padding = Ttk_UniformPadding(0);
+ style->labelAnchor = TTK_PACK_TOP | TTK_STICK_W;
+ style->labelOutside = 0;
+
+ if ((objPtr = Ttk_QueryOption(layout, "-borderwidth", 0)) != NULL) {
+ Tk_GetPixelsFromObj(NULL, lf->core.tkwin, objPtr, &style->borderWidth);
+ }
+ if ((objPtr = Ttk_QueryOption(layout, "-padding", 0)) != NULL) {
+ Ttk_GetPaddingFromObj(NULL, lf->core.tkwin, objPtr, &style->padding);
+ }
+ if ((objPtr = Ttk_QueryOption(layout,"-labelanchor", 0)) != NULL) {
+ TtkGetLabelAnchorFromObj(NULL, objPtr, &style->labelAnchor);
+ }
+ if ((objPtr = Ttk_QueryOption(layout,"-labelmargins", 0)) != NULL) {
+ Ttk_GetBorderFromObj(NULL, objPtr, &style->labelMargins);
+ } else {
+ if (style->labelAnchor & (TTK_PACK_TOP|TTK_PACK_BOTTOM)) {
+ style->labelMargins =
+ Ttk_MakePadding(DEFAULT_LABELINSET,0,DEFAULT_LABELINSET,0);
+ } else {
+ style->labelMargins =
+ Ttk_MakePadding(0,DEFAULT_LABELINSET,0,DEFAULT_LABELINSET);
+ }
+ }
+ if ((objPtr = Ttk_QueryOption(layout,"-labeloutside", 0)) != NULL) {
+ Tcl_GetBooleanFromObj(NULL, objPtr, &style->labelOutside);
+ }
+
+ return;
+}
+
+/* LabelframeLabelSize --
+ * Extract the requested width and height of the labelframe's label:
+ * taken from the label widget if specified, otherwise the text label.
+ */
+static void
+LabelframeLabelSize(Labelframe *lframePtr, int *widthPtr, int *heightPtr)
+{
+ Tk_Window labelWidget = lframePtr->label.labelWidget;
+ Ttk_Layout labelLayout = lframePtr->label.labelLayout;
+
+ if (labelWidget) {
+ *widthPtr = Tk_ReqWidth(labelWidget);
+ *heightPtr = Tk_ReqHeight(labelWidget);
+ } else if (labelLayout) {
+ Ttk_LayoutSize(labelLayout, 0, widthPtr, heightPtr);
+ } else {
+ *widthPtr = *heightPtr = 0;
+ }
+}
+
+/*
+ * LabelframeSize --
+ * Like the frame, this doesn't request a size of its own
+ * but it does have internal padding and a minimum size.
+ */
+static int LabelframeSize(void *recordPtr, int *widthPtr, int *heightPtr)
+{
+ Labelframe *lframePtr = recordPtr;
+ WidgetCore *corePtr = &lframePtr->core;
+ Ttk_Padding margins;
+ LabelframeStyle style;
+ int labelWidth, labelHeight;
+
+ LabelframeStyleOptions(lframePtr, &style);
+
+ /* Compute base margins (See also: FrameMargins)
+ */
+ margins = Ttk_AddPadding(
+ style.padding, Ttk_UniformPadding((short)style.borderWidth));
+
+ /* Adjust margins based on label size and position:
+ */
+ LabelframeLabelSize(lframePtr, &labelWidth, &labelHeight);
+ labelWidth += Ttk_PaddingWidth(style.labelMargins);
+ labelHeight += Ttk_PaddingHeight(style.labelMargins);
+
+ switch (LabelAnchorSide(style.labelAnchor)) {
+ case TTK_SIDE_LEFT: margins.left += labelWidth; break;
+ case TTK_SIDE_RIGHT: margins.right += labelWidth; break;
+ case TTK_SIDE_TOP: margins.top += labelHeight; break;
+ case TTK_SIDE_BOTTOM: margins.bottom += labelHeight; break;
+ }
+
+ Ttk_SetMargins(corePtr->tkwin,margins);
+
+ /* Request minimum size based on border width and label size:
+ */
+ Tk_SetMinimumRequestSize(corePtr->tkwin,
+ labelWidth + 2*style.borderWidth,
+ labelHeight + 2*style.borderWidth);
+
+ return 0;
+}
+
+/*
+ * LabelframeGetLayout --
+ * Getlayout widget hook.
+ */
+
+static Ttk_Layout LabelframeGetLayout(
+ Tcl_Interp *interp, Ttk_Theme theme, void *recordPtr)
+{
+ Labelframe *lf = recordPtr;
+ Ttk_Layout frameLayout = TtkWidgetGetLayout(interp, theme, recordPtr);
+ Ttk_Layout labelLayout;
+
+ if (!frameLayout) {
+ return NULL;
+ }
+
+ labelLayout = Ttk_CreateSublayout(
+ interp, theme, frameLayout, ".Label", lf->core.optionTable);
+
+ if (labelLayout) {
+ if (lf->label.labelLayout) {
+ Ttk_FreeLayout(lf->label.labelLayout);
+ }
+ Ttk_RebindSublayout(labelLayout, recordPtr);
+ lf->label.labelLayout = labelLayout;
+ }
+
+ return frameLayout;
+}
+
+/*
+ * LabelframeDoLayout --
+ * Labelframe layout hook.
+ *
+ * Side effects: Computes labelParcel.
+ */
+
+static void LabelframeDoLayout(void *recordPtr)
+{
+ Labelframe *lframePtr = recordPtr;
+ WidgetCore *corePtr = &lframePtr->core;
+ int lw, lh; /* Label width and height */
+ LabelframeStyle style;
+ Ttk_Box borderParcel = Ttk_WinBox(lframePtr->core.tkwin);
+ Ttk_Box labelParcel;
+
+ /*
+ * Compute label parcel:
+ */
+ LabelframeStyleOptions(lframePtr, &style);
+ LabelframeLabelSize(lframePtr, &lw, &lh);
+ lw += Ttk_PaddingWidth(style.labelMargins);
+ lh += Ttk_PaddingHeight(style.labelMargins);
+
+ labelParcel = Ttk_PadBox(
+ Ttk_PositionBox(&borderParcel, lw, lh, style.labelAnchor),
+ style.labelMargins);
+
+ if (!style.labelOutside) {
+ /* Move border edge so it's over label:
+ */
+ switch (LabelAnchorSide(style.labelAnchor)) {
+ case TTK_SIDE_LEFT: borderParcel.x -= lw / 2;
+ case TTK_SIDE_RIGHT: borderParcel.width += lw/2; break;
+ case TTK_SIDE_TOP: borderParcel.y -= lh / 2;
+ case TTK_SIDE_BOTTOM: borderParcel.height += lh / 2; break;
+ }
+ }
+
+ /*
+ * Place border and label:
+ */
+ Ttk_PlaceLayout(corePtr->layout, corePtr->state, borderParcel);
+ if (lframePtr->label.labelLayout) {
+ Ttk_PlaceLayout(
+ lframePtr->label.labelLayout, corePtr->state, labelParcel);
+ }
+ /* labelWidget placed in LabelframePlaceSlaves GM hook */
+ lframePtr->label.labelParcel = labelParcel;
+}
+
+static void LabelframeDisplay(void *recordPtr, Drawable d)
+{
+ Labelframe *lframePtr = recordPtr;
+ Ttk_DrawLayout(lframePtr->core.layout, lframePtr->core.state, d);
+ if (lframePtr->label.labelLayout) {
+ Ttk_DrawLayout(lframePtr->label.labelLayout, lframePtr->core.state, d);
+ }
+}
+
+/* +++ Labelframe geometry manager hooks.
+ */
+
+/* LabelframePlaceSlaves --
+ * Sets the position and size of the labelwidget.
+ */
+static void LabelframePlaceSlaves(void *recordPtr)
+{
+ Labelframe *lframe = recordPtr;
+
+ if (Ttk_NumberSlaves(lframe->label.mgr) == 1) {
+ Ttk_Box b;
+ LabelframeDoLayout(recordPtr);
+ b = lframe->label.labelParcel;
+ /* ASSERT: slave #0 is lframe->label.labelWidget */
+ Ttk_PlaceSlave(lframe->label.mgr, 0, b.x,b.y,b.width,b.height);
+ }
+}
+
+static int LabelRequest(void *managerData, int index, int width, int height)
+{
+ return 1;
+}
+
+/* LabelRemoved --
+ * Unset the -labelwidget option.
+ *
+ * <<NOTE-LABELREMOVED>>:
+ * This routine is also called when the widget voluntarily forgets
+ * the slave in LabelframeConfigure.
+ */
+static void LabelRemoved(void *managerData, int slaveIndex)
+{
+ Labelframe *lframe = managerData;
+ lframe->label.labelWidget = 0;
+}
+
+static Ttk_ManagerSpec LabelframeManagerSpec = {
+ { "labelframe", Ttk_GeometryRequestProc, Ttk_LostSlaveProc },
+ LabelframeSize,
+ LabelframePlaceSlaves,
+ LabelRequest,
+ LabelRemoved
+};
+
+/* LabelframeInitialize --
+ * Initialization hook.
+ */
+static void LabelframeInitialize(Tcl_Interp *interp, void *recordPtr)
+{
+ Labelframe *lframe = recordPtr;
+
+ lframe->label.mgr = Ttk_CreateManager(
+ &LabelframeManagerSpec, lframe, lframe->core.tkwin);
+ lframe->label.labelWidget = 0;
+ lframe->label.labelLayout = 0;
+ lframe->label.labelParcel = Ttk_MakeBox(-1,-1,-1,-1);
+}
+
+/* LabelframeCleanup --
+ * Cleanup hook.
+ */
+static void LabelframeCleanup(void *recordPtr)
+{
+ Labelframe *lframe = recordPtr;
+ Ttk_DeleteManager(lframe->label.mgr);
+ if (lframe->label.labelLayout) {
+ Ttk_FreeLayout(lframe->label.labelLayout);
+ }
+}
+
+/* RaiseLabelWidget --
+ * Raise the -labelwidget to ensure that the labelframe doesn't
+ * obscure it (if it's not a direct child), or bring it to
+ * the top of the stacking order (if it is).
+ */
+static void RaiseLabelWidget(Labelframe *lframe)
+{
+ Tk_Window parent = Tk_Parent(lframe->label.labelWidget);
+ Tk_Window sibling = NULL;
+ Tk_Window w = lframe->core.tkwin;
+
+ while (w && w != parent) {
+ sibling = w;
+ w = Tk_Parent(w);
+ }
+
+ Tk_RestackWindow(lframe->label.labelWidget, Above, sibling);
+}
+
+/* LabelframeConfigure --
+ * Configuration hook.
+ */
+static int LabelframeConfigure(Tcl_Interp *interp,void *recordPtr,int mask)
+{
+ Labelframe *lframePtr = recordPtr;
+ Tk_Window labelWidget = lframePtr->label.labelWidget;
+ Ttk_PositionSpec unused;
+
+ /* Validate options:
+ */
+ if (mask & LABELWIDGET_CHANGED && labelWidget != NULL) {
+ if (!Ttk_Maintainable(interp, labelWidget, lframePtr->core.tkwin)) {
+ return TCL_ERROR;
+ }
+ }
+
+ if (TtkGetLabelAnchorFromObj(
+ interp, lframePtr->label.labelAnchorObj, &unused) != TCL_OK)
+ {
+ return TCL_ERROR;
+ }
+
+ /* Base class configuration:
+ */
+ if (FrameConfigure(interp, recordPtr, mask) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ /* Update -labelwidget changes, if any:
+ */
+ if (mask & LABELWIDGET_CHANGED) {
+ if (Ttk_NumberSlaves(lframePtr->label.mgr) == 1) {
+ Ttk_ForgetSlave(lframePtr->label.mgr, 0);
+ /* Restore labelWidget field (see <<NOTE-LABELREMOVED>>)
+ */
+ lframePtr->label.labelWidget = labelWidget;
+ }
+
+ if (labelWidget) {
+ Ttk_InsertSlave(lframePtr->label.mgr, 0, labelWidget, NULL);
+ RaiseLabelWidget(lframePtr);
+ }
+ }
+
+ if (mask & GEOMETRY_CHANGED) {
+ Ttk_ManagerSizeChanged(lframePtr->label.mgr);
+ Ttk_ManagerLayoutChanged(lframePtr->label.mgr);
+ }
+
+ return TCL_OK;
+}
+
+static WidgetSpec LabelframeWidgetSpec = {
+ "TLabelframe", /* className */
+ sizeof(Labelframe), /* recordSize */
+ LabelframeOptionSpecs, /* optionSpecs */
+ FrameCommands, /* subcommands */
+ LabelframeInitialize, /* initializeProc */
+ LabelframeCleanup, /* cleanupProc */
+ LabelframeConfigure, /* configureProc */
+ TtkNullPostConfigure, /* postConfigureProc */
+ LabelframeGetLayout, /* getLayoutProc */
+ LabelframeSize, /* sizeProc */
+ LabelframeDoLayout, /* layoutProc */
+ LabelframeDisplay /* displayProc */
+};
+
+TTK_BEGIN_LAYOUT(LabelframeLayout)
+ TTK_NODE("Labelframe.border", TTK_FILL_BOTH)
+TTK_END_LAYOUT
+
+TTK_BEGIN_LAYOUT(LabelSublayout)
+ TTK_GROUP("Label.fill", TTK_FILL_BOTH,
+ TTK_NODE("Label.text", TTK_FILL_BOTH))
+TTK_END_LAYOUT
+
+/* ======================================================================
+ * +++ Initialization.
+ */
+
+MODULE_SCOPE
+void TtkFrame_Init(Tcl_Interp *interp)
+{
+ Ttk_Theme theme = Ttk_GetDefaultTheme(interp);
+
+ Ttk_RegisterLayout(theme, "TFrame", FrameLayout);
+ Ttk_RegisterLayout(theme, "TLabelframe", LabelframeLayout);
+ Ttk_RegisterLayout(theme, "Label", LabelSublayout);
+
+ RegisterWidget(interp, "ttk::frame", &FrameWidgetSpec);
+ RegisterWidget(interp, "ttk::labelframe", &LabelframeWidgetSpec);
+}
+
diff --git a/generic/ttk/ttkGenStubs.tcl b/generic/ttk/ttkGenStubs.tcl
new file mode 100644
index 0000000..90dea25
--- /dev/null
+++ b/generic/ttk/ttkGenStubs.tcl
@@ -0,0 +1,921 @@
+# ttkGenStubs.tcl --
+#
+# This script generates a set of stub files for a given
+# interface.
+#
+#
+# Copyright (c) 1998-1999 by Scriptics Corporation.
+# See the file "license.terms" for information on usage and redistribution
+# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+#
+# SOURCE: tcl/tools/genStubs.tcl, revision 1.20
+#
+# CHANGES:
+# + Remove xxx_TCL_DECLARED #ifdeffery
+# + Use application-defined storage class specifier instead of "EXTERN"
+# + Add "epoch" and "revision" fields to stubs table record
+# + Remove dead code related to USE_*_STUB_PROCS (emitStubs, makeStub)
+# + Second argument to "declare" is used as a status guard
+# instead of a platform guard.
+# + Use void (*reserved$i)(void) = 0 instead of void *reserved$i = NULL
+# for unused stub entries, in case pointer-to-function and
+# pointer-to-object are different sizes.
+# + Allow trailing semicolon in function declarations
+# + stubs table is const-qualified
+#
+
+package require Tcl 8
+
+namespace eval genStubs {
+ # libraryName --
+ #
+ # The name of the entire library. This value is used to compute
+ # the USE_*_STUBS macro and the name of the init file.
+
+ variable libraryName "UNKNOWN"
+
+ # interfaces --
+ #
+ # An array indexed by interface name that is used to maintain
+ # the set of valid interfaces. The value is empty.
+
+ array set interfaces {}
+
+ # curName --
+ #
+ # The name of the interface currently being defined.
+
+ variable curName "UNKNOWN"
+
+ # scspec --
+ #
+ # Storage class specifier for external function declarations.
+ # Normally "extern", may be set to something like XYZAPI
+ #
+ variable scspec "extern"
+
+ # epoch, revision --
+ #
+ # The epoch and revision numbers of the interface currently being defined.
+ # (@@@TODO: should be an array mapping interface names -> numbers)
+ #
+
+ variable epoch 0
+ variable revision 0
+
+ # hooks --
+ #
+ # An array indexed by interface name that contains the set of
+ # subinterfaces that should be defined for a given interface.
+
+ array set hooks {}
+
+ # stubs --
+ #
+ # This three dimensional array is indexed first by interface name,
+ # second by field name, and third by a numeric offset or the
+ # constant "lastNum". The lastNum entry contains the largest
+ # numeric offset used for a given interface.
+ #
+ # Field "decl,$i" contains the C function specification that
+ # should be used for the given entry in the stub table. The spec
+ # consists of a list in the form returned by parseDecl.
+ # Other fields TBD later.
+
+ array set stubs {}
+
+ # outDir --
+ #
+ # The directory where the generated files should be placed.
+
+ variable outDir .
+}
+
+# genStubs::library --
+#
+# This function is used in the declarations file to set the name
+# of the library that the interfaces are associated with (e.g. "tcl").
+# This value will be used to define the inline conditional macro.
+#
+# Arguments:
+# name The library name.
+#
+# Results:
+# None.
+
+proc genStubs::library {name} {
+ variable libraryName $name
+}
+
+# genStubs::interface --
+#
+# This function is used in the declarations file to set the name
+# of the interface currently being defined.
+#
+# Arguments:
+# name The name of the interface.
+#
+# Results:
+# None.
+
+proc genStubs::interface {name} {
+ variable curName $name
+ variable interfaces
+ variable stubs
+
+ set interfaces($name) {}
+ set stubs($name,lastNum) 0
+ return
+}
+
+# genStubs::scspec --
+#
+# Define the storage class macro used for external function declarations.
+# Typically, this will be a macro like XYZAPI or EXTERN that
+# expands to either DLLIMPORT or DLLEXPORT, depending on whether
+# -DBUILD_XYZ has been set.
+#
+proc genStubs::scspec {value} {
+ variable scspec $value
+}
+
+# genStubs::epoch --
+#
+# Define the epoch number for this library. The epoch
+# should be incrememented when a release is made that
+# contains incompatible changes to the public API.
+#
+proc genStubs::epoch {value} {
+ variable epoch $value
+}
+
+# genStubs::hooks --
+#
+# This function defines the subinterface hooks for the current
+# interface.
+#
+# Arguments:
+# names The ordered list of interfaces that are reachable through the
+# hook vector.
+#
+# Results:
+# None.
+
+proc genStubs::hooks {names} {
+ variable curName
+ variable hooks
+
+ set hooks($curName) $names
+ return
+}
+
+# genStubs::declare --
+#
+# This function is used in the declarations file to declare a new
+# interface entry.
+#
+# Arguments:
+# index The index number of the interface.
+# status Status of the interface: one of "current",
+# "deprecated", or "obsolete".
+# decl The C function declaration, or {} for an undefined
+# entry.
+#
+proc genStubs::declare {args} {
+ variable stubs
+ variable curName
+ variable revision
+
+ incr revision
+
+ if {[llength $args] == 2} {
+ lassign $args index decl
+ set status current
+ } elseif {[llength $args] == 3} {
+ lassign $args index status decl
+ } else {
+ puts stderr "wrong # args: declare $args"
+ return
+ }
+
+ # Check for duplicate declarations, then add the declaration and
+ # bump the lastNum counter if necessary.
+
+ if {[info exists stubs($curName,decl,$index)]} {
+ puts stderr "Duplicate entry: $index"
+ }
+ regsub -all const $decl CONST decl
+ regsub -all "\[ \t\n\]+" [string trim $decl] " " decl
+ set decl [parseDecl $decl]
+
+ set stubs($curName,status,$index) $status
+ set stubs($curName,decl,$index) $decl
+
+ if {$index > $stubs($curName,lastNum)} {
+ set stubs($curName,lastNum) $index
+ }
+
+ return
+}
+
+# genStubs::rewriteFile --
+#
+# This function replaces the machine generated portion of the
+# specified file with new contents. It looks for the !BEGIN! and
+# !END! comments to determine where to place the new text.
+#
+# Arguments:
+# file The name of the file to modify.
+# text The new text to place in the file.
+#
+# Results:
+# None.
+
+proc genStubs::rewriteFile {file text} {
+ if {![file exists $file]} {
+ puts stderr "Cannot find file: $file"
+ return
+ }
+ set in [open ${file} r]
+ set out [open ${file}.new w]
+ fconfigure $out -translation lf
+
+ while {![eof $in]} {
+ set line [gets $in]
+ if {[string match "*!BEGIN!*" $line]} {
+ break
+ }
+ puts $out $line
+ }
+ puts $out "/* !BEGIN!: Do not edit below this line. */"
+ puts $out $text
+ while {![eof $in]} {
+ set line [gets $in]
+ if {[string match "*!END!*" $line]} {
+ break
+ }
+ }
+ puts $out "/* !END!: Do not edit above this line. */"
+ puts -nonewline $out [read $in]
+ close $in
+ close $out
+ file rename -force ${file}.new ${file}
+ return
+}
+
+# genStubs::addPlatformGuard --
+#
+# Wrap a string inside a platform #ifdef.
+#
+# Arguments:
+# plat Platform to test.
+#
+# Results:
+# Returns the original text inside an appropriate #ifdef.
+
+proc genStubs::addPlatformGuard {plat text} {
+ switch $plat {
+ win {
+ return "#ifdef __WIN32__\n${text}#endif /* __WIN32__ */\n"
+ }
+ unix {
+ return "#if !defined(__WIN32__) /* UNIX */\n${text}#endif /* UNIX */\n"
+ }
+ macosx {
+ return "#ifdef MAC_OSX_TCL\n${text}#endif /* MAC_OSX_TCL */\n"
+ }
+ aqua {
+ return "#ifdef MAC_OSX_TK\n${text}#endif /* MAC_OSX_TK */\n"
+ }
+ x11 {
+ return "#if !(defined(__WIN32__) || defined(MAC_OSX_TK)) /* X11 */\n${text}#endif /* X11 */\n"
+ }
+ }
+ return $text
+}
+
+# genStubs::emitSlots --
+#
+# Generate the stub table slots for the given interface.
+#
+# Arguments:
+# name The name of the interface being emitted.
+# textVar The variable to use for output.
+#
+# Results:
+# None.
+
+proc genStubs::emitSlots {name textVar} {
+ upvar $textVar text
+ forAllStubs $name makeSlot noGuard text {" void (*reserved$i)(void);\n"}
+ return
+}
+
+# genStubs::parseDecl --
+#
+# Parse a C function declaration into its component parts.
+#
+# Arguments:
+# decl The function declaration.
+#
+# Results:
+# Returns a list of the form {returnType name args}. The args
+# element consists of a list of type/name pairs, or a single
+# element "void". If the function declaration is malformed
+# then an error is displayed and the return value is {}.
+
+proc genStubs::parseDecl {decl} {
+ if {![regexp {^(.*)\((.*)\);?$} $decl all prefix args]} {
+ set prefix $decl
+ set args {}
+ }
+ set prefix [string trim $prefix]
+ if {![regexp {^(.+[ ][*]*)([^ *]+)$} $prefix all rtype fname]} {
+ puts stderr "Bad return type: $decl"
+ return
+ }
+ set rtype [string trim $rtype]
+ if {$args == ""} {
+ return [list $rtype $fname {}]
+ }
+ foreach arg [split $args ,] {
+ lappend argList [string trim $arg]
+ }
+ if {![string compare [lindex $argList end] "..."]} {
+ set args TCL_VARARGS
+ foreach arg [lrange $argList 0 end-1] {
+ set argInfo [parseArg $arg]
+ if {[llength $argInfo] == 2 || [llength $argInfo] == 3} {
+ lappend args $argInfo
+ } else {
+ puts stderr "Bad argument: '$arg' in '$decl'"
+ return
+ }
+ }
+ } else {
+ set args {}
+ foreach arg $argList {
+ set argInfo [parseArg $arg]
+ if {![string compare $argInfo "void"]} {
+ lappend args "void"
+ break
+ } elseif {[llength $argInfo] == 2 || [llength $argInfo] == 3} {
+ lappend args $argInfo
+ } else {
+ puts stderr "Bad argument: '$arg' in '$decl'"
+ return
+ }
+ }
+ }
+ return [list $rtype $fname $args]
+}
+
+# genStubs::parseArg --
+#
+# This function parses a function argument into a type and name.
+#
+# Arguments:
+# arg The argument to parse.
+#
+# Results:
+# Returns a list of type and name with an optional third array
+# indicator. If the argument is malformed, returns "".
+
+proc genStubs::parseArg {arg} {
+ if {![regexp {^(.+[ ][*]*)([^][ *]+)(\[\])?$} $arg all type name array]} {
+ if {$arg == "void"} {
+ return $arg
+ } else {
+ return
+ }
+ }
+ set result [list [string trim $type] $name]
+ if {$array != ""} {
+ lappend result $array
+ }
+ return $result
+}
+
+# genStubs::makeDecl --
+#
+# Generate the prototype for a function.
+#
+# Arguments:
+# name The interface name.
+# decl The function declaration.
+# index The slot index for this function.
+#
+# Results:
+# Returns the formatted declaration string.
+
+proc genStubs::makeDecl {name decl index} {
+ variable scspec
+
+ lassign $decl rtype fname args
+
+ append text "/* $index */\n"
+ if {$rtype != "void"} {
+ regsub -all void $rtype VOID rtype
+ }
+ set line "$scspec $rtype"
+ set count [expr {2 - ([string length $line] / 8)}]
+ append line [string range "\t\t\t" 0 $count]
+ set pad [expr {24 - [string length $line]}]
+ if {$pad <= 0} {
+ append line " "
+ set pad 0
+ }
+ if {$args == ""} {
+ append line $fname
+ append text $line
+ append text ";\n"
+ return $text
+ }
+ append line $fname
+
+ regsub -all void $args VOID args
+ set arg1 [lindex $args 0]
+ switch -exact $arg1 {
+ VOID {
+ append line "(void)"
+ }
+ TCL_VARARGS {
+ set sep "("
+ foreach arg [lrange $args 1 end] {
+ append line $sep
+ set next {}
+ append next [lindex $arg 0]
+ if {[string index $next end] ne "*"} {
+ append next " "
+ }
+ append next [lindex $arg 1] [lindex $arg 2]
+ if {[string length $line] + [string length $next] \
+ + $pad > 76} {
+ append text [string trimright $line] \n
+ set line "\t\t\t\t"
+ set pad 28
+ }
+ append line $next
+ set sep ", "
+ }
+ append line ", ...)"
+ }
+ default {
+ set sep "("
+ foreach arg $args {
+ append line $sep
+ set next {}
+ append next [lindex $arg 0]
+ if {[string index $next end] ne "*"} {
+ append next " "
+ }
+ append next [lindex $arg 1] [lindex $arg 2]
+ if {[string length $line] + [string length $next] \
+ + $pad > 76} {
+ append text [string trimright $line] \n
+ set line "\t\t\t\t"
+ set pad 28
+ }
+ append line $next
+ set sep ", "
+ }
+ append line ")"
+ }
+ }
+ return "$text$line;\n"
+}
+
+# genStubs::makeMacro --
+#
+# Generate the inline macro for a function.
+#
+# Arguments:
+# name The interface name.
+# decl The function declaration.
+# index The slot index for this function.
+#
+# Results:
+# Returns the formatted macro definition.
+
+proc genStubs::makeMacro {name decl index} {
+ lassign $decl rtype fname args
+
+ set lfname [string tolower [string index $fname 0]]
+ append lfname [string range $fname 1 end]
+
+ set text "#define $fname \\\n\t("
+ if {$args == ""} {
+ append text "*"
+ }
+ append text "${name}StubsPtr->$lfname)"
+ append text " /* $index */\n"
+ return $text
+}
+
+# genStubs::makeSlot --
+#
+# Generate the stub table entry for a function.
+#
+# Arguments:
+# name The interface name.
+# decl The function declaration.
+# index The slot index for this function.
+#
+# Results:
+# Returns the formatted table entry.
+
+proc genStubs::makeSlot {name decl index} {
+ lassign $decl rtype fname args
+
+ set lfname [string tolower [string index $fname 0]]
+ append lfname [string range $fname 1 end]
+
+ set text " "
+ if {$rtype != "void"} {
+ regsub -all void $rtype VOID rtype
+ }
+ if {$args == ""} {
+ append text $rtype " *" $lfname "; /* $index */\n"
+ return $text
+ }
+ append text $rtype " (*" $lfname ") "
+
+ regsub -all void $args VOID args
+ set arg1 [lindex $args 0]
+ switch -exact $arg1 {
+ VOID {
+ append text "(void)"
+ }
+ TCL_VARARGS {
+ set sep "("
+ foreach arg [lrange $args 1 end] {
+ append text $sep [lindex $arg 0]
+ if {[string index $text end] ne "*"} {
+ append text " "
+ }
+ append text [lindex $arg 1] [lindex $arg 2]
+ set sep ", "
+ }
+ append text ", ...)"
+ }
+ default {
+ set sep "("
+ foreach arg $args {
+ append text $sep [lindex $arg 0]
+ if {[string index $text end] ne "*"} {
+ append text " "
+ }
+ append text [lindex $arg 1] [lindex $arg 2]
+ set sep ", "
+ }
+ append text ")"
+ }
+ }
+
+ append text "; /* $index */\n"
+ return $text
+}
+
+# genStubs::makeInit --
+#
+# Generate the prototype for a function.
+#
+# Arguments:
+# name The interface name.
+# decl The function declaration.
+# index The slot index for this function.
+#
+# Results:
+# Returns the formatted declaration string.
+
+proc genStubs::makeInit {name decl index} {
+ if {[lindex $decl 2] == ""} {
+ append text " &" [lindex $decl 1] ", /* " $index " */\n"
+ } else {
+ append text " " [lindex $decl 1] ", /* " $index " */\n"
+ }
+ return $text
+}
+
+# genStubs::forAllStubs --
+#
+# This function iterates over all of the slots and invokes
+# a callback for each slot. The result of the callback is then
+# placed inside appropriate guards.
+#
+# Arguments:
+# name The interface name.
+# slotProc The proc to invoke to handle the slot. It will
+# have the interface name, the declaration, and
+# the index appended.
+# guardProc The proc to invoke to add guards. It will have
+# the slot status and text appended.
+# textVar The variable to use for output.
+# skipString The string to emit if a slot is skipped. This
+# string will be subst'ed in the loop so "$i" can
+# be used to substitute the index value.
+#
+# Results:
+# None.
+
+proc genStubs::forAllStubs {name slotProc guardProc textVar
+ {skipString {"/* Slot $i is reserved */\n"}}} {
+ variable stubs
+ upvar $textVar text
+
+ set lastNum $stubs($name,lastNum)
+
+ for {set i 0} {$i <= $lastNum} {incr i} {
+ if {[info exists stubs($name,decl,$i)]} {
+ append text [$guardProc $stubs($name,status,$i) \
+ [$slotProc $name $stubs($name,decl,$i) $i]]
+ } else {
+ eval {append text} $skipString
+ }
+ }
+}
+
+proc genStubs::noGuard {status text} { return $text }
+
+proc genStubs::addGuard {status text} {
+ variable libraryName
+ set upName [string toupper $libraryName]
+
+ switch -- $status {
+ current {
+ # No change
+ }
+ deprecated {
+ set text [ifdeffed "${upName}_DEPRECATED" $text]
+ }
+ obsolete {
+ set text ""
+ }
+ default {
+ puts stderr "Unrecognized status code $status"
+ }
+ }
+ return $text
+}
+
+proc genStubs::ifdeffed {macro text} {
+ join [list "#ifdef $macro" $text "#endif" ""] \n
+}
+
+# genStubs::emitDeclarations --
+#
+# This function emits the function declarations for this interface.
+#
+# Arguments:
+# name The interface name.
+# textVar The variable to use for output.
+#
+# Results:
+# None.
+
+proc genStubs::emitDeclarations {name textVar} {
+ upvar $textVar text
+
+ append text "\n/*\n * Exported function declarations:\n */\n\n"
+ forAllStubs $name makeDecl noGuard text
+ return
+}
+
+# genStubs::emitMacros --
+#
+# This function emits the inline macros for an interface.
+#
+# Arguments:
+# name The name of the interface being emitted.
+# textVar The variable to use for output.
+#
+# Results:
+# None.
+
+proc genStubs::emitMacros {name textVar} {
+ variable libraryName
+ upvar $textVar text
+
+ set upName [string toupper $libraryName]
+ append text "\n#if defined(USE_${upName}_STUBS)\n"
+ append text "\n/*\n * Inline function declarations:\n */\n\n"
+
+ forAllStubs $name makeMacro addGuard text
+
+ append text "\n#endif /* defined(USE_${upName}_STUBS) */\n"
+ return
+}
+
+# genStubs::emitHeader --
+#
+# This function emits the body of the <name>Decls.h file for
+# the specified interface.
+#
+# Arguments:
+# name The name of the interface being emitted.
+#
+# Results:
+# None.
+
+proc genStubs::emitHeader {name} {
+ variable outDir
+ variable hooks
+ variable epoch
+ variable revision
+
+ set capName [string toupper [string index $name 0]]
+ append capName [string range $name 1 end]
+
+ set CAPName [string toupper $name]
+ append text "\n"
+ append text "#define ${CAPName}_STUBS_EPOCH $epoch\n"
+ append text "#define ${CAPName}_STUBS_REVISION $revision\n"
+
+ emitDeclarations $name text
+
+ if {[info exists hooks($name)]} {
+ append text "\ntypedef struct ${capName}StubHooks {\n"
+ foreach hook $hooks($name) {
+ set capHook [string toupper [string index $hook 0]]
+ append capHook [string range $hook 1 end]
+ append text " const struct ${capHook}Stubs *${hook}Stubs;\n"
+ }
+ append text "} ${capName}StubHooks;\n"
+ }
+ append text "\ntypedef struct ${capName}Stubs {\n"
+ append text " int magic;\n"
+ append text " int epoch;\n"
+ append text " int revision;\n"
+ append text " const struct ${capName}StubHooks *hooks;\n\n"
+
+ emitSlots $name text
+
+ append text "} ${capName}Stubs;\n\n"
+
+ append text "#ifdef __cplusplus\nextern \"C\" {\n#endif\n"
+ append text "extern const ${capName}Stubs *${name}StubsPtr;\n"
+ append text "#ifdef __cplusplus\n}\n#endif\n"
+
+ emitMacros $name text
+
+ rewriteFile [file join $outDir ${name}Decls.h] $text
+ return
+}
+
+# genStubs::emitInit --
+#
+# Generate the table initializers for an interface.
+#
+# Arguments:
+# name The name of the interface to initialize.
+# textVar The variable to use for output.
+#
+# Results:
+# Returns the formatted output.
+
+proc genStubs::emitInit {name textVar} {
+ variable hooks
+ variable interfaces
+ variable epoch
+ variable revision
+
+ upvar $textVar text
+ set root 1
+
+ set capName [string toupper [string index $name 0]]
+ append capName [string range $name 1 end]
+ set CAPName [string toupper $name]
+
+ if {[info exists hooks($name)]} {
+ append text "\nstatic const ${capName}StubHooks ${name}StubHooks = \{\n"
+ set sep " "
+ foreach sub $hooks($name) {
+ append text $sep "&${sub}Stubs"
+ set sep ",\n "
+ }
+ append text "\n\};\n"
+ }
+ foreach intf [array names interfaces] {
+ if {[info exists hooks($intf)]} {
+ if {0<=[lsearch -exact $hooks($intf) $name]} {
+ set root 0
+ break;
+ }
+ }
+ }
+
+ if {$root} {
+ append text "\nconst ${capName}Stubs ${name}Stubs = \{\n"
+ } else {
+ append text "\nstatic const ${capName}Stubs ${name}Stubs = \{\n"
+ }
+ append text " TCL_STUB_MAGIC,\n"
+ append text " ${CAPName}_STUBS_EPOCH,\n"
+ append text " ${CAPName}_STUBS_REVISION,\n"
+ if {[info exists hooks($name)]} {
+ append text " &${name}StubHooks,\n"
+ } else {
+ append text " 0,\n"
+ }
+
+ forAllStubs $name makeInit noGuard text {" 0, /* $i */\n"}
+
+ append text "\};\n"
+ return
+}
+
+# genStubs::emitInits --
+#
+# This function emits the body of the <name>StubInit.c file for
+# the specified interface.
+#
+# Arguments:
+# name The name of the interface being emitted.
+#
+# Results:
+# None.
+
+proc genStubs::emitInits {} {
+ variable hooks
+ variable outDir
+ variable libraryName
+ variable interfaces
+
+ # Assuming that dependencies only go one level deep, we need to emit
+ # all of the leaves first to avoid needing forward declarations.
+
+ set leaves {}
+ set roots {}
+ foreach name [lsort [array names interfaces]] {
+ if {[info exists hooks($name)]} {
+ lappend roots $name
+ } else {
+ lappend leaves $name
+ }
+ }
+ foreach name $leaves {
+ emitInit $name text
+ }
+ foreach name $roots {
+ emitInit $name text
+ }
+
+ rewriteFile [file join $outDir ${libraryName}StubInit.c] $text
+}
+
+# genStubs::init --
+#
+# This is the main entry point.
+#
+# Arguments:
+# None.
+#
+# Results:
+# None.
+
+proc genStubs::init {} {
+ global argv argv0
+ variable outDir
+ variable interfaces
+
+ if {[llength $argv] < 2} {
+ puts stderr "usage: $argv0 outDir declFile ?declFile...?"
+ exit 1
+ }
+
+ set outDir [lindex $argv 0]
+
+ foreach file [lrange $argv 1 end] {
+ source $file
+ }
+
+ foreach name [lsort [array names interfaces]] {
+ puts "Emitting $name"
+ emitHeader $name
+ }
+
+ emitInits
+}
+
+# lassign --
+#
+# This function emulates the TclX lassign command.
+#
+# Arguments:
+# valueList A list containing the values to be assigned.
+# args The list of variables to be assigned.
+#
+# Results:
+# Returns any values that were not assigned to variables.
+
+if {[string length [namespace which lassign]] == 0} {
+ proc lassign {valueList args} {
+ if {[llength $args] == 0} {
+ error "wrong # args: should be \"lassign list varName ?varName ...?\""
+ }
+ uplevel [list foreach $args $valueList {break}]
+ return [lrange $valueList [llength $args] end]
+ }
+}
+
+genStubs::init
diff --git a/generic/ttk/ttkImage.c b/generic/ttk/ttkImage.c
new file mode 100644
index 0000000..2b12864
--- /dev/null
+++ b/generic/ttk/ttkImage.c
@@ -0,0 +1,415 @@
+/*
+ * Image specifications and image element factory.
+ *
+ * Copyright (C) 2004 Pat Thoyts <patthoyts@users.sf.net>
+ * Copyright (C) 2004 Joe English
+ *
+ * An imageSpec is a multi-element list; the first element
+ * is the name of the default image to use, the remainder of the
+ * list is a sequence of statespec/imagename options as per
+ * [style map].
+ */
+
+#include <string.h>
+#include <tk.h>
+#include "ttkTheme.h"
+
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+
+/*------------------------------------------------------------------------
+ * +++ ImageSpec management.
+ */
+
+struct TtkImageSpec {
+ Tk_Image baseImage; /* Base image to use */
+ int mapCount; /* #state-specific overrides */
+ Ttk_StateSpec *states; /* array[mapCount] of states ... */
+ Tk_Image *images; /* ... per-state images to use */
+};
+
+/* NullImageChanged --
+ * Do-nothing Tk_ImageChangedProc.
+ */
+static void NullImageChanged(ClientData clientData,
+ int x, int y, int width, int height, int imageWidth, int imageHeight)
+{ /* No-op */ }
+
+/* TtkGetImageSpec --
+ * Constructs a Ttk_ImageSpec * from a Tcl_Obj *.
+ * Result must be released using TtkFreeImageSpec.
+ *
+ * TODO: Need a variant of this that takes a user-specified ImageChanged proc
+ */
+Ttk_ImageSpec *
+TtkGetImageSpec(Tcl_Interp *interp, Tk_Window tkwin, Tcl_Obj *objPtr)
+{
+ Ttk_ImageSpec *imageSpec = 0;
+ int i = 0, n = 0, objc;
+ Tcl_Obj **objv;
+
+ imageSpec = (Ttk_ImageSpec *)ckalloc(sizeof(*imageSpec));
+ imageSpec->baseImage = 0;
+ imageSpec->mapCount = 0;
+ imageSpec->states = 0;
+ imageSpec->images = 0;
+
+ if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) {
+ goto error;
+ }
+
+ if ((objc % 2) != 1) {
+ if (interp) {
+ Tcl_SetResult(interp,
+ "image specification must contain an odd number of elements",
+ TCL_STATIC);
+ }
+ goto error;
+ }
+
+ n = (objc - 1) / 2;
+ imageSpec->states = (Ttk_StateSpec*)ckalloc(n * sizeof(Ttk_StateSpec));
+ imageSpec->images = (Tk_Image*)ckalloc(n * sizeof(Tk_Image *));
+
+ /* Get base image:
+ */
+ imageSpec->baseImage = Tk_GetImage(
+ interp, tkwin, Tcl_GetString(objv[0]), NullImageChanged, NULL);
+ if (!imageSpec->baseImage) {
+ goto error;
+ }
+
+ /* Extract state and image specifications:
+ */
+ for (i = 0; i < n; ++i) {
+ Tcl_Obj *stateSpec = objv[2*i + 1];
+ const char *imageName = Tcl_GetString(objv[2*i + 2]);
+ Ttk_StateSpec state;
+
+ if (Ttk_GetStateSpecFromObj(interp, stateSpec, &state) != TCL_OK) {
+ goto error;
+ }
+ imageSpec->states[i] = state;
+
+ imageSpec->images[i] = Tk_GetImage(
+ interp, tkwin, imageName, NullImageChanged, NULL);
+ if (imageSpec->images[i] == NULL) {
+ goto error;
+ }
+ imageSpec->mapCount = i+1;
+ }
+
+ return imageSpec;
+
+error:
+ TtkFreeImageSpec(imageSpec);
+ return NULL;
+}
+
+/* TtkFreeImageSpec --
+ * Dispose of an image specification.
+ */
+void TtkFreeImageSpec(Ttk_ImageSpec *imageSpec)
+{
+ int i;
+
+ for (i=0; i < imageSpec->mapCount; ++i) {
+ Tk_FreeImage(imageSpec->images[i]);
+ }
+
+ if (imageSpec->baseImage) { Tk_FreeImage(imageSpec->baseImage); }
+ if (imageSpec->states) { ckfree((ClientData)imageSpec->states); }
+ if (imageSpec->images) { ckfree((ClientData)imageSpec->images); }
+
+ ckfree((ClientData)imageSpec);
+}
+
+/* TtkSelectImage --
+ * Return a state-specific image from an ImageSpec
+ */
+Tk_Image TtkSelectImage(Ttk_ImageSpec *imageSpec, Ttk_State state)
+{
+ int i;
+ for (i = 0; i < imageSpec->mapCount; ++i) {
+ if (Ttk_StateMatches(state, imageSpec->states+i)) {
+ return imageSpec->images[i];
+ }
+ }
+ return imageSpec->baseImage;
+}
+
+/*------------------------------------------------------------------------
+ * +++ Drawing utilities.
+ */
+
+/* LPadding, CPadding, RPadding --
+ * Split a box+padding pair into left, center, and right boxes.
+ */
+static Ttk_Box LPadding(Ttk_Box b, Ttk_Padding p)
+ { return Ttk_MakeBox(b.x, b.y, p.left, b.height); }
+
+static Ttk_Box CPadding(Ttk_Box b, Ttk_Padding p)
+ { return Ttk_MakeBox(b.x+p.left, b.y, b.width-p.left-p.right, b.height); }
+
+static Ttk_Box RPadding(Ttk_Box b, Ttk_Padding p)
+ { return Ttk_MakeBox(b.x+b.width-p.right, b.y, p.right, b.height); }
+
+/* TPadding, MPadding, BPadding --
+ * Split a box+padding pair into top, middle, and bottom parts.
+ */
+static Ttk_Box TPadding(Ttk_Box b, Ttk_Padding p)
+ { return Ttk_MakeBox(b.x, b.y, b.width, p.top); }
+
+static Ttk_Box MPadding(Ttk_Box b, Ttk_Padding p)
+ { return Ttk_MakeBox(b.x, b.y+p.top, b.width, b.height-p.top-p.bottom); }
+
+static Ttk_Box BPadding(Ttk_Box b, Ttk_Padding p)
+ { return Ttk_MakeBox(b.x, b.y+b.height-p.bottom, b.width, p.bottom); }
+
+/* Ttk_Fill --
+ * Fill the destination area of the drawable by replicating
+ * the source area of the image.
+ */
+static void Ttk_Fill(
+ Tk_Window tkwin, Drawable d, Tk_Image image, Ttk_Box src, Ttk_Box dst)
+{
+ int dr = dst.x + dst.width;
+ int db = dst.y + dst.height;
+ int x,y;
+
+ if (!(src.width && src.height && dst.width && dst.height))
+ return;
+
+ for (x = dst.x; x < dr; x += src.width) {
+ int cw = MIN(src.width, dr - x);
+ for (y = dst.y; y <= db; y += src.height) {
+ int ch = MIN(src.height, db - y);
+ Tk_RedrawImage(image, src.x, src.y, cw, ch, d, x, y);
+ }
+ }
+}
+
+/* Ttk_Stripe --
+ * Fill a horizontal stripe of the destination drawable.
+ */
+static void Ttk_Stripe(
+ Tk_Window tkwin, Drawable d, Tk_Image image,
+ Ttk_Box src, Ttk_Box dst, Ttk_Padding p)
+{
+ Ttk_Fill(tkwin, d, image, LPadding(src,p), LPadding(dst,p));
+ Ttk_Fill(tkwin, d, image, CPadding(src,p), CPadding(dst,p));
+ Ttk_Fill(tkwin, d, image, RPadding(src,p), RPadding(dst,p));
+}
+
+/* Ttk_Tile --
+ * Fill successive horizontal stripes of the destination drawable.
+ */
+static void Ttk_Tile(
+ Tk_Window tkwin, Drawable d, Tk_Image image,
+ Ttk_Box src, Ttk_Box dst, Ttk_Padding p)
+{
+ Ttk_Stripe(tkwin, d, image, TPadding(src,p), TPadding(dst,p), p);
+ Ttk_Stripe(tkwin, d, image, MPadding(src,p), MPadding(dst,p), p);
+ Ttk_Stripe(tkwin, d, image, BPadding(src,p), BPadding(dst,p), p);
+}
+
+/*------------------------------------------------------------------------
+ * +++ Image element definition.
+ */
+
+typedef struct { /* ClientData for image elements */
+ Ttk_ImageSpec *imageSpec; /* Image(s) to use */
+ int minWidth; /* Minimum width; overrides image width */
+ int minHeight; /* Minimum width; overrides image width */
+ Ttk_Sticky sticky; /* -stickiness specification */
+ Ttk_Padding border; /* Fixed border region */
+ Ttk_Padding padding; /* Internal padding */
+
+#if TILE_07_COMPAT
+ Ttk_ResourceCache cache; /* Resource cache for images */
+ Ttk_StateMap imageMap; /* State-based lookup table for images */
+#endif
+} ImageData;
+
+static void FreeImageData(void *clientData)
+{
+ ImageData *imageData = clientData;
+ if (imageData->imageSpec) { TtkFreeImageSpec(imageData->imageSpec); }
+#if TILE_07_COMPAT
+ if (imageData->imageMap) { Tcl_DecrRefCount(imageData->imageMap); }
+#endif
+ ckfree(clientData);
+}
+
+static void ImageElementSize(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+{
+ ImageData *imageData = clientData;
+ Tk_Image image = imageData->imageSpec->baseImage;
+
+ if (image) {
+ Tk_SizeOfImage(image, widthPtr, heightPtr);
+ }
+ if (imageData->minWidth >= 0) {
+ *widthPtr = imageData->minWidth;
+ }
+ if (imageData->minHeight >= 0) {
+ *heightPtr = imageData->minHeight;
+ }
+
+ *paddingPtr = imageData->padding;
+}
+
+static void ImageElementDraw(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ Drawable d, Ttk_Box b, unsigned int state)
+{
+ ImageData *imageData = clientData;
+ Tk_Image image = 0;
+ int imgWidth, imgHeight;
+ Ttk_Box src, dst;
+
+#if TILE_07_COMPAT
+ if (imageData->imageMap) {
+ Tcl_Obj *imageObj = Ttk_StateMapLookup(NULL,imageData->imageMap,state);
+ if (imageObj) {
+ image = Ttk_UseImage(imageData->cache, tkwin, imageObj);
+ }
+ }
+ if (!image) {
+ image = TtkSelectImage(imageData->imageSpec, state);
+ }
+#else
+ image = TtkSelectImage(imageData->imageSpec, state);
+#endif
+
+ if (!image) {
+ return;
+ }
+
+ Tk_SizeOfImage(image, &imgWidth, &imgHeight);
+ src = Ttk_MakeBox(0, 0, imgWidth, imgHeight);
+ dst = Ttk_StickBox(b, imgWidth, imgHeight, imageData->sticky);
+
+ Ttk_Tile(tkwin, d, image, src, dst, imageData->border);
+}
+
+static Ttk_ElementSpec ImageElementSpec =
+{
+ TK_STYLE_VERSION_2,
+ sizeof(NullElement),
+ TtkNullElementOptions,
+ ImageElementSize,
+ ImageElementDraw
+};
+
+/*------------------------------------------------------------------------
+ * +++ Image element factory.
+ */
+static int
+Ttk_CreateImageElement(
+ Tcl_Interp *interp,
+ void *clientData,
+ Ttk_Theme theme,
+ const char *elementName,
+ int objc, Tcl_Obj *const objv[])
+{
+ static const char *optionStrings[] =
+ { "-border","-height","-padding","-sticky","-width",NULL };
+ enum { O_BORDER, O_HEIGHT, O_PADDING, O_STICKY, O_WIDTH };
+
+ Ttk_ImageSpec *imageSpec = 0;
+ ImageData *imageData = 0;
+ int padding_specified = 0;
+ int i;
+
+ if (objc <= 0) {
+ Tcl_AppendResult(interp, "Must supply a base image", NULL);
+ return TCL_ERROR;
+ }
+
+ imageSpec = TtkGetImageSpec(interp, Tk_MainWindow(interp), objv[0]);
+ if (!imageSpec) {
+ return TCL_ERROR;
+ }
+
+ imageData = (ImageData*)ckalloc(sizeof(*imageData));
+ imageData->imageSpec = imageSpec;
+ imageData->minWidth = imageData->minHeight = -1;
+ imageData->sticky = TTK_FILL_BOTH;
+ imageData->border = imageData->padding = Ttk_UniformPadding(0);
+#if TILE_07_COMPAT
+ imageData->cache = Ttk_GetResourceCache(interp);
+ imageData->imageMap = 0;
+#endif
+
+ for (i = 1; i < objc; i += 2) {
+ int option;
+
+ if (i == objc - 1) {
+ Tcl_AppendResult(interp,
+ "Value for ", Tcl_GetString(objv[i]), " missing",
+ NULL);
+ goto error;
+ }
+
+#if TILE_07_COMPAT
+ if (!strcmp("-map", Tcl_GetString(objv[i]))) {
+ imageData->imageMap = objv[i+1];
+ Tcl_IncrRefCount(imageData->imageMap);
+ continue;
+ }
+#endif
+
+ if (Tcl_GetIndexFromObj(interp, objv[i], optionStrings,
+ "option", 0, &option) != TCL_OK) { goto error; }
+
+ switch (option) {
+ case O_BORDER:
+ if (Ttk_GetBorderFromObj(interp, objv[i+1], &imageData->border)
+ != TCL_OK) { goto error; }
+ if (!padding_specified) {
+ imageData->padding = imageData->border;
+ }
+ break;
+ case O_PADDING:
+ if (Ttk_GetBorderFromObj(interp, objv[i+1], &imageData->padding)
+ != TCL_OK) { goto error; }
+ padding_specified = 1;
+ break;
+ case O_WIDTH:
+ if (Tcl_GetIntFromObj(interp, objv[i+1], &imageData->minWidth)
+ != TCL_OK) { goto error; }
+ break;
+ case O_HEIGHT:
+ if (Tcl_GetIntFromObj(interp, objv[i+1], &imageData->minHeight)
+ != TCL_OK) { goto error; }
+ break;
+ case O_STICKY:
+ if (Ttk_GetStickyFromObj(interp, objv[i+1], &imageData->sticky)
+ != TCL_OK) { goto error; }
+ }
+ }
+
+ if (!Ttk_RegisterElement(interp, theme, elementName, &ImageElementSpec,
+ imageData))
+ {
+ goto error;
+ }
+
+ Ttk_RegisterCleanup(interp, imageData, FreeImageData);
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(elementName, -1));
+ return TCL_OK;
+
+error:
+ FreeImageData(imageData);
+ return TCL_ERROR;
+}
+
+MODULE_SCOPE
+void TtkImage_Init(Tcl_Interp *interp)
+{
+ Ttk_RegisterElementFactory(interp, "image", Ttk_CreateImageElement, NULL);
+}
+
+/*EOF*/
diff --git a/generic/ttk/ttkInit.c b/generic/ttk/ttkInit.c
new file mode 100644
index 0000000..78676c6
--- /dev/null
+++ b/generic/ttk/ttkInit.c
@@ -0,0 +1,282 @@
+/*
+ * Copyright (c) 2003, Joe English
+ *
+ * Ttk package: initialization routine and miscellaneous utilities.
+ */
+
+#include <string.h>
+#include <tk.h>
+#include "ttkTheme.h"
+#include "ttkWidget.h"
+
+/*
+ * Legal values for the button -default option.
+ * See also: enum Ttk_ButtonDefaultState.
+ */
+const char *ttkDefaultStrings[] = {
+ "normal", "active", "disabled", NULL
+};
+
+int Ttk_GetButtonDefaultStateFromObj(
+ Tcl_Interp *interp, Tcl_Obj *objPtr, int *statePtr)
+{
+ *statePtr = TTK_BUTTON_DEFAULT_DISABLED;
+ return Tcl_GetIndexFromObj(interp, objPtr,
+ ttkDefaultStrings, "default state", 0, statePtr);
+}
+
+/*
+ * Legal values for the -compound option.
+ * See also: enum Ttk_Compound.
+ */
+const char *ttkCompoundStrings[] = {
+ "none", "text", "image", "center",
+ "top", "bottom", "left", "right", NULL
+};
+
+int Ttk_GetCompoundFromObj(
+ Tcl_Interp *interp, Tcl_Obj *objPtr, int *statePtr)
+{
+ *statePtr = TTK_COMPOUND_NONE;
+ return Tcl_GetIndexFromObj(interp, objPtr,
+ ttkCompoundStrings, "compound layout", 0, statePtr);
+}
+
+/*
+ * Legal values for the -orient option.
+ * See also: enum Ttk_Orient.
+ */
+const char *ttkOrientStrings[] = {
+ "horizontal", "vertical", NULL
+};
+
+int Ttk_GetOrientFromObj(
+ Tcl_Interp *interp, Tcl_Obj *objPtr, int *resultPtr)
+{
+ *resultPtr = TTK_ORIENT_HORIZONTAL;
+ return Tcl_GetIndexFromObj(interp, objPtr,
+ ttkOrientStrings, "orientation", 0, resultPtr);
+}
+
+/*
+ * Recognized values for the -state compatibility option.
+ * Other options are accepted and interpreted as synonyms for "normal".
+ */
+static const char *ttkStateStrings[] = {
+ "normal", "readonly", "disabled", "active", NULL
+};
+enum {
+ TTK_COMPAT_STATE_NORMAL,
+ TTK_COMPAT_STATE_READONLY,
+ TTK_COMPAT_STATE_DISABLED,
+ TTK_COMPAT_STATE_ACTIVE
+};
+
+/* TtkCheckStateOption --
+ * Handle -state compatibility option.
+ *
+ * NOTE: setting -state disabled / -state enabled affects the
+ * widget state, but the internal widget state does *not* affect
+ * the value of the -state option.
+ * This option is present for compatibility only.
+ */
+void TtkCheckStateOption(WidgetCore *corePtr, Tcl_Obj *objPtr)
+{
+ int stateOption = TTK_COMPAT_STATE_NORMAL;
+ unsigned all = TTK_STATE_DISABLED|TTK_STATE_READONLY|TTK_STATE_ACTIVE;
+# define SETFLAGS(f) TtkWidgetChangeState(corePtr, f, all^f)
+
+ (void)Tcl_GetIndexFromObj(NULL,objPtr,ttkStateStrings,"",0,&stateOption);
+ switch (stateOption) {
+ case TTK_COMPAT_STATE_NORMAL:
+ default:
+ SETFLAGS(0);
+ break;
+ case TTK_COMPAT_STATE_READONLY:
+ SETFLAGS(TTK_STATE_READONLY);
+ break;
+ case TTK_COMPAT_STATE_DISABLED:
+ SETFLAGS(TTK_STATE_DISABLED);
+ break;
+ case TTK_COMPAT_STATE_ACTIVE:
+ SETFLAGS(TTK_STATE_ACTIVE);
+ break;
+ }
+# undef SETFLAGS
+}
+
+/* TtkSendVirtualEvent --
+ * Send a virtual event notification to the specified target window.
+ * Equivalent to "event generate $tgtWindow <<$eventName>>"
+ *
+ * Note that we use Tk_QueueWindowEvent, not Tk_HandleEvent,
+ * so this routine does not reenter the interpreter.
+ */
+void TtkSendVirtualEvent(Tk_Window tgtWin, const char *eventName)
+{
+ union {XEvent general; XVirtualEvent virtual;} event;
+
+ memset(&event, 0, sizeof(event));
+ event.general.xany.type = VirtualEvent;
+ event.general.xany.serial = NextRequest(Tk_Display(tgtWin));
+ event.general.xany.send_event = False;
+ event.general.xany.window = Tk_WindowId(tgtWin);
+ event.general.xany.display = Tk_Display(tgtWin);
+ event.virtual.name = Tk_GetUid(eventName);
+
+ Tk_QueueWindowEvent(&event.general, TCL_QUEUE_TAIL);
+}
+
+/* TtkEnumerateOptions, TtkGetOptionValue --
+ * Common factors for data accessor commands.
+ */
+int TtkEnumerateOptions(
+ Tcl_Interp *interp, void *recordPtr, const Tk_OptionSpec *specPtr,
+ Tk_OptionTable optionTable, Tk_Window tkwin)
+{
+ Tcl_Obj *result = Tcl_NewListObj(0,0);
+ while (specPtr->type != TK_OPTION_END)
+ {
+ Tcl_Obj *optionName = Tcl_NewStringObj(specPtr->optionName, -1);
+ Tcl_Obj *optionValue =
+ Tk_GetOptionValue(interp,recordPtr,optionTable,optionName,tkwin);
+ if (optionValue) {
+ Tcl_ListObjAppendElement(interp, result, optionName);
+ Tcl_ListObjAppendElement(interp, result, optionValue);
+ }
+ ++specPtr;
+
+ if (specPtr->type == TK_OPTION_END && specPtr->clientData != NULL) {
+ /* Chain to next option spec array: */
+ specPtr = specPtr->clientData;
+ }
+ }
+ Tcl_SetObjResult(interp, result);
+ return TCL_OK;
+}
+
+int TtkGetOptionValue(
+ Tcl_Interp *interp, void *recordPtr, Tcl_Obj *optionName,
+ Tk_OptionTable optionTable, Tk_Window tkwin)
+{
+ Tcl_Obj *result =
+ Tk_GetOptionValue(interp,recordPtr,optionTable,optionName,tkwin);
+ if (result) {
+ Tcl_SetObjResult(interp, result);
+ return TCL_OK;
+ }
+ return TCL_ERROR;
+}
+
+
+/*------------------------------------------------------------------------
+ * Core Option specifications:
+ * type name dbName dbClass default objOffset intOffset flags clientData mask
+ */
+
+/* public */
+Tk_OptionSpec ttkCoreOptionSpecs[] =
+{
+ {TK_OPTION_CURSOR, "-cursor", "cursor", "Cursor", NULL,
+ Tk_Offset(WidgetCore, cursorObj), -1, TK_OPTION_NULL_OK,0,0 },
+ {TK_OPTION_STRING, "-style", "style", "Style", "",
+ Tk_Offset(WidgetCore,styleObj), -1, 0,0,STYLE_CHANGED},
+ {TK_OPTION_STRING, "-class", "", "", NULL,
+ Tk_Offset(WidgetCore,classObj), -1, 0,0,READONLY_OPTION},
+ {TK_OPTION_END, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0}
+};
+
+/*------------------------------------------------------------------------
+ * +++ Initialization: elements and element factories.
+ */
+
+extern void TtkElements_Init(Tcl_Interp *);
+extern void TtkLabel_Init(Tcl_Interp *);
+extern void TtkImage_Init(Tcl_Interp *);
+
+static void RegisterElements(Tcl_Interp *interp)
+{
+ TtkElements_Init(interp);
+ TtkLabel_Init(interp);
+ TtkImage_Init(interp);
+}
+
+/*------------------------------------------------------------------------
+ * +++ Initialization: Widget definitions.
+ */
+
+extern void TtkButton_Init(Tcl_Interp *);
+extern void TtkEntry_Init(Tcl_Interp *);
+extern void TtkFrame_Init(Tcl_Interp *);
+extern void TtkNotebook_Init(Tcl_Interp *);
+extern void TtkPanedwindow_Init(Tcl_Interp *);
+extern void TtkProgressbar_Init(Tcl_Interp *);
+extern void TtkScale_Init(Tcl_Interp *);
+extern void TtkScrollbar_Init(Tcl_Interp *);
+extern void TtkSeparator_Init(Tcl_Interp *);
+extern void TtkTreeview_Init(Tcl_Interp *);
+
+#ifdef TTK_SQUARE_WIDGET
+extern int TtkSquareWidget_Init(Tcl_Interp *);
+#endif
+
+static void RegisterWidgets(Tcl_Interp *interp)
+{
+ TtkButton_Init(interp);
+ TtkEntry_Init(interp);
+ TtkFrame_Init(interp);
+ TtkNotebook_Init(interp);
+ TtkPanedwindow_Init(interp);
+ TtkProgressbar_Init(interp);
+ TtkScale_Init(interp);
+ TtkScrollbar_Init(interp);
+ TtkSeparator_Init(interp);
+ TtkTreeview_Init(interp);
+#ifdef TTK_SQUARE_WIDGET
+ TtkSquareWidget_Init(interp);
+#endif
+}
+
+/*------------------------------------------------------------------------
+ * +++ Initialization: Built-in themes.
+ */
+
+extern int TtkAltTheme_Init(Tcl_Interp *);
+extern int TtkClassicTheme_Init(Tcl_Interp *);
+extern int TtkClamTheme_Init(Tcl_Interp *);
+
+static void RegisterThemes(Tcl_Interp *interp)
+{
+
+ TtkAltTheme_Init(interp);
+ TtkClassicTheme_Init(interp);
+ TtkClamTheme_Init(interp);
+}
+
+/*
+ * Ttk initialization.
+ */
+
+extern const TtkStubs ttkStubs;
+
+MODULE_SCOPE int
+Ttk_Init(Tcl_Interp *interp)
+{
+ /*
+ * This will be run for both safe and regular interp init.
+ * Use Tcl_IsSafe if necessary to not initialize unsafe bits.
+ */
+ Ttk_StylePkgInit(interp);
+
+ RegisterElements(interp);
+ RegisterWidgets(interp);
+ RegisterThemes(interp);
+
+ Ttk_PlatformInit(interp);
+
+ Tcl_PkgProvideEx(interp, "Ttk", TTK_PATCH_LEVEL, (ClientData)&ttkStubs);
+
+ return TCL_OK;
+}
+
+/*EOF*/
diff --git a/generic/ttk/ttkLabel.c b/generic/ttk/ttkLabel.c
new file mode 100644
index 0000000..597682f
--- /dev/null
+++ b/generic/ttk/ttkLabel.c
@@ -0,0 +1,693 @@
+/*
+ * text, image, and label elements.
+ *
+ * The label element combines text and image elements,
+ * with layout determined by the "-compound" option.
+ *
+ */
+
+#include <tcl.h>
+#include <tkInt.h>
+#include "ttkTheme.h"
+
+/*----------------------------------------------------------------------
+ * +++ Text element.
+ *
+ * This element displays a textual label in the foreground color.
+ *
+ * Optionally underlines the mnemonic character if the -underline resource
+ * is present and >= 0.
+ */
+
+typedef struct {
+ /*
+ * Element options:
+ */
+ Tcl_Obj *textObj;
+ Tcl_Obj *fontObj;
+ Tcl_Obj *foregroundObj;
+ Tcl_Obj *underlineObj;
+ Tcl_Obj *widthObj;
+ Tcl_Obj *anchorObj;
+ Tcl_Obj *justifyObj;
+ Tcl_Obj *wrapLengthObj;
+ Tcl_Obj *embossedObj;
+
+ /*
+ * Computed resources:
+ */
+ Tk_Font tkfont;
+ Tk_TextLayout textLayout;
+ int width;
+ int height;
+ int embossed;
+
+} TextElement;
+
+/* Text element options table.
+ * NB: Keep in sync with label element option table.
+ */
+static Ttk_ElementOptionSpec TextElementOptions[] = {
+ { "-text", TK_OPTION_STRING,
+ Tk_Offset(TextElement,textObj), "" },
+ { "-font", TK_OPTION_FONT,
+ Tk_Offset(TextElement,fontObj), DEFAULT_FONT },
+ { "-foreground", TK_OPTION_COLOR,
+ Tk_Offset(TextElement,foregroundObj), "black" },
+ { "-underline", TK_OPTION_INT,
+ Tk_Offset(TextElement,underlineObj), "-1"},
+ { "-width", TK_OPTION_INT,
+ Tk_Offset(TextElement,widthObj), "-1"},
+ { "-anchor", TK_OPTION_ANCHOR,
+ Tk_Offset(TextElement,anchorObj), "w"},
+ { "-justify", TK_OPTION_JUSTIFY,
+ Tk_Offset(TextElement,justifyObj), "left" },
+ { "-wraplength", TK_OPTION_PIXELS,
+ Tk_Offset(TextElement,wrapLengthObj), "0" },
+ { "-embossed", TK_OPTION_INT,
+ Tk_Offset(TextElement,embossedObj), "0"},
+ { NULL, 0, 0, NULL }
+};
+
+static int TextSetup(TextElement *text, Tk_Window tkwin)
+{
+ const char *string = Tcl_GetString(text->textObj);
+ Tk_Justify justify = TK_JUSTIFY_LEFT;
+ int wrapLength = 0;
+
+ text->tkfont = Tk_GetFontFromObj(tkwin, text->fontObj);
+ Tk_GetJustifyFromObj(NULL, text->justifyObj, &justify);
+ Tk_GetPixelsFromObj(NULL, tkwin, text->wrapLengthObj, &wrapLength);
+ Tcl_GetBooleanFromObj(NULL, text->embossedObj, &text->embossed);
+
+ text->textLayout = Tk_ComputeTextLayout(
+ text->tkfont, string, -1/*numChars*/, wrapLength, justify,
+ 0/*flags*/, &text->width, &text->height);
+
+ return 1;
+}
+
+/*
+ * TextReqWidth -- compute the requested width of a text element.
+ *
+ * If -width is positive, use that as the width
+ * If -width is negative, use that as the minimum width
+ * If not specified or empty, use the natural size of the text
+ */
+
+static int TextReqWidth(TextElement *text)
+{
+ int reqWidth;
+
+ if ( text->widthObj
+ && Tcl_GetIntFromObj(NULL, text->widthObj, &reqWidth) == TCL_OK)
+ {
+ int avgWidth = Tk_TextWidth(text->tkfont, "0", 1);
+ if (reqWidth <= 0) {
+ int specWidth = avgWidth * -reqWidth;
+ if (specWidth > text->width)
+ return specWidth;
+ } else {
+ return avgWidth * reqWidth;
+ }
+ }
+ return text->width;
+}
+
+static void TextCleanup(TextElement *text)
+{
+ Tk_FreeTextLayout(text->textLayout);
+}
+
+/*
+ * TextDraw --
+ * Draw a text element.
+ * Called by TextElementDraw() and LabelElementDraw().
+ */
+static void TextDraw(TextElement *text, Tk_Window tkwin, Drawable d, Ttk_Box b)
+{
+ XColor *color = Tk_GetColorFromObj(tkwin, text->foregroundObj);
+ int underline = -1;
+ XGCValues gcValues;
+ GC gc1, gc2;
+ Tk_Anchor anchor = TK_ANCHOR_CENTER;
+ TkRegion clipRegion = NULL;
+
+ gcValues.font = Tk_FontId(text->tkfont);
+ gcValues.foreground = color->pixel;
+ gc1 = Tk_GetGC(tkwin, GCFont | GCForeground, &gcValues);
+ gcValues.foreground = WhitePixelOfScreen(Tk_Screen(tkwin));
+ gc2 = Tk_GetGC(tkwin, GCFont | GCForeground, &gcValues);
+
+ /*
+ * Place text according to -anchor:
+ */
+ Tk_GetAnchorFromObj(NULL, text->anchorObj, &anchor);
+ b = Ttk_AnchorBox(b, text->width, text->height, anchor);
+
+ /*
+ * Clip text if it's too wide:
+ */
+ if (b.width < text->width) {
+ XRectangle rect;
+
+ clipRegion = TkCreateRegion();
+ rect.x = b.x;
+ rect.y = b.y;
+ rect.width = b.width + (text->embossed ? 1 : 0);
+ rect.height = b.height + (text->embossed ? 1 : 0);
+ TkUnionRectWithRegion(&rect, clipRegion, clipRegion);
+ TkSetRegion(Tk_Display(tkwin), gc1, clipRegion);
+ TkSetRegion(Tk_Display(tkwin), gc2, clipRegion);
+#ifdef HAVE_XFT
+ TkUnixSetXftClipRegion(clipRegion);
+#endif
+ }
+
+ if (text->embossed) {
+ Tk_DrawTextLayout(Tk_Display(tkwin), d, gc2,
+ text->textLayout, b.x+1, b.y+1, 0/*firstChar*/, -1/*lastChar*/);
+ }
+ Tk_DrawTextLayout(Tk_Display(tkwin), d, gc1,
+ text->textLayout, b.x, b.y, 0/*firstChar*/, -1/*lastChar*/);
+
+ Tcl_GetIntFromObj(NULL, text->underlineObj, &underline);
+ if (underline >= 0) {
+ if (text->embossed) {
+ Tk_UnderlineTextLayout(Tk_Display(tkwin), d, gc2,
+ text->textLayout, b.x+1, b.y+1, underline);
+ }
+ Tk_UnderlineTextLayout(Tk_Display(tkwin), d, gc1,
+ text->textLayout, b.x, b.y, underline);
+ }
+
+ if (clipRegion != NULL) {
+#ifdef HAVE_XFT
+ TkUnixSetXftClipRegion(None);
+#endif
+ XSetClipMask(Tk_Display(tkwin), gc1, None);
+ XSetClipMask(Tk_Display(tkwin), gc2, None);
+ TkDestroyRegion(clipRegion);
+ }
+ Tk_FreeGC(Tk_Display(tkwin), gc1);
+ Tk_FreeGC(Tk_Display(tkwin), gc2);
+}
+
+static void TextElementSize(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+{
+ TextElement *text = elementRecord;
+
+ if (!TextSetup(text, tkwin))
+ return;
+
+ *heightPtr = text->height;
+ *widthPtr = TextReqWidth(text);
+
+ TextCleanup(text);
+
+ return;
+}
+
+static void TextElementDraw(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ Drawable d, Ttk_Box b, Ttk_State state)
+{
+ TextElement *text = elementRecord;
+ if (TextSetup(text, tkwin)) {
+ TextDraw(text, tkwin, d, b);
+ TextCleanup(text);
+ }
+}
+
+static Ttk_ElementSpec TextElementSpec = {
+ TK_STYLE_VERSION_2,
+ sizeof(TextElement),
+ TextElementOptions,
+ TextElementSize,
+ TextElementDraw
+};
+
+/*----------------------------------------------------------------------
+ * +++ Image element.
+ * Draws an image.
+ */
+
+typedef struct {
+ Tcl_Obj *imageObj;
+ Tcl_Obj *stippleObj; /* For TTK_STATE_DISABLED */
+ Tcl_Obj *backgroundObj; /* " " */
+
+ Ttk_ImageSpec *imageSpec;
+ Tk_Image tkimg;
+ int width;
+ int height;
+} ImageElement;
+
+/* ===> NB: Keep in sync with label element option table. <===
+ */
+static Ttk_ElementOptionSpec ImageElementOptions[] = {
+ { "-image", TK_OPTION_STRING,
+ Tk_Offset(ImageElement,imageObj), "" },
+ { "-stipple", TK_OPTION_STRING, /* Really: TK_OPTION_BITMAP */
+ Tk_Offset(ImageElement,stippleObj), "gray50" },
+ { "-background", TK_OPTION_COLOR,
+ Tk_Offset(ImageElement,backgroundObj), DEFAULT_BACKGROUND },
+ { NULL, 0, 0, NULL }
+};
+
+/*
+ * ImageSetup() --
+ * Look up the Tk_Image from the image element's imageObj resource.
+ * Caller must release the image with ImageCleanup().
+ *
+ * Returns:
+ * 1 if successful, 0 if there was an error (unreported)
+ * or the image resource was not specified.
+ */
+
+static int ImageSetup(
+ ImageElement *image, Tk_Window tkwin, Ttk_State state)
+{
+
+ if (!image->imageObj) {
+ return 0;
+ }
+ image->imageSpec = TtkGetImageSpec(NULL, tkwin, image->imageObj);
+ if (!image->imageSpec) {
+ return 0;
+ }
+ image->tkimg = TtkSelectImage(image->imageSpec, state);
+ if (!image->tkimg) {
+ TtkFreeImageSpec(image->imageSpec);
+ return 0;
+ }
+ Tk_SizeOfImage(image->tkimg, &image->width, &image->height);
+
+ return 1;
+}
+
+static void ImageCleanup(ImageElement *image)
+{
+ TtkFreeImageSpec(image->imageSpec);
+}
+
+/*
+ * StippleOver --
+ * Draw a stipple over the image area, to make it look "grayed-out"
+ * when TTK_STATE_DISABLED is set.
+ */
+static void StippleOver(
+ ImageElement *image, Tk_Window tkwin, Drawable d, int x, int y)
+{
+ Pixmap stipple = Tk_AllocBitmapFromObj(NULL, tkwin, image->stippleObj);
+ XColor *color = Tk_GetColorFromObj(tkwin, image->backgroundObj);
+
+ if (stipple != None) {
+ unsigned long mask = GCFillStyle | GCStipple | GCForeground;
+ XGCValues gcvalues;
+ GC gc;
+ gcvalues.foreground = color->pixel;
+ gcvalues.fill_style = FillStippled;
+ gcvalues.stipple = stipple;
+ gc = Tk_GetGC(tkwin, mask, &gcvalues);
+ XFillRectangle(Tk_Display(tkwin),d,gc,x,y,image->width,image->height);
+ Tk_FreeGC(Tk_Display(tkwin), gc);
+ Tk_FreeBitmapFromObj(tkwin, image->stippleObj);
+ }
+}
+
+static void ImageDraw(
+ ImageElement *image, Tk_Window tkwin,Drawable d,Ttk_Box b,Ttk_State state)
+{
+ int width = image->width, height = image->height;
+
+ /* Clip width and height to remain within window bounds:
+ */
+ if (b.x + width > Tk_Width(tkwin)) {
+ width = Tk_Width(tkwin) - b.x;
+ }
+ if (b.y + height > Tk_Height(tkwin)) {
+ height = Tk_Height(tkwin) - b.y;
+ }
+
+ if (height <= 0 || width <= 0) {
+ /* Completely clipped - bail out.
+ */
+ return;
+ }
+
+ Tk_RedrawImage(image->tkimg, 0,0, width, height, d, b.x, b.y);
+
+ /* If we're disabled there's no state-specific 'disabled' image,
+ * stipple the image.
+ * @@@ Possibly: Don't do disabled-stippling at all;
+ * @@@ it's ugly and out of fashion.
+ * Do not stipple at all under Aqua, just draw the image: it shows up
+ * as a white rectangle otherwise.
+ */
+ if (state & TTK_STATE_DISABLED) {
+ if (TtkSelectImage(image->imageSpec, 0ul) == image->tkimg) {
+#ifndef MAC_OSX_TK
+ StippleOver(image, tkwin, d, b.x,b.y);
+#endif
+ }
+ }
+}
+
+static void ImageElementSize(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+{
+ ImageElement *image = elementRecord;
+
+ if (ImageSetup(image, tkwin, 0)) {
+ *widthPtr = image->width;
+ *heightPtr = image->height;
+ ImageCleanup(image);
+ }
+}
+
+static void ImageElementDraw(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ Drawable d, Ttk_Box b, Ttk_State state)
+{
+ ImageElement *image = elementRecord;
+
+ if (ImageSetup(image, tkwin, state)) {
+ ImageDraw(image, tkwin, d, b, state);
+ ImageCleanup(image);
+ }
+}
+
+static Ttk_ElementSpec ImageElementSpec = {
+ TK_STYLE_VERSION_2,
+ sizeof(ImageElement),
+ ImageElementOptions,
+ ImageElementSize,
+ ImageElementDraw
+};
+
+/*------------------------------------------------------------------------
+ * +++ Label element.
+ *
+ * Displays an image and/or text, as determined by the -compound option.
+ *
+ * Differences from Tk 8.4 compound elements:
+ *
+ * This adds two new values for the -compound option, "text"
+ * and "image". (This is useful for configuring toolbars to
+ * display icons, text and icons, or text only, as found in
+ * many browsers.)
+ *
+ * "-compound none" is supported, but I'd like to get rid of it;
+ * it makes the logic more complex, and the only benefit is
+ * backwards compatibility with Tk < 8.3.0 scripts.
+ *
+ * This adds a new resource, -space, for determining how much
+ * space to leave between the text and image; Tk 8.4 reuses the
+ * -padx or -pady option for this purpose.
+ *
+ * -width always specifies the length in characters of the text part;
+ * in Tk 8.4 it's either characters or pixels, depending on the
+ * value of -compound.
+ *
+ * Negative values of -width are interpreted as a minimum width
+ * on all platforms, not just on Windows.
+ *
+ * Tk 8.4 ignores -padx and -pady if -compound is set to "none".
+ * Here, padding is handled by a different element.
+ */
+
+typedef struct {
+ /*
+ * Element options:
+ */
+ Tcl_Obj *compoundObj;
+ Tcl_Obj *spaceObj;
+ TextElement text;
+ ImageElement image;
+
+ /*
+ * Computed values (see LabelSetup)
+ */
+ Ttk_Compound compound;
+ int space;
+ int totalWidth, totalHeight;
+} LabelElement;
+
+static Ttk_ElementOptionSpec LabelElementOptions[] = {
+ { "-compound", TK_OPTION_ANY,
+ Tk_Offset(LabelElement,compoundObj), "none" },
+ { "-space", TK_OPTION_PIXELS,
+ Tk_Offset(LabelElement,spaceObj), "4" },
+
+ /* Text element part:
+ * NB: Keep in sync with TextElementOptions.
+ */
+ { "-text", TK_OPTION_STRING,
+ Tk_Offset(LabelElement,text.textObj), "" },
+ { "-font", TK_OPTION_FONT,
+ Tk_Offset(LabelElement,text.fontObj), DEFAULT_FONT },
+ { "-foreground", TK_OPTION_COLOR,
+ Tk_Offset(LabelElement,text.foregroundObj), "black" },
+ { "-underline", TK_OPTION_INT,
+ Tk_Offset(LabelElement,text.underlineObj), "-1"},
+ { "-width", TK_OPTION_INT,
+ Tk_Offset(LabelElement,text.widthObj), ""},
+ { "-anchor", TK_OPTION_ANCHOR,
+ Tk_Offset(LabelElement,text.anchorObj), "w"},
+ { "-justify", TK_OPTION_JUSTIFY,
+ Tk_Offset(LabelElement,text.justifyObj), "left" },
+ { "-wraplength", TK_OPTION_PIXELS,
+ Tk_Offset(LabelElement,text.wrapLengthObj), "0" },
+ { "-embossed", TK_OPTION_INT,
+ Tk_Offset(LabelElement,text.embossedObj), "0"},
+
+ /* Image element part:
+ * NB: Keep in sync with ImageElementOptions.
+ */
+ { "-image", TK_OPTION_STRING,
+ Tk_Offset(LabelElement,image.imageObj), "" },
+ { "-stipple", TK_OPTION_STRING, /* Really: TK_OPTION_BITMAP */
+ Tk_Offset(LabelElement,image.stippleObj), "gray50" },
+ { "-background", TK_OPTION_COLOR,
+ Tk_Offset(LabelElement,image.backgroundObj), DEFAULT_BACKGROUND },
+ { NULL, 0, 0, NULL }
+};
+
+/*
+ * LabelSetup --
+ * Fills in computed fields of the label element.
+ *
+ * Calculate the text, image, and total width and height.
+ */
+
+#define MAX(a,b) ((a) > (b) ? a : b);
+static void LabelSetup(
+ LabelElement *c, Tk_Window tkwin, Ttk_State state)
+{
+ Ttk_Compound *compoundPtr = &c->compound;
+
+ Tk_GetPixelsFromObj(NULL,tkwin,c->spaceObj,&c->space);
+ Ttk_GetCompoundFromObj(NULL,c->compoundObj,(int*)compoundPtr);
+
+ /*
+ * Deal with TTK_COMPOUND_NONE.
+ */
+ if (c->compound == TTK_COMPOUND_NONE) {
+ if (ImageSetup(&c->image, tkwin, state)) {
+ c->compound = TTK_COMPOUND_IMAGE;
+ } else {
+ c->compound = TTK_COMPOUND_TEXT;
+ }
+ } else if (c->compound != TTK_COMPOUND_TEXT) {
+ if (!ImageSetup(&c->image, tkwin, state)) {
+ c->compound = TTK_COMPOUND_TEXT;
+ }
+ }
+ if (c->compound != TTK_COMPOUND_IMAGE)
+ TextSetup(&c->text, tkwin);
+
+ /*
+ * ASSERT:
+ * if c->compound != IMAGE, then TextSetup() has been called
+ * if c->compound != TEXT, then ImageSetup() has returned successfully
+ * c->compound != COMPOUND_NONE.
+ */
+
+ switch (c->compound)
+ {
+ case TTK_COMPOUND_NONE:
+ /* Can't happen */
+ break;
+ case TTK_COMPOUND_TEXT:
+ c->totalWidth = c->text.width;
+ c->totalHeight = c->text.height;
+ break;
+ case TTK_COMPOUND_IMAGE:
+ c->totalWidth = c->image.width;
+ c->totalHeight = c->image.height;
+ break;
+ case TTK_COMPOUND_CENTER:
+ c->totalWidth = MAX(c->image.width, c->text.width);
+ c->totalHeight = MAX(c->image.height, c->text.height);
+ break;
+ case TTK_COMPOUND_TOP:
+ case TTK_COMPOUND_BOTTOM:
+ c->totalWidth = MAX(c->image.width, c->text.width);
+ c->totalHeight = c->image.height + c->text.height + c->space;
+ break;
+
+ case TTK_COMPOUND_LEFT:
+ case TTK_COMPOUND_RIGHT:
+ c->totalWidth = c->image.width + c->text.width + c->space;
+ c->totalHeight = MAX(c->image.height, c->text.height);
+ break;
+ }
+}
+
+static void LabelCleanup(LabelElement *c)
+{
+ if (c->compound != TTK_COMPOUND_TEXT)
+ ImageCleanup(&c->image);
+ if (c->compound != TTK_COMPOUND_IMAGE)
+ TextCleanup(&c->text);
+}
+
+static void LabelElementSize(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+{
+ LabelElement *label = elementRecord;
+ int textReqWidth = 0;
+
+ LabelSetup(label, tkwin, 0);
+
+ *heightPtr = label->totalHeight;
+
+ /* Requested width based on -width option, not actual text width:
+ */
+ if (label->compound != TTK_COMPOUND_IMAGE)
+ textReqWidth = TextReqWidth(&label->text);
+
+ switch (label->compound)
+ {
+ case TTK_COMPOUND_TEXT:
+ *widthPtr = textReqWidth;
+ break;
+ case TTK_COMPOUND_IMAGE:
+ *widthPtr = label->image.width;
+ break;
+ case TTK_COMPOUND_TOP:
+ case TTK_COMPOUND_BOTTOM:
+ case TTK_COMPOUND_CENTER:
+ *widthPtr = MAX(label->image.width, textReqWidth);
+ break;
+ case TTK_COMPOUND_LEFT:
+ case TTK_COMPOUND_RIGHT:
+ *widthPtr = label->image.width + textReqWidth + label->space;
+ break;
+ case TTK_COMPOUND_NONE:
+ break; /* Can't happen */
+ }
+
+ LabelCleanup(label);
+}
+
+/*
+ * DrawCompound --
+ * Helper routine for LabelElementDraw;
+ * Handles layout for -compound {left,right,top,bottom}
+ */
+static void DrawCompound(
+ LabelElement *l, Ttk_Box b, Tk_Window tkwin, Drawable d, Ttk_State state,
+ int imageSide, int textSide)
+{
+ Ttk_Box imageBox =
+ Ttk_PlaceBox(&b, l->image.width, l->image.height, imageSide, 0);
+ Ttk_Box textBox =
+ Ttk_PlaceBox(&b, l->text.width, l->text.height, textSide, 0);
+ ImageDraw(&l->image,tkwin,d,imageBox,state);
+ TextDraw(&l->text,tkwin,d,textBox);
+}
+
+static void LabelElementDraw(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ Drawable d, Ttk_Box b, Ttk_State state)
+{
+ LabelElement *l = elementRecord;
+ Tk_Anchor anchor = TK_ANCHOR_CENTER;
+
+ LabelSetup(l, tkwin, state);
+
+ /*
+ * Adjust overall parcel based on -anchor:
+ */
+ Tk_GetAnchorFromObj(NULL, l->text.anchorObj, &anchor);
+ b = Ttk_AnchorBox(b, l->totalWidth, l->totalHeight, anchor);
+
+ /*
+ * Draw text and/or image parts based on -compound:
+ */
+ switch (l->compound)
+ {
+ case TTK_COMPOUND_NONE:
+ /* Can't happen */
+ break;
+ case TTK_COMPOUND_TEXT:
+ TextDraw(&l->text,tkwin,d,b);
+ break;
+ case TTK_COMPOUND_IMAGE:
+ ImageDraw(&l->image,tkwin,d,b,state);
+ break;
+ case TTK_COMPOUND_CENTER:
+ {
+ Ttk_Box pb = Ttk_AnchorBox(
+ b, l->image.width, l->image.height, TK_ANCHOR_CENTER);
+ ImageDraw(&l->image, tkwin, d, pb, state);
+ pb = Ttk_AnchorBox(
+ b, l->text.width, l->text.height, TK_ANCHOR_CENTER);
+ TextDraw(&l->text, tkwin, d, pb);
+ break;
+ }
+ case TTK_COMPOUND_TOP:
+ DrawCompound(l, b, tkwin, d, state, TTK_SIDE_TOP, TTK_SIDE_BOTTOM);
+ break;
+ case TTK_COMPOUND_BOTTOM:
+ DrawCompound(l, b, tkwin, d, state, TTK_SIDE_BOTTOM, TTK_SIDE_TOP);
+ break;
+ case TTK_COMPOUND_LEFT:
+ DrawCompound(l, b, tkwin, d, state, TTK_SIDE_LEFT, TTK_SIDE_RIGHT);
+ break;
+ case TTK_COMPOUND_RIGHT:
+ DrawCompound(l, b, tkwin, d, state, TTK_SIDE_RIGHT, TTK_SIDE_LEFT);
+ break;
+ }
+
+ LabelCleanup(l);
+}
+
+static Ttk_ElementSpec LabelElementSpec = {
+ TK_STYLE_VERSION_2,
+ sizeof(LabelElement),
+ LabelElementOptions,
+ LabelElementSize,
+ LabelElementDraw
+};
+
+/*------------------------------------------------------------------------
+ * +++ Initialization.
+ */
+
+MODULE_SCOPE
+void TtkLabel_Init(Tcl_Interp *interp)
+{
+ Ttk_Theme theme = Ttk_GetDefaultTheme(interp);
+
+ Ttk_RegisterElement(interp, theme, "text", &TextElementSpec, NULL);
+ Ttk_RegisterElement(interp, theme, "image", &ImageElementSpec, NULL);
+ Ttk_RegisterElement(interp, theme, "label", &LabelElementSpec, NULL);
+}
+
diff --git a/generic/ttk/ttkLayout.c b/generic/ttk/ttkLayout.c
new file mode 100644
index 0000000..58c99eb
--- /dev/null
+++ b/generic/ttk/ttkLayout.c
@@ -0,0 +1,1252 @@
+/*
+ * ttkLayout.c --
+ *
+ * Generic layout processing.
+ *
+ * Copyright (c) 2003 Joe English. Freely redistributable.
+ */
+
+#include <string.h>
+#include <tk.h>
+#include "ttkThemeInt.h"
+
+#define MAX(a,b) (a > b ? a : b)
+#define MIN(a,b) (a < b ? a : b)
+
+/*------------------------------------------------------------------------
+ * +++ Ttk_Box and Ttk_Padding utilities:
+ */
+
+Ttk_Box
+Ttk_MakeBox(int x, int y, int width, int height)
+{
+ Ttk_Box b;
+ b.x = x; b.y = y; b.width = width; b.height = height;
+ return b;
+}
+
+int
+Ttk_BoxContains(Ttk_Box box, int x, int y)
+{
+ return box.x <= x && x < box.x + box.width
+ && box.y <= y && y < box.y + box.height;
+}
+
+Tcl_Obj *
+Ttk_NewBoxObj(Ttk_Box box)
+{
+ Tcl_Obj *result[4];
+
+ result[0] = Tcl_NewIntObj(box.x);
+ result[1] = Tcl_NewIntObj(box.y);
+ result[2] = Tcl_NewIntObj(box.width);
+ result[3] = Tcl_NewIntObj(box.height);
+
+ return Tcl_NewListObj(4, result);
+}
+
+/*
+ * packTop, packBottom, packLeft, packRight --
+ * Carve out a parcel of the specified height (resp width)
+ * from the specified cavity.
+ *
+ * Returns:
+ * The new parcel.
+ *
+ * Side effects:
+ * Adjust the cavity.
+ */
+
+static Ttk_Box packTop(Ttk_Box *cavity, int height)
+{
+ Ttk_Box parcel;
+ height = MIN(height, cavity->height);
+ parcel = Ttk_MakeBox(cavity->x, cavity->y, cavity->width, height);
+ cavity->y += height;
+ cavity->height -= height;
+ return parcel;
+}
+
+static Ttk_Box packBottom(Ttk_Box *cavity, int height)
+{
+ height = MIN(height, cavity->height);
+ cavity->height -= height;
+ return Ttk_MakeBox(
+ cavity->x, cavity->y + cavity->height,
+ cavity->width, height);
+}
+
+static Ttk_Box packLeft(Ttk_Box *cavity, int width)
+{
+ Ttk_Box parcel;
+ width = MIN(width, cavity->width);
+ parcel = Ttk_MakeBox(cavity->x, cavity->y, width,cavity->height);
+ cavity->x += width;
+ cavity->width -= width;
+ return parcel;
+}
+
+static Ttk_Box packRight(Ttk_Box *cavity, int width)
+{
+ width = MIN(width, cavity->width);
+ cavity->width -= width;
+ return Ttk_MakeBox(cavity->x + cavity->width,
+ cavity->y, width, cavity->height);
+}
+
+/*
+ * Ttk_PackBox --
+ * Carve out a parcel of the specified size on the specified side
+ * in the specified cavity.
+ *
+ * Returns:
+ * The new parcel.
+ *
+ * Side effects:
+ * Adjust the cavity.
+ */
+
+Ttk_Box Ttk_PackBox(Ttk_Box *cavity, int width, int height, Ttk_Side side)
+{
+ switch (side) {
+ default:
+ case TTK_SIDE_TOP: return packTop(cavity, height);
+ case TTK_SIDE_BOTTOM: return packBottom(cavity, height);
+ case TTK_SIDE_LEFT: return packLeft(cavity, width);
+ case TTK_SIDE_RIGHT: return packRight(cavity, width);
+ }
+}
+
+/*
+ * Ttk_PadBox --
+ * Shrink a box by the specified padding amount.
+ */
+Ttk_Box Ttk_PadBox(Ttk_Box b, Ttk_Padding p)
+{
+ b.x += p.left;
+ b.y += p.top;
+ b.width -= (p.left + p.right);
+ b.height -= (p.top + p.bottom);
+ if (b.width <= 0) b.width = 1;
+ if (b.height <= 0) b.height = 1;
+ return b;
+}
+
+/*
+ * Ttk_ExpandBox --
+ * Grow a box by the specified padding amount.
+ */
+Ttk_Box Ttk_ExpandBox(Ttk_Box b, Ttk_Padding p)
+{
+ b.x -= p.left;
+ b.y -= p.top;
+ b.width += (p.left + p.right);
+ b.height += (p.top + p.bottom);
+ return b;
+}
+
+/*
+ * Ttk_StickBox --
+ * Place a box of size w * h in the specified parcel,
+ * according to the specified sticky bits.
+ */
+Ttk_Box Ttk_StickBox(Ttk_Box parcel, int width, int height, unsigned sticky)
+{
+ int dx, dy;
+
+ if (width > parcel.width) width = parcel.width;
+ if (height > parcel.height) height = parcel.height;
+
+ dx = parcel.width - width;
+ dy = parcel.height - height;
+
+ /*
+ * X coordinate adjustment:
+ */
+ switch (sticky & (TTK_STICK_W | TTK_STICK_E))
+ {
+ case TTK_STICK_W | TTK_STICK_E:
+ /* no-op -- use entire parcel width */
+ break;
+ case TTK_STICK_W:
+ parcel.width = width;
+ break;
+ case TTK_STICK_E:
+ parcel.x += dx;
+ parcel.width = width;
+ break;
+ default :
+ parcel.x += dx / 2;
+ parcel.width = width;
+ break;
+ }
+
+ /*
+ * Y coordinate adjustment:
+ */
+ switch (sticky & (TTK_STICK_N | TTK_STICK_S))
+ {
+ case TTK_STICK_N | TTK_STICK_S:
+ /* use entire parcel height */
+ break;
+ case TTK_STICK_N:
+ parcel.height = height;
+ break;
+ case TTK_STICK_S:
+ parcel.y += dy;
+ parcel.height = height;
+ break;
+ default :
+ parcel.y += dy / 2;
+ parcel.height = height;
+ break;
+ }
+
+ return parcel;
+}
+
+/*
+ * AnchorToSticky --
+ * Convert a Tk_Anchor enum to a TTK_STICKY bitmask.
+ */
+static Ttk_Sticky AnchorToSticky(Tk_Anchor anchor)
+{
+ switch (anchor)
+ {
+ case TK_ANCHOR_N: return TTK_STICK_N;
+ case TK_ANCHOR_NE: return TTK_STICK_N | TTK_STICK_E;
+ case TK_ANCHOR_E: return TTK_STICK_E;
+ case TK_ANCHOR_SE: return TTK_STICK_S | TTK_STICK_E;
+ case TK_ANCHOR_S: return TTK_STICK_S;
+ case TK_ANCHOR_SW: return TTK_STICK_S | TTK_STICK_W;
+ case TK_ANCHOR_W: return TTK_STICK_W;
+ case TK_ANCHOR_NW: return TTK_STICK_N | TTK_STICK_W;
+ default:
+ case TK_ANCHOR_CENTER: return 0;
+ }
+}
+
+/*
+ * Ttk_AnchorBox --
+ * Place a box of size w * h in the specified parcel,
+ * according to the specified anchor.
+ */
+Ttk_Box Ttk_AnchorBox(Ttk_Box parcel, int width, int height, Tk_Anchor anchor)
+{
+ return Ttk_StickBox(parcel, width, height, AnchorToSticky(anchor));
+}
+
+/*
+ * Ttk_PlaceBox --
+ * Combine Ttk_PackBox() and Ttk_StickBox().
+ */
+Ttk_Box Ttk_PlaceBox(
+ Ttk_Box *cavity, int width, int height, Ttk_Side side, unsigned sticky)
+{
+ return Ttk_StickBox(
+ Ttk_PackBox(cavity, width, height, side), width, height, sticky);
+}
+
+/*
+ * Ttk_PositionBox --
+ * Pack and stick a box according to PositionSpec flags.
+ */
+MODULE_SCOPE Ttk_Box
+Ttk_PositionBox(Ttk_Box *cavity, int width, int height, Ttk_PositionSpec flags)
+{
+ Ttk_Box parcel;
+
+ if (flags & TTK_EXPAND) parcel = *cavity;
+ else if (flags & TTK_PACK_TOP) parcel = packTop(cavity, height);
+ else if (flags & TTK_PACK_LEFT) parcel = packLeft(cavity, width);
+ else if (flags & TTK_PACK_BOTTOM) parcel = packBottom(cavity, height);
+ else if (flags & TTK_PACK_RIGHT) parcel = packRight(cavity, width);
+ else parcel = *cavity;
+
+ return Ttk_StickBox(parcel, width, height, flags);
+}
+
+/*
+ * TTKInitPadding --
+ * Common factor of Ttk_GetPaddingFromObj and Ttk_GetBorderFromObj.
+ * Initializes Ttk_Padding record, supplying default values
+ * for missing entries.
+ */
+static void TTKInitPadding(int padc, int pixels[4], Ttk_Padding *pad)
+{
+ switch (padc)
+ {
+ case 0: pixels[0] = 0; /*FALLTHRU*/
+ case 1: pixels[1] = pixels[0]; /*FALLTHRU*/
+ case 2: pixels[2] = pixels[0]; /*FALLTHRU*/
+ case 3: pixels[3] = pixels[1]; /*FALLTHRU*/
+ }
+
+ pad->left = (short)pixels[0];
+ pad->top = (short)pixels[1];
+ pad->right = (short)pixels[2];
+ pad->bottom = (short)pixels[3];
+}
+
+/*
+ * Ttk_GetPaddingFromObj --
+ *
+ * Extract a padding specification from a Tcl_Obj * scaled
+ * to work with a particular Tk_Window.
+ *
+ * The string representation of a Ttk_Padding is a list
+ * of one to four Tk_Pixel specifications, corresponding
+ * to the left, top, right, and bottom padding.
+ *
+ * If the 'bottom' (fourth) element is missing, it defaults to 'top'.
+ * If the 'right' (third) element is missing, it defaults to 'left'.
+ * If the 'top' (second) element is missing, it defaults to 'left'.
+ *
+ * The internal representation is a Tcl_ListObj containing
+ * one to four Tk_PixelObj objects.
+ *
+ * Returns:
+ * TCL_OK or TCL_ERROR. In the latter case an error message is
+ * left in 'interp' and '*paddingPtr' is set to all-zeros.
+ * Otherwise, *paddingPtr is filled in with the padding specification.
+ *
+ */
+int Ttk_GetPaddingFromObj(
+ Tcl_Interp *interp,
+ Tk_Window tkwin,
+ Tcl_Obj *objPtr,
+ Ttk_Padding *pad)
+{
+ Tcl_Obj **padv;
+ int i, padc, pixels[4];
+
+ if (TCL_OK != Tcl_ListObjGetElements(interp, objPtr, &padc, &padv)) {
+ goto error;
+ }
+
+ if (padc > 4) {
+ if (interp) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "Wrong #elements in padding spec", NULL);
+ }
+ goto error;
+ }
+
+ for (i=0; i < padc; ++i) {
+ if (Tk_GetPixelsFromObj(interp, tkwin, padv[i], &pixels[i]) != TCL_OK) {
+ goto error;
+ }
+ }
+
+ TTKInitPadding(padc, pixels, pad);
+ return TCL_OK;
+
+error:
+ pad->left = pad->top = pad->right = pad->bottom = 0;
+ return TCL_ERROR;
+}
+
+/* Ttk_GetBorderFromObj --
+ * Same as Ttk_GetPaddingFromObj, except padding is a list of integers
+ * instead of Tk_Pixel specifications. Does not require a Tk_Window
+ * parameter.
+ *
+ */
+int Ttk_GetBorderFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, Ttk_Padding *pad)
+{
+ Tcl_Obj **padv;
+ int i, padc, pixels[4];
+
+ if (TCL_OK != Tcl_ListObjGetElements(interp, objPtr, &padc, &padv)) {
+ goto error;
+ }
+
+ if (padc > 4) {
+ if (interp) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "Wrong #elements in border spec", NULL);
+ }
+ goto error;
+ }
+
+ for (i=0; i < padc; ++i) {
+ if (Tcl_GetIntFromObj(interp, padv[i], &pixels[i]) != TCL_OK) {
+ goto error;
+ }
+ }
+
+ TTKInitPadding(padc, pixels, pad);
+ return TCL_OK;
+
+error:
+ pad->left = pad->top = pad->right = pad->bottom = 0;
+ return TCL_ERROR;
+}
+
+/*
+ * Ttk_MakePadding --
+ * Return an initialized Ttk_Padding structure.
+ */
+Ttk_Padding Ttk_MakePadding(short left, short top, short right, short bottom)
+{
+ Ttk_Padding pad;
+ pad.left = left;
+ pad.top = top;
+ pad.right = right;
+ pad.bottom = bottom;
+ return pad;
+}
+
+/*
+ * Ttk_UniformPadding --
+ * Returns a uniform Ttk_Padding structure, with the same
+ * border width on all sides.
+ */
+Ttk_Padding Ttk_UniformPadding(short borderWidth)
+{
+ Ttk_Padding pad;
+ pad.left = pad.top = pad.right = pad.bottom = borderWidth;
+ return pad;
+}
+
+/*
+ * Ttk_AddPadding --
+ * Combine two padding records.
+ */
+Ttk_Padding Ttk_AddPadding(Ttk_Padding p1, Ttk_Padding p2)
+{
+ p1.left += p2.left;
+ p1.top += p2.top;
+ p1.right += p2.right;
+ p1.bottom += p2.bottom;
+ return p1;
+}
+
+/* Ttk_RelievePadding --
+ * Add an extra n pixels of padding according to specified relief.
+ * This may be used in element geometry procedures to simulate
+ * a "pressed-in" look for pushbuttons.
+ */
+Ttk_Padding Ttk_RelievePadding(Ttk_Padding padding, int relief, int n)
+{
+ switch (relief)
+ {
+ case TK_RELIEF_RAISED:
+ padding.right += n;
+ padding.bottom += n;
+ break;
+ case TK_RELIEF_SUNKEN: /* shift */
+ padding.left += n;
+ padding.top += n;
+ break;
+ default:
+ {
+ int h1 = n/2, h2 = h1 + n % 2;
+ padding.left += h1;
+ padding.top += h1;
+ padding.right += h2;
+ padding.bottom += h2;
+ break;
+ }
+ }
+ return padding;
+}
+
+/*
+ * Ttk_GetStickyFromObj --
+ * Returns a stickiness specification from the specified Tcl_Obj*,
+ * consisting of any combination of n, s, e, and w.
+ *
+ * Returns: TCL_OK if objPtr holds a valid stickiness specification,
+ * otherwise TCL_ERROR. interp is used for error reporting if non-NULL.
+ *
+ */
+int Ttk_GetStickyFromObj(
+ Tcl_Interp *interp, Tcl_Obj *objPtr, Ttk_Sticky *result)
+{
+ const char *string = Tcl_GetString(objPtr);
+ Ttk_Sticky sticky = 0;
+ char c;
+
+ while ((c = *string++) != '\0') {
+ switch (c) {
+ case 'w': case 'W': sticky |= TTK_STICK_W; break;
+ case 'e': case 'E': sticky |= TTK_STICK_E; break;
+ case 'n': case 'N': sticky |= TTK_STICK_N; break;
+ case 's': case 'S': sticky |= TTK_STICK_S; break;
+ default:
+ if (interp) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp,
+ "Bad -sticky specification ",
+ Tcl_GetString(objPtr),
+ NULL);
+ }
+ return TCL_ERROR;
+ }
+ }
+
+ *result = sticky;
+ return TCL_OK;
+}
+
+/* Ttk_NewStickyObj --
+ * Construct a new Tcl_Obj * containing a stickiness specification.
+ */
+Tcl_Obj *Ttk_NewStickyObj(Ttk_Sticky sticky)
+{
+ char buf[5];
+ char *p = buf;
+
+ if (sticky & TTK_STICK_N) *p++ = 'n';
+ if (sticky & TTK_STICK_S) *p++ = 's';
+ if (sticky & TTK_STICK_W) *p++ = 'w';
+ if (sticky & TTK_STICK_E) *p++ = 'e';
+
+ *p = '\0';
+ return Tcl_NewStringObj(buf, p - buf);
+}
+
+/*------------------------------------------------------------------------
+ * +++ Layout nodes.
+ */
+
+typedef struct Ttk_LayoutNode_ Ttk_LayoutNode;
+struct Ttk_LayoutNode_
+{
+ unsigned flags; /* Packing and sticky flags */
+ Ttk_ElementClass *eclass; /* Class record */
+ Ttk_State state; /* Current state */
+ Ttk_Box parcel; /* allocated parcel */
+ Ttk_LayoutNode *next, *child;
+};
+
+static Ttk_LayoutNode *Ttk_NewLayoutNode(
+ unsigned flags, Ttk_ElementClass *elementClass)
+{
+ Ttk_LayoutNode *node = (Ttk_LayoutNode*)ckalloc(sizeof(*node));
+
+ node->flags = flags;
+ node->eclass = elementClass;
+ node->state = 0u;
+ node->next = node->child = 0;
+ node->parcel = Ttk_MakeBox(0,0,0,0);
+
+ return node;
+}
+
+static void Ttk_FreeLayoutNode(Ttk_LayoutNode *node)
+{
+ while (node) {
+ Ttk_LayoutNode *next = node->next;
+ Ttk_FreeLayoutNode(node->child);
+ ckfree((ClientData)node);
+ node = next;
+ }
+}
+
+/*------------------------------------------------------------------------
+ * +++ Layout templates.
+ */
+
+struct Ttk_TemplateNode_ {
+ char *name;
+ unsigned flags;
+ struct Ttk_TemplateNode_ *next, *child;
+};
+
+static Ttk_TemplateNode *Ttk_NewTemplateNode(const char *name, unsigned flags)
+{
+ Ttk_TemplateNode *op = (Ttk_TemplateNode*)ckalloc(sizeof(*op));
+ op->name = ckalloc(strlen(name) + 1); strcpy(op->name, name);
+ op->flags = flags;
+ op->next = op->child = 0;
+ return op;
+}
+
+void Ttk_FreeLayoutTemplate(Ttk_LayoutTemplate op)
+{
+ while (op) {
+ Ttk_LayoutTemplate next = op->next;
+ Ttk_FreeLayoutTemplate(op->child);
+ ckfree(op->name);
+ ckfree((ClientData)op);
+ op = next;
+ }
+}
+
+/* InstantiateLayout --
+ * Create a layout tree from a template.
+ */
+static Ttk_LayoutNode *
+Ttk_InstantiateLayout(Ttk_Theme theme, Ttk_TemplateNode *op)
+{
+ Ttk_ElementClass *elementClass = Ttk_GetElement(theme, op->name);
+ Ttk_LayoutNode *node = Ttk_NewLayoutNode(op->flags, elementClass);
+
+ if (op->next) {
+ node->next = Ttk_InstantiateLayout(theme,op->next);
+ }
+ if (op->child) {
+ node->child = Ttk_InstantiateLayout(theme,op->child);
+ }
+
+ return node;
+}
+
+/*
+ * Ttk_ParseLayoutTemplate --
+ * Convert a Tcl list into a layout template.
+ *
+ * Syntax:
+ * layoutSpec ::= { elementName ?-option value ...? }+
+ */
+
+/* NB: This must match bit definitions TTK_PACK_LEFT etc. */
+static const char *packSideStrings[] =
+ { "left", "right", "top", "bottom", NULL };
+
+Ttk_LayoutTemplate Ttk_ParseLayoutTemplate(Tcl_Interp *interp, Tcl_Obj *objPtr)
+{
+ enum { OP_SIDE, OP_STICKY, OP_EXPAND, OP_BORDER, OP_UNIT, OP_CHILDREN };
+ static const char *optStrings[] = {
+ "-side", "-sticky", "-expand", "-border", "-unit", "-children", 0 };
+
+ int i = 0, objc;
+ Tcl_Obj **objv;
+ Ttk_TemplateNode *head = 0, *tail = 0;
+
+ if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK)
+ return 0;
+
+ while (i < objc) {
+ const char *elementName = Tcl_GetString(objv[i]);
+ unsigned flags = 0x0, sticky = TTK_FILL_BOTH;
+ Tcl_Obj *childSpec = 0;
+
+ /*
+ * Parse options:
+ */
+ ++i;
+ while (i < objc) {
+ const char *optName = Tcl_GetString(objv[i]);
+ int option, value;
+
+ if (optName[0] != '-')
+ break;
+
+ if (Tcl_GetIndexFromObj(
+ interp, objv[i], optStrings, "option", 0, &option)
+ != TCL_OK)
+ {
+ goto error;
+ }
+
+ if (++i >= objc) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp,
+ "Missing value for option ",Tcl_GetString(objv[i-1]),
+ NULL);
+ goto error;
+ }
+
+ switch (option) {
+ case OP_SIDE: /* <<NOTE-PACKSIDE>> */
+ if (Tcl_GetIndexFromObj(interp, objv[i], packSideStrings,
+ "side", 0, &value) != TCL_OK)
+ {
+ goto error;
+ }
+ flags |= (TTK_PACK_LEFT << value);
+
+ break;
+ case OP_STICKY:
+ if (Ttk_GetStickyFromObj(interp,objv[i],&sticky) != TCL_OK)
+ goto error;
+ break;
+ case OP_EXPAND:
+ if (Tcl_GetBooleanFromObj(interp,objv[i],&value) != TCL_OK)
+ goto error;
+ if (value)
+ flags |= TTK_EXPAND;
+ break;
+ case OP_BORDER:
+ if (Tcl_GetBooleanFromObj(interp,objv[i],&value) != TCL_OK)
+ goto error;
+ if (value)
+ flags |= TTK_BORDER;
+ break;
+ case OP_UNIT:
+ if (Tcl_GetBooleanFromObj(interp,objv[i],&value) != TCL_OK)
+ goto error;
+ if (value)
+ flags |= TTK_UNIT;
+ break;
+ case OP_CHILDREN:
+ childSpec = objv[i];
+ break;
+ }
+ ++i;
+ }
+
+ /*
+ * Build new node:
+ */
+ if (tail) {
+ tail->next = Ttk_NewTemplateNode(elementName, flags | sticky);
+ tail = tail->next;
+ } else {
+ head = tail = Ttk_NewTemplateNode(elementName, flags | sticky);
+ }
+ if (childSpec) {
+ tail->child = Ttk_ParseLayoutTemplate(interp, childSpec);
+ if (!tail->child) {
+ goto error;
+ }
+ }
+ }
+
+ return head;
+
+error:
+ Ttk_FreeLayoutTemplate(head);
+ return 0;
+}
+
+/* Ttk_BuildLayoutTemplate --
+ * Build a layout template tree from a statically defined
+ * Ttk_LayoutSpec array.
+ */
+Ttk_LayoutTemplate Ttk_BuildLayoutTemplate(Ttk_LayoutSpec spec)
+{
+ Ttk_TemplateNode *first = 0, *last = 0;
+
+ for ( ; !(spec->opcode & _TTK_LAYOUT_END) ; ++spec) {
+ if (spec->elementName) {
+ Ttk_TemplateNode *node =
+ Ttk_NewTemplateNode(spec->elementName, spec->opcode);
+
+ if (last) {
+ last->next = node;
+ } else {
+ first = node;
+ }
+ last = node;
+ }
+
+ if (spec->opcode & _TTK_CHILDREN && last) {
+ int depth = 1;
+ last->child = Ttk_BuildLayoutTemplate(spec+1);
+
+ /* Skip to end of group:
+ */
+ while (depth) {
+ ++spec;
+ if (spec->opcode & _TTK_CHILDREN) {
+ ++depth;
+ }
+ if (spec->opcode & _TTK_LAYOUT_END) {
+ --depth;
+ }
+ }
+ }
+
+ } /* for */
+
+ return first;
+}
+
+void Ttk_RegisterLayouts(Ttk_Theme theme, Ttk_LayoutSpec spec)
+{
+ while (!(spec->opcode & _TTK_LAYOUT_END)) {
+ Ttk_LayoutTemplate layoutTemplate = Ttk_BuildLayoutTemplate(spec+1);
+ Ttk_RegisterLayoutTemplate(theme, spec->elementName, layoutTemplate);
+ do {
+ ++spec;
+ } while (!(spec->opcode & _TTK_LAYOUT));
+ }
+}
+
+Tcl_Obj *Ttk_UnparseLayoutTemplate(Ttk_TemplateNode *node)
+{
+ Tcl_Obj *result = Tcl_NewListObj(0,0);
+
+# define APPENDOBJ(obj) Tcl_ListObjAppendElement(NULL, result, obj)
+# define APPENDSTR(str) APPENDOBJ(Tcl_NewStringObj(str,-1))
+
+ while (node) {
+ unsigned flags = node->flags;
+
+ APPENDSTR(node->name);
+
+ /* Back-compute -side. <<NOTE-PACKSIDE>>
+ * @@@ NOTES: Ick.
+ */
+ if (flags & TTK_EXPAND) {
+ APPENDSTR("-expand");
+ APPENDSTR("1");
+ } else {
+ if (flags & _TTK_MASK_PACK) {
+ int side = 0;
+ unsigned sideFlags = flags & _TTK_MASK_PACK;
+
+ while ((sideFlags & TTK_PACK_LEFT) == 0) {
+ ++side;
+ sideFlags >>= 1;
+ }
+ APPENDSTR("-side");
+ APPENDSTR(packSideStrings[side]);
+ }
+ }
+
+ /* In Ttk_ParseLayoutTemplate, default -sticky is "nsew",
+ * so always include this even if no sticky bits are set.
+ */
+ APPENDSTR("-sticky");
+ APPENDOBJ(Ttk_NewStickyObj(flags & _TTK_MASK_STICK));
+
+ /* @@@ Check again: are these necessary? */
+ if (flags & TTK_BORDER) { APPENDSTR("-border"); APPENDSTR("1"); }
+ if (flags & TTK_UNIT) { APPENDSTR("-unit"); APPENDSTR("1"); }
+
+ if (node->child) {
+ APPENDSTR("-children");
+ APPENDOBJ(Ttk_UnparseLayoutTemplate(node->child));
+ }
+ node = node->next;
+ }
+
+# undef APPENDOBJ
+# undef APPENDSTR
+
+ return result;
+}
+
+/*------------------------------------------------------------------------
+ * +++ Layouts.
+ */
+struct Ttk_Layout_
+{
+ Ttk_Style style;
+ void *recordPtr;
+ Tk_OptionTable optionTable;
+ Tk_Window tkwin;
+ Ttk_LayoutNode *root;
+};
+
+static Ttk_Layout TTKNewLayout(
+ Ttk_Style style,
+ void *recordPtr,Tk_OptionTable optionTable, Tk_Window tkwin,
+ Ttk_LayoutNode *root)
+{
+ Ttk_Layout layout = (Ttk_Layout)ckalloc(sizeof(*layout));
+ layout->style = style;
+ layout->recordPtr = recordPtr;
+ layout->optionTable = optionTable;
+ layout->tkwin = tkwin;
+ layout->root = root;
+ return layout;
+}
+
+void Ttk_FreeLayout(Ttk_Layout layout)
+{
+ Ttk_FreeLayoutNode(layout->root);
+ ckfree((ClientData)layout);
+}
+
+/*
+ * Ttk_CreateLayout --
+ * Create a layout from the specified theme and style name.
+ * Returns: New layout, 0 on error.
+ * Leaves an error message in interp's result if there is an error.
+ */
+Ttk_Layout Ttk_CreateLayout(
+ Tcl_Interp *interp, /* where to leave error messages */
+ Ttk_Theme themePtr,
+ const char *styleName,
+ void *recordPtr,
+ Tk_OptionTable optionTable,
+ Tk_Window tkwin)
+{
+ Ttk_Style style = Ttk_GetStyle(themePtr, styleName);
+ Ttk_LayoutTemplate layoutTemplate =
+ Ttk_FindLayoutTemplate(themePtr,styleName);
+ Ttk_ElementClass *bgelement = Ttk_GetElement(themePtr, "background");
+ Ttk_LayoutNode *bgnode;
+
+ if (!layoutTemplate) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "Layout ", styleName, " not found", NULL);
+ return 0;
+ }
+
+ bgnode = Ttk_NewLayoutNode(TTK_FILL_BOTH, bgelement);
+ bgnode->next = Ttk_InstantiateLayout(themePtr, layoutTemplate);
+
+ return TTKNewLayout(style, recordPtr, optionTable, tkwin, bgnode);
+}
+
+/* Ttk_CreateSublayout --
+ * Creates a new sublayout.
+ *
+ * Sublayouts are used to draw subparts of a compound widget.
+ * They use the same Tk_Window, but a different option table
+ * and data record.
+ */
+Ttk_Layout
+Ttk_CreateSublayout(
+ Tcl_Interp *interp,
+ Ttk_Theme themePtr,
+ Ttk_Layout parentLayout,
+ const char *baseName,
+ Tk_OptionTable optionTable)
+{
+ Tcl_DString buf;
+ const char *styleName;
+ Ttk_Style style;
+ Ttk_LayoutTemplate layoutTemplate;
+
+ Tcl_DStringInit(&buf);
+ Tcl_DStringAppend(&buf, Ttk_StyleName(parentLayout->style), -1);
+ Tcl_DStringAppend(&buf, baseName, -1);
+ styleName = Tcl_DStringValue(&buf);
+
+ style = Ttk_GetStyle(themePtr, styleName);
+ layoutTemplate = Ttk_FindLayoutTemplate(themePtr, styleName);
+
+ if (!layoutTemplate) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "Layout ", styleName, " not found", NULL);
+ return 0;
+ }
+
+ Tcl_DStringFree(&buf);
+
+ return TTKNewLayout(
+ style, 0, optionTable, parentLayout->tkwin,
+ Ttk_InstantiateLayout(themePtr, layoutTemplate));
+}
+
+/* Ttk_RebindSublayout --
+ * Bind sublayout to new data source.
+ */
+void Ttk_RebindSublayout(Ttk_Layout layout, void *recordPtr)
+{
+ layout->recordPtr = recordPtr;
+}
+
+/*
+ * Ttk_QueryOption --
+ * Look up an option from a layout's associated option.
+ */
+Tcl_Obj *Ttk_QueryOption(
+ Ttk_Layout layout, const char *optionName, Ttk_State state)
+{
+ return Ttk_QueryStyle(
+ layout->style,layout->recordPtr,layout->optionTable,optionName,state);
+}
+
+/*
+ * Ttk_LayoutStyle --
+ * Extract Ttk_Style from Ttk_Layout.
+ */
+Ttk_Style Ttk_LayoutStyle(Ttk_Layout layout)
+{
+ return layout->style;
+}
+
+/*------------------------------------------------------------------------
+ * +++ Size computation.
+ */
+static void Ttk_NodeListSize(
+ Ttk_Layout layout, Ttk_LayoutNode *node,
+ Ttk_State state, int *widthPtr, int *heightPtr); /* Forward */
+
+static void Ttk_NodeSize(
+ Ttk_Layout layout, Ttk_LayoutNode *node, Ttk_State state,
+ int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+{
+ int elementWidth, elementHeight, subWidth, subHeight;
+ Ttk_Padding elementPadding;
+
+ Ttk_ElementSize(node->eclass,
+ layout->style, layout->recordPtr,layout->optionTable, layout->tkwin,
+ state|node->state,
+ &elementWidth, &elementHeight, &elementPadding);
+
+ Ttk_NodeListSize(layout,node->child,state,&subWidth,&subHeight);
+ subWidth += Ttk_PaddingWidth(elementPadding);
+ subHeight += Ttk_PaddingHeight(elementPadding);
+
+ *widthPtr = MAX(elementWidth, subWidth);
+ *heightPtr = MAX(elementHeight, subHeight);
+ *paddingPtr = elementPadding;
+}
+
+static void Ttk_NodeListSize(
+ Ttk_Layout layout, Ttk_LayoutNode *node,
+ Ttk_State state, int *widthPtr, int *heightPtr)
+{
+ if (!node) {
+ *widthPtr = *heightPtr = 0;
+ } else {
+ int width, height, restWidth, restHeight;
+ Ttk_Padding unused;
+
+ Ttk_NodeSize(layout, node, state, &width, &height, &unused);
+ Ttk_NodeListSize(layout, node->next, state, &restWidth, &restHeight);
+
+ if (node->flags & (TTK_PACK_LEFT|TTK_PACK_RIGHT)) {
+ *widthPtr = width + restWidth;
+ } else {
+ *widthPtr = MAX(width, restWidth);
+ }
+
+ if (node->flags & (TTK_PACK_TOP|TTK_PACK_BOTTOM)) {
+ *heightPtr = height + restHeight;
+ } else {
+ *heightPtr = MAX(height, restHeight);
+ }
+ }
+}
+
+/*
+ * Ttk_LayoutNodeInternalPadding --
+ * Returns the internal padding of a layout node.
+ */
+Ttk_Padding Ttk_LayoutNodeInternalPadding(
+ Ttk_Layout layout, Ttk_LayoutNode *node)
+{
+ int unused;
+ Ttk_Padding padding;
+ Ttk_ElementSize(node->eclass,
+ layout->style, layout->recordPtr, layout->optionTable, layout->tkwin,
+ 0/*state*/, &unused, &unused, &padding);
+ return padding;
+}
+
+/*
+ * Ttk_LayoutNodeInternalParcel --
+ * Returns the inner area of a specified layout node,
+ * based on current parcel and element's internal padding.
+ */
+Ttk_Box Ttk_LayoutNodeInternalParcel(Ttk_Layout layout, Ttk_LayoutNode *node)
+{
+ Ttk_Padding padding = Ttk_LayoutNodeInternalPadding(layout, node);
+ return Ttk_PadBox(node->parcel, padding);
+}
+
+/* Ttk_LayoutSize --
+ * Compute requested size of a layout.
+ */
+void Ttk_LayoutSize(
+ Ttk_Layout layout, Ttk_State state, int *widthPtr, int *heightPtr)
+{
+ Ttk_NodeListSize(layout, layout->root, state, widthPtr, heightPtr);
+}
+
+void Ttk_LayoutNodeReqSize( /* @@@ Rename this */
+ Ttk_Layout layout, Ttk_LayoutNode *node, int *widthPtr, int *heightPtr)
+{
+ Ttk_Padding unused;
+ Ttk_NodeSize(layout, node, 0/*state*/, widthPtr, heightPtr, &unused);
+}
+
+/*------------------------------------------------------------------------
+ * +++ Layout placement.
+ */
+
+/* Ttk_PlaceNodeList --
+ * Compute parcel for each node in a layout tree
+ * according to position specification and overall size.
+ */
+static void Ttk_PlaceNodeList(
+ Ttk_Layout layout, Ttk_LayoutNode *node, Ttk_State state, Ttk_Box cavity)
+{
+ for (; node; node = node->next)
+ {
+ int width, height;
+ Ttk_Padding padding;
+
+ /* Compute node size: (@@@ cache this instead?)
+ */
+ Ttk_NodeSize(layout, node, state, &width, &height, &padding);
+
+ /* Compute parcel:
+ */
+ node->parcel = Ttk_PositionBox(&cavity, width, height, node->flags);
+
+ /* Place child nodes:
+ */
+ if (node->child) {
+ Ttk_Box childBox = Ttk_PadBox(node->parcel, padding);
+ Ttk_PlaceNodeList(layout,node->child, state, childBox);
+ }
+ }
+}
+
+void Ttk_PlaceLayout(Ttk_Layout layout, Ttk_State state, Ttk_Box b)
+{
+ Ttk_PlaceNodeList(layout, layout->root, state, b);
+}
+
+/*------------------------------------------------------------------------
+ * +++ Layout drawing.
+ */
+
+/*
+ * Ttk_DrawLayout --
+ * Draw a layout tree.
+ */
+static void Ttk_DrawNodeList(
+ Ttk_Layout layout, Ttk_State state, Ttk_LayoutNode *node, Drawable d)
+{
+ for (; node; node = node->next)
+ {
+ int border = node->flags & TTK_BORDER;
+ int substate = state;
+
+ if (node->flags & TTK_UNIT)
+ substate |= node->state;
+
+ if (node->child && border)
+ Ttk_DrawNodeList(layout, substate, node->child, d);
+
+ Ttk_DrawElement(
+ node->eclass,
+ layout->style,layout->recordPtr,layout->optionTable,layout->tkwin,
+ d, node->parcel, state | node->state);
+
+ if (node->child && !border)
+ Ttk_DrawNodeList(layout, substate, node->child, d);
+ }
+}
+
+void Ttk_DrawLayout(Ttk_Layout layout, Ttk_State state, Drawable d)
+{
+ Ttk_DrawNodeList(layout, state, layout->root, d);
+}
+
+/*------------------------------------------------------------------------
+ * +++ Inquiry and modification.
+ */
+
+/*
+ * Ttk_IdentifyElement --
+ * Find the element at the specified x,y coordinate.
+ */
+static Ttk_Element IdentifyNode(Ttk_Element node, int x, int y)
+{
+ Ttk_Element closest = NULL;
+
+ for (; node; node = node->next) {
+ if (Ttk_BoxContains(node->parcel, x, y)) {
+ closest = node;
+ if (node->child && !(node->flags & TTK_UNIT)) {
+ Ttk_Element childNode = IdentifyNode(node->child, x,y);
+ if (childNode) {
+ closest = childNode;
+ }
+ }
+ }
+ }
+ return closest;
+}
+
+Ttk_Element Ttk_IdentifyElement(Ttk_Layout layout, int x, int y)
+{
+ return IdentifyNode(layout->root, x, y);
+}
+
+/*
+ * tail --
+ * Return the last component of an element name, e.g.,
+ * "Scrollbar.thumb" => "thumb"
+ */
+static const char *tail(const char *elementName)
+{
+ const char *dot;
+ while ((dot=strchr(elementName,'.')) != NULL)
+ elementName = dot + 1;
+ return elementName;
+}
+
+/*
+ * Ttk_FindElement --
+ * Look up an element by name
+ */
+static Ttk_Element
+FindNode(Ttk_Element node, const char *nodeName)
+{
+ for (; node ; node = node->next) {
+ if (!strcmp(tail(Ttk_ElementName(node)), nodeName))
+ return node;
+
+ if (node->child) {
+ Ttk_Element childNode = FindNode(node->child, nodeName);
+ if (childNode)
+ return childNode;
+ }
+ }
+ return 0;
+}
+
+Ttk_Element Ttk_FindElement(Ttk_Layout layout, const char *nodeName)
+{
+ return FindNode(layout->root, nodeName);
+}
+
+/*
+ * Ttk_ClientRegion --
+ * Find the internal parcel of a named element within a given layout.
+ * If the element is not present, use the entire window.
+ */
+Ttk_Box Ttk_ClientRegion(Ttk_Layout layout, const char *elementName)
+{
+ Ttk_Element element = Ttk_FindElement(layout, elementName);
+ return element
+ ? Ttk_LayoutNodeInternalParcel(layout, element)
+ : Ttk_WinBox(layout->tkwin)
+ ;
+}
+
+/*
+ * Ttk_ElementName --
+ * Return the name (class name) of the element.
+ */
+const char *Ttk_ElementName(Ttk_Element node)
+{
+ return Ttk_ElementClassName(node->eclass);
+}
+
+/*
+ * Ttk_ElementParcel --
+ * Return the element's current parcel.
+ */
+Ttk_Box Ttk_ElementParcel(Ttk_Element node)
+{
+ return node->parcel;
+}
+
+/*
+ * Ttk_PlaceElement --
+ * Explicitly specify an element's parcel.
+ */
+void Ttk_PlaceElement(Ttk_Layout layout, Ttk_Element node, Ttk_Box b)
+{
+ node->parcel = b;
+ if (node->child) {
+ Ttk_PlaceNodeList(layout, node->child, 0,
+ Ttk_PadBox(b, Ttk_LayoutNodeInternalPadding(layout, node)));
+ }
+}
+
+/*
+ * Ttk_ChangeElementState --
+ */
+void Ttk_ChangeElementState(Ttk_LayoutNode *node,unsigned set,unsigned clr)
+{
+ node->state = (node->state | set) & ~clr;
+}
+
+/*EOF*/
diff --git a/generic/ttk/ttkManager.c b/generic/ttk/ttkManager.c
new file mode 100644
index 0000000..ba9e5c0
--- /dev/null
+++ b/generic/ttk/ttkManager.c
@@ -0,0 +1,552 @@
+/*
+ * Copyright 2005, Joe English. Freely redistributable.
+ *
+ * Support routines for geometry managers.
+ */
+
+#include <string.h>
+#include <tk.h>
+#include "ttkManager.h"
+
+/*------------------------------------------------------------------------
+ * +++ The Geometry Propagation Dance.
+ *
+ * When a slave window requests a new size or some other parameter changes,
+ * the manager recomputes the required size for the master window and calls
+ * Tk_GeometryRequest(). This is scheduled as an idle handler so multiple
+ * updates can be processed as a single batch.
+ *
+ * If all goes well, the master's manager will process the request
+ * (and so on up the chain to the toplevel window), and the master
+ * window will eventually receive a <Configure> event. At this point
+ * it recomputes the size and position of all slaves and places them.
+ *
+ * If all does not go well, however, the master's request may be ignored
+ * (typically because the top-level window has a fixed, user-specified size).
+ * Tk doesn't provide any notification when this happens; to account for this,
+ * we also schedule an idle handler to call the layout procedure
+ * after making a geometry request.
+ *
+ * +++ Slave removal <<NOTE-LOSTSLAVE>>.
+ *
+ * There are three conditions under which a slave is removed:
+ *
+ * (1) Another GM claims control
+ * (2) Manager voluntarily relinquishes control
+ * (3) Slave is destroyed
+ *
+ * In case (1), Tk calls the manager's lostSlaveProc.
+ * Case (2) is performed by calling Tk_ManageGeometry(slave,NULL,0);
+ * in this case Tk does _not_ call the LostSlaveProc (documented behavior).
+ * Tk doesn't handle case (3) either; to account for that we
+ * register an event handler on the slave widget to track <Destroy> events.
+ */
+
+/* ++ Data structures.
+ */
+typedef struct
+{
+ Tk_Window slaveWindow;
+ Ttk_Manager *manager;
+ void *slaveData;
+ unsigned flags;
+} Ttk_Slave;
+
+/* slave->flags bits:
+ */
+#define SLAVE_MAPPED 0x1 /* slave to be mapped when master is */
+
+struct TtkManager_
+{
+ Ttk_ManagerSpec *managerSpec;
+ void *managerData;
+ Tk_Window masterWindow;
+ unsigned flags;
+ int nSlaves;
+ Ttk_Slave **slaves;
+};
+
+/* manager->flags bits:
+ */
+#define MGR_UPDATE_PENDING 0x1
+#define MGR_RESIZE_REQUIRED 0x2
+#define MGR_RELAYOUT_REQUIRED 0x4
+
+static void ManagerIdleProc(void *); /* forward */
+
+/* ++ ScheduleUpdate --
+ * Schedule a call to recompute the size and/or layout,
+ * depending on flags.
+ */
+static void ScheduleUpdate(Ttk_Manager *mgr, unsigned flags)
+{
+ if (!(mgr->flags & MGR_UPDATE_PENDING)) {
+ Tcl_DoWhenIdle(ManagerIdleProc, mgr);
+ mgr->flags |= MGR_UPDATE_PENDING;
+ }
+ mgr->flags |= flags;
+}
+
+/* ++ RecomputeSize --
+ * Recomputes the required size of the master window,
+ * makes geometry request.
+ */
+static void RecomputeSize(Ttk_Manager *mgr)
+{
+ int width = 1, height = 1;
+
+ if (mgr->managerSpec->RequestedSize(mgr->managerData, &width, &height)) {
+ Tk_GeometryRequest(mgr->masterWindow, width, height);
+ ScheduleUpdate(mgr, MGR_RELAYOUT_REQUIRED);
+ }
+ mgr->flags &= ~MGR_RESIZE_REQUIRED;
+}
+
+/* ++ RecomputeLayout --
+ * Recompute geometry of all slaves.
+ */
+static void RecomputeLayout(Ttk_Manager *mgr)
+{
+ mgr->managerSpec->PlaceSlaves(mgr->managerData);
+ mgr->flags &= ~MGR_RELAYOUT_REQUIRED;
+}
+
+/* ++ ManagerIdleProc --
+ * DoWhenIdle procedure for deferred updates.
+ */
+static void ManagerIdleProc(ClientData clientData)
+{
+ Ttk_Manager *mgr = clientData;
+ mgr->flags &= ~MGR_UPDATE_PENDING;
+
+ if (mgr->flags & MGR_RESIZE_REQUIRED) {
+ RecomputeSize(mgr);
+ }
+ if (mgr->flags & MGR_RELAYOUT_REQUIRED) {
+ if (mgr->flags & MGR_UPDATE_PENDING) {
+ /* RecomputeSize has scheduled another update; relayout later */
+ return;
+ }
+ RecomputeLayout(mgr);
+ }
+}
+
+/*------------------------------------------------------------------------
+ * +++ Event handlers.
+ */
+
+/* ++ ManagerEventHandler --
+ * Recompute slave layout when master widget is resized.
+ * Keep the slave's map state in sync with the master's.
+ */
+static const int ManagerEventMask = StructureNotifyMask;
+static void ManagerEventHandler(ClientData clientData, XEvent *eventPtr)
+{
+ Ttk_Manager *mgr = clientData;
+ int i;
+
+ switch (eventPtr->type)
+ {
+ case ConfigureNotify:
+ RecomputeLayout(mgr);
+ break;
+ case MapNotify:
+ for (i = 0; i < mgr->nSlaves; ++i) {
+ Ttk_Slave *slave = mgr->slaves[i];
+ if (slave->flags & SLAVE_MAPPED) {
+ Tk_MapWindow(slave->slaveWindow);
+ }
+ }
+ break;
+ case UnmapNotify:
+ for (i = 0; i < mgr->nSlaves; ++i) {
+ Ttk_Slave *slave = mgr->slaves[i];
+ Tk_UnmapWindow(slave->slaveWindow);
+ }
+ break;
+ }
+}
+
+/* ++ SlaveEventHandler --
+ * Notifies manager when a slave is destroyed
+ * (see <<NOTE-LOSTSLAVE>>).
+ */
+static const unsigned SlaveEventMask = StructureNotifyMask;
+static void SlaveEventHandler(ClientData clientData, XEvent *eventPtr)
+{
+ Ttk_Slave *slave = clientData;
+ if (eventPtr->type == DestroyNotify) {
+ slave->manager->managerSpec->tkGeomMgr.lostSlaveProc(
+ slave->manager, slave->slaveWindow);
+ }
+}
+
+/*------------------------------------------------------------------------
+ * +++ Slave initialization and cleanup.
+ */
+
+static Ttk_Slave *NewSlave(
+ Ttk_Manager *mgr, Tk_Window slaveWindow, void *slaveData)
+{
+ Ttk_Slave *slave = (Ttk_Slave*)ckalloc(sizeof(*slave));
+
+ slave->slaveWindow = slaveWindow;
+ slave->manager = mgr;
+ slave->flags = 0;
+ slave->slaveData = slaveData;
+
+ return slave;
+}
+
+static void DeleteSlave(Ttk_Slave *slave)
+{
+ ckfree((ClientData)slave);
+}
+
+/*------------------------------------------------------------------------
+ * +++ Manager initialization and cleanup.
+ */
+
+Ttk_Manager *Ttk_CreateManager(
+ Ttk_ManagerSpec *managerSpec, void *managerData, Tk_Window masterWindow)
+{
+ Ttk_Manager *mgr = (Ttk_Manager*)ckalloc(sizeof(*mgr));
+
+ mgr->managerSpec = managerSpec;
+ mgr->managerData = managerData;
+ mgr->masterWindow = masterWindow;
+ mgr->nSlaves = 0;
+ mgr->slaves = NULL;
+ mgr->flags = 0;
+
+ Tk_CreateEventHandler(
+ mgr->masterWindow, ManagerEventMask, ManagerEventHandler, mgr);
+
+ return mgr;
+}
+
+void Ttk_DeleteManager(Ttk_Manager *mgr)
+{
+ Tk_DeleteEventHandler(
+ mgr->masterWindow, ManagerEventMask, ManagerEventHandler, mgr);
+
+ while (mgr->nSlaves > 0) {
+ Ttk_ForgetSlave(mgr, mgr->nSlaves - 1);
+ }
+ if (mgr->slaves) {
+ ckfree((ClientData)mgr->slaves);
+ }
+
+ Tk_CancelIdleCall(ManagerIdleProc, mgr);
+
+ ckfree((ClientData)mgr);
+}
+
+/*------------------------------------------------------------------------
+ * +++ Slave management.
+ */
+
+/* ++ InsertSlave --
+ * Adds slave to the list of managed windows.
+ */
+static void InsertSlave(Ttk_Manager *mgr, Ttk_Slave *slave, int index)
+{
+ int endIndex = mgr->nSlaves++;
+ mgr->slaves = (Ttk_Slave**)ckrealloc(
+ (ClientData)mgr->slaves, mgr->nSlaves * sizeof(Ttk_Slave *));
+
+ while (endIndex > index) {
+ mgr->slaves[endIndex] = mgr->slaves[endIndex - 1];
+ --endIndex;
+ }
+
+ mgr->slaves[index] = slave;
+
+ Tk_ManageGeometry(slave->slaveWindow,
+ &mgr->managerSpec->tkGeomMgr, (ClientData)mgr);
+
+ Tk_CreateEventHandler(slave->slaveWindow,
+ SlaveEventMask, SlaveEventHandler, (ClientData)slave);
+
+ ScheduleUpdate(mgr, MGR_RESIZE_REQUIRED);
+}
+
+/* RemoveSlave --
+ * Unmanage and delete the slave.
+ *
+ * NOTES/ASSUMPTIONS:
+ *
+ * [1] It's safe to call Tk_UnmapWindow / Tk_UnmaintainGeometry even if this
+ * routine is called from the slave's DestroyNotify event handler.
+ */
+static void RemoveSlave(Ttk_Manager *mgr, int index)
+{
+ Ttk_Slave *slave = mgr->slaves[index];
+ int i;
+
+ /* Notify manager:
+ */
+ mgr->managerSpec->SlaveRemoved(mgr->managerData, index);
+
+ /* Remove from array:
+ */
+ --mgr->nSlaves;
+ for (i = index ; i < mgr->nSlaves; ++i) {
+ mgr->slaves[i] = mgr->slaves[i+1];
+ }
+
+ /* Clean up:
+ */
+ Tk_DeleteEventHandler(
+ slave->slaveWindow, SlaveEventMask, SlaveEventHandler, slave);
+
+ /* Note [1] */
+ Tk_UnmaintainGeometry(slave->slaveWindow, mgr->masterWindow);
+ Tk_UnmapWindow(slave->slaveWindow);
+
+ DeleteSlave(slave);
+
+ ScheduleUpdate(mgr, MGR_RESIZE_REQUIRED);
+}
+
+/*------------------------------------------------------------------------
+ * +++ Tk_GeomMgr hooks.
+ */
+
+void Ttk_GeometryRequestProc(ClientData clientData, Tk_Window slaveWindow)
+{
+ Ttk_Manager *mgr = clientData;
+ int slaveIndex = Ttk_SlaveIndex(mgr, slaveWindow);
+ int reqWidth = Tk_ReqWidth(slaveWindow);
+ int reqHeight= Tk_ReqHeight(slaveWindow);
+
+ if (mgr->managerSpec->SlaveRequest(
+ mgr->managerData, slaveIndex, reqWidth, reqHeight))
+ {
+ ScheduleUpdate(mgr, MGR_RESIZE_REQUIRED);
+ }
+}
+
+void Ttk_LostSlaveProc(ClientData clientData, Tk_Window slaveWindow)
+{
+ Ttk_Manager *mgr = clientData;
+ int index = Ttk_SlaveIndex(mgr, slaveWindow);
+
+ /* ASSERT: index >= 0 */
+ RemoveSlave(mgr, index);
+}
+
+/*------------------------------------------------------------------------
+ * +++ Public API.
+ */
+
+/* ++ Ttk_InsertSlave --
+ * Add a new slave window at the specified index.
+ */
+void Ttk_InsertSlave(
+ Ttk_Manager *mgr, int index, Tk_Window tkwin, void *slaveData)
+{
+ Ttk_Slave *slave = NewSlave(mgr, tkwin, slaveData);
+ InsertSlave(mgr, slave, index);
+}
+
+/* ++ Ttk_ForgetSlave --
+ * Unmanage the specified slave.
+ */
+void Ttk_ForgetSlave(Ttk_Manager *mgr, int slaveIndex)
+{
+ Tk_Window slaveWindow = mgr->slaves[slaveIndex]->slaveWindow;
+ RemoveSlave(mgr, slaveIndex);
+ Tk_ManageGeometry(slaveWindow, NULL, 0);
+}
+
+/* ++ Ttk_PlaceSlave --
+ * Set the position and size of the specified slave window.
+ *
+ * NOTES:
+ * Contrary to documentation, Tk_MaintainGeometry doesn't always
+ * map the slave.
+ */
+void Ttk_PlaceSlave(
+ Ttk_Manager *mgr, int slaveIndex, int x, int y, int width, int height)
+{
+ Ttk_Slave *slave = mgr->slaves[slaveIndex];
+ Tk_MaintainGeometry(slave->slaveWindow,mgr->masterWindow,x,y,width,height);
+ slave->flags |= SLAVE_MAPPED;
+ if (Tk_IsMapped(mgr->masterWindow)) {
+ Tk_MapWindow(slave->slaveWindow);
+ }
+}
+
+/* ++ Ttk_UnmapSlave --
+ * Unmap the specified slave, but leave it managed.
+ */
+void Ttk_UnmapSlave(Ttk_Manager *mgr, int slaveIndex)
+{
+ Ttk_Slave *slave = mgr->slaves[slaveIndex];
+ Tk_UnmaintainGeometry(slave->slaveWindow, mgr->masterWindow);
+ slave->flags &= ~SLAVE_MAPPED;
+ /* Contrary to documentation, Tk_UnmaintainGeometry doesn't always
+ * unmap the slave:
+ */
+ Tk_UnmapWindow(slave->slaveWindow);
+}
+
+/* LayoutChanged, SizeChanged --
+ * Schedule a relayout, resp. resize request.
+ */
+void Ttk_ManagerLayoutChanged(Ttk_Manager *mgr)
+{
+ ScheduleUpdate(mgr, MGR_RELAYOUT_REQUIRED);
+}
+
+void Ttk_ManagerSizeChanged(Ttk_Manager *mgr)
+{
+ ScheduleUpdate(mgr, MGR_RESIZE_REQUIRED);
+}
+
+/* +++ Accessors.
+ */
+int Ttk_NumberSlaves(Ttk_Manager *mgr)
+{
+ return mgr->nSlaves;
+}
+void *Ttk_SlaveData(Ttk_Manager *mgr, int slaveIndex)
+{
+ return mgr->slaves[slaveIndex]->slaveData;
+}
+Tk_Window Ttk_SlaveWindow(Ttk_Manager *mgr, int slaveIndex)
+{
+ return mgr->slaves[slaveIndex]->slaveWindow;
+}
+
+/*------------------------------------------------------------------------
+ * +++ Utility routines.
+ */
+
+/* ++ Ttk_SlaveIndex --
+ * Returns the index of specified slave window, -1 if not found.
+ */
+int Ttk_SlaveIndex(Ttk_Manager *mgr, Tk_Window slaveWindow)
+{
+ int index;
+ for (index = 0; index < mgr->nSlaves; ++index)
+ if (mgr->slaves[index]->slaveWindow == slaveWindow)
+ return index;
+ return -1;
+}
+
+/* ++ Ttk_GetSlaveIndexFromObj(interp, mgr, objPtr, indexPtr) --
+ * Return the index of the slave specified by objPtr.
+ * Slaves may be specified as an integer index or
+ * as the name of the managed window.
+ *
+ * Returns:
+ * Standard Tcl completion code. Leaves an error message in case of error.
+ */
+
+int Ttk_GetSlaveIndexFromObj(
+ Tcl_Interp *interp, Ttk_Manager *mgr, Tcl_Obj *objPtr, int *indexPtr)
+{
+ const char *string = Tcl_GetString(objPtr);
+ int slaveIndex = 0;
+ Tk_Window tkwin;
+
+ /* Try interpreting as an integer first:
+ */
+ if (Tcl_GetIntFromObj(NULL, objPtr, &slaveIndex) == TCL_OK) {
+ if (slaveIndex < 0 || slaveIndex >= mgr->nSlaves) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp,
+ "Slave index ", Tcl_GetString(objPtr), " out of bounds",
+ NULL);
+ return TCL_ERROR;
+ }
+ *indexPtr = slaveIndex;
+ return TCL_OK;
+ }
+
+ /* Try interpreting as a slave window name;
+ */
+ if ( (*string == '.')
+ && (tkwin = Tk_NameToWindow(interp, string, mgr->masterWindow)))
+ {
+ slaveIndex = Ttk_SlaveIndex(mgr, tkwin);
+ if (slaveIndex < 0) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp,
+ string, " is not managed by ", Tk_PathName(mgr->masterWindow),
+ NULL);
+ return TCL_ERROR;
+ }
+ *indexPtr = slaveIndex;
+ return TCL_OK;
+ }
+
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "Invalid slave specification ", string, NULL);
+ return TCL_ERROR;
+}
+
+/* ++ Ttk_ReorderSlave(mgr, fromIndex, toIndex) --
+ * Change slave order.
+ */
+void Ttk_ReorderSlave(Ttk_Manager *mgr, int fromIndex, int toIndex)
+{
+ Ttk_Slave *moved = mgr->slaves[fromIndex];
+
+ /* Shuffle down: */
+ while (fromIndex > toIndex) {
+ mgr->slaves[fromIndex] = mgr->slaves[fromIndex - 1];
+ --fromIndex;
+ }
+ /* Or, shuffle up: */
+ while (fromIndex < toIndex) {
+ mgr->slaves[fromIndex] = mgr->slaves[fromIndex + 1];
+ ++fromIndex;
+ }
+ /* ASSERT: fromIndex == toIndex */
+ mgr->slaves[fromIndex] = moved;
+
+ /* Schedule a relayout. In general, rearranging slaves
+ * may also change the size:
+ */
+ ScheduleUpdate(mgr, MGR_RESIZE_REQUIRED);
+}
+
+/* ++ Ttk_Maintainable(interp, slave, master) --
+ * Utility routine. Verifies that 'master' may be used to maintain
+ * the geometry of 'slave' via Tk_MaintainGeometry:
+ *
+ * + 'master' is either 'slave's parent -OR-
+ * + 'master is a descendant of 'slave's parent.
+ * + 'slave' is not a toplevel window
+ * + 'slave' belongs to the same toplevel as 'master'
+ *
+ * Returns: 1 if OK; otherwise 0, leaving an error message in 'interp'.
+ */
+int Ttk_Maintainable(Tcl_Interp *interp, Tk_Window slave, Tk_Window master)
+{
+ Tk_Window ancestor = master, parent = Tk_Parent(slave);
+
+ if (Tk_IsTopLevel(slave) || slave == master) {
+ goto badWindow;
+ }
+
+ while (ancestor != parent) {
+ if (Tk_IsTopLevel(ancestor)) {
+ goto badWindow;
+ }
+ ancestor = Tk_Parent(ancestor);
+ }
+
+ return 1;
+
+badWindow:
+ Tcl_AppendResult(interp,
+ "can't add ", Tk_PathName(slave),
+ " as slave of ", Tk_PathName(master),
+ NULL);
+ return 0;
+}
+
diff --git a/generic/ttk/ttkManager.h b/generic/ttk/ttkManager.h
new file mode 100644
index 0000000..d22ff98
--- /dev/null
+++ b/generic/ttk/ttkManager.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2005, Joe English. Freely redistributable.
+ *
+ * Geometry manager utilities.
+ */
+
+#ifndef _TTKMANAGER
+#define _TTKMANAGER
+
+#include "ttkTheme.h"
+
+typedef struct TtkManager_ Ttk_Manager;
+
+/*
+ * Geometry manager specification record:
+ *
+ * RequestedSize computes the requested size of the master window.
+ *
+ * PlaceSlaves sets the position and size of all managed slaves
+ * by calling Ttk_PlaceSlave().
+ *
+ * SlaveRemoved() is called immediately before a slave is removed.
+ * NB: the associated slave window may have been destroyed when this
+ * routine is called.
+ *
+ * SlaveRequest() is called when a slave requests a size change.
+ * It should return 1 if the request should propagate, 0 otherwise.
+ */
+typedef struct { /* Manager hooks */
+ Tk_GeomMgr tkGeomMgr; /* "real" Tk Geometry Manager */
+
+ int (*RequestedSize)(void *managerData, int *widthPtr, int *heightPtr);
+ void (*PlaceSlaves)(void *managerData);
+ int (*SlaveRequest)(void *managerData, int slaveIndex, int w, int h);
+ void (*SlaveRemoved)(void *managerData, int slaveIndex);
+} Ttk_ManagerSpec;
+
+/*
+ * Default implementations for Tk_GeomMgr hooks:
+ */
+MODULE_SCOPE void Ttk_GeometryRequestProc(ClientData, Tk_Window slave);
+MODULE_SCOPE void Ttk_LostSlaveProc(ClientData, Tk_Window slave);
+
+/*
+ * Public API:
+ */
+MODULE_SCOPE Ttk_Manager *Ttk_CreateManager(
+ Ttk_ManagerSpec *, void *managerData, Tk_Window masterWindow);
+MODULE_SCOPE void Ttk_DeleteManager(Ttk_Manager *);
+
+MODULE_SCOPE void Ttk_InsertSlave(
+ Ttk_Manager *, int position, Tk_Window, void *slaveData);
+
+MODULE_SCOPE void Ttk_ForgetSlave(Ttk_Manager *, int slaveIndex);
+
+MODULE_SCOPE void Ttk_ReorderSlave(Ttk_Manager *, int fromIndex, int toIndex);
+ /* Rearrange slave positions */
+
+MODULE_SCOPE void Ttk_PlaceSlave(
+ Ttk_Manager *, int slaveIndex, int x, int y, int width, int height);
+ /* Position and map the slave */
+
+MODULE_SCOPE void Ttk_UnmapSlave(Ttk_Manager *, int slaveIndex);
+ /* Unmap the slave */
+
+MODULE_SCOPE void Ttk_ManagerSizeChanged(Ttk_Manager *);
+MODULE_SCOPE void Ttk_ManagerLayoutChanged(Ttk_Manager *);
+ /* Notify manager that size (resp. layout) needs to be recomputed */
+
+/* Utilities:
+ */
+MODULE_SCOPE int Ttk_SlaveIndex(Ttk_Manager *, Tk_Window);
+ /* Returns: index in slave array of specified window, -1 if not found */
+
+MODULE_SCOPE int Ttk_GetSlaveIndexFromObj(
+ Tcl_Interp *, Ttk_Manager *, Tcl_Obj *, int *indexPtr);
+
+/* Accessor functions:
+ */
+MODULE_SCOPE int Ttk_NumberSlaves(Ttk_Manager *);
+ /* Returns: number of managed slaves */
+
+MODULE_SCOPE void *Ttk_SlaveData(Ttk_Manager *, int slaveIndex);
+ /* Returns: client data associated with slave */
+
+MODULE_SCOPE Tk_Window Ttk_SlaveWindow(Ttk_Manager *, int slaveIndex);
+ /* Returns: slave window */
+
+MODULE_SCOPE int Ttk_Maintainable(Tcl_Interp *, Tk_Window slave, Tk_Window master);
+ /* Returns: 1 if master can manage slave; 0 otherwise leaving error msg */
+
+#endif /* _TTKMANAGER */
diff --git a/generic/ttk/ttkNotebook.c b/generic/ttk/ttkNotebook.c
new file mode 100644
index 0000000..551f4a6
--- /dev/null
+++ b/generic/ttk/ttkNotebook.c
@@ -0,0 +1,1413 @@
+/*
+ * Copyright (c) 2004, Joe English
+ */
+
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <tk.h>
+
+#include "ttkTheme.h"
+#include "ttkWidget.h"
+#include "ttkManager.h"
+
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+#define MAX(a,b) ((a) > (b) ? (a) : (b))
+
+/*------------------------------------------------------------------------
+ * +++ Tab resources.
+ */
+
+#define DEFAULT_MIN_TAB_WIDTH 24
+
+static const char *const TabStateStrings[] = { "normal", "disabled", "hidden", 0 };
+typedef enum {
+ TAB_STATE_NORMAL, TAB_STATE_DISABLED, TAB_STATE_HIDDEN
+} TAB_STATE;
+
+typedef struct
+{
+ /* Internal data:
+ */
+ int width, height; /* Requested size of tab */
+ Ttk_Box parcel; /* Tab position */
+
+ /* Tab options:
+ */
+ TAB_STATE state;
+
+ /* Child window options:
+ */
+ Tcl_Obj *paddingObj; /* Padding inside pane */
+ Ttk_Padding padding;
+ Tcl_Obj *stickyObj;
+ Ttk_Sticky sticky;
+
+ /* Label options:
+ */
+ Tcl_Obj *textObj;
+ Tcl_Obj *imageObj;
+ Tcl_Obj *compoundObj;
+ Tcl_Obj *underlineObj;
+
+} Tab;
+
+/* Two different option tables are used for tabs:
+ * TabOptionSpecs is used to draw the tab, and only includes resources
+ * relevant to the tab.
+ *
+ * PaneOptionSpecs includes additional options for child window placement
+ * and is used to configure the slave.
+ */
+static Tk_OptionSpec TabOptionSpecs[] =
+{
+ {TK_OPTION_STRING_TABLE, "-state", "", "",
+ "normal", -1,Tk_Offset(Tab,state),
+ 0,(ClientData)TabStateStrings,0 },
+ {TK_OPTION_STRING, "-text", "text", "Text", "",
+ Tk_Offset(Tab,textObj), -1, 0,0,GEOMETRY_CHANGED },
+ {TK_OPTION_STRING, "-image", "image", "Image", NULL/*default*/,
+ Tk_Offset(Tab,imageObj), -1, TK_OPTION_NULL_OK,0,GEOMETRY_CHANGED },
+ {TK_OPTION_STRING_TABLE, "-compound", "compound", "Compound",
+ "none", Tk_Offset(Tab,compoundObj), -1,
+ 0,(ClientData)ttkCompoundStrings,GEOMETRY_CHANGED },
+ {TK_OPTION_INT, "-underline", "underline", "Underline", "-1",
+ Tk_Offset(Tab,underlineObj), -1, 0,0,GEOMETRY_CHANGED },
+ {TK_OPTION_END, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0 }
+};
+
+static Tk_OptionSpec PaneOptionSpecs[] =
+{
+ {TK_OPTION_STRING, "-padding", "padding", "Padding", "0",
+ Tk_Offset(Tab,paddingObj), -1, 0,0,GEOMETRY_CHANGED },
+ {TK_OPTION_STRING, "-sticky", "sticky", "Sticky", "nsew",
+ Tk_Offset(Tab,stickyObj), -1, 0,0,GEOMETRY_CHANGED },
+
+ WIDGET_INHERIT_OPTIONS(TabOptionSpecs)
+};
+
+/*------------------------------------------------------------------------
+ * +++ Notebook resources.
+ */
+typedef struct
+{
+ Tcl_Obj *widthObj; /* Default width */
+ Tcl_Obj *heightObj; /* Default height */
+ Tcl_Obj *paddingObj; /* Padding around notebook */
+
+ Ttk_Manager *mgr; /* Geometry manager */
+ Tk_OptionTable tabOptionTable; /* Tab options */
+ Tk_OptionTable paneOptionTable; /* Tab+pane options */
+ int currentIndex; /* index of currently selected tab */
+ int activeIndex; /* index of currently active tab */
+ Ttk_Layout tabLayout; /* Sublayout for tabs */
+
+ Ttk_Box clientArea; /* Where to pack slave widgets */
+} NotebookPart;
+
+typedef struct
+{
+ WidgetCore core;
+ NotebookPart notebook;
+} Notebook;
+
+static Tk_OptionSpec NotebookOptionSpecs[] =
+{
+ {TK_OPTION_INT, "-width", "width", "Width", "0",
+ Tk_Offset(Notebook,notebook.widthObj),-1,
+ 0,0,GEOMETRY_CHANGED },
+ {TK_OPTION_INT, "-height", "height", "Height", "0",
+ Tk_Offset(Notebook,notebook.heightObj),-1,
+ 0,0,GEOMETRY_CHANGED },
+ {TK_OPTION_STRING, "-padding", "padding", "Padding", NULL,
+ Tk_Offset(Notebook,notebook.paddingObj),-1,
+ TK_OPTION_NULL_OK,0,GEOMETRY_CHANGED },
+
+ WIDGET_TAKEFOCUS_TRUE,
+ WIDGET_INHERIT_OPTIONS(ttkCoreOptionSpecs)
+};
+
+/* Notebook style options:
+ */
+typedef struct
+{
+ Ttk_PositionSpec tabPosition; /* Where to place tabs */
+ Ttk_Padding tabMargins; /* Margins around tab row */
+ Ttk_PositionSpec tabPlacement; /* How to pack tabs within tab row */
+ Ttk_Orient tabOrient; /* ... */
+ int minTabWidth; /* Minimum tab width */
+ Ttk_Padding padding; /* External padding */
+} NotebookStyle;
+
+static void NotebookStyleOptions(Notebook *nb, NotebookStyle *nbstyle)
+{
+ Tcl_Obj *objPtr;
+
+ nbstyle->tabPosition = TTK_PACK_TOP | TTK_STICK_W;
+ if ((objPtr = Ttk_QueryOption(nb->core.layout, "-tabposition", 0)) != 0) {
+ TtkGetLabelAnchorFromObj(NULL, objPtr, &nbstyle->tabPosition);
+ }
+
+ /* Guess default tabPlacement as function of tabPosition:
+ */
+ if (nbstyle->tabPosition & TTK_PACK_LEFT) {
+ nbstyle->tabPlacement = TTK_PACK_TOP | TTK_STICK_E;
+ } else if (nbstyle->tabPosition & TTK_PACK_RIGHT) {
+ nbstyle->tabPlacement = TTK_PACK_TOP | TTK_STICK_W;
+ } else if (nbstyle->tabPosition & TTK_PACK_BOTTOM) {
+ nbstyle->tabPlacement = TTK_PACK_LEFT | TTK_STICK_N;
+ } else { /* Assume TTK_PACK_TOP */
+ nbstyle->tabPlacement = TTK_PACK_LEFT | TTK_STICK_S;
+ }
+ if ((objPtr = Ttk_QueryOption(nb->core.layout, "-tabplacement", 0)) != 0) {
+ TtkGetLabelAnchorFromObj(NULL, objPtr, &nbstyle->tabPlacement);
+ }
+
+ /* Compute tabOrient as function of tabPlacement:
+ */
+ if (nbstyle->tabPlacement & (TTK_PACK_LEFT|TTK_PACK_RIGHT)) {
+ nbstyle->tabOrient = TTK_ORIENT_HORIZONTAL;
+ } else {
+ nbstyle->tabOrient = TTK_ORIENT_VERTICAL;
+ }
+
+ nbstyle->tabMargins = Ttk_UniformPadding(0);
+ if ((objPtr = Ttk_QueryOption(nb->core.layout, "-tabmargins", 0)) != 0) {
+ Ttk_GetBorderFromObj(NULL, objPtr, &nbstyle->tabMargins);
+ }
+
+ nbstyle->padding = Ttk_UniformPadding(0);
+ if ((objPtr = Ttk_QueryOption(nb->core.layout, "-padding", 0)) != 0) {
+ Ttk_GetPaddingFromObj(NULL,nb->core.tkwin,objPtr,&nbstyle->padding);
+ }
+
+ nbstyle->minTabWidth = DEFAULT_MIN_TAB_WIDTH;
+ if ((objPtr = Ttk_QueryOption(nb->core.layout, "-mintabwidth", 0)) != 0) {
+ Tcl_GetIntFromObj(NULL, objPtr, &nbstyle->minTabWidth);
+ }
+}
+
+/*------------------------------------------------------------------------
+ * +++ Tab management.
+ */
+
+static Tab *CreateTab(Tcl_Interp *interp, Notebook *nb, Tk_Window slaveWindow)
+{
+ Tk_OptionTable optionTable = nb->notebook.paneOptionTable;
+ void *record = ckalloc(sizeof(Tab));
+ memset(record, 0, sizeof(Tab));
+
+ if (Tk_InitOptions(interp, record, optionTable, slaveWindow) != TCL_OK) {
+ ckfree(record);
+ return NULL;
+ }
+
+ return record;
+}
+
+static void DestroyTab(Notebook *nb, Tab *tab)
+{
+ void *record = tab;
+ Tk_FreeConfigOptions(record, nb->notebook.paneOptionTable, nb->core.tkwin);
+ ckfree(record);
+}
+
+static int ConfigureTab(
+ Tcl_Interp *interp, Notebook *nb, Tab *tab, Tk_Window slaveWindow,
+ int objc, Tcl_Obj *const objv[])
+{
+ Ttk_Sticky sticky = tab->sticky;
+ Ttk_Padding padding = tab->padding;
+ Tk_SavedOptions savedOptions;
+ int mask = 0;
+
+ if (Tk_SetOptions(interp, (ClientData)tab, nb->notebook.paneOptionTable,
+ objc, objv, slaveWindow, &savedOptions, &mask) != TCL_OK)
+ {
+ return TCL_ERROR;
+ }
+
+ /* Check options:
+ * @@@ TODO: validate -image option.
+ */
+ if (Ttk_GetStickyFromObj(interp, tab->stickyObj, &sticky) != TCL_OK)
+ {
+ goto error;
+ }
+ if (Ttk_GetPaddingFromObj(interp, slaveWindow, tab->paddingObj, &padding)
+ != TCL_OK)
+ {
+ goto error;
+ }
+
+ tab->sticky = sticky;
+ tab->padding = padding;
+
+ Tk_FreeSavedOptions(&savedOptions);
+ Ttk_ManagerSizeChanged(nb->notebook.mgr);
+ TtkRedisplayWidget(&nb->core);
+
+ return TCL_OK;
+error:
+ Tk_RestoreSavedOptions(&savedOptions);
+ return TCL_ERROR;
+}
+
+/*
+ * IdentifyTab --
+ * Return the index of the tab at point x,y,
+ * or -1 if no tab at that point.
+ */
+static int IdentifyTab(Notebook *nb, int x, int y)
+{
+ int index;
+ for (index = 0; index < Ttk_NumberSlaves(nb->notebook.mgr); ++index) {
+ Tab *tab = Ttk_SlaveData(nb->notebook.mgr,index);
+ if ( tab->state != TAB_STATE_HIDDEN
+ && Ttk_BoxContains(tab->parcel, x,y))
+ {
+ return index;
+ }
+ }
+ return -1;
+}
+
+/*
+ * ActivateTab --
+ * Set the active tab index, redisplay if necessary.
+ */
+static void ActivateTab(Notebook *nb, int index)
+{
+ if (index != nb->notebook.activeIndex) {
+ nb->notebook.activeIndex = index;
+ TtkRedisplayWidget(&nb->core);
+ }
+}
+
+/*
+ * TabState --
+ * Return the state of the specified tab, based on
+ * notebook state, currentIndex, activeIndex, and user-specified tab state.
+ * The USER1 bit is set for the leftmost tab, and USER2
+ * is set for the rightmost tab.
+ */
+static Ttk_State TabState(Notebook *nb, int index)
+{
+ Ttk_State state = nb->core.state;
+ Tab *tab = Ttk_SlaveData(nb->notebook.mgr, index);
+
+ if (index == nb->notebook.currentIndex) {
+ state |= TTK_STATE_SELECTED;
+ } else {
+ state &= ~TTK_STATE_FOCUS;
+ }
+
+ if (index == nb->notebook.activeIndex) {
+ state |= TTK_STATE_ACTIVE;
+ }
+ if (index == 0) {
+ state |= TTK_STATE_USER1;
+ }
+ if (index == Ttk_NumberSlaves(nb->notebook.mgr) - 1) {
+ state |= TTK_STATE_USER2;
+ }
+ if (tab->state == TAB_STATE_DISABLED) {
+ state |= TTK_STATE_DISABLED;
+ }
+
+ return state;
+}
+
+/*------------------------------------------------------------------------
+ * +++ Geometry management - size computation.
+ */
+
+/* TabrowSize --
+ * Compute max height and total width of all tabs (horizontal layouts)
+ * or total height and max width (vertical layouts).
+ *
+ * Side effects:
+ * Sets width and height fields for all tabs.
+ *
+ * Notes:
+ * Hidden tabs are included in the perpendicular computation
+ * (max height/width) but not parallel (total width/height).
+ */
+static void TabrowSize(
+ Notebook *nb, Ttk_Orient orient, int *widthPtr, int *heightPtr)
+{
+ Ttk_Layout tabLayout = nb->notebook.tabLayout;
+ int tabrowWidth = 0, tabrowHeight = 0;
+ int i;
+
+ for (i = 0; i < Ttk_NumberSlaves(nb->notebook.mgr); ++i) {
+ Tab *tab = Ttk_SlaveData(nb->notebook.mgr, i);
+ Ttk_State tabState = TabState(nb,i);
+
+ Ttk_RebindSublayout(tabLayout, tab);
+ Ttk_LayoutSize(tabLayout,tabState,&tab->width,&tab->height);
+
+ if (orient == TTK_ORIENT_HORIZONTAL) {
+ tabrowHeight = MAX(tabrowHeight, tab->height);
+ if (tab->state != TAB_STATE_HIDDEN) { tabrowWidth += tab->width; }
+ } else {
+ tabrowWidth = MAX(tabrowWidth, tab->width);
+ if (tab->state != TAB_STATE_HIDDEN) { tabrowHeight += tab->height; }
+ }
+ }
+
+ *widthPtr = tabrowWidth;
+ *heightPtr = tabrowHeight;
+}
+
+/* NotebookSize -- GM and widget size hook.
+ *
+ * Total height is tab height + client area height + pane internal padding
+ * Total width is max(client width, tab width) + pane internal padding
+ * Client area size determined by max size of slaves,
+ * overridden by -width and/or -height if nonzero.
+ */
+
+static int NotebookSize(void *clientData, int *widthPtr, int *heightPtr)
+{
+ Notebook *nb = clientData;
+ NotebookStyle nbstyle;
+ Ttk_Padding padding;
+ Ttk_Element clientNode = Ttk_FindElement(nb->core.layout, "client");
+ int clientWidth = 0, clientHeight = 0,
+ reqWidth = 0, reqHeight = 0,
+ tabrowWidth = 0, tabrowHeight = 0;
+ int i;
+
+ NotebookStyleOptions(nb, &nbstyle);
+
+ /* Compute max requested size of all slaves:
+ */
+ for (i = 0; i < Ttk_NumberSlaves(nb->notebook.mgr); ++i) {
+ Tk_Window slaveWindow = Ttk_SlaveWindow(nb->notebook.mgr, i);
+ Tab *tab = Ttk_SlaveData(nb->notebook.mgr, i);
+ int slaveWidth
+ = Tk_ReqWidth(slaveWindow) + Ttk_PaddingWidth(tab->padding);
+ int slaveHeight
+ = Tk_ReqHeight(slaveWindow) + Ttk_PaddingHeight(tab->padding);
+
+ clientWidth = MAX(clientWidth, slaveWidth);
+ clientHeight = MAX(clientHeight, slaveHeight);
+ }
+
+ /* Client width/height overridable by widget options:
+ */
+ Tcl_GetIntFromObj(NULL, nb->notebook.widthObj,&reqWidth);
+ Tcl_GetIntFromObj(NULL, nb->notebook.heightObj,&reqHeight);
+ if (reqWidth > 0)
+ clientWidth = reqWidth;
+ if (reqHeight > 0)
+ clientHeight = reqHeight;
+
+ /* Tab row:
+ */
+ TabrowSize(nb, nbstyle.tabOrient, &tabrowWidth, &tabrowHeight);
+ tabrowHeight += Ttk_PaddingHeight(nbstyle.tabMargins);
+ tabrowWidth += Ttk_PaddingWidth(nbstyle.tabMargins);
+
+ /* Account for exterior and interior padding:
+ */
+ padding = nbstyle.padding;
+ if (clientNode) {
+ Ttk_Padding ipad =
+ Ttk_LayoutNodeInternalPadding(nb->core.layout, clientNode);
+ padding = Ttk_AddPadding(padding, ipad);
+ }
+
+ if (nbstyle.tabPosition & (TTK_PACK_TOP|TTK_PACK_BOTTOM)) {
+ *widthPtr = MAX(tabrowWidth, clientWidth) + Ttk_PaddingWidth(padding);
+ *heightPtr = tabrowHeight + clientHeight + Ttk_PaddingHeight(padding);
+ } else {
+ *widthPtr = tabrowWidth + clientWidth + Ttk_PaddingWidth(padding);
+ *heightPtr = MAX(tabrowHeight,clientHeight) + Ttk_PaddingHeight(padding);
+ }
+
+ return 1;
+}
+
+/*------------------------------------------------------------------------
+ * +++ Geometry management - layout.
+ */
+
+/* SqueezeTabs --
+ * Squeeze or stretch tabs to fit within the tab area parcel.
+ *
+ * All tabs are adjusted by an equal amount, but will not be made
+ * smaller than the minimum width. (If all the tabs still do
+ * not fit in the available space, the rightmost ones will
+ * be further squozen by PlaceTabs()).
+ *
+ * The algorithm does not always yield an optimal layout, but does
+ * have the important property that decreasing the available width
+ * by one pixel will cause at most one tab to shrink by one pixel;
+ * this means that tabs resize "smoothly" when the window shrinks
+ * and grows.
+ *
+ * @@@ <<NOTE-TABPOSITION>> bug: only works for horizontal orientations
+ * @@@ <<NOTE-SQUEEZE-HIDDEN>> does not account for hidden tabs.
+ */
+
+static void SqueezeTabs(
+ Notebook *nb, int needed, int available, int minTabWidth)
+{
+ int nTabs = Ttk_NumberSlaves(nb->notebook.mgr);
+
+ if (nTabs > 0) {
+ int difference = available - needed,
+ delta = difference / nTabs,
+ remainder = difference % nTabs,
+ slack = 0;
+ int i;
+
+ if (remainder < 0) { remainder += nTabs; --delta; }
+
+ for (i = 0; i < nTabs; ++i) {
+ Tab *tab = Ttk_SlaveData(nb->notebook.mgr,i);
+ int adj = delta + (i < remainder) + slack;
+
+ if (tab->width + adj >= minTabWidth) {
+ tab->width += adj;
+ slack = 0;
+ } else {
+ slack = adj - (minTabWidth - tab->width);
+ tab->width = minTabWidth;
+ }
+ }
+ }
+}
+
+/* PlaceTabs --
+ * Compute all tab parcels.
+ */
+static void PlaceTabs(
+ Notebook *nb, Ttk_Box tabrowBox, Ttk_PositionSpec tabPlacement)
+{
+ Ttk_Layout tabLayout = nb->notebook.tabLayout;
+ int nTabs = Ttk_NumberSlaves(nb->notebook.mgr);
+ int i;
+
+ for (i = 0; i < nTabs; ++i) {
+ Tab *tab = Ttk_SlaveData(nb->notebook.mgr, i);
+ Ttk_State tabState = TabState(nb, i);
+
+ if (tab->state != TAB_STATE_HIDDEN) {
+ Ttk_Padding expand = Ttk_UniformPadding(0);
+ Tcl_Obj *expandObj = Ttk_QueryOption(tabLayout,"-expand",tabState);
+
+ if (expandObj) {
+ Ttk_GetBorderFromObj(NULL, expandObj, &expand);
+ }
+
+ tab->parcel =
+ Ttk_ExpandBox(
+ Ttk_PositionBox(&tabrowBox,
+ tab->width, tab->height, tabPlacement),
+ expand);
+ }
+ }
+}
+
+/* NotebookDoLayout --
+ * Computes notebook layout and places tabs.
+ *
+ * Side effects:
+ * Sets clientArea, used to place slave panes.
+ */
+static void NotebookDoLayout(void *recordPtr)
+{
+ Notebook *nb = recordPtr;
+ Tk_Window nbwin = nb->core.tkwin;
+ Ttk_Box cavity = Ttk_WinBox(nbwin);
+ int tabrowWidth = 0, tabrowHeight = 0;
+ Ttk_Element clientNode = Ttk_FindElement(nb->core.layout, "client");
+ Ttk_Box tabrowBox;
+ NotebookStyle nbstyle;
+
+ NotebookStyleOptions(nb, &nbstyle);
+
+ /* Notebook internal padding:
+ */
+ cavity = Ttk_PadBox(cavity, nbstyle.padding);
+
+ /* Layout for notebook background (base layout):
+ */
+ Ttk_PlaceLayout(nb->core.layout, nb->core.state, Ttk_WinBox(nbwin));
+
+ /* Place tabs:
+ */
+ TabrowSize(nb, nbstyle.tabOrient, &tabrowWidth, &tabrowHeight);
+ tabrowBox = Ttk_PadBox(
+ Ttk_PositionBox(&cavity,
+ tabrowWidth + Ttk_PaddingWidth(nbstyle.tabMargins),
+ tabrowHeight + Ttk_PaddingHeight(nbstyle.tabMargins),
+ nbstyle.tabPosition),
+ nbstyle.tabMargins);
+
+ SqueezeTabs(nb, tabrowWidth, tabrowBox.width, nbstyle.minTabWidth);
+ PlaceTabs(nb, tabrowBox, nbstyle.tabPlacement);
+
+ /* Layout for client area frame:
+ */
+ if (clientNode) {
+ Ttk_PlaceElement(nb->core.layout, clientNode, cavity);
+ cavity = Ttk_LayoutNodeInternalParcel(nb->core.layout, clientNode);
+ }
+
+ if (cavity.height <= 0) cavity.height = 1;
+ if (cavity.width <= 0) cavity.width = 1;
+
+ nb->notebook.clientArea = cavity;
+}
+
+/*
+ * NotebookPlaceSlave --
+ * Set the position and size of a child widget
+ * based on the current client area and slave options:
+ */
+static void NotebookPlaceSlave(Notebook *nb, int slaveIndex)
+{
+ Tab *tab = Ttk_SlaveData(nb->notebook.mgr, slaveIndex);
+ Tk_Window slaveWindow = Ttk_SlaveWindow(nb->notebook.mgr, slaveIndex);
+ Ttk_Box slaveBox =
+ Ttk_StickBox(Ttk_PadBox(nb->notebook.clientArea, tab->padding),
+ Tk_ReqWidth(slaveWindow), Tk_ReqHeight(slaveWindow),tab->sticky);
+
+ Ttk_PlaceSlave(nb->notebook.mgr, slaveIndex,
+ slaveBox.x, slaveBox.y, slaveBox.width, slaveBox.height);
+}
+
+/* NotebookPlaceSlaves --
+ * Geometry manager hook.
+ */
+static void NotebookPlaceSlaves(void *recordPtr)
+{
+ Notebook *nb = recordPtr;
+ int currentIndex = nb->notebook.currentIndex;
+ if (currentIndex >= 0) {
+ NotebookDoLayout(nb);
+ NotebookPlaceSlave(nb, currentIndex);
+ }
+}
+
+/*
+ * SelectTab(nb, index) --
+ * Change the currently-selected tab.
+ */
+static void SelectTab(Notebook *nb, int index)
+{
+ Tab *tab = Ttk_SlaveData(nb->notebook.mgr,index);
+ int currentIndex = nb->notebook.currentIndex;
+
+ if (index == currentIndex) {
+ return;
+ }
+
+ if (TabState(nb, index) & TTK_STATE_DISABLED) {
+ return;
+ }
+
+ /* Unhide the tab if it is currently hidden and being selected.
+ */
+ if (tab->state == TAB_STATE_HIDDEN) {
+ tab->state = TAB_STATE_NORMAL;
+ }
+
+ if (currentIndex >= 0) {
+ Ttk_UnmapSlave(nb->notebook.mgr, currentIndex);
+ }
+
+ NotebookPlaceSlave(nb, index);
+
+ nb->notebook.currentIndex = index;
+ TtkRedisplayWidget(&nb->core);
+
+ TtkSendVirtualEvent(nb->core.tkwin, "NotebookTabChanged");
+}
+
+/* NextTab --
+ * Returns the index of the next tab after the specified tab
+ * in the normal state (e.g., not hidden or disabled),
+ * or -1 if all tabs are disabled or hidden.
+ */
+static int NextTab(Notebook *nb, int index)
+{
+ int nTabs = Ttk_NumberSlaves(nb->notebook.mgr);
+ int nextIndex;
+
+ /* Scan forward for following usable tab:
+ */
+ for (nextIndex = index + 1; nextIndex < nTabs; ++nextIndex) {
+ Tab *tab = Ttk_SlaveData(nb->notebook.mgr, nextIndex);
+ if (tab->state == TAB_STATE_NORMAL) {
+ return nextIndex;
+ }
+ }
+
+ /* Not found -- scan backwards.
+ */
+ for (nextIndex = index - 1; nextIndex >= 0; --nextIndex) {
+ Tab *tab = Ttk_SlaveData(nb->notebook.mgr, nextIndex);
+ if (tab->state == TAB_STATE_NORMAL) {
+ return nextIndex;
+ }
+ }
+
+ /* Still nothing. Give up.
+ */
+ return -1;
+}
+
+/* SelectNearestTab --
+ * Handles the case where the current tab is forgotten, hidden,
+ * or destroyed.
+ *
+ * Unmap the current tab and schedule the next available one
+ * to be mapped at the next GM update.
+ */
+static void SelectNearestTab(Notebook *nb)
+{
+ int currentIndex = nb->notebook.currentIndex;
+ int nextIndex = NextTab(nb, currentIndex);
+
+ if (currentIndex >= 0) {
+ Ttk_UnmapSlave(nb->notebook.mgr, currentIndex);
+ }
+ if (currentIndex != nextIndex) {
+ TtkSendVirtualEvent(nb->core.tkwin, "NotebookTabChanged");
+ }
+
+ nb->notebook.currentIndex = nextIndex;
+ Ttk_ManagerLayoutChanged(nb->notebook.mgr);
+ TtkRedisplayWidget(&nb->core);
+}
+
+/* TabRemoved -- GM SlaveRemoved hook.
+ * Select the next tab if the current one is being removed.
+ * Adjust currentIndex to account for removed slave.
+ */
+static void TabRemoved(void *managerData, int index)
+{
+ Notebook *nb = managerData;
+ Tab *tab = Ttk_SlaveData(nb->notebook.mgr, index);
+
+ if (index == nb->notebook.currentIndex) {
+ SelectNearestTab(nb);
+ }
+
+ if (index < nb->notebook.currentIndex) {
+ --nb->notebook.currentIndex;
+ }
+
+ DestroyTab(nb, tab);
+
+ TtkRedisplayWidget(&nb->core);
+}
+
+static int TabRequest(void *managerData, int index, int width, int height)
+{
+ return 1;
+}
+
+/* AddTab --
+ * Add new tab at specified index.
+ */
+static int AddTab(
+ Tcl_Interp *interp, Notebook *nb,
+ int destIndex, Tk_Window slaveWindow,
+ int objc, Tcl_Obj *const objv[])
+{
+ Tab *tab;
+ if (!Ttk_Maintainable(interp, slaveWindow, nb->core.tkwin)) {
+ return TCL_ERROR;
+ }
+#if 0 /* can't happen */
+ if (Ttk_SlaveIndex(nb->notebook.mgr, slaveWindow) >= 0) {
+ Tcl_AppendResult(interp,
+ Tk_PathName(slaveWindow), " already added",
+ NULL);
+ return TCL_ERROR;
+ }
+#endif
+
+ /* Create and insert tab.
+ */
+ tab = CreateTab(interp, nb, slaveWindow);
+ if (!tab) {
+ return TCL_ERROR;
+ }
+ if (ConfigureTab(interp, nb, tab, slaveWindow, objc, objv) != TCL_OK) {
+ DestroyTab(nb, tab);
+ return TCL_ERROR;
+ }
+
+ Ttk_InsertSlave(nb->notebook.mgr, destIndex, slaveWindow, tab);
+
+ /* Adjust indices and/or autoselect first tab:
+ */
+ if (nb->notebook.currentIndex < 0) {
+ SelectTab(nb, destIndex);
+ } else if (nb->notebook.currentIndex >= destIndex) {
+ ++nb->notebook.currentIndex;
+ }
+
+ return TCL_OK;
+}
+
+static Ttk_ManagerSpec NotebookManagerSpec = {
+ { "notebook", Ttk_GeometryRequestProc, Ttk_LostSlaveProc },
+ NotebookSize,
+ NotebookPlaceSlaves,
+ TabRequest,
+ TabRemoved
+};
+
+/*------------------------------------------------------------------------
+ * +++ Event handlers.
+ */
+
+/* NotebookEventHandler --
+ * Tracks the active tab.
+ */
+static const int NotebookEventMask
+ = StructureNotifyMask
+ | PointerMotionMask
+ | LeaveWindowMask
+ ;
+static void NotebookEventHandler(ClientData clientData, XEvent *eventPtr)
+{
+ Notebook *nb = clientData;
+
+ if (eventPtr->type == DestroyNotify) { /* Remove self */
+ Tk_DeleteEventHandler(nb->core.tkwin,
+ NotebookEventMask, NotebookEventHandler, clientData);
+ } else if (eventPtr->type == MotionNotify) {
+ int index = IdentifyTab(nb, eventPtr->xmotion.x, eventPtr->xmotion.y);
+ ActivateTab(nb, index);
+ } else if (eventPtr->type == LeaveNotify) {
+ ActivateTab(nb, -1);
+ }
+}
+
+/*------------------------------------------------------------------------
+ * +++ Utilities.
+ */
+
+/* FindTabIndex --
+ * Find the index of the specified tab.
+ * Tab identifiers are one of:
+ *
+ * + positional specifications @x,y,
+ * + "current",
+ * + numeric indices [0..nTabs],
+ * + slave window names
+ *
+ * Stores index of specified tab in *index_rtn, -1 if not found.
+ *
+ * Returns TCL_ERROR and leaves an error message in interp->result
+ * if the tab identifier was incorrect.
+ *
+ * See also: GetTabIndex.
+ */
+static int FindTabIndex(
+ Tcl_Interp *interp, Notebook *nb, Tcl_Obj *objPtr, int *index_rtn)
+{
+ const char *string = Tcl_GetString(objPtr);
+ int x, y;
+
+ *index_rtn = -1;
+
+ /* Check for @x,y ...
+ */
+ if (string[0] == '@' && sscanf(string, "@%d,%d",&x,&y) == 2) {
+ *index_rtn = IdentifyTab(nb, x, y);
+ return TCL_OK;
+ }
+
+ /* ... or "current" ...
+ */
+ if (!strcmp(string, "current")) {
+ *index_rtn = nb->notebook.currentIndex;
+ return TCL_OK;
+ }
+
+ /* ... or integer index or slave window name:
+ */
+ if (Ttk_GetSlaveIndexFromObj(
+ interp, nb->notebook.mgr, objPtr, index_rtn) == TCL_OK)
+ {
+ return TCL_OK;
+ }
+
+ /* Nothing matched; Ttk_GetSlaveIndexFromObj will have left error message.
+ */
+ return TCL_ERROR;
+}
+
+/* GetTabIndex --
+ * Get the index of an existing tab.
+ * Tab identifiers are as per FindTabIndex.
+ * Returns TCL_ERROR if the tab does not exist.
+ */
+static int GetTabIndex(
+ Tcl_Interp *interp, Notebook *nb, Tcl_Obj *objPtr, int *index_rtn)
+{
+ int status = FindTabIndex(interp, nb, objPtr, index_rtn);
+
+ if (status == TCL_OK && *index_rtn < 0) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp,
+ "tab '", Tcl_GetString(objPtr), "' not found",
+ NULL);
+ status = TCL_ERROR;
+ }
+ return status;
+}
+
+/*------------------------------------------------------------------------
+ * +++ Widget command routines.
+ */
+
+/* $nb add window ?options ... ?
+ */
+static int NotebookAddCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ Notebook *nb = recordPtr;
+ int index = Ttk_NumberSlaves(nb->notebook.mgr);
+ Tk_Window slaveWindow;
+ int slaveIndex;
+ Tab *tab;
+
+ if (objc <= 2 || objc % 2 != 1) {
+ Tcl_WrongNumArgs(interp, 2, objv, "window ?-option value ...?");
+ return TCL_ERROR;
+ }
+
+ slaveWindow = Tk_NameToWindow(interp,Tcl_GetString(objv[2]),nb->core.tkwin);
+ if (!slaveWindow) {
+ return TCL_ERROR;
+ }
+ slaveIndex = Ttk_SlaveIndex(nb->notebook.mgr, slaveWindow);
+
+ if (slaveIndex < 0) { /* New tab */
+ return AddTab(interp, nb, index, slaveWindow, objc-3,objv+3);
+ }
+
+ tab = Ttk_SlaveData(nb->notebook.mgr, slaveIndex);
+ if (tab->state == TAB_STATE_HIDDEN) {
+ tab->state = TAB_STATE_NORMAL;
+ }
+ if (ConfigureTab(interp, nb, tab, slaveWindow, objc-4,objv+4) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ TtkRedisplayWidget(&nb->core);
+
+ return TCL_OK;
+}
+
+/* $nb insert $index $tab ?-option value ...?
+ * Insert new tab, or move existing one.
+ */
+static int NotebookInsertCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ Notebook *nb = recordPtr;
+ int current = nb->notebook.currentIndex;
+ int nSlaves = Ttk_NumberSlaves(nb->notebook.mgr);
+ int srcIndex, destIndex;
+
+ if (objc < 4) {
+ Tcl_WrongNumArgs(interp, 2,objv, "index slave ?-option value ...?");
+ return TCL_ERROR;
+ }
+
+ if (!strcmp(Tcl_GetString(objv[2]), "end")) {
+ destIndex = Ttk_NumberSlaves(nb->notebook.mgr);
+ } else if (TCL_OK != Ttk_GetSlaveIndexFromObj(
+ interp, nb->notebook.mgr, objv[2], &destIndex)) {
+ return TCL_ERROR;
+ }
+
+ if (Tcl_GetString(objv[3])[0] == '.') {
+ /* Window name -- could be new or existing slave.
+ */
+ Tk_Window slaveWindow =
+ Tk_NameToWindow(interp,Tcl_GetString(objv[3]),nb->core.tkwin);
+
+ if (!slaveWindow) {
+ return TCL_ERROR;
+ }
+
+ srcIndex = Ttk_SlaveIndex(nb->notebook.mgr, slaveWindow);
+ if (srcIndex < 0) { /* New slave */
+ return AddTab(interp, nb, destIndex, slaveWindow, objc-4,objv+4);
+ }
+ } else if (Ttk_GetSlaveIndexFromObj(
+ interp, nb->notebook.mgr, objv[3], &srcIndex) != TCL_OK)
+ {
+ return TCL_ERROR;
+ }
+
+ /* Move existing slave:
+ */
+ if (ConfigureTab(interp, nb,
+ Ttk_SlaveData(nb->notebook.mgr,srcIndex),
+ Ttk_SlaveWindow(nb->notebook.mgr,srcIndex),
+ objc-4,objv+4) != TCL_OK)
+ {
+ return TCL_ERROR;
+ }
+
+ if (destIndex >= nSlaves) {
+ destIndex = nSlaves - 1;
+ }
+ Ttk_ReorderSlave(nb->notebook.mgr, srcIndex, destIndex);
+
+ /* Adjust internal indexes:
+ */
+ nb->notebook.activeIndex = -1;
+ if (current == srcIndex) {
+ nb->notebook.currentIndex = destIndex;
+ } else if (destIndex <= current && current < srcIndex) {
+ ++nb->notebook.currentIndex;
+ } else if (srcIndex < current && current <= destIndex) {
+ --nb->notebook.currentIndex;
+ }
+
+ TtkRedisplayWidget(&nb->core);
+
+ return TCL_OK;
+}
+
+/* $nb forget $tab --
+ * Removes the specified tab.
+ */
+static int NotebookForgetCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ Notebook *nb = recordPtr;
+ int index;
+
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "tab");
+ return TCL_ERROR;
+ }
+
+ if (GetTabIndex(interp, nb, objv[2], &index) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ Ttk_ForgetSlave(nb->notebook.mgr, index);
+ TtkRedisplayWidget(&nb->core);
+
+ return TCL_OK;
+}
+
+/* $nb hide $tab --
+ * Hides the specified tab.
+ */
+static int NotebookHideCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ Notebook *nb = recordPtr;
+ int index;
+ Tab *tab;
+
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "tab");
+ return TCL_ERROR;
+ }
+
+ if (GetTabIndex(interp, nb, objv[2], &index) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ tab = Ttk_SlaveData(nb->notebook.mgr, index);
+ tab->state = TAB_STATE_HIDDEN;
+ if (index == nb->notebook.currentIndex) {
+ SelectNearestTab(nb);
+ }
+
+ TtkRedisplayWidget(&nb->core);
+
+ return TCL_OK;
+}
+
+/* $nb identify $x $y --
+ * Returns name of tab element at $x,$y; empty string if none.
+ */
+static int NotebookIdentifyCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ static const char *whatTable[] = { "element", "tab", NULL };
+ enum { IDENTIFY_ELEMENT, IDENTIFY_TAB };
+ int what = IDENTIFY_ELEMENT;
+ Notebook *nb = recordPtr;
+ Ttk_Element element = NULL;
+ int x, y, tabIndex;
+
+ if (objc < 4 || objc > 5) {
+ Tcl_WrongNumArgs(interp, 2,objv, "?what? x y");
+ return TCL_ERROR;
+ }
+
+ if ( Tcl_GetIntFromObj(interp, objv[objc-2], &x) != TCL_OK
+ || Tcl_GetIntFromObj(interp, objv[objc-1], &y) != TCL_OK
+ || (objc == 5 &&
+ Tcl_GetIndexFromObj(interp, objv[2], whatTable, "option", 0, &what)
+ != TCL_OK)
+ ) {
+ return TCL_ERROR;
+ }
+
+ tabIndex = IdentifyTab(nb, x, y);
+ if (tabIndex >= 0) {
+ Tab *tab = Ttk_SlaveData(nb->notebook.mgr, tabIndex);
+ Ttk_State state = TabState(nb, tabIndex);
+ Ttk_Layout tabLayout = nb->notebook.tabLayout;
+
+ Ttk_RebindSublayout(tabLayout, tab);
+ Ttk_PlaceLayout(tabLayout, state, tab->parcel);
+
+ element = Ttk_IdentifyElement(tabLayout, x, y);
+ }
+
+ switch (what) {
+ case IDENTIFY_ELEMENT:
+ if (element) {
+ const char *elementName = Ttk_ElementName(element);
+ Tcl_SetObjResult(interp,Tcl_NewStringObj(elementName,-1));
+ }
+ break;
+ case IDENTIFY_TAB:
+ if (tabIndex >= 0) {
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(tabIndex));
+ }
+ break;
+ }
+ return TCL_OK;
+}
+
+/* $nb index $item --
+ * Returns the integer index of the tab specified by $item,
+ * the empty string if $item does not identify a tab.
+ * See above for valid item formats.
+ */
+static int NotebookIndexCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ Notebook *nb = recordPtr;
+ int index, status;
+
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "tab");
+ return TCL_ERROR;
+ }
+
+ /*
+ * Special-case for "end":
+ */
+ if (!strcmp("end", Tcl_GetString(objv[2]))) {
+ int nSlaves = Ttk_NumberSlaves(nb->notebook.mgr);
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(nSlaves));
+ return TCL_OK;
+ }
+
+ status = FindTabIndex(interp, nb, objv[2], &index);
+ if (status == TCL_OK && index >= 0) {
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(index));
+ }
+
+ return status;
+}
+
+/* $nb select ?$item? --
+ * Select the specified tab, or return the widget path of
+ * the currently-selected pane.
+ */
+static int NotebookSelectCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ Notebook *nb = recordPtr;
+
+ if (objc == 2) {
+ if (nb->notebook.currentIndex >= 0) {
+ Tk_Window pane = Ttk_SlaveWindow(
+ nb->notebook.mgr, nb->notebook.currentIndex);
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(Tk_PathName(pane), -1));
+ }
+ return TCL_OK;
+ } else if (objc == 3) {
+ int index, status = GetTabIndex(interp, nb, objv[2], &index);
+ if (status == TCL_OK) {
+ SelectTab(nb, index);
+ }
+ return status;
+ } /*else*/
+ Tcl_WrongNumArgs(interp, 2, objv, "?tab?");
+ return TCL_ERROR;
+}
+
+/* $nb tabs --
+ * Return list of tabs.
+ */
+static int NotebookTabsCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ Notebook *nb = recordPtr;
+ Ttk_Manager *mgr = nb->notebook.mgr;
+ Tcl_Obj *result;
+ int i;
+
+ if (objc != 2) {
+ Tcl_WrongNumArgs(interp, 2, objv, "");
+ return TCL_ERROR;
+ }
+
+ result = Tcl_NewListObj(0, NULL);
+ for (i = 0; i < Ttk_NumberSlaves(mgr); ++i) {
+ const char *pathName = Tk_PathName(Ttk_SlaveWindow(mgr,i));
+ Tcl_ListObjAppendElement(interp, result, Tcl_NewStringObj(pathName,-1));
+ }
+ Tcl_SetObjResult(interp, result);
+
+ return TCL_OK;
+}
+
+/* $nb tab $tab ?-option ?value -option value...??
+ */
+static int NotebookTabCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ Notebook *nb = recordPtr;
+ Ttk_Manager *mgr = nb->notebook.mgr;
+ int index;
+ Tk_Window slaveWindow;
+ Tab *tab;
+
+ if (objc < 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "tab ?-option ?value??...");
+ return TCL_ERROR;
+ }
+
+ if (GetTabIndex(interp, nb, objv[2], &index) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ tab = Ttk_SlaveData(mgr, index);
+ slaveWindow = Ttk_SlaveWindow(mgr, index);
+
+ if (objc == 3) {
+ return TtkEnumerateOptions(interp, tab,
+ PaneOptionSpecs, nb->notebook.paneOptionTable, slaveWindow);
+ } else if (objc == 4) {
+ return TtkGetOptionValue(interp, tab, objv[3],
+ nb->notebook.paneOptionTable, slaveWindow);
+ } /* else */
+
+ if (ConfigureTab(interp, nb, tab, slaveWindow, objc-3,objv+3) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ /* If the current tab has become disabled or hidden,
+ * select the next nondisabled, unhidden one:
+ */
+ if (index == nb->notebook.currentIndex && tab->state != TAB_STATE_NORMAL) {
+ SelectNearestTab(nb);
+ }
+
+ return TCL_OK;
+}
+
+/* Subcommand table:
+ */
+static const Ttk_Ensemble NotebookCommands[] = {
+ { "add", NotebookAddCommand,0 },
+ { "configure", TtkWidgetConfigureCommand,0 },
+ { "cget", TtkWidgetCgetCommand,0 },
+ { "forget", NotebookForgetCommand,0 },
+ { "hide", NotebookHideCommand,0 },
+ { "identify", NotebookIdentifyCommand,0 },
+ { "index", NotebookIndexCommand,0 },
+ { "insert", NotebookInsertCommand,0 },
+ { "instate", TtkWidgetInstateCommand,0 },
+ { "select", NotebookSelectCommand,0 },
+ { "state", TtkWidgetStateCommand,0 },
+ { "tab", NotebookTabCommand,0 },
+ { "tabs", NotebookTabsCommand,0 },
+ { 0,0,0 }
+};
+
+/*------------------------------------------------------------------------
+ * +++ Widget class hooks.
+ */
+
+static void NotebookInitialize(Tcl_Interp *interp, void *recordPtr)
+{
+ Notebook *nb = recordPtr;
+
+ nb->notebook.mgr = Ttk_CreateManager(
+ &NotebookManagerSpec, recordPtr, nb->core.tkwin);
+
+ nb->notebook.tabOptionTable = Tk_CreateOptionTable(interp,TabOptionSpecs);
+ nb->notebook.paneOptionTable = Tk_CreateOptionTable(interp,PaneOptionSpecs);
+
+ nb->notebook.currentIndex = -1;
+ nb->notebook.activeIndex = -1;
+ nb->notebook.tabLayout = 0;
+
+ nb->notebook.clientArea = Ttk_MakeBox(0,0,1,1);
+
+ Tk_CreateEventHandler(
+ nb->core.tkwin, NotebookEventMask, NotebookEventHandler, recordPtr);
+}
+
+static void NotebookCleanup(void *recordPtr)
+{
+ Notebook *nb = recordPtr;
+
+ Ttk_DeleteManager(nb->notebook.mgr);
+ if (nb->notebook.tabLayout)
+ Ttk_FreeLayout(nb->notebook.tabLayout);
+}
+
+static int NotebookConfigure(Tcl_Interp *interp, void *clientData, int mask)
+{
+ Notebook *nb = clientData;
+
+ /*
+ * Error-checks:
+ */
+ if (nb->notebook.paddingObj) {
+ /* Check for valid -padding: */
+ Ttk_Padding unused;
+ if (Ttk_GetPaddingFromObj(
+ interp, nb->core.tkwin, nb->notebook.paddingObj, &unused)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+
+ return TtkCoreConfigure(interp, clientData, mask);
+}
+
+/* NotebookGetLayout --
+ * GetLayout widget hook.
+ */
+static Ttk_Layout NotebookGetLayout(
+ Tcl_Interp *interp, Ttk_Theme theme, void *recordPtr)
+{
+ Notebook *nb = recordPtr;
+ Ttk_Layout notebookLayout = TtkWidgetGetLayout(interp, theme, recordPtr);
+ Ttk_Layout tabLayout;
+
+ if (!notebookLayout) {
+ return NULL;
+ }
+
+ tabLayout = Ttk_CreateSublayout(
+ interp, theme, notebookLayout, ".Tab", nb->notebook.tabOptionTable);
+
+ if (tabLayout) {
+ if (nb->notebook.tabLayout) {
+ Ttk_FreeLayout(nb->notebook.tabLayout);
+ }
+ nb->notebook.tabLayout = tabLayout;
+ }
+
+ return notebookLayout;
+}
+
+/*------------------------------------------------------------------------
+ * +++ Display routines.
+ */
+
+static void DisplayTab(Notebook *nb, int index, Drawable d)
+{
+ Ttk_Layout tabLayout = nb->notebook.tabLayout;
+ Tab *tab = Ttk_SlaveData(nb->notebook.mgr, index);
+ Ttk_State state = TabState(nb, index);
+
+ if (tab->state != TAB_STATE_HIDDEN) {
+ Ttk_RebindSublayout(tabLayout, tab);
+ Ttk_PlaceLayout(tabLayout, state, tab->parcel);
+ Ttk_DrawLayout(tabLayout, state, d);
+ }
+}
+
+static void NotebookDisplay(void *clientData, Drawable d)
+{
+ Notebook *nb = clientData;
+ int nSlaves = Ttk_NumberSlaves(nb->notebook.mgr);
+ int index;
+
+ /* Draw notebook background (base layout):
+ */
+ Ttk_DrawLayout(nb->core.layout, nb->core.state, d);
+
+ /* Draw tabs from left to right, but draw the current tab last
+ * so it will overwrite its neighbors.
+ */
+ for (index = 0; index < nSlaves; ++index) {
+ if (index != nb->notebook.currentIndex) {
+ DisplayTab(nb, index, d);
+ }
+ }
+ if (nb->notebook.currentIndex >= 0) {
+ DisplayTab(nb, nb->notebook.currentIndex, d);
+ }
+}
+
+/*------------------------------------------------------------------------
+ * +++ Widget specification and layout definitions.
+ */
+
+static WidgetSpec NotebookWidgetSpec =
+{
+ "TNotebook", /* className */
+ sizeof(Notebook), /* recordSize */
+ NotebookOptionSpecs, /* optionSpecs */
+ NotebookCommands, /* subcommands */
+ NotebookInitialize, /* initializeProc */
+ NotebookCleanup, /* cleanupProc */
+ NotebookConfigure, /* configureProc */
+ TtkNullPostConfigure, /* postConfigureProc */
+ NotebookGetLayout, /* getLayoutProc */
+ NotebookSize, /* geometryProc */
+ NotebookDoLayout, /* layoutProc */
+ NotebookDisplay /* displayProc */
+};
+
+TTK_BEGIN_LAYOUT(NotebookLayout)
+ TTK_NODE("Notebook.client", TTK_FILL_BOTH)
+TTK_END_LAYOUT
+
+TTK_BEGIN_LAYOUT(TabLayout)
+ TTK_GROUP("Notebook.tab", TTK_FILL_BOTH,
+ TTK_GROUP("Notebook.padding", TTK_PACK_TOP|TTK_FILL_BOTH,
+ TTK_GROUP("Notebook.focus", TTK_PACK_TOP|TTK_FILL_BOTH,
+ TTK_NODE("Notebook.label", TTK_PACK_TOP))))
+TTK_END_LAYOUT
+
+/*------------------------------------------------------------------------
+ * +++ Initialization.
+ */
+
+MODULE_SCOPE
+void TtkNotebook_Init(Tcl_Interp *interp)
+{
+ Ttk_Theme themePtr = Ttk_GetDefaultTheme(interp);
+
+ Ttk_RegisterLayout(themePtr, "Tab", TabLayout);
+ Ttk_RegisterLayout(themePtr, "TNotebook", NotebookLayout);
+
+ RegisterWidget(interp, "ttk::notebook", &NotebookWidgetSpec);
+}
+
+/*EOF*/
diff --git a/generic/ttk/ttkPanedwindow.c b/generic/ttk/ttkPanedwindow.c
new file mode 100644
index 0000000..b301372
--- /dev/null
+++ b/generic/ttk/ttkPanedwindow.c
@@ -0,0 +1,975 @@
+/*
+ * Copyright (c) 2005, Joe English. Freely redistributable.
+ *
+ * ttk::panedwindow widget implementation.
+ *
+ * TODO: track active/pressed sash.
+ */
+
+#include <string.h>
+#include <tk.h>
+#include "ttkManager.h"
+#include "ttkTheme.h"
+#include "ttkWidget.h"
+
+/*------------------------------------------------------------------------
+ * +++ Layout algorithm.
+ *
+ * (pos=x/y, size=width/height, depending on -orient=horizontal/vertical)
+ *
+ * Each pane carries two pieces of state: the request size and the
+ * position of the following sash. (The final pane has no sash,
+ * its sash position is used as a sentinel value).
+ *
+ * Pane geometry is determined by the sash positions.
+ * When resizing, sash positions are computed from the request sizes,
+ * the available space, and pane weights (see PlaceSashes()).
+ * This ensures continuous resize behavior (that is: changing
+ * the size by X pixels then changing the size by Y pixels
+ * gives the same result as changing the size by X+Y pixels
+ * in one step).
+ *
+ * The request size is initially set to the slave window's requested size.
+ * When the user drags a sash, each pane's request size is set to its
+ * actual size. This ensures that panes "stay put" on the next resize.
+ *
+ * If reqSize == 0, use 0 for the weight as well. This ensures that
+ * "collapsed" panes stay collapsed during a resize, regardless of
+ * their nominal -weight.
+ *
+ * +++ Invariants.
+ *
+ * #sash = #pane - 1
+ * pos(pane[0]) = 0
+ * pos(sash[i]) = pos(pane[i]) + size(pane[i]), 0 <= i <= #sash
+ * pos(pane[i+1]) = pos(sash[i]) + size(sash[i]), 0 <= i < #sash
+ * pos(sash[#sash]) = size(pw) // sentinel value, constraint
+ *
+ * size(pw) = sum(size(pane(0..#pane))) + sum(size(sash(0..#sash)))
+ * size(pane[i]) >= 0, for 0 <= i < #pane
+ * size(sash[i]) >= 0, for 0 <= i < #sash
+ * ==> pos(pane[i]) <= pos(sash[i]) <= pos(pane[i+1]), for 0 <= i < #sash
+ *
+ * Assumption: all sashes are the same size.
+ */
+
+/*------------------------------------------------------------------------
+ * +++ Widget record.
+ */
+
+typedef struct {
+ Tcl_Obj *orientObj;
+ int orient;
+ int width;
+ int height;
+ Ttk_Manager *mgr;
+ Tk_OptionTable paneOptionTable;
+ Ttk_Layout sashLayout;
+ int sashThickness;
+} PanedPart;
+
+typedef struct {
+ WidgetCore core;
+ PanedPart paned;
+} Paned;
+
+/* @@@ NOTE: -orient is readonly 'cause dynamic oriention changes NYI
+ */
+static Tk_OptionSpec PanedOptionSpecs[] = {
+ {TK_OPTION_STRING_TABLE, "-orient", "orient", "Orient", "vertical",
+ Tk_Offset(Paned,paned.orientObj), Tk_Offset(Paned,paned.orient),
+ 0,(ClientData)ttkOrientStrings,READONLY_OPTION|STYLE_CHANGED },
+ {TK_OPTION_INT, "-width", "width", "Width", "0",
+ -1,Tk_Offset(Paned,paned.width),
+ 0,0,GEOMETRY_CHANGED },
+ {TK_OPTION_INT, "-height", "height", "Height", "0",
+ -1,Tk_Offset(Paned,paned.height),
+ 0,0,GEOMETRY_CHANGED },
+
+ WIDGET_TAKEFOCUS_FALSE,
+ WIDGET_INHERIT_OPTIONS(ttkCoreOptionSpecs)
+};
+
+/*------------------------------------------------------------------------
+ * +++ Slave pane record.
+ */
+typedef struct {
+ int reqSize; /* Pane request size */
+ int sashPos; /* Folowing sash position */
+ int weight; /* Pane -weight, for resizing */
+} Pane;
+
+static Tk_OptionSpec PaneOptionSpecs[] = {
+ {TK_OPTION_INT, "-weight", "weight", "Weight", "0",
+ -1,Tk_Offset(Pane,weight), 0,0,GEOMETRY_CHANGED },
+ {TK_OPTION_END, 0,0,0, NULL, -1,-1, 0,0,0}
+};
+
+/* CreatePane --
+ * Create a new pane record.
+ */
+static Pane *CreatePane(Tcl_Interp *interp, Paned *pw, Tk_Window slaveWindow)
+{
+ Tk_OptionTable optionTable = pw->paned.paneOptionTable;
+ void *record = ckalloc(sizeof(Pane));
+ Pane *pane = record;
+
+ memset(record, 0, sizeof(Pane));
+ if (Tk_InitOptions(interp, record, optionTable, slaveWindow) != TCL_OK) {
+ ckfree(record);
+ return NULL;
+ }
+
+ pane->reqSize
+ = pw->paned.orient == TTK_ORIENT_HORIZONTAL
+ ? Tk_ReqWidth(slaveWindow) : Tk_ReqHeight(slaveWindow);
+
+ return pane;
+}
+
+/* DestroyPane --
+ * Free pane record.
+ */
+static void DestroyPane(Paned *pw, Pane *pane)
+{
+ void *record = pane;
+ Tk_FreeConfigOptions(record, pw->paned.paneOptionTable, pw->core.tkwin);
+ ckfree(record);
+}
+
+/* ConfigurePane --
+ * Set pane options.
+ */
+static int ConfigurePane(
+ Tcl_Interp *interp, Paned *pw, Pane *pane, Tk_Window slaveWindow,
+ int objc, Tcl_Obj *const objv[])
+{
+ Ttk_Manager *mgr = pw->paned.mgr;
+ Tk_SavedOptions savedOptions;
+ int mask = 0;
+
+ if (Tk_SetOptions(interp, (void*)pane, pw->paned.paneOptionTable,
+ objc, objv, slaveWindow, &savedOptions, &mask) != TCL_OK)
+ {
+ return TCL_ERROR;
+ }
+
+ /* Sanity-check:
+ */
+ if (pane->weight < 0) {
+ Tcl_AppendResult(interp, "-weight must be nonnegative", NULL);
+ goto error;
+ }
+
+ /* Done.
+ */
+ Tk_FreeSavedOptions(&savedOptions);
+ Ttk_ManagerSizeChanged(mgr);
+ return TCL_OK;
+
+error:
+ Tk_RestoreSavedOptions(&savedOptions);
+ return TCL_ERROR;
+}
+
+
+/*------------------------------------------------------------------------
+ * +++ Sash adjustment.
+ */
+
+/* ShoveUp --
+ * Place sash i at specified position, recursively shoving
+ * previous sashes upwards as needed, until hitting the top
+ * of the window. If that happens, shove back down.
+ *
+ * Returns: final position of sash i.
+ */
+
+static int ShoveUp(Paned *pw, int i, int pos)
+{
+ Pane *pane = Ttk_SlaveData(pw->paned.mgr, i);
+ int sashThickness = pw->paned.sashThickness;
+
+ if (i == 0) {
+ if (pos < 0)
+ pos = 0;
+ } else {
+ Pane *prevPane = Ttk_SlaveData(pw->paned.mgr, i-1);
+ if (pos < prevPane->sashPos + sashThickness)
+ pos = ShoveUp(pw, i-1, pos - sashThickness) + sashThickness;
+ }
+ return pane->sashPos = pos;
+}
+
+/* ShoveDown --
+ * Same as ShoveUp, but going in the opposite direction
+ * and stopping at the sentinel sash.
+ */
+static int ShoveDown(Paned *pw, int i, int pos)
+{
+ Pane *pane = Ttk_SlaveData(pw->paned.mgr,i);
+ int sashThickness = pw->paned.sashThickness;
+
+ if (i == Ttk_NumberSlaves(pw->paned.mgr) - 1) {
+ pos = pane->sashPos; /* Sentinel value == master window size */
+ } else {
+ Pane *nextPane = Ttk_SlaveData(pw->paned.mgr,i+1);
+ if (pos + sashThickness > nextPane->sashPos)
+ pos = ShoveDown(pw, i+1, pos + sashThickness) - sashThickness;
+ }
+ return pane->sashPos = pos;
+}
+
+/* PanedSize --
+ * Compute the requested size of the paned widget
+ * from the individual pane request sizes.
+ *
+ * Used as the WidgetSpec sizeProc and the ManagerSpec sizeProc.
+ */
+static int PanedSize(void *recordPtr, int *widthPtr, int *heightPtr)
+{
+ Paned *pw = recordPtr;
+ int nPanes = Ttk_NumberSlaves(pw->paned.mgr);
+ int nSashes = nPanes - 1;
+ int sashThickness = pw->paned.sashThickness;
+ int width = 0, height = 0;
+ int index;
+
+ if (pw->paned.orient == TTK_ORIENT_HORIZONTAL) {
+ for (index = 0; index < nPanes; ++index) {
+ Pane *pane = Ttk_SlaveData(pw->paned.mgr, index);
+ Tk_Window slaveWindow = Ttk_SlaveWindow(pw->paned.mgr, index);
+
+ if (height < Tk_ReqHeight(slaveWindow))
+ height = Tk_ReqHeight(slaveWindow);
+ width += pane->reqSize;
+ }
+ width += nSashes * sashThickness;
+ } else {
+ for (index = 0; index < nPanes; ++index) {
+ Pane *pane = Ttk_SlaveData(pw->paned.mgr, index);
+ Tk_Window slaveWindow = Ttk_SlaveWindow(pw->paned.mgr, index);
+
+ if (width < Tk_ReqWidth(slaveWindow))
+ width = Tk_ReqWidth(slaveWindow);
+ height += pane->reqSize;
+ }
+ height += nSashes * sashThickness;
+ }
+
+ *widthPtr = pw->paned.width > 0 ? pw->paned.width : width;
+ *heightPtr = pw->paned.height > 0 ? pw->paned.height : height;
+ return 1;
+}
+
+/* AdjustPanes --
+ * Set pane request sizes from sash positions.
+ *
+ * NOTE:
+ * AdjustPanes followed by PlaceSashes (called during relayout)
+ * will leave the sashes in the same place, as long as available size
+ * remains contant.
+ */
+static void AdjustPanes(Paned *pw)
+{
+ int sashThickness = pw->paned.sashThickness;
+ int pos = 0;
+ int index;
+
+ for (index = 0; index < Ttk_NumberSlaves(pw->paned.mgr); ++index) {
+ Pane *pane = Ttk_SlaveData(pw->paned.mgr, index);
+ int size = pane->sashPos - pos;
+ pane->reqSize = size >= 0 ? size : 0;
+ pos = pane->sashPos + sashThickness;
+ }
+}
+
+/* PlaceSashes --
+ * Set sash positions from pane request sizes and available space.
+ * The sentinel sash position is set to the available space.
+ *
+ * Allocate pane->reqSize pixels to each pane, and distribute
+ * the difference = available size - requested size according
+ * to pane->weight.
+ *
+ * If there's still some left over, squeeze panes from the bottom up
+ * (This can happen if all weights are zero, or if one or more panes
+ * are too small to absorb the required shrinkage).
+ *
+ * Notes:
+ * This doesn't distribute the remainder pixels as evenly as it could
+ * when more than one pane has weight > 1.
+ */
+static void PlaceSashes(Paned *pw, int width, int height)
+{
+ Ttk_Manager *mgr = pw->paned.mgr;
+ int nPanes = Ttk_NumberSlaves(mgr);
+ int sashThickness = pw->paned.sashThickness;
+ int available = pw->paned.orient == TTK_ORIENT_HORIZONTAL ? width : height;
+ int reqSize = 0, totalWeight = 0;
+ int difference, delta, remainder, pos, i;
+
+ if (nPanes == 0)
+ return;
+
+ /* Compute total required size and total available weight:
+ */
+ for (i = 0; i < nPanes; ++i) {
+ Pane *pane = Ttk_SlaveData(mgr, i);
+ reqSize += pane->reqSize;
+ totalWeight += pane->weight * (pane->reqSize != 0);
+ }
+
+ /* Compute difference to be redistributed:
+ */
+ difference = available - reqSize - sashThickness*(nPanes-1);
+ if (totalWeight != 0) {
+ delta = difference / totalWeight;
+ remainder = difference % totalWeight;
+ if (remainder < 0) {
+ --delta;
+ remainder += totalWeight;
+ }
+ } else {
+ delta = remainder = 0;
+ }
+ /* ASSERT: 0 <= remainder < totalWeight */
+
+ /* Place sashes:
+ */
+ pos = 0;
+ for (i = 0; i < nPanes; ++i) {
+ Pane *pane = Ttk_SlaveData(mgr, i);
+ int weight = pane->weight * (pane->reqSize != 0);
+ int size = pane->reqSize + delta * weight;
+
+ if (weight > remainder)
+ weight = remainder;
+ remainder -= weight;
+ size += weight;
+
+ if (size < 0)
+ size = 0;
+
+ pane->sashPos = (pos += size);
+ pos += sashThickness;
+ }
+
+ /* Handle emergency shrink/emergency stretch:
+ * Set sentinel sash position to end of widget,
+ * shove preceding sashes up.
+ */
+ ShoveUp(pw, nPanes - 1, available);
+}
+
+/* PlacePanes --
+ * Places slave panes based on sash positions.
+ */
+static void PlacePanes(Paned *pw)
+{
+ int horizontal = pw->paned.orient == TTK_ORIENT_HORIZONTAL;
+ int width = Tk_Width(pw->core.tkwin), height = Tk_Height(pw->core.tkwin);
+ int sashThickness = pw->paned.sashThickness;
+ int pos = 0;
+ int index;
+
+ for (index = 0; index < Ttk_NumberSlaves(pw->paned.mgr); ++index) {
+ Pane *pane = Ttk_SlaveData(pw->paned.mgr, index);
+ int size = pane->sashPos - pos;
+
+ if (size > 0) {
+ if (horizontal) {
+ Ttk_PlaceSlave(pw->paned.mgr, index, pos, 0, size, height);
+ } else {
+ Ttk_PlaceSlave(pw->paned.mgr, index, 0, pos, width, size);
+ }
+ } else {
+ Ttk_UnmapSlave(pw->paned.mgr, index);
+ }
+
+ pos = pane->sashPos + sashThickness;
+ }
+}
+
+/*------------------------------------------------------------------------
+ * +++ Manager specification.
+ */
+
+static void PanedPlaceSlaves(void *managerData)
+{
+ Paned *pw = managerData;
+ PlaceSashes(pw, Tk_Width(pw->core.tkwin), Tk_Height(pw->core.tkwin));
+ PlacePanes(pw);
+}
+
+static void PaneRemoved(void *managerData, int index)
+{
+ Paned *pw = managerData;
+ Pane *pane = Ttk_SlaveData(pw->paned.mgr, index);
+ DestroyPane(pw, pane);
+}
+
+static int AddPane(
+ Tcl_Interp *interp, Paned *pw,
+ int destIndex, Tk_Window slaveWindow,
+ int objc, Tcl_Obj *const objv[])
+{
+ Pane *pane;
+ if (!Ttk_Maintainable(interp, slaveWindow, pw->core.tkwin)) {
+ return TCL_ERROR;
+ }
+ if (Ttk_SlaveIndex(pw->paned.mgr, slaveWindow) >= 0) {
+ Tcl_AppendResult(interp,
+ Tk_PathName(slaveWindow), " already added",
+ NULL);
+ return TCL_ERROR;
+ }
+
+ pane = CreatePane(interp, pw, slaveWindow);
+ if (!pane) {
+ return TCL_ERROR;
+ }
+ if (ConfigurePane(interp, pw, pane, slaveWindow, objc, objv) != TCL_OK) {
+ DestroyPane(pw, pane);
+ return TCL_ERROR;
+ }
+
+ Ttk_InsertSlave(pw->paned.mgr, destIndex, slaveWindow, pane);
+ return TCL_OK;
+}
+
+/* PaneRequest --
+ * Only update pane request size if slave is currently unmapped.
+ * Geometry requests from mapped slaves are not directly honored
+ * in order to avoid unexpected pane resizes (esp. while the
+ * user is dragging a sash [#1325286]).
+ */
+static int PaneRequest(void *managerData, int index, int width, int height)
+{
+ Paned *pw = managerData;
+ Pane *pane = Ttk_SlaveData(pw->paned.mgr, index);
+ Tk_Window slaveWindow = Ttk_SlaveWindow(pw->paned.mgr, index);
+ int horizontal = pw->paned.orient == TTK_ORIENT_HORIZONTAL;
+
+ if (!Tk_IsMapped(slaveWindow)) {
+ pane->reqSize = horizontal ? width : height;
+ }
+ return 1;
+}
+
+static Ttk_ManagerSpec PanedManagerSpec = {
+ { "panedwindow", Ttk_GeometryRequestProc, Ttk_LostSlaveProc },
+ PanedSize,
+ PanedPlaceSlaves,
+ PaneRequest,
+ PaneRemoved
+};
+
+/*------------------------------------------------------------------------
+ * +++ Event handler.
+ *
+ * <<NOTE-PW-LEAVE-NOTIFYINFERIOR>>
+ * Tk does not execute binding scripts for <Leave> events when
+ * the pointer crosses from a parent to a child. This widget
+ * needs to know when that happens, though, so it can reset
+ * the cursor.
+ *
+ * This event handler generates an <<EnteredChild>> virtual event
+ * on LeaveNotify/NotifyInferior.
+ */
+
+static const unsigned PanedEventMask = LeaveWindowMask;
+static void PanedEventProc(ClientData clientData, XEvent *eventPtr)
+{
+ WidgetCore *corePtr = clientData;
+ if ( eventPtr->type == LeaveNotify
+ && eventPtr->xcrossing.detail == NotifyInferior)
+ {
+ TtkSendVirtualEvent(corePtr->tkwin, "EnteredChild");
+ }
+}
+
+/*------------------------------------------------------------------------
+ * +++ Initialization and cleanup hooks.
+ */
+
+static void PanedInitialize(Tcl_Interp *interp, void *recordPtr)
+{
+ Paned *pw = recordPtr;
+
+ Tk_CreateEventHandler(pw->core.tkwin,
+ PanedEventMask, PanedEventProc, recordPtr);
+ pw->paned.mgr = Ttk_CreateManager(&PanedManagerSpec, pw, pw->core.tkwin);
+ pw->paned.paneOptionTable = Tk_CreateOptionTable(interp,PaneOptionSpecs);
+ pw->paned.sashLayout = 0;
+ pw->paned.sashThickness = 1;
+}
+
+static void PanedCleanup(void *recordPtr)
+{
+ Paned *pw = recordPtr;
+
+ if (pw->paned.sashLayout)
+ Ttk_FreeLayout(pw->paned.sashLayout);
+ Tk_DeleteEventHandler(pw->core.tkwin,
+ PanedEventMask, PanedEventProc, recordPtr);
+ Ttk_DeleteManager(pw->paned.mgr);
+}
+
+/* Post-configuration hook.
+ */
+static int PanedPostConfigure(Tcl_Interp *interp, void *clientData, int mask)
+{
+ Paned *pw = clientData;
+
+ if (mask & GEOMETRY_CHANGED) {
+ /* User has changed -width or -height.
+ * Recalculate sash positions based on requested size.
+ */
+ Tk_Window tkwin = pw->core.tkwin;
+ PlaceSashes(pw,
+ pw->paned.width > 0 ? pw->paned.width : Tk_Width(tkwin),
+ pw->paned.height > 0 ? pw->paned.height : Tk_Height(tkwin));
+ }
+
+ return TCL_OK;
+}
+
+/*------------------------------------------------------------------------
+ * +++ Layout management hooks.
+ */
+static Ttk_Layout PanedGetLayout(
+ Tcl_Interp *interp, Ttk_Theme themePtr, void *recordPtr)
+{
+ Paned *pw = recordPtr;
+ Ttk_Layout panedLayout = TtkWidgetGetLayout(interp, themePtr, recordPtr);
+
+ if (panedLayout) {
+ int horizontal = pw->paned.orient == TTK_ORIENT_HORIZONTAL;
+ const char *layoutName =
+ horizontal ? ".Vertical.Sash" : ".Horizontal.Sash";
+ Ttk_Layout sashLayout = Ttk_CreateSublayout(
+ interp, themePtr, panedLayout, layoutName, pw->core.optionTable);
+
+ if (sashLayout) {
+ int sashWidth, sashHeight;
+
+ Ttk_LayoutSize(sashLayout, 0, &sashWidth, &sashHeight);
+ pw->paned.sashThickness = horizontal ? sashWidth : sashHeight;
+
+ if (pw->paned.sashLayout)
+ Ttk_FreeLayout(pw->paned.sashLayout);
+ pw->paned.sashLayout = sashLayout;
+ } else {
+ Ttk_FreeLayout(panedLayout);
+ return 0;
+ }
+ }
+
+ return panedLayout;
+}
+
+/*------------------------------------------------------------------------
+ * +++ Drawing routines.
+ */
+
+/* SashLayout --
+ * Place the sash sublayout after the specified pane,
+ * in preparation for drawing.
+ */
+static Ttk_Layout SashLayout(Paned *pw, int index)
+{
+ Pane *pane = Ttk_SlaveData(pw->paned.mgr, index);
+ int thickness = pw->paned.sashThickness,
+ height = Tk_Height(pw->core.tkwin),
+ width = Tk_Width(pw->core.tkwin),
+ sashPos = pane->sashPos;
+
+ Ttk_PlaceLayout(
+ pw->paned.sashLayout, pw->core.state,
+ pw->paned.orient == TTK_ORIENT_HORIZONTAL
+ ? Ttk_MakeBox(sashPos, 0, thickness, height)
+ : Ttk_MakeBox(0, sashPos, width, thickness));
+
+ return pw->paned.sashLayout;
+}
+
+static void DrawSash(Paned *pw, int index, Drawable d)
+{
+ Ttk_DrawLayout(SashLayout(pw, index), pw->core.state, d);
+}
+
+static void PanedDisplay(void *recordPtr, Drawable d)
+{
+ Paned *pw = recordPtr;
+ int i, nSashes = Ttk_NumberSlaves(pw->paned.mgr) - 1;
+
+ TtkWidgetDisplay(recordPtr, d);
+ for (i = 0; i < nSashes; ++i) {
+ DrawSash(pw, i, d);
+ }
+}
+
+/*------------------------------------------------------------------------
+ * +++ Widget commands.
+ */
+
+/* $pw add window [ options ... ]
+ */
+static int PanedAddCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ Paned *pw = recordPtr;
+ Tk_Window slaveWindow;
+
+ if (objc < 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "window");
+ return TCL_ERROR;
+ }
+
+ slaveWindow = Tk_NameToWindow(
+ interp, Tcl_GetString(objv[2]), pw->core.tkwin);
+
+ if (!slaveWindow) {
+ return TCL_ERROR;
+ }
+
+ return AddPane(interp, pw, Ttk_NumberSlaves(pw->paned.mgr), slaveWindow,
+ objc - 3, objv + 3);
+}
+
+/* $pw insert $index $slave ?-option value ...?
+ * Insert new slave, or move existing one.
+ */
+static int PanedInsertCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ Paned *pw = recordPtr;
+ int nSlaves = Ttk_NumberSlaves(pw->paned.mgr);
+ int srcIndex, destIndex;
+ Tk_Window slaveWindow;
+
+ if (objc < 4) {
+ Tcl_WrongNumArgs(interp, 2,objv, "index slave ?-option value ...?");
+ return TCL_ERROR;
+ }
+
+ slaveWindow = Tk_NameToWindow(
+ interp, Tcl_GetString(objv[3]), pw->core.tkwin);
+ if (!slaveWindow) {
+ return TCL_ERROR;
+ }
+
+ if (!strcmp(Tcl_GetString(objv[2]), "end")) {
+ destIndex = Ttk_NumberSlaves(pw->paned.mgr);
+ } else if (TCL_OK != Ttk_GetSlaveIndexFromObj(
+ interp,pw->paned.mgr,objv[2],&destIndex))
+ {
+ return TCL_ERROR;
+ }
+
+ srcIndex = Ttk_SlaveIndex(pw->paned.mgr, slaveWindow);
+ if (srcIndex < 0) { /* New slave: */
+ return AddPane(interp, pw, destIndex, slaveWindow, objc-4, objv+4);
+ } /* else -- move existing slave: */
+
+ if (destIndex >= nSlaves)
+ destIndex = nSlaves - 1;
+ Ttk_ReorderSlave(pw->paned.mgr, srcIndex, destIndex);
+
+ return objc == 4 ? TCL_OK :
+ ConfigurePane(interp, pw,
+ Ttk_SlaveData(pw->paned.mgr, destIndex),
+ Ttk_SlaveWindow(pw->paned.mgr, destIndex),
+ objc-4,objv+4);
+}
+
+/* $pw forget $pane
+ */
+static int PanedForgetCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ Paned *pw = recordPtr;
+ int paneIndex;
+
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 2,objv, "pane");
+ return TCL_ERROR;
+ }
+
+ if (TCL_OK != Ttk_GetSlaveIndexFromObj(
+ interp, pw->paned.mgr, objv[2], &paneIndex))
+ {
+ return TCL_ERROR;
+ }
+ Ttk_ForgetSlave(pw->paned.mgr, paneIndex);
+
+ return TCL_OK;
+}
+
+/* $pw identify ?what? $x $y --
+ * Return index of sash at $x,$y
+ */
+static int PanedIdentifyCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ static const char *whatTable[] = { "element", "sash", NULL };
+ enum { IDENTIFY_ELEMENT, IDENTIFY_SASH };
+ int what = IDENTIFY_SASH;
+ Paned *pw = recordPtr;
+ int sashThickness = pw->paned.sashThickness;
+ int nSashes = Ttk_NumberSlaves(pw->paned.mgr) - 1;
+ int x, y, pos;
+ int index;
+
+ if (objc < 4 || objc > 5) {
+ Tcl_WrongNumArgs(interp, 2,objv, "?what? x y");
+ return TCL_ERROR;
+ }
+
+ if ( Tcl_GetIntFromObj(interp, objv[objc-2], &x) != TCL_OK
+ || Tcl_GetIntFromObj(interp, objv[objc-1], &y) != TCL_OK
+ || (objc == 5 &&
+ Tcl_GetIndexFromObj(interp, objv[2], whatTable, "option", 0, &what)
+ != TCL_OK)
+ ) {
+ return TCL_ERROR;
+ }
+
+ pos = pw->paned.orient == TTK_ORIENT_HORIZONTAL ? x : y;
+ for (index = 0; index < nSashes; ++index) {
+ Pane *pane = Ttk_SlaveData(pw->paned.mgr, index);
+ if (pane->sashPos <= pos && pos <= pane->sashPos + sashThickness) {
+ /* Found it. */
+ switch (what) {
+ case IDENTIFY_SASH:
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(index));
+ return TCL_OK;
+ case IDENTIFY_ELEMENT:
+ {
+ Ttk_Element element =
+ Ttk_IdentifyElement(SashLayout(pw, index), x, y);
+ if (element) {
+ Tcl_SetObjResult(interp,
+ Tcl_NewStringObj(Ttk_ElementName(element), -1));
+ }
+ return TCL_OK;
+ }
+ }
+ }
+ }
+
+ return TCL_OK; /* nothing found - return empty string */
+}
+
+/* $pw pane $pane ?-option ?value -option value ...??
+ * Query/modify pane options.
+ */
+static int PanedPaneCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ Paned *pw = recordPtr;
+ int paneIndex;
+ Tk_Window slaveWindow;
+ Pane *pane;
+
+ if (objc < 3) {
+ Tcl_WrongNumArgs(interp, 2,objv, "pane ?-option value ...?");
+ return TCL_ERROR;
+ }
+
+ if (TCL_OK != Ttk_GetSlaveIndexFromObj(
+ interp,pw->paned.mgr,objv[2],&paneIndex))
+ {
+ return TCL_ERROR;
+ }
+
+ pane = Ttk_SlaveData(pw->paned.mgr, paneIndex);
+ slaveWindow = Ttk_SlaveWindow(pw->paned.mgr, paneIndex);
+
+ switch (objc) {
+ case 3:
+ return TtkEnumerateOptions(interp, pane, PaneOptionSpecs,
+ pw->paned.paneOptionTable, slaveWindow);
+ case 4:
+ return TtkGetOptionValue(interp, pane, objv[3],
+ pw->paned.paneOptionTable, slaveWindow);
+ default:
+ return ConfigurePane(interp, pw, pane, slaveWindow, objc-3,objv+3);
+ }
+}
+
+/* $pw panes --
+ * Return list of managed panes.
+ */
+static int PanedPanesCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ Paned *pw = recordPtr;
+ Ttk_Manager *mgr = pw->paned.mgr;
+ Tcl_Obj *panes;
+ int i;
+
+ if (objc != 2) {
+ Tcl_WrongNumArgs(interp, 2, objv, "");
+ return TCL_ERROR;
+ }
+
+ panes = Tcl_NewListObj(0, NULL);
+ for (i = 0; i < Ttk_NumberSlaves(mgr); ++i) {
+ const char *pathName = Tk_PathName(Ttk_SlaveWindow(mgr,i));
+ Tcl_ListObjAppendElement(interp, panes, Tcl_NewStringObj(pathName,-1));
+ }
+ Tcl_SetObjResult(interp, panes);
+
+ return TCL_OK;
+}
+
+
+/* $pw sashpos $index ?$newpos?
+ * Query or modify sash position.
+ */
+static int PanedSashposCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ Paned *pw = recordPtr;
+ int sashIndex, position = -1;
+ Pane *pane;
+
+ if (objc < 3 || objc > 4) {
+ Tcl_WrongNumArgs(interp, 2,objv, "index ?newpos?");
+ return TCL_ERROR;
+ }
+ if (Tcl_GetIntFromObj(interp, objv[2], &sashIndex) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (sashIndex < 0 || sashIndex >= Ttk_NumberSlaves(pw->paned.mgr) - 1) {
+ Tcl_AppendResult(interp,
+ "sash index ", Tcl_GetString(objv[2]), " out of range",
+ NULL);
+ return TCL_ERROR;
+ }
+
+ pane = Ttk_SlaveData(pw->paned.mgr, sashIndex);
+
+ if (objc == 3) {
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(pane->sashPos));
+ return TCL_OK;
+ }
+ /* else -- set new sash position */
+
+ if (Tcl_GetIntFromObj(interp, objv[3], &position) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ if (position < pane->sashPos) {
+ ShoveUp(pw, sashIndex, position);
+ } else {
+ ShoveDown(pw, sashIndex, position);
+ }
+
+ AdjustPanes(pw);
+ Ttk_ManagerLayoutChanged(pw->paned.mgr);
+
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(pane->sashPos));
+ return TCL_OK;
+}
+
+static const Ttk_Ensemble PanedCommands[] = {
+ { "add", PanedAddCommand,0 },
+ { "configure", TtkWidgetConfigureCommand,0 },
+ { "cget", TtkWidgetCgetCommand,0 },
+ { "forget", PanedForgetCommand,0 },
+ { "identify", PanedIdentifyCommand,0 },
+ { "insert", PanedInsertCommand,0 },
+ { "instate", TtkWidgetInstateCommand,0 },
+ { "pane", PanedPaneCommand,0 },
+ { "panes", PanedPanesCommand,0 },
+ { "sashpos", PanedSashposCommand,0 },
+ { "state", TtkWidgetStateCommand,0 },
+ { 0,0,0 }
+};
+
+/*------------------------------------------------------------------------
+ * +++ Widget specification.
+ */
+
+static WidgetSpec PanedWidgetSpec =
+{
+ "TPanedwindow", /* className */
+ sizeof(Paned), /* recordSize */
+ PanedOptionSpecs, /* optionSpecs */
+ PanedCommands, /* subcommands */
+ PanedInitialize, /* initializeProc */
+ PanedCleanup, /* cleanupProc */
+ TtkCoreConfigure, /* configureProc */
+ PanedPostConfigure, /* postConfigureProc */
+ PanedGetLayout, /* getLayoutProc */
+ PanedSize, /* sizeProc */
+ TtkWidgetDoLayout, /* layoutProc */
+ PanedDisplay /* displayProc */
+};
+
+/*------------------------------------------------------------------------
+ * +++ Elements and layouts.
+ */
+
+static const int DEFAULT_SASH_THICKNESS = 5;
+
+typedef struct {
+ Tcl_Obj *thicknessObj;
+} SashElement;
+
+static Ttk_ElementOptionSpec SashElementOptions[] = {
+ { "-sashthickness", TK_OPTION_INT,
+ Tk_Offset(SashElement,thicknessObj), "5" },
+ { NULL, 0, 0, NULL }
+};
+
+static void SashElementSize(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+{
+ SashElement *sash = elementRecord;
+ int thickness = DEFAULT_SASH_THICKNESS;
+ Tcl_GetIntFromObj(NULL, sash->thicknessObj, &thickness);
+ *widthPtr = *heightPtr = thickness;
+}
+
+static Ttk_ElementSpec SashElementSpec = {
+ TK_STYLE_VERSION_2,
+ sizeof(SashElement),
+ SashElementOptions,
+ SashElementSize,
+ TtkNullElementDraw
+};
+
+TTK_BEGIN_LAYOUT(PanedLayout)
+ TTK_NODE("Panedwindow.background", 0)/* @@@ BUG: empty layouts don't work */
+TTK_END_LAYOUT
+
+TTK_BEGIN_LAYOUT(HorizontalSashLayout)
+ TTK_NODE("Sash.hsash", TTK_FILL_X)
+TTK_END_LAYOUT
+
+TTK_BEGIN_LAYOUT(VerticalSashLayout)
+ TTK_NODE("Sash.vsash", TTK_FILL_Y)
+TTK_END_LAYOUT
+
+/*------------------------------------------------------------------------
+ * +++ Registration routine.
+ */
+MODULE_SCOPE
+void TtkPanedwindow_Init(Tcl_Interp *interp)
+{
+ Ttk_Theme themePtr = Ttk_GetDefaultTheme(interp);
+ RegisterWidget(interp, "ttk::panedwindow", &PanedWidgetSpec);
+
+ Ttk_RegisterElement(interp, themePtr, "hsash", &SashElementSpec, 0);
+ Ttk_RegisterElement(interp, themePtr, "vsash", &SashElementSpec, 0);
+
+ Ttk_RegisterLayout(themePtr, "TPanedwindow", PanedLayout);
+ Ttk_RegisterLayout(themePtr, "Horizontal.Sash", HorizontalSashLayout);
+ Ttk_RegisterLayout(themePtr, "Vertical.Sash", VerticalSashLayout);
+}
+
diff --git a/generic/ttk/ttkProgress.c b/generic/ttk/ttkProgress.c
new file mode 100644
index 0000000..4dc50a2
--- /dev/null
+++ b/generic/ttk/ttkProgress.c
@@ -0,0 +1,545 @@
+/*
+ * Copyright (c) Joe English, Pat Thoyts, Michael Kirkham
+ *
+ * ttk::progressbar widget.
+ */
+
+#include <math.h>
+#include <tk.h>
+
+#include "ttkTheme.h"
+#include "ttkWidget.h"
+
+/*------------------------------------------------------------------------
+ * +++ Widget record:
+ */
+
+#define DEF_PROGRESSBAR_LENGTH "100"
+enum {
+ TTK_PROGRESSBAR_DETERMINATE, TTK_PROGRESSBAR_INDETERMINATE
+};
+static const char *const ProgressbarModeStrings[] = {
+ "determinate", "indeterminate", NULL
+};
+
+typedef struct {
+ Tcl_Obj *orientObj;
+ Tcl_Obj *lengthObj;
+ Tcl_Obj *modeObj;
+ Tcl_Obj *variableObj;
+ Tcl_Obj *maximumObj;
+ Tcl_Obj *valueObj;
+ Tcl_Obj *phaseObj;
+
+ int mode;
+ Ttk_TraceHandle *variableTrace; /* Trace handle for -variable option */
+ int period; /* Animation period */
+ int maxPhase; /* Max animation phase */
+ Tcl_TimerToken timer; /* Animation timer */
+
+} ProgressbarPart;
+
+typedef struct {
+ WidgetCore core;
+ ProgressbarPart progress;
+} Progressbar;
+
+static Tk_OptionSpec ProgressbarOptionSpecs[] =
+{
+ {TK_OPTION_STRING_TABLE, "-orient", "orient", "Orient",
+ "horizontal", Tk_Offset(Progressbar,progress.orientObj), -1,
+ 0, (ClientData)ttkOrientStrings, STYLE_CHANGED },
+ {TK_OPTION_PIXELS, "-length", "length", "Length",
+ DEF_PROGRESSBAR_LENGTH, Tk_Offset(Progressbar,progress.lengthObj), -1,
+ 0, 0, GEOMETRY_CHANGED },
+ {TK_OPTION_STRING_TABLE, "-mode", "mode", "ProgressMode", "determinate",
+ Tk_Offset(Progressbar,progress.modeObj),
+ Tk_Offset(Progressbar,progress.mode),
+ 0, (ClientData)ProgressbarModeStrings, 0 },
+ {TK_OPTION_DOUBLE, "-maximum", "maximum", "Maximum",
+ "100", Tk_Offset(Progressbar,progress.maximumObj), -1,
+ 0, 0, 0 },
+ {TK_OPTION_STRING, "-variable", "variable", "Variable",
+ NULL, Tk_Offset(Progressbar,progress.variableObj), -1,
+ TK_OPTION_NULL_OK, 0, 0 },
+ {TK_OPTION_DOUBLE, "-value", "value", "Value",
+ "0.0", Tk_Offset(Progressbar,progress.valueObj), -1,
+ 0, 0, 0 },
+ {TK_OPTION_INT, "-phase", "phase", "Phase",
+ "0", Tk_Offset(Progressbar,progress.phaseObj), -1,
+ 0, 0, 0 },
+
+ WIDGET_TAKEFOCUS_FALSE,
+ WIDGET_INHERIT_OPTIONS(ttkCoreOptionSpecs)
+};
+
+/*------------------------------------------------------------------------
+ * +++ Animation procedures:
+ */
+
+/* AnimationEnabled --
+ * Returns 1 if animation should be active, 0 otherwise.
+ */
+static int AnimationEnabled(Progressbar *pb)
+{
+ double maximum = 100, value = 0;
+
+ Tcl_GetDoubleFromObj(NULL, pb->progress.maximumObj, &maximum);
+ Tcl_GetDoubleFromObj(NULL, pb->progress.valueObj, &value);
+
+ return pb->progress.period > 0
+ && value > 0.0
+ && ( value < maximum
+ || pb->progress.mode == TTK_PROGRESSBAR_INDETERMINATE);
+}
+
+/* AnimateProgressProc --
+ * Timer callback for progress bar animation.
+ * Increments the -phase option, redisplays the widget,
+ * and reschedules itself if animation still enabled.
+ */
+static void AnimateProgressProc(ClientData clientData)
+{
+ Progressbar *pb = clientData;
+
+ pb->progress.timer = 0;
+
+ if (AnimationEnabled(pb)) {
+ int phase = 0;
+ Tcl_GetIntFromObj(NULL, pb->progress.phaseObj, &phase);
+
+ /*
+ * Update -phase:
+ */
+ ++phase;
+ if (pb->progress.maxPhase)
+ phase %= pb->progress.maxPhase;
+ Tcl_DecrRefCount(pb->progress.phaseObj);
+ pb->progress.phaseObj = Tcl_NewIntObj(phase);
+ Tcl_IncrRefCount(pb->progress.phaseObj);
+
+ /*
+ * Reschedule:
+ */
+ pb->progress.timer = Tcl_CreateTimerHandler(
+ pb->progress.period, AnimateProgressProc, clientData);
+
+ TtkRedisplayWidget(&pb->core);
+ }
+}
+
+/* CheckAnimation --
+ * If animation is enabled and not scheduled, schedule it.
+ * If animation is disabled but scheduled, cancel it.
+ */
+static void CheckAnimation(Progressbar *pb)
+{
+ if (AnimationEnabled(pb)) {
+ if (pb->progress.timer == 0) {
+ pb->progress.timer = Tcl_CreateTimerHandler(
+ pb->progress.period, AnimateProgressProc, (ClientData)pb);
+ }
+ } else {
+ if (pb->progress.timer != 0) {
+ Tcl_DeleteTimerHandler(pb->progress.timer);
+ pb->progress.timer = 0;
+ }
+ }
+}
+
+/*------------------------------------------------------------------------
+ * +++ Trace hook for progressbar -variable option:
+ */
+
+static void VariableChanged(void *recordPtr, const char *value)
+{
+ Progressbar *pb = recordPtr;
+ Tcl_Obj *newValue;
+ double scratch;
+
+ if (WidgetDestroyed(&pb->core)) {
+ return;
+ }
+
+ if (!value) {
+ /* Linked variable is unset -- disable widget */
+ TtkWidgetChangeState(&pb->core, TTK_STATE_DISABLED, 0);
+ return;
+ }
+ TtkWidgetChangeState(&pb->core, 0, TTK_STATE_DISABLED);
+
+ newValue = Tcl_NewStringObj(value, -1);
+ Tcl_IncrRefCount(newValue);
+ if (Tcl_GetDoubleFromObj(NULL, newValue, &scratch) != TCL_OK) {
+ TtkWidgetChangeState(&pb->core, TTK_STATE_INVALID, 0);
+ return;
+ }
+ TtkWidgetChangeState(&pb->core, 0, TTK_STATE_INVALID);
+ Tcl_DecrRefCount(pb->progress.valueObj);
+ pb->progress.valueObj = newValue;
+
+ CheckAnimation(pb);
+ TtkRedisplayWidget(&pb->core);
+}
+
+/*------------------------------------------------------------------------
+ * +++ Widget class methods:
+ */
+
+static void ProgressbarInitialize(Tcl_Interp *interp, void *recordPtr)
+{
+ Progressbar *pb = recordPtr;
+ pb->progress.variableTrace = 0;
+ pb->progress.timer = 0;
+}
+
+static void ProgressbarCleanup(void *recordPtr)
+{
+ Progressbar *pb = recordPtr;
+ if (pb->progress.variableTrace)
+ Ttk_UntraceVariable(pb->progress.variableTrace);
+ if (pb->progress.timer)
+ Tcl_DeleteTimerHandler(pb->progress.timer);
+}
+
+/*
+ * Configure hook:
+ *
+ * @@@ TODO: deal with [$pb configure -value ... -variable ...]
+ */
+static int ProgressbarConfigure(Tcl_Interp *interp, void *recordPtr, int mask)
+{
+ Progressbar *pb = recordPtr;
+ Tcl_Obj *varName = pb->progress.variableObj;
+ Ttk_TraceHandle *vt = 0;
+
+ if (varName != NULL && *Tcl_GetString(varName) != '\0') {
+ vt = Ttk_TraceVariable(interp, varName, VariableChanged, recordPtr);
+ if (!vt) return TCL_ERROR;
+ }
+
+ if (TtkCoreConfigure(interp, recordPtr, mask) != TCL_OK) {
+ if (vt) Ttk_UntraceVariable(vt);
+ return TCL_ERROR;
+ }
+
+ if (pb->progress.variableTrace) {
+ Ttk_UntraceVariable(pb->progress.variableTrace);
+ }
+ pb->progress.variableTrace = vt;
+
+ return TCL_OK;
+}
+
+/*
+ * Post-configuration hook:
+ */
+static int ProgressbarPostConfigure(
+ Tcl_Interp *interp, void *recordPtr, int mask)
+{
+ Progressbar *pb = recordPtr;
+ int status = TCL_OK;
+
+ if (pb->progress.variableTrace) {
+ status = Ttk_FireTrace(pb->progress.variableTrace);
+ if (WidgetDestroyed(&pb->core)) {
+ return TCL_ERROR;
+ }
+ if (status != TCL_OK) {
+ /* Unset -variable: */
+ Ttk_UntraceVariable(pb->progress.variableTrace);
+ Tcl_DecrRefCount(pb->progress.variableObj);
+ pb->progress.variableTrace = 0;
+ pb->progress.variableObj = NULL;
+ return TCL_ERROR;
+ }
+ }
+
+ CheckAnimation(pb);
+
+ return status;
+}
+
+/*
+ * Size hook:
+ * Compute base layout size, overrid
+ */
+static int ProgressbarSize(void *recordPtr, int *widthPtr, int *heightPtr)
+{
+ Progressbar *pb = recordPtr;
+ int length = 100, orient = TTK_ORIENT_HORIZONTAL;
+
+ TtkWidgetSize(recordPtr, widthPtr, heightPtr);
+
+ /* Override requested width (height) based on -length and -orient
+ */
+ Tk_GetPixelsFromObj(NULL, pb->core.tkwin, pb->progress.lengthObj, &length);
+ Ttk_GetOrientFromObj(NULL, pb->progress.orientObj, &orient);
+
+ if (orient == TTK_ORIENT_HORIZONTAL) {
+ *widthPtr = length;
+ } else {
+ *heightPtr = length;
+ }
+
+ return 1;
+}
+
+/*
+ * Layout hook:
+ * Adjust size and position of pbar element, if present.
+ */
+
+static void ProgressbarDeterminateLayout(
+ Progressbar *pb,
+ Ttk_Element pbar,
+ Ttk_Box parcel,
+ double fraction,
+ Ttk_Orient orient)
+{
+ if (fraction < 0.0) fraction = 0.0;
+ if (fraction > 1.0) fraction = 1.0;
+
+ if (orient == TTK_ORIENT_HORIZONTAL) {
+ parcel.width = (int)(parcel.width * fraction);
+ } else {
+ int newHeight = (int)(parcel.height * fraction);
+ parcel.y += (parcel.height - newHeight);
+ parcel.height = newHeight;
+ }
+ Ttk_PlaceElement(pb->core.layout, pbar, parcel);
+}
+
+static void ProgressbarIndeterminateLayout(
+ Progressbar *pb,
+ Ttk_Element pbar,
+ Ttk_Box parcel,
+ double fraction,
+ Ttk_Orient orient)
+{
+ Ttk_Box pbarBox = Ttk_ElementParcel(pbar);
+
+ fraction = fmod(fabs(fraction), 2.0);
+ if (fraction > 1.0) {
+ fraction = 2.0 - fraction;
+ }
+
+ if (orient == TTK_ORIENT_HORIZONTAL) {
+ pbarBox.x = parcel.x + (int)(fraction * (parcel.width-pbarBox.width));
+ } else {
+ pbarBox.y = parcel.y + (int)(fraction * (parcel.height-pbarBox.height));
+ }
+ Ttk_PlaceElement(pb->core.layout, pbar, pbarBox);
+}
+
+static void ProgressbarDoLayout(void *recordPtr)
+{
+ Progressbar *pb = recordPtr;
+ WidgetCore *corePtr = &pb->core;
+ Ttk_Element pbar = Ttk_FindElement(corePtr->layout, "pbar");
+ double value = 0.0, maximum = 100.0;
+ int orient = TTK_ORIENT_HORIZONTAL;
+
+ Ttk_PlaceLayout(corePtr->layout,corePtr->state,Ttk_WinBox(corePtr->tkwin));
+
+ /* Adjust the bar size:
+ */
+
+ Tcl_GetDoubleFromObj(NULL, pb->progress.valueObj, &value);
+ Tcl_GetDoubleFromObj(NULL, pb->progress.maximumObj, &maximum);
+ Ttk_GetOrientFromObj(NULL, pb->progress.orientObj, &orient);
+
+ if (pbar) {
+ double fraction = value / maximum;
+ Ttk_Box parcel = Ttk_ClientRegion(corePtr->layout, "trough");
+
+ if (pb->progress.mode == TTK_PROGRESSBAR_DETERMINATE) {
+ ProgressbarDeterminateLayout(
+ pb, pbar, parcel, fraction, orient);
+ } else {
+ ProgressbarIndeterminateLayout(
+ pb, pbar, parcel, fraction, orient);
+ }
+ }
+}
+
+static Ttk_Layout ProgressbarGetLayout(
+ Tcl_Interp *interp, Ttk_Theme theme, void *recordPtr)
+{
+ Progressbar *pb = recordPtr;
+ Ttk_Layout layout = TtkWidgetGetOrientedLayout(
+ interp, theme, recordPtr, pb->progress.orientObj);
+
+ /*
+ * Check if the style supports animation:
+ */
+ pb->progress.period = 0;
+ pb->progress.maxPhase = 0;
+ if (layout) {
+ Tcl_Obj *periodObj = Ttk_QueryOption(layout,"-period", 0);
+ Tcl_Obj *maxPhaseObj = Ttk_QueryOption(layout,"-maxphase", 0);
+ if (periodObj)
+ Tcl_GetIntFromObj(NULL, periodObj, &pb->progress.period);
+ if (maxPhaseObj)
+ Tcl_GetIntFromObj(NULL, maxPhaseObj, &pb->progress.maxPhase);
+ }
+
+ return layout;
+}
+
+/*------------------------------------------------------------------------
+ * +++ Widget commands:
+ */
+
+/* $sb step ?amount?
+ */
+static int ProgressbarStepCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ Progressbar *pb = recordPtr;
+ double value = 0.0, stepAmount = 1.0;
+ Tcl_Obj *newValueObj;
+
+ if (objc == 3) {
+ if (Tcl_GetDoubleFromObj(interp, objv[2], &stepAmount) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ } else if (objc != 2) {
+ Tcl_WrongNumArgs(interp, 2,objv, "?stepAmount?");
+ return TCL_ERROR;
+ }
+
+ (void)Tcl_GetDoubleFromObj(NULL, pb->progress.valueObj, &value);
+ value += stepAmount;
+
+ /* In determinate mode, wrap around if value exceeds maximum:
+ */
+ if (pb->progress.mode == TTK_PROGRESSBAR_DETERMINATE) {
+ double maximum = 100.0;
+ (void)Tcl_GetDoubleFromObj(NULL, pb->progress.maximumObj, &maximum);
+ value = fmod(value, maximum);
+ }
+
+ newValueObj = Tcl_NewDoubleObj(value);
+
+ TtkRedisplayWidget(&pb->core);
+
+ /* Update value by setting the linked -variable, if there is one:
+ */
+ if (pb->progress.variableTrace) {
+ return Tcl_ObjSetVar2(
+ interp, pb->progress.variableObj, 0, newValueObj,
+ TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG)
+ ? TCL_OK : TCL_ERROR;
+ }
+
+ /* Otherwise, change the -value directly:
+ */
+ Tcl_IncrRefCount(newValueObj);
+ Tcl_DecrRefCount(pb->progress.valueObj);
+ pb->progress.valueObj = newValueObj;
+ CheckAnimation(pb);
+
+ return TCL_OK;
+}
+
+/* $sb start|stop ?args? --
+ * Change [$sb $cmd ...] to [ttk::progressbar::$cmd ...]
+ * and pass to interpreter.
+ */
+static int ProgressbarStartStopCommand(
+ Tcl_Interp *interp, const char *cmdName, int objc, Tcl_Obj *const objv[])
+{
+ Tcl_Obj *cmd = Tcl_NewListObj(objc, objv);
+ Tcl_Obj *prefix[2];
+ int status;
+
+ /* ASSERT: objc >= 2 */
+
+ prefix[0] = Tcl_NewStringObj(cmdName, -1);
+ prefix[1] = objv[0];
+ Tcl_ListObjReplace(interp, cmd, 0,2, 2,prefix);
+
+ Tcl_IncrRefCount(cmd);
+ status = Tcl_EvalObjEx(interp, cmd, 0);
+ Tcl_DecrRefCount(cmd);
+
+ return status;
+}
+
+static int ProgressbarStartCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ return ProgressbarStartStopCommand(
+ interp, "::ttk::progressbar::start", objc, objv);
+}
+
+static int ProgressbarStopCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ return ProgressbarStartStopCommand(
+ interp, "::ttk::progressbar::stop", objc, objv);
+}
+
+static const Ttk_Ensemble ProgressbarCommands[] = {
+ { "configure", TtkWidgetConfigureCommand,0 },
+ { "cget", TtkWidgetCgetCommand,0 },
+ { "identify", TtkWidgetIdentifyCommand,0 },
+ { "instate", TtkWidgetInstateCommand,0 },
+ { "start", ProgressbarStartCommand,0 },
+ { "state", TtkWidgetStateCommand,0 },
+ { "step", ProgressbarStepCommand,0 },
+ { "stop", ProgressbarStopCommand,0 },
+ { 0,0,0 }
+};
+
+/*
+ * Widget specification:
+ */
+static WidgetSpec ProgressbarWidgetSpec =
+{
+ "TProgressbar", /* className */
+ sizeof(Progressbar), /* recordSize */
+ ProgressbarOptionSpecs, /* optionSpecs */
+ ProgressbarCommands, /* subcommands */
+ ProgressbarInitialize, /* initializeProc */
+ ProgressbarCleanup, /* cleanupProc */
+ ProgressbarConfigure, /* configureProc */
+ ProgressbarPostConfigure, /* postConfigureProc */
+ ProgressbarGetLayout, /* getLayoutProc */
+ ProgressbarSize, /* sizeProc */
+ ProgressbarDoLayout, /* layoutProc */
+ TtkWidgetDisplay /* displayProc */
+};
+
+/*
+ * Layouts:
+ */
+TTK_BEGIN_LAYOUT(VerticalProgressbarLayout)
+ TTK_GROUP("Vertical.Progressbar.trough", TTK_FILL_BOTH,
+ TTK_NODE("Vertical.Progressbar.pbar", TTK_PACK_BOTTOM|TTK_FILL_X))
+TTK_END_LAYOUT
+
+TTK_BEGIN_LAYOUT(HorizontalProgressbarLayout)
+ TTK_GROUP("Horizontal.Progressbar.trough", TTK_FILL_BOTH,
+ TTK_NODE("Horizontal.Progressbar.pbar", TTK_PACK_LEFT|TTK_FILL_Y))
+TTK_END_LAYOUT
+
+/*
+ * Initialization:
+ */
+
+MODULE_SCOPE
+void TtkProgressbar_Init(Tcl_Interp *interp)
+{
+ Ttk_Theme themePtr = Ttk_GetDefaultTheme(interp);
+
+ Ttk_RegisterLayout(themePtr,
+ "Vertical.TProgressbar", VerticalProgressbarLayout);
+ Ttk_RegisterLayout(themePtr,
+ "Horizontal.TProgressbar", HorizontalProgressbarLayout);
+
+ RegisterWidget(interp, "ttk::progressbar", &ProgressbarWidgetSpec);
+}
+
+/*EOF*/
diff --git a/generic/ttk/ttkScale.c b/generic/ttk/ttkScale.c
new file mode 100644
index 0000000..69753d1
--- /dev/null
+++ b/generic/ttk/ttkScale.c
@@ -0,0 +1,515 @@
+/*
+ * Copyright (C) 2004 Pat Thoyts <patthoyts@users.sourceforge.net>
+ *
+ * ttk::scale widget.
+ */
+
+#include <tk.h>
+#include <string.h>
+#include <stdio.h>
+#include "ttkTheme.h"
+#include "ttkWidget.h"
+
+#define DEF_SCALE_LENGTH "100"
+
+#define MAX(a,b) ((a) > (b) ? (a) : (b))
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+
+/*
+ * Scale widget record
+ */
+typedef struct
+{
+ /* slider element options */
+ Tcl_Obj *fromObj; /* minimum value */
+ Tcl_Obj *toObj; /* maximum value */
+ Tcl_Obj *valueObj; /* current value */
+ Tcl_Obj *lengthObj; /* length of the long axis of the scale */
+ Tcl_Obj *orientObj; /* widget orientation */
+ int orient;
+
+ /* widget options */
+ Tcl_Obj *commandObj;
+ Tcl_Obj *variableObj;
+
+ /* internal state */
+ Ttk_TraceHandle *variableTrace;
+
+} ScalePart;
+
+typedef struct
+{
+ WidgetCore core;
+ ScalePart scale;
+} Scale;
+
+static Tk_OptionSpec ScaleOptionSpecs[] =
+{
+ {TK_OPTION_STRING, "-command", "command", "Command", "",
+ Tk_Offset(Scale,scale.commandObj), -1,
+ TK_OPTION_NULL_OK,0,0},
+ {TK_OPTION_STRING, "-variable", "variable", "Variable", "",
+ Tk_Offset(Scale,scale.variableObj), -1,
+ 0,0,0},
+ {TK_OPTION_STRING_TABLE, "-orient", "orient", "Orient", "horizontal",
+ Tk_Offset(Scale,scale.orientObj),
+ Tk_Offset(Scale,scale.orient), 0,
+ (ClientData)ttkOrientStrings, STYLE_CHANGED },
+
+ {TK_OPTION_DOUBLE, "-from", "from", "From", "0",
+ Tk_Offset(Scale,scale.fromObj), -1, 0, 0, 0},
+ {TK_OPTION_DOUBLE, "-to", "to", "To", "1.0",
+ Tk_Offset(Scale,scale.toObj), -1, 0, 0, 0},
+ {TK_OPTION_DOUBLE, "-value", "value", "Value", "0",
+ Tk_Offset(Scale,scale.valueObj), -1, 0, 0, 0},
+ {TK_OPTION_PIXELS, "-length", "length", "Length",
+ DEF_SCALE_LENGTH, Tk_Offset(Scale,scale.lengthObj), -1, 0, 0,
+ GEOMETRY_CHANGED},
+
+ WIDGET_TAKEFOCUS_TRUE,
+ WIDGET_INHERIT_OPTIONS(ttkCoreOptionSpecs)
+};
+
+static XPoint ValueToPoint(Scale *scalePtr, double value);
+static double PointToValue(Scale *scalePtr, int x, int y);
+
+/* ScaleVariableChanged --
+ * Variable trace procedure for scale -variable;
+ * Updates the scale's value.
+ * If the linked variable is not a valid double,
+ * sets the 'invalid' state.
+ */
+static void ScaleVariableChanged(void *recordPtr, const char *value)
+{
+ Scale *scale = recordPtr;
+ double v;
+
+ if (value == NULL || Tcl_GetDouble(0, value, &v) != TCL_OK) {
+ TtkWidgetChangeState(&scale->core, TTK_STATE_INVALID, 0);
+ } else {
+ Tcl_Obj *valueObj = Tcl_NewDoubleObj(v);
+ Tcl_IncrRefCount(valueObj);
+ Tcl_DecrRefCount(scale->scale.valueObj);
+ scale->scale.valueObj = valueObj;
+ TtkWidgetChangeState(&scale->core, 0, TTK_STATE_INVALID);
+ }
+ TtkRedisplayWidget(&scale->core);
+}
+
+/* ScaleInitialize --
+ * Scale widget initialization hook.
+ */
+static void ScaleInitialize(Tcl_Interp *interp, void *recordPtr)
+{
+ Scale *scalePtr = recordPtr;
+ TtkTrackElementState(&scalePtr->core);
+}
+
+static void ScaleCleanup(void *recordPtr)
+{
+ Scale *scale = recordPtr;
+
+ if (scale->scale.variableTrace) {
+ Ttk_UntraceVariable(scale->scale.variableTrace);
+ scale->scale.variableTrace = 0;
+ }
+}
+
+/* ScaleConfigure --
+ * Configuration hook.
+ */
+static int ScaleConfigure(Tcl_Interp *interp, void *recordPtr, int mask)
+{
+ Scale *scale = recordPtr;
+ Tcl_Obj *varName = scale->scale.variableObj;
+ Ttk_TraceHandle *vt = 0;
+
+ if (varName != NULL && *Tcl_GetString(varName) != '\0') {
+ vt = Ttk_TraceVariable(interp,varName, ScaleVariableChanged,recordPtr);
+ if (!vt) return TCL_ERROR;
+ }
+
+ if (TtkCoreConfigure(interp, recordPtr, mask) != TCL_OK) {
+ if (vt) Ttk_UntraceVariable(vt);
+ return TCL_ERROR;
+ }
+
+ if (scale->scale.variableTrace) {
+ Ttk_UntraceVariable(scale->scale.variableTrace);
+ }
+ scale->scale.variableTrace = vt;
+
+ return TCL_OK;
+}
+
+/* ScalePostConfigure --
+ * Post-configuration hook.
+ */
+static int ScalePostConfigure(
+ Tcl_Interp *interp, void *recordPtr, int mask)
+{
+ Scale *scale = recordPtr;
+ int status = TCL_OK;
+
+ if (scale->scale.variableTrace) {
+ status = Ttk_FireTrace(scale->scale.variableTrace);
+ if (WidgetDestroyed(&scale->core)) {
+ return TCL_ERROR;
+ }
+ if (status != TCL_OK) {
+ /* Unset -variable: */
+ Ttk_UntraceVariable(scale->scale.variableTrace);
+ Tcl_DecrRefCount(scale->scale.variableObj);
+ scale->scale.variableTrace = 0;
+ scale->scale.variableObj = NULL;
+ status = TCL_ERROR;
+ }
+ }
+
+ return status;
+}
+
+/* ScaleGetLayout --
+ * getLayout hook.
+ */
+static Ttk_Layout
+ScaleGetLayout(Tcl_Interp *interp, Ttk_Theme theme, void *recordPtr)
+{
+ Scale *scalePtr = recordPtr;
+ return TtkWidgetGetOrientedLayout(
+ interp, theme, recordPtr, scalePtr->scale.orientObj);
+}
+
+/*
+ * TroughBox --
+ * Returns the inner area of the trough element.
+ */
+static Ttk_Box TroughBox(Scale *scalePtr)
+{
+ return Ttk_ClientRegion(scalePtr->core.layout, "trough");
+}
+
+/*
+ * TroughRange --
+ * Return the value area of the trough element, adjusted
+ * for slider size.
+ */
+static Ttk_Box TroughRange(Scale *scalePtr)
+{
+ Ttk_Box troughBox = TroughBox(scalePtr);
+ Ttk_Element slider = Ttk_FindElement(scalePtr->core.layout,"slider");
+
+ /*
+ * If this is a scale widget, adjust range for slider:
+ */
+ if (slider) {
+ Ttk_Box sliderBox = Ttk_ElementParcel(slider);
+ if (scalePtr->scale.orient == TTK_ORIENT_HORIZONTAL) {
+ troughBox.x += sliderBox.width / 2;
+ troughBox.width -= sliderBox.width;
+ } else {
+ troughBox.y += sliderBox.height / 2;
+ troughBox.height -= sliderBox.height;
+ }
+ }
+
+ return troughBox;
+}
+
+/*
+ * ScaleFraction --
+ */
+static double ScaleFraction(Scale *scalePtr, double value)
+{
+ double from = 0, to = 1, fraction;
+
+ Tcl_GetDoubleFromObj(NULL, scalePtr->scale.fromObj, &from);
+ Tcl_GetDoubleFromObj(NULL, scalePtr->scale.toObj, &to);
+
+ if (from == to) {
+ return 1.0;
+ }
+
+ fraction = (value - from) / (to - from);
+
+ return fraction < 0 ? 0 : fraction > 1 ? 1 : fraction;
+}
+
+/* $scale get ?x y? --
+ * Returns the current value of the scale widget, or if $x and
+ * $y are specified, the value represented by point @x,y.
+ */
+static int
+ScaleGetCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ Scale *scalePtr = recordPtr;
+ int x, y, r = TCL_OK;
+ double value = 0;
+
+ if ((objc != 2) && (objc != 4)) {
+ Tcl_WrongNumArgs(interp, 1, objv, "get ?x y?");
+ return TCL_ERROR;
+ }
+ if (objc == 2) {
+ Tcl_SetObjResult(interp, scalePtr->scale.valueObj);
+ } else {
+ r = Tcl_GetIntFromObj(interp, objv[2], &x);
+ if (r == TCL_OK)
+ r = Tcl_GetIntFromObj(interp, objv[3], &y);
+ if (r == TCL_OK) {
+ value = PointToValue(scalePtr, x, y);
+ Tcl_SetObjResult(interp, Tcl_NewDoubleObj(value));
+ }
+ }
+ return r;
+}
+
+/* $scale set $newValue
+ */
+static int
+ScaleSetCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ Scale *scalePtr = recordPtr;
+ double from = 0.0, to = 1.0, value;
+ int result = TCL_OK;
+
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 1, objv, "set value");
+ return TCL_ERROR;
+ }
+
+ if (Tcl_GetDoubleFromObj(interp, objv[2], &value) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ if (scalePtr->core.state & TTK_STATE_DISABLED) {
+ return TCL_OK;
+ }
+
+ /* ASSERT: fromObj and toObj are valid doubles.
+ */
+ Tcl_GetDoubleFromObj(interp, scalePtr->scale.fromObj, &from);
+ Tcl_GetDoubleFromObj(interp, scalePtr->scale.toObj, &to);
+
+ /* Limit new value to between 'from' and 'to':
+ */
+ if (from < to) {
+ value = value < from ? from : value > to ? to : value;
+ } else {
+ value = value < to ? to : value > from ? from : value;
+ }
+
+ /*
+ * Set value:
+ */
+ Tcl_DecrRefCount(scalePtr->scale.valueObj);
+ scalePtr->scale.valueObj = Tcl_NewDoubleObj(value);
+ Tcl_IncrRefCount(scalePtr->scale.valueObj);
+ TtkRedisplayWidget(&scalePtr->core);
+
+ /*
+ * Set attached variable, if any:
+ */
+ if (scalePtr->scale.variableObj != NULL) {
+ Tcl_ObjSetVar2(interp, scalePtr->scale.variableObj, NULL,
+ scalePtr->scale.valueObj, TCL_GLOBAL_ONLY);
+ }
+ if (WidgetDestroyed(&scalePtr->core)) {
+ return TCL_ERROR;
+ }
+
+ /*
+ * Invoke -command, if any:
+ */
+ if (scalePtr->scale.commandObj != NULL) {
+ Tcl_Obj *cmdObj = Tcl_DuplicateObj(scalePtr->scale.commandObj);
+ Tcl_IncrRefCount(cmdObj);
+ Tcl_AppendToObj(cmdObj, " ", 1);
+ Tcl_AppendObjToObj(cmdObj, scalePtr->scale.valueObj);
+ result = Tcl_EvalObjEx(interp, cmdObj, TCL_EVAL_GLOBAL);
+ Tcl_DecrRefCount(cmdObj);
+ }
+
+ return result;
+}
+
+static int
+ScaleCoordsCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ Scale *scalePtr = recordPtr;
+ double value;
+ int r = TCL_OK;
+
+ if (objc < 2 || objc > 3) {
+ Tcl_WrongNumArgs(interp, 1, objv, "coords ?value?");
+ return TCL_ERROR;
+ }
+
+ if (objc == 3) {
+ r = Tcl_GetDoubleFromObj(interp, objv[2], &value);
+ } else {
+ r = Tcl_GetDoubleFromObj(interp, scalePtr->scale.valueObj, &value);
+ }
+
+ if (r == TCL_OK) {
+ Tcl_Obj *point[2];
+ XPoint pt = ValueToPoint(scalePtr, value);
+ point[0] = Tcl_NewIntObj(pt.x);
+ point[1] = Tcl_NewIntObj(pt.y);
+ Tcl_SetObjResult(interp, Tcl_NewListObj(2, point));
+ }
+ return r;
+}
+
+static void ScaleDoLayout(void *clientData)
+{
+ WidgetCore *corePtr = clientData;
+ Ttk_Element slider = Ttk_FindElement(corePtr->layout, "slider");
+
+ Ttk_PlaceLayout(corePtr->layout,corePtr->state,Ttk_WinBox(corePtr->tkwin));
+
+ /* Adjust the slider position:
+ */
+ if (slider) {
+ Scale *scalePtr = clientData;
+ Ttk_Box troughBox = TroughBox(scalePtr);
+ Ttk_Box sliderBox = Ttk_ElementParcel(slider);
+ double value = 0.0;
+ double fraction;
+ int range;
+
+ Tcl_GetDoubleFromObj(NULL, scalePtr->scale.valueObj, &value);
+ fraction = ScaleFraction(scalePtr, value);
+
+ if (scalePtr->scale.orient == TTK_ORIENT_HORIZONTAL) {
+ range = troughBox.width - sliderBox.width;
+ sliderBox.x += (int)(fraction * range);
+ } else {
+ range = troughBox.height - sliderBox.height;
+ sliderBox.y += (int)(fraction * range);
+ }
+ Ttk_PlaceElement(corePtr->layout, slider, sliderBox);
+ }
+}
+
+/*
+ * ScaleSize --
+ * Compute requested size of scale.
+ */
+static int ScaleSize(void *clientData, int *widthPtr, int *heightPtr)
+{
+ WidgetCore *corePtr = clientData;
+ Scale *scalePtr = clientData;
+ int length;
+
+ Ttk_LayoutSize(corePtr->layout, corePtr->state, widthPtr, heightPtr);
+
+ /* Assert the -length configuration option */
+ Tk_GetPixelsFromObj(NULL, corePtr->tkwin,
+ scalePtr->scale.lengthObj, &length);
+ if (scalePtr->scale.orient == TTK_ORIENT_VERTICAL) {
+ *heightPtr = MAX(*heightPtr, length);
+ } else {
+ *widthPtr = MAX(*widthPtr, length);
+ }
+
+ return 1;
+}
+
+static double
+PointToValue(Scale *scalePtr, int x, int y)
+{
+ Ttk_Box troughBox = TroughRange(scalePtr);
+ double from = 0, to = 1, fraction;
+
+ Tcl_GetDoubleFromObj(NULL, scalePtr->scale.fromObj, &from);
+ Tcl_GetDoubleFromObj(NULL, scalePtr->scale.toObj, &to);
+
+ if (scalePtr->scale.orient == TTK_ORIENT_HORIZONTAL) {
+ fraction = (double)(x - troughBox.x) / (double)troughBox.width;
+ } else {
+ fraction = (double)(y - troughBox.y) / (double)troughBox.height;
+ }
+
+ fraction = fraction < 0 ? 0 : fraction > 1 ? 1 : fraction;
+
+ return from + fraction * (to-from);
+}
+
+/*
+ * Return the center point in the widget corresponding to the given
+ * value. This point can be used to center the slider.
+ */
+
+static XPoint
+ValueToPoint(Scale *scalePtr, double value)
+{
+ Ttk_Box troughBox = TroughRange(scalePtr);
+ double fraction = ScaleFraction(scalePtr, value);
+ XPoint pt = {0, 0};
+
+ if (scalePtr->scale.orient == TTK_ORIENT_HORIZONTAL) {
+ pt.x = troughBox.x + (int)(fraction * troughBox.width);
+ pt.y = troughBox.y + troughBox.height / 2;
+ } else {
+ pt.x = troughBox.x + troughBox.width / 2;
+ pt.y = troughBox.y + (int)(fraction * troughBox.height);
+ }
+ return pt;
+}
+
+static const Ttk_Ensemble ScaleCommands[] = {
+ { "configure", TtkWidgetConfigureCommand,0 },
+ { "cget", TtkWidgetCgetCommand,0 },
+ { "state", TtkWidgetStateCommand,0 },
+ { "instate", TtkWidgetInstateCommand,0 },
+ { "identify", TtkWidgetIdentifyCommand,0 },
+ { "set", ScaleSetCommand,0 },
+ { "get", ScaleGetCommand,0 },
+ { "coords", ScaleCoordsCommand,0 },
+ { 0,0,0 }
+};
+
+static WidgetSpec ScaleWidgetSpec =
+{
+ "TScale", /* Class name */
+ sizeof(Scale), /* record size */
+ ScaleOptionSpecs, /* option specs */
+ ScaleCommands, /* widget commands */
+ ScaleInitialize, /* initialization proc */
+ ScaleCleanup, /* cleanup proc */
+ ScaleConfigure, /* configure proc */
+ ScalePostConfigure, /* postConfigure */
+ ScaleGetLayout, /* getLayoutProc */
+ ScaleSize, /* sizeProc */
+ ScaleDoLayout, /* layoutProc */
+ TtkWidgetDisplay /* displayProc */
+};
+
+TTK_BEGIN_LAYOUT(VerticalScaleLayout)
+ TTK_GROUP("Vertical.Scale.trough", TTK_FILL_BOTH,
+ TTK_NODE("Vertical.Scale.slider", TTK_PACK_TOP) )
+TTK_END_LAYOUT
+
+TTK_BEGIN_LAYOUT(HorizontalScaleLayout)
+ TTK_GROUP("Horizontal.Scale.trough", TTK_FILL_BOTH,
+ TTK_NODE("Horizontal.Scale.slider", TTK_PACK_LEFT) )
+TTK_END_LAYOUT
+
+/*
+ * Initialization.
+ */
+MODULE_SCOPE
+void TtkScale_Init(Tcl_Interp *interp)
+{
+ Ttk_Theme theme = Ttk_GetDefaultTheme(interp);
+
+ Ttk_RegisterLayout(theme, "Vertical.TScale", VerticalScaleLayout);
+ Ttk_RegisterLayout(theme, "Horizontal.TScale", HorizontalScaleLayout);
+
+ RegisterWidget(interp, "ttk::scale", &ScaleWidgetSpec);
+}
+
diff --git a/generic/ttk/ttkScroll.c b/generic/ttk/ttkScroll.c
new file mode 100644
index 0000000..defe05a
--- /dev/null
+++ b/generic/ttk/ttkScroll.c
@@ -0,0 +1,252 @@
+/*
+ * Copyright 2004, Joe English
+ *
+ * Support routines for scrollable widgets.
+ *
+ * (This is sort of half-baked; needs some work)
+ *
+ * Scrollable interface:
+ *
+ * + 'first' is controlled by [xy]view widget command
+ * and other scrolling commands like 'see';
+ * + 'total' depends on widget contents;
+ * + 'last' depends on first, total, and widget size.
+ *
+ * Choreography (typical usage):
+ *
+ * 1. User adjusts scrollbar, scrollbar widget calls its -command
+ * 2. Scrollbar -command invokes the scrollee [xy]view widget method
+ * 3. TtkScrollviewCommand calls TtkScrollTo(), which updates
+ * 'first' and schedules a redisplay.
+ * 4. Once the scrollee knows 'total' and 'last' (typically in
+ * the LayoutProc), call TtkScrolled(h,first,last,total) to
+ * synchronize the scrollbar.
+ * 5. The scrollee -[xy]scrollcommand is called (in an idle callback)
+ * 6. Which calls the scrollbar 'set' method and redisplays the scrollbar.
+ *
+ * If the scrollee has internal scrolling (e.g., a 'see' method),
+ * it should TtkScrollTo() directly (step 2).
+ *
+ * If the widget value changes, it should call TtkScrolled() (step 4).
+ * (This usually happens automatically when the widget is redisplayed).
+ *
+ * If the scrollee's -[xy]scrollcommand changes, it should call
+ * TtkScrollbarUpdateRequired, which will invoke step (5) (@@@ Fix this)
+ */
+
+#include <tk.h>
+#include "ttkTheme.h"
+#include "ttkWidget.h"
+
+/* Private data:
+ */
+#define SCROLL_UPDATE_PENDING (0x1)
+#define SCROLL_UPDATE_REQUIRED (0x2)
+
+struct ScrollHandleRec
+{
+ unsigned flags;
+ WidgetCore *corePtr;
+ Scrollable *scrollPtr;
+};
+
+/* TtkCreateScrollHandle --
+ * Initialize scroll handle.
+ */
+ScrollHandle TtkCreateScrollHandle(WidgetCore *corePtr, Scrollable *scrollPtr)
+{
+ ScrollHandle h = (ScrollHandle)ckalloc(sizeof(*h));
+
+ h->flags = 0;
+ h->corePtr = corePtr;
+ h->scrollPtr = scrollPtr;
+
+ scrollPtr->first = 0;
+ scrollPtr->last = 1;
+ scrollPtr->total = 1;
+ return h;
+}
+
+/* UpdateScrollbar --
+ * Call the -scrollcommand callback to sync the scrollbar.
+ * Returns: Whatever the -scrollcommand does.
+ */
+static int UpdateScrollbar(Tcl_Interp *interp, ScrollHandle h)
+{
+ Scrollable *s = h->scrollPtr;
+ WidgetCore *corePtr = h->corePtr;
+ char arg1[TCL_DOUBLE_SPACE + 2];
+ char arg2[TCL_DOUBLE_SPACE + 2];
+ int code;
+
+ h->flags &= ~SCROLL_UPDATE_REQUIRED;
+
+ if (s->scrollCmd == NULL) {
+ return TCL_OK;
+ }
+
+ arg1[0] = arg2[0] = ' ';
+ Tcl_PrintDouble(interp, (double)s->first / s->total, arg1+1);
+ Tcl_PrintDouble(interp, (double)s->last / s->total, arg2+1);
+
+ Tcl_Preserve(corePtr);
+ code = Tcl_VarEval(interp, s->scrollCmd, arg1, arg2, NULL);
+ if (WidgetDestroyed(corePtr)) {
+ Tcl_Release(corePtr);
+ return TCL_ERROR;
+ }
+ Tcl_Release(corePtr);
+
+ if (code != TCL_OK && !Tcl_InterpDeleted(interp)) {
+ /* Disable the -scrollcommand, add to stack trace:
+ */
+ ckfree(s->scrollCmd);
+ s->scrollCmd = 0;
+
+ Tcl_AddErrorInfo(interp, /* @@@ "horizontal" / "vertical" */
+ "\n (scrolling command executed by ");
+ Tcl_AddErrorInfo(interp, Tk_PathName(h->corePtr->tkwin));
+ Tcl_AddErrorInfo(interp, ")");
+ }
+ return code;
+}
+
+/* UpdateScrollbarBG --
+ * Idle handler to update the scrollbar.
+ */
+static void UpdateScrollbarBG(ClientData clientData)
+{
+ ScrollHandle h = (ScrollHandle)clientData;
+ Tcl_Interp *interp = h->corePtr->interp;
+ int code;
+
+ h->flags &= ~SCROLL_UPDATE_PENDING;
+ Tcl_Preserve((ClientData) interp);
+ code = UpdateScrollbar(interp, h);
+ if (code == TCL_ERROR && !Tcl_InterpDeleted(interp)) {
+ Tcl_BackgroundError(interp);
+ }
+ Tcl_Release((ClientData) interp);
+}
+
+/* TtkScrolled --
+ * Update scroll info, schedule scrollbar update.
+ */
+void TtkScrolled(ScrollHandle h, int first, int last, int total)
+{
+ Scrollable *s = h->scrollPtr;
+
+ /* Sanity-check inputs:
+ */
+ if (total <= 0) {
+ first = 0;
+ last = 1;
+ total = 1;
+ }
+
+ if (last > total) {
+ first -= (last - total);
+ if (first < 0) first = 0;
+ last = total;
+ }
+
+ if (s->first != first || s->last != last || s->total != total
+ || (h->flags & SCROLL_UPDATE_REQUIRED))
+ {
+ s->first = first;
+ s->last = last;
+ s->total = total;
+
+ if (!(h->flags & SCROLL_UPDATE_PENDING)) {
+ Tcl_DoWhenIdle(UpdateScrollbarBG, (ClientData)h);
+ h->flags |= SCROLL_UPDATE_PENDING;
+ }
+ }
+}
+
+/* TtkScrollbarUpdateRequired --
+ * Force a scrollbar update at the next call to TtkScrolled(),
+ * even if scroll parameters haven't changed (e.g., if
+ * -yscrollcommand has changed).
+ */
+
+void TtkScrollbarUpdateRequired(ScrollHandle h)
+{
+ h->flags |= SCROLL_UPDATE_REQUIRED;
+}
+
+/* TtkScrollviewCommand --
+ * Widget [xy]view command implementation.
+ *
+ * $w [xy]view -- return current view region
+ * $w [xy]view $index -- set topmost item
+ * $w [xy]view moveto $fraction
+ * $w [xy]view scroll $number $what -- scrollbar interface
+ */
+int TtkScrollviewCommand(
+ Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], ScrollHandle h)
+{
+ Scrollable *s = h->scrollPtr;
+ int newFirst = s->first;
+
+ if (objc == 2) {
+ Tcl_Obj *result[2];
+ result[0] = Tcl_NewDoubleObj((double)s->first / s->total);
+ result[1] = Tcl_NewDoubleObj((double)s->last / s->total);
+ Tcl_SetObjResult(interp, Tcl_NewListObj(2, result));
+ return TCL_OK;
+ } else if (objc == 3) {
+ if (Tcl_GetIntFromObj(interp, objv[2], &newFirst) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ } else {
+ double fraction;
+ int count;
+
+ switch (Tk_GetScrollInfoObj(interp, objc, objv, &fraction, &count)) {
+ case TK_SCROLL_ERROR:
+ return TCL_ERROR;
+ case TK_SCROLL_MOVETO:
+ newFirst = (int) ((fraction * s->total) + 0.5);
+ break;
+ case TK_SCROLL_UNITS:
+ newFirst = s->first + count;
+ break;
+ case TK_SCROLL_PAGES: {
+ int perPage = s->last - s->first; /* @@@ */
+ newFirst = s->first + count * perPage;
+ break;
+ }
+ }
+ }
+
+ TtkScrollTo(h, newFirst);
+
+ return TCL_OK;
+}
+
+void TtkScrollTo(ScrollHandle h, int newFirst)
+{
+ Scrollable *s = h->scrollPtr;
+
+ if (newFirst >= s->total)
+ newFirst = s->total - 1;
+ if (newFirst > s->first && s->last >= s->total) /* don't scroll past end */
+ newFirst = s->first;
+ if (newFirst < 0)
+ newFirst = 0;
+
+ if (newFirst != s->first) {
+ s->first = newFirst;
+ TtkRedisplayWidget(h->corePtr);
+ }
+}
+
+void TtkFreeScrollHandle(ScrollHandle h)
+{
+ if (h->flags & SCROLL_UPDATE_PENDING) {
+ Tcl_CancelIdleCall(UpdateScrollbarBG, (ClientData)h);
+ }
+ ckfree((ClientData)h);
+}
+
diff --git a/generic/ttk/ttkScrollbar.c b/generic/ttk/ttkScrollbar.c
new file mode 100644
index 0000000..5b0c212
--- /dev/null
+++ b/generic/ttk/ttkScrollbar.c
@@ -0,0 +1,345 @@
+/*
+ * Copyright (c) 2003, Joe English
+ *
+ * ttk::scrollbar widget.
+ */
+
+#include <tk.h>
+
+#include "ttkTheme.h"
+#include "ttkWidget.h"
+
+/*------------------------------------------------------------------------
+ * +++ Scrollbar widget record.
+ */
+typedef struct
+{
+ Tcl_Obj *commandObj;
+
+ int orient;
+ Tcl_Obj *orientObj;
+
+ double first; /* top fraction */
+ double last; /* bottom fraction */
+
+ Ttk_Box troughBox; /* trough parcel */
+ int minSize; /* minimum size of thumb */
+} ScrollbarPart;
+
+typedef struct
+{
+ WidgetCore core;
+ ScrollbarPart scrollbar;
+} Scrollbar;
+
+static Tk_OptionSpec ScrollbarOptionSpecs[] =
+{
+ {TK_OPTION_STRING, "-command", "command", "Command", "",
+ Tk_Offset(Scrollbar,scrollbar.commandObj), -1, 0,0,0},
+
+ {TK_OPTION_STRING_TABLE, "-orient", "orient", "Orient", "vertical",
+ Tk_Offset(Scrollbar,scrollbar.orientObj),
+ Tk_Offset(Scrollbar,scrollbar.orient),
+ 0,(ClientData)ttkOrientStrings,STYLE_CHANGED },
+
+ WIDGET_TAKEFOCUS_FALSE,
+ WIDGET_INHERIT_OPTIONS(ttkCoreOptionSpecs)
+};
+
+/*------------------------------------------------------------------------
+ * +++ Widget hooks.
+ */
+
+static void
+ScrollbarInitialize(Tcl_Interp *interp, void *recordPtr)
+{
+ Scrollbar *sb = recordPtr;
+ sb->scrollbar.first = 0.0;
+ sb->scrollbar.last = 1.0;
+
+ TtkTrackElementState(&sb->core);
+}
+
+static Ttk_Layout ScrollbarGetLayout(
+ Tcl_Interp *interp, Ttk_Theme theme, void *recordPtr)
+{
+ Scrollbar *sb = recordPtr;
+ return TtkWidgetGetOrientedLayout(
+ interp, theme, recordPtr, sb->scrollbar.orientObj);
+}
+
+/*
+ * ScrollbarDoLayout --
+ * Layout hook. Adjusts the position of the scrollbar thumb.
+ *
+ * Side effects:
+ * Sets sb->troughBox and sb->minSize.
+ */
+static void ScrollbarDoLayout(void *recordPtr)
+{
+ Scrollbar *sb = recordPtr;
+ WidgetCore *corePtr = &sb->core;
+ Ttk_Element thumb;
+ Ttk_Box thumbBox;
+ int thumbWidth, thumbHeight;
+ double first, last, size;
+ int minSize;
+
+ /*
+ * Use generic layout manager to compute initial layout:
+ */
+ Ttk_PlaceLayout(corePtr->layout,corePtr->state,Ttk_WinBox(corePtr->tkwin));
+
+ /*
+ * Locate thumb element, extract parcel and requested minimum size:
+ */
+ thumb = Ttk_FindElement(corePtr->layout, "thumb");
+ if (!thumb) /* Something has gone wrong -- bail */
+ return;
+
+ sb->scrollbar.troughBox = thumbBox = Ttk_ElementParcel(thumb);
+ Ttk_LayoutNodeReqSize(
+ corePtr->layout, thumb, &thumbWidth,&thumbHeight);
+
+ /*
+ * Adjust thumb element parcel:
+ */
+ first = sb->scrollbar.first;
+ last = sb->scrollbar.last;
+
+ if (sb->scrollbar.orient == TTK_ORIENT_VERTICAL) {
+ minSize = thumbHeight;
+ size = thumbBox.height - minSize;
+ thumbBox.y += (int)(size * first);
+ thumbBox.height = (int)(size * last) + minSize - (int)(size * first);
+ } else {
+ minSize = thumbWidth;
+ size = thumbBox.width - minSize;
+ thumbBox.x += (int)(size * first);
+ thumbBox.width = (int)(size * last) + minSize - (int)(size * first);
+ }
+ sb->scrollbar.minSize = minSize;
+ Ttk_PlaceElement(corePtr->layout, thumb, thumbBox);
+}
+
+/*------------------------------------------------------------------------
+ * +++ Widget commands.
+ */
+
+/* $sb set $first $last --
+ * Set the position of the scrollbar.
+ */
+static int
+ScrollbarSetCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ Scrollbar *scrollbar = recordPtr;
+ Tcl_Obj *firstObj, *lastObj;
+ double first, last;
+
+ if (objc != 4) {
+ Tcl_WrongNumArgs(interp, 2, objv, "first last");
+ return TCL_ERROR;
+ }
+
+ firstObj = objv[2];
+ lastObj = objv[3];
+ if (Tcl_GetDoubleFromObj(interp, firstObj, &first) != TCL_OK
+ || Tcl_GetDoubleFromObj(interp, lastObj, &last) != TCL_OK)
+ return TCL_ERROR;
+
+ /* Range-checks:
+ */
+ if (first < 0.0) {
+ first = 0.0;
+ } else if (first > 1.0) {
+ first = 1.0;
+ }
+
+ if (last < first) {
+ last = first;
+ } else if (last > 1.0) {
+ last = 1.0;
+ }
+
+ /* ASSERT: 0.0 <= first <= last <= 1.0 */
+
+ scrollbar->scrollbar.first = first;
+ scrollbar->scrollbar.last = last;
+ if (first <= 0.0 && last >= 1.0) {
+ scrollbar->core.state |= TTK_STATE_DISABLED;
+ } else {
+ scrollbar->core.state &= ~TTK_STATE_DISABLED;
+ }
+
+ TtkRedisplayWidget(&scrollbar->core);
+
+ return TCL_OK;
+}
+
+/* $sb get --
+ * Returns the last thing passed to 'set'.
+ */
+static int
+ScrollbarGetCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ Scrollbar *scrollbar = recordPtr;
+ Tcl_Obj *result[2];
+
+ if (objc != 2) {
+ Tcl_WrongNumArgs(interp, 2, objv, "");
+ return TCL_ERROR;
+ }
+
+ result[0] = Tcl_NewDoubleObj(scrollbar->scrollbar.first);
+ result[1] = Tcl_NewDoubleObj(scrollbar->scrollbar.last);
+ Tcl_SetObjResult(interp, Tcl_NewListObj(2, result));
+
+ return TCL_OK;
+}
+
+/* $sb delta $dx $dy --
+ * Returns the percentage change corresponding to a mouse movement
+ * of $dx, $dy.
+ */
+static int
+ScrollbarDeltaCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ Scrollbar *sb = recordPtr;
+ double dx, dy;
+ double delta = 0.0;
+
+ if (objc != 4) {
+ Tcl_WrongNumArgs(interp, 2, objv, "dx dy");
+ return TCL_ERROR;
+ }
+
+ if (Tcl_GetDoubleFromObj(interp, objv[2], &dx) != TCL_OK
+ || Tcl_GetDoubleFromObj(interp, objv[3], &dy) != TCL_OK)
+ {
+ return TCL_ERROR;
+ }
+
+ delta = 0.0;
+ if (sb->scrollbar.orient == TTK_ORIENT_VERTICAL) {
+ int size = sb->scrollbar.troughBox.height - sb->scrollbar.minSize;
+ if (size > 0) {
+ delta = (double)dy / (double)size;
+ }
+ } else {
+ int size = sb->scrollbar.troughBox.width - sb->scrollbar.minSize;
+ if (size > 0) {
+ delta = (double)dx / (double)size;
+ }
+ }
+
+ Tcl_SetObjResult(interp, Tcl_NewDoubleObj(delta));
+ return TCL_OK;
+}
+
+/* $sb fraction $x $y --
+ * Returns a real number between 0 and 1 indicating where the
+ * point given by x and y lies in the trough area of the scrollbar.
+ */
+static int
+ScrollbarFractionCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ Scrollbar *sb = recordPtr;
+ Ttk_Box b = sb->scrollbar.troughBox;
+ int minSize = sb->scrollbar.minSize;
+ double x, y;
+ double fraction = 0.0;
+
+ if (objc != 4) {
+ Tcl_WrongNumArgs(interp, 2, objv, "x y");
+ return TCL_ERROR;
+ }
+
+ if (Tcl_GetDoubleFromObj(interp, objv[2], &x) != TCL_OK
+ || Tcl_GetDoubleFromObj(interp, objv[3], &y) != TCL_OK)
+ {
+ return TCL_ERROR;
+ }
+
+ fraction = 0.0;
+ if (sb->scrollbar.orient == TTK_ORIENT_VERTICAL) {
+ if (b.height > minSize) {
+ fraction = (double)(y - b.y) / (double)(b.height - minSize);
+ }
+ } else {
+ if (b.width > minSize) {
+ fraction = (double)(x - b.x) / (double)(b.width - minSize);
+ }
+ }
+
+ Tcl_SetObjResult(interp, Tcl_NewDoubleObj(fraction));
+ return TCL_OK;
+}
+
+static const Ttk_Ensemble ScrollbarCommands[] = {
+ { "configure", TtkWidgetConfigureCommand,0 },
+ { "cget", TtkWidgetCgetCommand,0 },
+ { "delta", ScrollbarDeltaCommand,0 },
+ { "fraction", ScrollbarFractionCommand,0 },
+ { "get", ScrollbarGetCommand,0 },
+ { "identify", TtkWidgetIdentifyCommand,0 },
+ { "instate", TtkWidgetInstateCommand,0 },
+ { "set", ScrollbarSetCommand,0 },
+ { "state", TtkWidgetStateCommand,0 },
+ { 0,0,0 }
+};
+
+/*------------------------------------------------------------------------
+ * +++ Widget specification.
+ */
+static WidgetSpec ScrollbarWidgetSpec =
+{
+ "TScrollbar", /* className */
+ sizeof(Scrollbar), /* recordSize */
+ ScrollbarOptionSpecs, /* optionSpecs */
+ ScrollbarCommands, /* subcommands */
+ ScrollbarInitialize, /* initializeProc */
+ TtkNullCleanup, /* cleanupProc */
+ TtkCoreConfigure, /* configureProc */
+ TtkNullPostConfigure, /* postConfigureProc */
+ ScrollbarGetLayout, /* getLayoutProc */
+ TtkWidgetSize, /* sizeProc */
+ ScrollbarDoLayout, /* layoutProc */
+ TtkWidgetDisplay /* displayProc */
+};
+
+TTK_BEGIN_LAYOUT(VerticalScrollbarLayout)
+ TTK_GROUP("Vertical.Scrollbar.trough", TTK_FILL_Y,
+ TTK_NODE("Vertical.Scrollbar.uparrow", TTK_PACK_TOP)
+ TTK_NODE("Vertical.Scrollbar.downarrow", TTK_PACK_BOTTOM)
+ TTK_NODE(
+ "Vertical.Scrollbar.thumb", TTK_PACK_TOP|TTK_EXPAND|TTK_FILL_BOTH))
+TTK_END_LAYOUT
+
+TTK_BEGIN_LAYOUT(HorizontalScrollbarLayout)
+ TTK_GROUP("Horizontal.Scrollbar.trough", TTK_FILL_X,
+ TTK_NODE("Horizontal.Scrollbar.leftarrow", TTK_PACK_LEFT)
+ TTK_NODE("Horizontal.Scrollbar.rightarrow", TTK_PACK_RIGHT)
+ TTK_NODE(
+ "Horizontal.Scrollbar.thumb", TTK_PACK_LEFT|TTK_EXPAND|TTK_FILL_BOTH))
+TTK_END_LAYOUT
+
+/*------------------------------------------------------------------------
+ * +++ Initialization.
+ */
+
+MODULE_SCOPE
+void TtkScrollbar_Init(Tcl_Interp *interp)
+{
+ Ttk_Theme theme = Ttk_GetDefaultTheme(interp);
+
+ Ttk_RegisterLayout(theme,"Vertical.TScrollbar",VerticalScrollbarLayout);
+ Ttk_RegisterLayout(theme,"Horizontal.TScrollbar",HorizontalScrollbarLayout);
+
+ RegisterWidget(interp, "ttk::scrollbar", &ScrollbarWidgetSpec);
+}
+
+/*EOF*/
diff --git a/generic/ttk/ttkSeparator.c b/generic/ttk/ttkSeparator.c
new file mode 100644
index 0000000..b52e6f4
--- /dev/null
+++ b/generic/ttk/ttkSeparator.c
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2004, Joe English
+ *
+ * ttk::separator and ttk::sizegrip widgets.
+ */
+
+#include <tk.h>
+
+#include "ttkTheme.h"
+#include "ttkWidget.h"
+
+/* +++ Separator widget record:
+ */
+typedef struct
+{
+ Tcl_Obj *orientObj;
+ int orient;
+} SeparatorPart;
+
+typedef struct
+{
+ WidgetCore core;
+ SeparatorPart separator;
+} Separator;
+
+static Tk_OptionSpec SeparatorOptionSpecs[] = {
+ {TK_OPTION_STRING_TABLE, "-orient", "orient", "Orient", "horizontal",
+ Tk_Offset(Separator,separator.orientObj),
+ Tk_Offset(Separator,separator.orient),
+ 0,(ClientData)ttkOrientStrings,STYLE_CHANGED },
+
+ WIDGET_TAKEFOCUS_FALSE,
+ WIDGET_INHERIT_OPTIONS(ttkCoreOptionSpecs)
+};
+
+/*
+ * GetLayout hook --
+ * Choose layout based on -orient option.
+ */
+static Ttk_Layout SeparatorGetLayout(
+ Tcl_Interp *interp, Ttk_Theme theme, void *recordPtr)
+{
+ Separator *sep = recordPtr;
+ return TtkWidgetGetOrientedLayout(
+ interp, theme, recordPtr, sep->separator.orientObj);
+}
+
+/*
+ * Widget commands:
+ */
+static const Ttk_Ensemble SeparatorCommands[] = {
+ { "configure", TtkWidgetConfigureCommand,0 },
+ { "cget", TtkWidgetCgetCommand,0 },
+ { "identify", TtkWidgetIdentifyCommand,0 },
+ { "instate", TtkWidgetInstateCommand,0 },
+ { "state", TtkWidgetStateCommand,0 },
+ { 0,0,0 }
+};
+
+/*
+ * Widget specification:
+ */
+static WidgetSpec SeparatorWidgetSpec =
+{
+ "TSeparator", /* className */
+ sizeof(Separator), /* recordSize */
+ SeparatorOptionSpecs, /* optionSpecs */
+ SeparatorCommands, /* subcommands */
+ TtkNullInitialize, /* initializeProc */
+ TtkNullCleanup, /* cleanupProc */
+ TtkCoreConfigure, /* configureProc */
+ TtkNullPostConfigure, /* postConfigureProc */
+ SeparatorGetLayout, /* getLayoutProc */
+ TtkWidgetSize, /* sizeProc */
+ TtkWidgetDoLayout, /* layoutProc */
+ TtkWidgetDisplay /* displayProc */
+};
+
+TTK_BEGIN_LAYOUT(SeparatorLayout)
+ TTK_NODE("Separator.separator", TTK_FILL_BOTH)
+TTK_END_LAYOUT
+
+/* +++ Sizegrip widget:
+ * Has no options or methods other than the standard ones.
+ */
+
+static Tk_OptionSpec SizegripOptionSpecs[] = {
+ WIDGET_TAKEFOCUS_FALSE,
+ WIDGET_INHERIT_OPTIONS(ttkCoreOptionSpecs)
+};
+
+static const Ttk_Ensemble SizegripCommands[] = {
+ { "configure", TtkWidgetConfigureCommand,0 },
+ { "cget", TtkWidgetCgetCommand,0 },
+ { "identify", TtkWidgetIdentifyCommand,0 },
+ { "instate", TtkWidgetInstateCommand,0 },
+ { "state", TtkWidgetStateCommand,0 },
+ { 0,0,0 }
+};
+
+static WidgetSpec SizegripWidgetSpec =
+{
+ "TSizegrip", /* className */
+ sizeof(WidgetCore), /* recordSize */
+ SizegripOptionSpecs, /* optionSpecs */
+ SizegripCommands, /* subcommands */
+ TtkNullInitialize, /* initializeProc */
+ TtkNullCleanup, /* cleanupProc */
+ TtkCoreConfigure, /* configureProc */
+ TtkNullPostConfigure, /* postConfigureProc */
+ TtkWidgetGetLayout, /* getLayoutProc */
+ TtkWidgetSize, /* sizeProc */
+ TtkWidgetDoLayout, /* layoutProc */
+ TtkWidgetDisplay /* displayProc */
+};
+
+TTK_BEGIN_LAYOUT(SizegripLayout)
+ TTK_NODE("Sizegrip.sizegrip", TTK_PACK_BOTTOM|TTK_STICK_S|TTK_STICK_E)
+TTK_END_LAYOUT
+
+/* +++ Initialization:
+ */
+
+MODULE_SCOPE
+void TtkSeparator_Init(Tcl_Interp *interp)
+{
+ Ttk_Theme theme = Ttk_GetDefaultTheme(interp);
+
+ Ttk_RegisterLayout(theme, "TSeparator", SeparatorLayout);
+ Ttk_RegisterLayout(theme, "TSizegrip", SizegripLayout);
+
+ RegisterWidget(interp, "ttk::separator", &SeparatorWidgetSpec);
+ RegisterWidget(interp, "ttk::sizegrip", &SizegripWidgetSpec);
+}
+
+/*EOF*/
diff --git a/generic/ttk/ttkSquare.c b/generic/ttk/ttkSquare.c
new file mode 100644
index 0000000..d002f2f
--- /dev/null
+++ b/generic/ttk/ttkSquare.c
@@ -0,0 +1,301 @@
+/* square.c - Copyright (C) 2004 Pat Thoyts <patthoyts@users.sourceforge.net>
+ *
+ * Minimal sample ttk widget.
+ */
+
+#include <tk.h>
+#include "ttkTheme.h"
+#include "ttkWidget.h"
+
+#if defined(TTK_SQUARE_WIDGET) || 1
+
+#ifndef DEFAULT_BORDERWIDTH
+#define DEFAULT_BORDERWIDTH "2"
+#endif
+
+/*
+ * First, we setup the widget record. The Ttk package provides a structure
+ * that contains standard widget data so it is only necessary to define
+ * a structure that holds the data required for our widget. We do this by
+ * defining a widget part and then specifying the widget record as the
+ * concatenation of the two structures.
+ */
+
+typedef struct
+{
+ Tcl_Obj *widthObj;
+ Tcl_Obj *heightObj;
+ Tcl_Obj *reliefObj;
+ Tcl_Obj *borderWidthObj;
+ Tcl_Obj *foregroundObj;
+ Tcl_Obj *paddingObj;
+ Tcl_Obj *anchorObj;
+} SquarePart;
+
+typedef struct
+{
+ WidgetCore core;
+ SquarePart square;
+} Square;
+
+/*
+ * Widget options.
+ *
+ * This structure is the same as the option specification structure used
+ * for Tk widgets. For each option we provide the type, name and options
+ * database name and class name and the position in the structure and
+ * default values. At the bottom we bring in the standard widget option
+ * defined for all widgets.
+ */
+
+static Tk_OptionSpec SquareOptionSpecs[] =
+{
+ {TK_OPTION_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
+ DEFAULT_BORDERWIDTH, Tk_Offset(Square,square.borderWidthObj), -1,
+ 0,0,GEOMETRY_CHANGED },
+ {TK_OPTION_BORDER, "-foreground", "foreground", "Foreground",
+ DEFAULT_BACKGROUND, Tk_Offset(Square,square.foregroundObj),
+ -1, 0, 0, 0},
+
+ {TK_OPTION_PIXELS, "-width", "width", "Width",
+ "50", Tk_Offset(Square,square.widthObj), -1, 0, 0,
+ GEOMETRY_CHANGED},
+ {TK_OPTION_PIXELS, "-height", "height", "Height",
+ "50", Tk_Offset(Square,square.heightObj), -1, 0, 0,
+ GEOMETRY_CHANGED},
+
+ {TK_OPTION_STRING, "-padding", "padding", "Pad", NULL,
+ Tk_Offset(Square,square.paddingObj), -1,
+ TK_OPTION_NULL_OK,0,GEOMETRY_CHANGED },
+
+ {TK_OPTION_RELIEF, "-relief", "relief", "Relief",
+ NULL, Tk_Offset(Square,square.reliefObj), -1, TK_OPTION_NULL_OK, 0, 0},
+
+ {TK_OPTION_ANCHOR, "-anchor", "anchor", "Anchor",
+ NULL, Tk_Offset(Square,square.anchorObj), -1, TK_OPTION_NULL_OK, 0, 0},
+
+ WIDGET_TAKEFOCUS_TRUE,
+ WIDGET_INHERIT_OPTIONS(ttkCoreOptionSpecs)
+};
+
+/*
+ * Almost all of the widget functionality is handled by the default Ttk
+ * widget code and the contained element. The one thing that we must handle
+ * is the -anchor option which positions the square element within the parcel
+ * of space available for the widget.
+ * To do this we must find out the layout preferences for the square
+ * element and adjust its position within our region.
+ *
+ * Note that if we do not have a "square" elememt then just the default
+ * layout will be done. So if someone places a label element into the
+ * widget layout it will still be handled but the -anchor option will be
+ * passed onto the label element instead of handled here.
+ */
+
+static void
+SquareDoLayout(void *clientData)
+{
+ WidgetCore *corePtr = (WidgetCore *)clientData;
+ Ttk_Box winBox;
+ Ttk_Element squareNode;
+
+ squareNode = Ttk_FindElement(corePtr->layout, "square");
+ winBox = Ttk_WinBox(corePtr->tkwin);
+ Ttk_PlaceLayout(corePtr->layout, corePtr->state, winBox);
+
+ /*
+ * Adjust the position of the square element within the widget according
+ * to the -anchor option.
+ */
+
+ if (squareNode) {
+ Square *squarePtr = clientData;
+ Tk_Anchor anchor = TK_ANCHOR_CENTER;
+ Ttk_Box b;
+
+ b = Ttk_ElementParcel(squareNode);
+ if (squarePtr->square.anchorObj != NULL)
+ Tk_GetAnchorFromObj(NULL, squarePtr->square.anchorObj, &anchor);
+ b = Ttk_AnchorBox(winBox, b.width, b.height, anchor);
+
+ Ttk_PlaceElement(corePtr->layout, squareNode, b);
+ }
+}
+
+/*
+ * Widget commands. A widget is impelemented as an ensemble and the
+ * subcommands are listed here. Ttk provides default implementations
+ * that are sufficient for our needs.
+ */
+
+static const Ttk_Ensemble SquareCommands[] = {
+ { "configure", TtkWidgetConfigureCommand,0 },
+ { "cget", TtkWidgetCgetCommand,0 },
+ { "identify", TtkWidgetIdentifyCommand,0 },
+ { "instate", TtkWidgetInstateCommand,0 },
+ { "state", TtkWidgetStateCommand,0 },
+ { 0,0,0 }
+};
+
+/*
+ * The Widget specification structure holds all the implementation
+ * information about this widget and this is what must be registered
+ * with Tk in the package initialization code (see bottom).
+ */
+
+static WidgetSpec SquareWidgetSpec =
+{
+ "TSquare", /* className */
+ sizeof(Square), /* recordSize */
+ SquareOptionSpecs, /* optionSpecs */
+ SquareCommands, /* subcommands */
+ TtkNullInitialize, /* initializeProc */
+ TtkNullCleanup, /* cleanupProc */
+ TtkCoreConfigure, /* configureProc */
+ TtkNullPostConfigure, /* postConfigureProc */
+ TtkWidgetGetLayout, /* getLayoutProc */
+ TtkWidgetSize, /* sizeProc */
+ SquareDoLayout, /* layoutProc */
+ TtkWidgetDisplay /* displayProc */
+};
+
+/* ----------------------------------------------------------------------
+ * Square element
+ *
+ * In this section we demonstrate what is required to create a new themed
+ * element.
+ */
+
+typedef struct
+{
+ Tcl_Obj *borderObj;
+ Tcl_Obj *foregroundObj;
+ Tcl_Obj *borderWidthObj;
+ Tcl_Obj *reliefObj;
+ Tcl_Obj *widthObj;
+ Tcl_Obj *heightObj;
+} SquareElement;
+
+static Ttk_ElementOptionSpec SquareElementOptions[] =
+{
+ { "-background", TK_OPTION_BORDER, Tk_Offset(SquareElement,borderObj),
+ DEFAULT_BACKGROUND },
+ { "-foreground", TK_OPTION_BORDER, Tk_Offset(SquareElement,foregroundObj),
+ DEFAULT_BACKGROUND },
+ { "-borderwidth", TK_OPTION_PIXELS, Tk_Offset(SquareElement,borderWidthObj),
+ DEFAULT_BORDERWIDTH },
+ { "-relief", TK_OPTION_RELIEF, Tk_Offset(SquareElement,reliefObj),
+ "raised" },
+ { "-width", TK_OPTION_PIXELS, Tk_Offset(SquareElement,widthObj), "20"},
+ { "-height", TK_OPTION_PIXELS, Tk_Offset(SquareElement,heightObj), "20"},
+ { NULL, 0, 0, NULL }
+};
+
+/*
+ * The element geometry function is called when the layout code wishes to
+ * find out how big this element wants to be. We must return our preferred
+ * size and padding information
+ */
+
+static void SquareElementSize(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+{
+ SquareElement *square = elementRecord;
+ int borderWidth = 0;
+
+ Tcl_GetIntFromObj(NULL, square->borderWidthObj, &borderWidth);
+ *paddingPtr = Ttk_UniformPadding((short)borderWidth);
+ Tk_GetPixelsFromObj(NULL, tkwin, square->widthObj, widthPtr);
+ Tk_GetPixelsFromObj(NULL, tkwin, square->heightObj, heightPtr);
+}
+
+/*
+ * Draw the element in the box provided.
+ */
+
+static void SquareElementDraw(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ Drawable d, Ttk_Box b, unsigned int state)
+{
+ SquareElement *square = elementRecord;
+ Tk_3DBorder foreground = NULL;
+ int borderWidth = 1, relief = TK_RELIEF_FLAT;
+
+ foreground = Tk_Get3DBorderFromObj(tkwin, square->foregroundObj);
+ Tcl_GetIntFromObj(NULL, square->borderWidthObj, &borderWidth);
+ Tk_GetReliefFromObj(NULL, square->reliefObj, &relief);
+
+ Tk_Fill3DRectangle(tkwin, d, foreground,
+ b.x, b.y, b.width, b.height, borderWidth, relief);
+}
+
+static Ttk_ElementSpec SquareElementSpec =
+{
+ TK_STYLE_VERSION_2,
+ sizeof(SquareElement),
+ SquareElementOptions,
+ SquareElementSize,
+ SquareElementDraw
+};
+
+/* ----------------------------------------------------------------------
+ *
+ * Layout section.
+ *
+ * Every widget class needs a layout style that specifies which elements
+ * are part of the widget and how they should be placed. The element layout
+ * engine is similar to the Tk pack geometry manager. Read the documentation
+ * for the details. In this example we just need to have the square element
+ * that has been defined for this widget placed on a background. We will
+ * also need some padding to keep it away from the edges.
+ */
+
+TTK_BEGIN_LAYOUT(SquareLayout)
+ TTK_NODE("Square.background", TTK_FILL_BOTH)
+ TTK_GROUP("Square.padding", TTK_FILL_BOTH,
+ TTK_NODE("Square.square", 0))
+TTK_END_LAYOUT
+
+/* ----------------------------------------------------------------------
+ *
+ * Widget initialization.
+ *
+ * This file defines a new element and a new widget. We need to register
+ * the element with the themes that will need it. In this case we will
+ * register with the default theme that is the root of the theme inheritance
+ * tree. This means all themes will find this element.
+ * We then need to register the widget class style. This is the layout
+ * specification. If a different theme requires an alternative layout, we
+ * could register that here. For instance, in some themes the scrollbars have
+ * one uparrow, in other themes there are two uparrow elements.
+ * Finally we register the widget itself. This step creates a tcl command so
+ * that we can actually create an instance of this class. The widget is
+ * linked to a particular style by the widget class name. This is important
+ * to realise as the programmer may change the classname when creating a
+ * new instance. If this is done, a new layout will need to be created (which
+ * can be done at script level). Some widgets may require particular elements
+ * to be present but we try to avoid this where possible. In this widget's C
+ * code, no reference is made to any particular elements. The programmer is
+ * free to specify a new style using completely different elements.
+ */
+
+/* public */ MODULE_SCOPE int
+TtkSquareWidget_Init(Tcl_Interp *interp)
+{
+ Ttk_Theme theme = Ttk_GetDefaultTheme(interp);
+
+ /* register the new elements for this theme engine */
+ Ttk_RegisterElement(interp, theme, "square", &SquareElementSpec, NULL);
+
+ /* register the layout for this theme */
+ Ttk_RegisterLayout(theme, "TSquare", SquareLayout);
+
+ /* register the widget */
+ RegisterWidget(interp, "ttk::square", &SquareWidgetSpec);
+
+ return TCL_OK;
+}
+
+#endif /* TTK_SQUARE_WIDGET */
+
diff --git a/generic/ttk/ttkState.c b/generic/ttk/ttkState.c
new file mode 100644
index 0000000..a71ae21
--- /dev/null
+++ b/generic/ttk/ttkState.c
@@ -0,0 +1,273 @@
+/*
+ * Tk widget state utilities.
+ *
+ * Copyright (c) 2003 Joe English. Freely redistributable.
+ *
+ */
+
+#include <string.h>
+
+#include <tk.h>
+#include "ttkTheme.h"
+
+/*
+ * Table of state names. Must be kept in sync with TTK_STATE_*
+ * #defines in ttkTheme.h.
+ */
+static const char *const stateNames[] =
+{
+ "active", /* Mouse cursor is over widget or element */
+ "disabled", /* Widget is disabled */
+ "focus", /* Widget has keyboard focus */
+ "pressed", /* Pressed or "armed" */
+ "selected", /* "on", "true", "current", etc. */
+ "background", /* Top-level window lost focus (Mac,Win "inactive") */
+ "alternate", /* Widget-specific alternate display style */
+ "invalid", /* Bad value */
+ "readonly", /* Editing/modification disabled */
+ "hover", /* Mouse cursor is over widget */
+ "reserved1", /* Reserved for future extension */
+ "reserved2", /* Reserved for future extension */
+ "reserved3", /* Reserved for future extension */
+ "user3", /* User-definable state */
+ "user2", /* User-definable state */
+ "user1", /* User-definable state */
+ NULL
+};
+
+/*------------------------------------------------------------------------
+ * +++ StateSpec object type:
+ *
+ * The string representation consists of a list of state names,
+ * each optionally prefixed by an exclamation point (!).
+ *
+ * The internal representation uses the upper half of the longValue
+ * to store the on bits and the lower half to store the off bits.
+ * If we ever get more than 16 states, this will need to be reconsidered...
+ */
+
+static int StateSpecSetFromAny(Tcl_Interp *interp, Tcl_Obj *obj);
+/* static void StateSpecFreeIntRep(Tcl_Obj *); */
+#define StateSpecFreeIntRep 0 /* not needed */
+static void StateSpecDupIntRep(Tcl_Obj *, Tcl_Obj *);
+static void StateSpecUpdateString(Tcl_Obj *);
+
+static
+struct Tcl_ObjType StateSpecObjType =
+{
+ "StateSpec",
+ StateSpecFreeIntRep,
+ StateSpecDupIntRep,
+ StateSpecUpdateString,
+ StateSpecSetFromAny
+};
+
+static void StateSpecDupIntRep(Tcl_Obj *srcPtr, Tcl_Obj *copyPtr)
+{
+ copyPtr->internalRep.longValue = srcPtr->internalRep.longValue;
+ copyPtr->typePtr = &StateSpecObjType;
+}
+
+static int StateSpecSetFromAny(Tcl_Interp *interp, Tcl_Obj *objPtr)
+{
+ int status;
+ int objc;
+ Tcl_Obj **objv;
+ int i;
+ unsigned int onbits = 0, offbits = 0;
+
+ status = Tcl_ListObjGetElements(interp, objPtr, &objc, &objv);
+ if (status != TCL_OK)
+ return status;
+
+ for (i = 0; i < objc; ++i) {
+ const char *stateName = Tcl_GetString(objv[i]);
+ int on, j;
+
+ if (*stateName == '!') {
+ ++stateName;
+ on = 0;
+ } else {
+ on = 1;
+ }
+
+ for (j = 0; stateNames[j] != 0; ++j) {
+ if (strcmp(stateName, stateNames[j]) == 0)
+ break;
+ }
+
+ if (stateNames[j] == 0) {
+ if (interp) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "Invalid state name ", stateName,NULL);
+ }
+ return TCL_ERROR;
+ }
+
+ if (on) {
+ onbits |= (1<<j);
+ } else {
+ offbits |= (1<<j);
+ }
+ }
+
+ /* Invalidate old intrep:
+ */
+ if (objPtr->typePtr && objPtr->typePtr->freeIntRepProc) {
+ objPtr->typePtr->freeIntRepProc(objPtr);
+ }
+
+ objPtr->typePtr = &StateSpecObjType;
+ objPtr->internalRep.longValue = (onbits << 16) | offbits;
+
+ return TCL_OK;
+}
+
+static void StateSpecUpdateString(Tcl_Obj *objPtr)
+{
+ unsigned int onbits = (objPtr->internalRep.longValue & 0xFFFF0000) >> 16;
+ unsigned int offbits = objPtr->internalRep.longValue & 0x0000FFFF;
+ unsigned int mask = onbits | offbits;
+ Tcl_DString result;
+ int i, len;
+
+ Tcl_DStringInit(&result);
+
+ for (i=0; stateNames[i] != NULL; ++i) {
+ if (mask & (1<<i)) {
+ if (offbits & (1<<i))
+ Tcl_DStringAppend(&result, "!", 1);
+ Tcl_DStringAppend(&result, stateNames[i], -1);
+ Tcl_DStringAppend(&result, " ", 1);
+ }
+ }
+
+ len = Tcl_DStringLength(&result);
+ if (len) {
+ /* 'len' includes extra trailing ' ' */
+ objPtr->bytes = Tcl_Alloc((unsigned)len);
+ objPtr->length = len-1;
+ strncpy(objPtr->bytes, Tcl_DStringValue(&result), (size_t)len-1);
+ objPtr->bytes[len-1] = '\0';
+ } else {
+ /* empty string */
+ objPtr->length = 0;
+ objPtr->bytes = Tcl_Alloc(1);
+ *objPtr->bytes = '\0';
+ }
+
+ Tcl_DStringFree(&result);
+}
+
+Tcl_Obj *Ttk_NewStateSpecObj(unsigned int onbits, unsigned int offbits)
+{
+ Tcl_Obj *objPtr = Tcl_NewObj();
+
+ Tcl_InvalidateStringRep(objPtr);
+ objPtr->typePtr = &StateSpecObjType;
+ objPtr->internalRep.longValue = (onbits << 16) | offbits;
+
+ return objPtr;
+}
+
+int Ttk_GetStateSpecFromObj(
+ Tcl_Interp *interp,
+ Tcl_Obj *objPtr,
+ Ttk_StateSpec *spec)
+{
+ if (objPtr->typePtr != &StateSpecObjType) {
+ int status = StateSpecSetFromAny(interp, objPtr);
+ if (status != TCL_OK)
+ return status;
+ }
+
+ spec->onbits = (objPtr->internalRep.longValue & 0xFFFF0000) >> 16;
+ spec->offbits = objPtr->internalRep.longValue & 0x0000FFFF;
+ return TCL_OK;
+}
+
+
+/*
+ * Tk_StateMapLookup --
+ *
+ * A state map is a paired list of StateSpec / value pairs.
+ * Returns the value corresponding to the first matching state
+ * specification, or NULL if not found or an error occurs.
+ */
+Tcl_Obj *Ttk_StateMapLookup(
+ Tcl_Interp *interp, /* Where to leave error messages; may be NULL */
+ Ttk_StateMap map, /* State map */
+ Ttk_State state) /* State to look up */
+{
+ Tcl_Obj **specs;
+ int nSpecs;
+ int j, status;
+
+ status = Tcl_ListObjGetElements(interp, map, &nSpecs, &specs);
+ if (status != TCL_OK)
+ return NULL;
+
+ for (j = 0; j < nSpecs; j += 2) {
+ Ttk_StateSpec spec;
+ status = Ttk_GetStateSpecFromObj(interp, specs[j], &spec);
+ if (status != TCL_OK)
+ return NULL;
+ if (Ttk_StateMatches(state, &spec))
+ return specs[j+1];
+ }
+ if (interp) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "No match in state map", NULL);
+ }
+ return NULL;
+}
+
+/* Ttk_GetStateMapFromObj --
+ * Returns a Ttk_StateMap from a Tcl_Obj*.
+ * Since a Ttk_StateMap is just a specially-formatted Tcl_Obj,
+ * this basically just checks for errors.
+ */
+Ttk_StateMap Ttk_GetStateMapFromObj(
+ Tcl_Interp *interp, /* Where to leave error messages; may be NULL */
+ Tcl_Obj *mapObj) /* State map */
+{
+ Tcl_Obj **specs;
+ int nSpecs;
+ int j, status;
+
+ status = Tcl_ListObjGetElements(interp, mapObj, &nSpecs, &specs);
+ if (status != TCL_OK)
+ return NULL;
+
+ if (nSpecs % 2 != 0) {
+ if (interp)
+ Tcl_SetResult(interp,
+ "State map must have an even number of elements",
+ TCL_STATIC);
+ return 0;
+ }
+
+ for (j = 0; j < nSpecs; j += 2) {
+ Ttk_StateSpec spec;
+ if (Ttk_GetStateSpecFromObj(interp, specs[j], &spec) != TCL_OK)
+ return NULL;
+ }
+
+ return mapObj;
+}
+
+/*
+ * Ttk_StateTableLooup --
+ * Look up an index from a statically allocated state table.
+ */
+int Ttk_StateTableLookup(Ttk_StateTable *map, unsigned int state)
+{
+ while ((state & map->onBits) != map->onBits
+ || (~state & map->offBits) != map->offBits)
+ {
+ ++map;
+ }
+ return map->index;
+}
+
+/*EOF*/
diff --git a/generic/ttk/ttkStubInit.c b/generic/ttk/ttkStubInit.c
new file mode 100644
index 0000000..87b33dc
--- /dev/null
+++ b/generic/ttk/ttkStubInit.c
@@ -0,0 +1,61 @@
+/*
+ * This file is (mostly) automatically generated from ttk.decls.
+ * It is compiled and linked in with the ttk package proper.
+ */
+
+#include "tk.h"
+#include "ttkTheme.h"
+
+MODULE_SCOPE const TtkStubs ttkStubs;
+
+/* !BEGIN!: Do not edit below this line. */
+
+const TtkStubs ttkStubs = {
+ TCL_STUB_MAGIC,
+ TTK_STUBS_EPOCH,
+ TTK_STUBS_REVISION,
+ 0,
+ Ttk_GetTheme, /* 0 */
+ Ttk_GetDefaultTheme, /* 1 */
+ Ttk_GetCurrentTheme, /* 2 */
+ Ttk_CreateTheme, /* 3 */
+ Ttk_RegisterCleanup, /* 4 */
+ Ttk_RegisterElementSpec, /* 5 */
+ Ttk_RegisterElement, /* 6 */
+ Ttk_RegisterElementFactory, /* 7 */
+ Ttk_RegisterLayout, /* 8 */
+ 0, /* 9 */
+ Ttk_GetStateSpecFromObj, /* 10 */
+ Ttk_NewStateSpecObj, /* 11 */
+ Ttk_GetStateMapFromObj, /* 12 */
+ Ttk_StateMapLookup, /* 13 */
+ Ttk_StateTableLookup, /* 14 */
+ 0, /* 15 */
+ 0, /* 16 */
+ 0, /* 17 */
+ 0, /* 18 */
+ 0, /* 19 */
+ Ttk_GetPaddingFromObj, /* 20 */
+ Ttk_GetBorderFromObj, /* 21 */
+ Ttk_GetStickyFromObj, /* 22 */
+ Ttk_MakePadding, /* 23 */
+ Ttk_UniformPadding, /* 24 */
+ Ttk_AddPadding, /* 25 */
+ Ttk_RelievePadding, /* 26 */
+ Ttk_MakeBox, /* 27 */
+ Ttk_BoxContains, /* 28 */
+ Ttk_PackBox, /* 29 */
+ Ttk_StickBox, /* 30 */
+ Ttk_AnchorBox, /* 31 */
+ Ttk_PadBox, /* 32 */
+ Ttk_ExpandBox, /* 33 */
+ Ttk_PlaceBox, /* 34 */
+ Ttk_NewBoxObj, /* 35 */
+ 0, /* 36 */
+ 0, /* 37 */
+ 0, /* 38 */
+ 0, /* 39 */
+ Ttk_GetOrientFromObj, /* 40 */
+};
+
+/* !END!: Do not edit above this line. */
diff --git a/generic/ttk/ttkStubLib.c b/generic/ttk/ttkStubLib.c
new file mode 100644
index 0000000..2c07b9d
--- /dev/null
+++ b/generic/ttk/ttkStubLib.c
@@ -0,0 +1,74 @@
+/*
+ * We need to ensure that we use the tcl stub macros so that this file
+ * contains no references to any of the tcl stub functions.
+ */
+
+#undef USE_TCL_STUBS
+#define USE_TCL_STUBS
+
+#include "tk.h"
+
+#define USE_TTK_STUBS 1
+#include "ttkTheme.h"
+
+MODULE_SCOPE const TtkStubs *ttkStubsPtr;
+const TtkStubs *ttkStubsPtr = NULL;
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TtkInitializeStubs --
+ * Load the Ttk package, initialize stub table pointer.
+ * Do not call this function directly, use Ttk_InitStubs() macro instead.
+ *
+ * Results:
+ * The actual version of the package that satisfies the request, or
+ * NULL to indicate that an error occurred.
+ *
+ * Side effects:
+ * Sets the stub table pointer.
+ *
+ */
+MODULE_SCOPE const char *
+TtkInitializeStubs(
+ Tcl_Interp *interp, const char *version, int epoch, int revision)
+{
+ int exact = 0;
+ const char *packageName = "Ttk";
+ const char *errMsg = NULL;
+ ClientData pkgClientData = NULL;
+ const char *actualVersion = Tcl_PkgRequireEx(
+ interp, packageName, version, exact, &pkgClientData);
+ const TtkStubs *stubsPtr = pkgClientData;
+
+ if (!actualVersion) {
+ return NULL;
+ }
+
+ if (!stubsPtr) {
+ errMsg = "missing stub table pointer";
+ goto error;
+ }
+ if (stubsPtr->epoch != epoch) {
+ errMsg = "epoch number mismatch";
+ goto error;
+ }
+ if (stubsPtr->revision < revision) {
+ errMsg = "require later revision";
+ goto error;
+ }
+
+ ttkStubsPtr = stubsPtr;
+ return actualVersion;
+
+error:
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp,
+ "Error loading ", packageName, " package",
+ " (requested version '", version,
+ "', loaded version '", actualVersion, "'): ",
+ errMsg,
+ NULL);
+ return NULL;
+}
+
diff --git a/generic/ttk/ttkTagSet.c b/generic/ttk/ttkTagSet.c
new file mode 100644
index 0000000..9f2a87b
--- /dev/null
+++ b/generic/ttk/ttkTagSet.c
@@ -0,0 +1,306 @@
+/*
+ * Tag tables. 3/4-baked, work in progress.
+ *
+ * Copyright (C) 2005, Joe English. Freely redistributable.
+ */
+
+#include <string.h> /* for memset() */
+#include <tcl.h>
+#include <tk.h>
+
+#include "ttkTheme.h"
+#include "ttkWidget.h"
+
+/*------------------------------------------------------------------------
+ * +++ Internal data structures.
+ */
+struct TtkTag {
+ int priority; /* 1=>highest */
+ const char *tagName; /* Back-pointer to hash table entry */
+ void *tagRecord; /* User data */
+};
+
+struct TtkTagTable {
+ Tk_Window tkwin; /* owner window */
+ Tk_OptionSpec *optionSpecs; /* ... */
+ Tk_OptionTable optionTable; /* ... */
+ int recordSize; /* size of tag record */
+ int nTags; /* #tags defined so far */
+ Tcl_HashTable tags; /* defined tags */
+};
+
+/*------------------------------------------------------------------------
+ * +++ Tags.
+ */
+static Ttk_Tag NewTag(Ttk_TagTable tagTable, const char *tagName)
+{
+ Ttk_Tag tag = (Ttk_Tag)ckalloc(sizeof(*tag));
+ tag->tagRecord = ckalloc(tagTable->recordSize);
+ memset(tag->tagRecord, 0, tagTable->recordSize);
+ /* Don't need Tk_InitOptions() here, all defaults should be NULL. */
+ tag->priority = ++tagTable->nTags;
+ tag->tagName = tagName;
+ return tag;
+}
+
+static void DeleteTag(Ttk_TagTable tagTable, Ttk_Tag tag)
+{
+ Tk_FreeConfigOptions(tag->tagRecord,tagTable->optionTable,tagTable->tkwin);
+ ckfree(tag->tagRecord);
+ ckfree((void*)tag);
+}
+
+/*------------------------------------------------------------------------
+ * +++ Tag tables.
+ */
+
+Ttk_TagTable Ttk_CreateTagTable(
+ Tcl_Interp *interp, Tk_Window tkwin,
+ Tk_OptionSpec optionSpecs[], int recordSize)
+{
+ Ttk_TagTable tagTable = (Ttk_TagTable)ckalloc(sizeof(*tagTable));
+ tagTable->tkwin = tkwin;
+ tagTable->optionSpecs = optionSpecs;
+ tagTable->optionTable = Tk_CreateOptionTable(interp, optionSpecs);
+ tagTable->recordSize = recordSize;
+ tagTable->nTags = 0;
+ Tcl_InitHashTable(&tagTable->tags, TCL_STRING_KEYS);
+ return tagTable;
+}
+
+void Ttk_DeleteTagTable(Ttk_TagTable tagTable)
+{
+ Tcl_HashSearch search;
+ Tcl_HashEntry *entryPtr;
+
+ entryPtr = Tcl_FirstHashEntry(&tagTable->tags, &search);
+ while (entryPtr != NULL) {
+ DeleteTag(tagTable, Tcl_GetHashValue(entryPtr));
+ entryPtr = Tcl_NextHashEntry(&search);
+ }
+
+ Tcl_DeleteHashTable(&tagTable->tags);
+ ckfree((void*)tagTable);
+}
+
+Ttk_Tag Ttk_GetTag(Ttk_TagTable tagTable, const char *tagName)
+{
+ int isNew = 0;
+ Tcl_HashEntry *entryPtr = Tcl_CreateHashEntry(
+ &tagTable->tags, tagName, &isNew);
+
+ if (isNew) {
+ tagName = Tcl_GetHashKey(&tagTable->tags, entryPtr);
+ Tcl_SetHashValue(entryPtr, NewTag(tagTable,tagName));
+ }
+ return Tcl_GetHashValue(entryPtr);
+}
+
+Ttk_Tag Ttk_GetTagFromObj(Ttk_TagTable tagTable, Tcl_Obj *objPtr)
+{
+ return Ttk_GetTag(tagTable, Tcl_GetString(objPtr));
+}
+
+/*------------------------------------------------------------------------
+ * +++ Tag sets.
+ */
+
+/* Ttk_GetTagSetFromObj --
+ * Extract an array of pointers to Ttk_Tags from a Tcl_Obj.
+ * objPtr may be NULL, in which case a new empty tag set is returned.
+ *
+ * Returns NULL and leaves an error message in interp->result on error.
+ *
+ * Non-NULL results must be passed to Ttk_FreeTagSet().
+ */
+Ttk_TagSet Ttk_GetTagSetFromObj(
+ Tcl_Interp *interp, Ttk_TagTable tagTable, Tcl_Obj *objPtr)
+{
+ Ttk_TagSet tagset = (Ttk_TagSet)(ckalloc(sizeof *tagset));
+ Tcl_Obj **objv;
+ int i, objc;
+
+ if (objPtr == NULL) {
+ tagset->tags = NULL;
+ tagset->nTags = 0;
+ return tagset;
+ }
+
+ if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) {
+ ckfree((ClientData)tagset);
+ return NULL;
+ }
+
+ tagset->tags = (Ttk_Tag*)ckalloc((objc+1) * sizeof(Ttk_Tag));
+ for (i=0; i<objc; ++i) {
+ tagset->tags[i] = Ttk_GetTagFromObj(tagTable, objv[i]);
+ }
+ tagset->tags[i] = NULL;
+ tagset->nTags = objc;
+
+ return tagset;
+}
+
+/* Ttk_NewTagSetObj --
+ * Construct a fresh Tcl_Obj * from a tag set.
+ */
+Tcl_Obj *Ttk_NewTagSetObj(Ttk_TagSet tagset)
+{
+ Tcl_Obj *result = Tcl_NewListObj(0,0);
+ int i;
+
+ for (i = 0; i < tagset->nTags; ++i) {
+ Tcl_ListObjAppendElement(
+ NULL, result, Tcl_NewStringObj(tagset->tags[i]->tagName, -1));
+ }
+ return result;
+}
+
+void Ttk_FreeTagSet(Ttk_TagSet tagset)
+{
+ ckfree((ClientData)tagset->tags);
+ ckfree((ClientData)tagset);
+}
+
+/* Ttk_TagSetContains -- test if tag set contains a tag.
+ */
+int Ttk_TagSetContains(Ttk_TagSet tagset, Ttk_Tag tag)
+{
+ int i;
+ for (i = 0; i < tagset->nTags; ++i) {
+ if (tagset->tags[i] == tag) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* Ttk_TagSetAdd -- add a tag to a tag set.
+ *
+ * Returns: 0 if tagset already contained tag,
+ * 1 if tagset was modified.
+ */
+int Ttk_TagSetAdd(Ttk_TagSet tagset, Ttk_Tag tag)
+{
+ int i;
+ for (i = 0; i < tagset->nTags; ++i) {
+ if (tagset->tags[i] == tag) {
+ return 0;
+ }
+ }
+ tagset->tags = (void*)ckrealloc((void*)tagset->tags,
+ (tagset->nTags+1)*sizeof(tagset->tags[0]));
+ tagset->tags[tagset->nTags++] = tag;
+ return 1;
+}
+
+/* Ttk_TagSetRemove -- remove a tag from a tag set.
+ *
+ * Returns: 0 if tagset did not contain tag,
+ * 1 if tagset was modified.
+ */
+int Ttk_TagSetRemove(Ttk_TagSet tagset, Ttk_Tag tag)
+{
+ int i = 0, j = 0;
+ while (i < tagset->nTags) {
+ if ((tagset->tags[j] = tagset->tags[i]) != tag) {
+ ++j;
+ }
+ ++i;
+ }
+ tagset->nTags = j;
+ return j != i;
+}
+
+/*------------------------------------------------------------------------
+ * +++ Utilities for widget commands.
+ */
+
+/* Ttk_EnumerateTags -- implements [$w tag names]
+ */
+int Ttk_EnumerateTags(
+ Tcl_Interp *interp, Ttk_TagTable tagTable)
+{
+ return TtkEnumerateHashTable(interp, &tagTable->tags);
+}
+
+/* Ttk_EnumerateTagOptions -- implements [$w tag configure $tag]
+ */
+int Ttk_EnumerateTagOptions(
+ Tcl_Interp *interp, Ttk_TagTable tagTable, Ttk_Tag tag)
+{
+ return TtkEnumerateOptions(interp, tag->tagRecord,
+ tagTable->optionSpecs, tagTable->optionTable, tagTable->tkwin);
+}
+
+/* Ttk_TagOptionValue -- implements [$w tag configure $tag -option]
+ */
+Tcl_Obj *Ttk_TagOptionValue(
+ Tcl_Interp *interp,
+ Ttk_TagTable tagTable,
+ Ttk_Tag tag,
+ Tcl_Obj *optionName)
+{
+ return Tk_GetOptionValue(interp,
+ tag->tagRecord, tagTable->optionTable, optionName, tagTable->tkwin);
+}
+
+/* Ttk_ConfigureTag -- implements [$w tag configure $tag -option value...]
+ */
+int Ttk_ConfigureTag(
+ Tcl_Interp *interp,
+ Ttk_TagTable tagTable,
+ Ttk_Tag tag,
+ int objc, Tcl_Obj *const objv[])
+{
+ return Tk_SetOptions(
+ interp, tag->tagRecord, tagTable->optionTable,
+ objc, objv, tagTable->tkwin, NULL/*savedOptions*/, NULL/*mask*/);
+}
+
+/*------------------------------------------------------------------------
+ * +++ Tag values.
+ */
+
+#define OBJ_AT(record, offset) (*(Tcl_Obj**)(((char*)record)+offset))
+
+void Ttk_TagSetValues(Ttk_TagTable tagTable, Ttk_TagSet tagSet, void *record)
+{
+ const int LOWEST_PRIORITY = 0x7FFFFFFF;
+ int i, j;
+
+ memset(record, 0, tagTable->recordSize);
+
+ for (i = 0; tagTable->optionSpecs[i].type != TK_OPTION_END; ++i) {
+ Tk_OptionSpec *optionSpec = tagTable->optionSpecs + i;
+ int offset = optionSpec->objOffset;
+ int prio = LOWEST_PRIORITY;
+
+ for (j = 0; j < tagSet->nTags; ++j) {
+ Ttk_Tag tag = tagSet->tags[j];
+ if (OBJ_AT(tag->tagRecord, offset) != 0 && tag->priority < prio) {
+ OBJ_AT(record, offset) = OBJ_AT(tag->tagRecord, offset);
+ prio = tag->priority;
+ }
+ }
+ }
+}
+
+void Ttk_TagSetApplyStyle(
+ Ttk_TagTable tagTable, Ttk_Style style, Ttk_State state, void *record)
+{
+ Tk_OptionSpec *optionSpec = tagTable->optionSpecs;
+
+ while (optionSpec->type != TK_OPTION_END) {
+ int offset = optionSpec->objOffset;
+ const char *optionName = optionSpec->optionName;
+ Tcl_Obj *val = Ttk_StyleMap(style, optionName, state);
+ if (val) {
+ OBJ_AT(record, offset) = val;
+ } else if (OBJ_AT(record, offset) == 0) {
+ OBJ_AT(record, offset) = Ttk_StyleDefault(style, optionName);
+ }
+ ++optionSpec;
+ }
+}
+
diff --git a/generic/ttk/ttkTheme.c b/generic/ttk/ttkTheme.c
new file mode 100644
index 0000000..5095487
--- /dev/null
+++ b/generic/ttk/ttkTheme.c
@@ -0,0 +1,1737 @@
+/*
+ * ttkTheme.c --
+ *
+ * This file implements the widget styles and themes support.
+ *
+ * Copyright (c) 2002 Frederic Bonnet
+ * Copyright (c) 2003 Joe English
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <tk.h>
+#include <tkInt.h>
+#include "ttkThemeInt.h"
+
+#define PKG_ASSOC_KEY "Ttk"
+
+/*------------------------------------------------------------------------
+ * +++ Styles.
+ *
+ * Invariants:
+ * If styleName contains a dot, parentStyle->styleName is everything
+ * after the first dot; otherwise, parentStyle is the theme's root
+ * style ".". The root style's parentStyle is NULL.
+ *
+ */
+
+typedef struct Ttk_Style_
+{
+ const char *styleName; /* points to hash table key */
+ Tcl_HashTable settingsTable; /* KEY: string; VALUE: StateMap */
+ Tcl_HashTable defaultsTable; /* KEY: string; VALUE: resource */
+ Ttk_LayoutTemplate layoutTemplate; /* Layout template for style, or NULL */
+ Ttk_Style parentStyle; /* Previous style in chain */
+ Ttk_ResourceCache cache; /* Back-pointer to resource cache */
+} Style;
+
+static Style *NewStyle()
+{
+ Style *stylePtr = (Style*)ckalloc(sizeof(Style));
+
+ stylePtr->styleName = NULL;
+ stylePtr->parentStyle = NULL;
+ stylePtr->layoutTemplate = NULL;
+ stylePtr->cache = NULL;
+ Tcl_InitHashTable(&stylePtr->settingsTable, TCL_STRING_KEYS);
+ Tcl_InitHashTable(&stylePtr->defaultsTable, TCL_STRING_KEYS);
+
+ return stylePtr;
+}
+
+static void FreeStyle(Style *stylePtr)
+{
+ Tcl_HashSearch search;
+ Tcl_HashEntry *entryPtr;
+
+ entryPtr = Tcl_FirstHashEntry(&stylePtr->settingsTable, &search);
+ while (entryPtr != NULL) {
+ Ttk_StateMap stateMap = Tcl_GetHashValue(entryPtr);
+ Tcl_DecrRefCount(stateMap);
+ entryPtr = Tcl_NextHashEntry(&search);
+ }
+ Tcl_DeleteHashTable(&stylePtr->settingsTable);
+
+ entryPtr = Tcl_FirstHashEntry(&stylePtr->defaultsTable, &search);
+ while (entryPtr != NULL) {
+ Tcl_Obj *defaultValue = Tcl_GetHashValue(entryPtr);
+ Tcl_DecrRefCount(defaultValue);
+ entryPtr = Tcl_NextHashEntry(&search);
+ }
+ Tcl_DeleteHashTable(&stylePtr->defaultsTable);
+
+ Ttk_FreeLayoutTemplate(stylePtr->layoutTemplate);
+
+ ckfree((ClientData)stylePtr);
+}
+
+/*
+ * Ttk_StyleMap --
+ * Look up state-specific option value from specified style.
+ */
+Tcl_Obj *Ttk_StyleMap(Ttk_Style style, const char *optionName, Ttk_State state)
+{
+ while (style) {
+ Tcl_HashEntry *entryPtr =
+ Tcl_FindHashEntry(&style->settingsTable, optionName);
+ if (entryPtr) {
+ Ttk_StateMap stateMap = Tcl_GetHashValue(entryPtr);
+ return Ttk_StateMapLookup(NULL, stateMap, state);
+ }
+ style = style->parentStyle;
+ }
+ return 0;
+}
+
+/*
+ * Ttk_StyleDefault --
+ * Look up default resource setting the in the specified style.
+ */
+Tcl_Obj *Ttk_StyleDefault(Ttk_Style style, const char *optionName)
+{
+ while (style) {
+ Tcl_HashEntry *entryPtr =
+ Tcl_FindHashEntry(&style->defaultsTable, optionName);
+ if (entryPtr)
+ return Tcl_GetHashValue(entryPtr);
+ style= style->parentStyle;
+ }
+ return 0;
+}
+
+/*------------------------------------------------------------------------
+ * +++ Elements.
+ */
+typedef const Tk_OptionSpec **OptionMap;
+ /* array of Tk_OptionSpecs mapping widget options to element options */
+
+struct Ttk_ElementClass_ {
+ const char *name; /* Points to hash table key */
+ Ttk_ElementSpec *specPtr; /* Template provided during registration. */
+ void *clientData; /* Client data passed in at registration time */
+ void *elementRecord; /* Scratch buffer for element record storage */
+ int nResources; /* #Element options */
+ Tcl_Obj **defaultValues; /* Array of option default values */
+ Tcl_HashTable optMapCache; /* Map: Tk_OptionTable * -> OptionMap */
+};
+
+/* TTKGetOptionSpec --
+ * Look up a Tk_OptionSpec by name from a Tk_OptionTable,
+ * and verify that it's compatible with the specified Tk_OptionType,
+ * along with other constraints (see below).
+ */
+static const Tk_OptionSpec *TTKGetOptionSpec(
+ const char *optionName,
+ Tk_OptionTable optionTable,
+ Tk_OptionType optionType)
+{
+ const Tk_OptionSpec *optionSpec = TkGetOptionSpec(optionName, optionTable);
+
+ if (!optionSpec)
+ return 0;
+
+ /* Make sure widget option has a Tcl_Obj* entry:
+ */
+ if (optionSpec->objOffset < 0) {
+ return 0;
+ }
+
+ /* Grrr. Ignore accidental mismatches caused by prefix-matching:
+ */
+ if (strcmp(optionSpec->optionName, optionName)) {
+ return 0;
+ }
+
+ /* Ensure that the widget option type is compatible with
+ * the element option type.
+ *
+ * TK_OPTION_STRING element options are compatible with anything.
+ * As a workaround for the workaround for Bug #967209,
+ * TK_OPTION_STRING widget options are also compatible with anything
+ * (see <<NOTE-NULLOPTIONS>>).
+ */
+ if ( optionType != TK_OPTION_STRING
+ && optionSpec->type != TK_OPTION_STRING
+ && optionType != optionSpec->type)
+ {
+ return 0;
+ }
+
+ return optionSpec;
+}
+
+/* BuildOptionMap --
+ * Construct the mapping from element options to widget options.
+ */
+static OptionMap
+BuildOptionMap(Ttk_ElementClass *elementClass, Tk_OptionTable optionTable)
+{
+ OptionMap optionMap = (OptionMap)ckalloc(
+ sizeof(const Tk_OptionSpec) * elementClass->nResources + 1);
+ int i;
+
+ for (i = 0; i < elementClass->nResources; ++i) {
+ Ttk_ElementOptionSpec *e = elementClass->specPtr->options+i;
+ optionMap[i] = TTKGetOptionSpec(e->optionName, optionTable, e->type);
+ }
+
+ return optionMap;
+}
+
+/* GetOptionMap --
+ * Return a cached OptionMap matching the specified optionTable
+ * for the specified element, creating it if necessary.
+ */
+static OptionMap
+GetOptionMap(Ttk_ElementClass *elementClass, Tk_OptionTable optionTable)
+{
+ OptionMap optionMap;
+ int isNew;
+ Tcl_HashEntry *entryPtr = Tcl_CreateHashEntry(
+ &elementClass->optMapCache, (void*)optionTable, &isNew);
+
+ if (isNew) {
+ optionMap = BuildOptionMap(elementClass, optionTable);
+ Tcl_SetHashValue(entryPtr, optionMap);
+ } else {
+ optionMap = Tcl_GetHashValue(entryPtr);
+ }
+
+ return optionMap;
+}
+
+/*
+ * NewElementClass --
+ * Allocate and initialize an element class record
+ * from the specified element specification.
+ */
+static Ttk_ElementClass *
+NewElementClass(const char *name, Ttk_ElementSpec *specPtr,void *clientData)
+{
+ Ttk_ElementClass *elementClass =
+ (Ttk_ElementClass*)ckalloc(sizeof(Ttk_ElementClass));
+ int i;
+
+ elementClass->name = name;
+ elementClass->specPtr = specPtr;
+ elementClass->clientData = clientData;
+ elementClass->elementRecord = ckalloc(specPtr->elementSize);
+
+ /* Count #element resources:
+ */
+ for (i = 0; specPtr->options[i].optionName != 0; ++i)
+ continue;
+ elementClass->nResources = i;
+
+ /* Initialize default values:
+ */
+ elementClass->defaultValues = (Tcl_Obj**)
+ ckalloc(elementClass->nResources * sizeof(Tcl_Obj *) + 1);
+ for (i=0; i < elementClass->nResources; ++i) {
+ const char *defaultValue = specPtr->options[i].defaultValue;
+ if (defaultValue) {
+ elementClass->defaultValues[i] = Tcl_NewStringObj(defaultValue,-1);
+ Tcl_IncrRefCount(elementClass->defaultValues[i]);
+ } else {
+ elementClass->defaultValues[i] = 0;
+ }
+ }
+
+ /* Initialize option map cache:
+ */
+ Tcl_InitHashTable(&elementClass->optMapCache, TCL_ONE_WORD_KEYS);
+
+ return elementClass;
+}
+
+/*
+ * FreeElementClass --
+ * Release resources associated with an element class record.
+ */
+static void FreeElementClass(Ttk_ElementClass *elementClass)
+{
+ Tcl_HashSearch search;
+ Tcl_HashEntry *entryPtr;
+ int i;
+
+ /*
+ * Free default values:
+ */
+ for (i = 0; i < elementClass->nResources; ++i) {
+ if (elementClass->defaultValues[i]) {
+ Tcl_DecrRefCount(elementClass->defaultValues[i]);
+ }
+ }
+ ckfree((ClientData)elementClass->defaultValues);
+
+ /*
+ * Free option map cache:
+ */
+ entryPtr = Tcl_FirstHashEntry(&elementClass->optMapCache, &search);
+ while (entryPtr != NULL) {
+ ckfree(Tcl_GetHashValue(entryPtr));
+ entryPtr = Tcl_NextHashEntry(&search);
+ }
+ Tcl_DeleteHashTable(&elementClass->optMapCache);
+
+ ckfree(elementClass->elementRecord);
+ ckfree((ClientData)elementClass);
+}
+
+/*------------------------------------------------------------------------
+ * +++ Themes.
+ */
+
+static int ThemeEnabled(Ttk_Theme theme, void *clientData) { return 1; }
+ /* Default ThemeEnabledProc -- always return true */
+
+typedef struct Ttk_Theme_
+{
+ Ttk_Theme parentPtr; /* Parent theme. */
+ Tcl_HashTable elementTable; /* Map element names to class records */
+ Tcl_HashTable styleTable; /* Map style names to Styles */
+ Ttk_Style rootStyle; /* "." style, root of chain */
+ Ttk_ThemeEnabledProc *enabledProc; /* Function called by SetTheme */
+ void *enabledData; /* ClientData for enabledProc */
+ Ttk_ResourceCache cache; /* Back-pointer to resource cache */
+} Theme;
+
+static Theme *NewTheme(Ttk_ResourceCache cache, Ttk_Theme parent)
+{
+ Theme *themePtr = (Theme*)ckalloc(sizeof(Theme));
+ Tcl_HashEntry *entryPtr;
+ int unused;
+
+ themePtr->parentPtr = parent;
+ themePtr->enabledProc = ThemeEnabled;
+ themePtr->enabledData = NULL;
+ themePtr->cache = cache;
+ Tcl_InitHashTable(&themePtr->elementTable, TCL_STRING_KEYS);
+ Tcl_InitHashTable(&themePtr->styleTable, TCL_STRING_KEYS);
+
+ /*
+ * Create root style "."
+ */
+ entryPtr = Tcl_CreateHashEntry(&themePtr->styleTable, ".", &unused);
+ themePtr->rootStyle = NewStyle();
+ themePtr->rootStyle->styleName =
+ Tcl_GetHashKey(&themePtr->styleTable, entryPtr);
+ themePtr->rootStyle->cache = themePtr->cache;
+ Tcl_SetHashValue(entryPtr, themePtr->rootStyle);
+
+ return themePtr;
+}
+
+static void FreeTheme(Theme *themePtr)
+{
+ Tcl_HashSearch search;
+ Tcl_HashEntry *entryPtr;
+
+ /*
+ * Free element table:
+ */
+ entryPtr = Tcl_FirstHashEntry(&themePtr->elementTable, &search);
+ while (entryPtr != NULL) {
+ Ttk_ElementClass *elementClass = Tcl_GetHashValue(entryPtr);
+ FreeElementClass(elementClass);
+ entryPtr = Tcl_NextHashEntry(&search);
+ }
+ Tcl_DeleteHashTable(&themePtr->elementTable);
+
+ /*
+ * Free style table:
+ */
+ entryPtr = Tcl_FirstHashEntry(&themePtr->styleTable, &search);
+ while (entryPtr != NULL) {
+ Style *stylePtr = Tcl_GetHashValue(entryPtr);
+ FreeStyle(stylePtr);
+ entryPtr = Tcl_NextHashEntry(&search);
+ }
+ Tcl_DeleteHashTable(&themePtr->styleTable);
+
+ /*
+ * Free theme record:
+ */
+ ckfree((ClientData)themePtr);
+
+ return;
+}
+
+/*
+ * Element constructors.
+ */
+typedef struct {
+ Ttk_ElementFactory factory;
+ void *clientData;
+} FactoryRec;
+
+/*
+ * Cleanup records:
+ */
+typedef struct CleanupStruct {
+ void *clientData;
+ Ttk_CleanupProc *cleanupProc;
+ struct CleanupStruct *next;
+} Cleanup;
+
+/*------------------------------------------------------------------------
+ * +++ Master style package data structure.
+ */
+typedef struct
+{
+ Tcl_Interp *interp; /* Owner interp */
+ Tcl_HashTable themeTable; /* KEY: name; VALUE: Theme pointer */
+ Tcl_HashTable factoryTable; /* KEY: name; VALUE: FactoryRec ptr */
+ Theme *defaultTheme; /* Default theme; global fallback*/
+ Theme *currentTheme; /* Currently-selected theme */
+ Cleanup *cleanupList; /* Cleanup records */
+ Ttk_ResourceCache cache; /* Resource cache */
+ int themeChangePending; /* scheduled ThemeChangedProc call? */
+} StylePackageData;
+
+static void ThemeChangedProc(ClientData); /* Forward */
+
+/* Ttk_StylePkgFree --
+ * Cleanup procedure for StylePackageData.
+ */
+static void Ttk_StylePkgFree(ClientData clientData, Tcl_Interp *interp)
+{
+ StylePackageData *pkgPtr = clientData;
+ Tcl_HashSearch search;
+ Tcl_HashEntry *entryPtr;
+ Cleanup *cleanup;
+
+ /*
+ * Cancel any pending ThemeChanged calls:
+ */
+ if (pkgPtr->themeChangePending) {
+ Tcl_CancelIdleCall(ThemeChangedProc, pkgPtr);
+ }
+
+ /*
+ * Free themes.
+ */
+ entryPtr = Tcl_FirstHashEntry(&pkgPtr->themeTable, &search);
+ while (entryPtr != NULL) {
+ Theme *themePtr = Tcl_GetHashValue(entryPtr);
+ FreeTheme(themePtr);
+ entryPtr = Tcl_NextHashEntry(&search);
+ }
+ Tcl_DeleteHashTable(&pkgPtr->themeTable);
+
+ /*
+ * Free element constructor table:
+ */
+ entryPtr = Tcl_FirstHashEntry(&pkgPtr->factoryTable, &search);
+ while (entryPtr != NULL) {
+ ckfree(Tcl_GetHashValue(entryPtr));
+ entryPtr = Tcl_NextHashEntry(&search);
+ }
+ Tcl_DeleteHashTable(&pkgPtr->factoryTable);
+
+ /*
+ * Release cache:
+ */
+ Ttk_FreeResourceCache(pkgPtr->cache);
+
+ /*
+ * Call all registered cleanup procedures:
+ */
+ cleanup = pkgPtr->cleanupList;
+ while (cleanup) {
+ Cleanup *next = cleanup->next;
+ cleanup->cleanupProc(cleanup->clientData);
+ ckfree((ClientData)cleanup);
+ cleanup = next;
+ }
+
+ ckfree((ClientData)pkgPtr);
+}
+
+/*
+ * GetStylePackageData --
+ * Look up the package data registered with the interp.
+ */
+
+static StylePackageData *GetStylePackageData(Tcl_Interp *interp)
+{
+ return Tcl_GetAssocData(interp, PKG_ASSOC_KEY, NULL);
+}
+
+/*
+ * Ttk_RegisterCleanup --
+ *
+ * Register a function to be called when a theme engine is deleted.
+ * (This only happens when the main interp is destroyed). The cleanup
+ * function is called with the current Tcl interpreter and the client
+ * data provided here.
+ *
+ */
+void Ttk_RegisterCleanup(
+ Tcl_Interp *interp, ClientData clientData, Ttk_CleanupProc *cleanupProc)
+{
+ StylePackageData *pkgPtr = GetStylePackageData(interp);
+ Cleanup *cleanup = (Cleanup*)ckalloc(sizeof(*cleanup));
+
+ cleanup->clientData = clientData;
+ cleanup->cleanupProc = cleanupProc;
+ cleanup->next = pkgPtr->cleanupList;
+ pkgPtr->cleanupList = cleanup;
+}
+
+/* ThemeChangedProc --
+ * Notify all widgets that the theme has been changed.
+ * Scheduled as an idle callback; clientData is a StylePackageData *.
+ *
+ * Sends a <<ThemeChanged>> event to every widget in the hierarchy.
+ * Widgets respond to this by calling the WorldChanged class proc,
+ * which in turn recreates the layout.
+ *
+ * The Tk C API doesn't doesn't provide an easy way to traverse
+ * the widget hierarchy, so this is done by evaluating a Tcl script.
+ */
+
+static void ThemeChangedProc(ClientData clientData)
+{
+ static char ThemeChangedScript[] = "ttk::ThemeChanged";
+ StylePackageData *pkgPtr = clientData;
+
+ if (Tcl_GlobalEval(pkgPtr->interp, ThemeChangedScript) != TCL_OK) {
+ Tcl_BackgroundError(pkgPtr->interp);
+ }
+ pkgPtr->themeChangePending = 0;
+}
+
+/*
+ * ThemeChanged --
+ * Schedule a call to ThemeChanged if one is not already pending.
+ */
+static void ThemeChanged(StylePackageData *pkgPtr)
+{
+ if (!pkgPtr->themeChangePending) {
+ Tcl_DoWhenIdle(ThemeChangedProc, pkgPtr);
+ pkgPtr->themeChangePending = 1;
+ }
+}
+
+/*
+ * Ttk_CreateTheme --
+ * Create a new theme and register it in the global theme table.
+ *
+ * Returns:
+ * Pointer to new Theme structure; NULL if named theme already exists.
+ * Leaves an error message in interp's result on error.
+ */
+
+Ttk_Theme
+Ttk_CreateTheme(
+ Tcl_Interp *interp, /* Interpreter in which to create theme */
+ const char *name, /* Name of the theme to create. */
+ Ttk_Theme parent) /* Parent/fallback theme, NULL for default */
+{
+ StylePackageData *pkgPtr = GetStylePackageData(interp);
+ Tcl_HashEntry *entryPtr;
+ int newEntry;
+ Theme *themePtr;
+
+ entryPtr = Tcl_CreateHashEntry(&pkgPtr->themeTable, name, &newEntry);
+ if (!newEntry) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "Theme ", name, " already exists", NULL);
+ return NULL;
+ }
+
+ /*
+ * Initialize new theme:
+ */
+ if (!parent) parent = pkgPtr->defaultTheme;
+
+ themePtr = NewTheme(pkgPtr->cache, parent);
+ Tcl_SetHashValue(entryPtr, themePtr);
+
+ return themePtr;
+}
+
+/*
+ * Ttk_SetThemeEnabledProc --
+ * Sets a procedure that is used to check that this theme is available.
+ */
+
+void Ttk_SetThemeEnabledProc(
+ Ttk_Theme theme, Ttk_ThemeEnabledProc enabledProc, void *enabledData)
+{
+ theme->enabledProc = enabledProc;
+ theme->enabledData = enabledData;
+}
+
+/*
+ * LookupTheme --
+ * Retrieve a registered theme by name. If not found,
+ * returns NULL and leaves an error message in interp's result.
+ */
+
+static Ttk_Theme LookupTheme(
+ Tcl_Interp *interp, /* where to leave error messages */
+ StylePackageData *pkgPtr, /* style package master record */
+ const char *name) /* theme name */
+{
+ Tcl_HashEntry *entryPtr;
+
+ entryPtr = Tcl_FindHashEntry(&pkgPtr->themeTable, name);
+ if (!entryPtr) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "theme \"", name, "\" doesn't exist", NULL);
+ return NULL;
+ }
+
+ return Tcl_GetHashValue(entryPtr);
+}
+
+/*
+ * Ttk_GetTheme --
+ * Public interface to LookupTheme.
+ */
+Ttk_Theme Ttk_GetTheme(Tcl_Interp *interp, const char *themeName)
+{
+ StylePackageData *pkgPtr = GetStylePackageData(interp);
+
+ return LookupTheme(interp, pkgPtr, themeName);
+}
+
+Ttk_Theme Ttk_GetCurrentTheme(Tcl_Interp *interp)
+{
+ StylePackageData *pkgPtr = GetStylePackageData(interp);
+ return pkgPtr->currentTheme;
+}
+
+Ttk_Theme Ttk_GetDefaultTheme(Tcl_Interp *interp)
+{
+ StylePackageData *pkgPtr = GetStylePackageData(interp);
+ return pkgPtr->defaultTheme;
+}
+
+/*
+ * Ttk_UseTheme --
+ * Set the current theme, notify all widgets that the theme has changed.
+ */
+int Ttk_UseTheme(Tcl_Interp *interp, Ttk_Theme theme)
+{
+ StylePackageData *pkgPtr = GetStylePackageData(interp);
+
+ /*
+ * Check if selected theme is enabled:
+ */
+ while (theme && !theme->enabledProc(theme, theme->enabledData)) {
+ theme = theme->parentPtr;
+ }
+ if (!theme) {
+ /* This shouldn't happen -- default theme should always work */
+ Tcl_Panic("No themes available?");
+ return TCL_ERROR;
+ }
+
+ pkgPtr->currentTheme = theme;
+ ThemeChanged(pkgPtr);
+ return TCL_OK;
+}
+
+/*
+ * Ttk_GetResourceCache --
+ * Return the resource cache associated with 'interp'
+ */
+Ttk_ResourceCache
+Ttk_GetResourceCache(Tcl_Interp *interp)
+{
+ StylePackageData *pkgPtr = GetStylePackageData(interp);
+ return pkgPtr->cache;
+}
+
+/*
+ * Register a new layout specification with a style.
+ * @@@ TODO: Make sure layoutName is not ".", root style must not have a layout
+ */
+MODULE_SCOPE
+void Ttk_RegisterLayoutTemplate(
+ Ttk_Theme theme, /* Target theme */
+ const char *layoutName, /* Name of new layout */
+ Ttk_LayoutTemplate layoutTemplate) /* Template */
+{
+ Ttk_Style style = Ttk_GetStyle(theme, layoutName);
+ if (style->layoutTemplate) {
+ Ttk_FreeLayoutTemplate(style->layoutTemplate);
+ }
+ style->layoutTemplate = layoutTemplate;
+}
+
+void Ttk_RegisterLayout(
+ Ttk_Theme themePtr, /* Target theme */
+ const char *layoutName, /* Name of new layout */
+ Ttk_LayoutSpec specPtr) /* Static layout information */
+{
+ Ttk_LayoutTemplate layoutTemplate = Ttk_BuildLayoutTemplate(specPtr);
+ Ttk_RegisterLayoutTemplate(themePtr, layoutName, layoutTemplate);
+}
+
+/*
+ * Ttk_GetStyle --
+ * Look up a Style from a Theme, create new style if not found.
+ */
+Ttk_Style Ttk_GetStyle(Ttk_Theme themePtr, const char *styleName)
+{
+ Tcl_HashEntry *entryPtr;
+ int newStyle;
+
+ entryPtr = Tcl_CreateHashEntry(&themePtr->styleTable, styleName, &newStyle);
+ if (newStyle) {
+ Ttk_Style stylePtr = NewStyle();
+ const char *dot = strchr(styleName, '.');
+
+ if (dot) {
+ stylePtr->parentStyle = Ttk_GetStyle(themePtr, dot + 1);
+ } else {
+ stylePtr->parentStyle = themePtr->rootStyle;
+ }
+
+ stylePtr->styleName = Tcl_GetHashKey(&themePtr->styleTable, entryPtr);
+ stylePtr->cache = stylePtr->parentStyle->cache;
+ Tcl_SetHashValue(entryPtr, stylePtr);
+ return stylePtr;
+ }
+ return Tcl_GetHashValue(entryPtr);
+}
+
+/* FindLayoutTemplate --
+ * Locate a layout template in the layout table, checking
+ * generic names to specific names first, then looking for
+ * the full name in the parent theme.
+ */
+Ttk_LayoutTemplate
+Ttk_FindLayoutTemplate(Ttk_Theme themePtr, const char *layoutName)
+{
+ while (themePtr) {
+ Ttk_Style stylePtr = Ttk_GetStyle(themePtr, layoutName);
+ while (stylePtr) {
+ if (stylePtr->layoutTemplate) {
+ return stylePtr->layoutTemplate;
+ }
+ stylePtr = stylePtr->parentStyle;
+ }
+ themePtr = themePtr->parentPtr;
+ }
+ return NULL;
+}
+
+const char *Ttk_StyleName(Ttk_Style stylePtr)
+{
+ return stylePtr->styleName;
+}
+
+/*
+ * Ttk_GetElement --
+ * Look up an element class by name in a given theme.
+ * If not found, try generic element names in this theme, then
+ * repeat the lookups in the parent theme.
+ * If not found, return the null element.
+ */
+Ttk_ElementClass *Ttk_GetElement(Ttk_Theme themePtr, const char *elementName)
+{
+ Tcl_HashEntry *entryPtr;
+ const char *dot = elementName;
+
+ /*
+ * Check if element has already been registered:
+ */
+ entryPtr = Tcl_FindHashEntry(&themePtr->elementTable, elementName);
+ if (entryPtr) {
+ return Tcl_GetHashValue(entryPtr);
+ }
+
+ /*
+ * Check generic names:
+ */
+ while (!entryPtr && ((dot = strchr(dot, '.')) != NULL)) {
+ dot++;
+ entryPtr = Tcl_FindHashEntry(&themePtr->elementTable, dot);
+ }
+ if (entryPtr) {
+ return Tcl_GetHashValue(entryPtr);
+ }
+
+ /*
+ * Check parent theme:
+ */
+ if (themePtr->parentPtr) {
+ return Ttk_GetElement(themePtr->parentPtr, elementName);
+ }
+
+ /*
+ * Not found, and this is the root theme; return null element, "".
+ * (@@@ SHOULD: signal a background error)
+ */
+ entryPtr = Tcl_FindHashEntry(&themePtr->elementTable, "");
+ /* ASSERT: entryPtr != 0 */
+ return Tcl_GetHashValue(entryPtr);
+}
+
+const char *Ttk_ElementClassName(Ttk_ElementClass *elementClass)
+{
+ return elementClass->name;
+}
+
+/*
+ * Ttk_RegisterElementFactory --
+ * Register a new element factory.
+ */
+int Ttk_RegisterElementFactory(
+ Tcl_Interp *interp, const char *name,
+ Ttk_ElementFactory factory, void *clientData)
+{
+ StylePackageData *pkgPtr = GetStylePackageData(interp);
+ FactoryRec *recPtr = (FactoryRec*)ckalloc(sizeof(*recPtr));
+ Tcl_HashEntry *entryPtr;
+ int newEntry;
+
+ recPtr->factory = factory;
+ recPtr->clientData = clientData;
+
+ entryPtr = Tcl_CreateHashEntry(&pkgPtr->factoryTable, name, &newEntry);
+ if (!newEntry) {
+ /* Free old factory: */
+ ckfree(Tcl_GetHashValue(entryPtr));
+ }
+ Tcl_SetHashValue(entryPtr, recPtr);
+
+ return TCL_OK;
+}
+
+/* Ttk_CloneElement -- element factory procedure.
+ * (style element create $name) "from" $theme ?$element?
+ */
+static int Ttk_CloneElement(
+ Tcl_Interp *interp, void *clientData,
+ Ttk_Theme theme, const char *elementName,
+ int objc, Tcl_Obj *const objv[])
+{
+ Ttk_Theme fromTheme;
+ Ttk_ElementClass *fromElement;
+
+ if (objc <= 0 || objc > 2) {
+ Tcl_WrongNumArgs(interp, 0, objv, "theme ?element?");
+ return TCL_ERROR;
+ }
+
+ fromTheme = Ttk_GetTheme(interp, Tcl_GetString(objv[0]));
+ if (!fromTheme) {
+ return TCL_ERROR;
+ }
+
+ if (objc == 2) {
+ fromElement = Ttk_GetElement(fromTheme, Tcl_GetString(objv[1]));
+ } else {
+ fromElement = Ttk_GetElement(fromTheme, elementName);
+ }
+ if (!fromElement) {
+ return TCL_ERROR;
+ }
+
+ if (Ttk_RegisterElement(interp, theme, elementName,
+ fromElement->specPtr, fromElement->clientData) == NULL)
+ {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/* Ttk_RegisterElement--
+ * Register an element in the given theme.
+ * Returns: Element handle if successful, NULL otherwise.
+ * On failure, leaves an error message in interp's result
+ * if interp is non-NULL.
+ */
+
+Ttk_ElementClass *Ttk_RegisterElement(
+ Tcl_Interp *interp, /* Where to leave error messages */
+ Ttk_Theme theme, /* Style engine providing the implementation. */
+ const char *name, /* Name of new element */
+ Ttk_ElementSpec *specPtr, /* Static template information */
+ void *clientData) /* application-specific data */
+{
+ Ttk_ElementClass *elementClass;
+ Tcl_HashEntry *entryPtr;
+ int newEntry;
+
+ if (specPtr->version != TK_STYLE_VERSION_2) {
+ /* Version mismatch */
+ if (interp) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "Internal error: Ttk_RegisterElement (",
+ name, "): invalid version",
+ NULL);
+ }
+ return 0;
+ }
+
+ entryPtr = Tcl_CreateHashEntry(&theme->elementTable, name, &newEntry);
+ if (!newEntry) {
+ if (interp) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "Duplicate element ", name, NULL);
+ }
+ return 0;
+ }
+
+ name = Tcl_GetHashKey(&theme->elementTable, entryPtr);
+ elementClass = NewElementClass(name, specPtr, clientData);
+ Tcl_SetHashValue(entryPtr, elementClass);
+
+ return elementClass;
+}
+
+/* Ttk_RegisterElementSpec (deprecated) --
+ * Register a new element.
+ */
+int Ttk_RegisterElementSpec(Ttk_Theme theme,
+ const char *name, Ttk_ElementSpec *specPtr, void *clientData)
+{
+ return Ttk_RegisterElement(NULL, theme, name, specPtr, clientData)
+ ? TCL_OK : TCL_ERROR;
+}
+
+/*------------------------------------------------------------------------
+ * +++ Element record initialization.
+ */
+
+/*
+ * AllocateResource --
+ * Extra initialization for element options like TK_OPTION_COLOR, etc.
+ *
+ * Returns: 1 if OK, 0 on failure.
+ *
+ * Note: if resource allocation fails at this point (just prior
+ * to drawing an element), there's really no good place to
+ * report the error. Instead we just silently fail.
+ */
+
+static int AllocateResource(
+ Ttk_ResourceCache cache,
+ Tk_Window tkwin,
+ Tcl_Obj **destPtr,
+ int optionType)
+{
+ Tcl_Obj *resource = *destPtr;
+
+ switch (optionType)
+ {
+ case TK_OPTION_FONT:
+ return (*destPtr = Ttk_UseFont(cache, tkwin, resource)) != NULL;
+ case TK_OPTION_COLOR:
+ return (*destPtr = Ttk_UseColor(cache, tkwin, resource)) != NULL;
+ case TK_OPTION_BORDER:
+ return (*destPtr = Ttk_UseBorder(cache, tkwin, resource)) != NULL;
+ default:
+ /* no-op; always succeeds */
+ return 1;
+ }
+}
+
+/*
+ * InitializeElementRecord --
+ *
+ * Fill in the element record based on the element's option table.
+ * Resources are initialized from:
+ * the corresponding widget option if present and non-NULL,
+ * otherwise the dynamic state map if specified,
+ * otherwise from the corresponding widget resource if present,
+ * otherwise the default value specified at registration time.
+ *
+ * Returns:
+ * 1 if OK, 0 if an error is detected.
+ *
+ * NOTES:
+ * Tcl_Obj * reference counts are _NOT_ adjusted.
+ */
+
+static
+int InitializeElementRecord(
+ Ttk_ElementClass *eclass, /* Element instance to initialize */
+ Ttk_Style style, /* Style table */
+ char *widgetRecord, /* Source of widget option values */
+ Tk_OptionTable optionTable, /* Option table describing widget record */
+ Tk_Window tkwin, /* Corresponding window */
+ Ttk_State state) /* Widget or element state */
+{
+ char *elementRecord = eclass->elementRecord;
+ OptionMap optionMap = GetOptionMap(eclass,optionTable);
+ int nResources = eclass->nResources;
+ Ttk_ResourceCache cache = style->cache;
+ Ttk_ElementOptionSpec *elementOption = eclass->specPtr->options;
+
+ int i;
+ for (i=0; i<nResources; ++i, ++elementOption) {
+ Tcl_Obj **dest = (Tcl_Obj **)
+ (elementRecord + elementOption->offset);
+ const char *optionName = elementOption->optionName;
+ Tcl_Obj *dynamicSetting = Ttk_StyleMap(style, optionName, state);
+ Tcl_Obj *widgetValue = 0;
+ Tcl_Obj *elementDefault = eclass->defaultValues[i];
+
+ if (optionMap[i]) {
+ widgetValue = *(Tcl_Obj **)
+ (widgetRecord + optionMap[i]->objOffset);
+ }
+
+ if (widgetValue) {
+ *dest = widgetValue;
+ } else if (dynamicSetting) {
+ *dest = dynamicSetting;
+ } else {
+ Tcl_Obj *styleDefault = Ttk_StyleDefault(style, optionName);
+ *dest = styleDefault ? styleDefault : elementDefault;
+ }
+
+ if (!AllocateResource(cache, tkwin, dest, elementOption->type)) {
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/*------------------------------------------------------------------------
+ * +++ Public API.
+ */
+
+/*
+ * Ttk_QueryStyle --
+ * Look up a style option based on the current state.
+ */
+Tcl_Obj *Ttk_QueryStyle(
+ Ttk_Style style, /* Style to query */
+ void *recordPtr, /* Widget record */
+ Tk_OptionTable optionTable, /* Option table describing widget record */
+ const char *optionName, /* Option name */
+ Ttk_State state) /* Current state */
+{
+ const Tk_OptionSpec *optionSpec;
+ Tcl_Obj *result;
+
+ /*
+ * Check widget record:
+ */
+ optionSpec = TTKGetOptionSpec(optionName, optionTable, TK_OPTION_ANY);
+ if (optionSpec) {
+ result = *(Tcl_Obj**)(((char*)recordPtr) + optionSpec->objOffset);
+ if (result) {
+ return result;
+ }
+ }
+
+ /*
+ * Check dynamic settings:
+ */
+ result = Ttk_StyleMap(style, optionName, state);
+ if (result) {
+ return result;
+ }
+
+ /*
+ * Use style default:
+ */
+ return Ttk_StyleDefault(style, optionName);
+}
+
+/*
+ * Ttk_ElementSize --
+ * Compute the requested size of the given element.
+ */
+
+void
+Ttk_ElementSize(
+ Ttk_ElementClass *eclass, /* Element to query */
+ Ttk_Style style, /* Style settings */
+ char *recordPtr, /* The widget record. */
+ Tk_OptionTable optionTable, /* Description of widget record */
+ Tk_Window tkwin, /* The widget window. */
+ Ttk_State state, /* Current widget state */
+ int *widthPtr, /* Requested width */
+ int *heightPtr, /* Reqested height */
+ Ttk_Padding *paddingPtr) /* Requested inner border */
+{
+ paddingPtr->left = paddingPtr->right = paddingPtr->top = paddingPtr->bottom
+ = *widthPtr = *heightPtr = 0;
+
+ if (!InitializeElementRecord(
+ eclass, style, recordPtr, optionTable, tkwin, state))
+ {
+ return;
+ }
+ eclass->specPtr->size(
+ eclass->clientData, eclass->elementRecord,
+ tkwin, widthPtr, heightPtr, paddingPtr);
+}
+
+/*
+ * Ttk_DrawElement --
+ * Draw the given widget element in a given drawable area.
+ */
+
+void
+Ttk_DrawElement(
+ Ttk_ElementClass *eclass, /* Element instance */
+ Ttk_Style style, /* Style settings */
+ char *recordPtr, /* The widget record. */
+ Tk_OptionTable optionTable, /* Description of option table */
+ Tk_Window tkwin, /* The widget window. */
+ Drawable d, /* Where to draw element. */
+ Ttk_Box b, /* Element area */
+ Ttk_State state) /* Widget or element state flags. */
+{
+ if (b.width <= 0 || b.height <= 0)
+ return;
+ if (!InitializeElementRecord(
+ eclass, style, recordPtr, optionTable, tkwin, state))
+ {
+ return;
+ }
+ eclass->specPtr->draw(
+ eclass->clientData, eclass->elementRecord,
+ tkwin, d, b, state);
+}
+
+/*------------------------------------------------------------------------
+ * +++ 'style' command ensemble procedures.
+ */
+
+/*
+ * TtkEnumerateHashTable --
+ * Helper routine. Sets interp's result to the list of all keys
+ * in the hash table.
+ *
+ * Returns: TCL_OK.
+ * Side effects: Sets interp's result.
+ */
+
+MODULE_SCOPE
+int TtkEnumerateHashTable(Tcl_Interp *interp, Tcl_HashTable *ht)
+{
+ Tcl_HashSearch search;
+ Tcl_Obj *result = Tcl_NewListObj(0, NULL);
+ Tcl_HashEntry *entryPtr = Tcl_FirstHashEntry(ht, &search);
+
+ while (entryPtr != NULL) {
+ Tcl_Obj *nameObj = Tcl_NewStringObj(Tcl_GetHashKey(ht, entryPtr),-1);
+ Tcl_ListObjAppendElement(interp, result, nameObj);
+ entryPtr = Tcl_NextHashEntry(&search);
+ }
+
+ Tcl_SetObjResult(interp, result);
+ return TCL_OK;
+}
+
+/* HashTableToDict --
+ * Helper routine. Converts a TCL_STRING_KEYS Tcl_HashTable
+ * with Tcl_Obj * entries into a dictionary.
+ */
+static Tcl_Obj* HashTableToDict(Tcl_HashTable *ht)
+{
+ Tcl_HashSearch search;
+ Tcl_Obj *result = Tcl_NewListObj(0, NULL);
+ Tcl_HashEntry *entryPtr = Tcl_FirstHashEntry(ht, &search);
+
+ while (entryPtr != NULL) {
+ Tcl_Obj *nameObj = Tcl_NewStringObj(Tcl_GetHashKey(ht, entryPtr),-1);
+ Tcl_Obj *valueObj = Tcl_GetHashValue(entryPtr);
+ Tcl_ListObjAppendElement(NULL, result, nameObj);
+ Tcl_ListObjAppendElement(NULL, result, valueObj);
+ entryPtr = Tcl_NextHashEntry(&search);
+ }
+
+ return result;
+}
+
+/* + style map $style ? -resource statemap ... ?
+ *
+ * Note that resource names are unconstrained; the Style
+ * doesn't know what resources individual elements may use.
+ */
+static int
+StyleMapCmd(
+ ClientData clientData, /* Master StylePackageData pointer */
+ Tcl_Interp *interp, /* Current interpreter */
+ int objc, /* Number of arguments */
+ Tcl_Obj *const objv[]) /* Argument objects */
+{
+ StylePackageData *pkgPtr = clientData;
+ Ttk_Theme theme = pkgPtr->currentTheme;
+ const char *styleName;
+ Style *stylePtr;
+ int i;
+
+ if (objc < 3) {
+usage:
+ Tcl_WrongNumArgs(interp,2,objv,"style ?-option ?value...??");
+ return TCL_ERROR;
+ }
+
+ styleName = Tcl_GetString(objv[2]);
+ stylePtr = Ttk_GetStyle(theme, styleName);
+
+ /* NOTE: StateMaps are actually Tcl_Obj *s, so HashTableToDict works
+ * for settingsTable.
+ */
+ if (objc == 3) { /* style map $styleName */
+ Tcl_SetObjResult(interp, HashTableToDict(&stylePtr->settingsTable));
+ return TCL_OK;
+ } else if (objc == 4) { /* style map $styleName -option */
+ const char *optionName = Tcl_GetString(objv[3]);
+ Tcl_HashEntry *entryPtr =
+ Tcl_FindHashEntry(&stylePtr->settingsTable, optionName);
+ if (entryPtr) {
+ Tcl_SetObjResult(interp, (Tcl_Obj*)Tcl_GetHashValue(entryPtr));
+ }
+ return TCL_OK;
+ } else if (objc % 2 != 1) {
+ goto usage;
+ }
+
+ for (i = 3; i < objc; i += 2) {
+ const char *optionName = Tcl_GetString(objv[i]);
+ Tcl_Obj *stateMap = objv[i+1];
+ Tcl_HashEntry *entryPtr;
+ int newEntry;
+
+ /* Make sure 'stateMap' is legal:
+ * (@@@ SHOULD: check for valid resource values as well,
+ * but we don't know what types they should be at this level.)
+ */
+ if (!Ttk_GetStateMapFromObj(interp, stateMap))
+ return TCL_ERROR;
+
+ entryPtr = Tcl_CreateHashEntry(
+ &stylePtr->settingsTable,optionName,&newEntry);
+
+ Tcl_IncrRefCount(stateMap);
+ if (!newEntry) {
+ Tcl_DecrRefCount((Tcl_Obj*)Tcl_GetHashValue(entryPtr));
+ }
+ Tcl_SetHashValue(entryPtr, stateMap);
+ }
+ ThemeChanged(pkgPtr);
+ return TCL_OK;
+}
+
+/* + style configure $style -option ?value...
+ */
+static int StyleConfigureCmd(
+ ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ StylePackageData *pkgPtr = clientData;
+ Ttk_Theme theme = pkgPtr->currentTheme;
+ const char *styleName;
+ Style *stylePtr;
+ int i;
+
+ if (objc < 3) {
+usage:
+ Tcl_WrongNumArgs(interp,2,objv,"style ?-option ?value...??");
+ return TCL_ERROR;
+ }
+
+ styleName = Tcl_GetString(objv[2]);
+ stylePtr = Ttk_GetStyle(theme, styleName);
+
+ if (objc == 3) { /* style default $styleName */
+ Tcl_SetObjResult(interp, HashTableToDict(&stylePtr->defaultsTable));
+ return TCL_OK;
+ } else if (objc == 4) { /* style default $styleName -option */
+ const char *optionName = Tcl_GetString(objv[3]);
+ Tcl_HashEntry *entryPtr =
+ Tcl_FindHashEntry(&stylePtr->defaultsTable, optionName);
+ if (entryPtr) {
+ Tcl_SetObjResult(interp, (Tcl_Obj*)Tcl_GetHashValue(entryPtr));
+ }
+ return TCL_OK;
+ } else if (objc % 2 != 1) {
+ goto usage;
+ }
+
+ for (i = 3; i < objc; i += 2) {
+ const char *optionName = Tcl_GetString(objv[i]);
+ Tcl_Obj *value = objv[i+1];
+ Tcl_HashEntry *entryPtr;
+ int newEntry;
+
+ entryPtr = Tcl_CreateHashEntry(
+ &stylePtr->defaultsTable,optionName,&newEntry);
+
+ Tcl_IncrRefCount(value);
+ if (!newEntry) {
+ Tcl_DecrRefCount((Tcl_Obj*)Tcl_GetHashValue(entryPtr));
+ }
+ Tcl_SetHashValue(entryPtr, value);
+ }
+
+ ThemeChanged(pkgPtr);
+ return TCL_OK;
+}
+
+/* + style lookup $style -option ?statespec? ?defaultValue?
+ */
+static int StyleLookupCmd(
+ ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ StylePackageData *pkgPtr = clientData;
+ Ttk_Theme theme = pkgPtr->currentTheme;
+ Ttk_Style style = NULL;
+ const char *optionName;
+ Ttk_State state = 0ul;
+ Tcl_Obj *result;
+
+ if (objc < 4 || objc > 6) {
+ Tcl_WrongNumArgs(interp, 2, objv, "style -option ?state? ?default?");
+ return TCL_ERROR;
+ }
+
+ style = Ttk_GetStyle(theme, Tcl_GetString(objv[2]));
+ if (!style) {
+ return TCL_ERROR;
+ }
+ optionName = Tcl_GetString(objv[3]);
+
+ if (objc >= 5) {
+ Ttk_StateSpec stateSpec;
+ /* @@@ SB: Ttk_GetStateFromObj(); 'offbits' spec is ignored */
+ if (Ttk_GetStateSpecFromObj(interp, objv[4], &stateSpec) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ state = stateSpec.onbits;
+ }
+
+ result = Ttk_QueryStyle(style, NULL,NULL, optionName, state);
+ if (result == NULL && objc >= 6) { /* Use caller-supplied fallback */
+ result = objv[5];
+ }
+
+ if (result) {
+ Tcl_SetObjResult(interp, result);
+ }
+
+ return TCL_OK;
+}
+
+static int StyleThemeCurrentCmd(
+ ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj * const objv[])
+{
+ StylePackageData *pkgPtr = clientData;
+ Tcl_HashSearch search;
+ Tcl_HashEntry *entryPtr = NULL;
+ const char *name = NULL;
+
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 3, objv, "");
+ return TCL_ERROR;
+ }
+
+ entryPtr = Tcl_FirstHashEntry(&pkgPtr->themeTable, &search);
+ while (entryPtr != NULL) {
+ Theme *ptr = Tcl_GetHashValue(entryPtr);
+ if (ptr == pkgPtr->currentTheme) {
+ name = Tcl_GetHashKey(&pkgPtr->themeTable, entryPtr);
+ break;
+ }
+ entryPtr = Tcl_NextHashEntry(&search);
+ }
+
+ if (name == NULL) {
+ Tcl_SetObjResult(interp,
+ Tcl_NewStringObj("error: failed to get theme name", -1));
+ return TCL_ERROR;
+ }
+
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(name, -1));
+ return TCL_OK;
+}
+
+/* + style theme create name ?-parent $theme? ?-settings { script }?
+ */
+static int StyleThemeCreateCmd(
+ ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ StylePackageData *pkgPtr = clientData;
+ static const char *optStrings[] =
+ { "-parent", "-settings", NULL };
+ enum { OP_PARENT, OP_SETTINGS };
+ Ttk_Theme parentTheme = pkgPtr->defaultTheme, newTheme;
+ Tcl_Obj *settingsScript = NULL;
+ const char *themeName;
+ int i;
+
+ if (objc < 4 || objc % 2 != 0) {
+ Tcl_WrongNumArgs(interp, 3, objv, "name ?-option value ...?");
+ return TCL_ERROR;
+ }
+
+ themeName = Tcl_GetString(objv[3]);
+
+ for (i=4; i < objc; i +=2) {
+ int option;
+ if (Tcl_GetIndexFromObj(
+ interp, objv[i], optStrings, "option", 0, &option) != TCL_OK)
+ {
+ return TCL_ERROR;
+ }
+
+ switch (option) {
+ case OP_PARENT:
+ parentTheme = LookupTheme(
+ interp, pkgPtr, Tcl_GetString(objv[i+1]));
+ if (!parentTheme)
+ return TCL_ERROR;
+ break;
+ case OP_SETTINGS:
+ settingsScript = objv[i+1];
+ break;
+ }
+ }
+
+ newTheme = Ttk_CreateTheme(interp, themeName, parentTheme);
+ if (!newTheme) {
+ return TCL_ERROR;
+ }
+
+ /*
+ * Evaluate the -settings script, if supplied:
+ */
+ if (settingsScript) {
+ Ttk_Theme oldTheme = pkgPtr->currentTheme;
+ int status;
+
+ pkgPtr->currentTheme = newTheme;
+ status = Tcl_EvalObjEx(interp, settingsScript, 0);
+ pkgPtr->currentTheme = oldTheme;
+ return status;
+ } else {
+ return TCL_OK;
+ }
+}
+
+/* + style theme names --
+ * Return list of registered themes.
+ */
+static int StyleThemeNamesCmd(
+ ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ StylePackageData *pkgPtr = clientData;
+ return TtkEnumerateHashTable(interp, &pkgPtr->themeTable);
+}
+
+/* + style theme settings $theme $script
+ *
+ * Temporarily sets the current theme to $themeName,
+ * evaluates $script, then restores the old theme.
+ */
+static int
+StyleThemeSettingsCmd(
+ ClientData clientData, /* Master StylePackageData pointer */
+ Tcl_Interp *interp, /* Current interpreter */
+ int objc, /* Number of arguments */
+ Tcl_Obj *const objv[]) /* Argument objects */
+{
+ StylePackageData *pkgPtr = clientData;
+ Ttk_Theme oldTheme = pkgPtr->currentTheme;
+ Ttk_Theme newTheme;
+ int status;
+
+ if (objc != 5) {
+ Tcl_WrongNumArgs(interp, 3, objv, "theme script");
+ return TCL_ERROR;
+ }
+
+ newTheme = LookupTheme(interp, pkgPtr, Tcl_GetString(objv[3]));
+ if (!newTheme)
+ return TCL_ERROR;
+
+ pkgPtr->currentTheme = newTheme;
+ status = Tcl_EvalObjEx(interp, objv[4], 0);
+ pkgPtr->currentTheme = oldTheme;
+
+ return status;
+}
+
+/* + style element create name type ? ...args ?
+ */
+static int StyleElementCreateCmd(
+ ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ StylePackageData *pkgPtr = clientData;
+ Ttk_Theme theme = pkgPtr->currentTheme;
+ const char *elementName, *factoryName;
+ Tcl_HashEntry *entryPtr;
+ FactoryRec *recPtr;
+
+ if (objc < 5) {
+ Tcl_WrongNumArgs(interp, 3, objv, "name type ?-option value ...?");
+ return TCL_ERROR;
+ }
+
+ elementName = Tcl_GetString(objv[3]);
+ factoryName = Tcl_GetString(objv[4]);
+
+ entryPtr = Tcl_FindHashEntry(&pkgPtr->factoryTable, factoryName);
+ if (!entryPtr) {
+ Tcl_AppendResult(interp, "No such element type ", factoryName, NULL);
+ return TCL_ERROR;
+ }
+
+ recPtr = Tcl_GetHashValue(entryPtr);
+
+ return recPtr->factory(interp, recPtr->clientData,
+ theme, elementName, objc - 5, objv + 5);
+}
+
+/* + style element names --
+ * Return a list of elements defined in the current theme.
+ */
+static int StyleElementNamesCmd(
+ ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ StylePackageData *pkgPtr = clientData;
+ Ttk_Theme theme = pkgPtr->currentTheme;
+
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 3, objv, NULL);
+ return TCL_ERROR;
+ }
+ return TtkEnumerateHashTable(interp, &theme->elementTable);
+}
+
+/* + style element options $element --
+ * Return list of element options for specified element
+ */
+static int StyleElementOptionsCmd(
+ ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ StylePackageData *pkgPtr = clientData;
+ Ttk_Theme theme = pkgPtr->currentTheme;
+ const char *elementName;
+ Ttk_ElementClass *elementClass;
+
+ if (objc != 4) {
+ Tcl_WrongNumArgs(interp, 3, objv, "element");
+ return TCL_ERROR;
+ }
+
+ elementName = Tcl_GetString(objv[3]);
+ elementClass = Ttk_GetElement(theme, elementName);
+ if (elementClass) {
+ Ttk_ElementSpec *specPtr = elementClass->specPtr;
+ Ttk_ElementOptionSpec *option = specPtr->options;
+ Tcl_Obj *result = Tcl_NewListObj(0,0);
+
+ while (option->optionName) {
+ Tcl_ListObjAppendElement(
+ interp, result, Tcl_NewStringObj(option->optionName,-1));
+ ++option;
+ }
+
+ Tcl_SetObjResult(interp, result);
+ return TCL_OK;
+ }
+
+ Tcl_AppendResult(interp, "element ", elementName, " not found", NULL);
+ return TCL_ERROR;
+}
+
+/* + style layout name ?spec?
+ */
+static int StyleLayoutCmd(
+ ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ StylePackageData *pkgPtr = clientData;
+ Ttk_Theme theme = pkgPtr->currentTheme;
+ const char *layoutName;
+ Ttk_LayoutTemplate layoutTemplate;
+
+ if (objc < 3 || objc > 4) {
+ Tcl_WrongNumArgs(interp, 2, objv, "name ?spec?");
+ return TCL_ERROR;
+ }
+
+ layoutName = Tcl_GetString(objv[2]);
+
+ if (objc == 3) {
+ layoutTemplate = Ttk_FindLayoutTemplate(theme, layoutName);
+ if (!layoutTemplate) {
+ Tcl_AppendResult(interp, "Layout ", layoutName, " not found", NULL);
+ return TCL_ERROR;
+ }
+ Tcl_SetObjResult(interp, Ttk_UnparseLayoutTemplate(layoutTemplate));
+ } else {
+ layoutTemplate = Ttk_ParseLayoutTemplate(interp, objv[3]);
+ if (!layoutTemplate) {
+ return TCL_ERROR;
+ }
+ Ttk_RegisterLayoutTemplate(theme, layoutName, layoutTemplate);
+ ThemeChanged(pkgPtr);
+ }
+ return TCL_OK;
+}
+
+/* + style theme use $theme --
+ * Sets the current theme to $theme
+ */
+static int
+StyleThemeUseCmd(
+ ClientData clientData, /* Master StylePackageData pointer */
+ Tcl_Interp *interp, /* Current interpreter */
+ int objc, /* Number of arguments */
+ Tcl_Obj *const objv[]) /* Argument objects */
+{
+ StylePackageData *pkgPtr = clientData;
+ Ttk_Theme theme;
+
+ if (objc < 3 || objc > 4) {
+ Tcl_WrongNumArgs(interp, 3, objv, "?theme?");
+ return TCL_ERROR;
+ }
+
+ if (objc == 3) {
+ return StyleThemeCurrentCmd(clientData, interp, objc, objv);
+ }
+
+ theme = LookupTheme(interp, pkgPtr, Tcl_GetString(objv[3]));
+ if (!theme) {
+ return TCL_ERROR;
+ }
+
+ return Ttk_UseTheme(interp, theme);
+}
+
+/*
+ * StyleObjCmd --
+ * Implementation of the [style] command.
+ */
+
+static const Ttk_Ensemble StyleThemeEnsemble[] = {
+ { "create", StyleThemeCreateCmd, 0 },
+ { "names", StyleThemeNamesCmd, 0 },
+ { "settings", StyleThemeSettingsCmd, 0 },
+ { "use", StyleThemeUseCmd, 0 },
+ { NULL, 0, 0 }
+};
+
+static const Ttk_Ensemble StyleElementEnsemble[] = {
+ { "create", StyleElementCreateCmd, 0 },
+ { "names", StyleElementNamesCmd, 0 },
+ { "options", StyleElementOptionsCmd, 0 },
+ { NULL, 0, 0 }
+};
+
+static const Ttk_Ensemble StyleEnsemble[] = {
+ { "configure", StyleConfigureCmd, 0 },
+ { "map", StyleMapCmd, 0 },
+ { "lookup", StyleLookupCmd, 0 },
+ { "layout", StyleLayoutCmd, 0 },
+ { "theme", 0, StyleThemeEnsemble },
+ { "element", 0, StyleElementEnsemble },
+ { NULL, 0, 0 }
+};
+
+static int
+StyleObjCmd(
+ ClientData clientData, /* Master StylePackageData pointer */
+ Tcl_Interp *interp, /* Current interpreter */
+ int objc, /* Number of arguments */
+ Tcl_Obj *const objv[]) /* Argument objects */
+{
+ return Ttk_InvokeEnsemble(StyleEnsemble, 1, clientData,interp,objc,objv);
+}
+
+MODULE_SCOPE
+int Ttk_InvokeEnsemble( /* Run an ensemble command */
+ const Ttk_Ensemble *ensemble, int cmdIndex,
+ void *clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ while (cmdIndex < objc) {
+ int index;
+ if (Tcl_GetIndexFromObjStruct(interp,
+ objv[cmdIndex], ensemble, sizeof(ensemble[0]),
+ "command", 0, &index)
+ != TCL_OK)
+ {
+ return TCL_ERROR;
+ }
+
+ if (ensemble[index].command) {
+ return ensemble[index].command(clientData, interp, objc, objv);
+ }
+ ensemble = ensemble[index].ensemble;
+ ++cmdIndex;
+ }
+ Tcl_WrongNumArgs(interp, cmdIndex, objv, "option ?arg ...?");
+ return TCL_ERROR;
+}
+
+/*
+ * Ttk_StylePkgInit --
+ * Initializes all the structures that are used by the style
+ * package on a per-interp basis.
+ */
+
+void Ttk_StylePkgInit(Tcl_Interp *interp)
+{
+ Tcl_Namespace *nsPtr;
+
+ StylePackageData *pkgPtr = (StylePackageData *)
+ ckalloc(sizeof(StylePackageData));
+
+ pkgPtr->interp = interp;
+ Tcl_InitHashTable(&pkgPtr->themeTable, TCL_STRING_KEYS);
+ Tcl_InitHashTable(&pkgPtr->factoryTable, TCL_STRING_KEYS);
+ pkgPtr->cleanupList = NULL;
+ pkgPtr->cache = Ttk_CreateResourceCache(interp);
+ pkgPtr->themeChangePending = 0;
+
+ Tcl_SetAssocData(interp, PKG_ASSOC_KEY, Ttk_StylePkgFree, pkgPtr);
+
+ /*
+ * Create the default system theme:
+ *
+ * pkgPtr->defaultTheme must be initialized to 0 before
+ * calling Ttk_CreateTheme for the first time, since it's used
+ * as the parent theme.
+ */
+ pkgPtr->defaultTheme = 0;
+ pkgPtr->defaultTheme = pkgPtr->currentTheme =
+ Ttk_CreateTheme(interp, "default", NULL);
+
+ /*
+ * Register null element, used as a last-resort fallback:
+ */
+ Ttk_RegisterElement(interp, pkgPtr->defaultTheme, "", &ttkNullElementSpec, 0);
+
+ /*
+ * Register commands:
+ */
+ Tcl_CreateObjCommand(interp, "::ttk::style", StyleObjCmd, pkgPtr, 0);
+
+ nsPtr = Tcl_FindNamespace(interp, "::ttk", NULL, TCL_LEAVE_ERR_MSG);
+ Tcl_Export(interp, nsPtr, "style", 0 /* dontResetList */);
+
+ Ttk_RegisterElementFactory(interp, "from", Ttk_CloneElement, 0);
+}
+
+/*EOF*/
diff --git a/generic/ttk/ttkTheme.h b/generic/ttk/ttkTheme.h
new file mode 100644
index 0000000..7bf2a7f
--- /dev/null
+++ b/generic/ttk/ttkTheme.h
@@ -0,0 +1,444 @@
+/*
+ * Copyright (c) 2003 Joe English. Freely redistributable.
+ *
+ * Declarations for Tk theme engine.
+ */
+
+#ifndef _TTKTHEME
+#define _TTKTHEME
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef MODULE_SCOPE
+# ifdef __cplusplus
+# define MODULE_SCOPE extern "C"
+# else
+# define MODULE_SCOPE extern
+# endif
+#endif
+
+#define TTKAPI MODULE_SCOPE
+
+/* Ttk syncs to the Tk version & patchlevel */
+#define TTK_VERSION TK_VERSION
+#define TTK_PATCH_LEVEL TK_PATCH_LEVEL
+
+/*------------------------------------------------------------------------
+ * +++ Defaults for element option specifications.
+ */
+#define DEFAULT_FONT "TkDefaultFont"
+#define DEFAULT_BACKGROUND "#d9d9d9"
+#define DEFAULT_FOREGROUND "black"
+
+/*------------------------------------------------------------------------
+ * +++ Widget states.
+ * Keep in sync with stateNames[] in tkstate.c.
+ */
+
+typedef unsigned int Ttk_State;
+
+#define TTK_STATE_ACTIVE (1<<0)
+#define TTK_STATE_DISABLED (1<<1)
+#define TTK_STATE_FOCUS (1<<2)
+#define TTK_STATE_PRESSED (1<<3)
+#define TTK_STATE_SELECTED (1<<4)
+#define TTK_STATE_BACKGROUND (1<<5)
+#define TTK_STATE_ALTERNATE (1<<6)
+#define TTK_STATE_INVALID (1<<7)
+#define TTK_STATE_READONLY (1<<8)
+#define TTK_STATE_HOVER (1<<9)
+#define TTK_STATE_USER6 (1<<10)
+#define TTK_STATE_USER5 (1<<11)
+#define TTK_STATE_USER4 (1<<12)
+#define TTK_STATE_USER3 (1<<13)
+#define TTK_STATE_USER2 (1<<14)
+#define TTK_STATE_USER1 (1<<15)
+
+/* Maintenance note: if you get all the way to "USER1",
+ * see tkstate.c
+ */
+typedef struct
+{
+ unsigned int onbits; /* bits to turn on */
+ unsigned int offbits; /* bits to turn off */
+} Ttk_StateSpec;
+
+#define Ttk_StateMatches(state, spec) \
+ (((state) & ((spec)->onbits|(spec)->offbits)) == (spec)->onbits)
+
+#define Ttk_ModifyState(state, spec) \
+ (((state) & ~(spec)->offbits) | (spec)->onbits)
+
+TTKAPI int Ttk_GetStateSpecFromObj(Tcl_Interp *, Tcl_Obj *, Ttk_StateSpec *);
+TTKAPI Tcl_Obj *Ttk_NewStateSpecObj(unsigned int onbits,unsigned int offbits);
+
+/*------------------------------------------------------------------------
+ * +++ State maps and state tables.
+ */
+typedef Tcl_Obj *Ttk_StateMap;
+
+TTKAPI Ttk_StateMap Ttk_GetStateMapFromObj(Tcl_Interp *, Tcl_Obj *);
+TTKAPI Tcl_Obj *Ttk_StateMapLookup(Tcl_Interp*, Ttk_StateMap, Ttk_State);
+
+/*
+ * Table for looking up an integer index based on widget state:
+ */
+typedef struct
+{
+ int index; /* Value to return if this entry matches */
+ unsigned int onBits; /* Bits which must be set */
+ unsigned int offBits; /* Bits which must be cleared */
+} Ttk_StateTable;
+
+TTKAPI int Ttk_StateTableLookup(Ttk_StateTable map[], Ttk_State);
+
+/*------------------------------------------------------------------------
+ * +++ Padding.
+ * Used to represent internal padding and borders.
+ */
+typedef struct
+{
+ short left;
+ short top;
+ short right;
+ short bottom;
+} Ttk_Padding;
+
+TTKAPI int Ttk_GetPaddingFromObj(Tcl_Interp*,Tk_Window,Tcl_Obj*,Ttk_Padding*);
+TTKAPI int Ttk_GetBorderFromObj(Tcl_Interp*,Tcl_Obj*,Ttk_Padding*);
+
+TTKAPI Ttk_Padding Ttk_MakePadding(short l, short t, short r, short b);
+TTKAPI Ttk_Padding Ttk_UniformPadding(short borderWidth);
+TTKAPI Ttk_Padding Ttk_AddPadding(Ttk_Padding, Ttk_Padding);
+TTKAPI Ttk_Padding Ttk_RelievePadding(Ttk_Padding, int relief, int n);
+
+#define Ttk_PaddingWidth(p) ((p).left + (p).right)
+#define Ttk_PaddingHeight(p) ((p).top + (p).bottom)
+
+#define Ttk_SetMargins(tkwin, pad) \
+ Tk_SetInternalBorderEx(tkwin, pad.left, pad.right, pad.top, pad.bottom)
+
+/*------------------------------------------------------------------------
+ * +++ Boxes.
+ * Used to represent rectangular regions
+ */
+typedef struct /* Hey, this is an XRectangle! */
+{
+ int x;
+ int y;
+ int width;
+ int height;
+} Ttk_Box;
+
+TTKAPI Ttk_Box Ttk_MakeBox(int x, int y, int width, int height);
+TTKAPI int Ttk_BoxContains(Ttk_Box, int x, int y);
+
+#define Ttk_WinBox(tkwin) Ttk_MakeBox(0,0,Tk_Width(tkwin),Tk_Height(tkwin))
+
+/*------------------------------------------------------------------------
+ * +++ Layout utilities.
+ */
+typedef enum {
+ TTK_SIDE_LEFT, TTK_SIDE_TOP, TTK_SIDE_RIGHT, TTK_SIDE_BOTTOM
+} Ttk_Side;
+
+typedef unsigned int Ttk_Sticky;
+
+/*
+ * -sticky bits for Ttk_StickBox:
+ */
+#define TTK_STICK_W (0x1)
+#define TTK_STICK_E (0x2)
+#define TTK_STICK_N (0x4)
+#define TTK_STICK_S (0x8)
+
+/*
+ * Aliases and useful combinations:
+ */
+#define TTK_FILL_X (0x3) /* -sticky ew */
+#define TTK_FILL_Y (0xC) /* -sticky ns */
+#define TTK_FILL_BOTH (0xF) /* -sticky nswe */
+
+TTKAPI int Ttk_GetStickyFromObj(Tcl_Interp *, Tcl_Obj *, Ttk_Sticky *);
+TTKAPI Tcl_Obj *Ttk_NewStickyObj(Ttk_Sticky);
+
+/*
+ * Extra bits for position specifications (combine -side and -sticky)
+ */
+
+typedef unsigned int Ttk_PositionSpec; /* See below */
+
+#define TTK_PACK_LEFT (0x10) /* pack at left of current parcel */
+#define TTK_PACK_RIGHT (0x20) /* pack at right of current parcel */
+#define TTK_PACK_TOP (0x40) /* pack at top of current parcel */
+#define TTK_PACK_BOTTOM (0x80) /* pack at bottom of current parcel */
+#define TTK_EXPAND (0x100) /* use entire parcel */
+#define TTK_BORDER (0x200) /* draw this element after children */
+#define TTK_UNIT (0x400) /* treat descendants as a part of element */
+
+/*
+ * Extra bits for layout specifications
+ */
+#define _TTK_CHILDREN (0x1000)/* for LayoutSpecs -- children follow */
+#define _TTK_LAYOUT_END (0x2000)/* for LayoutSpecs -- end of child list */
+#define _TTK_LAYOUT (0x4000)/* for LayoutSpec tables -- define layout */
+
+#define _TTK_MASK_STICK (0x0F) /* See Ttk_UnparseLayout() */
+#define _TTK_MASK_PACK (0xF0) /* See Ttk_UnparseLayout(), packStrings */
+
+TTKAPI Ttk_Box Ttk_PackBox(Ttk_Box *cavity, int w, int h, Ttk_Side side);
+TTKAPI Ttk_Box Ttk_StickBox(Ttk_Box parcel, int w, int h, Ttk_Sticky sticky);
+TTKAPI Ttk_Box Ttk_AnchorBox(Ttk_Box parcel, int w, int h, Tk_Anchor anchor);
+TTKAPI Ttk_Box Ttk_PadBox(Ttk_Box b, Ttk_Padding p);
+TTKAPI Ttk_Box Ttk_ExpandBox(Ttk_Box b, Ttk_Padding p);
+TTKAPI Ttk_Box Ttk_PlaceBox(Ttk_Box *cavity, int w,int h, Ttk_Side,Ttk_Sticky);
+TTKAPI Ttk_Box Ttk_PositionBox(Ttk_Box *cavity, int w, int h, Ttk_PositionSpec);
+
+/*------------------------------------------------------------------------
+ * +++ Themes.
+ */
+MODULE_SCOPE void Ttk_StylePkgInit(Tcl_Interp *);
+
+typedef struct Ttk_Theme_ *Ttk_Theme;
+typedef struct Ttk_ElementClass_ Ttk_ElementClass;
+typedef struct Ttk_Layout_ *Ttk_Layout;
+typedef struct Ttk_LayoutNode_ *Ttk_Element;
+typedef struct Ttk_Style_ *Ttk_Style;
+
+TTKAPI Ttk_Theme Ttk_GetTheme(Tcl_Interp *interp, const char *name);
+TTKAPI Ttk_Theme Ttk_GetDefaultTheme(Tcl_Interp *interp);
+TTKAPI Ttk_Theme Ttk_GetCurrentTheme(Tcl_Interp *interp);
+
+TTKAPI Ttk_Theme Ttk_CreateTheme(
+ Tcl_Interp *interp, const char *name, Ttk_Theme parent);
+
+typedef int (Ttk_ThemeEnabledProc)(Ttk_Theme theme, void *clientData);
+MODULE_SCOPE void Ttk_SetThemeEnabledProc(Ttk_Theme, Ttk_ThemeEnabledProc, void *);
+
+MODULE_SCOPE int Ttk_UseTheme(Tcl_Interp *, Ttk_Theme);
+
+typedef void (Ttk_CleanupProc)(void *clientData);
+TTKAPI void Ttk_RegisterCleanup(
+ Tcl_Interp *interp, void *deleteData, Ttk_CleanupProc *cleanupProc);
+
+/*------------------------------------------------------------------------
+ * +++ Elements.
+ */
+
+enum TTKStyleVersion2 { TK_STYLE_VERSION_2 = 2 };
+
+typedef void (Ttk_ElementSizeProc)(void *clientData, void *elementRecord,
+ Tk_Window tkwin, int *widthPtr, int *heightPtr, Ttk_Padding*);
+typedef void (Ttk_ElementDrawProc)(void *clientData, void *elementRecord,
+ Tk_Window tkwin, Drawable d, Ttk_Box b, Ttk_State state);
+
+typedef struct Ttk_ElementOptionSpec
+{
+ const char *optionName; /* Command-line name of the widget option */
+ Tk_OptionType type; /* Accepted option types */
+ int offset; /* Offset of Tcl_Obj* field in element record */
+ const char *defaultValue; /* Default value to used if resource missing */
+} Ttk_ElementOptionSpec;
+
+#define TK_OPTION_ANY TK_OPTION_STRING
+
+typedef struct Ttk_ElementSpec {
+ enum TTKStyleVersion2 version; /* Version of the style support. */
+ size_t elementSize; /* Size of element record */
+ Ttk_ElementOptionSpec *options; /* List of options, NULL-terminated */
+ Ttk_ElementSizeProc *size; /* Compute min size and padding */
+ Ttk_ElementDrawProc *draw; /* Draw the element */
+} Ttk_ElementSpec;
+
+TTKAPI Ttk_ElementClass *Ttk_RegisterElement(
+ Tcl_Interp *interp, Ttk_Theme theme, const char *elementName,
+ Ttk_ElementSpec *, void *clientData);
+
+typedef int (*Ttk_ElementFactory)
+ (Tcl_Interp *, void *clientData,
+ Ttk_Theme, const char *elementName, int objc, Tcl_Obj *const objv[]);
+
+TTKAPI int Ttk_RegisterElementFactory(
+ Tcl_Interp *, const char *name, Ttk_ElementFactory, void *clientData);
+
+/*
+ * Null element implementation:
+ * has no geometry or layout; may be used as a stub or placeholder.
+ */
+
+typedef struct {
+ Tcl_Obj *unused;
+} NullElement;
+
+MODULE_SCOPE void TtkNullElementSize
+ (void *, void *, Tk_Window, int *, int *, Ttk_Padding *);
+MODULE_SCOPE void TtkNullElementDraw
+ (void *, void *, Tk_Window, Drawable, Ttk_Box, Ttk_State);
+MODULE_SCOPE Ttk_ElementOptionSpec TtkNullElementOptions[];
+MODULE_SCOPE Ttk_ElementSpec ttkNullElementSpec;
+
+/*------------------------------------------------------------------------
+ * +++ Layout templates.
+ */
+typedef struct {
+ const char * elementName;
+ unsigned opcode;
+} TTKLayoutInstruction, *Ttk_LayoutSpec;
+
+#define TTK_BEGIN_LAYOUT_TABLE(name) \
+ static TTKLayoutInstruction name[] = {
+#define TTK_LAYOUT(name, content) \
+ { name, _TTK_CHILDREN|_TTK_LAYOUT }, \
+ content \
+ { 0, _TTK_LAYOUT_END },
+#define TTK_GROUP(name, flags, children) \
+ { name, flags | _TTK_CHILDREN }, \
+ children \
+ { 0, _TTK_LAYOUT_END },
+#define TTK_NODE(name, flags) { name, flags },
+#define TTK_END_LAYOUT_TABLE { 0, _TTK_LAYOUT | _TTK_LAYOUT_END } };
+
+#define TTK_BEGIN_LAYOUT(name) static TTKLayoutInstruction name[] = {
+#define TTK_END_LAYOUT { 0, _TTK_LAYOUT_END } };
+
+TTKAPI void Ttk_RegisterLayout(
+ Ttk_Theme theme, const char *className, Ttk_LayoutSpec layoutSpec);
+
+TTKAPI void Ttk_RegisterLayouts(
+ Ttk_Theme theme, Ttk_LayoutSpec layoutTable);
+
+/*------------------------------------------------------------------------
+ * +++ Layout instances.
+ */
+
+MODULE_SCOPE Ttk_Layout Ttk_CreateLayout(
+ Tcl_Interp *, Ttk_Theme, const char *name,
+ void *recordPtr, Tk_OptionTable, Tk_Window tkwin);
+
+MODULE_SCOPE Ttk_Layout Ttk_CreateSublayout(
+ Tcl_Interp *, Ttk_Theme, Ttk_Layout, const char *name, Tk_OptionTable);
+
+MODULE_SCOPE void Ttk_FreeLayout(Ttk_Layout);
+
+MODULE_SCOPE void Ttk_LayoutSize(Ttk_Layout,Ttk_State,int *widthPtr,int *heightPtr);
+MODULE_SCOPE void Ttk_PlaceLayout(Ttk_Layout, Ttk_State, Ttk_Box);
+MODULE_SCOPE void Ttk_DrawLayout(Ttk_Layout, Ttk_State, Drawable);
+
+MODULE_SCOPE void Ttk_RebindSublayout(Ttk_Layout, void *recordPtr);
+
+MODULE_SCOPE Ttk_Element Ttk_IdentifyElement(Ttk_Layout, int x, int y);
+MODULE_SCOPE Ttk_Element Ttk_FindElement(Ttk_Layout, const char *nodeName);
+
+MODULE_SCOPE const char *Ttk_ElementName(Ttk_Element);
+MODULE_SCOPE Ttk_Box Ttk_ElementParcel(Ttk_Element);
+
+MODULE_SCOPE Ttk_Box Ttk_ClientRegion(Ttk_Layout, const char *elementName);
+
+MODULE_SCOPE Ttk_Box Ttk_LayoutNodeInternalParcel(Ttk_Layout,Ttk_Element);
+MODULE_SCOPE Ttk_Padding Ttk_LayoutNodeInternalPadding(Ttk_Layout,Ttk_Element);
+MODULE_SCOPE void Ttk_LayoutNodeReqSize(Ttk_Layout, Ttk_Element, int *w, int *h);
+
+MODULE_SCOPE void Ttk_PlaceElement(Ttk_Layout, Ttk_Element, Ttk_Box);
+MODULE_SCOPE void Ttk_ChangeElementState(Ttk_Element,unsigned set,unsigned clr);
+
+MODULE_SCOPE Tcl_Obj *Ttk_QueryOption(Ttk_Layout, const char *, Ttk_State);
+
+TTKAPI Ttk_Style Ttk_LayoutStyle(Ttk_Layout);
+TTKAPI Tcl_Obj *Ttk_StyleDefault(Ttk_Style, const char *optionName);
+TTKAPI Tcl_Obj *Ttk_StyleMap(Ttk_Style, const char *optionName, Ttk_State);
+
+/*------------------------------------------------------------------------
+ * +++ Resource cache.
+ * See resource.c for explanation.
+ */
+
+typedef struct Ttk_ResourceCache_ *Ttk_ResourceCache;
+MODULE_SCOPE Ttk_ResourceCache Ttk_CreateResourceCache(Tcl_Interp *);
+MODULE_SCOPE void Ttk_FreeResourceCache(Ttk_ResourceCache);
+
+MODULE_SCOPE Ttk_ResourceCache Ttk_GetResourceCache(Tcl_Interp*);
+MODULE_SCOPE Tcl_Obj *Ttk_UseFont(Ttk_ResourceCache, Tk_Window, Tcl_Obj *);
+MODULE_SCOPE Tcl_Obj *Ttk_UseColor(Ttk_ResourceCache, Tk_Window, Tcl_Obj *);
+MODULE_SCOPE Tcl_Obj *Ttk_UseBorder(Ttk_ResourceCache, Tk_Window, Tcl_Obj *);
+MODULE_SCOPE Tk_Image Ttk_UseImage(Ttk_ResourceCache, Tk_Window, Tcl_Obj *);
+
+MODULE_SCOPE void Ttk_RegisterNamedColor(Ttk_ResourceCache, const char *, XColor *);
+
+/*------------------------------------------------------------------------
+ * +++ Image specifications.
+ */
+
+typedef struct TtkImageSpec Ttk_ImageSpec;
+TTKAPI Ttk_ImageSpec *TtkGetImageSpec(Tcl_Interp *, Tk_Window, Tcl_Obj *);
+TTKAPI void TtkFreeImageSpec(Ttk_ImageSpec *);
+TTKAPI Tk_Image TtkSelectImage(Ttk_ImageSpec *, Ttk_State);
+
+/*------------------------------------------------------------------------
+ * +++ Miscellaneous enumerations.
+ * Other stuff that element implementations need to know about.
+ */
+typedef enum /* -default option values */
+{
+ TTK_BUTTON_DEFAULT_NORMAL, /* widget defaultable */
+ TTK_BUTTON_DEFAULT_ACTIVE, /* currently the default widget */
+ TTK_BUTTON_DEFAULT_DISABLED /* not defaultable */
+} Ttk_ButtonDefaultState;
+
+TTKAPI int Ttk_GetButtonDefaultStateFromObj(Tcl_Interp *, Tcl_Obj *, int *);
+
+typedef enum /* -compound option values */
+{
+ TTK_COMPOUND_NONE, /* image if specified, otherwise text */
+ TTK_COMPOUND_TEXT, /* text only */
+ TTK_COMPOUND_IMAGE, /* image only */
+ TTK_COMPOUND_CENTER, /* text overlays image */
+ TTK_COMPOUND_TOP, /* image above text */
+ TTK_COMPOUND_BOTTOM, /* image below text */
+ TTK_COMPOUND_LEFT, /* image to left of text */
+ TTK_COMPOUND_RIGHT /* image to right of text */
+} Ttk_Compound;
+
+TTKAPI int Ttk_GetCompoundFromObj(Tcl_Interp *, Tcl_Obj *, int *);
+
+typedef enum { /* -orient option values */
+ TTK_ORIENT_HORIZONTAL,
+ TTK_ORIENT_VERTICAL
+} Ttk_Orient;
+
+/*------------------------------------------------------------------------
+ * +++ Utilities.
+ */
+
+typedef struct TtkEnsemble {
+ const char *name; /* subcommand name */
+ Tcl_ObjCmdProc *command; /* subcommand implementation, OR: */
+ const struct TtkEnsemble *ensemble; /* subcommand ensemble */
+} Ttk_Ensemble;
+
+MODULE_SCOPE int Ttk_InvokeEnsemble( /* Run an ensemble command */
+ const Ttk_Ensemble *commands, int cmdIndex,
+ void *clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]);
+
+MODULE_SCOPE int TtkEnumerateHashTable(Tcl_Interp *, Tcl_HashTable *);
+
+/*------------------------------------------------------------------------
+ * +++ Stub table declarations.
+ */
+
+#include "ttkDecls.h"
+
+/*
+ * Drawing utilities for theme code:
+ * (@@@ find a better home for this)
+ */
+typedef enum { ARROW_UP, ARROW_DOWN, ARROW_LEFT, ARROW_RIGHT } ArrowDirection;
+MODULE_SCOPE void TtkArrowSize(int h, ArrowDirection, int *widthPtr, int *heightPtr);
+MODULE_SCOPE void TtkDrawArrow(Display *, Drawable, GC, Ttk_Box, ArrowDirection);
+MODULE_SCOPE void TtkFillArrow(Display *, Drawable, GC, Ttk_Box, ArrowDirection);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* _TTKTHEME */
diff --git a/generic/ttk/ttkThemeInt.h b/generic/ttk/ttkThemeInt.h
new file mode 100644
index 0000000..3aaada8
--- /dev/null
+++ b/generic/ttk/ttkThemeInt.h
@@ -0,0 +1,42 @@
+/*
+ * Theme engine: private definitions.
+ *
+ * Copyright (c) 2004 Joe English. Freely redistributable.
+ */
+
+#ifndef _TTKTHEMEINT
+#define _TTKTHEMEINT
+
+#include "ttkTheme.h"
+
+typedef struct Ttk_TemplateNode_ Ttk_TemplateNode, *Ttk_LayoutTemplate;
+
+MODULE_SCOPE Ttk_ElementClass *Ttk_GetElement(Ttk_Theme, const char *name);
+MODULE_SCOPE const char *Ttk_ElementClassName(Ttk_ElementClass *);
+
+MODULE_SCOPE void Ttk_ElementSize(
+ Ttk_ElementClass *, Ttk_Style, char *recordPtr, Tk_OptionTable,
+ Tk_Window tkwin, Ttk_State state,
+ int *widthPtr, int *heightPtr, Ttk_Padding*);
+MODULE_SCOPE void Ttk_DrawElement(
+ Ttk_ElementClass *, Ttk_Style, char *recordPtr, Tk_OptionTable,
+ Tk_Window tkwin, Drawable d, Ttk_Box b, Ttk_State state);
+
+MODULE_SCOPE Tcl_Obj *Ttk_QueryStyle(
+ Ttk_Style, void *, Tk_OptionTable, const char *, Ttk_State state);
+
+MODULE_SCOPE Ttk_LayoutTemplate Ttk_ParseLayoutTemplate(
+ Tcl_Interp *, Tcl_Obj *);
+MODULE_SCOPE Tcl_Obj *Ttk_UnparseLayoutTemplate(Ttk_LayoutTemplate);
+MODULE_SCOPE Ttk_LayoutTemplate Ttk_BuildLayoutTemplate(Ttk_LayoutSpec);
+MODULE_SCOPE void Ttk_FreeLayoutTemplate(Ttk_LayoutTemplate);
+MODULE_SCOPE void Ttk_RegisterLayoutTemplate(
+ Ttk_Theme theme, const char *layoutName, Ttk_LayoutTemplate);
+
+MODULE_SCOPE Ttk_Style Ttk_GetStyle(Ttk_Theme themePtr, const char *styleName);
+MODULE_SCOPE Ttk_LayoutTemplate Ttk_FindLayoutTemplate(
+ Ttk_Theme themePtr, const char *layoutName);
+
+MODULE_SCOPE const char *Ttk_StyleName(Ttk_Style);
+
+#endif /* _TTKTHEMEINT */
diff --git a/generic/ttk/ttkTrace.c b/generic/ttk/ttkTrace.c
new file mode 100644
index 0000000..8bc8519
--- /dev/null
+++ b/generic/ttk/ttkTrace.c
@@ -0,0 +1,190 @@
+/*
+ * Copyright 2003, Joe English
+ *
+ * Simplified interface to Tcl_TraceVariable.
+ *
+ * PROBLEM: Can't distinguish "variable does not exist" (which is OK)
+ * from other errors (which are not).
+ */
+
+#include <tk.h>
+#include "ttkTheme.h"
+#include "ttkWidget.h"
+
+struct TtkTraceHandle_
+{
+ Tcl_Interp *interp; /* Containing interpreter */
+ Tcl_Obj *varnameObj; /* Name of variable being traced */
+ Ttk_TraceProc callback; /* Callback procedure */
+ void *clientData; /* Data to pass to callback */
+};
+
+/*
+ * Tcl_VarTraceProc for trace handles.
+ */
+static char *
+VarTraceProc(
+ ClientData clientData, /* Widget record pointer */
+ Tcl_Interp *interp, /* Interpreter containing variable. */
+ const char *name1, /* (unused) */
+ const char *name2, /* (unused) */
+ int flags) /* Information about what happened. */
+{
+ Ttk_TraceHandle *tracePtr = clientData;
+ const char *name, *value;
+ Tcl_Obj *valuePtr;
+
+ if (flags & TCL_INTERP_DESTROYED) {
+ return NULL;
+ }
+
+ name = Tcl_GetString(tracePtr->varnameObj);
+
+ /*
+ * If the variable is being unset, then re-establish the trace:
+ */
+ if (flags & TCL_TRACE_DESTROYED) {
+ /*
+ * If a prior call to Ttk_UntraceVariable() left behind an
+ * indicator that we wanted this handler to be deleted (see below),
+ * cleanup the ClientData bits and exit.
+ */
+ if (tracePtr->interp == NULL) {
+ Tcl_DecrRefCount(tracePtr->varnameObj);
+ ckfree((ClientData)tracePtr);
+ return NULL;
+ }
+ Tcl_TraceVar(interp, name,
+ TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
+ VarTraceProc, clientData);
+ tracePtr->callback(tracePtr->clientData, NULL);
+ return NULL;
+ }
+
+ /*
+ * Call the callback:
+ */
+ valuePtr = Tcl_GetVar2Ex(interp, name, NULL, TCL_GLOBAL_ONLY);
+ value = valuePtr ? Tcl_GetString(valuePtr) : NULL;
+ tracePtr->callback(tracePtr->clientData, value);
+
+ return NULL;
+}
+
+/* Ttk_TraceVariable(interp, varNameObj, callback, clientdata) --
+ * Attach a write trace to the specified variable,
+ * which will pass the variable's value to 'callback'
+ * whenever the variable is set.
+ *
+ * When the variable is unset, passes NULL to the callback
+ * and reattaches the trace.
+ */
+Ttk_TraceHandle *Ttk_TraceVariable(
+ Tcl_Interp *interp,
+ Tcl_Obj *varnameObj,
+ Ttk_TraceProc callback,
+ void *clientData)
+{
+ Ttk_TraceHandle *h = (Ttk_TraceHandle*)ckalloc(sizeof(*h));
+ int status;
+
+ h->interp = interp;
+ h->varnameObj = Tcl_DuplicateObj(varnameObj);
+ Tcl_IncrRefCount(h->varnameObj);
+ h->clientData = clientData;
+ h->callback = callback;
+
+ status = Tcl_TraceVar(interp, Tcl_GetString(varnameObj),
+ TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
+ VarTraceProc, (ClientData)h);
+
+ if (status != TCL_OK) {
+ Tcl_DecrRefCount(h->varnameObj);
+ ckfree((ClientData)h);
+ return NULL;
+ }
+
+ return h;
+}
+
+/*
+ * Ttk_UntraceVariable --
+ * Remove previously-registered trace and free the handle.
+ */
+void Ttk_UntraceVariable(Ttk_TraceHandle *h)
+{
+ if (h) {
+ ClientData cd = NULL;
+
+ /*
+ * Workaround for Tcl Bug 3062331. The trace design problem is
+ * that when variable unset traces fire, Tcl documents that the
+ * traced variable has already been unset. It's already gone.
+ * So from within an unset trace, if you try to call
+ * Tcl_UntraceVar() on that variable, it will do nothing, because
+ * the variable by that name can no longer be found. It's gone.
+ * This means callers of Tcl_UntraceVar() that might be running
+ * in response to an unset trace have to handle the possibility
+ * that their Tcl_UntraceVar() call will do nothing. In this case,
+ * we have to support the possibility that Tcl_UntraceVar() will
+ * leave the trace in place, so we need to leave the ClientData
+ * untouched so when that trace does fire it will not crash.
+ */
+
+ /*
+ * Search the traces on the variable to see if the one we are tasked
+ * with removing is present.
+ */
+ while ((cd = Tcl_VarTraceInfo(h->interp, Tcl_GetString(h->varnameObj),
+ TCL_GLOBAL_ONLY, VarTraceProc, cd)) != NULL) {
+ if (cd == (ClientData) h) {
+ break;
+ }
+ }
+ /*
+ * If the trace we wish to delete is not visible, Tcl_UntraceVar
+ * will do nothing, so don't try to call it. Instead set an
+ * indicator in the Ttk_TraceHandle that we need to cleanup later.
+ */
+ if (cd == NULL) {
+ h->interp = NULL;
+ return;
+ }
+ Tcl_UntraceVar(h->interp, Tcl_GetString(h->varnameObj),
+ TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
+ VarTraceProc, (ClientData)h);
+ Tcl_DecrRefCount(h->varnameObj);
+ ckfree((ClientData)h);
+ }
+}
+
+/*
+ * Ttk_FireTrace --
+ * Executes a trace handle as if the variable has been written.
+ *
+ * Note: may reenter the interpreter.
+ */
+int Ttk_FireTrace(Ttk_TraceHandle *tracePtr)
+{
+ Tcl_Interp *interp = tracePtr->interp;
+ void *clientData = tracePtr->clientData;
+ const char *name = Tcl_GetString(tracePtr->varnameObj);
+ Ttk_TraceProc callback = tracePtr->callback;
+ Tcl_Obj *valuePtr;
+ const char *value;
+
+ /* Read the variable.
+ * Note that this can reenter the interpreter, and anything can happen --
+ * including the current trace handle being freed!
+ */
+ valuePtr = Tcl_GetVar2Ex(interp, name, NULL, TCL_GLOBAL_ONLY);
+ value = valuePtr ? Tcl_GetString(valuePtr) : NULL;
+
+ /* Call callback.
+ */
+ callback(clientData, value);
+
+ return TCL_OK;
+}
+
+/*EOF*/
diff --git a/generic/ttk/ttkTrack.c b/generic/ttk/ttkTrack.c
new file mode 100644
index 0000000..9cf8267
--- /dev/null
+++ b/generic/ttk/ttkTrack.c
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 2004, Joe English
+ *
+ * TtkTrackElementState() -- helper routine for widgets
+ * like scrollbars in which individual elements may
+ * be active or pressed instead of the widget as a whole.
+ *
+ * Usage:
+ * TtkTrackElementState(&recordPtr->core);
+ *
+ * Registers an event handler on the widget that tracks pointer
+ * events and updates the state of the element under the
+ * mouse cursor.
+ *
+ * The "active" element is the one under the mouse cursor,
+ * and is normally set to the ACTIVE state unless another element
+ * is currently being pressed.
+ *
+ * The active element becomes "pressed" on <ButtonPress> events,
+ * and remains "active" and "pressed" until the corresponding
+ * <ButtonRelease> event.
+ *
+ * TODO: Handle "chords" properly (e.g., <B1-ButtonPress-2>)
+ */
+
+#include <tk.h>
+#include "ttkTheme.h"
+#include "ttkWidget.h"
+
+typedef struct {
+ WidgetCore *corePtr; /* widget to track */
+ Ttk_Layout tracking; /* current layout being tracked */
+ Ttk_Element activeElement; /* element under the mouse cursor */
+ Ttk_Element pressedElement; /* currently pressed element */
+} ElementStateTracker;
+
+/*
+ * ActivateElement(es, node) --
+ * Make 'node' the active element if non-NULL.
+ * Deactivates the currently active element if different.
+ *
+ * The active element has TTK_STATE_ACTIVE set _unless_
+ * another element is 'pressed'
+ */
+static void ActivateElement(ElementStateTracker *es, Ttk_Element element)
+{
+ if (es->activeElement == element) {
+ /* No change */
+ return;
+ }
+
+ if (!es->pressedElement) {
+ if (es->activeElement) {
+ /* Deactivate old element */
+ Ttk_ChangeElementState(es->activeElement, 0,TTK_STATE_ACTIVE);
+ }
+ if (element) {
+ /* Activate new element */
+ Ttk_ChangeElementState(element, TTK_STATE_ACTIVE,0);
+ }
+ TtkRedisplayWidget(es->corePtr);
+ }
+
+ es->activeElement = element;
+}
+
+/* ReleaseElement --
+ * Releases the currently pressed element, if any.
+ */
+static void ReleaseElement(ElementStateTracker *es)
+{
+ if (!es->pressedElement)
+ return;
+
+ Ttk_ChangeElementState(
+ es->pressedElement, 0,TTK_STATE_PRESSED|TTK_STATE_ACTIVE);
+ es->pressedElement = 0;
+
+ /* Reactivate element under the mouse cursor:
+ */
+ if (es->activeElement)
+ Ttk_ChangeElementState(es->activeElement, TTK_STATE_ACTIVE,0);
+
+ TtkRedisplayWidget(es->corePtr);
+}
+
+/* PressElement --
+ * Presses the specified element.
+ */
+static void PressElement(ElementStateTracker *es, Ttk_Element element)
+{
+ if (es->pressedElement) {
+ ReleaseElement(es);
+ }
+
+ if (element) {
+ Ttk_ChangeElementState(
+ element, TTK_STATE_PRESSED|TTK_STATE_ACTIVE, 0);
+ }
+
+ es->pressedElement = element;
+ TtkRedisplayWidget(es->corePtr);
+}
+
+/* ElementStateEventProc --
+ * Event handler for tracking element states.
+ */
+
+static const unsigned ElementStateMask =
+ ButtonPressMask
+ | ButtonReleaseMask
+ | PointerMotionMask
+ | LeaveWindowMask
+ | EnterWindowMask
+ | StructureNotifyMask
+ ;
+
+static void
+ElementStateEventProc(ClientData clientData, XEvent *ev)
+{
+ ElementStateTracker *es = clientData;
+ Ttk_Layout layout = es->corePtr->layout;
+ Ttk_Element element;
+
+ /* Guard against dangling pointers [#2431428]
+ */
+ if (es->tracking != layout) {
+ es->pressedElement = es->activeElement = 0;
+ es->tracking = layout;
+ }
+
+ switch (ev->type)
+ {
+ case MotionNotify :
+ element = Ttk_IdentifyElement(
+ layout, ev->xmotion.x, ev->xmotion.y);
+ ActivateElement(es, element);
+ break;
+ case LeaveNotify:
+ ActivateElement(es, 0);
+ if (ev->xcrossing.mode == NotifyGrab)
+ PressElement(es, 0);
+ break;
+ case EnterNotify:
+ element = Ttk_IdentifyElement(
+ layout, ev->xcrossing.x, ev->xcrossing.y);
+ ActivateElement(es, element);
+ break;
+ case ButtonPress:
+ element = Ttk_IdentifyElement(
+ layout, ev->xbutton.x, ev->xbutton.y);
+ if (element)
+ PressElement(es, element);
+ break;
+ case ButtonRelease:
+ ReleaseElement(es);
+ break;
+ case DestroyNotify:
+ /* Unregister this event handler and free client data.
+ */
+ Tk_DeleteEventHandler(es->corePtr->tkwin,
+ ElementStateMask, ElementStateEventProc, es);
+ ckfree(clientData);
+ break;
+ }
+}
+
+/*
+ * TtkTrackElementState --
+ * Register an event handler to manage the 'pressed'
+ * and 'active' states of individual widget elements.
+ */
+
+void TtkTrackElementState(WidgetCore *corePtr)
+{
+ ElementStateTracker *es = (ElementStateTracker*)ckalloc(sizeof(*es));
+ es->corePtr = corePtr;
+ es->tracking = 0;
+ es->activeElement = es->pressedElement = 0;
+ Tk_CreateEventHandler(corePtr->tkwin,
+ ElementStateMask,ElementStateEventProc,es);
+}
+
diff --git a/generic/ttk/ttkTreeview.c b/generic/ttk/ttkTreeview.c
new file mode 100644
index 0000000..862c7f6
--- /dev/null
+++ b/generic/ttk/ttkTreeview.c
@@ -0,0 +1,3442 @@
+/*
+ * Copyright (c) 2004, Joe English
+ *
+ * ttk::treeview widget implementation.
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <tk.h>
+#include "ttkTheme.h"
+#include "ttkWidget.h"
+
+#define DEF_TREE_ROWS "10"
+#define DEF_COLWIDTH "200"
+#define DEF_MINWIDTH "20"
+
+static const int DEFAULT_ROWHEIGHT = 20;
+static const int DEFAULT_INDENT = 20;
+static const int HALO = 4; /* separator */
+
+#define TTK_STATE_OPEN TTK_STATE_USER1
+#define TTK_STATE_LEAF TTK_STATE_USER2
+
+#define STATE_CHANGED (0x100) /* item state option changed */
+
+/*------------------------------------------------------------------------
+ * +++ Tree items.
+ *
+ * INVARIANTS:
+ * item->children ==> item->children->parent == item
+ * item->next ==> item->next->parent == item->parent
+ * item->next ==> item->next->prev == item
+ * item->prev ==> item->prev->next == item
+ */
+
+typedef struct TreeItemRec TreeItem;
+struct TreeItemRec {
+ Tcl_HashEntry *entryPtr; /* Back-pointer to hash table entry */
+ TreeItem *parent; /* Parent item */
+ TreeItem *children; /* Linked list of child items */
+ TreeItem *next; /* Next sibling */
+ TreeItem *prev; /* Previous sibling */
+
+ /*
+ * Options and instance data:
+ */
+ Ttk_State state;
+ Tcl_Obj *textObj;
+ Tcl_Obj *imageObj;
+ Tcl_Obj *valuesObj;
+ Tcl_Obj *openObj;
+ Tcl_Obj *tagsObj;
+
+ /*
+ * Derived resources:
+ */
+ Ttk_TagSet tagset;
+ Ttk_ImageSpec *imagespec;
+};
+
+#define ITEM_OPTION_TAGS_CHANGED 0x100
+#define ITEM_OPTION_IMAGE_CHANGED 0x200
+
+static Tk_OptionSpec ItemOptionSpecs[] = {
+ {TK_OPTION_STRING, "-text", "text", "Text",
+ "", Tk_Offset(TreeItem,textObj), -1,
+ 0,0,0 },
+ {TK_OPTION_STRING, "-image", "image", "Image",
+ NULL, Tk_Offset(TreeItem,imageObj), -1,
+ TK_OPTION_NULL_OK,0,ITEM_OPTION_IMAGE_CHANGED },
+ {TK_OPTION_STRING, "-values", "values", "Values",
+ NULL, Tk_Offset(TreeItem,valuesObj), -1,
+ TK_OPTION_NULL_OK,0,0 },
+ {TK_OPTION_BOOLEAN, "-open", "open", "Open",
+ "0", Tk_Offset(TreeItem,openObj), -1,
+ 0,0,0 },
+ {TK_OPTION_STRING, "-tags", "tags", "Tags",
+ NULL, Tk_Offset(TreeItem,tagsObj), -1,
+ TK_OPTION_NULL_OK,0,ITEM_OPTION_TAGS_CHANGED },
+
+ {TK_OPTION_END, 0,0,0, NULL, -1,-1, 0,0,0}
+};
+
+/* + NewItem --
+ * Allocate a new, uninitialized, unlinked item
+ */
+static TreeItem *NewItem(void)
+{
+ TreeItem *item = (TreeItem*)ckalloc(sizeof(*item));
+
+ item->entryPtr = 0;
+ item->parent = item->children = item->next = item->prev = NULL;
+
+ item->state = 0ul;
+ item->textObj = NULL;
+ item->imageObj = NULL;
+ item->valuesObj = NULL;
+ item->openObj = NULL;
+ item->tagsObj = NULL;
+
+ item->tagset = NULL;
+ item->imagespec = NULL;
+
+ return item;
+}
+
+/* + FreeItem --
+ * Destroy an item
+ */
+static void FreeItem(TreeItem *item)
+{
+ if (item->textObj) { Tcl_DecrRefCount(item->textObj); }
+ if (item->imageObj) { Tcl_DecrRefCount(item->imageObj); }
+ if (item->valuesObj) { Tcl_DecrRefCount(item->valuesObj); }
+ if (item->openObj) { Tcl_DecrRefCount(item->openObj); }
+ if (item->tagsObj) { Tcl_DecrRefCount(item->tagsObj); }
+
+ if (item->tagset) { Ttk_FreeTagSet(item->tagset); }
+ if (item->imagespec) { TtkFreeImageSpec(item->imagespec); }
+
+ ckfree((ClientData)item);
+}
+
+static void FreeItemCB(void *clientData) { FreeItem(clientData); }
+
+/* + DetachItem --
+ * Unlink an item from the tree.
+ */
+static void DetachItem(TreeItem *item)
+{
+ if (item->parent && item->parent->children == item)
+ item->parent->children = item->next;
+ if (item->prev)
+ item->prev->next = item->next;
+ if (item->next)
+ item->next->prev = item->prev;
+ item->next = item->prev = item->parent = NULL;
+}
+
+/* + InsertItem --
+ * Insert an item into the tree after the specified item.
+ *
+ * Preconditions:
+ * + item is currently detached
+ * + prev != NULL ==> prev->parent == parent.
+ */
+static void InsertItem(TreeItem *parent, TreeItem *prev, TreeItem *item)
+{
+ item->parent = parent;
+ item->prev = prev;
+ if (prev) {
+ item->next = prev->next;
+ prev->next = item;
+ } else {
+ item->next = parent->children;
+ parent->children = item;
+ }
+ if (item->next) {
+ item->next->prev = item;
+ }
+}
+
+/* + NextPreorder --
+ * Return the next item in preorder traversal order.
+ */
+
+static TreeItem *NextPreorder(TreeItem *item)
+{
+ if (item->children)
+ return item->children;
+ while (!item->next) {
+ item = item->parent;
+ if (!item)
+ return 0;
+ }
+ return item->next;
+}
+
+/*------------------------------------------------------------------------
+ * +++ Display items and tag options.
+ */
+
+typedef struct {
+ Tcl_Obj *textObj; /* taken from item / data cell */
+ Tcl_Obj *imageObj; /* taken from item */
+ Tcl_Obj *anchorObj; /* from column <<NOTE-ANCHOR>> */
+ Tcl_Obj *backgroundObj; /* remainder from tag */
+ Tcl_Obj *foregroundObj;
+ Tcl_Obj *fontObj;
+} DisplayItem;
+
+static Tk_OptionSpec TagOptionSpecs[] = {
+ {TK_OPTION_STRING, "-text", "text", "Text",
+ NULL, Tk_Offset(DisplayItem,textObj), -1,
+ TK_OPTION_NULL_OK,0,0 },
+ {TK_OPTION_STRING, "-image", "image", "Image",
+ NULL, Tk_Offset(DisplayItem,imageObj), -1,
+ TK_OPTION_NULL_OK,0,0 },
+ {TK_OPTION_ANCHOR, "-anchor", "anchor", "Anchor",
+ NULL, Tk_Offset(DisplayItem,anchorObj), -1,
+ TK_OPTION_NULL_OK, 0, GEOMETRY_CHANGED}, /* <<NOTE-ANCHOR>> */
+ {TK_OPTION_COLOR, "-background", "windowColor", "WindowColor",
+ NULL, Tk_Offset(DisplayItem,backgroundObj), -1,
+ TK_OPTION_NULL_OK,0,0 },
+ {TK_OPTION_COLOR, "-foreground", "textColor", "TextColor",
+ NULL, Tk_Offset(DisplayItem,foregroundObj), -1,
+ TK_OPTION_NULL_OK,0,0 },
+ {TK_OPTION_FONT, "-font", "font", "Font",
+ NULL, Tk_Offset(DisplayItem,fontObj), -1,
+ TK_OPTION_NULL_OK,0,GEOMETRY_CHANGED },
+
+ {TK_OPTION_END, 0,0,0, NULL, -1,-1, 0,0,0}
+};
+
+/*------------------------------------------------------------------------
+ * +++ Columns.
+ *
+ * There are separate option tables associated with the column record:
+ * ColumnOptionSpecs is for configuring the column,
+ * and HeadingOptionSpecs is for drawing headings.
+ */
+typedef struct {
+ int width; /* Column width, in pixels */
+ int minWidth; /* Minimum column width, in pixels */
+ int stretch; /* Should column stretch while resizing? */
+ Tcl_Obj *idObj; /* Column identifier, from -columns option */
+
+ Tcl_Obj *anchorObj; /* -anchor for cell data <<NOTE-ANCHOR>> */
+
+ /* Column heading data:
+ */
+ Tcl_Obj *headingObj; /* Heading label */
+ Tcl_Obj *headingImageObj; /* Heading image */
+ Tcl_Obj *headingAnchorObj; /* -anchor for heading label */
+ Tcl_Obj *headingCommandObj; /* Command to execute */
+ Tcl_Obj *headingStateObj; /* @@@ testing ... */
+ Ttk_State headingState; /* ... */
+
+ /* Temporary storage for cell data
+ */
+ Tcl_Obj *data;
+} TreeColumn;
+
+static void InitColumn(TreeColumn *column)
+{
+ column->width = 200;
+ column->minWidth = 20;
+ column->stretch = 1;
+ column->idObj = 0;
+ column->anchorObj = 0;
+
+ column->headingState = 0;
+ column->headingObj = 0;
+ column->headingImageObj = 0;
+ column->headingAnchorObj = 0;
+ column->headingStateObj = 0;
+ column->headingCommandObj = 0;
+
+ column->data = 0;
+}
+
+static void FreeColumn(TreeColumn *column)
+{
+ if (column->idObj) { Tcl_DecrRefCount(column->idObj); }
+ if (column->anchorObj) { Tcl_DecrRefCount(column->anchorObj); }
+
+ if (column->headingObj) { Tcl_DecrRefCount(column->headingObj); }
+ if (column->headingImageObj) { Tcl_DecrRefCount(column->headingImageObj); }
+ if (column->headingAnchorObj) { Tcl_DecrRefCount(column->headingAnchorObj); }
+ if (column->headingStateObj) { Tcl_DecrRefCount(column->headingStateObj); }
+ if (column->headingCommandObj) { Tcl_DecrRefCount(column->headingCommandObj); }
+
+ /* Don't touch column->data, it's scratch storage */
+}
+
+static Tk_OptionSpec ColumnOptionSpecs[] = {
+ {TK_OPTION_INT, "-width", "width", "Width",
+ DEF_COLWIDTH, -1, Tk_Offset(TreeColumn,width),
+ 0,0,GEOMETRY_CHANGED },
+ {TK_OPTION_INT, "-minwidth", "minWidth", "MinWidth",
+ DEF_MINWIDTH, -1, Tk_Offset(TreeColumn,minWidth),
+ 0,0,0 },
+ {TK_OPTION_BOOLEAN, "-stretch", "stretch", "Stretch",
+ "1", -1, Tk_Offset(TreeColumn,stretch),
+ 0,0,0 },
+ {TK_OPTION_ANCHOR, "-anchor", "anchor", "Anchor",
+ "w", Tk_Offset(TreeColumn,anchorObj), -1, /* <<NOTE-ANCHOR>> */
+ 0,0,0 },
+ {TK_OPTION_STRING, "-id", "id", "ID",
+ NULL, Tk_Offset(TreeColumn,idObj), -1,
+ TK_OPTION_NULL_OK,0,READONLY_OPTION },
+ {TK_OPTION_END, 0,0,0, NULL, -1,-1, 0,0,0}
+};
+
+static Tk_OptionSpec HeadingOptionSpecs[] = {
+ {TK_OPTION_STRING, "-text", "text", "Text",
+ "", Tk_Offset(TreeColumn,headingObj), -1,
+ 0,0,0 },
+ {TK_OPTION_STRING, "-image", "image", "Image",
+ "", Tk_Offset(TreeColumn,headingImageObj), -1,
+ 0,0,0 },
+ {TK_OPTION_ANCHOR, "-anchor", "anchor", "Anchor",
+ "center", Tk_Offset(TreeColumn,headingAnchorObj), -1,
+ 0,0,0 },
+ {TK_OPTION_STRING, "-command", "", "",
+ "", Tk_Offset(TreeColumn,headingCommandObj), -1,
+ TK_OPTION_NULL_OK,0,0 },
+ {TK_OPTION_STRING, "state", "", "",
+ "", Tk_Offset(TreeColumn,headingStateObj), -1,
+ 0,0,STATE_CHANGED },
+ {TK_OPTION_END, 0,0,0, NULL, -1,-1, 0,0,0}
+};
+
+/*------------------------------------------------------------------------
+ * +++ -show option:
+ * TODO: Implement SHOW_BRANCHES.
+ */
+
+#define SHOW_TREE (0x1) /* Show tree column? */
+#define SHOW_HEADINGS (0x2) /* Show heading row? */
+
+#define DEFAULT_SHOW "tree headings"
+
+static const char *showStrings[] = {
+ "tree", "headings", NULL
+};
+
+static int GetEnumSetFromObj(
+ Tcl_Interp *interp,
+ Tcl_Obj *objPtr,
+ const char *table[],
+ unsigned *resultPtr)
+{
+ unsigned result = 0;
+ int i, objc;
+ Tcl_Obj **objv;
+
+ if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK)
+ return TCL_ERROR;
+
+ for (i = 0; i < objc; ++i) {
+ int index;
+ if (TCL_OK != Tcl_GetIndexFromObj(
+ interp, objv[i], table, "value", TCL_EXACT, &index))
+ {
+ return TCL_ERROR;
+ }
+ result |= (1 << index);
+ }
+
+ *resultPtr = result;
+ return TCL_OK;
+}
+
+/*------------------------------------------------------------------------
+ * +++ Treeview widget record.
+ *
+ * Dependencies:
+ * columns, columnNames: -columns
+ * displayColumns: -columns, -displaycolumns
+ * headingHeight: [layout]
+ * rowHeight, indent: style
+ */
+typedef struct {
+ /* Resources acquired at initialization-time:
+ */
+ Tk_OptionTable itemOptionTable;
+ Tk_OptionTable columnOptionTable;
+ Tk_OptionTable headingOptionTable;
+ Tk_OptionTable tagOptionTable;
+ Tk_BindingTable bindingTable;
+ Ttk_TagTable tagTable;
+
+ /* Acquired in GetLayout hook:
+ */
+ Ttk_Layout itemLayout;
+ Ttk_Layout cellLayout;
+ Ttk_Layout headingLayout;
+ Ttk_Layout rowLayout;
+
+ int headingHeight; /* Space for headings */
+ int rowHeight; /* Height of each item */
+ int indent; /* #pixels horizontal offset for child items */
+
+ /* Tree data:
+ */
+ Tcl_HashTable items; /* Map: item name -> item */
+ int serial; /* Next item # for autogenerated names */
+ TreeItem *root; /* Root item */
+
+ TreeColumn column0; /* Column options for display column #0 */
+ TreeColumn *columns; /* Array of column options for data columns */
+
+ TreeItem *focus; /* Current focus item */
+ TreeItem *endPtr; /* See EndPosition() */
+
+ /* Widget options:
+ */
+ Tcl_Obj *columnsObj; /* List of symbolic column names */
+ Tcl_Obj *displayColumnsObj; /* List of columns to display */
+
+ Tcl_Obj *heightObj; /* height (rows) */
+ Tcl_Obj *paddingObj; /* internal padding */
+
+ Tcl_Obj *showObj; /* -show list */
+ Tcl_Obj *selectModeObj; /* -selectmode option */
+
+ Scrollable xscroll;
+ ScrollHandle xscrollHandle;
+ Scrollable yscroll;
+ ScrollHandle yscrollHandle;
+
+ /* Derived resources:
+ */
+ Tcl_HashTable columnNames; /* Map: column name -> column table entry */
+ int nColumns; /* #columns */
+ unsigned showFlags; /* bitmask of subparts to display */
+
+ TreeColumn **displayColumns; /* List of columns for display (incl tree) */
+ int nDisplayColumns; /* #display columns */
+ Ttk_Box headingArea; /* Display area for column headings */
+ Ttk_Box treeArea; /* Display area for tree */
+ int slack; /* Slack space (see Resizing section) */
+
+} TreePart;
+
+typedef struct {
+ WidgetCore core;
+ TreePart tree;
+} Treeview;
+
+#define USER_MASK 0x0100
+#define COLUMNS_CHANGED (USER_MASK)
+#define DCOLUMNS_CHANGED (USER_MASK<<1)
+#define SCROLLCMD_CHANGED (USER_MASK<<2)
+#define SHOW_CHANGED (USER_MASK<<3)
+
+static const char *SelectModeStrings[] = { "none", "browse", "extended", NULL };
+
+static Tk_OptionSpec TreeviewOptionSpecs[] = {
+ {TK_OPTION_STRING, "-columns", "columns", "Columns",
+ "", Tk_Offset(Treeview,tree.columnsObj), -1,
+ 0,0,COLUMNS_CHANGED | GEOMETRY_CHANGED /*| READONLY_OPTION*/ },
+ {TK_OPTION_STRING, "-displaycolumns","displayColumns","DisplayColumns",
+ "#all", Tk_Offset(Treeview,tree.displayColumnsObj), -1,
+ 0,0,DCOLUMNS_CHANGED | GEOMETRY_CHANGED },
+ {TK_OPTION_STRING, "-show", "show", "Show",
+ DEFAULT_SHOW, Tk_Offset(Treeview,tree.showObj), -1,
+ 0,0,SHOW_CHANGED | GEOMETRY_CHANGED },
+
+ {TK_OPTION_STRING_TABLE, "-selectmode", "selectMode", "SelectMode",
+ "extended", Tk_Offset(Treeview,tree.selectModeObj), -1,
+ 0,(ClientData)SelectModeStrings,0 },
+
+ {TK_OPTION_PIXELS, "-height", "height", "Height",
+ DEF_TREE_ROWS, Tk_Offset(Treeview,tree.heightObj), -1,
+ 0,0,GEOMETRY_CHANGED},
+ {TK_OPTION_STRING, "-padding", "padding", "Pad",
+ NULL, Tk_Offset(Treeview,tree.paddingObj), -1,
+ TK_OPTION_NULL_OK,0,GEOMETRY_CHANGED },
+
+ {TK_OPTION_STRING, "-xscrollcommand", "xScrollCommand", "ScrollCommand",
+ NULL, -1, Tk_Offset(Treeview, tree.xscroll.scrollCmd),
+ TK_OPTION_NULL_OK, 0, SCROLLCMD_CHANGED},
+ {TK_OPTION_STRING, "-yscrollcommand", "yScrollCommand", "ScrollCommand",
+ NULL, -1, Tk_Offset(Treeview, tree.yscroll.scrollCmd),
+ TK_OPTION_NULL_OK, 0, SCROLLCMD_CHANGED},
+
+ WIDGET_TAKEFOCUS_TRUE,
+ WIDGET_INHERIT_OPTIONS(ttkCoreOptionSpecs)
+};
+
+/*------------------------------------------------------------------------
+ * +++ Utilities.
+ */
+typedef void (*HashEntryIterator)(void *hashValue);
+
+static void foreachHashEntry(Tcl_HashTable *ht, HashEntryIterator func)
+{
+ Tcl_HashSearch search;
+ Tcl_HashEntry *entryPtr = Tcl_FirstHashEntry(ht, &search);
+ while (entryPtr != NULL) {
+ func(Tcl_GetHashValue(entryPtr));
+ entryPtr = Tcl_NextHashEntry(&search);
+ }
+}
+
+/* + unshareObj(objPtr) --
+ * Ensure that a Tcl_Obj * has refcount 1 -- either return objPtr
+ * itself, or a duplicated copy.
+ */
+static Tcl_Obj *unshareObj(Tcl_Obj *objPtr)
+{
+ if (Tcl_IsShared(objPtr)) {
+ Tcl_Obj *newObj = Tcl_DuplicateObj(objPtr);
+ Tcl_DecrRefCount(objPtr);
+ Tcl_IncrRefCount(newObj);
+ return newObj;
+ }
+ return objPtr;
+}
+
+/* DisplayLayout --
+ * Rebind, place, and draw a layout + object combination.
+ */
+static void DisplayLayout(
+ Ttk_Layout layout, void *recordPtr, Ttk_State state, Ttk_Box b, Drawable d)
+{
+ Ttk_RebindSublayout(layout, recordPtr);
+ Ttk_PlaceLayout(layout, state, b);
+ Ttk_DrawLayout(layout, state, d);
+}
+
+/* + GetColumn --
+ * Look up column by name or number.
+ * Returns: pointer to column table entry, NULL if not found.
+ * Leaves an error message in interp->result on error.
+ */
+static TreeColumn *GetColumn(
+ Tcl_Interp *interp, Treeview *tv, Tcl_Obj *columnIDObj)
+{
+ Tcl_HashEntry *entryPtr;
+ int columnIndex;
+
+ /* Check for named column:
+ */
+ entryPtr = Tcl_FindHashEntry(
+ &tv->tree.columnNames, Tcl_GetString(columnIDObj));
+ if (entryPtr) {
+ return Tcl_GetHashValue(entryPtr);
+ }
+
+ /* Check for number:
+ */
+ if (Tcl_GetIntFromObj(NULL, columnIDObj, &columnIndex) == TCL_OK) {
+ if (columnIndex < 0 || columnIndex >= tv->tree.nColumns) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp,
+ "Column index ",
+ Tcl_GetString(columnIDObj),
+ " out of bounds",
+ NULL);
+ return NULL;
+ }
+
+ return tv->tree.columns + columnIndex;
+ }
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp,
+ "Invalid column index ", Tcl_GetString(columnIDObj),
+ NULL);
+ return NULL;
+}
+
+/* + FindColumn --
+ * Look up column by name, number, or display index.
+ */
+static TreeColumn *FindColumn(
+ Tcl_Interp *interp, Treeview *tv, Tcl_Obj *columnIDObj)
+{
+ int colno;
+
+ if (sscanf(Tcl_GetString(columnIDObj), "#%d", &colno) == 1)
+ { /* Display column specification, #n */
+ if (colno >= 0 && colno < tv->tree.nDisplayColumns) {
+ return tv->tree.displayColumns[colno];
+ }
+ /* else */
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp,
+ "Column ", Tcl_GetString(columnIDObj), " out of range",
+ NULL);
+ return NULL;
+ }
+
+ return GetColumn(interp, tv, columnIDObj);
+}
+
+/* + FindItem --
+ * Locates the item with the specified identifier in the tree.
+ * If there is no such item, leaves an error message in interp.
+ */
+static TreeItem *FindItem(
+ Tcl_Interp *interp, Treeview *tv, Tcl_Obj *itemNameObj)
+{
+ const char *itemName = Tcl_GetString(itemNameObj);
+ Tcl_HashEntry *entryPtr = Tcl_FindHashEntry(&tv->tree.items, itemName);
+
+ if (!entryPtr) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "Item ", itemName, " not found", NULL);
+ return 0;
+ }
+ return Tcl_GetHashValue(entryPtr);
+}
+
+/* + GetItemListFromObj --
+ * Parse a Tcl_Obj * as a list of items.
+ * Returns a NULL-terminated array of items; result must
+ * be ckfree()d. On error, returns NULL and leaves an error
+ * message in interp.
+ */
+
+static TreeItem **GetItemListFromObj(
+ Tcl_Interp *interp, Treeview *tv, Tcl_Obj *objPtr)
+{
+ TreeItem **items;
+ Tcl_Obj **elements;
+ int i, nElements;
+
+ if (Tcl_ListObjGetElements(interp,objPtr,&nElements,&elements) != TCL_OK) {
+ return NULL;
+ }
+
+ items = (TreeItem**)ckalloc((nElements + 1)*sizeof(TreeItem*));
+ for (i = 0; i < nElements; ++i) {
+ items[i] = FindItem(interp, tv, elements[i]);
+ if (!items[i]) {
+ ckfree((ClientData)items);
+ return NULL;
+ }
+ }
+ items[i] = NULL;
+ return items;
+}
+
+/* + ItemName --
+ * Returns the item's ID.
+ */
+static const char *ItemName(Treeview *tv, TreeItem *item)
+{
+ return Tcl_GetHashKey(&tv->tree.items, item->entryPtr);
+}
+
+/* + ItemID --
+ * Returns a fresh Tcl_Obj * (refcount 0) holding the
+ * item identifier of the specified item.
+ */
+static Tcl_Obj *ItemID(Treeview *tv, TreeItem *item)
+{
+ return Tcl_NewStringObj(ItemName(tv, item), -1);
+}
+
+/*------------------------------------------------------------------------
+ * +++ Column configuration.
+ */
+
+/* + TreeviewFreeColumns --
+ * Free column data.
+ */
+static void TreeviewFreeColumns(Treeview *tv)
+{
+ int i;
+
+ Tcl_DeleteHashTable(&tv->tree.columnNames);
+ Tcl_InitHashTable(&tv->tree.columnNames, TCL_STRING_KEYS);
+
+ if (tv->tree.columns) {
+ for (i = 0; i < tv->tree.nColumns; ++i)
+ FreeColumn(tv->tree.columns + i);
+ ckfree((ClientData)tv->tree.columns);
+ tv->tree.columns = 0;
+ }
+}
+
+/* + TreeviewInitColumns --
+ * Initialize column data when -columns changes.
+ * Returns: TCL_OK or TCL_ERROR;
+ */
+static int TreeviewInitColumns(Tcl_Interp *interp, Treeview *tv)
+{
+ Tcl_Obj **columns;
+ int i, ncols;
+
+ if (Tcl_ListObjGetElements(
+ interp, tv->tree.columnsObj, &ncols, &columns) != TCL_OK)
+ {
+ return TCL_ERROR;
+ }
+
+ /*
+ * Free old values:
+ */
+ TreeviewFreeColumns(tv);
+
+ /*
+ * Initialize columns array and columnNames hash table:
+ */
+ tv->tree.nColumns = ncols;
+ tv->tree.columns =
+ (TreeColumn*)ckalloc(tv->tree.nColumns * sizeof(TreeColumn));
+
+ for (i = 0; i < ncols; ++i) {
+ int isNew;
+ Tcl_Obj *columnName = Tcl_DuplicateObj(columns[i]);
+
+ Tcl_HashEntry *entryPtr = Tcl_CreateHashEntry(
+ &tv->tree.columnNames, Tcl_GetString(columnName), &isNew);
+ Tcl_SetHashValue(entryPtr, tv->tree.columns + i);
+
+ InitColumn(tv->tree.columns + i);
+ Tk_InitOptions(
+ interp, (ClientData)(tv->tree.columns + i),
+ tv->tree.columnOptionTable, tv->core.tkwin);
+ Tk_InitOptions(
+ interp, (ClientData)(tv->tree.columns + i),
+ tv->tree.headingOptionTable, tv->core.tkwin);
+ Tcl_IncrRefCount(columnName);
+ tv->tree.columns[i].idObj = columnName;
+ }
+
+ return TCL_OK;
+}
+
+/* + TreeviewInitDisplayColumns --
+ * Initializes the 'displayColumns' array.
+ *
+ * Note that displayColumns[0] is always the tree column,
+ * even when SHOW_TREE is not set.
+ *
+ * @@@ TODO: disallow duplicated columns
+ */
+static int TreeviewInitDisplayColumns(Tcl_Interp *interp, Treeview *tv)
+{
+ Tcl_Obj **dcolumns;
+ int index, ndcols;
+ TreeColumn **displayColumns = 0;
+
+ if (Tcl_ListObjGetElements(interp,
+ tv->tree.displayColumnsObj, &ndcols, &dcolumns) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ if (!strcmp(Tcl_GetString(tv->tree.displayColumnsObj), "#all")) {
+ ndcols = tv->tree.nColumns;
+ displayColumns = (TreeColumn**)ckalloc((ndcols+1)*sizeof(TreeColumn*));
+ for (index = 0; index < ndcols; ++index) {
+ displayColumns[index+1] = tv->tree.columns + index;
+ }
+ } else {
+ displayColumns = (TreeColumn**)ckalloc((ndcols+1)*sizeof(TreeColumn*));
+ for (index = 0; index < ndcols; ++index) {
+ displayColumns[index+1] = GetColumn(interp, tv, dcolumns[index]);
+ if (!displayColumns[index+1]) {
+ ckfree((ClientData)displayColumns);
+ return TCL_ERROR;
+ }
+ }
+ }
+ displayColumns[0] = &tv->tree.column0;
+
+ if (tv->tree.displayColumns)
+ ckfree((ClientData)tv->tree.displayColumns);
+ tv->tree.displayColumns = displayColumns;
+ tv->tree.nDisplayColumns = ndcols + 1;
+
+ return TCL_OK;
+}
+
+/*------------------------------------------------------------------------
+ * +++ Resizing.
+ * slack invariant: TreeWidth(tree) + slack = treeArea.width
+ */
+
+#define FirstColumn(tv) ((tv->tree.showFlags&SHOW_TREE) ? 0 : 1)
+
+/* + TreeWidth --
+ * Compute the requested tree width from the sum of visible column widths.
+ */
+static int TreeWidth(Treeview *tv)
+{
+ int i = FirstColumn(tv);
+ int width = 0;
+
+ while (i < tv->tree.nDisplayColumns) {
+ width += tv->tree.displayColumns[i++]->width;
+ }
+ return width;
+}
+
+/* + RecomputeSlack --
+ */
+static void RecomputeSlack(Treeview *tv)
+{
+ tv->tree.slack = tv->tree.treeArea.width - TreeWidth(tv);
+}
+
+/* + PickupSlack/DepositSlack --
+ * When resizing columns, distribute extra space to 'slack' first,
+ * and only adjust column widths if 'slack' goes to zero.
+ * That is, don't bother changing column widths if the tree
+ * is already scrolled or short.
+ */
+static int PickupSlack(Treeview *tv, int extra)
+{
+ int newSlack = tv->tree.slack + extra;
+
+ if ( (newSlack < 0 && 0 <= tv->tree.slack)
+ || (newSlack > 0 && 0 >= tv->tree.slack))
+ {
+ tv->tree.slack = 0;
+ return newSlack;
+ } else {
+ tv->tree.slack = newSlack;
+ return 0;
+ }
+}
+
+static void DepositSlack(Treeview *tv, int extra)
+{
+ tv->tree.slack += extra;
+}
+
+/* + Stretch --
+ * Adjust width of column by N pixels, down to minimum width.
+ * Returns: #pixels actually moved.
+ */
+static int Stretch(TreeColumn *c, int n)
+{
+ int newWidth = n + c->width;
+ if (newWidth < c->minWidth) {
+ n = c->minWidth - c->width;
+ c->width = c->minWidth;
+ } else {
+ c->width = newWidth;
+ }
+ return n;
+}
+
+/* + ShoveLeft --
+ * Adjust width of (stretchable) columns to the left by N pixels.
+ * Returns: leftover slack.
+ */
+static int ShoveLeft(Treeview *tv, int i, int n)
+{
+ int first = FirstColumn(tv);
+ while (n != 0 && i >= first) {
+ TreeColumn *c = tv->tree.displayColumns[i];
+ if (c->stretch) {
+ n -= Stretch(c, n);
+ }
+ --i;
+ }
+ return n;
+}
+
+/* + ShoveRight --
+ * Adjust width of (stretchable) columns to the right by N pixels.
+ * Returns: leftover slack.
+ */
+static int ShoveRight(Treeview *tv, int i, int n)
+{
+ while (n != 0 && i < tv->tree.nDisplayColumns) {
+ TreeColumn *c = tv->tree.displayColumns[i];
+ if (c->stretch) {
+ n -= Stretch(c, n);
+ }
+ ++i;
+ }
+ return n;
+}
+
+/* + DistributeWidth --
+ * Distribute n pixels evenly across all stretchable display columns.
+ * Returns: leftover slack.
+ * Notes:
+ * The "((++w % m) < r)" term is there so that the remainder r = n % m
+ * is distributed round-robin.
+ */
+static int DistributeWidth(Treeview *tv, int n)
+{
+ int w = TreeWidth(tv);
+ int m = 0;
+ int i, d, r;
+
+ for (i = FirstColumn(tv); i < tv->tree.nDisplayColumns; ++i) {
+ if (tv->tree.displayColumns[i]->stretch) {
+ ++m;
+ }
+ }
+ if (m == 0) {
+ return n;
+ }
+
+ d = n / m;
+ r = n % m;
+ if (r < 0) { r += m; --d; }
+
+ for (i = FirstColumn(tv); i < tv->tree.nDisplayColumns; ++i) {
+ TreeColumn *c = tv->tree.displayColumns[i];
+ if (c->stretch) {
+ n -= Stretch(c, d + ((++w % m) < r));
+ }
+ }
+ return n;
+}
+
+/* + ResizeColumns --
+ * Recompute column widths based on available width.
+ * Pick up slack first;
+ * Distribute the remainder evenly across stretchable columns;
+ * If any is still left over due to minwidth constraints, shove left.
+ */
+static void ResizeColumns(Treeview *tv, int newWidth)
+{
+ int delta = newWidth - (TreeWidth(tv) + tv->tree.slack);
+ DepositSlack(tv,
+ ShoveLeft(tv, tv->tree.nDisplayColumns - 1,
+ DistributeWidth(tv, PickupSlack(tv, delta))));
+}
+
+/* + DragColumn --
+ * Move the separator to the right of specified column,
+ * adjusting other column widths as necessary.
+ */
+static void DragColumn(Treeview *tv, int i, int delta)
+{
+ TreeColumn *c = tv->tree.displayColumns[i];
+ int dl = delta - ShoveLeft(tv, i-1, delta - Stretch(c, delta));
+ int dr = ShoveRight(tv, i+1, PickupSlack(tv, -dl));
+ DepositSlack(tv, dr);
+}
+
+/*------------------------------------------------------------------------
+ * +++ Event handlers.
+ */
+
+static TreeItem *IdentifyItem(Treeview *tv, int y); /*forward*/
+
+static const unsigned int TreeviewBindEventMask =
+ KeyPressMask|KeyReleaseMask
+ | ButtonPressMask|ButtonReleaseMask
+ | PointerMotionMask|ButtonMotionMask
+ | VirtualEventMask
+ ;
+
+static void TreeviewBindEventProc(void *clientData, XEvent *event)
+{
+ Treeview *tv = clientData;
+ TreeItem *item = NULL;
+ Ttk_TagSet tagset;
+
+ /*
+ * Figure out where to deliver the event.
+ */
+ switch (event->type)
+ {
+ case KeyPress:
+ case KeyRelease:
+ case VirtualEvent:
+ item = tv->tree.focus;
+ break;
+ case ButtonPress:
+ case ButtonRelease:
+ item = IdentifyItem(tv, event->xbutton.y);
+ break;
+ case MotionNotify:
+ item = IdentifyItem(tv, event->xmotion.y);
+ break;
+ default:
+ break;
+ }
+
+ if (!item) {
+ return;
+ }
+
+ /* ASSERT: Ttk_GetTagSetFromObj succeeds.
+ * NB: must use a local copy of the tagset,
+ * in case a binding script stomps on -tags.
+ */
+ tagset = Ttk_GetTagSetFromObj(NULL, tv->tree.tagTable, item->tagsObj);
+
+ /*
+ * Fire binding:
+ */
+ Tcl_Preserve(clientData);
+ Tk_BindEvent(tv->tree.bindingTable, event, tv->core.tkwin,
+ tagset->nTags, (void **)tagset->tags);
+ Tcl_Release(clientData);
+
+ Ttk_FreeTagSet(tagset);
+}
+
+/*------------------------------------------------------------------------
+ * +++ Initialization and cleanup.
+ */
+
+static void TreeviewInitialize(Tcl_Interp *interp, void *recordPtr)
+{
+ Treeview *tv = recordPtr;
+ int unused;
+
+ tv->tree.itemOptionTable =
+ Tk_CreateOptionTable(interp, ItemOptionSpecs);
+ tv->tree.columnOptionTable =
+ Tk_CreateOptionTable(interp, ColumnOptionSpecs);
+ tv->tree.headingOptionTable =
+ Tk_CreateOptionTable(interp, HeadingOptionSpecs);
+ tv->tree.tagOptionTable =
+ Tk_CreateOptionTable(interp, TagOptionSpecs);
+
+ tv->tree.tagTable = Ttk_CreateTagTable(
+ interp, tv->core.tkwin, TagOptionSpecs, sizeof(DisplayItem));
+ tv->tree.bindingTable = Tk_CreateBindingTable(interp);
+ Tk_CreateEventHandler(tv->core.tkwin,
+ TreeviewBindEventMask, TreeviewBindEventProc, tv);
+
+ tv->tree.itemLayout
+ = tv->tree.cellLayout
+ = tv->tree.headingLayout
+ = tv->tree.rowLayout
+ = 0;
+ tv->tree.headingHeight = tv->tree.rowHeight = DEFAULT_ROWHEIGHT;
+ tv->tree.indent = DEFAULT_INDENT;
+
+ Tcl_InitHashTable(&tv->tree.columnNames, TCL_STRING_KEYS);
+ tv->tree.nColumns = tv->tree.nDisplayColumns = 0;
+ tv->tree.columns = NULL;
+ tv->tree.displayColumns = NULL;
+ tv->tree.showFlags = ~0;
+
+ InitColumn(&tv->tree.column0);
+ Tk_InitOptions(
+ interp, (ClientData)(&tv->tree.column0),
+ tv->tree.columnOptionTable, tv->core.tkwin);
+ Tk_InitOptions(
+ interp, (ClientData)(&tv->tree.column0),
+ tv->tree.headingOptionTable, tv->core.tkwin);
+
+ Tcl_InitHashTable(&tv->tree.items, TCL_STRING_KEYS);
+ tv->tree.serial = 0;
+
+ tv->tree.focus = tv->tree.endPtr = 0;
+
+ /* Create root item "":
+ */
+ tv->tree.root = NewItem();
+ Tk_InitOptions(interp, (ClientData)tv->tree.root,
+ tv->tree.itemOptionTable, tv->core.tkwin);
+ tv->tree.root->tagset = Ttk_GetTagSetFromObj(NULL, tv->tree.tagTable, NULL);
+ tv->tree.root->entryPtr = Tcl_CreateHashEntry(&tv->tree.items, "", &unused);
+ Tcl_SetHashValue(tv->tree.root->entryPtr, tv->tree.root);
+
+ /* Scroll handles:
+ */
+ tv->tree.xscrollHandle = TtkCreateScrollHandle(&tv->core,&tv->tree.xscroll);
+ tv->tree.yscrollHandle = TtkCreateScrollHandle(&tv->core,&tv->tree.yscroll);
+
+ /* Size parameters:
+ */
+ tv->tree.treeArea = tv->tree.headingArea = Ttk_MakeBox(0,0,0,0);
+ tv->tree.slack = 0;
+}
+
+static void TreeviewCleanup(void *recordPtr)
+{
+ Treeview *tv = recordPtr;
+
+ Tk_DeleteEventHandler(tv->core.tkwin,
+ TreeviewBindEventMask, TreeviewBindEventProc, tv);
+ Tk_DeleteBindingTable(tv->tree.bindingTable);
+ Ttk_DeleteTagTable(tv->tree.tagTable);
+
+ if (tv->tree.itemLayout) Ttk_FreeLayout(tv->tree.itemLayout);
+ if (tv->tree.cellLayout) Ttk_FreeLayout(tv->tree.cellLayout);
+ if (tv->tree.headingLayout) Ttk_FreeLayout(tv->tree.headingLayout);
+ if (tv->tree.rowLayout) Ttk_FreeLayout(tv->tree.rowLayout);
+
+ TreeviewFreeColumns(tv);
+
+ if (tv->tree.displayColumns)
+ Tcl_Free((ClientData)tv->tree.displayColumns);
+
+ foreachHashEntry(&tv->tree.items, FreeItemCB);
+ Tcl_DeleteHashTable(&tv->tree.items);
+
+ TtkFreeScrollHandle(tv->tree.xscrollHandle);
+ TtkFreeScrollHandle(tv->tree.yscrollHandle);
+}
+
+/* + TreeviewConfigure --
+ * Configuration widget hook.
+ *
+ * BUG: If user sets -columns and -displaycolumns, but -displaycolumns
+ * has an error, the widget is left in an inconsistent state.
+ */
+static int
+TreeviewConfigure(Tcl_Interp *interp, void *recordPtr, int mask)
+{
+ Treeview *tv = recordPtr;
+ unsigned showFlags = tv->tree.showFlags;
+
+ if (mask & COLUMNS_CHANGED) {
+ if (TreeviewInitColumns(interp, tv) != TCL_OK)
+ return TCL_ERROR;
+ mask |= DCOLUMNS_CHANGED;
+ }
+ if (mask & DCOLUMNS_CHANGED) {
+ if (TreeviewInitDisplayColumns(interp, tv) != TCL_OK)
+ return TCL_ERROR;
+ }
+ if (mask & SCROLLCMD_CHANGED) {
+ TtkScrollbarUpdateRequired(tv->tree.xscrollHandle);
+ TtkScrollbarUpdateRequired(tv->tree.yscrollHandle);
+ }
+ if ( (mask & SHOW_CHANGED)
+ && GetEnumSetFromObj(
+ interp,tv->tree.showObj,showStrings,&showFlags) != TCL_OK)
+ {
+ return TCL_ERROR;
+ }
+
+ if (TtkCoreConfigure(interp, recordPtr, mask) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ tv->tree.showFlags = showFlags;
+
+ if (mask & (SHOW_CHANGED | DCOLUMNS_CHANGED)) {
+ RecomputeSlack(tv);
+ }
+ return TCL_OK;
+}
+
+/* + ConfigureItem --
+ * Set item options.
+ */
+static int ConfigureItem(
+ Tcl_Interp *interp, Treeview *tv, TreeItem *item,
+ int objc, Tcl_Obj *const objv[])
+{
+ Tk_SavedOptions savedOptions;
+ int mask;
+ Ttk_ImageSpec *newImageSpec = NULL;
+ Ttk_TagSet newTagSet = NULL;
+
+ if (Tk_SetOptions(interp, (ClientData)item, tv->tree.itemOptionTable,
+ objc, objv, tv->core.tkwin, &savedOptions, &mask)
+ != TCL_OK)
+ {
+ return TCL_ERROR;
+ }
+
+ /* Make sure that -values is a valid list:
+ */
+ if (item->valuesObj) {
+ int unused;
+ if (Tcl_ListObjLength(interp, item->valuesObj, &unused) != TCL_OK)
+ goto error;
+ }
+
+ /* Check -image.
+ */
+ if ((mask & ITEM_OPTION_IMAGE_CHANGED) && item->imageObj) {
+ newImageSpec = TtkGetImageSpec(interp, tv->core.tkwin, item->imageObj);
+ if (!newImageSpec) {
+ goto error;
+ }
+ }
+
+ /* Check -tags.
+ * Side effect: may create new tags.
+ */
+ if (mask & ITEM_OPTION_TAGS_CHANGED) {
+ newTagSet = Ttk_GetTagSetFromObj(
+ interp, tv->tree.tagTable, item->tagsObj);
+ if (!newTagSet) {
+ goto error;
+ }
+ }
+
+ /* Keep TTK_STATE_OPEN flag in sync with item->openObj.
+ * We use both a state flag and a Tcl_Obj* resource so elements
+ * can access the value in either way.
+ */
+ if (item->openObj) {
+ int isOpen;
+ if (Tcl_GetBooleanFromObj(interp, item->openObj, &isOpen) != TCL_OK)
+ goto error;
+ if (isOpen)
+ item->state |= TTK_STATE_OPEN;
+ else
+ item->state &= ~TTK_STATE_OPEN;
+ }
+
+ /* All OK.
+ */
+ Tk_FreeSavedOptions(&savedOptions);
+ if (mask & ITEM_OPTION_TAGS_CHANGED) {
+ if (item->tagset) { Ttk_FreeTagSet(item->tagset); }
+ item->tagset = newTagSet;
+ }
+ if (mask & ITEM_OPTION_IMAGE_CHANGED) {
+ if (item->imagespec) { TtkFreeImageSpec(item->imagespec); }
+ item->imagespec = newImageSpec;
+ }
+ TtkRedisplayWidget(&tv->core);
+ return TCL_OK;
+
+error:
+ Tk_RestoreSavedOptions(&savedOptions);
+ if (newTagSet) { Ttk_FreeTagSet(newTagSet); }
+ if (newImageSpec) { TtkFreeImageSpec(newImageSpec); }
+ return TCL_ERROR;
+}
+
+/* + ConfigureColumn --
+ * Set column options.
+ */
+static int ConfigureColumn(
+ Tcl_Interp *interp, Treeview *tv, TreeColumn *column,
+ int objc, Tcl_Obj *const objv[])
+{
+ Tk_SavedOptions savedOptions;
+ int mask;
+
+ if (Tk_SetOptions(interp, (ClientData)column,
+ tv->tree.columnOptionTable, objc, objv, tv->core.tkwin,
+ &savedOptions,&mask) != TCL_OK)
+ {
+ return TCL_ERROR;
+ }
+
+ if (mask & READONLY_OPTION) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "Attempt to change read-only option", NULL);
+ goto error;
+ }
+
+ /* Propagate column width changes to overall widget request width,
+ * but only if the widget is currently unmapped, in order to prevent
+ * geometry jumping during interactive column resize.
+ */
+ if (mask & GEOMETRY_CHANGED) {
+ if (!Tk_IsMapped(tv->core.tkwin)) {
+ TtkResizeWidget(&tv->core);
+ }
+ RecomputeSlack(tv);
+ }
+ TtkRedisplayWidget(&tv->core);
+
+ /* ASSERT: SLACKINVARIANT */
+
+ Tk_FreeSavedOptions(&savedOptions);
+ return TCL_OK;
+
+error:
+ Tk_RestoreSavedOptions(&savedOptions);
+ return TCL_ERROR;
+}
+
+/* + ConfigureHeading --
+ * Set heading options.
+ */
+static int ConfigureHeading(
+ Tcl_Interp *interp, Treeview *tv, TreeColumn *column,
+ int objc, Tcl_Obj *const objv[])
+{
+ Tk_SavedOptions savedOptions;
+ int mask;
+
+ if (Tk_SetOptions(interp, (ClientData)column,
+ tv->tree.headingOptionTable, objc, objv, tv->core.tkwin,
+ &savedOptions,&mask) != TCL_OK)
+ {
+ return TCL_ERROR;
+ }
+
+ /* @@@ testing ... */
+ if ((mask & STATE_CHANGED) && column->headingStateObj) {
+ Ttk_StateSpec stateSpec;
+ if (Ttk_GetStateSpecFromObj(
+ interp, column->headingStateObj, &stateSpec) != TCL_OK)
+ {
+ goto error;
+ }
+ column->headingState = Ttk_ModifyState(column->headingState,&stateSpec);
+ Tcl_DecrRefCount(column->headingStateObj);
+ column->headingStateObj = Ttk_NewStateSpecObj(column->headingState,0);
+ Tcl_IncrRefCount(column->headingStateObj);
+ }
+
+ TtkRedisplayWidget(&tv->core);
+ Tk_FreeSavedOptions(&savedOptions);
+ return TCL_OK;
+
+error:
+ Tk_RestoreSavedOptions(&savedOptions);
+ return TCL_ERROR;
+}
+
+/*------------------------------------------------------------------------
+ * +++ Geometry routines.
+ */
+
+/* + CountRows --
+ * Returns the number of viewable rows rooted at item
+ */
+static int CountRows(TreeItem *item)
+{
+ int rows = 1;
+
+ if (item->state & TTK_STATE_OPEN) {
+ TreeItem *child = item->children;
+ while (child) {
+ rows += CountRows(child);
+ child = child->next;
+ }
+ }
+ return rows;
+}
+
+/* + IdentifyRow --
+ * Recursive search for item at specified y position.
+ * Main work routine for IdentifyItem()
+ */
+static TreeItem *IdentifyRow(
+ Treeview *tv, /* Widget record */
+ TreeItem *item, /* Where to start search */
+ int *ypos, /* Scan position */
+ int y) /* Target y coordinate */
+{
+ while (item) {
+ int next_ypos = *ypos + tv->tree.rowHeight;
+ if (*ypos <= y && y <= next_ypos) {
+ return item;
+ }
+ *ypos = next_ypos;
+ if (item->state & TTK_STATE_OPEN) {
+ TreeItem *subitem = IdentifyRow(tv, item->children, ypos, y);
+ if (subitem) {
+ return subitem;
+ }
+ }
+ item = item->next;
+ }
+ return 0;
+}
+
+/* + IdentifyItem --
+ * Locate the item at the specified y position, if any.
+ */
+static TreeItem *IdentifyItem(Treeview *tv, int y)
+{
+ int rowHeight = tv->tree.rowHeight;
+ int ypos = tv->tree.treeArea.y - rowHeight * tv->tree.yscroll.first;
+ return IdentifyRow(tv, tv->tree.root->children, &ypos, y);
+}
+
+/* + IdentifyDisplayColumn --
+ * Returns the display column number at the specified x position,
+ * or -1 if x is outside any columns.
+ */
+static int IdentifyDisplayColumn(Treeview *tv, int x, int *x1)
+{
+ int colno = FirstColumn(tv);
+ int xpos = tv->tree.treeArea.x - tv->tree.xscroll.first;
+
+ while (colno < tv->tree.nDisplayColumns) {
+ TreeColumn *column = tv->tree.displayColumns[colno];
+ int next_xpos = xpos + column->width;
+ if (xpos <= x && x <= next_xpos + HALO) {
+ *x1 = next_xpos;
+ return colno;
+ }
+ ++colno;
+ xpos = next_xpos;
+ }
+
+ return -1;
+}
+
+/* + RowNumber --
+ * Calculate which row the specified item appears on;
+ * returns -1 if the item is not viewable.
+ * Xref: DrawForest, IdentifyItem.
+ */
+static int RowNumber(Treeview *tv, TreeItem *item)
+{
+ TreeItem *p = tv->tree.root->children;
+ int n = 0;
+
+ while (p) {
+ if (p == item)
+ return n;
+
+ ++n;
+
+ /* Find next viewable item in preorder traversal order
+ */
+ if (p->children && (p->state & TTK_STATE_OPEN)) {
+ p = p->children;
+ } else {
+ while (!p->next && p && p->parent)
+ p = p->parent;
+ if (p)
+ p = p->next;
+ }
+ }
+
+ return -1;
+}
+
+/* + ItemDepth -- return the depth of a tree item.
+ * The depth of an item is equal to the number of proper ancestors,
+ * not counting the root node.
+ */
+static int ItemDepth(TreeItem *item)
+{
+ int depth = 0;
+ while (item->parent) {
+ ++depth;
+ item = item->parent;
+ }
+ return depth-1;
+}
+
+/* + ItemRow --
+ * Returns row number of specified item relative to root,
+ * -1 if item is not viewable.
+ */
+static int ItemRow(Treeview *tv, TreeItem *p)
+{
+ TreeItem *root = tv->tree.root;
+ int rowNumber = 0;
+
+ for (;;) {
+ if (p->prev) {
+ p = p->prev;
+ rowNumber += CountRows(p);
+ } else {
+ p = p->parent;
+ if (!(p && (p->state & TTK_STATE_OPEN))) {
+ /* detached or closed ancestor */
+ return -1;
+ }
+ if (p == root) {
+ return rowNumber;
+ }
+ ++rowNumber;
+ }
+ }
+}
+
+/* + BoundingBox --
+ * Compute the parcel of the specified column of the specified item,
+ * (or the entire item if column is NULL)
+ * Returns: 0 if item or column is not viewable, 1 otherwise.
+ */
+static int BoundingBox(
+ Treeview *tv, /* treeview widget */
+ TreeItem *item, /* desired item */
+ TreeColumn *column, /* desired column */
+ Ttk_Box *bbox_rtn) /* bounding box of item */
+{
+ int row = ItemRow(tv, item);
+ Ttk_Box bbox = tv->tree.treeArea;
+
+ if (row < tv->tree.yscroll.first || row > tv->tree.yscroll.last) {
+ /* not viewable, or off-screen */
+ return 0;
+ }
+
+ bbox.y += (row - tv->tree.yscroll.first) * tv->tree.rowHeight;
+ bbox.height = tv->tree.rowHeight;
+
+ bbox.x -= tv->tree.xscroll.first;
+ bbox.width = TreeWidth(tv);
+
+ if (column) {
+ int xpos = 0, i = FirstColumn(tv);
+ while (i < tv->tree.nDisplayColumns) {
+ if (tv->tree.displayColumns[i] == column) {
+ break;
+ }
+ xpos += tv->tree.displayColumns[i]->width;
+ ++i;
+ }
+ if (i == tv->tree.nDisplayColumns) { /* specified column unviewable */
+ return 0;
+ }
+ bbox.x += xpos;
+ bbox.width = column->width;
+
+ /* Account for indentation in tree column:
+ */
+ if (column == &tv->tree.column0) {
+ int indent = tv->tree.indent * ItemDepth(item);
+ bbox.x += indent;
+ bbox.width -= indent;
+ }
+ }
+ *bbox_rtn = bbox;
+ return 1;
+}
+
+/* + IdentifyRegion --
+ */
+
+typedef enum {
+ REGION_NOTHING = 0,
+ REGION_HEADING,
+ REGION_SEPARATOR,
+ REGION_TREE,
+ REGION_CELL
+} TreeRegion;
+
+static const char *regionStrings[] = {
+ "nothing", "heading", "separator", "tree", "cell", 0
+};
+
+static TreeRegion IdentifyRegion(Treeview *tv, int x, int y)
+{
+ int x1 = 0, colno;
+
+ colno = IdentifyDisplayColumn(tv, x, &x1);
+ if (Ttk_BoxContains(tv->tree.headingArea, x, y)) {
+ if (colno < 0) {
+ return REGION_NOTHING;
+ } else if (-HALO <= x1 - x && x1 - x <= HALO) {
+ return REGION_SEPARATOR;
+ } else {
+ return REGION_HEADING;
+ }
+ } else if (Ttk_BoxContains(tv->tree.treeArea, x, y)) {
+ TreeItem *item = IdentifyItem(tv, y);
+ if (item && colno > 0) {
+ return REGION_CELL;
+ } else if (item) {
+ return REGION_TREE;
+ }
+ }
+ return REGION_NOTHING;
+}
+
+/*------------------------------------------------------------------------
+ * +++ Display routines.
+ */
+
+/* + GetSublayout --
+ * Utility routine; acquires a sublayout for items, cells, etc.
+ */
+static Ttk_Layout GetSublayout(
+ Tcl_Interp *interp,
+ Ttk_Theme themePtr,
+ Ttk_Layout parentLayout,
+ const char *layoutName,
+ Tk_OptionTable optionTable,
+ Ttk_Layout *layoutPtr)
+{
+ Ttk_Layout newLayout = Ttk_CreateSublayout(
+ interp, themePtr, parentLayout, layoutName, optionTable);
+
+ if (newLayout) {
+ if (*layoutPtr)
+ Ttk_FreeLayout(*layoutPtr);
+ *layoutPtr = newLayout;
+ }
+ return newLayout;
+}
+
+/* + TreeviewGetLayout --
+ * GetLayout() widget hook.
+ */
+static Ttk_Layout TreeviewGetLayout(
+ Tcl_Interp *interp, Ttk_Theme themePtr, void *recordPtr)
+{
+ Treeview *tv = recordPtr;
+ Ttk_Layout treeLayout = TtkWidgetGetLayout(interp, themePtr, recordPtr);
+ Tcl_Obj *objPtr;
+ int unused;
+
+ if (!(
+ treeLayout
+ && GetSublayout(interp, themePtr, treeLayout, ".Item",
+ tv->tree.tagOptionTable, &tv->tree.itemLayout)
+ && GetSublayout(interp, themePtr, treeLayout, ".Cell",
+ tv->tree.tagOptionTable, &tv->tree.cellLayout)
+ && GetSublayout(interp, themePtr, treeLayout, ".Heading",
+ tv->tree.headingOptionTable, &tv->tree.headingLayout)
+ && GetSublayout(interp, themePtr, treeLayout, ".Row",
+ tv->tree.tagOptionTable, &tv->tree.rowLayout)
+ )) {
+ return 0;
+ }
+
+ /* Compute heading height.
+ */
+ Ttk_RebindSublayout(tv->tree.headingLayout, &tv->tree.column0);
+ Ttk_LayoutSize(tv->tree.headingLayout, 0, &unused, &tv->tree.headingHeight);
+
+ /* Get item height, indent from style:
+ * @@@ TODO: sanity-check.
+ */
+ tv->tree.rowHeight = DEFAULT_ROWHEIGHT;
+ tv->tree.indent = DEFAULT_INDENT;
+ if ((objPtr = Ttk_QueryOption(treeLayout, "-rowheight", 0))) {
+ (void)Tcl_GetIntFromObj(NULL, objPtr, &tv->tree.rowHeight);
+ }
+ if ((objPtr = Ttk_QueryOption(treeLayout, "-indent", 0))) {
+ (void)Tcl_GetIntFromObj(NULL, objPtr, &tv->tree.indent);
+ }
+
+ return treeLayout;
+}
+
+/* + TreeviewDoLayout --
+ * DoLayout() widget hook. Computes widget layout.
+ *
+ * Side effects:
+ * Computes headingArea and treeArea.
+ * Computes subtree height.
+ * Invokes scroll callbacks.
+ */
+static void TreeviewDoLayout(void *clientData)
+{
+ Treeview *tv = clientData;
+ int visibleRows;
+
+ /* ASSERT: SLACKINVARIANT */
+
+ Ttk_PlaceLayout(tv->core.layout,tv->core.state,Ttk_WinBox(tv->core.tkwin));
+ tv->tree.treeArea = Ttk_ClientRegion(tv->core.layout, "treearea");
+
+ ResizeColumns(tv, tv->tree.treeArea.width);
+ /* ASSERT: SLACKINVARIANT */
+
+ TtkScrolled(tv->tree.xscrollHandle,
+ tv->tree.xscroll.first,
+ tv->tree.xscroll.first + tv->tree.treeArea.width,
+ TreeWidth(tv));
+
+ if (tv->tree.showFlags & SHOW_HEADINGS) {
+ tv->tree.headingArea = Ttk_PackBox(
+ &tv->tree.treeArea, 1, tv->tree.headingHeight, TTK_SIDE_TOP);
+ } else {
+ tv->tree.headingArea = Ttk_MakeBox(0,0,0,0);
+ }
+
+ visibleRows = tv->tree.treeArea.height / tv->tree.rowHeight;
+ tv->tree.root->state |= TTK_STATE_OPEN;
+ TtkScrolled(tv->tree.yscrollHandle,
+ tv->tree.yscroll.first,
+ tv->tree.yscroll.first + visibleRows,
+ CountRows(tv->tree.root) - 1);
+}
+
+/* + TreeviewSize --
+ * SizeProc() widget hook. Size is determined by
+ * -height option and column widths.
+ */
+static int TreeviewSize(void *clientData, int *widthPtr, int *heightPtr)
+{
+ Treeview *tv = clientData;
+ int nRows, padHeight, padWidth;
+
+ Ttk_LayoutSize(tv->core.layout, tv->core.state, &padWidth, &padHeight);
+ Tcl_GetIntFromObj(NULL, tv->tree.heightObj, &nRows);
+
+ *widthPtr = padWidth + TreeWidth(tv);
+ *heightPtr = padHeight + tv->tree.rowHeight * nRows;
+
+ if (tv->tree.showFlags & SHOW_HEADINGS) {
+ *heightPtr += tv->tree.headingHeight;
+ }
+
+ return 1;
+}
+
+/* + ItemState --
+ * Returns the state of the specified item, based
+ * on widget state, item state, and other information.
+ */
+static Ttk_State ItemState(Treeview *tv, TreeItem *item)
+{
+ Ttk_State state = tv->core.state | item->state;
+ if (!item->children)
+ state |= TTK_STATE_LEAF;
+ if (item != tv->tree.focus)
+ state &= ~TTK_STATE_FOCUS;
+ return state;
+}
+
+/* + DrawHeadings --
+ * Draw tree headings.
+ */
+static void DrawHeadings(Treeview *tv, Drawable d)
+{
+ const int x0 = tv->tree.headingArea.x - tv->tree.xscroll.first;
+ const int y0 = tv->tree.headingArea.y;
+ const int h0 = tv->tree.headingArea.height;
+ int i = FirstColumn(tv);
+ int x = 0;
+
+ while (i < tv->tree.nDisplayColumns) {
+ TreeColumn *column = tv->tree.displayColumns[i];
+ Ttk_Box parcel = Ttk_MakeBox(x0+x, y0, column->width, h0);
+ DisplayLayout(tv->tree.headingLayout,
+ column, column->headingState, parcel, d);
+ x += column->width;
+ ++i;
+ }
+}
+
+/* + PrepareItem --
+ * Fill in a displayItem record.
+ */
+static void PrepareItem(
+ Treeview *tv, TreeItem *item, DisplayItem *displayItem)
+{
+ Ttk_Style style = Ttk_LayoutStyle(tv->core.layout);
+ Ttk_State state = ItemState(tv, item);
+
+ Ttk_TagSetValues(tv->tree.tagTable, item->tagset, displayItem);
+ Ttk_TagSetApplyStyle(tv->tree.tagTable, style, state, displayItem);
+}
+
+/* + DrawCells --
+ * Draw data cells for specified item.
+ */
+static void DrawCells(
+ Treeview *tv, TreeItem *item, DisplayItem *displayItem,
+ Drawable d, int x, int y)
+{
+ Ttk_Layout layout = tv->tree.cellLayout;
+ Ttk_State state = ItemState(tv, item);
+ Ttk_Padding cellPadding = {4, 0, 4, 0};
+ int rowHeight = tv->tree.rowHeight;
+ int nValues = 0;
+ Tcl_Obj **values = 0;
+ int i;
+
+ if (!item->valuesObj) {
+ return;
+ }
+
+ Tcl_ListObjGetElements(NULL, item->valuesObj, &nValues, &values);
+ for (i = 0; i < tv->tree.nColumns; ++i) {
+ tv->tree.columns[i].data = (i < nValues) ? values[i] : 0;
+ }
+
+ for (i = 1; i < tv->tree.nDisplayColumns; ++i) {
+ TreeColumn *column = tv->tree.displayColumns[i];
+ Ttk_Box parcel = Ttk_PadBox(
+ Ttk_MakeBox(x, y, column->width, rowHeight), cellPadding);
+
+ displayItem->textObj = column->data;
+ displayItem->anchorObj = column->anchorObj; /* <<NOTE-ANCHOR>> */
+
+ DisplayLayout(layout, displayItem, state, parcel, d);
+ x += column->width;
+ }
+}
+
+/* + DrawItem --
+ * Draw an item (row background, tree label, and cells).
+ */
+static void DrawItem(
+ Treeview *tv, TreeItem *item, Drawable d, int depth, int row)
+{
+ Ttk_State state = ItemState(tv, item);
+ DisplayItem displayItem;
+ int rowHeight = tv->tree.rowHeight;
+ int x = tv->tree.treeArea.x - tv->tree.xscroll.first;
+ int y = tv->tree.treeArea.y + rowHeight * (row - tv->tree.yscroll.first);
+
+ if (row % 2) state |= TTK_STATE_ALTERNATE;
+
+ PrepareItem(tv, item, &displayItem);
+
+ /* Draw row background:
+ */
+ {
+ Ttk_Box rowBox = Ttk_MakeBox(x, y, TreeWidth(tv), rowHeight);
+ DisplayLayout(tv->tree.rowLayout, &displayItem, state, rowBox, d);
+ }
+
+ /* Draw tree label:
+ */
+ if (tv->tree.showFlags & SHOW_TREE) {
+ int indent = depth * tv->tree.indent;
+ int colwidth = tv->tree.column0.width;
+ Ttk_Box parcel = Ttk_MakeBox(
+ x+indent, y, colwidth-indent, rowHeight);
+ if (item->textObj) { displayItem.textObj = item->textObj; }
+ if (item->imageObj) { displayItem.imageObj = item->imageObj; }
+ /* ??? displayItem.anchorObj = 0; <<NOTE-ANCHOR>> */
+ DisplayLayout(tv->tree.itemLayout, &displayItem, state, parcel, d);
+ x += colwidth;
+ }
+
+ /* Draw data cells:
+ */
+ DrawCells(tv, item, &displayItem, d, x, y);
+}
+
+/* + DrawSubtree --
+ * Draw an item and all of its (viewable) descendants.
+ *
+ * Returns:
+ * Row number of the last item drawn.
+ */
+
+static int DrawForest( /* forward */
+ Treeview *tv, TreeItem *item, Drawable d, int depth, int row);
+
+static int DrawSubtree(
+ Treeview *tv, TreeItem *item, Drawable d, int depth, int row)
+{
+ if (row >= tv->tree.yscroll.first) {
+ DrawItem(tv, item, d, depth, row);
+ }
+
+ if (item->state & TTK_STATE_OPEN) {
+ return DrawForest(tv, item->children, d, depth + 1, row + 1);
+ } else {
+ return row + 1;
+ }
+}
+
+/* + DrawForest --
+ * Draw a sequence of items and their visible descendants.
+ *
+ * Returns:
+ * Row number of the last item drawn.
+ */
+static int DrawForest(
+ Treeview *tv, TreeItem *item, Drawable d, int depth, int row)
+{
+ while (item && row <= tv->tree.yscroll.last) {
+ row = DrawSubtree(tv, item, d, depth, row);
+ item = item->next;
+ }
+ return row;
+}
+
+/* + TreeviewDisplay --
+ * Display() widget hook. Draw the widget contents.
+ */
+static void TreeviewDisplay(void *clientData, Drawable d)
+{
+ Treeview *tv = clientData;
+
+ Ttk_DrawLayout(tv->core.layout, tv->core.state, d);
+ if (tv->tree.showFlags & SHOW_HEADINGS) {
+ DrawHeadings(tv, d);
+ }
+ DrawForest(tv, tv->tree.root->children, d, 0,0);
+}
+
+/*------------------------------------------------------------------------
+ * +++ Utilities for widget commands
+ */
+
+/* + InsertPosition --
+ * Locate the previous sibling for [$tree insert].
+ *
+ * Returns a pointer to the item just before the specified index,
+ * or 0 if the item is to be inserted at the beginning.
+ */
+static TreeItem *InsertPosition(TreeItem *parent, int index)
+{
+ TreeItem *prev = 0, *next = parent->children;
+
+ while (next != 0 && index > 0) {
+ --index;
+ prev = next;
+ next = prev->next;
+ }
+
+ return prev;
+}
+
+/* + EndPosition --
+ * Locate the last child of the specified node.
+ *
+ * To avoid quadratic-time behavior in the common cases
+ * where the treeview is populated in breadth-first or
+ * depth-first order using [$tv insert $parent end ...],
+ * we cache the result from the last call to EndPosition()
+ * and start the search from there on a cache hit.
+ *
+ */
+static TreeItem *EndPosition(Treeview *tv, TreeItem *parent)
+{
+ TreeItem *endPtr = tv->tree.endPtr;
+
+ while (endPtr && endPtr->parent != parent) {
+ endPtr = endPtr->parent;
+ }
+ if (!endPtr) {
+ endPtr = parent->children;
+ }
+
+ if (endPtr) {
+ while (endPtr->next) {
+ endPtr = endPtr->next;
+ }
+ tv->tree.endPtr = endPtr;
+ }
+
+ return endPtr;
+}
+
+/* + AncestryCheck --
+ * Verify that specified item is not an ancestor of the specified parent;
+ * returns 1 if OK, 0 and leaves an error message in interp otherwise.
+ */
+static int AncestryCheck(
+ Tcl_Interp *interp, Treeview *tv, TreeItem *item, TreeItem *parent)
+{
+ TreeItem *p = parent;
+ while (p) {
+ if (p == item) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp,
+ "Cannot insert ", ItemName(tv, item),
+ " as a descendant of ", ItemName(tv, parent),
+ NULL);
+ return 0;
+ }
+ p = p->parent;
+ }
+ return 1;
+}
+
+/* + DeleteItems --
+ * Remove an item and all of its descendants from the hash table
+ * and detach them from the tree; returns a linked list (chained
+ * along the ->next pointer) of deleted items.
+ */
+static TreeItem *DeleteItems(TreeItem *item, TreeItem *delq)
+{
+ if (item->entryPtr) {
+ DetachItem(item);
+ while (item->children) {
+ delq = DeleteItems(item->children, delq);
+ }
+ Tcl_DeleteHashEntry(item->entryPtr);
+ item->entryPtr = 0;
+ item->next = delq;
+ delq = item;
+ } /* else -- item has already been unlinked */
+ return delq;
+}
+
+/*------------------------------------------------------------------------
+ * +++ Widget commands -- item inquiry.
+ */
+
+/* + $tv children $item ?newchildren? --
+ * Return the list of children associated with $item
+ */
+static int TreeviewChildrenCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ Treeview *tv = recordPtr;
+ TreeItem *item;
+ Tcl_Obj *result;
+
+ if (objc < 3 || objc > 4) {
+ Tcl_WrongNumArgs(interp, 2, objv, "item ?newchildren?");
+ return TCL_ERROR;
+ }
+ item = FindItem(interp, tv, objv[2]);
+ if (!item) {
+ return TCL_ERROR;
+ }
+
+ if (objc == 3) {
+ result = Tcl_NewListObj(0,0);
+ for (item = item->children; item; item = item->next) {
+ Tcl_ListObjAppendElement(interp, result, ItemID(tv, item));
+ }
+ Tcl_SetObjResult(interp, result);
+ } else {
+ TreeItem **newChildren = GetItemListFromObj(interp, tv, objv[3]);
+ TreeItem *child;
+ int i;
+
+ if (!newChildren)
+ return TCL_ERROR;
+
+ /* Sanity-check:
+ */
+ for (i=0; newChildren[i]; ++i) {
+ if (!AncestryCheck(interp, tv, newChildren[i], item)) {
+ ckfree((ClientData)newChildren);
+ return TCL_ERROR;
+ }
+ }
+
+ /* Detach old children:
+ */
+ child = item->children;
+ while (child) {
+ TreeItem *next = child->next;
+ DetachItem(child);
+ child = next;
+ }
+
+ /* Detach new children from their current locations:
+ */
+ for (i=0; newChildren[i]; ++i) {
+ DetachItem(newChildren[i]);
+ }
+
+ /* Reinsert new children:
+ * Note: it is not an error for an item to be listed more than once,
+ * though it probably should be...
+ */
+ child = 0;
+ for (i=0; newChildren[i]; ++i) {
+ if (newChildren[i]->parent) {
+ /* This is a duplicate element which has already been
+ * inserted. Ignore it.
+ */
+ continue;
+ }
+ InsertItem(item, child, newChildren[i]);
+ child = newChildren[i];
+ }
+
+ ckfree((ClientData)newChildren);
+ TtkRedisplayWidget(&tv->core);
+ }
+
+ return TCL_OK;
+}
+
+/* + $tv parent $item --
+ * Return the item ID of $item's parent.
+ */
+static int TreeviewParentCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ Treeview *tv = recordPtr;
+ TreeItem *item;
+
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "item");
+ return TCL_ERROR;
+ }
+ item = FindItem(interp, tv, objv[2]);
+ if (!item) {
+ return TCL_ERROR;
+ }
+
+ if (item->parent) {
+ Tcl_SetObjResult(interp, ItemID(tv, item->parent));
+ } else {
+ /* This is the root item. @@@ Return an error? */
+ Tcl_ResetResult(interp);
+ }
+
+ return TCL_OK;
+}
+
+/* + $tv next $item
+ * Return the ID of $item's next sibling.
+ */
+static int TreeviewNextCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ Treeview *tv = recordPtr;
+ TreeItem *item;
+
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "item");
+ return TCL_ERROR;
+ }
+ item = FindItem(interp, tv, objv[2]);
+ if (!item) {
+ return TCL_ERROR;
+ }
+
+ if (item->next) {
+ Tcl_SetObjResult(interp, ItemID(tv, item->next));
+ } /* else -- leave interp-result empty */
+
+ return TCL_OK;
+}
+
+/* + $tv prev $item
+ * Return the ID of $item's previous sibling.
+ */
+static int TreeviewPrevCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ Treeview *tv = recordPtr;
+ TreeItem *item;
+
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "item");
+ return TCL_ERROR;
+ }
+ item = FindItem(interp, tv, objv[2]);
+ if (!item) {
+ return TCL_ERROR;
+ }
+
+ if (item->prev) {
+ Tcl_SetObjResult(interp, ItemID(tv, item->prev));
+ } /* else -- leave interp-result empty */
+
+ return TCL_OK;
+}
+
+/* + $tv index $item --
+ * Return the index of $item within its parent.
+ */
+static int TreeviewIndexCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ Treeview *tv = recordPtr;
+ TreeItem *item;
+ int index = 0;
+
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "item");
+ return TCL_ERROR;
+ }
+ item = FindItem(interp, tv, objv[2]);
+ if (!item) {
+ return TCL_ERROR;
+ }
+
+ while (item->prev) {
+ ++index;
+ item = item->prev;
+ }
+
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(index));
+ return TCL_OK;
+}
+
+/* + $tv exists $itemid --
+ * Test if the specified item id is present in the tree.
+ */
+static int TreeviewExistsCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ Treeview *tv = recordPtr;
+ Tcl_HashEntry *entryPtr;
+
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "itemid");
+ return TCL_ERROR;
+ }
+
+ entryPtr = Tcl_FindHashEntry(&tv->tree.items, Tcl_GetString(objv[2]));
+ Tcl_SetObjResult(interp, Tcl_NewBooleanObj(entryPtr != 0));
+ return TCL_OK;
+}
+
+/* + $tv bbox $itemid ?$column? --
+ * Return bounding box [x y width height] of specified item.
+ */
+static int TreeviewBBoxCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ Treeview *tv = recordPtr;
+ TreeItem *item = 0;
+ TreeColumn *column = 0;
+ Ttk_Box bbox;
+
+ if (objc < 3 || objc > 4) {
+ Tcl_WrongNumArgs(interp, 2, objv, "itemid ?column");
+ return TCL_ERROR;
+ }
+
+ item = FindItem(interp, tv, objv[2]);
+ if (!item) {
+ return TCL_ERROR;
+ }
+ if (objc >=4 && (column = FindColumn(interp,tv,objv[3])) == NULL) {
+ return TCL_ERROR;
+ }
+
+ if (BoundingBox(tv, item, column, &bbox)) {
+ Tcl_SetObjResult(interp, Ttk_NewBoxObj(bbox));
+ }
+
+ return TCL_OK;
+}
+
+/* + $tv identify $x $y -- (obsolescent)
+ * Implements the old, horrible, 2-argument form of [$tv identify].
+ *
+ * Returns: one of
+ * heading #n
+ * cell itemid #n
+ * item itemid element
+ * row itemid
+ */
+static int TreeviewHorribleIdentify(
+ Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], Treeview *tv)
+{
+ const char *what = "nothing", *detail = NULL;
+ TreeItem *item = 0;
+ Tcl_Obj *result;
+ int dColumnNumber;
+ char dcolbuf[16];
+ int x, y, x1;
+
+ /* ASSERT: objc == 4 */
+
+ if ( Tcl_GetIntFromObj(interp, objv[2], &x) != TCL_OK
+ || Tcl_GetIntFromObj(interp, objv[3], &y) != TCL_OK
+ ) {
+ return TCL_ERROR;
+ }
+
+ dColumnNumber = IdentifyDisplayColumn(tv, x, &x1);
+ if (dColumnNumber < 0) {
+ goto done;
+ }
+ sprintf(dcolbuf, "#%d", dColumnNumber);
+
+ if (Ttk_BoxContains(tv->tree.headingArea,x,y)) {
+ if (-HALO <= x1 - x && x1 - x <= HALO) {
+ what = "separator";
+ } else {
+ what = "heading";
+ }
+ detail = dcolbuf;
+ } else if (Ttk_BoxContains(tv->tree.treeArea,x,y)) {
+ item = IdentifyItem(tv, y);
+ if (item && dColumnNumber > 0) {
+ what = "cell";
+ detail = dcolbuf;
+ } else if (item) {
+ Ttk_Layout layout = tv->tree.itemLayout;
+ Ttk_Box itemBox;
+ DisplayItem displayItem;
+ Ttk_Element element;
+
+ BoundingBox(tv, item, NULL, &itemBox);
+ PrepareItem(tv, item, &displayItem); /*@@@ FIX: -text, etc*/
+ Ttk_RebindSublayout(layout, &displayItem);
+ Ttk_PlaceLayout(layout, ItemState(tv,item), itemBox);
+ element = Ttk_IdentifyElement(layout, x, y);
+
+ if (element) {
+ what = "item";
+ detail = Ttk_ElementName(element);
+ } else {
+ what = "row";
+ }
+ }
+ }
+
+done:
+ result = Tcl_NewListObj(0,0);
+ Tcl_ListObjAppendElement(NULL, result, Tcl_NewStringObj(what, -1));
+ if (item)
+ Tcl_ListObjAppendElement(NULL, result, ItemID(tv, item));
+ if (detail)
+ Tcl_ListObjAppendElement(NULL, result, Tcl_NewStringObj(detail, -1));
+
+ Tcl_SetObjResult(interp, result);
+ return TCL_OK;
+}
+
+/* + $tv identify $component $x $y --
+ * Identify the component at position x,y.
+ */
+
+static int TreeviewIdentifyCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ static const char *submethodStrings[] =
+ { "region", "item", "column", "row", "element", NULL };
+ enum { I_REGION, I_ITEM, I_COLUMN, I_ROW, I_ELEMENT };
+
+ Treeview *tv = recordPtr;
+ int submethod;
+ int x, y;
+
+ TreeRegion region;
+ Ttk_Box bbox;
+ TreeItem *item;
+ TreeColumn *column = 0;
+ int colno, x1;
+
+ if (objc == 4) { /* Old form */
+ return TreeviewHorribleIdentify(interp, objc, objv, tv);
+ } else if (objc != 5) {
+ Tcl_WrongNumArgs(interp, 2, objv, "command x y");
+ return TCL_ERROR;
+ }
+
+ if ( Tcl_GetIndexFromObj(interp, objv[2],
+ submethodStrings, "command", TCL_EXACT, &submethod) != TCL_OK
+ || Tcl_GetIntFromObj(interp, objv[3], &x) != TCL_OK
+ || Tcl_GetIntFromObj(interp, objv[4], &y) != TCL_OK
+ ) {
+ return TCL_ERROR;
+ }
+
+ region = IdentifyRegion(tv, x, y);
+ item = IdentifyItem(tv, y);
+ colno = IdentifyDisplayColumn(tv, x, &x1);
+ column = (colno >= 0) ? tv->tree.displayColumns[colno] : NULL;
+
+ switch (submethod)
+ {
+ case I_REGION :
+ Tcl_SetObjResult(interp,Tcl_NewStringObj(regionStrings[region],-1));
+ break;
+
+ case I_ITEM :
+ case I_ROW :
+ if (item) {
+ Tcl_SetObjResult(interp, ItemID(tv, item));
+ }
+ break;
+
+ case I_COLUMN :
+ if (colno >= 0) {
+ char dcolbuf[16];
+ sprintf(dcolbuf, "#%d", colno);
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(dcolbuf, -1));
+ }
+ break;
+
+ case I_ELEMENT :
+ {
+ Ttk_Layout layout = 0;
+ DisplayItem displayItem;
+ Ttk_Element element;
+
+ switch (region) {
+ case REGION_NOTHING:
+ layout = tv->core.layout;
+ return TCL_OK; /* @@@ NYI */
+ case REGION_HEADING:
+ case REGION_SEPARATOR:
+ layout = tv->tree.headingLayout;
+ return TCL_OK; /* @@@ NYI */
+ case REGION_TREE:
+ layout = tv->tree.itemLayout;
+ break;
+ case REGION_CELL:
+ layout = tv->tree.cellLayout;
+ break;
+ }
+
+ if (!BoundingBox(tv, item, column, &bbox)) {
+ return TCL_OK;
+ }
+
+ PrepareItem(tv, item, &displayItem); /*@@@ FIX: fill in -text,etc */
+ Ttk_RebindSublayout(layout, &displayItem);
+ Ttk_PlaceLayout(layout, ItemState(tv,item), bbox);
+ element = Ttk_IdentifyElement(layout, x, y);
+
+ if (element) {
+ const char *elementName = Ttk_ElementName(element);
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(elementName, -1));
+ }
+ break;
+ }
+ }
+ return TCL_OK;
+}
+
+/*------------------------------------------------------------------------
+ * +++ Widget commands -- item and column configuration.
+ */
+
+/* + $tv item $item ?options ....?
+ * Query or configure item options.
+ */
+static int TreeviewItemCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ Treeview *tv = recordPtr;
+ TreeItem *item;
+
+ if (objc < 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "item ?option ?value??...");
+ return TCL_ERROR;
+ }
+ if (!(item = FindItem(interp, tv, objv[2]))) {
+ return TCL_ERROR;
+ }
+
+ if (objc == 3) {
+ return TtkEnumerateOptions(interp, item, ItemOptionSpecs,
+ tv->tree.itemOptionTable, tv->core.tkwin);
+ } else if (objc == 4) {
+ return TtkGetOptionValue(interp, item, objv[3],
+ tv->tree.itemOptionTable, tv->core.tkwin);
+ } else {
+ return ConfigureItem(interp, tv, item, objc-3, objv+3);
+ }
+}
+
+/* + $tv column column ?options ....?
+ * Column data accessor
+ */
+static int TreeviewColumnCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ Treeview *tv = recordPtr;
+ TreeColumn *column;
+
+ if (objc < 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "column -option value...");
+ return TCL_ERROR;
+ }
+ if (!(column = FindColumn(interp, tv, objv[2]))) {
+ return TCL_ERROR;
+ }
+
+ if (objc == 3) {
+ return TtkEnumerateOptions(interp, column, ColumnOptionSpecs,
+ tv->tree.columnOptionTable, tv->core.tkwin);
+ } else if (objc == 4) {
+ return TtkGetOptionValue(interp, column, objv[3],
+ tv->tree.columnOptionTable, tv->core.tkwin);
+ } else {
+ return ConfigureColumn(interp, tv, column, objc-3, objv+3);
+ }
+}
+
+/* + $tv heading column ?options ....?
+ * Heading data accessor
+ */
+static int TreeviewHeadingCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ Treeview *tv = recordPtr;
+ Tk_OptionTable optionTable = tv->tree.headingOptionTable;
+ Tk_Window tkwin = tv->core.tkwin;
+ TreeColumn *column;
+
+ if (objc < 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "column -option value...");
+ return TCL_ERROR;
+ }
+ if (!(column = FindColumn(interp, tv, objv[2]))) {
+ return TCL_ERROR;
+ }
+
+ if (objc == 3) {
+ return TtkEnumerateOptions(
+ interp, column, HeadingOptionSpecs, optionTable, tkwin);
+ } else if (objc == 4) {
+ return TtkGetOptionValue(
+ interp, column, objv[3], optionTable, tkwin);
+ } else {
+ return ConfigureHeading(interp, tv, column, objc-3,objv+3);
+ }
+}
+
+/* + $tv set $item ?$column ?value??
+ * Query or configure cell values
+ */
+static int TreeviewSetCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ Treeview *tv = recordPtr;
+ TreeItem *item;
+ TreeColumn *column;
+ int columnNumber;
+
+ if (objc < 3 || objc > 5) {
+ Tcl_WrongNumArgs(interp, 2, objv, "item ?column ?value??");
+ return TCL_ERROR;
+ }
+ if (!(item = FindItem(interp, tv, objv[2])))
+ return TCL_ERROR;
+
+ /* Make sure -values exists:
+ */
+ if (!item->valuesObj) {
+ item->valuesObj = Tcl_NewListObj(0,0);
+ Tcl_IncrRefCount(item->valuesObj);
+ }
+
+ if (objc == 3) {
+ /* Return dictionary:
+ */
+ Tcl_Obj *result = Tcl_NewListObj(0,0);
+ Tcl_Obj *value;
+ for (columnNumber=0; columnNumber<tv->tree.nColumns; ++columnNumber) {
+ Tcl_ListObjIndex(interp, item->valuesObj, columnNumber, &value);
+ if (value) {
+ Tcl_ListObjAppendElement(interp, result,
+ tv->tree.columns[columnNumber].idObj);
+ Tcl_ListObjAppendElement(interp, result, value);
+ }
+ }
+ Tcl_SetObjResult(interp, result);
+ return TCL_OK;
+ }
+
+ /* else -- get or set column
+ */
+ if (!(column = FindColumn(interp, tv, objv[3])))
+ return TCL_ERROR;
+
+ if (column == &tv->tree.column0) {
+ /* @@@ Maybe set -text here instead? */
+ Tcl_AppendResult(interp, "Display column #0 cannot be set", NULL);
+ return TCL_ERROR;
+ }
+
+ /* Note: we don't do any error checking in the list operations,
+ * since item->valuesObj is guaranteed to be a list.
+ */
+ columnNumber = column - tv->tree.columns;
+
+ if (objc == 4) { /* get column */
+ Tcl_Obj *result = 0;
+ Tcl_ListObjIndex(interp, item->valuesObj, columnNumber, &result);
+ if (!result) {
+ result = Tcl_NewStringObj("",0);
+ }
+ Tcl_SetObjResult(interp, result);
+ return TCL_OK;
+ } else { /* set column */
+ int length;
+
+ item->valuesObj = unshareObj(item->valuesObj);
+
+ /* Make sure -values is fully populated:
+ */
+ Tcl_ListObjLength(interp, item->valuesObj, &length);
+ while (length < tv->tree.nColumns) {
+ Tcl_Obj *empty = Tcl_NewStringObj("",0);
+ Tcl_ListObjAppendElement(interp, item->valuesObj, empty);
+ ++length;
+ }
+
+ /* Set value:
+ */
+ Tcl_ListObjReplace(interp,item->valuesObj,columnNumber,1,1,objv+4);
+ TtkRedisplayWidget(&tv->core);
+ return TCL_OK;
+ }
+}
+
+/*------------------------------------------------------------------------
+ * +++ Widget commands -- tree modification.
+ */
+
+/* + $tv insert $parent $index ?-id id? ?-option value ...?
+ * Insert a new item.
+ */
+static int TreeviewInsertCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ Treeview *tv = recordPtr;
+ TreeItem *parent, *sibling, *newItem;
+ Tcl_HashEntry *entryPtr;
+ int isNew;
+
+ if (objc < 4) {
+ Tcl_WrongNumArgs(interp, 2, objv, "parent index ?-id id? -options...");
+ return TCL_ERROR;
+ }
+
+ /* Get parent node:
+ */
+ if ((parent = FindItem(interp, tv, objv[2])) == NULL) {
+ return TCL_ERROR;
+ }
+
+ /* Locate previous sibling based on $index:
+ */
+ if (!strcmp(Tcl_GetString(objv[3]), "end")) {
+ sibling = EndPosition(tv, parent);
+ } else {
+ int index;
+ if (Tcl_GetIntFromObj(interp, objv[3], &index) != TCL_OK)
+ return TCL_ERROR;
+ sibling = InsertPosition(parent, index);
+ }
+
+ /* Get node name:
+ * If -id supplied and does not already exist, use that;
+ * Otherwise autogenerate new one.
+ */
+ objc -= 4; objv += 4;
+ if (objc >= 2 && !strcmp("-id", Tcl_GetString(objv[0]))) {
+ const char *itemName = Tcl_GetString(objv[1]);
+ entryPtr = Tcl_CreateHashEntry(&tv->tree.items, itemName, &isNew);
+ if (!isNew) {
+ Tcl_AppendResult(interp, "Item ",itemName," already exists",NULL);
+ return TCL_ERROR;
+ }
+ objc -= 2; objv += 2;
+ } else {
+ char idbuf[16];
+ do {
+ ++tv->tree.serial;
+ sprintf(idbuf, "I%03X", tv->tree.serial);
+ entryPtr = Tcl_CreateHashEntry(&tv->tree.items, idbuf, &isNew);
+ } while (!isNew);
+ }
+
+ /* Create and configure new item:
+ */
+ newItem = NewItem();
+ Tk_InitOptions(
+ interp, (ClientData)newItem, tv->tree.itemOptionTable, tv->core.tkwin);
+ newItem->tagset = Ttk_GetTagSetFromObj(NULL, tv->tree.tagTable, NULL);
+ if (ConfigureItem(interp, tv, newItem, objc, objv) != TCL_OK) {
+ Tcl_DeleteHashEntry(entryPtr);
+ FreeItem(newItem);
+ return TCL_ERROR;
+ }
+
+ /* Store in hash table, link into tree:
+ */
+ Tcl_SetHashValue(entryPtr, newItem);
+ newItem->entryPtr = entryPtr;
+ InsertItem(parent, sibling, newItem);
+ TtkRedisplayWidget(&tv->core);
+
+ Tcl_SetObjResult(interp, ItemID(tv, newItem));
+ return TCL_OK;
+}
+
+/* + $tv detach $item --
+ * Unlink $item from the tree.
+ */
+static int TreeviewDetachCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ Treeview *tv = recordPtr;
+ TreeItem **items;
+ int i;
+
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "item");
+ return TCL_ERROR;
+ }
+ if (!(items = GetItemListFromObj(interp, tv, objv[2]))) {
+ return TCL_ERROR;
+ }
+
+ /* Sanity-check */
+ for (i = 0; items[i]; ++i) {
+ if (items[i] == tv->tree.root) {
+ Tcl_AppendResult(interp, "Cannot detach root item", NULL);
+ ckfree((ClientData)items);
+ return TCL_ERROR;
+ }
+ }
+
+ for (i = 0; items[i]; ++i) {
+ DetachItem(items[i]);
+ }
+
+ TtkRedisplayWidget(&tv->core);
+ ckfree((ClientData)items);
+ return TCL_OK;
+}
+
+/* + $tv delete $items --
+ * Delete each item in $items.
+ *
+ * Do this in two passes:
+ * First detach the item and all its descendants and remove them
+ * from the hash table. Free the items themselves in a second pass.
+ *
+ * It's done this way because an item may appear more than once
+ * in the list of items to delete (either directly or as a descendant
+ * of a previously deleted item.)
+ */
+
+static int TreeviewDeleteCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ Treeview *tv = recordPtr;
+ TreeItem **items, *delq;
+ int i;
+
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "items");
+ return TCL_ERROR;
+ }
+
+ if (!(items = GetItemListFromObj(interp, tv, objv[2]))) {
+ return TCL_ERROR;
+ }
+
+ /* Sanity-check:
+ */
+ for (i=0; items[i]; ++i) {
+ if (items[i] == tv->tree.root) {
+ ckfree((ClientData)items);
+ Tcl_AppendResult(interp, "Cannot delete root item", NULL);
+ return TCL_ERROR;
+ }
+ }
+
+ /* Remove items from hash table.
+ */
+ delq = 0;
+ for (i=0; items[i]; ++i) {
+ delq = DeleteItems(items[i], delq);
+ }
+
+ /* Free items:
+ */
+ while (delq) {
+ TreeItem *next = delq->next;
+ if (tv->tree.focus == delq)
+ tv->tree.focus = 0;
+ if (tv->tree.endPtr == delq)
+ tv->tree.endPtr = 0;
+ FreeItem(delq);
+ delq = next;
+ }
+
+ ckfree((ClientData)items);
+ TtkRedisplayWidget(&tv->core);
+ return TCL_OK;
+}
+
+/* + $tv move $item $parent $index
+ * Move $item to the specified $index in $parent's child list.
+ */
+static int TreeviewMoveCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ Treeview *tv = recordPtr;
+ TreeItem *item, *parent;
+ TreeItem *sibling;
+
+ if (objc != 5) {
+ Tcl_WrongNumArgs(interp, 2, objv, "item parent index");
+ return TCL_ERROR;
+ }
+ if ( (item = FindItem(interp, tv, objv[2])) == 0
+ || (parent = FindItem(interp, tv, objv[3])) == 0)
+ {
+ return TCL_ERROR;
+ }
+
+ /* Locate previous sibling based on $index:
+ */
+ if (!strcmp(Tcl_GetString(objv[4]), "end")) {
+ sibling = EndPosition(tv, parent);
+ } else {
+ TreeItem *p;
+ int index;
+
+ if (Tcl_GetIntFromObj(interp, objv[4], &index) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ sibling = 0;
+ for (p = parent->children; p != NULL && index > 0; p = p->next) {
+ if (p != item) {
+ --index;
+ } /* else -- moving node forward, count index+1 nodes */
+ sibling = p;
+ }
+ }
+
+ /* Check ancestry:
+ */
+ if (!AncestryCheck(interp, tv, item, parent)) {
+ return TCL_ERROR;
+ }
+
+ /* Moving an item after itself is a no-op:
+ */
+ if (item == sibling) {
+ return TCL_OK;
+ }
+
+ /* Move item:
+ */
+ DetachItem(item);
+ InsertItem(parent, sibling, item);
+
+ TtkRedisplayWidget(&tv->core);
+ return TCL_OK;
+}
+
+/*------------------------------------------------------------------------
+ * +++ Widget commands -- scrolling
+ */
+
+static int TreeviewXViewCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ Treeview *tv = recordPtr;
+ return TtkScrollviewCommand(interp, objc, objv, tv->tree.xscrollHandle);
+}
+
+static int TreeviewYViewCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ Treeview *tv = recordPtr;
+ return TtkScrollviewCommand(interp, objc, objv, tv->tree.yscrollHandle);
+}
+
+/* $tree see $item --
+ * Ensure that $item is visible.
+ */
+static int TreeviewSeeCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ Treeview *tv = recordPtr;
+ TreeItem *item, *parent;
+ int rowNumber;
+
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "item");
+ return TCL_ERROR;
+ }
+ if (!(item = FindItem(interp, tv, objv[2]))) {
+ return TCL_ERROR;
+ }
+
+ /* Make sure all ancestors are open:
+ */
+ for (parent = item->parent; parent; parent = parent->parent) {
+ if (!(parent->state & TTK_STATE_OPEN)) {
+ parent->openObj = unshareObj(parent->openObj);
+ Tcl_SetBooleanObj(parent->openObj, 1);
+ parent->state |= TTK_STATE_OPEN;
+ TtkRedisplayWidget(&tv->core);
+ }
+ }
+ tv->tree.yscroll.total = CountRows(tv->tree.root) - 1;
+
+ /* Make sure item is visible:
+ */
+ rowNumber = RowNumber(tv, item);
+ if (rowNumber < tv->tree.yscroll.first) {
+ TtkScrollTo(tv->tree.yscrollHandle, rowNumber);
+ } else if (rowNumber >= tv->tree.yscroll.last) {
+ TtkScrollTo(tv->tree.yscrollHandle,
+ tv->tree.yscroll.first + (1+rowNumber - tv->tree.yscroll.last));
+ }
+
+ return TCL_OK;
+}
+
+/*------------------------------------------------------------------------
+ * +++ Widget commands -- interactive column resize
+ */
+
+/* + $tree drag $column $newX --
+ * Set right edge of display column $column to x position $X
+ */
+static int TreeviewDragCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ Treeview *tv = recordPtr;
+ int left = tv->tree.treeArea.x - tv->tree.xscroll.first;
+ int i = FirstColumn(tv);
+ TreeColumn *column;
+ int newx;
+
+ if (objc != 4) {
+ Tcl_WrongNumArgs(interp, 2, objv, "column xposition");
+ return TCL_ERROR;
+ }
+
+ if ( (column = FindColumn(interp, tv, objv[2])) == 0
+ || Tcl_GetIntFromObj(interp, objv[3], &newx) != TCL_OK)
+ {
+ return TCL_ERROR;
+ }
+
+ for (;i < tv->tree.nDisplayColumns; ++i) {
+ TreeColumn *c = tv->tree.displayColumns[i];
+ int right = left + c->width;
+ if (c == column) {
+ DragColumn(tv, i, newx - right);
+ /* ASSERT: SLACKINVARIANT */
+ TtkRedisplayWidget(&tv->core);
+ return TCL_OK;
+ }
+ left = right;
+ }
+
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp,
+ "column ", Tcl_GetString(objv[2]), " is not displayed",
+ NULL);
+ return TCL_ERROR;
+}
+
+/*------------------------------------------------------------------------
+ * +++ Widget commands -- focus and selection
+ */
+
+/* + $tree focus ?item?
+ */
+static int TreeviewFocusCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ Treeview *tv = recordPtr;
+
+ if (objc == 2) {
+ if (tv->tree.focus) {
+ Tcl_SetObjResult(interp, ItemID(tv, tv->tree.focus));
+ }
+ return TCL_OK;
+ } else if (objc == 3) {
+ TreeItem *newFocus = FindItem(interp, tv, objv[2]);
+ if (!newFocus)
+ return TCL_ERROR;
+ tv->tree.focus = newFocus;
+ TtkRedisplayWidget(&tv->core);
+ return TCL_OK;
+ } else {
+ Tcl_WrongNumArgs(interp, 2, objv, "?newFocus?");
+ return TCL_ERROR;
+ }
+}
+
+/* + $tree selection ?add|remove|set|toggle $items?
+ */
+static int TreeviewSelectionCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ enum {
+ SELECTION_SET, SELECTION_ADD, SELECTION_REMOVE, SELECTION_TOGGLE
+ };
+ static const char *selopStrings[] = {
+ "set", "add", "remove", "toggle", NULL
+ };
+
+ Treeview *tv = recordPtr;
+ int selop, i;
+ TreeItem *item, **items;
+
+ if (objc == 2) {
+ Tcl_Obj *result = Tcl_NewListObj(0,0);
+ for (item = tv->tree.root->children; item; item=NextPreorder(item)) {
+ if (item->state & TTK_STATE_SELECTED)
+ Tcl_ListObjAppendElement(NULL, result, ItemID(tv, item));
+ }
+ Tcl_SetObjResult(interp, result);
+ return TCL_OK;
+ }
+
+ if (objc != 4) {
+ Tcl_WrongNumArgs(interp, 2, objv, "?add|remove|set|toggle items?");
+ return TCL_ERROR;
+ }
+
+ if (Tcl_GetIndexFromObj(interp, objv[2], selopStrings,
+ "selection operation", 0, &selop) != TCL_OK)
+ {
+ return TCL_ERROR;
+ }
+
+ items = GetItemListFromObj(interp, tv, objv[3]);
+ if (!items) {
+ return TCL_ERROR;
+ }
+
+ switch (selop)
+ {
+ case SELECTION_SET:
+ for (item=tv->tree.root; item; item=NextPreorder(item)) {
+ item->state &= ~TTK_STATE_SELECTED;
+ }
+ /*FALLTHRU*/
+ case SELECTION_ADD:
+ for (i=0; items[i]; ++i) {
+ items[i]->state |= TTK_STATE_SELECTED;
+ }
+ break;
+ case SELECTION_REMOVE:
+ for (i=0; items[i]; ++i) {
+ items[i]->state &= ~TTK_STATE_SELECTED;
+ }
+ break;
+ case SELECTION_TOGGLE:
+ for (i=0; items[i]; ++i) {
+ items[i]->state ^= TTK_STATE_SELECTED;
+ }
+ break;
+ }
+
+ ckfree((ClientData)items);
+ TtkSendVirtualEvent(tv->core.tkwin, "TreeviewSelect");
+ TtkRedisplayWidget(&tv->core);
+
+ return TCL_OK;
+}
+
+/*------------------------------------------------------------------------
+ * +++ Widget commands -- tags and bindings.
+ */
+
+/* + $tv tag bind $tag ?$sequence ?$script??
+ */
+static int TreeviewTagBindCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ Treeview *tv = recordPtr;
+ Ttk_TagTable tagTable = tv->tree.tagTable;
+ Tk_BindingTable bindingTable = tv->tree.bindingTable;
+ Ttk_Tag tag;
+
+ if (objc < 4 || objc > 6) {
+ Tcl_WrongNumArgs(interp, 3, objv, "tagName ?sequence? ?script?");
+ return TCL_ERROR;
+ }
+
+ tag = Ttk_GetTagFromObj(tagTable, objv[3]);
+ if (!tag) { return TCL_ERROR; }
+
+ if (objc == 4) { /* $tv tag bind $tag */
+ Tk_GetAllBindings(interp, bindingTable, tag);
+ } else if (objc == 5) { /* $tv tag bind $tag $sequence */
+ /* TODO: distinguish "no such binding" (OK) from "bad pattern" (ERROR)
+ */
+ const char *script = Tk_GetBinding(interp,
+ bindingTable, tag, Tcl_GetString(objv[4]));
+ if (script != NULL) {
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(script,-1));
+ }
+ } else if (objc == 6) { /* $tv tag bind $tag $sequence $script */
+ const char *sequence = Tcl_GetString(objv[4]);
+ const char *script = Tcl_GetString(objv[5]);
+
+ if (!*script) { /* Delete existing binding */
+ Tk_DeleteBinding(interp, bindingTable, tag, sequence);
+ } else {
+ unsigned long mask = Tk_CreateBinding(interp,
+ bindingTable, tag, sequence, script, 0);
+
+ /* Test mask to make sure event is supported:
+ */
+ if (mask & (~TreeviewBindEventMask)) {
+ Tk_DeleteBinding(interp, bindingTable, tag, sequence);
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "unsupported event ", sequence,
+ "\nonly key, button, motion, and virtual events supported",
+ NULL);
+ return TCL_ERROR;
+ }
+ }
+ }
+ return TCL_OK;
+}
+
+/* + $tv tag configure $tag ?-option ?value -option value...??
+ */
+static int TreeviewTagConfigureCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ Treeview *tv = recordPtr;
+ Ttk_TagTable tagTable = tv->tree.tagTable;
+ Ttk_Tag tag;
+
+ if (objc < 4) {
+ Tcl_WrongNumArgs(interp, 3, objv, "tagName ?-option ?value ...??");
+ return TCL_ERROR;
+ }
+
+ tag = Ttk_GetTagFromObj(tagTable, objv[3]);
+
+ if (objc == 4) {
+ return Ttk_EnumerateTagOptions(interp, tagTable, tag);
+ } else if (objc == 5) {
+ Tcl_Obj *result = Ttk_TagOptionValue(interp, tagTable, tag, objv[4]);
+ if (result) {
+ Tcl_SetObjResult(interp, result);
+ return TCL_OK;
+ } /* else */
+ return TCL_ERROR;
+ }
+ /* else */
+ TtkRedisplayWidget(&tv->core);
+ return Ttk_ConfigureTag(interp, tagTable, tag, objc - 4, objv + 4);
+}
+
+/* + $tv tag has $tag ?$item?
+ */
+static int TreeviewTagHasCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ Treeview *tv = recordPtr;
+
+ if (objc == 4) { /* Return list of all items with tag */
+ Ttk_Tag tag = Ttk_GetTagFromObj(tv->tree.tagTable, objv[3]);
+ TreeItem *item = tv->tree.root;
+ Tcl_Obj *result = Tcl_NewListObj(0,0);
+
+ while (item) {
+ if (Ttk_TagSetContains(item->tagset, tag)) {
+ Tcl_ListObjAppendElement(NULL, result, ItemID(tv, item));
+ }
+ item = NextPreorder(item);
+ }
+
+ Tcl_SetObjResult(interp, result);
+ return TCL_OK;
+ } else if (objc == 5) { /* Test if item has specified tag */
+ Ttk_Tag tag = Ttk_GetTagFromObj(tv->tree.tagTable, objv[3]);
+ TreeItem *item = FindItem(interp, tv, objv[4]);
+ if (!item) {
+ return TCL_ERROR;
+ }
+ Tcl_SetObjResult(interp,
+ Tcl_NewBooleanObj(Ttk_TagSetContains(item->tagset, tag)));
+ return TCL_OK;
+ } else {
+ Tcl_WrongNumArgs(interp, 3, objv, "tagName ?item?");
+ return TCL_ERROR;
+ }
+}
+
+/* + $tv tag names $tag
+ */
+static int TreeviewTagNamesCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ Treeview *tv = recordPtr;
+
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 3, objv, "");
+ return TCL_ERROR;
+ }
+
+ return Ttk_EnumerateTags(interp, tv->tree.tagTable);
+}
+
+/* + $tv tag add $tag $items
+ */
+static void AddTag(TreeItem *item, Ttk_Tag tag)
+{
+ if (Ttk_TagSetAdd(item->tagset, tag)) {
+ if (item->tagsObj) Tcl_DecrRefCount(item->tagsObj);
+ item->tagsObj = Ttk_NewTagSetObj(item->tagset);
+ Tcl_IncrRefCount(item->tagsObj);
+ }
+}
+
+static int TreeviewTagAddCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ Treeview *tv = recordPtr;
+ Ttk_Tag tag;
+ TreeItem **items;
+ int i;
+
+ if (objc != 5) {
+ Tcl_WrongNumArgs(interp, 3, objv, "tagName items");
+ return TCL_ERROR;
+ }
+
+ tag = Ttk_GetTagFromObj(tv->tree.tagTable, objv[3]);
+ items = GetItemListFromObj(interp, tv, objv[4]);
+
+ if (!items) {
+ return TCL_ERROR;
+ }
+
+ for (i=0; items[i]; ++i) {
+ AddTag(items[i], tag);
+ }
+
+ return TCL_OK;
+}
+
+/* + $tv tag remove $tag ?$items?
+ */
+static void RemoveTag(TreeItem *item, Ttk_Tag tag)
+{
+ if (Ttk_TagSetRemove(item->tagset, tag)) {
+ if (item->tagsObj) Tcl_DecrRefCount(item->tagsObj);
+ item->tagsObj = Ttk_NewTagSetObj(item->tagset);
+ Tcl_IncrRefCount(item->tagsObj);
+ }
+}
+
+static int TreeviewTagRemoveCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ Treeview *tv = recordPtr;
+ Ttk_Tag tag;
+
+ if (objc < 4) {
+ Tcl_WrongNumArgs(interp, 3, objv, "tagName items");
+ return TCL_ERROR;
+ }
+
+ tag = Ttk_GetTagFromObj(tv->tree.tagTable, objv[3]);
+
+ if (objc == 5) {
+ TreeItem **items = GetItemListFromObj(interp, tv, objv[4]);
+ int i;
+
+ if (!items) {
+ return TCL_ERROR;
+ }
+ for (i=0; items[i]; ++i) {
+ RemoveTag(items[i], tag);
+ }
+ } else if (objc == 4) {
+ TreeItem *item = tv->tree.root;
+ while (item) {
+ RemoveTag(item, tag);
+ item=NextPreorder(item);
+ }
+ }
+ return TCL_OK;
+}
+
+static const Ttk_Ensemble TreeviewTagCommands[] = {
+ { "add", TreeviewTagAddCommand,0 },
+ { "bind", TreeviewTagBindCommand,0 },
+ { "configure", TreeviewTagConfigureCommand,0 },
+ { "has", TreeviewTagHasCommand,0 },
+ { "names", TreeviewTagNamesCommand,0 },
+ { "remove", TreeviewTagRemoveCommand,0 },
+ { 0,0,0 }
+};
+
+/*------------------------------------------------------------------------
+ * +++ Widget commands record.
+ */
+static const Ttk_Ensemble TreeviewCommands[] = {
+ { "bbox", TreeviewBBoxCommand,0 },
+ { "children", TreeviewChildrenCommand,0 },
+ { "cget", TtkWidgetCgetCommand,0 },
+ { "column", TreeviewColumnCommand,0 },
+ { "configure", TtkWidgetConfigureCommand,0 },
+ { "delete", TreeviewDeleteCommand,0 },
+ { "detach", TreeviewDetachCommand,0 },
+ { "drag", TreeviewDragCommand,0 },
+ { "exists", TreeviewExistsCommand,0 },
+ { "focus", TreeviewFocusCommand,0 },
+ { "heading", TreeviewHeadingCommand,0 },
+ { "identify", TreeviewIdentifyCommand,0 },
+ { "index", TreeviewIndexCommand,0 },
+ { "instate", TtkWidgetInstateCommand,0 },
+ { "insert", TreeviewInsertCommand,0 },
+ { "item", TreeviewItemCommand,0 },
+ { "move", TreeviewMoveCommand,0 },
+ { "next", TreeviewNextCommand,0 },
+ { "parent", TreeviewParentCommand,0 },
+ { "prev", TreeviewPrevCommand,0 },
+ { "see", TreeviewSeeCommand,0 },
+ { "selection" , TreeviewSelectionCommand,0 },
+ { "set", TreeviewSetCommand,0 },
+ { "state", TtkWidgetStateCommand,0 },
+ { "tag", 0,TreeviewTagCommands },
+ { "xview", TreeviewXViewCommand,0 },
+ { "yview", TreeviewYViewCommand,0 },
+ { 0,0,0 }
+};
+
+/*------------------------------------------------------------------------
+ * +++ Widget definition.
+ */
+
+static WidgetSpec TreeviewWidgetSpec = {
+ "Treeview", /* className */
+ sizeof(Treeview), /* recordSize */
+ TreeviewOptionSpecs, /* optionSpecs */
+ TreeviewCommands, /* subcommands */
+ TreeviewInitialize, /* initializeProc */
+ TreeviewCleanup, /* cleanupProc */
+ TreeviewConfigure, /* configureProc */
+ TtkNullPostConfigure, /* postConfigureProc */
+ TreeviewGetLayout, /* getLayoutProc */
+ TreeviewSize, /* sizeProc */
+ TreeviewDoLayout, /* layoutProc */
+ TreeviewDisplay /* displayProc */
+};
+
+/*------------------------------------------------------------------------
+ * +++ Layout specifications.
+ */
+
+TTK_BEGIN_LAYOUT_TABLE(LayoutTable)
+
+TTK_LAYOUT("Treeview",
+ TTK_GROUP("Treeview.field", TTK_FILL_BOTH|TTK_BORDER,
+ TTK_GROUP("Treeview.padding", TTK_FILL_BOTH,
+ TTK_NODE("Treeview.treearea", TTK_FILL_BOTH))))
+
+TTK_LAYOUT("Item",
+ TTK_GROUP("Treeitem.padding", TTK_FILL_BOTH,
+ TTK_NODE("Treeitem.indicator", TTK_PACK_LEFT)
+ TTK_NODE("Treeitem.image", TTK_PACK_LEFT)
+ TTK_GROUP("Treeitem.focus", TTK_PACK_LEFT,
+ TTK_NODE("Treeitem.text", TTK_PACK_LEFT))))
+
+TTK_LAYOUT("Cell",
+ TTK_GROUP("Treedata.padding", TTK_FILL_BOTH,
+ TTK_NODE("Treeitem.text", TTK_FILL_BOTH)))
+
+TTK_LAYOUT("Heading",
+ TTK_NODE("Treeheading.cell", TTK_FILL_BOTH)
+ TTK_GROUP("Treeheading.border", TTK_FILL_BOTH,
+ TTK_GROUP("Treeheading.padding", TTK_FILL_BOTH,
+ TTK_NODE("Treeheading.image", TTK_PACK_RIGHT)
+ TTK_NODE("Treeheading.text", TTK_FILL_X))))
+
+TTK_LAYOUT("Row",
+ TTK_NODE("Treeitem.row", TTK_FILL_BOTH))
+
+TTK_END_LAYOUT_TABLE
+
+/*------------------------------------------------------------------------
+ * +++ Tree indicator element.
+ */
+
+typedef struct {
+ Tcl_Obj *colorObj;
+ Tcl_Obj *sizeObj;
+ Tcl_Obj *marginsObj;
+} TreeitemIndicator;
+
+static Ttk_ElementOptionSpec TreeitemIndicatorOptions[] = {
+ { "-foreground", TK_OPTION_COLOR,
+ Tk_Offset(TreeitemIndicator,colorObj), DEFAULT_FOREGROUND },
+ { "-indicatorsize", TK_OPTION_PIXELS,
+ Tk_Offset(TreeitemIndicator,sizeObj), "12" },
+ { "-indicatormargins", TK_OPTION_STRING,
+ Tk_Offset(TreeitemIndicator,marginsObj), "2 2 4 2" },
+ { NULL, 0, 0, NULL }
+};
+
+static void TreeitemIndicatorSize(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+{
+ TreeitemIndicator *indicator = elementRecord;
+ Ttk_Padding margins;
+ int size = 0;
+
+ Ttk_GetPaddingFromObj(NULL, tkwin, indicator->marginsObj, &margins);
+ Tk_GetPixelsFromObj(NULL, tkwin, indicator->sizeObj, &size);
+
+ *widthPtr = size + Ttk_PaddingWidth(margins);
+ *heightPtr = size + Ttk_PaddingHeight(margins);
+}
+
+static void TreeitemIndicatorDraw(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ Drawable d, Ttk_Box b, Ttk_State state)
+{
+ TreeitemIndicator *indicator = elementRecord;
+ ArrowDirection direction =
+ (state & TTK_STATE_OPEN) ? ARROW_DOWN : ARROW_RIGHT;
+ Ttk_Padding margins;
+ XColor *borderColor = Tk_GetColorFromObj(tkwin, indicator->colorObj);
+ XGCValues gcvalues; GC gc; unsigned mask;
+
+ if (state & TTK_STATE_LEAF) /* don't draw anything */
+ return;
+
+ Ttk_GetPaddingFromObj(NULL,tkwin,indicator->marginsObj,&margins);
+ b = Ttk_PadBox(b, margins);
+
+ gcvalues.foreground = borderColor->pixel;
+ gcvalues.line_width = 1;
+ mask = GCForeground | GCLineWidth;
+ gc = Tk_GetGC(tkwin, mask, &gcvalues);
+
+ TtkDrawArrow(Tk_Display(tkwin), d, gc, b, direction);
+
+ Tk_FreeGC(Tk_Display(tkwin), gc);
+}
+
+static Ttk_ElementSpec TreeitemIndicatorElementSpec = {
+ TK_STYLE_VERSION_2,
+ sizeof(TreeitemIndicator),
+ TreeitemIndicatorOptions,
+ TreeitemIndicatorSize,
+ TreeitemIndicatorDraw
+};
+
+/*------------------------------------------------------------------------
+ * +++ Row element.
+ */
+
+typedef struct {
+ Tcl_Obj *backgroundObj;
+ Tcl_Obj *rowNumberObj;
+} RowElement;
+
+static Ttk_ElementOptionSpec RowElementOptions[] = {
+ { "-background", TK_OPTION_COLOR,
+ Tk_Offset(RowElement,backgroundObj), DEFAULT_BACKGROUND },
+ { "-rownumber", TK_OPTION_INT,
+ Tk_Offset(RowElement,rowNumberObj), "0" },
+ { NULL, 0, 0, NULL }
+};
+
+static void RowElementDraw(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ Drawable d, Ttk_Box b, Ttk_State state)
+{
+ RowElement *row = elementRecord;
+ XColor *color = Tk_GetColorFromObj(tkwin, row->backgroundObj);
+ GC gc = Tk_GCForColor(color, d);
+ XFillRectangle(Tk_Display(tkwin), d, gc,
+ b.x, b.y, b.width, b.height);
+}
+
+static Ttk_ElementSpec RowElementSpec = {
+ TK_STYLE_VERSION_2,
+ sizeof(RowElement),
+ RowElementOptions,
+ TtkNullElementSize,
+ RowElementDraw
+};
+
+/*------------------------------------------------------------------------
+ * +++ Initialisation.
+ */
+
+MODULE_SCOPE
+void TtkTreeview_Init(Tcl_Interp *interp)
+{
+ Ttk_Theme theme = Ttk_GetDefaultTheme(interp);
+
+ RegisterWidget(interp, "ttk::treeview", &TreeviewWidgetSpec);
+
+ Ttk_RegisterElement(interp, theme, "Treeitem.indicator",
+ &TreeitemIndicatorElementSpec, 0);
+ Ttk_RegisterElement(interp, theme, "Treeitem.row", &RowElementSpec, 0);
+ Ttk_RegisterElement(interp, theme, "Treeheading.cell", &RowElementSpec, 0);
+ Ttk_RegisterElement(interp, theme, "treearea", &ttkNullElementSpec, 0);
+
+ Ttk_RegisterLayouts(theme, LayoutTable);
+}
+
+/*EOF*/
diff --git a/generic/ttk/ttkWidget.c b/generic/ttk/ttkWidget.c
new file mode 100644
index 0000000..d5e0484
--- /dev/null
+++ b/generic/ttk/ttkWidget.c
@@ -0,0 +1,789 @@
+/*
+ * Copyright (c) 2003, Joe English
+ *
+ * Core widget utilities.
+ */
+
+#include <string.h>
+#include <tk.h>
+#include "ttkTheme.h"
+#include "ttkWidget.h"
+
+#ifdef MAC_OSX_TK
+#define TK_NO_DOUBLE_BUFFERING 1
+#endif
+
+/*------------------------------------------------------------------------
+ * +++ Internal helper routines.
+ */
+
+/* UpdateLayout --
+ * Call the widget's get-layout hook to recompute corePtr->layout.
+ * Returns TCL_OK if successful, returns TCL_ERROR and leaves
+ * the layout unchanged otherwise.
+ */
+static int UpdateLayout(Tcl_Interp *interp, WidgetCore *corePtr)
+{
+ Ttk_Theme themePtr = Ttk_GetCurrentTheme(interp);
+ Ttk_Layout newLayout =
+ corePtr->widgetSpec->getLayoutProc(interp, themePtr,corePtr);
+
+ if (newLayout) {
+ if (corePtr->layout) {
+ Ttk_FreeLayout(corePtr->layout);
+ }
+ corePtr->layout = newLayout;
+ return TCL_OK;
+ }
+ return TCL_ERROR;
+}
+
+/* SizeChanged --
+ * Call the widget's sizeProc to compute new requested size
+ * and pass it to the geometry manager.
+ */
+static void SizeChanged(WidgetCore *corePtr)
+{
+ int reqWidth = 1, reqHeight = 1;
+
+ if (corePtr->widgetSpec->sizeProc(corePtr,&reqWidth,&reqHeight)) {
+ Tk_GeometryRequest(corePtr->tkwin, reqWidth, reqHeight);
+ }
+}
+
+#ifndef TK_NO_DOUBLE_BUFFERING
+
+/* BeginDrawing --
+ * Returns a Drawable for drawing the widget contents.
+ * This is normally an off-screen Pixmap, copied to
+ * the window by EndDrawing().
+ */
+static Drawable BeginDrawing(Tk_Window tkwin)
+{
+ return Tk_GetPixmap(Tk_Display(tkwin), Tk_WindowId(tkwin),
+ Tk_Width(tkwin), Tk_Height(tkwin), Tk_Depth(tkwin));
+}
+
+/* EndDrawing --
+ * Copy the drawable contents to the screen and release resources.
+ */
+static void EndDrawing(Tk_Window tkwin, Drawable d)
+{
+ XGCValues gcValues;
+ GC gc;
+
+ gcValues.function = GXcopy;
+ gcValues.graphics_exposures = False;
+ gc = Tk_GetGC(tkwin, GCFunction|GCGraphicsExposures, &gcValues);
+
+ XCopyArea(Tk_Display(tkwin), d, Tk_WindowId(tkwin), gc,
+ 0, 0, (unsigned) Tk_Width(tkwin), (unsigned) Tk_Height(tkwin),
+ 0, 0);
+
+ Tk_FreePixmap(Tk_Display(tkwin), d);
+ Tk_FreeGC(Tk_Display(tkwin), gc);
+}
+#else
+/* No double-buffering: draw directly into the window. */
+static Drawable BeginDrawing(Tk_Window tkwin) { return Tk_WindowId(tkwin); }
+static void EndDrawing(Tk_Window tkwin, Drawable d) { }
+#endif
+
+/* DrawWidget --
+ * Redraw a widget. Called as an idle handler.
+ */
+static void DrawWidget(ClientData recordPtr)
+{
+ WidgetCore *corePtr = recordPtr;
+
+ corePtr->flags &= ~REDISPLAY_PENDING;
+ if (Tk_IsMapped(corePtr->tkwin)) {
+ Drawable d = BeginDrawing(corePtr->tkwin);
+ corePtr->widgetSpec->layoutProc(recordPtr);
+ corePtr->widgetSpec->displayProc(recordPtr, d);
+ EndDrawing(corePtr->tkwin, d);
+ }
+}
+
+/* TtkRedisplayWidget --
+ * Schedule redisplay as an idle handler.
+ */
+void TtkRedisplayWidget(WidgetCore *corePtr)
+{
+ if (corePtr->flags & WIDGET_DESTROYED) {
+ return;
+ }
+
+ if (!(corePtr->flags & REDISPLAY_PENDING)) {
+ Tcl_DoWhenIdle(DrawWidget, corePtr);
+ corePtr->flags |= REDISPLAY_PENDING;
+ }
+}
+
+/* TtkResizeWidget --
+ * Recompute widget size, schedule geometry propagation and redisplay.
+ */
+void TtkResizeWidget(WidgetCore *corePtr)
+{
+ if (corePtr->flags & WIDGET_DESTROYED) {
+ return;
+ }
+
+ SizeChanged(corePtr);
+ TtkRedisplayWidget(corePtr);
+}
+
+/* TtkWidgetChangeState --
+ * Set / clear the specified bits in the 'state' flag,
+ */
+void TtkWidgetChangeState(WidgetCore *corePtr,
+ unsigned int setBits, unsigned int clearBits)
+{
+ Ttk_State oldState = corePtr->state;
+ corePtr->state = (oldState & ~clearBits) | setBits;
+ if (corePtr->state ^ oldState) {
+ TtkRedisplayWidget(corePtr);
+ }
+}
+
+/* WidgetInstanceObjCmd --
+ * Widget instance command implementation.
+ */
+static int
+WidgetInstanceObjCmd(
+ ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ WidgetCore *corePtr = clientData;
+ const Ttk_Ensemble *commands = corePtr->widgetSpec->commands;
+ int status;
+
+ Tcl_Preserve(clientData);
+ status = Ttk_InvokeEnsemble(commands,1, clientData,interp,objc,objv);
+ Tcl_Release(clientData);
+
+ return status;
+}
+
+/*------------------------------------------------------------------------
+ * +++ Widget destruction.
+ *
+ * A widget can be destroyed when the application explicitly
+ * destroys the window or one of its ancestors via [destroy]
+ * or Tk_DestroyWindow(); when the application deletes the widget
+ * instance command; when there is an error in the widget constructor;
+ * or when another application calls XDestroyWindow on the window ID.
+ *
+ * The window receives a <DestroyNotify> event in all cases,
+ * so we do the bulk of the cleanup there. See [#2207435] for
+ * further notes (esp. re: Tk_FreeConfigOptions).
+ *
+ * Widget code that reenters the interp should only do so
+ * when the widtget is Tcl_Preserve()d, and should check
+ * the WIDGET_DESTROYED flag bit upon return.
+ */
+
+/* WidgetInstanceObjCmdDeleted --
+ * Widget instance command deletion callback.
+ */
+static void
+WidgetInstanceObjCmdDeleted(ClientData clientData)
+{
+ WidgetCore *corePtr = clientData;
+ corePtr->widgetCmd = NULL;
+ if (corePtr->tkwin != NULL)
+ Tk_DestroyWindow(corePtr->tkwin);
+}
+
+/* FreeWidget --
+ * Final cleanup for widget; called via Tcl_EventuallyFree().
+ */
+static void
+FreeWidget(char *memPtr)
+{
+ ckfree(memPtr);
+}
+
+/* DestroyWidget --
+ * Main widget destructor; called from <DestroyNotify> event handler.
+ */
+static void
+DestroyWidget(WidgetCore *corePtr)
+{
+ corePtr->flags |= WIDGET_DESTROYED;
+
+ corePtr->widgetSpec->cleanupProc(corePtr);
+
+ Tk_FreeConfigOptions(
+ (ClientData)corePtr, corePtr->optionTable, corePtr->tkwin);
+
+ if (corePtr->layout) {
+ Ttk_FreeLayout(corePtr->layout);
+ }
+
+ if (corePtr->flags & REDISPLAY_PENDING) {
+ Tcl_CancelIdleCall(DrawWidget, corePtr);
+ }
+
+ corePtr->tkwin = NULL;
+ if (corePtr->widgetCmd) {
+ Tcl_Command cmd = corePtr->widgetCmd;
+ corePtr->widgetCmd = 0;
+ /* NB: this can reenter the interpreter via a command traces */
+ Tcl_DeleteCommandFromToken(corePtr->interp, cmd);
+ }
+ Tcl_EventuallyFree(corePtr, FreeWidget);
+}
+
+/*
+ * CoreEventProc --
+ * Event handler for basic events.
+ * Processes Expose, Configure, FocusIn/Out, and Destroy events.
+ * Also handles <<ThemeChanged>> virtual events.
+ *
+ * For Expose and Configure, simply schedule the widget for redisplay.
+ * For Destroy events, handle the cleanup process.
+ *
+ * For Focus events, set/clear the focus bit in the state field.
+ * It turns out this is impossible to do correctly in a binding script,
+ * because Tk filters out focus events with detail == NotifyInferior.
+ *
+ * For Deactivate/Activate pseudo-events, set/clear the background state
+ * flag.
+ */
+
+static const unsigned CoreEventMask
+ = ExposureMask
+ | StructureNotifyMask
+ | FocusChangeMask
+ | VirtualEventMask
+ | ActivateMask
+ | EnterWindowMask
+ | LeaveWindowMask
+ ;
+
+static void CoreEventProc(ClientData clientData, XEvent *eventPtr)
+{
+ WidgetCore *corePtr = clientData;
+
+ switch (eventPtr->type)
+ {
+ case ConfigureNotify :
+ TtkRedisplayWidget(corePtr);
+ break;
+ case Expose :
+ if (eventPtr->xexpose.count == 0) {
+ TtkRedisplayWidget(corePtr);
+ }
+ break;
+ case DestroyNotify :
+ Tk_DeleteEventHandler(
+ corePtr->tkwin, CoreEventMask,CoreEventProc,clientData);
+ DestroyWidget(corePtr);
+ break;
+ case FocusIn:
+ case FocusOut:
+ /* Don't process "virtual crossing" events */
+ if ( eventPtr->xfocus.detail == NotifyInferior
+ || eventPtr->xfocus.detail == NotifyAncestor
+ || eventPtr->xfocus.detail == NotifyNonlinear)
+ {
+ if (eventPtr->type == FocusIn)
+ corePtr->state |= TTK_STATE_FOCUS;
+ else
+ corePtr->state &= ~TTK_STATE_FOCUS;
+ TtkRedisplayWidget(corePtr);
+ }
+ break;
+ case ActivateNotify:
+ corePtr->state &= ~TTK_STATE_BACKGROUND;
+ TtkRedisplayWidget(corePtr);
+ break;
+ case DeactivateNotify:
+ corePtr->state |= TTK_STATE_BACKGROUND;
+ TtkRedisplayWidget(corePtr);
+ break;
+ case LeaveNotify:
+ corePtr->state &= ~TTK_STATE_HOVER;
+ TtkRedisplayWidget(corePtr);
+ break;
+ case EnterNotify:
+ corePtr->state |= TTK_STATE_HOVER;
+ TtkRedisplayWidget(corePtr);
+ break;
+ case VirtualEvent:
+ if (!strcmp("ThemeChanged", ((XVirtualEvent *)(eventPtr))->name)) {
+ (void)UpdateLayout(corePtr->interp, corePtr);
+ SizeChanged(corePtr);
+ TtkRedisplayWidget(corePtr);
+ }
+ default:
+ /* can't happen... */
+ break;
+ }
+}
+
+/*
+ * WidgetWorldChanged --
+ * Default Tk_ClassWorldChangedProc() for widgets.
+ * Invoked whenever fonts or other system resources are changed;
+ * recomputes geometry.
+ */
+static void WidgetWorldChanged(ClientData clientData)
+{
+ WidgetCore *corePtr = clientData;
+ SizeChanged(corePtr);
+ TtkRedisplayWidget(corePtr);
+}
+
+static Tk_ClassProcs widgetClassProcs = {
+ sizeof(Tk_ClassProcs), /* size */
+ WidgetWorldChanged, /* worldChangedProc */
+ NULL, /* createProc */
+ NULL /* modalProc */
+};
+
+/*
+ * TtkWidgetConstructorObjCmd --
+ * General-purpose widget constructor command implementation.
+ * ClientData is a WidgetSpec *.
+ */
+int TtkWidgetConstructorObjCmd(
+ ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ WidgetSpec *widgetSpec = clientData;
+ const char *className = widgetSpec->className;
+ Tk_OptionTable optionTable =
+ Tk_CreateOptionTable(interp, widgetSpec->optionSpecs);
+ Tk_Window tkwin;
+ void *recordPtr;
+ WidgetCore *corePtr;
+ Tk_SavedOptions savedOptions;
+ int i;
+
+ if (objc < 2 || objc % 2 == 1) {
+ Tcl_WrongNumArgs(interp, 1, objv, "pathName ?-option value ...?");
+ return TCL_ERROR;
+ }
+
+ /* Check if a -class option has been specified.
+ * We have to do this before the InitOptions() call,
+ * since InitOptions() is affected by the widget class.
+ */
+ for (i = 2; i < objc; i += 2) {
+ if (!strcmp(Tcl_GetString(objv[i]), "-class")) {
+ className = Tcl_GetString(objv[i+1]);
+ break;
+ }
+ }
+
+ tkwin = Tk_CreateWindowFromPath(
+ interp, Tk_MainWindow(interp), Tcl_GetString(objv[1]), NULL);
+ if (tkwin == NULL)
+ return TCL_ERROR;
+
+ /*
+ * Allocate and initialize the widget record.
+ */
+ recordPtr = ckalloc(widgetSpec->recordSize);
+ memset(recordPtr, 0, widgetSpec->recordSize);
+ corePtr = recordPtr;
+
+ corePtr->tkwin = tkwin;
+ corePtr->interp = interp;
+ corePtr->widgetSpec = widgetSpec;
+ corePtr->widgetCmd = Tcl_CreateObjCommand(interp, Tk_PathName(tkwin),
+ WidgetInstanceObjCmd, recordPtr, WidgetInstanceObjCmdDeleted);
+ corePtr->optionTable = optionTable;
+ corePtr->layout = NULL;
+ corePtr->flags = 0;
+ corePtr->state = 0;
+
+ Tk_SetClass(tkwin, className);
+ Tk_SetClassProcs(tkwin, &widgetClassProcs, recordPtr);
+ Tk_SetWindowBackgroundPixmap(tkwin, ParentRelative);
+
+ widgetSpec->initializeProc(interp, recordPtr);
+
+ Tk_CreateEventHandler(tkwin, CoreEventMask, CoreEventProc, recordPtr);
+
+ /*
+ * Initial configuration.
+ */
+
+ Tcl_Preserve(corePtr);
+ if (Tk_InitOptions(interp, recordPtr, optionTable, tkwin) != TCL_OK) {
+ goto error;
+ }
+
+ if (Tk_SetOptions(interp, recordPtr, optionTable,
+ objc - 2, objv + 2, tkwin, &savedOptions, NULL) != TCL_OK) {
+ Tk_RestoreSavedOptions(&savedOptions);
+ goto error;
+ } else {
+ Tk_FreeSavedOptions(&savedOptions);
+ }
+ if (widgetSpec->configureProc(interp, recordPtr, ~0) != TCL_OK)
+ goto error;
+ if (widgetSpec->postConfigureProc(interp, recordPtr, ~0) != TCL_OK)
+ goto error;
+
+ if (WidgetDestroyed(corePtr))
+ goto error;
+
+ Tcl_Release(corePtr);
+
+ SizeChanged(corePtr);
+ Tk_MakeWindowExist(tkwin);
+
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(Tk_PathName(tkwin), -1));
+ return TCL_OK;
+
+error:
+ if (WidgetDestroyed(corePtr)) {
+ Tcl_SetResult(interp, "Widget has been destroyed", TCL_STATIC);
+ } else {
+ Tk_DestroyWindow(tkwin);
+ }
+ Tcl_Release(corePtr);
+ return TCL_ERROR;
+}
+
+/*------------------------------------------------------------------------
+ * +++ Default implementations for widget hook procedures.
+ */
+
+/* TtkWidgetGetLayout --
+ * Default getLayoutProc.
+ * Looks up the layout based on the -style resource (if specified),
+ * otherwise use the widget class.
+ */
+Ttk_Layout TtkWidgetGetLayout(
+ Tcl_Interp *interp, Ttk_Theme themePtr, void *recordPtr)
+{
+ WidgetCore *corePtr = recordPtr;
+ const char *styleName = 0;
+
+ if (corePtr->styleObj)
+ styleName = Tcl_GetString(corePtr->styleObj);
+
+ if (!styleName || *styleName == '\0')
+ styleName = corePtr->widgetSpec->className;
+
+ return Ttk_CreateLayout(interp, themePtr, styleName,
+ recordPtr, corePtr->optionTable, corePtr->tkwin);
+}
+
+/*
+ * TtkWidgetGetOrientedLayout --
+ * Helper routine. Same as TtkWidgetGetLayout, but prefixes
+ * "Horizontal." or "Vertical." to the style name, depending
+ * on the value of the 'orient' option.
+ */
+Ttk_Layout TtkWidgetGetOrientedLayout(
+ Tcl_Interp *interp, Ttk_Theme themePtr, void *recordPtr, Tcl_Obj *orientObj)
+{
+ WidgetCore *corePtr = recordPtr;
+ const char *baseStyleName = 0;
+ Tcl_DString styleName;
+ int orient = TTK_ORIENT_HORIZONTAL;
+ Ttk_Layout layout;
+
+ Tcl_DStringInit(&styleName);
+
+ /* Prefix:
+ */
+ Ttk_GetOrientFromObj(NULL, orientObj, &orient);
+ if (orient == TTK_ORIENT_HORIZONTAL)
+ Tcl_DStringAppend(&styleName, "Horizontal.", -1);
+ else
+ Tcl_DStringAppend(&styleName, "Vertical.", -1);
+
+ /* Add base style name:
+ */
+ if (corePtr->styleObj)
+ baseStyleName = Tcl_GetString(corePtr->styleObj);
+ if (!baseStyleName || *baseStyleName == '\0')
+ baseStyleName = corePtr->widgetSpec->className;
+
+ Tcl_DStringAppend(&styleName, baseStyleName, -1);
+
+ /* Create layout:
+ */
+ layout= Ttk_CreateLayout(interp, themePtr, Tcl_DStringValue(&styleName),
+ recordPtr, corePtr->optionTable, corePtr->tkwin);
+
+ Tcl_DStringFree(&styleName);
+
+ return layout;
+}
+
+/* TtkNullInitialize --
+ * Default widget initializeProc (no-op)
+ */
+void TtkNullInitialize(Tcl_Interp *interp, void *recordPtr)
+{
+}
+
+/* TtkNullPostConfigure --
+ * Default widget postConfigureProc (no-op)
+ */
+int TtkNullPostConfigure(Tcl_Interp *interp, void *clientData, int mask)
+{
+ return TCL_OK;
+}
+
+/* TtkCoreConfigure --
+ * Default widget configureProc.
+ * Handles -style option.
+ */
+int TtkCoreConfigure(Tcl_Interp *interp, void *clientData, int mask)
+{
+ WidgetCore *corePtr = clientData;
+ int status = TCL_OK;
+
+ if (mask & STYLE_CHANGED) {
+ status = UpdateLayout(interp, corePtr);
+ }
+
+ return status;
+}
+
+/* TtkNullCleanup --
+ * Default widget cleanupProc (no-op)
+ */
+void TtkNullCleanup(void *recordPtr)
+{
+ return;
+}
+
+/* TtkWidgetDoLayout --
+ * Default widget layoutProc.
+ */
+void TtkWidgetDoLayout(void *clientData)
+{
+ WidgetCore *corePtr = clientData;
+ Ttk_PlaceLayout(corePtr->layout,corePtr->state,Ttk_WinBox(corePtr->tkwin));
+}
+
+/* TtkWidgetDisplay --
+ * Default widget displayProc.
+ */
+void TtkWidgetDisplay(void *recordPtr, Drawable d)
+{
+ WidgetCore *corePtr = recordPtr;
+ Ttk_DrawLayout(corePtr->layout, corePtr->state, d);
+}
+
+/* TtkWidgetSize --
+ * Default widget sizeProc()
+ */
+int TtkWidgetSize(void *recordPtr, int *widthPtr, int *heightPtr)
+{
+ WidgetCore *corePtr = recordPtr;
+ Ttk_LayoutSize(corePtr->layout, corePtr->state, widthPtr, heightPtr);
+ return 1;
+}
+
+/*------------------------------------------------------------------------
+ * +++ Default implementations for widget subcommands.
+ */
+
+/* $w cget -option
+ */
+int TtkWidgetCgetCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ WidgetCore *corePtr = recordPtr;
+ Tcl_Obj *result;
+
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "option");
+ return TCL_ERROR;
+ }
+ result = Tk_GetOptionValue(interp, recordPtr,
+ corePtr->optionTable, objv[2], corePtr->tkwin);
+ if (result == NULL)
+ return TCL_ERROR;
+ Tcl_SetObjResult(interp, result);
+ return TCL_OK;
+}
+
+/* $w configure ?-option ?value ....??
+ */
+int TtkWidgetConfigureCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ WidgetCore *corePtr = recordPtr;
+ Tcl_Obj *result;
+
+ if (objc == 2) {
+ result = Tk_GetOptionInfo(interp, recordPtr,
+ corePtr->optionTable, NULL, corePtr->tkwin);
+ } else if (objc == 3) {
+ result = Tk_GetOptionInfo(interp, recordPtr,
+ corePtr->optionTable, objv[2], corePtr->tkwin);
+ } else {
+ Tk_SavedOptions savedOptions;
+ int status;
+ int mask = 0;
+
+ status = Tk_SetOptions(interp, recordPtr,
+ corePtr->optionTable, objc - 2, objv + 2,
+ corePtr->tkwin, &savedOptions, &mask);
+ if (status != TCL_OK)
+ return status;
+
+ if (mask & READONLY_OPTION) {
+ Tcl_SetResult(interp,
+ "Attempt to change read-only option", TCL_STATIC);
+ Tk_RestoreSavedOptions(&savedOptions);
+ return TCL_ERROR;
+ }
+
+ status = corePtr->widgetSpec->configureProc(interp, recordPtr, mask);
+ if (status != TCL_OK) {
+ Tk_RestoreSavedOptions(&savedOptions);
+ return status;
+ }
+ Tk_FreeSavedOptions(&savedOptions);
+
+ status = corePtr->widgetSpec->postConfigureProc(interp,recordPtr,mask);
+ if (WidgetDestroyed(corePtr)) {
+ Tcl_SetResult(interp, "Widget has been destroyed", TCL_STATIC);
+ status = TCL_ERROR;
+ }
+ if (status != TCL_OK) {
+ return status;
+ }
+
+ if (mask & (STYLE_CHANGED | GEOMETRY_CHANGED)) {
+ SizeChanged(corePtr);
+ }
+
+ TtkRedisplayWidget(corePtr);
+ result = Tcl_NewObj();
+ }
+
+ if (result == 0) {
+ return TCL_ERROR;
+ }
+ Tcl_SetObjResult(interp, result);
+ return TCL_OK;
+}
+
+/* $w state ? $stateSpec ?
+ *
+ * If $stateSpec is specified, modify the widget state accordingly,
+ * return a new stateSpec representing the changed bits.
+ *
+ * Otherwise, return a statespec matching all the currently-set bits.
+ */
+
+int TtkWidgetStateCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ WidgetCore *corePtr = recordPtr;
+ Ttk_StateSpec spec;
+ int status;
+ Ttk_State oldState, changed;
+
+ if (objc == 2) {
+ Tcl_SetObjResult(interp,
+ Ttk_NewStateSpecObj(corePtr->state, 0ul));
+ return TCL_OK;
+ }
+
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "state-spec");
+ return TCL_ERROR;
+ }
+ status = Ttk_GetStateSpecFromObj(interp, objv[2], &spec);
+ if (status != TCL_OK)
+ return status;
+
+ oldState = corePtr->state;
+ corePtr->state = Ttk_ModifyState(corePtr->state, &spec);
+ changed = corePtr->state ^ oldState;
+
+ TtkRedisplayWidget(corePtr);
+
+ Tcl_SetObjResult(interp,
+ Ttk_NewStateSpecObj(oldState & changed, ~oldState & changed));
+ return status;
+}
+
+/* $w instate $stateSpec ?$script?
+ *
+ * Tests if widget state matches $stateSpec.
+ * If $script is specified, execute script if state matches.
+ * Otherwise, return true/false
+ */
+
+int TtkWidgetInstateCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ WidgetCore *corePtr = recordPtr;
+ Ttk_State state = corePtr->state;
+ Ttk_StateSpec spec;
+ int status = TCL_OK;
+
+ if (objc < 3 || objc > 4) {
+ Tcl_WrongNumArgs(interp, 2, objv, "state-spec ?script?");
+ return TCL_ERROR;
+ }
+ status = Ttk_GetStateSpecFromObj(interp, objv[2], &spec);
+ if (status != TCL_OK)
+ return status;
+
+ if (objc == 3) {
+ Tcl_SetObjResult(interp,
+ Tcl_NewBooleanObj(Ttk_StateMatches(state,&spec)));
+ } else if (objc == 4) {
+ if (Ttk_StateMatches(state,&spec)) {
+ status = Tcl_EvalObjEx(interp, objv[3], 0);
+ }
+ }
+ return status;
+}
+
+/* $w identify $x $y
+ * $w identify element $x $y
+ * Returns: name of element at $x, $y
+ */
+int TtkWidgetIdentifyCommand(
+ void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
+{
+ WidgetCore *corePtr = recordPtr;
+ Ttk_Element element;
+ static const char *whatTable[] = { "element", NULL };
+ int x, y, what;
+
+ if (objc < 4 || objc > 5) {
+ Tcl_WrongNumArgs(interp, 2, objv, "?what? x y");
+ return TCL_ERROR;
+ }
+ if (objc == 5) {
+ /* $w identify element $x $y */
+ if (Tcl_GetIndexFromObj(interp,objv[2],whatTable,"option",0,&what)
+ != TCL_OK)
+ {
+ return TCL_ERROR;
+ }
+ }
+
+ if ( Tcl_GetIntFromObj(interp, objv[objc-2], &x) != TCL_OK
+ || Tcl_GetIntFromObj(interp, objv[objc-1], &y) != TCL_OK
+ ) {
+ return TCL_ERROR;
+ }
+
+ element = Ttk_IdentifyElement(corePtr->layout, x, y);
+ if (element) {
+ const char *elementName = Ttk_ElementName(element);
+ Tcl_SetObjResult(interp,Tcl_NewStringObj(elementName,-1));
+ }
+
+ return TCL_OK;
+}
+
+/*EOF*/
diff --git a/generic/ttk/ttkWidget.h b/generic/ttk/ttkWidget.h
new file mode 100644
index 0000000..9e9ab69
--- /dev/null
+++ b/generic/ttk/ttkWidget.h
@@ -0,0 +1,273 @@
+/*
+ * Copyright (c) 2003, Joe English
+ * Helper routines for widget implementations.
+ */
+
+#ifndef _TTKWIDGET
+#define _TTKWIDGET
+
+/*
+ * State flags for 'flags' field.
+ */
+#define WIDGET_DESTROYED 0x0001
+#define REDISPLAY_PENDING 0x0002 /* scheduled call to RedisplayWidget */
+#define CURSOR_ON 0x0020 /* See TtkBlinkCursor() */
+#define WIDGET_USER_FLAG 0x0100 /* 0x0100 - 0x8000 for user flags */
+
+/*
+ * Bit fields for OptionSpec 'mask' field:
+ */
+#define READONLY_OPTION 0x1
+#define STYLE_CHANGED 0x2
+#define GEOMETRY_CHANGED 0x4
+
+/*
+ * Core widget elements
+ */
+typedef struct WidgetSpec_ WidgetSpec; /* Forward */
+
+typedef struct
+{
+ Tk_Window tkwin; /* Window associated with widget */
+ Tcl_Interp *interp; /* Interpreter associated with widget. */
+ WidgetSpec *widgetSpec; /* Widget class hooks */
+ Tcl_Command widgetCmd; /* Token for widget command. */
+ Tk_OptionTable optionTable; /* Option table */
+ Ttk_Layout layout; /* Widget layout */
+
+ /*
+ * Storage for resources:
+ */
+ Tcl_Obj *takeFocusPtr; /* Storage for -takefocus option */
+ Tcl_Obj *cursorObj; /* Storage for -cursor option */
+ Tcl_Obj *styleObj; /* Name of currently-applied style */
+ Tcl_Obj *classObj; /* Class name (readonly option) */
+
+ Ttk_State state; /* Current widget state */
+ unsigned int flags; /* internal flags, see above */
+
+} WidgetCore;
+
+/*
+ * Widget specifications:
+ */
+struct WidgetSpec_
+{
+ const char *className; /* Widget class name */
+ size_t recordSize; /* #bytes in widget record */
+ const Tk_OptionSpec *optionSpecs; /* Option specifications */
+ const Ttk_Ensemble *commands; /* Widget instance subcommands */
+
+ /*
+ * Hooks:
+ */
+ void (*initializeProc)(Tcl_Interp *, void *recordPtr);
+ void (*cleanupProc)(void *recordPtr);
+ int (*configureProc)(Tcl_Interp *, void *recordPtr, int flags);
+ int (*postConfigureProc)(Tcl_Interp *, void *recordPtr, int flags);
+ Ttk_Layout (*getLayoutProc)(Tcl_Interp *,Ttk_Theme, void *recordPtr);
+ int (*sizeProc)(void *recordPtr, int *widthPtr, int *heightPtr);
+ void (*layoutProc)(void *recordPtr);
+ void (*displayProc)(void *recordPtr, Drawable d);
+};
+
+/*
+ * Common factors for widget implementations:
+ */
+MODULE_SCOPE void TtkNullInitialize(Tcl_Interp *, void *);
+MODULE_SCOPE int TtkNullPostConfigure(Tcl_Interp *, void *, int);
+MODULE_SCOPE void TtkNullCleanup(void *recordPtr);
+MODULE_SCOPE Ttk_Layout TtkWidgetGetLayout(
+ Tcl_Interp *, Ttk_Theme, void *recordPtr);
+MODULE_SCOPE Ttk_Layout TtkWidgetGetOrientedLayout(
+ Tcl_Interp *, Ttk_Theme, void *recordPtr, Tcl_Obj *orientObj);
+MODULE_SCOPE int TtkWidgetSize(void *recordPtr, int *w, int *h);
+MODULE_SCOPE void TtkWidgetDoLayout(void *recordPtr);
+MODULE_SCOPE void TtkWidgetDisplay(void *recordPtr, Drawable);
+
+MODULE_SCOPE int TtkCoreConfigure(Tcl_Interp*, void *, int mask);
+
+/* Common widget commands:
+ */
+MODULE_SCOPE int TtkWidgetConfigureCommand(
+ void *,Tcl_Interp *, int, Tcl_Obj*const[]);
+MODULE_SCOPE int TtkWidgetCgetCommand(
+ void *,Tcl_Interp *, int, Tcl_Obj*const[]);
+MODULE_SCOPE int TtkWidgetInstateCommand(
+ void *,Tcl_Interp *, int, Tcl_Obj*const[]);
+MODULE_SCOPE int TtkWidgetStateCommand(
+ void *,Tcl_Interp *, int, Tcl_Obj*const[]);
+MODULE_SCOPE int TtkWidgetIdentifyCommand(
+ void *,Tcl_Interp *, int, Tcl_Obj*const[]);
+
+/* Widget constructor:
+ */
+MODULE_SCOPE int TtkWidgetConstructorObjCmd(
+ ClientData, Tcl_Interp*, int, Tcl_Obj*const[]);
+
+#define RegisterWidget(interp, name, specPtr) \
+ Tcl_CreateObjCommand(interp, name, \
+ TtkWidgetConstructorObjCmd, (ClientData)specPtr,NULL)
+
+/* WIDGET_TAKEFOCUS_TRUE --
+ * WIDGET_TAKEFOCUS_FALSE --
+ * Add one or the other of these to each OptionSpecs table
+ * to indicate whether the widget should take focus
+ * during keyboard traversal.
+ */
+#define WIDGET_TAKEFOCUS_TRUE \
+ {TK_OPTION_STRING, "-takefocus", "takeFocus", "TakeFocus", \
+ "ttk::takefocus", Tk_Offset(WidgetCore, takeFocusPtr), -1, 0,0,0 }
+#define WIDGET_TAKEFOCUS_FALSE \
+ {TK_OPTION_STRING, "-takefocus", "takeFocus", "TakeFocus", \
+ "", Tk_Offset(WidgetCore, takeFocusPtr), -1, 0,0,0 }
+
+/* WIDGET_INHERIT_OPTIONS(baseOptionSpecs) --
+ * Add this at the end of an OptionSpecs table to inherit
+ * the options from 'baseOptionSpecs'.
+ */
+#define WIDGET_INHERIT_OPTIONS(baseOptionSpecs) \
+ {TK_OPTION_END, 0,0,0, NULL, -1,-1, 0, (ClientData)baseOptionSpecs, 0}
+
+/* All widgets should inherit from ttkCoreOptionSpecs[].
+ */
+MODULE_SCOPE Tk_OptionSpec ttkCoreOptionSpecs[];
+
+/*
+ * Useful routines for use inside widget implementations:
+ */
+/* extern int WidgetDestroyed(WidgetCore *); */
+#define WidgetDestroyed(corePtr) ((corePtr)->flags & WIDGET_DESTROYED)
+
+MODULE_SCOPE void TtkWidgetChangeState(WidgetCore *,
+ unsigned int setBits, unsigned int clearBits);
+
+MODULE_SCOPE void TtkRedisplayWidget(WidgetCore *);
+MODULE_SCOPE void TtkResizeWidget(WidgetCore *);
+
+MODULE_SCOPE void TtkTrackElementState(WidgetCore *);
+MODULE_SCOPE void TtkBlinkCursor(WidgetCore *);
+
+/*
+ * -state option values (compatibility)
+ */
+MODULE_SCOPE void TtkCheckStateOption(WidgetCore *, Tcl_Obj *);
+
+/*
+ * Variable traces:
+ */
+typedef void (*Ttk_TraceProc)(void *recordPtr, const char *value);
+typedef struct TtkTraceHandle_ Ttk_TraceHandle;
+
+MODULE_SCOPE Ttk_TraceHandle *Ttk_TraceVariable(
+ Tcl_Interp*, Tcl_Obj *varnameObj, Ttk_TraceProc callback, void *clientData);
+MODULE_SCOPE void Ttk_UntraceVariable(Ttk_TraceHandle *);
+MODULE_SCOPE int Ttk_FireTrace(Ttk_TraceHandle *);
+
+/*
+ * Virtual events:
+ */
+MODULE_SCOPE void TtkSendVirtualEvent(Tk_Window tgtWin, const char *eventName);
+
+/*
+ * Helper routines for data accessor commands:
+ */
+MODULE_SCOPE int TtkEnumerateOptions(
+ Tcl_Interp *, void *, const Tk_OptionSpec *, Tk_OptionTable, Tk_Window);
+MODULE_SCOPE int TtkGetOptionValue(
+ Tcl_Interp *, void *, Tcl_Obj *optName, Tk_OptionTable, Tk_Window);
+
+/*
+ * Helper routines for scrolling widgets (see scroll.c).
+ */
+typedef struct {
+ int first; /* First visible item */
+ int last; /* Last visible item */
+ int total; /* Total #items */
+ char *scrollCmd; /* Widget option */
+} Scrollable;
+
+typedef struct ScrollHandleRec *ScrollHandle;
+
+MODULE_SCOPE ScrollHandle TtkCreateScrollHandle(WidgetCore *, Scrollable *);
+MODULE_SCOPE void TtkFreeScrollHandle(ScrollHandle);
+
+MODULE_SCOPE int TtkScrollviewCommand(
+ Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], ScrollHandle);
+
+MODULE_SCOPE void TtkScrollTo(ScrollHandle, int newFirst);
+MODULE_SCOPE void TtkScrolled(ScrollHandle, int first, int last, int total);
+MODULE_SCOPE void TtkScrollbarUpdateRequired(ScrollHandle);
+
+/*
+ * Tag sets (work in progress, half-baked)
+ */
+
+typedef struct TtkTag *Ttk_Tag;
+typedef struct TtkTagTable *Ttk_TagTable;
+typedef struct TtkTagSet { /* TODO: make opaque */
+ Ttk_Tag *tags;
+ int nTags;
+} *Ttk_TagSet;
+
+MODULE_SCOPE Ttk_TagTable Ttk_CreateTagTable(
+ Tcl_Interp *, Tk_Window tkwin, Tk_OptionSpec[], int recordSize);
+MODULE_SCOPE void Ttk_DeleteTagTable(Ttk_TagTable);
+
+MODULE_SCOPE Ttk_Tag Ttk_GetTag(Ttk_TagTable, const char *tagName);
+MODULE_SCOPE Ttk_Tag Ttk_GetTagFromObj(Ttk_TagTable, Tcl_Obj *);
+
+MODULE_SCOPE Tcl_Obj *Ttk_TagOptionValue(
+ Tcl_Interp *, Ttk_TagTable, Ttk_Tag, Tcl_Obj *optionName);
+
+MODULE_SCOPE int Ttk_EnumerateTagOptions(
+ Tcl_Interp *, Ttk_TagTable, Ttk_Tag);
+
+MODULE_SCOPE int Ttk_EnumerateTags(Tcl_Interp *, Ttk_TagTable);
+
+MODULE_SCOPE int Ttk_ConfigureTag(
+ Tcl_Interp *interp, Ttk_TagTable tagTable, Ttk_Tag tag,
+ int objc, Tcl_Obj *const objv[]);
+
+MODULE_SCOPE Ttk_TagSet Ttk_GetTagSetFromObj(
+ Tcl_Interp *interp, Ttk_TagTable, Tcl_Obj *objPtr);
+MODULE_SCOPE Tcl_Obj *Ttk_NewTagSetObj(Ttk_TagSet);
+
+MODULE_SCOPE void Ttk_FreeTagSet(Ttk_TagSet);
+
+MODULE_SCOPE int Ttk_TagSetContains(Ttk_TagSet, Ttk_Tag tag);
+MODULE_SCOPE int Ttk_TagSetAdd(Ttk_TagSet, Ttk_Tag tag);
+MODULE_SCOPE int Ttk_TagSetRemove(Ttk_TagSet, Ttk_Tag tag);
+
+MODULE_SCOPE void Ttk_TagSetValues(Ttk_TagTable, Ttk_TagSet, void *record);
+MODULE_SCOPE void Ttk_TagSetApplyStyle(Ttk_TagTable,Ttk_Style,Ttk_State,void*);
+
+/*
+ * String tables for widget resource specifications:
+ */
+
+MODULE_SCOPE const char *ttkOrientStrings[];
+MODULE_SCOPE const char *ttkCompoundStrings[];
+MODULE_SCOPE const char *ttkDefaultStrings[];
+
+/*
+ * ... other option types...
+ */
+MODULE_SCOPE int TtkGetLabelAnchorFromObj(
+ Tcl_Interp*, Tcl_Obj*, Ttk_PositionSpec *);
+
+/*
+ * Platform-specific initialization.
+ */
+
+#if defined(__WIN32__)
+#define Ttk_PlatformInit Ttk_WinPlatformInit
+MODULE_SCOPE int Ttk_PlatformInit(Tcl_Interp *);
+#elif defined(MAC_OSX_TK)
+#define Ttk_PlatformInit Ttk_MacOSXPlatformInit
+MODULE_SCOPE int Ttk_PlatformInit(Tcl_Interp *);
+#else
+#define Ttk_PlatformInit(interp) /* TTK_X11PlatformInit() */
+#endif
+
+#endif /* _TTKWIDGET */