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