5 #include <X11/StringDefs.h>
6 #include <X11/IntrinsicP.h>
11 #include <Xm/BulletinB.h>
12 #include <Xm/BulletinBP.h>
17 #include <Xm/PushBG.h>
18 #include <Xm/CascadeB.h>
19 #include <Xm/ToggleB.h>
20 #include <Xm/ToggleBG.h>
21 #include <Xm/RowColumn.h>
22 #include <Xm/RowColumnP.h>
23 #include <Xm/Separator.h>
24 #include <Xm/Traversal.h>
27 static char rcsid[] = "$Header$";
30 #define MAX(a,b) ((a > b) ? a : b)
33 #define MIN(a,b) ((a < b) ? a : b)
42 Widget BuildMenuTree();
43 Widget MakeRadioField();
44 int button_callback();
45 void radio_callback();
46 void string_callback();
47 void boolean_callback();
51 EntryForm *WidgetToForm();
53 extern void UpdateForm();
54 extern int PopupErrorMessage();
55 extern void PopupHelpWindow();
56 extern int AppendToLog();
57 extern void MakeWatchCursor();
58 extern void MakeNormalCursor();
59 extern Widget SetupLogWidget();
61 static XtActionsRec myactions[] = {
62 { "MoiraFocusOut", MoiraFocusOut },
67 manage_widget(w, widget, call_data)
69 XmAnyCallbackStruct *call_data;
71 XtManageChild(widget);
75 button_callback(w, client_data, call_data)
77 EntryForm *client_data;
78 XmAnyCallbackStruct *call_data;
80 XtUnmanageChild(client_data->formpointer);
85 BuildMenuTree(topW, spec)
89 return (CreateMenu(topW, spec->submenu, XmHORIZONTAL));
93 ** Read the specification and put up a menu to match...
97 CreateMenu(parent, spec, orientation)
103 MenuItem *curmenuitem;
104 Widget childbutton, childmenu;
107 XmString label; /* !@#$%^ compound string required */
109 label = XmStringCreate( "Complete junk", XmSTRING_DEFAULT_CHARSET);
112 XtSetArg(wargs[n], XmNlabelString, label); n++;
114 if (orientation == XmHORIZONTAL) {
115 XtSetArg(wargs[n], XmNspacing, 5); n++;
116 menuparent = XmCreateMenuBar( parent, "randommenu",
118 XtManageChild(menuparent);
121 menuparent = XmCreatePulldownMenu(parent, "randommenu",
124 for ( curmenuitem = (*spec);
126 spec++, curmenuitem = (*spec)) {
129 printf ("Making entry: %s\n", curmenuitem->label);
131 label = XmStringCreate( curmenuitem->label,
132 XmSTRING_DEFAULT_CHARSET);
134 XtSetArg(wargs[n], XmNlabelString, label); n++;
135 if (curmenuitem->accel) {
136 XtSetArg(wargs[n], XmNmnemonic, *(curmenuitem->accel)); n++;
139 if (curmenuitem->submenu) {
141 printf ("It has a submenu, which I'm recursing on...\n");
143 childmenu = CreateMenu( menuparent,
144 curmenuitem->submenu,
146 XtSetArg(wargs[n], XmNsubMenuId, childmenu); n++;
148 childbutton = XtCreateManagedWidget( "child",
149 xmCascadeButtonWidgetClass,
150 menuparent, wargs, n);
155 childbutton = XtCreateManagedWidget( "child",
156 xmPushButtonGadgetClass,
157 menuparent, wargs, n);
159 XtAddCallback( childbutton,
161 menu_callback, curmenuitem);
169 ** Read the specification and put up a form to match...
173 CreateForm(parent, spec)
180 XmString label; /* compound string required */
181 Dimension height_so_far = 0, width_so_far = 0;
182 Dimension height, width;
183 Widget titleW, instructionW;
187 if (spec->formpointer) {
189 return(spec->formpointer);
192 height_so_far = vpad;
194 #define GETSIZE(foo) n = 0; \
195 XtSetArg(wargs[n], XtNwidth, &width); n++; \
196 XtSetArg(wargs[n], XtNheight, &height); n++; \
197 XtGetValues (foo, wargs, n); \
199 #define STORESIZE if (width > width_so_far) width_so_far = width;\
200 height_so_far += height + vpad;
204 XtSetArg(wargs[n], XmNautoUnmanage, False); n++;
205 bb = XmCreateBulletinBoardDialog(parent, spec->formname, wargs, n);
206 MapWidgetToForm(bb, spec);
208 spec->formpointer = bb;
211 label = XmStringCreate(spec->formname, XmSTRING_DEFAULT_CHARSET);
213 XtSetArg(wargs[n], XmNlabelString, label); n++;
214 XtSetArg(wargs[n], XtNx, 0); n++;
215 XtSetArg(wargs[n], XtNy, 0); n++;
216 titleW = XtCreateManagedWidget( "title",
223 label = XmStringCreate(spec->instructions, XmSTRING_DEFAULT_CHARSET);
225 XtSetArg(wargs[n], XmNlabelString, label); n++;
226 XtSetArg(wargs[n], XtNx, 0); n++;
227 XtSetArg(wargs[n], XtNy, height_so_far); n++;
228 instructionW = XtCreateManagedWidget( "instructions",
231 GETSIZE(instructionW);
234 height = height_so_far;
235 width = width_so_far;
236 MakeInputLines(bb, &height, &width, spec);
239 height = height_so_far;
240 width = width_so_far;
241 MakeButtons(bb, &height, &width, spec);
245 ** Center the title of the form
249 XtSetArg(wargs[n], XtNwidth, &width); n++;
250 XtGetValues (titleW, wargs, n);
252 x = (width_so_far - width) / 2;
255 XtSetArg(wargs[n], XtNx, x); n++;
256 XtSetValues (titleW, wargs, n);
259 XtSetArg(wargs[n], XtNwidth, &width); n++;
260 XtGetValues (instructionW, wargs, n);
262 x = (width_so_far - width) / 2;
265 XtSetArg(wargs[n], XtNx, x); n++;
266 XtSetValues (instructionW, wargs, n);
272 ** Pheight and pwidth start with the values-to-date of the bboard so far.
273 ** Return your height and width in them when you're done.
275 ** Positioning the widgets happens in two phases:
276 ** First, we set their y-positions as we create them.
277 ** After they're created, we go back and adjust the x-positions
278 ** according to the widest left side noted.
281 MakeInputLines(parent, pheight, pwidth, spec)
288 XmString label; /* compound string required */
292 Dimension width, height, maxleftwidth = 0, maxrightwidth = 0;
293 Dimension localy, leftheight = 0, rightheight = 0;
294 UserPrompt **myinputlines = spec->inputlines;
297 static XtTranslations trans = NULL;
299 for ( current = (*myinputlines), localy = 0, i = 0;
301 myinputlines++, current = (*myinputlines), i++) {
305 printf ("Making entry %d: %s of type %d\n",
306 i, current->prompt, current->type);
309 ** First, make the prompt
311 if (current->type == FT_KEYWORD) {
314 p = index(current->prompt, '|');
317 current->keyword_name = p;
321 label = XmStringCreate( current->prompt,
322 XmSTRING_DEFAULT_CHARSET);
324 XtSetArg(wargs[n], XmNlabelString, label); n++;
325 XtSetArg(wargs[n], XtNy, localy + *pheight); n++;
326 child = XtCreateManagedWidget( "prompt",
332 if (width > maxleftwidth)
333 maxleftwidth = width;
335 if (current->type == FT_KEYWORD && current->keyword_name) {
336 label = XmStringCreate("add new value",
337 XmSTRING_DEFAULT_CHARSET);
339 XtSetArg(wargs[n], XmNlabelString, label); n++;
340 XtSetArg(wargs[n], XtNy, localy + *pheight + height); n++;
341 XtSetArg(wargs[n], XtNx, height); n++;
342 child = XtCreateManagedWidget("newvalue",
343 xmPushButtonWidgetClass,
345 XtAddCallback(child, XmNactivateCallback,
349 leftheight += height;
350 if (width + height > maxleftwidth)
351 maxleftwidth = width + height;
354 ** Second, make the input widget
357 XtSetArg(wargs[n], XtNy, localy + *pheight); n++;
358 XtSetArg(wargs[n], XmNtraversalOn, True); n++;
359 XtSetArg(wargs[n], XtNsensitive,
360 !(current->insensitive)); n++;
361 switch (current->type) {
363 children[i] = XtCreateManagedWidget( "textwidget",
366 XtAddCallback( children[i], XmNvalueChangedCallback,
367 string_callback, current);
369 XtAppAddActions(XtWidgetToApplicationContext(children[i]),
370 myactions, XtNumber(myactions));
372 trans = XtParseTranslationTable(resources.text_trans);
374 XtOverrideTranslations(children[i], trans);
375 if (current->returnvalue.stringvalue) {
376 XmTextSetString (children[i], current->returnvalue.stringvalue);
378 GETSIZE (children[i]);
379 rightheight = height;
380 if (width > maxrightwidth)
381 maxrightwidth = width;
385 XtSetArg(wargs[n], XmNset,
386 current->returnvalue.booleanvalue ? True : False); n++;
388 if (current->returnvalue.booleanvalue)
389 label = XmStringCreate( "(True)", XmSTRING_DEFAULT_CHARSET);
391 label = XmStringCreate( "(False)", XmSTRING_DEFAULT_CHARSET);
392 XtSetArg(wargs[n], XmNlabelString, label); n++;
394 children[i] = XtCreateManagedWidget( "ignore this",
395 xmToggleButtonWidgetClass,
398 XtAddCallback( children[i], XmNvalueChangedCallback,
399 boolean_callback, current);
401 GETSIZE (children[i]);
402 rightheight = height;
403 if (width > maxrightwidth)
404 maxrightwidth = width;
409 MakeRadioField(parent, current,
411 XtManageChild(children[i]);
412 XtSetValues(children[i], wargs, n);
413 GETSIZE (children[i]);
414 if (width > maxrightwidth)
415 maxrightwidth = width;
419 printf ("Sorry, don't recognize that type\n");
422 XmAddTabGroup(children[i]);
423 MapWidgetToForm(children[i], spec);
424 current->parent = (caddr_t) spec;
426 current->mywidget = children[i];
428 localy += MAX(rightheight, leftheight) + vpad;
432 ** Now slide the input widgets right as far as the widest prompt.
435 XtSetArg(wargs[n], XtNx, maxleftwidth + hpad); n++;
437 XtSetValues (children[i - 1], wargs, n);
439 *pheight = localy - vpad;
440 *pwidth = maxleftwidth + maxrightwidth + hpad;
444 ** All the junk about keeping track of the sum of the children's heights
445 ** is because the !#$% RowColumn widget doesn't sum them for us, NOR
446 ** does it accept SetValues on its XtNHeight! Thanks, Motif!
450 MakeRadioField(parent, prompt, pheight, spec)
456 Widget radioparent, child = NULL;
460 XmString label; /* accursed compound string required */
461 Dimension height, width;
462 char **keywords, *null[2];
464 if (!prompt->keywords) {
465 fprintf (stderr, "Warning: No list of keywords for widget\n");
466 prompt->keywords = null;
469 for ( count = 0, keywords = prompt->keywords;
471 keywords++, count++);
474 ** Although the XmNnumColumns resource is documented as actually
475 ** representing the number of _rows_ when XmNorientation is set to XmVERTICAL,
476 ** it doesn't. So I need to count the items myself and manually set the
477 ** number of columns to get a maximum of five rows. There's no XmNnumRows
478 ** resource. Thanks, Motif!
482 XtSetArg(wargs[n], XmNspacing, 0); n++;
485 XtSetArg(wargs[n], XmNnumColumns, 1 + (count-1) / 5); n++;
486 XtSetArg(wargs[n], XmNorientation, XmVERTICAL); n++;
487 XtSetArg(wargs[n], XmNpacking, XmPACK_COLUMN); n++;
489 radioparent = XmCreateRadioBox(parent, "radio", wargs, n);
491 keywords = prompt->keywords;
492 for (current=(*keywords); current; keywords++, current=(*keywords)) {
494 label = XmStringCreate(current, XmSTRING_DEFAULT_CHARSET);
495 XtSetArg(wargs[n], XmNlabelString, label); n++;
496 if ((prompt->returnvalue.stringvalue) &&
497 (!strcmp (current, prompt->returnvalue.stringvalue))) {
498 XtSetArg(wargs[n], XmNset, True); n++;
501 XtSetArg(wargs[n], XmNset, False); n++;
503 child = XtCreateManagedWidget( current,
504 xmToggleButtonWidgetClass,
505 radioparent, wargs, n);
506 MapWidgetToForm(child, spec);
508 XtAddCallback( child, XmNvalueChangedCallback,
509 radio_callback, prompt);
513 ** Assume all child widgets are the same height. Increase height by
514 ** five times this, or the actual number of children, whichever is lesser.
521 *pheight = (height * MIN(5, count)) + vpad;
527 /* This is called when the list of keywords changes. The old radio box
528 * will be destroyed and a new one created.
531 RemakeRadioField(form, field)
535 Dimension x, y, parent_y, oldheight, newheight;
538 XmBulletinBoardWidget bb;
539 XmRowColumnWidget rc;
540 static XtTranslations trans = NULL;
541 extern char form_override_table[];
544 XtSetArg(wargs[0], XtNx, &x);
545 XtSetArg(wargs[1], XtNy, &y);
546 XtSetArg(wargs[2], XtNheight, &oldheight);
547 XtGetValues(form->inputlines[field]->mywidget, wargs, 3);
548 XtUnmanageChild(form->inputlines[field]->mywidget);
549 form->inputlines[field]->mywidget = w =
550 MakeRadioField(form->formpointer, form->inputlines[field],
552 XtSetArg(wargs[0], XtNx, x);
553 XtSetArg(wargs[1], XtNy, y);
554 XtSetValues(w, wargs, 2);
555 MapWidgetToForm(w, form);
557 if (newheight > oldheight) {
558 bb = (XmBulletinBoardWidget) form->formpointer;
560 for (i = 0; i < bb->composite.num_children; i++) {
561 XtSetArg(wargs[0], XtNy, &y);
562 XtGetValues(bb->composite.children[i], wargs, 1);
564 y = (y + newheight) - oldheight;
565 XtSetArg(wargs[0], XtNy, y);
566 XtSetValues(bb->composite.children[i], wargs, 1);
572 trans = XtParseTranslationTable(resources.form_trans);
573 XtOverrideTranslations(w, trans);
574 rc = (XmRowColumnWidget) w;
575 for (i = 0; i < rc->composite.num_children; i++)
576 XtOverrideTranslations(rc->composite.children[i], trans);
582 MakeButtons(parent, pheight, pwidth, spec)
588 BottomButton *current;
589 XmString label; /* compound string required */
592 Dimension newwidth, width = 25;
594 BottomButton **buttons = spec->buttons;
599 XtSetArg(wargs[n], XtNy, *pheight); n++;
600 XtSetArg(wargs[n], XtNx, 0); n++;
601 XtSetArg(wargs[n], XtNwidth, *pwidth); n++;
602 XtCreateManagedWidget( "separator",
603 xmSeparatorWidgetClass,
607 for ( current=(*buttons);
609 buttons++, current=(*buttons)) {
612 printf ("Making a button labeled %s\n", current->label);
614 label = XmStringCreate( current->label,
615 XmSTRING_DEFAULT_CHARSET);
617 XtSetArg(wargs[n], XtNy, (*pheight)); n++;
618 XtSetArg(wargs[n], XtNx, width); n++;
619 XtSetArg(wargs[n], XmNlabelString, label); n++;
621 newbutton = XtCreateManagedWidget( current->label,
622 xmPushButtonWidgetClass,
625 XtAddCallback(newbutton, XmNactivateCallback,
626 (XtCallbackProc) current->returnfunction,
629 XtSetArg(wargs[n], XtNwidth, &newwidth); n++;
630 XtGetValues (newbutton, wargs, n);
632 width += (newwidth + hpad);
639 radio_callback(w, client_data, call_data)
641 XmAnyCallbackStruct *client_data;
642 XmAnyCallbackStruct *call_data;
648 UserPrompt *prompt = (UserPrompt *) client_data;
651 XtSetArg(wargs[n], XmNset, &is_set); n++;
652 XtGetValues (w, wargs, n);
658 ** Since Motif insists on using !@#$% Compound Strings as the text for
659 ** its label widgets, but doesn't provide a way of getting a char* back
660 ** from a !@#$% Compound String, I can't retrieve the label of the button
663 ** Fortunately, I was smart enough to use the button label as the name
664 ** of the widget, and I can extract it via XtName(). Thanks, Motif!
666 if (prompt->returnvalue.stringvalue &&
667 (strcmp(prompt->returnvalue.stringvalue, XtName(w)))) {
668 strcpy(prompt->returnvalue.stringvalue, XtName(w));
669 if (prompt->valuechanged)
670 (*prompt->valuechanged)(WidgetToForm(w), prompt);
676 boolean_callback(w, client_data, call_data)
678 XmAnyCallbackStruct *client_data;
679 XmAnyCallbackStruct *call_data;
684 UserPrompt *current = (UserPrompt *)client_data;
688 XtSetArg(wargs[n], XmNset, &is_set); n++;
689 XtGetValues (w, wargs, n);
691 current->returnvalue.booleanvalue = is_set;
694 label = XmStringCreate( "(True)", XmSTRING_DEFAULT_CHARSET);
696 label = XmStringCreate( "(False)", XmSTRING_DEFAULT_CHARSET);
698 XtSetArg(wargs[n], XmNlabelString, label); n++;
699 XtSetValues (w, wargs, n);
701 if (current->valuechanged)
702 (*current->valuechanged)(WidgetToForm(w), current);
705 printf ("boolean_callback: button %x is %s\n",
706 w, (is_set ? "True" : "False"));
711 menu_callback(w, client_data, call_data)
713 XmAnyCallbackStruct *client_data;
714 XmAnyCallbackStruct *call_data;
716 MenuItem *itemhit = (MenuItem *) client_data;
718 /* printf ("menu_callback: item '%s', op %d and string '%s'\n",
722 XtManageChild(entryformwidget);
724 MoiraMenuRequest(itemhit);
729 string_callback(w, client_data, call_data)
731 XmAnyCallbackStruct *client_data;
732 XmAnyCallbackStruct *call_data;
734 UserPrompt *current = (UserPrompt *)client_data;
737 newvalue = XmTextGetString(w);
739 if (strcmp(current->returnvalue.stringvalue, newvalue)) {
740 /* printf ("Replacing old value of selection, '%s', with '%s'\n",
741 current->returnvalue.stringvalue,
743 strcpy(current->returnvalue.stringvalue, newvalue);
744 if (current->valuechanged)
745 (*current->valuechanged)(WidgetToForm(w), current);
751 void MoiraFocusOut(w, event, p, n)
758 UserPrompt *current = NULL;
760 XmTextRec *tr = (XmTextRec *)w;
763 if (tr->core.self != w || tr->core.widget_class != xmTextWidgetClass)
765 newvalue = XmTextGetString(w);
767 for (i = 0; f->inputlines[i]; i++)
768 if (f->inputlines[i]->mywidget == w)
769 current = f->inputlines[i];
770 if (current == NULL) {
771 fprintf(stderr, "Couldn't find prompt structure!\n");
775 if (strcmp(current->returnvalue.stringvalue, newvalue)) {
776 strcpy(current->returnvalue.stringvalue, newvalue);
777 if (current->valuechanged)
778 (*current->valuechanged)(f, current);
785 newvalue(w, client_data, call_data)
787 XmAnyCallbackStruct *client_data;
788 XmAnyCallbackStruct *call_data;
790 UserPrompt *current = (UserPrompt *)client_data;
795 if (current->keyword_name == NULL) {
796 PopupErrorMessage("Sorry, that keyword cannot be changed.", NULL);
799 form = (EntryForm *)current->parent;
800 for (i = 0; form->inputlines[i]; i++)
801 if (form->inputlines[i] == current)
803 f = GetAndClearForm("add_new_value");
804 mi.operation = MM_NEW_VALUE;
805 mi.query = "add_alias";
807 mi.form = form->formname;
808 mi.accel = (char *) i;
810 f->extrastuff = current->keyword_name;
815 /* WARNING: This routine uses Motif internal undocumented routines.
816 * It was the only way to get carriage return to Do The Right Thing.
817 * If you are in a single-item tab group, this routine will call
818 * MoiraFormComplete() (same as pressing OK on the bottom of the form).
819 * otherwise, it advances the focus the same as pressing TAB.
822 void EnterPressed(w, event, argv, count)
831 next = _XmFindNextTabGroup(w);
833 MoiraFocusOut(w, event, argv, count);
834 form = WidgetToForm(w);
835 MoiraFormComplete(NULL, form);
837 _XmMgrTraversal(w, XmTRAVERSE_NEXT_TAB_GROUP);
842 void CancelForm(w, event, argv, count)
850 form = WidgetToForm(w);
852 XtUnmanageChild(form->formpointer);
856 void ExecuteForm(w, event, argv, count)
864 form = WidgetToForm(w);
866 MoiraFocusOut(w, event, argv, count);
867 MoiraFormComplete(NULL, form);
872 void DoHelp(w, event, argv, count)
880 form = WidgetToForm(w);
882 help(form->formname);
886 extern struct hash *create_hash();
887 static struct hash *WFmap = NULL;
889 MapWidgetToForm(w, f)
894 WFmap = create_hash(101);
896 hash_store(WFmap, w, f);
899 EntryForm *WidgetToForm(w)
902 return((EntryForm *) hash_lookup(WFmap, w));