MCPcopy Create free account
hub / github.com/AutoHotkey/AutoHotkey / GetFreeMenuItemID

Method GetFreeMenuItemID

source/script_menu.cpp:353–406  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

351
352
353UINT Script::GetFreeMenuItemID()
354// Returns an unused menu item ID, or 0 if all IDs are used.
355{
356 // Need to find a menuID that isn't already in use by one of the other menu items.
357 // But also need to conserve menu items since only a relatively small number of IDs is available.
358 // Can't simply use ID_USER_FIRST + mMenuItemCount because: 1) There might be more than one
359 // user defined menu; 2) a menu item in the middle of the list may have been deleted,
360 // in which case that value would already be in use by the last item.
361 // Update: Now using caching of last successfully found free-ID to greatly improve avg.
362 // performance, especially for menus that contain thousands of items and submenus, such as
363 // ones that are built to mirror an entire nested directory structure. Caching should
364 // improve performance even after all menu IDs within the available range have been
365 // allocated once (via adding and deleting menus + menu items) since large blocks of free IDs
366 // should be free, and on average, the caching will exploit these large free blocks. However,
367 // if large amounts of menus and menu items are continually deleted and re-added by a script,
368 // the pool of free IDs will become fragmented over time, which will reduce performance.
369 // Since that kind of script behavior seems very rare, no attempt is made to "defragment".
370 // If more performance is needed in the future (seems unlikely for 99.9999% of scripts),
371 // could maintain an field of ~64000 bits, each bit representing whether a menu item ID is
372 // free. Then, every time a menu or one or more of its IDs is deleted or added, the corresponding
373 // ID could be marked as free/taken. That would add quite a bit of complexity to the menu
374 // delete code, however, and it would reduce the overall maintainability. So it definitely
375 // doesn't seem worth it, especially since Windows XP seems to have trouble even displaying
376 // menus larger than around 15000-25000 items.
377 static UINT sLastFreeID = ID_USER_FIRST - 1;
378 // Increment by one for each new search, both due to the above line and because the
379 // last-found free ID has a high likelihood of still being in use:
380 ++sLastFreeID;
381 bool id_in_use;
382 // Note that the i variable is used to force the loop to complete exactly one full
383 // circuit through all available IDs, regardless of where the starting/cached value:
384 for (int i = 0; i < (ID_USER_LAST - ID_USER_FIRST + 1); ++i, ++sLastFreeID) // FOR EACH ID
385 {
386 if (sLastFreeID > ID_USER_LAST)
387 sLastFreeID = ID_USER_FIRST; // Wrap around to the beginning so that one complete circuit is made.
388 id_in_use = false; // Reset the default each iteration (overridden if the below finds a match).
389 for (UserMenu *m = mFirstMenu; m; m = m->mNextMenu) // FOR EACH MENU
390 {
391 for (UserMenuItem *mi = m->mFirstMenuItem; mi; mi = mi->mNextMenuItem) // FOR EACH MENU ITEM
392 {
393 if (mi->mMenuID == sLastFreeID)
394 {
395 id_in_use = true;
396 break;
397 }
398 }
399 if (id_in_use) // No point in searching the other menus, since it's now known to be in use.
400 break;
401 }
402 if (!id_in_use) // Break before the loop increments sLastFreeID.
403 break;
404 }
405 return id_in_use ? 0 : sLastFreeID;
406}
407
408
409

Callers 2

InvokeMethod · 0.80
ModifyItemMethod · 0.80

Calls

no outgoing calls

Tested by

no test coverage detected