The Interface Kit: BMenuItem

Derived from: public BObject

Declared in: <interface/MenuItem.h>


Overview

A BMenuItem is an object that contains and displays one item within a menu. By default, Menu items are displayed simply as textual labels, like "Options..." or "Save As". Derived classes can be defined to draw something other than a label--or something in addition to the label.


Kinds of Items

Some menu items play a role in helping users navigate the menu hierarchy. They give the user access to submenus. A submenu remains hidden until the user operates the item that controls it.

Other items accomplish specific actions. When the user invokes the item, a message is posted so that it will be delivered to a target BHandler, usually the window where the menu at the root of the hierarchy (a BMenuBar object) is displayed. The action that the item initiates, or the state that it sets, depends entirely on the message and the handler's response to it.

The target handler and the message can be customized for every item. Each BMenuItem retains a model for the BMessage it posts and can have a target that's different from other items in the same menu.

Items can also have a visual presence, but do nothing. Instances of the BSeparatorItem class, which is derived from BMenuItem, serve only to visually separate groups of items in the menu.


Shortcuts and Triggers

Any menu item (except for those that control submenus) can be associated with a keyboard shortcut, a character the user can type in combination with a Command key (and possibly other modifiers) to invoke the item. The shortcut character is displayed in the menu item to the right of the label. All shortcuts for menu items require the user to hold down the Command key.

A shortcut works even when the item it invokes isn't visible on-screen. It, therefore, has to be unique within the window (within the entire menu hierarchy).

Every menu item is also associated with a trigger, a character that the user can type (without the Command key) to invoke the item. The trigger works only while the menu is both open on-screen and can be operated using the keyboard. It therefore must be unique only within a particular branch of the menu hierarchy (within the menu).

The trigger is one of the characters that's displayed within the item --either the keyboard shortcut or a character in the label. When it's possible for the trigger to invoke the item, the character is underlined. Like shortcuts, triggers are case-insensitive.

For an item to have a keyboard shortcut, the application must explicitly assign one. However, by default, the Interface Kit chooses and assigns triggers for all items. The default choice can be altered by the SetTrigger() function.


Marked Items

An item can also be marked (with a check mark drawn to the left of the label) in order to indicate that the state it sets is currently in effect. Items are marked by the SetMarked() function. A menu can be set up so that items are automatically marked when they're selected and exactly one item is marked at all times. (See SetRadioMode() in the BMenu class.)


Disabled Items

Items can also be enabled or disabled (by the SetEnabled() function). A disabled item is drawn in muted tones to indicate that it doesn't work. It can't be selected or invoked. If the item controls a specific action, it won't post the message that initiates the action. If it controls a submenu, it will still bring the submenu to the screen, but all the items in submenu will be disabled. If an item in the submenu brings its own submenu to the screen, items in that submenu will also be disabled. Disabling the superitem for a submenu in effect disables a whole branch of the menu hierarchy.

See also: the BMenu class, the BSeparatorItem class


Hook Functions

All BMenuItem hook functions are protected. They should be implemented only if you design a special type of menu item that displays something other than a textual label.

Draw() Draws the entire item; can be reimplemented to draw the item in a different way.
DrawContents() Draws the item label; can be reimplemented to draw something other than a label.
GetContentSize() Provides the width and height of the item's content area, which is based on the length of the label and the current font; can be reimplemented to provide the size required to draw something other than a label.
Highlight() Highlights the item when it's selected; can be reimplemented to do highlighting in some way other than the default.


Constructor and Destructor


BMenuItem()

      BMenuItem(const char *label, BMessage *message,
         char shortcut = NULL, ulong modifiers = NULL) 
      BMenuItem(BMenu *submenu, BMessage *message = NULL) 

Initializes the BMenuItem to display label (which can be NULL if the item belongs to a derived class that's designed to display something other than text) and assigns it a model message (which also can be NULL).

Whenever the user invokes the item, the model message is copied and the copy is posted and marked for delivery to the target handler. Three pieces of information are added to the copy before it's posted:

Data name Type code Description
"when" B_DOUBLE_TYPE The time the item was invoked, as measured in microseconds since the machine was last booted.
"source" B_OBJECT_TYPE A pointer to the BMenuItem object.
"index" B_LONG_TYPE The index of the item, its ordinal position in the menu. Indices begin at 0.

These names should not be used for any data that you place in the message.

By default, the target of the message is the window associated with the item's menu hierarchy--the window where the BMenuBar at the root of the hierarchy is located. Another target can be designated by calling the SetTarget() function.

The constructor can also optionally set a keyboard shortcut for the item. The character that's passed as the shortcut parameter will be displayed to the right of the item's label. It's the accepted practice to display uppercase shortcut characters only, even though the actual character the user types may not be uppercase.

The modifiers mask, not the shortcut character, determines which modifier keys the user must hold down for the shortcut to work--including whether the Shift key must be down. The mask can be formed by combining any of the modifiers constants, especially these:

B_SHIFT_KEY
B_CONTROL_KEY
B_OPTION_KEY
B_COMMAND_KEY

However, B_COMMAND_KEY is required for all keyboard shortcuts; it doesn't have to be explicitly included in the mask. For example, setting the shortcut to 'U' with no modifiers would mean that the letter 'U' would be displayed alongside the item label and Command- u would invoke the item. The same shortcut with a B_SHIFT_KEY modifiers mask would mean that the uppercase character (Command-Shift-U) would invoke the item.

If the BMenuItem is constructed to control a submenu, it can't take a shortcut and it typically doesn't post messages--its role is to bring up the submenu. However, it can be assigned a model message if the application must take some collateral action when the submenu is opened. The item's initial label will be taken from the name of the submenu. It can be changed after construction by calling SetLabel() .

See also: SetTarget(), SetMessage(), SetLabel()


~BMenuItem()

      virtual ~BMenuItem(void)

Frees the item's label and its model BMessage object. If the item controls a submenu, that menu and all its items are also freed. Deleting a BMenuItem destroys the entire menu hierarchy under that item.


Member Functions


Command() see SetMessage()


ContentLocation()

protected:

      BPoint ContentLocation(void) const

Returns the left top corner of the content area of the item, in the coordinate system of the BMenu to which it belongs. The content area of an item is the area where it displays its label (or whatever graphic substitutes for the label). It doesn't include the part of the item where a check mark or a keyboard shortcut could be displayed, nor the border and background around the content area.

You would need to call this function only if you're implementing a DrawContent() function to draw the contents of the menu item (likely something other than a label). The content rectangle can be calculated from the point returned by this function and the size specified by GetContentSize().

If the item isn't part of a menu, the return value is indeterminate.

See also: GetContentSize() , DrawContent()


Draw(), DrawContent()

protected:

      virtual void Draw(void)

      virtual void DrawContent(void)

These functions draw the menu item and highlight it if it's currently selected. They're called by the Draw() function of the BMenu where the item is located whenever the menu is required to display itself; they don't need to be called from within application code.

However, they can both be overridden by derived classes that display something other than a textual label. The Draw() function is called first. It draws the background for the entire item, then calls DrawContent() to draw the label within the item's content area. After DrawContent() returns, it draws the check mark (if the item is currently marked) and the keyboard shortcut (if any). It finishes by calling Highlight() if the item is currently selected.

Both functions draw by calling functions of the BMenu in which the item is located. For example:

   void MyItem::DrawContent()
   {
       . . .
       Menu()->DrawBitmap(image);
       . . .
   }

A derived class can override either Draw(), if it needs to draw the entire item, or DrawContent(), if it needs to draw only within the content area. A Draw() function can find the frame rectangle it should draw within by calling the BMenuItem's Frame() function; a DrawContent() function can calculate the content area from the point returned by ContentLocation() and the dimensions provided by GetContentSize().

When DrawContent() is called, the pen is positioned to draw the item's label and the high color is appropriately set. The high color may be a shade of gray, if the item is disabled, or black if it's enabled. If some other distinction is used to distinguish disabled from enabled items, DrawContent() should check the item's current state by calling IsEnabled().

Note


: If a derived class implements its own DrawContent() function, but still wants to draw a textual string, it should do so by assigning the string as the BMenuItem's label and calling the inherited version of DrawContent(), not by calling DrawString(). This preserves the BMenuItem's ability to display a trigger character in the string.

See also: Highlight(), Frame(), ContentLocation(), GetContentSize()


Frame()

      BRect Frame(void) const

Returns the rectangle that frames the entire menu item, in the coordinate system of the BMenu to which the item belongs. If the item hasn't been added to a menu, the return value is indeterminate.

See also: BMenu::AddItem()


GetContentSize()

protected:

      virtual void GetContentSize(float *width, float *height)

Writes the size of the item's content area into the variables referred to by width and height. The content area of an item is the area where its label (or whatever substitutes for the label) is drawn.

A BMenu calls GetContentSize() for each of its items as it arranges them in a column or a row; the function is not called for items in a matrix. The information it provides helps determine where each item is located and the overall size of the menu.

GetContentSize() must report a size that's large enough to display the content of the item (and separate one item from another). By default, it reports an area just large enough to display the item's label. This area is calculated from the label and the BMenu's current font.

If you design a class derived from BMenuItem and implement your own Draw() or DrawContent() function, you should also implement a GetContentSize() function to report how much room will be needed to draw the item's contents.

See also: DrawContent() , ContentLocation()


Highlight()

protected:

      virtual void Highlight(bool flag)

Highlights the menu item when flag is TRUE, and removes the highlighting when flag is FALSE. Highlighting simply inverts all the colors in the item's frame rectangle (except for the check mark).

This function is called by the Draw() function whenever the item is selected and needs to be drawn in its highlighted state. There's no reason to call it yourself, unless you define your own version of Draw(). However, it can be reimplemented in a derived class, if items belonging to that class need to be highlighted in some way other than simple inversion.

See also: Draw()


IsEnabled() see SetEnabled()


isMarked() see SetMarked()


IsSelected()

protected:

      bool IsSelected(void) const

Returns TRUE if the menu item is currently selected, and FALSE if not. Selected items are highlighted.


Label() see SetLabel()


Menu()

      BMenu *Menu(void) const

Returns the menu where the item is located, or NULL if the item hasn't yet been added to a menu.

See also: BMenu::AddItem()


Message() see SetMessage()


SetEnabled(), IsEnabled()

      virtual void SetEnabled(bool enabled)
      bool IsEnabled(void) const

SetEnabled() enables the BMenuItem if the enabled flag is TRUE, disables it if enabled is FALSE, and updates the item if it's visible on-screen. If the item controls a submenu, this function calls the submenu's SetEnabled() virtual function, passing it the same flag. This ensures that the submenu is enabled or disabled as well.

IsEnabled() returns TRUE if the BMenuItem is enabled, its menu is enabled, and all menus above it in the hierarchy are enabled. It returns FALSE if the item is disabled or any objects above it in the menu hierarchy are disabled.

Items and menus are enabled by default.

When using these functions, keep in mind that:

See also: BMenu::SetEnabled()


SetLabel(), Label()

      virtual void SetLabel(const char *string)
      const char *Label(void) const

SetLabel() frees the item's current label and copies string to replace it. If the menu is visible on-screen, it will be redisplayed with the item's new label. If necessary, the menu will become wider (or narrower) so that it fits the new label.

The Interface Kit calls this virtual function to:

Label() returns a pointer to the current label.

See also: BMenu::SetLabelFromMarked(), the BMenuItem constructor


SetMarked(), IsMarked()

      virtual void SetMarked(bool flag)
      bool IsMarked(void) const

SetMarked() adds a check mark to the left of the item label if flag is TRUE, or removes an existing mark if flag is FALSE. If the menu is visible on-screen. it's redisplayed with or without the mark.

IsMarked() returns whether the item is currently marked.

See also: BMenu::SetLabelFromMarked(), BMenu::FindMarked()


SetMessage(), Message(), Command()

      virtual void SetMessage(BMessage *message)
      BMessage *Message(void) const
      ulong Command(void) const

SetMessage() makes message the model BMessage for the menu item, deleting any previous message assigned to the item. The model message is first set by the BMenuItem constructor; SetMessage() allows you to change the message in midstream. You might need to change it, for example, when the item's label changes. Passing a NULL message frees the current model BMessage object without replacing it.

When a menu item is invoked, its model message is copied, relevant information is added to the copy, and the copy is posted so that it will be dispatched to the target BHandler. (The information that gets added to the copy is described under the BMenuItem constructor.)

Message() returns a pointer to the BMenuItem's model message and Command() returns its what data member. If the BMenuItem doesn't post a message, both functions return NULL.

The BMessage that Message() returns belongs to the BMenuItem. You can modify it by adding and removing data, but you shouldn't delete it or do anything that will cause it to be deleted. In particular, you shouldn't post or send the message anywhere, since that would transfer ownership to a message loop and subject the message to automatic deletion.

It's possible to set and return a model BMessage for a separator item. However, the message will never be used.

See also: the BMenuItem constructor, SetTarget()


SetShortcut(), Shortcut()

      virtual void SetShortcut(char shortcut, ulong modifiers) 
      char Shortcut(ulong *modifiers = NULL) const

SetShortcut() sets the shortcut character that's displayed at the right edge of the menu item and the set of modifiers that are associated with the character. These two arguments work just like the arguments passed to the BMenuItem constructor. See the constructor for a more complete description.

Shortcut() returns the character that's used as the keyboard shortcut for invoking the item, and writes a mask of all the modifier keys the shortcut requires to the variable referred to by modifiers. Since the Command key is required to operate the keyboard shortcut for any menu item, B_COMMAND_KEY will always be part of the modifiers mask. The mask can also be tested against the B_CONTROL_KEY , B_OPTION_KEY , and B_SHIFT_KEY constants.

The shortcut is initially set by the BMenuItem constructor.

See also: the BMenuItem constructor


SetTarget(), Target()

      virtual long SetTarget(BHandler *target)
      virtual long SetTarget(BLooper *target, bool targetsPreferredHandler)

      BHandler *Target(BLooper **looper = NULL) const

These functions set and return the object that's targeted to handle messages posted by the BMenuItem.

The version of SetTarget() that takes a single argument sets the target BHandler object. It's successful only if it can also discern a BLooper object where the BMenuItem can post messages so that they will be dispatched to that target. To post a message, the BMenuItem calls the BLooper's PostMessage() function and names the target as the object that should receive the message:

   theLooper->PostMessage(theMessage, target);

Therefore, the target BHandler must be able, through its Looper() function, to reveal the BLooper object with which it is associated. It can do so if:

Once it becomes the BMenuItem's target, the BHandler must maintain its association with the BLooper. If it moves to another BLooper, PostMessage() will fail.

The version of SetTarget() that takes two arguments sets the BLooper object where the BMenuItem should post messages. If the targetsPreferredHandler flag is FALSE, messages will be targeted to the looper object itself --it will act both as BLooper and BHandler. In other words, passing a BLooper and FALSE to the version of SetTarget() that takes two arguments accomplishes the same thing as simply passing the BLooper alone to the version that takes one argument. These two lines of code have the same result:

   myItem->SetTarget(someLooper, FALSE);
   myItem->SetTarget(someLooper);

The two-argument version of SetTarget() becomes interesting only if the targetsPreferredHandler flag is TRUE. In this case, messages are targeted to the looper's preferred handler (the object returned by its PreferredHandler() function). This permits the targeting decision to be made dynamically, when the user invokes the item:

   looper->PostMessage(theMessage, looper->PreferredHandler());

For example, the preferred handler for a BWindow object is the current focus view. Therefore, by passing a BWindow looper and TRUE to SetTarget(),

   myItem->SetTarget(someWindow, TRUE);

the menu item can be targeted to whatever BView happens to be in focus at the time the user operates the menu. This is useful for items--like Cut, Copy, and Paste--that act on the current selection. (Note, however, that if the looper's PreferredHandler() is NULL, the BLooper itself becomes the target, just as it would if the targetsPreferredHandler flag were FALSE.)

When successful, SetTarget() returns B_NO_ERROR . It fails and returns B_BAD_VALUE if the proposed target or looper is NULL. The one-argument version also returns B_BAD_VALUE if it can't discover a BLooper from the proposed target.

Target() returns the current target and, if a pointer to a looper is provided, fills in the BLooper where the BMenuItem will post messages. If the target BHandler is the preferred handler of the looper, Target() returns NULL . In other words, passing a BLooper and TRUE to SetTarget() causes Target() to report that there is a looper , but a NULL target; the BLooper is known, but the target BHandler is not. Passing a BLooper and FALSE to SetTarget() causes Target() to report that the same object is both looper and target.

By default, the BLooper and BHandler roles are both filled by the BWindow at the root of the menu hierarchy (the BWindow where the menu bar is located). These defaults are established when the BMenuItem becomes part of a menu hierarchy that's rooted in a window, but only if another target (or looper) hasn't already been set. If a target hasn't been set and the BMenuItem isn't part of a rooted menu hierarchy, Target() returns NULL.

See also: BView::Looper(), BWindow::PreferredHandler()


SetTrigger(), Trigger()

      virtual void SetTrigger(char trigger)
      char Trigger(void) const

SetTrigger() sets the trigger character that the user can type to invoke the item while the item's menu is open on-screen. If a trigger is not set, the Interface Kit will select one for the item, so it's not necessary to call SetTrigger().

The character passed to this function has to match a character displayed in the item --either the keyboard shortcut or a character in the label. The case of the character doesn't matter; lowercase arguments will match uppercase characters in the item and uppercase arguments will match lowercase characters. When the item can be invoked by its trigger, the trigger character is underlined.

If more than one character in the item matches the character passed, SetTrigger() tries first to mark the keyboard shortcut. Failing that, it tries to mark an uppercase letter at the beginning of a word. Failing that, it marks the first instance of the character in the label.

If the trigger doesn't match any characters in the item, the item won't have a trigger, not even one selected by the system.

Trigger() returns the character set by SetTrigger() , or NULL if SetTrigger() didn't succeed or if SetTrigger() was never called and the trigger is selected automatically.

See also: BMenu::SetTriggersEnabled()


Shortcut() see SetShortcut()


Submenu()

      BMenu *Submenu(void) const

Returns the BMenu object that the item controls, or NULL if the item doesn't control a submenu.

See also: the BMenuItem constructor, the BMenu class


Target() see SetTarget()


Trigger() see SetTrigger()






The Be Book, HTML Edition, for Developer Release 8 of the Be Operating System.

Copyright © 1996 Be, Inc. All rights reserved.

Be, the Be logo, BeBox, BeOS, BeWare, and GeekPort are trademarks of Be, Inc.

Last modified September 6, 1996.