Wednesday, February 08, 2006

The above is a screenshot after porting the concrete menu classes to Gtk+.

It's interesting to compare the complexity of dealing with menuitem shortcuts in the Cocoa implementation with the Gtk+ implementation. First, let's look at the Cocoa implementation of the concrete menuitem widget function SetShortcut. It's only argument is an ASCII letter. As was described previously, a lowercase letter is used by Cocoa to indicate a clover-letter shortcut, while an uppercase letter is used to indicate a shift-clover-letter shortcut. Here is the code:

PRStatus CocoaMenuItemImpl::SetShortcut(const string& shortcut)
if (m_menuitem) {
NSString *key = [NSString stringWithCString: shortcut.c_str()];
if (key) {
[m_menuitem setKeyEquivalent: key];
return PR_SUCCESS;
return PR_FAILURE;

The above code is trivial -- create an NSString from the STL string shortcut, and then pass the result to NSMenuItem setKeyEquivalent:.

Now, let's look at the Gtk+ implementation:

PRStatus GtkMenuItemImpl::SetShortcut(const string& shortcut)
string accel;
guint accel_key;
GdkModifierType accel_mods;

accel = "<ctrl>" + shortcut;
gtk_accelerator_parse(accel.c_str(), &accel_key, &accel_mods);

if (!m_accelGroup) {
m_accelGroup = gtk_accel_group_new();

gtk_widget_add_accelerator(m_menuitem, "activate", m_accelGroup,
accel_key, accel_mods, GTK_ACCEL_VISIBLE);

return PR_SUCCESS;

Here, we must create a string that is of the form "<ctrl>letter", where letter is the shortcut specified in markup. This string is the parsed by a Gtk+ function named gtk_accelerator_parse(), which decomposes the string into the key and the X modifier flags that describe what modifier(s) (e.g., Alt, Ctrl) must be pressed, along with the key, to trigger a gtk_signal. The signal in question, ("activate") and the key and modifier(s) combination are mapped to the menuitem widget by calling gtk_widget_add_accelerator(). If needed, an accelerator group object is created prior to this mapping with a call to gtk_accel_group_new(). Effectively, what we are doing is telling the menuitem widget to active itself when the ctrl-key combination is pressed. The activate signal is mapped to a signal handler that is also invoked when the user selects the menuitem with the mouse.