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