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;
298 #define newtrans "<FocusOut>: focusOut() MoiraFocusOut()"
301 for ( current = (*myinputlines), localy = 0, i = 0;
303 myinputlines++, current = (*myinputlines), i++) {
307 printf ("Making entry %d: %s of type %d\n",
308 i, current->prompt, current->type);
311 ** First, make the prompt
313 if (current->type == FT_KEYWORD) {
316 p = index(current->prompt, '|');
319 current->keyword_name = p;
323 label = XmStringCreate( current->prompt,
324 XmSTRING_DEFAULT_CHARSET);
326 XtSetArg(wargs[n], XmNlabelString, label); n++;
327 XtSetArg(wargs[n], XtNy, localy + *pheight); n++;
328 child = XtCreateManagedWidget( "prompt",
334 if (width > maxleftwidth)
335 maxleftwidth = width;
337 if (current->type == FT_KEYWORD && current->keyword_name) {
338 label = XmStringCreate("add new value",
339 XmSTRING_DEFAULT_CHARSET);
341 XtSetArg(wargs[n], XmNlabelString, label); n++;
342 XtSetArg(wargs[n], XtNy, localy + *pheight + height); n++;
343 XtSetArg(wargs[n], XtNx, height); n++;
344 child = XtCreateManagedWidget("newvalue",
345 xmPushButtonWidgetClass,
347 XtAddCallback(child, XmNactivateCallback,
351 leftheight += height;
352 if (width + height > maxleftwidth)
353 maxleftwidth = width + height;
356 ** Second, make the input widget
359 XtSetArg(wargs[n], XtNy, localy + *pheight); n++;
360 XtSetArg(wargs[n], XmNtraversalOn, True); n++;
361 XtSetArg(wargs[n], XtNsensitive,
362 !(current->insensitive)); n++;
363 switch (current->type) {
365 children[i] = XtCreateManagedWidget( "textwidget",
368 XtAddCallback( children[i], XmNvalueChangedCallback,
369 string_callback, current);
371 XtAppAddActions(XtWidgetToApplicationContext(children[i]),
372 myactions, XtNumber(myactions));
374 trans = XtParseTranslationTable(newtrans);
376 XtOverrideTranslations(children[i], trans);
377 if (current->returnvalue.stringvalue) {
378 XmTextSetString (children[i], current->returnvalue.stringvalue);
380 GETSIZE (children[i]);
381 rightheight = height;
382 if (width > maxrightwidth)
383 maxrightwidth = width;
387 XtSetArg(wargs[n], XmNset,
388 current->returnvalue.booleanvalue ? True : False); n++;
390 if (current->returnvalue.booleanvalue)
391 label = XmStringCreate( "(True)", XmSTRING_DEFAULT_CHARSET);
393 label = XmStringCreate( "(False)", XmSTRING_DEFAULT_CHARSET);
394 XtSetArg(wargs[n], XmNlabelString, label); n++;
396 children[i] = XtCreateManagedWidget( "ignore this",
397 xmToggleButtonWidgetClass,
400 XtAddCallback( children[i], XmNvalueChangedCallback,
401 boolean_callback, current);
403 GETSIZE (children[i]);
404 rightheight = height;
405 if (width > maxrightwidth)
406 maxrightwidth = width;
411 MakeRadioField(parent, current,
413 XtManageChild(children[i]);
414 XtSetValues(children[i], wargs, n);
415 GETSIZE (children[i]);
416 if (width > maxrightwidth)
417 maxrightwidth = width;
421 printf ("Sorry, don't recognize that type\n");
424 XmAddTabGroup(children[i]);
425 MapWidgetToForm(children[i], spec);
426 current->parent = (caddr_t) spec;
428 current->mywidget = children[i];
430 localy += MAX(rightheight, leftheight) + vpad;
434 ** Now slide the input widgets right as far as the widest prompt.
437 XtSetArg(wargs[n], XtNx, maxleftwidth + hpad); n++;
439 XtSetValues (children[i - 1], wargs, n);
441 *pheight = localy - vpad;
442 *pwidth = maxleftwidth + maxrightwidth + hpad;
446 ** All the junk about keeping track of the sum of the children's heights
447 ** is because the !#$% RowColumn widget doesn't sum them for us, NOR
448 ** does it accept SetValues on its XtNHeight! Thanks, Motif!
452 MakeRadioField(parent, prompt, pheight, spec)
458 Widget radioparent, child = NULL;
462 XmString label; /* accursed compound string required */
463 Dimension height, width;
464 char **keywords, *null[2];
466 if (!prompt->keywords) {
467 fprintf (stderr, "Warning: No list of keywords for widget\n");
468 prompt->keywords = null;
471 for ( count = 0, keywords = prompt->keywords;
473 keywords++, count++);
476 ** Although the XmNnumColumns resource is documented as actually
477 ** representing the number of _rows_ when XmNorientation is set to XmVERTICAL,
478 ** it doesn't. So I need to count the items myself and manually set the
479 ** number of columns to get a maximum of five rows. There's no XmNnumRows
480 ** resource. Thanks, Motif!
484 XtSetArg(wargs[n], XmNspacing, 0); n++;
487 XtSetArg(wargs[n], XmNnumColumns, 1 + (count-1) / 5); n++;
488 XtSetArg(wargs[n], XmNorientation, XmVERTICAL); n++;
489 XtSetArg(wargs[n], XmNpacking, XmPACK_COLUMN); n++;
491 radioparent = XmCreateRadioBox(parent, "radio", wargs, n);
493 keywords = prompt->keywords;
494 for (current=(*keywords); current; keywords++, current=(*keywords)) {
496 label = XmStringCreate(current, XmSTRING_DEFAULT_CHARSET);
497 XtSetArg(wargs[n], XmNlabelString, label); n++;
498 if ((prompt->returnvalue.stringvalue) &&
499 (!strcmp (current, prompt->returnvalue.stringvalue))) {
500 XtSetArg(wargs[n], XmNset, True); n++;
503 XtSetArg(wargs[n], XmNset, False); n++;
505 child = XtCreateManagedWidget( current,
506 xmToggleButtonWidgetClass,
507 radioparent, wargs, n);
508 MapWidgetToForm(child, spec);
510 XtAddCallback( child, XmNvalueChangedCallback,
511 radio_callback, prompt);
515 ** Assume all child widgets are the same height. Increase height by
516 ** five times this, or the actual number of children, whichever is lesser.
523 *pheight = (height * MIN(5, count)) + vpad;
529 /* This is called when the list of keywords changes. The old radio box
530 * will be destroyed and a new one created.
533 RemakeRadioField(form, field)
537 Dimension x, y, parent_y, oldheight, newheight;
540 XmBulletinBoardWidget bb;
541 XmRowColumnWidget rc;
542 static XtTranslations trans = NULL;
543 extern char form_override_table[];
546 XtSetArg(wargs[0], XtNx, &x);
547 XtSetArg(wargs[1], XtNy, &y);
548 XtSetArg(wargs[2], XtNheight, &oldheight);
549 XtGetValues(form->inputlines[field]->mywidget, wargs, 3);
550 XtUnmanageChild(form->inputlines[field]->mywidget);
551 form->inputlines[field]->mywidget = w =
552 MakeRadioField(form->formpointer, form->inputlines[field],
554 XtSetArg(wargs[0], XtNx, x);
555 XtSetArg(wargs[1], XtNy, y);
556 XtSetValues(w, wargs, 2);
557 MapWidgetToForm(w, form);
559 if (newheight > oldheight) {
560 bb = (XmBulletinBoardWidget) form->formpointer;
562 for (i = 0; i < bb->composite.num_children; i++) {
563 XtSetArg(wargs[0], XtNy, &y);
564 XtGetValues(bb->composite.children[i], wargs, 1);
566 y = (y + newheight) - oldheight;
567 XtSetArg(wargs[0], XtNy, y);
568 XtSetValues(bb->composite.children[i], wargs, 1);
574 trans = XtParseTranslationTable(form_override_table);
575 XtOverrideTranslations(w, trans);
576 rc = (XmRowColumnWidget) w;
577 for (i = 0; i < rc->composite.num_children; i++)
578 XtOverrideTranslations(rc->composite.children[i], trans);
584 MakeButtons(parent, pheight, pwidth, spec)
590 BottomButton *current;
591 XmString label; /* compound string required */
594 Dimension newwidth, width = 25;
596 BottomButton **buttons = spec->buttons;
601 XtSetArg(wargs[n], XtNy, *pheight); n++;
602 XtSetArg(wargs[n], XtNx, 0); n++;
603 XtSetArg(wargs[n], XtNwidth, *pwidth); n++;
604 XtCreateManagedWidget( "separator",
605 xmSeparatorWidgetClass,
609 for ( current=(*buttons);
611 buttons++, current=(*buttons)) {
614 printf ("Making a button labeled %s\n", current->label);
616 label = XmStringCreate( current->label,
617 XmSTRING_DEFAULT_CHARSET);
619 XtSetArg(wargs[n], XtNy, (*pheight)); n++;
620 XtSetArg(wargs[n], XtNx, width); n++;
621 XtSetArg(wargs[n], XmNlabelString, label); n++;
623 newbutton = XtCreateManagedWidget( current->label,
624 xmPushButtonWidgetClass,
627 XtAddCallback(newbutton, XmNactivateCallback,
628 (XtCallbackProc) current->returnfunction,
631 XtSetArg(wargs[n], XtNwidth, &newwidth); n++;
632 XtGetValues (newbutton, wargs, n);
634 width += (newwidth + hpad);
641 radio_callback(w, client_data, call_data)
643 XmAnyCallbackStruct *client_data;
644 XmAnyCallbackStruct *call_data;
650 UserPrompt *prompt = (UserPrompt *) client_data;
653 XtSetArg(wargs[n], XmNset, &is_set); n++;
654 XtGetValues (w, wargs, n);
660 ** Since Motif insists on using !@#$% Compound Strings as the text for
661 ** its label widgets, but doesn't provide a way of getting a char* back
662 ** from a !@#$% Compound String, I can't retrieve the label of the button
665 ** Fortunately, I was smart enough to use the button label as the name
666 ** of the widget, and I can extract it via XtName(). Thanks, Motif!
668 if (prompt->returnvalue.stringvalue &&
669 (strcmp(prompt->returnvalue.stringvalue, XtName(w)))) {
670 strcpy(prompt->returnvalue.stringvalue, XtName(w));
671 if (prompt->valuechanged)
672 (*prompt->valuechanged)(WidgetToForm(w), prompt);
678 boolean_callback(w, client_data, call_data)
680 XmAnyCallbackStruct *client_data;
681 XmAnyCallbackStruct *call_data;
686 UserPrompt *current = (UserPrompt *)client_data;
690 XtSetArg(wargs[n], XmNset, &is_set); n++;
691 XtGetValues (w, wargs, n);
693 current->returnvalue.booleanvalue = is_set;
696 label = XmStringCreate( "(True)", XmSTRING_DEFAULT_CHARSET);
698 label = XmStringCreate( "(False)", XmSTRING_DEFAULT_CHARSET);
700 XtSetArg(wargs[n], XmNlabelString, label); n++;
701 XtSetValues (w, wargs, n);
703 if (current->valuechanged)
704 (*current->valuechanged)(WidgetToForm(w), current);
707 printf ("boolean_callback: button %x is %s\n",
708 w, (is_set ? "True" : "False"));
713 menu_callback(w, client_data, call_data)
715 XmAnyCallbackStruct *client_data;
716 XmAnyCallbackStruct *call_data;
718 MenuItem *itemhit = (MenuItem *) client_data;
720 /* printf ("menu_callback: item '%s', op %d and string '%s'\n",
724 XtManageChild(entryformwidget);
726 MoiraMenuRequest(itemhit);
731 string_callback(w, client_data, call_data)
733 XmAnyCallbackStruct *client_data;
734 XmAnyCallbackStruct *call_data;
736 UserPrompt *current = (UserPrompt *)client_data;
739 newvalue = XmTextGetString(w);
741 if (strcmp(current->returnvalue.stringvalue, newvalue)) {
742 /* printf ("Replacing old value of selection, '%s', with '%s'\n",
743 current->returnvalue.stringvalue,
745 strcpy(current->returnvalue.stringvalue, newvalue);
746 if (current->valuechanged)
747 (*current->valuechanged)(WidgetToForm(w), current);
753 void MoiraFocusOut(w, event, p, n)
760 UserPrompt *current = NULL;
762 XmTextRec *tr = (XmTextRec *)w;
765 if (tr->core.self != w || tr->core.widget_class != xmTextWidgetClass)
767 newvalue = XmTextGetString(w);
769 for (i = 0; f->inputlines[i]; i++)
770 if (f->inputlines[i]->mywidget == w)
771 current = f->inputlines[i];
772 if (current == NULL) {
773 fprintf(stderr, "Couldn't find prompt structure!\n");
777 if (strcmp(current->returnvalue.stringvalue, newvalue)) {
778 strcpy(current->returnvalue.stringvalue, newvalue);
779 if (current->valuechanged)
780 (*current->valuechanged)(f, current);
787 newvalue(w, client_data, call_data)
789 XmAnyCallbackStruct *client_data;
790 XmAnyCallbackStruct *call_data;
792 UserPrompt *current = (UserPrompt *)client_data;
797 if (current->keyword_name == NULL) {
798 PopupErrorMessage("Sorry, that keyword cannot be changed.", NULL);
801 form = (EntryForm *)current->parent;
802 for (i = 0; form->inputlines[i]; i++)
803 if (form->inputlines[i] == current)
805 f = GetAndClearForm("add_new_value");
806 mi.operation = MM_NEW_VALUE;
807 mi.query = "add_alias";
809 mi.form = form->formname;
810 mi.accel = (char *) i;
812 f->extrastuff = current->keyword_name;
817 /* WARNING: This routine uses Motif internal undocumented routines.
818 * It was the only way to get carriage return to Do The Right Thing.
819 * If you are in a single-item tab group, this routine will call
820 * MoiraFormComplete() (same as pressing OK on the bottom of the form).
821 * otherwise, it advances the focus the same as pressing TAB.
824 void EnterPressed(w, event, argv, count)
833 next = _XmFindNextTabGroup(w);
835 MoiraFocusOut(w, event, argv, count);
836 form = WidgetToForm(w);
837 MoiraFormComplete(NULL, form);
839 _XmMgrTraversal(w, XmTRAVERSE_NEXT_TAB_GROUP);
844 void CancelForm(w, event, argv, count)
852 form = WidgetToForm(w);
854 XtUnmanageChild(form->formpointer);
858 void ExecuteForm(w, event, argv, count)
866 form = WidgetToForm(w);
868 MoiraFocusOut(w, event, argv, count);
869 MoiraFormComplete(NULL, form);
874 void DoHelp(w, event, argv, count)
882 form = WidgetToForm(w);
884 help(form->formname);
888 extern struct hash *create_hash();
889 static struct hash *WFmap = NULL;
891 MapWidgetToForm(w, f)
896 WFmap = create_hash(101);
898 hash_store(WFmap, w, f);
901 EntryForm *WidgetToForm(w)
904 return((EntryForm *) hash_lookup(WFmap, w));