summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaleb Keithley <kaleb@freedesktop.org>2003-11-14 16:49:23 +0000
committerKaleb Keithley <kaleb@freedesktop.org>2003-11-14 16:49:23 +0000
commitcf123a1c2e624e7b4908a03870ca0a48420a3c43 (patch)
tree3f41d5e5a19a9de4f3ff8cf5ba363f8bc7b0f2fa
Initial revisionXORG-STABLE
-rw-r--r--Xmh.ad173
-rw-r--r--Xmh.sample108
-rw-r--r--actions.h102
-rw-r--r--bbox.c373
-rw-r--r--bbox.h63
-rw-r--r--bboxint.h46
-rw-r--r--black64
-rw-r--r--box64
-rw-r--r--command.c478
-rw-r--r--compfuncs.c129
-rw-r--r--externs.h186
-rw-r--r--folder.c1188
-rw-r--r--globals.h166
-rw-r--r--init.c517
-rw-r--r--main.c142
-rw-r--r--menu.c84
-rw-r--r--miscfuncs.c169
-rw-r--r--mlist.c127
-rw-r--r--mlist.h38
-rw-r--r--msg.c947
-rw-r--r--msg.h59
-rw-r--r--pick.c753
-rw-r--r--popup.c513
-rw-r--r--screen.c512
-rw-r--r--toc.c1220
-rw-r--r--toc.h76
-rw-r--r--tocfuncs.c1078
-rw-r--r--tocintrnl.h97
-rw-r--r--tocutil.c645
-rw-r--r--tocutil.h47
-rw-r--r--tsource.c343
-rw-r--r--tsource.h77
-rw-r--r--tsourceP.h110
-rw-r--r--util.c485
-rw-r--r--version.h32
-rw-r--r--viewfuncs.c286
-rw-r--r--xmh.h152
-rw-r--r--xmh.man1539
38 files changed, 13068 insertions, 0 deletions
diff --git a/Xmh.ad b/Xmh.ad
new file mode 100644
index 0000000..fd71a5c
--- /dev/null
+++ b/Xmh.ad
@@ -0,0 +1,173 @@
+! $XConsortium: Xmh.ad,v 1.32 94/02/09 19:44:06 gildea Exp $
+! AppDefaultsVersion should only be defined in the site-wide file
+Xmh.AppDefaultsVersion: 1
+
+Xmh.Geometry: 508x750
+Xmh.ReplyInsertFilter: cat
+Xmh.SendBreakWidth: 2000
+*ShapeStyle: Oval
+*Command.BorderWidth: 1
+*MenuButton.BorderWidth: 1
+*toc*cursor: left_ptr
+
+*menuBox.folderButton.Label: Folder
+*menuBox.tocButton.Label: Table of Contents
+*menuBox.messageButton.Label: Message
+*menuBox.sequenceButton.Label: Sequence
+*menuBox.viewButton.Label: View
+*menuBox.optionButton.Label: Options
+*SimpleMenu*SmeLine.Height: 20
+
+*close.Label: Close Window
+*compose.Label: Compose Message
+*open.Label: Open Folder
+*openInNew.Label: Open Folder in New Window
+*create.Label: Create Folder
+*folderMenu*delete.Label: Delete Folder
+*inc.Label: Incorporate New Mail
+*next.Label: View Next Message
+*prev.Label: View Previous
+*delete.Label: Delete
+*move.Label: Move
+*copy.Label: Copy as Link
+*unmark.Label: Unmark
+*viewNew.Label: View In New
+*reply.Label: Reply
+*forward.Label: Forward
+*useAsComp.Label: Use as Composition
+*commit.Label: Commit Changes
+*print.Label: Print
+*pack.Label: Pack Folder
+*sort.Label: Sort Folder
+*rescan.Label: Rescan Folder
+*pick.Label: Pick
+*openSeq.Label: Open Sequence
+*addToSeq.Label: Add to Sequence
+*removeFromSeq.Label: Remove from Sequence
+*deleteSeq.Label: Delete Sequence
+*edit.Label: Edit Message
+*save.Label: Save Message
+*send.Label: Send
+*reset.Label: New Headers
+*insert.Label: Insert
+*optionMenu*reverse.Label: Read in Reverse
+
+Xmh.notice.Dialog.Text.BorderWidth: 0
+Xmh.notice.Dialog.Text.TextSink.Font: -*-courier-bold-r-*--*-120-*-*-*-*-iso8859-1
+Xmh.notice.Dialog.confirm.Label:Acknowledged
+Xmh.notice.BorderWidth: 2
+Xmh.confirm.Dialog.yes.Label: Yes
+Xmh.confirm.Dialog.no.Label: No
+Xmh.prompt.Dialog.okay.Label: Okay
+Xmh.prompt.Dialog.cancel.Label: Cancel
+Xmh.error.Dialog.OK.Label: Acknowledged
+
+*toc.rightMargin: 0
+*toc.scrollVertical: WhenNeeded
+*view.scrollVertical: Always
+*view.scrollHorizontal: WhenNeeded
+*view.autoFill: True
+*comp.scrollVertical: Always
+*comp.scrollHorizontal: WhenNeeded
+*comp.autoFill: True
+
+*sequenceMenu.Translations: #override\n\
+<Btn2Up>:XtMenuPopdown()notify()XmhOpenSequence()unhighlight()\n\
+<Btn3Up>:XtMenuPopdown()XmhPushSequence()notify()XmhAddToSequence()XmhPopSequence()unhighlight()\n\
+<BtnUp>:XtMenuPopdown()notify()unhighlight()\n
+
+*sequenceMenu.baseTranslations: #override\n\
+<Btn2Up>:XtMenuPopdown()notify()XmhOpenSequence()unhighlight()\n\
+<Btn3Up>:XtMenuPopdown()XmhPushSequence()notify()XmhAddToSequence()XmhPopSequence()unhighlight()\n\
+<BtnUp>:XtMenuPopdown()notify()unhighlight()\n
+
+*folders*MenuButton.Translations:#override\n\
+<BtnDown>:set()XmhPopupFolderMenu()\n\
+<Btn2Up>:XmhSetCurrentFolder()XmhOpenFolder()reset()\n\
+<Btn3Up>:XmhPushFolder()XmhSetCurrentFolder()XmhMarkMove()XmhPopFolder()reset()\n\
+<BtnUp>:XmhSetCurrentFolder()reset()\n\
+<LeaveWindow>:reset()XmhLeaveFolderButton()\n
+
+*folders*MenuButton.baseTranslations:#override\n\
+<BtnDown>:set()XmhPopupFolderMenu()\n\
+<Btn2Up>:XmhSetCurrentFolder()XmhOpenFolder()reset()\n\
+<Btn3Up>:XmhPushFolder()XmhSetCurrentFolder()XmhMarkMove()XmhPopFolder()reset()\n\
+<BtnUp>:XmhSetCurrentFolder()reset()\n\
+<LeaveWindow>:reset()XmhLeaveFolderButton()\n
+
+*folders*SimpleMenu.Translations:#override\n\
+<Btn2Up>:XtMenuPopdown()notify()XmhOpenFolder()unhighlight()\n\
+<Btn3Up>:XtMenuPopdown()XmhPushFolder()notify()XmhMarkMove()XmhPopFolder()unhighlight()\n\
+<BtnUp>:XtMenuPopdown()notify()unhighlight()\n
+
+*folders*SimpleMenu.baseTranslations:#override\n\
+<Btn2Up>:XtMenuPopdown()notify()XmhOpenFolder()unhighlight()\n\
+<Btn3Up>:XtMenuPopdown()XmhPushFolder()notify()XmhMarkMove()XmhPopFolder()unhighlight()\n\
+<BtnUp>:XtMenuPopdown()notify()unhighlight()\n
+
+*toc.Translations: #override\n\
+ <Btn2Down>: select-start()\n\
+ <Btn2Up>:select-end(PRIMARY)XmhViewNextMessage()\n\
+ Ctrl<Key>R: no-op(RingBell)\n\
+ Ctrl<Key>S: no-op(RingBell)\n
+
+*toc.baseTranslations: #override\n\
+ <Btn2Down>: select-start()\n\
+ <Btn2Up>:select-end(PRIMARY)XmhViewNextMessage()\n\
+ Ctrl<Key>R: no-op(RingBell)\n\
+ Ctrl<Key>S: no-op(RingBell)\n
+
+*toc.Accelerators: #override\n\
+ :Ctrl<Key>V: next-page()\n\
+ :Meta<Key>V: previous-page()\n
+*view.Accelerators: #override\n\
+ :Ctrl<Key>v: next-page()\n\
+ :Meta<Key>v: previous-page()\n
+
+*tocMenu.Accelerators: #override\n\
+ :Meta<Key>I: XmhIncorporateNewMail()\n\
+ :Meta<Key>C: XmhCommitChanges()\n\
+ :Meta<Key>R: XmhForceRescan()\n\
+ :Meta<Key>P: XmhPackFolder()\n\
+ :Meta<Key>S: XmhSortFolder()\n
+
+*messageMenu.Accelerators: #override\n\
+ Meta<Key>space: XmhViewNextMessage()\n\
+ :Meta<Key>c: XmhMarkCopy()\n\
+ :Meta<Key>d: XmhMarkDelete()\n\
+ :Meta<Key>f: XmhForward()\n\
+ :Meta<Key>m: XmhMarkMove()\n\
+ :Meta<Key>n: XmhViewNextMessage()\n\
+ :Meta<Key>p: XmhViewPreviousMessage()\n\
+ :Meta<Key>r: XmhReply()\n\
+ :Meta<Key>u: XmhUnmark()\n
+
+*viewButtons.close.Translations:#override\n\
+ <Btn1Down>,<Btn1Up>: XmhCloseView()unset()\n
+*viewButtons.reply.Translations:#override\n\
+ <Btn1Down>,<Btn1Up>: XmhViewReply()unset()\n
+*viewButtons.forward.Translations:#override\n\
+ <Btn1Down>,<Btn1Up>: XmhViewForward()unset()\n
+*viewButtons.useAsComp.Translations:#override\n\
+ <Btn1Down>,<Btn1Up>: XmhViewUseAsComposition()unset()\n
+*viewButtons.edit.Translations:#override\n\
+ <Btn1Down>,<Btn1Up>: XmhEditView()unset()\n
+*viewButtons.save.Translations:#override\n\
+ <Btn1Down>,<Btn1Up>: XmhSaveView()unset()\n
+*viewButtons.print.Translations:#override\n\
+ <Btn1Down>,<Btn1Up>: XmhPrintView()unset()\n
+*viewButtons.delete.Translations:#override\n\
+ <Btn1Down>,<Btn1Up>: XmhViewMarkDelete()unset()\n
+
+*compButtons.close.Translations:#override\n\
+ <Btn1Down>,<Btn1Up>: XmhCloseView()unset()\n
+*compButtons.send.Translations:#override\n\
+ <Btn1Down>,<Btn1Up>: XmhSend()unset()\n
+*compButtons.reset.Translations:#override\n\
+ <Btn1Down>,<Btn1Up>: XmhResetCompose()unset()\n
+*compButtons.compose.Translations:#override\n\
+ <Btn1Down>,<Btn1Up>: XmhComposeMessage()unset()\n
+*compButtons.save.Translations:#override\n\
+ <Btn1Down>,<Btn1Up>: XmhSave()unset()\n
+*compButtons.insert.Translations:#override\n\
+ <Btn1Down>,<Btn1Up>: XmhInsert()unset()\n
diff --git a/Xmh.sample b/Xmh.sample
new file mode 100644
index 0000000..e69ca82
--- /dev/null
+++ b/Xmh.sample
@@ -0,0 +1,108 @@
+
+! Examples of customizing xmh with resource specifications.
+! These can be copied to your private X resource file or to
+! a private Xmh application defaults file.
+
+
+
+! To create command buttons in the middle of the main window:
+
+Xmh*CommandButtonCount: 8
+
+Xmh*commandBox.button1.label: inc
+Xmh*commandBox.button1.translations: #override\
+ <Btn1Down>,<Btn1Up>: XmhIncorporateNewMail() unset()
+
+Xmh*commandBox.button2.label: compose
+Xmh*commandBox.button2.translations: #override\
+ <Btn1Down>,<Btn1Up>: XmhComposeMessage() unset()
+
+Xmh*commandBox.button3.label: next
+Xmh*commandBox.button3.translations: #override\
+ <Btn1Down>,<Btn1Up>: XmhViewNextMessage() unset()
+
+Xmh*commandBox.button4.label: prev
+Xmh*commandBox.button4.translations: #override\
+ <Btn1Down>,<Btn1Up>: XmhViewPreviousMessage() unset()
+
+Xmh*commandBox.button5.label: commit
+Xmh*commandBox.button5.translations: #override\
+ <Btn1Down>,<Btn1Up>: XmhCommitChanges() unset()
+
+Xmh*commandBox.button6.label: delete
+Xmh*commandBox.button6.translations: #override\
+ <Btn1Down>,<Btn1Up>: XmhMarkDelete() unset()
+
+Xmh*commandBox.button7.label: move
+Xmh*commandBox.button7.translations: #override\
+ <Btn1Down>,<Btn1Up>: XmhMarkMove() unset()
+
+Xmh*commandBox.button8.label: reply to viewed msg
+Xmh*commandBox.button8.translations: #override\
+ <Btn1Down>,<Btn1Up>: XmhViewReply() unset()
+
+
+! To use popup menus on the title bars of the main window,
+! have them popup with the pointer over the previously selected item,
+! and not be clipped by the screen boundary:
+
+Xmh*stickyMenu: True
+Xmh*messageMenu.MenuOnScreen: True
+
+Xmh*folderTitlebar.translations: #override\n\
+<BtnDown>: XawPositionSimpleMenu(folderMenu)MenuPopup(folderMenu)\n
+
+Xmh*tocTitlebar.translations: #override\n\
+<Btn2Down>: XawPositionSimpleMenu(messageMenu)MenuPopup(messageMenu)\n\
+<BtnDown>: XawPositionSimpleMenu(tocMenu)MenuPopup(tocMenu)\n
+
+Xmh.Paned.viewTitlebar.translations: #override\n\
+<Btn2Down>: XawPositionSimpleMenu(sequenceMenu)MenuPopup(sequenceMenu)\n\
+<BtnDown>: XawPositionSimpleMenu(viewMenu)MenuPopup(viewMenu)\n
+
+
+! To redefine the accelerator bindings to exclude modifier keys,
+! and add a translation for Compose Message:
+
+Xmh*tocMenu.accelerators: #override\n\
+ !:<Key>I: XmhIncorporateNewMail()\n\
+ !:<Key>C: XmhCommitChanges()\n\
+ !:<Key>R: XmhForceRescan()\n\
+ !:<Key>P: XmhPackFolder()\n\
+ !:<Key>S: XmhSortFolder()\n
+Xmh*messageMenu.accelerators: #override\n\
+ !:<Key>M: XmhComposeMessage()\n\
+ !<Key>space: XmhViewNextMessage()\n\
+ !:<Key>c: XmhMarkCopy()\n\
+ !:<Key>d: XmhMarkDelete()\n\
+ !:<Key>f: XmhForward()\n\
+ !:<Key>m: XmhMarkMove()\n\
+ !:<Key>n: XmhViewNextMessage()\n\
+ !:<Key>p: XmhViewPreviousMessage()\n\
+ !:<Key>r: XmhReply()\n\
+ !:<Key>u: XmhUnmark()\n
+
+
+! Here is an example of some miscellaneous accelerators:
+! ("clients/xmh" is a subfolder; it must be existing.)
+
+Xmh*toc.accelerators: #override\n\
+ <Key>F1: XmhOpenFolder(inbox)XmhOpenSequence(all)\n\
+ <Key>F2: XmhOpenFolder(drafts)\n\
+ <Key>F3: XmhOpenFolder(clients/xmh)\n\
+ <Key>F4: XmhViewInNewWindow()\n\
+ <Key>F5: XmhPickMessages()\n
+
+
+! Define Meta-S in the Compose window to do a send and close.
+
+Xmh*comp.translations: #override\n\
+ !:Meta<Key>S: XmhSend()XmhCloseView()
+
+
+
+
+
+
+
+
diff --git a/actions.h b/actions.h
new file mode 100644
index 0000000..9694489
--- /dev/null
+++ b/actions.h
@@ -0,0 +1,102 @@
+/*
+ * $XConsortium: actions.h,v 1.10 94/04/17 20:24:00 converse Exp $
+ *
+Copyright (c) 1989 X Consortium
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the X Consortium shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from the X Consortium.
+ *
+ */
+/* $XFree86: xc/programs/xmh/actions.h,v 1.3 2002/07/01 02:26:05 tsi Exp $ */
+
+#define XMH_ACTION_ARGS Widget, XEvent *, String *, Cardinal *
+
+ /* from compfuncs.c */
+
+extern void XmhResetCompose (XMH_ACTION_ARGS);
+extern void XmhSend (XMH_ACTION_ARGS);
+extern void XmhSave (XMH_ACTION_ARGS);
+
+ /* from folder.c */
+
+extern void XmhClose (XMH_ACTION_ARGS);
+extern void XmhComposeMessage (XMH_ACTION_ARGS);
+extern void XmhOpenFolder (XMH_ACTION_ARGS);
+extern void XmhOpenFolderInNewWindow (XMH_ACTION_ARGS);
+extern void XmhCreateFolder (XMH_ACTION_ARGS);
+extern void XmhDeleteFolder (XMH_ACTION_ARGS);
+extern void XmhPopupFolderMenu (XMH_ACTION_ARGS);
+extern void XmhSetCurrentFolder (XMH_ACTION_ARGS);
+extern void XmhLeaveFolderButton (XMH_ACTION_ARGS);
+extern void XmhPushFolder (XMH_ACTION_ARGS);
+extern void XmhPopFolder (XMH_ACTION_ARGS);
+extern void XmhWMProtocols (XMH_ACTION_ARGS);
+
+ /* from msg.c */
+
+extern void XmhInsert (XMH_ACTION_ARGS);
+
+ /* from popup.c */
+
+extern void XmhPromptOkayAction (XMH_ACTION_ARGS);
+
+ /* from toc.c */
+
+extern void XmhPushSequence (XMH_ACTION_ARGS);
+extern void XmhPopSequence (XMH_ACTION_ARGS);
+extern void XmhReloadSeqLists (XMH_ACTION_ARGS);
+
+ /* from tocfuncs.c */
+
+extern void XmhCheckForNewMail (XMH_ACTION_ARGS);
+extern void XmhIncorporateNewMail (XMH_ACTION_ARGS);
+extern void XmhCommitChanges (XMH_ACTION_ARGS);
+extern void XmhPackFolder (XMH_ACTION_ARGS);
+extern void XmhSortFolder (XMH_ACTION_ARGS);
+extern void XmhForceRescan (XMH_ACTION_ARGS);
+extern void XmhViewNextMessage (XMH_ACTION_ARGS);
+extern void XmhViewPreviousMessage (XMH_ACTION_ARGS);
+extern void XmhMarkDelete (XMH_ACTION_ARGS);
+extern void XmhMarkMove (XMH_ACTION_ARGS);
+extern void XmhMarkCopy (XMH_ACTION_ARGS);
+extern void XmhUnmark (XMH_ACTION_ARGS);
+extern void XmhViewInNewWindow (XMH_ACTION_ARGS);
+extern void XmhReply (XMH_ACTION_ARGS);
+extern void XmhForward (XMH_ACTION_ARGS);
+extern void XmhUseAsComposition (XMH_ACTION_ARGS);
+extern void XmhPrint (XMH_ACTION_ARGS);
+extern void XmhShellCommand (XMH_ACTION_ARGS);
+extern void XmhPickMessages (XMH_ACTION_ARGS);
+extern void XmhOpenSequence (XMH_ACTION_ARGS);
+extern void XmhAddToSequence (XMH_ACTION_ARGS);
+extern void XmhRemoveFromSequence (XMH_ACTION_ARGS);
+extern void XmhDeleteSequence (XMH_ACTION_ARGS);
+
+ /* from viewfuncs.c */
+
+extern void XmhCloseView (XMH_ACTION_ARGS);
+extern void XmhViewReply (XMH_ACTION_ARGS);
+extern void XmhViewForward (XMH_ACTION_ARGS);
+extern void XmhViewUseAsComposition (XMH_ACTION_ARGS);
+extern void XmhEditView (XMH_ACTION_ARGS);
+extern void XmhSaveView (XMH_ACTION_ARGS);
+extern void XmhPrintView (XMH_ACTION_ARGS);
+extern void XmhViewMarkDelete (XMH_ACTION_ARGS);
diff --git a/bbox.c b/bbox.c
new file mode 100644
index 0000000..2295eaf
--- /dev/null
+++ b/bbox.c
@@ -0,0 +1,373 @@
+/*
+ * $XConsortium: bbox.c,v 2.35 91/07/10 19:34:59 converse 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/bbox.c,v 1.3 2002/04/05 21:06:28 dickey Exp $ */
+
+/* bbox.c -- management of buttons and buttonboxes.
+ *
+ * This module implements a simple interface to buttonboxes, allowing a client
+ * to create new buttonboxes and manage their contents.
+ */
+
+#include "xmh.h"
+#include "bboxint.h"
+
+static XtTranslations RadioButtonTranslations = NULL;
+
+
+void BBoxInit(void)
+{
+ RadioButtonTranslations =
+ XtParseTranslationTable("<Btn1Down>,<Btn1Up>:set()\n");
+}
+
+
+/*
+ * Create a new button box. The widget for it will be a child of the given
+ * scrn's widget, and it will be added to the scrn's pane.
+ */
+
+ButtonBox BBoxCreate(
+ Scrn scrn,
+ char *name) /* name of the buttonbox widgets */
+{
+ Cardinal n;
+ ButtonBox buttonbox = XtNew(ButtonBoxRec);
+ Arg args[5];
+
+ n = 0;
+ XtSetArg(args[n], XtNallowVert, True); n++;
+ XtSetArg(args[n], XtNskipAdjust, True); n++;
+
+ buttonbox->outer =
+ XtCreateManagedWidget(name, viewportWidgetClass, scrn->widget,
+ args, n);
+ buttonbox->inner =
+ XtCreateManagedWidget(name, boxWidgetClass, buttonbox->outer,
+ args, (Cardinal) 0);
+ buttonbox->numbuttons = 0;
+ buttonbox->button = (Button *) NULL;
+ buttonbox->scrn = scrn;
+ return buttonbox;
+}
+
+
+ButtonBox RadioBBoxCreate(
+ Scrn scrn,
+ char *name) /* name of the buttonbox widgets */
+{
+ return BBoxCreate(scrn, name);
+}
+
+
+/* Create a new button, and add it to a buttonbox. */
+
+static void bboxAddButton(
+ ButtonBox buttonbox,
+ char *name,
+ WidgetClass kind,
+ Boolean enabled,
+ Boolean radio)
+{
+ Button button;
+ Cardinal i;
+ Widget radio_group;
+ Arg args[5];
+
+ buttonbox->numbuttons++;
+ buttonbox->button = (Button *)
+ XtRealloc((char *) buttonbox->button,
+ (unsigned) buttonbox->numbuttons * sizeof(Button));
+ button = buttonbox->button[buttonbox->numbuttons - 1] = XtNew(ButtonRec);
+ button->buttonbox = buttonbox;
+ button->name = XtNewString(name);
+ button->menu = (Widget) NULL;
+
+ i = 0;
+ if (!enabled) {
+ XtSetArg(args[i], XtNsensitive, False); i++;
+ }
+
+ if (radio && kind == toggleWidgetClass) {
+ if (buttonbox->numbuttons > 1)
+ radio_group = (button == buttonbox->button[0])
+ ? (buttonbox->button[1]->widget)
+ : (buttonbox->button[0]->widget);
+ else radio_group = NULL;
+ XtSetArg(args[i], XtNradioGroup, radio_group); i++;
+ XtSetArg(args[i], XtNradioData, button->name); i++;
+ }
+
+ /* Prevent the folder buttons from picking up labels from resources */
+
+ if (buttonbox == buttonbox->scrn->folderbuttons) {
+ XtSetArg(args[i], XtNlabel, button->name); i++;
+ }
+
+ button->widget =
+ XtCreateManagedWidget(name, kind, buttonbox->inner, args, i);
+
+ if (radio)
+ XtOverrideTranslations(button->widget, RadioButtonTranslations);
+}
+
+
+void BBoxAddButton(
+ ButtonBox buttonbox,
+ char *name,
+ WidgetClass kind,
+ Boolean enabled)
+{
+ bboxAddButton(buttonbox, name, kind, enabled, False);
+}
+
+
+void RadioBBoxAddButton(
+ ButtonBox buttonbox,
+ char *name,
+ Boolean enabled)
+{
+ bboxAddButton(buttonbox, name, toggleWidgetClass, enabled, True);
+}
+
+
+/* Set the current button in a radio buttonbox. */
+
+void RadioBBoxSet(
+ Button button)
+{
+ XawToggleSetCurrent(button->widget, button->name);
+}
+
+
+/* Get the name of the current button in a radio buttonbox. */
+
+char *RadioBBoxGetCurrent(
+ ButtonBox buttonbox)
+{
+ return ((char *) XawToggleGetCurrent(buttonbox->button[0]->widget));
+}
+
+
+/* Remove the given button from its buttonbox, and free all resources
+ * used in association with the button. If the button was the current
+ * button in a radio buttonbox, the current button becomes the first
+ * button in the box.
+ */
+
+void BBoxDeleteButton(
+ Button button)
+{
+ ButtonBox buttonbox;
+ int i, found;
+
+ if (button == NULL) return;
+ buttonbox = button->buttonbox;
+ found = False;
+
+ for (i=0 ; i<buttonbox->numbuttons; i++) {
+ if (found)
+ buttonbox->button[i-1] = buttonbox->button[i];
+ else if (buttonbox->button[i] == button) {
+ found = True;
+
+ /* Free the resources used by the given button. */
+
+ if (button->menu != NULL && button->menu != NoMenuForButton)
+ XtDestroyWidget(button->menu);
+ XtDestroyWidget(button->widget);
+ XtFree(button->name);
+ XtFree((char *) button);
+ }
+ }
+ if (found)
+ buttonbox->numbuttons--;
+}
+
+
+void RadioBBoxDeleteButton(
+ Button button)
+{
+ ButtonBox buttonbox;
+ Boolean reradio = False;
+ char * current;
+
+ if (button == NULL) return;
+ buttonbox = button->buttonbox;
+ current = RadioBBoxGetCurrent(buttonbox);
+ if (current) reradio = ! strcmp(current, button->name);
+ BBoxDeleteButton(button);
+
+ if (reradio && BBoxNumButtons(buttonbox))
+ RadioBBoxSet(buttonbox->button[0]);
+}
+
+
+/* Enable or disable the given button widget. */
+
+static void SendEnableMsg(
+ Widget widget,
+ int value) /* TRUE for enable, FALSE for disable. */
+{
+ static Arg arglist[] = {{XtNsensitive, (XtArgVal)False}};
+ arglist[0].value = (XtArgVal) value;
+ XtSetValues(widget, arglist, XtNumber(arglist));
+}
+
+
+/* Enable the given button (if it's not already). */
+
+void BBoxEnable(
+ Button button)
+{
+ SendEnableMsg(button->widget, True);
+}
+
+
+/* Disable the given button (if it's not already). */
+
+void BBoxDisable(
+ Button button)
+{
+ SendEnableMsg(button->widget, False);
+}
+
+
+/* Given a buttonbox and a button name, find the button in the box with that
+ name. */
+
+Button BBoxFindButtonNamed(
+ ButtonBox buttonbox,
+ char *name)
+{
+ register int i;
+ for (i=0 ; i<buttonbox->numbuttons; i++)
+ if (strcmp(name, buttonbox->button[i]->name) == 0)
+ return buttonbox->button[i];
+ return (Button) NULL;
+}
+
+
+/* Given a buttonbox and a widget, find the button which is that widget. */
+
+Button BBoxFindButton(
+ ButtonBox buttonbox,
+ Widget w)
+{
+ register int i;
+ for (i=0; i < buttonbox->numbuttons; i++)
+ if (buttonbox->button[i]->widget == w)
+ return buttonbox->button[i];
+ return (Button) NULL;
+}
+
+
+/* Return the nth button in the given buttonbox. */
+
+Button BBoxButtonNumber(
+ ButtonBox buttonbox,
+ int n)
+{
+ return buttonbox->button[n];
+}
+
+
+/* Return how many buttons are in a buttonbox. */
+
+int BBoxNumButtons(
+ ButtonBox buttonbox)
+{
+ return buttonbox->numbuttons;
+}
+
+
+/* Given a button, return its name. */
+
+char *BBoxNameOfButton(
+ Button button)
+{
+ return button->name;
+}
+
+
+/* Given a button, return its menu. */
+
+Widget BBoxMenuOfButton(
+ Button button)
+{
+ return button->menu;
+}
+
+
+/* Set maximum size for a bbox so that it cannot be resized any taller
+ * than the space needed to stack all the buttons on top of each other.
+ * Allow the user to set the minimum size.
+ */
+
+void BBoxLockSize(
+ ButtonBox buttonbox)
+{
+ Dimension maxheight;
+ Arg args[1];
+
+ if (buttonbox == NULL) return;
+ maxheight = (Dimension) GetHeight(buttonbox->inner);
+ XtSetArg(args[0], XtNmax, maxheight); /* for Paned widget */
+ XtSetValues(buttonbox->outer, args, (Cardinal) 1);
+}
+
+
+Boolean BBoxIsGrandparent(
+ ButtonBox buttonbox,
+ Widget widget)
+{
+ return (XtParent(XtParent(widget)) == buttonbox->inner);
+}
+
+
+void BBoxMailFlag(
+ ButtonBox buttonbox,
+ char* name,
+ int up)
+{
+ Arg args[1];
+ Pixel flag;
+ Button button = BBoxFindButtonNamed(buttonbox, name);
+
+ if (button) {
+ /* avoid unnecessary exposures */
+ XtSetArg(args[0], XtNleftBitmap, &flag);
+ XtGetValues(button->widget, args, (Cardinal)1);
+ if (up && flag != app_resources.flag_up) {
+ XtSetArg(args[0], XtNleftBitmap, app_resources.flag_up);
+ XtSetValues(button->widget, args, (Cardinal)1);
+ }
+ else if (!up && flag != app_resources.flag_down) {
+ XtSetArg(args[0], XtNleftBitmap, app_resources.flag_down);
+ XtSetValues(button->widget, args, (Cardinal)1);
+ }
+ }
+}
diff --git a/bbox.h b/bbox.h
new file mode 100644
index 0000000..4aaa7fa
--- /dev/null
+++ b/bbox.h
@@ -0,0 +1,63 @@
+/* $XConsortium: bbox.h,v 2.12 91/07/10 19:34:09 converse Exp $ */
+/*
+ * COPYRIGHT 1987
+ * 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 documen-
+ * tation, 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/bbox.h,v 1.3 2002/07/01 02:26:05 tsi Exp $ */
+
+#ifndef _bbox_h
+#define _bbox_h
+
+ /* for radio button boxes only */
+
+extern ButtonBox RadioBBoxCreate (Scrn, char *);
+extern void RadioBBoxAddButton (ButtonBox, char *, Boolean);
+extern void RadioBBoxSet (Button);
+extern char * RadioBBoxGetCurrent (ButtonBox);
+extern void RadioBBoxDeleteButton(Button);
+
+ /* for other kinds of button boxes */
+
+extern ButtonBox BBoxCreate (Scrn, char *);
+extern void BBoxAddButton (ButtonBox, char *, WidgetClass, Boolean);
+extern void BBoxDeleteButton (Button);
+
+ /* for all kinds of button boxes */
+
+extern void BBoxInit (void);
+extern void BBoxEnable (Button);
+extern void BBoxDisable (Button);
+extern Button BBoxFindButtonNamed (ButtonBox, char *);
+extern Button BBoxFindButton (ButtonBox, Widget);
+extern Button BBoxButtonNumber (ButtonBox, int);
+extern int BBoxNumButtons (ButtonBox);
+extern char * BBoxNameOfButton (Button);
+extern Widget BBoxMenuOfButton (Button);
+extern void BBoxLockSize (ButtonBox);
+extern Boolean BBoxIsGrandparent (ButtonBox, Widget);
+
+ /* operations upon folder buttons */
+
+extern void BBoxMailFlag (ButtonBox, char*, int);
+
+#endif /* _bbox_h */
diff --git a/bboxint.h b/bboxint.h
new file mode 100644
index 0000000..fc062b0
--- /dev/null
+++ b/bboxint.h
@@ -0,0 +1,46 @@
+/* $XConsortium: bboxint.h,v 2.10 89/09/15 16:10:22 converse Exp $
+ *
+ * COPYRIGHT 1987
+ * 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.
+ */
+
+/* Includes for modules implementing buttonbox stuff. */
+
+#ifndef _bboxint_h
+#define _bboxint_h
+
+typedef struct _ButtonRec {
+ Widget widget; /* Widget containing this button. */
+ ButtonBox buttonbox; /* Button box containing this button. */
+ char *name; /* Name of the button. */
+ Widget menu; /* Menu widget, for menu buttons only */
+} ButtonRec;
+
+typedef struct _XmhButtonBoxRec {
+ Widget outer; /* Widget containing scollbars & inner */
+ Widget inner; /* Widget containing the buttons. */
+ Scrn scrn; /* Scrn containing this button box. */
+ int numbuttons; /* How many buttons in this box. */
+ Button *button; /* Array of pointers to buttons. */
+} ButtonBoxRec;
+
+#endif /* _bboxint_h */
diff --git a/black6 b/black6
new file mode 100644
index 0000000..4e5384e
--- /dev/null
+++ b/black6
@@ -0,0 +1,4 @@
+#define black6_width 6
+#define black6_height 6
+static char black6_bits[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
diff --git a/box6 b/box6
new file mode 100644
index 0000000..83ac4a7
--- /dev/null
+++ b/box6
@@ -0,0 +1,4 @@
+#define box6_width 6
+#define box6_height 6
+static char box6_bits[] = {
+ 0x3f, 0x21, 0x21, 0x21, 0x21, 0x3f};
diff --git a/command.c b/command.c
new file mode 100644
index 0000000..b6e8468
--- /dev/null
+++ b/command.c
@@ -0,0 +1,478 @@
+/* $XConsortium: command.c,v 2.49 95/04/05 19:59:06 kaleb Exp $ */
+/* $XFree86: xc/programs/xmh/command.c,v 3.9 2002/04/05 21:06:28 dickey 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.
+ */
+
+/* command.c -- interface to exec mh commands. */
+
+#include "xmh.h"
+#include <X11/Xpoll.h>
+#include <sys/ioctl.h>
+#include <signal.h>
+#ifndef SYSV
+#include <sys/wait.h>
+#endif /* SYSV */
+#if defined(SVR4) && !defined(SCO325) && !defined(DGUX)
+#include <sys/filio.h>
+#endif
+
+/* number of user input events to queue before malloc */
+#define TYPEAHEADSIZE 20
+
+#ifndef HAS_VFORK
+#define vfork() fork()
+#else
+#if defined(sun) && !defined(SVR4)
+#include <vfork.h>
+#endif
+#endif
+
+typedef struct _CommandStatus {
+ Widget popup; /* must be first; see PopupStatus */
+ struct _LastInput lastInput; /* must be second; ditto */
+ char* shell_command; /* must be third; for XmhShellCommand */
+ int child_pid;
+ XtInputId output_inputId;
+ XtInputId error_inputId;
+ int output_pipe[2];
+ int error_pipe[2];
+ char* output_buffer;
+ int output_buf_size;
+ char* error_buffer;
+ int error_buf_size;
+} CommandStatusRec, *CommandStatus;
+
+typedef char* Pointer;
+static void FreeStatus(XMH_CB_ARGS);
+static void CheckReadFromPipe(int, char **, int *, Bool);
+
+static void SystemError(char* text)
+{
+ char msg[BUFSIZ];
+ sprintf( msg, "%s; errno = %d %s", text, errno,
+ strerror(errno));
+ XtWarning( msg );
+}
+
+
+/* Return the full path name of the given mh command. */
+
+static char *FullPathOfCommand(char *str)
+{
+ static char result[100];
+ (void) sprintf(result, "%s/%s", app_resources.mh_path, str);
+ return result;
+}
+
+
+/*ARGSUSED*/
+static void ReadStdout(
+ XtPointer closure,
+ int *fd,
+ XtInputId *id) /* unused */
+{
+ register CommandStatus status = (CommandStatus)closure;
+ CheckReadFromPipe(*fd, &status->output_buffer, &status->output_buf_size,
+ False);
+}
+
+
+/*ARGSUSED*/
+static void ReadStderr(
+ XtPointer closure,
+ int *fd,
+ XtInputId *id) /* unused */
+{
+ register CommandStatus status = (CommandStatus)closure;
+ CheckReadFromPipe(*fd, &status->error_buffer, &status->error_buf_size,
+ False);
+}
+
+
+static volatile int childdone; /* Gets nonzero when the child process
+ finishes. */
+/* ARGSUSED */
+static void
+ChildDone(int n)
+{
+ childdone++;
+}
+
+/* Execute the given command, and wait until it has finished. While the
+ command is executing, watch the X socket and cause Xlib to read in any
+ incoming data. This will prevent the socket from overflowing during
+ long commands. Returns 0 if stderr empty, -1 otherwise. */
+
+static int _DoCommandToFileOrPipe(
+ char **argv, /* The command to execute, and its args. */
+ int inputfd, /* Input stream for command. */
+ int outputfd, /* Output stream; /dev/null if == -1 */
+ char **bufP, /* output buffer ptr if outputfd == -2 */
+ int *lenP) /* output length ptr if outputfd == -2 */
+{
+ XtAppContext appCtx = XtWidgetToApplicationContext(toplevel);
+ int return_status;
+ int old_stdin = 0, old_stdout = 0, old_stderr = 0;
+ int pid;
+ fd_set readfds, fds;
+ Boolean output_to_pipe = False;
+ CommandStatus status = XtNew(CommandStatusRec);
+ FD_ZERO(&fds);
+ FD_SET(ConnectionNumber(theDisplay), &fds);
+ DEBUG1("Executing %s ...", argv[0])
+
+ if (inputfd != -1) {
+ old_stdin = dup(fileno(stdin));
+ (void) dup2(inputfd, fileno(stdin));
+ close(inputfd);
+ }
+
+ if (outputfd == -1) {
+ if (!app_resources.debug) { /* Throw away stdout. */
+ outputfd = open( "/dev/null", O_WRONLY, 0 );
+ }
+ }
+ else if (outputfd == -2) { /* make pipe */
+ if (pipe(status->output_pipe) /*failed*/) {
+ SystemError( "couldn't re-direct standard output" );
+ status->output_pipe[0]=0;
+ }
+ else {
+ outputfd = status->output_pipe[1];
+ FD_SET(status->output_pipe[0], &fds);
+ status->output_inputId =
+ XtAppAddInput( appCtx,
+ status->output_pipe[0], (XtPointer)XtInputReadMask,
+ ReadStdout, (XtPointer)status
+ );
+ status->output_buffer = NULL;
+ status->output_buf_size = 0;
+ output_to_pipe = True;
+ }
+ }
+
+ if (pipe(status->error_pipe) /*failed*/) {
+ SystemError( "couldn't re-direct standard error" );
+ status->error_pipe[0]=0;
+ }
+ else {
+ old_stderr = dup(fileno(stderr));
+ (void) dup2(status->error_pipe[1], fileno(stderr));
+ close(status->error_pipe[1]);
+ FD_SET(status->error_pipe[0], &fds);
+ status->error_inputId =
+ XtAppAddInput( appCtx,
+ status->error_pipe[0], (XtPointer)XtInputReadMask,
+ ReadStderr, (XtPointer)status
+ );
+ }
+ if (outputfd != -1) {
+ old_stdout = dup(fileno(stdout));
+ (void) dup2(outputfd, fileno(stdout));
+ close(outputfd);
+ }
+ childdone = FALSE;
+ status->popup = (Widget)NULL;
+ status->lastInput = lastInput;
+ status->error_buffer = NULL;
+ status->error_buf_size = 0;
+ (void) signal(SIGCHLD, ChildDone);
+ pid = vfork();
+ if (inputfd != -1) {
+ if (pid != 0) dup2(old_stdin, fileno(stdin));
+ close(old_stdin);
+ }
+ if (outputfd != -1) {
+ if (pid != 0) dup2(old_stdout, fileno(stdout));
+ close(old_stdout);
+ }
+ if (status->error_pipe[0]) {
+ if (pid != 0) dup2(old_stderr, fileno(stderr));
+ close(old_stderr);
+ }
+
+ if (pid == -1) Punt("Couldn't fork!");
+ if (pid) { /* We're the parent process. */
+ XEvent typeAheadQueue[TYPEAHEADSIZE], *eventP = typeAheadQueue;
+ XEvent *altQueue = NULL;
+ int type_ahead_count = 0, alt_queue_size = 0, alt_queue_count = 0;
+ XtAppContext app = XtWidgetToApplicationContext(toplevel);
+ int num_fds = ConnectionNumber(theDisplay)+1;
+ if (output_to_pipe && status->output_pipe[0] >= num_fds)
+ num_fds = status->output_pipe[0]+1;
+ if (status->error_pipe[0] >= num_fds)
+ num_fds = status->error_pipe[0]+1;
+ status->child_pid = pid;
+ DEBUG1( " pid=%d ", pid )
+ subProcessRunning = True;
+ while (!childdone) {
+ while (!(XtAppPending(app) & XtIMXEvent)) {
+ /* this is gross, but the only other way is by
+ * polling on timers or an extra pipe, since we're not
+ * guaranteed to be able to malloc in a signal handler.
+ */
+ readfds = fds;
+ if (childdone) break;
+ DEBUG("blocking.\n")
+ (void) Select(num_fds, &readfds, NULL, NULL, NULL);
+ DEBUG1("unblocked; child%s done.\n", childdone ? "" : " not")
+ if (childdone) break;
+ if (!FD_ISSET(ConnectionNumber(theDisplay), &readfds)) {
+ DEBUG("reading alternate input...")
+ XtAppProcessEvent(appCtx, (unsigned)XtIMAlternateInput);
+ DEBUG("read.\n")
+ }
+ }
+ if (childdone) break;
+ XtAppNextEvent(app, eventP);
+ switch(eventP->type) {
+ case LeaveNotify:
+ if (type_ahead_count) {
+ /* do compress_enterleave here to save memory */
+ XEvent *prevEvent;
+ if (alt_queue_size && (alt_queue_count == 0))
+ prevEvent = &typeAheadQueue[type_ahead_count-1];
+ else
+ prevEvent = eventP - 1;
+ if (prevEvent->type == EnterNotify
+ && prevEvent->xany.display == eventP->xany.display
+ && prevEvent->xany.window == eventP->xany.window) {
+ eventP = prevEvent;
+ if (alt_queue_count > 0)
+ alt_queue_count--;
+ else
+ type_ahead_count--;
+ break;
+ }
+ }
+ /* fall through */
+ case KeyPress:
+ case KeyRelease:
+ case EnterNotify:
+ case ButtonPress:
+ case ButtonRelease:
+ case MotionNotify:
+ if (type_ahead_count < TYPEAHEADSIZE) {
+ if (++type_ahead_count == TYPEAHEADSIZE) {
+ altQueue = (XEvent*)XtMalloc(
+ (Cardinal)TYPEAHEADSIZE*sizeof(XEvent) );
+ alt_queue_size = TYPEAHEADSIZE;
+ eventP = altQueue;
+ }
+ else
+ eventP++;
+ }
+ else {
+ if (++alt_queue_count == alt_queue_size) {
+ alt_queue_size += TYPEAHEADSIZE;
+ altQueue = (XEvent*)XtRealloc(
+ (char*)altQueue,
+ (Cardinal)alt_queue_size*sizeof(XEvent) );
+ eventP = &altQueue[alt_queue_count];
+ }
+ else
+ eventP++;
+ }
+ break;
+
+ default:
+ XtDispatchEvent(eventP);
+ }
+ }
+ (void) wait(0);
+
+ DEBUG("done\n")
+ subProcessRunning = False;
+ if (output_to_pipe) {
+ CheckReadFromPipe( status->output_pipe[0],
+ &status->output_buffer,
+ &status->output_buf_size,
+ True
+ );
+ *bufP = status->output_buffer;
+ *lenP = status->output_buf_size;
+ close( status->output_pipe[0] );
+ XtRemoveInput( status->output_inputId );
+ }
+ if (status->error_pipe[0]) {
+ CheckReadFromPipe( status->error_pipe[0],
+ &status->error_buffer,
+ &status->error_buf_size,
+ True
+ );
+ close( status->error_pipe[0] );
+ XtRemoveInput( status->error_inputId );
+ }
+ if (status->error_buffer != NULL) {
+ /* special case for arbitrary shell commands: capture command */
+ if ((strcmp(argv[0], "/bin/sh") == 0) &&
+ (strcmp(argv[1], "-c") == 0)) {
+ status->shell_command = XtNewString(argv[2]);
+ } else status->shell_command = (char*) NULL;
+
+ while (status->error_buffer[status->error_buf_size-1] == '\0')
+ status->error_buf_size--;
+ while (status->error_buffer[status->error_buf_size-1] == '\n')
+ status->error_buffer[--status->error_buf_size] = '\0';
+ DEBUG1( "stderr = \"%s\"\n", status->error_buffer )
+ PopupNotice( status->error_buffer, FreeStatus, (Pointer)status );
+ return_status = -1;
+ }
+ else {
+ XtFree( (Pointer)status );
+ return_status = 0;
+ }
+ for (;alt_queue_count;alt_queue_count--) {
+ XPutBackEvent(theDisplay, --eventP);
+ }
+ if (type_ahead_count) {
+ if (alt_queue_size) eventP = &typeAheadQueue[type_ahead_count];
+ for (;type_ahead_count;type_ahead_count--) {
+ XPutBackEvent(theDisplay, --eventP);
+ }
+ }
+ } else { /* We're the child process. */
+ /* take it from the user's path, else fall back to the mhPath */
+ (void) execvp(argv[0], argv);
+ (void) execv(FullPathOfCommand(argv[0]), argv);
+ progName = argv[0]; /* for Punt message */
+ Punt("(cannot execvp it)");
+ return_status = -1;
+ }
+ return return_status;
+}
+
+
+static void
+CheckReadFromPipe(
+ int fd,
+ char **bufP,
+ int *lenP,
+ Bool waitEOF)
+{
+ int nread;
+/* DEBUG2( " CheckReadFromPipe #%d,len=%d,", fd, *lenP ) */
+#ifdef FIONREAD
+ if (!ioctl( fd, FIONREAD, &nread )) {
+/* DEBUG1( "nread=%d ...", nread ) */
+ if (nread) {
+ int old_end = *lenP;
+ *bufP = XtRealloc( *bufP, (Cardinal) ((*lenP += nread) + 1) );
+ read( fd, *bufP+old_end, nread );
+ (*bufP)[old_end+nread] = '\0';
+ }
+ return;
+ }
+#endif
+ do {
+ char buf[512];
+ int old_end = *lenP;
+ nread = read( fd, buf, 512 );
+ if (nread <= 0)
+ break;
+ *bufP = XtRealloc( *bufP, (Cardinal) ((*lenP += nread) + 1) );
+ memmove( *bufP+old_end, buf, (int) nread );
+ (*bufP)[old_end+nread] = '\0';
+ } while (waitEOF);
+}
+
+
+/* ARGSUSED */
+static void FreeStatus(
+ Widget w, /* unused */
+ XtPointer closure,
+ XtPointer call_data) /* unused */
+{
+ CommandStatus status = (CommandStatus)closure;
+ if (status->popup != (Widget)NULL) {
+ XtPopdown( status->popup );
+ XtDestroyWidget( status->popup );
+ }
+ if (status->error_buffer != NULL) XtFree(status->error_buffer);
+ XtFree( closure );
+}
+
+/* Execute the given command, waiting until it's finished. Put the output
+ in the specified file path. Returns 0 if stderr empty, -1 otherwise */
+
+int DoCommand(
+ char **argv, /* The command to execute, and its args. */
+ char *inputfile, /* Input file for command. */
+ char *outputfile) /* Output file for command. */
+{
+ int fd_in, fd_out;
+ int status;
+
+ if (inputfile != NULL) {
+ FILEPTR file = FOpenAndCheck(inputfile, "r");
+ fd_in = dup(fileno(file));
+ myfclose(file);
+ }
+ else
+ fd_in = -1;
+
+ if (outputfile) {
+ FILEPTR file = FOpenAndCheck(outputfile, "w");
+ fd_out = dup(fileno(file));
+ myfclose(file);
+ }
+ else
+ fd_out = -1;
+
+ status = _DoCommandToFileOrPipe( argv, fd_in, fd_out, (char **) NULL,
+ (int *) NULL );
+ return status;
+}
+
+/* Execute the given command, waiting until it's finished. Put the output
+ in a newly mallocced string, and return a pointer to that string. */
+
+char *DoCommandToString(char ** argv)
+{
+ char *result = NULL;
+ int len = 0;
+ _DoCommandToFileOrPipe( argv, -1, -2, &result, &len );
+ if (result == NULL) result = XtMalloc((Cardinal) 1);
+ result[len] = '\0';
+ DEBUG1("('%s')\n", result)
+ return result;
+}
+
+
+/* Execute the command to a temporary file, and return the name of the file. */
+
+char *DoCommandToFile(char **argv)
+{
+ char *name;
+ FILEPTR file;
+ int fd;
+ name = MakeNewTempFileName();
+ file = FOpenAndCheck(name, "w");
+ fd = dup(fileno(file));
+ myfclose(file);
+ _DoCommandToFileOrPipe(argv, -1, fd, (char **) NULL, (int *) NULL);
+ return name;
+}
diff --git a/compfuncs.c b/compfuncs.c
new file mode 100644
index 0000000..957249e
--- /dev/null
+++ b/compfuncs.c
@@ -0,0 +1,129 @@
+/*
+ * $XConsortium: compfuncs.c,v 2.18 92/04/08 12:20:08 rws 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/compfuncs.c,v 1.3 2002/04/05 21:06:28 dickey Exp $ */
+
+/* comp.c -- action procedures to handle composition buttons. */
+
+#include "xmh.h"
+#include "actions.h"
+
+/* Reset this composition widget to be one with just a blank message
+ template. */
+
+/*ARGSUSED*/
+void DoResetCompose(
+ Widget widget, /* unused */
+ XtPointer client_data,
+ XtPointer call_data) /* unused */
+{
+ Scrn scrn = (Scrn) client_data;
+ Msg msg;
+ XtCallbackRec confirms[2];
+
+ confirms[0].callback = (XtCallbackProc) DoResetCompose;
+ confirms[0].closure = (XtPointer) scrn;
+ confirms[1].callback = (XtCallbackProc) NULL;
+ confirms[1].closure = (XtPointer) NULL;
+
+ if (MsgSetScrn((Msg) NULL, scrn, confirms, (XtCallbackList) NULL) ==
+ NEEDS_CONFIRMATION)
+ return;
+
+ msg = TocMakeNewMsg(DraftsFolder);
+ MsgLoadComposition(msg);
+ MsgSetTemporary(msg);
+ MsgSetReapable(msg);
+ (void) MsgSetScrn(msg, scrn, (XtCallbackList) NULL, (XtCallbackList) NULL);
+}
+
+/*ARGSUSED*/
+void XmhResetCompose(
+ Widget w,
+ XEvent *event,
+ String *params,
+ Cardinal *num_params)
+{
+ Scrn scrn = ScrnFromWidget(w);
+ DoResetCompose(w, (XtPointer) scrn, (XtPointer) NULL);
+}
+
+
+/* Send the message in this widget. Avoid sending the same message twice.
+ (Code elsewhere actually makes sure this button is disabled to avoid
+ sending the same message twice, but it doesn't hurt to be safe here.) */
+
+/*ARGSUSED*/
+void XmhSend(
+ Widget w,
+ XEvent *event,
+ String *params,
+ Cardinal *num_params)
+{
+ Scrn scrn = ScrnFromWidget(w);
+ if (scrn->msg == NULL) return;
+ if (MsgChanged(scrn->msg) || ! MsgGetReapable(scrn->msg)) {
+ MsgSend(scrn->msg);
+ MsgSetReapable(scrn->msg);
+ }
+}
+
+
+/* Save any changes to the message. This also makes this message permanent. */
+
+/*ARGSUSED*/
+void XmhSave(
+ Widget w,
+ XEvent *event,
+ String *params,
+ Cardinal *num_params)
+{
+ Scrn scrn = ScrnFromWidget(w);
+ DEBUG("XmhSave\n")
+ if (scrn->msg == NULL) return;
+ MsgSetPermanent(scrn->msg);
+ if (MsgSaveChanges(scrn->msg))
+ MsgClearReapable(scrn->msg);
+}
+
+
+/* Utility routine; creates a composition screen containing a forward message
+ of the messages in the given msglist. */
+
+void CreateForward(
+ MsgList mlist,
+ String *params,
+ Cardinal num_params)
+{
+ Scrn scrn;
+ Msg msg;
+ scrn = NewCompScrn();
+ msg = TocMakeNewMsg(DraftsFolder);
+ MsgLoadForward(scrn, msg, mlist, params, num_params);
+ MsgSetTemporary(msg);
+ MsgSetScrnForComp(msg, scrn);
+ MapScrn(scrn);
+}
diff --git a/externs.h b/externs.h
new file mode 100644
index 0000000..668ffaa
--- /dev/null
+++ b/externs.h
@@ -0,0 +1,186 @@
+/*
+ * $XConsortium: externs.h /main/36 1996/01/14 16:51:37 kaleb $
+ *
+ *
+ * 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/externs.h,v 1.5 2002/04/05 21:06:28 dickey Exp $ */
+
+#include <X11/Intrinsic.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* Action routines are declared in actions.h */
+/* Functions which begin with `Do' are the corresponding callbacks. */
+
+ /* from command.c */
+
+extern int DoCommand (char **, char *, char *);
+extern char * DoCommandToFile (char **);
+extern char * DoCommandToString (char **);
+
+ /* from compfuncs.c */
+
+extern void DoResetCompose (XMH_CB_ARGS);
+extern void CreateForward (MsgList, String *, Cardinal);
+
+ /* from folder.c */
+
+extern void DoClose (XMH_CB_ARGS);
+extern void DoComposeMessage (XMH_CB_ARGS);
+extern void DoOpenFolder (XMH_CB_ARGS);
+extern void DoOpenFolderInNewWindow (XMH_CB_ARGS);
+extern void DoCreateFolder (XMH_CB_ARGS);
+extern void DoDeleteFolder (XMH_CB_ARGS);
+extern void DoSaveYourself (XMH_CB_ARGS);
+extern void Push (Stack *, char *);
+extern char * Pop (Stack *);
+
+ /* from init.c */
+
+extern void InitializeWorld (int, char **);
+
+ /* from menu.c */
+
+extern void AttachMenuToButton (Button, Widget, char *);
+extern void AddMenuEntry (Widget, char *, ...);
+extern void DoRememberMenuSelection (XMH_CB_ARGS);
+extern void SendMenuEntryEnableMsg (Button, char *, int);
+extern void ToggleMenuItem (Widget, Boolean);
+
+ /* from miscfuncs.c */
+
+extern int ScanDir (char *, char ***, int (*)(char *));
+
+ /* from msg.c */
+
+extern Widget CreateFileSource (Widget, String, Boolean);
+extern char* MsgName (Msg);
+
+ /* from pick.c */
+
+extern void InitPick (void);
+extern void AddPick (Scrn, Toc, char *, char *);
+
+ /* from popup.c */
+
+extern void DestroyPopup (XMH_CB_ARGS);
+extern void WMDeletePopup (Widget, XEvent*);
+extern void PopupPrompt (Widget, String, XtCallbackProc);
+extern void PopupConfirm (Widget, String,
+ XtCallbackList, XtCallbackList);
+extern void PopupNotice (char *, XtCallbackProc, XtPointer);
+extern void PopupError (Widget, String);
+extern void PopupWarningHandler(String, String, String, String, String *, Cardinal *);
+
+ /* from screen.c */
+
+extern void EnableProperButtons (Scrn);
+extern Scrn CreateNewScrn (ScrnKind);
+extern Scrn NewViewScrn (void);
+extern Scrn NewCompScrn (void);
+extern void ScreenSetAssocMsg (Scrn, Msg);
+extern void DestroyScrn (Scrn);
+extern void MapScrn (Scrn);
+extern Scrn ScrnFromWidget (Widget);
+
+ /* from toc.c */
+
+extern int TocFolderExists (Toc);
+extern Boolean TocHasChanges (Toc);
+
+ /* from tocfuncs.c */
+
+extern Boolean UserWantsAction (Widget, Scrn);
+extern void DoIncorporateNewMail (XMH_CB_ARGS);
+extern void DoCommit (XMH_CB_ARGS);
+extern void DoPack (XMH_CB_ARGS);
+extern void DoSort (XMH_CB_ARGS);
+extern void DoForceRescan (XMH_CB_ARGS);
+extern void DoReverseReadOrder (XMH_CB_ARGS);
+extern void DoNextView (XMH_CB_ARGS);
+extern void DoPrevView (XMH_CB_ARGS);
+extern void DoDelete (XMH_CB_ARGS);
+extern void DoMove (XMH_CB_ARGS);
+extern void DoCopy (XMH_CB_ARGS);
+extern void DoUnmark (XMH_CB_ARGS);
+extern void DoViewNew (XMH_CB_ARGS);
+extern void DoReply (XMH_CB_ARGS);
+extern void DoForward (XMH_CB_ARGS);
+extern void DoTocUseAsComp (XMH_CB_ARGS);
+extern void DoPrint (XMH_CB_ARGS);
+extern void DoPickMessages (XMH_CB_ARGS);
+extern void DoSelectSequence (XMH_CB_ARGS);
+extern void DoOpenSeq (XMH_CB_ARGS);
+extern void DoAddToSeq (XMH_CB_ARGS);
+extern void DoRemoveFromSeq (XMH_CB_ARGS);
+extern void DoDeleteSeq (XMH_CB_ARGS);
+
+ /* from util.c */
+
+extern void Punt (char *);
+extern int myopen (char *, int, int);
+extern FILE * myfopen (char *, char *);
+extern void myclose (int);
+extern void myfclose (FILE *);
+extern char * MakeNewTempFileName (void);
+extern char ** MakeArgv (int);
+extern char ** ResizeArgv (char **, int);
+extern FILEPTR FOpenAndCheck (char *, char *);
+extern char * ReadLine (FILE *);
+extern char * ReadLineWithCR (FILE *);
+extern void DeleteFileAndCheck (char *);
+extern void CopyFileAndCheck (char *, char *);
+extern void RenameAndCheck (char *, char *);
+extern char * CreateGeometry (int, int, int, int, int);
+extern int FileExists (char *);
+extern long LastModifyDate (char *);
+extern int GetFileLength (char *);
+extern Boolean IsSubfolder (char *);
+extern void SetCurrentFolderName (Scrn, char *);
+extern void ChangeLabel (Widget, char *);
+extern Widget CreateTextSW (Scrn, char *, ArgList, Cardinal);
+extern Widget CreateTitleBar (Scrn, char *);
+extern void Feep (int, int, Window);
+extern MsgList CurMsgListOrCurMsg (Toc);
+extern int GetWidth (Widget);
+extern int GetHeight (Widget);
+extern Toc SelectedToc (Scrn);
+extern Toc CurrentToc (Scrn);
+extern int strncmpIgnoringCase(char *, char *, int);
+extern void StoreWindowName (Scrn, char *);
+extern void InitBusyCursor (Scrn);
+extern void ShowBusyCursor (void);
+extern void UnshowBusyCursor (void);
+extern void SetCursorColor (Widget, Cursor, unsigned long);
+
+ /* from viewfuncs.c */
+
+extern void DoCloseView (XMH_CB_ARGS);
+extern void DoViewReply (XMH_CB_ARGS);
+extern void DoViewForward (XMH_CB_ARGS);
+extern void DoViewUseAsComposition (XMH_CB_ARGS);
+extern void DoEditView (XMH_CB_ARGS);
+extern void DoSaveView (XMH_CB_ARGS);
+extern void DoPrintView (XMH_CB_ARGS);
diff --git a/folder.c b/folder.c
new file mode 100644
index 0000000..91502d9
--- /dev/null
+++ b/folder.c
@@ -0,0 +1,1188 @@
+/*
+ * $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);
+ }
+ }
+ }
+}
diff --git a/globals.h b/globals.h
new file mode 100644
index 0000000..286aca5
--- /dev/null
+++ b/globals.h
@@ -0,0 +1,166 @@
+/*
+ * $XConsortium: globals.h /main/37 1996/02/02 14:27:39 kaleb $
+ *
+ *
+ * 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/globals.h,v 1.3 2002/07/01 02:26:05 tsi Exp $ */
+
+#ifdef MAIN
+#define ext
+#else
+#define ext extern
+#endif
+
+ext Display *theDisplay; /* Display variable. */
+ext Widget toplevel; /* The top level widget (A hack %%%). */
+ext char *progName; /* Program name. */
+ext char *homeDir; /* User's home directory. */
+ext Atom wm_protocols; /* WM_PROTOCOLS atom for this display */
+ext Atom wm_delete_window; /* see ICCCM on Window Deletion */
+ext Atom wm_save_yourself; /* see ICCCM on session management */
+ext Atom protocolList[2]; /* contains the two above */
+
+ext struct _resources {
+ Boolean debug;
+ char *mail_path; /* mh's mail directory. */
+ char *temp_dir; /* Directory for temporary files. */
+ char *mh_path; /* Path for mh commands. */
+ char *initial_folder_name; /* Initial folder to use. */
+ char *initial_inc_file; /* -file for inc on initial folder */
+ char *insert_filter; /* Insert message filter command */
+ char *drafts_folder_name; /* Folder for drafts. */
+ int send_line_width; /* How long to break lines on send. */
+ int break_send_line_width; /* Minimum length of a line before
+ we'll break it. */
+ char *print_command; /* Printing command. */
+ int toc_width; /* How many characters wide to use in tocs */
+ Boolean skip_deleted; /* If true, skip over deleted msgs. */
+ Boolean skip_moved; /* If true, skip over moved msgs. */
+ Boolean skip_copied; /* If true, skip over copied msgs. */
+ Boolean hide_boring_headers;
+ char *geometry; /* Default geometry to use for things. */
+ char *toc_geometry;
+ char *view_geometry;
+ char *comp_geometry;
+ char *pick_geometry;
+ int toc_percentage; /* % of toc and view used by toc */
+ Boolean new_mail_check; /* should xmh check for new mail? */
+ Boolean make_checkpoints; /* should xmh save edits in progress?*/
+ int check_frequency; /* backwards compatibility */
+ int mail_waiting_flag; /* If true, change icon on new mail */
+ int mail_interval; /* how often to check for new mail */
+ int rescan_interval; /* how often to check viewed tocs */
+ int checkpoint_interval; /* how often to save edits */
+ char * checkpoint_name_format; /* format of checkpoint file name */
+ Pixmap flag_up; /* folder has new mail */
+ Pixmap flag_down; /* folder has no new mail */
+ Pixmap new_mail_icon; /* new mail icon for wm hints */
+ Pixmap no_mail_icon; /* no mail icon for wm hints */
+ Cursor cursor; /* application cursor */
+ Pixel pointer_color; /* application cursor color */
+ Boolean show_on_inc; /* show 1st new message after inc? */
+ Boolean sticky_menu; /* command menu entries are sticky? */
+ Boolean prefix_wm_and_icon_name;/* prefix wm names with progName ? */
+ Boolean reverse_read_order; /* decrement counter to next msg ? */
+ Boolean block_events_on_busy; /* disallow user input while busy ? */
+ Cursor busy_cursor; /* the cursor while input blocked */
+ Pixel busy_pointer_color; /* busy cursor color */
+ int command_button_count; /* number of buttons in command box */
+ int app_defaults_version; /* for sanity check */
+ char *banner; /* defaults to xmh version string */
+ XtTranslations wm_protocols_translations; /* for all shells seen by WM */
+} app_resources;
+
+ext char *draftFile; /* Filename of draft. */
+ext char *xmhDraftFile; /* Filename for sending. */
+ext Toc *folderList; /* Array of folders. */
+ext int numFolders; /* Number of entries in above array. */
+ext Toc InitialFolder; /* Toc containing initial folder. */
+ext Toc DraftsFolder; /* Toc containing drafts. */
+ext Scrn *scrnList; /* Array of scrns in use. */
+ext int numScrns; /* Number of scrns in above array. */
+ext Widget NoMenuForButton;/* Flag menu widget value: no menu */
+ext Widget LastMenuButtonPressed; /* to `toggle' menu buttons */
+ext Widget NullSource; /* null text widget source */
+ext Dimension rootwidth; /* Dimensions of root window. */
+ext Dimension rootheight;
+ext Pixmap MenuItemBitmap; /* Options menu item checkmark */
+ext XtTranslations NoTextSearchAndReplace; /* no-op ^S and ^R in Text */
+
+ext struct _LastInput {
+ Window win;
+ int x;
+ int y;
+} lastInput;
+
+ext Boolean subProcessRunning; /* interlock for DoCommand/CheckMail */
+
+#define PNullSource (NullSource != NULL ? NullSource : \
+(NullSource = (Widget) CreateFileSource(scrn->viewlabel, "/dev/null", False)))
+
+
+typedef struct _XmhMenuEntry {
+ char *name; /* menu entry name */
+ void (*function)(XMH_CB_ARGS); /* menu entry callback function */
+} XmhMenuEntryRec, *XmhMenuEntry;
+
+
+typedef struct _XmhMenuButtonDesc {
+ char *button_name; /* menu button name */
+ char *menu_name; /* menu name */
+ int id; /* an internal key */
+ XmhMenuEntry entry; /* list of menu entries */
+ Cardinal num_entries; /* count of menu entries in list */
+} XmhMenuButtonDescRec, *XmhMenuButtonDesc;
+
+
+extern XmhMenuEntryRec folderMenu[];
+extern XmhMenuEntryRec tocMenu[];
+extern XmhMenuEntryRec messageMenu[];
+extern XmhMenuEntryRec sequenceMenu[];
+extern XmhMenuEntryRec viewMenu[];
+extern XmhMenuEntryRec optionMenu[];
+
+extern XmhMenuButtonDescRec MenuBoxButtons[];
+
+/* Used as indices into MenuBoxButtons; these must correspond. */
+
+#define XMH_FOLDER 0
+#define XMH_TOC 1
+#define XMH_MESSAGE 2
+#define XMH_SEQUENCE 3
+#define XMH_VIEW 4
+#define XMH_OPTION 5
+
+/* Bell types Feep() */
+#ifdef XKB
+#include <X11/extensions/XKBbells.h>
+#else
+#define XkbBI_Info 0
+#define XkbBI_MinorError 1
+#define XkbBI_MajorError 2
+#define XkbBI_Failure 6
+#define XkbBI_Wait 7
+#define XkbBI_NewMail 12
+#endif
diff --git a/init.c b/init.c
new file mode 100644
index 0000000..b8c98b0
--- /dev/null
+++ b/init.c
@@ -0,0 +1,517 @@
+/*
+ * $XConsortium: init.c,v 2.81 95/01/25 14:56:39 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/init.c,v 1.6 2002/04/05 21:06:28 dickey Exp $ */
+
+/* Init.c - Handle start-up initialization. */
+
+#include "xmh.h"
+#include "actions.h"
+#include "version.h"
+#include <errno.h>
+
+#define MIN_APP_DEFAULTS_VERSION 1
+#define xmhCkpDefault "%d.CKP"
+
+static String FallbackResources[] = {
+"*folderButton.label: Close",
+"*folderButton.borderWidth: 4",
+"*folderButton.translations: #override <Btn1Down>: XmhClose()",
+NULL
+};
+
+static Boolean static_variable; /* whose address is not a widget ID */
+
+/* This is for the check mark in the Options menu */
+#define check_width 9
+#define check_height 8
+static unsigned char check_bits[] = {
+ 0x00, 0x01, 0x80, 0x01, 0xc0, 0x00, 0x60, 0x00,
+ 0x31, 0x00, 0x1b, 0x00, 0x0e, 0x00, 0x04, 0x00
+};
+
+#define Offset(field) XtOffsetOf(struct _resources, field)
+
+/* Xmh application resources. */
+
+static XtResource resources[] = {
+ {"debug", "Debug", XtRBoolean, sizeof(Boolean),
+ Offset(debug), XtRImmediate, (XtPointer)False},
+
+ {"tempDir", "TempDir", XtRString, sizeof(char *),
+ Offset(temp_dir), XtRString, "/tmp"},
+ {"mhPath", "MhPath", XtRString, sizeof(char *),
+ Offset(mh_path), XtRString, "/usr/local/mh6"},
+ {"mailPath", "MailPath", XtRString, sizeof(char *),
+ Offset(mail_path), XtRString, NULL},
+ {"initialFolder", "InitialFolder", XtRString, sizeof(char *),
+ Offset(initial_folder_name), XtRString, "inbox"},
+ {"initialIncFile", "InitialIncFile", XtRString, sizeof(char *),
+ Offset(initial_inc_file), XtRString, NULL},
+ {"replyInsertFilter", "ReplyInsertFilter", XtRString, sizeof(char *),
+ Offset(insert_filter), XtRString, NULL},
+ {"draftsFolder", "DraftsFolder", XtRString, sizeof(char *),
+ Offset(drafts_folder_name), XtRString, "drafts"},
+ {"printCommand", "PrintCommand", XtRString, sizeof(char *),
+ Offset(print_command), XtRString,
+ "enscript > /dev/null 2>/dev/null"},
+
+ {"sendWidth", "SendWidth", XtRInt, sizeof(int),
+ Offset(send_line_width), XtRImmediate, (XtPointer)72},
+ {"sendBreakWidth", "SendBreakWidth", XtRInt, sizeof(int),
+ Offset(break_send_line_width), XtRImmediate, (XtPointer)85},
+ {"tocWidth", "TocWidth", XtRInt, sizeof(int),
+ Offset(toc_width), XtRImmediate, (XtPointer)100},
+ {"skipDeleted", "SkipDeleted", XtRBoolean, sizeof(Boolean),
+ Offset(skip_deleted), XtRImmediate, (XtPointer)True},
+ {"skipMoved", "SkipMoved", XtRBoolean, sizeof(Boolean),
+ Offset(skip_moved), XtRImmediate, (XtPointer)True},
+ {"skipCopied", "SkipCopied", XtRBoolean, sizeof(Boolean),
+ Offset(skip_copied), XtRImmediate, (XtPointer)False},
+ {"hideBoringHeaders", "HideBoringHeaders", XtRBoolean, sizeof(Boolean),
+ Offset(hide_boring_headers), XtRImmediate, (XtPointer)True},
+
+ {"geometry", "Geometry", XtRString, sizeof(char *),
+ Offset(geometry), XtRString, NULL},
+ {"tocGeometry", "TocGeometry", XtRString, sizeof(char *),
+ Offset(toc_geometry), XtRString, NULL},
+ {"viewGeometry", "ViewGeometry", XtRString, sizeof(char *),
+ Offset(view_geometry), XtRString, NULL},
+ {"compGeometry", "CompGeometry", XtRString, sizeof(char *),
+ Offset(comp_geometry), XtRString, NULL},
+ {"pickGeometry", "PickGeometry", XtRString, sizeof(char *),
+ Offset(pick_geometry), XtRString, NULL},
+ {"tocPercentage", "TocPercentage", XtRInt, sizeof(int),
+ Offset(toc_percentage), XtRImmediate, (XtPointer)33},
+
+ {"checkNewMail", "CheckNewMail", XtRBoolean, sizeof(Boolean),
+ Offset(new_mail_check), XtRImmediate, (XtPointer)True},
+ {"mailInterval", "Interval", XtRInt, sizeof(int),
+ Offset(mail_interval), XtRImmediate, (XtPointer)-1},
+ {"makeCheckpoints", "MakeCheckpoints", XtRBoolean, sizeof(Boolean),
+ Offset(make_checkpoints), XtRImmediate, (XtPointer)False},
+ {"checkpointInterval", "Interval", XtRInt, sizeof(int),
+ Offset(checkpoint_interval), XtRImmediate, (XtPointer)-1},
+ {"checkpointNameFormat", "CheckpointNameFormat",
+ XtRString, sizeof(char *),
+ Offset(checkpoint_name_format), XtRString, xmhCkpDefault},
+ {"rescanInterval", "Interval", XtRInt, sizeof(int),
+ Offset(rescan_interval), XtRImmediate, (XtPointer)-1},
+ {"checkFrequency", "CheckFrequency", XtRInt, sizeof(int),
+ Offset(check_frequency), XtRImmediate, (XtPointer)1},
+ {"mailWaitingFlag", "MailWaitingFlag", XtRBoolean, sizeof(Boolean),
+ Offset(mail_waiting_flag), XtRImmediate, (XtPointer)False},
+ {"newMailIconBitmap", "NewMailBitmap", XtRBitmap, sizeof(Pixmap),
+ Offset(new_mail_icon), XtRString, (XtPointer)"flagup"},
+ {"noMailIconBitmap", "NoMailBitmap", XtRBitmap, sizeof(Pixmap),
+ Offset(no_mail_icon), XtRString, (XtPointer)"flagdown"},
+ {"newMailBitmap", "NewMailBitmap", XtRBitmap, sizeof(Pixmap),
+ Offset(flag_up), XtRString, (XtPointer)"black6"},
+ {"noMailBitmap", "NoMailBitmap", XtRBitmap, sizeof(Pixmap),
+ Offset(flag_down), XtRString, (XtPointer)"box6"},
+
+ {"cursor", "Cursor", XtRCursor, sizeof(Cursor),
+ Offset(cursor), XtRString, "left_ptr"},
+ {"pointerColor", "PointerColor", XtRPixel, sizeof(Pixel),
+ Offset(pointer_color), XtRString, XtDefaultForeground},
+ {"showOnInc", "ShowOnInc", XtRBoolean, sizeof(Boolean),
+ Offset(show_on_inc), XtRImmediate, (XtPointer)True},
+ {"stickyMenu", "StickyMenu", XtRBoolean, sizeof(Boolean),
+ Offset(sticky_menu), XtRImmediate, (XtPointer)False},
+ {"prefixWmAndIconName", "PrefixWmAndIconName", XtRBoolean, sizeof(Boolean),
+ Offset(prefix_wm_and_icon_name), XtRImmediate, (XtPointer)True},
+ {"reverseReadOrder", "ReverseReadOrder", XtRBoolean, sizeof(Boolean),
+ Offset(reverse_read_order), XtRImmediate, (XtPointer)False},
+ {"blockEventsOnBusy", "BlockEventsOnBusy", XtRBoolean, sizeof(Boolean),
+ Offset(block_events_on_busy), XtRImmediate, (XtPointer)True},
+ {"busyCursor", "BusyCursor", XtRCursor, sizeof(Cursor),
+ Offset(busy_cursor), XtRString, "watch"},
+ {"busyPointerColor", "BusyPointerColor", XtRPixel, sizeof(Pixel),
+ Offset(busy_pointer_color), XtRString, XtDefaultForeground},
+ {"commandButtonCount", "CommandButtonCount", XtRInt, sizeof(int),
+ Offset(command_button_count), XtRImmediate, (XtPointer)0},
+ {"appDefaultsVersion", "AppDefaultsVersion", XtRInt, sizeof(int),
+ Offset(app_defaults_version), XtRImmediate, (XtPointer)0},
+ {"banner", "Banner", XtRString, sizeof(char *),
+ Offset(banner), XtRString, XMH_VERSION},
+ {"wmProtocolsTranslations", "WMProtocolsTranslations",
+ XtRTranslationTable, sizeof(XtTranslations),
+ Offset(wm_protocols_translations), XtRString,
+ "<Message>WM_PROTOCOLS: XmhWMProtocols()\n"}
+};
+
+#undef Offset
+
+static XrmOptionDescRec table[] = {
+ {"-debug", "debug", XrmoptionNoArg, "on"},
+ {"-flag", "mailWaitingFlag", XrmoptionNoArg, "on"},
+ {"-initial","initialFolder", XrmoptionSepArg, NULL},
+ {"-path", "mailPath", XrmoptionSepArg, NULL},
+};
+
+/* Tell the user how to use this program. */
+static void Syntax(char *call)
+{
+ (void) fprintf(stderr, "usage: %s [-path <path>] [-initial <folder>]\n",
+ call);
+ exit(2);
+}
+
+
+static char *FixUpGeometry(char *geo, unsigned defwidth, unsigned defheight)
+{
+ int gbits;
+ int x, y;
+ unsigned int width, height;
+ if (geo == NULL) geo = app_resources.geometry;
+ x = y = 0;
+ gbits = XParseGeometry(geo, &x, &y, &width, &height);
+ if (!(gbits & WidthValue)) {
+ width = defwidth;
+ gbits |= WidthValue;
+ }
+ if (!(gbits & HeightValue)) {
+ height = defheight;
+ gbits |= HeightValue;
+ }
+ return CreateGeometry(gbits, x, y, (int) width, (int) height);
+}
+
+
+static int _IOErrorHandler(Display *dpy)
+{
+ (void) fprintf (stderr,
+ "%s:\tfatal IO error after %lu requests (%lu known processed)\n",
+ progName,
+ NextRequest(dpy) - 1, LastKnownRequestProcessed(dpy));
+ (void) fprintf (stderr, "\t%d unprocessed events remaining.\r\n",
+ QLength(dpy));
+
+ if (errno == EPIPE) {
+ (void) fprintf (stderr,
+ "\tThe connection was probably broken by a server shutdown or KillClient.\r\n");
+ }
+
+ Punt("Cannot continue from server error.");
+ return 0;
+}
+
+/*ARGSUSED*/
+static void PopupAppDefaultsWarning(
+ Widget w,
+ XtPointer closure,
+ XEvent *event,
+ Boolean *cont)
+{
+ if (event->type == MapNotify) {
+ PopupError(w,
+"The minimum application default resources\n\
+were not properly installed; many features\n\
+will not work properly, if at all. See the\n\
+xmh man page for further information."
+ );
+ XtRemoveEventHandler(w, XtAllEvents, True,
+ PopupAppDefaultsWarning, closure);
+ }
+}
+
+
+/*ARGSUSED*/
+static void _Die(
+ Widget w, /* == toplevel */
+ XtPointer client_data, /* unused */
+ XtPointer call_data) /* unused */
+{
+ int i;
+
+ for (i=0; i<numScrns; i++)
+ if (scrnList[i]->mapped)
+ XtUnmapWidget(scrnList[i]->parent);
+
+ XtDestroyApplicationContext(XtWidgetToApplicationContext(w));
+ exit(0);
+}
+
+
+/* All the start-up initialization goes here. */
+
+extern char** environ; /* POSIX doesn't specify a .h for this */
+
+void InitializeWorld(int argc, char **argv)
+{
+ int l;
+ FILEPTR fid;
+ char str[500], str2[500], *ptr;
+ Scrn scrn;
+ XtAppContext app;
+ static XtActionsRec actions[] = {
+
+ /* general Xmh action procedures */
+
+ {"XmhClose", XmhClose},
+ {"XmhComposeMessage", XmhComposeMessage},
+ {"XmhWMProtocols", XmhWMProtocols},
+
+ /* actions upon folders */
+
+ {"XmhOpenFolder", XmhOpenFolder},
+ {"XmhOpenFolderInNewWindow", XmhOpenFolderInNewWindow},
+ {"XmhCreateFolder", XmhCreateFolder},
+ {"XmhDeleteFolder", XmhDeleteFolder},
+
+ /* actions to support easier folder manipulation */
+
+ {"XmhPushFolder", XmhPushFolder},
+ {"XmhPopFolder", XmhPopFolder},
+ {"XmhPopupFolderMenu", XmhPopupFolderMenu},
+ {"XmhSetCurrentFolder", XmhSetCurrentFolder},
+ {"XmhLeaveFolderButton", XmhLeaveFolderButton},
+ {"XmhCheckForNewMail", XmhCheckForNewMail},
+
+ /* actions upon the Table of Contents */
+
+ {"XmhIncorporateNewMail", XmhIncorporateNewMail},
+ {"XmhCommitChanges", XmhCommitChanges},
+ {"XmhPackFolder", XmhPackFolder},
+ {"XmhSortFolder", XmhSortFolder},
+ {"XmhForceRescan", XmhForceRescan},
+ {"XmhReloadSeqLists", XmhReloadSeqLists},
+
+ /* actions upon the currently selected message(s) */
+
+ {"XmhViewNextMessage", XmhViewNextMessage},
+ {"XmhViewPreviousMessage", XmhViewPreviousMessage},
+ {"XmhMarkDelete", XmhMarkDelete},
+ {"XmhMarkMove", XmhMarkMove},
+ {"XmhMarkCopy", XmhMarkCopy},
+ {"XmhUnmark", XmhUnmark},
+ {"XmhViewInNewWindow", XmhViewInNewWindow},
+ {"XmhReply", XmhReply},
+ {"XmhForward", XmhForward},
+ {"XmhUseAsComposition", XmhUseAsComposition},
+ {"XmhPrint", XmhPrint},
+ {"XmhShellCommand", XmhShellCommand},
+
+ /* actions upon sequences */
+
+ {"XmhPickMessages", XmhPickMessages},
+ {"XmhOpenSequence", XmhOpenSequence},
+ {"XmhAddToSequence", XmhAddToSequence},
+ {"XmhRemoveFromSequence", XmhRemoveFromSequence},
+ {"XmhDeleteSequence", XmhDeleteSequence},
+
+ /* actions to support easier sequence manipulation */
+
+ {"XmhPushSequence", XmhPushSequence},
+ {"XmhPopSequence", XmhPopSequence},
+
+ /* actions upon the currently viewed message */
+
+ {"XmhCloseView", XmhCloseView},
+ {"XmhViewReply", XmhViewReply},
+ {"XmhViewForward", XmhViewForward},
+ {"XmhViewUseAsComposition", XmhViewUseAsComposition},
+ {"XmhEditView", XmhEditView},
+ {"XmhSaveView", XmhSaveView},
+ {"XmhPrintView", XmhPrintView},
+ {"XmhViewMarkDelete", XmhViewMarkDelete},
+
+ /* actions upon a composition, reply, or forward */
+
+ /* Close button XmhCloseView (see above) */
+ {"XmhResetCompose", XmhResetCompose},
+ /* Compose button XmhComposeMessage (see above) */
+ {"XmhSave", XmhSave},
+ {"XmhSend", XmhSend},
+ {"XmhInsert", XmhInsert},
+
+ /* popup dialog box button action procedures */
+
+ {"XmhPromptOkayAction", XmhPromptOkayAction},
+
+ /* retained for backward compatibility with user resources */
+
+ {"XmhCancelPick", XmhWMProtocols}
+ };
+
+ static Arg shell_args[] = {
+ {XtNinput, (XtArgVal)True},
+ {XtNjoinSession, (XtArgVal)False}, /* join is delayed to end of init */
+ {XtNenvironment, (XtArgVal)NULL}, /* set dynamically below */
+ {XtNmappedWhenManaged, (XtArgVal)False}
+ };
+
+ ptr = strrchr(argv[0], '/');
+ if (ptr) progName = ptr + 1;
+ else progName = argv[0];
+
+ shell_args[2].value = (XtArgVal)environ;
+ toplevel = XtOpenApplication(&app, "Xmh", table, XtNumber(table),
+ &argc, argv, FallbackResources,
+ sessionShellWidgetClass,
+ shell_args, XtNumber(shell_args));
+ if (argc > 1) Syntax(progName);
+
+ XSetIOErrorHandler(_IOErrorHandler);
+
+ theDisplay = XtDisplay(toplevel);
+
+ homeDir = getenv("HOME");
+ homeDir = XtNewString(homeDir);
+
+ XtGetApplicationResources( toplevel, (XtPointer)&app_resources,
+ resources, XtNumber(resources),
+ NULL, (Cardinal)0 );
+
+ if (app_resources.app_defaults_version < MIN_APP_DEFAULTS_VERSION)
+ XtAddEventHandler(toplevel, StructureNotifyMask, False,
+ PopupAppDefaultsWarning, NULL);
+
+ if (app_resources.mail_waiting_flag) app_resources.new_mail_check = True;
+ if (app_resources.mail_interval == -1)
+ app_resources.mail_interval = app_resources.check_frequency;
+ if (app_resources.checkpoint_interval == -1)
+ app_resources.checkpoint_interval = 5 * app_resources.check_frequency;
+ if (app_resources.rescan_interval == -1)
+ app_resources.rescan_interval = 5 * app_resources.check_frequency;
+ ptr = strchr(app_resources.checkpoint_name_format, '%');
+ while (ptr && *(++ptr) != 'd')
+ ptr = strchr(app_resources.checkpoint_name_format, '%');
+ if (!ptr || strlen(app_resources.checkpoint_name_format) == 2)
+ app_resources.checkpoint_name_format = xmhCkpDefault;
+
+ ptr = getenv("MH");
+ if (!ptr) {
+ (void) sprintf(str, "%s/.mh_profile", homeDir);
+ ptr = str;
+ }
+ fid = myfopen(ptr, "r");
+ if (fid) {
+ while ((ptr = ReadLine(fid))) {
+ char *cp;
+
+ (void) strncpy(str2, ptr, 5);
+ str2[5] = '\0';
+ for (cp = str2; *cp; cp++) {
+ if ('A' <= *cp && *cp <= 'Z')
+ *cp = *cp - 'A' + 'a';
+ }
+ if (strcmp(str2, "path:") == 0) {
+ ptr += 5;
+ while (*ptr == ' ' || *ptr == '\t')
+ ptr++;
+ (void) strcpy(str, ptr);
+ }
+ }
+ myfclose(fid);
+ } else {
+ (void) strcpy(str, "Mail");
+ }
+ for (l=strlen(str) - 1; l>=0 && (str[l] == ' ' || str[l] == '\t'); l--)
+ str[l] = 0;
+ if (str[0] == '/')
+ (void) strcpy(str2, str);
+ else
+ (void) sprintf(str2, "%s/%s", homeDir, str);
+
+ (void) sprintf(str, "%s/draft", str2);
+ draftFile = XtNewString(str);
+ (void) sprintf(str, "%s/xmhdraft", str2);
+ xmhDraftFile = XtNewString(str);
+
+ if (app_resources.mail_path == NULL)
+ app_resources.mail_path = XtNewString(str2);
+
+ NullSource = (Widget) NULL;
+
+ l = strlen(app_resources.mh_path) - 1;
+ if (l > 0 && app_resources.mh_path[l] == '/')
+ app_resources.mh_path[l] = 0;
+
+ rootwidth = WidthOfScreen(XtScreen(toplevel));
+ rootheight = HeightOfScreen(XtScreen(toplevel));
+
+ app_resources.toc_geometry =
+ FixUpGeometry(app_resources.toc_geometry,
+ (unsigned)rootwidth / 2, 3 * (unsigned)rootheight / 4);
+ app_resources.view_geometry =
+ FixUpGeometry(app_resources.view_geometry,
+ (unsigned)rootwidth / 2, (unsigned)rootheight / 2);
+ app_resources.comp_geometry =
+ FixUpGeometry(app_resources.comp_geometry,
+ (unsigned)rootwidth / 2, (unsigned)rootheight / 2);
+ app_resources.pick_geometry =
+ FixUpGeometry(app_resources.pick_geometry,
+ (unsigned)rootwidth / 2, (unsigned)rootheight / 2);
+
+ numScrns = 0;
+ scrnList = (Scrn *) NULL;
+ NoMenuForButton = (Widget) &static_variable;
+ LastMenuButtonPressed = (Widget) NULL;
+
+ TocInit();
+ InitPick();
+ BBoxInit();
+
+ XtAppAddActions(app, actions, XtNumber(actions));
+ XtRegisterGrabAction(XmhPopupFolderMenu, True,
+ ButtonPressMask | ButtonReleaseMask,
+ GrabModeAsync, GrabModeAsync);
+ wm_protocols = XInternAtom(XtDisplay(toplevel), "WM_PROTOCOLS", False);
+ protocolList[0] = wm_delete_window =
+ XInternAtom(XtDisplay(toplevel), "WM_DELETE_WINDOW", False);
+ protocolList[1] = wm_save_yourself =
+ XInternAtom(XtDisplay(toplevel), "WM_SAVE_YOURSELF", False);
+
+ XtAddCallback(toplevel, XtNsaveCallback, DoSaveYourself, (XtPointer)NULL);
+ XtAddCallback(toplevel, XtNdieCallback, _Die, (XtPointer)NULL);
+
+ MenuItemBitmap =
+ XCreateBitmapFromData( XtDisplay(toplevel),
+ RootWindowOfScreen( XtScreen(toplevel)),
+ (char *)check_bits, check_width, check_height);
+
+ DEBUG("Making screen ... ")
+
+ scrn = CreateNewScrn(STtocAndView);
+
+ SetCursorColor(scrn->parent, app_resources.cursor,
+ app_resources.pointer_color);
+ if (app_resources.block_events_on_busy)
+ SetCursorColor(scrn->parent, app_resources.busy_cursor,
+ app_resources.busy_pointer_color);
+
+ DEBUG(" setting toc ... ")
+
+ TocSetScrn(InitialFolder, scrn);
+
+ DEBUG("done.\n");
+
+ XtVaSetValues(toplevel, XtNjoinSession, (XtArgVal)True, NULL);
+
+ MapScrn(scrn);
+}
diff --git a/main.c b/main.c
new file mode 100644
index 0000000..a67b82a
--- /dev/null
+++ b/main.c
@@ -0,0 +1,142 @@
+/* $XConsortium: main.c,v 2.30 95/01/25 14:33:57 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/main.c,v 1.3 2002/04/05 21:06:28 dickey Exp $ */
+
+#define MAIN 1 /* Makes global.h actually declare vars */
+#include "xmh.h"
+#include "actions.h"
+
+/*ARGSUSED*/
+static void NeedToCheckScans(
+ XtPointer client_data,
+ XtIntervalId *id) /* unused */
+{
+ int i;
+ if (!subProcessRunning) {
+ DEBUG("[magic toc check ...")
+ for (i = 0; i < numScrns; i++) {
+ if (scrnList[i]->toc)
+ TocRecheckValidity(scrnList[i]->toc);
+ if (scrnList[i]->msg)
+ TocRecheckValidity(MsgGetToc(scrnList[i]->msg));
+ }
+ DEBUG(" done]\n")
+ }
+ (void) XtAppAddTimeOut((XtAppContext)client_data,
+ (unsigned long) app_resources.rescan_interval,
+ NeedToCheckScans, client_data);
+}
+
+/*ARGSUSED*/
+static void Checkpoint(
+ XtPointer client_data,
+ XtIntervalId *id) /* unused */
+{
+ if (!subProcessRunning) {
+ Cardinal n = 1;
+ String params = "wm_save_yourself";
+ DEBUG("(Checkpointing...")
+ XmhWMProtocols(NULL, NULL, &params, &n);
+ DEBUG(" done)\n")
+ }
+ (void) XtAppAddTimeOut((XtAppContext)client_data,
+ (unsigned long) app_resources.checkpoint_interval,
+ Checkpoint, client_data);
+}
+
+/*ARGSUSED*/
+static void CheckMail(
+ XtPointer client_data,
+ XtIntervalId *id) /* unused */
+{
+ if (!subProcessRunning) {
+ DEBUG("(Checking for new mail...")
+ XmhCheckForNewMail(NULL, NULL, NULL, NULL);
+ DEBUG(" done)\n")
+ }
+ (void) XtAppAddTimeOut((XtAppContext)client_data,
+ (unsigned long) app_resources.mail_interval,
+ CheckMail, client_data);
+}
+
+/* Main loop. */
+
+#ifdef DEBUG_CLEANUP
+Boolean ExitLoop = FALSE;
+#endif
+
+int main(int argc, char **argv)
+{
+ XtAppContext appCtx;
+
+ InitializeWorld(argc, argv);
+ subProcessRunning = False;
+ appCtx = XtWidgetToApplicationContext(toplevel);
+ (void) XtAppSetWarningMsgHandler(appCtx, PopupWarningHandler);
+
+ if (app_resources.new_mail_check && app_resources.mail_interval > 0) {
+ app_resources.mail_interval *= 60000;
+ (void) XtAppAddTimeOut(appCtx, (unsigned long) 0,
+ CheckMail, (XtPointer)appCtx);
+ }
+ if (app_resources.rescan_interval > 0) {
+ app_resources.rescan_interval *= 60000;
+ (void) XtAppAddTimeOut(appCtx,
+ (unsigned long) app_resources.rescan_interval,
+ NeedToCheckScans, (XtPointer)appCtx);
+ }
+ if (app_resources.make_checkpoints &&
+ app_resources.checkpoint_interval > 0) {
+ app_resources.checkpoint_interval *= 60000;
+ (void) XtAppAddTimeOut(appCtx, (unsigned long)
+ app_resources.checkpoint_interval,
+ Checkpoint, (XtPointer)appCtx);
+ }
+
+ lastInput.win = -1; /* nothing mapped yet */
+#ifdef DEBUG_CLEANUP
+ while (!ExitLoop) {
+#else
+ for (;;) {
+#endif
+ XEvent ev;
+ XtAppNextEvent( appCtx, &ev );
+ if (ev.type == KeyPress) {
+ lastInput.win = ev.xany.window;
+ lastInput.x = ev.xkey.x_root;
+ lastInput.y = ev.xkey.y_root;
+ } else if (ev.type == ButtonPress) {
+ lastInput.win = ev.xany.window;
+ lastInput.x = ev.xbutton.x_root;
+ lastInput.y = ev.xbutton.y_root;
+ }
+ XtDispatchEvent( &ev );
+ }
+#ifdef DEBUG_CLEANUP
+ XtDestroyApplicationContext(appCtx);
+#endif
+ exit(0);
+}
diff --git a/menu.c b/menu.c
new file mode 100644
index 0000000..6372094
--- /dev/null
+++ b/menu.c
@@ -0,0 +1,84 @@
+/*
+ * $XConsortium: menu.c,v 1.7 94/04/17 20:24:02 kaleb Exp $
+ *
+Copyright (c) 1989 X Consortium
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the X Consortium shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from the X Consortium.
+ *
+ */
+/* $XFree86: xc/programs/xmh/menu.c,v 1.3 2001/10/28 03:34:38 tsi Exp $ */
+
+#include "xmh.h"
+#include "bboxint.h"
+
+
+void AttachMenuToButton(button, menu, menu_name)
+ Button button;
+ Widget menu;
+ char * menu_name;
+{
+ Arg args[3];
+
+ if (button == NULL) return;
+ button->menu = menu;
+ /* Yup, this is a memory leak. :-) */
+ XtSetArg(args[0], XtNmenuName, XtNewString(menu_name));
+ XtSetValues(button->widget, args, (Cardinal) 1);
+}
+
+
+/*ARGSUSED*/
+void DoRememberMenuSelection(widget, client_data, call_data)
+ Widget widget; /* menu entry object */
+ XtPointer client_data;
+ XtPointer call_data;
+{
+ static Arg args[] = {
+ { XtNpopupOnEntry, (XtArgVal) NULL },
+ };
+ args[0].value = (XtArgVal) widget;
+ XtSetValues(XtParent(widget), args, XtNumber(args));
+}
+
+
+void SendMenuEntryEnableMsg(button, entry_name, value)
+ Button button;
+ char * entry_name;
+ int value;
+{
+ Widget entry;
+ static Arg args[] = {{XtNsensitive, (XtArgVal) NULL}};
+
+ if ((entry = XtNameToWidget(button->menu, entry_name)) != NULL) {
+ args[0].value = (XtArgVal) ((value == 0) ? False : True);
+ XtSetValues(entry, args, (Cardinal) 1);
+ }
+}
+
+
+void ToggleMenuItem(Widget entry, Boolean state)
+{
+ Arg args[1];
+
+ XtSetArg(args[0], XtNleftBitmap, (state ? MenuItemBitmap : None));
+ XtSetValues(entry, args, (Cardinal) 1);
+}
diff --git a/miscfuncs.c b/miscfuncs.c
new file mode 100644
index 0000000..fef77dd
--- /dev/null
+++ b/miscfuncs.c
@@ -0,0 +1,169 @@
+/* $XConsortium: miscfuncs.c,v 1.7 94/12/01 17:15:05 kaleb Exp $ */
+/* $XFree86: xc/programs/xmh/miscfuncs.c,v 3.7 2002/04/05 21:06:28 dickey Exp $ */
+
+#include "xmh.h"
+
+#include <X11/Xos.h>
+
+#ifndef X_NOT_POSIX
+#include <dirent.h>
+#else
+#ifdef SYSV
+#include <dirent.h>
+#else
+#ifdef USG
+#include <dirent.h>
+#else
+#include <sys/dir.h>
+#ifndef dirent
+#define dirent direct
+#endif
+#endif
+#endif
+#endif
+
+#include <stdlib.h>
+
+#if defined(SYSV) && (defined(i386) || defined(MOTOROLA))
+
+/* These systems don't have the ftruncate() system call, so we emulate it.
+ * This emulation can only shorten, not lengthen.
+ * For convenience, we pass in the name of the file, even though the
+ * real ftruncate doesn't.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+
+#define CHUNKSIZE 1024
+
+int ftruncate_emu(
+ int fd,
+ off_t length,
+ char *name)
+{
+ char tmp_file[15];
+ int new_fid, bytes_left, i;
+ unsigned char buffer[CHUNKSIZE];
+ struct stat stat_val;
+
+ /* Open a temp file. */
+ sprintf(tmp_file, ".xmhtmp%d~", getpid());
+ (void) unlink(tmp_file);
+ new_fid = open(tmp_file, O_RDWR | O_CREAT);
+ lseek(fd, (off_t)0, 0);
+
+ /* Copy original file to temp file. */
+ for (i = 0; i < length / CHUNKSIZE; i++) {
+ if (read(fd, buffer, CHUNKSIZE) != CHUNKSIZE) {
+ (void)fprintf(stderr, "xmh: read error in ftruncate emulation\n");
+ return -1;
+ }
+ else if (write(new_fid, buffer, CHUNKSIZE) != CHUNKSIZE) {
+ (void)fprintf(stderr, "xmh: write error in ftruncate emulation\n");
+ return -1;
+ }
+ }
+ bytes_left = length % CHUNKSIZE;
+ if (read(fd, buffer, bytes_left) != bytes_left) {
+ (void)fprintf(stderr, "xmh: read error in ftruncate() emulation\n");
+ return -1;
+ }
+ else if (write(new_fid, buffer, bytes_left) != bytes_left) {
+ (void)fprintf(stderr, "xmh: write error in ftruncate() emulation\n");
+ return -1;
+ }
+
+ /* Set mode of temp file to that of original file. */
+ (void) fstat(fd, &stat_val);
+ (void) chmod(tmp_file, stat_val.st_mode);
+
+ /* Close files, delete original, rename temp file to original. */
+ myclose(new_fid);
+ myclose(fd);
+ (void) unlink(name); /* remove original */
+ (void) rename(tmp_file, name); /* rename temp file */
+
+ /* If we weren't going to close the file right away in the one
+ place this is called from, we'd have to do something like this:
+ new_fid = myopen(name, O_RDWR, 0666);
+ if (new_fid != fd) {
+ dup2(new_fid, fd);
+ close(new_fid);
+ }
+ but the file does get closed, so we don't bother. */
+
+ return 0;
+}
+#endif /* SYSV variant that needs ftruncate emulation */
+
+
+/*
+** This code is by Rich Salz (rsalz@bbn.com), and ported to SVR4
+** by David Elliott (dce@smsc.sony.com). No copyrights were found
+** in the original. Subsequently modified by Bob Scheifler.
+*/
+
+/* A convenient shorthand. */
+typedef struct dirent ENTRY;
+
+/* Initial guess at directory size. */
+#define INITIAL_SIZE 20
+
+static int StrCmp(char **a, char **b)
+{
+ return strcmp(*a, *b);
+}
+
+int
+ScanDir(
+ char *Name,
+ char ***List,
+ int (*Selector)(char *))
+{
+ register char **names;
+ register ENTRY *E;
+ register DIR *Dp;
+ register int i;
+ register int size;
+
+ /* Get initial list space and open directory. */
+ size = INITIAL_SIZE;
+ if (!(names = (char **)malloc(size * sizeof(char *))) ||
+ !(Dp = opendir(Name)))
+ return(-1);
+
+ /* Read entries in the directory. */
+ for (i = 0; (E = readdir(Dp)); )
+ if (!Selector || (*Selector)(E->d_name)) {
+ /* User wants them all, or he wants this one. */
+ if (++i >= size) {
+ size <<= 1;
+ names = (char**)realloc((char *)names, size * sizeof(char*));
+ if (!names) {
+ closedir(Dp);
+ return(-1);
+ }
+ }
+
+ /* Copy the entry. */
+ if (!(names[i - 1] = (char *)malloc(strlen(E->d_name) + 1))) {
+ closedir(Dp);
+ return(-1);
+ }
+ (void)strcpy(names[i - 1], E->d_name);
+ }
+
+ /* Close things off. */
+ names[i] = (char *)0;
+ *List = names;
+ closedir(Dp);
+
+ /* Sort? */
+ if (i)
+ qsort((char *)names, i, sizeof(char *), (int (*)())StrCmp);
+
+ return(i);
+}
diff --git a/mlist.c b/mlist.c
new file mode 100644
index 0000000..66ef29a
--- /dev/null
+++ b/mlist.c
@@ -0,0 +1,127 @@
+/* $XConsortium: mlist.c,v 2.10 91/01/06 21:08:51 rws Exp $" */
+
+/*
+ * COPYRIGHT 1987
+ * 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/mlist.c,v 1.3 2002/07/01 02:26:05 tsi Exp $ */
+
+/* mlist.c -- functions to deal with message lists. */
+
+#include "xmh.h"
+
+
+/* Create a message list containing no messages. */
+
+MsgList MakeNullMsgList(void)
+{
+ MsgList mlist;
+ mlist = XtNew(MsgListRec);
+ mlist->nummsgs = 0;
+ mlist->msglist = XtNew(Msg);
+ mlist->msglist[0] = NULL;
+ return mlist;
+}
+
+
+/* Append a message to the given message list. */
+
+void AppendMsgList(MsgList mlist, Msg msg)
+{
+ mlist->nummsgs++;
+ mlist->msglist =
+ (Msg *) XtRealloc((char *) mlist->msglist,
+ (unsigned) (mlist->nummsgs + 1) * sizeof(Msg));
+ mlist->msglist[mlist->nummsgs - 1] = msg;
+ mlist->msglist[mlist->nummsgs] = 0;
+}
+
+
+
+/* Delete a message from a message list. */
+
+void DeleteMsgFromMsgList(MsgList mlist, Msg msg)
+{
+ int i;
+ for (i=0 ; i<mlist->nummsgs ; i++) {
+ if (mlist->msglist[i] == msg) {
+ mlist->nummsgs--;
+ for (; i<mlist->nummsgs ; i++)
+ mlist->msglist[i] = mlist->msglist[i+1];
+ return;
+ }
+ }
+}
+
+
+
+/* Create a new messages list containing only the one given message. */
+
+MsgList MakeSingleMsgList(Msg msg)
+{
+ MsgList result;
+ result = MakeNullMsgList();
+ AppendMsgList(result, msg);
+ return result;
+}
+
+
+/* We're done with this message list; free it's storage. */
+
+void FreeMsgList(MsgList mlist)
+{
+ XtFree((char *) mlist->msglist);
+ XtFree((char *) mlist);
+}
+
+
+
+/* Parse the given string into a message list. The string contains mh-style
+ message numbers. This routine assumes those messages numbers refer to
+ messages in the given toc. */
+
+MsgList StringToMsgList(Toc toc, char *str)
+{
+ MsgList mlist;
+ char *ptr;
+ int first, second, i;
+ Msg msg;
+ mlist = MakeNullMsgList();
+ while (*str) {
+ while (*str == ' ')
+ str++;
+ first = second = atoi(str);
+ str++;
+ for (ptr = str; *ptr >= '0' && *ptr <= '9'; ptr++) ;
+ if (*ptr == '-')
+ second = atoi(ptr + 1);
+ if (first > 0) {
+ for (i = first; i <= second; i++) {
+ msg = TocMsgFromId(toc, i);
+ if (msg) AppendMsgList(mlist, msg);
+ }
+ }
+ str = ptr;
+ }
+ return mlist;
+}
diff --git a/mlist.h b/mlist.h
new file mode 100644
index 0000000..085dd4d
--- /dev/null
+++ b/mlist.h
@@ -0,0 +1,38 @@
+/* $XConsortium: mlist.h,v 2.4 89/05/04 15:11:31 converse Exp $ */
+/*
+ * COPYRIGHT 1987
+ * 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/mlist.h,v 1.3 2002/07/01 02:26:05 tsi Exp $ */
+
+#ifndef _mlist_h
+#define _mlist_h
+
+extern MsgList MakeNullMsgList(void);
+extern void AppendMsgList(MsgList, Msg);
+extern void DeleteMsgFromMsgList(MsgList, Msg);
+extern MsgList MakeSingleMsgList(Msg);
+extern void FreeMsgList(MsgList);
+extern MsgList StringToMsgList(Toc, char *);
+
+#endif /* _mlist_h */
diff --git a/msg.c b/msg.c
new file mode 100644
index 0000000..ee03a74
--- /dev/null
+++ b/msg.c
@@ -0,0 +1,947 @@
+/*
+ * $XConsortium: msg.c /main/2 1996/01/14 16:51:45 kaleb $
+ *
+ *
+ * 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/msg.c,v 1.4 2002/04/05 21:06:28 dickey Exp $ */
+
+/* msgs.c -- handle operations on messages. */
+
+#include <X11/Xaw/Cardinals.h>
+
+#include "xmh.h"
+#include "tocintrnl.h"
+#include "actions.h"
+
+static int SetScrn(Msg, Scrn, Boolean, XtCallbackList, XtCallbackList);
+
+/* Function Name: SetEditable
+ * Description: Sets the editable flag for this message.
+ * Arguments: msg - the message.
+ * edit - set editable to this.
+ * Returns: none
+ */
+
+static void
+SetEditable(Msg msg, Boolean edit)
+{
+ Arg args[1];
+
+ if (edit)
+ XtSetArg(args[0], XtNeditType, XawtextEdit);
+ else
+ XtSetArg(args[0], XtNeditType, XawtextRead);
+
+ XtSetValues(msg->source, args, ONE);
+}
+
+/* Function Name: IsEditable
+ * Description: Returns true if this is an editable message.
+ * Arguments: msg - the message to edit.
+ * Returns: TRUE if editable.
+ */
+
+static Boolean
+IsEditable(Msg msg)
+{
+ Arg args[1];
+ XawTextEditType type;
+
+ XtSetArg(args[0], XtNeditType, &type);
+ XtGetValues(msg->source, args, ONE);
+
+ return(type == XawtextEdit);
+}
+
+/* Return the user-viewable name of the given message. */
+
+char *MsgName(Msg msg)
+{
+ static char result[100];
+ (void) sprintf(result, "%s:%d", msg->toc->foldername, msg->msgid);
+ return result;
+}
+
+
+/* Update the message titlebar in the given scrn. */
+
+static void ResetMsgLabel(Scrn scrn)
+{
+ Msg msg;
+ char str[200];
+ if (scrn) {
+ msg = scrn->msg;
+ if (msg == NULL) (void) strcpy(str, app_resources.banner);
+ else {
+ (void) strcpy(str, MsgName(msg));
+ switch (msg->fate) {
+ case Fdelete:
+ (void) strcat(str, " -> *Delete*");
+ break;
+ case Fcopy:
+ case Fmove:
+ (void) strcat(str, " -> ");
+ (void) strcat(str, msg->desttoc->foldername);
+ if (msg->fate == Fcopy)
+ (void) strcat(str, " (Copy)");
+ break;
+ }
+ if (msg->temporary) (void)strcat(str, " [Temporary]");
+ }
+ ChangeLabel((Widget) scrn->viewlabel, str);
+ }
+}
+
+
+/* A major msg change has occured; redisplay it. (This also should
+work even if we now have a new source to display stuff from.) This
+routine arranges to hide boring headers, and also will set the text
+insertion point to the proper place if this is a composition and we're
+viewing it for the first time. */
+
+static void RedisplayMsg(Scrn scrn)
+{
+ Msg msg;
+ XawTextPosition startPos, lastPos, nextPos;
+ int length; char str[100];
+ XawTextBlock block;
+ if (scrn) {
+ msg = scrn->msg;
+ if (msg) {
+ startPos = 0;
+ if (app_resources.hide_boring_headers && scrn->kind != STcomp) {
+ lastPos = XawTextSourceScan(msg->source, (XawTextPosition) 0,
+ XawstAll, XawsdRight, 1, FALSE);
+ while (startPos < lastPos) {
+ nextPos = startPos;
+ length = 0;
+ while (length < 8 && nextPos < lastPos) {
+ nextPos = XawTextSourceRead(msg->source, nextPos,
+ &block, 8 - length);
+ (void) strncpy(str + length, block.ptr, block.length);
+ length += block.length;
+ }
+ if (length == 8) {
+ if (strncmp(str, "From:", 5) == 0 ||
+ strncmp(str, "To:", 3) == 0 ||
+ strncmp(str, "Date:", 5) == 0 ||
+ strncmp(str, "Subject:", 8) == 0) break;
+ }
+ startPos = XawTextSourceScan(msg->source, startPos,
+ XawstEOL, XawsdRight, 1, TRUE);
+ }
+ if (startPos >= lastPos) startPos = 0;
+ }
+ XawTextSetSource(scrn->viewwidget, msg->source, startPos);
+ if (msg->startPos > 0) {
+ XawTextSetInsertionPoint(scrn->viewwidget, msg->startPos);
+ msg->startPos = 0; /* Start in magic place only once. */
+ }
+ } else {
+ XawTextSetSource(scrn->viewwidget, PNullSource,
+ (XawTextPosition)0);
+ }
+ }
+}
+
+
+
+static char tempDraftFile[100] = "";
+
+/* Temporarily move the draftfile somewhere else, so we can exec an mh
+ command that affects it. */
+
+static void TempMoveDraft(void)
+{
+ char *ptr;
+ if (FileExists(draftFile)) {
+ do {
+ ptr = MakeNewTempFileName();
+ (void) strcpy(tempDraftFile, draftFile);
+ (void) strcpy(strrchr(tempDraftFile, '/'), strrchr(ptr, '/'));
+ } while (FileExists(tempDraftFile));
+ RenameAndCheck(draftFile, tempDraftFile);
+ }
+}
+
+
+
+/* Restore the draftfile from its temporary hiding place. */
+
+static void RestoreDraft(void)
+{
+ if (*tempDraftFile) {
+ RenameAndCheck(tempDraftFile, draftFile);
+ *tempDraftFile = 0;
+ }
+}
+
+
+
+/* Public routines */
+
+
+/* Given a message, return the corresponding filename. */
+
+char *MsgFileName(Msg msg)
+{
+ static char result[500];
+ (void) sprintf(result, "%s/%d", msg->toc->path, msg->msgid);
+ return result;
+}
+
+
+
+/* Save any changes to a message. Also calls the toc routine to update the
+ scanline for this msg. Returns True if saved, false otherwise. */
+
+int MsgSaveChanges(Msg msg)
+{
+ int i;
+ Window w;
+ if (msg->source) {
+ if (XawAsciiSave(msg->source)) {
+ for (i=0; i < (int) msg->num_scrns; i++)
+ EnableProperButtons(msg->scrn[i]);
+ if (!msg->temporary)
+ TocMsgChanged(msg->toc, msg);
+ return True;
+ }
+ else {
+ char str[256];
+ (void) sprintf(str, "Cannot save changes to \"%s/%d\"!",
+ msg->toc->foldername, msg->msgid);
+ PopupError((Widget)NULL, str);
+ return False;
+ }
+ }
+ w= (msg->source?XtWindow(msg->source):None);
+ Feep(XkbBI_Failure,0,w);
+ return False;
+}
+
+
+/*
+ * Show the given message in the given scrn. If a message is changed, and we
+ * are removing it from any scrn, then ask for confirmation first. If the
+ * scrn was showing a temporary msg that is not being shown in any other scrn,
+ * it is deleted. If scrn is NULL, then remove the message from every scrn
+ * that's showing it.
+ */
+
+
+/*ARGSUSED*/
+static void ConfirmedNoScrn(
+ Widget widget, /* unused */
+ XtPointer client_data,
+ XtPointer call_data) /* unused */
+{
+ Msg msg = (Msg) client_data;
+ register int i;
+
+ for (i=msg->num_scrns - 1 ; i >= 0 ; i--)
+ SetScrn((Msg)NULL, msg->scrn[i], TRUE, (XtCallbackList) NULL,
+ (XtCallbackList) NULL);
+}
+
+
+static void RemoveMsgConfirmed(Scrn scrn)
+{
+ if (scrn->kind == STtocAndView && MsgChanged(scrn->msg)) {
+ Arg args[1];
+ XtSetArg(args[0], XtNtranslations, scrn->read_translations);
+ XtSetValues(scrn->viewwidget, args, (Cardinal) 1);
+ }
+ scrn->msg->scrn[0] = NULL;
+ scrn->msg->num_scrns = 0;
+ XawTextSetSource(scrn->viewwidget, PNullSource, (XawTextPosition) 0);
+ XtDestroyWidget(scrn->msg->source);
+ scrn->msg->source = NULL;
+ if (scrn->msg->temporary) {
+ (void) unlink(MsgFileName(scrn->msg));
+ TocRemoveMsg(scrn->msg->toc, scrn->msg);
+ MsgFree(scrn->msg);
+ }
+}
+
+
+static void SetScrnNewMsg(
+ Msg msg,
+ Scrn scrn)
+{
+ scrn->msg = msg;
+ if (msg == NULL) {
+ XawTextSetSource(scrn->viewwidget, PNullSource, (XawTextPosition) 0);
+ ResetMsgLabel(scrn);
+ EnableProperButtons(scrn);
+ if (scrn->kind != STtocAndView && scrn->kind != STcomp) {
+ StoreWindowName(scrn, progName);
+ DestroyScrn(scrn);
+ }
+ } else {
+ msg->num_scrns++;
+ msg->scrn = (Scrn *) XtRealloc((char *)msg->scrn,
+ (unsigned) sizeof(Scrn)*msg->num_scrns);
+ msg->scrn[msg->num_scrns - 1] = scrn;
+ if (msg->source == NULL)
+ msg->source = CreateFileSource(scrn->viewwidget, MsgFileName(msg),
+ scrn->kind == STcomp);
+ ResetMsgLabel(scrn);
+ RedisplayMsg(scrn);
+ EnableProperButtons(scrn);
+ if (scrn->kind != STtocAndView)
+ StoreWindowName(scrn, MsgName(msg));
+ }
+}
+
+typedef struct _MsgAndScrn {
+ Msg msg;
+ Scrn scrn;
+} MsgAndScrnRec, *MsgAndScrn;
+
+/*ARGSUSED*/
+static void ConfirmedWithScrn(
+ Widget widget, /* unused */
+ XtPointer client_data,
+ XtPointer call_data) /* unused */
+{
+ MsgAndScrn mas = (MsgAndScrn) client_data;
+ RemoveMsgConfirmed(mas->scrn);
+ SetScrnNewMsg(mas->msg, mas->scrn);
+ XtFree((char *) mas);
+}
+
+
+static int SetScrn(
+ Msg msg,
+ Scrn scrn,
+ Boolean force, /* if true, force msg set scrn */
+ XtCallbackList confirms, /* callbacks upon confirmation */
+ XtCallbackList cancels) /* callbacks upon cancellation */
+{
+ register int i, num_scrns;
+ static XtCallbackRec yes_callbacks[] = {
+ {(XtCallbackProc) NULL, (XtPointer) NULL},
+ {(XtCallbackProc) NULL, (XtPointer) NULL},
+ {(XtCallbackProc) NULL, (XtPointer) NULL}
+ };
+
+ if (scrn == NULL) {
+ if (msg == NULL || msg->num_scrns == 0) return 0;
+ if (!force && XawAsciiSourceChanged(msg->source)) {
+ char str[100];
+ (void) sprintf(str,
+ "Are you sure you want to remove changes to %s?",
+ MsgName(msg));
+
+ yes_callbacks[0].callback = ConfirmedNoScrn;
+ yes_callbacks[0].closure = (XtPointer) msg;
+ yes_callbacks[1].callback = confirms[0].callback;
+ yes_callbacks[1].closure = confirms[0].closure;
+
+ PopupConfirm((Widget) NULL, str, yes_callbacks, cancels);
+ return NEEDS_CONFIRMATION;
+ }
+ ConfirmedNoScrn((Widget)NULL, (XtPointer) msg, (XtPointer) NULL);
+ return 0;
+ }
+
+ if (scrn->msg == msg) return 0;
+
+ if (scrn->msg) {
+ num_scrns = scrn->msg->num_scrns;
+ for (i=0 ; i<num_scrns ; i++)
+ if (scrn->msg->scrn[i] == scrn) break;
+ if (i >= num_scrns) Punt("Couldn't find scrn in SetScrn!");
+ if (num_scrns > 1)
+ scrn->msg->scrn[i] = scrn->msg->scrn[--(scrn->msg->num_scrns)];
+ else {
+ if (!force && XawAsciiSourceChanged(scrn->msg->source)) {
+ char str[100];
+ MsgAndScrn cb_data;
+
+ cb_data = XtNew(MsgAndScrnRec);
+ cb_data->msg = msg;
+ cb_data->scrn = scrn;
+ (void)sprintf(str,
+ "Are you sure you want to remove changes to %s?",
+ MsgName(scrn->msg));
+ yes_callbacks[0].callback = ConfirmedWithScrn;
+ yes_callbacks[0].closure = (XtPointer) cb_data;
+ yes_callbacks[1].callback = confirms[0].callback;
+ yes_callbacks[1].closure = confirms[0].closure;
+ PopupConfirm(scrn->viewwidget, str, yes_callbacks, cancels);
+ return NEEDS_CONFIRMATION;
+ }
+ RemoveMsgConfirmed(scrn);
+ }
+ }
+ SetScrnNewMsg(msg, scrn);
+ return 0;
+}
+
+
+
+/* Associate the given msg and scrn, asking for confirmation if necessary. */
+
+int MsgSetScrn(
+ Msg msg,
+ Scrn scrn,
+ XtCallbackList confirms,
+ XtCallbackList cancels)
+{
+ return SetScrn(msg, scrn, FALSE, confirms, cancels);
+}
+
+
+/* Same as above, but with the extra information that the message is actually
+ a composition. (Nothing currently takes advantage of that extra fact.) */
+
+void MsgSetScrnForComp(Msg msg, Scrn scrn)
+{
+ (void) SetScrn(msg, scrn, FALSE, (XtCallbackList) NULL,
+ (XtCallbackList) NULL);
+}
+
+
+/* Associate the given msg and scrn, even if it means losing some unsaved
+ changes. */
+
+void MsgSetScrnForce(Msg msg, Scrn scrn)
+{
+ (void) SetScrn(msg, scrn, TRUE, (XtCallbackList) NULL,
+ (XtCallbackList) NULL);
+}
+
+
+
+/* Set the fate of the given message. */
+
+void MsgSetFate(Msg msg, FateType fate, Toc desttoc)
+{
+ Toc toc = msg->toc;
+ XawTextBlock block;
+ int i;
+ msg->fate = fate;
+ msg->desttoc = desttoc;
+ if (fate == Fignore && msg == msg->toc->curmsg)
+ block.ptr = "+";
+ else {
+ switch (fate) {
+ case Fignore: block.ptr = " "; break;
+ case Fcopy: block.ptr = "C"; break;
+ case Fmove: block.ptr = "^"; break;
+ case Fdelete: block.ptr = "D"; break;
+ }
+ }
+ block.firstPos = 0;
+ block.format = FMT8BIT;
+ block.length = 1;
+ if (toc->stopupdate)
+ toc->needsrepaint = TRUE;
+ if (toc->num_scrns && msg->visible && !toc->needsrepaint &&
+ *block.ptr != msg->buf[MARKPOS])
+ (void)XawTextReplace(msg->toc->scrn[0]->tocwidget, /*%%%SourceReplace*/
+ msg->position + MARKPOS,
+ msg->position + MARKPOS + 1, &block);
+ else
+ msg->buf[MARKPOS] = *block.ptr;
+ for (i=0; i < (int) msg->num_scrns; i++)
+ ResetMsgLabel(msg->scrn[i]);
+}
+
+
+
+/* Get the fate of this message. */
+
+FateType MsgGetFate(Msg msg, Toc *toc)
+{
+ if (toc) *toc = msg->desttoc;
+ return msg->fate;
+}
+
+
+/* Make this a temporary message. */
+
+void MsgSetTemporary(Msg msg)
+{
+ int i;
+ msg->temporary = TRUE;
+ for (i=0; i < (int) msg->num_scrns; i++)
+ ResetMsgLabel(msg->scrn[i]);
+}
+
+
+/* Make this a permanent message. */
+
+void MsgSetPermanent(Msg msg)
+{
+ int i;
+ msg->temporary = FALSE;
+ for (i=0; i < (int) msg->num_scrns; i++)
+ ResetMsgLabel(msg->scrn[i]);
+}
+
+
+
+/* Return the id# of this message. */
+
+int MsgGetId(Msg msg)
+{
+ return msg->msgid;
+}
+
+
+/* Return the scanline for this message. */
+
+char *MsgGetScanLine(Msg msg)
+{
+ return msg->buf;
+}
+
+
+
+/* Return the toc this message is in. */
+
+Toc MsgGetToc(Msg msg)
+{
+ return msg->toc;
+}
+
+
+/* Set the reapable flag for this msg. */
+
+void MsgSetReapable(Msg msg)
+{
+ int i;
+ msg->reapable = TRUE;
+ for (i=0; i < (int) msg->num_scrns; i++)
+ EnableProperButtons(msg->scrn[i]);
+}
+
+
+
+/* Clear the reapable flag for this msg. */
+
+void MsgClearReapable(Msg msg)
+{
+ int i;
+ msg->reapable = FALSE;
+ for (i=0; i < (int) msg->num_scrns; i++)
+ EnableProperButtons(msg->scrn[i]);
+}
+
+
+/* Get the reapable value for this msg. Returns TRUE iff the reapable flag
+ is set AND no changes have been made. */
+
+int MsgGetReapable(Msg msg)
+{
+ return msg == NULL || (msg->reapable &&
+ (msg->source == NULL ||
+ !XawAsciiSourceChanged(msg->source)));
+}
+
+
+/* Make it possible to edit the given msg. */
+void MsgSetEditable(Msg msg)
+{
+ int i;
+ if (msg && msg->source) {
+ SetEditable(msg, TRUE);
+ for (i=0; i < (int) msg->num_scrns; i++)
+ EnableProperButtons(msg->scrn[i]);
+ }
+}
+
+
+
+/* Turn off editing for the given msg. */
+
+void MsgClearEditable(Msg msg)
+{
+ int i;
+ if (msg && msg->source) {
+ SetEditable(msg, FALSE);
+ for (i=0; i < (int) msg->num_scrns; i++)
+ EnableProperButtons(msg->scrn[i]);
+ }
+}
+
+
+
+/* Get whether the msg is editable. */
+
+int MsgGetEditable(Msg msg)
+{
+ return msg && msg->source && IsEditable(msg);
+}
+
+
+/* Get whether the msg has changed since last saved. */
+
+int MsgChanged(Msg msg)
+{
+ return msg && msg->source && XawAsciiSourceChanged(msg->source);
+}
+
+/* Call the given function when the msg changes. */
+
+void
+MsgSetCallOnChange(Msg msg, void (*func)(XMH_CB_ARGS), XtPointer param)
+{
+ Arg args[1];
+ static XtCallbackRec cb[] = { {NULL, NULL}, {NULL, NULL} };
+
+ if (func != NULL) {
+ cb[0].callback = func;
+ cb[0].closure = param;
+ XtSetArg(args[0], XtNcallback, cb);
+ }
+ else
+ XtSetArg(args[0], XtNcallback, NULL);
+
+ XtSetValues(msg->source, args, (Cardinal) 1);
+
+}
+
+/* Send (i.e., mail) the given message as is. First break it up into lines,
+ and copy it to a new file in the process. The new file is one of 10
+ possible draft files; we rotate amoung the 10 so that the user can have up
+ to 10 messages being sent at once. (Using a file in /tmp is a bad idea
+ because these files never actually get deleted, but renamed with some
+ prefix. Also, these should stay in an area private to the user for
+ security.) */
+
+void MsgSend(Msg msg)
+{
+ FILEPTR from;
+ FILEPTR to;
+ int p, c, l, inheader, sendwidth, sendbreakwidth;
+ char *ptr, *ptr2, **argv, str[100];
+ static int sendcount = -1;
+ (void) MsgSaveChanges(msg);
+ from = FOpenAndCheck(MsgFileName(msg), "r");
+ sendcount = (sendcount + 1) % 10;
+ (void) sprintf(str, "%s%d", xmhDraftFile, sendcount);
+ to = FOpenAndCheck(str, "w");
+ sendwidth = app_resources.send_line_width;
+ sendbreakwidth = app_resources.break_send_line_width;
+ inheader = TRUE;
+ while ((ptr = ReadLine(from))) {
+ if (inheader) {
+ if (strncmpIgnoringCase(ptr, "sendwidth:", 10) == 0) {
+ if (atoi(ptr+10) > 0) sendwidth = atoi(ptr+10);
+ continue;
+ }
+ if (strncmpIgnoringCase(ptr, "sendbreakwidth:", 15) == 0) {
+ if (atoi(ptr+15) > 0) sendbreakwidth = atoi(ptr+15);
+ continue;
+ }
+ for (l = 0, ptr2 = ptr ; *ptr2 && !l ; ptr2++)
+ l = (*ptr2 != ' ' && *ptr2 != '\t' && *ptr != '-');
+ if (l) {
+ (void) fprintf(to, "%s\n", ptr);
+ continue;
+ }
+ inheader = FALSE;
+ if (sendbreakwidth < sendwidth) sendbreakwidth = sendwidth;
+ }
+ do {
+ for (p = c = l = 0, ptr2 = ptr;
+ *ptr2 && c < sendbreakwidth;
+ p++, ptr2++) {
+ if (*ptr2 == ' ' && c < sendwidth)
+ l = p;
+ if (*ptr2 == '\t') {
+ if (c < sendwidth) l = p;
+ c += 8 - (c % 8);
+ }
+ else
+ c++;
+ }
+ if (c < sendbreakwidth) {
+ (void) fprintf(to, "%s\n", ptr);
+ *ptr = 0;
+ }
+ else
+ if (l) {
+ ptr[l] = 0;
+ (void) fprintf(to, "%s\n", ptr);
+ ptr += l + 1;
+ }
+ else {
+ for (c = 0; c < sendwidth; ) {
+ if (*ptr == '\t') c += 8 - (c % 8);
+ else c++;
+ (void) fputc(*ptr++, to);
+ }
+ (void) fputc('\n', to);
+ }
+ } while (*ptr);
+ }
+ myfclose(from);
+ myfclose(to);
+ argv = MakeArgv(3);
+ argv[0] = "send";
+ argv[1] = "-push";
+ argv[2] = str;
+ DoCommand(argv, (char *) NULL, (char *) NULL);
+ XtFree((char *) argv);
+}
+
+
+/* Make the msg into the form for a generic composition. Set msg->startPos
+ so that the text insertion point will be placed at the end of the first
+ line (which is usually the "To:" field). */
+
+void MsgLoadComposition(Msg msg)
+{
+ static char *blankcomp = NULL; /* Array containing comp template */
+ static int compsize = 0;
+ static XawTextPosition startPos;
+ char *file, **argv;
+ int fid;
+ if (blankcomp == NULL) {
+ file = MakeNewTempFileName();
+ argv = MakeArgv(5);
+ argv[0] = "comp";
+ argv[1] = "-file";
+ argv[2] = file;
+ argv[3] = "-nowhatnowproc";
+ argv[4] = "-nodraftfolder";
+ DoCommand(argv, (char *) NULL, (char *) NULL);
+ XtFree((char *) argv);
+ compsize = GetFileLength(file);
+ if (compsize > 0) {
+ blankcomp = XtMalloc((Cardinal) compsize);
+ fid = myopen(file, O_RDONLY, 0666);
+ if (compsize != read(fid, blankcomp, compsize))
+ Punt("Error reading in MsgLoadComposition!");
+ myclose(fid);
+ DeleteFileAndCheck(file);
+ } else {
+ blankcomp = "To: \n--------\n";
+ compsize = strlen(blankcomp);
+ }
+ startPos = strchr(blankcomp, '\n') - blankcomp;
+ }
+ fid = myopen(MsgFileName(msg), O_WRONLY | O_TRUNC | O_CREAT, 0666);
+ if (compsize != write(fid, blankcomp, compsize))
+ Punt("Error writing in MsgLoadComposition!");
+ myclose(fid);
+ TocSetCacheValid(msg->toc);
+ msg->startPos = startPos;
+}
+
+
+
+/* Load a msg with a template of a reply to frommsg. Set msg->startPos so
+ that the text insertion point will be placed at the beginning of the
+ message body. */
+
+void MsgLoadReply(
+ Msg msg,
+ Msg frommsg,
+ String *params,
+ Cardinal num_params)
+{
+ char **argv;
+ char str[100];
+ int status;
+
+ TempMoveDraft();
+ argv = MakeArgv(5 + num_params);
+ argv[0] = "repl";
+ argv[1] = TocMakeFolderName(frommsg->toc);
+ (void) sprintf(str, "%d", frommsg->msgid);
+ argv[2] = str;
+ argv[3] = "-nowhatnowproc";
+ argv[4] = "-nodraftfolder";
+ memmove( (char *)(argv + 5), (char *)params, num_params * sizeof(String *));
+ status = DoCommand(argv, (char *) NULL, (char *) NULL);
+ XtFree(argv[1]);
+ XtFree((char*)argv);
+ if (!status) {
+ RenameAndCheck(draftFile, MsgFileName(msg));
+ RestoreDraft();
+ TocSetCacheValid(frommsg->toc); /* If -anno is set, this keeps us from
+ rescanning folder. */
+ TocSetCacheValid(msg->toc);
+ msg->startPos = GetFileLength(MsgFileName(msg));
+ }
+}
+
+
+
+/* Load a msg with a template of forwarding a list of messages. Set
+ msg->startPos so that the text insertion point will be placed at the end
+ of the first line (which is usually a "To:" field). */
+
+void MsgLoadForward(
+ Scrn scrn,
+ Msg msg,
+ MsgList mlist,
+ String *params,
+ Cardinal num_params)
+{
+ char **argv, str[100];
+ int i;
+ TempMoveDraft();
+ argv = MakeArgv(4 + mlist->nummsgs + num_params);
+ argv[0] = "forw";
+ argv[1] = TocMakeFolderName(mlist->msglist[0]->toc);
+ for (i = 0; i < mlist->nummsgs; i++) {
+ (void) sprintf(str, "%d", mlist->msglist[i]->msgid);
+ argv[2 + i] = XtNewString(str);
+ }
+ argv[2 + i] = "-nowhatnowproc";
+ argv[3 + i] = "-nodraftfolder";
+ memmove( (char *)(argv + 4 + i), (char *)params,
+ num_params * sizeof(String *));
+ DoCommand(argv, (char *) NULL, (char *) NULL);
+ for (i = 1; i < 2 + mlist->nummsgs; i++)
+ XtFree((char *) argv[i]);
+ XtFree((char *) argv);
+ RenameAndCheck(draftFile, MsgFileName(msg));
+ RestoreDraft();
+ TocSetCacheValid(msg->toc);
+ msg->source = CreateFileSource(scrn->viewlabel, MsgFileName(msg), True);
+ msg->startPos = XawTextSourceScan(msg->source, (XawTextPosition) 0,
+ XawstEOL, XawsdRight, 1, False);
+}
+
+
+/* Load msg with a copy of frommsg. */
+
+void MsgLoadCopy(Msg msg, Msg frommsg)
+{
+ char str[500];
+ (void)strcpy(str, MsgFileName(msg));
+ CopyFileAndCheck(MsgFileName(frommsg), str);
+ TocSetCacheValid(msg->toc);
+}
+
+/* Checkpoint the given message if it contains unsaved edits. */
+
+void MsgCheckPoint(Msg msg)
+{
+ int len;
+ char file[500];
+
+ if (!msg || !msg->source || !IsEditable(msg) ||
+ !XawAsciiSourceChanged(msg->source))
+ return;
+
+ if (*app_resources.checkpoint_name_format == '/') {
+ (void) sprintf(file, app_resources.checkpoint_name_format, msg->msgid);
+ } else {
+ (void) sprintf(file, "%s/", msg->toc->path);
+ len = strlen(file);
+ (void) sprintf(file + len, app_resources.checkpoint_name_format,
+ msg->msgid);
+ }
+ if (!XawAsciiSaveAsFile(msg->source, file)) {
+ char str[256];
+ (void) sprintf(str, "Unsaved edits cannot be checkpointed to %s.",
+ file);
+ PopupError((Widget)NULL, str);
+ }
+ TocSetCacheValid(msg->toc);
+}
+
+/* Free the storage being used by the given msg. */
+
+void MsgFree(Msg msg)
+{
+ XtFree(msg->buf);
+ XtFree((char *)msg);
+}
+
+/* Insert the associated message, if any, filtering it first */
+
+/*ARGSUSED*/
+void XmhInsert(
+ Widget w,
+ XEvent *event,
+ String *params,
+ Cardinal *num_params)
+{
+ Scrn scrn = ScrnFromWidget(w);
+ Msg msg = scrn->msg;
+ XawTextPosition pos;
+ XawTextBlock block;
+
+ if (msg == NULL || scrn->assocmsg == NULL) return;
+
+ if (app_resources.insert_filter && *app_resources.insert_filter) {
+ char command[1024];
+ char *argv[4];
+ argv[0] = "/bin/sh";
+ argv[1] = "-c";
+ sprintf(command, "%s %s", app_resources.insert_filter,
+ MsgFileName(scrn->assocmsg));
+ argv[2] = command;
+ argv[3] = 0;
+ block.ptr = DoCommandToString(argv);
+ block.length = strlen(block.ptr);
+ }
+ else {
+ /* default filter is equivalent to 'echo "<filename>"' */
+ block.ptr = XtNewString(MsgFileName(scrn->assocmsg));
+ block.length = strlen(block.ptr);
+ }
+ block.firstPos = 0;
+ block.format = FMT8BIT;
+ pos = XawTextGetInsertionPoint(scrn->viewwidget);
+ if (XawTextReplace(scrn->viewwidget, pos, pos, &block) != XawEditDone)
+ PopupError(scrn->parent, "Insertion failed!");
+ XtFree(block.ptr);
+}
+
+/* Function Name: CreateFileSource
+ * Description: Creates an AsciiSource for a file.
+ * Arguments: w - the widget to create the source for.
+ * filename - the file to assign to this source.
+ * edit - if TRUE then this disk source is editable.
+ * Returns: the source.
+ */
+
+Widget
+CreateFileSource(Widget w, String filename, Boolean edit)
+{
+ Arg arglist[10];
+ Cardinal num_args = 0;
+
+ XtSetArg(arglist[num_args], XtNtype, XawAsciiFile); num_args++;
+ XtSetArg(arglist[num_args], XtNstring, filename); num_args++;
+ if (edit)
+ XtSetArg(arglist[num_args], XtNeditType, XawtextEdit);
+ else
+ XtSetArg(arglist[num_args], XtNeditType, XawtextRead);
+ num_args++;
+
+ return(XtCreateWidget("textSource", asciiSrcObjectClass, w,
+ arglist, num_args));
+}
diff --git a/msg.h b/msg.h
new file mode 100644
index 0000000..ba14fc0
--- /dev/null
+++ b/msg.h
@@ -0,0 +1,59 @@
+/* $XConsortium: msg.h,v 2.7 89/07/20 21:12:59 converse Exp $ */
+/*
+ * COPYRIGHT 1987
+ * 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/msg.h,v 1.3 2002/07/01 02:26:05 tsi Exp $ */
+
+#ifndef _msg_h
+#define _msg_h
+
+extern char *MsgFileName(Msg);
+extern int MsgSaveChanges(Msg);
+extern int MsgSetScrn(Msg, Scrn, XtCallbackList, XtCallbackList);
+extern void MsgSetScrnForComp(Msg, Scrn);
+extern void MsgSetScrnForce(Msg, Scrn);
+extern void MsgSetFate(Msg, FateType, Toc);
+extern FateType MsgGetFate(Msg, Toc *);
+extern void MsgSetTemporary(Msg);
+extern void MsgSetPermanent(Msg);
+extern int MsgGetId(Msg);
+extern char *MsgGetScanLine(Msg);
+extern Toc MsgGetToc(Msg);
+extern void MsgSetReapable(Msg);
+extern void MsgClearReapable(Msg);
+extern int MsgGetReapable(Msg);
+extern void MsgSetEditable(Msg);
+extern void MsgClearEditable(Msg);
+extern int MsgGetEditable(Msg);
+extern int MsgChanged(Msg);
+extern void MsgSetCallOnChange(Msg, void (*)(XMH_CB_ARGS), XtPointer);
+extern void MsgSend(Msg);
+extern void MsgLoadComposition(Msg);
+extern void MsgLoadReply(Msg, Msg, String *, Cardinal);
+extern void MsgLoadForward(Scrn, Msg, MsgList, String *, Cardinal);
+extern void MsgLoadCopy(Msg, Msg);
+extern void MsgCheckPoint(Msg);
+extern void MsgFree(Msg);
+
+#endif /* _msg_h */
diff --git a/pick.c b/pick.c
new file mode 100644
index 0000000..ef20911
--- /dev/null
+++ b/pick.c
@@ -0,0 +1,753 @@
+/*
+ * $XConsortium: pick.c,v 2.45 91/07/17 21:26:54 converse Exp $
+ *
+ *
+ * COPYRIGHT 1987
+ * 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/pick.c,v 1.3 2002/04/05 21:06:29 dickey Exp $ */
+
+/* pick.c -- handle a pick subwidget. */
+
+#include "xmh.h"
+
+#define WTlabel labelWidgetClass
+#define WTbutton commandWidgetClass
+#define WTtextentry asciiTextWidgetClass
+
+#define RTfrom 0
+#define RTto 1
+#define RTcc 2
+#define RTdate 3
+#define RTsubject 4
+#define RTsearch 5
+#define RTother 6
+#define RTignore 7
+
+#define FIRSTROWTYPE RTfrom
+#define LASTUSEFULROWTYPE RTother
+#define NUMROWTYPE (RTignore+1)
+
+static int stdwidth = -1; /* Width to make text fields, and other
+ things that want to be the same width as
+ text fields. */
+
+static char *TypeName[NUMROWTYPE];
+static short true_data = 1; /* radio data */
+static short false_data = 0; /* radio data */
+
+typedef struct {
+ WidgetClass type; /* Encode what type of Widget this is. */
+ Widget widget; /* The widget id itself. */
+ struct _RowListRec *row; /* Which row this widget is in. */
+} FormEntryRec, *FormEntry;
+
+typedef struct _RowListRec {
+ short type; /* Encode what type of list this is. */
+ Widget widget; /* Widget containing this row */
+ short numwidgets; /* How many widgets in this list. */
+ FormEntry *wlist; /* List of widgets. */
+ struct _GroupRec *group; /* Which group this is in. */
+} RowListRec, *RowList;
+
+typedef struct _GroupRec {
+ short numrows; /* How many rows of widget. */
+ Widget widget; /* Widget containing this group */
+ RowList *rlist; /* List of widget rows. */
+ struct _FormBoxRec *form; /* Which form this is in. */
+} GroupRec, *Group;
+
+typedef struct _FormBoxRec {
+ Widget outer; /* Outer widget (contains scrollbars if any) */
+ Widget inner; /* Inner widget (contains master form) */
+ short numgroups; /* How many groups of form entries we have. */
+ Group *glist; /* List of form groups. */
+ struct _PickRec *pick; /* Which pick this is in. */
+} FormBoxRec, *FormBox;
+
+typedef struct _PickRec {
+ Scrn scrn; /* Scrn containing this pick. */
+ Widget label; /* Widget with label for this pick. */
+ Toc toc; /* Toc for folder being scanned. */
+ FormBox general; /* Form for general info about this pick. */
+ FormBox details; /* Form for details about this pick. */
+} PickRec;
+
+
+static FormEntry CreateWidget(RowList, WidgetClass, ArgList, Cardinal);
+static void DeleteWidget(FormEntry);
+static void AddDetailGroup(FormBox);
+
+void InitPick(void)
+{
+ TypeName[RTfrom] = "From:";
+ TypeName[RTto] = "To:";
+ TypeName[RTcc] = "Cc:";
+ TypeName[RTdate] = "Date:";
+ TypeName[RTsubject] = "Subject:";
+ TypeName[RTsearch] = "Search:";
+ TypeName[RTother] = NULL;
+
+ /* Translations which will prevent the Search and Replace functionality
+ * of the Text widget in text fields of pick and popups. The toc's
+ * Search and Replace functionality is removed in the app defaults file.
+ */
+ NoTextSearchAndReplace = XtParseTranslationTable
+ ("Ctrl<Key>R: no-op(RingBell)\n\
+ Ctrl<Key>S: no-op(RingBell)\n");
+}
+
+
+static void PrepareToUpdate(FormBox form)
+{
+ XawFormDoLayout(form->inner, FALSE);
+}
+
+static void ExecuteUpdate(FormBox form)
+{
+ XawFormDoLayout(form->inner, TRUE);
+ XtManageChild(form->inner);
+ XtManageChild(form->outer);
+}
+
+static void AddLabel(RowList row, char *text, int usestd)
+{
+ static Arg arglist[] = {
+ {XtNlabel, (XtArgVal)NULL},
+ {XtNborderWidth, (XtArgVal) 0},
+ {XtNjustify, (XtArgVal) XtJustifyRight},
+ {XtNwidth, (XtArgVal) NULL}
+ };
+
+ arglist[0].value = (XtArgVal) text;
+ arglist[XtNumber(arglist) - 1].value = (XtArgVal) stdwidth;
+ (void) CreateWidget(row, WTlabel, arglist,
+ usestd ? XtNumber(arglist) : XtNumber(arglist) - 1);
+}
+
+
+static void AddButton(RowList row, char *text, void (*func)(XMH_CB_ARGS))
+{
+ FormEntry entry;
+ static Arg args[] = {
+ {XtNlabel, (XtArgVal)NULL},
+ };
+
+ args[0].value = (XtArgVal)text;
+ entry = CreateWidget( row, WTbutton, args, XtNumber(args) );
+ XtAddCallback( entry->widget, XtNcallback, func, (XtPointer)entry );
+}
+
+
+static void AddToggle(
+ RowList row,
+ char *text,
+ int initial_state,
+ Widget radio_group,
+ XtPointer radio_data)
+{
+ FormEntry entry;
+ Arg args[4];
+ int n = 0;
+ XtTranslations translations;
+
+ XtSetArg(args[n], XtNlabel, text); n++;
+ XtSetArg(args[n], XtNstate, initial_state); n++;
+ XtSetArg(args[n], XtNradioGroup, radio_group); n++;
+ XtSetArg(args[n], XtNradioData, radio_data); n++;
+ entry = CreateWidget(row, toggleWidgetClass, args, (Cardinal)n);
+ translations = XtParseTranslationTable("<Btn1Down>,<Btn1Up>:set()\n");
+ XtOverrideTranslations(entry->widget, translations);
+}
+
+
+static void AddTextEntry(RowList row, char *str)
+{
+ FormEntry entry;
+ static Arg arglist[] = {
+ {XtNstring, (XtArgVal) NULL},
+ {XtNwidth, (XtArgVal) NULL},
+ {XtNlength, (XtArgVal) 300},
+ {XtNresize, (XtArgVal) XawtextResizeBoth},
+ {XtNeditType, (XtArgVal)XawtextEdit},
+ };
+ arglist[0].value = (XtArgVal) str;
+ arglist[1].value = (XtArgVal) stdwidth;
+ entry = CreateWidget( row, WTtextentry, arglist, XtNumber(arglist) );
+ XtOverrideTranslations(entry->widget, NoTextSearchAndReplace);
+}
+
+
+static void ChangeTextEntry(FormEntry entry, char *str)
+{
+ Arg arglist[1];
+ char *ptr;
+
+ XtSetArg(arglist[0], XtNstring, &ptr);
+ XtGetValues(entry->widget, arglist, (Cardinal) 1);
+ if (strcmp(str, ptr) == 0)
+ return;
+
+ XtSetArg(arglist[0], XtNstring, str);
+ XtSetValues(entry->widget, arglist, (Cardinal) 1);
+}
+
+/* ARGSUSED */
+static void ExecRowOr(
+ Widget w, /* unused */
+ XtPointer closure, /* FormEntry */
+ XtPointer call_data) /* unused */
+{
+ FormEntry entry = (FormEntry)closure;
+ RowList row = entry->row;
+ FormBox form = row->group->form;
+ PrepareToUpdate(form);
+ DeleteWidget(entry);
+ AddLabel(row, "or", FALSE);
+ AddTextEntry(row, "");
+ AddButton(row, "Or", ExecRowOr);
+ ExecuteUpdate(form);
+}
+
+
+/* ARGSUSED */
+static void ExecGroupOr(
+ Widget w, /* unused */
+ XtPointer closure, /* FormEntry */
+ XtPointer call_data) /* unused */
+{
+ FormBox form = ((FormEntry)closure)->row->group->form;
+/* %%% XUnmapWindow(theDisplay, XtWindow(form->inner)); */
+ PrepareToUpdate(form);
+ AddDetailGroup(form);
+ ExecuteUpdate(form);
+/* %%% XtMapWidget(form->inner); */
+}
+
+static char **argv;
+static int argvsize;
+
+
+static void AppendArgv(char *ptr)
+{
+ argvsize++;
+ argv = ResizeArgv(argv, argvsize);
+ argv[argvsize - 1] = XtNewString(ptr);
+}
+
+static void EraseLast(void)
+{
+ argvsize--;
+ XtFree((char *) argv[argvsize]);
+ argv[argvsize] = 0;
+}
+
+
+
+static Boolean ParseRow(RowList row)
+{
+ int result = FALSE;
+ int i;
+ FormEntry entry;
+ char str[1000];
+ Arg args[4];
+ char *ptr;
+ char *other;
+
+ if (row->type > LASTUSEFULROWTYPE)
+ return FALSE;
+
+ for (i = 3; i < row->numwidgets; i += 2) {
+ entry = row->wlist[i];
+ XtSetArg(args[0], XtNstring, &ptr);
+ XtGetValues(entry->widget, args, (Cardinal) 1);
+ if (ptr && *ptr) {
+ if (!result) {
+ result = TRUE;
+ if (! (*((short *)
+ (XawToggleGetCurrent(row->wlist[0]->widget)))))
+ AppendArgv("-not");
+ AppendArgv("-lbrace");
+ }
+ switch (row->type) {
+ case RTfrom:
+ AppendArgv("-from");
+ break;
+ case RTto:
+ AppendArgv("-to");
+ break;
+ case RTcc:
+ AppendArgv("-cc");
+ break;
+ case RTdate:
+ AppendArgv("-date");
+ break;
+ case RTsubject:
+ AppendArgv("-subject");
+ break;
+ case RTsearch:
+ AppendArgv("-search");
+ break;
+ case RTother:
+ XtSetArg(args[0], XtNstring, &other);
+ XtGetValues(row->wlist[2]->widget, args, (Cardinal) 1);
+ (void) sprintf(str, "--%s", other);
+ AppendArgv(str);
+ break;
+ }
+ AppendArgv(ptr);
+ AppendArgv("-or");
+ }
+ }
+ if (result) {
+ EraseLast();
+ AppendArgv("-rbrace");
+ AppendArgv("-and");
+ }
+ return result;
+}
+
+
+static Boolean ParseGroup(Group group)
+{
+ int found = FALSE;
+ int i;
+ for (i=0 ; i<group->numrows ; i++)
+ found |= ParseRow(group->rlist[i]);
+ if (found) {
+ EraseLast();
+ AppendArgv("-rbrace");
+ AppendArgv("-or");
+ AppendArgv("-lbrace");
+ }
+ return found;
+}
+
+/* ARGSUSED */
+static void ExecOK(
+ Widget w, /* unused */
+ XtPointer closure, /* FormEntry */
+ XtPointer call_data) /* unused */
+{
+ Pick pick = ((FormEntry)closure)->row->group->form->pick;
+ Toc toc = pick->toc;
+ FormBox details = pick->details;
+ FormBox general = pick->general;
+ Group group = general->glist[0];
+ RowList row0 = group->rlist[0];
+ RowList row1 = group->rlist[1];
+ RowList row2 = group->rlist[2];
+ char *fromseq;
+ char *toseq;
+ char *datefield;
+ char *fromdate;
+ char *todate;
+ short removeoldmsgs =
+ *((short*)XawToggleGetCurrent(row2->wlist[1]->widget));
+ int i, found;
+ char *folderpath;
+ int cmd_status;
+ Arg args[5];
+
+ XtSetArg(args[0], XtNstring, &toseq);
+ XtGetValues(row0->wlist[1]->widget, args, (Cardinal) 1);
+ if (strcmp(toseq, "all") == 0) {
+ PopupError(pick->scrn->parent,
+ "Can't create a sequence called \"all\".");
+ return;
+ }
+ XtSetArg(args[0], XtNstring, &fromseq);
+ XtGetValues(row0->wlist[3]->widget, args, (Cardinal) 1);
+ if (TocGetSeqNamed(toc, fromseq) == NULL) {
+ char str[200];
+ (void) sprintf(str, "Sequence \"%s\" doesn't exist!", fromseq);
+ PopupError(pick->scrn->parent, str);
+ return;
+ }
+
+ argv = MakeArgv(1);
+ argvsize = 0;
+ AppendArgv("pick");
+ AppendArgv(folderpath = TocMakeFolderName(toc));
+ XtFree(folderpath);
+ AppendArgv(fromseq);
+ AppendArgv("-sequence");
+ AppendArgv(toseq);
+ if (removeoldmsgs)
+ AppendArgv("-zero");
+ else
+ AppendArgv("-nozero");
+ XtSetArg(args[0], XtNstring, &datefield);
+ XtGetValues(row1->wlist[5]->widget, args, (Cardinal) 1);
+ if (*datefield) {
+ AppendArgv("-datefield");
+ AppendArgv(datefield);
+ }
+ XtSetArg(args[0], XtNstring, &fromdate);
+ XtGetValues(row1->wlist[1]->widget, args, (Cardinal) 1);
+ if (*fromdate) {
+ AppendArgv("-after");
+ AppendArgv(fromdate);
+ AppendArgv("-and");
+ }
+ XtSetArg(args[0], XtNstring, &todate);
+ XtGetValues(row1->wlist[3]->widget, args, (Cardinal) 1);
+ if (*todate) {
+ AppendArgv("-before");
+ AppendArgv(todate);
+ AppendArgv("-and");
+ }
+ found = FALSE;
+ AppendArgv("-lbrace");
+ AppendArgv("-lbrace");
+ for (i=0 ; i<details->numgroups ; i++)
+ found |= ParseGroup(details->glist[i]);
+ EraseLast();
+ EraseLast();
+ if (found) AppendArgv("-rbrace");
+ else if (*fromdate || *todate) EraseLast();
+ if (app_resources.debug) {
+ for (i=0 ; i<argvsize ; i++)
+ (void) fprintf(stderr, "%s ", argv[i]);
+ (void) fprintf(stderr, "\n");
+ }
+ if (app_resources.block_events_on_busy) ShowBusyCursor();
+
+ cmd_status = DoCommand(argv, (char*)NULL, (char*)NULL);
+ TocReloadSeqLists(toc);
+ TocChangeViewedSeq(toc, TocGetSeqNamed(toc, toseq));
+
+ if (app_resources.block_events_on_busy) UnshowBusyCursor();
+ if (cmd_status == 0 /*succeeded*/) DestroyScrn(pick->scrn);
+ for (i=0 ; i<argvsize ; i++) XtFree((char *) argv[i]);
+ XtFree((char *) argv);
+}
+
+
+/* ARGSUSED */
+static void ExecCancel(
+ Widget w, /* unused */
+ XtPointer closure, /* FormEntry */
+ XtPointer call_data) /* unused */
+{
+ Pick pick = ((FormEntry)closure)->row->group->form->pick;
+ Scrn scrn = pick->scrn;
+ (void) DestroyScrn(scrn);
+}
+
+
+static FormEntry CreateWidget(
+ RowList row,
+ WidgetClass class,
+ ArgList args,
+ Cardinal num_args)
+{
+ static Arg arglist[] = {
+ {XtNfromHoriz, (XtArgVal)NULL},
+ {XtNresizable, (XtArgVal)TRUE},
+ {XtNtop, (XtArgVal) XtChainTop},
+ {XtNleft, (XtArgVal) XtChainLeft},
+ {XtNbottom, (XtArgVal) XtChainTop},
+ {XtNright, (XtArgVal) XtChainLeft},
+ };
+ ArgList merged_args;
+ FormEntry entry;
+
+ row->numwidgets++;
+ row->wlist = (FormEntry *)
+ XtRealloc((char *) row->wlist,
+ (unsigned) row->numwidgets * sizeof(FormEntry));
+ entry = XtNew(FormEntryRec);
+ entry->row = row;
+ entry->type = class;
+ row->wlist[row->numwidgets - 1] = entry;
+ if (row->numwidgets > 1)
+ arglist[0].value = (XtArgVal) row->wlist[row->numwidgets - 2]->widget;
+ else
+ arglist[0].value = (XtArgVal) NULL;
+
+ merged_args = XtMergeArgLists(args, num_args, arglist, XtNumber(arglist) );
+
+ entry->widget = XtCreateManagedWidget( (String) NULL, class, row->widget,
+ merged_args,
+ num_args + XtNumber(arglist) );
+
+ XtFree((char *) merged_args);
+ return entry;
+}
+
+
+static void
+DeleteWidget(FormEntry entry)
+{
+ RowList row = entry->row;
+ int i;
+ XtDestroyWidget(entry->widget);
+ for (i = 0; i < row->numwidgets; i++)
+ if (row->wlist[i] == entry)
+ break;
+ row->numwidgets--;
+ for (; i < row->numwidgets; i++)
+ row->wlist[i] = row->wlist[i + 1];
+}
+
+
+/* Figure out how wide text fields and labels should be so that they'll all
+ line up correctly, and be big enough to hold everything, but not too big. */
+
+static void FindStdWidth(void)
+{
+ stdwidth = 100; /* %%% HACK! */
+}
+
+
+static RowList AddRow(Group group, int type)
+{
+ static Arg arglist[] = {
+ {XtNborderWidth, (XtArgVal) 0},
+ {XtNfromVert, (XtArgVal) NULL},
+ {XtNresizable, (XtArgVal) TRUE},
+ {XtNtop, (XtArgVal) XtChainTop},
+ {XtNleft, (XtArgVal) XtChainLeft},
+ {XtNbottom, (XtArgVal) XtChainTop},
+ {XtNright, (XtArgVal) XtChainLeft}
+ };
+ RowList row;
+ group->numrows++;
+ group->rlist = (RowList *)
+ XtRealloc((char *) group->rlist,
+ (unsigned) group->numrows * sizeof(RowList));
+ group->rlist[group->numrows - 1] = row = XtNew(RowListRec);
+ row->type = type;
+ row->numwidgets = 0;
+ row->wlist = (FormEntry *) NULL;
+ row->group = group;
+ if (group->numrows > 1)
+ arglist[1].value = (XtArgVal)group->rlist[group->numrows - 2]->widget;
+ else
+ arglist[1].value = (XtArgVal) NULL;
+ row->widget = XtCreateWidget("rowform", formWidgetClass, group->widget,
+ arglist, XtNumber(arglist) );
+ if (type == RTignore) return row;
+ AddToggle(row, "Pick", TRUE, (Widget)NULL, (XtPointer)(&true_data));
+ AddToggle(row, "Skip", FALSE, row->wlist[row->numwidgets - 1]->widget,
+ (XtPointer)(&false_data));
+ if (TypeName[type])
+ AddLabel(row, TypeName[type], TRUE);
+ else
+ AddTextEntry(row, "");
+ AddTextEntry(row, "");
+ AddButton(row, "Or", ExecRowOr);
+ XtManageChild(row->widget);
+ return row;
+}
+
+
+static Group AddGroup(FormBox form)
+{
+ static Arg arglist[] = {
+ {XtNborderWidth, (XtArgVal) 0},
+ {XtNfromVert, (XtArgVal) NULL},
+ {XtNresizable, (XtArgVal) TRUE},
+ {XtNtop, (XtArgVal) XtChainTop},
+ {XtNleft, (XtArgVal) XtChainLeft},
+ {XtNbottom, (XtArgVal) XtChainTop},
+ {XtNright, (XtArgVal) XtChainLeft}
+ };
+ Group group;
+ form->numgroups++;
+ form->glist = (Group *)
+ XtRealloc((char *) form->glist,
+ (unsigned) form->numgroups * sizeof(Group));
+ form->glist[form->numgroups - 1] = group =
+ (Group) XtMalloc((Cardinal) sizeof(GroupRec));
+ group->numrows = 0;
+ group->form = form;
+ group->rlist = (RowList *) NULL;
+ if (form->numgroups > 1)
+ arglist[1].value = (XtArgVal)form->glist[form->numgroups - 2]->widget;
+ else
+ arglist[1].value = (XtArgVal)NULL;
+ group->widget = XtCreateWidget("groupform", formWidgetClass, form->inner,
+ arglist, XtNumber(arglist) );
+ return group;
+}
+
+
+
+static void
+AddDetailGroup(FormBox form)
+{
+ Group group;
+ RowList row;
+ int type;
+
+ if (form->numgroups > 0) {
+ group = form->glist[form->numgroups - 1];
+ row = group->rlist[group->numrows - 1];
+ DeleteWidget(row->wlist[0]);
+ AddLabel(row, "- or -", FALSE);
+ }
+ group = AddGroup(form);
+ for (type = FIRSTROWTYPE; type <= LASTUSEFULROWTYPE; type++)
+ (void) AddRow(group, type);
+ row = AddRow(group, RTignore);
+ AddButton(row, "- Or -", ExecGroupOr);
+ XtManageChild(row->widget);
+ if (XtIsRealized(XtParent(group->widget)))
+ XtRealizeWidget(group->widget);
+ XtManageChild(group->widget);
+}
+
+
+static void AddGeneralGroup(FormBox form)
+{
+ Group group;
+ RowList row;
+ Widget widgetList[4];
+ group = AddGroup(form);
+ row = AddRow(group, RTignore);
+ widgetList[0] = row->widget;
+ AddLabel(row, "Creating sequence:", FALSE);
+ AddTextEntry(row, "");
+ AddLabel(row, "with msgs from sequence:", FALSE);
+ AddTextEntry(row, "");
+ row = AddRow(group, RTignore);
+ widgetList[1] = row->widget;
+ AddLabel(row, "Date range:", FALSE);
+ AddTextEntry(row, "");
+ AddLabel(row, " - ", FALSE);
+ AddTextEntry(row, "");
+ AddLabel(row, "Date field:", FALSE);
+ AddTextEntry(row, "");
+ row = AddRow(group, RTignore);
+ widgetList[2] = row->widget;
+ AddLabel(row, "Clear old entries from sequence?", FALSE);
+ AddToggle(row, "Yes", TRUE, (Widget) NULL, (XtPointer)(&true_data));
+ AddToggle(row, "No", FALSE, row->wlist[row->numwidgets - 1]->widget,
+ (XtPointer)(&false_data));
+ row = AddRow(group, RTignore);
+ widgetList[3] = row->widget;
+ AddButton(row, "OK", ExecOK);
+ AddButton(row, "Cancel", ExecCancel);
+ XtManageChildren(widgetList, XtNumber(widgetList));
+ XtManageChild(group->widget);
+}
+
+
+static void InitGeneral(Pick pick, char *fromseq, char *toseq)
+{
+ RowList row;
+ row = pick->general->glist[0]->rlist[0];
+ ChangeTextEntry(row->wlist[1], toseq);
+ ChangeTextEntry(row->wlist[3], fromseq);
+}
+
+
+static void CleanForm(FormBox form)
+{
+ int i, j, k;
+ Group group;
+ RowList row;
+ FormEntry entry;
+ for (i=0 ; i<form->numgroups ; i++) {
+ group = form->glist[i];
+ for (j=0 ; j<group->numrows ; j++) {
+ row = group->rlist[j];
+ for (k=0 ; k<row->numwidgets ; k++) {
+ entry = row->wlist[k];
+ if (entry->type == WTtextentry)
+ ChangeTextEntry(entry, "");
+ }
+ }
+ }
+}
+
+
+static FormBox MakeAForm(Pick pick)
+{
+ static Arg arglist1[] = {
+ {XtNallowHoriz, (XtArgVal)TRUE},
+ {XtNallowVert, (XtArgVal)TRUE},
+/* %%% {XtNallowResize, (XtArgVal)TRUE}, */
+ {XtNmin, (XtArgVal)50},
+ {XtNmax, (XtArgVal)1500}
+ };
+ static Arg arglist2[] = {
+ {XtNborderWidth, (XtArgVal) 0}
+ };
+ FormBox result;
+ result = (FormBox) XtMalloc((Cardinal) sizeof(FormBoxRec));
+ result->outer = XtCreateWidget( "pick", viewportWidgetClass,
+ pick->scrn->widget,
+ arglist1, XtNumber(arglist1) );
+ result->inner = XtCreateWidget( "form", formWidgetClass, result->outer,
+ arglist2, XtNumber(arglist2) );
+ result->pick = pick;
+ result->numgroups = 0;
+ result->glist = (Group *) NULL;
+ return result;
+}
+
+
+void AddPick(Scrn scrn, Toc toc, char *fromseq, char *toseq)
+{
+ Pick pick;
+ FormBox general, details;
+ char str[100];
+#ifdef notdef
+ int height;
+#endif /* notdef */
+
+ if (scrn->pick) {
+ pick = scrn->pick;
+ CleanForm(pick->details);
+ CleanForm(pick->general);
+ } else {
+ pick = scrn->pick = (Pick) XtMalloc((Cardinal) sizeof(PickRec));
+ pick->scrn = scrn;
+ pick->label = CreateTitleBar(scrn, "pickTitlebar");
+ pick->details = details = MakeAForm(pick);
+ pick->general = general = MakeAForm(pick);
+ FindStdWidth();
+ XawPanedSetRefigureMode(scrn->widget, False);
+ PrepareToUpdate(details);
+ AddDetailGroup(details);
+ ExecuteUpdate(details);
+ PrepareToUpdate(general);
+ AddGeneralGroup(general);
+ ExecuteUpdate(general);
+#ifdef notdef
+ height = general->inner->core.height;
+ if (general->inner->core.width > scrn->widget->core.width)
+ height += XtScrollMgrGetThickness(general->outer);
+ XawPanedSetMinMax(scrn->widget, general->outer, height, height);
+ XawPanedSetMinMax(scrn->widget, general->outer, 10, 10000);
+#endif /* notdef */
+ XawPanedSetRefigureMode(scrn->widget, True);
+ }
+ pick->toc = toc;
+ InitGeneral(pick, fromseq, toseq);
+ (void) sprintf(str, "Pick: %s", TocName(toc));
+ ChangeLabel(pick->label, str);
+ StoreWindowName(scrn, str);
+}
diff --git a/popup.c b/popup.c
new file mode 100644
index 0000000..72cc6ab
--- /dev/null
+++ b/popup.c
@@ -0,0 +1,513 @@
+/* $XConsortium: popup.c,v 2.38 94/08/26 18:04:22 swick Exp $
+ *
+ *
+ * COPYRIGHT 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/popup.c,v 1.3 2002/07/01 02:26:05 tsi Exp $ */
+
+/* popup.c -- Handle pop-up widgets. */
+
+#include "xmh.h"
+#include "actions.h"
+
+#include <X11/Xaw/Cardinals.h>
+
+typedef struct _PopupStatus {
+ Widget popup; /* order of fields same as CommandStatusRec */
+ struct _LastInput lastInput;
+ char* shell_command; /* NULL, or contains sh -c command */
+} PopupStatusRec, *PopupStatus;
+
+/* these are just strings which are used more than one place in the code */
+static String XmhNconfirm = "confirm";
+static String XmhNdialog = "dialog";
+static String XmhNerror = "error";
+static String XmhNnotice = "notice";
+static String XmhNokay = "okay";
+static String XmhNprompt = "prompt";
+static String XmhNvalue = "value";
+
+/* The popups were originally parented from toplevel and neglected the
+ * transientFor resource. In order not to break existing user resource
+ * settings for the popups, transientFor is set independent of the parent,
+ * which remains the toplevel widget.
+ */
+
+static void DeterminePopupPosition(
+ Position *x_ptr,
+ Position *y_ptr,
+ Widget *transFor_return) /* return a suitable top level shell */
+{
+ if (lastInput.win != (Window) -1) {
+ if (transFor_return) {
+ Widget source;
+ source = XtWindowToWidget(XtDisplay(toplevel), lastInput.win);
+ while (source && !XtIsWMShell(source))
+ source = XtParent(source);
+ *transFor_return = source;
+ }
+ /* use the site of the last KeyPress or ButtonPress */
+ *x_ptr = lastInput.x;
+ *y_ptr = lastInput.y;
+ } else {
+ Widget source;
+ int i = 0;
+ Dimension width, height;
+ Arg args[2];
+
+ /* %%% need to keep track of last screen */
+ /* guess which screen and use the the center of it */
+ while (i < numScrns && !scrnList[i]->mapped)
+ i++;
+ source = ((i < numScrns) ? scrnList[i]->parent : toplevel);
+ XtSetArg(args[0], XtNwidth, &width);
+ XtSetArg(args[1], XtNheight, &height);
+ XtGetValues(source, args, TWO);
+ XtTranslateCoords(source, (Position) (width / 2),
+ (Position) (height / 2), x_ptr, y_ptr);
+ if (transFor_return) *transFor_return = source;
+ }
+}
+
+static Boolean PositionThePopup(
+ Widget popup,
+ Position x,
+ Position y)
+{
+ /* Hack. Fix up the position of the popup. The xmh app defaults file
+ * contains an Xmh*Geometry specification; the effects of that on
+ * popups, and the lack of any user-supplied geometry specification for
+ * popups, are mitigated here, by giving the popup shell a position.
+ * (Xmh*Geometry is needed in case there is no user-supplied default.)
+ * Returns True if an explicit geometry was inferred; false if the
+ * widget was repositioned to (x,y).
+ */
+
+ Arg args[4];
+ String top_geom, pop_geom;
+
+ XtSetArg( args[0], XtNgeometry, &top_geom );
+ XtGetValues( toplevel, args, ONE );
+ XtSetArg( args[0], XtNgeometry, &pop_geom );
+ XtGetValues( popup, args, ONE );
+
+ if (pop_geom == NULL || pop_geom == top_geom) {
+ /* if same db entry, then ... */
+ XtSetArg( args[0], XtNgeometry, (String) NULL);
+ XtSetArg( args[1], XtNx, x);
+ XtSetArg( args[2], XtNy, y);
+ XtSetArg( args[3], XtNwinGravity, SouthWestGravity);
+ XtSetValues( popup, args, FOUR);
+ return False;
+ }
+ return True;
+}
+
+
+static void CenterPopupPosition(
+ Widget widget,
+ Widget popup,
+ Position px,
+ Position py)
+{
+ Position x, y;
+ Position nx, ny;
+ Arg args[3];
+
+ if (widget == NULL) return;
+ XtSetArg(args[0], XtNx, &x);
+ XtSetArg(args[1], XtNy, &y);
+ XtGetValues(popup, args, TWO);
+ if (x == px && y == py) {
+
+ /* Program sets geometry. Correct our earlier calculations. */
+
+ nx = (GetWidth(widget) - GetWidth(popup)) / 2;
+ ny = (GetHeight(widget) - GetHeight(popup)) / 2;
+ if (nx < 0) nx = 0;
+ if (ny < 0) ny = 0;
+ XtTranslateCoords(widget, nx, ny, &x, &y);
+ XtSetArg(args[0], XtNx, x);
+ XtSetArg(args[1], XtNy, y);
+ XtSetArg(args[2], XtNwinGravity, CenterGravity);
+ XtSetValues(popup, args, THREE);
+ }
+}
+
+
+/* Insure that the popup is wholly showing on the screen.
+ Optionally center the widget horizontally and/or vertically
+ on current position.
+ */
+
+static void InsureVisibility(
+ Widget popup,
+ Widget popup_child,
+ Position x, /* assert: current position = (x,y) */
+ Position y,
+ Boolean centerX,
+ Boolean centerY)
+{
+ Position root_x, root_y;
+ Dimension width, height, border;
+ Arg args[3];
+
+
+ XtSetArg( args[0], XtNwidth, &width );
+ XtSetArg( args[1], XtNheight, &height );
+ XtSetArg( args[2], XtNborderWidth, &border );
+ XtGetValues( popup, args, THREE );
+
+ XtTranslateCoords(popup_child, (Position)0, (Position)0, &root_x, &root_y);
+ if (centerX) root_x -= width/2 + border;
+ if (centerY) root_y -= height/2 + border;
+ if (root_x < 0) root_x = 0;
+ if (root_y < 0) root_y = 0;
+ border <<= 1;
+
+ if ((int)(root_x + width + border) > WidthOfScreen(XtScreen(toplevel))) {
+ root_x = WidthOfScreen(XtScreen(toplevel)) - width - border;
+ }
+ if ((int)(root_y + height + border) > HeightOfScreen(XtScreen(toplevel))) {
+ root_y = HeightOfScreen(XtScreen(toplevel)) - height - border;
+ }
+
+ if (root_x != x || root_y != y) {
+ XtSetArg( args[0], XtNx, root_x );
+ XtSetArg( args[1], XtNy, root_y );
+ XtSetValues( popup, args, TWO );
+ }
+}
+
+
+/*ARGSUSED*/
+void DestroyPopup(
+ Widget widget, /* unused */
+ XtPointer client_data,
+ XtPointer call_data) /* unused */
+{
+ Widget popup = (Widget) client_data;
+ XtPopdown(popup);
+ XtDestroyWidget(popup);
+}
+
+void WMDeletePopup(
+ Widget popup, /* transient shell */
+ XEvent* event)
+{
+ String shellName;
+ String buttonName;
+ Widget button;
+
+ shellName = XtName(popup);
+ if (strcmp(shellName, XmhNconfirm) == 0)
+ buttonName = "*no";
+ else if (strcmp(shellName, XmhNprompt) == 0)
+ buttonName = "*cancel";
+ else if (strcmp(shellName, XmhNnotice) == 0)
+ buttonName = "*confirm";
+ else if (strcmp(shellName, XmhNerror) == 0)
+ buttonName = "*OK";
+ else
+ return; /* WM may kill us */
+
+ button = XtNameToWidget(popup, buttonName);
+ if (! button) return;
+ XtCallActionProc(button, "set", event, (String*)NULL, ZERO);
+ XtCallActionProc(button, "notify", event, (String*)NULL, ZERO);
+ XtCallActionProc(button, "unset", event, (String*)NULL, ZERO);
+}
+
+static void TheUsual(
+ Widget popup) /* shell */
+{
+ XtInstallAllAccelerators(popup, popup);
+ XtAugmentTranslations(popup, app_resources.wm_protocols_translations);
+ XtRealizeWidget(popup);
+ XDefineCursor(XtDisplay(popup), XtWindow(popup), app_resources.cursor);
+ (void) XSetWMProtocols(XtDisplay(popup), XtWindow(popup),
+ protocolList, XtNumber(protocolList));
+}
+
+
+/*ARGSUSED*/
+void XmhPromptOkayAction(
+ Widget w, /* the "value" widget in the Dialog box */
+ XEvent *event, /* unused */
+ String *params, /* unused */
+ Cardinal *num_params) /* unused */
+{
+ XtCallCallbacks(XtNameToWidget(XtParent(w), XmhNokay), XtNcallback,
+ (XtPointer)XtParent(w));
+}
+
+
+void PopupPrompt(
+ Widget transientFor, /* required to be a top-level shell */
+ String question, /* the prompting string */
+ XtCallbackProc okayCallback) /* CreateFolder() */
+{
+ Widget popup;
+ Widget dialog;
+ Widget value;
+ Position x, y;
+ Boolean positioned;
+ Arg args[3];
+ static XtTranslations PromptTextTranslations = NULL;
+
+ DeterminePopupPosition(&x, &y, (Widget*)NULL);
+ XtSetArg(args[0], XtNallowShellResize, True);
+ XtSetArg(args[1], XtNinput, True);
+ XtSetArg(args[2], XtNtransientFor, transientFor);
+ popup = XtCreatePopupShell(XmhNprompt, transientShellWidgetClass, toplevel,
+ args, THREE);
+ positioned = PositionThePopup(popup, x, y);
+
+ XtSetArg(args[0], XtNlabel, question);
+ XtSetArg(args[1], XtNvalue, "");
+ dialog = XtCreateManagedWidget(XmhNdialog, dialogWidgetClass, popup, args,
+ TWO);
+ XtSetArg(args[0], XtNresizable, True);
+ XtSetValues( XtNameToWidget(dialog, "label"), args, ONE);
+ value = XtNameToWidget(dialog, XmhNvalue);
+ XtSetValues( value, args, ONE);
+ if (! PromptTextTranslations)
+ PromptTextTranslations = XtParseTranslationTable
+ ("<Key>Return: XmhPromptOkayAction()\n\
+ Ctrl<Key>R: no-op(RingBell)\n\
+ Ctrl<Key>S: no-op(RingBell)\n");
+ XtOverrideTranslations(value, PromptTextTranslations);
+
+ XawDialogAddButton(dialog, XmhNokay, okayCallback, (XtPointer) dialog);
+ XawDialogAddButton(dialog, "cancel", DestroyPopup, (XtPointer) popup);
+ TheUsual(popup);
+ InsureVisibility(popup, dialog, x, y, !positioned, False);
+ XtPopup(popup, XtGrabNone);
+}
+
+
+/* ARGSUSED */
+static void FreePopupStatus(
+ Widget w, /* unused */
+ XtPointer closure,
+ XtPointer call_data) /* unused */
+{
+ PopupStatus popup = (PopupStatus)closure;
+ XtPopdown(popup->popup);
+ XtDestroyWidget(popup->popup);
+ if (popup->shell_command)
+ XtFree(popup->shell_command);
+ XtFree((char *) closure);
+}
+
+
+void PopupNotice(
+ String message,
+ XtCallbackProc callback,
+ XtPointer closure)
+{
+ PopupStatus popup_status = (PopupStatus)closure;
+ Widget transientFor;
+ Widget dialog;
+ Widget value;
+ Position x, y;
+ Arg args[3];
+ char command[65], label[128];
+
+ if (popup_status == (PopupStatus)NULL) {
+ popup_status = XtNew(PopupStatusRec);
+ popup_status->lastInput = lastInput;
+ popup_status->shell_command = (char*)NULL;
+ }
+ if (! popup_status->shell_command) {
+ /* MH command */
+ if (sscanf( message, "%64s", command ) != 1)
+ (void) strcpy( command, "system" );
+ else {
+ int l = strlen(command);
+ if (l && command[--l] == ':')
+ command[l] = '\0';
+ }
+ (void) sprintf( label, "%.64s command returned:", command );
+ } else {
+ /* arbitrary shell command */
+ int len = strlen(popup_status->shell_command);
+ (void) sprintf(label, "%.88s %s\nshell command returned:",
+ popup_status->shell_command,
+ ((len > 88) ? "[truncated]" : ""));
+ }
+
+ DeterminePopupPosition(&x, &y, &transientFor);
+ XtSetArg( args[0], XtNallowShellResize, True );
+ XtSetArg( args[1], XtNinput, True );
+ XtSetArg( args[2], XtNtransientFor, transientFor);
+ popup_status->popup = XtCreatePopupShell(XmhNnotice,
+ transientShellWidgetClass, toplevel, args, THREE);
+ PositionThePopup(popup_status->popup, x, y);
+
+ XtSetArg( args[0], XtNlabel, label );
+ XtSetArg( args[1], XtNvalue, message );
+ dialog = XtCreateManagedWidget(XmhNdialog, dialogWidgetClass,
+ popup_status->popup, args, TWO);
+
+ /* The text area of the dialog box will not be editable. */
+ value = XtNameToWidget(dialog, XmhNvalue);
+ XtSetArg( args[0], XtNeditType, XawtextRead);
+ XtSetArg( args[1], XtNdisplayCaret, False);
+ XtSetValues( value, args, TWO);
+ XtOverrideTranslations(value, NoTextSearchAndReplace);
+
+ XawDialogAddButton( dialog, XmhNconfirm,
+ ((callback != (XtCallbackProc) NULL)
+ ? callback : (XtCallbackProc) FreePopupStatus),
+ (XtPointer) popup_status
+ );
+
+ TheUsual(popup_status->popup);
+ InsureVisibility(popup_status->popup, dialog, x, y, False, False);
+ XtPopup(popup_status->popup, XtGrabNone);
+}
+
+
+void PopupConfirm(
+ Widget center_widget, /* where to center; may be NULL */
+ String question,
+ XtCallbackList affirm_callbacks,
+ XtCallbackList negate_callbacks)
+{
+ Widget popup;
+ Widget dialog;
+ Widget button;
+ Widget transientFor;
+ Position x, y;
+ Arg args[3];
+ static XtCallbackRec callbacks[] = {
+ {DestroyPopup, (XtPointer) NULL},
+ {(XtCallbackProc) NULL, (XtPointer) NULL}
+ };
+
+ DeterminePopupPosition(&x, &y, &transientFor);
+ XtSetArg(args[0], XtNinput, True);
+ XtSetArg(args[1], XtNallowShellResize, True);
+ XtSetArg(args[2], XtNtransientFor, transientFor);
+ popup = XtCreatePopupShell(XmhNconfirm, transientShellWidgetClass,
+ toplevel, args, THREE);
+ PositionThePopup(popup, x, y);
+
+ XtSetArg(args[0], XtNlabel, question);
+ dialog = XtCreateManagedWidget(XmhNdialog, dialogWidgetClass, popup, args,
+ ONE);
+
+ callbacks[0].closure = (XtPointer) popup;
+ XtSetArg(args[0], XtNcallback, callbacks);
+ button = XtCreateManagedWidget("yes", commandWidgetClass, dialog,
+ args, ONE);
+ if (affirm_callbacks)
+ XtAddCallbacks(button, XtNcallback, affirm_callbacks);
+
+ button = XtCreateManagedWidget("no", commandWidgetClass, dialog,
+ args, ZERO);
+ XtAddCallback(button, XtNcallback, DestroyPopup, (XtPointer) popup);
+ if (negate_callbacks)
+ XtAddCallbacks(button, XtNcallback, negate_callbacks);
+
+ TheUsual(popup);
+ CenterPopupPosition(center_widget ? center_widget : transientFor,
+ popup, x, y);
+ InsureVisibility(popup, dialog, x, y, False, False);
+ XtPopup(popup, XtGrabNone);
+}
+
+
+void PopupError(
+ Widget widget, /* transient for this top-level shell, or NULL */
+ String message)
+{
+ Widget transFor, error_popup, dialog;
+ Position x, y;
+ Boolean positioned;
+ Arg args[3];
+ static XtCallbackRec callbacks[] = {
+ {DestroyPopup, (XtPointer) NULL},
+ {(XtCallbackProc) NULL, (XtPointer) NULL}
+ };
+
+ transFor = widget;
+ DeterminePopupPosition(&x, &y, transFor ? (Widget*)NULL : &transFor);
+
+ XtSetArg(args[0], XtNallowShellResize, True);
+ XtSetArg(args[1], XtNinput, True);
+ XtSetArg(args[2], XtNtransientFor, transFor);
+ error_popup = XtCreatePopupShell(XmhNerror, transientShellWidgetClass,
+ toplevel, args, THREE);
+ positioned = PositionThePopup(error_popup, x, y);
+
+ XtSetArg(args[0], XtNlabel, message);
+ dialog = XtCreateManagedWidget(XmhNdialog, dialogWidgetClass, error_popup,
+ args, ONE);
+ callbacks[0].closure = (XtPointer) error_popup;
+ XtSetArg(args[0], XtNcallback, callbacks);
+ XawDialogAddButton(dialog, "OK", DestroyPopup, (XtPointer) error_popup);
+ TheUsual(error_popup);
+ InsureVisibility(error_popup, dialog, x, y, !positioned, !positioned);
+ XtPopup(error_popup, XtGrabNone);
+}
+
+/*ARGSUSED*/
+void PopupWarningHandler(
+ String name,
+ String type,
+ String class,
+ String msg,
+ String *params,
+ Cardinal *num)
+{
+ char *ptr;
+ int i;
+ String par[10];
+ char message[500];
+ char buffer[500];
+ static Boolean allowPopup = True; /* protect against recursion */
+
+ XtGetErrorDatabaseText(name, type, class, msg, buffer, 500);
+
+ if (params && num && *num) {
+ i = (*num <= 10) ? *num : 10;
+ memmove( (char*)par, (char*)params, i * sizeof(String));
+ bzero( &par[i], (10-i) * sizeof(String));
+ if (*num > 10)
+ par[9] = "(truncated)";
+ (void) sprintf(message, buffer, par[0], par[1], par[2], par[3],
+ par[4], par[5], par[6], par[7], par[8], par[9]);
+ ptr = message;
+ } else {
+ ptr = buffer;
+ }
+ if (allowPopup) {
+ allowPopup = False;
+ PopupError((Widget)NULL, ptr);
+ allowPopup = True;
+ } else {
+ fprintf(stderr, ptr);
+ }
+}
diff --git a/screen.c b/screen.c
new file mode 100644
index 0000000..50b8221
--- /dev/null
+++ b/screen.c
@@ -0,0 +1,512 @@
+/*
+ * $XConsortium: screen.c,v 2.65 95/01/06 16:39:19 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/screen.c,v 1.3 2002/04/05 21:06:29 dickey Exp $ */
+
+/* scrn.c -- management of scrns. */
+
+#include "xmh.h"
+
+XmhMenuEntryRec folderMenu[] = {
+ {"open", DoOpenFolder},
+ {"openInNew", DoOpenFolderInNewWindow},
+ {"create", DoCreateFolder},
+ {"delete", DoDeleteFolder},
+ {"line", (XtCallbackProc) NULL},
+ {"close", DoClose},
+};
+
+XmhMenuEntryRec tocMenu[] = {
+ {"inc", DoIncorporateNewMail},
+ {"commit", DoCommit},
+ {"pack", DoPack},
+ {"sort", DoSort},
+ {"rescan", DoForceRescan},
+};
+
+XmhMenuEntryRec messageMenu[] = {
+ {"compose", DoComposeMessage},
+ {"next", DoNextView},
+ {"prev", DoPrevView},
+ {"delete", DoDelete},
+ {"move", DoMove},
+ {"copy", DoCopy},
+ {"unmark", DoUnmark},
+ {"viewNew", DoViewNew},
+ {"reply", DoReply},
+ {"forward", DoForward},
+ {"useAsComp", DoTocUseAsComp},
+ {"print", DoPrint},
+};
+
+XmhMenuEntryRec sequenceMenu[] = {
+ {"pick", DoPickMessages},
+ {"openSeq", DoOpenSeq},
+ {"addToSeq", DoAddToSeq},
+ {"removeFromSeq", DoRemoveFromSeq},
+ {"deleteSeq", DoDeleteSeq},
+ {"line", (XtCallbackProc) NULL},
+ {"all", DoSelectSequence},
+};
+
+XmhMenuEntryRec viewMenu[] = {
+ {"reply", DoViewReply},
+ {"forward", DoViewForward},
+ {"useAsComp", DoViewUseAsComposition},
+ {"edit", DoEditView},
+ {"save", DoSaveView},
+ {"print", DoPrintView},
+};
+
+XmhMenuEntryRec optionMenu[] = {
+ {"reverse", DoReverseReadOrder},
+};
+
+XmhMenuButtonDescRec MenuBoxButtons[] = {
+ {"folderButton", "folderMenu", XMH_FOLDER, folderMenu,
+ XtNumber(folderMenu) },
+ {"tocButton", "tocMenu", XMH_TOC, tocMenu,
+ XtNumber(tocMenu) },
+ {"messageButton", "messageMenu", XMH_MESSAGE, messageMenu,
+ XtNumber(messageMenu) },
+ {"sequenceButton", "sequenceMenu", XMH_SEQUENCE, sequenceMenu,
+ XtNumber(sequenceMenu) },
+ {"viewButton", "viewMenu", XMH_VIEW, viewMenu,
+ XtNumber(viewMenu) },
+ {"optionButton", "optionMenu", XMH_OPTION, optionMenu,
+ XtNumber(optionMenu) },
+};
+
+
+/* Fill in the buttons for the view commands. */
+
+static void FillViewButtons(
+Scrn scrn)
+{
+ ButtonBox buttonbox = scrn->viewbuttons;
+ BBoxAddButton(buttonbox, "close", commandWidgetClass, True);
+ BBoxAddButton(buttonbox, "reply", commandWidgetClass, True);
+ BBoxAddButton(buttonbox, "forward", commandWidgetClass, True);
+ BBoxAddButton(buttonbox, "useAsComp", commandWidgetClass, True);
+ BBoxAddButton(buttonbox, "edit", commandWidgetClass, True);
+ BBoxAddButton(buttonbox, "save", commandWidgetClass, False);
+ BBoxAddButton(buttonbox, "print", commandWidgetClass, True);
+ BBoxAddButton(buttonbox, "delete", commandWidgetClass, True);
+}
+
+
+
+static void FillCompButtons(
+Scrn scrn)
+{
+ ButtonBox buttonbox = scrn->viewbuttons;
+ BBoxAddButton(buttonbox, "close", commandWidgetClass, True);
+ BBoxAddButton(buttonbox, "send", commandWidgetClass, True);
+ BBoxAddButton(buttonbox, "reset", commandWidgetClass, True);
+ BBoxAddButton(buttonbox, "compose", commandWidgetClass, True);
+ BBoxAddButton(buttonbox, "save", commandWidgetClass, True);
+ BBoxAddButton(buttonbox, "insert", commandWidgetClass, True);
+}
+
+
+static void MakeCommandMenu(
+ Scrn scrn,
+ XmhMenuButtonDesc mbd)
+{
+ register Cardinal i;
+ Cardinal n;
+ Widget menu;
+ ButtonBox buttonbox = scrn->mainbuttons;
+ XmhMenuEntry e;
+ Boolean indent;
+ WidgetClass widgetclass;
+ Arg args[4];
+ static XtCallbackRec callbacks[] = {
+ { (XtCallbackProc) NULL, (XtPointer) NULL},
+ { (XtCallbackProc) NULL, (XtPointer) NULL},
+ { (XtCallbackProc) NULL, (XtPointer) NULL},
+ };
+
+ /* Menus are created as childen of the Paned widget of the scrn in order
+ * that they can be used both as pop-up and as pull-down menus.
+ */
+
+ n = 0;
+ if (mbd->id == XMH_SEQUENCE) {
+ XtSetArg(args[n], XtNallowShellResize, True); n++;
+ }
+ menu = XtCreatePopupShell(mbd->menu_name, simpleMenuWidgetClass,
+ scrn->widget, args, n);
+
+ indent = (mbd->id == XMH_SEQUENCE || mbd->id == XMH_OPTION) ? True : False;
+ e = mbd->entry;
+ for (i=0; i < mbd->num_entries; i++, e++) {
+ n = 0;
+ if (e->function) {
+ callbacks[0].callback = e->function;
+ callbacks[0].closure = (XtPointer) scrn;
+ callbacks[1].callback = (app_resources.sticky_menu
+ ? (XtCallbackProc) DoRememberMenuSelection
+ : (XtCallbackProc) NULL);
+ XtSetArg(args[n], XtNcallback, callbacks); n++;
+
+ if (indent) { XtSetArg(args[n], XtNleftMargin, 18); n++; }
+ widgetclass = smeBSBObjectClass;
+ } else
+ widgetclass = smeLineObjectClass;
+ XtCreateManagedWidget(e->name, widgetclass, menu, args, n);
+ }
+
+ AttachMenuToButton( BBoxFindButtonNamed( buttonbox, mbd->button_name),
+ menu, mbd->menu_name);
+ if (mbd->id == XMH_OPTION && app_resources.reverse_read_order)
+ ToggleMenuItem(XtNameToWidget(menu, "reverse"), True);
+}
+
+
+/* Create subwidgets for a toc&view window. */
+
+static void MakeTocAndView(Scrn scrn)
+{
+ register int i;
+ XmhMenuButtonDesc mbd;
+ ButtonBox buttonbox;
+ char *name;
+ static XawTextSelectType sarray[] = {XawselectLine,
+ XawselectPosition,
+ XawselectAll,
+ XawselectNull};
+ static Arg args[] = {
+ { XtNselectTypes, (XtArgVal) sarray},
+ { XtNdisplayCaret, (XtArgVal) False}
+ };
+
+ scrn->mainbuttons = BBoxCreate(scrn, "menuBox");
+ scrn->folderlabel = CreateTitleBar(scrn, "folderTitlebar");
+ scrn->folderbuttons = BBoxCreate(scrn, "folders");
+ scrn->toclabel = CreateTitleBar(scrn, "tocTitlebar");
+ scrn->tocwidget = CreateTextSW(scrn, "toc", args, XtNumber(args));
+ if (app_resources.command_button_count > 0)
+ scrn->miscbuttons = BBoxCreate(scrn, "commandBox");
+ scrn->viewlabel = CreateTitleBar(scrn, "viewTitlebar");
+ scrn->viewwidget = CreateTextSW(scrn, "view", args, (Cardinal) 0);
+
+ /* the command buttons and menus */
+
+ buttonbox = scrn->mainbuttons;
+ mbd = MenuBoxButtons;
+ for (i=0; i < XtNumber(MenuBoxButtons); i++, mbd++) {
+ name = mbd->button_name;
+ BBoxAddButton(buttonbox, name, menuButtonWidgetClass, True);
+ MakeCommandMenu(scrn, mbd);
+ }
+
+ /* the folder buttons; folder menus are created on demand. */
+
+ buttonbox = scrn->folderbuttons;
+ for (i=0 ; i<numFolders ; i++) {
+ name = TocName(folderList[i]);
+ if (! IsSubfolder(name))
+ BBoxAddButton(buttonbox, name, menuButtonWidgetClass, True);
+ if (app_resources.new_mail_check &&
+ numScrns > 1 &&
+ TocCanIncorporate(folderList[i]))
+ BBoxMailFlag(buttonbox, name, TocHasMail(folderList[i]));
+ }
+
+ /* the optional miscellaneous command buttons */
+
+ if (app_resources.command_button_count > 0) {
+ char name[12];
+ if (app_resources.command_button_count > 500)
+ app_resources.command_button_count = 500;
+ for (i=1; i <= app_resources.command_button_count; i++) {
+ sprintf(name, "button%d", i);
+ BBoxAddButton(scrn->miscbuttons, name, commandWidgetClass, True);
+ }
+ }
+}
+
+static void MakeView(Scrn scrn)
+{
+ scrn->viewlabel = CreateTitleBar(scrn, "viewTitlebar");
+ scrn->viewwidget = CreateTextSW(scrn, "view", (ArgList)NULL, (Cardinal)0);
+ scrn->viewbuttons = BBoxCreate(scrn, "viewButtons");
+ FillViewButtons(scrn);
+}
+
+
+static void MakeComp(Scrn scrn)
+{
+ scrn->viewlabel = CreateTitleBar(scrn, "composeTitlebar");
+ scrn->viewwidget = CreateTextSW(scrn, "comp", (ArgList)NULL, (Cardinal)0);
+ scrn->viewbuttons = BBoxCreate(scrn, "compButtons");
+ FillCompButtons(scrn);
+}
+
+
+/* Create a scrn of the given type. */
+
+Scrn CreateNewScrn(ScrnKind kind)
+{
+ int i;
+ Scrn scrn;
+ static Arg arglist[] = {
+ { XtNgeometry, (XtArgVal) NULL},
+ { XtNinput, (XtArgVal) True}
+ };
+
+ for (i=0 ; i<numScrns ; i++)
+ if (scrnList[i]->kind == kind && !scrnList[i]->mapped)
+ return scrnList[i];
+ switch (kind) {
+ case STtocAndView: arglist[0].value =
+ (XtArgVal)app_resources.toc_geometry; break;
+ case STview: arglist[0].value =
+ (XtArgVal)app_resources.view_geometry; break;
+ case STcomp: arglist[0].value =
+ (XtArgVal)app_resources.comp_geometry; break;
+ case STpick: arglist[0].value =
+ (XtArgVal)app_resources.pick_geometry; break;
+ }
+
+ numScrns++;
+ scrnList = (Scrn *)
+ XtRealloc((char *) scrnList, (unsigned) numScrns*sizeof(Scrn));
+ scrn = scrnList[numScrns - 1] = XtNew(ScrnRec);
+ bzero((char *)scrn, sizeof(ScrnRec));
+ scrn->kind = kind;
+ if (numScrns == 1) scrn->parent = toplevel;
+ else scrn->parent = XtCreatePopupShell(
+ progName, topLevelShellWidgetClass,
+ toplevel, arglist, XtNumber(arglist));
+ XtAugmentTranslations(scrn->parent,
+ app_resources.wm_protocols_translations);
+ scrn->widget =
+ XtCreateManagedWidget(progName, panedWidgetClass, scrn->parent,
+ (ArgList) NULL, (Cardinal) 0);
+
+ switch (kind) {
+ case STtocAndView: MakeTocAndView(scrn); break;
+ case STview: MakeView(scrn); break;
+ case STcomp: MakeComp(scrn); break;
+ default: break;
+ }
+
+ if (kind != STpick) {
+ int theight, min, max;
+ Arg args[1];
+
+ DEBUG("Realizing...")
+ XtRealizeWidget(scrn->parent);
+ DEBUG(" done.\n")
+
+ switch (kind) {
+ case STtocAndView:
+ BBoxLockSize(scrn->mainbuttons);
+ BBoxLockSize(scrn->folderbuttons);
+ theight = GetHeight(scrn->tocwidget) + GetHeight(scrn->viewwidget);
+ theight = app_resources.toc_percentage * theight / 100;
+ XawPanedGetMinMax((Widget) scrn->tocwidget, &min, &max);
+ XawPanedSetMinMax((Widget) scrn->tocwidget, theight, theight);
+ XawPanedSetMinMax((Widget) scrn->tocwidget, min, max);
+ if (scrn->miscbuttons)
+ BBoxLockSize(scrn->miscbuttons);
+
+ /* fall through */
+
+ case STview:
+
+ /* Install accelerators; not active while editing in the view */
+
+ XtSetArg(args[0], XtNtranslations, &(scrn->edit_translations));
+ XtGetValues(scrn->viewwidget, args, (Cardinal) 1);
+ XtInstallAllAccelerators(scrn->widget, scrn->widget);
+ if (kind == STtocAndView)
+ XtInstallAllAccelerators(scrn->tocwidget, scrn->widget);
+ XtInstallAllAccelerators(scrn->viewwidget, scrn->widget);
+ XtSetArg(args[0], XtNtranslations, &(scrn->read_translations));
+ XtGetValues(scrn->viewwidget, args, (Cardinal) 1);
+
+ if (kind == STview)
+ BBoxLockSize(scrn->viewbuttons);
+ break;
+
+ case STcomp:
+ BBoxLockSize(scrn->viewbuttons);
+ XtInstallAllAccelerators(scrn->viewwidget, scrn->widget);
+ XtSetKeyboardFocus(scrn->parent, scrn->viewwidget);
+ break;
+
+ default:
+ break;
+ }
+
+ InitBusyCursor(scrn);
+ XDefineCursor(XtDisplay(scrn->parent), XtWindow(scrn->parent),
+ app_resources.cursor);
+ (void) XSetWMProtocols(XtDisplay(scrn->parent), XtWindow(scrn->parent),
+ protocolList, XtNumber(protocolList));
+ }
+ scrn->mapped = False;
+ return scrn;
+}
+
+
+Scrn NewViewScrn(void)
+{
+ return CreateNewScrn(STview);
+}
+
+Scrn NewCompScrn(void)
+{
+ Scrn scrn;
+ scrn = CreateNewScrn(STcomp);
+ scrn->assocmsg = (Msg)NULL;
+ return scrn;
+}
+
+void ScreenSetAssocMsg(Scrn scrn, Msg msg)
+{
+ scrn->assocmsg = msg;
+}
+
+/* Destroy the screen. If unsaved changes are in a msg, too bad. */
+
+void DestroyScrn(Scrn scrn)
+{
+ if (scrn->mapped) {
+ scrn->mapped = False;
+ XtPopdown(scrn->parent);
+ TocSetScrn((Toc) NULL, scrn);
+ MsgSetScrnForce((Msg) NULL, scrn);
+ lastInput.win = -1;
+ }
+}
+
+
+void MapScrn(Scrn scrn)
+{
+ if (!scrn->mapped) {
+ XtPopup(scrn->parent, XtGrabNone);
+ scrn->mapped = True;
+ }
+}
+
+
+Scrn ScrnFromWidget(Widget w) /* heavily used, should be efficient */
+{
+ register int i;
+ while (w && ! XtIsTopLevelShell(w))
+ w = XtParent(w);
+ if (w) {
+ for (i=0 ; i<numScrns ; i++) {
+ if (w == (Widget) scrnList[i]->parent)
+ return scrnList[i];
+ }
+ }
+ Punt("ScrnFromWidget failed!");
+ return NULL;
+}
+
+
+/* Figure out which buttons should and shouldn't be enabled in the given
+ * screen. This should be called whenever something major happens to the
+ * screen.
+ */
+
+
+/*ARGSUSED*/
+static void EnableCallback(Widget w, XtPointer data, XtPointer junk)
+{
+ EnableProperButtons( (Scrn) data);
+}
+
+#define SetButton(buttonbox, name, value) \
+ if (value) BBoxEnable(BBoxFindButtonNamed(buttonbox, name)); \
+ else BBoxDisable(BBoxFindButtonNamed(buttonbox, name));
+
+
+void EnableProperButtons(Scrn scrn)
+{
+ int value, changed, reapable;
+ Button button;
+
+ if (scrn) {
+ switch (scrn->kind) {
+ case STtocAndView:
+ button = BBoxFindButtonNamed
+ (scrn->mainbuttons, MenuBoxButtons[XMH_TOC].button_name);
+ value = TocCanIncorporate(scrn->toc);
+ SendMenuEntryEnableMsg(button, "inc", value);
+
+ button = BBoxFindButtonNamed
+ (scrn->mainbuttons, MenuBoxButtons[XMH_SEQUENCE].button_name);
+ value = TocHasSequences(scrn->toc);
+ SendMenuEntryEnableMsg(button, "openSeq", value);
+ SendMenuEntryEnableMsg(button, "addToSeq", value);
+ SendMenuEntryEnableMsg(button, "removeFromSeq", value);
+ SendMenuEntryEnableMsg(button, "deleteSeq", value);
+
+ button = BBoxFindButtonNamed
+ (scrn->mainbuttons, MenuBoxButtons[XMH_VIEW].button_name);
+ value = (scrn->msg != NULL && !MsgGetEditable(scrn->msg));
+ SendMenuEntryEnableMsg(button, "edit", value);
+ SendMenuEntryEnableMsg(button, "save",
+ scrn->msg != NULL && !value);
+ break;
+ case STview:
+ value = (scrn->msg != NULL && !MsgGetEditable(scrn->msg));
+ SetButton(scrn->viewbuttons, "edit", value);
+ SetButton(scrn->viewbuttons, "save", scrn->msg != NULL && !value);
+ break;
+ case STcomp:
+ if (scrn->msg != NULL) {
+ changed = MsgChanged(scrn->msg);
+ reapable = MsgGetReapable(scrn->msg);
+ SetButton(scrn->viewbuttons, "send", changed || !reapable);
+ SetButton(scrn->viewbuttons, "save", changed || reapable);
+ SetButton(scrn->viewbuttons, "insert",
+ scrn->assocmsg != NULL ? True : False);
+
+ if (!changed)
+ MsgSetCallOnChange(scrn->msg, EnableCallback,
+ (XtPointer) scrn);
+ else
+ MsgSetCallOnChange(scrn->msg, (XtCallbackProc) NULL,
+ (XtPointer) NULL);
+
+ } else {
+ BBoxDisable( BBoxFindButtonNamed(scrn->viewbuttons, "send"));
+ BBoxDisable( BBoxFindButtonNamed(scrn->viewbuttons, "save"));
+ BBoxDisable( BBoxFindButtonNamed(scrn->viewbuttons, "insert"));
+ }
+ break;
+ default:
+ break;
+ }
+ }
+}
diff --git a/toc.c b/toc.c
new file mode 100644
index 0000000..c3bcd49
--- /dev/null
+++ b/toc.c
@@ -0,0 +1,1220 @@
+/* $XConsortium: toc.c,v 2.59 95/01/09 16:52:53 swick Exp $
+ * $XFree86: xc/programs/xmh/toc.c,v 3.5 2002/04/05 21:06:29 dickey Exp $
+ *
+ *
+ * COPYRIGHT 1987
+ * 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.
+ */
+
+/* toc.c -- handle things in the toc widget. */
+
+#include "xmh.h"
+#include "tocintrnl.h"
+#include "toc.h"
+#include "tocutil.h"
+#include "actions.h"
+
+#include <sys/stat.h>
+
+static int IsDir(char *name)
+{
+ char str[500];
+ struct stat buf;
+ if (*name == '.')
+ return FALSE;
+ (void) sprintf(str, "%s/%s", app_resources.mail_path, name);
+ if (stat(str, &buf) /* failed */) return False;
+#ifdef S_ISDIR
+ return S_ISDIR(buf.st_mode);
+#else
+ return (buf.st_mode & S_IFMT) == S_IFDIR;
+#endif
+}
+
+
+static void MakeSureFolderExists(
+ char ***namelistptr,
+ int *numfoldersptr,
+ char *name)
+{
+ int i;
+ char str[200];
+ for (i=0 ; i<*numfoldersptr ; i++)
+ if (strcmp((*namelistptr)[i], name) == 0) return;
+ (void) sprintf(str, "%s/%s", app_resources.mail_path, name);
+ (void) mkdir(str, 0700);
+ *numfoldersptr = ScanDir(app_resources.mail_path, namelistptr, IsDir);
+ for (i=0 ; i<*numfoldersptr ; i++)
+ if (strcmp((*namelistptr)[i], name) == 0) return;
+ Punt("Can't create new mail folder!");
+}
+
+
+static void MakeSureSubfolderExists(
+ char *** namelistptr,
+ int * numfoldersptr,
+ char * name)
+{
+ char folder[300];
+ char subfolder_path[300];
+ char *subfolder;
+ struct stat buf;
+
+ /* Make sure that the parent folder exists */
+
+ subfolder = strchr( strcpy(folder, name), '/');
+ *subfolder = '\0';
+ subfolder++;
+ MakeSureFolderExists(namelistptr, numfoldersptr, folder);
+
+ /* The parent folder exists. Make sure the subfolder exists. */
+
+ (void) sprintf(subfolder_path, "%s/%s", app_resources.mail_path, name);
+ if (stat(subfolder_path, &buf) /* failed */) {
+ (void) mkdir(subfolder_path, 0700);
+ if (stat(subfolder_path, &buf) /* failed */)
+ Punt("Can't create new xmh subfolder!");
+ }
+#ifdef S_ISDIR
+ if (!S_ISDIR(buf.st_mode))
+#else
+ if ((buf.st_mode & S_IFMT) != S_IFDIR)
+#endif
+ Punt("Can't create new xmh subfolder!");
+}
+
+int TocFolderExists(Toc toc)
+{
+ struct stat buf;
+ if (! toc->path) {
+ char str[500];
+ (void) sprintf(str, "%s/%s", app_resources.mail_path, toc->foldername);
+ toc->path = XtNewString(str);
+ }
+ return ((stat(toc->path, &buf) == 0) &&
+#ifdef S_ISDIR
+ (S_ISDIR(buf.st_mode)));
+#else
+ ((buf.st_mode & S_IFMT) == S_IFDIR));
+#endif
+}
+
+static void LoadCheckFiles(void)
+{
+ FILE *fid;
+ char str[1024];
+
+ (void) sprintf(str, "%s/.xmhcheck", homeDir);
+ fid = myfopen(str, "r");
+ if (fid) {
+ int i;
+ char *ptr, *ptr2;
+
+ while ((ptr = ReadLine(fid))) {
+ while (*ptr == ' ' || *ptr == '\t') ptr++;
+ ptr2 = ptr;
+ while (*ptr2 && *ptr2 != ' ' && *ptr2 != '\t') ptr2++;
+ if (*ptr2 == 0) continue;
+ *ptr2++ = 0;
+ while (*ptr2 == ' ' || *ptr2 == '\t') ptr2++;
+ if (*ptr2 == 0) continue;
+ for (i=0 ; i<numFolders ; i++) {
+ if (strcmp(ptr, folderList[i]->foldername) == 0) {
+ folderList[i]->incfile = XtNewString(ptr2);
+ break;
+ }
+ }
+ }
+ myfclose(fid);
+ } else if ( app_resources.initial_inc_file &&
+ *app_resources.initial_inc_file)
+ InitialFolder->incfile = app_resources.initial_inc_file;
+}
+
+
+/* PUBLIC ROUTINES */
+
+
+/* Read in the list of folders. */
+
+void TocInit(void)
+{
+ Toc toc;
+ char **namelist;
+ int i;
+ numFolders = ScanDir(app_resources.mail_path, &namelist, IsDir);
+ if (numFolders < 0) {
+ (void) mkdir(app_resources.mail_path, 0700);
+ numFolders = ScanDir(app_resources.mail_path, &namelist, IsDir);
+ if (numFolders < 0)
+ Punt("Can't create or read mail directory!");
+ }
+ if (IsSubfolder(app_resources.initial_folder_name))
+ MakeSureSubfolderExists(&namelist, &numFolders,
+ app_resources.initial_folder_name);
+ else
+ MakeSureFolderExists(&namelist, &numFolders,
+ app_resources.initial_folder_name);
+
+ if (IsSubfolder(app_resources.drafts_folder_name))
+ MakeSureSubfolderExists(&namelist, &numFolders,
+ app_resources.drafts_folder_name);
+ else
+ MakeSureFolderExists(&namelist, &numFolders,
+ app_resources.drafts_folder_name);
+ folderList = (Toc *) XtMalloc((Cardinal)numFolders * sizeof(Toc));
+ for (i=0 ; i<numFolders ; i++) {
+ toc = folderList[i] = TUMalloc();
+ toc->foldername = XtNewString(namelist[i]);
+ free((char *)namelist[i]);
+ }
+ if (! (InitialFolder = TocGetNamed(app_resources.initial_folder_name)))
+ InitialFolder = TocCreate(app_resources.initial_folder_name);
+
+ if (! (DraftsFolder = TocGetNamed(app_resources.drafts_folder_name)))
+ DraftsFolder = TocCreate(app_resources.drafts_folder_name);
+ free((char *)namelist);
+ LoadCheckFiles();
+}
+
+
+
+/* Create a toc and add a folder to the folderList. */
+
+Toc TocCreate(char *foldername)
+{
+ Toc toc = TUMalloc();
+
+ toc->foldername = XtNewString(foldername);
+ folderList = (Toc *) XtRealloc((char *) folderList,
+ (unsigned) ++numFolders * sizeof(Toc));
+ folderList[numFolders - 1] = toc;
+ return toc;
+}
+
+
+/* Create a new folder with the given name. */
+
+Toc TocCreateFolder(char *foldername)
+{
+ Toc toc;
+ char str[500];
+ if (TocGetNamed(foldername)) return NULL;
+ (void) sprintf(str, "%s/%s", app_resources.mail_path, foldername);
+ if (mkdir(str, 0700) < 0) return NULL;
+ toc = TocCreate(foldername);
+ return toc;
+}
+
+int TocHasMail(Toc toc)
+{
+ return toc->mailpending;
+}
+
+static int CheckForNewMail(Toc toc)
+{
+ if (toc->incfile)
+ return (GetFileLength(toc->incfile) > 0);
+ else if (toc == InitialFolder) {
+ char **argv;
+ char *result;
+ int hasmail;
+
+ argv = MakeArgv(4);
+ argv[0] = "msgchk";
+ argv[1] = "-nonotify";
+ argv[2] = "nomail";
+ argv[3] = "-nodate";
+ result = DoCommandToString(argv);
+ hasmail = (*result != '\0');
+ XtFree(result);
+ XtFree((char*)argv);
+ return hasmail;
+ }
+ return False;
+}
+
+/*ARGSUSED*/
+void TocCheckForNewMail(
+ Boolean update) /* if True, actually make the check */
+{
+ Toc toc;
+ Scrn scrn;
+ int i, j, hasmail;
+ Boolean mail_waiting = False;
+
+ if (update) {
+ for (i=0 ; i<numFolders ; i++) {
+ toc = folderList[i];
+ if (TocCanIncorporate(toc)) {
+ toc->mailpending = hasmail = CheckForNewMail(toc);
+ if (hasmail) mail_waiting = True;
+ for (j=0 ; j<numScrns ; j++) {
+ scrn = scrnList[j];
+ if (scrn->kind == STtocAndView)
+ /* give visual indication of new mail waiting */
+ BBoxMailFlag(scrn->folderbuttons, TocName(toc),
+ hasmail);
+ }
+ }
+ }
+ } else {
+ for (i=0; i < numFolders; i++) {
+ toc = folderList[i];
+ if (toc->mailpending) {
+ mail_waiting = True;
+ break;
+ }
+ }
+ }
+
+ if (app_resources.mail_waiting_flag) {
+ Arg args[1];
+ static Boolean icon_state = -1;
+
+ if (icon_state != mail_waiting) {
+ icon_state = mail_waiting;
+ for (i=0; i < numScrns; i++) {
+ scrn = scrnList[i];
+ if (scrn->kind == STtocAndView) {
+ XtSetArg(args[0], XtNiconPixmap,
+ (mail_waiting ? app_resources.new_mail_icon
+ : app_resources.no_mail_icon));
+ XtSetValues(scrn->parent, args, (Cardinal)1);
+ }
+ }
+ }
+ }
+}
+
+/* Intended to support mutual exclusion on deleting folders, so that you
+ * cannot have two confirm popups at the same time on the same folder.
+ *
+ * You can have confirm popups on different folders simultaneously.
+ * However, I did not protect the user from popping up a delete confirm
+ * popup on folder A, then popping up a delete confirm popup on folder
+ * A/subA, then deleting A, then deleting A/subA -- which of course is
+ * already gone, and will cause xmh to Punt.
+ *
+ * TocClearDeletePending is a callback from the No confirmation button
+ * of the confirm popup.
+ */
+
+Boolean TocTestAndSetDeletePending(Toc toc)
+{
+ Boolean flag;
+
+ flag = toc->delete_pending;
+ toc->delete_pending = True;
+ return flag;
+}
+
+void TocClearDeletePending(Toc toc)
+{
+ toc->delete_pending = False;
+}
+
+
+/* Recursively delete an entire directory. Nasty. */
+
+static void NukeDirectory(char *path)
+{
+ struct stat buf;
+
+#ifdef S_IFLNK
+ /* POSIX.1 does not discuss symbolic links. */
+ if (lstat(path, &buf) /* failed */)
+ return;
+ if ((buf.st_mode & S_IFMT) == S_IFLNK) {
+ (void) unlink(path);
+ return;
+ }
+#endif
+ if (stat(path, &buf) /* failed */)
+ return;
+ if (buf.st_mode & S_IWRITE) {
+ char **argv = MakeArgv(3);
+ argv[0] = "/bin/rm";
+ argv[1] = "-rf";
+ argv[2] = path;
+ (void) DoCommand(argv, (char*)NULL, (char*)NULL);
+ XtFree((char*)argv);
+ }
+}
+
+
+/* Destroy the given folder. */
+
+void TocDeleteFolder(Toc toc)
+{
+ Toc toc2;
+ int i, j, w;
+ if (toc == NULL) return;
+ TUGetFullFolderInfo(toc);
+
+ w = -1;
+ for (i=0 ; i<numFolders ; i++) {
+ toc2 = folderList[i];
+ if (toc2 == toc)
+ w = i;
+ else if (toc2->validity == valid)
+ for (j=0 ; j<toc2->nummsgs ; j++)
+ if (toc2->msgs[j]->desttoc == toc)
+ MsgSetFate(toc2->msgs[j], Fignore, (Toc) NULL);
+ }
+ if (w < 0) Punt("Couldn't find it in TocDeleteFolder!");
+ NukeDirectory(toc->path);
+ if (toc->validity == valid) {
+ for (i=0 ; i<toc->nummsgs ; i++) {
+ MsgSetScrnForce(toc->msgs[i], (Scrn) NULL);
+ MsgFree(toc->msgs[i]);
+ }
+ XtFree((char *) toc->msgs);
+ }
+ XtFree((char *)toc);
+ numFolders--;
+ for (i=w ; i<numFolders ; i++) folderList[i] = folderList[i+1];
+}
+
+
+/*
+ * Display the given toc in the given scrn. If scrn is NULL, then remove the
+ * toc from all scrns displaying it.
+ */
+
+void TocSetScrn(Toc toc, Scrn scrn)
+{
+ Cardinal i;
+
+ if (toc == NULL && scrn == NULL) return;
+ if (scrn == NULL) {
+ for (i=0 ; i<toc->num_scrns ; i++)
+ TocSetScrn((Toc) NULL, toc->scrn[i]);
+ return;
+ }
+ if (scrn->toc == toc) return;
+ if (scrn->toc != NULL) {
+ for (i=0 ; i<scrn->toc->num_scrns ; i++)
+ if (scrn->toc->scrn[i] == scrn) break;
+ if (i >= scrn->toc->num_scrns)
+ Punt("Couldn't find scrn in TocSetScrn!");
+ scrn->toc->scrn[i] = scrn->toc->scrn[--scrn->toc->num_scrns];
+ }
+ scrn->toc = toc;
+ if (toc == NULL) {
+ TUResetTocLabel(scrn);
+ TURedisplayToc(scrn);
+ StoreWindowName(scrn, progName);
+ } else {
+ toc->num_scrns++;
+ toc->scrn = (Scrn *) XtRealloc((char *) toc->scrn,
+ (unsigned)toc->num_scrns*sizeof(Scrn));
+ toc->scrn[toc->num_scrns - 1] = scrn;
+ TUEnsureScanIsValidAndOpen(toc, True);
+ TUResetTocLabel(scrn);
+ if (app_resources.prefix_wm_and_icon_name) {
+ char wm_name[64];
+ int length = strlen(progName);
+ (void) strncpy(wm_name, progName, length);
+ (void) strncpy(wm_name + length , ": ", 2);
+ (void) strcpy(wm_name + length + 2, toc->foldername);
+ StoreWindowName(scrn, wm_name);
+ }
+ else
+ StoreWindowName(scrn, toc->foldername);
+ TURedisplayToc(scrn);
+ SetCurrentFolderName(scrn, toc->foldername);
+ }
+ EnableProperButtons(scrn);
+}
+
+
+
+/* Remove the given message from the toc. Doesn't actually touch the file.
+ Also note that it does not free the storage for the msg. */
+
+void TocRemoveMsg(Toc toc, Msg msg)
+{
+ Msg newcurmsg;
+ MsgList mlist;
+ int i;
+ if (toc->validity == unknown)
+ TUGetFullFolderInfo(toc);
+ if (toc->validity != valid)
+ return;
+ newcurmsg = TocMsgAfter(toc, msg);
+ if (newcurmsg) newcurmsg->changed = TRUE;
+ newcurmsg = toc->curmsg;
+ if (msg == toc->curmsg) {
+ newcurmsg = TocMsgAfter(toc, msg);
+ if (newcurmsg == NULL) newcurmsg = TocMsgBefore(toc, msg);
+ toc->curmsg = NULL;
+ }
+ toc->length -= msg->length;
+ if (msg->visible) toc->lastPos -= msg->length;
+ for(i = TUGetMsgPosition(toc, msg), toc->nummsgs--; i<toc->nummsgs ; i++) {
+ toc->msgs[i] = toc->msgs[i+1];
+ if (msg->visible) toc->msgs[i]->position -= msg->length;
+ }
+ for (i=0 ; i<toc->numsequences ; i++) {
+ mlist = toc->seqlist[i]->mlist;
+ if (mlist) DeleteMsgFromMsgList(mlist, msg);
+ }
+
+ if (msg->visible && toc->num_scrns > 0 && !toc->needsrepaint)
+ TSourceInvalid(toc, msg->position, -msg->length);
+ TocSetCurMsg(toc, newcurmsg);
+ TUSaveTocFile(toc);
+}
+
+
+
+void TocRecheckValidity(Toc toc)
+{
+ Cardinal i;
+
+ if (toc && toc->validity == valid && TUScanFileOutOfDate(toc)) {
+ if (app_resources.block_events_on_busy) ShowBusyCursor();
+
+ TUScanFileForToc(toc);
+ if (toc->source)
+ TULoadTocFile(toc);
+ for (i=0 ; i<toc->num_scrns ; i++)
+ TURedisplayToc(toc->scrn[i]);
+
+ if (app_resources.block_events_on_busy) UnshowBusyCursor();
+ }
+}
+
+
+/* Set the current message. */
+
+void TocSetCurMsg(Toc toc, Msg msg)
+{
+ Msg msg2;
+ Cardinal i;
+
+ if (toc->validity != valid) return;
+ if (msg != toc->curmsg) {
+ msg2 = toc->curmsg;
+ toc->curmsg = msg;
+ if (msg2)
+ MsgSetFate(msg2, msg2->fate, msg2->desttoc);
+ }
+ if (msg) {
+ MsgSetFate(msg, msg->fate, msg->desttoc);
+ if (toc->num_scrns) {
+ if (toc->stopupdate)
+ toc->needsrepaint = TRUE;
+ else {
+ for (i=0 ; i<toc->num_scrns ; i++)
+ XawTextSetInsertionPoint(toc->scrn[i]->tocwidget,
+ msg->position);
+ }
+ }
+ }
+}
+
+
+/* Return the current message. */
+
+Msg TocGetCurMsg(Toc toc)
+{
+ return toc->curmsg;
+}
+
+
+
+
+/* Return the message after the given one. (If none, return NULL.) */
+
+Msg TocMsgAfter(Toc toc, Msg msg)
+{
+ int i;
+ i = TUGetMsgPosition(toc, msg);
+ do {
+ i++;
+ if (i >= toc->nummsgs)
+ return NULL;
+ } while (!(toc->msgs[i]->visible));
+ return toc->msgs[i];
+}
+
+
+
+/* Return the message before the given one. (If none, return NULL.) */
+
+Msg TocMsgBefore(Toc toc, Msg msg)
+{
+ int i;
+ i = TUGetMsgPosition(toc, msg);
+ do {
+ i--;
+ if (i < 0)
+ return NULL;
+ } while (!(toc->msgs[i]->visible));
+ return toc->msgs[i];
+}
+
+
+
+/* The caller KNOWS the toc's information is out of date; rescan it. */
+
+void TocForceRescan(Toc toc)
+{
+ register Cardinal i;
+
+ if (toc->num_scrns) {
+ toc->viewedseq = toc->seqlist[0];
+ for (i=0 ; i<toc->num_scrns ; i++)
+ TUResetTocLabel(toc->scrn[i]);
+ TUScanFileForToc(toc);
+ TULoadTocFile(toc);
+ for (i=0 ; i<toc->num_scrns ; i++)
+ TURedisplayToc(toc->scrn[i]);
+ } else {
+ TUGetFullFolderInfo(toc);
+ (void) unlink(toc->scanfile);
+ toc->validity = invalid;
+ }
+}
+
+
+
+/* The caller has just changed a sequence list. Reread them from mh. */
+
+void TocReloadSeqLists(Toc toc)
+{
+ Cardinal i;
+
+ TocSetCacheValid(toc);
+ TULoadSeqLists(toc);
+ TURefigureWhatsVisible(toc);
+ for (i=0 ; i<toc->num_scrns ; i++) {
+ TUResetTocLabel(toc->scrn[i]);
+ EnableProperButtons(toc->scrn[i]);
+ }
+}
+
+
+/*ARGSUSED*/
+void XmhReloadSeqLists(
+ Widget w,
+ XEvent *event,
+ String *params,
+ Cardinal *num_params)
+{
+ Scrn scrn = ScrnFromWidget(w);
+ TocReloadSeqLists(scrn->toc);
+ TUCheckSequenceMenu(scrn->toc);
+}
+
+
+
+/* Return TRUE if the toc has an interesting sequence. */
+
+int TocHasSequences(Toc toc)
+{
+ return toc && toc->numsequences > 1;
+}
+
+
+/* Change which sequence is being viewed. */
+
+void TocChangeViewedSeq(Toc toc, Sequence seq)
+{
+ if (seq == NULL) seq = toc->viewedseq;
+ toc->viewedseq = seq;
+ toc->force_reset = True; /* %%% force Text source to be reset */
+ TURefigureWhatsVisible(toc);
+}
+
+
+/* Return the sequence with the given name in the given toc. */
+
+Sequence TocGetSeqNamed(Toc toc, char *name)
+{
+ register int i;
+ if (name == NULL)
+ return (Sequence) NULL;
+
+ for (i=0 ; i<toc->numsequences ; i++)
+ if (strcmp(toc->seqlist[i]->name, name) == 0)
+ return toc->seqlist[i];
+ return (Sequence) NULL;
+}
+
+
+/* Return the sequence currently being viewed in the toc. */
+
+Sequence TocViewedSequence(Toc toc)
+{
+ return toc->viewedseq;
+}
+
+
+/* Set the selected sequence in the toc */
+
+void TocSetSelectedSequence(
+ Toc toc,
+ Sequence sequence)
+{
+ if (toc)
+ toc->selectseq = sequence;
+}
+
+
+/* Return the sequence currently selected */
+
+Sequence TocSelectedSequence(Toc toc)
+{
+ if (toc) return (toc->selectseq);
+ else return (Sequence) NULL;
+}
+
+
+/* Return the list of messages currently selected. */
+
+#define SrcScan XawTextSourceScan
+
+MsgList TocCurMsgList(Toc toc)
+{
+ MsgList result;
+ XawTextPosition pos1, pos2;
+
+ if (toc->num_scrns == 0) return NULL;
+ result = MakeNullMsgList();
+ XawTextGetSelectionPos( toc->scrn[0]->tocwidget, &pos1, &pos2); /* %%% */
+ if (pos1 < pos2) {
+ pos1 = SrcScan(toc->source, pos1, XawstEOL, XawsdLeft, 1, FALSE);
+ pos2 = SrcScan(toc->source, pos2, XawstPositions, XawsdLeft, 1, TRUE);
+ pos2 = SrcScan(toc->source, pos2, XawstEOL, XawsdRight, 1, FALSE);
+ while (pos1 < pos2) {
+ AppendMsgList(result, MsgFromPosition(toc, pos1, XawsdRight));
+ pos1 = SrcScan(toc->source, pos1, XawstEOL, XawsdRight, 1, TRUE);
+ }
+ }
+ return result;
+}
+
+
+
+/* Unset the current selection. */
+
+void TocUnsetSelection(Toc toc)
+{
+ if (toc->source)
+ XawTextUnsetSelection(toc->scrn[0]->tocwidget);
+}
+
+
+
+/* Create a brand new, blank message. */
+
+Msg TocMakeNewMsg(Toc toc)
+{
+ Msg msg;
+ static int looping = False;
+ TUEnsureScanIsValidAndOpen(toc, False);
+ msg = TUAppendToc(toc, "#### empty\n");
+ if (FileExists(MsgFileName(msg))) {
+ if (looping++) Punt( "Cannot correct scan file" );
+ DEBUG2("**** FOLDER %s WAS INVALID; msg %d already existed!\n",
+ toc->foldername, msg->msgid);
+ TocForceRescan(toc);
+ return TocMakeNewMsg(toc); /* Try again. Using recursion here is ugly,
+ but what the hack ... */
+ }
+ CopyFileAndCheck("/dev/null", MsgFileName(msg));
+ looping = False;
+ return msg;
+}
+
+
+/* Set things to not update cache or display until further notice. */
+
+void TocStopUpdate(Toc toc)
+{
+ Cardinal i;
+
+ for (i=0 ; i<toc->num_scrns ; i++)
+ XawTextDisableRedisplay(toc->scrn[i]->tocwidget);
+ toc->stopupdate++;
+}
+
+
+/* Start updating again, and do whatever updating has been queued. */
+
+void TocStartUpdate(Toc toc)
+{
+ Cardinal i;
+
+ if (toc->stopupdate && --(toc->stopupdate) == 0) {
+ for (i=0 ; i<toc->num_scrns ; i++) {
+ if (toc->needsrepaint)
+ TURedisplayToc(toc->scrn[i]);
+ if (toc->needslabelupdate)
+ TUResetTocLabel(toc->scrn[i]);
+ }
+ if (toc->needscachesave)
+ TUSaveTocFile(toc);
+ }
+ for (i=0 ; i<toc->num_scrns ; i++)
+ XawTextEnableRedisplay(toc->scrn[i]->tocwidget);
+}
+
+
+
+/* Something has happened that could later convince us that our cache is out
+ of date. Make this not happen; our cache really *is* up-to-date. */
+
+void TocSetCacheValid(Toc toc)
+{
+ TUSaveTocFile(toc);
+}
+
+
+/* Return the full folder pathname of the given toc, prefixed w/'+' */
+
+char *TocMakeFolderName(Toc toc)
+{
+ char* name = XtMalloc((Cardinal) (strlen(toc->path) + 2) );
+ (void)sprintf( name, "+%s", toc->path );
+ return name;
+}
+
+char *TocName(Toc toc)
+{
+ return toc->foldername;
+}
+
+
+
+/* Given a foldername, return the corresponding toc. */
+
+Toc TocGetNamed(char *name)
+{
+ int i;
+ for (i=0; i<numFolders ; i++)
+ if (strcmp(folderList[i]->foldername, name) == 0) return folderList[i];
+ return NULL;
+}
+
+
+Boolean TocHasChanges(Toc toc)
+{
+ int i;
+ for (i=0 ; i<toc->nummsgs ; i++)
+ if (toc->msgs[i]->fate != Fignore) return True;
+
+ return False;
+}
+
+
+
+/* Throw out all changes to this toc, and close all views of msgs in it.
+ Requires confirmation by the user. */
+
+/*ARGSUSED*/
+static void TocCataclysmOkay(
+ Widget widget, /* unused */
+ XtPointer client_data,
+ XtPointer call_data) /* unused */
+{
+ Toc toc = (Toc) client_data;
+ register int i;
+
+ for (i=0; i < toc->nummsgs; i++)
+ MsgSetFate(toc->msgs[i], Fignore, (Toc)NULL);
+
+/* Doesn't make sense to have this MsgSetScrn for loop here. dmc. %%% */
+ for (i=0; i < toc->nummsgs; i++)
+ MsgSetScrn(toc->msgs[i], (Scrn) NULL, (XtCallbackList) NULL,
+ (XtCallbackList) NULL);
+}
+
+int TocConfirmCataclysm(
+ Toc toc,
+ XtCallbackList confirms,
+ XtCallbackList cancels)
+{
+ register int i;
+
+ static XtCallbackRec yes_callbacks[] = {
+ {TocCataclysmOkay, (XtPointer) NULL},
+ {(XtCallbackProc) NULL, (XtPointer) NULL},
+ {(XtCallbackProc) NULL, (XtPointer) NULL}
+ };
+
+ if (! toc)
+ return 0;
+
+ if (TocHasChanges(toc)) {
+ char str[300];
+ Widget tocwidget;
+
+ (void)sprintf(str,"Are you sure you want to remove all changes to %s?",
+ toc->foldername);
+ yes_callbacks[0].closure = (XtPointer) toc;
+ yes_callbacks[1].callback = confirms[0].callback;
+ yes_callbacks[1].closure = confirms[0].closure;
+
+ tocwidget = NULL;
+ for (i=0; i < toc->num_scrns; i++)
+ if (toc->scrn[i]->mapped) {
+ tocwidget = toc->scrn[i]->tocwidget;
+ break;
+ }
+
+ PopupConfirm(tocwidget, str, yes_callbacks, cancels);
+ return NEEDS_CONFIRMATION;
+ }
+ else {
+/* Doesn't make sense to have this MsgSetFate for loop here. dmc. %%% */
+ for (i=0 ; i<toc->nummsgs ; i++)
+ MsgSetFate(toc->msgs[i], Fignore, (Toc)NULL);
+
+ for (i=0 ; i<toc->nummsgs ; i++)
+ if (MsgSetScrn(toc->msgs[i], (Scrn) NULL, confirms, cancels))
+ return NEEDS_CONFIRMATION;
+ return 0;
+ }
+}
+
+
+/* Commit all the changes in this toc; all messages will meet their 'fate'. */
+
+/*ARGSUSED*/
+void TocCommitChanges(
+ Widget widget, /* unused */
+ XtPointer client_data,
+ XtPointer call_data) /* unused */
+{
+ Toc toc = (Toc) client_data;
+ Msg msg;
+ int i, cur = 0;
+ char str[100], **argv = NULL;
+ FateType curfate, fate;
+ Toc desttoc;
+ Toc curdesttoc = NULL;
+ XtCallbackRec confirms[2];
+
+ confirms[0].callback = TocCommitChanges;
+ confirms[0].closure = (XtPointer) toc;
+ confirms[1].callback = (XtCallbackProc) NULL;
+ confirms[1].closure = (XtPointer) NULL;
+
+ if (toc == NULL) return;
+ for (i=0 ; i<toc->nummsgs ; i++) {
+ msg = toc->msgs[i];
+ fate = MsgGetFate(msg, (Toc *)NULL);
+ if (fate != Fignore && fate != Fcopy)
+ if (MsgSetScrn(msg, (Scrn) NULL, confirms, (XtCallbackList) NULL)
+ == NEEDS_CONFIRMATION)
+ return;
+ }
+ XFlush(XtDisplay(toc->scrn[0]->parent));
+ for (i=0 ; i<numFolders ; i++)
+ TocStopUpdate(folderList[i]);
+ toc->haschanged = TRUE;
+ if (app_resources.block_events_on_busy) ShowBusyCursor();
+
+ do {
+ curfate = Fignore;
+ i = 0;
+ while (i < toc->nummsgs) {
+ msg = toc->msgs[i];
+ fate = MsgGetFate(msg, &desttoc);
+ if (curfate == Fignore && fate != Fignore) {
+ curfate = fate;
+ argv = MakeArgv(2);
+ switch (curfate) {
+ case Fdelete:
+ argv[0] = XtNewString("rmm");
+ argv[1] = TocMakeFolderName(toc);
+ cur = 2;
+ curdesttoc = NULL;
+ break;
+ case Fmove:
+ case Fcopy:
+ argv[0] = XtNewString("refile");
+ cur = 1;
+ curdesttoc = desttoc;
+ break;
+ default:
+ break;
+ }
+ }
+ if (curfate != Fignore &&
+ curfate == fate && desttoc == curdesttoc) {
+ argv = ResizeArgv(argv, cur + 1);
+ (void) sprintf(str, "%d", MsgGetId(msg));
+ argv[cur++] = XtNewString(str);
+ MsgSetFate(msg, Fignore, (Toc)NULL);
+ if (curdesttoc) {
+ (void) TUAppendToc(curdesttoc, MsgGetScanLine(msg));
+ curdesttoc->haschanged = TRUE;
+ }
+ if (curfate != Fcopy) {
+ TocRemoveMsg(toc, msg);
+ MsgFree(msg);
+ i--;
+ }
+ if (cur > 40)
+ break; /* Do only 40 at a time, just to be safe. */
+ }
+ i++;
+ }
+ if (curfate != Fignore) {
+ switch (curfate) {
+ case Fmove:
+ case Fcopy:
+ argv = ResizeArgv(argv, cur + 4);
+ argv[cur++] = XtNewString(curfate == Fmove ? "-nolink"
+ : "-link");
+ argv[cur++] = XtNewString("-src");
+ argv[cur++] = TocMakeFolderName(toc);
+ argv[cur++] = TocMakeFolderName(curdesttoc);
+ break;
+ default:
+ break;
+ }
+ if (app_resources.debug) {
+ for (i = 0; i < cur; i++)
+ (void) fprintf(stderr, "%s ", argv[i]);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ }
+ DoCommand(argv, (char *) NULL, (char *) NULL);
+ for (i = 0; argv[i]; i++)
+ XtFree((char *) argv[i]);
+ XtFree((char *) argv);
+ }
+ } while (curfate != Fignore);
+ for (i=0 ; i<numFolders ; i++) {
+ if (folderList[i]->haschanged) {
+ TocReloadSeqLists(folderList[i]);
+ folderList[i]->haschanged = FALSE;
+ }
+ TocStartUpdate(folderList[i]);
+ }
+
+ if (app_resources.block_events_on_busy) UnshowBusyCursor();
+}
+
+
+
+/* Return whether the given toc can incorporate mail. */
+
+int TocCanIncorporate(Toc toc)
+{
+ return (toc && (toc == InitialFolder || toc->incfile));
+}
+
+
+/* Incorporate new messages into the given toc. */
+
+int TocIncorporate(Toc toc)
+{
+ char **argv;
+ char str[100], *file, *ptr;
+ Msg msg, firstmessage = NULL;
+ FILEPTR fid;
+
+ argv = MakeArgv(toc->incfile ? 7 : 4);
+ argv[0] = "inc";
+ argv[1] = TocMakeFolderName(toc);
+ argv[2] = "-width";
+ (void) sprintf(str, "%d", app_resources.toc_width);
+ argv[3] = str;
+ if (toc->incfile) {
+ argv[4] = "-file";
+ argv[5] = toc->incfile;
+ argv[6] = "-truncate";
+ }
+ if (app_resources.block_events_on_busy) ShowBusyCursor();
+
+ file = DoCommandToFile(argv);
+ XtFree(argv[1]);
+ XtFree((char *)argv);
+ TUGetFullFolderInfo(toc);
+ if (toc->validity == valid) {
+ fid = FOpenAndCheck(file, "r");
+ TocStopUpdate(toc);
+ while ((ptr = ReadLineWithCR(fid))) {
+ if (atoi(ptr) > 0) {
+ msg = TUAppendToc(toc, ptr);
+ if (firstmessage == NULL) firstmessage = msg;
+ }
+ }
+ if (firstmessage && firstmessage->visible) {
+ TocSetCurMsg(toc, firstmessage);
+ }
+ TocStartUpdate(toc);
+ myfclose(fid);
+ }
+ DeleteFileAndCheck(file);
+
+ if (app_resources.block_events_on_busy) UnshowBusyCursor();
+
+ toc->mailpending = False;
+ return (firstmessage != NULL);
+}
+
+
+/* The given message has changed. Rescan it and change the scanfile. */
+
+void TocMsgChanged(Toc toc, Msg msg)
+{
+ char **argv, str[100], str2[10], *ptr;
+ int length, delta;
+ int i;
+ FateType fate;
+ Toc desttoc;
+
+ if (toc->validity != valid) return;
+ fate = MsgGetFate(msg, &desttoc);
+ MsgSetFate(msg, Fignore, (Toc) NULL);
+ argv = MakeArgv(6);
+ argv[0] = "scan";
+ argv[1] = TocMakeFolderName(toc);
+ (void) sprintf(str, "%d", msg->msgid);
+ argv[2] = str;
+ argv[3] = "-width";
+ (void) sprintf(str2, "%d", app_resources.toc_width);
+ argv[4] = str2;
+ argv[5] = "-noheader";
+ ptr = DoCommandToString(argv);
+ XtFree(argv[1]);
+ XtFree((char *) argv);
+ if (strcmp(ptr, msg->buf) != 0) {
+ length = strlen(ptr);
+ delta = length - msg->length;
+ XtFree(msg->buf);
+ msg->buf = ptr;
+ msg->length = length;
+ toc->length += delta;
+ if (msg->visible) {
+ if (delta != 0) {
+ for (i=TUGetMsgPosition(toc, msg)+1; i<toc->nummsgs ; i++)
+ toc->msgs[i]->position += delta;
+ toc->lastPos += delta;
+ }
+ for (i=0 ; i<toc->num_scrns ; i++)
+ TURedisplayToc(toc->scrn[i]);
+ }
+ MsgSetFate(msg, fate, desttoc);
+ TUSaveTocFile(toc);
+ } else XtFree(ptr);
+}
+
+
+
+Msg TocMsgFromId(Toc toc, int msgid)
+{
+ int h, l, m;
+ l = 0;
+ h = toc->nummsgs - 1;
+ if (h < 0) {
+ if (app_resources.debug) {
+ char str[100];
+ (void)sprintf(str, "Toc is empty! folder=%s\n", toc->foldername);
+ DEBUG( str )
+ }
+ return NULL;
+ }
+ while (l < h - 1) {
+ m = (l + h) / 2;
+ if (toc->msgs[m]->msgid > msgid)
+ h = m;
+ else
+ l = m;
+ }
+ if (toc->msgs[l]->msgid == msgid) return toc->msgs[l];
+ if (toc->msgs[h]->msgid == msgid) return toc->msgs[h];
+ if (app_resources.debug) {
+ char str[100];
+ (void) sprintf(str,
+ "TocMsgFromId search failed! hi=%d, lo=%d, msgid=%d\n",
+ h, l, msgid);
+ DEBUG( str )
+ }
+ return NULL;
+}
+
+/* Sequence names are put on a stack which is specific to the folder.
+ * Sequence names are very volatile, so we make our own copies of the strings.
+ */
+
+/*ARGSUSED*/
+void XmhPushSequence(
+ Widget w,
+ XEvent *event,
+ String *params,
+ Cardinal *count)
+{
+ Scrn scrn = ScrnFromWidget(w);
+ Toc toc;
+ Cardinal i;
+
+ if (! (toc = scrn->toc)) return;
+
+ if (*count == 0) {
+ if (toc->selectseq)
+ Push(&toc->sequence_stack, XtNewString(toc->selectseq->name));
+ }
+ else
+ for (i=0; i < *count; i++)
+ Push(&toc->sequence_stack, XtNewString(params[i]));
+}
+
+
+/*ARGSUSED*/
+void XmhPopSequence(
+ Widget w, /* any widget on the screen of interest */
+ XEvent *event,
+ String *params,
+ Cardinal *count)
+{
+ Scrn scrn = ScrnFromWidget(w);
+ char *seqname;
+ Widget sequenceMenu, selected, original;
+ Button button;
+ Sequence sequence;
+
+ if ((seqname = Pop(&scrn->toc->sequence_stack)) != NULL) {
+
+ button = BBoxFindButtonNamed(scrn->mainbuttons,
+ MenuBoxButtons[XMH_SEQUENCE].button_name);
+ sequenceMenu = BBoxMenuOfButton(button);
+
+ if ((selected = XawSimpleMenuGetActiveEntry(sequenceMenu)))
+ ToggleMenuItem(selected, False);
+
+ if ((original = XtNameToWidget(sequenceMenu, seqname))) {
+ ToggleMenuItem(original, True);
+ sequence = TocGetSeqNamed(scrn->toc, seqname);
+ TocSetSelectedSequence(scrn->toc, sequence);
+ }
+ XtFree(seqname);
+ }
+}
diff --git a/toc.h b/toc.h
new file mode 100644
index 0000000..f070851
--- /dev/null
+++ b/toc.h
@@ -0,0 +1,76 @@
+/*
+ * $XConsortium: toc.h,v 2.13 91/07/17 12:28:29 converse 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/toc.h,v 1.3 2002/07/01 02:26:05 tsi Exp $ */
+
+#ifndef _toc_h
+#define _toc_h
+
+extern void TocInit (void);
+extern Toc TocCreate (char *);
+extern Toc TocCreateFolder (char *);
+extern int TocHasMail (Toc);
+extern void TocCheckForNewMail (Boolean);
+extern Boolean TocTestAndSetDeletePending(Toc);
+extern void TocClearDeletePending (Toc);
+extern void TocDeleteFolder (Toc);
+extern void TocSetScrn (Toc, Scrn);
+
+extern void TocRemoveMsg (Toc, Msg);
+extern void TocRecheckValidity (Toc);
+extern void TocSetCurMsg (Toc, Msg);
+extern Msg TocGetCurMsg (Toc);
+extern Msg TocMsgAfter (Toc, Msg);
+extern Msg TocMsgBefore (Toc, Msg);
+extern void TocForceRescan (Toc);
+
+extern void TocReloadSeqLists (Toc);
+extern int TocHasSequences (Toc);
+extern void TocChangeViewedSeq (Toc, Sequence);
+extern Sequence TocViewedSequence (Toc);
+extern Sequence TocGetSeqNamed (Toc, char *);
+extern void TocSetSelectedSequence (Toc, Sequence);
+extern Sequence TocSelectedSequence (Toc);
+
+extern MsgList TocCurMsgList (Toc);
+extern void TocUnsetSelection (Toc);
+extern Msg TocMakeNewMsg (Toc);
+extern void TocStopUpdate (Toc);
+extern void TocStartUpdate (Toc);
+extern void TocSetCacheValid (Toc);
+
+extern char * TocMakeFolderName (Toc);
+extern char * TocName (Toc);
+extern Toc TocGetNamed (char*);
+
+extern int TocConfirmCataclysm(Toc, XtCallbackList, XtCallbackList);
+extern void TocCommitChanges (Widget, XtPointer, XtPointer);
+extern int TocCanIncorporate (Toc);
+extern int TocIncorporate (Toc);
+extern void TocMsgChanged (Toc, Msg);
+extern Msg TocMsgFromId (Toc, int);
+
+#endif /* _toc_h */
diff --git a/tocfuncs.c b/tocfuncs.c
new file mode 100644
index 0000000..b60f413
--- /dev/null
+++ b/tocfuncs.c
@@ -0,0 +1,1078 @@
+/*
+ * $XConsortium: tocfuncs.c /main/36 1996/02/02 14:27:42 kaleb $
+ *
+ *
+ * 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/tocfuncs.c,v 1.4 2002/04/05 21:06:29 dickey Exp $ */
+
+/* tocfuncs.c -- action procedures concerning things in the toc widget. */
+
+#include "xmh.h"
+#include "tocutil.h"
+#include "actions.h"
+
+#define MAX_SYSTEM_LEN 510
+
+Boolean UserWantsAction(
+ Widget w,
+ Scrn scrn)
+{
+ /* Commands in the command menus invoke callbacks directly.
+ * Keyboard accelerators use the command menus as source widgets.
+ * Actions can also be specified in the translations for menu buttons.
+ * Actions can also be specified in the translations for menus.
+ * In fact, the user can attach actions to any (reasonable) widget.
+ *
+ * The purpose of this check is to prevent actions specified as
+ * translations for folder menus and for folder buttons from executing
+ * after the mouse pointer has left the folder button or the when the
+ * mouse button is released outside of the folder menu.
+ *
+ * The side effect of this routine is that it restricts keyboard
+ * accelerators from originating from folder buttons or folder menus.
+ */
+
+ if (XtIsSubclass(w, menuButtonWidgetClass) && /* w is a menu button */
+ w != LastMenuButtonPressed) /* pointer left the window */
+ return False;
+
+ if (XtIsSubclass(w, simpleMenuWidgetClass) && /* w is a menu */
+ (! XawSimpleMenuGetActiveEntry(w)) && /* no entry was selected */
+ (BBoxIsGrandparent(scrn->folderbuttons, w))) /* w is a folder menu */
+ return False;
+
+ return True;
+}
+
+
+/*ARGSUSED*/
+static void NextAndPreviousView(
+ Scrn scrn,
+ Boolean next) /* if true, next or forward; if false, previous */
+{
+ Toc toc = scrn->toc;
+ MsgList mlist;
+ FateType fate = Fignore;
+ Msg msg;
+
+ if (toc == NULL) return;
+ mlist = TocCurMsgList(toc);
+ if (mlist->nummsgs)
+ msg = (next ? mlist->msglist[0] : mlist->msglist[mlist->nummsgs - 1]);
+ else {
+ msg = TocGetCurMsg(toc);
+ if (msg && msg == scrn->msg)
+ msg = (next ? TocMsgAfter(toc, msg) : TocMsgBefore(toc, msg));
+ if (msg) fate = MsgGetFate(msg, (Toc *)NULL);
+ while (msg && ((app_resources.skip_deleted && fate == Fdelete)
+ || (app_resources.skip_moved && fate == Fmove)
+ || (app_resources.skip_copied && fate == Fcopy))) {
+ msg = (next ? TocMsgAfter(toc, msg) : TocMsgBefore(toc, msg));
+ if (msg) fate = MsgGetFate(msg, (Toc *)NULL);
+ }
+ }
+
+ if (msg) {
+ XtCallbackRec confirms[2];
+ if (next)
+ confirms[0].callback = (XtCallbackProc) DoNextView;
+ else
+ confirms[0].callback = (XtCallbackProc) DoPrevView;
+ confirms[0].closure = (XtPointer) scrn;
+ confirms[1].callback = (XtCallbackProc) NULL;
+ confirms[1].closure = (XtPointer) NULL;
+ if (MsgSetScrn(msg, scrn, confirms, (XtCallbackList) NULL) !=
+ NEEDS_CONFIRMATION) {
+ TocUnsetSelection(toc);
+ TocSetCurMsg(toc, msg);
+ }
+ }
+ FreeMsgList(mlist);
+}
+
+
+/*ARGSUSED*/
+void DoReverseReadOrder(
+ Widget widget, /* the menu entry widget */
+ XtPointer client_data,
+ XtPointer call_data)
+{
+ app_resources.reverse_read_order =
+ (app_resources.reverse_read_order ? False : True);
+ ToggleMenuItem(widget, app_resources.reverse_read_order);
+}
+
+
+/*ARGSUSED*/
+void DoNextView(
+ Widget widget, /* unused */
+ XtPointer client_data,
+ XtPointer call_data) /* unused */
+{
+ NextAndPreviousView((Scrn) client_data,
+ (app_resources.reverse_read_order ? False : True));
+}
+
+/*ARGSUSED*/
+void XmhViewNextMessage(
+ Widget w,
+ XEvent *event,
+ String *params,
+ Cardinal *num_params)
+{
+ Scrn scrn = ScrnFromWidget(w);
+ if (UserWantsAction(w, scrn))
+ DoNextView(w, (XtPointer) scrn, (XtPointer) NULL);
+}
+
+/*ARGSUSED*/
+void DoPrevView(
+ Widget widget, /* unused */
+ XtPointer client_data,
+ XtPointer call_data) /* unused */
+{
+ NextAndPreviousView((Scrn) client_data,
+ (app_resources.reverse_read_order ? True : False));
+}
+
+/*ARGSUSED*/
+void XmhViewPreviousMessage(
+ Widget w,
+ XEvent *event,
+ String *params,
+ Cardinal *num_params)
+{
+ Scrn scrn = ScrnFromWidget(w);
+ if (UserWantsAction(w, scrn))
+ DoPrevView(w, (XtPointer) scrn, (XtPointer) NULL);
+}
+
+
+/*ARGSUSED*/
+void DoViewNew(
+ Widget w,
+ XtPointer client_data,
+ XtPointer call_data)
+{
+ Scrn scrn = (Scrn) client_data;
+ Toc toc = scrn->toc;
+ Scrn vscrn;
+ MsgList mlist;
+
+ if (toc == NULL) return;
+ mlist = CurMsgListOrCurMsg(toc);
+ if (mlist->nummsgs) {
+ vscrn = NewViewScrn();
+ (void) MsgSetScrn(mlist->msglist[0], vscrn, (XtCallbackList) NULL,
+ (XtCallbackList) NULL);
+ MapScrn(vscrn);
+ }
+ FreeMsgList(mlist);
+}
+
+
+/*ARGSUSED*/
+void XmhViewInNewWindow(
+ Widget w,
+ XEvent *event,
+ String *params,
+ Cardinal *num_params)
+{
+ Scrn scrn = ScrnFromWidget(w);
+ if (UserWantsAction(w, scrn))
+ DoViewNew(w, (XtPointer) scrn, (XtPointer) NULL);
+}
+
+
+static void DoForwardMsg(
+ Scrn scrn,
+ String *params,
+ Cardinal num_params)
+{
+ Toc toc = scrn->toc;
+ MsgList mlist;
+
+ if (toc == NULL) return;
+ mlist = CurMsgListOrCurMsg(toc);
+ if (mlist->nummsgs)
+ CreateForward(mlist, params, num_params);
+ FreeMsgList(mlist);
+}
+
+
+/*ARGSUSED*/
+void DoForward(
+ Widget w,
+ XtPointer client_data,
+ XtPointer call_data)
+{
+ DoForwardMsg((Scrn) client_data, (String *)NULL, (Cardinal)0);
+}
+
+
+/*ARGSUSED*/
+void XmhForward(
+ Widget w,
+ XEvent *event,
+ String *params,
+ Cardinal *num_params)
+{
+ Scrn scrn = ScrnFromWidget(w);
+ if (UserWantsAction(w, scrn))
+ DoForwardMsg(scrn, params, *num_params);
+}
+
+
+/*ARGSUSED*/
+void DoTocUseAsComp(
+ Widget w,
+ XtPointer client_data,
+ XtPointer call_data)
+{
+ Scrn scrn = (Scrn) client_data;
+ Toc toc = scrn->toc;
+ Scrn vscrn;
+ MsgList mlist;
+ Msg msg;
+
+ if (toc == NULL) return;
+ mlist = CurMsgListOrCurMsg(toc);
+ if (mlist->nummsgs) {
+ vscrn = NewCompScrn();
+ if (DraftsFolder == toc) {
+ msg = mlist->msglist[0];
+ } else {
+ msg = TocMakeNewMsg(DraftsFolder);
+ MsgLoadCopy(msg, mlist->msglist[0]);
+ MsgSetTemporary(msg);
+ }
+ MsgSetScrnForComp(msg, vscrn);
+ MapScrn(vscrn);
+ }
+ FreeMsgList(mlist);
+}
+
+
+/*ARGSUSED*/
+void XmhUseAsComposition(
+ Widget w,
+ XEvent *event,
+ String *params,
+ Cardinal *num_params)
+{
+ Scrn scrn = ScrnFromWidget(w);
+ if (UserWantsAction(w, scrn))
+ DoTocUseAsComp(w, (XtPointer) scrn, (XtPointer) NULL);
+}
+
+
+/* Utility: change the fate of a set of messages. */
+
+static void MarkMessages(Scrn scrn, FateType fate, int skip)
+{
+ Toc toc = scrn->toc;
+ Toc desttoc;
+ int i;
+ MsgList mlist;
+ Msg msg;
+ if (toc == NULL) return;
+ if (fate == Fcopy || fate == Fmove)
+ desttoc = SelectedToc(scrn);
+ else
+ desttoc = NULL;
+ if (desttoc == toc)
+ Feep(XkbBI_MinorError,0,None);
+ else {
+ mlist = TocCurMsgList(toc);
+ if (mlist->nummsgs == 0) {
+ msg = TocGetCurMsg(toc);
+ if (msg) {
+ MsgSetFate(msg, fate, desttoc);
+ if (skip)
+ DoNextView(scrn->widget, (XtPointer) scrn,
+ (XtPointer) NULL);
+ }
+ } else {
+ for (i = 0; i < mlist->nummsgs; i++)
+ MsgSetFate(mlist->msglist[i], fate, desttoc);
+ }
+ FreeMsgList(mlist);
+ }
+}
+
+
+/*ARGSUSED*/
+void XmhMarkDelete(
+ Widget w,
+ XEvent *event,
+ String *params,
+ Cardinal *num_params)
+{
+ Scrn scrn = ScrnFromWidget(w);
+ if (UserWantsAction(w, scrn))
+ DoDelete(w, (XtPointer) scrn, (XtPointer) NULL);
+}
+
+
+/*ARGSUSED*/
+void DoDelete(
+ Widget w,
+ XtPointer client_data,
+ XtPointer call_data)
+{
+ Scrn scrn = (Scrn) client_data;
+ MarkMessages(scrn, Fdelete, app_resources.skip_deleted);
+}
+
+
+/*ARGSUSED*/
+void DoCopy(
+ Widget w,
+ XtPointer client_data,
+ XtPointer call_data)
+{
+ Scrn scrn = (Scrn) client_data;
+ MarkMessages(scrn, Fcopy, app_resources.skip_copied);
+}
+
+
+/*ARGSUSED*/
+void XmhMarkCopy(
+ Widget w,
+ XEvent *event,
+ String *params,
+ Cardinal *num_params)
+{
+ Scrn scrn = ScrnFromWidget(w);
+ if (UserWantsAction(w, scrn))
+ DoCopy(w, (XtPointer) scrn, (XtPointer) NULL);
+}
+
+
+/*ARGSUSED*/
+void DoMove(
+ Widget w,
+ XtPointer client_data,
+ XtPointer call_data)
+{
+ Scrn scrn = (Scrn) client_data;
+ MarkMessages(scrn, Fmove, app_resources.skip_moved);
+}
+
+
+/*ARGSUSED*/
+void XmhMarkMove(
+ Widget w,
+ XEvent *event,
+ String *params,
+ Cardinal *num_params)
+{
+ Scrn scrn = ScrnFromWidget(w);
+ if (UserWantsAction(w, scrn))
+ DoMove(w, (XtPointer) scrn, (XtPointer) NULL);
+}
+
+
+/*ARGSUSED*/
+void DoUnmark(
+ Widget w,
+ XtPointer client_data,
+ XtPointer call_data)
+{
+ Scrn scrn = (Scrn) client_data;
+ MarkMessages(scrn, Fignore, FALSE);
+}
+
+
+/*ARGSUSED*/
+void XmhUnmark(
+ Widget w,
+ XEvent *event,
+ String *params,
+ Cardinal *num_params)
+{
+ Scrn scrn = ScrnFromWidget(w);
+ if (UserWantsAction(w, scrn))
+ DoUnmark(w, (XtPointer) scrn, (XtPointer) NULL);
+}
+
+
+/*ARGSUSED*/
+void DoCommit(
+ Widget w,
+ XtPointer client_data,
+ XtPointer call_data)
+{
+ Scrn scrn = (Scrn) client_data;
+ TocCommitChanges(w, (XtPointer) scrn->toc, (XtPointer) NULL);
+}
+
+
+/*ARGSUSED*/
+void XmhCommitChanges(
+ Widget w,
+ XEvent *event,
+ String *params,
+ Cardinal *num_params)
+{
+ Scrn scrn = ScrnFromWidget(w);
+ if (UserWantsAction(w, scrn))
+ TocCommitChanges(w, (XtPointer) scrn->toc, (XtPointer) NULL);
+}
+
+
+/*ARGSUSED*/
+void XmhShellCommand(
+ Widget w, /* any widget on same scrn as the messages */
+ XEvent *event, /* unused */
+ String *params, /* shell command to execute with msgs appended */
+ Cardinal *num_params)
+{
+ int i, len, used;
+ MsgList mlist;
+ String *p;
+ Scrn scrn = ScrnFromWidget(w);
+ char str[MAX_SYSTEM_LEN];
+
+ if (! UserWantsAction(w, scrn) || ! scrn->toc)
+ return;
+ if (! *num_params) {
+ PopupError(scrn->parent, "XmhShellCommand: no command given.");
+ return;
+ }
+ used = 0;
+ p = params;
+ for (i = *num_params; --i >= 0; p++) {
+ len = strlen(*p);
+ if ((used + len + 1) >= MAX_SYSTEM_LEN) {
+ PopupError(scrn->parent, "XmhShellCommand: command too long.");
+ return;
+ }
+ strncpy(&str[used], *p, len);
+ str[(used += len)] = ' ';
+ used++;
+ }
+ str[used] = '\0';
+
+ mlist = CurMsgListOrCurMsg(scrn->toc);
+ if (mlist->nummsgs) {
+ char *msg;
+ int prefix = used;
+ i = 0;
+ while (i < mlist->nummsgs) {
+ used = prefix;
+ while (i < mlist->nummsgs &&
+ (msg = MsgFileName(mlist->msglist[i])) &&
+ (used + (len = strlen(msg)) + 1) < MAX_SYSTEM_LEN) {
+ strncpy(&str[used], msg, len);
+ str[(used += len)] = ' ';
+ used++;
+ i++;
+ }
+ if (used != prefix) {
+ char **argv;
+ str[used] = '\0';
+ DEBUG( str );
+ argv = MakeArgv(3);
+ argv[0] = "/bin/sh";
+ argv[1] = "-c"; /* commands are read from the next argument */
+ argv[2] = str;
+ (void) DoCommand(argv, (char*)NULL, (char*)NULL);
+ /* a "notice" popup should appear with stderr output */
+ XtFree((char*)argv);
+ }
+ }
+ } else
+ PopupError(scrn->parent, "XmhShellCommand: no messages selected.");
+
+ FreeMsgList(mlist);
+}
+
+
+void XmhPrint(
+ Widget w,
+ XEvent *event,
+ String *params,
+ Cardinal *num_params)
+{
+ if (! num_params || ! *num_params) {
+ /* use the print command specified in application resources */
+ Cardinal argc = 1;
+ String *argv = MakeArgv(argc);
+ argv[0] = app_resources.print_command;
+ XmhShellCommand(w, event, argv, &argc);
+ XtFree((char *) argv);
+ } else {
+ /* do whatever the user has specified as action parameters */
+ XmhShellCommand(w, event, params, num_params);
+ }
+}
+
+
+/*ARGSUSED*/
+void DoPrint(
+ Widget w,
+ XtPointer client_data,
+ XtPointer call_data) /* unused */
+{
+ Scrn scrn = (Scrn) client_data;
+ Cardinal num_params = 0;
+ /* The callback interface will not be entered unless the user requested
+ * the action, so pass a widget which will succeed the test in
+ * UserWantsAction.
+ */
+ XmhPrint(scrn->parent, (XEvent*)NULL, (String*)NULL, &num_params);
+}
+
+
+/*ARGSUSED*/
+void DoPack(
+ Widget widget,
+ XtPointer client_data,
+ XtPointer call_data) /* unused */
+{
+ Scrn scrn = (Scrn) client_data;
+ Toc toc = scrn->toc;
+ XtCallbackRec confirms[2];
+ char **argv;
+
+ if (toc == NULL) return;
+ confirms[0].callback = (XtCallbackProc) DoPack;
+ confirms[0].closure = (XtPointer) scrn;
+ confirms[1].callback = (XtCallbackProc) NULL;
+ confirms[1].closure = (XtPointer) NULL;
+ if (TocConfirmCataclysm(toc, confirms, (XtCallbackRec *) NULL))
+ return;
+ argv = MakeArgv(4);
+ argv[0] = "folder";
+ argv[1] = TocMakeFolderName(toc);
+ argv[2] = "-pack";
+ argv[3] = "-fast";
+ if (app_resources.block_events_on_busy) ShowBusyCursor();
+
+ DoCommand(argv, (char *) NULL, (char *) NULL);
+ XtFree(argv[1]);
+ XtFree((char *) argv);
+ TocForceRescan(toc);
+
+ if (app_resources.block_events_on_busy) UnshowBusyCursor();
+
+}
+
+
+/*ARGSUSED*/
+void XmhPackFolder(
+ Widget w,
+ XEvent *event,
+ String *params,
+ Cardinal *num_params)
+{
+ Scrn scrn = ScrnFromWidget(w);
+ if (UserWantsAction(w, scrn))
+ DoPack(w, (XtPointer) scrn, (XtPointer) NULL);
+}
+
+
+/*ARGSUSED*/
+void DoSort(
+ Widget widget,
+ XtPointer client_data,
+ XtPointer call_data) /* unused */
+{
+ Scrn scrn = (Scrn) client_data;
+ Toc toc = scrn->toc;
+ char ** argv;
+ XtCallbackRec confirms[2];
+
+ if (toc == NULL) return;
+ confirms[0].callback = (XtCallbackProc) DoSort;
+ confirms[0].closure = (XtPointer) scrn;
+ confirms[1].callback = (XtCallbackProc) NULL;
+ confirms[1].closure = (XtPointer) NULL;
+ if (TocConfirmCataclysm(toc, confirms, (XtCallbackRec *) NULL))
+ return;
+ argv = MakeArgv(3);
+ argv[0] = "sortm";
+ argv[1] = TocMakeFolderName(toc);
+ argv[2] = "-noverbose";
+ if (app_resources.block_events_on_busy) ShowBusyCursor();
+
+ DoCommand(argv, (char *) NULL, (char *) NULL);
+ XtFree(argv[1]);
+ XtFree((char *) argv);
+ TocForceRescan(toc);
+
+ if (app_resources.block_events_on_busy) UnshowBusyCursor();
+}
+
+
+/*ARGSUSED*/
+void XmhSortFolder(
+ Widget w,
+ XEvent *event,
+ String *params,
+ Cardinal *num_params)
+{
+ Scrn scrn = ScrnFromWidget(w);
+ if (UserWantsAction(w, scrn))
+ DoSort(w, (XtPointer) scrn, (XtPointer) NULL);
+}
+
+
+/*ARGSUSED*/
+void XmhForceRescan(
+ Widget w,
+ XEvent *event,
+ String *params,
+ Cardinal *num_params)
+{
+ Scrn scrn = ScrnFromWidget(w);
+ if (UserWantsAction(w, scrn))
+ DoForceRescan(w, (XtPointer) scrn, (XtPointer) NULL);
+}
+
+/*ARGSUSED*/
+void DoForceRescan(
+ Widget w,
+ XtPointer client_data,
+ XtPointer call_data)
+{
+ Scrn scrn = (Scrn) client_data;
+ Toc toc = scrn->toc;
+ if (toc == NULL) return;
+ if (app_resources.block_events_on_busy) ShowBusyCursor();
+
+ TocForceRescan(toc);
+
+ if (app_resources.block_events_on_busy) UnshowBusyCursor();
+}
+
+/*ARGSUSED*/
+void XmhCheckForNewMail(
+ Widget w,
+ XEvent *e,
+ String *p,
+ Cardinal *n)
+{
+ TocCheckForNewMail(True);
+}
+
+/* Incorporate new mail. */
+
+/*ARGSUSED*/
+void XmhIncorporateNewMail(
+ Widget w,
+ XEvent *event,
+ String *params,
+ Cardinal *num_params)
+{
+ Scrn scrn = ScrnFromWidget(w);
+ if (UserWantsAction(w, scrn)) {
+ if (TocCanIncorporate(scrn->toc))
+ DoIncorporateNewMail(w, (XtPointer) scrn, (XtPointer) NULL);
+ }
+}
+
+
+void DoIncorporateNewMail(
+ Widget w, /* unused */
+ XtPointer client_data, /* screen */
+ XtPointer call_data) /* unused */
+{
+ Scrn scrn = (Scrn) client_data;
+ Toc toc = scrn->toc;
+ int i;
+ int newmail;
+
+ if (! toc) return;
+ newmail = TocIncorporate(toc);
+
+ if (app_resources.show_on_inc && newmail)
+ DoNextView(w, client_data, call_data);
+
+ if (app_resources.new_mail_check)
+ /* update the folder button */
+ for (i=0; i < numScrns; i++) {
+ scrn = scrnList[i];
+ if (scrn->kind == STtocAndView)
+ /* give visual indication of no mail waiting */
+ BBoxMailFlag(scrn->folderbuttons, TocName(toc), False);
+ }
+
+ if (app_resources.mail_waiting_flag)
+ /* update the icon */
+ TocCheckForNewMail(False);
+}
+
+
+static void DoReplyMsg(
+ Scrn scrn,
+ String *params,
+ Cardinal num_params)
+{
+ Toc toc = scrn->toc;
+ Scrn nscrn;
+ MsgList mlist;
+ Msg msg;
+
+ if (toc == NULL) return;
+ mlist = CurMsgListOrCurMsg(toc);
+ if (mlist->nummsgs) {
+ nscrn = NewCompScrn();
+ ScreenSetAssocMsg(nscrn, mlist->msglist[0]);
+ msg = TocMakeNewMsg(DraftsFolder);
+ MsgSetTemporary(msg);
+ MsgLoadReply(msg, mlist->msglist[0], params, num_params);
+ MsgSetScrnForComp(msg, nscrn);
+ MapScrn(nscrn);
+ }
+ FreeMsgList(mlist);
+}
+
+
+/*ARGSUSED*/
+void DoReply(
+ Widget w,
+ XtPointer client_data,
+ XtPointer call_data)
+{
+ DoReplyMsg((Scrn) client_data, (String *)NULL, (Cardinal)0);
+}
+
+
+/*ARGSUSED*/
+void XmhReply(
+ Widget w,
+ XEvent *event,
+ String *params,
+ Cardinal *num_params)
+{
+ Scrn scrn = ScrnFromWidget(w);
+ if (UserWantsAction(w, scrn))
+ DoReplyMsg(scrn, params, *num_params);
+}
+
+
+/*ARGSUSED*/
+void DoPickMessages(
+ Widget w,
+ XtPointer client_data,
+ XtPointer call_data)
+{
+ Scrn scrn = (Scrn) client_data;
+ Toc toc = scrn->toc;
+ Scrn nscrn;
+ char * toseq;
+ Sequence selectedseq;
+ Boolean recycled;
+
+ if (toc == NULL) return;
+ if ((selectedseq = TocSelectedSequence(toc)) == NULL)
+ toseq = "temp";
+ else {
+ toseq = selectedseq->name;
+ if (strcmp(toseq, "all") == 0)
+ toseq = "temp";
+ }
+ nscrn = CreateNewScrn(STpick);
+ recycled = (nscrn->pick) ? True : False;
+ AddPick(nscrn, toc, (TocViewedSequence(toc))->name, toseq);
+ DEBUG("Realizing Pick...")
+ XtRealizeWidget(nscrn->parent);
+ DEBUG(" done.\n")
+ if (! recycled) {
+ InitBusyCursor(nscrn);
+ XDefineCursor(XtDisplay(nscrn->parent), XtWindow(nscrn->parent),
+ app_resources.cursor);
+ (void) XSetWMProtocols(XtDisplay(toplevel), XtWindow(nscrn->parent),
+ protocolList, XtNumber(protocolList));
+ }
+ MapScrn(nscrn);
+}
+
+
+/*ARGSUSED*/
+void XmhPickMessages(
+ Widget w,
+ XEvent *event,
+ String *params,
+ Cardinal *num_params)
+{
+ Scrn scrn = ScrnFromWidget(w);
+ if (UserWantsAction(w, scrn))
+ DoPickMessages(w, (XtPointer) scrn, (XtPointer) NULL);
+}
+
+
+/*ARGSUSED*/
+void DoSelectSequence(
+ Widget widget, /* sequence menu entry object */
+ XtPointer client_data, /* the screen */
+ XtPointer call_data)
+{
+ Scrn scrn = (Scrn) client_data;
+ Toc toc = (Toc) scrn->toc;
+ Sequence seq;
+
+ if ((seq = TocSelectedSequence(toc)) != NULL) {
+ Widget item, menu;
+ Button button;
+
+ button = BBoxFindButtonNamed
+ (scrn->mainbuttons, MenuBoxButtons[XMH_SEQUENCE].button_name);
+ menu = BBoxMenuOfButton(button);
+ if ((item = XtNameToWidget(menu, seq->name)) != NULL)
+ ToggleMenuItem(item, False);
+ }
+
+ ToggleMenuItem(widget, True);
+ TocSetSelectedSequence(toc, TocGetSeqNamed(toc, XtName(widget)));
+}
+
+
+/*ARGSUSED*/
+void DoOpenSeq(
+ Widget w,
+ XtPointer client_data,
+ XtPointer call_data)
+{
+ Scrn scrn = (Scrn) client_data;
+ Toc toc = scrn->toc;
+ if (toc == NULL) return;
+ TocChangeViewedSeq(toc, TocSelectedSequence(toc));
+}
+
+
+/*ARGSUSED*/
+void XmhOpenSequence(
+ Widget w,
+ XEvent *event,
+ String *params,
+ Cardinal *num_params)
+{
+ Widget entry_object;
+ Scrn scrn = ScrnFromWidget(w);
+ Sequence selected_sequence;
+
+ /* In case this action is called from translations defined by the
+ * user on folder menu buttons or on folder menu widgets.
+ */
+ if (! UserWantsAction(w, scrn))
+ return;
+
+ /* In case there is nothing to do anyway. */
+ if (! TocHasSequences(scrn->toc))
+ return;
+
+ /* In case the action was given the name of a sequence to open. */
+ if (*num_params) {
+ Toc toc = scrn->toc;
+ if ((selected_sequence = TocGetSeqNamed(toc, params[0]))) {
+ TocSetSelectedSequence(toc, selected_sequence);
+ TocChangeViewedSeq(toc, selected_sequence);
+ }
+ return;
+ }
+
+ /* In case this action is a translation on the sequence menu. */
+
+ if ((strcmp(XtName(w), "sequenceMenu") == 0) &&
+ (event->type == ButtonRelease)) {
+
+ /* The user released the mouse button. We must distinguish between
+ * a button release on a selectable menu entry, and a button release
+ * occuring elsewhere. The button releases occuring elsewhere are
+ * either outside of the menu, or on unselectable menu entries.
+ */
+
+ if ((entry_object = XawSimpleMenuGetActiveEntry(w)) == NULL)
+ return;
+
+ /* Some entry in the menu was selected. The menu entry's callback
+ * procedure has already executed. If a sequence name was selected,
+ * the callback procedure has caused that sequence to become the
+ * currently selected sequence. If selected menu entry object's
+ * name matches the currently selected sequence, we should open
+ * that sequence. Otherwise, the user must have selected a sequence
+ * manipulation command, such as Pick. The assumptions here are that
+ * the name of a menu entry object which represents a sequence is
+ * identical to the name of the sequence, and in the translations,
+ * that the notify() action was specified before this action.
+ */
+
+ if ((selected_sequence = TocSelectedSequence(scrn->toc)) &&
+ (strcmp(XtName(entry_object), selected_sequence->name) == 0))
+ DoOpenSeq(w, (XtPointer) scrn, (XtPointer) NULL);
+ return;
+ }
+
+ /* An accelerator open sequence function */
+
+ DoOpenSeq(w, (XtPointer) scrn, (XtPointer) NULL);
+}
+
+
+typedef enum {ADD, REMOVE, DELETE} TwiddleOperation;
+
+static void TwiddleSequence(Scrn scrn, TwiddleOperation op)
+{
+ Toc toc = scrn->toc;
+ char **argv, str[100];
+ int i;
+ MsgList mlist;
+ Sequence selectedseq;
+
+ if (toc == NULL || ((selectedseq = TocSelectedSequence(toc)) == NULL))
+ return;
+ if (strcmp(selectedseq->name, "all") == 0) {
+ Feep(XkbBI_MinorError,0,None);
+ return;
+ }
+ if (op == DELETE)
+ mlist = MakeNullMsgList();
+ else {
+ mlist = CurMsgListOrCurMsg(toc);
+ if (mlist->nummsgs == 0) {
+ FreeMsgList(mlist);
+ Feep(XkbBI_MinorError,0,None);
+ return;
+ }
+ }
+ argv = MakeArgv(6 + mlist->nummsgs);
+ argv[0] = "mark";
+ argv[1] = TocMakeFolderName(toc);
+ argv[2] = "-sequence";
+ argv[3] = selectedseq->name;
+ switch (op) {
+ case ADD:
+ argv[4] = "-add";
+ argv[5] = "-nozero";
+ break;
+ case REMOVE:
+ argv[4] = "-delete";
+ argv[5] = "-nozero";
+ break;
+ case DELETE:
+ argv[4] = "-delete";
+ argv[5] = "all";
+ break;
+ }
+ for (i = 0; i < mlist->nummsgs; i++) {
+ (void) sprintf(str, "%d", MsgGetId(mlist->msglist[i]));
+ argv[6 + i] = XtNewString(str);
+ }
+ DoCommand(argv, (char *) NULL, (char *) NULL);
+ for (i = 0; i < mlist->nummsgs; i++)
+ XtFree((char *) argv[6 + i]);
+ XtFree(argv[1]);
+ XtFree((char *) argv);
+ FreeMsgList(mlist);
+ TocReloadSeqLists(toc);
+}
+
+
+/*ARGSUSED*/
+void DoAddToSeq(
+ Widget w,
+ XtPointer client_data,
+ XtPointer call_data)
+{
+ Scrn scrn = (Scrn) client_data;
+ TwiddleSequence(scrn, ADD);
+}
+
+
+/*ARGSUSED*/
+void XmhAddToSequence(
+ Widget w,
+ XEvent *event,
+ String *params,
+ Cardinal *num_params)
+{
+ Scrn scrn = ScrnFromWidget(w);
+ if (! UserWantsAction(w, scrn))
+ return;
+ if ((strcmp(XtName(w), "sequenceMenu") == 0) &&
+ (event->type == ButtonRelease) &&
+ (XawSimpleMenuGetActiveEntry(w) == NULL))
+ return;
+ if (TocHasSequences(scrn->toc))
+ TwiddleSequence(scrn, ADD);
+}
+
+
+/*ARGSUSED*/
+void DoRemoveFromSeq(
+ Widget w,
+ XtPointer client_data,
+ XtPointer call_data)
+{
+ Scrn scrn = (Scrn) client_data;
+ TwiddleSequence(scrn, REMOVE);
+}
+
+
+/*ARGSUSED*/
+void XmhRemoveFromSequence(
+ Widget w,
+ XEvent *event,
+ String *params,
+ Cardinal *num_params)
+{
+ Scrn scrn = ScrnFromWidget(w);
+ if (UserWantsAction(w, scrn))
+ if (TocHasSequences(scrn->toc))
+ TwiddleSequence(scrn, REMOVE);
+}
+
+
+/*ARGSUSED*/
+void DoDeleteSeq(
+ Widget w,
+ XtPointer client_data,
+ XtPointer call_data)
+{
+ Scrn scrn = (Scrn) client_data;
+ TwiddleSequence(scrn, DELETE);
+ TUCheckSequenceMenu(scrn->toc);
+}
+
+
+/*ARGSUSED*/
+void XmhDeleteSequence(
+ Widget w,
+ XEvent *event,
+ String *params,
+ Cardinal *num_params)
+{
+ Scrn scrn = ScrnFromWidget(w);
+ if (! UserWantsAction(w, scrn))
+ return;
+ if ((strcmp(XtName(w), "sequenceMenu") == 0) &&
+ (event->type == ButtonRelease) &&
+ (XawSimpleMenuGetActiveEntry(w) == NULL))
+ return;
+ if (TocHasSequences(scrn->toc))
+ DoDeleteSeq(w, (XtPointer) scrn, (XtPointer) NULL);
+}
diff --git a/tocintrnl.h b/tocintrnl.h
new file mode 100644
index 0000000..7676a3f
--- /dev/null
+++ b/tocintrnl.h
@@ -0,0 +1,97 @@
+/* $XConsortium: tocintrnl.h,v 2.18 91/07/14 18:53:37 converse Exp $ */
+/*
+ * COPYRIGHT 1987
+ * 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.
+ */
+
+/* Includes for modules implementing toc stuff. */
+
+#ifndef _tocinternal_h
+#define _tocinternal_h
+
+#include <X11/IntrinsicP.h> /* %%% */
+#include "tsource.h"
+
+typedef enum {
+ unknown, valid, invalid
+} ValidType;
+
+typedef struct _MsgRec {
+ Toc toc; /* Which toc this message is in. */
+ Toc desttoc; /* Folder to copy or move to (NULL if none) */
+ Scrn *scrn; /* Scrns showing this message (if any) */
+ Widget source; /* Source (if any) containing this msg. */
+ XawTextPosition position; /* Position in the scanfile for this msg. */
+ XawTextPosition startPos; /* Where to start the insertion point. */
+ char *buf; /* The scanline for this message. */
+ int msgid; /* Message id for this message. */
+ short length; /* #/chars for this msg's entry in scanfile */
+ unsigned char num_scrns; /* How many scrns are currently showing msg */
+ unsigned fate:2; /* What will be done to this message */
+ unsigned changed:1; /* True iff this entry needs to be saved */
+ unsigned visible:1; /* Whether we should show this message */
+ unsigned temporary:1; /* Whether we should delete this message when
+ it is no longer visible */
+ unsigned reapable:1; /* True iff we don't need to keep this
+ composition around */
+ unsigned unused:2;
+} MsgRec;
+
+typedef struct _TocRec {
+ Scrn *scrn; /* Scrns containing this table of contents. */
+ Cardinal num_scrns; /* How many scrns are currently showing toc. */
+ char *foldername; /* Folder name for this toc */
+ char *path; /* Full path to folder's directory. */
+ char *scanfile; /* Full path to file containing scan. */
+ Msg curmsg; /* Current msgid for this toc. */
+ int nummsgs; /* How many info entries we currently have. */
+ Msg *msgs; /* Array of pointers to info about each msg. */
+ int numsequences; /* #/sequences defined for this folder. */
+ Sequence *seqlist; /* Array of pointers to sequences. */
+ Sequence viewedseq; /* Seq currently shown (NULL == all msgs) */
+ Sequence selectseq; /* The most recently selected sequence */
+ Widget source; /* Source for the file containing info. */
+ Boolean hasselection; /* Whether we own the selection. */
+ XawTextPosition left, right; /* Left and right extents of selection. */
+ int length; /* #/chars in the scanfile. */
+ int origlength; /* Original #/chars in the scanfile. */
+ int lastPos; /* Last legal position */
+ ValidType validity; /* Whether the scan file for this toc is */
+ /* up to date. */
+ Boolean needsrepaint; /* TRUE if we should repaint this toc. */
+ Boolean needscachesave; /* TRUE if the cache needs saving. */
+ Boolean needslabelupdate;/* TRUE if the toclabel needs repainting. */
+ Boolean stopupdate; /* Zero if refreshing; nonzero if refreshing is
+ currently inhibited. */
+ Boolean haschanged; /* Whether it's changed in the process of */
+ /* the current commit. */
+ Boolean delete_pending; /* Is a delete folder operation pending? */
+ Boolean force_reset; /* temporary bug work-around for sequences */
+ char *incfile; /* Which file to incorporate from (if any). */
+ int mailpending; /* True if we're currently displaying
+ mail pending true for this folder */
+ long lastreaddate; /* Last time we read or wrote the cache. */
+ Stack sequence_stack; /* Stack of sequence names. */
+} TocRec;
+
+#endif /* _tocinternal_h */
diff --git a/tocutil.c b/tocutil.c
new file mode 100644
index 0000000..72be172
--- /dev/null
+++ b/tocutil.c
@@ -0,0 +1,645 @@
+/*
+ * $XConsortium: tocutil.c,v 2.60 95/01/09 16:52:53 swick Exp $
+ * $XFree86: xc/programs/xmh/tocutil.c,v 3.4 2002/04/05 21:06:29 dickey 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.
+ */
+
+/* tocutil.c -- internal routines for toc stuff. */
+
+#include "xmh.h"
+#include "toc.h"
+#include "tocutil.h"
+#include "tocintrnl.h"
+
+#ifdef X_NOT_POSIX
+extern long lseek();
+#endif
+
+Toc TUMalloc(void)
+{
+ Toc toc;
+ toc = XtNew(TocRec);
+ bzero((char *)toc, (int) sizeof(TocRec));
+ toc->msgs = (Msg *) NULL;
+ toc->seqlist = (Sequence *) NULL;
+ toc->validity = unknown;
+ return toc;
+}
+
+
+/* Returns TRUE if the scan file for the given toc is out of date. */
+
+int TUScanFileOutOfDate(Toc toc)
+{
+ return LastModifyDate(toc->path) > toc->lastreaddate;
+}
+
+
+/* Make sure the sequence menu entries correspond exactly to the sequences
+ * for this toc.
+ */
+
+void TUCheckSequenceMenu(Toc toc)
+{
+ Scrn scrn;
+ register int i, n;
+ Arg query_args[2];
+ char *name;
+ Cardinal j;
+ int numChildren;
+ Widget menu, item;
+ Button button;
+ WidgetList children;
+
+ static XtCallbackRec callbacks[] = {
+ { DoSelectSequence, (XtPointer) NULL},
+ { (XtCallbackProc) NULL, (XtPointer) NULL},
+ };
+ static Arg args[] = {
+ { XtNcallback, (XtArgVal) callbacks},
+ { XtNleftMargin, (XtArgVal) 18},
+ };
+
+ for (j=0; j < toc->num_scrns; j++) {
+ scrn = toc->scrn[j];
+
+ /* Find the sequence menu and the number of entries in it. */
+
+ name = MenuBoxButtons[XMH_SEQUENCE].button_name;
+ button = BBoxFindButtonNamed(scrn->mainbuttons, name);
+ menu = BBoxMenuOfButton(button);
+ XtSetArg(query_args[0], XtNnumChildren, &numChildren);
+ XtSetArg(query_args[1], XtNchildren, &children);
+ XtGetValues(menu, query_args, (Cardinal) 2);
+ n = MenuBoxButtons[XMH_SEQUENCE].num_entries;
+ if (strcmp(XtName(children[0]), "menuLabel") == 0)
+ n++;
+
+ /* Erase the current check mark. */
+
+ for (i=(n-1); i < numChildren; i++)
+ ToggleMenuItem(children[i], False);
+
+ /* Delete any entries which should be deleted. */
+
+ for (i=n; i < numChildren; i++)
+ if (! TocGetSeqNamed(toc, XtName(children[i])))
+ XtDestroyWidget(children[i]);
+
+ /* Create any entries which should be created. */
+
+ callbacks[0].closure = (XtPointer) scrn;
+ for (i=1; i < toc->numsequences; i++)
+ if (! XtNameToWidget(menu, toc->seqlist[i]->name))
+ XtCreateManagedWidget(toc->seqlist[i]->name, smeBSBObjectClass,
+ menu, args, XtNumber(args));
+
+ /* Set the check mark. */
+
+ name = toc->viewedseq->name;
+ if ((item = XtNameToWidget(menu, name)) != NULL)
+ ToggleMenuItem(item, True);
+ }
+ TocSetSelectedSequence(toc, toc->viewedseq);
+}
+
+
+void TUScanFileForToc(Toc toc)
+{
+ Scrn scrn;
+ char **argv, str[100];
+ if (toc) {
+ TUGetFullFolderInfo(toc);
+ if (toc->num_scrns) scrn = toc->scrn[0];
+ else scrn = scrnList[0];
+
+ (void) sprintf(str, "Rescanning %s", toc->foldername);
+ ChangeLabel(scrn->toclabel, str);
+
+ argv = MakeArgv(5);
+ argv[0] = "scan";
+ argv[1] = TocMakeFolderName(toc);
+ argv[2] = "-width";
+ (void) sprintf(str, "%d", app_resources.toc_width);
+ argv[3] = str;
+ argv[4] = "-noheader";
+ DoCommand(argv, (char *) NULL, toc->scanfile);
+ XtFree(argv[1]);
+ XtFree((char *) argv);
+
+ toc->needslabelupdate = True;
+ toc->validity = valid;
+ toc->curmsg = NULL; /* Get cur msg somehow! %%% */
+ }
+}
+
+
+
+int TUGetMsgPosition(Toc toc, Msg msg)
+{
+ int msgid, h = 0, l, m;
+ char str[100];
+ static Boolean ordered = True;
+ msgid = msg->msgid;
+ if (ordered) {
+ l = 0;
+ h = toc->nummsgs - 1;
+ while (l < h - 1) {
+ m = (l + h) / 2;
+ if (toc->msgs[m]->msgid > msgid)
+ h = m;
+ else
+ l = m;
+ }
+ if (toc->msgs[l] == msg) return l;
+ if (toc->msgs[h] == msg) return h;
+ }
+ ordered = False;
+ for (l = 0; l < toc->nummsgs; l++) {
+ if (msgid == toc->msgs[l]->msgid) return l;
+ }
+ (void) sprintf(str,
+ "TUGetMsgPosition search failed! hi=%d, lo=%d, msgid=%d",
+ h, l, msgid);
+ Punt(str);
+ return 0; /* Keep lint happy. */
+}
+
+
+void TUResetTocLabel(Scrn scrn)
+{
+ char str[500];
+ Toc toc;
+ if (scrn) {
+ toc = scrn->toc;
+ if (toc == NULL)
+ (void) strcpy(str, " ");
+ else {
+ if (toc->stopupdate) {
+ toc->needslabelupdate = TRUE;
+ return;
+ }
+ (void) sprintf(str, "%s:%s", toc->foldername,
+ toc->viewedseq->name);
+ toc->needslabelupdate = FALSE;
+ }
+ ChangeLabel((Widget) scrn->toclabel, str);
+ }
+}
+
+
+/* A major toc change has occured; redisplay it. (This also should work even
+ if we now have a new source to display stuff from.) */
+
+void TURedisplayToc(Scrn scrn)
+{
+ Toc toc;
+ Widget source;
+ if (scrn != NULL && scrn->tocwidget != NULL) {
+ toc = scrn->toc;
+ if (toc) {
+ if (toc->stopupdate) {
+ toc->needsrepaint = TRUE;
+ return;
+ }
+ XawTextDisableRedisplay(scrn->tocwidget);
+ source = XawTextGetSource(scrn->tocwidget);
+ if (toc->force_reset || source != toc->source) {
+ XawTextSetSource(scrn->tocwidget, toc->source,
+ (XawTextPosition) 0);
+ toc->force_reset = False; /* %%% temporary */
+ }
+ TocSetCurMsg(toc, TocGetCurMsg(toc));
+ XawTextEnableRedisplay(scrn->tocwidget);
+ TUCheckSequenceMenu(toc);
+ toc->needsrepaint = FALSE;
+ } else {
+ XawTextSetSource(scrn->tocwidget, PNullSource, (XawTextPosition) 0);
+ }
+ }
+}
+
+
+void TULoadSeqLists(Toc toc)
+{
+ Sequence seq;
+ FILEPTR fid;
+ char str[500], *ptr, *ptr2, viewed[500], selected[500];
+ int i;
+ if (toc->viewedseq) (void) strcpy(viewed, toc->viewedseq->name);
+ else *viewed = 0;
+ if (toc->selectseq) (void) strcpy(selected, toc->selectseq->name);
+ else *selected = 0;
+ for (i = 0; i < toc->numsequences; i++) {
+ seq = toc->seqlist[i];
+ XtFree((char *) seq->name);
+ if (seq->mlist) FreeMsgList(seq->mlist);
+ XtFree((char *)seq);
+ }
+ toc->numsequences = 1;
+ toc->seqlist = (Sequence *) XtRealloc((char *) toc->seqlist,
+ (Cardinal) sizeof(Sequence));
+ seq = toc->seqlist[0] = XtNew(SequenceRec);
+ seq->name = XtNewString("all");
+ seq->mlist = NULL;
+ toc->viewedseq = seq;
+ toc->selectseq = seq;
+ (void) sprintf(str, "%s/.mh_sequences", toc->path);
+ fid = myfopen(str, "r");
+ if (fid) {
+ while ((ptr = ReadLine(fid))) {
+ ptr2 = strchr(ptr, ':');
+ if (ptr2) {
+ *ptr2 = 0;
+ if (strcmp(ptr, "all") != 0 &&
+ strcmp(ptr, "cur") != 0 &&
+ strcmp(ptr, "unseen") != 0) {
+ toc->numsequences++;
+ toc->seqlist = (Sequence *)
+ XtRealloc((char *) toc->seqlist, (Cardinal)
+ toc->numsequences * sizeof(Sequence));
+ seq = toc->seqlist[toc->numsequences - 1] =
+ XtNew(SequenceRec);
+ seq->name = XtNewString(ptr);
+ seq->mlist = StringToMsgList(toc, ptr2 + 1);
+ if (strcmp(seq->name, viewed) == 0) {
+ toc->viewedseq = seq;
+ *viewed = 0;
+ }
+ if (strcmp(seq->name, selected) == 0) {
+ toc->selectseq = seq;
+ *selected = 0;
+ }
+ }
+ }
+ }
+ (void) myfclose(fid);
+ }
+}
+
+
+
+/* Refigure what messages are visible. */
+
+void TURefigureWhatsVisible(Toc toc)
+{
+ MsgList mlist;
+ Msg msg, oldcurmsg;
+ int i;
+ int w, changed, newval, msgid;
+ Sequence seq = toc->viewedseq;
+ mlist = seq->mlist;
+ oldcurmsg = toc->curmsg;
+ TocSetCurMsg(toc, (Msg)NULL);
+ w = 0;
+ changed = FALSE;
+
+ for (i = 0; i < toc->nummsgs; i++) {
+ msg = toc->msgs[i];
+ msgid = msg->msgid;
+ while (mlist && mlist->msglist[w] && mlist->msglist[w]->msgid < msgid)
+ w++;
+ newval = (!mlist
+ || (mlist->msglist[w] && mlist->msglist[w]->msgid == msgid));
+ if (newval != msg->visible) {
+ changed = TRUE;
+ msg->visible = newval;
+ }
+ }
+ if (changed) {
+ TURefigureTocPositions(toc);
+ if (oldcurmsg) {
+ if (!oldcurmsg->visible) {
+ toc->curmsg = TocMsgAfter(toc, oldcurmsg);
+ if (toc->curmsg == NULL)
+ toc->curmsg = TocMsgBefore(toc, oldcurmsg);
+ } else toc->curmsg = oldcurmsg;
+ }
+ for (i=0 ; i<toc->num_scrns ; i++)
+ TURedisplayToc(toc->scrn[i]);
+ } else TocSetCurMsg(toc, oldcurmsg);
+ for (i=0 ; i<toc->num_scrns ; i++)
+ TUResetTocLabel(toc->scrn[i]);
+}
+
+
+/* (Re)load the toc from the scanfile. If reloading, this makes efforts to
+ keep the fates of msgs, and to keep msgs that are being edited. Note that
+ this routine must know of all places that msg ptrs are stored; it expects
+ them to be kept only in tocs, in scrns, and in msg sequences. */
+
+#define SeemsIdentical(msg1, msg2) ((msg1)->msgid == (msg2)->msgid && \
+ ((msg1)->temporary || (msg2)->temporary ||\
+ strcmp((msg1)->buf, (msg2)->buf) == 0))
+
+void TULoadTocFile(Toc toc)
+{
+ int maxmsgs, l, orignummsgs, i, j, origcurmsgid;
+ FILEPTR fid;
+ XawTextPosition position;
+ char *ptr;
+ Msg msg, curmsg;
+ Msg *origmsgs;
+ int bufsiz = app_resources.toc_width + 1;
+ static char *buf;
+
+ if (!buf)
+ buf = XtMalloc((Cardinal) bufsiz);
+ TocStopUpdate(toc);
+ toc->lastreaddate = LastModifyDate(toc->scanfile);
+ if (toc->curmsg) {
+ origcurmsgid = toc->curmsg->msgid;
+ TocSetCurMsg(toc, (Msg)NULL);
+ } else origcurmsgid = 0; /* The "default" current msg; 0 means none */
+ fid = FOpenAndCheck(toc->scanfile, "r");
+ maxmsgs = orignummsgs = toc->nummsgs;
+ if (maxmsgs == 0) maxmsgs = 100;
+ toc->nummsgs = 0;
+ origmsgs = toc->msgs;
+ toc->msgs = (Msg *) XtMalloc((Cardinal) maxmsgs * sizeof(Msg));
+ position = 0;
+ i = 0;
+ curmsg = NULL;
+ while ((ptr = fgets(buf, bufsiz, fid))) {
+ toc->msgs[toc->nummsgs++] = msg = XtNew(MsgRec);
+ bzero((char *) msg, sizeof(MsgRec));
+ msg->toc = toc;
+ msg->position = position;
+ msg->length = l = strlen(ptr);
+ position += l;
+ if (l == app_resources.toc_width && buf[bufsiz-2] != '\n') {
+ buf[bufsiz-2] = '\n';
+ msg->buf = strcpy(XtMalloc((Cardinal) ++l), ptr);
+ msg->msgid = atoi(ptr);
+ do
+ ptr = fgets(buf, bufsiz, fid);
+ while (ptr && (int) strlen(ptr) == app_resources.toc_width
+ && buf[bufsiz-2] != '\n');
+ } else {
+ msg->buf = strcpy(XtMalloc((Cardinal) ++l), ptr);
+ msg->msgid = atoi(ptr);
+ }
+ if (msg->msgid == origcurmsgid)
+ curmsg = msg;
+ msg->buf[MARKPOS] = ' ';
+ msg->changed = FALSE;
+ msg->fate = Fignore;
+ msg->desttoc = NULL;
+ msg->visible = TRUE;
+ if (toc->nummsgs >= maxmsgs) {
+ maxmsgs += 100;
+ toc->msgs = (Msg *) XtRealloc((char *) toc->msgs,
+ (Cardinal) maxmsgs * sizeof(Msg));
+ }
+ while (i < orignummsgs && origmsgs[i]->msgid < msg->msgid) i++;
+ if (i < orignummsgs) {
+ origmsgs[i]->buf[MARKPOS] = ' ';
+ if (SeemsIdentical(origmsgs[i], msg))
+ MsgSetFate(msg, origmsgs[i]->fate, origmsgs[i]->desttoc);
+ }
+ }
+ toc->length = toc->origlength = toc->lastPos = position;
+ toc->msgs = (Msg *) XtRealloc((char *) toc->msgs,
+ (Cardinal) toc->nummsgs * sizeof(Msg));
+ (void) myfclose(fid);
+ if ( (toc->source == NULL) && ( toc->num_scrns > 0 ) ) {
+ Arg args[1];
+
+ XtSetArg(args[0], XtNtoc, toc);
+ toc->source = XtCreateWidget("tocSource", tocSourceWidgetClass,
+ toc->scrn[0]->tocwidget,
+ args, (Cardinal) 1);
+ }
+ for (i=0 ; i<numScrns ; i++) {
+ msg = scrnList[i]->msg;
+ if (msg && msg->toc == toc) {
+ for (j=0 ; j<toc->nummsgs ; j++) {
+ if (SeemsIdentical(toc->msgs[j], msg)) {
+ msg->position = toc->msgs[j]->position;
+ msg->visible = TRUE;
+ ptr = toc->msgs[j]->buf;
+ l = toc->msgs[j]->length;
+ *(toc->msgs[j]) = *msg;
+ toc->msgs[j]->buf = ptr;
+ toc->msgs[j]->length = l;
+ scrnList[i]->msg = toc->msgs[j];
+ break;
+ }
+ }
+ if (j >= toc->nummsgs) {
+ msg->temporary = FALSE; /* Don't try to auto-delete msg. */
+ MsgSetScrnForce(msg, (Scrn) NULL);
+ }
+ }
+ }
+ for (i=0 ; i<orignummsgs ; i++)
+ MsgFree(origmsgs[i]);
+ XtFree((char *)origmsgs);
+ TocSetCurMsg(toc, curmsg);
+ TULoadSeqLists(toc);
+ TocStartUpdate(toc);
+}
+
+
+void TUSaveTocFile(Toc toc)
+{
+ Msg msg;
+ int fid;
+ int i;
+ XawTextPosition position;
+ char c;
+ if (toc->stopupdate) {
+ toc->needscachesave = TRUE;
+ return;
+ }
+ fid = -1;
+ position = 0;
+ for (i = 0; i < toc->nummsgs; i++) {
+ msg = toc->msgs[i];
+ if (fid < 0 && msg->changed) {
+ fid = myopen(toc->scanfile, O_RDWR, 0666);
+ (void) lseek(fid, (long)position, 0);
+ }
+ if (fid >= 0) {
+ c = msg->buf[MARKPOS];
+ msg->buf[MARKPOS] = ' ';
+ (void) write(fid, msg->buf, msg->length);
+ msg->buf[MARKPOS] = c;
+ }
+ position += msg->length;
+ }
+ if (fid < 0 && toc->length != toc->origlength)
+ fid = myopen(toc->scanfile, O_RDWR, 0666);
+ if (fid >= 0) {
+#if defined(SYSV) && (defined(i386) || defined(MOTOROLA))
+ (void) ftruncate_emu(fid, toc->length, toc->scanfile);
+#else
+ (void) ftruncate(fid, toc->length);
+ myclose(fid);
+#endif
+ toc->origlength = toc->length;
+ }
+ toc->needscachesave = FALSE;
+ toc->lastreaddate = LastModifyDate(toc->scanfile);
+}
+
+
+static Boolean UpdateScanFile(
+ XtPointer client_data) /* Toc */
+{
+ Toc toc = (Toc)client_data;
+ int i;
+
+ if (app_resources.block_events_on_busy) ShowBusyCursor();
+
+ TUScanFileForToc(toc);
+ TULoadTocFile(toc);
+
+ for (i=0 ; i<toc->num_scrns ; i++)
+ TURedisplayToc(toc->scrn[i]);
+
+ if (app_resources.block_events_on_busy) UnshowBusyCursor();
+
+ return True;
+}
+
+
+void TUEnsureScanIsValidAndOpen(Toc toc, Boolean delay)
+{
+ if (toc) {
+ TUGetFullFolderInfo(toc);
+ if (TUScanFileOutOfDate(toc)) {
+ if (!delay)
+ UpdateScanFile((XtPointer)toc);
+ else {
+ /* this is a hack to get the screen mapped before
+ * spawning the subprocess (and blocking).
+ * Need to make sure the scanfile exists at this point.
+ */
+ int fid = myopen(toc->scanfile, O_RDWR|O_CREAT, 0666);
+ myclose(fid);
+ XtAppAddWorkProc(XtWidgetToApplicationContext(toplevel),
+ UpdateScanFile,
+ (XtPointer)toc);
+ }
+ }
+ if (toc->source == NULL)
+ TULoadTocFile(toc);
+ }
+}
+
+
+
+/* Refigure all the positions, based on which lines are visible. */
+
+void TURefigureTocPositions(Toc toc)
+{
+ int i;
+ Msg msg;
+ XawTextPosition position, length;
+ position = length = 0;
+ for (i=0; i<toc->nummsgs ; i++) {
+ msg = toc->msgs[i];
+ msg->position = position;
+ if (msg->visible) position += msg->length;
+ length += msg->length;
+ }
+ toc->lastPos = position;
+ toc->length = length;
+}
+
+
+
+/* Make sure we've loaded ALL the folder info for this toc, including its
+ path and sequence lists. */
+
+void TUGetFullFolderInfo(Toc toc)
+{
+ char str[500];
+ if (! toc->scanfile) {
+ if (! toc->path) {
+ /* usually preset by TocFolderExists */
+ (void) sprintf(str, "%s/%s", app_resources.mail_path,
+ toc->foldername);
+ toc->path = XtNewString(str);
+ }
+ (void) sprintf(str, "%s/.xmhcache", toc->path);
+ toc->scanfile = XtNewString(str);
+ toc->lastreaddate = LastModifyDate(toc->scanfile);
+ if (TUScanFileOutOfDate(toc))
+ toc->validity = invalid;
+ else {
+ toc->validity = valid;
+ TULoadTocFile(toc);
+ }
+ }
+}
+
+/* Append a message to the end of the toc. It has the given scan line. This
+ routine will figure out the message number, and change the scan line
+ accordingly. */
+
+Msg TUAppendToc(Toc toc, char *ptr)
+{
+ Msg msg;
+ int msgid;
+
+ TUGetFullFolderInfo(toc);
+ if (toc->validity != valid)
+ return NULL;
+
+ if (toc->nummsgs > 0)
+ msgid = toc->msgs[toc->nummsgs - 1]->msgid + 1;
+ else
+ msgid = 1;
+ (toc->nummsgs)++;
+ toc->msgs = (Msg *) XtRealloc((char *) toc->msgs,
+ (Cardinal) toc->nummsgs * sizeof(Msg));
+ toc->msgs[toc->nummsgs - 1] = msg = XtNew(MsgRec);
+ bzero((char *) msg, (int) sizeof(MsgRec));
+ msg->toc = toc;
+ msg->buf = XtNewString(ptr);
+ if (msgid >= 10000)
+ msgid %= 10000;
+ (void)sprintf(msg->buf, "%4d", msgid);
+ msg->buf[MARKPOS] = ' ';
+ msg->msgid = msgid;
+ msg->position = toc->lastPos;
+ msg->length = strlen(ptr);
+ msg->changed = TRUE;
+ msg->fate = Fignore;
+ msg->desttoc = NULL;
+ if (toc->viewedseq == toc->seqlist[0]) {
+ msg->visible = TRUE;
+ toc->lastPos += msg->length;
+ }
+ else
+ msg->visible = FALSE;
+ toc->length += msg->length;
+ if ( (msg->visible) && (toc->source != NULL) )
+ TSourceInvalid(toc, msg->position, msg->length);
+ TUSaveTocFile(toc);
+ return msg;
+}
diff --git a/tocutil.h b/tocutil.h
new file mode 100644
index 0000000..5b0c593
--- /dev/null
+++ b/tocutil.h
@@ -0,0 +1,47 @@
+/* $XConsortium: tocutil.h,v 2.7 95/01/09 16:52:53 swick Exp $ */
+/*
+ * COPYRIGHT 1987
+ * 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 documen-
+ * tation, 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/tocutil.h,v 1.3 2002/07/01 02:26:05 tsi Exp $ */
+
+#ifndef _tocutil_h
+#define _tocutil_h
+
+extern Toc TUMalloc (void);
+extern int TUScanFileOutOfDate (Toc);
+extern void TUCheckSequenceMenu (Toc);
+extern void TUScanFileForToc (Toc);
+extern int TUGetMsgPosition (Toc, Msg);
+extern void TUResetTocLabel (Scrn);
+extern void TURedisplayToc (Scrn);
+extern void TULoadSeqLists (Toc);
+extern void TURefigureWhatsVisible (Toc);
+extern void TULoadTocFile (Toc);
+extern void TUSaveTocFile (Toc);
+extern void TUEnsureScanIsValidAndOpen (Toc, Boolean);
+extern void TURefigureTocPositions (Toc);
+extern void TUGetFullFolderInfo (Toc);
+extern Msg TUAppendToc (Toc, char *);
+
+#endif /* _tocutil_h */
diff --git a/tsource.c b/tsource.c
new file mode 100644
index 0000000..b9dec38
--- /dev/null
+++ b/tsource.c
@@ -0,0 +1,343 @@
+/* $XConsortium: tsource.c,v 2.24 91/10/21 14:32:36 eswu Exp $ */
+
+/*
+ * COPYRIGHT 1987
+ * 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/tsource.c,v 1.4 2002/04/07 03:57:46 tsi Exp $ */
+
+/* File: tsource.c -- the code for a toc source */
+
+#include "xmh.h"
+#include "tocintrnl.h"
+#include <X11/Xatom.h>
+#include "tsourceP.h"
+
+/****************************************************************
+ *
+ * Full class record constant
+ *
+ ****************************************************************/
+
+/* Private Data */
+
+#define Offset(field) XtOffsetOf(TocSourceRec, toc_source.field)
+
+static XtResource resources[] = {
+ {XtNtoc, XtCToc, XtRPointer, sizeof(caddr_t),
+ Offset(toc), XtRPointer, NULL},
+};
+
+#undef Offset
+
+static void Initialize(Widget, Widget, ArgList, Cardinal *num_args);
+static XawTextPosition Read(Widget, XawTextPosition, XawTextBlock *, int);
+static XawTextPosition Scan(Widget, XawTextPosition, XawTextScanType, XawTextScanDirection, int, Bool);
+static XawTextPosition Search(Widget, XawTextPosition, XawTextScanDirection, XawTextBlock *);
+static int Replace(Widget, XawTextPosition, XawTextPosition, XawTextBlock *);
+
+#define SuperClass (&textSrcClassRec)
+TocSourceClassRec tocSourceClassRec = {
+ {
+/* core_class fields */
+ /* superclass */ (WidgetClass) SuperClass,
+ /* class_name */ "TocSrc",
+ /* widget_size */ sizeof(TocSourceRec),
+ /* class_initialize */ NULL,
+ /* class_part_initialize */ NULL,
+ /* class_inited */ FALSE,
+ /* initialize */ Initialize,
+ /* initialize_hook */ NULL,
+ /* realize */ NULL,
+ /* actions */ NULL,
+ /* num_actions */ 0,
+ /* resources */ resources,
+ /* num_resources */ XtNumber(resources),
+ /* xrm_class */ NULLQUARK,
+ /* compress_motion */ FALSE,
+ /* compress_exposure */ FALSE,
+ /* compress_enterleave */ FALSE,
+ /* visible_interest */ FALSE,
+ /* destroy */ NULL,
+ /* resize */ NULL,
+ /* expose */ NULL,
+ /* set_values */ NULL,
+ /* set_values_hook */ NULL,
+ /* set_values_almost */ NULL,
+ /* get_values_hook */ NULL,
+ /* accept_focus */ NULL,
+ /* version */ XtVersion,
+ /* callback_private */ NULL,
+ /* tm_table */ NULL,
+ /* query_geometry */ NULL,
+ /* display_accelerator */ NULL,
+ /* extension */ NULL
+ },
+/* textSrc_class fields */
+ {
+ /* Read */ Read,
+ /* Replace */ Replace,
+ /* Scan */ Scan,
+ /* Search */ Search,
+ /* SetSelection */ XtInheritSetSelection,
+ /* ConvertSelection */ XtInheritConvertSelection
+ },
+/* toc_source_class fields */
+ {
+ /* keeping the compiler happy */ 0
+ }
+};
+
+WidgetClass tocSourceWidgetClass = (WidgetClass)&tocSourceClassRec;
+
+/************************************************************
+ *
+ * Class specific methods.
+ *
+ ************************************************************/
+
+Msg MsgFromPosition(
+ Toc toc,
+ XawTextPosition position,
+ XawTextScanDirection dir)
+{
+ Msg msg;
+ int h, l, m;
+ if (position > toc->lastPos) position = toc->lastPos;
+ if (dir == XawsdLeft) position--;
+ l = 0;
+ h = toc->nummsgs - 1;
+ while (l < h - 1) {
+ m = (l + h) / 2;
+ if (toc->msgs[m]->position > position)
+ h = m;
+ else
+ l = m;
+ }
+ msg = toc->msgs[h];
+ if (msg->position > position)
+ msg = toc->msgs[h = l];
+ while (!msg->visible)
+ msg = toc->msgs[h++];
+ if (position < msg->position || position > msg->position + msg->length)
+ Punt("Error in MsgFromPosition!");
+ return msg;
+}
+
+
+static XawTextPosition
+CoerceToLegalPosition(Toc toc, XawTextPosition position)
+{
+ return (position < 0) ? 0 :
+ ((position > toc->lastPos) ? toc->lastPos : position);
+}
+
+static XawTextPosition
+Read(
+ Widget w,
+ XawTextPosition position,
+ XawTextBlock *block,
+ int length)
+{
+ TocSourceWidget source = (TocSourceWidget) w;
+ Toc toc = source->toc_source.toc;
+ Msg msg;
+ int count;
+
+ if (position < toc->lastPos) {
+ block->firstPos = position;
+ msg = MsgFromPosition(toc, position, XawsdRight);
+ block->ptr = msg->buf + (position - msg->position);
+ count = msg->length - (position - msg->position);
+ block->length = (count < length) ? count : length;
+ position += block->length;
+ }
+ else {
+ block->firstPos = 0;
+ block->length = 0;
+ block->ptr = "";
+ }
+ block->format = FMT8BIT;
+ return position;
+}
+
+/* Right now, we can only replace a piece with another piece of the same size,
+ and it can't cross between lines. */
+
+static int
+Replace(
+ Widget w,
+ XawTextPosition startPos,
+ XawTextPosition endPos,
+ XawTextBlock *block)
+{
+ TocSourceWidget source = (TocSourceWidget) w;
+ Toc toc = source->toc_source.toc;
+ Msg msg;
+ int i;
+
+ if (block->length != endPos - startPos)
+ return XawEditError;
+ msg = MsgFromPosition(toc, startPos, XawsdRight);
+ for (i = 0; i < block->length; i++)
+ msg->buf[startPos - msg->position + i] = block->ptr[i];
+/* for (i=0 ; i<toc->numwidgets ; i++)
+ XawTextInvalidate(toc->widgets[i], startPos, endPos);
+*
+* CDP 9/1/89
+*/
+ return XawEditDone;
+}
+
+
+#define Look(ti, c)\
+{ \
+ if ((dir == XawsdLeft && ti <= 0) || \
+ (dir == XawsdRight && ti >= toc->lastPos)) \
+ c = 0; \
+ else { \
+ if (ti + doff < msg->position || \
+ ti + doff >= msg->position + msg->length) \
+ msg = MsgFromPosition(toc, ti, dir); \
+ c = msg->buf[ti + doff - msg->position]; \
+ } \
+}
+
+
+
+static XawTextPosition
+Scan(
+ Widget w,
+ XawTextPosition position,
+ XawTextScanType sType,
+ XawTextScanDirection dir,
+ int count,
+ Bool include)
+{
+ TocSourceWidget source = (TocSourceWidget) w;
+ Toc toc = source->toc_source.toc;
+ XawTextPosition textindex;
+ Msg msg;
+ char c;
+ int ddir, doff, i, whiteSpace = 0;
+ ddir = (dir == XawsdRight) ? 1 : -1;
+ doff = (dir == XawsdRight) ? 0 : -1;
+
+ if (toc->lastPos == 0) return 0;
+ textindex = position;
+ if (textindex + doff < 0) return 0;
+ if (dir == XawsdRight && textindex >= toc->lastPos) return toc->lastPos;
+ msg = MsgFromPosition(toc, textindex, dir);
+ switch (sType) {
+ case XawstPositions:
+ if (!include && count > 0)
+ count--;
+ textindex = CoerceToLegalPosition(toc, textindex + count * ddir);
+ break;
+ case XawstWhiteSpace:
+ for (i = 0; i < count; i++) {
+ whiteSpace = -1;
+ while (textindex >= 0 && textindex <= toc->lastPos) {
+ Look(textindex, c);
+ if ((c == ' ') || (c == '\t') || (c == '\n')) {
+ if (whiteSpace < 0) whiteSpace = textindex;
+ } else if (whiteSpace >= 0)
+ break;
+ textindex += ddir;
+ }
+ }
+ if (!include) {
+ if (whiteSpace < 0 && dir == XawsdRight)
+ whiteSpace = toc->lastPos;
+ textindex = whiteSpace;
+ }
+ textindex = CoerceToLegalPosition(toc, textindex);
+ break;
+ case XawstEOL:
+ case XawstParagraph:
+ for (i = 0; i < count; i++) {
+ while (textindex >= 0 && textindex <= toc->lastPos) {
+ Look(textindex, c);
+ if (c == '\n')
+ break;
+ textindex += ddir;
+ }
+ if (i < count - 1)
+ textindex += ddir;
+ }
+ if (include)
+ textindex += ddir;
+ textindex = CoerceToLegalPosition(toc, textindex);
+ break;
+ case XawstAll:
+ if (dir == XawsdLeft)
+ textindex = 0;
+ else
+ textindex = toc->lastPos;
+ break;
+ default:
+ break;
+ }
+ return textindex;
+}
+
+/*ARGSUSED*/
+static XawTextPosition Search(
+ Widget w,
+ XawTextPosition position,
+ XawTextScanDirection direction,
+ XawTextBlock *block)
+{
+ /* TocSourceWidget source = (TocSourceWidget) w;
+ * Toc toc = source->toc_source.toc;
+ * not implemented
+ */
+ return XawTextSearchError;
+}
+
+/* Public definitions. */
+
+/* ARGSUSED*/
+static void Initialize(
+ Widget request,
+ Widget new,
+ ArgList args,
+ Cardinal *num_args)
+{
+ Toc toc;
+ TocSourceWidget source = (TocSourceWidget) new;
+
+ source->text_source.edit_mode = XawtextRead; /* force read only. */
+
+ toc = source->toc_source.toc;
+
+ toc->hasselection = FALSE;
+ toc->left = toc->right = 0;
+}
+
+void
+TSourceInvalid(Toc toc, XawTextPosition position, int length)
+{
+ XawTextInvalidate(XtParent(toc->source), position,
+ (XawTextPosition) position+length-1);
+}
diff --git a/tsource.h b/tsource.h
new file mode 100644
index 0000000..66eaf99
--- /dev/null
+++ b/tsource.h
@@ -0,0 +1,77 @@
+/* $XConsortium: tsource.h,v 1.3 94/04/17 20:24:04 converse Exp $ */
+
+/***********************************************************
+
+Copyright (c) 1987, 1988 X Consortium
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the X Consortium shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from the X Consortium.
+
+
+Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts.
+
+ All Rights Reserved
+
+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 not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+/* $XFree86: xc/programs/xmh/tsource.h,v 1.3 2002/07/01 02:26:05 tsi Exp $ */
+
+#ifndef _tsource_h
+#define _tsource_h
+
+#include <X11/Xaw/TextSrc.h>
+
+/* xmh TextSrc widget resources:
+ Name Class RepType Default Value
+ ---- ----- ------- -------------
+ toc Toc Pointer NULL
+*/
+
+#define XtCToc "Toc"
+#define XtNtoc "toc"
+
+/* Class record constants */
+
+extern WidgetClass tocSourceWidgetClass;
+
+typedef struct _TocSourceClassRec *TocSourceWidgetClass;
+typedef struct _TocSourceRec *TocSourceWidget;
+
+extern Msg MsgFromPosition(Toc, XawTextPosition, XawTextScanDirection);
+extern void TSourceInvalid(Toc, XawTextPosition, int);
+
+#endif /* _XawTextSrc_h */
+/* DON'T ADD STUFF AFTER THIS #endif */
diff --git a/tsourceP.h b/tsourceP.h
new file mode 100644
index 0000000..71582bd
--- /dev/null
+++ b/tsourceP.h
@@ -0,0 +1,110 @@
+/*
+* $XConsortium: tsourceP.h,v 1.2 94/04/17 20:24:05 kit Exp $
+*/
+
+
+/***********************************************************
+
+Copyright (c) 1987, 1988 X Consortium
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the X Consortium shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from the X Consortium.
+
+
+Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts.
+
+ All Rights Reserved
+
+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 not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+/*
+ * tocSourceP.h - Private definitions for tocSource widget
+ *
+ */
+
+#ifndef _XawtocSourceP_h
+#define _XawtocSourceP_h
+
+/***********************************************************************
+ *
+ * tocSource Widget Private Data
+ *
+ ***********************************************************************/
+
+#include <X11/ObjectP.h>
+#include <X11/Xaw/TextSrcP.h>
+#include "tsource.h"
+#include "tocintrnl.h"
+
+/************************************************************
+ *
+ * New fields for the TocSource widget class record.
+ *
+ ************************************************************/
+
+typedef struct _TocSourceClassPart {
+ char foo; /* keep compiler happy. */
+} TocSourceClassPart;
+
+/* Full class record declaration */
+typedef struct _TocSourceClassRec {
+ ObjectClassPart object_class;
+ TextSrcClassPart text_source_class;
+ TocSourceClassPart toc_source_class;
+} TocSourceClassRec;
+
+extern TocSourceClassRec tocSourceClassRec;
+
+/* New fields for the TextSrc widget record */
+typedef struct {
+ /* resources */
+ Toc toc;
+} TocSourcePart;
+
+/****************************************************************
+ *
+ * Full instance record declaration
+ *
+ ****************************************************************/
+
+typedef struct _TocSourceRec {
+ ObjectPart object;
+ TextSrcPart text_source;
+ TocSourcePart toc_source;
+} TocSourceRec;
+
+#endif /* _XawTextSrcP_h */
diff --git a/util.c b/util.c
new file mode 100644
index 0000000..94a0719
--- /dev/null
+++ b/util.c
@@ -0,0 +1,485 @@
+/*
+ * $XConsortium: util.c /main/42 1996/01/14 16:51:55 kaleb $
+ * $XFree86: xc/programs/xmh/util.c,v 3.7 2002/04/05 21:06:29 dickey Exp $
+ *
+ *
+ * COPYRIGHT 1987
+ * 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.
+ */
+
+/* util.c -- little miscellaneous utilities. */
+
+#include "xmh.h"
+#include <sys/stat.h>
+#include <errno.h>
+#include <ctype.h>
+#include <X11/cursorfont.h>
+#include <X11/Xos.h>
+
+#ifndef abs
+#define abs(x) ((x) < 0 ? (-(x)) : (x))
+#endif
+
+static char *SysErrorMsg (int n)
+{
+ char *s = strerror(n);
+
+ return (s ? s : "no such error");
+}
+
+/* Something went wrong; panic and quit. */
+
+void Punt(char *str)
+{
+ (void) fprintf( stderr, "%s: %s\nerrno = %d; %s\007\n",
+ progName, str, errno, SysErrorMsg(errno) );
+ if (app_resources.debug) {
+ (void)fprintf(stderr, "forcing core dump.\n");
+ (void) fflush(stderr);
+ abort();
+ }
+ else {
+ (void)fprintf(stderr, "exiting.\n");
+ (void)fflush(stderr);
+ _exit(-1);
+ }
+}
+
+
+int myopen(char *path, int flags, int mode)
+{
+ int fid;
+ fid = open(path, flags, mode);
+ if (fid >= 0) DEBUG2("# %d : %s\n", fid, path)
+ return fid;
+}
+
+
+FILE *myfopen(char *path, char *mode)
+{
+ FILE *result;
+ result = fopen(path, mode);
+ if (result) DEBUG2("# %d : %s\n", fileno(result), path)
+ return result;
+}
+
+
+
+void myclose(int fid)
+{
+ if (close(fid) < 0) Punt("Error in myclose!");
+ DEBUG1( "# %d : <Closed>\n", fid)
+}
+
+
+void myfclose(FILE *file)
+{
+ int fid = fileno(file);
+ if (fclose(file) < 0) Punt("Error in myfclose!");
+ DEBUG1("# %d : <Closed>\n", fid)
+}
+
+
+
+/* Return a unique file name. */
+
+char *MakeNewTempFileName(void)
+{
+ static char name[60];
+ static int uniqueid = 0;
+ do {
+ (void) sprintf(name, "%s/xmh_%ld_%d", app_resources.temp_dir,
+ (long)getpid(), uniqueid++);
+ } while (FileExists(name));
+ return name;
+}
+
+
+/* Make an array of string pointers big enough to hold n+1 entries. */
+
+char **MakeArgv(int n)
+{
+ char **result;
+ result = ((char **) XtMalloc((unsigned) (n+1) * sizeof(char *)));
+ result[n] = 0;
+ return result;
+}
+
+
+char **ResizeArgv(char **argv, int n)
+{
+ argv = ((char **) XtRealloc((char *) argv, (unsigned) (n+1) * sizeof(char *)));
+ argv[n] = 0;
+ return argv;
+}
+
+/* Open a file, and punt if we can't. */
+
+FILEPTR FOpenAndCheck(char *name, char *mode)
+{
+ FILEPTR result;
+ result = myfopen(name, mode);
+ if (result == NULL) {
+ char str[500];
+ perror(progName);
+ (void)sprintf(str, "Error in FOpenAndCheck(%s, %s)", name, mode);
+ Punt(str);
+ }
+ return result;
+}
+
+
+/* Read one line from a file. */
+
+static char *DoReadLine(FILEPTR fid, char lastchar)
+{
+ static char *buf;
+ static int maxlength = 0;
+ char *ptr, c;
+ int length = 0;
+ ptr = buf;
+ c = ' ';
+ while (c != '\n' && !feof(fid)) {
+ c = getc(fid);
+ if (length++ > maxlength - 5) {
+ if (maxlength)
+ buf = XtRealloc(buf, (unsigned) (maxlength *= 2));
+ else
+ buf = XtMalloc((unsigned) (maxlength = 512));
+ ptr = buf + length - 1;
+ }
+ *ptr++ = c;
+ }
+ if (!feof(fid) || length > 1) {
+ *ptr = 0;
+ *--ptr = lastchar;
+ return buf;
+ }
+ return NULL;
+}
+
+
+char *ReadLine(FILEPTR fid)
+{
+ return DoReadLine(fid, 0);
+}
+
+
+/* Read a line, and keep the CR at the end. */
+
+char *ReadLineWithCR(FILEPTR fid)
+{
+ return DoReadLine(fid, '\n');
+}
+
+
+
+/* Delete a file, and Punt if it fails. */
+
+void DeleteFileAndCheck(char *name)
+{
+ if (strcmp(name, "/dev/null") != 0 && unlink(name) == -1) {
+ char str[500];
+ perror(progName);
+ (void)sprintf(str, "DeleteFileAndCheck(%s) failed!", name);
+ Punt(str);
+ }
+}
+
+void CopyFileAndCheck(char *from, char *to)
+{
+ int fromfid, tofid, n;
+ char buf[512];
+ fromfid = myopen(from, O_RDONLY, 0666);
+ tofid = myopen(to, O_WRONLY | O_TRUNC | O_CREAT, 0666);
+ if (fromfid < 0 || tofid < 0) {
+ perror(progName);
+ (void)sprintf(buf, "CopyFileAndCheck(%s->%s) failed!", from, to);
+ Punt(buf);
+ }
+ do {
+ n = read(fromfid, buf, 512);
+ if (n) (void) write(tofid, buf, n);
+ } while (n);
+ myclose(fromfid);
+ myclose(tofid);
+}
+
+
+void RenameAndCheck(char *from, char *to)
+{
+ if (rename(from, to) == -1) {
+ if (errno != EXDEV) {
+ char str[500];
+ perror(progName);
+ (void)sprintf(str, "RenameAndCheck(%s->%s) failed!", from, to);
+ Punt(str);
+ }
+ CopyFileAndCheck(from, to);
+ DeleteFileAndCheck(from);
+ }
+}
+
+
+char *CreateGeometry(int gbits, int x, int y, int width, int height)
+{
+ char *result, str1[10], str2[10], str3[10], str4[10];
+ if (gbits & WidthValue)
+ (void) sprintf(str1, "=%d", width);
+ else
+ (void) strcpy(str1, "=");
+ if (gbits & HeightValue)
+ (void) sprintf(str2, "x%d", height);
+ else
+ (void) strcpy(str2, "x");
+ if (gbits & XValue)
+ (void) sprintf(str3, "%c%d", (gbits & XNegative) ? '-' : '+', abs(x));
+ else
+ (void) strcpy(str3, "");
+ if (gbits & YValue)
+ (void) sprintf(str4, "%c%d", (gbits & YNegative) ? '-' : '+', abs(y));
+ else
+ (void) strcpy(str4, "");
+ result = XtMalloc((unsigned) 22);
+ (void) sprintf(result, "%s%s%s%s", str1, str2, str3, str4);
+ return result;
+}
+
+int FileExists(char *file)
+{
+ return (access(file, F_OK) == 0);
+}
+
+long LastModifyDate(char *file)
+{
+ struct stat buf;
+ if (stat(file, &buf)) return -1;
+ return buf.st_mtime;
+}
+
+int GetFileLength(char *file)
+{
+ struct stat buf;
+ if (stat(file, &buf)) return -1;
+ return buf.st_size;
+}
+
+Boolean IsSubfolder(char *foldername)
+{
+ return (strchr(foldername, '/')) ? True : False;
+}
+
+void SetCurrentFolderName(Scrn scrn, char *foldername)
+{
+ scrn->curfolder = foldername;
+ ChangeLabel((Widget) scrn->folderlabel, foldername);
+}
+
+
+void ChangeLabel(Widget widget, char *str)
+{
+ static Arg arglist[] = {{XtNlabel, (XtArgVal)NULL}};
+ arglist[0].value = (XtArgVal) str;
+ XtSetValues(widget, arglist, XtNumber(arglist));
+}
+
+
+Widget CreateTextSW(
+ Scrn scrn,
+ char *name,
+ ArgList args,
+ Cardinal num_args)
+{
+ /* most text widget options are set in the application defaults file */
+
+ return XtCreateManagedWidget( name, asciiTextWidgetClass, scrn->widget,
+ args, num_args);
+}
+
+
+Widget CreateTitleBar(Scrn scrn, char *name)
+{
+ Widget result;
+ int height;
+ static Arg arglist[] = {
+ {XtNlabel, (XtArgVal)NULL},
+ };
+ arglist[0].value = (XtArgVal) app_resources.banner; /* xmh version */
+ result = XtCreateManagedWidget( name, labelWidgetClass, scrn->widget,
+ arglist, XtNumber(arglist) );
+ height = GetHeight(result);
+ XawPanedSetMinMax(result, height, height);
+ return result;
+}
+
+void Feep(int type, int volume, Window win)
+{
+#ifdef XKB
+ XkbStdBell(theDisplay, win, volume, type);
+#else
+ XBell(theDisplay, volume);
+#endif
+}
+
+
+MsgList CurMsgListOrCurMsg(Toc toc)
+{
+ MsgList result;
+ Msg curmsg;
+ result = TocCurMsgList(toc);
+ if (result->nummsgs == 0 && (curmsg = TocGetCurMsg(toc))) {
+ FreeMsgList(result);
+ result = MakeSingleMsgList(curmsg);
+ }
+ return result;
+}
+
+
+int GetHeight(Widget w)
+{
+ Dimension height;
+ Arg args[1];
+
+ XtSetArg(args[0], XtNheight, &height);
+ XtGetValues( w, args, (Cardinal)1 );
+ return (int)height;
+}
+
+
+int GetWidth(Widget w)
+{
+ Dimension width;
+ Arg args[1];
+
+ XtSetArg(args[0], XtNwidth, &width);
+ XtGetValues( w, args, (Cardinal)1 );
+ return (int)width;
+}
+
+
+Toc SelectedToc(Scrn scrn)
+{
+ Toc toc;
+
+ /* tocs of subfolders are created upon the first reference */
+
+ if ((toc = TocGetNamed(scrn->curfolder)) == NULL)
+ toc = TocCreate(scrn->curfolder);
+ return toc;
+}
+
+
+Toc CurrentToc(Scrn scrn)
+{
+ /* return the toc currently being viewed */
+
+ return scrn->toc;
+}
+
+
+int strncmpIgnoringCase(char *str1, char *str2, int length)
+{
+ int i, diff;
+ for (i=0 ; i<length ; i++, str1++, str2++) {
+ diff = ((*str1 >= 'A' && *str1 <= 'Z') ? (*str1 + 'a' - 'A') : *str1) -
+ ((*str2 >= 'A' && *str2 <= 'Z') ? (*str2 + 'a' - 'A') : *str2);
+ if (diff) return diff;
+ }
+ return 0;
+}
+
+
+void StoreWindowName(Scrn scrn, char *str)
+{
+ static Arg arglist[] = {
+ {XtNiconName, (XtArgVal) NULL},
+ {XtNtitle, (XtArgVal) NULL},
+ };
+ arglist[0].value = arglist[1].value = (XtArgVal) str;
+ XtSetValues(scrn->parent, arglist, XtNumber(arglist));
+}
+
+
+/* Create an input-only window with a busy-wait cursor. */
+
+void InitBusyCursor(Scrn scrn)
+{
+ unsigned long valuemask;
+ XSetWindowAttributes attributes;
+
+ /* the second condition is for the pick scrn */
+ if (! app_resources.block_events_on_busy || scrn->wait_window)
+ return;
+
+ /* Ignore device events while the busy cursor is displayed. */
+
+ valuemask = CWDontPropagate | CWCursor;
+ attributes.do_not_propagate_mask = (KeyPressMask | KeyReleaseMask |
+ ButtonPressMask | ButtonReleaseMask |
+ PointerMotionMask);
+ attributes.cursor = app_resources.busy_cursor;
+
+ /* The window will be as big as the display screen, and clipped by
+ * it's own parent window, so we never have to worry about resizing.
+ */
+
+ scrn->wait_window =
+ XCreateWindow(XtDisplay(scrn->parent), XtWindow(scrn->parent), 0, 0,
+ rootwidth, rootheight, (unsigned int) 0, CopyFromParent,
+ InputOnly, CopyFromParent, valuemask, &attributes);
+}
+
+void ShowBusyCursor(void)
+{
+ register int i;
+
+ for (i=0; i < numScrns; i++)
+ if (scrnList[i]->mapped)
+ XMapWindow(theDisplay, scrnList[i]->wait_window);
+}
+
+void UnshowBusyCursor(void)
+{
+ register int i;
+
+ for (i=0; i < numScrns; i++)
+ if (scrnList[i]->mapped)
+ XUnmapWindow(theDisplay, scrnList[i]->wait_window);
+}
+
+
+void SetCursorColor(
+ Widget widget,
+ Cursor cursor,
+ unsigned long foreground)
+{
+ XColor colors[2];
+ Arg args[2];
+ Colormap cmap;
+
+ colors[0].pixel = foreground;
+ XtSetArg(args[0], XtNbackground, &(colors[1].pixel));
+ XtSetArg(args[1], XtNcolormap, &cmap);
+ XtGetValues(widget, args, (Cardinal) 2);
+ XQueryColors(XtDisplay(widget), cmap, colors, 2);
+ XRecolorCursor(XtDisplay(widget), cursor, &colors[0], &colors[1]);
+}
diff --git a/version.h b/version.h
new file mode 100644
index 0000000..d87d9c4
--- /dev/null
+++ b/version.h
@@ -0,0 +1,32 @@
+/* $XConsortium: version.h /main/6 1996/12/09 17:57:11 kaleb $ */
+/*
+
+Copyright (c) 1993 X Consortium
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the X Consortium shall
+not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization
+from the X Consortium.
+
+*/
+
+#define XMH_VERSION "xmh X Consortium R6.3"
diff --git a/viewfuncs.c b/viewfuncs.c
new file mode 100644
index 0000000..18bbb41
--- /dev/null
+++ b/viewfuncs.c
@@ -0,0 +1,286 @@
+/*
+ * $XConsortium: viewfuncs.c,v 2.23 92/04/08 12:18:41 rws 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/viewfuncs.c,v 1.3 2002/07/01 02:26:05 tsi Exp $ */
+
+/* view.c -- action procedures to handle viewing of a message */
+
+#include "xmh.h"
+#include "actions.h"
+
+/*ARGSUSED*/
+void DoCloseView(
+ Widget widget, /* unused */
+ XtPointer client_data,
+ XtPointer call_data) /* unused */
+{
+ Scrn scrn = (Scrn) client_data;
+ XtCallbackRec confirms[2];
+
+ confirms[0].callback = DoCloseView;
+ confirms[0].closure = (XtPointer) scrn;
+ confirms[1].callback = (XtCallbackProc) NULL;
+ confirms[1].closure = (XtPointer) NULL;
+
+ if (MsgSetScrn((Msg) NULL, scrn, confirms, (XtCallbackList) NULL) ==
+ NEEDS_CONFIRMATION)
+ return;
+ DestroyScrn(scrn);
+}
+
+
+/*ARGSUSED*/
+void XmhCloseView(
+ Widget w,
+ XEvent *event,
+ String *params,
+ Cardinal *num_params)
+{
+ Scrn scrn = ScrnFromWidget(w);
+ DoCloseView(w, (XtPointer) scrn, (XtPointer) NULL);
+}
+
+
+static void DoViewReplyMsg(
+ Scrn scrn,
+ String *params,
+ Cardinal num_params)
+{
+ Msg msg;
+ Scrn nscrn;
+
+ if (scrn->msg == NULL) return;
+ nscrn = NewCompScrn();
+ ScreenSetAssocMsg(nscrn, scrn->msg);
+ msg = TocMakeNewMsg(DraftsFolder);
+ MsgSetTemporary(msg);
+ MsgLoadReply(msg, scrn->msg, params, num_params);
+ MsgSetScrnForComp(msg, nscrn);
+ MapScrn(nscrn);
+}
+
+/*ARGSUSED*/
+void DoViewReply(
+ Widget w,
+ XtPointer client_data,
+ XtPointer call_data)
+{
+ DoViewReplyMsg((Scrn) client_data, (String *)NULL, (Cardinal)0);
+}
+
+
+
+/*ARGSUSED*/
+void XmhViewReply(
+ Widget w,
+ XEvent *event,
+ String *params,
+ Cardinal *num_params)
+{
+ Scrn scrn = ScrnFromWidget(w);
+ DoViewReplyMsg(scrn, params, *num_params);
+}
+
+
+/*ARGSUSED*/
+static void DoViewForwardMsg(
+ Scrn scrn,
+ String *params,
+ Cardinal num_params)
+{
+ MsgList mlist;
+
+ if (scrn->msg == NULL) return;
+ mlist = MakeSingleMsgList(scrn->msg);
+ CreateForward(mlist, params, num_params);
+ FreeMsgList(mlist);
+}
+
+/*ARGSUSED*/
+void DoViewForward(
+ Widget w,
+ XtPointer client_data,
+ XtPointer call_data)
+{
+ DoViewForwardMsg((Scrn) client_data, (String *)NULL, (Cardinal)0);
+}
+
+/*ARGSUSED*/
+void XmhViewForward(
+ Widget w,
+ XEvent *event,
+ String *params,
+ Cardinal *num_params)
+{
+ DoViewForwardMsg(ScrnFromWidget(w), params, *num_params);
+}
+
+
+/*ARGSUSED*/
+void DoViewUseAsComposition(
+ Widget w,
+ XtPointer client_data,
+ XtPointer call_data)
+{
+ Scrn scrn = (Scrn) client_data;
+ Msg msg;
+ Scrn nscrn;
+
+ if (scrn->msg == NULL) return;
+ nscrn = NewCompScrn();
+ if (MsgGetToc(scrn->msg) == DraftsFolder)
+ msg = scrn->msg;
+ else {
+ msg = TocMakeNewMsg(DraftsFolder);
+ MsgLoadCopy(msg, scrn->msg);
+ MsgSetTemporary(msg);
+ }
+ MsgSetScrnForComp(msg, nscrn);
+ MapScrn(nscrn);
+}
+
+
+/*ARGSUSED*/
+void XmhViewUseAsComposition(
+ Widget w,
+ XEvent *event,
+ String *params,
+ Cardinal *num_params)
+{
+ Scrn scrn = ScrnFromWidget(w);
+ DoViewUseAsComposition(w, (XtPointer) scrn, (XtPointer) NULL);
+}
+
+
+/*ARGSUSED*/
+void DoEditView(
+ Widget w,
+ XtPointer client_data,
+ XtPointer call_data)
+{
+ Scrn scrn = (Scrn) client_data;
+ Arg args[1];
+ XtTranslations editTranslations = scrn->edit_translations;
+
+ if (scrn->msg == NULL) return;
+ XtSetArg(args[0], XtNtranslations, editTranslations);
+ XtSetValues(scrn->viewwidget, args, (Cardinal) 1);
+ MsgSetEditable(scrn->msg);
+}
+
+
+/*ARGSUSED*/
+void XmhEditView(
+ Widget w,
+ XEvent *event,
+ String *params,
+ Cardinal *num_params)
+{
+ Scrn scrn = ScrnFromWidget(w);
+ if (scrn->msg != NULL && ! MsgGetEditable(scrn->msg))
+ DoEditView(w, (XtPointer) scrn, (XtPointer) NULL);
+}
+
+
+/*ARGSUSED*/
+void DoSaveView(
+ Widget w,
+ XtPointer client_data,
+ XtPointer call_data)
+{
+ Scrn scrn = (Scrn) client_data;
+ Arg args[2];
+
+ if (scrn->msg == NULL) return;
+ if (MsgSaveChanges(scrn->msg)) {
+ XtSetArg(args[0], XtNtranslations, scrn->read_translations);
+ XtSetValues(scrn->viewwidget, args, (Cardinal) 1);
+ MsgClearEditable(scrn->msg);
+ }
+}
+
+
+/*ARGSUSED*/
+void XmhSaveView(
+ Widget w,
+ XEvent *event,
+ String *params,
+ Cardinal *num_params)
+{
+ Scrn scrn = ScrnFromWidget(w);
+ if (MsgChanged(scrn->msg) || MsgGetReapable(scrn->msg))
+ DoSaveView(w, (XtPointer) scrn, (XtPointer) NULL);
+}
+
+
+/*ARGSUSED*/
+void DoPrintView(
+ Widget w,
+ XtPointer client_data,
+ XtPointer call_data)
+{
+ Scrn scrn = (Scrn) client_data;
+ char **argv;
+ char str[200];
+
+ if (! scrn->msg) return;
+ (void) sprintf(str, "%s %s", app_resources.print_command,
+ MsgFileName(scrn->msg));
+ argv = MakeArgv(3);
+ argv[0] = "/bin/sh";
+ argv[1] = "-c"; /* commands are read from the next argument */
+ argv[2] = str;
+ (void) DoCommand(argv, (char*)NULL, (char*)NULL);
+ /* a "notice" popup should appear with any stderr output */
+ XtFree((char*)argv);
+}
+
+
+/*ARGSUSED*/
+void XmhPrintView(
+ Widget w,
+ XEvent *event,
+ String *params,
+ Cardinal *num_params)
+{
+ Scrn scrn = ScrnFromWidget(w);
+ DoPrintView(w, (XtPointer) scrn, (XtPointer) NULL);
+}
+
+
+/*ARGSUSED*/
+void XmhViewMarkDelete(
+ Widget w,
+ XEvent *event,
+ String *params,
+ Cardinal *num_params)
+{
+ Scrn scrn = ScrnFromWidget(w);
+ if (scrn->msg == NULL) return;
+ MsgSetFate(scrn->msg, Fdelete, (Toc)NULL);
+}
+
+
diff --git a/xmh.h b/xmh.h
new file mode 100644
index 0000000..61ba22e
--- /dev/null
+++ b/xmh.h
@@ -0,0 +1,152 @@
+/*
+ * $XConsortium: xmh.h,v 2.32 93/09/08 15:31:11 kaleb Exp $
+ *
+ *
+ * COPYRIGHT 1987
+ * 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/xmh.h,v 1.3 2002/07/01 02:26:05 tsi Exp $ */
+
+#ifndef _xmh_h
+#define _xmh_h
+
+#include <X11/Intrinsic.h>
+#include <X11/StringDefs.h>
+#include <X11/Shell.h>
+#include <X11/Xos.h>
+#include <X11/Xfuncs.h>
+#include <X11/Xutil.h>
+#include <X11/cursorfont.h>
+#include <X11/Xaw/AsciiText.h>
+#include <X11/Xaw/SmeBSB.h>
+#include <X11/Xaw/Box.h>
+#include <X11/Xaw/Command.h>
+#include <X11/Xaw/Dialog.h>
+#include <X11/Xaw/Form.h>
+#include <X11/Xaw/Label.h>
+#include <X11/Xaw/SmeLine.h>
+#include <X11/Xaw/MenuButton.h>
+#include <X11/Xaw/SimpleMenu.h>
+#include <X11/Xaw/Toggle.h>
+#include <X11/Xaw/Viewport.h>
+#include <X11/Xaw/Paned.h>
+#if defined(sun) && defined(SVR4)
+#define _XOPEN_SOURCE
+#include <stdio.h>
+#undef _XOPEN_SOURCE
+#else
+#include <stdio.h>
+#endif
+
+#define DELETEABORTED -1
+#define NEEDS_CONFIRMATION -1
+#define MARKPOS 4
+
+#define xMargin 2
+#define yMargin 2
+
+#define DEBUG(msg) \
+ if (app_resources.debug) \
+ {(void)fprintf(stderr, msg); (void)fflush(stderr);}
+
+#define DEBUG1(msg, arg) \
+ if (app_resources.debug) \
+ {(void)fprintf(stderr, msg, arg); (void)fflush(stderr);}
+
+#define DEBUG2(msg, arg1, arg2) \
+ if (app_resources.debug) \
+ {(void)fprintf(stderr,msg,arg1,arg2); (void)fflush(stderr);}
+
+typedef int * dp; /* For debugging. */
+
+typedef FILE* FILEPTR;
+
+typedef struct _ButtonRec *Button;
+typedef struct _XmhButtonBoxRec *ButtonBox;
+typedef struct _TocRec *Toc;
+typedef struct _MsgRec *Msg;
+typedef struct _PickRec *Pick;
+
+typedef enum {
+ Fignore, Fmove, Fcopy, Fdelete
+} FateType;
+
+typedef enum {
+ STtocAndView,
+ STview,
+ STcomp,
+ STpick
+} ScrnKind;
+
+typedef struct _StackRec {
+ char *data;
+ struct _StackRec *next;
+} StackRec, *Stack;
+
+
+typedef struct _ScrnRec {
+ Widget parent; /* The parent widget of the scrn */
+ Widget widget; /* The pane widget for the scrn */
+ int mapped; /* TRUE only if we've mapped this screen. */
+ ScrnKind kind; /* What kind of scrn we have. */
+ ButtonBox mainbuttons; /* Main xmh control buttons. */
+ Widget folderlabel; /* Folder titlebar */
+ ButtonBox folderbuttons; /* Folder buttons. */
+ Widget toclabel; /* Toc titlebar. */
+ Widget tocwidget; /* Toc text. */
+ ButtonBox miscbuttons; /* optional miscellaneous command buttons */
+ Widget viewlabel; /* View titlebar. */
+ Widget viewwidget; /* View text. */
+ ButtonBox viewbuttons; /* View control buttons. */
+ char * curfolder; /* Currently selected folder name */
+ Toc toc; /* The table of contents. */
+ Msg msg; /* The message being viewed. */
+ Pick pick; /* Pick in this screen. */
+ XtTranslations edit_translations; /* Text widget translations */
+ XtTranslations read_translations; /* overridden by accelerators */
+ Msg assocmsg; /* Associated message for reply, etc. */
+ Window wait_window; /* InputOnly window with busy cursor */
+ Stack folder_stack; /* Stack of folder names */
+} ScrnRec, *Scrn;
+
+
+typedef struct {
+ int nummsgs;
+ Msg *msglist;
+} MsgListRec, *MsgList;
+
+
+typedef struct {
+ char *name; /* Name of this sequence. */
+ MsgList mlist; /* Messages in this sequence. */
+} SequenceRec, *Sequence;
+
+#define XMH_CB_ARGS Widget, XtPointer, XtPointer
+
+#include "globals.h"
+#include "externs.h"
+#include "mlist.h"
+#include "bbox.h"
+#include "msg.h"
+#include "toc.h"
+
+#endif /* _xmh_h */
diff --git a/xmh.man b/xmh.man
new file mode 100644
index 0000000..66d42ed
--- /dev/null
+++ b/xmh.man
@@ -0,0 +1,1539 @@
+.\" $XConsortium: xmh.man /main/39 1996/12/09 17:10:05 kaleb $
+.\" Copyright (c) 1989, 1991, 1994 X Consortium
+.\" Copyright 1988, 1989, Digital Equipment Corporation.
+.\"
+.\" Permission is hereby granted, free of charge, to any person obtaining
+.\" a copy of this software and associated documentation files (the
+.\" "Software"), to deal in the Software without restriction, including
+.\" without limitation the rights to use, copy, modify, merge, publish,
+.\" distribute, sublicense, and/or sell copies of the Software, and to
+.\" permit persons to whom the Software is furnished to do so, subject to
+.\" the following conditions:
+.\"
+.\" The above copyright notice and this permission notice shall be included
+.\" in all copies or substantial portions of the Software.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+.\" OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+.\" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+.\" IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR
+.\" OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+.\" ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+.\" OTHER DEALINGS IN THE SOFTWARE.
+.\"
+.\" Except as contained in this notice, the name of the X Consortium shall
+.\" not be used in advertising or otherwise to promote the sale, use or
+.\" other dealings in this Software without prior written authorization
+.\" from the X Consortium.
+.\"
+.\" $XFree86: xc/programs/xmh/xmh.man,v 1.3 2001/01/27 18:21:19 dawes Exp $
+.\"
+.TH XMH 1 __xorgversion__
+.SH NAME
+xmh \- send and read mail with an X interface to MH
+.SH SYNOPSIS
+.B xmh
+[\-path \fImailpath\fR] [\-initial \fIfoldername\fR] [\-flag] [\-\fItoolkitoption\fR ...]
+.SH DESCRIPTION
+The
+.I xmh
+program provides a graphical user interface to the \fIMH\fR Message
+Handling System. To actually do things with your mail, it makes calls to the
+\fIMH\fR package. Electronic mail messages may be composed, sent,
+received, replied to, forwarded, sorted, and stored in folders. \fIxmh\fR
+provides extensive mechanism for customization of the user interface.
+.PP
+This document introduces many aspects of the Athena Widget Set.
+
+.SH OPTIONS
+.TP 8
+.B \-path \fIdirectory\fP
+This option specifies an alternate collection of mail folders in which to
+process mail. The directory is specified as an absolute pathname.
+The default mail path is the value of the Path component in the \fIMH\fP
+profile, which is determined by the \fBMH\fP environment variable and
+defaults to $HOME/.mh_profile. $HOME/Mail will be used as the path if
+the \fIMH\fP Path is not given in the profile.
+.TP 8
+.B \-initial \fIfolder\fP
+This option specifies an alternate folder which may receive new mail and is
+initially opened by \fIxmh\fR.
+The default initial folder is ``inbox''.
+.TP 8
+.B \-flag
+This option will cause \fIxmh\fR to change the appearance of appropriate
+folder buttons and to request the window manager to change the appearance
+of the \fIxmh\fP icon when new mail has arrived. By default,
+\fIxmh\fP will change the appearance of the ``inbox'' folder button when
+new mail is waiting. The application-specific resource \fBcheckNewMail\fP
+can be used to turn off this notification, and the \fB\-flag\fP option will
+still override it.
+.PP
+These three options have corresponding application-specific resources,
+\fBMailPath\fR, \fBInitialFolder\fR, and \fBMailWaitingFlag\fR, which
+can be specified in a resource file.
+.PP
+The standard toolkit command line options are given in \fIX(__miscmansuffix__)\fP.
+
+.SH INSTALLATION
+.PP
+\fIxmh\fR requires that the user is already set up
+to use \fIMH\fR, version 6. To do so, see if there is a file
+called .mh_profile in your home directory. If it exists, check to see if it
+contains a line that starts with ``Current-Folder''. If it does,
+you've been using version 4 or earlier of \fIMH\fR; to convert to version
+6, you must remove that line. (Failure to do so causes spurious output to
+stderr, which can hang \fIxmh\fR depending on your setup.)
+.PP
+If you do not already have a .mh_profile, you can create one (and
+everything else you need) by typing ``inc'' to the shell. You should
+do this before using \fIxmh\fR to incorporate new mail.
+.PP
+For more information, refer to the \fImh(1)\fP documentation.
+.PP
+Much of the user interface of \fIxmh\fP is configured in the \fIXmh\fP
+application class defaults file; if this file was not installed properly
+a warning message will appear when \fIxmh\fP is used. \fIxmh\fP is
+backwards compatible with the R4 application class defaults file.
+.PP
+The default value of the SendBreakWidth resource has changed since R4.
+
+.SH BASIC SCREEN LAYOUT
+\fIxmh\fR starts out with a single window, divided into four major areas:
+
+.TP 4
+.B \-
+Six buttons with pull-down command menus.
+.PP
+.TP 4
+.B \-
+A collection of buttons, one for each top level folder.
+New users of \fIMH\fP will have two folders, ``drafts'' and ``inbox''.
+.PP
+.TP 4
+.B \-
+A listing, or Table of Contents, of the messages in the open folder.
+Initially, this will show the messages in ``inbox''.
+.PP
+.TP 4
+.B \-
+A view of one of your messages. Initially this is blank.
+
+.SH XMH AND THE ATHENA WIDGET SET
+\fIxmh\fR uses the X Toolkit Intrinsics and the Athena Widget Set.
+Many of the features described below (scrollbars, buttonboxes, etc.) are
+actually part of the Athena Widget Set, and are described here only for
+completeness. For more information, see the Athena Widget Set documentation.
+
+.SS SCROLLBARS
+Some parts of the main window will have a vertical area on the left containing
+a grey bar. This area is a \fIscrollbar\fR. They are used whenever the
+data in a window takes up more space than can be displayed.
+The grey bar indicates what portion of your data is visible. Thus, if the
+entire length of the area is grey, then you are looking at all your data.
+If only the first half is grey, then you are looking at the top half of
+your data.
+The message viewing area will have a horizontal scrollbar if the text
+of the message is wider than the viewing area.
+.PP
+You can use the pointer in the scrollbar to change what part of the data is
+visible. If you click with pointer button 2, the top of the grey
+area will move to where the pointer is, and the corresponding
+portion of data will be displayed. If you hold down pointer button 2,
+you can drag around the grey area. This makes it easy to get to the top
+of the data: just press with button 2, drag off the top of the
+scrollbar, and release.
+.PP
+If you click with button 1, then the data to the right of the
+pointer will scroll to the top of the window. If you click with pointer
+button 3, then the data at the top of the window will scroll down to where
+the pointer is.
+
+.SS BUTTONBOXES, BUTTONS, AND MENUS
+Any area containing many words or short phrases, each enclosed in a
+rectangular or rounded boundary, is called a \fIbuttonbox\fR.
+Each rectangle or rounded area is actually a button that you
+can press by moving the pointer onto it and pressing pointer button 1.
+If a given buttonbox has more buttons in it than can fit, it will
+be displayed with a scrollbar, so you can always scroll to the button you
+want.
+.PP
+Some buttons have pull-down menus.
+Pressing the pointer button while the pointer is over one of these
+buttons will pull down a menu. Continuing to hold the button down while
+moving the
+pointer over the menu, called dragging the pointer, will highlight each
+selectable item
+on the menu as the pointer passes over it. To select an item in the menu,
+release the pointer button while the item is highlighted.
+
+.SS ADJUSTING THE RELATIVE SIZES OF AREAS
+If you're not satisfied with the sizes of the various areas of the main window,
+they can easily be changed. Near the right edge of the border between
+each region is a black box, called a \fIgrip\fR. Simply point to that
+grip with the pointer, press a pointer button, drag up or down, and
+release. Exactly what happens depends on which pointer button you press.
+.PP
+If you drag with the pointer button 2, then only that border will move. This
+mode is simplest to understand, but is the least useful.
+.PP
+If you drag with pointer button 1, then you are adjusting the size of
+the window above. \fIxmh\fR will attempt to compensate by adjusting some
+window below it.
+.PP
+If you drag with pointer button 3, then you are adjusting the size
+of the window below. \fIxmh\fR will attempt to compensate by adjusting
+some window above it.
+.PP
+All windows have a minimum and maximum size; you will never be allowed to
+move a border past the point where it would make a window have an invalid
+size.
+
+.SH PROCESSING YOUR MAIL
+This section will define the concepts of the selected folder, current folder,
+selected message(s), current message, selected sequence, and current
+sequence. Each \fIxmh\fR command is introduced.
+.PP
+For use in customization,
+action procedures corresponding to each command are given; these action
+procedures can be used to customize the user interface, particularly the
+keyboard accelerators and the functionality of the buttons in the optional
+button box created by the application resource \fBCommandButtonCount\fR.
+
+.SS FOLDERS AND SEQUENCES
+A folder contains a collection of mail messages, or is empty. \fIxmh\fP
+supports folders with one level of subfolders.
+.PP
+The selected folder is whichever foldername appears in the bar above the
+folder buttons. Note that this is not necessarily the same folder that is
+currently being viewed.
+To change the selected folder, just press on the desired folder button
+with pointer button 1;
+if that folder has subfolders, select a folder from the pull-down menu.
+.PP
+The Table of Contents, or toc, lists the messages in the viewed folder.
+The title bar above the Table of Contents displays the name of the
+viewed folder.
+.PP
+The toc title bar also displays the name of the viewed sequence of messages
+within the viewed folder.
+Every folder has an implicit ``all'' sequence, which contains all the messages
+in the folder, and initially the toc title bar will show ``inbox:all''.
+
+.SS FOLDER COMMANDS
+The \fIFolder\fR command menu contains commands of a global nature:
+
+.TP 8
+.B Open Folder
+Display the data in the selected folder. Thus, the selected folder also
+becomes the viewed folder.
+The action procedure corresponding
+to this command is \fBXmhOpenFolder(\fR[\fIfoldername\fR]\fB)\fR.
+It takes an optional argument as the name of a folder to select and open; if no
+folder is specified, the selected folder is opened. It may be specified
+as part of an event translation from a folder menu button or from a
+folder menu, or as a binding of a keyboard accelerator to any widget other
+than the folder menu buttons or the folder menus.
+.TP 8
+.B Open Folder in New Window
+Displays the selected folder in an additional main window.
+Note, however, that you cannot reliably display the same folder in more
+than one window at a time, although \fIxmh\fR will not prevent you from trying.
+The corresponding action is \fBXmhOpenFolderInNewWindow()\fR.
+.TP 8
+.B Create Folder
+Create a new folder.
+You will be prompted for a name for the new folder;
+to enter the name, move the pointer to the blank box provided and type.
+Subfolders are created by specifying the parent folder, a slash, and the
+subfolder name. For example,
+to create a folder named ``xmh'' which is a subfolder of an existing folder
+named ``clients'', type ``clients/xmh''.
+Click on the Okay button when finished, or just type Return;
+click on Cancel to cancel this operation.
+The action corresponding to Create Folder is \fBXmhCreateFolder()\fR.
+.PP
+.TP 8
+.B Delete Folder
+Destroy the selected folder. You will be asked to confirm this action (see
+CONFIRMATION WINDOWS). Destroying a folder will also destroy any subfolders
+of that folder. The corresponding action is \fBXmhDeleteFolder()\fP.
+.PP
+.TP 8
+.B Close Window
+Exits \fIxmh\fR, after first confirming that you won't lose any changes;
+or, if selected from any additional \fIxmh\fP window, simply closes that
+window. The corresponding action is \fBXmhClose()\fP.
+
+.SS HIGHLIGHTED MESSAGES, SELECTED MESSAGES
+.SS AND THE CURRENT MESSAGE
+It is possible to highlight a set of adjacent messages in the area of the
+Table of Contents.
+To highlight a message, click on it with pointer button 1.
+To highlight a range of messages, click on the first one with
+pointer button 1 and on the last one with pointer button 3; or
+press pointer button 1, drag, and release.
+To extend a range of selected messages, use pointer button 3.
+To highlight all messages in the table of contents,
+click rapidly three times with pointer button 1.
+To cancel any selection in the table of contents, click rapidly twice.
+
+The selected messages are the same as the highlighted messages, if any. If no
+messages are highlighted, then the selected messages are considered the same
+as the current message.
+
+The current message is indicated by a `+' next to the message number. It
+usually corresponds to the message currently being viewed. Upon opening
+a new folder, for example, the current message will be different from the
+viewed message.
+When a message is viewed, the title bar above the view will identify the message.
+
+.SS TABLE OF CONTENTS COMMANDS
+The \fITable of Contents\fP command menu
+contains commands which operate on the open, or viewed, folder.
+
+.TP 18
+.B Incorporate New Mail
+Add any new mail received to viewed folder, and set the current
+message to be the first new message. This command is selectable in the menu
+and will execute only if the viewed folder is allowed to receive new mail.
+By default, only ``inbox'' is allowed to incorporate new mail.
+The corresponding action is \fBXmhIncorporateNewMail()\fP.
+.TP 18
+.B Commit Changes
+Execute all deletions, moves, and copies that have been marked in this
+folder. The corresponding action is \fBXmhCommitChanges()\fP.
+.TP 18
+.B Pack Folder
+Renumber the messages in this folder so they start with 1 and increment by
+1. The corresponding action is \fBXmhPackFolder()\fP.
+.TP 18
+.B Sort Folder
+Sort the messages in this folder in chronological order. (As a side
+effect, this may also pack the folder.) The corresponding action is
+\fBXmhSortFolder()\fP.
+.TP 18
+.B Rescan Folder
+Rebuild the list of messages. This can be used whenever you suspect
+that \fIxmh\fR's
+idea of what messages you have is wrong. (In particular, this is necessary
+if you change things using straight \fIMH\fR commands without using
+\fIxmh\fR.) The corresponding action is \fBXmhForceRescan()\fP.
+
+.SS MESSAGE COMMANDS
+The \fIMessage\fR command menu contains commands which operate on the selected
+message(s), or if there are no selected messages, the current message.
+
+.TP 18
+.B Compose Message
+Composes a new message. A new window will be brought up for composition;
+a description of it is given in the COMPOSITION WINDOWS section below.
+This command does not affect the current message.
+The corresponding action is \fBXmhComposeMessage()\fP.
+.PP
+.TP 18
+.B View Next Message
+View the first selected message. If no messages are highlighted, view the
+current message. If current message is already being viewed, view the
+first unmarked message after the current message.
+The corresponding action is \fBXmhViewNextMessage()\fP.
+.PP
+.TP 18
+.B View Previous
+View the last selected message. If no messages are highlighted, view the
+current message. If current message is already being viewed, view the
+first unmarked message before the current message.
+The corresponding action is \fBXmhViewPrevious()\fP.
+.PP
+.TP 18
+.B Delete
+Mark the selected messages for deletion. If no messages are highlighted,
+mark the current message for deletion and automatically display the
+next unmarked message.
+The corresponding action is \fBXmhMarkDelete()\fP.
+.PP
+.TP 18
+.B Move
+Mark the selected messages to be moved into the currently selected folder.
+(If the selected folder is the same as the viewed folder,
+this command will just beep.) If no messages are highlighted,
+mark the current message to be moved and display the next unmarked message.
+The corresponding action is \fBXmhMarkMove()\fP.
+.PP
+.TP 18
+.B Copy as Link
+Mark the selected messages to be copied into the selected folder. (If the
+selected folder is the same as the viewed folder, this command will just
+beep.) If no messages are highlighted, mark the current message to be
+copied. Note that messages are actually linked, not copied; editing
+a message copied by \fIxmh\fP will affect all copies of the message.
+The corresponding action is \fBXmhMarkCopy()\fP.
+.PP
+.TP 18
+.B Unmark
+Remove any of the above three marks from the selected messages, or the
+current message, if none are highlighted.
+The corresponding action is \fBXmhUnmark()\fP.
+.PP
+.TP 18
+.B View in New
+Create a new window containing only a view of the first selected message,
+or the current message, if none are highlighted.
+The corresponding action is \fBXmhViewInNewWindow()\fP.
+.PP
+.TP 18
+.B Reply
+Create a composition window in reply to the first selected message, or the
+current message, if none are highlighted.
+The corresponding action is \fBXmhReply()\fP.
+.PP
+.TP 18
+.B Forward
+Create a composition window whose body is initialized to contain an
+encapsulation of
+of the selected messages, or the current message if none are highlighted.
+The corresponding action is \fBXmhForward()\fP.
+.PP
+.TP 18
+.B Use as Composition
+Create a composition window whose body is initialized to be the contents
+of the first selected message, or the current message if none are selected.
+Any changes you make in the composition will be saved in a new
+message in the ``drafts'' folder, and will not change the original message.
+However, there is an exception to this rule.
+If the message to be used as composition was selected from the ``drafts''
+folder, (see BUGS), the changes will be reflected in the original message
+(see COMPOSITION WINDOWS). The action procedure corresponding to this
+command is \fBXmhUseAsComposition()\fR.
+.PP
+.TP 18
+.B Print
+Print the selected messages, or the current message if none are selected.
+\fIxmh\fR normally prints by invoking
+the \fIenscript\fR(1) command, but this can be customized with the \fIxmh\fP
+application-specific resource \fBPrintCommand\fR.
+The corresponding action is \fBXmhPrint()\fR.
+
+.SS SEQUENCE COMMANDS
+The \fISequence\fR command menu contains commands pertaining to
+message sequences (See MESSAGE-SEQUENCES),
+and a list of the message-sequences defined for the currently viewed folder.
+The selected message-sequence is indicated by a check mark in its entry
+in the margin of the menu. To change the selected message-sequence,
+select a new message-sequence from the sequence menu.
+
+.TP 18
+.B Pick Messages
+Define a new message-sequence.
+The corresponding action is \fBXmhPickMessages()\fP.
+.PP
+The following menu entries will be sensitive only if the current folder
+has any message-sequences other than the ``all'' message-sequence.
+.TP 18
+.B Open Sequence
+Change the viewed sequence to be the same as the selected sequence.
+The corresponding action is \fBXmhOpenSequence()\fP.
+.PP
+.TP 18
+.B Add to Sequence
+Add the selected messages to the selected sequence.
+The corresponding action is \fBXmhAddToSequence()\fP.
+.PP
+.TP 18
+.B Remove from Sequence
+Remove the selected messages from the selected sequence.
+The corresponding action is \fBXmhRemoveFromSequence()\fP.
+.PP
+.TP 18
+.B Delete Sequence
+Remove the selected sequence entirely. The messages themselves are
+not affected; they simply are no longer grouped together to define a
+message-sequence. The corresponding action is \fBXmhDeleteSequence()\fP.
+
+.SS VIEW COMMANDS
+Commands in the \fIView\fP menu and in the buttonboxes of view windows
+(which result from the \fIMessage\fP menu command \fBView In New\fP)
+correspond in functionality to commands of the same
+name in the \fIMessage\fP menu, but they operate on the viewed message
+rather than the selected messages or current message.
+
+.TP 18
+.B Close Window
+When the viewed message is in a separate view window, this command will
+close the view, after confirming the status of any unsaved edits.
+The corresponding action procedure is \fBXmhCloseView()\fR.
+.TP 18
+.B Reply
+Create a composition window in reply to the viewed message.
+The related action procedure is \fBXmhViewReply()\fR.
+.TP 18
+.B Forward
+Create a composition window whose body is initialized contain an
+encapsulation of
+the viewed message. The corresponding action is \fBXmhViewForward()\fR.
+.TP 18
+.B Use As Composition
+Create a composition window whose body is initialized to be the contents of
+the viewed message. Any changes made in the composition window will be
+saved in a new message in the ``drafts'' folder, and will not change the
+original message. An exception: if the viewed message was selected from
+the ``drafts'' folder, (see BUGS) the original message is edited.
+The action
+procedure corresponding to this command is \fBXmhViewUseAsComposition()\fR.
+.TP 18
+.B Edit Message
+This command enables the direct editing of the viewed message.
+The action procedure is \fBXmhEditView()\fR.
+.TP 18
+.B Save Message
+This command is insensitive until the message has been edited; when
+activated, edits will be saved to the original message in the view.
+The corresponding action is \fBXmhSaveView()\fR.
+.TP 18
+.B Print
+Print the viewed message. \fIxmh\fR prints by invoking
+the \fIenscript\fR(1) command, but this can be customized with the
+application-specific resource \fBPrintCommand\fR.
+The corresponding action procedure is \fBXmhPrintView()\fR.
+.TP 18
+.B Delete
+Marks the viewed message for deletion.
+The corresponding action procedure is \fBXmhViewMarkDelete()\fR.
+
+.SH OPTIONS
+The \fIOptions\fR menu contains one entry.
+
+.TP
+.B Read in Reverse
+When selected, a check mark appears in the margin of this menu entry.
+Read in Reverse will switch the meaning of the next and previous
+messages, and will increment to the current message marker
+in the opposite direction. This is useful
+if you want to read your messages in the order of most recent first.
+The option acts as a toggle; select it from the menu a second time to
+undo the effect. The check mark appears when the option is selected.
+
+.SH COMPOSITION WINDOWS
+Composition windows are created by selecting \fBCompose Message\fP
+from the \fIMessage\fP command menu, or by selecting
+\fBReply\fP or \fBForward\fP or \fBUse as Composition\fP from the
+\fIMessage\fP or \fIView\fP command menu.
+These are used to compose mail messages.
+Aside from the normal text editing functions, there are six command
+buttons associated with composition windows:
+.TP 18
+.B Close Window
+Close this composition window. If changes have been made since the
+most recent Save or Send, you will be asked to confirm losing them.
+The corresponding action is \fBXmhCloseView()\fP.
+.PP
+.TP 18
+.B Send
+Send this composition. The corresponding action is \fBXmhSend()\fP.
+.PP
+.TP 18
+.B New Headers
+Replace the current composition with an empty message. If changes have
+been made since the most recent Send or Save, you will be
+asked to confirm losing them.
+The corresponding action is \fBXmhResetCompose()\fP.
+.PP
+.TP 18
+.B Compose Message
+Bring up another new composition window. The corresponding action
+is \fBXmhComposeMessage()\fP.
+.PP
+.TP 18
+.B Save Message
+Save this composition in your drafts folder. Then you can safely close the
+composition. At some future date, you can continue working on the
+composition by opening the drafts folder, selecting the message, and
+using the ``Use as Composition'' command.
+The corresponding action is \fBXmhSave()\fP.
+.PP
+.TP 18
+.B Insert
+Insert a related message into the composition. If the composition window
+was created with a ``Reply'' command, the related message is the message
+being replied to, otherwise no related message is defined and this button
+is insensitive. The message may be filtered before being inserted;
+see \fBReplyInsertFilter\fP under APPLICATION RESOURCES for more information.
+The corresponding action is \fBXmhInsert()\fP.
+
+.SH ACCELERATORS
+Accelerators are shortcuts. They allow you to invoke commands
+without using the menus, either from the keyboard or by using the pointer.
+.PP
+\fIxmh\fP defines pointer accelerators for common actions:
+To select and view a message with a single click, use pointer button
+2 on the message's entry in the table of contents. To select and open
+a folder or a sequence in a single action, make the folder or sequence
+selection with pointer button 2.
+
+To mark the highlighted messages,
+or current message if none have been highlighted,
+to be moved to a folder in a single action, use pointer button 3 to select
+the target folder and simultaneously mark the messages.
+Similarly, selecting a sequence with pointer button 3 will add
+the highlighted or current message(s) to that sequence.
+In both of these operations, the selected folder or sequence
+and the viewed folder or sequence are not changed.
+
+\fIxmh\fP defines the following keyboard accelerators over the surface of
+the main window, except in the view area while editing a message:
+.nf
+ Meta-I Incorporate New Mail
+ Meta-C Commit Changes
+ Meta-R Rescan Folder
+ Meta-P Pack Folder
+ Meta-S Sort Folder
+
+ Meta-space View Next Message
+ Meta-c Mark Copy
+ Meta-d Mark Deleted
+ Meta-f Forward the selected or current message
+ Meta-m Mark Move
+ Meta-n View Next Message
+ Meta-p View Previous Message
+ Meta-r Reply to the selected or current message
+ Meta-u Unmark
+
+ Ctrl-V Scroll the table of contents forward
+ Meta-V Scroll the table of contents backward
+ Ctrl-v Scroll the view forward
+ Meta-v Scroll the view backward
+.fi
+
+.SH TEXT EDITING COMMANDS
+All of the text editing commands are actually defined by the Text widget
+in the Athena Widget Set.
+The commands may be bound to different keys than the defaults
+described below through the X Toolkit Intrinsics key re-binding mechanisms.
+See the X Toolkit Intrinsics and the Athena Widget Set documentation for
+more details.
+
+Whenever you are asked to enter any text, you will be using a standard
+text editing interface. Various control and meta keystroke combinations
+are bound to a somewhat Emacs-like set of commands. In addition, the
+pointer buttons may be used to select a portion of text or to move the
+insertion point in the text. Pressing pointer button 1 causes the
+insertion point to move to the pointer. Double-clicking
+button 1 selects a word, triple-clicking selects a line, quadruple-clicking
+selects a paragraph, and clicking rapidly five times selects
+everything. Any selection may be extended in
+either direction by using pointer button 3.
+
+In the following, a \fIline\fR refers to one displayed row of characters
+in the window. A \fIparagraph\fR refers to the text between carriage
+returns. Text within a paragraph is broken into lines for display based on the
+current width of the window.
+When a message is sent, text is broken into lines based upon the values
+of the \fBSendBreakWidth\fP and \fBSendWidth\fP application-specific
+resources.
+
+The following keystroke combinations are defined:
+.sp
+.nf
+.ta 1.0i 3.0i 4.5i
+Ctrl-a Beginning Of Line Meta-b Backward Word
+Ctrl-b Backward Character Meta-f Forward Word
+Ctrl-d Delete Next Character Meta-i Insert File
+Ctrl-e End Of Line Meta-k Kill To End Of Paragraph
+Ctrl-f Forward Character Meta-q Form Paragraph
+Ctrl-g Multiply Reset Meta-v Previous Page
+Ctrl-h Delete Previous Character Meta-y Insert Current Selection
+Ctrl-j Newline And Indent Meta-z Scroll One Line Down
+Ctrl-k Kill To End Of Line Meta-d Delete Next Word
+Ctrl-l Redraw Display Meta-D Kill Word
+Ctrl-m Newline Meta-h Delete Previous Word
+Ctrl-n Next Line Meta-H Backward Kill Word
+Ctrl-o Newline And Backup Meta-< Beginning Of File
+Ctrl-p Previous Line Meta-> End Of File
+Ctrl-r Search/Replace Backward Meta-] Forward Paragraph
+Ctrl-s Search/Replace Forward Meta-[ Backward Paragraph
+Ctrl-t Transpose Characters
+Ctrl-u Multiply by 4 Meta-Delete Delete Previous Word
+Ctrl-v Next Page Meta-Shift Delete Kill Previous Word
+Ctrl-w Kill Selection Meta-Backspace Delete Previous Word
+Ctrl-y Unkill Meta-Shift Backspace Kill Previous Word
+Ctrl-z Scroll One Line Up
+.sp
+In addition, the pointer may be used to copy and paste text:
+.ta .5i 2.0i
+ Button 1 Down Start Selection
+ Button 1 Motion Adjust Selection
+ Button 1 Up End Selection (copy)
+
+ Button 2 Down Insert Current Selection (paste)
+
+ Button 3 Down Extend Current Selection
+ Button 3 Motion Adjust Selection
+ Button 3 Up End Selection (copy)
+.fi
+.sp
+.SH CONFIRMATION DIALOG BOXES
+Whenever you press a button that may cause you to lose some work or is
+otherwise dangerous, a popup dialog box will appear asking you to confirm the
+action. This window will contain an ``Abort'' or ``No'' button and a
+``Confirm'' or ``Yes''
+button. Pressing the ``No'' button cancels the operation, and pressing
+the ``Yes'' will proceed with the operation.
+
+When \fIxmh\fR is run under a Release 6 session manager it will prompt
+the user for confirmation during a checkpoint operation. The dialog
+box asks whether any current changes should be committed (saved)
+during the checkpoint. Responding ``Yes'' will have the same effect
+as pressing the ``Commit Changes'' or ``Save Message'' buttons in the
+respective folder and view windows. Responding ``No'' will cause the
+checkpoint to continue successfully to completion without actually
+saving any pending changes. If the session manager disallows user
+interaction during the checkpoint a ``Yes'' response is assumed; i.e.
+all changes will be committed during the checkpoint.
+
+Some dialog boxes contain messages from \fIMH\fR. Occasionally when the
+message is more than one line long,
+not all of the text will be visible. Clicking on the message field will
+cause the dialog box to resize so that you can read the entire message.
+
+.SH MESSAGE-SEQUENCES
+An \fIMH\fP message sequence is just a set of messages associated with some name.
+They are local to a particular folder; two different folders can have
+sequences with the same name. The sequence named ``all'' is predefined in
+every folder; it consists of the set of all messages in that folder. As
+many as nine sequences may be defined for each folder, including
+the predefined ``all'' sequence. (The
+sequence ``cur'' is also usually defined for every folder; it consists of
+only the current message. \fIxmh\fR hides ``cur'' from the user, instead
+placing a ``+'' by the current message. Also, \fIxmh\fR does not support
+\fIMH\fP's``unseen'' sequence, so that one is also hidden from the user.)
+
+The message sequences for a folder (including one for ``all'') are
+displayed in the ``Sequence'' menu, below the sequence commands.
+The table of contents (also known as the ``toc'') is at any one time
+displaying one message sequence. This is called the ``viewed sequence'',
+and its name will be displayed in the toc title bar after the
+folder name. Also, at any time one of the sequences in the menu will
+have a check mark next to it. This is called the ``selected sequence''.
+Note that the viewed sequence and the selected sequence are not necessarily
+the same. (This all pretty much corresponds to the way folders work.)
+
+The \fBOpen Sequence\fR, \fBAdd to Sequence\fR, \fBRemove from Sequence\fR,
+and \fBDelete Sequence\fR commands are active only if the viewed folder
+contains message-sequences other than ``all'' sequence.
+.PP
+Note that none of the above actually affect whether a message is in the
+folder. Remember that a sequence is a set of messages within the folder;
+the above operations just affect what messages are in that set.
+
+To create a new sequence, select the ``Pick'' menu entry. A new window will
+appear, with lots of places to enter text. Basically, you can describe the
+sequence's initial set of messages based on characteristics of the
+message. Thus, you can define a sequence to be all the messages that were
+from a particular person, or with a particular subject, and so on. You
+can also connect things up with boolean operators, so you can select all
+things from ``weissman'' with a subject containing ``xmh''.
+
+The layout should be fairly obvious. The simplest cases are the
+easiest: just point to the proper field and type. If you enter in more
+than one field, it will only select messages which match all non-empty
+fields.
+
+The more complicated cases arise when you want things that match one field
+or another one, but not necessarily both. That's what all the ``or''
+buttons are for. If you want all things with subjects that include ``xmh'' or
+``xterm'', just press the ``or'' button next to the ``Subject:'' field.
+Another box will appear where you can enter another subject.
+
+If you want all things either from ``weissman'' or with subject ``xmh'', but
+not necessarily both, select the ``\-Or\-'' button. This will essentially
+double the size of the form. You can then enter ``weissman'' in a from: box
+on the top half, and ``xmh'' in a subject: box on the lower part.
+
+If you select the ``Skip'' button, then only those messages that
+\fIdon't\fR match the fields on that row are included.
+
+Finally, in the bottom part of the window will appear several more boxes.
+One is the name of the sequence you're defining. (It defaults to the name
+of the selected sequence when ``Pick'' was pressed, or to ``temp'' if
+``all'' was the selected sequence.) Another box defines which sequence to
+look through for potential members of this sequence; it defaults to the
+viewed sequence when ``Pick'' was pressed.
+
+Two more boxes define a date range; only messages within that date range
+will be considered. These dates must be entered in RFC 822-style format: each
+date is of the form ``dd mmm yy hh:mm:ss zzz'', where dd is a one or two
+digit day of the month, mmm is the three-letter abbreviation for a month,
+and yy is a year. The remaining fields are optional: hh, mm, and ss
+specify a time of day, and zzz selects a time zone. Note that if the time
+is left out, it defaults to midnight; thus if you select a range of ``7
+nov 86'' \- ``8 nov 86'', you will only get messages from the 7th, as all
+messages on the 8th will have arrived after midnight.
+
+``Date field'' specifies which field in the header to look at for
+this date range; it defaults to ``Date''. If the sequence
+you're defining already exists, you can optionally merge the old set with
+the new; that's what the ``Yes'' and ``No'' buttons are all about.
+Finally, you can ``OK'' the whole thing, or ``Cancel'' it.
+
+In general, most people will rarely use these features. However, it's
+nice to occasionally use ``Pick'' to find some messages, look through
+them, and then hit ``Delete Sequence'' to put things back in their original
+state.
+
+.SH WIDGET HIERARCHY
+In order to specify resources, it is useful to know the hierarchy of
+widgets which compose \fIxmh\fR. In the notation below, indentation
+indicates hierarchical structure. The widget class name is given first,
+followed by the widget instance name.
+The application class name is Xmh.
+.PP
+The hierarchy of the main toc and view window is identical for additional
+toc and view windows, except that a TopLevelShell widget is inserted
+in the hierarchy between the application shell and the Paned widget.
+.sp
+.nf
+.ta .5i 1.0i 1.5i 2.0i 2.5i 3.0i 3.5i 4.0i 4.5i 5.0i 5.5i 6.0i 6.5i 7.0i
+Xmh xmh
+ Paned xmh
+ SimpleMenu folderMenu
+ SmeBSB open
+ SmeBSB openInNew
+ SmeBSB create
+ SmeBSB delete
+ SmeLine line
+ SmeBSB close
+ SimpleMenu tocMenu
+ SmeBSB inc
+ SmeBSB commit
+ SmeBSB pack
+ SmeBSB sort
+ SmeBSB rescan
+ SimpleMenu messageMenu
+ SmeBSB compose
+ SmeBSB next
+ SmeBSB prev
+ SmeBSB delete
+ SmeBSB move
+ SmeBSB copy
+ SmeBSB unmark
+ SmeBSB viewNew
+ SmeBSB reply
+ SmeBSB forward
+ SmeBSB useAsComp
+ SmeBSB print
+ SimpleMenu sequenceMenu
+ SmeBSB pick
+ SmeBSB openSeq
+ SmeBSB addToSeq
+ SmeBSB removeFromSeq
+ SmeBSB deleteSeq
+ SmeLine line
+ SmeBSB all
+ SimpleMenu viewMenu
+ SmeBSB reply
+ SmeBSB forward
+ SmeBSB useAsComp
+ SmeBSB edit
+ SmeBSB save
+ SmeBSB print
+ SimpleMenu optionMenu
+ SmeBSB reverse
+ Viewport.Core menuBox.clip
+ Box menuBox
+ MenuButton folderButton
+ MenuButton tocButton
+ MenuButton messageButton
+ MenuButton sequenceButton
+ MenuButton viewButton
+ MenuButton optionButton
+ Grip grip
+ Label folderTitlebar
+ Grip grip
+ Viewport.Core folders.clip
+ Box folders
+ MenuButton inbox
+ MenuButton drafts
+ SimpleMenu menu
+ SmeBSB <folder_name>
+ .
+ .
+ .
+
+ Grip grip
+ Label tocTitlebar
+ Grip grip
+ Text toc
+ Scrollbar vScrollbar
+ Grip grip
+ Label viewTitlebar
+ Grip grip
+ Text view
+ Scrollbar vScrollbar
+ Scrollbar hScrollbar
+.sp
+\fIThe hierarchy of the Create Folder popup dialog box:\fR
+.sp
+ TransientShell prompt
+ Dialog dialog
+ Label label
+ Text value
+ Command okay
+ Command cancel
+.sp
+\fIThe hierarchy of the Notice dialog box, which reports messages from MH:\fR
+.sp
+ TransientShell notice
+ Dialog dialog
+ Label label
+ Text value
+ Command confirm
+.sp
+\fIThe hierarchy of the Confirmation dialog box:\fR
+.sp
+ TransientShell confirm
+ Dialog dialog
+ Label label
+ Command yes
+ Command no
+.sp
+\fIThe hierarchy of the dialog box which reports errors:\fR
+.sp
+ TransientShell error
+ Dialog dialog
+ Label label
+ Command OK
+.sp
+\fIThe hierarchy of the composition window:\fR
+.sp
+ TopLevelShell xmh
+ Paned xmh
+ Label composeTitlebar
+ Text comp
+ Viewport.Core compButtons.clip
+ Box compButtons
+ Command close
+ Command send
+ Command reset
+ Command compose
+ Command save
+ Command insert
+.sp
+\fIThe hierarchy of the view window:\fR
+.sp
+ TopLevelShell xmh
+ Paned xmh
+ Label viewTitlebar
+ Text view
+ Viewport.Core viewButtons.clip
+ Box viewButtons
+ Command close
+ Command reply
+ Command forward
+ Command useAsComp
+ Command edit
+ Command save
+ Command print
+ Command delete
+.sp
+\fIThe hierarchy of the pick window:\fR
+\fI(Unnamed widgets have no name.)\fR
+.sp
+ TopLevelShell xmh
+ Paned xmh
+ Label pickTitlebar
+ Viewport.Core pick.clip
+ Form form
+ Form groupform
+\fIThe first 6 rows of the pick window have identical structure:\fR
+ Form rowform
+ Toggle
+ Toggle
+ Label
+ Text
+ Command
+
+ Form rowform
+ Toggle
+ Toggle
+ Text
+ Text
+ Command
+ Form rowform
+ Command
+ Viewport.core pick.clip
+ Form form
+ From groupform
+ Form rowform
+ Label
+ Text
+ Label
+ Text
+ Form rowform
+ Label
+ Text
+ Label
+ Text
+ Label
+ Text
+ Form rowform
+ Label
+ Toggle
+ Toggle
+ Form rowform
+ Command
+ Command
+
+.fi
+.SH APPLICATION-SPECIFIC RESOURCES
+.PP
+The application class name is \fBXmh\fP.
+Application-specific resources are listed below by name.
+Application-specific resource class names always begin with an upper case
+character, but unless noted, are otherwise identical to the instance names
+given below.
+.PP
+Any of these options may also be specified on the command line by
+using the X Toolkit Intrinsics resource specification mechanism.
+Thus, to run \fIxmh\fR showing all message headers,
+.br
+% xmh \-xrm '*HideBoringHeaders:off'
+.PP
+If \fBTocGeometry\fR, \fBViewGeometry\fR, \fBCompGeometry\fR, or
+\fBPickGeometry\fR are not
+specified, then the value of \fBGeometry\fR is used instead. If the resulting
+height is not specified (e.g., "", "=500", "+0-0"), then the default
+height of windows is calculated from fonts and line counts. If
+the width is not specified (e.g., "", "=x300", "-0+0"), then half of the
+display width is used. If unspecified, the height of a pick window
+defaults to half the height of the display.
+.PP
+The following resources are defined:
+.TP 8
+.B banner
+A short string that is the default label of the folder, Table of Contents,
+and view. The default shows the program name, vendor, and release.
+.PP
+.TP 8
+.B blockEventsOnBusy
+Whether to disallow user input and show a busy cursor while \fIxmh\fP is
+busy processing a command. If false, the user can `mouse ahead' and
+type ahead; if true, user input is discarded when processing lengthy
+\fImh\fP commands. The default is true.
+.PP
+.TP 8
+.B busyCursor
+The name of the symbol used to represent the position of the pointer,
+displayed if \fBblockEventsOnBusy\fR is true, when \fIxmh\fR is
+processing a time-consuming command.
+The default is "watch".
+.PP
+.TP 8
+.B busyPointerColor
+The foreground color of the busy cursor. Default is XtDefaultForeground.
+.PP
+.TP 8
+.B checkFrequency
+How often to check for new mail, make checkpoints, and rescan the Table
+of Contents, in minutes. If \fBcheckNewMail\fR is true, \fIxmh\fR checks
+to see if you have new mail each interval. If \fBmakeCheckpoints\fR is
+true, checkpoints are made every fifth interval. Also every fifth
+interval, the Table of Contents is checked for inconsistencies with the
+file system, and rescanned if out of date. To prevent all of these checks
+from occurring, set \fBCheckFrequency\fR to 0. The default is 1.
+This resource is retained for backward compatibility with user resource
+files; see also \fBcheckpointInterval\fP, \fBmailInterval\fP,
+and \fBrescanInterval\fP.
+.PP
+.TP 8
+.B checkNewMail
+If true, \fIxmh\fP will check at regular intervals to see if new mail
+has arrived for any of the top level folders and any opened subfolders.
+A visual indication will be given if new mail is waiting to be incorporated
+into a top level folder.
+Default is true.
+The interval can be adjusted with \fBmailInterval\fR.
+.PP
+.TP 8
+.B "checkpointInterval \fP(class \fBInterval\fP)"
+Specifies in minutes how often to make checkpoints of volatile state,
+if \fBmakeCheckpoints\fP is true.
+The default is 5 times the value of \fBcheckFrequency\fP.
+.PP
+.TP 8
+.B checkpointNameFormat
+Specifies how checkpointed files are to be named. The value of this
+resource will be used to compose a file name by inserting the message
+number as a string in place of the required single occurance of `%d'. If
+the value of the resource is the empty string, or if no `%d' occurs in
+the string, or if "%d" is the value of the resource, the default will be
+used instead. The default is "%d.CKP". Checkpointing is done in the
+folder of origin unless an absolute pathname is given. \fIxmh\fP does
+not assist the user in recovering checkpoints, nor does it provide for
+removal of the checkpoint files.
+.PP
+.TP 8
+.B commandButtonCount
+The number of command buttons to create in a button box in between the toc
+and the view areas of the main window. \fIxmh\fP will create these buttons
+with the names \fIbutton1, button2\fP and so on, in a box with the name
+\fIcommandBox\fR. The default is 0.
+\fIxmh\fP users can specify labels and actions for the buttons in a private
+resource file; see the section ACTIONS AND INTERFACE CUSTOMIZATION.
+.PP
+.TP 8
+.B compGeometry
+Initial geometry for windows containing compositions.
+.PP
+.TP 8
+.B cursor
+The name of the symbol used to represent the pointer. Default is ``left_ptr''.
+.PP
+.TP 8
+.B debug
+Whether or not to print information to stderr as \fIxmh\fP runs.
+Default is false.
+.PP
+.TP 8
+.B draftsFolder
+The folder used for message drafts. Default is ``drafts''.
+.PP
+.TP 8
+.B geometry
+Default geometry to use. Default is none.
+.PP
+.TP 8
+.B hideBoringHeaders
+If ``on'', then \fIxmh\fR will attempt to skip uninteresting header lines
+within messages by scrolling them off the top of the view.
+Default is ``on''.
+.PP
+.TP 8
+.B initialFolder
+Which folder to display on startup. May also be set with the command-line
+option \fB\-initial\fR. Default is ``inbox''.
+.PP
+.TP 8
+.B initialIncFile
+The absolute path name of your incoming mail drop file.
+In some installations, for example those using the Post Office Protocol,
+no file is appropriate.
+In this case, \fBinitialIncFile\fR should not be specified,
+or may be specified as the empty string,
+and \fIinc\fR will be invoked without a \-file argument.
+By default, this resource has no value.
+This resource is ignored if \fIxmh\fP finds an \fI.xmhcheck\fP file; see
+the section on multiple mail drops.
+.PP
+.TP 8
+.B "mailInterval (\fPclass\fB Interval)"
+Specifies the interval in minutes at which the mail should be checked, if
+\fBmailWaitingFlag\fP or \fBcheckNewMail\fP is true.
+The default is the value of \fBcheckFrequency\fR.
+.PP
+.TP 8
+.B mailPath
+The full path prefix for locating your mail folders. May also be set
+with the command line option, \fB\-path\fR. The default is the
+Path component in the \fIMH\fP profile, or ``$HOME/Mail'' if none.
+.PP
+.TP 8
+.B mailWaitingFlag
+If true, \fIxmh\fP will attempt to set an indication in its icon when
+new mail is waiting to be retrieved. If \fBmailWaitingFlag\fP is true, then
+\fBcheckNewMail\fP is assumed to be true as well. The \fB\-flag\fP command
+line option is a quick way to turn on this resource.
+.PP
+.TP 8
+.B makeCheckpoints
+If true, \fIxmh\fP will attempt to save checkpoints of volatile edits.
+The default is false. The frequency of checkpointing is controlled by the
+resource \fBcheckpointInterval\fR. For the location of checkpointing, see
+\fBcheckpointNameFormat\fP.
+.PP
+.TP 8
+.B mhPath
+What directory in which to find the \fIMH\fR commands. If a command isn't
+found in the user's path, then the path specified here is used.
+Default is ``/usr/local/mh6''.
+.PP
+.TP 8
+.B "newMailBitmap \fP(class \fBNewMailBitmap\fP)"
+The bitmap to show in the folder button when a folder has new mail.
+The default is ``black6''.
+.PP
+.TP 8
+.B "newMailIconBitmap \fP(class \fBNewMailBitmap\fP)"
+The bitmap suggested to the window manager for the icon when any folder
+has new mail. The default is ``flagup''.
+.PP
+.TP 8
+.B "noMailBitmap (\fPclass\fB NoMailBitmap)"
+The bitmap to show in the folder button when a folder has no new mail.
+The default is ``box6''.
+.PP
+.TP 8
+.B "noMailIconBitmap (\fPclass\fB NoMailBitmap)"
+The bitmap suggested to the window manager for the icon when no folders
+have new mail. The default is ``flagdown''.
+.PP
+.TP 8
+.B pickGeometry
+Initial geometry for pick windows.
+.PP
+.TP 8
+.B pointerColor
+The foreground color of the pointer. Default is XtDefaultForeground.
+.PP
+.TP 8
+.B prefixWmAndIconName
+Whether to prefix the window and icon name with "xmh: ". Default is true.
+.PP
+.TP 8
+.B printCommand
+An \fIsh\fP command to execute to print a message. Note that stdout and
+stderr must be specifically redirected. If a message or range of messages is
+selected for printing, the full file paths of each message file are
+appended to the specified print command. The default is ``enscript >/dev/null
+2>/dev/null''.
+.PP
+.TP 8
+.B replyInsertFilter
+An \fIsh\fP command to be executed when the \fIInsert\fP button is activated
+in a composition window. The full path and filename of the source
+message is appended to the command before being passed to \fIsh\fP(1).
+The default filter is \fIcat\fP; i.e. it inserts the entire message
+into the composition. Interesting filters are:
+\fIsed 's/^/> /'\fP or
+\fIawk -e '{print " " $0}'\fP or
+\fI<mh directory>/lib/mhl \-form mhl.body\fP.
+.PP
+.TP 8
+.B "rescanInterval \fP(class \fBInterval\fP)"
+How often to check the Table of Contents of currently viewed folders
+and of folders with messages currently being viewed, and to update the Table
+of Contents if \fIxmh\fP sees inconsistencies with the file system in these
+folders.
+The default is 5 times the value of \fBcheckFrequency\fP.
+.PP
+.TP 8
+.B reverseReadOrder
+When true, the next message will be the message prior to the current message
+in the table of contents, and the previous message will be the message
+after the current message in the table of contents. The default is false.
+.PP
+.TP 8
+.B sendBreakWidth
+When a message is sent from \fIxmh\fP, lines longer than this value will be
+split into multiple lines, each of which is no longer than \fBSendWidth\fP.
+This value may be overridden for a single message by inserting an additional
+line in the message header of the form \fISendBreakWidth: value\fP. This
+line will be removed from the header before the message is sent.
+The default is 2000 (to allow for sending mail containing source patches).
+.PP
+.TP 8
+.B sendWidth
+When a message is sent from \fIxmh\fP, lines longer than \fBSendBreakWidth\fP
+characters will be split into multiple lines, each of which is no longer than
+this value.
+This value may be overridden for a single message by inserting an additional
+line in the message header of the form \fISendWidth: value\fP. This
+line will be removed from the header before the message is sent.
+The default is 72.
+.PP
+.TP 8
+.B showOnInc
+Whether to automatically show the current message after incorporating new
+mail. Default is true.
+.PP
+.TP 8
+.B skipCopied
+Whether to skip over messages marked for copying when using ``View Next
+Message'' and ``View Previous Message''. Default is true.
+.PP
+.TP 8
+.B skipDeleted
+Whether to skip over messages marked for deletion when using ``View Next
+Message'' and ``View Previous Message''. Default is true.
+.PP
+.TP 8
+.B skipMoved
+Whether to skip over messages marked for moving to other folders when
+using ``View Next Message'' and ``View Previous Message''. Default is true.
+.PP
+.TP 8
+.B stickyMenu
+If true, when popup command menus are used, the most recently selected
+entry will be under the cursor when the menu pops up. Default is false.
+See the file \fIclients/xmh/Xmh.sample\fR for an example of how to
+specify resources for popup command menus.
+.PP
+.TP 8
+.B tempDir
+Directory for \fIxmh\fR to store temporary files. For privacy, a user
+might want to change this to a private directory. Default is ``/tmp''.
+.PP
+.TP 8
+.B tocGeometry
+Initial geometry for main \fIxmh\fR toc and view windows.
+.PP
+.TP 8
+.B tocPercentage
+The percentage of the main window that is used to display the Table of
+Contents. Default is 33.
+.PP
+.TP 8
+.B tocWidth
+How many characters to generate for each message in a folder's table of
+contents. Default is 100. Use less if the geometry of the main \fIxmh\fP
+window results in the listing being clipped at the right hand boundary, or
+if you plan to use \fImhl\fR a lot,
+because it will be faster, and the extra characters may not be useful.
+.PP
+.TP 8
+.B viewGeometry
+Initial geometry for windows showing a view of a message.
+
+.SH MULTIPLE MAIL DROPS
+.PP
+Users may need to incorporate mail from multiple spool files or mail drops.
+If incoming mail is forwarded to the \fIMH slocal\fP program, it can
+be sorted as specified by the user into multiple incoming mail drops.
+Refer to the \fIMH\fP man page for \fIslocal\fP to learn how to specify
+fowarding and the automatic sorting of incoming mail in a \fI.maildelivery\fP
+file.
+.PP
+To inform \fIxmh\fP about the various mail drops, create a file in your
+home directory called \fI.xmhcheck\fP. In this file, a mapping between
+existing folder names and mail drops is created by giving a folder name
+followed by the absolute pathname of the mail drop site, with some white
+space separating them, one mapping per line. \fIxmh\fP will read this file
+whether or not resources are set for notification of new mail arrival, and
+will allow incorporation of new mail into any folder with a mail drop.
+\fIxmh\fP will invoke \fIinc\fP with the \fI\-file\fP argument,
+and if \fIxmh\fP has been requested to check for new mail,
+it will check directly, instead of using \fImsgchk\fP.
+.PP
+An example of \fI.xmhcheck\fP file format, for the folders ``inbox'' and
+``xpert'':
+.nf
+inbox /usr/spool/mail/converse
+xpert /users/converse/maildrops/xpert
+.fi
+.sp
+.SH ACTIONS AND INTERFACE CUSTOMIZATION
+.PP
+Because \fIxmh\fR provides action procedures which correspond to command
+functionality and installs accelerators, users can customize accelerators
+and new button functionality in a private resource file.
+For examples of specifying customized resources, see the file
+\fImit/clients/xmh/Xmh.sample\fR. To understand the syntax, see the
+Appendix of the \fIX Toolkit Intrinsics\fP specification
+on \fITranslation Table Syntax\fP, and any general explanation of
+using and specifying \fIX\fP resources.
+Unpredictable results can occur if
+actions are bound to events or widgets for which they were not designed.
+.PP
+Here's an example of how to bind actions to your own \fIxmh\fP buttons,
+and how to redefine the default accelerators so that the Meta key is
+not required, in case you don't have access to the sample file mentioned
+above.
+.sp
+.nf
+! To create buttons in the middle of the main window and give them semantics:
+
+Xmh*CommandButtonCount: 5
+
+Xmh*commandBox.button1.label: Inc
+Xmh*commandBox.button1.translations: #override\\
+ <Btn1Down>,<Btn1Up>: XmhIncorporateNewMail() unset()
+
+Xmh*commandBox.button2.label: Compose
+Xmh*commandBox.button2.translations: #override\\
+ <Btn1Down>,<Btn1Up>: XmhComposeMessage() unset()
+
+Xmh*commandBox.button3.label: Next
+Xmh*commandBox.button3.translations: #override\\
+ <Btn1Down>,<Btn1Up>: XmhViewNextMessage() unset()
+
+Xmh*commandBox.button4.label: Delete
+Xmh*commandBox.button4.translations: #override\\
+ <Btn1Down>,<Btn1Up>: XmhMarkDelete() unset()
+
+Xmh*commandBox.button5.label: Commit
+Xmh*commandBox.button5.translations: #override\\
+ <Btn1Down>,<Btn1Up>: XmhCommitChanges() unset()
+
+! To redefine the accelerator bindings to exclude modifier keys,
+! and add your own keyboard accelerator for Compose Message:
+
+Xmh*tocMenu.accelerators: #override\\n\\
+ !:<Key>I: XmhIncorporateNewMail()\\n\\
+ !:<Key>C: XmhCommitChanges()\\n\\
+ !:<Key>R: XmhForceRescan()\\n\\
+ !:<Key>P: XmhPackFolder()\\n\\
+ !:<Key>S: XmhSortFolder()\\n
+Xmh*messageMenu.accelerators: #override\\n\\
+ !:<Key>E: XmhComposeMessage()\\n\\
+ !<Key>space: XmhViewNextMessage()\\n\\
+ !:<Key>c: XmhMarkCopy()\\n\\
+ !:<Key>d: XmhMarkDelete()\\n\\
+ !:<Key>f: XmhForward()\\n\\
+ !:<Key>m: XmhMarkMove()\\n\\
+ !:<Key>n: XmhViewNextMessage()\\n\\
+ !:<Key>p: XmhViewPreviousMessage()\\n\\
+ !:<Key>r: XmhReply()\\n\\
+ !:<Key>u: XmhUnmark()\\n
+.fi
+.PP
+\fIxmh\fR provides action procedures
+which correspond to entries in the command menus; these are given in the
+sections describing menu commmands, not here.
+In addition to the actions corresponding to commands in the menus,
+these action routines are defined:
+.TP 10
+.B XmhPushFolder(\fR[\fIfoldername, ...\fR]\fB)\fR
+This action pushes each of its argument(s) onto a stack of foldernames.
+If no arguments are given, the selected folder is pushed onto the stack.
+.TP 10
+.B XmhPopFolder()
+This action pops one foldername from the stack and sets the selected folder.
+.TP 10
+.B XmhPopupFolderMenu()
+This action should always 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, 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 toggle
+button. When subfolders exist, the menu will popup, using the menu button
+action PopupMenu().
+.TP 10
+.B XmhSetCurrentFolder()
+This action allows menu buttons to emulate toggle buttons in the function
+of selecting a folder. This action is for menu button widgets only,
+and sets the selected folder.
+.TP 10
+.B XmhLeaveFolderButton()
+This action ensures that the menu button behaves properly when the user
+moves the pointer out of the menu button window.
+.TP 10
+.B XmhPushSequence(\fR[\fIsequencename, ...\fR]\fB)\fR
+This action pushes each of its arguments onto the stack of sequence names.
+If no arguments are given, the selected sequence is pushed onto the stack.
+.TP 10
+.B XmhPopSequence()
+This action pops one sequence name from the stack of sequence names,
+which then becomes the selected sequence.
+.TP 10
+.B XmhPromptOkayAction()
+This action is equivalent to pressing the okay button in the Create Folder popup.
+.TP 10
+.B XmhReloadSeqLists()
+This action rescans the contents of the public \fIMH\fP sequences for the
+currently opened folder and updates the sequence menu if necessary.
+.TP 10
+.B XmhShellCommand(\fI parameter \fR[\fI, parameter\fR]\fB)\fR
+At least one parameter must be specified. The parameters will be concatenated
+with a space character separator, into a single string, and the list of
+selected messsages, or if no messages are selected, the current message,
+will be appended to the string of parameters. The string will be executed
+as a shell command. The messages are always given as absolute pathnames.
+It is an error to cause this action to execute when there are no selected
+messages and no current message.
+.TP 10
+.B XmhCheckForNewMail()
+This action will check all mail drops known to xmh. If no mail drops have
+been specified by the user either through the \fI.xmhcheck\fR file or by
+the \fBinitialIncFile\fP resource, the \fIMH\fP command \fImsgchk\fP is
+used to check for new mail, otherwise, \fIxmh\fP checks directly.
+.TP 10
+.B XmhWMProtocols(\fP[\fBwm_delete_window\fP] [\fBwm_save_yourself\fP])
+This action is responsible for participation in window manager communication
+protocols. It responds to delete window and save yourself messages.
+The user can cause \fIxmh\fP to respond to one or both of these protocols,
+exactly as if the window manager had made the request, by invoking the
+action with the appropriate parameters. The action is insensitive to the
+case of the string parameters. If the event received is a ClientMessage
+event and parameters are present, at least one of the parameters must
+correspond to the protocol requested by the event for the request to be
+honored by \fIxmh\fP.
+
+.SH CUSTOMIZATION USING \fIMH\fR
+The initial text displayed in a composition window is generated by
+executing the corresponding \fIMH\fP command; i.e. \fIcomp\fP, \fIrepl\fP,
+or \fIforw\fP, and therefore message components may be customized as
+specified for those commands. \fIcomp\fP is executed only once per
+invocation of \fIxmh\fP and the message template is re-used for every
+successive new composition.
+.PP
+\fIxmh\fP uses \fIMH\fP commands, including \fIinc\fP, \fImsgchk\fP,
+\fIcomp\fP, \fIsend\fP, \fIrepl\fP, \fIforw\fP,
+\fIrefile\fP, \fIrmm\fP, \fIpick\fP, \fIpack\fP, \fIsort\fP, and \fIscan\fP.
+Some flags for these commands can be specified
+in the \fIMH\fP profile; \fIxmh\fP may override them. The application
+resource \fBdebug\fP can be set to true to see how \fIxmh\fP
+uses \fIMH\fP commands.
+
+.SH ENVIRONMENT
+.br
+HOME - users's home directory
+.br
+MH - to get the location of the \fIMH\fP profile file
+.SH FILES
+~/.mh_profile - \fIMH\fR profile, used if the MH environment variable is not set
+.br
+~/Mail - directory of folders, used if the \fIMH\fR profile cannot be found
+.br
+~/.xmhcheck - optional, for multiple mail drops in cooperation with \fIslocal\fP.
+.br
+/usr/local/mh6 - \fIMH\fR commands, as a last resort, see \fBmhPath\fP.
+.br
+~/Mail/<folder>/.xmhcache - \fIscan\fP output in each folder
+.br
+~/Mail/<folder>/.mh_sequences - sequence definitions, in each folder
+.br
+/tmp - temporary files, see \fBtempDir\fP.
+.SH SEE ALSO
+X(__miscmansuffix__), xrdb(1), X Toolkit Intrinsics, Athena Widget Set, mh(1), enscript(1)
+.br
+At least one book has been published about \fIMH\fP and \fIxmh\fP.
+.SH BUGS
+- When the user closes a window, all windows which are transient for that
+window should also be closed by \fIxmh\fP.
+.br
+- When \fBXmhUseAsComposition\fP and \fBXmhViewUseAsComposition\fP operate
+on messages in the \fBDraftsFolder\fP, \fIxmh\fP disallows editing of the
+composition if the same message is also being viewed in another window.
+.br
+- Occasionally after committing changes, the table of contents will appear
+to be completely blank when there are actually messages present.
+When this happens, refreshing the display, or typing Control-L in the
+table of contents, will often cause the correct listing to appear.
+If this doesn't work, force a rescan of the folder.
+.br
+- Should recognize and use the ``unseen'' message-sequence.
+.br
+- Should determine by itself if the user hasn't used \fIMH\fR before, and
+offer to create the .mh_profile, instead of hanging on inc.
+.br
+- A few commands are missing (rename folder, resend message).
+.br
+- WM_DELETE_WINDOW protocol doesn't work right when requesting deletion
+of the first toc and view, while trying to keep other \fIxmh\fP windows around.
+.br
+- Doesn't support annotations when replying to messages.
+.br
+- Doesn't allow folders to be shared without write permission.
+.br
+- Doesn't recognize private sequences.
+.br
+- \fIMH\fP will report that the \fI.mh_sequences\fP file is poorly formatted
+if any sequence definition in a particular folder contains more
+than \fIBUFSIZ\fP characters. \fIxmh\fP tries to capture these messages
+and display them when they occur, but it cannot correct the problem.
+.br
+- Should save a temporary checkpoint file rather than requiring changes
+to be committed in the non-shutdown case.
+.SH AUTHOR
+Terry Weissman, formerly of Digital Western Research Laboratory
+.br
+Donna Converse, MIT X Consortium