ToC DocOverview CGDoc RelNotes FAQ Index PermutedIndex
Allegro CL version 10.1

7. Menus

This document contains the following sections:

7.1 Some menu notation
7.2 Usual menu behavior
7.3 Pull-down and pop-up menus
7.4 Menu bars
7.5 The menu editor
7.6 Getting rid of a menu bar
7.7 Specifying menu titles and shortcuts
7.8 Access keys
7.9 Working with the menu pane
7.10 More on the menu editor toolbar and some anomalies
7.11 The Menu Item group
7.12 How choosing a menu command causes something to happen
7.13 Some examples of getting menus to do something
7.14 Changing a menu or command (menu-item) programmatically
7.15 Doing something just before a menu is displayed
7.16 Menu Classes
7.17 Creating and modifying menus at run time
7.18 Menu tutorial
7.19 Displaying Pop-up Menus
7.20 Shortcut menu tutorial
7.21 Pop-up menu

This is chapter 7 of the User Guide for the Allegro CL 10.1 Integrated Development Environment (IDE).

The chapters of the IDE User Guide are:

Chapter 1: Introduction to the IDE
Chapter 2: The Allegro CL Development Environment (IDE)
Chapter 3: An example
Chapter 4: Projects
Chapter 5: Components
Chapter 6: Designing a user interface using forms
Chapter 7: Menus (this chapter)
Chapter 8: Events

A menu is a pull-down or pop-up list of commands. Because menus can contain any (reasonable) number of commands and are displayed only by user action, they provide a convenient way to group related commands in a way that they are available at any time to users.

Only top-level windows can have a menu bar. A top-level window is a window that is owned by the screen or another top-level window and is not a child window.

In the following illustration, the window has a menu bar with two menus: File and Edit. Each menu can have zero or more menu-items. For the File menu, there are 6 menu items. The horizontal line is a special type of menu-item called a separator. The menus of a menu bar are of type pull-down-menu because as you click on the title of the menu its menu items are displayed.

7.1 Some menu notation

Menus contain commands. The object corresponding to a command is a menu-item. However, we use menu-item when describing the programmatic aspects of menu but command when discussing the appearance of menus in an application. There are special kinds of menu-items, such as the separator in the illustration above.

A user can point to a command while the menu is visible. The command pointed to is highlighted and if it names a submenu, the submenu is displayed.

You click a menu command not associated with a submenu to effect it (choose can be used in place of click). When a command is clicked, the menu disappears and the action associated with the command is performed.

In the title of a menu or menu item, you can define an access key. An access key lets your user open the menu or access the menu item using the keyboard. If you wanted to open the File menu in the illustration above, you would press ALT+F. To invoke the Exit menu item, you would press "x". Within a particular menu, you should have each access key be as unique and mnemonic as possible.

Shortcut keys are another way for your users to access your menu commands. A shortcut key is (typically) a combination of a key like Control or Alt and a letter or number. It runs a menu item immediately when pressed. In the illustration above, Control-O is the shortcut key for the Open command on the File menu. You should assign shortcut keys to frequently used commands. Windows reserves some shortcut keys for cross-application commands like Cut, Copy and Paste.

A menu command may be associated with another menu (called a submenu). Pointing to (putting the mouse over) the menu command causes the submenu to be displayed and the user can point to or click on a command from the submenu just as with the main menu. (Submenus allow very many commands to be presented rather conveniently since most commands are not displayed at any one time.)

In this document, we specify a menu and command with the menu title followed by a horizontal bar (|) followed by the command name, all in bold. Thus File | Save is the Save command on the File menu. Commands from submenus are shown with additional |'s, thus View | Manage Windows | Iconize is the Iconize command on the Manage Windows submenu of the View menu. (We have been using this notation in other chapters of this document.)

7.2 Usual menu behavior

Clicking or choosing a command causes some action. Menus are rarely used simply to display information (although a command can cause an informative dialog to appear). Information can be conveyed incidentally, however. The fact that a command is unavailable (cannot be pointed to or chosen) can be informative. Some editors make the Save command on the File menu unavailable if there are no changes to save and Paste can be made unavailable when there is nothing on the clipboard.

A user can click on no command and cause the menu to disappear. Typically nothing happens when no item is selected and the state of the program is typically the same as if the menu had never been displayed. (We say typically twice in the last sentence because the application programmer can associate any action – displaying the menu or pointing to a command or whatever – with any effect. If you are programming a game, you might have a Hint menu and cause the game player to lose points if the Hint menu is looked at, whether or not a command is chosen. Or an application designed to study user interfaces may record how often menus are displayed and items chosen as a way of learning about user behavior, perhaps with an eye to improving the interface.)

7.3 Pull-down and pop-up menus

A pull-down menu is displayed on a menu bar. Menu bars are displayed only on a top-level window. The menu bar is typically always visible and the user can display a menu any time the mouse is available.

Pop-up menus are displayed by specific user action but have no specific location and no title. Pressing or clicking the right mouse button in various locations in the Allegro CL development environment displays a pop-up menu with context-related items. Another pop-up menu is the symbol completion menu displayed by choosing Search | Complete Symbol. While typing in the Debug window or in an editor window. For example, at a prompt in the Debug window, type

string>

and, leaving the cursor right after the >, choose Search | Complete Symbol. A pop-up menu appears with items string> and string>= (the two symbols whose names begin with string>).

This chapter is concerned with pull-down menus. For information on pop-up menus, see the online help topics pop-up-shortcut-menu and pop-up-lettered-menu.

7.4 Menu bars

To add a menubar to a form, select the form, and then either (1) click on Form | Add Menu Bar or (2) select the menu property in the Inspector, and click on the Extended Editor button:

This displays the menu editor.

To have a pull-down menu on a window, you must:

Do all that and you will have a working menu. Here are some more things that you can do:

Each one of these issues will be dealt with below. The basic tool for placing menu bars on top-level windows and creating pull-down menus is the Menu Editor, which we describe next.

7.5 The menu editor

The menu editor looks like this:

You are given a default starter menu bar. You can make changes and accept the menu bar by clicking on the OK button. As you work in the menu editor, you will see the menu bar update on your form. If you click the Cancel button, the editor is closed and the menu property is left unchanged (meaning no menu bar if it was nil before, the previous menubar if there was one before). The starter menu bar has a File menu and an Edit menu containing some Windows-standard commands.

7.6 Getting rid of a menu bar

If a form has a menu bar and you decide you do not want one on that form, you can get rid of it by clicking Form | Remove Menu Bar while the form is selected. You can also do it by selecting the contents of the menu property when the form is being inspected and replacing those contents with nil (by typing n i l).

The menubar will also not be displayed if you

7.7 Specifying menu titles and shortcuts

The menu titles, command names, and shortcuts are displayed in the Menu group in the center of the menu editor. The display is an outline control, with leaves (terminal entries with no subentries) corresponding to commands and nodes (entries with subentries) corresponding to menus or submenus. The title of a menu appears in the menu bar or, if the menu is a submenu, as an item in the parent menu. The title is not a command. Clicking on it displays the menu whose title it is.

A shortcut is a key combination which when pressed has the same effect as choosing the menu command. The shortcuts are shown in the second column. Menus do not have shortcuts, only commands, shown as leaves in the display.

7.8 Access keys

An access key in combination with the Alt key lets you display a menu and choose a command from the keyboard. A tilde (~) is a non-displaying character in a menu title or command name which marks the next character as the one to press when the Alt key is down to display a menu or submenu (in a menu or submenu title) or choose a command. Thus, Alt-F displays the File menu (whose title is ~File). Pressing Alt and keeping it down while pressing F (displays the File menu) and N (chooses the New command) is the same as choosing New from the File menu. The character after the tilde is underlined when the menu is displayed. If no tilde appears, the command cannot be selected using Alt and other keys. Note: because of an apparent Windows bug in some versions of Windows (but not in WIndows 2000), access keys may not work when running a project with a form that has a menubar but no other controls. If that happens, then to see access keys working, add any control (e.g., a button) to the form.

7.9 Working with the menu pane

The Menu group is the portion of the menu editor labeled Menu Name:

You cannot type in the Menu group. (You make changes that require typing – changing a title of a command name, adding or removing a tilde, etc. – in the Menu Item pane described below.) When a line is selected (as the ~File line is in the illustration), its details are displayed in the Menu Item group described below and the toolbar buttons will affect it as follows:

7.10 More on the menu editor toolbar and some anomalies

If you add an entry (by clicking on the Add button -- with a +), the new entry has the same indentation as the currently selected entry and becomes the selected entry.

If you delete all entries, the menu bar ceases to be visible but still exists. (Make the menu property nil if you want no menubar.)

7.11 The Menu Item group

The Menu Item pane, at the bottom of the Menu Editor, allows you to specify the details of the entries in the Menu group. The details of the selected entry in the Menu Pane are displayed in the Menu Item pane and changes affect that entry only. Only one entry can be selected at a time. Here is the Menu Item pane from the default menus (i.e. the File and Edit menus displayed when the Menu Editor first appears) with the third (~Open) entry selected:

The elements of this pane are:

If you want a tilde displayed, enter two tildes. Thus, if the title is ~~Open, it will display as ~Open and it cannot be chosen using the Alt key.

7.12 How choosing a menu command causes something to happen

When you click on a menu command, the system knows what you have done: it knows which window contained the menu bar, it knows which menu contained the command, and it knows which command was chosen. But it doesn't know what to do with this information other than pass it to the application owning the window, so that is what it does. In an Allegro CL application, every menu has an on-click function. When a menu command is clicked, the menu on-click function is called with three arguments: the menu object, the menu-item corresponding to the command clicked on, and the window owning the menubar.

You can specify the menu on-click function in the On Click field of the menu-item group. This field is only active when a menu or submenu (rather than a command) is selected.

The default selection function for the default menus is funcall-menu-item-with-window. This function examines the menu-item, extracts the menu-item value (which in this case is a symbol naming a function of one argument, a window), and funcalls that function with the window (owning the menu bar) as the argument.

It is common to have the menu-item value contain the information about what should be done when the user clicks on a menu command. Menu-item values can be any Lisp object but symbols naming functions are common choices. Since you define the function, you have great flexibility about what it does. The menu on-click function is then just a function which funcalls the menu-item value with whatever arguments you want, often no arguments or just the owning window as an argument.

The menu-item value is specified in the menu-item group when a menu item is selected.

We have already mentioned the predefined function funcall-menu-item-with-window. Another useful predefined selection function is funcall-menu-item, which funcalls the menu-item value with no arguments.

7.13 Some examples of getting menus to do something

The function pop-up-message-dialog takes arguments stream, title, text, icon, button-label. We are going to construct a menu whose command pops-up a message using that function where the title and text say what command you chose. Now, of course, this is likely not very useful in a real application but if you can get a menu command to pop-up a message like this, you can probably get it to do many other things by emulation.

Our menu will be called Messages and have two commands: Hello and Goodbye. The menu on-click function will be funcall-menu-item and the menu-item-values will be the symbols hello-function and goodbye-function, which are defined as follows:

(defun hello-function ()
  (pop-up-message-dialog (screen *system*) "Hello" 
                         "Clicked Hello Command!!!" warning-icon "OK"))
(defun goodbye-function ()
 (pop-up-message-dialog (screen *system*) "Goodbye" 
                        "Clicked Goodbye Command!!!" warning-icon "OK"))

We define these to the system (by, for example, evaluating them in the debug window). (screen *system*) is the screen (meaning the dialog can pop-up anywhere), warning-icon is a picture of a yellow triangle with an exclamation point, and OK is the label for the button. Our menu editor after we have defined the new menu (and deleted the default File and Edit menus) looks like this:

Note that the On Click field when Messages is selected is funcall-menu-item. The On Click field for Messages will only be available after you have added and indented the Hello and Goodbye items, and when it is available, its value starts as funcall-menu-item-with-window. You must edit that to be funcall-menu-item for this example to work.

If we run the form and click on the Hello command in the Messages menu, we see:

If we click on Goodbye, we see:

There is not in this example much information in the menu-items. We could define a selection function that extracts information from the menu-item but processes it itself rather than having the menu-item value do the work. This can be useful when you define menu commands on the fly, based on runtime information. In this case, the menu-item values are strings "Hello" and "Goodbye". The menu-selection function is the following:

(defun my-menu-on-click-function 
    (menu menu-item window)
  (declare (ignore menu window))
  (let ((title (value menu-item))
        (message (concatenate 'string 
                   "You clicked " 
                   (value menu-item)
                   "!!!!")))
    (pop-up-message-dialog 
     (screen *system*) 
     title message warning-icon "OK"))) 

The behavior is the same as before.

7.14 Changing a menu or command (menu-item) programmatically

Commands are what appear on menus. On our example menus, the commands are Hello and Goodbye. However, the programmatic object associated with a command is a menu-item, which is an instance of class menu-item and has various properties. One is the available property. If mi-var is a variable whose value is a menu item,

(available mi-var)

returns true or false as the menu-item is or is not available. You can use setf with available to change (or set) the availability of a menu-item. Evaluating the following makes the menu-item mi-var unavailable:

(setf (available mi-var) nil)

So, the only issue is how to find a menu-item programatically. There are various ways to do this, but one of the easiest is to give the object a name and get it using find-named-object and that name. Here is the menu editor again with our Messages menu. We have given the Hello menu-item the name :hello-item (illustrated) and the Goodbye menu-item the name :goodbye-item (not shown):

Now let us get a handle on the the form or window. When developing, click on Get Component on the Tools menu and then click on the form (or window, if you are running the form) and then in the debug window, enter

(setq mywin *)

Programmatically, you can use find-window.

Now that mywin is the window with the Messages menu,

(find-named-object :hello-item mywin)

will return the Hello menu-item,

(available (find-named-object :hello-item mywin))

returns true or false as the Hello item is or is not available, and

(setf (available 
       (find-named-object :hello-item mywin)) 
  nil)

makes it unavailable.

7.15 Doing something just before a menu is displayed

The system calls about-to-show-menu with arguments the window and the menu when user action should cause a menu to appear (user action being, e.g., clicking on the menu title on a menu bar or pressing Alt and the underlined letter of the menu title). about-to-show-menu is a generic function. You can write a before method that will run before the system call to the function (making items unavailable, registering the fact that the menu was looked at, or whatever). If you do this, it is best to make your own class of windows and specialize the method on that class. This avoids paying the overhead every time any menu is displayed anywhere within Allegro CL or your application.

7.16 Menu Classes

There are several classes related to menus. The following is a class diagram showing the menu classes.

window
   ^
   |-- basic-pane
   |
   |-- menu
         ^
         |-- windows-menu
                 ^
                 |---------- menu-bar
                 |
                 |---------- pop-up-menu
                                ^
                                |
                                |----- pull-down-menu
                                |
                                |----- shortcut-menu
                                |
                                |----- builder-menu
Key: ^ = "is a subclass of", reading up.

Notice that all menus inherit from window. That is a very primitive window class that provides a handle to an OS window object. The menu-item class is not shown because it does not inherit from menu because menu-items are not an OS visible object. Instead, menu-items inherit from standard-object.

There are several different functions related to creating an instance of a menu; the table shows which function should be used for each menu class. menu-item is created with make-instance. A menu-bar is created with make-window. A pop-up-menu, a pull-down-menu, and a shorcut-menu are all created with open-menu.

7.17 Creating and modifying menus at run time

Code similar to the following is generated if you accept the default menu bar that is created when you open the Menu Editor. It provides the value of the menu keyword argument to make-window. If you want to see the code for yourself, then toggle the menu property on a form without a menu bar to on. In the Menu Editor, click on OK to accept the default menu bar. With the focus on the form, save it using the File | Save command. In the editor, open the .bil file and search for open-menu.

(open-menu
 (list (make-instance 'menu-item :name :file-menu
         :title "~File" :value
         (open-menu          
          (list
           (make-instance 'menu-item
             :name 'new-text-editor
             :title "~New"
             :value 'new-text-editor
             :selected nil
             :available t
             :event-synonym '(control-key #\N)
             :help-string "New editor")
           (make-instance 'menu-item
             :name 'open-text-file
             :title "~Open"
             :value 'open-text-file
             :selected nil
             :available t
             :event-synonym '(control-key #\O)
             :help-string "Open a file")
           (make-instance 'menu-item
             :name :save
             :title "~Save"
             :value 'save-text-file
             :selected nil
             :available t
             :event-synonym '(control-key #\S)
             :help-string "Save to file")
           (make-instance 'menu-item
             :name 'save-as-text-file
             :title "Save ~As..."
             :value 'save-as-text-file
             :selected nil
             :available t
             :event-synonym nil
             :help-string "Save to new file")
           (make-instance 'menu-item
             :name nil
             :title "-"
             :value nil
             :selected nil
             :available nil
             :event-synonym nil
             :help-string nil)
           (make-instance 'menu-item
             :name 'user-close
             :title "E~xit"
             :value 'user-close
             :selected nil
             :available t
             :event-synonym '(alt-key vk-f4)
             :help-string "Exit application"))
          'pull-down-menu
          (screen *system*)
          :name :file-menu          
          :show-help-strings-as-tooltips nil
          :on-click 'funcall-menu-item-with-window)
         :selected nil :available t
         :event-synonym nil
         :help-string nil)
       (make-instance 'menu-item :name :edit-menu
         :title "~Edit" :value
         (open-menu          
          (list
           (make-instance 'menu-item
             :name 'cut-command
             :title "~Cut"
             :value 'cut-command
             :selected nil
             :available t
             :event-synonym '(control-key #\X)
             :help-string "Copy contents to clipboard and delete")
           (make-instance 'menu-item
             :name 'copy-command
             :title "C~opy"
             :value 'copy-command
             :selected nil
             :available t
             :event-synonym '(control-key #\C)
             :help-string "Copy contents to clipboard")
           (make-instance 'menu-item
             :name 'paste-command
             :title "~Paste"
             :value 'paste-command
             :selected nil
             :available t
             :event-synonym '(control-key #\V)
             :help-string "Paste contents from clipboard"))
          'pull-down-menu
          (screen *system*)
          :name :edit-menu
          :show-help-strings-as-tooltips nil
          :on-click 'funcall-menu-item-with-window)
         :selected nil :available t
         :event-synonym nil
         :help-string nil))
 'menu-bar (screen *system*) :name :default-menu
 :show-help-strings-as-tooltips nil
 :on-click 'funcall-menu-item)
open-menu
(open-menu (list ...) 'pull-down-menu (screen *system*)
                      :on-click 'funcall-menu-item-with-window ...)

Use the open-menu function to create a pop-up-menu or pull-down-menu. The arguments to open-menu are the list of menu-items, the type of menu, the screen, and zero or more keyword arguments for the properties of the menu.

name The symbolic name of the menu item
title The title of the command. Place a "~" before the access key of the command.
Use "-" to indicate a menu separator.
on-click The event handler that is invoked when the user clicks on a menu item in the menu


make-instance

(make-instance 'menu-item :name :new :title "~New" ...)

For every command on a menu, you need to create an instance of menu-item. In general, you need to specify most of the properties of a menu-item to have it perform properly.

allow-during-modality A Boolean value indicating whether or not the command can be invoked when a modal dialog is displayed
available A Boolean value indicating whether the command is displayed as available or dimmed
event-synonym The shortcut key(s) that can invoke the command. Values can be nil or a list of key constants/characters. Here are two examples, '(vk-f8) and '(control-key #\N).
help-string A string that is displayed when the user points to the menu item
name The symbolic name of the menu item
selected A Boolean value indicating whether or not the menu item is displayed with a check mark or not.
title The title of the command. Place a "~" before the access key of the command. Use "-" to indicate a menu separator.
value The value of the menu item. If the menu item has sub-items, then the value is a pull-down or pop-up menu.

(make-instance 'menu-item :name nil :title "-")

Creates a menu separator. When the title of a menu-item is "-", then a menu separator is displayed as a horizontal line in the menu. You can also use the constant, menu-separator, instead of creating a menu-item. Menu items can be placed on more than one menu.

7.18 Menu tutorial

This short tutorial demonstrates creating and customizing an application's menu bar. The sample application is a mini-text editor that extends the default menu bar.

Setup

  1. Open a new project using File | New Project.
  2. If there is no form by default, add a form with File | New Form.
  3. Inspect the new form by double-clicking on it.
  4. In the form inspector, change its name to :menu-example, title to "Menu Example", and status-bar to t.
  5. View the Project Manager, using View | Project Manager if necessary.
  6. In the Project Manager, change the name of the project to :menu-example by clicking on the Options tab and entering :menu-example for the name. Press the tab key and notice that the title in the top Project window (the Allegro CL window with the menus and component toolbar) changes to "Menu-example".
  7. Because you are creating a new text application, you will need to have your own window class with its unique behavior. Later, it will be necessary to add methods customized on your window class. The class property of a form determines the type of running window that is created. Before you can change the device to your window's class, you have to define a new window class.
  8. In the Project Manager, view the source code for the menu-example form. You do this by clicking on the General tab and selecting the menu-example module and press the View Selected Code button (the second one with binoculars in the icon).
  9. In the menu-example editor, add the following class definition. Since we want our application to behave as a text editor we inherit from text-edit-window. In future steps, we will customize methods on our new window class.
  1. The text-edit-window class is derived from frame-with-single-child. Therefore, creating an instance of my-window also creates an instance of a pane. For some of the commands, it is necessary to add methods specialized on the pane of my-window. Therefore, you need to specify a custom pane class for my-window. In the menu-example editor, add the following class and method definitions.
(defclass my-pane (text-edit-pane)
    ())

(defmethod default-pane-class ((window my-window))
    'my-pane)
  1. You need to compile the new window class definitions so they are known. Because you have added several new definitions, it is easier to let the Project Manager do the compiling for you. First, save all your changes by clicking on File | Save All. If you chose a directory for the new project, the files will be saved there. Otherwise save to a convenient directory.
  2. Then, compile the project using Tools | Compile Project.
  3. In the form inspector, change the class to my-window. Whenever you run the form, an instance of my-window is created.
  4. Save your changes using File | Save All.

Adding a Menu Bar

  1. Add a menu bar to your form. In the form inspector, select the menu property and click on the extended editor button (the one with three dots). Notice that the Menu Editor is displayed and the default menu bar is added to your form.
  2. In the Menu Editor, click on the File menu item. Notice that it has an on-click function named funcall-menu-item-with-window. This is the default on-click function for menus and applicable for most cases. Later in this tutorial (under the heading On-click event handler) we will show you how to use a different on-click function.
  3. In the Menu Editor, click on the File | New menu item. Notice that it has a value, which is a function name, and that it does not have an on-click function. Also, notice that there is a help string assigned to the menu item. In the next step, we will display this help string in the status bar of our form.
  4. Accept the default menu bar by clicking on the OK button in the Menu Editor. Notice that the menu bar remains on your form and that you can preview the menu by selecting menu items.
  5. Run your project and try the menu bar using Run | Run Project. Try selecting menu items and notice the help strings displayed in the status bar. Try selecting commands on the File and Edit menus. In future steps, we will customize the behavior so that your text editor behaves better. When you are satisfied, stop the project using Run | Stop.

Implementing Menu Commands

  1. In the previous step, you may have tried the File | New and File | Open commands. By default, these commands created new editor windows similar to a multi-document interface (MDI) style. For our simple example, we want a single document interface (SDI) style similar to Notepad's behavior. In Notepad, you can only edit a single file at a time.
  2. First, let's fix the behavior of the File | New command. When File | New command is selected, the function, new-text-editor is called. You can determine what function is called by using the Menu Editor. Inspect the menu-example form. In the inspector, select the menu property and click on the extended editor button. In the Menu Editor, select the File | New menu item. Notice that its value is new-text-editor.
    Click Cancel on the Menu Editor when satisfied.
  3. To add the new method for new-text-editor, display the source code for the menu-example form. View the Project Manager using View | Project Manager. In the project manager, select the menu-example module on the General tab and press the View Selected Code button.
  4. In the menu-example editor, add the following method. Notice that this method needs to be specialized on the pane rather than the window. This is because funcall-menu-item-with-window automatically calls the menu-item's value on the pane if one exists. This method uses an internal common-graphics function for convenience.
(defmethod new-text-editor ((window my-pane))
   (setf (file window) nil)
   (clear-page window))
  1. Next, let's customize the behavior of the File | Open command. File | Open calls the function open-text-file. In the menu-example editor, add another method specialized on the pane. Again, internal common-graphics functions are used for convenience.
(defmethod open-text-file ((window my-pane))
   (let* ((pathname 
            (ask-user-for-existing-pathname "Edit File"
             :stream window)))
      (cond ((or (null pathname) (not (probe-file pathname))) nil)
            (t 
              (load-file window pathname)))))
  1. Save all your changes using the File | Save All. Try running your application and using the File | New and Open commands. Notice the new SDI behavior. You can try opening an existing file and making changes. The File | Save and Save As commands do not actually save your changes. That is the next step. When you are satisfied, stop the application using the Run | Stop command.
  2. To complete the File menu, you need to fix the File | Save and File | Save As commands. In the menu-example editor, add the following methods. Common-graphics functions are used again, for convenience. The save-file function requires a file argument, so we call ask-user-for-new-pathname if one is required (the first save for Save and any save for Save As).
(defmethod save-text-file ((window my-pane))
   (let ((file-to-save (file (parent window))))
     (if (null file-to-save) 
         (setq file-to-save 
           (ask-user-for-new-pathname "Save file as"
                :allowed-types '(("Text Files" . "*.txt") 
                                 ("All Files" . "*.*")))))
     (if file-to-save (save-file window file-to-save))))

(defmethod save-as-text-file ((window my-pane))
   (let ((file-to-save 
           (ask-user-for-new-pathname "Save file as"
                :allowed-types '(("Text Files" . "*.txt")
                                 ("All Files" . "*.*")))))
     (if file-to-save (save-file window file-to-save))))
  1. Save all your changes using File | Save All. Try running your application using the Run | Run Project command. In the running window, try using the File commands. Notice that the Edit menu works but it would be nice to invalidate the Edit | Cut and Copy command when there is no selected text. When you are satisfied, stop the application using the Run | Stop.

Customizing Showing Menus

  1. In the previous step, you noticed that the Edit | Cut and Copy commands were available even if nothing was selected. For this section, we will customize the about-to-show-menu behavior for our window to provide better user feedback.
  2. In the menu-example editor, add the following method to about-to-show-menu. Notice that this method needs to specialize on the window rather than its pane. This about-to-show-menu method makes the Edit | Cut and Copy commands unavailable if there is no selected text. Also, the Edit | Paste command is made unavailable if nothing is on the clipboard. For all other menus, the default behavior is invoked by using call-next-method.
(defmethod about-to-show-menu ((window my-window) menu)
   (case (name menu)
     (:edit-menu
       (multiple-value-bind (start end)
           (get-selection (frame-child window))
          (setf (available (find-named-object 'cut-command menu))
                (not (eq start end)))
          (setf (available (find-named-object 'copy-command menu))
                (not (eq start end)))
          (setf (available (find-named-object 'paste-command menu))
                (first (first *clipboard*)))))
     (t
       (call-next-method))))
  1. Save all your changes using File | Save All. Try running your application using Run | Run Project. In the running window, try using the Edit commands with and without selected text. When you are satisfied, stop the application using Run | Stop.

On-Click event handler

  1. Up to this point, you have not needed to define an on-click event handler for your menus. You have been using the default on-click event handler, which is funcall-menu-item-with-window. We will extend our mini-text editor to allow users to select a different background color from a menu. For our example, we will allow the user to choose between system specified, red, green and blue. The background color menu will display a list of color names that the user can select.
  2. First, you need to add a Background menu to your menu bar. Inspect the menu-example form. In the form inspector, select the menu property and click on the extended editor button. In the Menu Editor, scroll down and select the Edit menu item. With the Edit item selected, press the Insert Item button (the big + in the upper left). Scroll down and notice that an Untitled menu has been added to the list.
  3. In the Menu Editor, make sure that the new Untitled menu item is selected. Change its name to :background-menu and title to "~Background".
  4. Let's add the system specified menu item to the Background menu. In the Menu Editor, make sure that Background menu is selected and press the Insert Item button. Select the new Untitled menu item and press the Indent button (right-pointing arrow). Change the name of the menu item to :system and title to "~System". The value of the menu is deliberately left nil because a background-color of nil means use the system-specified color.
  5. Let's add a red background menu item. In the Menu Editor, select the System menu item and press the Insert Item button. Select the Untitled menu item and change its name to :red, title to "~Red" and value to red. The symbol, red, is bound to an rgb constant for red. Later, we will use the value of the menu item to specify the background color of the pane.
  6. Repeat the previous step for Blue and Green menu items. Set their values to blue and green, respectively. At this point, your background menu should have four items, System, Red, Green and Blue.
  7. Stylistically, it is better to have System separated from the other menu items because it behaves a little differently. In the Menu Editor, select the Background | System menu item and press the Insert Item button. Select the Untitled menu item and change the title to "-". Setting the title to "-" is a special case that creates a menu separator for you.
  8. Finally, you should change the on-click event handler for the Background menu to background-color-on-click (which we define in 41 below). You cannot use the default on-click event handler because it tries to call the value of the menu item as a function. For this menu, the values of the menu items are the color for the background of the editor.
  9. Save your menu bar by clicking on the OK button in the Menu Editor.
  10. Next, you add a background-color-on-click function. View the Project Manager using View | Project Manager. In the Project Manager, select the General tab, select the menu-example module, and press the View Selected Code button.
  11. In the menu-example editor, add the following method. This method is invoked whenever the user clicks on a menu item in the Background menu. Notice that you have to change the background color of the frame-child (pane) rather than the editor window.
(defmethod background-color-on-click 
    (menu menu-item (window my-window))
   (declare (ignore menu))
   (setf (background-color (frame-child window)) 
         (value menu-item)))
  1. Save all your changes using File | Save All. Try running your application using Run | Run Project. In the running window, try selecting different Background colors. Notice that the Background menu works but it would be nice to indicate what the current background color is by a check mark. When you are satisfied, stop the application using Run | Stop.

Checking Menu Items

  1. In the previous section, you noticed that it would be nice to check the current background color when the Background menu is displayed. This is very similar to setting the availability of the Edit menu commands before the Edit menu is shown. In fact, you need to extend the about-to-show-menu method for your window.
  2. Edit the about-to-show-menu method for my-window. View the Project Manager using View | Project Manager. In the Project Manager, select the General tab, select the menu-example module, and press the View Selected Code button.
  3. In the menu-example editor, change the about-to-show-menu method to match the following. The selected property of a menu item indicates whether or not it is checked. There is a check for the menu separator item so that it will not appear checked and the system item is checked if the background color is nil.
(defmethod about-to-show-menu ((window my-window) menu)
   (case (name menu)
     (:edit-menu
       (multiple-value-bind (start end)
           (get-selection (frame-child window))
          (setf (available (find-named-object 'cut-command menu))
                (not (equalp start end)))
          (setf (available (find-named-object 'copy-command menu))
                (not (equalp start end)))
          (setf (available (find-named-object 'paste-command menu))
                (first (first *clipboard*)))))
     (:background-menu
       (let ((color (background-color (frame-child window))))
            (dolist (item (menu-items menu))
               (setf (selected item)
                     (and (null (string-equal (title item) "-"))
                          (or (and (null color) (null (value item)))
                              (if (and color (value item))
                                 (rgb-equal (symbol-value (value item)) 
                                            color))))))))
     (t
       (call-next-method))))
  1. Save all your changes using File | Save All. Try running your application using Run | Run Project. In the running window, try selecting different Background colors. Notice that the Background menu checks the current background color. When you are satisfied, stop the application using Run | Stop.

7.19 Displaying Pop-up Menus

Floating menus are implemented using pop-up-menus. A pop-up-menu is similar to a pull-down-menu because it can have one or more menu items. The difference is that a pop-up menu is not activated by clicking on the menu's title or by using the ALT key. Usually, pop-up-menus are opened by using the right mouse click. You cannot use the Menu Editor to specify pop-up-menus.

Pop-up menus that are opened using a mouse right click are called shortcut menus. If you want to define a shortcut menu for your window, you do the following:

  1. Define your own window class that inherits from one of Common Graphics window classes like dialog.
  2. Optionally, define your own shortcut menu class that inherits from shortcut-menu. Add a shortcut-menu-class method specialized on your window that returns the name of your shortcut menu class.
  3. Add a mouse-right-down method specialized on your window that calls pop-up-shortcut-menu.
  4. Add a shortcut-commands method specialized on your window and on your shortcut menu class that returns a list of menu items for the shortcut menu.

There is a more general exported function called pop-up-menu. If you use pop-up-menu, you are responsible for opening the menu, creating the menu items, and positioning the pop-up menu.

The utility function called pop-up-lettered-menu takes a list of strings and displays a pop-up-menu with alphabetized accelerator keys. If the user clicks on an option, then the string of the selected item is returned. Otherwise, nil is returned if no item is selected. pop-up-lettered-menu enables an end user to type a single key to select an item. It is used by the Search | Complete Symbol command in the Integrated Development Environment.

7.20 Shortcut menu tutorial

The following steps extend the previous mini-text editor example. For shortcut menus, you cannot use the Menu Editor and will have to work programmatically. We use the menu-example project created in section 7.18 above.

Shortcut Menu

  1. Open the menu-example project using File | Open Project.
  2. First, let's define a new shortcut-menu class that is associated with my-window. View the Project Manager using View | Project Manager. In the Project Manager, select the General tab, select the menu-example module, and press the View Selected Code button.
  3. In the menu-example editor, add the class definition for your shortcut-menu.
(defclass my-shortcut-menu (shortcut-menu)
    ())
  1. Next, you need to associate your new shortcut-menu class with your pane class. You need to specialize for your pane rather than the window because the user will be clicking inside the pane not the window. Therefore, you need to add a shortcut-menu-class method specialized for my-pane to return my-shortcut-menu.
(defmethod shortcut-menu-class ((window my-pane))
   'my-shortcut-menu)
  1. Whenever the user presses the mouse right button, you want your shortcut menu to appear. By default, nothing happens when the user clicks the mouse right button in your window. Therefore, you need to add a mouse-right-down event method.
(defmethod mouse-right-down ((window my-pane) buttons data)
   (declare (ignore buttons data))
   (pop-up-shortcut-menu window))
  1. Finally, you must specify what commands you want on your shortcut menu. For this case, we have inspect and changing the background color.
(defmethod shortcut-commands ((window my-pane) (menu my-shortcut-menu))
   (list (make-instance 'menu-item
           :name :inspect
           :value 'inspect-command
           :title "Inspect")
     menu-separator
     (make-instance 'menu-item
       :name :system-background
       :value 'system-background-color-command
       :title "System Background"
       :selected (not (background-color window)))
     (make-instance 'menu-item
       :name :blue-background
       :value 'blue-background-color-command
       :title "Blue Background"
       :selected (rgb-equal (background-color window) blue))))

(defmethod inspect-command ((window my-pane))
   (inspect window))

(defmethod system-background-color-command ((window my-pane))
   (setf (background-color window) nil))

(defmethod blue-background-color-command ((window my-pane))
   (setf (background-color window) blue))
  1. Save all your changes using File | Save All. Try running your application using Run | Run Project. In the running window, try clicking the mouse right button in the editor pane. When you are satisfied, stop the application using Run | Stop.

7.21 Pop-up menu

Sometimes you may want to use the more general pop-up-menu function rather than pop-up-shortcut-menu. Here we use the menu-example project created in section 7.18 above.

In the menu-example editor, you would need to rewrite the mouse-right-down function to use pop-up-menu instead. Since you are not using the shortcut menu functions, you are responsible for creating the menu and its menu items. The pop-up-menu function displays a pop-up menu at a specified location.

;; Redefined mouse-right-down that calls pop-up-menu.
(defmethod mouse-right-down ((window my-pane) buttons data)
  (declare (ignore buttons data))
  (let ((menu (open-menu 
	       (list (make-instance 'menu-item
		       :name :inspect
		       :value 'inspect-command
		       :title "Inspect")
		     menu-separator
		     (make-instance 'menu-item
		       :name :system-background
		       :value 'system-background-color-command
		       :title "System Specified Background"
		       :selected (not (background-color window)))
		     (make-instance 'menu-item
		       :name :blue-background
		       :value 'blue-background-color-command
		       :title "Blue Background"
		       :selected (rgb-equal (background-color window) blue)))
	       'my-shortcut-menu (screen *system*)
	       :window window)))
    (prog1 (pop-up-menu menu (screen *system*))
      (close menu))))

Go to chapter 8. Go to the beginning of this chapter.


Copyright (c) 1998-2017, Franz Inc. Berkeley, CA., USA. All rights reserved.
Documentation for Allegro CL version 10.1.
Created 2012.2.27.

ToC DocOverview CGDoc RelNotes Index FAQ PermutedIndex
Allegro CL version 10.1