home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / X / mit / clients / xmh / toc.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-07-23  |  29.1 KB  |  1,235 lines

  1. /* $XConsortium: toc.c,v 2.55 91/07/23 16:25:56 converse Exp $
  2.  *
  3.  *
  4.  *              COPYRIGHT 1987
  5.  *           DIGITAL EQUIPMENT CORPORATION
  6.  *               MAYNARD, MASSACHUSETTS
  7.  *            ALL RIGHTS RESERVED.
  8.  *
  9.  * THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT NOTICE AND
  10.  * SHOULD NOT BE CONSTRUED AS A COMMITMENT BY DIGITAL EQUIPMENT CORPORATION.
  11.  * DIGITAL MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THIS SOFTWARE FOR
  12.  * ANY PURPOSE.  IT IS SUPPLIED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
  13.  *
  14.  * IF THE SOFTWARE IS MODIFIED IN A MANNER CREATING DERIVATIVE COPYRIGHT
  15.  * RIGHTS, APPROPRIATE LEGENDS MAY BE PLACED ON THE DERIVATIVE WORK IN
  16.  * ADDITION TO THAT SET FORTH ABOVE.
  17.  *
  18.  * Permission to use, copy, modify, and distribute this software and its
  19.  * documentation for any purpose and without fee is hereby granted, provided
  20.  * that the above copyright notice appear in all copies and that both that
  21.  * copyright notice and this permission notice appear in supporting
  22.  * documentation, and that the name of Digital Equipment Corporation not be
  23.  * used in advertising or publicity pertaining to distribution of the software
  24.  * without specific, written prior permission.
  25.  */
  26.  
  27. /* toc.c -- handle things in the toc widget. */
  28.  
  29. #include "xmh.h"
  30. #include "tocintrnl.h"
  31. #include "toc.h"
  32. #include "tocutil.h"
  33. #include <sys/stat.h>
  34.  
  35. static int IsDir(name)
  36. char *name;
  37. {
  38.     char str[500];
  39.     struct stat buf;
  40.     if (*name == '.')
  41.     return FALSE;
  42.     (void) sprintf(str, "%s/%s", app_resources.mail_path, name);
  43.     if (stat(str, &buf) /* failed */) return False;
  44.     return (buf.st_mode & S_IFMT) == S_IFDIR;
  45. }
  46.  
  47.  
  48. static void MakeSureFolderExists(namelistptr, numfoldersptr, name)
  49. char ***namelistptr;
  50. int *numfoldersptr;
  51. char *name;
  52. {
  53.     int i;
  54.     char str[200];
  55.     for (i=0 ; i<*numfoldersptr ; i++)
  56.     if (strcmp((*namelistptr)[i], name) == 0) return;
  57.     (void) sprintf(str, "%s/%s", app_resources.mail_path, name);
  58.     (void) mkdir(str, 0700);
  59.     *numfoldersptr = ScanDir(app_resources.mail_path, namelistptr, IsDir);
  60.     for (i=0 ; i<*numfoldersptr ; i++)
  61.     if (strcmp((*namelistptr)[i], name) == 0) return;
  62.     Punt("Can't create new mail folder!");
  63. }
  64.  
  65.  
  66. static void MakeSureSubfolderExists(namelistptr, numfoldersptr, name)
  67.     char ***        namelistptr;
  68.     int *        numfoldersptr;
  69.     char *        name;
  70. {
  71.     char folder[300];
  72.     char subfolder_path[300];
  73.     char *subfolder;
  74.     struct stat buf;
  75.  
  76.     /* Make sure that the parent folder exists */
  77.  
  78.     subfolder = index( strcpy(folder, name), '/');
  79.     *subfolder = '\0';
  80.     subfolder++;
  81.     MakeSureFolderExists(namelistptr, numfoldersptr, folder);
  82.     
  83.     /* The parent folder exists.  Make sure the subfolder exists. */
  84.  
  85.     (void) sprintf(subfolder_path, "%s/%s", app_resources.mail_path, name);
  86.     if (stat(subfolder_path, &buf) /* failed */) {
  87.     (void) mkdir(subfolder_path, 0700);
  88.     if (stat(subfolder_path, &buf) /* failed */)
  89.         Punt("Can't create new xmh subfolder!");
  90.     }
  91.  
  92.     if ((buf.st_mode & S_IFMT) != S_IFDIR)
  93.     Punt("Can't create new xmh subfolder!");
  94. }
  95.  
  96. int TocFolderExists(toc)
  97.     Toc    toc;
  98. {
  99.     struct stat buf;
  100.     if (! toc->path) {
  101.     char str[500];
  102.     (void) sprintf(str, "%s/%s", app_resources.mail_path, toc->foldername);
  103.     toc->path = XtNewString(str);
  104.     }
  105.     return ((stat(toc->path, &buf) == 0) &&
  106.         ((buf.st_mode & S_IFMT) == S_IFDIR));
  107. }
  108.  
  109. static void LoadCheckFiles()
  110. {
  111.     FILE *fid;
  112.     char str[1024];
  113.  
  114.     (void) sprintf(str, "%s/.xmhcheck", homeDir);
  115.     fid = myfopen(str, "r");
  116.     if (fid) {
  117.     int i;
  118.     char *ptr, *ptr2;
  119.  
  120.     while (ptr = ReadLine(fid)) {
  121.         while (*ptr == ' ' || *ptr == '\t') ptr++;
  122.         ptr2 = ptr;
  123.         while (*ptr2 && *ptr2 != ' ' && *ptr2 != '\t') ptr2++;
  124.         if (*ptr2 == 0) continue;
  125.         *ptr2++ = 0;
  126.         while (*ptr2 == ' ' || *ptr2 == '\t') ptr2++;
  127.         if (*ptr2 == 0) continue;
  128.         for (i=0 ; i<numFolders ; i++) {
  129.         if (strcmp(ptr, folderList[i]->foldername) == 0) {
  130.             folderList[i]->incfile = XtNewString(ptr2);
  131.             break;
  132.         }
  133.         }
  134.     }
  135.     myfclose(fid);
  136.     } else if ( app_resources.initial_inc_file &&
  137.            *app_resources.initial_inc_file)
  138.     InitialFolder->incfile = app_resources.initial_inc_file;
  139. }
  140.         
  141.  
  142. /*    PUBLIC ROUTINES     */
  143.  
  144.  
  145. /* Read in the list of folders. */
  146.  
  147. void TocInit()
  148. {
  149.     Toc toc;
  150.     char **namelist;
  151.     int i;
  152.     extern alphasort();
  153.     numFolders = ScanDir(app_resources.mail_path, &namelist, IsDir);
  154.     if (numFolders < 0) {
  155.     (void) mkdir(app_resources.mail_path, 0700);
  156.     numFolders = ScanDir(app_resources.mail_path, &namelist, IsDir);
  157.     if (numFolders < 0)
  158.         Punt("Can't create or read mail directory!");
  159.     }
  160.     if (IsSubfolder(app_resources.initial_folder_name))
  161.     MakeSureSubfolderExists(&namelist, &numFolders,
  162.                 app_resources.initial_folder_name);
  163.     else
  164.     MakeSureFolderExists(&namelist, &numFolders,
  165.                  app_resources.initial_folder_name);
  166.  
  167.     if (IsSubfolder(app_resources.drafts_folder_name))
  168.     MakeSureSubfolderExists(&namelist, &numFolders,
  169.                 app_resources.drafts_folder_name);
  170.     else
  171.     MakeSureFolderExists(&namelist, &numFolders,
  172.                  app_resources.drafts_folder_name);
  173.     folderList = (Toc *) XtMalloc((Cardinal)numFolders * sizeof(Toc));
  174.     for (i=0 ; i<numFolders ; i++) {
  175.     toc = folderList[i] = TUMalloc();
  176.     toc->foldername = XtNewString(namelist[i]);
  177.     free((char *)namelist[i]);
  178.     }
  179.     if (! (InitialFolder = TocGetNamed(app_resources.initial_folder_name)))
  180.     InitialFolder = TocCreate(app_resources.initial_folder_name);
  181.  
  182.     if (! (DraftsFolder = TocGetNamed(app_resources.drafts_folder_name)))
  183.     DraftsFolder = TocCreate(app_resources.drafts_folder_name);
  184.     free((char *)namelist);
  185.     LoadCheckFiles();
  186. }
  187.  
  188.  
  189.  
  190. /* Create a toc and add a folder to the folderList.  */
  191.  
  192. Toc TocCreate(foldername)
  193.     char    *foldername;
  194. {
  195.     Toc        toc = TUMalloc();
  196.  
  197.     toc->foldername = XtNewString(foldername);
  198.     folderList = (Toc *) XtRealloc((char *) folderList,
  199.                    (unsigned) ++numFolders * sizeof(Toc));
  200.     folderList[numFolders - 1] = toc;
  201.     return toc;
  202. }
  203.  
  204.  
  205. /* Create a new folder with the given name. */
  206.  
  207. Toc TocCreateFolder(foldername)
  208. char *foldername;
  209. {
  210.     Toc toc;
  211.     char str[500];
  212.     if (TocGetNamed(foldername)) return NULL;
  213.     (void) sprintf(str, "%s/%s", app_resources.mail_path, foldername);
  214.     if (mkdir(str, 0700) < 0) return NULL;
  215.     toc = TocCreate(foldername);
  216.     return toc;
  217. }
  218.  
  219. int TocHasMail(toc)
  220.     Toc toc;
  221. {
  222.     return toc->mailpending;
  223. }
  224.  
  225. static int CheckForNewMail(toc)
  226.     Toc toc;
  227. {
  228.     if (toc->incfile)
  229.     return (GetFileLength(toc->incfile) > 0);
  230.     else if (toc == InitialFolder) {
  231.     char **argv;
  232.     char *result;
  233.     int hasmail;
  234.  
  235.     argv = MakeArgv(4);
  236.     argv[0] = "msgchk";
  237.     argv[1] = "-nonotify";
  238.     argv[2] = "nomail";
  239.     argv[3] = "-nodate";
  240.     result = DoCommandToString(argv);
  241.     hasmail = (*result != '\0');
  242.     XtFree(result);
  243.     XtFree((char*)argv);
  244.     return hasmail;
  245.     }
  246.     return False;
  247. }
  248.  
  249. /*ARGSUSED*/
  250. void TocCheckForNewMail(update)
  251.     Boolean update;    /* if True, actually make the check */
  252. {
  253.     Toc toc;
  254.     Scrn scrn;
  255.     int i, j, hasmail;
  256.     Boolean mail_waiting = False;
  257.  
  258.     if (update) {
  259.     for (i=0 ; i<numFolders ; i++) {
  260.         toc = folderList[i];
  261.         if (TocCanIncorporate(toc)) {
  262.         toc->mailpending = hasmail = CheckForNewMail(toc);
  263.         if (hasmail) mail_waiting = True;
  264.         for (j=0 ; j<numScrns ; j++) {
  265.             scrn = scrnList[j];
  266.             if (scrn->kind == STtocAndView)
  267.             /* give visual indication of new mail waiting */
  268.             BBoxMailFlag(scrn->folderbuttons, TocName(toc),
  269.                      hasmail);
  270.         }
  271.         }
  272.     }
  273.     } else {
  274.     for (i=0; i < numFolders; i++) {
  275.         toc = folderList[i];
  276.         if (toc->mailpending) {
  277.         mail_waiting = True;
  278.         break;
  279.         }
  280.     }
  281.     }
  282.  
  283.     if (app_resources.mail_waiting_flag) {
  284.     Arg args[1];
  285.     static Boolean icon_state = -1;
  286.  
  287.     if (icon_state != mail_waiting) {
  288.         icon_state = mail_waiting;
  289.         for (i=0; i < numScrns; i++) {
  290.         scrn = scrnList[i];
  291.         if (scrn->kind == STtocAndView) {
  292.             XtSetArg(args[0], XtNiconPixmap,
  293.                  (mail_waiting ? app_resources.new_mail_icon
  294.                                : app_resources.no_mail_icon));
  295.             XtSetValues(scrn->parent, args, (Cardinal)1);
  296.         }
  297.         }
  298.     }
  299.     }
  300. }
  301.  
  302. /* Intended to support mutual exclusion on deleting folders, so that you
  303.  * cannot have two confirm popups at the same time on the same folder.
  304.  *
  305.  * You can have confirm popups on different folders simultaneously.
  306.  * However, I did not protect the user from popping up a delete confirm
  307.  * popup on folder A, then popping up a delete confirm popup on folder
  308.  * A/subA, then deleting A, then deleting A/subA -- which of course is 
  309.  * already gone, and will cause xmh to Punt.
  310.  *
  311.  * TocClearDeletePending is a callback from the No confirmation button
  312.  * of the confirm popup.
  313.  */
  314.  
  315. Boolean TocTestAndSetDeletePending(toc)
  316.     Toc    toc;
  317. {
  318.     Boolean flag;
  319.  
  320.     flag = toc->delete_pending;
  321.     toc->delete_pending = True;
  322.     return flag;
  323. }
  324.  
  325. void TocClearDeletePending(toc)
  326.     Toc    toc;
  327. {
  328.     toc->delete_pending = False;
  329. }
  330.  
  331.  
  332. /* Recursively delete an entire directory.  Nasty. */
  333.  
  334. static void NukeDirectory(path)
  335.     char *path;
  336. {
  337.     struct stat buf;
  338.  
  339. #ifdef S_IFLNK
  340.     /* POSIX.1 does not discuss symbolic links. */
  341.     if (lstat(path, &buf) /* failed */)
  342.     return;
  343.     if ((buf.st_mode & S_IFMT) == S_IFLNK) {
  344.     (void) unlink(path);
  345.     return;
  346.     }
  347. #endif
  348.     if (stat(path, &buf) /* failed */)
  349.     return;
  350.     if (buf.st_mode & S_IWRITE) {
  351.     char **argv = MakeArgv(3);
  352.     argv[0] = "/bin/rm";
  353.     argv[1] = "-rf";
  354.     argv[2] = path;
  355.     (void) DoCommand(argv, (char*)NULL, (char*)NULL);
  356.     XtFree((char*)argv);
  357.     } 
  358. }
  359.  
  360.  
  361. /* Destroy the given folder. */
  362.  
  363. void TocDeleteFolder(toc)
  364. Toc toc;
  365. {
  366.     Toc toc2;
  367.     int i, j, w;
  368.     if (toc == NULL) return;
  369.     TUGetFullFolderInfo(toc);
  370.  
  371.     w = -1;
  372.     for (i=0 ; i<numFolders ; i++) {
  373.     toc2 = folderList[i];
  374.     if (toc2 == toc)
  375.         w = i;
  376.     else if (toc2->validity == valid)
  377.         for (j=0 ; j<toc2->nummsgs ; j++)
  378.         if (toc2->msgs[j]->desttoc == toc)
  379.             MsgSetFate(toc2->msgs[j], Fignore, (Toc) NULL);
  380.     }
  381.     if (w < 0) Punt("Couldn't find it in TocDeleteFolder!");
  382.     NukeDirectory(toc->path);
  383.     if (toc->validity == valid) {
  384.     for (i=0 ; i<toc->nummsgs ; i++) {
  385.         MsgSetScrnForce(toc->msgs[i], (Scrn) NULL);
  386.         MsgFree(toc->msgs[i]);
  387.     }
  388.     XtFree((char *) toc->msgs);
  389.     }
  390.     XtFree((char *)toc);
  391.     numFolders--;
  392.     for (i=w ; i<numFolders ; i++) folderList[i] = folderList[i+1];
  393. }
  394.  
  395.  
  396. /*
  397.  * Display the given toc in the given scrn.  If scrn is NULL, then remove the
  398.  * toc from all scrns displaying it.
  399.  */
  400.  
  401. void TocSetScrn(toc, scrn)
  402. Toc toc;
  403. Scrn scrn;
  404. {
  405.     int i;
  406.     if (toc == NULL && scrn == NULL) return;
  407.     if (scrn == NULL) {
  408.     for (i=0 ; i<toc->num_scrns ; i++)
  409.         TocSetScrn((Toc) NULL, toc->scrn[i]);
  410.     return;
  411.     }
  412.     if (scrn->toc == toc) return;
  413.     if (scrn->toc != NULL) {
  414.     for (i=0 ; i<scrn->toc->num_scrns ; i++)
  415.         if (scrn->toc->scrn[i] == scrn) break;
  416.     if (i >= scrn->toc->num_scrns)
  417.         Punt("Couldn't find scrn in TocSetScrn!");
  418.     scrn->toc->scrn[i] = scrn->toc->scrn[--scrn->toc->num_scrns];
  419.     }
  420.     scrn->toc = toc;
  421.     if (toc == NULL) {
  422.     TUResetTocLabel(scrn);
  423.     TURedisplayToc(scrn);
  424.     StoreWindowName(scrn, progName);
  425.     } else {
  426.     toc->num_scrns++;
  427.     toc->scrn = (Scrn *) XtRealloc((char *) toc->scrn,
  428.                        (unsigned)toc->num_scrns*sizeof(Scrn));
  429.     toc->scrn[toc->num_scrns - 1] = scrn;
  430.     TUEnsureScanIsValidAndOpen(toc);
  431.     TUResetTocLabel(scrn);
  432.     if (app_resources.prefix_wm_and_icon_name) {
  433.         char wm_name[64];
  434.         int length = strlen(progName);
  435.         (void) strncpy(wm_name, progName, length);
  436.         (void) strncpy(wm_name + length , ": ", 2);
  437.         (void) strcpy(wm_name + length + 2, toc->foldername);
  438.         StoreWindowName(scrn, wm_name);
  439.     }
  440.     else
  441.         StoreWindowName(scrn, toc->foldername);
  442.     TURedisplayToc(scrn);
  443.     SetCurrentFolderName(scrn, toc->foldername);
  444.     }
  445.     EnableProperButtons(scrn);
  446. }
  447.  
  448.  
  449.  
  450. /* Remove the given message from the toc.  Doesn't actually touch the file.
  451.    Also note that it does not free the storage for the msg. */
  452.  
  453. void TocRemoveMsg(toc, msg)
  454. Toc toc;
  455. Msg msg;
  456. {
  457.     Msg newcurmsg;
  458.     MsgList mlist;
  459.     int i;
  460.     if (toc->validity == unknown)
  461.     TUGetFullFolderInfo(toc);
  462.     if (toc->validity != valid)
  463.     return;
  464.     newcurmsg = TocMsgAfter(toc, msg);
  465.     if (newcurmsg) newcurmsg->changed = TRUE;
  466.     newcurmsg = toc->curmsg;
  467.     if (msg == toc->curmsg) {
  468.     newcurmsg = TocMsgAfter(toc, msg);
  469.     if (newcurmsg == NULL) newcurmsg = TocMsgBefore(toc, msg);
  470.     toc->curmsg = NULL;
  471.     }
  472.     toc->length -= msg->length;
  473.     if (msg->visible) toc->lastPos -= msg->length;
  474.     for(i = TUGetMsgPosition(toc, msg), toc->nummsgs--; i<toc->nummsgs ; i++) {
  475.     toc->msgs[i] = toc->msgs[i+1];
  476.     if (msg->visible) toc->msgs[i]->position -= msg->length;
  477.     }
  478.     for (i=0 ; i<toc->numsequences ; i++) {
  479.     mlist = toc->seqlist[i]->mlist;
  480.     if (mlist) DeleteMsgFromMsgList(mlist, msg);
  481.     }
  482.  
  483.     if (msg->visible && toc->num_scrns > 0 && !toc->needsrepaint)
  484.     TSourceInvalid(toc, msg->position, -msg->length);
  485.     TocSetCurMsg(toc, newcurmsg);
  486.     TUSaveTocFile(toc);
  487. }
  488.     
  489.  
  490.  
  491. void TocRecheckValidity(toc)
  492.     Toc toc;
  493. {
  494.     int i;
  495.     if (toc && toc->validity == valid && TUScanFileOutOfDate(toc)) {
  496.     if (app_resources.block_events_on_busy) ShowBusyCursor();
  497.  
  498.     TUScanFileForToc(toc);
  499.     if (toc->source)
  500.         TULoadTocFile(toc);
  501.     for (i=0 ; i<toc->num_scrns ; i++)
  502.         TURedisplayToc(toc->scrn[i]);
  503.  
  504.     if (app_resources.block_events_on_busy) UnshowBusyCursor();
  505.     }
  506. }
  507.  
  508.  
  509. /* Set the current message. */
  510.  
  511. void TocSetCurMsg(toc, msg)
  512.   Toc toc;
  513.   Msg msg;
  514. {
  515.     Msg msg2;
  516.     int i;
  517.     if (toc->validity != valid) return;
  518.     if (msg != toc->curmsg) {
  519.     msg2 = toc->curmsg;
  520.     toc->curmsg = msg;
  521.     if (msg2)
  522.         MsgSetFate(msg2, msg2->fate, msg2->desttoc);
  523.     }
  524.     if (msg) {
  525.     MsgSetFate(msg, msg->fate, msg->desttoc);
  526.     if (toc->num_scrns) {
  527.         if (toc->stopupdate)
  528.         toc->needsrepaint = TRUE;
  529.         else {
  530.         for (i=0 ; i<toc->num_scrns ; i++)
  531.             XawTextSetInsertionPoint(toc->scrn[i]->tocwidget,
  532.                         msg->position);
  533.         }
  534.     }
  535.     }
  536. }
  537.  
  538.  
  539. /* Return the current message. */
  540.  
  541. Msg TocGetCurMsg(toc)
  542. Toc toc;
  543. {
  544.     return toc->curmsg;
  545. }
  546.  
  547.  
  548.  
  549.  
  550. /* Return the message after the given one.  (If none, return NULL.) */
  551.  
  552. Msg TocMsgAfter(toc, msg)
  553.   Toc toc;
  554.   Msg msg;
  555. {
  556.     int i;
  557.     i = TUGetMsgPosition(toc, msg);
  558.     do {
  559.     i++;
  560.     if (i >= toc->nummsgs)
  561.         return NULL;
  562.     } while (!(toc->msgs[i]->visible));
  563.     return toc->msgs[i];
  564. }
  565.  
  566.  
  567.  
  568. /* Return the message before the given one.  (If none, return NULL.) */
  569.  
  570. Msg TocMsgBefore(toc, msg)
  571.   Toc toc;
  572.   Msg msg;
  573. {
  574.     int i;
  575.     i = TUGetMsgPosition(toc, msg);
  576.     do {
  577.     i--;
  578.     if (i < 0)
  579.         return NULL;
  580.     } while (!(toc->msgs[i]->visible));
  581.     return toc->msgs[i];
  582. }
  583.  
  584.  
  585.  
  586. /* The caller KNOWS the toc's information is out of date; rescan it. */
  587.  
  588. void TocForceRescan(toc)
  589.     Toc    toc;
  590. {
  591.     register int i;
  592.     if (toc->num_scrns) {
  593.     toc->viewedseq = toc->seqlist[0];
  594.     for (i=0 ; i<toc->num_scrns ; i++)
  595.         TUResetTocLabel(toc->scrn[i]);
  596.     TUScanFileForToc(toc);
  597.     TULoadTocFile(toc);
  598.     for (i=0 ; i<toc->num_scrns ; i++)
  599.         TURedisplayToc(toc->scrn[i]);
  600.     } else {
  601.     TUGetFullFolderInfo(toc);
  602.     (void) unlink(toc->scanfile);
  603.     toc->validity = invalid;
  604.     }
  605. }
  606.  
  607.  
  608.  
  609. /* The caller has just changed a sequence list.  Reread them from mh. */
  610.  
  611. void TocReloadSeqLists(toc)
  612. Toc toc;
  613. {
  614.     int i;
  615.     TocSetCacheValid(toc);
  616.     TULoadSeqLists(toc);
  617.     TURefigureWhatsVisible(toc);
  618.     for (i=0 ; i<toc->num_scrns ; i++) {
  619.     TUResetTocLabel(toc->scrn[i]);
  620.     EnableProperButtons(toc->scrn[i]);
  621.     }
  622. }
  623.  
  624.  
  625. /*ARGSUSED*/
  626. void XmhReloadSeqLists(w, event, params, num_params)
  627.     Widget    w;
  628.     XEvent    *event;
  629.     String    *params;
  630.     Cardinal    *num_params;
  631. {
  632.     Scrn scrn = ScrnFromWidget(w);
  633.     TocReloadSeqLists(scrn->toc);
  634.     TUCheckSequenceMenu(scrn->toc);
  635. }
  636.  
  637.  
  638.  
  639. /* Return TRUE if the toc has an interesting sequence. */
  640.  
  641. int TocHasSequences(toc)
  642. Toc toc;
  643. {
  644.     return toc && toc->numsequences > 1;
  645. }
  646.  
  647.  
  648. /* Change which sequence is being viewed. */
  649.  
  650. void TocChangeViewedSeq(toc, seq)
  651.   Toc toc;
  652.   Sequence seq;
  653. {
  654.     if (seq == NULL) seq = toc->viewedseq;
  655.     toc->viewedseq = seq;
  656.     toc->force_reset = True; /* %%% force Text source to be reset */
  657.     TURefigureWhatsVisible(toc);
  658. }
  659.  
  660.  
  661. /* Return the sequence with the given name in the given toc. */
  662.  
  663. Sequence TocGetSeqNamed(toc, name)
  664. Toc toc;
  665. char *name;
  666. {
  667.     register int i;
  668.     if (name == NULL)
  669.     return (Sequence) NULL;
  670.  
  671.     for (i=0 ; i<toc->numsequences ; i++)
  672.     if (strcmp(toc->seqlist[i]->name, name) == 0)
  673.         return toc->seqlist[i];
  674.     return (Sequence) NULL;
  675. }
  676.  
  677.  
  678. /* Return the sequence currently being viewed in the toc. */
  679.  
  680. Sequence TocViewedSequence(toc)
  681. Toc toc;
  682. {
  683.     return toc->viewedseq;
  684. }
  685.  
  686.  
  687. /* Set the selected sequence in the toc */
  688.  
  689. void TocSetSelectedSequence(toc, sequence)
  690.     Toc        toc;
  691.     Sequence    sequence;
  692. {
  693.     if (toc) 
  694.     toc->selectseq = sequence;
  695. }
  696.  
  697.  
  698. /* Return the sequence currently selected */
  699.  
  700. Sequence TocSelectedSequence(toc)
  701.     Toc    toc;
  702. {
  703.     if (toc) return (toc->selectseq);
  704.     else return (Sequence) NULL;
  705. }
  706.  
  707.  
  708. /* Return the list of messages currently selected. */
  709.  
  710. #define SrcScan XawTextSourceScan
  711.  
  712. MsgList TocCurMsgList(toc)
  713.   Toc toc;
  714. {
  715.     MsgList result;
  716.     XawTextPosition pos1, pos2;
  717.     extern Msg MsgFromPosition();
  718.     if (toc->num_scrns == 0) return NULL;
  719.     result = MakeNullMsgList();
  720.     XawTextGetSelectionPos( toc->scrn[0]->tocwidget, &pos1, &pos2); /* %%% */
  721.     if (pos1 < pos2) {
  722.     pos1 = SrcScan(toc->source, pos1, XawstEOL, XawsdLeft, 1, FALSE);
  723.     pos2 = SrcScan(toc->source, pos2, XawstPositions, XawsdLeft, 1, TRUE);
  724.     pos2 = SrcScan(toc->source, pos2, XawstEOL, XawsdRight, 1, FALSE);
  725.     while (pos1 < pos2) {
  726.         AppendMsgList(result, MsgFromPosition(toc, pos1, XawsdRight));
  727.         pos1 = SrcScan(toc->source, pos1, XawstEOL, XawsdRight, 1, TRUE);
  728.     }
  729.     }
  730.     return result;
  731. }
  732.  
  733.  
  734.  
  735. /* Unset the current selection. */
  736.  
  737. void TocUnsetSelection(toc)
  738. Toc toc;
  739. {
  740.     if (toc->source)
  741.         XawTextUnsetSelection(toc->scrn[0]->tocwidget);
  742. }
  743.  
  744.  
  745.  
  746. /* Create a brand new, blank message. */
  747.  
  748. Msg TocMakeNewMsg(toc)
  749. Toc toc;
  750. {
  751.     Msg msg;
  752.     static int looping = False;
  753.     TUEnsureScanIsValidAndOpen(toc);
  754.     msg = TUAppendToc(toc, "####  empty\n");
  755.     if (FileExists(MsgFileName(msg))) {
  756.     if (looping++) Punt( "Cannot correct scan file" );
  757.         DEBUG2("**** FOLDER %s WAS INVALID; msg %d already existed!\n",
  758.            toc->foldername, msg->msgid);
  759.     TocForceRescan(toc);
  760.     return TocMakeNewMsg(toc); /* Try again.  Using recursion here is ugly,
  761.                       but what the hack ... */
  762.     }
  763.     CopyFileAndCheck("/dev/null", MsgFileName(msg));
  764.     looping = False;
  765.     return msg;
  766. }
  767.  
  768.  
  769. /* Set things to not update cache or display until further notice. */
  770.  
  771. void TocStopUpdate(toc)
  772. Toc toc;
  773. {
  774.     int i;
  775.     for (i=0 ; i<toc->num_scrns ; i++)
  776.     XawTextDisableRedisplay(toc->scrn[i]->tocwidget);
  777.     toc->stopupdate++;
  778. }
  779.  
  780.  
  781. /* Start updating again, and do whatever updating has been queued. */
  782.  
  783. void TocStartUpdate(toc)
  784. Toc toc;
  785. {
  786.     int i;
  787.     if (toc->stopupdate && --(toc->stopupdate) == 0) {
  788.     for (i=0 ; i<toc->num_scrns ; i++) {
  789.         if (toc->needsrepaint) 
  790.         TURedisplayToc(toc->scrn[i]);
  791.         if (toc->needslabelupdate)
  792.         TUResetTocLabel(toc->scrn[i]);
  793.     }
  794.     if (toc->needscachesave)
  795.         TUSaveTocFile(toc);
  796.     }
  797.     for (i=0 ; i<toc->num_scrns ; i++)
  798.     XawTextEnableRedisplay(toc->scrn[i]->tocwidget);
  799. }
  800.  
  801.  
  802.  
  803. /* Something has happened that could later convince us that our cache is out
  804.    of date.  Make this not happen; our cache really *is* up-to-date. */
  805.  
  806. void TocSetCacheValid(toc)
  807. Toc toc;
  808. {
  809.     TUSaveTocFile(toc);
  810. }
  811.  
  812.  
  813. /* Return the full folder pathname of the given toc, prefixed w/'+' */
  814.  
  815. char *TocMakeFolderName(toc)
  816. Toc toc;
  817. {
  818.     char* name = XtMalloc((Cardinal) (strlen(toc->path) + 2) );
  819.     (void)sprintf( name, "+%s", toc->path );
  820.     return name;
  821. }
  822.  
  823. char *TocName(toc)
  824. Toc toc;
  825. {
  826.     return toc->foldername;
  827. }
  828.  
  829.  
  830.  
  831. /* Given a foldername, return the corresponding toc. */
  832.  
  833. Toc TocGetNamed(name)
  834. char *name;
  835. {
  836.     int i;
  837.     for (i=0; i<numFolders ; i++)
  838.     if (strcmp(folderList[i]->foldername, name) == 0) return folderList[i];
  839.     return NULL;
  840. }
  841.  
  842.  
  843.  
  844. /* Throw out all changes to this toc, and close all views of msgs in it.
  845.    Requires confirmation by the user. */
  846.  
  847. /*ARGSUSED*/
  848. static void TocCataclysmOkay(widget, client_data, call_data)
  849.     Widget    widget;        /* unused */
  850.     XtPointer    client_data;
  851.     XtPointer    call_data;    /* unused */
  852. {
  853.     Toc            toc = (Toc) client_data;
  854.     register int    i;
  855.  
  856.     for (i=0; i < toc->nummsgs; i++)
  857.     MsgSetFate(toc->msgs[i], Fignore, (Toc)NULL);
  858.  
  859. /* Doesn't make sense to have this MsgSetScrn for loop here. dmc. %%% */
  860.     for (i=0; i < toc->nummsgs; i++)
  861.     MsgSetScrn(toc->msgs[i], (Scrn) NULL, (XtCallbackList) NULL, 
  862.            (XtCallbackList) NULL);
  863. }
  864.     
  865. int TocConfirmCataclysm(toc, confirms, cancels)
  866.     Toc            toc;
  867.     XtCallbackList    confirms;
  868.     XtCallbackList    cancels;
  869. {    
  870.     register int    i;
  871.     int            found = False;
  872.     static XtCallbackRec yes_callbacks[] = {
  873.     {TocCataclysmOkay,    (XtPointer) NULL},
  874.     {(XtCallbackProc) NULL,    (XtPointer) NULL},
  875.     {(XtCallbackProc) NULL,    (XtPointer) NULL}
  876.     };
  877.  
  878.     if (! toc)
  879.     return 0;
  880.  
  881.     for (i=0 ; i<toc->nummsgs && !found ; i++)
  882.     if (toc->msgs[i]->fate != Fignore) found = True;
  883.  
  884.     if (found) {
  885.     char        str[300];
  886.     Widget        tocwidget;
  887.     int        i;
  888.  
  889.     (void)sprintf(str,"Are you sure you want to remove all changes to %s?",
  890.               toc->foldername);
  891.     yes_callbacks[0].closure = (XtPointer) toc;
  892.     yes_callbacks[1].callback = confirms[0].callback;
  893.     yes_callbacks[1].closure = confirms[0].closure;
  894.  
  895.     tocwidget = NULL;
  896.     for (i=0; i < toc->num_scrns; i++)
  897.         if (toc->scrn[i]->mapped) {
  898.         tocwidget = toc->scrn[i]->tocwidget;
  899.         break;
  900.         }
  901.  
  902.     PopupConfirm(tocwidget, str, yes_callbacks, cancels);
  903.     return NEEDS_CONFIRMATION;
  904.     }
  905.     else {
  906. /* Doesn't make sense to have this MsgSetFate for loop here. dmc. %%% */
  907.     for (i=0 ; i<toc->nummsgs ; i++)
  908.         MsgSetFate(toc->msgs[i], Fignore, (Toc)NULL);
  909.  
  910.     for (i=0 ; i<toc->nummsgs ; i++)
  911.         if (MsgSetScrn(toc->msgs[i], (Scrn) NULL, confirms, cancels))
  912.         return NEEDS_CONFIRMATION;
  913.     return 0;
  914.     }
  915. }
  916.     
  917.  
  918. /* Commit all the changes in this toc; all messages will meet their 'fate'. */
  919.  
  920. /*ARGSUSED*/
  921. void TocCommitChanges(widget, client_data, call_data)
  922.     Widget    widget;        /* unused */
  923.     XtPointer    client_data;    
  924.     XtPointer    call_data;    /* unused */
  925. {
  926.     Toc toc = (Toc) client_data;
  927.     Msg msg;
  928.     int i, cur;
  929.     char str[100], **argv;
  930.     FateType curfate, fate; 
  931.     Toc desttoc;
  932.     Toc curdesttoc;
  933.     XtCallbackRec    confirms[2];
  934.  
  935.     confirms[0].callback = TocCommitChanges;
  936.     confirms[0].closure = (XtPointer) toc;
  937.     confirms[1].callback = (XtCallbackProc) NULL;
  938.     confirms[1].closure = (XtPointer) NULL;
  939.  
  940.     if (toc == NULL) return;
  941.     for (i=0 ; i<toc->nummsgs ; i++) {
  942.     msg = toc->msgs[i];
  943.     fate = MsgGetFate(msg, (Toc *)NULL);
  944.     if (fate != Fignore && fate != Fcopy)
  945.         if (MsgSetScrn(msg, (Scrn) NULL, confirms, (XtCallbackList) NULL)
  946.         == NEEDS_CONFIRMATION)
  947.             return;
  948.     }
  949.     XFlush(XtDisplay(toc->scrn[0]->parent));
  950.     for (i=0 ; i<numFolders ; i++)
  951.     TocStopUpdate(folderList[i]);
  952.     toc->haschanged = TRUE;
  953.     if (app_resources.block_events_on_busy) ShowBusyCursor();
  954.  
  955.     do {
  956.     curfate = Fignore;
  957.     i = 0;
  958.     while (i < toc->nummsgs) {
  959.         msg = toc->msgs[i];
  960.         fate = MsgGetFate(msg, &desttoc);
  961.         if (curfate == Fignore && fate != Fignore) {
  962.         curfate = fate;
  963.         argv = MakeArgv(2);
  964.         switch (curfate) {
  965.           case Fdelete:
  966.             argv[0] = XtNewString("rmm");
  967.             argv[1] = TocMakeFolderName(toc);
  968.             cur = 2;
  969.             curdesttoc = NULL;
  970.             break;
  971.           case Fmove:
  972.           case Fcopy:
  973.             argv[0] = XtNewString("refile");
  974.             cur = 1;
  975.             curdesttoc = desttoc;
  976.             break;
  977.         }
  978.         }
  979.         if (curfate != Fignore &&
  980.           curfate == fate && desttoc == curdesttoc) {
  981.         argv = ResizeArgv(argv, cur + 1);
  982.         (void) sprintf(str, "%d", MsgGetId(msg));
  983.         argv[cur++] = XtNewString(str);
  984.         MsgSetFate(msg, Fignore, (Toc)NULL);
  985.         if (curdesttoc) {
  986.             (void) TUAppendToc(curdesttoc, MsgGetScanLine(msg));
  987.             curdesttoc->haschanged = TRUE;
  988.         }
  989.         if (curfate != Fcopy) {
  990.             TocRemoveMsg(toc, msg);
  991.             MsgFree(msg);
  992.             i--;
  993.         }
  994.         if (cur > 40)
  995.             break;    /* Do only 40 at a time, just to be safe. */
  996.         } 
  997.         i++;
  998.     }
  999.     if (curfate != Fignore) {
  1000.         switch (curfate) {
  1001.           case Fmove:
  1002.           case Fcopy:
  1003.         argv = ResizeArgv(argv, cur + 4);
  1004.         argv[cur++] = XtNewString(curfate == Fmove ? "-nolink"
  1005.                                       : "-link");
  1006.         argv[cur++] = XtNewString("-src");
  1007.         argv[cur++] = TocMakeFolderName(toc);
  1008.         argv[cur++] = TocMakeFolderName(curdesttoc);
  1009.         break;
  1010.         }
  1011.         if (app_resources.debug) {
  1012.         for (i = 0; i < cur; i++)
  1013.             (void) fprintf(stderr, "%s ", argv[i]);
  1014.         (void) fprintf(stderr, "\n");
  1015.         (void) fflush(stderr);
  1016.         }
  1017.         DoCommand(argv, (char *) NULL, (char *) NULL);
  1018.         for (i = 0; argv[i]; i++)
  1019.         XtFree((char *) argv[i]);
  1020.         XtFree((char *) argv);
  1021.     }
  1022.     } while (curfate != Fignore);
  1023.     for (i=0 ; i<numFolders ; i++) {
  1024.     if (folderList[i]->haschanged) {
  1025.         TocReloadSeqLists(folderList[i]);
  1026.         folderList[i]->haschanged = FALSE;
  1027.     }
  1028.     TocStartUpdate(folderList[i]);
  1029.     }
  1030.  
  1031.     if (app_resources.block_events_on_busy) UnshowBusyCursor();
  1032. }
  1033.  
  1034.  
  1035.  
  1036. /* Return whether the given toc can incorporate mail. */
  1037.  
  1038. int TocCanIncorporate(toc)
  1039. Toc toc;
  1040. {
  1041.     return (toc && (toc == InitialFolder || toc->incfile));
  1042. }
  1043.  
  1044.  
  1045. /* Incorporate new messages into the given toc. */
  1046.  
  1047. int TocIncorporate(toc)
  1048. Toc toc;
  1049. {
  1050.     char **argv;
  1051.     char str[100], *file, *ptr;
  1052.     Msg msg, firstmessage = NULL;
  1053.     FILEPTR fid;
  1054.  
  1055.     argv = MakeArgv(toc->incfile ? 7 : 4);
  1056.     argv[0] = "inc";
  1057.     argv[1] = TocMakeFolderName(toc);
  1058.     argv[2] = "-width";
  1059.     (void) sprintf(str, "%d", app_resources.toc_width);
  1060.     argv[3] = str;
  1061.     if (toc->incfile) {
  1062.     argv[4] = "-file";
  1063.     argv[5] = toc->incfile;
  1064.     argv[6] = "-truncate";
  1065.     }
  1066.     if (app_resources.block_events_on_busy) ShowBusyCursor();
  1067.  
  1068.     file = DoCommandToFile(argv);
  1069.     XtFree(argv[1]);
  1070.     XtFree((char *)argv);
  1071.     TUGetFullFolderInfo(toc);
  1072.     if (toc->validity == valid) {
  1073.     fid = FOpenAndCheck(file, "r");
  1074.     TocStopUpdate(toc);
  1075.     while (ptr = ReadLineWithCR(fid)) {
  1076.         if (atoi(ptr) > 0) {
  1077.         msg = TUAppendToc(toc, ptr);
  1078.         if (firstmessage == NULL) firstmessage = msg;
  1079.         }
  1080.     }
  1081.     if (firstmessage && firstmessage->visible) {
  1082.         TocSetCurMsg(toc, firstmessage);
  1083.     }
  1084.     TocStartUpdate(toc);
  1085.     (void) myfclose(fid);
  1086.     }
  1087.     DeleteFileAndCheck(file);
  1088.  
  1089.     if (app_resources.block_events_on_busy) UnshowBusyCursor();
  1090.  
  1091.     toc->mailpending = False;
  1092.     return (firstmessage != NULL);
  1093. }
  1094.  
  1095.  
  1096. /* The given message has changed.  Rescan it and change the scanfile. */
  1097.  
  1098. void TocMsgChanged(toc, msg)
  1099. Toc toc;
  1100. Msg msg;
  1101. {
  1102.     char **argv, str[100], str2[10], *ptr;
  1103.     int length, delta, i;
  1104.     FateType fate;
  1105.     Toc desttoc;
  1106.     if (toc->validity != valid) return;
  1107.     fate = MsgGetFate(msg, &desttoc);
  1108.     MsgSetFate(msg, Fignore, (Toc) NULL);
  1109.     argv = MakeArgv(6);
  1110.     argv[0] = "scan";
  1111.     argv[1] = TocMakeFolderName(toc);
  1112.     (void) sprintf(str, "%d", msg->msgid);
  1113.     argv[2] = str;
  1114.     argv[3] = "-width";
  1115.     (void) sprintf(str2, "%d", app_resources.toc_width);
  1116.     argv[4] = str2;
  1117.     argv[5] = "-noheader";
  1118.     ptr = DoCommandToString(argv);
  1119.     XtFree(argv[1]);
  1120.     XtFree((char *) argv);
  1121.     if (strcmp(ptr, msg->buf) != 0) {
  1122.     length = strlen(ptr);
  1123.     delta = length - msg->length;
  1124.     XtFree(msg->buf);
  1125.     msg->buf = ptr;
  1126.     msg->length = length;
  1127.     toc->length += delta;
  1128.     if (msg->visible) {
  1129.         if (delta != 0) {
  1130.         for (i=TUGetMsgPosition(toc, msg)+1; i<toc->nummsgs ; i++)
  1131.             toc->msgs[i]->position += delta;
  1132.         toc->lastPos += delta;
  1133.         }
  1134.         for (i=0 ; i<toc->num_scrns ; i++)
  1135.         TURedisplayToc(toc->scrn[i]);
  1136.     }
  1137.     MsgSetFate(msg, fate, desttoc);
  1138.     TUSaveTocFile(toc);
  1139.     } else XtFree(ptr);
  1140. }
  1141.  
  1142.  
  1143.  
  1144. Msg TocMsgFromId(toc, msgid)
  1145. Toc toc;
  1146. int msgid;
  1147. {
  1148.     int h, l, m;
  1149.     l = 0;
  1150.     h = toc->nummsgs - 1;
  1151.     if (h < 0) {
  1152.     if (app_resources.debug) {
  1153.         char str[100];
  1154.         (void)sprintf(str, "Toc is empty! folder=%s\n", toc->foldername);
  1155.         DEBUG( str )
  1156.     }
  1157.     return NULL;
  1158.     }
  1159.     while (l < h - 1) {
  1160.     m = (l + h) / 2;
  1161.     if (toc->msgs[m]->msgid > msgid)
  1162.         h = m;
  1163.     else
  1164.         l = m;
  1165.     }
  1166.     if (toc->msgs[l]->msgid == msgid) return toc->msgs[l];
  1167.     if (toc->msgs[h]->msgid == msgid) return toc->msgs[h];
  1168.     if (app_resources.debug) {
  1169.     char str[100];
  1170.     (void) sprintf(str,
  1171.               "TocMsgFromId search failed! hi=%d, lo=%d, msgid=%d\n",
  1172.               h, l, msgid);
  1173.     DEBUG( str )
  1174.     }
  1175.     return NULL;
  1176. }
  1177.  
  1178. /* Sequence names are put on a stack which is specific to the folder. 
  1179.  * Sequence names are very volatile, so we make our own copies of the strings.
  1180.  */
  1181.  
  1182. /*ARGSUSED*/
  1183. void XmhPushSequence(w, event, params, count)
  1184.     Widget    w;
  1185.     XEvent    *event;
  1186.     String    *params;
  1187.     Cardinal    *count;
  1188. {
  1189.     Scrn    scrn = ScrnFromWidget(w);
  1190.     Toc        toc;
  1191.     int        i;
  1192.  
  1193.     if (! (toc = scrn->toc)) return;
  1194.     
  1195.     if (*count == 0) {
  1196.     if (toc->selectseq)
  1197.         Push(&toc->sequence_stack, XtNewString(toc->selectseq->name));
  1198.     }
  1199.     else
  1200.     for (i=0; i < *count; i++) 
  1201.         Push(&toc->sequence_stack, XtNewString(params[i]));
  1202. }
  1203.  
  1204.  
  1205. /*ARGSUSED*/
  1206. void XmhPopSequence(w, event, params, count)
  1207.     Widget    w;        /* any widget on the screen of interest */
  1208.     XEvent    *event;
  1209.     String    *params;
  1210.     Cardinal    *count;
  1211. {
  1212.     Scrn    scrn = ScrnFromWidget(w);
  1213.     char    *seqname;
  1214.     Widget    sequenceMenu, selected, original;
  1215.     Button    button;
  1216.     Sequence    sequence;
  1217.  
  1218.     if ((seqname = Pop(&scrn->toc->sequence_stack)) != NULL) {
  1219.  
  1220.     button = BBoxFindButtonNamed(scrn->mainbuttons,
  1221.                      MenuBoxButtons[XMH_SEQUENCE].button_name);
  1222.     sequenceMenu = BBoxMenuOfButton(button);
  1223.  
  1224.     if (selected = XawSimpleMenuGetActiveEntry(sequenceMenu))
  1225.         ToggleMenuItem(selected, False);
  1226.  
  1227.     if (original = XtNameToWidget(sequenceMenu, seqname)) {
  1228.         ToggleMenuItem(original, True);
  1229.         sequence = TocGetSeqNamed(scrn->toc, seqname);
  1230.         TocSetSelectedSequence(scrn->toc, sequence);
  1231.     }
  1232.     XtFree(seqname);
  1233.     }
  1234. }
  1235.