home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / x / volume13 / xmail / part08 / actions.c
C/C++ Source or Header  |  1991-06-15  |  29KB  |  1,023 lines

  1. /*
  2.  * xmail - X window system interface to the mail program
  3.  *
  4.  * Copyright 1989 The University of Texas at Austin
  5.  *
  6.  * Author:    Po Cheung
  7.  * Date:    March 10, 1989
  8.  *
  9.  * Permission to use, copy, modify, and distribute this software and
  10.  * its documentation for any purpose and without fee is hereby granted,
  11.  * provided that the above copyright notice appear in all copies and that
  12.  * both that copyright notice and this permission notice appear in
  13.  * supporting documentation.  The University of Texas at Austin makes no 
  14.  * representations about the suitability of this software for any purpose.  
  15.  * It is provided "as is" without express or implied warranty.
  16.  *
  17.  * Copyright 1990 by National Semiconductor Corporation
  18.  *
  19.  * Permission to use, copy, modify, and distribute this software and its
  20.  * documentation for any purpose is hereby granted without fee, provided that
  21.  * the above copyright notice appear in all copies and that both that
  22.  * copyright notice and this permission notice appear in supporting
  23.  * documentation, and that the name of National Semiconductor Corporation not
  24.  * be used in advertising or publicity pertaining to distribution of the
  25.  * software without specific, written prior permission.
  26.  *
  27.  * NATIONAL SEMICONDUCTOR CORPORATION MAKES NO REPRESENTATIONS ABOUT THE
  28.  * SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE.  IT IS PROVIDED "AS IS"
  29.  * WITHOUT EXPRESS OR IMPLIED WARRANTY.  NATIONAL SEMICONDUCTOR CORPORATION
  30.  * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED
  31.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  IN NO
  32.  * EVENT SHALL NATIONAL SEMICONDUCTOR CORPORATION BE LIABLE FOR ANY SPECIAL,
  33.  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  34.  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  35.  * OR OTHER TORTUOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  36.  * PERFORMANCE OF THIS SOFTWARE.
  37.  *
  38.  * The following software modules were created and are Copyrighted by
  39.  * National Semiconductor Corporation:
  40.  *
  41.  *  1. CheckInsert
  42.  *  2. DeleteChar
  43.  *  3. EraseIt:
  44.  *  4. DoCmd:
  45.  *  5. DoNothing:
  46.  *  6. DoReply:
  47.  *  7. DoSave:
  48.  *  8. DoSelected:
  49.  *  9. Folder:
  50.  * 10. Iconify:
  51.  * 11. MyNotify:
  52.  * 12. NextField:
  53.  * 13. PrintMsg:
  54.  * 14. SetAliases:
  55.  * 15. SetFolders:
  56.  * 16. SetMenu:
  57.  * 17. SetPopup:
  58.  * 18. SetSelect: and
  59.  * 19. ShowHelp.
  60.  *
  61.  * Author:  Michael C. Wagnitz - National Semiconductor Corporation
  62.  *
  63.  */
  64.  
  65.  
  66. #include "global.h"
  67. #include "xmailregex.h"
  68. #include <ctype.h>
  69.  
  70. #ifdef USE_DIRENT
  71. #include <dirent.h>
  72. #else
  73. #include <sys/dir.h>
  74. #endif
  75.  
  76.  
  77. /*
  78. ** @(#)CheckInsert() - prevents the user from munging up the File: prompt.
  79. ** If the current insertion point is less than the minimum StartPos, move
  80. ** the insertion point to the StartPos.
  81. */
  82. /* ARGSUSED */
  83. XtActionProc
  84. CheckInsert(w, event, params, num_params)
  85. Widget w;
  86. XEvent *event;
  87. String *params;
  88. Cardinal *num_params;
  89. {
  90.  if (XawTextGetInsertionPoint(w) < StartPos)
  91.      XawTextSetInsertionPoint(w, StartPos);
  92. } /* CheckInsert */
  93.  
  94.  
  95. /*
  96. ** @(#)EraseIt() - Delete the specified portion of text.
  97. */
  98. void
  99. EraseIt(w, i, pos)
  100. Widget    w;
  101. XawTextPosition    i, pos;
  102. {
  103.  XawTextBlock    textblock;
  104.  
  105.  textblock.firstPos = 0;
  106.  textblock.length   = 0;
  107.  textblock.ptr      = "";
  108.  
  109.  XawTextReplace(w, i, pos, &textblock);
  110.  
  111.  XawTextSetInsertionPoint(w, i);
  112. } /* EraseIt */
  113.  
  114.  
  115. /*
  116. ** @(#)DeleteChar() - prevents the user from deleting past the File: prompt.
  117. ** If the current insertion point is greater than the minimum StartPos, then
  118. ** delete the previous character.
  119. */
  120. /* ARGSUSED */
  121. XtActionProc
  122. DeleteChar(w, event, params, num_params)
  123. Widget w;
  124. XEvent *event;
  125. String *params;
  126. Cardinal *num_params;
  127. {
  128.  XawTextPosition pos, i;
  129.  
  130.  pos = XawTextGetInsertionPoint(w);
  131.  if (pos > StartPos) {
  132.     i = pos - 1;
  133.     EraseIt(w, i, pos);
  134.    }
  135. } /* DeleteChar */
  136.  
  137.  
  138. /*
  139. ** @(#)DeleteLine() - Deletes the entire current line from the file window.
  140. **                    Simulates the action of the KILL character (ctrl-U).
  141. */
  142. /* ARGSUSED */
  143. XtActionProc
  144. DeleteLine(w, event, params, num_params)
  145. Widget w;
  146. XEvent *event;
  147. String *params;
  148. Cardinal *num_params;
  149. {
  150.  XawTextPosition pos, i;
  151.  
  152.  
  153.  pos = XawTextGetInsertionPoint(w);
  154.  if (pos > StartPos) {
  155.     for (i = pos; i > StartPos && FileBuf[i - 1] != '\n'; i--);
  156.  
  157.     EraseIt(w, i, pos);
  158.    }
  159. } /* DeleteLine */
  160.  
  161.  
  162. /*
  163. ** @(#)DeleteWord() - Erases the preceding word in the fileWindow buffer.
  164. ** Simulates the action of the WERASE character (ctrl-W).
  165. */
  166. /* ARGSUSED */
  167. XtActionProc
  168. DeleteWord(w, event, params, num_params)
  169. Widget w;
  170. XEvent *event;
  171. String *params;
  172. Cardinal *num_params;
  173. {
  174.  XawTextPosition pos, i;
  175.  
  176.  pos = XawTextGetInsertionPoint(w);
  177.  if (pos > StartPos) {
  178.     for (i = pos; i > StartPos && FileBuf[i - 1] == ' '; i--);
  179.     for (; i > StartPos && FileBuf[i - 1] != ' '; i--);
  180.  
  181.     EraseIt(w, i, pos);
  182.    }
  183. } /* DeleteWord */
  184.  
  185.  
  186. /* ARGSUSED */
  187. /*
  188. ** @(#)DoCmd() - send (multi-word) command to mail
  189. */
  190. XtActionProc
  191. DoCmd(w, event, params, num_params)
  192. Widget        w;    /* unused */
  193. XEvent        *event;    /* unused */
  194. String        *params;
  195. Cardinal    *num_params;
  196. {
  197.  int        i, n;
  198.  char        buf[BUFSIZ];
  199.  Arg        args[1];
  200.  LabelWidget    lw = (LabelWidget) XtNameToWidget(toplevel, "topBox.titleBar.title");
  201.  
  202.  
  203.  SetCursor(1);
  204.  if (strcmp(params[0], "drop") == 0)
  205.     DropIt(w, *params, NULL);
  206.  else {
  207.     Command[0] = '\0';
  208.     for (i = 0; i < *num_params; i++) {
  209.         strcat(Command, params[i]);
  210.         strcat(Command, " ");
  211.        }
  212.     if (i)
  213.        Command[strlen(Command)-1] = '\0'; /* Drop the last trailing blank */
  214.     strcat(Command, "\n");
  215.  
  216.     if (mailpid) {            /* If connections are okay,... */
  217.        if ((n = match(&command_pattern, Command)) != C_FILE && n != C_NEWMAIL)
  218.           writeMail(Command);
  219.        else {                /* check for commit of any changes */
  220.           XtSetArg(args[0], XtNlabel, (XtArgVal) NULL);
  221.           XtGetValues(lw, args, ONE);
  222.           strcpy(buf, (char *)args[0].value);
  223.  
  224.           if (strcmp(&buf[strlen(buf)-7],"deleted") ||
  225.               strcmp(params[0], "inc") == 0 ||
  226.               Confirm("COMMIT all changes to this folder"))
  227.              writeMail(Command);
  228.          }
  229.       } else if (C_NEWMAIL != match(&command_pattern, Command))
  230.                 Bell("No current mail connection\n");    /* If not 'Newmail' */
  231.         else {
  232.              if (strcmp(mailargv[mailargc - 2], "-f") == 0) {
  233.                 mailargc -= 2;        /* throw away any folder argument */
  234.                 mailargv[mailargc] = NULL; /* and NULL end of argument list */
  235.                }
  236.              callMail(mailargc, mailargv); /* restart the mail connections */
  237.              strcpy(Command, "Start");    /* Let em know we've re-started */
  238.              UnsetNewmail(w, NULL, NULL);
  239.             }
  240.    }
  241. } /* DoCmd */
  242.  
  243.  
  244. /* ARGSUSED */
  245. /*
  246. ** @(#)DoNothing() - dummy action for unwanted button(s)
  247. */
  248. XtActionProc
  249. DoNothing(w, event, params, num_params)
  250. Widget w;
  251. XEvent *event;
  252. String *params;
  253. Cardinal *num_params;
  254. {
  255.  return;
  256. } /* DoNothing */
  257.  
  258.  
  259. /*
  260. ** @(#)DoReply() - call Reply() CallbackProc from an ActionProc
  261. */
  262. /* ARGSUSED */
  263. XtActionProc
  264. DoReply(w, event, params, num_params)
  265. Widget w;
  266. XEvent *event;
  267. String *params;
  268. Cardinal *num_params;
  269. {
  270.  Reply(w, *params, NULL);
  271. } /* DoReply */
  272.  
  273.  
  274. /*
  275. ** @(#)DoSave() - call Save() CallbackProc from an ActionProc
  276. */
  277. /* ARGSUSED */
  278. XtActionProc
  279. DoSave(w, event, params, num_params)
  280. Widget        w;
  281. XEvent        *event;
  282. String        *params;
  283. Cardinal    *num_params;
  284. {
  285.  Save(w, *params, NULL);
  286. } /* DoSave */
  287.  
  288.  
  289. /* ARGSUSED */
  290. /*
  291. ** @(#)DoSelected() - execute specified command using selected message number
  292. */
  293. XtActionProc
  294. DoSelected(w, event, params, num_params)
  295. Widget        w;
  296. XEvent        *event;
  297. String        *params;
  298. Cardinal    *num_params;
  299. {
  300.  int        num = 0;
  301.  
  302.  
  303.  SetCursor(1);
  304.  if (! mailpid) Bell("No current mail connection\n");
  305.  else if (num_params) {
  306.          if (*params[0] != 'n' && *params[0] != '-')
  307.             num = SelectionNumber(*params[0] == 'u');
  308.  
  309.          if (num) sprintf(Command, "%s %d\n", params[0], num);
  310.          else sprintf(Command, "%s\n", params[0]);
  311.  
  312.          writeMail(Command);
  313.  
  314.          if (strcmp(params[0], "preserve") == 0)
  315.             markIndex("P");
  316.         }
  317. } /* DoSelected */
  318.  
  319.  
  320. /*
  321. ** @(#)Folder() - change folders - must have specified folder name or error
  322. */
  323. /* ARGSUSED */
  324. XtActionProc
  325. Folder(w, event, params, num_params)
  326. Widget        w;
  327. XEvent        *event;
  328. String        *params;
  329. Cardinal    *num_params;
  330. {
  331.  char        buf[BUFSIZ];
  332.  Arg        args[1];
  333.  LabelWidget    lw = (LabelWidget) XtNameToWidget(toplevel, "topBox.titleBar.title");
  334.  XawTextPosition pos;
  335.  char        *p;
  336.  Cardinal    n;
  337.  
  338.  
  339.  SetCursor(1);                /* restore normally by next msg read */
  340.  pos = TextGetLastPos(XtNameToWidget(toplevel, "topBox.commandPanel.fileWindow"));
  341.  if ((n = pos - StartPos) <= 0) {
  342.     Bell("Specify a folder name (in the [File: ] box) first\n");
  343.    } else {
  344.     FileBuf[StartPos + n] = '\0';
  345.     p = FileBuf + StartPos;
  346.     if (mailpid) {            /* check for commit of any changes */
  347.        XtSetArg(args[0], XtNlabel, (XtArgVal) NULL);
  348.        XtGetValues(lw, args, ONE);
  349.        strcpy(buf, (char *)args[0].value);
  350.  
  351.        if (strcmp(&buf[strlen(buf) - 7], "deleted") ||
  352.            Confirm("COMMIT all changes to this folder")) {
  353.           sprintf(Command, "file %s\n", p);
  354.           writeMail(Command);
  355.          }
  356.       } else {
  357. /*
  358. ** We must first re-establish contact with the mail utility.
  359. ** This time, we indicate a specific mail folder to process.
  360. */
  361.        XMail.MFileName = XtNewString(p);
  362.        if (strcmp(mailargv[mailargc - 2], "-f") == 0) {
  363.           mailargv[mailargc - 1] = XMail.MFileName;
  364.          } else {
  365.           mailargv[mailargc++] = "-f";
  366.           mailargv[mailargc++] = XMail.MFileName;
  367.           mailargv[mailargc] = NULL;    /* list MUST be NULL terminated */
  368.          }
  369.        callMail(mailargc, mailargv);
  370.        strcpy(Command, "Start");    /* Let em know we've re-started */
  371.       }
  372.    }
  373. } /* Folder */
  374.  
  375.  
  376. /* ARGSUSED */
  377. /*
  378. ** @(#)Iconify() - request window iconification
  379. */
  380. XtActionProc
  381. Iconify(w, event, params, num_params)
  382. Widget w;
  383. XEvent *event;
  384. String *params;
  385. Cardinal *num_params;
  386. {
  387.  Display        *disp;
  388.  
  389.  disp = XtDisplay(toplevel);
  390.  
  391.  if (! XIconifyWindow(disp, XtWindow(toplevel), DefaultScreen(disp)))
  392.     XBell(XtDisplay(toplevel), 33);
  393. }
  394.  
  395.  
  396. /* ARGSUSED */
  397. /*
  398. ** @(#)MyNotify() - call widget callbacks with passed parameter
  399. */
  400. XtActionProc
  401. MyNotify(w, event, params, num_params)
  402. Widget        w;
  403. XEvent        *event;
  404. String        *params;
  405. Cardinal    *num_params;
  406. {
  407.  XtCallCallbacks(w, XtNcallback, *params);
  408. } /* MyNotify */
  409.  
  410.  
  411. /*
  412. ** @(#)NextField() - warps pointer to next field in the Send command window.
  413. ** This allows carriage return to focus attention on the next data requirement.
  414. */
  415. /* ARGSUSED */
  416. XtActionProc
  417. NextField(w, event, params, num_params)
  418. Widget w;
  419. XEvent *event;
  420. String *params;
  421. Cardinal *num_params;
  422. {
  423.  String    name;
  424.  Widget    shell;
  425.  
  426.  
  427.  if (strcmp(w->core.name, "Cc") == 0)
  428.     name = "Bcc"; else
  429.  if (strcmp(w->core.name, "Bcc") == 0)
  430.     name = "To"; else
  431.  if (strcmp(w->core.name, "To") == 0)
  432.     name = "Subject"; else
  433.     name = "Cc";
  434.  
  435.  if ((shell = XtNameToWidget(XtParent(w), name)) != NULL)
  436.     XWarpPointer(XtDisplay(toplevel), None, XtWindow(shell), 0,0,0,0, 10, 5);
  437.  
  438. } /* NextField */
  439.  
  440.  
  441. /*
  442. ** @(#)PrintMsg() - sends the selected mail message to the system printer
  443. */
  444. /* ARGSUSED */
  445. XtActionProc
  446. PrintMsg(w, event, params, num_params)
  447. Widget        w;
  448. XEvent        *event;
  449. String        *params;
  450. Cardinal    *num_params;
  451. {
  452.  char        *cp;
  453.  int        num;
  454.  
  455.  
  456.  SetCursor(1);
  457.  if (! mailpid) Bell("No current mail connection\n");
  458.  else {
  459.     num = SelectionNumber(False);    /* no current message returns zero */
  460.     if (! num) Bell("No messages to print.\n");
  461.     else {
  462.        cp = GetMailEnv("printmail");
  463.        if (! cp) {
  464.           sprintf(Command, "| %d \"lpr -p\"\n", num);
  465.          } else {
  466.           sprintf(Command, "| %d \"%s\"\n", num, cp);
  467.           XtFree(cp);
  468.          }
  469.        writeMail(Command);
  470.       }
  471.    }
  472. } /* PrintMsg */
  473.  
  474.  
  475. /*
  476. ** @(#)Quit() - call DoQuit() CallbackProc from the Quit ActionProc
  477. */
  478. /* ARGSUSED */
  479. XtActionProc
  480. Quit(w, event, params, num_params)
  481. Widget        w;    /* unused */
  482. XEvent        *event;    /* unused */
  483. String        *params;
  484. Cardinal    *num_params;
  485. {
  486.  if (event->type == ClientMessage &&
  487.     event->xclient.data.l[0] != wmDeleteWindow) {
  488.     XBell (XtDisplay(w), 0);
  489.     return;
  490.    }
  491.  
  492.  DoQuit(w, *params, NULL);
  493. } /* Quit */
  494.  
  495.  
  496. /*
  497. ** @(#)SetAliases() - create a menu list of alias names
  498. */
  499. /* ARGSUSED */
  500. XtActionProc
  501. SetAliases(w, event, params, num_params)
  502. Widget        w;
  503. XEvent        *event;
  504. String        *params;
  505. Cardinal    *num_params;
  506. {
  507.  Arg        args[7];
  508.  Cardinal    i, j, k, n;
  509.  Widget        bw, lw, popup, hold, left;
  510.  
  511.  static String l_Trans = "<Btn3Up>:    MenuPopdown(aliasList)";
  512.  
  513.  static String fl_trans = "<EnterWindow>: set() \n\
  514.                <LeaveWindow>: unset() \n\
  515.                     <Btn3Up>: notify()";
  516.  
  517.  static XtCallbackRec fl_callbacks[] = {
  518.         { (XtCallbackProc) GetAliasName, NULL },
  519.         { NULL,          NULL }
  520.        };
  521.  
  522.  
  523.  SetCursor(1);
  524.  popup = XtNameToWidget(w, "aliasList");
  525.  
  526.  if (! popup || popup->core.being_destroyed) {
  527.     XtSetArg(args[0], XtNtranslations, XtParseTranslationTable(l_Trans));
  528.     popup = XtCreatePopupShell("aliasList",overrideShellWidgetClass,w,args,1);
  529.  
  530.     (void) alias(NULL);            /* ensure our aliases list is set */
  531. /*
  532. ** determine proper label width by finding longest name length
  533. */
  534.     i = j = k = 0;
  535.     for (n = 0; aliases[n]; n++)
  536.         if ((k = strlen(aliases[n]->name)) > j) {
  537.            j = k;
  538.            i = n;
  539.           }
  540.  
  541.     if (j == 0) {                 /* If no alias names exist */
  542.        XtSetArg(args[0], XtNwidth, 1);        /* set these so MenuPopup */
  543.        XtSetArg(args[1], XtNheight, 1);        /* won't complain about a */
  544.        XtSetValues(popup, (ArgList) args, 2);    /* zero width or height  */
  545.        XtDestroyWidget(popup);        /* to bad MenuPopup() doesn't care */
  546.       } else {
  547. /*
  548. ** Make equal width command buttons which contain the alias names
  549. */
  550.        XtSetArg(args[0], XtNdefaultDistance, (XtArgVal) 1);
  551.        lw = XtCreateManagedWidget("table", formWidgetClass, popup, args, ONE);
  552.  
  553.        bw = left = NULL;
  554.        XtSetArg(args[0], XtNwidth, XTextWidth(XMail.buttonFont, aliases[i]->name, j) + 14);
  555.        XtSetArg(args[1], XtNfont, XMail.buttonFont);
  556.        XtSetArg(args[2], XtNtranslations, XtParseTranslationTable(fl_trans));
  557.        XtSetArg(args[3], XtNcallback, fl_callbacks);
  558.  
  559.        i = j = 0;
  560.        if (n > 10) {            /* if more than ten aliases, try to */
  561.           j = n / 4;            /* make an approximately square list */
  562.           while (j * j < n) j++;    /* (int. hack to avoid sqrt usage) */
  563.           i = n / --j;
  564.           while (i * j < n) i++;
  565.           while (j > 3 && i < 25) {    /* try to keep box inside main shell */
  566.                 i++;
  567.                 j--;
  568.                }
  569.          }
  570.  
  571.        for (n = 0; aliases[n]; n++) {
  572.            XtSetArg(args[4], XtNlabel, aliases[n]->name);
  573.            XtSetArg(args[5], XtNfromVert, bw);                j = 6;
  574.            if (left) {
  575.               XtSetArg(args[j], XtNfromHoriz, left);            j++;
  576.              }
  577.            bw = XtCreateManagedWidget("entry", commandWidgetClass, lw, args, j);
  578.            AddInfoHandler(bw, "Copy this alias to the current header field");
  579.  
  580.            if (i) {            /* post names in a rectangular list */
  581.               if (n % i == 0) hold = bw;
  582.               if ((n+1) % i == 0) {
  583.                  left = hold;
  584.                  bw = NULL;
  585.                 }
  586.              }
  587.           }
  588.       }    /* end - if some alias names exist */
  589.    }    /* end - if popup does not exist or was being destroyed */
  590. /*
  591. ** If the menu exists, set its x,y coordinates
  592. */
  593.  SetCursor(0);
  594.  if (popup->core.being_destroyed)
  595.     XBell(XtDisplay(toplevel), 33);
  596.  else {
  597.     SetXY(popup, w, XMail.menuX, XMail.buttonHeight / 2);
  598.    }
  599. } /* SetAliases */
  600.  
  601.  
  602. /*
  603. ** @(#)SetFolders() - create a menu list of folder names
  604. */
  605. /* ARGSUSED */
  606. XtActionProc
  607. SetFolders(w, event, params, num_params)
  608. Widget        w;
  609. XEvent        *event;
  610. String        *params;
  611. Cardinal    *num_params;
  612. {
  613.  Arg        args[8];
  614.  Widget        lw, above, this_one, to_left, popup;
  615.  int        x, n;
  616.  char        trans[BUFSIZ], tmp[BUFSIZ], *p, *List = NULL;
  617.  char        foldir[BUFSIZ], *GetMailEnv(), *getenv();
  618.  int        foldlen, List_size, newline = 0;
  619.  DIR        *dirp = NULL;
  620.  
  621. #ifdef USE_DIRENT
  622.  struct dirent    *dp;
  623. #else
  624.  struct direct    *dp;
  625. #endif
  626.  
  627.  
  628.  static String dir_Trans = "\
  629.             <Btn1Down>: SetDirectory(%s,%s,%s)";
  630.  
  631.  static String l_Trans = "<Btn3Up>:    MenuPopdown(popupList)";
  632.  
  633.  static String fl_trans = "<EnterWindow>: set() \n\
  634.                 <LeaveWindow>:  unset() \n\
  635.                 <Btn3Up>:       notify() MenuPopdown(popupList)";
  636.  
  637.  static XtCallbackRec fl_callbacks[] = {
  638.         { (XtCallbackProc) GetFolderName, NULL },
  639.         { NULL,          NULL }
  640.        };
  641.  
  642.  
  643.  SetCursor(1);
  644.  popup = XtNameToWidget(w, "popupList");
  645.  
  646.  if (! popup || popup->core.being_destroyed) {
  647.     p = GetMailEnv("folder");            /* returns NULL if none */
  648.     if (p && strlen(p)) {
  649.        /*
  650.     * Don't prepend HOME if it starts with a slash or a .
  651.     */
  652.        if (strchr("/.", *p))
  653.        strcpy(foldir, p);
  654.        else
  655.        sprintf(foldir, "%s/%s", getenv("HOME"), p);
  656.        XtFree((char *)p);
  657.        /*
  658.     * Make sure it ends with (only one) slash
  659.     */
  660.        if (LASTCH(foldir) != '/')
  661.           strcat(foldir, "/");
  662.       } else
  663.        foldir[0] = '\0';    /* If no folder variable, then no folders */
  664.     foldlen = strlen(foldir);
  665.  
  666.     XtSetArg(args[0], XtNtranslations, XtParseTranslationTable(l_Trans));
  667.     popup = XtCreatePopupShell("popupList",overrideShellWidgetClass,w,args,1);
  668.  
  669.     if (*foldir) {                /* if folder variable exists */
  670.        if (mailpid)
  671.           List = QueryMail("folders");
  672.        else {
  673.           if (dirp = opendir(foldir)) {        /* and folder is readable... */
  674.              List_size = BUFSIZ;
  675.              List = XtMalloc(List_size);    /* start with a BUFSIZ block */
  676.              List[0] = '\0';
  677.              x = 0;
  678.              for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp))
  679.                  if (strcmp(dp->d_name, ".") && strcmp(dp->d_name, "..")) {
  680.                     if (strlen(List) + strlen(dp->d_name) + 2 > List_size) {
  681.                        List_size += BUFSIZ;
  682.                        List = XtRealloc(List, List_size);
  683.                       }
  684.                     strcat(List, " ");
  685.                     strcat(List, dp->d_name);
  686.                     if (++x % 4 == 0) {
  687.                        x = 0;
  688.                        strcat(List, "\n");
  689.                       }
  690.                    }
  691.              List = XtRealloc(List, strlen(List) + 1);
  692.              closedir(dirp);
  693.             } /* end - if folder directory is readable */
  694.          } /* end - if mail process is running */
  695.       } /* end - if a folder value exists */
  696.  
  697.     if (List)                /* could be null if no current mail */
  698.        if (O_BELL == match(&output_pattern, List)) {
  699.           strcat(List, "\n");
  700.           Bell(List);
  701.           XtFree((char *)List);
  702.           List = NULL;
  703.          }
  704. /*
  705. ** determine proper label width by finding longest word length
  706. */
  707.     trans[0] = '\0';
  708.     x = 0;
  709.     if (List)                /* if folders exist and are readable */
  710.        for (p = List; *p; p++) {
  711.            if (*p == ' ' || *p == '\n' || *p == '\t') {
  712.               if (x) {
  713.                  tmp[x] = '\0';
  714.                  x = 0;
  715.                  if (strlen(trans) < strlen(tmp))
  716.                     strcpy(trans, tmp);
  717.                 }
  718.              } else tmp[x++] = *p;
  719.           }
  720.  
  721.     if (x) {
  722.        tmp[x] = '\0';
  723.        if (strlen(trans) < strlen(tmp))
  724.           strcpy(trans, tmp);
  725.       }
  726.  
  727.     if ((n = strlen(trans)) == 0) {    /* if no folders set dummy width and */
  728.        XtSetArg(args[0], XtNwidth, 1);        /* height so MenuPopup() */
  729.        XtSetArg(args[1], XtNheight, 1);        /* won't complain about */
  730.        XtSetValues(popup, (ArgList) args, 2);    /* zero width or height */
  731.  
  732.        XtDestroyWidget(popup);    /* it would be nice if MenuPopup() cared */
  733.       } else {
  734.        XtSetArg(args[0], XtNdefaultDistance, 1);
  735.        lw = XtCreateManagedWidget("list", formWidgetClass, popup, args, ONE);
  736. /*
  737. ** Now, make equal width command buttons which contain the folder names
  738. */
  739.        XtSetArg(args[0], XtNwidth, XTextWidth(XMail.buttonFont, trans, n) + 20);
  740.        XtSetArg(args[1], XtNfont, XMail.buttonFont);
  741.        XtSetArg(args[2], XtNtranslations, XtParseTranslationTable(fl_trans));
  742.        XtSetArg(args[3], XtNcallback, fl_callbacks);
  743.  
  744.        above = this_one = to_left = NULL;
  745.        for (x = 0, p = List; *p; p++) {
  746.            if (*p == '\n') {
  747.               newline = 1;
  748.              }
  749.            if (*p == ' ' || *p == '\n' || *p == '\t') {
  750.               if (x) {
  751.                  tmp[x] = '\0';
  752. /*
  753. ** If this 'folder' is really a directory, mark it with a trailing slash '/'
  754. */
  755.                  foldir[foldlen] = '\0';
  756.                  strcat(foldir, &tmp[1]);
  757.                  if ((dirp = opendir(foldir)) != NULL) {
  758.                     tmp[x++] = '/';
  759.                     tmp[x] = '\0';
  760.                    }
  761.                  XtSetArg(args[4], XtNlabel, tmp);
  762.                  XtSetArg(args[5], XtNfromHoriz, to_left);        n = 6;
  763.                  if (! to_left) XtSetArg(args[n], XtNfromVert, above);    n++;
  764.    
  765.                  this_one = XtCreateManagedWidget("listbutton", commandWidgetClass,
  766.                                                 lw, args, n);
  767.                  if (to_left == NULL) above = this_one;
  768.                  to_left = this_one;
  769.                  if (newline) {
  770.                     newline = 0;
  771.                     to_left = NULL;
  772.                    }
  773.                  x = 0;
  774. /*
  775. ** If this 'folder' is a directory, add a button to popup a menu of filenames.
  776. */
  777.                  if (dirp) {
  778.                     closedir(dirp);
  779.                     sprintf(trans, dir_Trans, &tmp[1], foldir, "0");
  780.                     XtOverrideTranslations(this_one, XtParseTranslationTable(trans));
  781.                     AddInfoHandler(this_one, Folder_Info[2]);
  782.                    } else
  783.                     AddInfoHandler(this_one, Folder_Info[1]);
  784.                 }
  785.              } else {
  786.                if (x == 0) tmp[x++] = '+';    /* start folder names with a 'plus' */
  787.                tmp[x++] = *p;
  788.              }
  789.           }
  790.  
  791.        if (x) {
  792.           tmp[x] = '\0';
  793.           foldir[foldlen] = '\0';
  794.           strcat(foldir, &tmp[1]);
  795.           if ((dirp = opendir(foldir)) != NULL) {
  796.              tmp[x++] = '/';
  797.              tmp[x] = '\0';
  798.             }
  799.           XtSetArg(args[4], XtNlabel, tmp);
  800.           XtSetArg(args[5], XtNfromHoriz, to_left);            n = 6;
  801.           if (! to_left) XtSetArg(args[n], XtNfromVert, above);        n++;
  802.  
  803.           this_one = XtCreateManagedWidget("listbutton", commandWidgetClass,
  804.                                                 lw, args, n);
  805.           if (dirp) {
  806.              closedir(dirp);
  807.              sprintf(trans, dir_Trans, &tmp[1], foldir, "0");
  808.              XtOverrideTranslations(this_one, XtParseTranslationTable(trans));
  809.              AddInfoHandler(this_one, Folder_Info[2]);
  810.             } else
  811.              AddInfoHandler(this_one, Folder_Info[1]);
  812.          }
  813.       }
  814.    }    /* end - if some trans strlen */
  815. /*
  816. ** If folders menu exists, pop it up, after setting x,y coordinates
  817. */
  818.  if (popup->core.being_destroyed) {
  819.     if (! *foldir)
  820.        Bell("No value set for \"folder\"\n");
  821.     else {
  822.        if (dirp) {
  823.           if (! mailpid) {
  824.              Bell("No mail folders exist\n");
  825.             } else {
  826.              foldir[foldlen - 1] = '\0';
  827.              sprintf(tmp, "%s not found\n", foldir);
  828.              Bell(tmp);
  829.             }
  830.          }
  831.       }
  832.    } else {
  833.     if (! XtIsRealized(popup))
  834.        XtRealizeWidget(popup);
  835.     /*
  836.     ** If folder list is small enough, anchor it to
  837.     ** the folder button instead of the commandPanel
  838.     */
  839.     if (popup->core.width + (3 * (XMail.buttonWidth + 12)) <= XMail.shellWidth)
  840.        SetXY(popup, w, XMail.menuX, XMail.buttonHeight / 2);
  841.     else
  842.        SetXY(popup, XtNameToWidget(toplevel, "topBox.commandPanel"),
  843.              XMail.menuX, XMail.commandHeight / 2);
  844.    }
  845.  
  846.  if (List)
  847.     XtFree((char *)List);
  848.  
  849.  SetCursor(0);
  850. } /* SetFolders */
  851.  
  852.  
  853.  
  854. /* 
  855. ** @(#)SetMenu() - create a menu for toggling selected mail options
  856. */
  857. XtActionProc
  858. SetMenu(parent, event, params, num_params)
  859. Widget        parent;
  860. XEvent        *event; /* unused */
  861. String        *params;
  862. Cardinal    *num_params;
  863. {
  864.  Arg         args[6];
  865.  Widget        menu, layout, previous, next;
  866.  char        *c, *info, label[BUFSIZ], name[BUFSIZ];        
  867.  int        indx;
  868.  
  869.  static String b_Trans =
  870.             "<EnterWindow>:    set() \n\
  871.               <LeaveWindow>: reset() \n\
  872.                    <Btn3Up>:    notify() unset()";
  873.  
  874.  static String m_Trans =
  875.             "<Btn3Up>:    MenuPopdown(set_menu)";
  876.  
  877.  static String list[] = { "alwaysignore", "autoprint", "hold", "expert", NULL };
  878.  
  879.  static String set_info[] = {
  880.  "Skip 'ignore'd header fields everywhere, not just during a print or read",
  881.  "Enable automatic printing of messages after delete and undelete commands",
  882.  "Preserve messages in the system mailbox after they have been read",
  883.  "Don't ask for confirmation when commiting changes or aborting a new letter",
  884.  NULL
  885.  };
  886.  
  887.  static String unset_info[] = {
  888.  "Skip 'ignore'd header fields only when doing a print or read command",
  889.  "Disable automatic printing of messages after delete and undelete commands",
  890.  "Move system mailbox messages to the mbox save file after you read them",
  891.  "Ask for confirmation before commiting any changes or aborting a new letter",
  892.  NULL
  893.  };
  894.  
  895.  
  896.  SetCursor(1);
  897.  menu = XtNameToWidget(parent, "set_menu");
  898.  
  899.  if (! menu || menu->core.being_destroyed) {
  900.     XtSetArg(args[0], XtNtranslations, XtParseTranslationTable(m_Trans));
  901.     menu = XtCreatePopupShell("set_menu",overrideShellWidgetClass,parent,args,1);
  902.  
  903.     XtSetArg(args[0], XtNdefaultDistance, (XtArgVal) 1);
  904.     layout = XtCreateManagedWidget("menu", formWidgetClass, menu, args, ONE);
  905. /*
  906. ** create the menu buttons
  907. */
  908.     previous = NULL;
  909.     XtSetArg(args[0], XtNwidth, figureWidth(XMail.buttonFont) * 18 + 12);
  910.     XtSetArg(args[1], XtNfont, XMail.buttonFont);
  911.     XtSetArg(args[2], XtNjustify, XtJustifyLeft);
  912.     XtSetArg(args[3], XtNtranslations, XtParseTranslationTable(b_Trans));
  913.     for (indx = 0; list[indx] != NULL; indx++) {
  914.         info = set_info[indx];
  915.         strcpy(label, "set ");
  916.         if (strcmp(list[indx], "expert") == 0) {
  917.            if (XMail.expert) {
  918.               info = unset_info[indx];
  919.               strcat(label, "no");
  920.              }
  921.           } else {
  922.            if ((c = GetMailEnv(list[indx])) != NULL) {
  923.               info = unset_info[indx];
  924.               strcat(label, "no");
  925.               XtFree(c);
  926.              }
  927.           }
  928.         strcat(label, list[indx]);        /* set window name by label */
  929.         strcpy(name, &label[4]);
  930.         XtSetArg(args[4], XtNlabel, label);
  931.         XtSetArg(args[5], XtNfromVert, previous);
  932.         next = XtCreateManagedWidget(name, commandWidgetClass, layout, args, 6);
  933.         XtAddCallback(next, XtNcallback, (XtCallbackProc) DoSet, NULL);
  934.         AddInfoHandler(next, info);
  935.         previous = next;
  936.        }
  937.    } /* end - menu creation */
  938.  SetXY(menu, parent, XMail.menuX, XMail.buttonHeight / 2);
  939.  SetCursor(0);
  940. } /* SetMenu */
  941.  
  942.  
  943. /*
  944. ** @(#)SetPopup() - place named popup at menuX, menuY relative to Widget w.
  945. */
  946. /* ARGSUSED */
  947. XtActionProc
  948. SetPopup(w, event, params, num_params)
  949. Widget w;
  950. XEvent *event; /* unused */
  951. String *params;
  952. Cardinal *num_params;
  953. {
  954.  Widget        shell;
  955.  String        p;
  956.  
  957.  
  958.  SetCursor(1);
  959.  if (*num_params == 0)
  960.     XtError("xmail had no name parameter passed to SetPopup()");
  961.  
  962.  p = params[0];
  963.  
  964.  if ((shell = XtNameToWidget(w, p)) == NULL)
  965.     XtError("xmail shell name passed to SetPopup() not found in list");
  966.  
  967.  SetXY(shell, w, XMail.menuX, XMail.menuY);
  968.  SetCursor(0);
  969. } /* SetPopup */
  970.  
  971.  
  972. /* ARGSUSED */
  973. /*
  974. ** @(#)SetSelect() - flag the index number of the selected message
  975. */
  976. XtActionProc
  977. SetSelect(w, event, params, num_params)
  978. Widget w;        /* unused */
  979. XEvent *event;        /* unused */
  980. String *params;        /* unused */
  981. Cardinal *num_params;    /* unused */
  982. {
  983.  markIndex(">");
  984. } /* SetSelect */
  985.  
  986.  
  987. /*
  988. ** @(#)ShowHelp() - set named string source as text for and popup help window.
  989. */
  990. /* ARGSUSED */
  991. XtActionProc
  992. ShowHelp(w, event, params, num_params)
  993. Widget        w;
  994. XEvent        *event;
  995. String        *params;            /* unused */
  996. Cardinal    *num_params;            /* unused */
  997. {
  998.  String        name;
  999.  Widget        tb;
  1000.  int        x;
  1001.  
  1002.  
  1003.  SetCursor(1);
  1004.  name = w->core.name;
  1005.  if (strcmp(name, "text") == 0 && event->type == KeyPress)
  1006.     name = "text2";
  1007.  
  1008.  for (x = 0; x < HelpList.indx; x++)
  1009.      if (strcmp(name, HelpList.name[x]) == 0) {
  1010.         tb = XtNameToWidget(toplevel, "topBox");
  1011.         XawTextSetSource(XtNameToWidget(tb, "help.helpWindow"),
  1012.                          HelpList.text[x], (XawTextPosition) 0);
  1013.  
  1014.         SetXY(XtNameToWidget(tb, "help"),
  1015.               XtNameToWidget(tb, "textWindow.text"),
  1016.               XMail.helpX, XMail.helpY);
  1017.  
  1018.         SetCursor(0);
  1019.         XtPopup(XtNameToWidget(tb, "help"), XtGrabNone);
  1020.         break;
  1021.        }
  1022. } /* ShowHelp */
  1023.