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