Parent document is top of "Motif FAQ (Part 5 of 9)"
Previous document is "137) TOPIC: PUSHBUTTON WIDGET"
Next document is "139) TOPIC: TOGGLEBUTTON WIDGET"
138) Why can't I use accelerators on buttons not in a menu?
[Last modified: Sept 94]
Answer:
It is apparently a difficult feature to implement, but OSF are considering
this for the future. It is problematic trying to use the Xt accelerators since
the Motif method interferes with this. one workaround suggested is to
duplicate your non-menu button by a button in a menu somewhere, which does
have a menu-accelerator installed. When the user invokes what they think is
the accelerator for the button they can see Motif actually invokes the button
on the menu that they can't see at the time. Another method is described below
and was contributed by Harald Albrecht of Institute of Geometry and Practical
Mathematics Rhine Westphalia Technical University Aachen (RWTH Aachen),
Germany
albrecht@igpm.rwth-aachen.de wrote (Jul 8, 1993):
NOTE: Pointers to a more recent solution by the same author follow this code
sample.
My work-around of this problem looks like this: (I've written that code for a
Motif Object Library in C++ so please forgive me for being object orientated!)
The hack consists of a rewritten message loop which checks for keypresses
<MAlt>+<key>. If MessageLoop() finds such a keypress HandleAcc() ist called
and the widget tree is searched for a suitable widget with the right mnemonic.
// --------------------------------------------------------------------------
// traverse the widget tree starting with the given widget.
//
BOOL TraverseWidgetTree(Widget w, char *pMnemonic, XKeyEvent *KeyEvent)
{
Widget wChild;
WidgetList ChildList;
int NumChilds, Child;
KeySym LabelMnemonic;
char *pMnemonicString;
// Check if the widget is a subclass of label -- then it may have an
// accelerator attached...
if ( XtIsSubclass(w, xmLabelWidgetClass) ) {
// ok. Now: get the widget's mnemonic, convert it to ASCII and compare
// it with the Key we're looking for.
XtVaGetValues(w, XmNmnemonic, &LabelMnemonic, NULL);
pMnemonicString = XKeysymToString(LabelMnemonic);
if ( pMnemonicString &&
(strcasecmp(pMnemonicString, pMnemonic) == 0) ) {
// stimulate the keypress
XmProcessTraversal((Widget)w, XmTRAVERSE_CURRENT);
KeyEvent->type = KeyPress;
KeyEvent->window = XtWindow(w);
KeyEvent->subwindow = XtWindow(w);
KeyEvent->state = 0;
KeyEvent->keycode =
XKeysymToKeycode(XtDisplay(w), XK_space);
XSendEvent(XtDisplay(w), XtWindow(w),
True,
ButtonPressMask, (XEvent*) KeyEvent);
KeyEvent->type = KeyRelease;
XSendEvent(XtDisplay(w), XtWindow(w),
True,
ButtonReleaseMask, (XEvent*) KeyEvent);
return True;
}
}
// if this widget is a subclass of Composite check all the widget's
// childs.
if ( XtIsSubclass(w, compositeWidgetClass) ) {
// if we're in a menu (or something like that) forget this leaf of the
// widget tree!
if ( XtIsSubclass(w, xmRowColumnWidgetClass) ) {
unsigned char RowColumnType;
XtVaGetValues(w, XmNrowColumnType, &RowColumnType, NULL);
if ( RowColumnType != XmWORK_AREA ) return False;
}
XtVaGetValues(w, XmNchildren, &ChildList,
XmNnumChildren, &NumChilds, NULL);
for ( Child = 0; Child < NumChilds; ++Child ) {
wChild = ChildList[Child];
if ( TraverseWidgetTree(wChild, pMnemonic, KeyEvent) )
return True;
}
}
return False;
} // TraverseWidgetTree
// --------------------------------------------------------------------------
// handle accelerators (keypress MAlt + key)
//
#define MAX_MAPPING 10
BOOL HandleAcc(Widget w, XEvent *event)
{
Widget widget, OldWidget;
static char keybuffer[MAX_MAPPING];
int CharCount;
static XComposeStatus composeStatus;
// convert KeyPress to ASCII
CharCount = XLookupString((XKeyEvent*) event,
keybuffer, sizeof(keybuffer),
NULL, &composeStatus);
keybuffer[CharCount] = 0;
// Only one char is alright -- then search the widget tree for a widget
// with the right mnemonic
if ( CharCount == 1 ) {
keybuffer[0] = tolower(keybuffer[0]);
widget = w;
while ( (widget != NULL) &&
!XtIsSubclass(widget, shellWidgetClass) ) {
OldWidget = widget; widget = XtParent(widget);
}
if ( !widget ) widget = OldWidget;
return TraverseWidgetTree(widget,
keybuffer, (XKeyEvent*) event);
}
return False; // no-one found.
} // HandleAcc
// --------------------------------------------------------------------------
// modified message loop
// loops until the Boolean pFlag points to is set to False
void MessageLoop(Boolean *pFlag)
{
XEvent nextEvent;
while ( *pFlag ) {
if ( XtAppPending(AppContext) ) {
XtAppNextEvent(AppContext, &nextEvent);
if ( nextEvent.type == KeyPress ) {
// Falls es ein Tastendruck ist, bei dem auch noch die ALT-Taste
// (=Modifier 1) gedrueckt ist, koennte es ein Accelerator sein!
if ( nextEvent.xkey.state & Mod1Mask )
if ( HandleAcc(XtWindowToWidget(nextEvent.xkey.display,
nextEvent.xkey.window),
&nextEvent) )
continue; // Mitteilung konnte ausgeliefert werden
// und darf daher nicht den ueblichen
// Weg gehen!
}
XtDispatchEvent(&nextEvent);
}
}
} // TApplication::MessageLoop
Harald Albrecht albrecht@igpm.rwth-aachen.de Institute of Geometry and
Practical Mathematics Rhine Westphalia Technical University Aachen (RWTH
Aachen), Germany
NOTE: Harald Albrecht has re-designed his solution so that you can assign
hotkeys to *every* widget by placing a label near that widget. Get the code
from:
ftp.informatik.rwth-aachen.de (137.226.112.172)
in: /pub/packages/Mnemonic/Mnemonic.tar.gz
or from the WWW:
file://134.130.161.30/arc/pub/unix/html/motifcorner.html
Parent document is top of "Motif FAQ (Part 5 of 9)"
Previous document is "137) TOPIC: PUSHBUTTON WIDGET"
Next document is "139) TOPIC: TOGGLEBUTTON WIDGET"