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