5 #include <X11/StringDefs.h>
6 #include <X11/IntrinsicP.h>
10 #include <X11/CompositeP.h>
12 #include <Xm/BulletinB.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/Separator.h>
23 #include <Xm/Traversal.h>
26 static char rcsid[] = "$Header$";
29 #define MAX(a,b) ((a > b) ? a : b)
32 #define MIN(a,b) ((a < b) ? a : b)
41 Widget BuildMenuTree();
42 Widget MakeRadioField();
43 int button_callback();
44 void radio_callback();
45 void string_callback();
46 void boolean_callback();
50 EntryForm *WidgetToForm();
52 extern void UpdateForm();
53 extern int PopupErrorMessage();
54 extern void PopupHelpWindow();
55 extern int AppendToLog();
56 extern void MakeWatchCursor();
57 extern void MakeNormalCursor();
58 extern Widget SetupLogWidget();
60 static XtActionsRec myactions[] = {
61 { "MoiraFocusOut", MoiraFocusOut },
66 manage_widget(w, widget, call_data)
68 XmAnyCallbackStruct *call_data;
70 XtManageChild(widget);
74 button_callback(w, client_data, call_data)
76 EntryForm *client_data;
77 XmAnyCallbackStruct *call_data;
79 XtUnmanageChild(client_data->formpointer);
84 BuildMenuTree(topW, spec)
88 return (CreateMenu(topW, spec->submenu, XmHORIZONTAL));
92 ** Read the specification and put up a menu to match...
96 CreateMenu(parent, spec, orientation)
102 MenuItem *curmenuitem;
103 Widget childbutton, childmenu;
106 XmString label; /* !@#$%^ compound string required */
108 label = XmStringCreate( "Complete junk", XmSTRING_DEFAULT_CHARSET);
111 XtSetArg(wargs[n], XmNlabelString, label); n++;
113 if (orientation == XmHORIZONTAL) {
114 XtSetArg(wargs[n], XmNspacing, 5); n++;
115 menuparent = XmCreateMenuBar( parent, "randommenu",
117 XtManageChild(menuparent);
120 menuparent = XmCreatePulldownMenu(parent, "randommenu",
123 for ( curmenuitem = (*spec);
125 spec++, curmenuitem = (*spec)) {
128 printf ("Making entry: %s\n", curmenuitem->label);
130 label = XmStringCreate( curmenuitem->label,
131 XmSTRING_DEFAULT_CHARSET);
133 XtSetArg(wargs[n], XmNlabelString, label); n++;
134 if (curmenuitem->accel) {
135 XtSetArg(wargs[n], XmNmnemonic, *(curmenuitem->accel)); n++;
138 if (curmenuitem->submenu) {
140 printf ("It has a submenu, which I'm recursing on...\n");
142 childmenu = CreateMenu( menuparent,
143 curmenuitem->submenu,
145 XtSetArg(wargs[n], XmNsubMenuId, childmenu); n++;
147 childbutton = XtCreateManagedWidget( "child",
148 xmCascadeButtonWidgetClass,
149 menuparent, wargs, n);
154 childbutton = XtCreateManagedWidget( "child",
155 xmPushButtonGadgetClass,
156 menuparent, wargs, n);
158 XtAddCallback( childbutton,
160 menu_callback, curmenuitem);
168 ** Read the specification and put up a form to match...
172 CreateForm(parent, spec)
179 XmString label; /* compound string required */
180 Dimension height_so_far = 0, width_so_far = 0;
181 Dimension height, width;
182 Widget titleW, instructionW;
186 if (spec->formpointer) {
188 return(spec->formpointer);
191 height_so_far = vpad;
193 #define GETSIZE(foo) n = 0; \
194 XtSetArg(wargs[n], XtNwidth, &width); n++; \
195 XtSetArg(wargs[n], XtNheight, &height); n++; \
196 XtGetValues (foo, wargs, n); \
198 #define STORESIZE if (width > width_so_far) width_so_far = width;\
199 height_so_far += height + vpad;
203 XtSetArg(wargs[n], XmNautoUnmanage, False); n++;
204 bb = XmCreateBulletinBoardDialog(parent, spec->formname, wargs, n);
205 MapWidgetToForm(bb, spec);
207 spec->formpointer = bb;
210 label = XmStringCreate(spec->formname, XmSTRING_DEFAULT_CHARSET);
212 XtSetArg(wargs[n], XmNlabelString, label); n++;
213 XtSetArg(wargs[n], XtNx, 0); n++;
214 XtSetArg(wargs[n], XtNy, 0); n++;
215 titleW = XtCreateManagedWidget( "title",
222 label = XmStringCreate(spec->instructions, XmSTRING_DEFAULT_CHARSET);
224 XtSetArg(wargs[n], XmNlabelString, label); n++;
225 XtSetArg(wargs[n], XtNx, 0); n++;
226 XtSetArg(wargs[n], XtNy, height_so_far); n++;
227 instructionW = XtCreateManagedWidget( "instructions",
230 GETSIZE(instructionW);
233 height = height_so_far;
234 width = width_so_far;
235 MakeInputLines(bb, &height, &width, spec);
238 height = height_so_far;
239 width = width_so_far;
240 MakeButtons(bb, &height, &width, spec);
244 ** Center the title of the form
248 XtSetArg(wargs[n], XtNwidth, &width); n++;
249 XtGetValues (titleW, wargs, n);
251 x = (width_so_far - width) / 2;
254 XtSetArg(wargs[n], XtNx, x); n++;
255 XtSetValues (titleW, wargs, n);
258 XtSetArg(wargs[n], XtNwidth, &width); n++;
259 XtGetValues (instructionW, wargs, n);
261 x = (width_so_far - width) / 2;
264 XtSetArg(wargs[n], XtNx, x); n++;
265 XtSetValues (instructionW, wargs, n);
271 ** Pheight and pwidth start with the values-to-date of the bboard so far.
272 ** Return your height and width in them when you're done.
274 ** Positioning the widgets happens in two phases:
275 ** First, we set their y-positions as we create them.
276 ** After they're created, we go back and adjust the x-positions
277 ** according to the widest left side noted.
280 MakeInputLines(parent, pheight, pwidth, spec)
287 XmString label; /* compound string required */
291 Dimension width, height, maxleftwidth = 0, maxrightwidth = 0;
292 Dimension localy, leftheight = 0, rightheight = 0;
293 UserPrompt **myinputlines = spec->inputlines;
296 static XtTranslations trans = NULL;
298 for ( current = (*myinputlines), localy = 0, i = 0;
300 myinputlines++, current = (*myinputlines), i++) {
304 printf ("Making entry %d: %s of type %d\n",
305 i, current->prompt, current->type);
308 ** First, make the prompt
310 if (current->type == FT_KEYWORD) {
313 p = index(current->prompt, '|');
316 current->keyword_name = p;
320 label = XmStringCreate( current->prompt,
321 XmSTRING_DEFAULT_CHARSET);
323 XtSetArg(wargs[n], XmNlabelString, label); n++;
324 XtSetArg(wargs[n], XtNy, localy + *pheight); n++;
325 child = XtCreateManagedWidget( "prompt",
331 if (width > maxleftwidth)
332 maxleftwidth = width;
334 if (current->type == FT_KEYWORD && current->keyword_name) {
335 label = XmStringCreate("add new value",
336 XmSTRING_DEFAULT_CHARSET);
338 XtSetArg(wargs[n], XmNlabelString, label); n++;
339 XtSetArg(wargs[n], XtNy, localy + *pheight + height); n++;
340 XtSetArg(wargs[n], XtNx, height); n++;
341 child = XtCreateManagedWidget("newvalue",
342 xmPushButtonWidgetClass,
344 XtAddCallback(child, XmNactivateCallback,
348 leftheight += height;
349 if (width + height > maxleftwidth)
350 maxleftwidth = width + height;
353 ** Second, make the input widget
356 XtSetArg(wargs[n], XtNy, localy + *pheight); n++;
357 XtSetArg(wargs[n], XmNtraversalOn, True); n++;
358 XtSetArg(wargs[n], XtNsensitive,
359 !(current->insensitive)); n++;
360 switch (current->type) {
362 children[i] = XtCreateManagedWidget( "textwidget",
365 XtAddCallback( children[i], XmNvalueChangedCallback,
366 string_callback, current);
368 XtAppAddActions(XtWidgetToApplicationContext(children[i]),
369 myactions, XtNumber(myactions));
371 trans = XtParseTranslationTable(resources.text_trans);
373 XtOverrideTranslations(children[i], trans);
374 if (current->returnvalue.stringvalue) {
375 XmTextSetString (children[i], current->returnvalue.stringvalue);
377 GETSIZE (children[i]);
378 rightheight = height;
379 if (width > maxrightwidth)
380 maxrightwidth = width;
384 XtSetArg(wargs[n], XmNset,
385 current->returnvalue.booleanvalue ? True : False); n++;
387 if (current->returnvalue.booleanvalue)
388 label = XmStringCreate( "(True)", XmSTRING_DEFAULT_CHARSET);
390 label = XmStringCreate( "(False)", XmSTRING_DEFAULT_CHARSET);
391 XtSetArg(wargs[n], XmNlabelString, label); n++;
393 children[i] = XtCreateManagedWidget( "ignore this",
394 xmToggleButtonWidgetClass,
397 XtAddCallback( children[i], XmNvalueChangedCallback,
398 boolean_callback, current);
400 GETSIZE (children[i]);
401 rightheight = height;
402 if (width > maxrightwidth)
403 maxrightwidth = width;
408 MakeRadioField(parent, current,
410 XtManageChild(children[i]);
411 XtSetValues(children[i], wargs, n);
412 GETSIZE (children[i]);
413 if (width > maxrightwidth)
414 maxrightwidth = width;
418 printf ("Sorry, don't recognize that type\n");
421 XmAddTabGroup(children[i]);
422 MapWidgetToForm(children[i], spec);
423 current->parent = (caddr_t) spec;
425 current->mywidget = children[i];
427 localy += MAX(rightheight, leftheight) + vpad;
431 ** Now slide the input widgets right as far as the widest prompt.
434 XtSetArg(wargs[n], XtNx, maxleftwidth + hpad); n++;
436 XtSetValues (children[i - 1], wargs, n);
438 *pheight = localy - vpad;
439 *pwidth = maxleftwidth + maxrightwidth + hpad;
443 ** All the junk about keeping track of the sum of the children's heights
444 ** is because the !#$% RowColumn widget doesn't sum them for us, NOR
445 ** does it accept SetValues on its XtNHeight! Thanks, Motif!
449 MakeRadioField(parent, prompt, pheight, spec)
455 Widget radioparent, child = NULL;
459 XmString label; /* accursed compound string required */
460 Dimension height, width;
461 char **keywords, *null[2];
463 if (!prompt->keywords) {
464 fprintf (stderr, "Warning: No list of keywords for widget\n");
465 prompt->keywords = null;
468 for ( count = 0, keywords = prompt->keywords;
470 keywords++, count++);
473 ** Although the XmNnumColumns resource is documented as actually
474 ** representing the number of _rows_ when XmNorientation is set to XmVERTICAL,
475 ** it doesn't. So I need to count the items myself and manually set the
476 ** number of columns to get a maximum of five rows. There's no XmNnumRows
477 ** resource. Thanks, Motif!
481 XtSetArg(wargs[n], XmNspacing, 0); n++;
484 XtSetArg(wargs[n], XmNnumColumns, 1 + (count-1) / 5); n++;
485 XtSetArg(wargs[n], XmNorientation, XmVERTICAL); n++;
486 XtSetArg(wargs[n], XmNpacking, XmPACK_COLUMN); n++;
488 radioparent = XmCreateRadioBox(parent, "radio", wargs, n);
490 keywords = prompt->keywords;
491 for (current=(*keywords); current; keywords++, current=(*keywords)) {
493 label = XmStringCreate(current, XmSTRING_DEFAULT_CHARSET);
494 XtSetArg(wargs[n], XmNlabelString, label); n++;
495 if ((prompt->returnvalue.stringvalue) &&
496 (!strcmp (current, prompt->returnvalue.stringvalue))) {
497 XtSetArg(wargs[n], XmNset, True); n++;
500 XtSetArg(wargs[n], XmNset, False); n++;
502 child = XtCreateManagedWidget( current,
503 xmToggleButtonWidgetClass,
504 radioparent, wargs, n);
505 MapWidgetToForm(child, spec);
507 XtAddCallback( child, XmNvalueChangedCallback,
508 radio_callback, prompt);
512 ** Assume all child widgets are the same height. Increase height by
513 ** five times this, or the actual number of children, whichever is lesser.
520 *pheight = (height * MIN(5, count)) + vpad;
526 /* This is called when the list of keywords changes. The old radio box
527 * will be destroyed and a new one created.
530 RemakeRadioField(form, field)
534 Dimension x, y, parent_y, oldheight, newheight;
537 static XtTranslations trans = NULL;
538 extern char form_override_table[];
541 XtSetArg(wargs[0], XtNx, &x);
542 XtSetArg(wargs[1], XtNy, &y);
543 XtSetArg(wargs[2], XtNheight, &oldheight);
544 XtGetValues(form->inputlines[field]->mywidget, wargs, 3);
545 XtUnmanageChild(form->inputlines[field]->mywidget);
546 form->inputlines[field]->mywidget = w =
547 MakeRadioField(form->formpointer, form->inputlines[field],
549 XtSetArg(wargs[0], XtNx, x);
550 XtSetArg(wargs[1], XtNy, y);
551 XtSetValues(w, wargs, 2);
552 MapWidgetToForm(w, form);
554 if (newheight > oldheight) {
556 for (i = 0; i < NumChildren(form->formpointer); i++) {
557 XtSetArg(wargs[0], XtNy, &y);
558 XtGetValues(NthChild(form->formpointer, i), wargs, 1);
560 y = (y + newheight) - oldheight;
561 XtSetArg(wargs[0], XtNy, y);
562 XtSetValues(NthChild(form->formpointer, i), wargs, 1);
568 trans = XtParseTranslationTable(resources.form_trans);
569 XtOverrideTranslations(w, trans);
570 for (i = 0; i < NumChildren(w); i++)
571 XtOverrideTranslations(NthChild(w, i), trans);
577 MakeButtons(parent, pheight, pwidth, spec)
583 BottomButton *current;
584 XmString label; /* compound string required */
587 Dimension newwidth, width = 25;
589 BottomButton **buttons = spec->buttons;
594 XtSetArg(wargs[n], XtNy, *pheight); n++;
595 XtSetArg(wargs[n], XtNx, 0); n++;
596 XtSetArg(wargs[n], XtNwidth, *pwidth); n++;
597 XtCreateManagedWidget( "separator",
598 xmSeparatorWidgetClass,
602 for ( current=(*buttons);
604 buttons++, current=(*buttons)) {
607 printf ("Making a button labeled %s\n", current->label);
609 label = XmStringCreate( current->label,
610 XmSTRING_DEFAULT_CHARSET);
612 XtSetArg(wargs[n], XtNy, (*pheight)); n++;
613 XtSetArg(wargs[n], XtNx, width); n++;
614 XtSetArg(wargs[n], XmNlabelString, label); n++;
616 newbutton = XtCreateManagedWidget( current->label,
617 xmPushButtonWidgetClass,
620 XtAddCallback(newbutton, XmNactivateCallback,
621 (XtCallbackProc) current->returnfunction,
624 XtSetArg(wargs[n], XtNwidth, &newwidth); n++;
625 XtGetValues (newbutton, wargs, n);
627 width += (newwidth + hpad);
634 radio_callback(w, client_data, call_data)
636 XmAnyCallbackStruct *client_data;
637 XmAnyCallbackStruct *call_data;
643 UserPrompt *prompt = (UserPrompt *) client_data;
646 XtSetArg(wargs[n], XmNset, &is_set); n++;
647 XtGetValues (w, wargs, n);
653 ** Since Motif insists on using !@#$% Compound Strings as the text for
654 ** its label widgets, but doesn't provide a way of getting a char* back
655 ** from a !@#$% Compound String, I can't retrieve the label of the button
658 ** Fortunately, I was smart enough to use the button label as the name
659 ** of the widget, and I can extract it via XtName(). Thanks, Motif!
661 if (prompt->returnvalue.stringvalue &&
662 (strcmp(prompt->returnvalue.stringvalue, XtName(w)))) {
663 strcpy(prompt->returnvalue.stringvalue, XtName(w));
664 if (prompt->valuechanged)
665 (*prompt->valuechanged)(WidgetToForm(w), prompt);
671 boolean_callback(w, client_data, call_data)
673 XmAnyCallbackStruct *client_data;
674 XmAnyCallbackStruct *call_data;
679 UserPrompt *current = (UserPrompt *)client_data;
683 XtSetArg(wargs[n], XmNset, &is_set); n++;
684 XtGetValues (w, wargs, n);
686 current->returnvalue.booleanvalue = is_set;
689 label = XmStringCreate( "(True)", XmSTRING_DEFAULT_CHARSET);
691 label = XmStringCreate( "(False)", XmSTRING_DEFAULT_CHARSET);
693 XtSetArg(wargs[n], XmNlabelString, label); n++;
694 XtSetValues (w, wargs, n);
696 if (current->valuechanged)
697 (*current->valuechanged)(WidgetToForm(w), current);
700 printf ("boolean_callback: button %x is %s\n",
701 w, (is_set ? "True" : "False"));
706 menu_callback(w, client_data, call_data)
708 XmAnyCallbackStruct *client_data;
709 XmAnyCallbackStruct *call_data;
711 MenuItem *itemhit = (MenuItem *) client_data;
713 /* printf ("menu_callback: item '%s', op %d and string '%s'\n",
717 XtManageChild(entryformwidget);
719 MoiraMenuRequest(itemhit);
724 string_callback(w, client_data, call_data)
726 XmAnyCallbackStruct *client_data;
727 XmAnyCallbackStruct *call_data;
729 UserPrompt *current = (UserPrompt *)client_data;
732 newvalue = XmTextGetString(w);
734 if (strcmp(current->returnvalue.stringvalue, newvalue)) {
735 /* printf ("Replacing old value of selection, '%s', with '%s'\n",
736 current->returnvalue.stringvalue,
738 strcpy(current->returnvalue.stringvalue, newvalue);
739 if (current->valuechanged)
740 (*current->valuechanged)(WidgetToForm(w), current);
746 void MoiraFocusOut(w, event, p, n)
753 UserPrompt *current = NULL;
755 XmTextRec *tr = (XmTextRec *)w;
758 if (tr->core.self != w || tr->core.widget_class != xmTextWidgetClass)
760 newvalue = XmTextGetString(w);
762 for (i = 0; f->inputlines[i]; i++)
763 if (f->inputlines[i]->mywidget == w)
764 current = f->inputlines[i];
765 if (current == NULL) {
766 fprintf(stderr, "Couldn't find prompt structure!\n");
770 if (strcmp(current->returnvalue.stringvalue, newvalue)) {
771 strcpy(current->returnvalue.stringvalue, newvalue);
772 if (current->valuechanged)
773 (*current->valuechanged)(f, current);
780 newvalue(w, client_data, call_data)
782 XmAnyCallbackStruct *client_data;
783 XmAnyCallbackStruct *call_data;
785 UserPrompt *current = (UserPrompt *)client_data;
790 if (current->keyword_name == NULL) {
791 PopupErrorMessage("Sorry, that keyword cannot be changed.", NULL);
794 form = (EntryForm *)current->parent;
795 for (i = 0; form->inputlines[i]; i++)
796 if (form->inputlines[i] == current)
798 f = GetAndClearForm("add_new_value");
799 mi.operation = MM_NEW_VALUE;
800 mi.query = "add_alias";
802 mi.form = form->formname;
803 mi.accel = (char *) i;
805 f->extrastuff = current->keyword_name;
810 /* WARNING: This routine uses Motif internal undocumented routines.
811 * It was the only way to get carriage return to Do The Right Thing.
812 * If you are in a single-item tab group, this routine will call
813 * MoiraFormComplete() (same as pressing OK on the bottom of the form).
814 * otherwise, it advances the focus the same as pressing TAB.
817 void EnterPressed(w, event, argv, count)
826 next = _XmFindNextTabGroup(w);
828 MoiraFocusOut(w, event, argv, count);
829 form = WidgetToForm(w);
830 MoiraFormComplete(NULL, form);
832 _XmMgrTraversal(w, XmTRAVERSE_NEXT_TAB_GROUP);
837 void CancelForm(w, event, argv, count)
845 form = WidgetToForm(w);
847 XtUnmanageChild(form->formpointer);
851 void ExecuteForm(w, event, argv, count)
859 form = WidgetToForm(w);
861 MoiraFocusOut(w, event, argv, count);
862 MoiraFormComplete(NULL, form);
867 void DoHelp(w, event, argv, count)
875 form = WidgetToForm(w);
877 help(form->formname);
881 extern struct hash *create_hash();
882 static struct hash *WFmap = NULL;
884 MapWidgetToForm(w, f)
889 WFmap = create_hash(101);
891 hash_store(WFmap, w, f);
894 EntryForm *WidgetToForm(w)
897 return((EntryForm *) hash_lookup(WFmap, w));
901 /* Routines to deal with children of composite widgets */
903 Widget NthChild(w, n)
907 return(w->composite.children[n]);
913 return(w->composite.num_children);