Files
xsrc/xfree/xc/programs/xmh/folder.c

1189 lines
32 KiB
C

/*
* $XConsortium: folder.c,v 2.44 94/08/29 20:25:49 swick Exp $
*
*
* COPYRIGHT 1987, 1989
* DIGITAL EQUIPMENT CORPORATION
* MAYNARD, MASSACHUSETTS
* ALL RIGHTS RESERVED.
*
* THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT NOTICE AND
* SHOULD NOT BE CONSTRUED AS A COMMITMENT BY DIGITAL EQUIPMENT CORPORATION.
* DIGITAL MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THIS SOFTWARE FOR
* ANY PURPOSE. IT IS SUPPLIED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
*
* IF THE SOFTWARE IS MODIFIED IN A MANNER CREATING DERIVATIVE COPYRIGHT
* RIGHTS, APPROPRIATE LEGENDS MAY BE PLACED ON THE DERIVATIVE WORK IN
* ADDITION TO THAT SET FORTH ABOVE.
*
* Permission to use, copy, modify, and distribute this software and its
* documentation for any purpose and without fee is hereby granted, provided
* that the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation, and that the name of Digital Equipment Corporation not be
* used in advertising or publicity pertaining to distribution of the software
* without specific, written prior permission.
*/
/* $XFree86: xc/programs/xmh/folder.c,v 1.4 2002/04/05 21:06:28 dickey Exp $ */
/* folder.c -- implement buttons relating to folders and other globals. */
#include "xmh.h"
#include <X11/Xmu/CharSet.h>
#include <X11/Xaw/Cardinals.h>
#include <X11/Xatom.h>
#include <sys/stat.h>
#include <ctype.h>
#include "bboxint.h"
#include "tocintrnl.h"
#include "actions.h"
typedef struct { /* client data structure for callbacks */
Scrn scrn; /* the xmh scrn of action */
Toc toc; /* the toc of the selected folder */
Toc original_toc; /* the toc of the current folder */
} DeleteDataRec, *DeleteData;
static void CheckAndDeleteFolder(XMH_CB_ARGS);
static void CancelDeleteFolder(XMH_CB_ARGS);
static void CheckAndConfirmDeleteFolder(XMH_CB_ARGS);
static void FreeMenuData(XMH_CB_ARGS);
static void CreateFolderMenu(Button);
static void AddFolderMenuEntry(Button, char *);
static void DeleteFolderMenuEntry(Button, char *);
#ifdef DEBUG_CLEANUP
extern Boolean ExitLoop;
#endif
/* Close this toc&view scrn. If this is the last toc&view, quit xmh. */
/*ARGSUSED*/
void DoClose(
Widget widget,
XtPointer client_data,
XtPointer call_data)
{
Scrn scrn = (Scrn) client_data;
register int i, count;
Toc toc;
XtCallbackRec confirm_callbacks[2];
count = 0;
for (i=0 ; i<numScrns ; i++)
if (scrnList[i]->kind == STtocAndView && scrnList[i]->mapped)
count++;
confirm_callbacks[0].callback = (XtCallbackProc) DoClose;
confirm_callbacks[0].closure = (XtPointer) scrn;
confirm_callbacks[1].callback = (XtCallbackProc) NULL;
confirm_callbacks[1].closure = (XtPointer) NULL;
if (count <= 1) {
for (i = numScrns - 1; i >= 0; i--)
if (scrnList[i] != scrn) {
if (MsgSetScrn((Msg) NULL, scrnList[i], confirm_callbacks,
(XtCallbackList) NULL) == NEEDS_CONFIRMATION)
return;
}
for (i = 0; i < numFolders; i++) {
toc = folderList[i];
if (TocConfirmCataclysm(toc, confirm_callbacks,
(XtCallbackList) NULL))
return;
}
/* if (MsgSetScrn((Msg) NULL, scrn))
* return;
* %%%
* for (i = 0; i < numFolders; i++) {
* toc = folderList[i];
* if (toc->scanfile && toc->curmsg)
* CmdSetSequence(toc, "cur", MakeSingleMsgList(toc->curmsg));
* }
*/
#ifdef DEBUG_CLEANUP
XtDestroyWidget(scrn->parent);
ExitLoop = TRUE;
return;
#else
XtVaSetValues(scrn->parent, XtNjoinSession, (XtArgVal)False, NULL);
XtUnmapWidget(scrn->parent);
XtDestroyApplicationContext
(XtWidgetToApplicationContext(scrn->parent));
exit(0);
#endif
}
else {
if (MsgSetScrn((Msg) NULL, scrn, confirm_callbacks,
(XtCallbackList) NULL) == NEEDS_CONFIRMATION)
return;
DestroyScrn(scrn); /* doesn't destroy first toc&view scrn */
}
}
/*ARGSUSED*/
void XmhClose(
Widget w,
XEvent *event, /* unused */
String *params, /* unused */
Cardinal *num_params) /* unused */
{
Scrn scrn = ScrnFromWidget(w);
DoClose(w, (XtPointer) scrn, (XtPointer) NULL);
}
/* Open the selected folder in this screen. */
/* ARGSUSED*/
void DoOpenFolder(
Widget widget,
XtPointer client_data,
XtPointer call_data)
{
/* Invoked by the Folder menu entry "Open Folder"'s notify action. */
Scrn scrn = (Scrn) client_data;
Toc toc = SelectedToc(scrn);
if (TocFolderExists(toc))
TocSetScrn(toc, scrn);
else
PopupError(scrn->parent, "Cannot open selected folder.");
}
/*ARGSUSED*/
void XmhOpenFolder(
Widget w,
XEvent *event, /* unused */
String *params,
Cardinal *num_params)
{
Scrn scrn = ScrnFromWidget(w);
/* This action may be invoked from folder menu buttons or from folder
* menus, as an action procedure on an event specified in translations.
* In this case, the action will open a folder only if that folder
* was actually selected from a folder button or menu. If the folder
* was selected from a folder menu, the menu entry callback procedure,
* which changes the selected folder, and is invoked by the "notify"
* action, must have already executed; and the menu entry "unhightlight"
* action must execute after this action.
*
* This action does not execute if invoked as an accelerator whose
* source widget is a menu button or a folder menu. However, it
* may be invoked as a keyboard accelerator of any widget other than
* the folder menu buttons or the folder menus. In that case, it will
* open the currently selected folder.
*
* If given a parameter, it will take it as the name of a folder to
* select and open.
*/
if (! UserWantsAction(w, scrn)) return;
if (*num_params) SetCurrentFolderName(scrn, params[0]);
DoOpenFolder(w, (XtPointer) scrn, (XtPointer) NULL);
}
/* Compose a new message. */
/*ARGSUSED*/
void DoComposeMessage(
Widget widget,
XtPointer client_data,
XtPointer call_data)
{
Scrn scrn = NewCompScrn();
Msg msg = TocMakeNewMsg(DraftsFolder);
MsgLoadComposition(msg);
MsgSetTemporary(msg);
MsgSetReapable(msg);
MsgSetScrnForComp(msg, scrn);
MapScrn(scrn);
}
/*ARGSUSED*/
void XmhComposeMessage(
Widget w,
XEvent *event, /* unused */
String *params, /* unused */
Cardinal *num_params) /* unused */
{
DoComposeMessage(w, (XtPointer) NULL, (XtPointer) NULL);
}
/* Make a new scrn displaying the given folder. */
/*ARGSUSED*/
void DoOpenFolderInNewWindow(
Widget widget,
XtPointer client_data,
XtPointer call_data)
{
Scrn scrn = (Scrn) client_data;
Toc toc = SelectedToc(scrn);
if (TocFolderExists(toc)) {
scrn = CreateNewScrn(STtocAndView);
TocSetScrn(toc, scrn);
MapScrn(scrn);
} else
PopupError(scrn->parent, "Cannot open selected folder.");
}
/*ARGSUSED*/
void XmhOpenFolderInNewWindow(
Widget w,
XEvent *event, /* unused */
String *params, /* unused */
Cardinal *num_params) /* unused */
{
Scrn scrn = ScrnFromWidget(w);
DoOpenFolderInNewWindow(w, (XtPointer) scrn, (XtPointer) NULL);
}
/* Create a new folder with the given name. */
static char *previous_label = NULL;
/*ARGSUSED*/
static void CreateFolder(
Widget widget, /* the okay button of the dialog widget */
XtPointer client_data, /* the dialog widget */
XtPointer call_data)
{
Toc toc;
register int i;
char *name;
Widget dialog = (Widget) client_data;
Arg args[3];
char str[300], *label;
name = XawDialogGetValueString(dialog);
for (i=0 ; name[i] > ' ' ; i++) ;
name[i] = '\0';
toc = TocGetNamed(name);
if ((toc) || (i==0) || (name[0]=='/') || ((toc = TocCreateFolder(name))
== NULL)) {
if (toc)
(void) sprintf(str, "Folder \"%s\" already exists. Try again.",
name);
else if (name[0]=='/')
(void) sprintf(str, "Please specify folders relative to \"%s\".",
app_resources.mail_path);
else
(void) sprintf(str, "Cannot create folder \"%s\". Try again.",
name);
label = XtNewString(str);
XtSetArg(args[0], XtNlabel, label);
XtSetArg(args[1], XtNvalue, "");
XtSetValues(dialog, args, TWO);
if (previous_label)
XtFree(previous_label);
previous_label = label;
return;
}
for (i = 0; i < numScrns; i++)
if (scrnList[i]->folderbuttons) {
char *c;
Button button;
if ((c = strchr(name, '/'))) { /* if is subfolder */
c[0] = '\0';
button = BBoxFindButtonNamed(scrnList[i]->folderbuttons,
name);
c[0] = '/';
if (button) AddFolderMenuEntry(button, name);
}
else
BBoxAddButton(scrnList[i]->folderbuttons, name,
menuButtonWidgetClass, True);
}
DestroyPopup(widget, (XtPointer) XtParent(dialog), (XtPointer) NULL);
}
/* Create a new folder. Requires the user to name the new folder. */
/*ARGSUSED*/
void DoCreateFolder(
Widget widget, /* unused */
XtPointer client_data,
XtPointer call_data) /* unused */
{
Scrn scrn = (Scrn) client_data;
PopupPrompt(scrn->parent, "Create folder named:", CreateFolder);
}
/*ARGSUSED*/
void XmhCreateFolder(
Widget w,
XEvent *event, /* unused */
String *params, /* unused */
Cardinal *num_params) /* unused */
{
Scrn scrn = ScrnFromWidget(w);
DoCreateFolder(w, (XtPointer)scrn, (XtPointer)NULL);
}
/*ARGSUSED*/
static void CancelDeleteFolder(
Widget widget, /* unused */
XtPointer client_data,
XtPointer call_data) /* unused */
{
DeleteData deleteData = (DeleteData) client_data;
TocClearDeletePending(deleteData->toc);
/* When the delete request is made, the toc currently being viewed is
* changed if necessary to be the toc under consideration for deletion.
* Once deletion has been confirmed or cancelled, we revert to display
* the toc originally under view, unless the toc originally under
* view has been deleted.
*/
if (deleteData->original_toc != NULL)
TocSetScrn(deleteData->original_toc, deleteData->scrn);
XtFree((char *) deleteData);
}
/*ARGSUSED*/
static void CheckAndConfirmDeleteFolder(
Widget widget, /* unreliable; sometimes NULL */
XtPointer client_data, /* data structure */
XtPointer call_data) /* unused */
{
DeleteData deleteData = (DeleteData) client_data;
Scrn scrn = deleteData->scrn;
Toc toc = deleteData->toc;
char str[300];
XtCallbackRec confirms[2];
XtCallbackRec cancels[2];
static XtCallbackRec yes_callbacks[] = {
{CheckAndDeleteFolder, (XtPointer) NULL},
{(XtCallbackProc) NULL, (XtPointer) NULL}
};
static XtCallbackRec no_callbacks[] = {
{CancelDeleteFolder, (XtPointer) NULL},
{(XtCallbackProc) NULL, (XtPointer) NULL}
};
/* Display the toc of the folder to be deleted. */
TocSetScrn(toc, scrn);
/* Check for pending delete, copy, move, or edits on messages in the
* folder to be deleted, and ask for confirmation if they are found.
*/
confirms[0].callback = (XtCallbackProc) CheckAndConfirmDeleteFolder;
confirms[0].closure = client_data;
confirms[1].callback = (XtCallbackProc) NULL;
confirms[1].closure = (XtPointer) NULL;
cancels[0].callback = (XtCallbackProc) CancelDeleteFolder;
cancels[0].closure = client_data;
cancels[1].callback = (XtCallbackProc) NULL;
cancels[1].closure = (XtPointer) NULL;
if (TocConfirmCataclysm(toc, confirms, cancels) == NEEDS_CONFIRMATION)
return;
/* Ask the user for confirmation on destroying the folder. */
yes_callbacks[0].closure = client_data;
no_callbacks[0].closure = client_data;
(void) sprintf(str, "Are you sure you want to destroy %s?", TocName(toc));
PopupConfirm(scrn->tocwidget, str, yes_callbacks, no_callbacks);
}
/*ARGSUSED*/
static void CheckAndDeleteFolder(
Widget widget, /* unused */
XtPointer client_data, /* data structure */
XtPointer call_data) /* unused */
{
DeleteData deleteData = (DeleteData) client_data;
Scrn scrn = deleteData->scrn;
Toc toc = deleteData->toc;
XtCallbackRec confirms[2];
XtCallbackRec cancels[2];
int i;
char *foldername;
/* Check for changes occurring after the popup was first presented. */
confirms[0].callback = (XtCallbackProc) CheckAndConfirmDeleteFolder;
confirms[0].closure = client_data;
confirms[1].callback = (XtCallbackProc) NULL;
confirms[1].closure = (XtPointer) NULL;
cancels[0].callback = (XtCallbackProc) CancelDeleteFolder;
cancels[0].closure = client_data;
cancels[1].callback = (XtCallbackProc) NULL;
cancels[1].closure = (XtPointer) NULL;
if (TocConfirmCataclysm(toc, confirms, cancels) == NEEDS_CONFIRMATION)
return;
/* Delete. Restore the previously viewed toc, if it wasn't deleted. */
foldername = TocName(toc);
TocSetScrn(toc, (Scrn) NULL);
TocDeleteFolder(toc);
for (i=0 ; i<numScrns ; i++)
if (scrnList[i]->folderbuttons) {
if (IsSubfolder(foldername)) {
char parent_folder[300];
char *c = strchr( strcpy(parent_folder, foldername), '/');
*c = '\0';
/* Since menus are built upon demand, and are a per-xmh-screen resource,
* not all xmh toc & view screens will have the same menus built.
* So the menu entry deletion routines must be able to handle a button
* whose menu field is null. It would be better to share folder menus
* between xmh screens, but accelerators call action procedures which depend
* upon being able to get the xmh screen (Scrn) from the widget argument.
*/
DeleteFolderMenuEntry
( BBoxFindButtonNamed( scrnList[i]->folderbuttons,
parent_folder),
foldername);
}
else {
BBoxDeleteButton
(BBoxFindButtonNamed( scrnList[i]->folderbuttons,
foldername));
}
/* If we've deleted the current folder, show the Initial Folder */
if ((! strcmp(scrnList[i]->curfolder, foldername))
&& (BBoxNumButtons(scrnList[i]->folderbuttons))
&& (strcmp(foldername, app_resources.initial_folder_name)))
TocSetScrn(InitialFolder, scrnList[i]);
}
XtFree(foldername);
if (deleteData->original_toc != NULL)
TocSetScrn(deleteData->original_toc, scrn);
XtFree((char *) deleteData);
}
/* Delete the selected folder. Requires confirmation! */
/*ARGSUSED*/
void DoDeleteFolder(
Widget w,
XtPointer client_data,
XtPointer call_data)
{
Scrn scrn = (Scrn) client_data;
Toc toc = SelectedToc(scrn);
DeleteData deleteData;
if (! TocFolderExists(toc)) {
/* Too hard to clean up xmh when the folder doesn't exist anymore. */
PopupError(scrn->parent,
"Cannot open selected folder for confirmation to delete.");
return;
}
/* Prevent more than one confirmation popup on the same folder.
* TestAndSet returns true if there is a delete pending on this folder.
*/
if (TocTestAndSetDeletePending(toc)) {
PopupError(scrn->parent, "There is a delete pending on this folder.");
return;
}
deleteData = XtNew(DeleteDataRec);
deleteData->scrn = scrn;
deleteData->toc = toc;
deleteData->original_toc = CurrentToc(scrn);
if (deleteData->original_toc == toc)
deleteData->original_toc = (Toc) NULL;
CheckAndConfirmDeleteFolder(w, (XtPointer) deleteData, (XtPointer) NULL);
}
/*ARGSUSED*/
void XmhDeleteFolder(
Widget w,
XEvent *event, /* unused */
String *params, /* unused */
Cardinal *num_params) /* unused */
{
Scrn scrn = ScrnFromWidget(w);
DoDeleteFolder(w, (XtPointer) scrn, (XtPointer) NULL);
}
/*----- Notes on MenuButtons as folder buttons ---------------------------
*
* I assume that the name of the button is identical to the name of the folder.
* Only top-level folders have buttons.
* Only top-level folders may have subfolders.
* Top-level folders and their subfolders may have messages.
*
*/
static char filename[500]; /* for IsFolder() and for callback */
static int flen = 0; /* length of a substring of filename */
/* Function name: IsFolder
* Description: determines if a file is an mh subfolder.
*/
static int IsFolder(char *name)
{
register int i, len;
struct stat buf;
/* mh does not like subfolder names to be strings of digits */
if (isdigit(name[0]) || name[0] == '#') {
len = strlen(name);
for(i=1; i < len && isdigit(name[i]); i++)
;
if (i == len) return FALSE;
}
else if (name[0] == '.')
return FALSE;
(void) sprintf(filename + flen, "/%s", name);
if (stat(filename, &buf) /* failed */) return False;
#ifdef S_ISDIR
return S_ISDIR(buf.st_mode);
#else
return (buf.st_mode & S_IFMT) == S_IFDIR;
#endif
}
/* menu entry selection callback for folder menus. */
/*ARGSUSED*/
static void DoSelectFolder(
Widget w, /* the menu entry object */
XtPointer closure, /* foldername */
XtPointer data)
{
Scrn scrn = ScrnFromWidget(w);
SetCurrentFolderName(scrn, (char *) closure);
}
/*ARGSUSED*/
static void FreeMenuData(
Widget w,
XtPointer client_data,
XtPointer call_data)
{
XtFree((char*) client_data);
}
/* Function name: AddFolderMenuEntry
* Description:
* Add an entry to a menu. If the menu is not already created,
* create it, including the (already existing) new subfolder directory.
* If the menu is already created, add the new entry.
*/
static void AddFolderMenuEntry(
Button button, /* the corresponding menu button */
char *entryname) /* the new entry, relative to MailDir */
{
Arg args[4];
char * name;
char * c;
char tmpname[300];
char * label;
static XtCallbackRec callbacks[] = {
{ DoSelectFolder, (XtPointer) NULL },
{ (XtCallbackProc) NULL, (XtPointer) NULL}
};
static XtCallbackRec destroyCallbacks[] = {
{ FreeMenuData, (XtPointer) NULL },
{ (XtCallbackProc) NULL, (XtPointer) NULL}
};
/* The menu must be created before we can add an entry to it. */
if (button->menu == NULL || button->menu == NoMenuForButton) {
CreateFolderMenu(button);
return;
}
name = XtNewString(entryname);
callbacks[0].closure = (XtPointer) name;
destroyCallbacks[0].closure = (XtPointer) name;
XtSetArg(args[0], XtNcallback, callbacks); /* ONE */
XtSetArg(args[1], XtNdestroyCallback, destroyCallbacks); /* TWO */
/* When a subfolder and its parent folder have identical names,
* the widget name of the subfolder's menu entry must be unique.
*/
label = entryname;
c = strchr( strcpy(tmpname, entryname), '/');
if (c) {
*c = '\0';
label = ++c;
if (strcmp(tmpname, c) == 0) {
c--;
*c = '_';
}
name = c;
}
XtSetArg(args[2], XtNlabel, label); /* THREE */
XtCreateManagedWidget(name, smeBSBObjectClass, button->menu,
args, THREE);
}
/* Function name: CreateFolderMenu
* Description:
* Menus are created for folder buttons if the folder has at least one
* subfolder. For the directory given by the concatentation of
* app_resources.mail_path, '/', and the name of the button,
* CreateFolderMenu creates the menu whose entries are
* the subdirectories which do not begin with '.' and do not have
* names which are all digits, and do not have names which are a '#'
* followed by all digits. The first entry is always the name of the
* parent folder. Remaining entries are alphabetized.
*/
static void CreateFolderMenu(
Button button)
{
char **namelist;
register int i, n, length;
char directory[500];
n = strlen(app_resources.mail_path);
(void) strncpy(directory, app_resources.mail_path, n);
directory[n++] = '/';
(void) strcpy(directory + n, button->name);
flen = strlen(directory); /* for IsFolder */
(void) strcpy(filename, directory); /* for IsFolder */
n = ScanDir(directory, &namelist, IsFolder);
if (n <= 0) {
/* no subfolders, therefore no menu */
button->menu = NoMenuForButton;
return;
}
button->menu = XtCreatePopupShell("menu", simpleMenuWidgetClass,
button->widget, (ArgList) NULL, ZERO);
/* The first entry is always the parent folder */
AddFolderMenuEntry(button, button->name);
/* Build the menu by adding all the current entries to the new menu. */
length = strlen(button->name);
(void) strncpy(directory, button->name, length);
directory[length++] = '/';
for (i=0; i < n; i++) {
(void) strcpy(directory + length, namelist[i]);
free((char *) namelist[i]);
AddFolderMenuEntry(button, directory);
}
free((char *) namelist);
}
/* Function: DeleteFolderMenuEntry
* Description: Remove a subfolder from a menu.
*/
static void DeleteFolderMenuEntry(
Button button,
char *foldername)
{
char * c;
Arg args[2];
char * subfolder;
int n;
char tmpname[300];
Widget entry;
if (button == NULL || button->menu == NULL) return;
XtSetArg(args[0], XtNnumChildren, &n);
XtSetArg(args[1], XtNlabel, &c);
XtGetValues(button->menu, args, TWO);
if ((n <= 3 && c) || n <= 2) {
XtDestroyWidget(button->menu);
button->menu = NoMenuForButton;
return;
}
c = strchr( strcpy(tmpname, foldername), '/');
if (c) {
*c = '\0';
subfolder = ++c;
if (strcmp(button->name, subfolder) == 0) {
c--;
*c = '_';
subfolder = c;
}
if ((entry = XtNameToWidget(button->menu, subfolder)) != NULL)
XtDestroyWidget(entry);
}
}
/* Function Name: PopupFolderMenu
* Description: This action should alwas be taken when the user
* selects a folder button. A folder button represents a folder
* and zero or more subfolders. The menu of subfolders is built upon
* the first reference to it, by this routine. If there are no
* subfolders, this routine will mark the folder as having no
* subfolders, and no menu will be built. In that case, the menu
* button emulates a command button. When subfolders exist,
* the menu will popup, using the menu button action PopupMenu.
*/
/*ARGSUSED*/
void XmhPopupFolderMenu(
Widget w,
XEvent *event, /* unused */
String *vector, /* unused */
Cardinal *count) /* unused */
{
Button button;
Scrn scrn;
scrn = ScrnFromWidget(w);
if ((button = BBoxFindButton(scrn->folderbuttons, w)) == NULL)
return;
if (button->menu == NULL)
CreateFolderMenu(button);
if (button->menu == NoMenuForButton)
LastMenuButtonPressed = w;
else {
XtCallActionProc(button->widget, "PopupMenu", (XEvent *) NULL,
(String *) NULL, (Cardinal) 0);
XtCallActionProc(button->widget, "reset", (XEvent *) NULL,
(String *) NULL, (Cardinal) 0);
}
}
/* Function Name: XmhSetCurrentFolder
* Description: This action procedure allows menu buttons to
* emulate toggle widgets in their function of folder selection.
* Therefore, mh folders with no subfolders can be represented
* by a button instead of a menu with one entry. Sets the currently
* selected folder.
*/
/*ARGSUSED*/
void XmhSetCurrentFolder(
Widget w,
XEvent *event, /* unused */
String *vector, /* unused */
Cardinal *count) /* unused */
{
Button button;
Scrn scrn;
/* The MenuButton widget has a button grab currently active; the
* currently selected folder will be updated if the user has released
* the mouse button while the mouse pointer was on the same menu button
* widget that orginally activated the button grab. This mechanism is
* insured by the XmhPopupFolderMenu action setting LastMenuButtonPressed.
* The action XmhLeaveFolderButton, and it's translation in the application
* defaults file, bound to LeaveWindow events, insures that the menu
* button behaves properly when the user moves the pointer out of the
* menu button window.
*
* This action is for menu button widgets only.
*/
if (w != LastMenuButtonPressed)
return;
scrn = ScrnFromWidget(w);
if ((button = BBoxFindButton(scrn->folderbuttons, w)) == NULL)
return;
SetCurrentFolderName(scrn, button->name);
}
/*ARGSUSED*/
void XmhLeaveFolderButton(
Widget w,
XEvent *event,
String *vector,
Cardinal *count)
{
LastMenuButtonPressed = NULL;
}
void Push(
Stack *stack_ptr,
char *data)
{
Stack new = XtNew(StackRec);
new->data = data;
new->next = *stack_ptr;
*stack_ptr = new;
}
char * Pop(
Stack *stack_ptr)
{
Stack top;
char *data = NULL;
if ((top = *stack_ptr) != NULL) {
data = top->data;
*stack_ptr = top->next;
XtFree((char *) top);
}
return data;
}
/* Parameters are taken as names of folders to be pushed on the stack.
* With no parameters, the currently selected folder is pushed.
*/
/*ARGSUSED*/
void XmhPushFolder(
Widget w,
XEvent *event,
String *params,
Cardinal *count)
{
Scrn scrn = ScrnFromWidget(w);
Cardinal i;
for (i=0; i < *count; i++)
Push(&scrn->folder_stack, params[i]);
if (*count == 0 && scrn->curfolder)
Push(&scrn->folder_stack, scrn->curfolder);
}
/* Pop the stack & take that folder to be the currently selected folder. */
/*ARGSUSED*/
void XmhPopFolder(
Widget w,
XEvent *event,
String *params,
Cardinal *count)
{
Scrn scrn = ScrnFromWidget(w);
char *folder;
if ((folder = Pop(&scrn->folder_stack)) != NULL)
SetCurrentFolderName(scrn, folder);
}
static Boolean InParams(
String str,
String *p,
Cardinal n)
{
Cardinal i;
for (i=0; i < n; p++, i++)
if (! XmuCompareISOLatin1(*p, str)) return True;
return False;
}
/* generalized routine for xmh participation in WM protocols */
/*ARGSUSED*/
void XmhWMProtocols(
Widget w, /* NULL if from checkpoint timer */
XEvent * event, /* NULL if from checkpoint timer */
String * params,
Cardinal * num_params)
{
Boolean dw = False; /* will we do delete window? */
Boolean sy = False; /* will we do save yourself? */
static char*WM_DELETE_WINDOW = "WM_DELETE_WINDOW";
static char*WM_SAVE_YOURSELF = "WM_SAVE_YOURSELF";
#define DO_DELETE_WINDOW InParams(WM_DELETE_WINDOW, params, *num_params)
#define DO_SAVE_YOURSELF InParams(WM_SAVE_YOURSELF, params, *num_params)
/* Respond to a recognized WM protocol request iff
* event type is ClientMessage and no parameters are passed, or
* event type is ClientMessage and event data is matched to parameters, or
* event type isn't ClientMessage and parameters make a request.
*/
if (event && event->type == ClientMessage) {
if (event->xclient.message_type == wm_protocols) {
if (event->xclient.data.l[0] == wm_delete_window &&
(*num_params == 0 || DO_DELETE_WINDOW))
dw = True;
else if (event->xclient.data.l[0] == wm_save_yourself &&
(*num_params == 0 || DO_SAVE_YOURSELF))
sy = True;
}
} else {
if (DO_DELETE_WINDOW)
dw = True;
if (DO_SAVE_YOURSELF)
sy = True;
}
#undef DO_DELETE_WINDOW
#undef DO_SAVE_YOURSELF
if (sy) {
register int i;
for (i=0; i<numScrns; i++)
if (scrnList[i]->msg)
MsgCheckPoint(scrnList[i]->msg);
if (w) /* don't generate a property notify via the checkpoint timer */
XChangeProperty(XtDisplay(toplevel), XtWindow(toplevel),
XA_WM_COMMAND, XA_STRING, 8, PropModeAppend,
(unsigned char *)"", 0);
}
if (dw && w) {
Scrn scrn;
while (w && !XtIsShell(w))
w = XtParent(w);
if (XtIsTransientShell(w)) {
WMDeletePopup(w, event);
return;
}
scrn = ScrnFromWidget(w);
switch (scrn->kind) {
case STtocAndView:
DoClose(w, (XtPointer)scrn, (XtPointer)NULL);
break;
case STview:
case STcomp:
DoCloseView(w, (XtPointer)scrn, (XtPointer)NULL);
break;
case STpick:
DestroyScrn(scrn);
break;
}
}
}
typedef struct _InteractMsgTokenRec {
Scrn scrn;
XtCheckpointToken cp_token;
} InteractMsgTokenRec, *InteractMsgToken;
static void CommitMsgChanges(
Widget w, /* unused */
XtPointer client_data, /* InteractMsgToken */
XtPointer call_data)
{
Cardinal zero = 0;
InteractMsgToken iToken = (InteractMsgToken) client_data;
XmhSave(iToken->scrn->parent, (XEvent*)NULL, (String*)NULL, &zero);
if (MsgChanged(iToken->scrn->msg))
iToken->cp_token->save_success = False;
XtSessionReturnToken(iToken->cp_token);
XtFree((XtPointer)iToken);
}
static void CancelMsgChanges(
Widget w, /* unused */
XtPointer client_data, /* InteractMsgToken */
XtPointer call_data)
{
InteractMsgToken iToken = (InteractMsgToken) client_data;
/* don't change any msg state now; this is only a checkpoint
* and the session might be continuing. */
MsgCheckPoint(iToken->scrn->msg);
XtSessionReturnToken(iToken->cp_token);
XtFree((XtPointer)iToken);
}
static void CommitMsgInteract(
Widget w, /* unused */
XtPointer client_data, /* Scrn */
XtPointer call_data) /* XtCheckpointToken */
{
Scrn scrn = (Scrn) client_data;
XtCheckpointToken cpToken = (XtCheckpointToken) call_data;
char str[300];
InteractMsgToken iToken;
static XtCallbackRec yes_callbacks[] = {
{CommitMsgChanges, (XtPointer) NULL},
{(XtCallbackProc) NULL, (XtPointer) NULL}
};
static XtCallbackRec no_callbacks[] = {
{CancelMsgChanges, (XtPointer) NULL},
{(XtCallbackProc) NULL, (XtPointer) NULL}
};
if (cpToken->interact_style != SmInteractStyleAny
|| cpToken->cancel_shutdown) {
XtSessionReturnToken(cpToken);
return;
}
iToken = XtNew(InteractMsgTokenRec);
iToken->scrn = scrn;
iToken->cp_token = cpToken;
yes_callbacks[0].closure = no_callbacks[0].closure = (XtPointer) iToken;
(void)sprintf(str,"Save changes to message %s?", MsgName(scrn->msg));
/* %%% should add cancel button */
PopupConfirm(scrn->parent, str, yes_callbacks, no_callbacks);
}
typedef struct _InteractTocTokenRec {
Toc toc;
XtCheckpointToken cp_token;
} InteractTocTokenRec, *InteractTocToken;
static void CommitTocChanges(
Widget w, /* unused */
XtPointer client_data, /* InteractTocToken */
XtPointer call_data)
{
InteractTocToken iToken = (InteractTocToken) client_data;
TocCommitChanges(w, (XtPointer) iToken->toc, (XtPointer) NULL);
XtSessionReturnToken(iToken->cp_token);
XtFree((XtPointer)iToken);
}
static void CancelTocChanges(
Widget w, /* unused */
XtPointer client_data, /* InteractTocToken */
XtPointer call_data)
{
InteractTocToken iToken = (InteractTocToken) client_data;
/* don't change any folder or msg state now; this is only
* a checkpoint and the session might be continuing. */
XtSessionReturnToken(iToken->cp_token);
XtFree((XtPointer)iToken);
}
static void CommitTocInteract(
Widget w, /* unused */
XtPointer client_data, /* Toc */
XtPointer call_data) /* XtCheckpointToken */
{
Toc toc = (Toc) client_data;
XtCheckpointToken cpToken = (XtCheckpointToken) call_data;
char str[300];
Widget tocwidget;
Cardinal i;
InteractTocToken iToken;
static XtCallbackRec yes_callbacks[] = {
{CommitTocChanges, (XtPointer) NULL},
{(XtCallbackProc) NULL, (XtPointer) NULL}
};
static XtCallbackRec no_callbacks[] = {
{CancelTocChanges, (XtPointer) NULL},
{(XtCallbackProc) NULL, (XtPointer) NULL}
};
if (cpToken->interact_style != SmInteractStyleAny
|| cpToken->cancel_shutdown) {
XtSessionReturnToken(cpToken);
return;
}
iToken = XtNew(InteractTocTokenRec);
iToken->toc = toc;
iToken->cp_token = cpToken;
yes_callbacks[0].closure = no_callbacks[0].closure = (XtPointer) iToken;
(void)sprintf(str,"Commit all changes to %s folder?", toc->foldername);
tocwidget = NULL;
for (i=0; i < toc->num_scrns; i++)
if (toc->scrn[i]->mapped) {
tocwidget = toc->scrn[i]->tocwidget;
break;
}
/* %%% should add cancel button */
PopupConfirm(tocwidget, str, yes_callbacks, no_callbacks);
}
/* Callback for Session Manager SaveYourself */
/*ARGSUSED*/
void DoSaveYourself(
Widget w, /* unused; s/b toplevel */
XtPointer client_data, /* unused */
XtPointer call_data) /* XtCheckpointToken */
{
XtCheckpointToken cpToken = (XtCheckpointToken)call_data;
{ /* confirm any uncommitted msg changes */
int i;
for (i=0 ; i<numScrns ; i++) {
if (MsgChanged(scrnList[i]->msg)) {
if (cpToken->interact_style == SmInteractStyleAny)
XtAddCallback(toplevel, XtNinteractCallback,
CommitMsgInteract, (XtPointer)scrnList[i]);
else {
Cardinal zero = 0;
XmhSave(scrnList[i]->parent, (XEvent*)NULL,
(String*)NULL, &zero);
if (MsgChanged(scrnList[i]->msg)) {
MsgCheckPoint(scrnList[i]->msg);
cpToken->save_success = False;
}
}
}
}
}
{ /* confirm any uncommitted folder changes */
int i;
for (i = 0; i < numFolders; i++) {
if (TocHasChanges(folderList[i])) {
if (cpToken->interact_style == SmInteractStyleAny)
XtAddCallback(toplevel, XtNinteractCallback,
CommitTocInteract, (XtPointer)folderList[i]);
else
TocCommitChanges(w, (XtPointer)folderList[i],
(XtPointer) NULL);
}
}
}
}