Update a menu with a list of open windows
This lesson demonstrates how to show a list of open windows in a menu.
The mouseDown message
The mouseDown message is sent when the user clicks an object. If a menu was clicked, the message is sent before the menu is displayed, so if you make changes to the button in the mouseDown handler, the changes appear in the menu. Remember that in LiveCode, menus are implemented as buttons, and a menu bar is a group of menu buttons. When the mouseDown message is sent to the menu button, it triggers this handler.
Cross-platform note: When a menu button is being displayed in the Mac OS menu bar, it does not receive mouseDown messages, but its group does. For this reason, this example handler should be placed in the script of the menu bar group, rather than in the menu button. The first line of the handler makes sure it's only executed if the user clicked the Window menu. This ensures that the example will work on all platforms.
The mouseDown handler
This mouseDown handler builds a list of open stack windows whenever the user clicks the menu. Recreating the list every time the menu is clicked ensures that the menu is always current. Any menu that is constructed dynamically must be created in a mouseDown handler, since that is the only way to ensure that the button's contents are updated before the menu is displayed.
The openStacks function
The handler begins with the openStacks function, which returns a list of the short name of each stack whose window is open.
The handler could put this list into the menu just as it is. However, this would list the name property of the stacks, rather than the title property. The title is the text shown in a stack's title bar, and is often more descriptive than the stack's name. It's also the name the user sees on the screen, so for most situations, it's better to show the user the title instead of the name.
The stack titles
To get the stack titles, the handler walks through the list of stack names in a repeat loop, using the repeat for each form. This form of repeat creates a variable (in this handler, the variable is called "thisStack") and places each stack name in turn into the variable. The loop repeats once for each open stack, gathering the title of each stack into a variable called "windowsList" that will be used as the menu's contents.
repeat for each line tStack in the openStacks put the title of stack tStack into tStackTitle ... end repeat
Checking for palettes and dialog boxes
In most cases, a standard Window menu does not include the names of palettes and dialog boxes. To filter out such windows, the handler checks the mode property of each of the open stacks, and skips over that stack if its mode is greater than 2. The mode property describes what kind of window a stack is in: 1 is an unlocked stack in an editable window, 2 is a locked stack in an editable window, and higher numbers are palettes, dialog boxes, or other ways of displaying a stack. So a mode of 1 or 2 indicates that the stack is in an editable window: the kind of window we want to show in the menu. A stack with a mode of zero is closed but loaded into memory. However, we don't need to worry about this sort of stack, because closed but loaded stacks are not returned in the openStacks function to begin with. For more information on stack modes, see Scripting a User Interface in the User's Guide.
As an extra, if the Alt key (Unix or Windows) or Option key (Mac OS or OS X) is held down, this step is skipped and all open stacks are shown, whatever their mode.
if the mode of stack tStack > 2 and the optionKey is not down then next repeat end if
Storing the stack name
The handler now stores the stack's name in a variable called tStacksList. This variable is used to collect all the stack names, in the same order as the stack titles. Later on, we'll store this list in a custom property of the menu. The reason for doing this is so that in another handler, we can take a menu choice and get the corresponding stack name. If we want to do anything with a stack chosen from the menu, the stack name (rather than the title) is needed, because two open stacks can have the same title. Without the stored list of names, we wouldn't necessarily be able to tell which stack was being referred to.
put tStack & return after tStacksList
Storing the stack title
Next, the handler stores the title of the stack in the variable tStackTitle, and then checks to see if the number of words in the title is greater than zero. It checks for the number of words, rather than just checking whether the title is empty, because LiveCode uses a one-space title to indicate that a stack should have no title at all in its title bar. Because of this, some stacks may have a title that consists of a single space, instead of being empty, and since showing a space in a menu isn't very helpful, we want to show such a stack's name instead of its title. If the title contains at least one word, the handler adds it to a variable called tMenuContents, which will become the contents of the Window menu. If the title has no words, the handler adds the stack's name, instead of the title.
put the title of stack tStack into tStackTitle if the number of words in tStackTitle <> zero then put tStackTitle & return after tWindowList else ## no title, so use the stack name instead: put tStack & return after tWindowList end if
Sorting the stack list
When the repeat loop is done, the variable tWindowList contains a list of all the open windows, in front-to-back order. The windows list is in this order because that's the order the openStacks function returns them in, and our list is based on the value returned by the openStacks. If we wanted to use another order, such as alphabetical, we would use the sort command at this point to sort the list:
sort lines of theMenuContents
But what if there are no open windows (that aren't palettes or dialog boxes)? In this case, the variable will be empty, but we don't want the Window menu to be empty, so in this case we put "(No windows open" into the variable. The leading parenthesis makes this a disabled menu item, and the menu will display this grayed-out explanation if there are no windows to list.
if tWindowList is empty then ## no stack windows open put "(No windows open" into tWindowList end if
Setting up the menu
Finally, the handler puts the variable tWindowList into the Window button. Because this handler is in the menu bar group's script, it can refer to the group using the me keyword. The expression button "Window" of me refers to the Window button that's part of the menu bar group. The menu contents is now set to the list of stack titles: when the menu is displayed a moment later, it will show this list.
put tWindowList into button "Window" of me
The mouseDown handler code
on mouseDown local tStacksList, tStackTitle, tWindowList ## goes in the menubar group script if the short name of the target is "Window" then ## the user clicked the Window menu repeat for each line tStack in the openStacks ## skip palettes and dialog boxes ## (unless Option or Alt key is pressed): if the mode of stack tStack > 2 and the optionKey is not down then next repeat end if ## List the stack names for later: put tStack & return after tStacksList ## List the stack by its title, if available: put the title of stack tStack into tStackTitle if the number of words in tStackTitle <> zero then put tStackTitle & return after tWindowList else ## no title, so use the stack name instead: put tStack & return after tWindowList end if end repeat if tWindowList is empty then ## no stack windows open put "(No windows open" into tWindowList end if ## put the list into the menu: put tWindowList into button "Window" of me ##set a custom property for later use: set the cCurrentStacksList of the target to tStacksList end if end mouseDown
This mouseDown handler only takes care of updating the menu for display; it doesn't do anything when the user chooses a menu item. Typically, a Window menu's script includes a menuPick handler that brings the chosen stack to the front (or does whatever other action is appropriate).
Dynamic menus, whose contents may change depending on the context, are often used in applications. For example, the wording of the Cut and Copy menu items may change depending on what's currently selected, or certain menu items may be enabled or disabled depending on which window is active. You can use the basic technique demonstrated in this example to create other changes in menus as needed.