home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / X / mit / clients / xmh / folder.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-07-20  |  27.4 KB  |  976 lines

  1. /*
  2.  * $XConsortium: folder.c,v 2.40 91/07/20 20:46:08 converse Exp $
  3.  *
  4.  *
  5.  *               COPYRIGHT 1987, 1989
  6.  *           DIGITAL EQUIPMENT CORPORATION
  7.  *               MAYNARD, MASSACHUSETTS
  8.  *            ALL RIGHTS RESERVED.
  9.  *
  10.  * THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT NOTICE AND
  11.  * SHOULD NOT BE CONSTRUED AS A COMMITMENT BY DIGITAL EQUIPMENT CORPORATION.
  12.  * DIGITAL MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THIS SOFTWARE FOR
  13.  * ANY PURPOSE.  IT IS SUPPLIED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
  14.  *
  15.  * IF THE SOFTWARE IS MODIFIED IN A MANNER CREATING DERIVATIVE COPYRIGHT
  16.  * RIGHTS, APPROPRIATE LEGENDS MAY BE PLACED ON THE DERIVATIVE WORK IN
  17.  * ADDITION TO THAT SET FORTH ABOVE.
  18.  *
  19.  * Permission to use, copy, modify, and distribute this software and its
  20.  * documentation for any purpose and without fee is hereby granted, provided
  21.  * that 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 Digital Equipment Corporation not be
  24.  * used in advertising or publicity pertaining to distribution of the software
  25.  * without specific, written prior permission.
  26.  */
  27.  
  28. /* folder.c -- implement buttons relating to folders and other globals. */
  29.  
  30.  
  31. #include "xmh.h"
  32. #include <X11/Xaw/Cardinals.h>
  33. #include <X11/Xatom.h>
  34. #include <sys/stat.h>
  35. #include <ctype.h>
  36. #include "bboxint.h"
  37. #include "tocintrnl.h"
  38.  
  39. typedef struct {    /* client data structure for callbacks */
  40.     Scrn    scrn;        /* the xmh scrn of action */
  41.     Toc        toc;        /* the toc of the selected folder */
  42.     Toc        original_toc;    /* the toc of the current folder */
  43. } DeleteDataRec, *DeleteData;
  44.  
  45.  
  46. static void CreateFolderMenu();
  47. static void AddFolderMenuEntry();
  48. static void DeleteFolderMenuEntry();
  49.  
  50. #ifdef DEBUG_CLEANUP
  51. extern Boolean ExitLoop;
  52. #endif
  53.  
  54. /* Close this toc&view scrn.  If this is the last toc&view, quit xmh. */
  55.  
  56. /*ARGSUSED*/
  57. void DoClose(widget, client_data, call_data)
  58.     Widget    widget;
  59.     XtPointer    client_data;
  60.     XtPointer    call_data;
  61. {
  62.     Scrn    scrn = (Scrn) client_data;
  63.     register int i, count;
  64.     Toc        toc;
  65.     XtCallbackRec    confirm_callbacks[2];
  66.  
  67.     count = 0;
  68.     for (i=0 ; i<numScrns ; i++)
  69.     if (scrnList[i]->kind == STtocAndView && scrnList[i]->mapped)
  70.         count++;
  71.  
  72.     confirm_callbacks[0].callback = (XtCallbackProc) DoClose;
  73.     confirm_callbacks[0].closure = (XtPointer) scrn;
  74.     confirm_callbacks[1].callback = (XtCallbackProc) NULL;
  75.     confirm_callbacks[1].closure = (XtPointer) NULL;
  76.  
  77.     if (count <= 1) {
  78.  
  79.     for (i = numScrns - 1; i >= 0; i--)
  80.         if (scrnList[i] != scrn) {
  81.         if (MsgSetScrn((Msg) NULL, scrnList[i], confirm_callbacks,
  82.                    (XtCallbackList) NULL) == NEEDS_CONFIRMATION)
  83.             return;
  84.         }
  85.     for (i = 0; i < numFolders; i++) {
  86.         toc = folderList[i];
  87.  
  88.         if (TocConfirmCataclysm(toc, confirm_callbacks,
  89.                     (XtCallbackList) NULL))
  90.         return;
  91.     }
  92. /*     if (MsgSetScrn((Msg) NULL, scrn))
  93.  *        return;
  94.  * %%%
  95.  *    for (i = 0; i < numFolders; i++) {
  96.  *        toc = folderList[i];
  97.  *        if (toc->scanfile && toc->curmsg)
  98.  *        CmdSetSequence(toc, "cur", MakeSingleMsgList(toc->curmsg));
  99.  *    }
  100.  */
  101. #ifdef DEBUG_CLEANUP
  102.     XtDestroyWidget(scrn->parent);
  103.     ExitLoop = TRUE;
  104.     return;
  105. #else
  106.     XtUnmapWidget(scrn->parent);
  107.     XtDestroyApplicationContext
  108.         (XtWidgetToApplicationContext(scrn->parent));
  109.     exit(0);
  110. #endif
  111.     }
  112.     else {
  113.     if (MsgSetScrn((Msg) NULL, scrn, confirm_callbacks, 
  114.                (XtCallbackList) NULL) == NEEDS_CONFIRMATION)
  115.         return;
  116.     DestroyScrn(scrn);    /* doesn't destroy first toc&view scrn */
  117.     }
  118. }
  119.  
  120. /*ARGSUSED*/
  121. void XmhClose(w, event, params, num_params)
  122.     Widget    w;
  123.     XEvent    *event;        /* unused */
  124.     String    *params;    /* unused */
  125.     Cardinal    *num_params;    /* unused */
  126. {
  127.     Scrn     scrn = ScrnFromWidget(w);
  128.     DoClose(w, (XtPointer) scrn, (XtPointer) NULL);
  129. }
  130.  
  131. /* Open the selected folder in this screen. */
  132.  
  133. /* ARGSUSED*/
  134. void DoOpenFolder(widget, client_data, call_data)
  135.     Widget    widget;
  136.     XtPointer    client_data;
  137.     XtPointer    call_data;
  138. {
  139.     /* Invoked by the Folder menu entry "Open Folder"'s notify action. */
  140.  
  141.     Scrn    scrn = (Scrn) client_data;
  142.     Toc        toc  = SelectedToc(scrn);
  143.     if (TocFolderExists(toc))
  144.     TocSetScrn(toc, scrn);
  145.     else
  146.     PopupError(scrn->parent, "Cannot open selected folder.");
  147. }
  148.  
  149.  
  150. /*ARGSUSED*/
  151. void XmhOpenFolder(w, event, params, num_params)
  152.     Widget    w;
  153.     XEvent    *event;        /* unused */
  154.     String    *params;
  155.     Cardinal    *num_params;
  156. {
  157.     Scrn    scrn = ScrnFromWidget(w);
  158.  
  159.     /* This action may be invoked from folder menu buttons or from folder
  160.      * menus, as an action procedure on an event specified in translations.
  161.      * In this case, the action will open a folder only if that folder
  162.      * was actually selected from a folder button or menu.  If the folder
  163.      * was selected from a folder menu, the menu entry callback procedure,
  164.      * which changes the selected folder, and is invoked by the "notify" 
  165.      * action, must have already executed; and the menu entry "unhightlight"
  166.      * action must execute after this action.
  167.      *
  168.      * This action does not execute if invoked as an accelerator whose
  169.      * source widget is a menu button or a folder menu.  However, it 
  170.      * may be invoked as a keyboard accelerator of any widget other than
  171.      * the folder menu buttons or the folder menus.  In that case, it will
  172.      * open the currently selected folder.
  173.      *
  174.      * If given a parameter, it will take it as the name of a folder to
  175.      * select and open.
  176.      */
  177.  
  178.     if (! UserWantsAction(w, scrn)) return;
  179.     if (*num_params) SetCurrentFolderName(scrn, params[0]);
  180.     DoOpenFolder(w, (XtPointer) scrn, (XtPointer) NULL);
  181. }
  182.  
  183.  
  184. /* Compose a new message. */
  185.  
  186. /*ARGSUSED*/
  187. void DoComposeMessage(widget, client_data, call_data)
  188.     Widget    widget;
  189.     XtPointer    client_data;
  190.     XtPointer    call_data;
  191. {
  192.     Scrn        scrn = NewCompScrn();
  193.     Msg        msg = TocMakeNewMsg(DraftsFolder);
  194.     MsgLoadComposition(msg);
  195.     MsgSetTemporary(msg);
  196.     MsgSetReapable(msg);
  197.     MsgSetScrnForComp(msg, scrn);
  198.     MapScrn(scrn);
  199. }
  200.  
  201.    
  202. /*ARGSUSED*/
  203. void XmhComposeMessage(w, event, params, num_params)
  204.     Widget    w;
  205.     XEvent    *event;        /* unused */
  206.     String    *params;    /* unused */
  207.     Cardinal    *num_params;    /* unused */
  208. {
  209.     DoComposeMessage(w, (XtPointer) NULL, (XtPointer) NULL);
  210. }
  211.  
  212.  
  213. /* Make a new scrn displaying the given folder. */
  214.  
  215. /*ARGSUSED*/
  216. void DoOpenFolderInNewWindow(widget, client_data, call_data)
  217.     Widget    widget;
  218.     XtPointer    client_data;
  219.     XtPointer    call_data;
  220. {
  221.     Scrn    scrn = (Scrn) client_data;
  222.     Toc     toc = SelectedToc(scrn);
  223.     if (TocFolderExists(toc)) {
  224.     scrn = CreateNewScrn(STtocAndView);
  225.     TocSetScrn(toc, scrn);
  226.     MapScrn(scrn);
  227.     } else 
  228.     PopupError(scrn->parent, "Cannot open selected folder.");
  229. }
  230.  
  231.  
  232. /*ARGSUSED*/
  233. void XmhOpenFolderInNewWindow(w, event, params, num_params)
  234.     Widget    w;
  235.     XEvent    *event;        /* unused */
  236.     String    *params;    /* unused */
  237.     Cardinal    *num_params;    /* unused */
  238. {
  239.     Scrn scrn = ScrnFromWidget(w);
  240.     DoOpenFolderInNewWindow(w, (XtPointer) scrn, (XtPointer) NULL);
  241. }
  242.  
  243.  
  244. /* Create a new folder with the given name. */
  245.  
  246. static char *previous_label = NULL;
  247. /*ARGSUSED*/
  248. static void CreateFolder(widget, client_data, call_data)
  249.     Widget    widget;        /* the okay button of the dialog widget */
  250.     XtPointer    client_data;    /* the dialog widget */
  251.     XtPointer    call_data;
  252. {
  253.     Toc        toc;
  254.     register int i;
  255.     char    *name;
  256.     Widget    dialog = (Widget) client_data;
  257.     Arg        args[3];
  258.     char     str[300], *label;
  259.  
  260.     name = XawDialogGetValueString(dialog);
  261.     for (i=0 ; name[i] > ' ' ; i++) ;
  262.     name[i] = '\0';
  263.     toc = TocGetNamed(name);
  264.     if ((toc) || (i==0) || (name[0]=='/') || ((toc = TocCreateFolder(name))
  265.                           == NULL)) {
  266.     if (toc) 
  267.         (void) sprintf(str, "Folder \"%s\" already exists.  Try again.",
  268.                name);
  269.     else if (name[0]=='/')
  270.         (void) sprintf(str, "Please specify folders relative to \"%s\".",
  271.                app_resources.mail_path);
  272.     else 
  273.         (void) sprintf(str, "Cannot create folder \"%s\".  Try again.",
  274.                name);
  275.     label = XtNewString(str);
  276.     XtSetArg(args[0], XtNlabel, label);
  277.     XtSetArg(args[1], XtNvalue, "");
  278.     XtSetValues(dialog, args, TWO);
  279.     if (previous_label)
  280.         XtFree(previous_label);
  281.     previous_label = label;
  282.     return;
  283.     }
  284.     for (i = 0; i < numScrns; i++)
  285.     if (scrnList[i]->folderbuttons) {
  286.         char    *c;
  287.         Button    button;
  288.         if (c = index(name, '/')) { /* if is subfolder */
  289.         c[0] = '\0';
  290.         button = BBoxFindButtonNamed(scrnList[i]->folderbuttons,
  291.                          name);
  292.         c[0] = '/';
  293.         if (button) AddFolderMenuEntry(button, name);
  294.         }
  295.         else
  296.         BBoxAddButton(scrnList[i]->folderbuttons, name,
  297.                   menuButtonWidgetClass, True);
  298.     }
  299.     DestroyPopup(widget, (XtPointer) XtParent(dialog), (XtPointer) NULL);
  300. }
  301.  
  302.  
  303. /* Create a new folder.  Requires the user to name the new folder. */
  304.  
  305. /*ARGSUSED*/
  306. void DoCreateFolder(widget, client_data, call_data)
  307.     Widget    widget;        /* unused */
  308.     XtPointer    client_data;
  309.     XtPointer    call_data;    /* unused */
  310. {
  311.     Scrn scrn = (Scrn) client_data;
  312.     PopupPrompt(scrn->parent, "Create folder named:", CreateFolder);
  313. }
  314.  
  315.  
  316. /*ARGSUSED*/
  317. void XmhCreateFolder(w, event, params, num_params)
  318.     Widget    w;
  319.     XEvent    *event;        /* unused */
  320.     String    *params;    /* unused */
  321.     Cardinal    *num_params;    /* unused */
  322. {
  323.     Scrn scrn = ScrnFromWidget(w);
  324.     DoCreateFolder(w, (XtPointer)scrn, (XtPointer)NULL);
  325. }
  326.  
  327.  
  328. /*ARGSUSED*/
  329. void CancelDeleteFolder(widget, client_data, call_data)
  330.     Widget    widget;        /* unused */
  331.     XtPointer    client_data;
  332.     XtPointer    call_data;    /* unused */
  333. {
  334.     DeleteData    deleteData = (DeleteData) client_data;
  335.  
  336.     TocClearDeletePending(deleteData->toc);
  337.  
  338.     /* When the delete request is made, the toc currently being viewed is
  339.      * changed if necessary to be the toc under consideration for deletion.
  340.      * Once deletion has been confirmed or cancelled, we revert to display
  341.      * the toc originally under view, unless the toc originally under
  342.      * view has been deleted.
  343.      */
  344.  
  345.     if (deleteData->original_toc != NULL)
  346.     TocSetScrn(deleteData->original_toc, deleteData->scrn);
  347.     XtFree((char *) deleteData);
  348. }
  349.  
  350.  
  351. /*ARGSUSED*/
  352. void CheckAndConfirmDeleteFolder(widget, client_data, call_data)
  353.     Widget    widget;        /* unreliable; sometimes NULL */
  354.     XtPointer    client_data;    /* data structure */
  355.     XtPointer    call_data;    /* unused */
  356. {
  357.     DeleteData  deleteData = (DeleteData) client_data;
  358.     Scrn    scrn = deleteData->scrn;
  359.     Toc        toc  = deleteData->toc;
  360.     char    str[300];
  361.     XtCallbackRec confirms[2];
  362.     XtCallbackRec cancels[2];
  363.     void CheckAndDeleteFolder();
  364.  
  365.     static XtCallbackRec yes_callbacks[] = {
  366.     {CheckAndDeleteFolder,    (XtPointer) NULL},
  367.     {(XtCallbackProc) NULL,    (XtPointer) NULL}
  368.     };
  369.  
  370.     static XtCallbackRec no_callbacks[] = {
  371.     {CancelDeleteFolder,    (XtPointer) NULL},
  372.     {(XtCallbackProc) NULL,    (XtPointer) NULL}
  373.     };
  374.  
  375.     /* Display the toc of the folder to be deleted. */
  376.  
  377.     TocSetScrn(toc, scrn);
  378.  
  379.     /* Check for pending delete, copy, move, or edits on messages in the
  380.      * folder to be deleted, and ask for confirmation if they are found.
  381.      */
  382.  
  383.     confirms[0].callback = (XtCallbackProc) CheckAndConfirmDeleteFolder;
  384.     confirms[0].closure = client_data;
  385.     confirms[1].callback = (XtCallbackProc) NULL;
  386.     confirms[1].closure = (XtPointer) NULL;
  387.     
  388.     cancels[0].callback = (XtCallbackProc) CancelDeleteFolder;
  389.     cancels[0].closure = client_data;
  390.     cancels[1].callback = (XtCallbackProc) NULL;
  391.     cancels[1].closure = (XtPointer) NULL;
  392.  
  393.     if (TocConfirmCataclysm(toc, confirms, cancels) ==    NEEDS_CONFIRMATION)
  394.     return;
  395.  
  396.     /* Ask the user for confirmation on destroying the folder. */
  397.  
  398.     yes_callbacks[0].closure = client_data;
  399.     no_callbacks[0].closure =  client_data;
  400.     (void) sprintf(str, "Are you sure you want to destroy %s?", TocName(toc));
  401.     PopupConfirm(scrn->tocwidget, str, yes_callbacks, no_callbacks);
  402. }
  403.  
  404.  
  405. /*ARGSUSED*/
  406. void CheckAndDeleteFolder(widget, client_data, call_data)
  407.     Widget    widget;        /* unused */
  408.     XtPointer    client_data;    /* data structure */
  409.     XtPointer    call_data;    /* unused */
  410. {
  411.     DeleteData  deleteData = (DeleteData) client_data;
  412.     Scrn    scrn = deleteData->scrn;
  413.     Toc        toc =  deleteData->toc;
  414.     XtCallbackRec confirms[2];
  415.     XtCallbackRec cancels[2];
  416.     int     i;
  417.     char    *foldername;
  418.     
  419.     /* Check for changes occurring after the popup was first presented. */
  420.  
  421.     confirms[0].callback = (XtCallbackProc) CheckAndConfirmDeleteFolder;
  422.     confirms[0].closure = client_data;
  423.     confirms[1].callback = (XtCallbackProc) NULL;
  424.     confirms[1].closure = (XtPointer) NULL;
  425.     
  426.     cancels[0].callback = (XtCallbackProc) CancelDeleteFolder;
  427.     cancels[0].closure = client_data;
  428.     cancels[1].callback = (XtCallbackProc) NULL;
  429.     cancels[1].closure = (XtPointer) NULL;
  430.     
  431.     if (TocConfirmCataclysm(toc, confirms, cancels) == NEEDS_CONFIRMATION)
  432.     return;
  433.  
  434.     /* Delete.  Restore the previously viewed toc, if it wasn't deleted. */
  435.  
  436.     foldername = TocName(toc);
  437.     TocSetScrn(toc, (Scrn) NULL);
  438.     TocDeleteFolder(toc);
  439.     for (i=0 ; i<numScrns ; i++)
  440.     if (scrnList[i]->folderbuttons) {
  441.  
  442.         if (IsSubfolder(foldername)) {
  443.         char parent_folder[300];
  444.         char *c = index( strcpy(parent_folder, foldername), '/');
  445.         *c = '\0';
  446.  
  447. /* Since menus are built upon demand, and are a per-xmh-screen resource, 
  448.  * not all xmh toc & view screens will have the same menus built.
  449.  * So the menu entry deletion routines must be able to handle a button
  450.  * whose menu field is null.  It would be better to share folder menus
  451.  * between xmh screens, but accelerators call action procedures which depend
  452.  * upon being able to get the xmh screen (Scrn) from the widget argument.
  453.  */
  454.  
  455.         DeleteFolderMenuEntry
  456.             ( BBoxFindButtonNamed( scrnList[i]->folderbuttons,
  457.                       parent_folder), 
  458.              foldername);
  459.         }
  460.         else {
  461.         BBoxDeleteButton
  462.             (BBoxFindButtonNamed( scrnList[i]->folderbuttons,
  463.                      foldername));
  464.         }
  465.  
  466.         /* If we've deleted the current folder, show the Initial Folder */
  467.  
  468.         if ((! strcmp(scrnList[i]->curfolder, foldername)) 
  469.         && (BBoxNumButtons(scrnList[i]->folderbuttons))
  470.         && (strcmp(foldername, app_resources.initial_folder_name)))
  471.         TocSetScrn(InitialFolder, scrnList[i]);
  472.     }
  473.     XtFree(foldername);
  474.     if (deleteData->original_toc != NULL) 
  475.     TocSetScrn(deleteData->original_toc, scrn);
  476.     XtFree((char *) deleteData);
  477. }
  478.  
  479.  
  480. /* Delete the selected folder.  Requires confirmation! */
  481.  
  482. /*ARGSUSED*/
  483. void DoDeleteFolder(w, client_data, call_data)
  484.     Widget    w;
  485.     XtPointer    client_data;
  486.     XtPointer    call_data;
  487. {
  488.     Scrn    scrn = (Scrn) client_data;
  489.     Toc        toc  = SelectedToc(scrn);
  490.     DeleteData    deleteData;
  491.  
  492.     if (! TocFolderExists(toc)) {
  493.     /* Too hard to clean up xmh when the folder doesn't exist anymore. */
  494.     PopupError(scrn->parent,
  495.            "Cannot open selected folder for confirmation to delete.");
  496.     return;
  497.     }
  498.  
  499.     /* Prevent more than one confirmation popup on the same folder. 
  500.      * TestAndSet returns true if there is a delete pending on this folder.
  501.      */
  502.     if (TocTestAndSetDeletePending(toc))    {
  503.     PopupError(scrn->parent, "There is a delete pending on this folder.");
  504.     return;
  505.     }
  506.  
  507.     deleteData = XtNew(DeleteDataRec);
  508.     deleteData->scrn = scrn;
  509.     deleteData->toc = toc;
  510.     deleteData->original_toc = CurrentToc(scrn);
  511.     if (deleteData->original_toc == toc)
  512.     deleteData->original_toc = (Toc) NULL;
  513.  
  514.     CheckAndConfirmDeleteFolder(w, (XtPointer) deleteData, (XtPointer) NULL);
  515. }
  516.  
  517.  
  518. /*ARGSUSED*/
  519. void XmhDeleteFolder(w, event, params, num_params)
  520.     Widget    w;
  521.     XEvent    *event;        /* unused */
  522.     String    *params;    /* unused */
  523.     Cardinal    *num_params;    /* unused */
  524. {
  525.     Scrn    scrn = ScrnFromWidget(w);
  526.     DoDeleteFolder(w, (XtPointer) scrn, (XtPointer) NULL);
  527. }
  528.  
  529.  
  530. /*-----    Notes on MenuButtons as folder buttons ---------------------------
  531.  *
  532.  * I assume that the name of the button is identical to the name of the folder.
  533.  * Only top-level folders have buttons.
  534.  * Only top-level folders may have subfolders.
  535.  * Top-level folders and their subfolders may have messages.
  536.  *
  537.  */
  538.  
  539. static char filename[500];    /* for IsFolder() and for callback */
  540. static int  flen = 0;        /* length of a substring of filename */
  541.  
  542.  
  543. /* Function name:    IsFolder
  544.  * Description:        determines if a file is an mh subfolder.
  545.  */
  546. static int IsFolder(name)
  547.     char *name;
  548. {
  549.     register int i, len;
  550.     struct stat buf;
  551.  
  552.     /* mh does not like subfolder names to be strings of digits */
  553.  
  554.     if (isdigit(name[0]) || name[0] == '#') {
  555.     len = strlen(name);
  556.     for(i=1; i < len && isdigit(name[i]); i++)
  557.         ;
  558.     if (i == len) return FALSE;
  559.     }
  560.     else if (name[0] == '.')
  561.     return FALSE;
  562.  
  563.     (void) sprintf(filename + flen, "/%s", name);
  564.     if (stat(filename, &buf) /* failed */) return False;
  565.     return (buf.st_mode & S_IFMT) == S_IFDIR;
  566. }
  567.  
  568.  
  569. /* menu entry selection callback for folder menus. */
  570.  
  571. /*ARGSUSED*/
  572. static void DoSelectFolder(w, closure, data)
  573.     Widget     w;        /* the menu entry object */
  574.     XtPointer    closure;    /* foldername */
  575.     XtPointer    data;    
  576. {
  577.     Scrn    scrn = ScrnFromWidget(w);
  578.     SetCurrentFolderName(scrn, (char *) closure);
  579. }
  580.  
  581. /*ARGSUSED*/
  582. void FreeMenuData(w, client_data, call_data)
  583.     Widget    w;
  584.     XtPointer    client_data, call_data;
  585. {
  586.     XtFree((char*) client_data);
  587. }
  588.  
  589. /* Function name:    AddFolderMenuEntry
  590.  * Description:    
  591.  *    Add an entry to a menu.  If the menu is not already created,
  592.  *    create it, including the (already existing) new subfolder directory.
  593.  *     If the menu is already created,    add the new entry.
  594.  */
  595.  
  596. static void AddFolderMenuEntry(button, entryname)
  597.     Button    button;        /* the corresponding menu button */
  598.     char    *entryname;    /* the new entry, relative to MailDir */
  599. {
  600.     Arg        args[4];
  601.     char *    name;
  602.     char *    c;
  603.     char        tmpname[300];
  604.     char *    label;
  605.     static XtCallbackRec callbacks[] = {
  606.     { DoSelectFolder,        (XtPointer) NULL },
  607.     { (XtCallbackProc) NULL,    (XtPointer) NULL}
  608.     };
  609.     static XtCallbackRec destroyCallbacks[] = {
  610.     { FreeMenuData,            (XtPointer) NULL },
  611.     { (XtCallbackProc) NULL,    (XtPointer) NULL}
  612.     };
  613.  
  614.     /* The menu must be created before we can add an entry to it. */
  615.  
  616.     if (button->menu == NULL || button->menu == NoMenuForButton) {
  617.     CreateFolderMenu(button);
  618.     return;
  619.     }
  620.     name = XtNewString(entryname);
  621.     callbacks[0].closure = (XtPointer) name;
  622.     destroyCallbacks[0].closure = (XtPointer) name;
  623.     XtSetArg(args[0], XtNcallback, callbacks);            /* ONE */
  624.     XtSetArg(args[1], XtNdestroyCallback, destroyCallbacks);    /* TWO */
  625.  
  626.     /* When a subfolder and its parent folder have identical names,
  627.      * the widget name of the subfolder's menu entry must be unique.
  628.      */
  629.     label = entryname;
  630.     c = index( strcpy(tmpname, entryname), '/');
  631.     if (c) {
  632.     *c = '\0';
  633.     label = ++c;
  634.     if (strcmp(tmpname, c) == 0) {
  635.         c--;
  636.         *c = '_';
  637.     }
  638.     name = c;
  639.     }
  640.     XtSetArg(args[2], XtNlabel, label);                /* THREE */
  641.     XtCreateManagedWidget(name, smeBSBObjectClass, button->menu, 
  642.               args, THREE);
  643. }
  644.  
  645.  
  646.  
  647. /* Function name:    CreateFolderMenu
  648.  * Description:    
  649.  *    Menus are created for folder buttons if the folder has at least one
  650.  *    subfolder.  For the directory given by the concatentation of 
  651.  *    app_resources.mail_path, '/', and the name of the button, 
  652.  *    CreateFolderMenu creates the menu whose entries are
  653.  *    the subdirectories which do not begin with '.' and do not have
  654.  *    names which are all digits, and do not have names which are a '#'
  655.  *    followed by all digits.  The first entry is always the name of the
  656.  *    parent folder.  Remaining entries are alphabetized.
  657.  */
  658.  
  659. static void CreateFolderMenu(button)
  660.     Button    button;
  661. {
  662.     char **namelist;
  663.     register int i, n, length;
  664.     char    directory[500];
  665.  
  666.     n = strlen(app_resources.mail_path);
  667.     (void) strncpy(directory, app_resources.mail_path, n);
  668.     directory[n++] = '/';
  669.     (void) strcpy(directory + n, button->name);
  670.     flen = strlen(directory);        /* for IsFolder */
  671.     (void) strcpy(filename, directory);    /* for IsFolder */
  672.     n = ScanDir(directory, &namelist, IsFolder);
  673.     if (n <= 0) {
  674.     /* no subfolders, therefore no menu */
  675.     button->menu = NoMenuForButton;
  676.     return;
  677.     }
  678.  
  679.     button->menu = XtCreatePopupShell("menu", simpleMenuWidgetClass,
  680.                       button->widget, (ArgList) NULL, ZERO);
  681.     
  682.     /* The first entry is always the parent folder */
  683.  
  684.     AddFolderMenuEntry(button, button->name);
  685.  
  686.     /* Build the menu by adding all the current entries to the new menu. */
  687.  
  688.     length = strlen(button->name);
  689.     (void) strncpy(directory, button->name, length);
  690.     directory[length++] = '/';
  691.     for (i=0; i < n; i++) {
  692.     (void) strcpy(directory + length, namelist[i]);
  693.     free((char *) namelist[i]);
  694.     AddFolderMenuEntry(button, directory);
  695.     }
  696.     free((char *) namelist);
  697. }
  698.  
  699.  
  700. /* Function:    DeleteFolderMenuEntry
  701.  * Description:    Remove a subfolder from a menu.
  702.  */
  703.  
  704. static void DeleteFolderMenuEntry(button, foldername)
  705.     Button    button;
  706.     char    *foldername;
  707. {
  708.     char *    c;
  709.     Arg        args[2];
  710.     char *    subfolder;
  711.     int        n;
  712.     char    tmpname[300];
  713.     Widget    entry;
  714.     
  715.     if (button == NULL || button->menu == NULL) return;
  716.     XtSetArg(args[0], XtNnumChildren, &n);
  717.     XtSetArg(args[1], XtNlabel, &c);
  718.     XtGetValues(button->menu, args, TWO);
  719.     if ((n <= 3 && c) || n <= 2) {
  720.     XtDestroyWidget(button->menu);    
  721.     button->menu = NoMenuForButton;
  722.     return;
  723.     }
  724.  
  725.     c = index( strcpy(tmpname, foldername), '/');
  726.     if (c) {
  727.     *c = '\0';
  728.     subfolder = ++c;
  729.     if (strcmp(button->name, subfolder) == 0) {
  730.         c--;
  731.         *c = '_';
  732.         subfolder = c;
  733.     }
  734.     if ((entry = XtNameToWidget(button->menu, subfolder)) != NULL)
  735.         XtDestroyWidget(entry);
  736.     }
  737. }
  738.  
  739. /* Function Name:    PopupFolderMenu
  740.  * Description:        This action should alwas be taken when the user
  741.  *    selects a folder button.  A folder button represents a folder 
  742.  *    and zero or more subfolders.  The menu of subfolders is built upon
  743.  *    the first reference to it, by this routine.  If there are no 
  744.  *    subfolders, this routine will mark the folder as having no 
  745.  *    subfolders, and no menu will be built.  In that case, the menu
  746.  *    button emulates a command button.  When subfolders exist,
  747.  *    the menu will popup, using the menu button action PopupMenu.
  748.  */
  749.  
  750. /*ARGSUSED*/
  751. void XmhPopupFolderMenu(w, event, vector, count)
  752.     Widget    w;
  753.     XEvent    *event;        /* unused */
  754.     String    *vector;    /* unused */
  755.     Cardinal    *count;        /* unused */
  756. {
  757.     Button    button;
  758.     Scrn    scrn;
  759.  
  760.     scrn = ScrnFromWidget(w);
  761.     if ((button = BBoxFindButton(scrn->folderbuttons, w)) == NULL)
  762.     return;
  763.     if (button->menu == NULL)
  764.     CreateFolderMenu(button);
  765.  
  766.     if (button->menu == NoMenuForButton)
  767.     LastMenuButtonPressed = w;
  768.     else {
  769.     XtCallActionProc(button->widget, "PopupMenu", (XEvent *) NULL,
  770.              (String *) NULL, (Cardinal) 0);
  771.     XtCallActionProc(button->widget, "reset", (XEvent *) NULL,
  772.              (String *) NULL, (Cardinal) 0);
  773.     }
  774. }
  775.  
  776.  
  777. /* Function Name:    XmhSetCurrentFolder
  778.  * Description:        This action procedure allows menu buttons to 
  779.  *    emulate toggle widgets in their function of folder selection.
  780.  *    Therefore, mh folders with no subfolders can be represented
  781.  *     by a button instead of a menu with one entry.  Sets the currently
  782.  *    selected folder.
  783.  */
  784.  
  785. /*ARGSUSED*/
  786. void XmhSetCurrentFolder(w, event, vector, count)
  787.     Widget    w;
  788.     XEvent    *event;        /* unused */
  789.     String    *vector;    /* unused */
  790.     Cardinal    *count;        /* unused */
  791. {
  792.     Button    button;
  793.     Scrn    scrn;
  794.  
  795.     /* The MenuButton widget has a button grab currently active; the
  796.      * currently selected folder will be updated if the user has released
  797.      * the mouse button while the mouse pointer was on the same menu button
  798.      * widget that orginally activated the button grab.  This mechanism is
  799.      * insured by the XmhPopupFolderMenu action setting LastMenuButtonPressed.
  800.      * The action XmhLeaveFolderButton, and it's translation in the application
  801.      * defaults file, bound to LeaveWindow events, insures that the menu
  802.      * button behaves properly when the user moves the pointer out of the 
  803.      * menu button window.
  804.      *
  805.      * This action is for menu button widgets only.
  806.      */
  807.  
  808.     if (w != LastMenuButtonPressed)
  809.     return;
  810.     scrn = ScrnFromWidget(w);
  811.     if ((button = BBoxFindButton(scrn->folderbuttons, w)) == NULL)
  812.     return;
  813.     SetCurrentFolderName(scrn, button->name);
  814. }
  815.  
  816.  
  817. /*ARGSUSED*/
  818. void XmhLeaveFolderButton(w, event, vector, count)
  819.     Widget    w;
  820.     XEvent    *event;
  821.     String    *vector;
  822.     Cardinal    *count;
  823. {
  824.     LastMenuButtonPressed = NULL;
  825. }
  826.  
  827.  
  828. void Push(stack_ptr, data)
  829.     Stack    *stack_ptr;
  830.     char     *data;
  831. {
  832.     Stack    new = XtNew(StackRec);
  833.     new->data = data;
  834.     new->next = *stack_ptr;
  835.     *stack_ptr = new;
  836. }
  837.  
  838. char * Pop(stack_ptr)
  839.     Stack    *stack_ptr;
  840. {
  841.     Stack    top;
  842.     char     *data = NULL;
  843.  
  844.     if ((top = *stack_ptr) != NULL) {
  845.     data = top->data;
  846.     *stack_ptr = top->next;
  847.     XtFree((char *) top);
  848.     }
  849.     return data;
  850. }
  851.  
  852. /* Parameters are taken as names of folders to be pushed on the stack.
  853.  * With no parameters, the currently selected folder is pushed.
  854.  */
  855.  
  856. /*ARGSUSED*/
  857. void XmhPushFolder(w, event, params, count)
  858.     Widget    w;
  859.     XEvent     *event;
  860.     String     *params;
  861.     Cardinal     *count;
  862. {
  863.     Scrn    scrn = ScrnFromWidget(w);
  864.     int        i;
  865.  
  866.     for (i=0; i < *count; i++) 
  867.     Push(&scrn->folder_stack, params[i]);
  868.  
  869.     if (*count == 0 && scrn->curfolder)
  870.     Push(&scrn->folder_stack, scrn->curfolder);
  871. }
  872.  
  873. /* Pop the stack & take that folder to be the currently selected folder. */
  874.  
  875. /*ARGSUSED*/
  876. void XmhPopFolder(w, event, params, count)
  877.     Widget    w;
  878.     XEvent     *event;
  879.     String     *params;
  880.     Cardinal     *count;
  881. {
  882.     Scrn    scrn = ScrnFromWidget(w);
  883.     char    *folder;
  884.  
  885.     if ((folder = Pop(&scrn->folder_stack)) != NULL)
  886.     SetCurrentFolderName(scrn, folder);
  887. }
  888.  
  889. static Boolean InParams(str, p, n)
  890.     String str;
  891.     String *p;
  892.     Cardinal n;
  893. {
  894.     int i;
  895.     for (i=0; i < n; p++, i++)
  896.     if (! XmuCompareISOLatin1(*p, str)) return True;
  897.     return False;
  898. }
  899.  
  900. /* generalized routine for xmh participation in WM protocols */
  901.  
  902. /*ARGSUSED*/
  903. void XmhWMProtocols(w, event, params, num_params)
  904.     Widget    w;    /* NULL if from checkpoint timer */
  905.     XEvent *    event;    /* NULL if from checkpoint timer */
  906.     String *    params;
  907.     Cardinal *    num_params;
  908. {
  909.     Boolean    dw = False;    /* will we do delete window? */
  910.     Boolean    sy = False;    /* will we do save yourself? */
  911.     static char*WM_DELETE_WINDOW = "WM_DELETE_WINDOW";
  912.     static char*WM_SAVE_YOURSELF = "WM_SAVE_YOURSELF";
  913.  
  914. #define DO_DELETE_WINDOW InParams(WM_DELETE_WINDOW, params, *num_params)
  915. #define DO_SAVE_YOURSELF InParams(WM_SAVE_YOURSELF, params, *num_params)
  916.  
  917.     /* Respond to a recognized WM protocol request iff 
  918.      * event type is ClientMessage and no parameters are passed, or
  919.      * event type is ClientMessage and event data is matched to parameters, or
  920.      * event type isn't ClientMessage and parameters make a request.
  921.      */
  922.  
  923.     if (event && event->type == ClientMessage) {
  924.     if (event->xclient.message_type == wm_protocols) {
  925.         if (event->xclient.data.l[0] == wm_delete_window &&
  926.         (*num_params == 0 || DO_DELETE_WINDOW))
  927.         dw = True;
  928.         else if (event->xclient.data.l[0] == wm_save_yourself &&
  929.              (*num_params == 0 || DO_SAVE_YOURSELF))
  930.         sy = True;
  931.     }
  932.     } else {
  933.     if (DO_DELETE_WINDOW)
  934.         dw = True;
  935.     if (DO_SAVE_YOURSELF)
  936.         sy = True;
  937.     }
  938.  
  939. #undef DO_DELETE_WINDOW
  940. #undef DO_SAVE_YOURSELF
  941.  
  942.     if (sy) {
  943.     register int i;
  944.     for (i=0; i<numScrns; i++)
  945.         if (scrnList[i]->msg) 
  946.         MsgCheckPoint(scrnList[i]->msg);
  947.     if (w) /* don't generate a property notify via the checkpoint timer */
  948.         XChangeProperty(XtDisplay(toplevel), XtWindow(toplevel),
  949.                 XA_WM_COMMAND, XA_STRING, 8, PropModeAppend,
  950.                 (unsigned char *)"", 0);
  951.     }
  952.     if (dw && w) {
  953.     Scrn scrn;
  954.  
  955.     while (w && !XtIsShell(w))
  956.         w = XtParent(w);
  957.     if (XtIsTransientShell(w)) {
  958.         WMDeletePopup(w, event);
  959.         return;
  960.     }
  961.     scrn = ScrnFromWidget(w);
  962.     switch (scrn->kind) {
  963.       case STtocAndView:
  964.         DoClose(w, (XtPointer)scrn, (XtPointer)NULL);
  965.         break;
  966.       case STview:
  967.       case STcomp:
  968.         DoCloseView(w, (XtPointer)scrn, (XtPointer)NULL);
  969.         break;
  970.       case STpick:
  971.         DestroyScrn(scrn);
  972.         break;
  973.     }
  974.     }
  975. }
  976.