[comp.windows.x] Use of accelerators

rick@cstr.ed.ac.uk (Rick Innis) (06/30/90)

SunOS 4.0.3, gcc 1.37.1, Sun4/330 and Sun3/80

I'm working on an application which uses accelerators to bind events in
TextWidgets to actions in CommandWidgets. However, the accelerations aren't
behaving the way I expect.

The following program creates a Form with two textWidgets and two
CommandWidgets. Given the acclerations I would *expect* that pressing the
return key in 'text1' would call the callback for command1' and pressing the
return key in 'text2' would call the callback for 'command2'. However,
although the first expectation is fulfilled, pressing the return key in
'text2' calls the callback for 'command1'.

Any and all thoughts appreciated.

cut here----------------------------------------------------------------
/* Xtest.c : test program for accelerator behaviour */

#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>

#include <X11/Xaw/AsciiText.h>
#include <X11/Xaw/Form.h>
#include <X11/Xaw/Command.h>

void CreateWidgets(Widget w);
void GetString1(Widget w, caddr_t client_data, caddr_t call_data);
void GetString2(Widget w, caddr_t client_data, caddr_t call_data);

void Open(Widget widget, XEvent *event, String *params, Cardinal *num_params);

void main(argc, argv)
int     argc;
char   *argv[];
{
    Widget toplevel, panel;
    XtAppContext appCon;
    Arg formArgs[] = {
	{ XtNheight, 200 },
	{ XtNwidth, 300 },
    };
    
    static XtActionsRec actiontable[] = {
	{"Open", (XtActionProc) Open},
	{"Nothing",   NULL}
    };

    toplevel = XtAppInitialize(&appCon, "Text", NULL, NULL, &argc, argv,
			       NULL, NULL, NULL);
    panel = XtCreateManagedWidget("Panel", formWidgetClass, toplevel,
				  formArgs, XtNumber(formArgs));
    XtAppAddActions(appCon, actiontable, XtNumber(actiontable));

    CreateWidgets(panel);
    XtRealizeWidget(toplevel);

    XtAppMainLoop(appCon);
}

void CreateWidgets(Widget parent)
{
    Widget form, text1, text2, command1, command2;

    XtCallbackRec commandCallback[] =  {
	{ NULL, NULL },
	{ NULL, NULL }
    };

    String acceleratorTable = 
	"#override\n\
	 Ctrl<Key>J:    Open()\n\
         Ctrl<Key>M:    Open()\n\
	 <Key>Linefeed: Open()\n\
         <Key>Return:   Open()\n";

    XtAccelerators accelerators = XtParseAcceleratorTable(acceleratorTable);

    Arg commandArgs[] = {
	{ XtNcallback, (XtArgVal) commandCallback },
	{ XtNfromHoriz, NULL },
	{ XtNfromVert, NULL },
	{ XtNaccelerators, (XtArgVal) accelerators },
    };
    Arg textArgs[] = {
	{ XtNstring, NULL },
	{ XtNfromHoriz, NULL},
	{ XtNfromVert, NULL},
	{ XtNeditType, (XtArgVal) "edit" },
    };

    XtSetArg(textArgs[0], XtNstring, "string1");
    text1 = XtCreateManagedWidget("tunefile", asciiTextWidgetClass, parent, textArgs,
				  XtNumber(textArgs));

    commandCallback[0].callback = GetString1;
    commandCallback[0].closure = (caddr_t) text1;
    XtSetArg(commandArgs[1], XtNfromHoriz, text1);
    XtSetArg(commandArgs[2], XtNfromVert, NULL);
    command1 = XtCreateManagedWidget("tune", commandWidgetClass, parent, commandArgs,
				     XtNumber(commandArgs));

    XtInstallAccelerators(text1, command1);

    XtSetArg(textArgs[0], XtNstring, "string2");
    XtSetArg(textArgs[2], XtNfromVert, text1);
    text2 = XtCreateManagedWidget("indexfile", asciiTextWidgetClass, parent, textArgs,
				  XtNumber(textArgs));

    commandCallback[0].callback = GetString2;
    commandCallback[0].closure = (caddr_t) text2;
    XtSetArg(commandArgs[1], XtNfromHoriz, text2);
    XtSetArg(commandArgs[2], XtNfromVert, command1);
    command2 = XtCreateManagedWidget("index", commandWidgetClass, parent, commandArgs,
				     XtNumber(commandArgs));
    XtInstallAccelerators(text2, command2);

}

void Open(Widget widget, XEvent *event, String *params, Cardinal *num_params)
{
    XtCallCallbacks((CommandWidget) widget, XtNcallback, NULL);
}

void GetString1(Widget w, caddr_t client_data, caddr_t call_data)
{
    TextWidget text = (TextWidget) client_data;
    String string;
    Arg textArgs[] = {
	{XtNstring, (XtArgVal) &string},
    };

    XtGetValues(text, textArgs, XtNumber(textArgs));
    if(string == NULL) {
	XtWarning("GetString1:Bad Result!");
	return;
    }
    printf("GetString1: %s\n", string);
}

void GetString2(Widget w, caddr_t client_data, caddr_t call_data)
{
    TextWidget text = (TextWidget) client_data;
    String string;
    Arg textArgs[] = {
	{XtNstring, (XtArgVal) &string},
    };

    XtGetValues(text, textArgs, XtNumber(textArgs));
    if(string == NULL) {
	XtWarning("GetString2:Bad Result!");
	return;
    }
    printf("GetString2: %s\n", string);
}

swick@ATHENA.MIT.EDU (Ralph Swick) (06/30/90)

    The following program creates a Form with two textWidgets and two
    CommandWidgets. Given the acclerations I would *expect* that pressing the
    return key in 'text1' would call the callback for command1' and pressing the
    return key in 'text2' would call the callback for 'command2'. However,
    although the first expectation is fulfilled, pressing the return key in
    'text2' calls the callback for 'command1'.

This due to a bug in the implementation of accelerators.  In R4 (and
earlier), parsed accelerator tables are not sharable objects; you need
a separate table for each accelerator source widget instance.  This
is handled automatically if accelerators are specified as resources,
but since you are parsing the table yourself you fall into the trap.

As a work-around, simply call XtParseAcceleratorTable a second time
for 'command2'.

Yes, we intend to fix this bug (soon, in fact :-).