diff options
author | faith <faith> | 2000-01-06 12:56:54 +0000 |
---|---|---|
committer | faith <faith> | 2000-01-06 12:56:54 +0000 |
commit | ac92f536404367c9977f8ae8fb7a3eee0242334e (patch) | |
tree | f7df43d396cf6739e97230eae89780109427b99a /xc/programs/xmh | |
parent | 11c6e363cc4cda0e9d64c8ba09b7d12dc796d6d0 (diff) |
Import of XFree86 3.9.17
Diffstat (limited to 'xc/programs/xmh')
39 files changed, 13244 insertions, 0 deletions
diff --git a/xc/programs/xmh/Imakefile b/xc/programs/xmh/Imakefile new file mode 100644 index 000000000..757afcb00 --- /dev/null +++ b/xc/programs/xmh/Imakefile @@ -0,0 +1,24 @@ +XCOMM $XConsortium: Imakefile /main/31 1996/09/28 17:18:46 rws $ + +#if HasVFork + VFORK_DEFINES = -DHAS_VFORK +#endif + DEFINES = $(VFORK_DEFINES) XkbClientDefines + DEPLIBS = XkbClientDepLibs XawClientDepLibs +LOCAL_LIBRARIES = XkbClientLibs XawClientLibs + + XMH_BITMAPS = black6 box6 + SRCS = bbox.c command.c compfuncs.c folder.c init.c main.c \ + menu.c mlist.c msg.c pick.c popup.c screen.c toc.c \ + tocfuncs.c tocutil.c tsource.c util.c viewfuncs.c \ + miscfuncs.c + OBJS = bbox.o command.o compfuncs.o folder.o init.o main.o \ + menu.o mlist.o msg.o pick.o popup.o screen.o toc.o \ + tocfuncs.o tocutil.o tsource.o util.o viewfuncs.o \ + miscfuncs.o + + +ComplexProgramTarget(xmh) +BuildIncludes($(XMH_BITMAPS),X11/bitmaps,../..) +InstallAppDefaults(Xmh) +InstallMultipleDestFlags(install,$(XMH_BITMAPS),$(INCDIR)/X11/bitmaps,$(INSTINCFLAGS)) diff --git a/xc/programs/xmh/Xmh.ad b/xc/programs/xmh/Xmh.ad new file mode 100644 index 000000000..fd71a5c51 --- /dev/null +++ b/xc/programs/xmh/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/xc/programs/xmh/Xmh.sample b/xc/programs/xmh/Xmh.sample new file mode 100644 index 000000000..e69ca82dd --- /dev/null +++ b/xc/programs/xmh/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/xc/programs/xmh/actions.h b/xc/programs/xmh/actions.h new file mode 100644 index 000000000..222dbbc5e --- /dev/null +++ b/xc/programs/xmh/actions.h @@ -0,0 +1,99 @@ +/* + * $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. + * + */ + + /* from compfuncs.c */ + +extern void XmhResetCompose(); +extern void XmhSend(); +extern void XmhSave(); + + /* from folder.c */ + +extern void XmhClose(); +extern void XmhComposeMessage(); +extern void XmhOpenFolder(); +extern void XmhOpenFolderInNewWindow(); +extern void XmhCreateFolder(); +extern void XmhDeleteFolder(); +extern void XmhPopupFolderMenu(); +extern void XmhSetCurrentFolder(); +extern void XmhLeaveFolderButton(); +extern void XmhPushFolder(); +extern void XmhPopFolder(); +extern void XmhWMProtocols(); + + /* from msg.c */ + +extern void XmhInsert(); + + /* from popup.c */ + +extern void XmhPromptOkayAction(); + + /* from toc.c */ + +extern void XmhPushSequence(); +extern void XmhPopSequence(); +extern void XmhReloadSeqLists(); + + /* from tocfuncs.c */ + +extern void XmhCheckForNewMail(); +extern void XmhIncorporateNewMail(); +extern void XmhCommitChanges(); +extern void XmhPackFolder(); +extern void XmhSortFolder(); +extern void XmhForceRescan(); +extern void XmhViewNextMessage(); +extern void XmhViewPreviousMessage(); +extern void XmhMarkDelete(); +extern void XmhMarkMove(); +extern void XmhMarkCopy(); +extern void XmhUnmark(); +extern void XmhViewInNewWindow(); +extern void XmhReply(); +extern void XmhForward(); +extern void XmhUseAsComposition(); +extern void XmhPrint(); +extern void XmhShellCommand(); +extern void XmhPickMessages(); +extern void XmhOpenSequence(); +extern void XmhAddToSequence(); +extern void XmhRemoveFromSequence(); +extern void XmhDeleteSequence(); + + /* from viewfuncs.c */ + +extern void XmhCloseView(); +extern void XmhViewReply(); +extern void XmhViewForward(); +extern void XmhViewUseAsComposition(); +extern void XmhEditView(); +extern void XmhSaveView(); +extern void XmhPrintView(); +extern void XmhViewMarkDelete(); diff --git a/xc/programs/xmh/bbox.c b/xc/programs/xmh/bbox.c new file mode 100644 index 000000000..9bb627d1a --- /dev/null +++ b/xc/programs/xmh/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. + */ + +/* 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() +{ + 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, name) + 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, name) + 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, name, kind, enabled, radio) + + 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, name, kind, enabled) + ButtonBox buttonbox; + char *name; + WidgetClass kind; + Boolean enabled; +{ + bboxAddButton(buttonbox, name, kind, enabled, False); +} + + +void RadioBBoxAddButton(buttonbox, name, enabled) + ButtonBox buttonbox; + char *name; + Boolean enabled; +{ + bboxAddButton(buttonbox, name, toggleWidgetClass, enabled, True); +} + + +/* Set the current button in a radio buttonbox. */ + +void RadioBBoxSet(button) + Button button; +{ + XawToggleSetCurrent(button->widget, button->name); +} + + +/* Get the name of the current button in a radio buttonbox. */ + +char *RadioBBoxGetCurrent(buttonbox) + 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 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 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, value) + 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 button; +{ + SendEnableMsg(button->widget, True); +} + + +/* Disable the given button (if it's not already). */ + +void BBoxDisable(button) + 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, name) + 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, w) + 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, n) + ButtonBox buttonbox; + int n; +{ + return buttonbox->button[n]; +} + + +/* Return how many buttons are in a buttonbox. */ + +int BBoxNumButtons(buttonbox) + ButtonBox buttonbox; +{ + return buttonbox->numbuttons; +} + + +/* Given a button, return its name. */ + +char *BBoxNameOfButton(button) + Button button; +{ + return button->name; +} + + +/* Given a button, return its menu. */ + +Widget BBoxMenuOfButton(button) + 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 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, widget) + ButtonBox buttonbox; + Widget widget; +{ + return (XtParent(XtParent(widget)) == buttonbox->inner); +} + + +void BBoxMailFlag(buttonbox, name, up) + 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/xc/programs/xmh/bbox.h b/xc/programs/xmh/bbox.h new file mode 100644 index 000000000..32d89f227 --- /dev/null +++ b/xc/programs/xmh/bbox.h @@ -0,0 +1,62 @@ +/* $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. + */ + +#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/xc/programs/xmh/bboxint.h b/xc/programs/xmh/bboxint.h new file mode 100644 index 000000000..fc062b049 --- /dev/null +++ b/xc/programs/xmh/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/xc/programs/xmh/black6 b/xc/programs/xmh/black6 new file mode 100644 index 000000000..4e5384e44 --- /dev/null +++ b/xc/programs/xmh/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/xc/programs/xmh/box6 b/xc/programs/xmh/box6 new file mode 100644 index 000000000..83ac4a791 --- /dev/null +++ b/xc/programs/xmh/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/xc/programs/xmh/command.c b/xc/programs/xmh/command.c new file mode 100644 index 000000000..d42bf19d5 --- /dev/null +++ b/xc/programs/xmh/command.c @@ -0,0 +1,483 @@ +/* $XConsortium: command.c,v 2.49 95/04/05 19:59:06 kaleb Exp $ */ +/* $XFree86: xc/programs/xmh/command.c,v 3.5 1998/12/13 07:37:52 dawes 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(); +static void CheckReadFromPipe(); + +static void SystemError(text) + 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(str) + char *str; +{ + static char result[100]; + (void) sprintf(result, "%s/%s", app_resources.mh_path, str); + return result; +} + + +/*ARGSUSED*/ +static void ReadStdout(closure, fd, id) + 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(closure, fd, id) + XtPointer closure; + int *fd; + XtInputId *id; /* unused */ +{ + register CommandStatus status = (CommandStatus)closure; + CheckReadFromPipe(*fd, &status->error_buffer, &status->error_buf_size, + False); +} + + +static int childdone; /* Gets nonzero when the child process + finishes. */ +/* ARGSUSED */ +static void +ChildDone(n) + 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(argv, inputfd, outputfd, bufP, lenP) + 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, old_stdout, old_stderr; + 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( fd, bufP, lenP, waitEOF ) + 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( w, closure, call_data ) + Widget w; /* unused */ + Pointer closure; + Pointer 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 */ + +DoCommand(argv, inputfile, outputfile) + 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(argv) +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(argv) + 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/xc/programs/xmh/compfuncs.c b/xc/programs/xmh/compfuncs.c new file mode 100644 index 000000000..ee200ca27 --- /dev/null +++ b/xc/programs/xmh/compfuncs.c @@ -0,0 +1,128 @@ +/* + * $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. + */ + +/* comp.c -- action procedures to handle composition buttons. */ + +#include "xmh.h" + + +/* Reset this composition widget to be one with just a blank message + template. */ + +/*ARGSUSED*/ +void DoResetCompose(widget, client_data, call_data) + 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(w, event, params, num_params) + 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(w, event, params, num_params) + 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(w, event, params, num_params) + 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. */ + +CreateForward(mlist, params, num_params) + 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/xc/programs/xmh/externs.h b/xc/programs/xmh/externs.h new file mode 100644 index 000000000..ce7ba4dc7 --- /dev/null +++ b/xc/programs/xmh/externs.h @@ -0,0 +1,172 @@ +/* + * $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. + */ + +#include <errno.h> +#ifndef X_NOT_STDC_ENV +#include <stdlib.h> +#else +extern int errno; +extern char *getenv(); +extern void exit(); +extern void free(); +#endif + +/* Action routines are declared in actions.h */ +/* Functions which begin with `Do' are the corresponding callbacks. */ + + /* from command.c */ + +extern char * DoCommandToFile (/* Widget, XtPointer, XtPointer */); +extern char * DoCommandToString (/* Widget, XtPointer, XtPointer */); + + /* from compfuncs. */ + +extern void DoResetCompose (/* Widget, XtPointer, XtPointer */); + + /* from folder.c */ + +extern void DoClose (/* Widget, XtPointer, XtPointer */); +extern void DoComposeMessage (/* Widget, XtPointer, XtPointer */); +extern void DoOpenFolder (/* Widget, XtPointer, XtPointer */); +extern void DoOpenFolderInNewWindow (/* Widget, XtPointer, XtPointer */); +extern void DoCreateFolder (/* Widget, XtPointer, XtPointer */); +extern void DoDeleteFolder (/* Widget, XtPointer, XtPointer */); +extern void DoSaveYourself (/* Widget, XtPointer, XtPointer */); +extern void Push (/* Stack, char* */); +extern char * Pop (/* Stack */); + + /* from menu.c */ + +extern void AttachMenuToButton (/* Button, Widget, char * */); +extern void AddMenuEntry (/* Widget, char *, ... */); +extern void DoRememberMenuSelection (/* Widget, XtPointer, XtPointer */); +extern void SendMenuEntryEnableMsg (/* Button, char *, int */); +extern void ToggleMenuItem (/* Widget, Boolean */); + + /* from msg.c */ + +extern Widget CreateFileSource (/* Widget, String, Boolean */); +extern char* MsgName (/* Msg */); + + /* from popup.c */ + +extern void DestroyPopup (/* Widget, XtPointer, XtPointer */); +extern void WMDeletePopup (/* Widget, XEvent* */); +extern void PopupPrompt (/* Widget, String, XtCallbackProc */); +extern void PopupConfirm (/* Widget, String, XtCallbackList, ... */); +extern void PopupNotice (/* char *, XtCallbackProc, XtPointer */); +extern void PopupError (/* Widget, String */); +extern void PopupWarningHandler(); /* for Xt to call */ + + /* 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 Boolean TocHasChanges (/* Toc */); + + /* from tocfuncs.c */ + +extern Boolean UserWantsAction (/* Widget, Scrn */); +extern void DoIncorporateNewMail (/* Widget, XtPointer, XtPointer */); +extern void DoCommit (/* Widget, XtPointer, XtPointer */); +extern void DoPack (/* Widget, XtPointer, XtPointer */); +extern void DoSort (/* Widget, XtPointer, XtPointer */); +extern void DoForceRescan (/* Widget, XtPointer, XtPointer */); +extern void DoReverseReadOrder (/* Widget, XtPointer, XtPointer */); +extern void DoNextView (/* Widget, XtPointer, XtPointer */); +extern void DoPrevView (/* Widget, XtPointer, XtPointer */); +extern void DoDelete (/* Widget, XtPointer, XtPointer */); +extern void DoMove (/* Widget, XtPointer, XtPointer */); +extern void DoCopy (/* Widget, XtPointer, XtPointer */); +extern void DoUnmark (/* Widget, XtPointer, XtPointer */); +extern void DoViewNew (/* Widget, XtPointer, XtPointer */); +extern void DoReply (/* Widget, XtPointer, XtPointer */); +extern void DoForward (/* Widget, XtPointer, XtPointer */); +extern void DoTocUseAsComp (/* Widget, XtPointer, XtPointer */); +extern void DoPrint (/* Widget, XtPointer, XtPointer */); +extern void DoPickMessages (/* Widget, XtPointer, XtPointer */); +extern void DoSelectSequence (/* Widget, XtPointer, XtPointer */); +extern void DoOpenSeq (/* Widget, XtPointer, XtPointer */); +extern void DoAddToSeq (/* Widget, XtPointer, XtPointer */); +extern void DoRemoveFromSeq (/* Widget, XtPointer, XtPointer */); +extern void DoDeleteSeq (/* Widget, XtPointer, XtPointer */); + + /* from util.c */ + +extern void Punt (/* char * */); +extern int myopen (/* char *, int, int */); +extern FILE * myfopen (/* char *, char * */); +extern int myclose (/* int */); +extern int 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 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 (/* type, volume, window */); +extern MsgList CurMsgListOrCurMsg (/* Toc */); +extern int GetWidth (/* Widget */); +extern int GetHeight (/* Widget */); +extern Toc SelectedToc (/* Scrn */); +extern Toc CurrentToc (/* Scrn */); +extern int strncmpIgnoringCase(); +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 (/* Widget, XtPointer, XtPointer */); +extern void DoViewReply (/* Widget, XtPointer, XtPointer */); +extern void DoViewForward (/* Widget, XtPointer, XtPointer */); +extern void DoViewUseAsComposition (/* Widget, XtPointer, XtPointer */); +extern void DoEditView (/* Widget, XtPointer, XtPointer */); +extern void DoSaveView (/* Widget, XtPointer, XtPointer */); +extern void DoPrintView (/* Widget, XtPointer, XtPointer */); diff --git a/xc/programs/xmh/folder.c b/xc/programs/xmh/folder.c new file mode 100644 index 000000000..55b825195 --- /dev/null +++ b/xc/programs/xmh/folder.c @@ -0,0 +1,1183 @@ +/* + * $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. + */ + +/* folder.c -- implement buttons relating to folders and other globals. */ + + +#include "xmh.h" +#include <X11/Xaw/Cardinals.h> +#include <X11/Xatom.h> +#include <sys/stat.h> +#include <ctype.h> +#include "bboxint.h" +#include "tocintrnl.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 CreateFolderMenu(); +static void AddFolderMenuEntry(); +static void DeleteFolderMenuEntry(); + +#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, client_data, call_data) + 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(w, event, params, num_params) + 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, client_data, call_data) + 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(w, event, params, num_params) + 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, client_data, call_data) + 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(w, event, params, num_params) + 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, client_data, call_data) + 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(w, event, params, num_params) + 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, client_data, call_data) + 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, client_data, call_data) + 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(w, event, params, num_params) + Widget w; + XEvent *event; /* unused */ + String *params; /* unused */ + Cardinal *num_params; /* unused */ +{ + Scrn scrn = ScrnFromWidget(w); + DoCreateFolder(w, (XtPointer)scrn, (XtPointer)NULL); +} + + +/*ARGSUSED*/ +void CancelDeleteFolder(widget, client_data, call_data) + 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*/ +void CheckAndConfirmDeleteFolder(widget, client_data, call_data) + 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]; + void CheckAndDeleteFolder(); + + 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*/ +void CheckAndDeleteFolder(widget, client_data, call_data) + 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(w, client_data, call_data) + 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(w, event, params, num_params) + 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(name) + 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(w, closure, data) + Widget w; /* the menu entry object */ + XtPointer closure; /* foldername */ + XtPointer data; +{ + Scrn scrn = ScrnFromWidget(w); + SetCurrentFolderName(scrn, (char *) closure); +} + +/*ARGSUSED*/ +void FreeMenuData(w, client_data, call_data) + Widget w; + XtPointer client_data, 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, entryname) + 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 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, foldername) + 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(w, event, vector, count) + 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(w, event, vector, count) + 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(w, event, vector, count) + Widget w; + XEvent *event; + String *vector; + Cardinal *count; +{ + LastMenuButtonPressed = NULL; +} + + +void Push(stack_ptr, data) + Stack *stack_ptr; + char *data; +{ + Stack new = XtNew(StackRec); + new->data = data; + new->next = *stack_ptr; + *stack_ptr = new; +} + +char * Pop(stack_ptr) + 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(w, event, params, count) + Widget w; + XEvent *event; + String *params; + Cardinal *count; +{ + Scrn scrn = ScrnFromWidget(w); + int 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(w, event, params, count) + 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(str, p, n) + String str; + String *p; + Cardinal n; +{ + int 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(w, event, params, num_params) + 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(w, client_data, call_data) + 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(w, client_data, call_data) + 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(w, client_data, call_data) + 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(w, client_data, call_data) + 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(w, client_data, call_data) + 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(w, client_data, call_data) + 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; + int 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(w, client_data, call_data) + 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/xc/programs/xmh/globals.h b/xc/programs/xmh/globals.h new file mode 100644 index 000000000..54d8f8d77 --- /dev/null +++ b/xc/programs/xmh/globals.h @@ -0,0 +1,165 @@ +/* + * $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. + */ + +#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)(); /* 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/xc/programs/xmh/init.c b/xc/programs/xmh/init.c new file mode 100644 index 000000000..d5f62c129 --- /dev/null +++ b/xc/programs/xmh/init.c @@ -0,0 +1,520 @@ +/* + * $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. + * + */ + +/* 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. */ +Syntax(call) + char *call; +{ + (void) fprintf(stderr, "usage: %s [-path <path>] [-initial <folder>]\n", + call); + exit(2); +} + + +static char *FixUpGeometry(geo, defwidth, defheight) +char *geo; +Dimension defwidth, 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 _IOErrorHandler(dpy) + Display *dpy; +{ + extern char* SysErrMsg(); + (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."); +} + +/*ARGSUSED*/ +static void PopupAppDefaultsWarning(w, closure, event, cont) + 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(w, client_data, call_data) + 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. */ + +InitializeWorld(argc, argv) +int argc; +char **argv; +{ + extern char** environ; /* POSIX doesn't specify a .h for this */ + 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 = XtNewString(getenv("HOME")); + + 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); + } + } + (void) 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/xc/programs/xmh/main.c b/xc/programs/xmh/main.c new file mode 100644 index 000000000..cd0d7d78f --- /dev/null +++ b/xc/programs/xmh/main.c @@ -0,0 +1,146 @@ +/* $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. + */ + +#define MAIN 1 /* Makes global.h actually declare vars */ +#include "xmh.h" + +/*ARGSUSED*/ +static void NeedToCheckScans(client_data, id) + 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(client_data, id) + XtPointer client_data; + XtIntervalId *id; /* unused */ +{ + extern void XmhWMProtocols(); + + if (!subProcessRunning) { + Cardinal n = 1; + String params = "wm_save_yourself"; + DEBUG("(Checkpointing...") + XmhWMProtocols(NULL, NULL, ¶ms, &n); + DEBUG(" done)\n") + } + (void) XtAppAddTimeOut((XtAppContext)client_data, + (unsigned long) app_resources.checkpoint_interval, + Checkpoint, client_data); +} + +/*ARGSUSED*/ +static void CheckMail(client_data, id) + XtPointer client_data; + XtIntervalId *id; /* unused */ +{ + extern void XmhCheckForNewMail(); + + 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 + +main(argc, argv) +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); + exit(0); +#endif +} diff --git a/xc/programs/xmh/menu.c b/xc/programs/xmh/menu.c new file mode 100644 index 000000000..ce538d4cc --- /dev/null +++ b/xc/programs/xmh/menu.c @@ -0,0 +1,85 @@ +/* + * $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. + * + */ + +#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(entry, state) + Widget entry; + Boolean state; +{ + Arg args[1]; + + XtSetArg(args[0], XtNleftBitmap, (state ? MenuItemBitmap : None)); + XtSetValues(entry, args, (Cardinal) 1); +} diff --git a/xc/programs/xmh/miscfuncs.c b/xc/programs/xmh/miscfuncs.c new file mode 100644 index 000000000..9d507c6f3 --- /dev/null +++ b/xc/programs/xmh/miscfuncs.c @@ -0,0 +1,175 @@ +/* $XConsortium: miscfuncs.c,v 1.7 94/12/01 17:15:05 kaleb Exp $ */ +/* $XFree86: xc/programs/xmh/miscfuncs.c,v 3.3 1996/12/26 01:41:01 dawes Exp $ */ + +#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 + +#ifdef X_NOT_STDC_ENV +char *malloc(); +char *realloc(); +#else +#include <stdlib.h> +#endif + + + +#if defined(SYSV) && (defined(i386) || defined(MOTOROLA)) || defined(MINIX) + +/* 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(fd, length, name) + 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. */ + (void) myclose(new_fid); + (void) 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(a, b) + char **a, **b; +{ + return strcmp(*a, *b); +} + +int +ScanDir(Name, List, Selector) + char *Name; + char ***List; + int (*Selector)(); +{ + 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/xc/programs/xmh/mlist.c b/xc/programs/xmh/mlist.c new file mode 100644 index 000000000..1dbd333c4 --- /dev/null +++ b/xc/programs/xmh/mlist.c @@ -0,0 +1,134 @@ +/* $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. + */ + +/* mlist.c -- functions to deal with message lists. */ + +#include "xmh.h" + + +/* Create a message list containing no messages. */ + +MsgList MakeNullMsgList() +{ + 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(mlist, msg) + 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(mlist, msg) +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 msg; +{ + MsgList result; + result = MakeNullMsgList(); + AppendMsgList(result, msg); + return result; +} + + +/* We're done with this message list; free it's storage. */ + +void FreeMsgList(mlist) + 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, str) +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/xc/programs/xmh/mlist.h b/xc/programs/xmh/mlist.h new file mode 100644 index 000000000..068f662bd --- /dev/null +++ b/xc/programs/xmh/mlist.h @@ -0,0 +1,37 @@ +/* $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. + */ + +#ifndef _mlist_h +#define _mlist_h + +extern MsgList MakeNullMsgList(); +extern void AppendMsgList(); +extern void DeleteMsgFromMsgList(); +extern MsgList MakeSingleMsgList(); +extern void FreeMsgList(); +extern MsgList StringToMsgList(); + +#endif /* _mlist_h */ diff --git a/xc/programs/xmh/msg.c b/xc/programs/xmh/msg.c new file mode 100644 index 000000000..c202a10e8 --- /dev/null +++ b/xc/programs/xmh/msg.c @@ -0,0 +1,986 @@ +/* + * $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. + */ + +/* msgs.c -- handle operations on messages. */ + +#include <X11/Xaw/Cardinals.h> + +#include "xmh.h" +#include "tocintrnl.h" + +static int SetScrn(); + +/* 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, edit) +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 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 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 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 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() +{ + 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() +{ + if (*tempDraftFile) { + RenameAndCheck(tempDraftFile, draftFile); + *tempDraftFile = 0; + } +} + + + +/* Public routines */ + + +/* Given a message, return the corresponding filename. */ + +char *MsgFileName(msg) +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. */ + +MsgSaveChanges(msg) +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, client_data, call_data) + 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 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, scrn) + 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, client_data, call_data) + 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, scrn, force, confirms, cancels) + 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, scrn, confirms, cancels) +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, scrn) +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, scrn) +Msg msg; +Scrn scrn; +{ + (void) SetScrn(msg, scrn, TRUE, (XtCallbackList) NULL, + (XtCallbackList) NULL); +} + + + +/* Set the fate of the given message. */ + +void MsgSetFate(msg, fate, desttoc) + 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, toc) +Msg msg; +Toc *toc; /* RETURN */ +{ + if (toc) *toc = msg->desttoc; + return msg->fate; +} + + +/* Make this a temporary message. */ + +void MsgSetTemporary(msg) +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 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 msg; +{ + return msg->msgid; +} + + +/* Return the scanline for this message. */ + +char *MsgGetScanLine(msg) +Msg msg; +{ + return msg->buf; +} + + + +/* Return the toc this message is in. */ + +Toc MsgGetToc(msg) +Msg msg; +{ + return msg->toc; +} + + +/* Set the reapable flag for this msg. */ + +void MsgSetReapable(msg) +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 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 msg; +{ + return msg == NULL || (msg->reapable && + (msg->source == NULL || + !XawAsciiSourceChanged(msg->source))); +} + + +/* Make it possible to edit the given msg. */ +void MsgSetEditable(msg) +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 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 msg; +{ + return msg && msg->source && IsEditable(msg); +} + + +/* Get whether the msg has changed since last saved. */ + +int MsgChanged(msg) +Msg msg; +{ + return msg && msg->source && XawAsciiSourceChanged(msg->source); +} + +/* Call the given function when the msg changes. */ + +void +MsgSetCallOnChange(msg, func, param) +Msg msg; +void (*func)(); +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 msg; +{ + FILEPTR from; + FILEPTR to; + int p, c, l, inheader, sendwidth, sendbreakwidth; + char *ptr, *ptr2, **argv, str[100]; + static 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); + } + (void) myfclose(from); + (void) 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 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!"); + (void) 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!"); + (void) 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, frommsg, params, num_params) +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, msg, mlist, params, num_params) + 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, frommsg) +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 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 msg; +{ + XtFree(msg->buf); + XtFree((char *)msg); +} + +/* Insert the associated message, if any, filtering it first */ + +/*ARGSUSED*/ +void XmhInsert(w, event, params, num_params) + 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(w, filename, edit) +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/xc/programs/xmh/msg.h b/xc/programs/xmh/msg.h new file mode 100644 index 000000000..5644d26ab --- /dev/null +++ b/xc/programs/xmh/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. + */ + +#ifndef _msg_h +#define _msg_h + +extern char *MsgFileName(); +extern int MsgSaveChanges(); +extern int MsgSetScrn(); +extern void MsgSetScrnForComp(); +extern void MsgSetScrnForce(); +extern void MsgSetFate(); +extern FateType MsgGetFate(); +extern void MsgSetTemporary(); +extern void MsgSetPermanent(); +extern int MsgGetId(); +extern char *MsgGetScanLine(); +extern Toc MsgGetToc(); +extern void MsgSetReapable(); +extern void MsgClearReapable(); +extern int MsgGetReapable(); +extern void MsgSetEditable(); +extern void MsgClearEditable(); +extern int MsgGetEditable(); +extern int MsgChanged(); +extern void MsgSetCallOnChange(); +extern void MsgClearCallOnChange(); +extern void MsgSend(); +extern void MsgLoadComposition(); +extern void MsgLoadReply(); +extern void MsgLoadForward(); +extern void MsgLoadCopy(); +extern void MsgCheckPoint(); +extern void MsgFree(); + +#endif /* _msg_h */ diff --git a/xc/programs/xmh/pick.c b/xc/programs/xmh/pick.c new file mode 100644 index 000000000..1050ad3d8 --- /dev/null +++ b/xc/programs/xmh/pick.c @@ -0,0 +1,784 @@ +/* + * $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. + */ + +/* 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(); +static void DeleteWidget(), AddDetailGroup(); + + +InitPick() +{ + 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 PrepareToUpdate(form) + FormBox form; +{ + XawFormDoLayout(form->inner, FALSE); +} + +static ExecuteUpdate(form) + FormBox form; +{ + XawFormDoLayout(form->inner, TRUE); + XtManageChild(form->inner); + XtManageChild(form->outer); +} + +static void AddLabel(row, text, usestd) + 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(row, text, func) + RowList row; + char *text; + void (*func)(); +{ + 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(row, text, initial_state, radio_group, radio_data) + 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(row, str) + 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(entry, str) +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(w, closure, call_data) + 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(w, closure, call_data) + 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 AppendArgv(ptr) + char *ptr; +{ + argvsize++; + argv = ResizeArgv(argv, argvsize); + argv[argvsize - 1] = XtNewString(ptr); +} + +static EraseLast() +{ + argvsize--; + XtFree((char *) argv[argvsize]); + argv[argvsize] = 0; +} + + + +static ParseRow(row) + 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 ParseGroup(group) + 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(w, closure, call_data) + 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(w, closure, call_data) + 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(row, class, args, num_args) + 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(entry) + 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() +{ + stdwidth = 100; /* %%% HACK! */ +} + + +static RowList AddRow(group, type) + 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(form) + 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(form) + 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 AddGeneralGroup(form) + 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, fromseq, toseq) +Pick pick; +char *fromseq, *toseq; +{ + RowList row; + row = pick->general->glist[0]->rlist[0]; + ChangeTextEntry(row->wlist[1], toseq); + ChangeTextEntry(row->wlist[3], fromseq); +} + + +static void CleanForm(form) +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 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; +} + + +AddPick(scrn, toc, fromseq, toseq) + Scrn scrn; + Toc toc; + char *fromseq, *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/xc/programs/xmh/popup.c b/xc/programs/xmh/popup.c new file mode 100644 index 000000000..1274a05b7 --- /dev/null +++ b/xc/programs/xmh/popup.c @@ -0,0 +1,504 @@ +/* $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. + */ + +/* popup.c -- Handle pop-up widgets. */ + +#include "xmh.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(x_ptr, y_ptr, transFor_return) + Position *x_ptr, *y_ptr; + Widget *transFor_return; /* return a suitable top level shell */ +{ + if (lastInput.win != -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(popup, x, y) + Widget popup; + Position x, 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, popup, px, py) + Widget widget; + Widget popup; + Position px, 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(popup, popup_child, x, y, centerX, centerY) + Widget popup, popup_child; + Position x, y; /* assert: current position = (x,y) */ + Boolean centerX, 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, client_data, call_data) + Widget widget; /* unused */ + XtPointer client_data; + XtPointer call_data; /* unused */ +{ + Widget popup = (Widget) client_data; + XtPopdown(popup); + XtDestroyWidget(popup); +} + +void WMDeletePopup(popup, event) + 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(popup) + 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(w, event, params, num_params) + 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(transientFor, question, okayCallback) + 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( w, closure, call_data ) + 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(message, callback, closure) + 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(center_widget, question, affirm_callbacks, negate_callbacks) + 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, message) + 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(name, type, class, msg, params, num) + 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/xc/programs/xmh/screen.c b/xc/programs/xmh/screen.c new file mode 100644 index 000000000..b54d889b3 --- /dev/null +++ b/xc/programs/xmh/screen.c @@ -0,0 +1,519 @@ +/* + * $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. + */ + +/* 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 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 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, mbd) + Scrn scrn; + XmhMenuButtonDesc mbd; +{ + register int 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 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; +{ + 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; +{ + 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(kind) +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; + } + + 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; + } + + 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() +{ + return CreateNewScrn(STview); +} + +Scrn NewCompScrn() +{ + Scrn scrn; + scrn = CreateNewScrn(STcomp); + scrn->assocmsg = (Msg)NULL; + return scrn; +} + +void ScreenSetAssocMsg(scrn, msg) + Scrn scrn; + Msg msg; +{ + scrn->assocmsg = msg; +} + +/* Destroy the screen. If unsaved changes are in a msg, too bad. */ + +void DestroyScrn(scrn) + 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 scrn; +{ + if (!scrn->mapped) { + XtPopup(scrn->parent, XtGrabNone); + scrn->mapped = True; + } +} + + +Scrn ScrnFromWidget(w) /* heavily used, should be efficient */ +Widget w; +{ + 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!"); + /*NOTREACHED*/ +} + + +/* 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(w, data, junk) +Widget w; +XtPointer data, junk; +{ + EnableProperButtons( (Scrn) data); +} + +static void EnableCallback(); + +#define SetButton(buttonbox, name, value) \ + if (value) BBoxEnable(BBoxFindButtonNamed(buttonbox, name)); \ + else BBoxDisable(BBoxFindButtonNamed(buttonbox, name)); + + +void EnableProperButtons(scrn) +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; + } + } +} diff --git a/xc/programs/xmh/toc.c b/xc/programs/xmh/toc.c new file mode 100644 index 000000000..ba23a8ddd --- /dev/null +++ b/xc/programs/xmh/toc.c @@ -0,0 +1,1256 @@ +/* $XConsortium: toc.c,v 2.59 95/01/09 16:52:53 swick Exp $ + * $XFree86: xc/programs/xmh/toc.c,v 3.1 1995/01/27 04:55:00 dawes 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 <sys/stat.h> + +#ifdef MINIX +#define S_IWRITE S_IWUSR +#endif + +static int IsDir(name) +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(namelistptr, numfoldersptr, name) +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(namelistptr, numfoldersptr, name) + 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 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() +{ + 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() +{ + Toc toc; + char **namelist; + int i; + extern alphasort(); + 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(foldername) + 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(foldername) +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 toc; +{ + return toc->mailpending; +} + +static int CheckForNewMail(toc) + 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(update) + 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 toc; +{ + Boolean flag; + + flag = toc->delete_pending; + toc->delete_pending = True; + return flag; +} + +void TocClearDeletePending(toc) + Toc toc; +{ + toc->delete_pending = False; +} + + +/* Recursively delete an entire directory. Nasty. */ + +static void NukeDirectory(path) + 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; +{ + 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, scrn) +Toc toc; +Scrn scrn; +{ + int 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, msg) +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 toc; +{ + int 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, msg) + Toc toc; + Msg msg; +{ + Msg msg2; + int 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 toc; +{ + return toc->curmsg; +} + + + + +/* Return the message after the given one. (If none, return NULL.) */ + +Msg TocMsgAfter(toc, msg) + 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, msg) + 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 toc; +{ + register int 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 toc; +{ + int 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(w, event, params, num_params) + 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 toc; +{ + return toc && toc->numsequences > 1; +} + + +/* Change which sequence is being viewed. */ + +void TocChangeViewedSeq(toc, seq) + 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, name) +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 toc; +{ + return toc->viewedseq; +} + + +/* Set the selected sequence in the toc */ + +void TocSetSelectedSequence(toc, sequence) + Toc toc; + Sequence sequence; +{ + if (toc) + toc->selectseq = sequence; +} + + +/* Return the sequence currently selected */ + +Sequence TocSelectedSequence(toc) + 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 toc; +{ + MsgList result; + XawTextPosition pos1, pos2; + extern Msg MsgFromPosition(); + 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 toc; +{ + if (toc->source) + XawTextUnsetSelection(toc->scrn[0]->tocwidget); +} + + + +/* Create a brand new, blank message. */ + +Msg TocMakeNewMsg(toc) +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 toc; +{ + int 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 toc; +{ + int 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 toc; +{ + TUSaveTocFile(toc); +} + + +/* Return the full folder pathname of the given toc, prefixed w/'+' */ + +char *TocMakeFolderName(toc) +Toc toc; +{ + char* name = XtMalloc((Cardinal) (strlen(toc->path) + 2) ); + (void)sprintf( name, "+%s", toc->path ); + return name; +} + +char *TocName(toc) +Toc toc; +{ + return toc->foldername; +} + + + +/* Given a foldername, return the corresponding toc. */ + +Toc TocGetNamed(name) +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 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, client_data, call_data) + 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, confirms, cancels) + 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, client_data, call_data) + Widget widget; /* unused */ + XtPointer client_data; + XtPointer call_data; /* unused */ +{ + Toc toc = (Toc) client_data; + Msg msg; + int i, cur; + char str[100], **argv; + FateType curfate, fate; + Toc desttoc; + Toc curdesttoc; + 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; + } + } + 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; + } + 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 toc; +{ + return (toc && (toc == InitialFolder || toc->incfile)); +} + + +/* Incorporate new messages into the given toc. */ + +int TocIncorporate(toc) +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); + (void) 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, msg) +Toc toc; +Msg msg; +{ + char **argv, str[100], str2[10], *ptr; + int length, delta, 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, msgid) +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(w, event, params, count) + Widget w; + XEvent *event; + String *params; + Cardinal *count; +{ + Scrn scrn = ScrnFromWidget(w); + Toc toc; + int 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(w, event, params, count) + 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/xc/programs/xmh/toc.h b/xc/programs/xmh/toc.h new file mode 100644 index 000000000..e1b300cf9 --- /dev/null +++ b/xc/programs/xmh/toc.h @@ -0,0 +1,75 @@ +/* + * $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. + */ + +#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/xc/programs/xmh/tocfuncs.c b/xc/programs/xmh/tocfuncs.c new file mode 100644 index 000000000..a411c2297 --- /dev/null +++ b/xc/programs/xmh/tocfuncs.c @@ -0,0 +1,1082 @@ +/* + * $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. + */ + +/* tocfuncs.c -- action procedures concerning things in the toc widget. */ + +#include "xmh.h" +#include "tocutil.h" + +#define MAX_SYSTEM_LEN 510 + +Boolean UserWantsAction(w, scrn) /* general action procedure "filter" */ + 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, next) + Scrn scrn; + Boolean next; /* if true, next or forward; if false, previous */ +{ + Toc toc = scrn->toc; + MsgList mlist; + FateType fate; + 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, client_data, call_data) + 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, client_data, call_data) + Widget widget; /* unused */ + XtPointer client_data; + XtPointer call_data; /* unused */ +{ + NextAndPreviousView((Scrn) client_data, + (app_resources.reverse_read_order ? False : True)); +} + +/*ARGSUSED*/ +void XmhViewNextMessage(w, event, params, num_params) + 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, client_data, call_data) + Widget widget; /* unused */ + XtPointer client_data; + XtPointer call_data; /* unused */ +{ + NextAndPreviousView((Scrn) client_data, + (app_resources.reverse_read_order ? True : False)); +} + +/*ARGSUSED*/ +void XmhViewPreviousMessage(w, event, params, num_params) + 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(w, client_data, call_data) + 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(w, event, params, num_params) + Widget w; + XEvent *event; + String *params; + Cardinal *num_params; +{ + Scrn scrn = ScrnFromWidget(w); + if (UserWantsAction(w, scrn)) + DoViewNew(w, (XtPointer) scrn, (XtPointer) NULL); +} + + +void DoForwardMsg(scrn, params, num_params) + 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(w, client_data, call_data) + Widget w; + XtPointer client_data; + XtPointer call_data; +{ + DoForwardMsg((Scrn) client_data, (String *)NULL, (Cardinal)0); +} + + +/*ARGSUSED*/ +void XmhForward(w, event, params, num_params) + 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(w, client_data, call_data) + 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(w, event, params, num_params) + 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 MarkMessages(scrn, fate, skip) +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(w, event, params, num_params) + 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(w, client_data, call_data) + Widget w; + XtPointer client_data; + XtPointer call_data; +{ + Scrn scrn = (Scrn) client_data; + MarkMessages(scrn, Fdelete, app_resources.skip_deleted); +} + + +/*ARGSUSED*/ +void DoCopy(w, client_data, call_data) + Widget w; + XtPointer client_data; + XtPointer call_data; +{ + Scrn scrn = (Scrn) client_data; + MarkMessages(scrn, Fcopy, app_resources.skip_copied); +} + + +/*ARGSUSED*/ +void XmhMarkCopy(w, event, params, num_params) + 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(w, client_data, call_data) + Widget w; + XtPointer client_data; + XtPointer call_data; +{ + Scrn scrn = (Scrn) client_data; + MarkMessages(scrn, Fmove, app_resources.skip_moved); +} + + +/*ARGSUSED*/ +void XmhMarkMove(w, event, params, num_params) + 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(w, client_data, call_data) + Widget w; + XtPointer client_data; + XtPointer call_data; +{ + Scrn scrn = (Scrn) client_data; + MarkMessages(scrn, Fignore, FALSE); +} + + +/*ARGSUSED*/ +void XmhUnmark(w, event, params, num_params) + 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(w, client_data, call_data) + Widget w; + XtPointer client_data; + XtPointer call_data; +{ + Scrn scrn = (Scrn) client_data; + TocCommitChanges(w, (XtPointer) scrn->toc, (XtPointer) NULL); +} + + +/*ARGSUSED*/ +void XmhCommitChanges(w, event, params, num_params) + 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(w, event, params, num_params) + 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(w, event, params, num_params) + 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(w, client_data, call_data) + 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, client_data, call_data) + 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(w, event, params, num_params) + 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, client_data, call_data) + 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(w, event, params, num_params) + 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(w, event, params, num_params) + 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(w, client_data, call_data) + 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(w, e, p, n) + Widget w; + XEvent *e; + String *p; + Cardinal *n; +{ + TocCheckForNewMail(True); +} + +/* Incorporate new mail. */ + +/*ARGSUSED*/ +void XmhIncorporateNewMail(w, event, params, num_params) + 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(w, client_data, call_data) + 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); +} + + +void DoReplyMsg(scrn, params, num_params) + 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(w, client_data, call_data) + Widget w; + XtPointer client_data; + XtPointer call_data; +{ + DoReplyMsg((Scrn) client_data, (String *)NULL, (Cardinal)0); +} + + +/*ARGSUSED*/ +void XmhReply(w, event, params, num_params) + 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(w, client_data, call_data) + 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(w, event, params, num_params) + 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, client_data, call_data) + 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(w, client_data, call_data) + 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(w, event, params, num_params) + 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 TwiddleSequence(scrn, op) +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(w, client_data, call_data) + Widget w; + XtPointer client_data; + XtPointer call_data; +{ + Scrn scrn = (Scrn) client_data; + TwiddleSequence(scrn, ADD); +} + + +/*ARGSUSED*/ +void XmhAddToSequence(w, event, params, num_params) + 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(w, client_data, call_data) + Widget w; + XtPointer client_data; + XtPointer call_data; +{ + Scrn scrn = (Scrn) client_data; + TwiddleSequence(scrn, REMOVE); +} + + +/*ARGSUSED*/ +void XmhRemoveFromSequence(w, event, params, num_params) + 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(w, client_data, call_data) + Widget w; + XtPointer client_data; + XtPointer call_data; +{ + Scrn scrn = (Scrn) client_data; + TwiddleSequence(scrn, DELETE); + TUCheckSequenceMenu(scrn->toc); +} + + +/*ARGSUSED*/ +void XmhDeleteSequence(w, event, params, num_params) + 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/xc/programs/xmh/tocintrnl.h b/xc/programs/xmh/tocintrnl.h new file mode 100644 index 000000000..7676a3fd1 --- /dev/null +++ b/xc/programs/xmh/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/xc/programs/xmh/tocutil.c b/xc/programs/xmh/tocutil.c new file mode 100644 index 000000000..020d63cb9 --- /dev/null +++ b/xc/programs/xmh/tocutil.c @@ -0,0 +1,660 @@ +/* + * $XConsortium: tocutil.c,v 2.60 95/01/09 16:52:53 swick Exp $ + * $XFree86: xc/programs/xmh/tocutil.c,v 3.1 1995/01/27 04:55:04 dawes 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() +{ + 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 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 toc; +{ + Scrn scrn; + register int i, n; + Arg query_args[2]; + char *name; + int j, 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 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, msg) + Toc toc; + Msg msg; +{ + int msgid, h, 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 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 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 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 toc; +{ + MsgList mlist; + Msg msg, oldcurmsg; + int i, 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 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 && 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 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)) || defined(MINIX) + (void) ftruncate_emu(fid, toc->length, toc->scanfile); +#else + (void) ftruncate(fid, toc->length); + (void) myclose(fid); +#endif + toc->origlength = toc->length; + } + toc->needscachesave = FALSE; + toc->lastreaddate = LastModifyDate(toc->scanfile); +} + + +static Boolean UpdateScanFile(client_data) + 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, delay) + 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); + (void) 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 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 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, ptr) + Toc toc; + char *ptr; +{ + Msg msg; + int msgid, i; + + 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/xc/programs/xmh/tocutil.h b/xc/programs/xmh/tocutil.h new file mode 100644 index 000000000..475003b3d --- /dev/null +++ b/xc/programs/xmh/tocutil.h @@ -0,0 +1,46 @@ +/* $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. + */ + +#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, delay */); +extern void TURefigureTocPositions (/* Toc */); +extern void TUGetFullFolderInfo (/* Toc */); +extern Msg TUAppendToc (/* Toc, char * */); + +#endif /* _tocutil_h */ diff --git a/xc/programs/xmh/tsource.c b/xc/programs/xmh/tsource.c new file mode 100644 index 000000000..67029bf00 --- /dev/null +++ b/xc/programs/xmh/tsource.c @@ -0,0 +1,338 @@ +/* $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. + */ + +/* 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(); +static XawTextPosition Read(), Scan(), Search(); +static int Replace(); + +#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, position, dir) + 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, position) + Toc toc; + XawTextPosition position; +{ + return (position < 0) ? 0 : + ((position > toc->lastPos) ? toc->lastPos : position); +} + +static XawTextPosition +Read(w, position, block, length) +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(w, startPos, endPos, block) +Widget w; +XawTextPosition startPos, 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(w, position, sType, dir, count, include) +Widget w; +XawTextPosition position; +XawTextScanType sType; +XawTextScanDirection dir; +int count; +Boolean include; +{ + TocSourceWidget source = (TocSourceWidget) w; + Toc toc = source->toc_source.toc; + XawTextPosition textindex; + Msg msg; + char c; + int ddir, doff, i, whiteSpace; + 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; + } + return textindex; +} +/*ARGSUSED*/ +static XawTextPosition Search(w, position, direction, block) +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(request, new, args, num_args) +Widget request, 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, position, length) + Toc toc; + XawTextPosition position; + int length; +{ + XawTextInvalidate(XtParent(toc->source), position, + (XawTextPosition) position+length-1); +} diff --git a/xc/programs/xmh/tsource.h b/xc/programs/xmh/tsource.h new file mode 100644 index 000000000..ca0f33ec7 --- /dev/null +++ b/xc/programs/xmh/tsource.h @@ -0,0 +1,75 @@ +/* $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. + +******************************************************************/ + +#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 void TSourceInvalid(); + +#endif /* _XawTextSrc_h */ +/* DON'T ADD STUFF AFTER THIS #endif */ diff --git a/xc/programs/xmh/tsourceP.h b/xc/programs/xmh/tsourceP.h new file mode 100644 index 000000000..71582bda6 --- /dev/null +++ b/xc/programs/xmh/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/xc/programs/xmh/util.c b/xc/programs/xmh/util.c new file mode 100644 index 000000000..2c00b448b --- /dev/null +++ b/xc/programs/xmh/util.c @@ -0,0 +1,529 @@ +/* + * $XConsortium: util.c /main/42 1996/01/14 16:51:55 kaleb $ + * $XFree86: xc/programs/xmh/util.c,v 3.3 1996/01/16 15:09:28 dawes 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 (n) + int n; +{ + char *s = strerror(n); + + return (s ? s : "no such error"); +} + +/* Something went wrong; panic and quit. */ + +void Punt(str) + 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(path, flags, mode) +char *path; +int flags, mode; +{ + int fid; + fid = open(path, flags, mode); + if (fid >= 0) DEBUG2("# %d : %s\n", fid, path) + return fid; +} + + +FILE *myfopen(path, mode) +char *path, *mode; +{ + FILE *result; + result = fopen(path, mode); + if (result) DEBUG2("# %d : %s\n", fileno(result), path) + return result; +} + + + +int myclose(fid) +{ + if (close(fid) < 0) Punt("Error in myclose!"); + DEBUG1( "# %d : <Closed>\n", fid) +} + + +int myfclose(file) +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() +{ + static char name[60]; + static int uniqueid = 0; + do { + (void) sprintf(name, "%s/xmh_%ld_%d", app_resources.temp_dir, + getpid(), uniqueid++); + } while (FileExists(name)); + return name; +} + + +/* Make an array of string pointers big enough to hold n+1 entries. */ + +char **MakeArgv(n) + int n; +{ + char **result; + result = ((char **) XtMalloc((unsigned) (n+1) * sizeof(char *))); + result[n] = 0; + return result; +} + + +char **ResizeArgv(argv, n) + 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(name, mode) + char *name, *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(fid, lastchar) + 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(fid) + FILEPTR fid; +{ + return DoReadLine(fid, 0); +} + + +/* Read a line, and keep the CR at the end. */ + +char *ReadLineWithCR(fid) + FILEPTR fid; +{ + return DoReadLine(fid, '\n'); +} + + + +/* Delete a file, and Punt if it fails. */ + +void DeleteFileAndCheck(name) + 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(from, to) + char *from, *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); + (void) myclose(fromfid); + (void) myclose(tofid); +} + + +void RenameAndCheck(from, to) + char *from, *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(gbits, x, y, width, height) + int gbits; + int x, y, width, 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; +} + + +FileExists(file) + char *file; +{ + return (access(file, F_OK) == 0); +} + +LastModifyDate(file) + char *file; +{ + struct stat buf; + if (stat(file, &buf)) return -1; + return buf.st_mtime; +} + +GetFileLength(file) +char *file; +{ + struct stat buf; + if (stat(file, &buf)) return -1; + return buf.st_size; +} + +Boolean IsSubfolder(foldername) + char *foldername; +{ + return (strchr(foldername, '/')) ? True : False; +} + +void SetCurrentFolderName(scrn, foldername) + Scrn scrn; + char *foldername; +{ + scrn->curfolder = foldername; + ChangeLabel((Widget) scrn->folderlabel, foldername); +} + + +void ChangeLabel(widget, str) +Widget widget; +char *str; +{ + static Arg arglist[] = {XtNlabel, (XtArgVal)NULL}; + arglist[0].value = (XtArgVal) str; + XtSetValues(widget, arglist, XtNumber(arglist)); +} + + +Widget CreateTextSW(scrn, name, args, num_args) +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, name) +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(type,volume,win) + int type; + int volume; + Window win; +{ +#ifdef XKB + XkbStdBell(theDisplay, win, volume, type); +#else + XBell(theDisplay, volume); +#endif +} + + +MsgList CurMsgListOrCurMsg(toc) + 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(w) + Widget w; +{ + Dimension height; + Arg args[1]; + + XtSetArg(args[0], XtNheight, &height); + XtGetValues( w, args, (Cardinal)1 ); + return (int)height; +} + + +int GetWidth(w) + Widget w; +{ + Dimension width; + Arg args[1]; + + XtSetArg(args[0], XtNwidth, &width); + XtGetValues( w, args, (Cardinal)1 ); + return (int)width; +} + + +Toc SelectedToc(scrn) +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 scrn; +{ + /* return the toc currently being viewed */ + + return scrn->toc; +} + + +int strncmpIgnoringCase(str1, str2, length) +char *str1, *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, str) +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 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() +{ + register int i; + + for (i=0; i < numScrns; i++) + if (scrnList[i]->mapped) + XMapWindow(theDisplay, scrnList[i]->wait_window); +} + +void UnshowBusyCursor() +{ + register int i; + + for (i=0; i < numScrns; i++) + if (scrnList[i]->mapped) + XUnmapWindow(theDisplay, scrnList[i]->wait_window); +} + + +void SetCursorColor(widget, cursor, foreground) + 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/xc/programs/xmh/version.h b/xc/programs/xmh/version.h new file mode 100644 index 000000000..d87d9c4bd --- /dev/null +++ b/xc/programs/xmh/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/xc/programs/xmh/viewfuncs.c b/xc/programs/xmh/viewfuncs.c new file mode 100644 index 000000000..1ac9bc922 --- /dev/null +++ b/xc/programs/xmh/viewfuncs.c @@ -0,0 +1,285 @@ +/* + * $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. + */ + +/* view.c -- action procedures to handle viewing of a message */ + +#include "xmh.h" + + +/*ARGSUSED*/ +void DoCloseView(widget, client_data, call_data) + 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(w, event, params, num_params) + Widget w; + XEvent *event; + String *params; + Cardinal *num_params; +{ + Scrn scrn = ScrnFromWidget(w); + DoCloseView(w, (XtPointer) scrn, (XtPointer) NULL); +} + + +void DoViewReplyMsg(scrn, params, num_params) + 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(w, client_data, call_data) + Widget w; + XtPointer client_data; + XtPointer call_data; +{ + DoViewReplyMsg((Scrn) client_data, (String *)NULL, (Cardinal)0); +} + + + +/*ARGSUSED*/ +void XmhViewReply(w, event, params, num_params) + Widget w; + XEvent *event; + String *params; + Cardinal *num_params; +{ + Scrn scrn = ScrnFromWidget(w); + DoViewReplyMsg(scrn, params, *num_params); +} + + +/*ARGSUSED*/ +void DoViewForwardMsg(scrn, params, num_params) + 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(w, client_data, call_data) + Widget w; + XtPointer client_data; + XtPointer call_data; +{ + DoViewForwardMsg((Scrn) client_data, (String *)NULL, (Cardinal)0); +} + +/*ARGSUSED*/ +void XmhViewForward(w, event, params, num_params) + Widget w; + XEvent *event; + String *params; + Cardinal *num_params; +{ + DoViewForwardMsg(ScrnFromWidget(w), params, *num_params); +} + + +/*ARGSUSED*/ +void DoViewUseAsComposition(w, client_data, call_data) + 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(w, event, params, num_params) + Widget w; + XEvent *event; + String *params; + Cardinal *num_params; +{ + Scrn scrn = ScrnFromWidget(w); + DoViewUseAsComposition(w, (XtPointer) scrn, (XtPointer) NULL); +} + + +/*ARGSUSED*/ +void DoEditView(w, client_data, call_data) + 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(w, event, params, num_params) + 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(w, client_data, call_data) + 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(w, event, params, num_params) + 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(w, client_data, call_data) + 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(w, event, params, num_params) + Widget w; + XEvent *event; + String *params; + Cardinal *num_params; +{ + Scrn scrn = ScrnFromWidget(w); + DoPrintView(w, (XtPointer) scrn, (XtPointer) NULL); +} + + +/*ARGSUSED*/ +void XmhViewMarkDelete(w, event, params, num_params) + 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/xc/programs/xmh/xmh.h b/xc/programs/xmh/xmh.h new file mode 100644 index 000000000..c72267a0a --- /dev/null +++ b/xc/programs/xmh/xmh.h @@ -0,0 +1,150 @@ +/* + * $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. + */ + +#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; + + +#include "globals.h" +#include "externs.h" +#include "mlist.h" +#include "bbox.h" +#include "msg.h" +#include "toc.h" + +#endif /* _xmh_h */ diff --git a/xc/programs/xmh/xmh.man b/xc/programs/xmh/xmh.man new file mode 100644 index 000000000..3a2e902c1 --- /dev/null +++ b/xc/programs/xmh/xmh.man @@ -0,0 +1,1536 @@ +.\" $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. +.TH XMH 1 "Release 6.3" "X Version 11" +.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(1)\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(1), 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 |