1 /*************************************************
3 *************************************************/
5 /* Copyright (c) University of Cambridge 1995 - 2016 */
6 /* See the file NOTICE for conditions of use and distribution. */
11 /* This module contains code for handling the popup menus. */
13 static Widget menushell;
14 static Widget queue_text_sink;
15 static Widget dialog_shell, dialog_widget;
17 static Widget text_create(uschar *, int);
19 static int highlighted_start, highlighted_end, highlighted_x, highlighted_y;
23 static Arg queue_get_arg[] = {
24 { "textSink", (XtArgVal)NULL },
25 { "textSource", (XtArgVal)NULL },
26 { "string", (XtArgVal)NULL } };
28 static Arg dialog_arg[] = {
29 { "label", (XtArgVal)"dialog" },
30 { "value", (XtArgVal)"value" } };
32 static Arg get_pos_args[] = {
33 {"x", (XtArgVal)NULL },
34 {"y", (XtArgVal)NULL } };
36 static Arg menushell_arg[] = {
37 { "label", (XtArgVal)NULL } };
39 static Arg button_arg[] = {
40 { XtNfromVert, (XtArgVal) NULL }, /* must be first */
41 { XtNlabel, (XtArgVal) " Dismiss " },
42 { "left", XawChainLeft },
43 { "right", XawChainLeft },
44 { "top", XawChainBottom },
45 { "bottom", XawChainBottom } };
47 static Arg text_arg[] = {
48 { XtNfromVert, (XtArgVal) NULL }, /* must be first */
49 { "editType", XawtextEdit },
50 { "string", (XtArgVal)"" }, /* dummy to get it going */
51 { "scrollVertical", XawtextScrollAlways },
52 { "wrap", XawtextWrapWord },
53 { "top", XawChainTop },
54 { "bottom", XawChainBottom } };
56 static Arg item_1_arg[] = {
57 { XtNfromVert, (XtArgVal)NULL }, /* must be first */
58 { "label", (XtArgVal)" Message log" } };
60 static Arg item_2_arg[] = {
61 { XtNfromVert, (XtArgVal) NULL }, /* must be first */
62 { "label", (XtArgVal)" Headers" } };
64 static Arg item_3_arg[] = {
65 { XtNfromVert, (XtArgVal) NULL }, /* must be first */
66 { "label", (XtArgVal)" Body" } };
68 static Arg item_4_arg[] = {
69 { XtNfromVert, (XtArgVal) NULL }, /* must be first */
70 { "label", (XtArgVal)" Deliver message" } };
72 static Arg item_5_arg[] = {
73 { XtNfromVert, (XtArgVal) NULL }, /* must be first */
74 { "label", (XtArgVal)" Freeze message" } };
76 static Arg item_6_arg[] = {
77 { XtNfromVert, (XtArgVal) NULL }, /* must be first */
78 { "label", (XtArgVal)" Thaw message" } };
80 static Arg item_7_arg[] = {
81 { XtNfromVert, (XtArgVal) NULL }, /* must be first */
82 { "label", (XtArgVal)" Give up on msg" } };
84 static Arg item_8_arg[] = {
85 { XtNfromVert, (XtArgVal) NULL }, /* must be first */
86 { "label", (XtArgVal)" Remove message" } };
88 static Arg item_9_arg[] = {
89 { XtNfromVert, (XtArgVal) NULL }, /* must be first */
90 { "label", (XtArgVal)"----------------" } };
92 static Arg item_10_arg[] = {
93 { XtNfromVert, (XtArgVal) NULL }, /* must be first */
94 { "label", (XtArgVal)" Add recipient" } };
96 static Arg item_11_arg[] = {
97 { XtNfromVert, (XtArgVal) NULL }, /* must be first */
98 { "label", (XtArgVal)" Mark delivered" } };
100 static Arg item_12_arg[] = {
101 { XtNfromVert, (XtArgVal) NULL }, /* must be first */
102 { "label", (XtArgVal)" Mark all delivered" } };
104 static Arg item_13_arg[] = {
105 { XtNfromVert, (XtArgVal) NULL }, /* must be first */
106 { "label", (XtArgVal)" Edit sender" } };
108 static Arg item_99_arg[] = {
109 { XtNfromVert, (XtArgVal) NULL }, /* must be first */
110 { "label", (XtArgVal)" " } };
114 /*************************************************
115 * Destroy the menu when popped down *
116 *************************************************/
118 static void popdownAction(Widget w, XtPointer client_data, XtPointer call_data)
120 client_data = client_data; /* Keep picky compilers happy */
121 call_data = call_data;
122 if (highlighted_x >= 0)
123 XawTextSinkDisplayText(queue_text_sink,
124 highlighted_x, highlighted_y,
125 highlighted_start, highlighted_end, 0);
132 /*************************************************
133 * Display the message log *
134 *************************************************/
136 static void msglogAction(Widget w, XtPointer client_data, XtPointer call_data)
140 Widget text = text_create((uschar *)client_data, text_depth);
143 w = w; /* Keep picky compilers happy */
144 call_data = call_data;
146 /* End up with the split version, so message looks right when non-exist */
148 for (i = 0; i < (spool_is_split? 2:1); i++)
150 message_subdir[0] = (i != 0)? ((uschar *)client_data)[5] : 0;
151 sprintf(CS buffer, "%s/msglog/%s/%s", spool_directory, message_subdir,
152 (uschar *)client_data);
153 f = fopen(CS buffer, "r");
154 if (f != NULL) break;
158 text_showf(text, "%s: %s\n", buffer, strerror(errno));
161 while (Ufgets(buffer, 256, f) != NULL) text_show(text, buffer);
168 /*************************************************
169 * Display the message body *
170 *************************************************/
172 static void bodyAction(Widget w, XtPointer client_data, XtPointer call_data)
176 Widget text = text_create((uschar *)client_data, text_depth);
179 w = w; /* Keep picky compilers happy */
180 call_data = call_data;
182 for (i = 0; i < (spool_is_split? 2:1); i++)
184 message_subdir[0] = (i != 0)? ((uschar *)client_data)[5] : 0;
185 sprintf(CS buffer, "%s/input/%s/%s-D", spool_directory, message_subdir,
186 (uschar *)client_data);
187 f = fopen(CS buffer, "r");
188 if (f != NULL) break;
192 text_showf(text, "Failed to open file: %s\n", strerror(errno));
196 while (Ufgets(buffer, 256, f) != NULL)
198 text_show(text, buffer);
199 count += Ustrlen(buffer);
200 if (count > body_max)
202 text_show(text, US"\n*** Message length exceeds BODY_MAX ***\n");
212 /*************************************************
213 * Do something to a message *
214 *************************************************/
216 /* The output is not shown in a window for non-delivery actions that succeed,
217 unless action_output is set. We can't, however, tell until we have run
218 the command whether we want the output or not, so the pipe has to be set up in
221 static void ActOnMessage(uschar *id, uschar *action, uschar *address_arg)
225 int delivery = Ustrcmp(action + Ustrlen(action) - 2, "-M") == 0;
226 uschar *quote = US"";
228 uschar *qualify = US"";
233 /* If the address arg is not empty and does not contain @ and there is a
234 qualify domain, qualify it. (But don't qualify '<>'.)*/
236 if (address_arg[0] != 0)
239 if (Ustrchr(address_arg, '@') == NULL &&
240 Ustrcmp(address_arg, "<>") != 0 &&
241 qualify_domain != NULL &&
242 qualify_domain[0] != 0)
245 qualify = qualify_domain;
248 sprintf(CS buffer, "%s %s %s %s %s %s%s%s%s%s", exim_path,
249 (alternate_config == NULL)? US"" : US"-C",
250 (alternate_config == NULL)? US"" : alternate_config,
251 action, id, quote, address_arg, at, qualify, quote);
253 /* If we know we are going to need the window, create it now. */
255 if (action_output || delivery)
257 text = text_create(id, text_depth);
258 text_showf(text, "%s\n", buffer);
261 /* Create the pipe for output. Remember, on most systems pipe[0] is
262 for reading and pipe[1] is for writing! Solaris, with its two-way
265 if (pipe(pipe_fd) != 0)
269 text = text_create(id, text_depth);
270 text_showf(text, "%s\n", buffer);
272 text_show(text, US"*** Failed to create pipe ***\n");
276 if ( fcntl(pipe_fd[0], F_SETFL, O_NONBLOCK)
277 || fcntl(pipe_fd[1], F_SETFL, O_NONBLOCK))
279 perror("set nonblocking on pipe");
283 /* Delivering a message can take some time, and we want to show the
284 output as it goes along. This requires subprocesses and is coded below. For
285 other commands, we can assume an immediate response, and so need not waste
286 resources with subprocesses. If action_output is FALSE, don't show the
292 int save_stdout = dup(1);
293 int save_stderr = dup(2);
302 rc = system(CS buffer);
307 if (action_output || rc != 0)
311 text = text_create(id, text_depth);
312 text_showf(text, "%s\n", buffer);
314 while ((count = read(pipe_fd[0], buffer, 254)) > 0)
317 text_show(text, buffer);
323 dup2(save_stdout, 1);
324 dup2(save_stderr, 2);
328 /* If action was to change the sender, and it succeeded, we have to
329 update the in-store data. */
331 if (rc == 0 && Ustrcmp(action + Ustrlen(action) - 4, "-Mes") == 0)
333 queue_item *q = find_queue(id, queue_noop, 0);
336 if (q->sender != NULL) store_free(q->sender);
337 q->sender = store_malloc(Ustrlen(address_arg) + 1);
338 Ustrcpy(q->sender, address_arg);
342 /* If configured, cause a display update and return */
344 if (action_queue_update) tick_queue_accumulator = 999999;
348 /* Message is to be delivered. Ensure that it is marked unfrozen,
349 because nothing will get written to the log to show that this has
350 happened. (Other freezing/unfreezings get logged and picked up from
353 qq = find_queue(id, queue_noop, 0);
354 if (qq != NULL) qq->frozen = FALSE;
356 /* New, asynchronous code runs in a subprocess for commands that
357 will take some time. The main process does not wait. There is a
358 SIGCHLD handler in the main program that cleans up any terminating
361 if ((pid = fork()) == 0)
378 /* Main process - set up an item for the main ticker to watch. */
380 if (pid < 0) text_showf(text, "Failed to fork: %s\n", strerror(errno)); else
382 pipe_item *p = (pipe_item *)store_malloc(sizeof(pipe_item));
386 text_show(text, US"Run out of store\n");
393 p->next = pipe_chain;
403 /*************************************************
404 * Cause a message to be delivered *
405 *************************************************/
407 static void deliverAction(Widget w, XtPointer client_data, XtPointer call_data)
409 w = w; /* Keep picky compilers happy */
410 call_data = call_data;
411 ActOnMessage((uschar *)client_data, US"-v -M", US"");
416 /*************************************************
417 * Cause a message to be Frozen *
418 *************************************************/
420 static void freezeAction(Widget w, XtPointer client_data, XtPointer call_data)
422 w = w; /* Keep picky compilers happy */
423 call_data = call_data;
424 ActOnMessage((uschar *)client_data, US"-Mf", US"");
429 /*************************************************
430 * Cause a message to be thawed *
431 *************************************************/
433 static void thawAction(Widget w, XtPointer client_data, XtPointer call_data)
435 w = w; /* Keep picky compilers happy */
436 call_data = call_data;
437 ActOnMessage((uschar *)client_data, US"-Mt", US"");
442 /*************************************************
443 * Take action using dialog data *
444 *************************************************/
446 /* This function is called after a dialog box has been filled
447 in. It is global because it is set up in the action table at
448 start-up time. If the string is empty, do nothing. */
450 XtActionProc dialogAction(Widget w, XEvent *event, String *ss, Cardinal *c)
452 uschar *s = US XawDialogGetValueString(dialog_widget);
454 w = w; /* Keep picky compilers happy */
459 XtPopdown((Widget)dialog_shell);
460 XtDestroyWidget((Widget)dialog_shell);
461 while (isspace(*s)) s++;
464 if (actioned_message[0] != 0)
465 ActOnMessage(actioned_message, action_required, s);
467 NonMessageDialogue(s); /* When called from somewhere else */
474 /*************************************************
475 * Create a dialog box *
476 *************************************************/
478 /* The focus is grabbed exclusively, so nothing else can
479 be done to the application until the box is filled in. This
480 function is also used by the Hide button handler. */
482 void create_dialog(uschar *label, uschar *value)
485 Dimension x, y, xx, yy;
486 XtTranslations pop_trans;
489 /* Get the position of a reference widget so the dialog box can be put
492 get_pos_args[0].value = (XtArgVal)(&x);
493 get_pos_args[1].value = (XtArgVal)(&y);
494 XtGetValues(dialog_ref_widget, get_pos_args, 2);
496 /* When this is not a message_specific thing, the position of the reference
497 widget is relative to the window. Get the position of the top level widget and
498 add to the position. */
500 if (dialog_ref_widget != menushell)
502 get_pos_args[0].value = (XtArgVal)(&xx);
503 get_pos_args[1].value = (XtArgVal)(&yy);
504 XtGetValues(toplevel_widget, get_pos_args, 2);
509 /* Create a transient shell for the dialog box. */
511 XtSetArg(warg[0], XtNtransientFor, queue_widget);
512 XtSetArg(warg[1], XtNx, x + 50);
513 XtSetArg(warg[2], XtNy, y + 50);
514 XtSetArg(warg[3], XtNallowShellResize, True);
515 dialog_shell = XtCreatePopupShell("forDialog", transientShellWidgetClass,
516 toplevel_widget, warg, 4);
518 /* Create the dialog box. */
520 dialog_arg[0].value = (XtArgVal)label;
521 dialog_arg[1].value = (XtArgVal)value;
522 dialog_widget = XtCreateManagedWidget("dialog", dialogWidgetClass, dialog_shell,
523 dialog_arg, XtNumber(dialog_arg));
525 /* Get the text widget from within the dialog box, give it the keyboard focus,
526 make it wider than the default, and override its translations to make Return
527 call the dialog action function. */
529 text = XtNameToWidget(dialog_widget, "value");
530 XawTextSetInsertionPoint(text, Ustrlen(value));
531 XtSetKeyboardFocus(dialog_widget, text);
532 xs_SetValues(text, 1, "width", 200);
533 pop_trans = XtParseTranslationTable(
534 "<Key>Return: dialogAction()\n");
535 XtOverrideTranslations(text, pop_trans);
537 /* Pop the thing up. */
539 XtPopup(dialog_shell, XtGrabExclusive);
547 /*************************************************
548 * Cause a recipient to be added *
549 *************************************************/
551 /* This just sets up the dialog box; the action happens when it has been filled
554 static void addrecipAction(Widget w, XtPointer client_data, XtPointer call_data)
556 w = w; /* Keep picky compilers happy */
557 call_data = call_data;
558 Ustrncpy(actioned_message, client_data, 24);
559 actioned_message[23] = '\0';
560 action_required = US"-Mar";
561 dialog_ref_widget = menushell;
562 create_dialog(US"Recipient address to add?", US"");
567 /*************************************************
568 * Cause an address to be marked delivered *
569 *************************************************/
571 static void markdelAction(Widget w, XtPointer client_data, XtPointer call_data)
573 w = w; /* Keep picky compilers happy */
574 call_data = call_data;
575 Ustrncpy(actioned_message, client_data, 24);
576 actioned_message[23] = '\0';
577 action_required = US"-Mmd";
578 dialog_ref_widget = menushell;
579 create_dialog(US"Recipient address to mark delivered?", US"");
583 /*************************************************
584 * Cause all addresses to be marked delivered *
585 *************************************************/
587 static void markalldelAction(Widget w, XtPointer client_data, XtPointer call_data)
589 w = w; /* Keep picky compilers happy */
590 call_data = call_data;
591 ActOnMessage(US client_data, US"-Mmad", US"");
595 /*************************************************
596 * Edit the message's sender *
597 *************************************************/
599 static void editsenderAction(Widget w, XtPointer client_data,
604 w = w; /* Keep picky compilers happy */
605 call_data = call_data;
606 Ustrncpy(actioned_message, client_data, 24);
607 actioned_message[23] = '\0';
608 q = find_queue(actioned_message, queue_noop, 0);
609 sender = !q ? US"" : q->sender[0] == 0 ? US"<>" : q->sender;
610 action_required = US"-Mes";
611 dialog_ref_widget = menushell;
612 create_dialog(US"New sender address?", sender);
616 /*************************************************
617 * Cause a message to be returned to sender *
618 *************************************************/
620 static void giveupAction(Widget w, XtPointer client_data, XtPointer call_data)
622 w = w; /* Keep picky compilers happy */
623 call_data = call_data;
624 ActOnMessage((uschar *)client_data, US"-v -Mg", US"");
629 /*************************************************
630 * Cause a message to be cancelled *
631 *************************************************/
633 static void removeAction(Widget w, XtPointer client_data, XtPointer call_data)
635 w = w; /* Keep picky compilers happy */
636 call_data = call_data;
637 ActOnMessage((uschar *)client_data, US"-Mrm", US"");
642 /*************************************************
643 * Display a message's headers *
644 *************************************************/
646 static void headersAction(Widget w, XtPointer client_data, XtPointer call_data)
649 header_line *h, *next;
650 Widget text = text_create((uschar *)client_data, text_depth);
653 w = w; /* Keep picky compilers happy */
654 call_data = call_data;
656 /* Remember the point in the dynamic store so we can recover to it afterwards.
657 Then use Exim's function to read the header. */
659 reset_point = store_get(0);
661 sprintf(CS buffer, "%s-H", (uschar *)client_data);
662 if (spool_read_header(buffer, TRUE, FALSE) != spool_read_OK)
664 if (errno == ERRNO_SPOOLFORMAT)
667 sprintf(CS big_buffer, "%s/input/%s", spool_directory, buffer);
668 if (Ustat(big_buffer, &statbuf) == 0)
669 text_showf(text, "Format error in spool file %s: size=%d\n", buffer,
671 else text_showf(text, "Format error in spool file %s\n", buffer);
673 else text_showf(text, "Read error for spool file %s\n", buffer);
674 store_reset(reset_point);
678 if (sender_address != NULL)
680 text_showf(text, "%s sender: <%s>\n", sender_local? "Local" : "Remote",
684 if (recipients_list != NULL)
687 text_show(text, US"Recipients:\n");
688 for (i = 0; i < recipients_count; i++)
690 text_showf(text, " %s %s\n",
691 (tree_search(tree_nonrecipients, recipients_list[i].address) == NULL)?
692 " ":"*", recipients_list[i].address);
694 text_show(text, US"\n");
697 for (h = header_list; h != NULL; h = next)
700 text_showf(text, "%c ", h->type); /* Don't push h->text through a %s */
701 text_show(text, h->text); /* expansion as it may be v large */
704 store_reset(reset_point);
710 /*************************************************
711 * Dismiss a text window *
712 *************************************************/
714 static void dismissAction(Widget w, XtPointer client_data, XtPointer call_data)
716 pipe_item *p = pipe_chain;
718 w = w; /* Keep picky compilers happy */
719 call_data = call_data;
721 XtPopdown((Widget)client_data);
722 XtDestroyWidget((Widget)client_data);
724 /* If this is a text widget for a sub-process, clear it out of
725 the chain so that subsequent data doesn't try to use it. We have
726 to search the parents of the saved widget to see if one of them
727 is what we have just destroyed. */
731 Widget pp = p->widget;
734 if (pp == (Widget)client_data) { p->widget = NULL; return; }
743 /*************************************************
744 * Set up popup text window *
745 *************************************************/
747 static Widget text_create(uschar *name, int height)
749 Widget textshell, form, text, button;
751 /* Create a popup shell widget to display as an additional
754 textshell = XtCreatePopupShell("textshell", topLevelShellWidgetClass,
755 toplevel_widget, NULL, 0);
756 xs_SetValues(textshell, 4,
762 /* Create a form widget, containing the text widget and the
763 dismiss button widget. */
765 form = XtCreateManagedWidget("textform", formWidgetClass,
767 xs_SetValues(form, 1, "defaultDistance", 8);
769 text = XtCreateManagedWidget("texttext", asciiTextWidgetClass,
770 form, text_arg, XtNumber(text_arg));
771 xs_SetValues(text, 4,
772 "editType", XawtextAppend,
775 "translations", text_trans);
776 XawTextDisplayCaret(text, TRUE);
778 /* Use the same font as for the queue display */
780 if (queue_font != NULL)
782 XFontStruct *f = XLoadQueryFont(X_display, CS queue_font);
783 if (f != NULL) xs_SetValues(text, 1, "font", f);
786 button_arg[0].value = (XtArgVal)text;
787 button = XtCreateManagedWidget("dismiss", commandWidgetClass,
788 form, button_arg, XtNumber(button_arg));
789 XtAddCallback(button, "callback", dismissAction, (XtPointer)textshell);
791 /* Get the toplevel popup displayed, and yield the text widget so
792 that text can be put into it. */
794 XtPopup(textshell, XtGrabNone);
801 /*************************************************
802 * Set up menu in queue window *
803 *************************************************/
805 /* We have added an action table that causes this function to
806 be called, and set up button 2 in the text widgets to call it. */
808 void menu_create(Widget w, XEvent *event, String *actargs, Cardinal *count)
814 Widget src, menu_line, item_1, item_2, item_3, item_4,
815 item_5, item_6, item_7, item_8, item_9, item_10, item_11,
817 XtTranslations menu_trans = XtParseTranslationTable(
818 "<EnterWindow>: highlight()\n\
819 <LeaveWindow>: unhighlight()\n\
820 <BtnMotion>: highlight()\n\
821 <BtnUp>: MenuPopdown()notify()unhighlight()\n\
824 actargs = actargs; /* Keep picky compilers happy */
827 /* Get the sink and source and the current text pointer */
829 queue_get_arg[0].value = (XtArgVal)(&queue_text_sink);
830 queue_get_arg[1].value = (XtArgVal)(&src);
831 queue_get_arg[2].value = (XtArgVal)(&s);
832 XtGetValues(w, queue_get_arg, 3);
834 /* Find the line number of the pointer in the window, and the
835 character offset of the top lefthand of the window. */
837 line = (event->xbutton).y / XawTextSinkMaxHeight(queue_text_sink, 1);
838 p = XawTextTopPosition(w);
840 /* Find the start of the line on which the button was clicked. */
845 while (s[p] != 0 && s[p++] != '\n');
848 /* Now pointing either at 0 or 1st uschar after \n, or very 1st uschar.
849 If 0, the click was beyond the end of the data; just set up a dummy
850 menu. (Not easy to ignore as several actions are specified for the
851 mouse click and it expects this one to set up a menu.) If on a
852 continuation line, move back to the main line. */
856 menushell_arg[0].value = (XtArgVal)"No message selected";
857 menushell = XtCreatePopupShell("menu", simpleMenuWidgetClass,
858 queue_widget, menushell_arg, XtNumber(menushell_arg));
859 XtAddCallback(menushell, "popdownCallback", popdownAction, NULL);
860 xs_SetValues(menushell, 2,
861 "cursor", XCreateFontCursor(X_display, XC_arrow),
862 "translations", menu_trans);
864 /* To keep the widgets in XFree86 happy, we have to create at least one menu
865 item, it seems. (Openwindows doesn't mind a menu with no items.) Otherwise
866 there's a complaint about a zero width menu, and a crash. */
868 menu_line = XtCreateManagedWidget("line", smeLineObjectClass, menushell,
871 item_99_arg[0].value = (XtArgVal)menu_line;
872 (void)XtCreateManagedWidget("item99", smeBSBObjectClass, menushell,
873 item_99_arg, XtNumber(item_99_arg));
879 while (p > 0 && s[p+11] == ' ')
883 while (p > 0 && s[p-1] != '\n') p--;
886 /* Now pointing at first character of a main line. */
888 Ustrncpy(message_id, s+p+11, MESSAGE_ID_LENGTH);
889 message_id[MESSAGE_ID_LENGTH] = 0;
891 /* Highlight the line being menued, and save its parameters so that it
892 can be de-highlighted at popdown. */
894 highlighted_start = highlighted_end = p;
895 while (s[highlighted_end] != '\n') highlighted_end++;
897 highlighted_y = line * XawTextSinkMaxHeight(queue_text_sink, 1) + 2;
899 XawTextSinkDisplayText(queue_text_sink,
900 highlighted_x, highlighted_y,
901 highlighted_start, highlighted_end, 1);
903 /* Create the popup shell and the other widgets that comprise the menu.
904 Set the translations and pointer shape, and add the callback pointers. */
906 menushell_arg[0].value = (XtArgVal)message_id;
907 menushell = XtCreatePopupShell("menu", simpleMenuWidgetClass,
908 queue_widget, menushell_arg, XtNumber(menushell_arg));
909 XtAddCallback(menushell, "popdownCallback", popdownAction, NULL);
911 xs_SetValues(menushell, 2,
912 "cursor", XCreateFontCursor(X_display, XC_arrow),
913 "translations", menu_trans);
915 menu_line = XtCreateManagedWidget("line", smeLineObjectClass, menushell,
918 item_1_arg[0].value = (XtArgVal)menu_line;
919 item_1 = XtCreateManagedWidget("item1", smeBSBObjectClass, menushell,
920 item_1_arg, XtNumber(item_1_arg));
921 XtAddCallback(item_1, "callback", msglogAction, (XtPointer)message_id);
923 item_2_arg[0].value = (XtArgVal)item_1;
924 item_2 = XtCreateManagedWidget("item2", smeBSBObjectClass, menushell,
925 item_2_arg, XtNumber(item_2_arg));
926 XtAddCallback(item_2, "callback", headersAction, (XtPointer)message_id);
928 item_3_arg[0].value = (XtArgVal)item_2;
929 item_3 = XtCreateManagedWidget("item3", smeBSBObjectClass, menushell,
930 item_3_arg, XtNumber(item_3_arg));
931 XtAddCallback(item_3, "callback", bodyAction, (XtPointer)message_id);
933 item_4_arg[0].value = (XtArgVal)item_3;
934 item_4 = XtCreateManagedWidget("item4", smeBSBObjectClass, menushell,
935 item_4_arg, XtNumber(item_4_arg));
936 XtAddCallback(item_4, "callback", deliverAction, (XtPointer)message_id);
938 item_5_arg[0].value = (XtArgVal)item_4;
939 item_5 = XtCreateManagedWidget("item5", smeBSBObjectClass, menushell,
940 item_5_arg, XtNumber(item_5_arg));
941 XtAddCallback(item_5, "callback", freezeAction, (XtPointer)message_id);
943 item_6_arg[0].value = (XtArgVal)item_5;
944 item_6 = XtCreateManagedWidget("item6", smeBSBObjectClass, menushell,
945 item_6_arg, XtNumber(item_6_arg));
946 XtAddCallback(item_6, "callback", thawAction, (XtPointer)message_id);
948 item_7_arg[0].value = (XtArgVal)item_6;
949 item_7 = XtCreateManagedWidget("item7", smeBSBObjectClass, menushell,
950 item_7_arg, XtNumber(item_7_arg));
951 XtAddCallback(item_7, "callback", giveupAction, (XtPointer)message_id);
953 item_8_arg[0].value = (XtArgVal)item_7;
954 item_8 = XtCreateManagedWidget("item8", smeBSBObjectClass, menushell,
955 item_8_arg, XtNumber(item_8_arg));
956 XtAddCallback(item_8, "callback", removeAction, (XtPointer)message_id);
958 item_9_arg[0].value = (XtArgVal)item_8;
959 item_9 = XtCreateManagedWidget("item9", smeBSBObjectClass, menushell,
960 item_9_arg, XtNumber(item_9_arg));
962 item_10_arg[0].value = (XtArgVal)item_9;
963 item_10 = XtCreateManagedWidget("item10", smeBSBObjectClass, menushell,
964 item_10_arg, XtNumber(item_10_arg));
965 XtAddCallback(item_10, "callback", addrecipAction, (XtPointer)message_id);
967 item_11_arg[0].value = (XtArgVal)item_10;
968 item_11 = XtCreateManagedWidget("item11", smeBSBObjectClass, menushell,
969 item_11_arg, XtNumber(item_11_arg));
970 XtAddCallback(item_11, "callback", markdelAction, (XtPointer)message_id);
972 item_12_arg[0].value = (XtArgVal)item_11;
973 item_12 = XtCreateManagedWidget("item12", smeBSBObjectClass, menushell,
974 item_12_arg, XtNumber(item_12_arg));
975 XtAddCallback(item_12, "callback", markalldelAction, (XtPointer)message_id);
977 item_13_arg[0].value = (XtArgVal)item_12;
978 item_13 = XtCreateManagedWidget("item13", smeBSBObjectClass, menushell,
979 item_13_arg, XtNumber(item_13_arg));
980 XtAddCallback(item_13, "callback", editsenderAction, (XtPointer)message_id);
982 /* Arrange that the menu pops up with the first item selected. */
984 xs_SetValues(menushell, 1, "popupOnEntry", item_1);
986 /* Flag that the menu is up to suppress queue updates. */
991 /* End of em_menu.c */