1 /* $Cambridge: exim/src/exim_monitor/em_menu.c,v 1.5 2009/11/16 19:50:36 nm4 Exp $ */
3 /*************************************************
5 *************************************************/
7 /* Copyright (c) University of Cambridge 1995 - 2009 */
8 /* See the file NOTICE for conditions of use and distribution. */
13 /* This module contains code for handling the popup menus. */
15 static Widget menushell;
16 static Widget queue_text_sink;
17 static Widget dialog_shell, dialog_widget;
19 static Widget text_create(uschar *, int);
21 static int highlighted_start, highlighted_end, highlighted_x, highlighted_y;
25 static Arg queue_get_arg[] = {
26 { "textSink", (XtArgVal)NULL },
27 { "textSource", (XtArgVal)NULL },
28 { "string", (XtArgVal)NULL } };
30 static Arg dialog_arg[] = {
31 { "label", (XtArgVal)"dialog" },
32 { "value", (XtArgVal)"value" } };
34 static Arg get_pos_args[] = {
35 {"x", (XtArgVal)NULL },
36 {"y", (XtArgVal)NULL } };
38 static Arg menushell_arg[] = {
39 { "label", (XtArgVal)NULL } };
41 static Arg button_arg[] = {
42 { XtNfromVert, (XtArgVal) NULL }, /* must be first */
43 { XtNlabel, (XtArgVal) " Dismiss " },
44 { "left", XawChainLeft },
45 { "right", XawChainLeft },
46 { "top", XawChainBottom },
47 { "bottom", XawChainBottom } };
49 static Arg text_arg[] = {
50 { XtNfromVert, (XtArgVal) NULL }, /* must be first */
51 { "editType", XawtextEdit },
52 { "string", (XtArgVal)"" }, /* dummy to get it going */
53 { "scrollVertical", XawtextScrollAlways },
54 { "wrap", XawtextWrapWord },
55 { "top", XawChainTop },
56 { "bottom", XawChainBottom } };
58 static Arg item_1_arg[] = {
59 { XtNfromVert, (XtArgVal)NULL }, /* must be first */
60 { "label", (XtArgVal)" Message log" } };
62 static Arg item_2_arg[] = {
63 { XtNfromVert, (XtArgVal) NULL }, /* must be first */
64 { "label", (XtArgVal)" Headers" } };
66 static Arg item_3_arg[] = {
67 { XtNfromVert, (XtArgVal) NULL }, /* must be first */
68 { "label", (XtArgVal)" Body" } };
70 static Arg item_4_arg[] = {
71 { XtNfromVert, (XtArgVal) NULL }, /* must be first */
72 { "label", (XtArgVal)" Deliver message" } };
74 static Arg item_5_arg[] = {
75 { XtNfromVert, (XtArgVal) NULL }, /* must be first */
76 { "label", (XtArgVal)" Freeze message" } };
78 static Arg item_6_arg[] = {
79 { XtNfromVert, (XtArgVal) NULL }, /* must be first */
80 { "label", (XtArgVal)" Thaw message" } };
82 static Arg item_7_arg[] = {
83 { XtNfromVert, (XtArgVal) NULL }, /* must be first */
84 { "label", (XtArgVal)" Give up on msg" } };
86 static Arg item_8_arg[] = {
87 { XtNfromVert, (XtArgVal) NULL }, /* must be first */
88 { "label", (XtArgVal)" Remove message" } };
90 static Arg item_9_arg[] = {
91 { XtNfromVert, (XtArgVal) NULL }, /* must be first */
92 { "label", (XtArgVal)"----------------" } };
94 static Arg item_10_arg[] = {
95 { XtNfromVert, (XtArgVal) NULL }, /* must be first */
96 { "label", (XtArgVal)" Add recipient" } };
98 static Arg item_11_arg[] = {
99 { XtNfromVert, (XtArgVal) NULL }, /* must be first */
100 { "label", (XtArgVal)" Mark delivered" } };
102 static Arg item_12_arg[] = {
103 { XtNfromVert, (XtArgVal) NULL }, /* must be first */
104 { "label", (XtArgVal)" Mark all delivered" } };
106 static Arg item_13_arg[] = {
107 { XtNfromVert, (XtArgVal) NULL }, /* must be first */
108 { "label", (XtArgVal)" Edit sender" } };
110 static Arg item_99_arg[] = {
111 { XtNfromVert, (XtArgVal) NULL }, /* must be first */
112 { "label", (XtArgVal)" " } };
116 /*************************************************
117 * Destroy the menu when popped down *
118 *************************************************/
120 static void popdownAction(Widget w, XtPointer client_data, XtPointer call_data)
122 client_data = client_data; /* Keep picky compilers happy */
123 call_data = call_data;
124 if (highlighted_x >= 0)
125 XawTextSinkDisplayText(queue_text_sink,
126 highlighted_x, highlighted_y,
127 highlighted_start, highlighted_end, 0);
134 /*************************************************
135 * Display the message log *
136 *************************************************/
138 static void msglogAction(Widget w, XtPointer client_data, XtPointer call_data)
142 Widget text = text_create((uschar *)client_data, text_depth);
145 w = w; /* Keep picky compilers happy */
146 call_data = call_data;
148 /* End up with the split version, so message looks right when non-exist */
150 for (i = 0; i < (spool_is_split? 2:1); i++)
152 message_subdir[0] = (i != 0)? ((uschar *)client_data)[5] : 0;
153 sprintf(CS buffer, "%s/msglog/%s/%s", spool_directory, message_subdir,
154 (uschar *)client_data);
155 f = fopen(CS buffer, "r");
156 if (f != NULL) break;
160 text_showf(text, "%s: %s\n", buffer, strerror(errno));
163 while (Ufgets(buffer, 256, f) != NULL) text_show(text, buffer);
170 /*************************************************
171 * Display the message body *
172 *************************************************/
174 static void bodyAction(Widget w, XtPointer client_data, XtPointer call_data)
178 Widget text = text_create((uschar *)client_data, text_depth);
181 w = w; /* Keep picky compilers happy */
182 call_data = call_data;
184 for (i = 0; i < (spool_is_split? 2:1); i++)
186 message_subdir[0] = (i != 0)? ((uschar *)client_data)[5] : 0;
187 sprintf(CS buffer, "%s/input/%s/%s-D", spool_directory, message_subdir,
188 (uschar *)client_data);
189 f = fopen(CS buffer, "r");
190 if (f != NULL) break;
194 text_showf(text, "Failed to open file: %s\n", strerror(errno));
198 while (Ufgets(buffer, 256, f) != NULL)
200 text_show(text, buffer);
201 count += Ustrlen(buffer);
202 if (count > body_max)
204 text_show(text, US"\n*** Message length exceeds BODY_MAX ***\n");
214 /*************************************************
215 * Do something to a message *
216 *************************************************/
218 /* The output is not shown in a window for non-delivery actions that succeed,
219 unless action_output is set. We can't, however, tell until we have run
220 the command whether we want the output or not, so the pipe has to be set up in
223 static void ActOnMessage(uschar *id, uschar *action, uschar *address_arg)
227 int delivery = Ustrcmp(action + Ustrlen(action) - 2, "-M") == 0;
228 uschar *quote = US"";
230 uschar *qualify = US"";
235 /* If the address arg is not empty and does not contain @ and there is a
236 qualify domain, qualify it. (But don't qualify '<>'.)*/
238 if (address_arg[0] != 0)
241 if (Ustrchr(address_arg, '@') == NULL &&
242 Ustrcmp(address_arg, "<>") != 0 &&
243 qualify_domain != NULL &&
244 qualify_domain[0] != 0)
247 qualify = qualify_domain;
250 sprintf(CS buffer, "%s %s %s %s %s %s%s%s%s%s", exim_path,
251 (alternate_config == NULL)? US"" : US"-C",
252 (alternate_config == NULL)? US"" : alternate_config,
253 action, id, quote, address_arg, at, qualify, quote);
255 /* If we know we are going to need the window, create it now. */
257 if (action_output || delivery)
259 text = text_create(id, text_depth);
260 text_showf(text, "%s\n", buffer);
263 /* Create the pipe for output. Remember, on most systems pipe[0] is
264 for reading and pipe[1] is for writing! Solaris, with its two-way
267 if (pipe(pipe_fd) != 0)
271 text = text_create(id, text_depth);
272 text_showf(text, "%s\n", buffer);
274 text_show(text, US"*** Failed to create pipe ***\n");
278 fcntl(pipe_fd[0], F_SETFL, O_NONBLOCK);
279 fcntl(pipe_fd[1], F_SETFL, O_NONBLOCK);
281 /* Delivering a message can take some time, and we want to show the
282 output as it goes along. This requires subprocesses and is coded below. For
283 other commands, we can assume an immediate response, and so need not waste
284 resources with subprocesses. If action_output is FALSE, don't show the
290 int save_stdout = dup(1);
291 int save_stderr = dup(2);
300 rc = system(CS buffer);
305 if (action_output || rc != 0)
309 text = text_create(id, text_depth);
310 text_showf(text, "%s\n", buffer);
312 while ((count = read(pipe_fd[0], buffer, 254)) > 0)
315 text_show(text, buffer);
321 dup2(save_stdout, 1);
322 dup2(save_stderr, 2);
326 /* If action was to change the sender, and it succeeded, we have to
327 update the in-store data. */
329 if (rc == 0 && Ustrcmp(action + Ustrlen(action) - 4, "-Mes") == 0)
331 queue_item *q = find_queue(id, queue_noop, 0);
334 if (q->sender != NULL) store_free(q->sender);
335 q->sender = store_malloc(Ustrlen(address_arg) + 1);
336 Ustrcpy(q->sender, address_arg);
340 /* If configured, cause a display update and return */
342 if (action_queue_update) tick_queue_accumulator = 999999;
346 /* Message is to be delivered. Ensure that it is marked unfrozen,
347 because nothing will get written to the log to show that this has
348 happened. (Other freezing/unfreezings get logged and picked up from
351 qq = find_queue(id, queue_noop, 0);
352 if (qq != NULL) qq->frozen = FALSE;
354 /* New, asynchronous code runs in a subprocess for commands that
355 will take some time. The main process does not wait. There is a
356 SIGCHLD handler in the main program that cleans up any terminating
359 if ((pid = fork()) == 0)
376 /* Main process - set up an item for the main ticker to watch. */
378 if (pid < 0) text_showf(text, "Failed to fork: %s\n", strerror(pid)); else
380 pipe_item *p = (pipe_item *)store_malloc(sizeof(pipe_item));
384 text_show(text, US"Run out of store\n");
391 p->next = pipe_chain;
401 /*************************************************
402 * Cause a message to be delivered *
403 *************************************************/
405 static void deliverAction(Widget w, XtPointer client_data, XtPointer call_data)
407 w = w; /* Keep picky compilers happy */
408 call_data = call_data;
409 ActOnMessage((uschar *)client_data, US"-v -M", US"");
414 /*************************************************
415 * Cause a message to be Frozen *
416 *************************************************/
418 static void freezeAction(Widget w, XtPointer client_data, XtPointer call_data)
420 w = w; /* Keep picky compilers happy */
421 call_data = call_data;
422 ActOnMessage((uschar *)client_data, US"-Mf", US"");
427 /*************************************************
428 * Cause a message to be thawed *
429 *************************************************/
431 static void thawAction(Widget w, XtPointer client_data, XtPointer call_data)
433 w = w; /* Keep picky compilers happy */
434 call_data = call_data;
435 ActOnMessage((uschar *)client_data, US"-Mt", US"");
440 /*************************************************
441 * Take action using dialog data *
442 *************************************************/
444 /* This function is called after a dialog box has been filled
445 in. It is global because it is set up in the action table at
446 start-up time. If the string is empty, do nothing. */
448 XtActionProc dialogAction(Widget w, XEvent *event, String *ss, Cardinal *c)
450 uschar *s = US XawDialogGetValueString(dialog_widget);
452 w = w; /* Keep picky compilers happy */
457 XtPopdown((Widget)dialog_shell);
458 XtDestroyWidget((Widget)dialog_shell);
459 while (isspace(*s)) s++;
462 if (actioned_message[0] != 0)
463 ActOnMessage(actioned_message, action_required, s);
465 NonMessageDialogue(s); /* When called from somewhere else */
472 /*************************************************
473 * Create a dialog box *
474 *************************************************/
476 /* The focus is grabbed exclusively, so nothing else can
477 be done to the application until the box is filled in. This
478 function is also used by the Hide button handler. */
480 void create_dialog(uschar *label, uschar *value)
483 Dimension x, y, xx, yy;
484 XtTranslations pop_trans;
487 /* Get the position of a reference widget so the dialog box can be put
490 get_pos_args[0].value = (XtArgVal)(&x);
491 get_pos_args[1].value = (XtArgVal)(&y);
492 XtGetValues(dialog_ref_widget, get_pos_args, 2);
494 /* When this is not a message_specific thing, the position of the reference
495 widget is relative to the window. Get the position of the top level widget and
496 add to the position. */
498 if (dialog_ref_widget != menushell)
500 get_pos_args[0].value = (XtArgVal)(&xx);
501 get_pos_args[1].value = (XtArgVal)(&yy);
502 XtGetValues(toplevel_widget, get_pos_args, 2);
507 /* Create a transient shell for the dialog box. */
509 XtSetArg(warg[0], XtNtransientFor, queue_widget);
510 XtSetArg(warg[1], XtNx, x + 50);
511 XtSetArg(warg[2], XtNy, y + 50);
512 XtSetArg(warg[3], XtNallowShellResize, True);
513 dialog_shell = XtCreatePopupShell("forDialog", transientShellWidgetClass,
514 toplevel_widget, warg, 4);
516 /* Create the dialog box. */
518 dialog_arg[0].value = (XtArgVal)label;
519 dialog_arg[1].value = (XtArgVal)value;
520 dialog_widget = XtCreateManagedWidget("dialog", dialogWidgetClass, dialog_shell,
521 dialog_arg, XtNumber(dialog_arg));
523 /* Get the text widget from within the dialog box, give it the keyboard focus,
524 make it wider than the default, and override its translations to make Return
525 call the dialog action function. */
527 text = XtNameToWidget(dialog_widget, "value");
528 XawTextSetInsertionPoint(text, Ustrlen(value));
529 XtSetKeyboardFocus(dialog_widget, text);
530 xs_SetValues(text, 1, "width", 200);
531 pop_trans = XtParseTranslationTable(
532 "<Key>Return: dialogAction()\n");
533 XtOverrideTranslations(text, pop_trans);
535 /* Pop the thing up. */
537 XtPopup(dialog_shell, XtGrabExclusive);
545 /*************************************************
546 * Cause a recipient to be added *
547 *************************************************/
549 /* This just sets up the dialog box; the action happens when it has been filled
552 static void addrecipAction(Widget w, XtPointer client_data, XtPointer call_data)
554 w = w; /* Keep picky compilers happy */
555 call_data = call_data;
556 Ustrcpy(actioned_message, (uschar *)client_data);
557 action_required = US"-Mar";
558 dialog_ref_widget = menushell;
559 create_dialog(US"Recipient address to add?", US"");
564 /*************************************************
565 * Cause an address to be marked delivered *
566 *************************************************/
568 static void markdelAction(Widget w, XtPointer client_data, XtPointer call_data)
570 w = w; /* Keep picky compilers happy */
571 call_data = call_data;
572 Ustrcpy(actioned_message, (uschar *)client_data);
573 action_required = US"-Mmd";
574 dialog_ref_widget = menushell;
575 create_dialog(US"Recipient address to mark delivered?", US"");
579 /*************************************************
580 * Cause all addresses to be marked delivered *
581 *************************************************/
583 static void markalldelAction(Widget w, XtPointer client_data, XtPointer call_data)
585 w = w; /* Keep picky compilers happy */
586 call_data = call_data;
587 ActOnMessage((uschar *)client_data, US"-Mmad", US"");
591 /*************************************************
592 * Edit the message's sender *
593 *************************************************/
595 static void editsenderAction(Widget w, XtPointer client_data,
600 w = w; /* Keep picky compilers happy */
601 call_data = call_data;
602 Ustrcpy(actioned_message, (uschar *)client_data);
603 q = find_queue(actioned_message, queue_noop, 0);
604 sender = (q == NULL)? US"" : (q->sender[0] == 0)? US"<>" : q->sender;
605 action_required = US"-Mes";
606 dialog_ref_widget = menushell;
607 create_dialog(US"New sender address?", sender);
611 /*************************************************
612 * Cause a message to be returned to sender *
613 *************************************************/
615 static void giveupAction(Widget w, XtPointer client_data, XtPointer call_data)
617 w = w; /* Keep picky compilers happy */
618 call_data = call_data;
619 ActOnMessage((uschar *)client_data, US"-v -Mg", US"");
624 /*************************************************
625 * Cause a message to be cancelled *
626 *************************************************/
628 static void removeAction(Widget w, XtPointer client_data, XtPointer call_data)
630 w = w; /* Keep picky compilers happy */
631 call_data = call_data;
632 ActOnMessage((uschar *)client_data, US"-Mrm", US"");
637 /*************************************************
638 * Display a message's headers *
639 *************************************************/
641 static void headersAction(Widget w, XtPointer client_data, XtPointer call_data)
644 header_line *h, *next;
645 Widget text = text_create((uschar *)client_data, text_depth);
648 w = w; /* Keep picky compilers happy */
649 call_data = call_data;
651 /* Remember the point in the dynamic store so we can recover to it afterwards.
652 Then use Exim's function to read the header. */
654 reset_point = store_get(0);
656 sprintf(CS buffer, "%s-H", (uschar *)client_data);
657 if (spool_read_header(buffer, TRUE, FALSE) != spool_read_OK)
659 if (errno == ERRNO_SPOOLFORMAT)
662 sprintf(CS big_buffer, "%s/input/%s", spool_directory, buffer);
663 if (Ustat(big_buffer, &statbuf) == 0)
664 text_showf(text, "Format error in spool file %s: size=%d\n", buffer,
666 else text_showf(text, "Format error in spool file %s\n", buffer);
668 else text_showf(text, "Read error for spool file %s\n", buffer);
669 store_reset(reset_point);
673 if (sender_address != NULL)
675 text_showf(text, "%s sender: <%s>\n", sender_local? "Local" : "Remote",
679 if (recipients_list != NULL)
682 text_show(text, US"Recipients:\n");
683 for (i = 0; i < recipients_count; i++)
685 text_showf(text, " %s %s\n",
686 (tree_search(tree_nonrecipients, recipients_list[i].address) == NULL)?
687 " ":"*", recipients_list[i].address);
689 text_show(text, US"\n");
692 for (h = header_list; h != NULL; h = next)
695 text_showf(text, "%c ", h->type); /* Don't push h->text through a %s */
696 text_show(text, h->text); /* expansion as it may be v large */
699 store_reset(reset_point);
705 /*************************************************
706 * Dismiss a text window *
707 *************************************************/
709 static void dismissAction(Widget w, XtPointer client_data, XtPointer call_data)
711 pipe_item *p = pipe_chain;
713 w = w; /* Keep picky compilers happy */
714 call_data = call_data;
716 XtPopdown((Widget)client_data);
717 XtDestroyWidget((Widget)client_data);
719 /* If this is a text widget for a sub-process, clear it out of
720 the chain so that subsequent data doesn't try to use it. We have
721 to search the parents of the saved widget to see if one of them
722 is what we have just destroyed. */
726 Widget pp = p->widget;
729 if (pp == (Widget)client_data) { p->widget = NULL; return; }
738 /*************************************************
739 * Set up popup text window *
740 *************************************************/
742 static Widget text_create(uschar *name, int height)
744 Widget textshell, form, text, button;
746 /* Create a popup shell widget to display as an additional
749 textshell = XtCreatePopupShell("textshell", topLevelShellWidgetClass,
750 toplevel_widget, NULL, 0);
751 xs_SetValues(textshell, 4,
757 /* Create a form widget, containing the text widget and the
758 dismiss button widget. */
760 form = XtCreateManagedWidget("textform", formWidgetClass,
762 xs_SetValues(form, 1, "defaultDistance", 8);
764 text = XtCreateManagedWidget("texttext", asciiTextWidgetClass,
765 form, text_arg, XtNumber(text_arg));
766 xs_SetValues(text, 4,
767 "editType", XawtextAppend,
770 "translations", text_trans);
771 XawTextDisplayCaret(text, TRUE);
773 /* Use the same font as for the queue display */
775 if (queue_font != NULL)
777 XFontStruct *f = XLoadQueryFont(X_display, CS queue_font);
778 if (f != NULL) xs_SetValues(text, 1, "font", f);
781 button_arg[0].value = (XtArgVal)text;
782 button = XtCreateManagedWidget("dismiss", commandWidgetClass,
783 form, button_arg, XtNumber(button_arg));
784 XtAddCallback(button, "callback", dismissAction, (XtPointer)textshell);
786 /* Get the toplevel popup displayed, and yield the text widget so
787 that text can be put into it. */
789 XtPopup(textshell, XtGrabNone);
796 /*************************************************
797 * Set up menu in queue window *
798 *************************************************/
800 /* We have added an action table that causes this function to
801 be called, and set up button 2 in the text widgets to call it. */
803 void menu_create(Widget w, XEvent *event, String *actargs, Cardinal *count)
809 Widget src, menu_line, item_1, item_2, item_3, item_4,
810 item_5, item_6, item_7, item_8, item_9, item_10, item_11,
812 XtTranslations menu_trans = XtParseTranslationTable(
813 "<EnterWindow>: highlight()\n\
814 <LeaveWindow>: unhighlight()\n\
815 <BtnMotion>: highlight()\n\
816 <BtnUp>: MenuPopdown()notify()unhighlight()\n\
819 actargs = actargs; /* Keep picky compilers happy */
822 /* Get the sink and source and the current text pointer */
824 queue_get_arg[0].value = (XtArgVal)(&queue_text_sink);
825 queue_get_arg[1].value = (XtArgVal)(&src);
826 queue_get_arg[2].value = (XtArgVal)(&s);
827 XtGetValues(w, queue_get_arg, 3);
829 /* Find the line number of the pointer in the window, and the
830 character offset of the top lefthand of the window. */
832 line = (event->xbutton).y / XawTextSinkMaxHeight(queue_text_sink, 1);
833 p = XawTextTopPosition(w);
835 /* Find the start of the line on which the button was clicked. */
840 while (s[p] != 0 && s[p++] != '\n');
843 /* Now pointing either at 0 or 1st uschar after \n, or very 1st uschar.
844 If 0, the click was beyond the end of the data; just set up a dummy
845 menu. (Not easy to ignore as several actions are specified for the
846 mouse click and it expects this one to set up a menu.) If on a
847 continuation line, move back to the main line. */
851 menushell_arg[0].value = (XtArgVal)"No message selected";
852 menushell = XtCreatePopupShell("menu", simpleMenuWidgetClass,
853 queue_widget, menushell_arg, XtNumber(menushell_arg));
854 XtAddCallback(menushell, "popdownCallback", popdownAction, NULL);
855 xs_SetValues(menushell, 2,
856 "cursor", XCreateFontCursor(X_display, XC_arrow),
857 "translations", menu_trans);
859 /* To keep the widgets in XFree86 happy, we have to create at least one menu
860 item, it seems. (Openwindows doesn't mind a menu with no items.) Otherwise
861 there's a complaint about a zero width menu, and a crash. */
863 menu_line = XtCreateManagedWidget("line", smeLineObjectClass, menushell,
866 item_99_arg[0].value = (XtArgVal)menu_line;
867 (void)XtCreateManagedWidget("item99", smeBSBObjectClass, menushell,
868 item_99_arg, XtNumber(item_99_arg));
874 while (p > 0 && s[p+11] == ' ')
878 while (p > 0 && s[p-1] != '\n') p--;
881 /* Now pointing at first character of a main line. */
883 Ustrncpy(message_id, s+p+11, MESSAGE_ID_LENGTH);
884 message_id[MESSAGE_ID_LENGTH] = 0;
886 /* Highlight the line being menued, and save its parameters so that it
887 can be de-highlighted at popdown. */
889 highlighted_start = highlighted_end = p;
890 while (s[highlighted_end] != '\n') highlighted_end++;
892 highlighted_y = line * XawTextSinkMaxHeight(queue_text_sink, 1) + 2;
894 XawTextSinkDisplayText(queue_text_sink,
895 highlighted_x, highlighted_y,
896 highlighted_start, highlighted_end, 1);
898 /* Create the popup shell and the other widgets that comprise the menu.
899 Set the translations and pointer shape, and add the callback pointers. */
901 menushell_arg[0].value = (XtArgVal)message_id;
902 menushell = XtCreatePopupShell("menu", simpleMenuWidgetClass,
903 queue_widget, menushell_arg, XtNumber(menushell_arg));
904 XtAddCallback(menushell, "popdownCallback", popdownAction, NULL);
906 xs_SetValues(menushell, 2,
907 "cursor", XCreateFontCursor(X_display, XC_arrow),
908 "translations", menu_trans);
910 menu_line = XtCreateManagedWidget("line", smeLineObjectClass, menushell,
913 item_1_arg[0].value = (XtArgVal)menu_line;
914 item_1 = XtCreateManagedWidget("item1", smeBSBObjectClass, menushell,
915 item_1_arg, XtNumber(item_1_arg));
916 XtAddCallback(item_1, "callback", msglogAction, (XtPointer)message_id);
918 item_2_arg[0].value = (XtArgVal)item_1;
919 item_2 = XtCreateManagedWidget("item2", smeBSBObjectClass, menushell,
920 item_2_arg, XtNumber(item_2_arg));
921 XtAddCallback(item_2, "callback", headersAction, (XtPointer)message_id);
923 item_3_arg[0].value = (XtArgVal)item_2;
924 item_3 = XtCreateManagedWidget("item3", smeBSBObjectClass, menushell,
925 item_3_arg, XtNumber(item_3_arg));
926 XtAddCallback(item_3, "callback", bodyAction, (XtPointer)message_id);
928 item_4_arg[0].value = (XtArgVal)item_3;
929 item_4 = XtCreateManagedWidget("item4", smeBSBObjectClass, menushell,
930 item_4_arg, XtNumber(item_4_arg));
931 XtAddCallback(item_4, "callback", deliverAction, (XtPointer)message_id);
933 item_5_arg[0].value = (XtArgVal)item_4;
934 item_5 = XtCreateManagedWidget("item5", smeBSBObjectClass, menushell,
935 item_5_arg, XtNumber(item_5_arg));
936 XtAddCallback(item_5, "callback", freezeAction, (XtPointer)message_id);
938 item_6_arg[0].value = (XtArgVal)item_5;
939 item_6 = XtCreateManagedWidget("item6", smeBSBObjectClass, menushell,
940 item_6_arg, XtNumber(item_6_arg));
941 XtAddCallback(item_6, "callback", thawAction, (XtPointer)message_id);
943 item_7_arg[0].value = (XtArgVal)item_6;
944 item_7 = XtCreateManagedWidget("item7", smeBSBObjectClass, menushell,
945 item_7_arg, XtNumber(item_7_arg));
946 XtAddCallback(item_7, "callback", giveupAction, (XtPointer)message_id);
948 item_8_arg[0].value = (XtArgVal)item_7;
949 item_8 = XtCreateManagedWidget("item8", smeBSBObjectClass, menushell,
950 item_8_arg, XtNumber(item_8_arg));
951 XtAddCallback(item_8, "callback", removeAction, (XtPointer)message_id);
953 item_9_arg[0].value = (XtArgVal)item_8;
954 item_9 = XtCreateManagedWidget("item9", smeBSBObjectClass, menushell,
955 item_9_arg, XtNumber(item_9_arg));
957 item_10_arg[0].value = (XtArgVal)item_9;
958 item_10 = XtCreateManagedWidget("item10", smeBSBObjectClass, menushell,
959 item_10_arg, XtNumber(item_10_arg));
960 XtAddCallback(item_10, "callback", addrecipAction, (XtPointer)message_id);
962 item_11_arg[0].value = (XtArgVal)item_10;
963 item_11 = XtCreateManagedWidget("item11", smeBSBObjectClass, menushell,
964 item_11_arg, XtNumber(item_11_arg));
965 XtAddCallback(item_11, "callback", markdelAction, (XtPointer)message_id);
967 item_12_arg[0].value = (XtArgVal)item_11;
968 item_12 = XtCreateManagedWidget("item12", smeBSBObjectClass, menushell,
969 item_12_arg, XtNumber(item_12_arg));
970 XtAddCallback(item_12, "callback", markalldelAction, (XtPointer)message_id);
972 item_13_arg[0].value = (XtArgVal)item_12;
973 item_13 = XtCreateManagedWidget("item13", smeBSBObjectClass, menushell,
974 item_13_arg, XtNumber(item_13_arg));
975 XtAddCallback(item_13, "callback", editsenderAction, (XtPointer)message_id);
977 /* Arrange that the menu pops up with the first item selected. */
979 xs_SetValues(menushell, 1, "popupOnEntry", item_1);
981 /* Flag that the menu is up to suppress queue updates. */
986 /* End of em_menu.c */