summaryrefslogtreecommitdiffstats
path: root/generic
diff options
context:
space:
mode:
authorsbron <sbron@tclcode.com>2023-03-16 23:08:12 (GMT)
committersbron <sbron@tclcode.com>2023-03-16 23:08:12 (GMT)
commit54ada25aed7a1c8425bc546d8c2e5376a6656190 (patch)
treea4b1ab1d24b1224365fa80e6967961a0d1ce483b /generic
parent3354d237c04d40a2472d1f32d510f253935d461a (diff)
downloadtk-54ada25aed7a1c8425bc546d8c2e5376a6656190.zip
tk-54ada25aed7a1c8425bc546d8c2e5376a6656190.tar.gz
tk-54ada25aed7a1c8425bc546d8c2e5376a6656190.tar.bz2
Implement TIP 658
Diffstat (limited to 'generic')
-rw-r--r--generic/tkMenu.c84
-rw-r--r--generic/tkMenu.h3
2 files changed, 83 insertions, 4 deletions
diff --git a/generic/tkMenu.c b/generic/tkMenu.c
index c5d2bf5..69ef6da 100644
--- a/generic/tkMenu.c
+++ b/generic/tkMenu.c
@@ -306,12 +306,12 @@ static const Tk_OptionSpec tkMenuConfigSpecs[] = {
static const char *const menuOptions[] = {
"activate", "add", "cget", "clone", "configure", "delete", "entrycget",
- "entryconfigure", "index", "insert", "invoke", "post", "postcascade",
+ "entryconfigure", "id", "index", "insert", "invoke", "post", "postcascade",
"type", "unpost", "xposition", "yposition", NULL
};
enum options {
MENU_ACTIVATE, MENU_ADD, MENU_CGET, MENU_CLONE, MENU_CONFIGURE,
- MENU_DELETE, MENU_ENTRYCGET, MENU_ENTRYCONFIGURE, MENU_INDEX,
+ MENU_DELETE, MENU_ENTRYCGET, MENU_ENTRYCONFIGURE, MENU_ID, MENU_INDEX,
MENU_INSERT, MENU_INVOKE, MENU_POST, MENU_POSTCASCADE, MENU_TYPE,
MENU_UNPOST, MENU_XPOSITION, MENU_YPOSITION
};
@@ -451,6 +451,8 @@ Tk_MenuObjCmd(
menuPtr->cursorPtr = NULL;
menuPtr->mainMenuPtr = menuPtr;
menuPtr->menuType = UNKNOWN_TYPE;
+ Tcl_InitHashTable(&menuPtr->items, TCL_STRING_KEYS);
+ menuPtr->serial = 0;
TkMenuInitializeDrawingFields(menuPtr);
Tk_SetClass(menuPtr->tkwin, "Menu");
@@ -821,6 +823,28 @@ MenuWidgetObjCmd(
Tcl_Release(mePtr);
break;
}
+ case MENU_ID: {
+ Tcl_Size index;
+ const char *idStr;
+ Tcl_HashEntry *entryPtr;
+
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "index");
+ goto error;
+ }
+ if (GetMenuIndex(interp, menuPtr, objv[2], 0, &index) != TCL_OK) {
+ goto error;
+ }
+ if (index == TCL_INDEX_NONE) {
+ goto done;
+ }
+ entryPtr = menuPtr->entries[index]->entryPtr;
+ if (entryPtr) {
+ idStr = Tcl_GetHashKey(&menuPtr->items, entryPtr);
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(idStr, TCL_INDEX_NONE));
+ }
+ break;
+ }
case MENU_INDEX: {
Tcl_Size index;
@@ -1189,6 +1213,7 @@ DestroyMenuInstance(
ckfree(menuPtr->entries);
menuPtr->entries = NULL;
}
+ Tcl_DeleteHashTable(&menuPtr->items);
TkMenuFreeDrawOptions(menuPtr);
Tk_FreeConfigOptions((char *) menuPtr,
tsdPtr->menuOptionTable, menuPtr->tkwin);
@@ -1455,6 +1480,10 @@ DestroyMenuEntry(
TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
MenuVarProc, mePtr);
}
+ if (mePtr->entryPtr) {
+ Tcl_DeleteHashEntry(mePtr->entryPtr);
+ mePtr->entryPtr = NULL;
+ }
TkpDestroyMenuEntry(mePtr);
TkMenuEntryFreeDrawOptions(mePtr);
Tk_FreeConfigOptions((char *) mePtr, mePtr->optionTable, menuPtr->tkwin);
@@ -2112,6 +2141,7 @@ GetMenuIndex(
{
int i;
const char *string;
+ Tcl_HashEntry *entryPtr;
if (TkGetIntForIndex(objPtr, menuPtr->numEntries - 1, lastOK, indexPtr) == TCL_OK) {
/* TCL_INDEX_NONE is only accepted if it does not result from a negative number */
@@ -2153,6 +2183,13 @@ GetMenuIndex(
}
}
+ entryPtr = Tcl_FindHashEntry(&menuPtr->items, string);
+ if (entryPtr) {
+ TkMenuEntry *mePtr = Tcl_GetHashValue(entryPtr);
+ *indexPtr = mePtr->index;
+ return TCL_OK;
+ }
+
for (i = 0; i < (int)menuPtr->numEntries; i++) {
Tcl_Obj *labelPtr = menuPtr->entries[i]->labelPtr;
const char *label = (labelPtr == NULL) ? NULL : Tcl_GetString(labelPtr);
@@ -2301,6 +2338,7 @@ MenuNewEntry(
ckfree(mePtr);
return NULL;
}
+ mePtr->entryPtr = NULL;
TkMenuInitializeEntryDrawingFields(mePtr);
if (TkpMenuNewEntry(mePtr) != TCL_OK) {
Tk_FreeConfigOptions((char *) mePtr, mePtr->optionTable,
@@ -2343,6 +2381,10 @@ MenuAddOrInsert(
Tcl_Size index;
TkMenuEntry *mePtr;
TkMenu *menuListPtr;
+ Tcl_HashEntry *entryPtr;
+ Tcl_Obj *idPtr = NULL;
+ int isNew;
+ int offs;
if (indexPtr != NULL) {
if (GetMenuIndex(interp, menuPtr, indexPtr, 1, &index) != TCL_OK) {
@@ -2369,11 +2411,26 @@ MenuAddOrInsert(
sizeof(char *), "menu entry type", 0, &type) != TCL_OK) {
return TCL_ERROR;
}
+ offs = 1;
/*
- * Now we have to add an entry for every instance related to this menu.
+ * Check for a user supplied id
*/
+ if (objc % 2 == 0) {
+ idPtr = objv[offs];
+ if (Tcl_FindHashEntry(&menuPtr->items, Tcl_GetString(idPtr))) {
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "Entry %s already exists", Tcl_GetString(idPtr)));
+ Tcl_SetErrorCode(interp, "TK", "MENU", "ENTRY_EXISTS", NULL);
+ return TCL_ERROR;
+ }
+ offs++;
+ }
+
+ /*
+ * Now we have to add an entry for every instance related to this menu.
+ */
for (menuListPtr = menuPtr->mainMenuPtr; menuListPtr != NULL;
menuListPtr = menuListPtr->nextInstancePtr) {
@@ -2381,7 +2438,7 @@ MenuAddOrInsert(
if (mePtr == NULL) {
return TCL_ERROR;
}
- if (ConfigureMenuEntry(mePtr, objc - 1, objv + 1) != TCL_OK) {
+ if (ConfigureMenuEntry(mePtr, objc - offs, objv + offs) != TCL_OK) {
TkMenu *errorMenuPtr;
Tcl_Size i;
@@ -2405,6 +2462,23 @@ MenuAddOrInsert(
return TCL_ERROR;
}
+ if (idPtr == NULL) {
+ char idbuf[16];
+ /* Generate an id for the new entry on the main menu */
+ do {
+ snprintf(idbuf, sizeof(idbuf), "e%03X", ++menuPtr->serial);
+ entryPtr =
+ Tcl_CreateHashEntry(&menuListPtr->items, idbuf, &isNew);
+ } while (!isNew);
+ idPtr = Tcl_NewStringObj(idbuf, TCL_INDEX_NONE);
+ } else {
+ /* Reuse the specified or previously generated id on all clones */
+ entryPtr = Tcl_CreateHashEntry(
+ &menuListPtr->items, Tcl_GetString(idPtr), &isNew);
+ }
+ Tcl_SetHashValue(entryPtr, mePtr);
+ mePtr->entryPtr = entryPtr;
+
/*
* If a menu has cascades, then every instance of the menu has to have
* its own parallel cascade structure. So adding an entry to a menu
@@ -2450,6 +2524,8 @@ MenuAddOrInsert(
}
}
}
+
+ Tcl_SetObjResult(interp, idPtr);
return TCL_OK;
}
diff --git a/generic/tkMenu.h b/generic/tkMenu.h
index 21ca097..f459277 100644
--- a/generic/tkMenu.h
+++ b/generic/tkMenu.h
@@ -183,6 +183,7 @@ typedef struct TkMenuEntry {
int index; /* Need to know which index we are. This is
* zero-based. This is the top-left entry of
* the menu. */
+ Tcl_HashEntry *entryPtr; /* Back-pointer to hash table entry */
/*
* Bookeeping for main menus and cascade menus.
@@ -379,6 +380,8 @@ typedef struct TkMenu {
* multiple menus get changed during one
* ConfigureMenu call. */
Tcl_Obj *activeReliefPtr; /* 3-d effect for active element. */
+ Tcl_HashTable items; /* Map: id -> entry */
+ int serial; /* Next item # for autogenerated ids */
} TkMenu;
/*