diff options
Diffstat (limited to 'tk/generic/tkListbox.c')
-rw-r--r-- | tk/generic/tkListbox.c | 726 |
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; } - - |