source: trunk/third/xmh/folder.c @ 9658

Revision 9658, 27.4 KB checked in by ghudson, 28 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r9657, which included commits to RCS files with non-trunk default branches.
Line 
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
39typedef 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
46static void CreateFolderMenu();
47static void AddFolderMenuEntry();
48static void DeleteFolderMenuEntry();
49
50#ifdef DEBUG_CLEANUP
51extern Boolean ExitLoop;
52#endif
53
54/* Close this toc&view scrn.  If this is the last toc&view, quit xmh. */
55
56/*ARGSUSED*/
57void 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*/
121void 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*/
134void 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*/
151void 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*/
187void 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*/
203void 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*/
216void 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*/
233void 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
246static char *previous_label = NULL;
247/*ARGSUSED*/
248static 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*/
306void 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*/
317void 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*/
329void 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*/
352void 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*/
406void 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*/
483void 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*/
519void 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
539static char filename[500];      /* for IsFolder() and for callback */
540static 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 */
546static 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*/
572static 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*/
582void 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
596static 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
659static 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
704static 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*/
751void 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*/
786void 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*/
818void XmhLeaveFolderButton(w, event, vector, count)
819    Widget      w;
820    XEvent      *event;
821    String      *vector;
822    Cardinal    *count;
823{
824    LastMenuButtonPressed = NULL;
825}
826
827
828void 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
838char * 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*/
857void 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*/
876void 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
889static 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*/
903void 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}
Note: See TracBrowser for help on using the repository browser.