summaryrefslogtreecommitdiff
path: root/tk/generic/tkListbox.c
diff options
context:
space:
mode:
Diffstat (limited to 'tk/generic/tkListbox.c')
-rw-r--r--tk/generic/tkListbox.c726
1 files changed, 447 insertions, 279 deletions
diff --git a/tk/generic/tkListbox.c b/tk/generic/tkListbox.c
index f6b0f75f6ca..7cfc23bed7f 100644
--- a/tk/generic/tkListbox.c
+++ b/tk/generic/tkListbox.c
@@ -18,6 +18,10 @@
#include "default.h"
#include "tkInt.h"
+#ifdef WIN32
+#include "tkWinInt.h"
+#endif
+
typedef struct {
Tk_OptionTable listboxOptionTable; /* Table defining configuration options
* available for the listbox */
@@ -74,6 +78,7 @@ typedef struct {
* room for borders. */
Tk_Font tkfont; /* Information about text font, or NULL. */
XColor *fgColorPtr; /* Text color in normal mode. */
+ XColor *dfgColorPtr; /* Text color in disabled mode. */
GC textGC; /* For drawing normal text. */
Tk_3DBorder selBorder; /* Borders and backgrounds for selected
* elements. */
@@ -127,6 +132,8 @@ typedef struct {
int active; /* Index of "active" element (the one that
* has been selected by keyboard traversal).
* -1 means none. */
+ int activeStyle; /* style in which to draw the active element.
+ * One of: underline, none, dotbox */
/*
* Information for scanning:
@@ -155,6 +162,8 @@ typedef struct {
char *xScrollCmd; /* Command prefix for communicating with
* horizontal scrollbar. NULL means no command
* to issue. Malloc'ed. */
+ int state; /* Listbox state. */
+ Pixmap gray; /* Pixmap for displaying disabled text. */
int flags; /* Various flag bits: see below for
* definitions. */
} Listbox;
@@ -194,10 +203,35 @@ typedef struct {
#define LISTBOX_DELETED 32
/*
+ * The following enum is used to define a type for the -state option
+ * of the Entry widget. These values are used as indices into the
+ * string table below.
+ */
+
+enum state {
+ STATE_DISABLED, STATE_NORMAL
+};
+
+static char *stateStrings[] = {
+ "disabled", "normal", (char *) NULL
+};
+
+enum activeStyle {
+ ACTIVE_STYLE_DOTBOX, ACTIVE_STYLE_NONE, ACTIVE_STYLE_UNDERLINE
+};
+
+static char *activeStyleStrings[] = {
+ "dotbox", "none", "underline", (char *) NULL
+};
+
+/*
* The optionSpecs table defines the valid configuration options for the
* listbox widget
*/
static Tk_OptionSpec optionSpecs[] = {
+ {TK_OPTION_STRING_TABLE, "-activestyle", "activeStyle", "ActiveStyle",
+ DEF_LISTBOX_ACTIVE_STYLE, -1, Tk_Offset(Listbox, activeStyle),
+ 0, (ClientData) activeStyleStrings, 0},
{TK_OPTION_BORDER, "-background", "background", "Background",
DEF_LISTBOX_BG_COLOR, -1, Tk_Offset(Listbox, normalBorder),
0, (ClientData) DEF_LISTBOX_BG_MONO, 0},
@@ -211,6 +245,9 @@ static Tk_OptionSpec optionSpecs[] = {
{TK_OPTION_CURSOR, "-cursor", "cursor", "Cursor",
DEF_LISTBOX_CURSOR, -1, Tk_Offset(Listbox, cursor),
TK_OPTION_NULL_OK, 0, 0},
+ {TK_OPTION_COLOR, "-disabledforeground", "disabledForeground",
+ "DisabledForeground", DEF_LISTBOX_DISABLED_FG, -1,
+ Tk_Offset(Listbox, dfgColorPtr), TK_OPTION_NULL_OK, 0, 0},
{TK_OPTION_BOOLEAN, "-exportselection", "exportSelection",
"ExportSelection", DEF_LISTBOX_EXPORT_SELECTION, -1,
Tk_Offset(Listbox, exportSelection), 0, 0, 0},
@@ -247,6 +284,9 @@ static Tk_OptionSpec optionSpecs[] = {
TK_OPTION_NULL_OK, 0, 0},
{TK_OPTION_BOOLEAN, "-setgrid", "setGrid", "SetGrid",
DEF_LISTBOX_SET_GRID, -1, Tk_Offset(Listbox, setGrid), 0, 0, 0},
+ {TK_OPTION_STRING_TABLE, "-state", "state", "State",
+ DEF_LISTBOX_STATE, -1, Tk_Offset(Listbox, state),
+ 0, (ClientData) stateStrings, 0},
{TK_OPTION_STRING, "-takefocus", "takeFocus", "TakeFocus",
DEF_LISTBOX_TAKE_FOCUS, -1, Tk_Offset(Listbox, takeFocus),
TK_OPTION_NULL_OK, 0, 0},
@@ -271,26 +311,26 @@ static Tk_OptionSpec optionSpecs[] = {
*/
static Tk_OptionSpec itemAttrOptionSpecs[] = {
{TK_OPTION_BORDER, "-background", "background", "Background",
- (char *)NULL, -1, Tk_Offset(ItemAttr, border),
- TK_OPTION_NULL_OK|TK_OPTION_DONT_SET_DEFAULT,
+ (char *)NULL, -1, Tk_Offset(ItemAttr, border),
+ TK_OPTION_NULL_OK|TK_OPTION_DONT_SET_DEFAULT,
(ClientData) DEF_LISTBOX_BG_MONO, 0},
{TK_OPTION_SYNONYM, "-bg", (char *) NULL, (char *) NULL,
- (char *) NULL, 0, -1, 0, (ClientData) "-background", 0},
+ (char *) NULL, 0, -1, 0, (ClientData) "-background", 0},
{TK_OPTION_SYNONYM, "-fg", "foreground", (char *) NULL,
- (char *) NULL, 0, -1, 0, (ClientData) "-foreground", 0},
+ (char *) NULL, 0, -1, 0, (ClientData) "-foreground", 0},
{TK_OPTION_COLOR, "-foreground", "foreground", "Foreground",
- (char *) NULL, -1, Tk_Offset(ItemAttr, fgColor),
- TK_OPTION_NULL_OK|TK_OPTION_DONT_SET_DEFAULT, 0, 0},
+ (char *) NULL, -1, Tk_Offset(ItemAttr, fgColor),
+ TK_OPTION_NULL_OK|TK_OPTION_DONT_SET_DEFAULT, 0, 0},
{TK_OPTION_BORDER, "-selectbackground", "selectBackground", "Foreground",
- (char *) NULL, -1, Tk_Offset(ItemAttr, selBorder),
- TK_OPTION_NULL_OK|TK_OPTION_DONT_SET_DEFAULT,
+ (char *) NULL, -1, Tk_Offset(ItemAttr, selBorder),
+ TK_OPTION_NULL_OK|TK_OPTION_DONT_SET_DEFAULT,
(ClientData) DEF_LISTBOX_SELECT_MONO, 0},
{TK_OPTION_COLOR, "-selectforeground", "selectForeground", "Background",
- (char *) NULL, -1, Tk_Offset(ItemAttr, selFgColor),
- TK_OPTION_NULL_OK|TK_OPTION_DONT_SET_DEFAULT,
+ (char *) NULL, -1, Tk_Offset(ItemAttr, selFgColor),
+ TK_OPTION_NULL_OK|TK_OPTION_DONT_SET_DEFAULT,
(ClientData) DEF_LISTBOX_SELECT_FG_MONO, 0},
{TK_OPTION_END, (char *) NULL, (char *) NULL, (char *) NULL,
- (char *) NULL, 0, -1, 0, 0, 0}
+ (char *) NULL, 0, -1, 0, 0, 0}
};
/*
@@ -298,22 +338,22 @@ static Tk_OptionSpec itemAttrOptionSpecs[] = {
* commands) and map the indexes into the string tables into
* enumerated types used to dispatch the listbox widget command.
*/
-static char *commandNames[] = {
+static CONST char *commandNames[] = {
"activate", "bbox", "cget", "configure", "curselection", "delete", "get",
- "index", "insert", "itemcget", "itemconfigure", "nearest", "scan",
- "see", "selection", "size", "xview", "yview",
+ "index", "insert", "itemcget", "itemconfigure", "nearest", "scan",
+ "see", "selection", "size", "xview", "yview",
(char *) NULL
};
enum command {
COMMAND_ACTIVATE, COMMAND_BBOX, COMMAND_CGET, COMMAND_CONFIGURE,
- COMMAND_CURSELECTION, COMMAND_DELETE, COMMAND_GET, COMMAND_INDEX,
- COMMAND_INSERT, COMMAND_ITEMCGET, COMMAND_ITEMCONFIGURE,
- COMMAND_NEAREST, COMMAND_SCAN, COMMAND_SEE, COMMAND_SELECTION,
- COMMAND_SIZE, COMMAND_XVIEW, COMMAND_YVIEW
+ COMMAND_CURSELECTION, COMMAND_DELETE, COMMAND_GET, COMMAND_INDEX,
+ COMMAND_INSERT, COMMAND_ITEMCGET, COMMAND_ITEMCONFIGURE,
+ COMMAND_NEAREST, COMMAND_SCAN, COMMAND_SEE, COMMAND_SELECTION,
+ COMMAND_SIZE, COMMAND_XVIEW, COMMAND_YVIEW
};
-static char *selCommandNames[] = {
+static CONST char *selCommandNames[] = {
"anchor", "clear", "includes", "set", (char *) NULL
};
@@ -321,7 +361,7 @@ enum selcommand {
SELECTION_ANCHOR, SELECTION_CLEAR, SELECTION_INCLUDES, SELECTION_SET
};
-static char *scanCommandNames[] = {
+static CONST char *scanCommandNames[] = {
"mark", "dragto", (char *) NULL
};
@@ -329,7 +369,7 @@ enum scancommand {
SCAN_MARK, SCAN_DRAGTO
};
-static char *indexNames[] = {
+static CONST char *indexNames[] = {
"active", "anchor", "end", (char *)NULL
};
@@ -402,8 +442,8 @@ static void ListboxWorldChanged _ANSI_ARGS_((
static int NearestListboxElement _ANSI_ARGS_((Listbox *listPtr,
int y));
static char * ListboxListVarProc _ANSI_ARGS_ ((ClientData clientData,
- Tcl_Interp *interp, char *name1, char *name2,
- int flags));
+ Tcl_Interp *interp, CONST char *name1,
+ CONST char *name2, int flags));
static void MigrateHashEntries _ANSI_ARGS_ ((Tcl_HashTable *table,
int first, int last, int offset));
/*
@@ -411,10 +451,9 @@ static void MigrateHashEntries _ANSI_ARGS_ ((Tcl_HashTable *table,
* that can be invoked from generic window code.
*/
-static TkClassProcs listboxClass = {
- NULL, /* createProc. */
- ListboxWorldChanged, /* geometryProc. */
- NULL /* modalProc. */
+static Tk_ClassProcs listboxClass = {
+ sizeof(Tk_ClassProcs), /* size */
+ ListboxWorldChanged, /* worldChangedProc */
};
@@ -438,7 +477,7 @@ static TkClassProcs listboxClass = {
int
Tk_ListboxObjCmd(clientData, interp, objc, objv)
- ClientData clientData; /* Either NULL or pointer to option table */
+ ClientData clientData; /* NULL. */
Tcl_Interp *interp; /* Current interpreter. */
int objc; /* Number of arguments. */
Tcl_Obj *CONST objv[]; /* Argument objects. */
@@ -447,18 +486,28 @@ Tk_ListboxObjCmd(clientData, interp, objc, objv)
Tk_Window tkwin;
ListboxOptionTables *optionTables;
- optionTables = (ListboxOptionTables *)clientData;
- if (optionTables == NULL) {
- Tcl_CmdInfo info;
- char *name;
+ if (objc < 2) {
+ Tcl_WrongNumArgs(interp, 1, objv, "pathName ?options?");
+ return TCL_ERROR;
+ }
+
+ tkwin = Tk_CreateWindowFromPath(interp, Tk_MainWindow(interp),
+ Tcl_GetString(objv[1]), (char *) NULL);
+ if (tkwin == NULL) {
+ return TCL_ERROR;
+ }
+ optionTables = (ListboxOptionTables *)
+ Tcl_GetAssocData(interp, "ListboxOptionTables", NULL);
+ if (optionTables == NULL) {
/*
* We haven't created the option tables for this widget class yet.
* Do it now and save the a pointer to them as the ClientData for
* the command, so future invocations will have access to it.
*/
- optionTables =
- (ListboxOptionTables *) ckalloc(sizeof(ListboxOptionTables));
+
+ optionTables = (ListboxOptionTables *)
+ ckalloc(sizeof(ListboxOptionTables));
/* Set up an exit handler to free the optionTables struct */
Tcl_SetAssocData(interp, "ListboxOptionTables",
DestroyListboxOptionTables, (ClientData) optionTables);
@@ -468,23 +517,6 @@ Tk_ListboxObjCmd(clientData, interp, objc, objv)
Tk_CreateOptionTable(interp, optionSpecs);
optionTables->itemAttrOptionTable =
Tk_CreateOptionTable(interp, itemAttrOptionSpecs);
-
- /* Store a pointer to the tables as the ClientData for the command */
- name = Tcl_GetString(objv[0]);
- Tcl_GetCommandInfo(interp, name, &info);
- info.objClientData = (ClientData) optionTables;
- Tcl_SetCommandInfo(interp, name, &info);
- }
-
- if (objc < 2) {
- Tcl_WrongNumArgs(interp, 1, objv, "pathName ?options?");
- return TCL_ERROR;
- }
-
- tkwin = Tk_CreateWindowFromPath(interp, Tk_MainWindow(interp),
- Tcl_GetString(objv[1]), (char *) NULL);
- if (tkwin == NULL) {
- return TCL_ERROR;
}
/*
@@ -493,6 +525,8 @@ Tk_ListboxObjCmd(clientData, interp, objc, objv)
* initialized already (e.g. resource pointers).
*/
listPtr = (Listbox *) ckalloc(sizeof(Listbox));
+ memset((void *) listPtr, 0, (sizeof(Listbox)));
+
listPtr->tkwin = tkwin;
listPtr->display = Tk_Display(tkwin);
listPtr->interp = interp;
@@ -501,56 +535,32 @@ Tk_ListboxObjCmd(clientData, interp, objc, objv)
(ClientData) listPtr, ListboxCmdDeletedProc);
listPtr->optionTable = optionTables->listboxOptionTable;
listPtr->itemAttrOptionTable = optionTables->itemAttrOptionTable;
- listPtr->listVarName = NULL;
- listPtr->listObj = NULL;
listPtr->selection =
(Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
Tcl_InitHashTable(listPtr->selection, TCL_ONE_WORD_KEYS);
listPtr->itemAttrTable =
(Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
Tcl_InitHashTable(listPtr->itemAttrTable, TCL_ONE_WORD_KEYS);
- listPtr->nElements = 0;
- listPtr->normalBorder = NULL;
- listPtr->borderWidth = 0;
listPtr->relief = TK_RELIEF_RAISED;
- listPtr->highlightWidth = 0;
- listPtr->highlightBgColorPtr = NULL;
- listPtr->highlightColorPtr = NULL;
- listPtr->inset = 0;
- listPtr->tkfont = NULL;
- listPtr->fgColorPtr = NULL;
listPtr->textGC = None;
- listPtr->selBorder = NULL;
- listPtr->selBorderWidth = 0;
listPtr->selFgColorPtr = None;
listPtr->selTextGC = None;
- listPtr->width = 0;
- listPtr->height = 0;
- listPtr->lineHeight = 0;
- listPtr->topIndex = 0;
listPtr->fullLines = 1;
- listPtr->partialLine = 0;
- listPtr->setGrid = 0;
- listPtr->maxWidth = 0;
listPtr->xScrollUnit = 1;
- listPtr->xOffset = 0;
- listPtr->selectMode = NULL;
- listPtr->numSelected = 0;
- listPtr->selectAnchor = 0;
listPtr->exportSelection = 1;
- listPtr->active = 0;
- listPtr->scanMarkX = 0;
- listPtr->scanMarkY = 0;
- listPtr->scanMarkXOffset = 0;
- listPtr->scanMarkYIndex = 0;
listPtr->cursor = None;
- listPtr->takeFocus = NULL;
- listPtr->xScrollCmd = NULL;
- listPtr->yScrollCmd = NULL;
- listPtr->flags = 0;
+ listPtr->state = STATE_NORMAL;
+ listPtr->gray = None;
+
+ /*
+ * Keep a hold of the associated tkwin until we destroy the listbox,
+ * otherwise Tk might free it while we still need it.
+ */
+
+ Tcl_Preserve((ClientData) listPtr->tkwin);
Tk_SetClass(listPtr->tkwin, "Listbox");
- TkSetClassProcs(listPtr->tkwin, &listboxClass, (ClientData) listPtr);
+ Tk_SetClassProcs(listPtr->tkwin, &listboxClass, (ClientData) listPtr);
Tk_CreateEventHandler(listPtr->tkwin,
ExposureMask|StructureNotifyMask|FocusChangeMask,
ListboxEventProc, (ClientData) listPtr);
@@ -604,7 +614,6 @@ ListboxWidgetObjCmd(clientData, interp, objc, objv)
Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg ...?");
return TCL_ERROR;
}
- Tcl_Preserve((ClientData)listPtr);
/*
* Parse the command by looking up the second argument in the list
@@ -613,10 +622,10 @@ ListboxWidgetObjCmd(clientData, interp, objc, objv)
result = Tcl_GetIndexFromObj(interp, objv[1], commandNames,
"option", 0, &cmdIndex);
if (result != TCL_OK) {
- Tcl_Release((ClientData)listPtr);
return result;
}
+ Tcl_Preserve((ClientData)listPtr);
/* The subcommand was valid, so continue processing */
switch (cmdIndex) {
case COMMAND_ACTIVATE: {
@@ -629,6 +638,11 @@ ListboxWidgetObjCmd(clientData, interp, objc, objv)
if (result != TCL_OK) {
break;
}
+
+ if (!(listPtr->state & STATE_NORMAL)) {
+ break;
+ }
+
if (index >= listPtr->nElements) {
index = listPtr->nElements-1;
}
@@ -733,6 +747,11 @@ ListboxWidgetObjCmd(clientData, interp, objc, objv)
if (result != TCL_OK) {
break;
}
+
+ if (!(listPtr->state & STATE_NORMAL)) {
+ break;
+ }
+
if (first < listPtr->nElements) {
/*
* if a "last index" was given, get it now; otherwise, use the
@@ -838,6 +857,11 @@ ListboxWidgetObjCmd(clientData, interp, objc, objv)
if (result != TCL_OK) {
break;
}
+
+ if (!(listPtr->state & STATE_NORMAL)) {
+ break;
+ }
+
result = ListboxInsertSubCmd(listPtr, index, objc-3, objv+3);
break;
}
@@ -1018,6 +1042,10 @@ ListboxWidgetObjCmd(clientData, interp, objc, objv)
}
case COMMAND_SELECTION: {
+ if (!(listPtr->state & STATE_NORMAL)) {
+ break;
+ }
+
result = ListboxSelectionSubCmd(interp, listPtr, objc, objv);
break;
}
@@ -1412,16 +1440,6 @@ DestroyListbox(memPtr)
Tcl_HashEntry *entry;
Tcl_HashSearch search;
- listPtr->flags |= LISTBOX_DELETED;
-
- Tcl_DeleteCommandFromToken(listPtr->interp, listPtr->widgetCmd);
- if (listPtr->setGrid) {
- Tk_UnsetGrid(listPtr->tkwin);
- }
- if (listPtr->flags & REDRAW_PENDING) {
- Tcl_CancelIdleCall(DisplayListbox, (ClientData) listPtr);
- }
-
/* If we have an internal list object, free it */
if (listPtr->listObj != NULL) {
Tcl_DecrRefCount(listPtr->listObj);
@@ -1458,8 +1476,13 @@ DestroyListbox(memPtr)
if (listPtr->selTextGC != None) {
Tk_FreeGC(listPtr->display, listPtr->selTextGC);
}
+ if (listPtr->gray != None) {
+ Tk_FreeBitmap(Tk_Display(listPtr->tkwin), listPtr->gray);
+ }
+
Tk_FreeConfigOptions((char *)listPtr, listPtr->optionTable,
listPtr->tkwin);
+ Tcl_Release((ClientData) listPtr->tkwin);
listPtr->tkwin = NULL;
ckfree((char *) listPtr);
}
@@ -1523,7 +1546,8 @@ ConfigureListbox(interp, listPtr, objc, objv, flags)
{
Tk_SavedOptions savedOptions;
Tcl_Obj *oldListObj = NULL;
- int oldExport;
+ Tcl_Obj *errorResult = NULL;
+ int oldExport, error;
oldExport = listPtr->exportSelection;
if (listPtr->listVarName != NULL) {
@@ -1531,99 +1555,118 @@ ConfigureListbox(interp, listPtr, objc, objv, flags)
TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
ListboxListVarProc, (ClientData) listPtr);
}
-
- if (Tk_SetOptions(interp, (char *)listPtr,
- listPtr->optionTable, objc, objv, listPtr->tkwin,
- &savedOptions, (int *)NULL) != TCL_OK) {
- Tk_RestoreSavedOptions(&savedOptions);
- return TCL_ERROR;
- }
-
- /*
- * A few options need special processing, such as setting the
- * background from a 3-D border.
- */
- Tk_SetBackgroundFromBorder(listPtr->tkwin, listPtr->normalBorder);
+ for (error = 0; error <= 1; error++) {
+ if (!error) {
+ /*
+ * First pass: set options to new values.
+ */
- if (listPtr->highlightWidth < 0) {
- listPtr->highlightWidth = 0;
- }
- listPtr->inset = listPtr->highlightWidth + listPtr->borderWidth;
+ if (Tk_SetOptions(interp, (char *) listPtr,
+ listPtr->optionTable, objc, objv,
+ listPtr->tkwin, &savedOptions, (int *) NULL) != TCL_OK) {
+ continue;
+ }
+ } else {
+ /*
+ * Second pass: restore options to old values.
+ */
- /*
- * Claim the selection if we've suddenly started exporting it and
- * there is a selection to export.
- */
+ errorResult = Tcl_GetObjResult(interp);
+ Tcl_IncrRefCount(errorResult);
+ Tk_RestoreSavedOptions(&savedOptions);
+ }
- if (listPtr->exportSelection && !oldExport
- && (listPtr->numSelected != 0)) {
- Tk_OwnSelection(listPtr->tkwin, XA_PRIMARY, ListboxLostSelection,
- (ClientData) listPtr);
- }
+ /*
+ * A few options need special processing, such as setting the
+ * background from a 3-D border.
+ */
-
- /* Verify the current status of the list var.
- * PREVIOUS STATE | NEW STATE | ACTION
- * ------------------+---------------+----------------------------------
- * no listvar | listvar | If listvar does not exist, create
- * it and copy the internal list obj's
- * content to the new var. If it does
- * exist, toss the internal list obj.
- *
- * listvar | no listvar | Copy old listvar content to the
- * internal list obj
- *
- * listvar | listvar | no special action
- *
- * no listvar | no listvar | no special action
- */
- oldListObj = listPtr->listObj;
- if (listPtr->listVarName != NULL) {
- Tcl_Obj *listVarObj = Tcl_GetVar2Ex(interp, listPtr->listVarName,
- (char *)NULL, TCL_GLOBAL_ONLY);
- int dummy;
- if (listVarObj == NULL) {
- if (listPtr->listObj != NULL) {
- listVarObj = listPtr->listObj;
- } else {
- listVarObj = Tcl_NewObj();
+ Tk_SetBackgroundFromBorder(listPtr->tkwin, listPtr->normalBorder);
+
+ if (listPtr->highlightWidth < 0) {
+ listPtr->highlightWidth = 0;
+ }
+ listPtr->inset = listPtr->highlightWidth + listPtr->borderWidth;
+
+ /*
+ * Claim the selection if we've suddenly started exporting it and
+ * there is a selection to export.
+ */
+
+ if (listPtr->exportSelection && !oldExport
+ && (listPtr->numSelected != 0)) {
+ Tk_OwnSelection(listPtr->tkwin, XA_PRIMARY, ListboxLostSelection,
+ (ClientData) listPtr);
+ }
+
+ /* Verify the current status of the list var.
+ * PREVIOUS STATE | NEW STATE | ACTION
+ * ---------------+------------+----------------------------------
+ * no listvar | listvar | If listvar does not exist, create
+ * it and copy the internal list obj's
+ * content to the new var. If it does
+ * exist, toss the internal list obj.
+ *
+ * listvar | no listvar | Copy old listvar content to the
+ * internal list obj
+ *
+ * listvar | listvar | no special action
+ *
+ * no listvar | no listvar | no special action
+ */
+ oldListObj = listPtr->listObj;
+ if (listPtr->listVarName != NULL) {
+ Tcl_Obj *listVarObj = Tcl_GetVar2Ex(interp, listPtr->listVarName,
+ (char *) NULL, TCL_GLOBAL_ONLY);
+ int dummy;
+ if (listVarObj == NULL) {
+ listVarObj = (oldListObj ? oldListObj : Tcl_NewObj());
+ if (Tcl_SetVar2Ex(interp, listPtr->listVarName, (char *) NULL,
+ listVarObj, TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG)
+ == NULL) {
+ if (oldListObj == NULL) {
+ Tcl_DecrRefCount(listVarObj);
+ }
+ continue;
+ }
}
- if (Tcl_SetVar2Ex(interp, listPtr->listVarName, (char *)NULL,
- listVarObj, TCL_GLOBAL_ONLY) == NULL) {
- Tcl_DecrRefCount(listVarObj);
- Tk_RestoreSavedOptions(&savedOptions);
- return TCL_ERROR;
+ /* Make sure the object is a good list object */
+ if (Tcl_ListObjLength(listPtr->interp, listVarObj, &dummy)
+ != TCL_OK) {
+ Tcl_AppendResult(listPtr->interp,
+ ": invalid -listvariable value", (char *) NULL);
+ continue;
}
- }
- /* Make sure the object is a good list object */
- if (Tcl_ListObjLength(listPtr->interp, listVarObj, &dummy) != TCL_OK) {
- Tk_RestoreSavedOptions(&savedOptions);
- Tcl_AppendResult(listPtr->interp, ": invalid listvar value",
- (char *)NULL);
- return TCL_ERROR;
- }
-
- listPtr->listObj = listVarObj;
- Tcl_TraceVar(listPtr->interp, listPtr->listVarName,
- TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
- ListboxListVarProc, (ClientData) listPtr);
- } else {
- if (listPtr->listObj == NULL) {
+
+ listPtr->listObj = listVarObj;
+ Tcl_TraceVar(listPtr->interp, listPtr->listVarName,
+ TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
+ ListboxListVarProc, (ClientData) listPtr);
+ } else if (listPtr->listObj == NULL) {
listPtr->listObj = Tcl_NewObj();
}
+ Tcl_IncrRefCount(listPtr->listObj);
+ if (oldListObj != NULL) {
+ Tcl_DecrRefCount(oldListObj);
+ }
+ break;
}
- Tcl_IncrRefCount(listPtr->listObj);
- if (oldListObj != NULL) {
- Tcl_DecrRefCount(oldListObj);
+ if (!error) {
+ Tk_FreeSavedOptions(&savedOptions);
}
/* Make sure that the list length is correct */
Tcl_ListObjLength(listPtr->interp, listPtr->listObj, &listPtr->nElements);
- Tk_FreeSavedOptions(&savedOptions);
- ListboxWorldChanged((ClientData) listPtr);
- return TCL_OK;
+ if (error) {
+ Tcl_SetObjResult(interp, errorResult);
+ Tcl_DecrRefCount(errorResult);
+ return TCL_ERROR;
+ } else {
+ ListboxWorldChanged((ClientData) listPtr);
+ return TCL_OK;
+ }
}
/*
@@ -1698,10 +1741,30 @@ ListboxWorldChanged(instanceData)
listPtr = (Listbox *) instanceData;
- gcValues.foreground = listPtr->fgColorPtr->pixel;
+ if (listPtr->state & STATE_NORMAL) {
+ gcValues.foreground = listPtr->fgColorPtr->pixel;
+ gcValues.graphics_exposures = False;
+ mask = GCForeground | GCFont | GCGraphicsExposures;
+ } else {
+ if (listPtr->dfgColorPtr != NULL) {
+ gcValues.foreground = listPtr->dfgColorPtr->pixel;
+ gcValues.graphics_exposures = False;
+ mask = GCForeground | GCFont | GCGraphicsExposures;
+ } else {
+ gcValues.foreground = listPtr->fgColorPtr->pixel;
+ mask = GCForeground | GCFont;
+ if (listPtr->gray == None) {
+ listPtr->gray = Tk_GetBitmap(NULL, listPtr->tkwin, "gray50");
+ }
+ if (listPtr->gray != None) {
+ gcValues.fill_style = FillStippled;
+ gcValues.stipple = listPtr->gray;
+ mask |= GCFillStyle | GCStipple;
+ }
+ }
+ }
+
gcValues.font = Tk_FontId(listPtr->tkfont);
- gcValues.graphics_exposures = False;
- mask = GCForeground | GCFont | GCGraphicsExposures;
gc = Tk_GetGC(listPtr->tkwin, mask, &gcValues);
if (listPtr->textGC != None) {
Tk_FreeGC(listPtr->display, listPtr->textGC);
@@ -1750,7 +1813,7 @@ DisplayListbox(clientData)
register Listbox *listPtr = (Listbox *) clientData;
register Tk_Window tkwin = listPtr->tkwin;
GC gc;
- int i, limit, x, y, width, prevSelected;
+ int i, limit, x, y, width, prevSelected, freeGC;
Tk_FontMetrics fm;
Tcl_Obj *curElement;
Tcl_HashEntry *entry;
@@ -1766,6 +1829,9 @@ DisplayListbox(clientData)
Pixmap pixmap;
listPtr->flags &= ~REDRAW_PENDING;
+ if (listPtr->flags & LISTBOX_DELETED) {
+ return;
+ }
if (listPtr->flags & MAXWIDTH_IS_STALE) {
ListboxComputeGeometry(listPtr, 0, 1, 0);
@@ -1773,16 +1839,23 @@ DisplayListbox(clientData)
listPtr->flags |= UPDATE_H_SCROLLBAR;
}
+ Tcl_Preserve((ClientData) listPtr);
if (listPtr->flags & UPDATE_V_SCROLLBAR) {
ListboxUpdateVScrollbar(listPtr);
+ if ((listPtr->flags & LISTBOX_DELETED) || !Tk_IsMapped(tkwin)) {
+ Tcl_Release((ClientData) listPtr);
+ return;
+ }
}
if (listPtr->flags & UPDATE_H_SCROLLBAR) {
ListboxUpdateHScrollbar(listPtr);
+ if ((listPtr->flags & LISTBOX_DELETED) || !Tk_IsMapped(tkwin)) {
+ Tcl_Release((ClientData) listPtr);
+ return;
+ }
}
listPtr->flags &= ~(REDRAW_PENDING|UPDATE_V_SCROLLBAR|UPDATE_H_SCROLLBAR);
- if ((listPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
- return;
- }
+ Tcl_Release((ClientData) listPtr);
/*
* Redrawing is done in a temporary pixmap that is allocated
@@ -1817,106 +1890,131 @@ DisplayListbox(clientData)
y = ((i - listPtr->topIndex) * listPtr->lineHeight)
+ listPtr->inset;
gc = listPtr->textGC;
+ freeGC = 0;
/*
* Lookup this item in the item attributes table, to see if it has
* special foreground/background colors
*/
entry = Tcl_FindHashEntry(listPtr->itemAttrTable, (char *)i);
- /* If the item is selected, it is drawn differently */
- if (Tcl_FindHashEntry(listPtr->selection, (char *)i) != NULL) {
- gc = listPtr->selTextGC;
- width = Tk_Width(tkwin) - 2*listPtr->inset;
- selectedBg = listPtr->selBorder;
-
- /* If there is attribute information for this item,
- * adjust the drawing accordingly */
- if (entry != NULL) {
- attrs = (ItemAttr *)Tcl_GetHashValue(entry);
- /* The default GC has the settings from the widget at large */
- gcValues.foreground = listPtr->selFgColorPtr->pixel;
- gcValues.font = Tk_FontId(listPtr->tkfont);
- gcValues.graphics_exposures = False;
- mask = GCForeground | GCFont | GCGraphicsExposures;
-
- if (attrs->selBorder != NULL) {
- selectedBg = attrs->selBorder;
- }
-
- if (attrs->selFgColor != NULL) {
- gcValues.foreground = attrs->selFgColor->pixel;
- gc = Tk_GetGC(listPtr->tkwin, mask, &gcValues);
+ /*
+ * If the listbox is enabled, items may be drawn differently;
+ * they may be drawn selected, or they may have special foreground
+ * or background colors.
+ */
+ if (listPtr->state & STATE_NORMAL) {
+ if (Tcl_FindHashEntry(listPtr->selection, (char *)i) != NULL) {
+ /* Selected items are drawn differently. */
+ gc = listPtr->selTextGC;
+ width = Tk_Width(tkwin) - 2*listPtr->inset;
+ selectedBg = listPtr->selBorder;
+
+ /* If there is attribute information for this item,
+ * adjust the drawing accordingly */
+ if (entry != NULL) {
+ attrs = (ItemAttr *)Tcl_GetHashValue(entry);
+ /* Default GC has the values from the widget at large */
+ gcValues.foreground = listPtr->selFgColorPtr->pixel;
+ gcValues.font = Tk_FontId(listPtr->tkfont);
+ gcValues.graphics_exposures = False;
+ mask = GCForeground | GCFont | GCGraphicsExposures;
+
+ if (attrs->selBorder != NULL) {
+ selectedBg = attrs->selBorder;
+ }
+
+ if (attrs->selFgColor != NULL) {
+ gcValues.foreground = attrs->selFgColor->pixel;
+ gc = Tk_GetGC(listPtr->tkwin, mask, &gcValues);
+ freeGC = 1;
+ }
}
- }
- Tk_Fill3DRectangle(tkwin, pixmap, selectedBg, x, y,
- width, listPtr->lineHeight, 0, TK_RELIEF_FLAT);
+ Tk_Fill3DRectangle(tkwin, pixmap, selectedBg, x, y,
+ width, listPtr->lineHeight, 0, TK_RELIEF_FLAT);
- /*
- * Draw beveled edges around the selection, if there are visible
- * edges next to this element. Special considerations:
- * 1. The left and right bevels may not be visible if horizontal
- * scrolling is enabled (the "left" and "right" variables
- * are zero to indicate that the corresponding bevel is
- * visible).
- * 2. Top and bottom bevels are only drawn if this is the
- * first or last seleted item.
- * 3. If the left or right bevel isn't visible, then the "left"
- * and "right" variables, computed above, have non-zero values
- * that extend the top and bottom bevels so that the mitered
- * corners are off-screen.
- */
+ /*
+ * Draw beveled edges around the selection, if there are
+ * visible edges next to this element. Special considerations:
+ *
+ * 1. The left and right bevels may not be visible if
+ * horizontal scrolling is enabled (the "left" & "right"
+ * variables are zero to indicate that the corresponding
+ * bevel is visible).
+ * 2. Top and bottom bevels are only drawn if this is the
+ * first or last seleted item.
+ * 3. If the left or right bevel isn't visible, then the
+ * "left" & "right" vars, computed above, have non-zero
+ * values that extend the top and bottom bevels so that
+ * the mitered corners are off-screen.
+ */
- /* Draw left bevel */
- if (left == 0) {
- Tk_3DVerticalBevel(tkwin, pixmap, selectedBg,
- x, y, listPtr->selBorderWidth, listPtr->lineHeight,
- 1, TK_RELIEF_RAISED);
- }
- /* Draw right bevel */
- if (right == 0) {
- Tk_3DVerticalBevel(tkwin, pixmap, selectedBg,
- x + width - listPtr->selBorderWidth, y,
- listPtr->selBorderWidth, listPtr->lineHeight,
- 0, TK_RELIEF_RAISED);
- }
- /* Draw top bevel */
- if (!prevSelected) {
- Tk_3DHorizontalBevel(tkwin, pixmap, selectedBg,
- x-left, y, width+left+right, listPtr->selBorderWidth,
- 1, 1, 1, TK_RELIEF_RAISED);
- }
- /* Draw bottom bevel */
- if (i + 1 == listPtr->nElements ||
- Tcl_FindHashEntry(listPtr->selection,
- (char *)(i + 1)) == NULL ) {
- Tk_3DHorizontalBevel(tkwin, pixmap, selectedBg, x-left,
- y + listPtr->lineHeight - listPtr->selBorderWidth,
- width+left+right, listPtr->selBorderWidth, 0, 0, 0,
- TK_RELIEF_RAISED);
- }
- prevSelected = 1;
- } else {
- /* If there is an item attributes record for this item,
- * draw the background box and set the foreground color
- * accordingly */
- if (entry != NULL) {
- attrs = (ItemAttr *)Tcl_GetHashValue(entry);
- gcValues.foreground = listPtr->fgColorPtr->pixel;
- gcValues.font = Tk_FontId(listPtr->tkfont);
- gcValues.graphics_exposures = False;
- mask = GCForeground | GCFont | GCGraphicsExposures;
- if (attrs->border != NULL) {
- width = Tk_Width(tkwin) - 2*listPtr->inset;
- Tk_Fill3DRectangle(tkwin, pixmap, attrs->border, x, y,
- width, listPtr->lineHeight, 0, TK_RELIEF_FLAT);
+ /* Draw left bevel */
+ if (left == 0) {
+ Tk_3DVerticalBevel(tkwin, pixmap, selectedBg,
+ x, y, listPtr->selBorderWidth, listPtr->lineHeight,
+ 1, TK_RELIEF_RAISED);
+ }
+ /* Draw right bevel */
+ if (right == 0) {
+ Tk_3DVerticalBevel(tkwin, pixmap, selectedBg,
+ x + width - listPtr->selBorderWidth, y,
+ listPtr->selBorderWidth, listPtr->lineHeight,
+ 0, TK_RELIEF_RAISED);
}
- if (attrs->fgColor != NULL) {
- gcValues.foreground = attrs->fgColor->pixel;
- gc = Tk_GetGC(listPtr->tkwin, mask, &gcValues);
+ /* Draw top bevel */
+ if (!prevSelected) {
+ Tk_3DHorizontalBevel(tkwin, pixmap, selectedBg,
+ x-left, y, width+left+right,
+ listPtr->selBorderWidth,
+ 1, 1, 1, TK_RELIEF_RAISED);
}
+ /* Draw bottom bevel */
+ if (i + 1 == listPtr->nElements ||
+ Tcl_FindHashEntry(listPtr->selection,
+ (char *)(i + 1)) == NULL ) {
+ Tk_3DHorizontalBevel(tkwin, pixmap, selectedBg, x-left,
+ y + listPtr->lineHeight - listPtr->selBorderWidth,
+ width+left+right, listPtr->selBorderWidth, 0, 0, 0,
+ TK_RELIEF_RAISED);
+ }
+ prevSelected = 1;
+ } else {
+ /*
+ * If there is an item attributes record for this item, draw
+ * the background box and set the foreground color accordingly
+ */
+ if (entry != NULL) {
+ attrs = (ItemAttr *)Tcl_GetHashValue(entry);
+ gcValues.foreground = listPtr->fgColorPtr->pixel;
+ gcValues.font = Tk_FontId(listPtr->tkfont);
+ gcValues.graphics_exposures = False;
+ mask = GCForeground | GCFont | GCGraphicsExposures;
+
+ /*
+ * If the item has its own background color, draw it now.
+ */
+
+ if (attrs->border != NULL) {
+ width = Tk_Width(tkwin) - 2*listPtr->inset;
+ Tk_Fill3DRectangle(tkwin, pixmap, attrs->border, x, y,
+ width, listPtr->lineHeight, 0, TK_RELIEF_FLAT);
+ }
+
+ /*
+ * If the item has its own foreground, use it to override
+ * the value in the gcValues structure.
+ */
+
+ if ((listPtr->state & STATE_NORMAL)
+ && attrs->fgColor != NULL) {
+ gcValues.foreground = attrs->fgColor->pixel;
+ gc = Tk_GetGC(listPtr->tkwin, mask, &gcValues);
+ freeGC = 1;
+ }
+ }
+ prevSelected = 0;
}
- prevSelected = 0;
}
/* Draw the actual text of this item */
@@ -1928,10 +2026,68 @@ DisplayListbox(clientData)
Tk_DrawChars(listPtr->display, pixmap, gc, listPtr->tkfont,
stringRep, stringLen, x, y);
- /* If this is the active element, underline it. */
+ /* If this is the active element, apply the activestyle to it. */
if ((i == listPtr->active) && (listPtr->flags & GOT_FOCUS)) {
- Tk_UnderlineChars(listPtr->display, pixmap, gc, listPtr->tkfont,
- stringRep, x, y, 0, stringLen);
+ if (listPtr->activeStyle == ACTIVE_STYLE_UNDERLINE) {
+ /* Underline the text. */
+ Tk_UnderlineChars(listPtr->display, pixmap, gc,
+ listPtr->tkfont, stringRep, x, y, 0, stringLen);
+ } else if (listPtr->activeStyle == ACTIVE_STYLE_DOTBOX) {
+#ifdef WIN32
+ /*
+ * This provides for exact default look and feel on Windows.
+ */
+ TkWinDCState state;
+ HDC dc;
+ RECT rect;
+
+ dc = TkWinGetDrawableDC(listPtr->display, pixmap, &state);
+ rect.left = listPtr->inset;
+ rect.top = ((i - listPtr->topIndex) * listPtr->lineHeight)
+ + listPtr->inset;
+ rect.right = rect.left + width;
+ rect.bottom = rect.top + listPtr->lineHeight;
+ DrawFocusRect(dc, &rect);
+ TkWinReleaseDrawableDC(pixmap, dc, &state);
+#else
+ /*
+ * Draw a dotted box around the text.
+ */
+ x = listPtr->inset;
+ y = ((i - listPtr->topIndex) * listPtr->lineHeight)
+ + listPtr->inset;
+ width = Tk_Width(tkwin) - 2*listPtr->inset - 1;
+
+ gcValues.line_style = LineOnOffDash;
+ gcValues.line_width = listPtr->selBorderWidth;
+ if (gcValues.line_width <= 0) {
+ gcValues.line_width = 1;
+ }
+ gcValues.dash_offset = 0;
+ gcValues.dashes = 1;
+ /*
+ * You would think the XSetDashes was necessary, but it
+ * appears that the default dotting for just saying we
+ * want dashes appears to work correctly.
+ static char dashList[] = { 1 };
+ static int dashLen = sizeof(dashList);
+ XSetDashes(listPtr->display, gc, 0, dashList, dashLen);
+ */
+ mask = GCLineWidth | GCLineStyle | GCDashList | GCDashOffset;
+ XChangeGC(listPtr->display, gc, mask, &gcValues);
+ XDrawRectangle(listPtr->display, pixmap, gc, x, y,
+ (unsigned) width, (unsigned) listPtr->lineHeight - 1);
+ if (!freeGC) {
+ /* Don't bother changing if it is about to be freed. */
+ gcValues.line_style = LineSolid;
+ XChangeGC(listPtr->display, gc, GCLineStyle, &gcValues);
+ }
+#endif
+ }
+ }
+
+ if (freeGC) {
+ Tk_FreeGC(listPtr->display, gc);
}
}
@@ -2365,7 +2521,17 @@ ListboxEventProc(clientData, eventPtr)
NearestListboxElement(listPtr, eventPtr->xexpose.y
+ eventPtr->xexpose.height));
} else if (eventPtr->type == DestroyNotify) {
- DestroyListbox((char *) clientData);
+ if (!(listPtr->flags & LISTBOX_DELETED)) {
+ listPtr->flags |= LISTBOX_DELETED;
+ Tcl_DeleteCommandFromToken(listPtr->interp, listPtr->widgetCmd);
+ if (listPtr->setGrid) {
+ Tk_UnsetGrid(listPtr->tkwin);
+ }
+ if (listPtr->flags & REDRAW_PENDING) {
+ Tcl_CancelIdleCall(DisplayListbox, clientData);
+ }
+ Tcl_EventuallyFree(clientData, DestroyListbox);
+ }
} else if (eventPtr->type == ConfigureNotify) {
int vertSpace;
@@ -2607,8 +2773,12 @@ ChangeListboxOffset(listPtr, offset)
/*
* Make sure that the new offset is within the allowable range, and
* round it off to an even multiple of xScrollUnit.
+ *
+ * Add half a scroll unit to do entry/text-like synchronization.
+ * [Bug #225025]
*/
+ offset += listPtr->xScrollUnit / 2;
maxOffset = listPtr->maxWidth - (Tk_Width(listPtr->tkwin) -
2*listPtr->inset - 2*listPtr->selBorderWidth)
+ listPtr->xScrollUnit - 1;
@@ -2979,7 +3149,7 @@ EventuallyRedrawRange(listPtr, first, last)
/* We don't have to register a redraw callback if one is already pending,
* or if the window doesn't exist, or if the window isn't mapped */
if ((listPtr->flags & REDRAW_PENDING)
- || (listPtr->tkwin == NULL)
+ || (listPtr->flags & LISTBOX_DELETED)
|| !Tk_IsMapped(listPtr->tkwin)) {
return;
}
@@ -3133,8 +3303,8 @@ static char *
ListboxListVarProc(clientData, interp, name1, name2, flags)
ClientData clientData; /* Information about button. */
Tcl_Interp *interp; /* Interpreter containing variable. */
- char *name1; /* Not used. */
- char *name2; /* Not used. */
+ CONST char *name1; /* Not used. */
+ CONST char *name2; /* Not used. */
int flags; /* Information about what happened. */
{
Listbox *listPtr = (Listbox *)clientData;
@@ -3281,5 +3451,3 @@ MigrateHashEntries(table, first, last, offset)
return;
}
-
-