PeanutWindowImpl.mesa
Copyright Ó 1984, 1985, 1988, 1990, 1992 by Xerox Corporation. All rights reserved.
Written by Scott McGregor, February 1983
Last edited by Paxton on March 1, 1983 8:59 am
Last edited by McGregor on March 4, 1983 9:55 am
Last Edited by: Pausch, July 14, 1983 2:28 pm
Last Edited by: Wyatt, April 9, 1985 2:04:54 pm PST
Willie-Sue, December 5, 1989 12:34:39 pm PST
Last changed by Pavel on March 8, 1990 1:47 pm PST
Jules Bloomenthal July 1, 1992 1:38 pm PDT
Doug Wyatt, July 10, 1992 12:29 pm PDT
DIRECTORY Buttons, Icons, IO, Menus, MJSContainers, PeanutProfile, PeanutWindow, PFS, PFSNames, Rope, Rules, TEditDocumentPrivate, TEditProfile, TextEdit, Tioga, TiogaIO, TiogaMenuOps, TiogaOps, TypeScript, VFonts, ViewerClasses, ViewerEvents, ViewerOps, ViewerSpecs, ViewerTools;
PeanutWindowImpl: CEDAR MONITOR
IMPORTS Buttons, Icons, IO, Menus, MJSContainers, PeanutProfile, PFS, PFSNames, Rope, Rules, TEditDocumentPrivate, TEditProfile, TextEdit, TiogaIO, TiogaMenuOps, TiogaOps, TypeScript, VFonts, ViewerEvents, ViewerOps, ViewerSpecs, ViewerTools
EXPORTS PeanutWindow
= BEGIN
Types
ROPE:      TYPE = Rope.ROPE;
STREAM:     TYPE = IO.STREAM;
Viewer:     TYPE = ViewerClasses.Viewer;
Node:      TYPE = Tioga.Node;
peanutDestroyEvent: ViewerEvents.EventRegistration;
Globals
peanutProp:    ATOM = $PeanutMailFileName;
newMailCaption:  ROPE ~ "Peanut - You have new mail";
noNewMailCaption:  ROPE ~ "Peanut - No new mail";
peanutParent:   PUBLIC MJSContainers.MJSContainer ¬ NIL;
peanutRule:    Rules.Rule;
peanutScript:    TypeScript.TS;
menu:      PUBLIC Menus.Menu ¬ Menus.CreateMenu[2];
newMailIcon:   PUBLIC Icons.IconFlavor ¬ Icons.NewIconFromFile["Peanut.icons", 0];
noMailIcon:    PUBLIC Icons.IconFlavor ¬ Icons.NewIconFromFile["Peanut.icons", 1];
messageSetIcon:   PUBLIC Icons.IconFlavor ¬ Icons.NewIconFromFile["Peanut.icons", 2];
dirtyMessageSetIcon: PUBLIC Icons.IconFlavor ¬ Icons.NewIconFromFile["Peanut.icons", 3];
mailMessageIcon:  PUBLIC Icons.IconFlavor ¬ Icons.NewIconFromFile["Peanut.icons", 4];
dirtyMailMessageIcon: PUBLIC Icons.IconFlavor ¬ Icons.NewIconFromFile["Peanut.icons", 5];
maxButtonX:    INTEGER ¬ 0;
buttonX:     INTEGER ¬ 0;
newMailExists:   BOOL ¬ FALSE;
mjsClass:     MJSContainers.MJSContainerClass ~
       NEW[MJSContainers.MJSContainerClassRep ¬ [save: MySave]];
Access to top level Peanut viewer
OutputRope: PUBLIC ENTRY PROC [text: ROPE] = {
ENABLE UNWIND => NULL;
IF ~Destroyed[] THEN TypeScript.PutRope[peanutScript, text];
};
OutputText: PUBLIC ENTRY PROC [text: REF READONLY TEXT] = {
ENABLE UNWIND => NULL;
IF ~Destroyed[] THEN TypeScript.PutText[peanutScript, text];
};
OutputChar: PUBLIC ENTRY PROC [char: CHAR] = {
ENABLE UNWIND => NULL;
IF ~Destroyed[] THEN TypeScript.PutChar[peanutScript, char];
};
SetNewMail: PUBLIC ENTRY PROC [newMail: BOOL] = {
ENABLE UNWIND => NULL;
IF newMailExists=newMail THEN RETURN;
newMailExists ¬ newMail;
IF Destroyed[] THEN RETURN;
peanutParent.icon ¬ IF newMailExists THEN newMailIcon ELSE noMailIcon;
peanutParent.name ¬ Caption[newMailExists];
ViewerOps.PaintViewer[peanutParent, caption];
};
AddCommand: PUBLIC ENTRY PROC [name: ROPE, proc: Menus.ClickProc, data: REF ANY ¬ NIL, fork: BOOL ¬ TRUE, guarded: BOOL ¬ FALSE, lineNum: Menus.MenuLine] = {
ENABLE UNWIND => NULL;
Menus.AppendMenuEntry[menu, Menus.CreateEntry[name, proc, data, NIL, fork, guarded], lineNum];
IF peanutScript#NIL THEN ViewerOps.PaintViewer[peanutParent, all];
};
AddButton: PUBLIC ENTRY PROC [name: ROPE, proc: Menus.ClickProc, data: REF ANY ¬ NIL, fork: BOOL ¬ TRUE, guarded: BOOL ¬ FALSE, redisplay: BOOL ¬ TRUE] = {
ENABLE UNWIND => NULL;
buttonHeight: INTEGER = 15;
buttonSpacing: INTEGER = 5;
width: INTEGER;
IF Destroyed[] THEN RETURN;
width ¬ VFonts.StringWidth[name]+6;
IF width+buttonX>maxButtonX THEN {
ViewerOps.MoveViewer[peanutRule, peanutRule.wx, peanutRule.wy+buttonHeight, peanutRule.ww, peanutRule.wh, FALSE];
ViewerOps.MoveViewer[peanutScript, peanutScript.wx, peanutScript.wy+buttonHeight, peanutScript.ww, peanutScript.wh-buttonHeight, FALSE];
buttonX ¬ 0;
peanutParent.openHeight ¬ peanutParent.openHeight+buttonHeight;
};
[] ¬ Buttons.Create[info: [parent: peanutParent, name: name, wx: buttonX, wy: peanutRule.wy-buttonHeight, border: FALSE, scrollable: FALSE], proc: proc, clientData: data, fork: fork, guarded: guarded];
buttonX ¬ buttonX+width+buttonSpacing;
IF ~peanutParent.iconic AND redisplay THEN ViewerOps.PaintViewer[peanutParent, all];
};
Create: PUBLIC ENTRY PROC [quit, save: PROCNIL] RETURNS [new: BOOL] = {
ENABLE UNWIND => NULL;
peanutOpenHeight: INTEGER;
startIconic: BOOL;
quitProc ← quit;
saveProc ← save;
IF ~Destroyed[] THEN RETURN [FALSE]; -- already exists
maxButtonX ¬ ViewerSpecs.openRightWidth-ViewerSpecs.scrollBarW-4;
peanutOpenHeight ¬ PeanutProfile.windowHeight;
startIconic ¬ PeanutProfile.startIconic;
peanutParent ¬ MJSContainers.Create[$PeanutContainer, [name: Caption[newMailExists], iconic: startIconic, icon: IF newMailExists THEN newMailIcon ELSE noMailIcon, column: right, openHeight: peanutOpenHeight, menu: menu, scrollable: FALSE]];
peanutRule ¬ Rules.Create[[parent: peanutParent, wx: 0, wy: 0, ww: maxButtonX, wh: 1, scrollable: FALSE, border: FALSE]];
peanutScript ¬ TypeScript.Create[[parent: peanutParent, wx: 0, wy: peanutRule.wy+peanutRule.wh, ww: maxButtonX, wh: 60, scrollable: TRUE, border: FALSE]];
peanutDestroyEvent ¬ ViewerEvents.RegisterEventProc[proc: QuitProc, event: destroy, filter: peanutParent, before: FALSE];
MJSContainers.ChildXBound[peanutParent, peanutRule];
MJSContainers.ChildXBound[peanutParent, peanutScript];
MJSContainers.ChildYBound[peanutParent, peanutScript];
buttonX ¬ LAST[INTEGER]/2;
RETURN [TRUE];
};
quitProc, saveProc: PROCNIL;
Destroy: PUBLIC ENTRY PROC = {
ENABLE UNWIND => NULL;
IF ~Destroyed[] THEN ViewerOps.DestroyViewer[peanutParent];
peanutParent ¬ peanutScript ¬ NIL;
};
QuitProc: ViewerEvents.EventProc = {
IF ( peanutParent # NIL ) AND ( peanutDestroyEvent # NIL ) THEN {
peanutParent ¬ NIL;
peanutScript ¬ NIL;
ViewerEvents.UnRegisterEventProc[peanutDestroyEvent, destroy];
peanutDestroyEvent ¬ NIL;
IF quitProc # NIL THEN quitProc[];
PeanutRetrieve.CloseConnection[];
};
RETURN[FALSE];
};
Destroyed: PROC RETURNS [destroyed: BOOL] = {
IF peanutParent = NIL THEN RETURN [TRUE]
ELSE IF peanutParent.destroyed THEN {
peanutParent ¬ peanutScript ¬ NIL;
RETURN [TRUE]
};
RETURN [FALSE];
};
Caption: PROC [newMail: BOOL] RETURNS [name: ROPE] = {
IF newMail THEN RETURN[newMailCaption] ELSE RETURN[noNewMailCaption];
};
SaveAllMailFiles: PUBLIC PROC [mailFiles: LIST OF ROPE, killViewers: BOOL] ~ {
once: BOOL ¬ FALSE;
FOR files: LIST OF ROPE ¬ mailFiles, files.rest UNTIL files = NIL DO
viewer: Viewer ¬ FindMailViewer[files.first];
IF viewer # NIL THEN {
IF viewer.newVersion THEN {
IF NOT once THEN {OutputRope["\nSaving "]; once ¬ TRUE};
OutputRope[files.first];
OutputChar[' ];
[] ¬ ViewerOps.SaveViewer[viewer];
};
IF killViewers THEN ViewerOps.DestroyViewer[viewer];
};
ENDLOOP;
IF once THEN OutputRope["(done)"];
IF killViewers THEN ViewerOps.CloseViewer[peanutParent];
};
MySave: PROC [self: ViewerClasses.Viewer, force: BOOL] -- ViewerClasses.SaveProc -- ~ {
IF saveProc # NIL THEN saveProc[];
};
Message Transfer
SetMailFileName: PROC[v: Viewer, name: ROPE] = {
ViewerOps.AddProp[viewer: v, prop: peanutProp, val: name];
ViewerOps.AddProp[viewer: v, prop: $IconLabel, val: v.label];
};
GetMailFileName: PROC[v: Viewer] RETURNS[ROPE] = {
WITH ViewerOps.FetchProp[viewer: v, prop: peanutProp] SELECT FROM
rope: ROPE => RETURN[rope];
ENDCASE => RETURN[NIL];
};
FindMailViewer: PUBLIC PROC[name: ROPE] RETURNS[viewer: Viewer ¬ NIL] = {
Enumerate top level viewers, looking for one with a matching $PeanutMailFile property
Test: ViewerOps.EnumProc -- PROC[v: Viewer] RETURNS[continue: BOOL ¬ TRUE] -- = {
rope: ROPE = GetMailFileName[v];
IF rope#NIL AND Rope.Equal[rope, name, FALSE] THEN { viewer ¬ v; RETURN[FALSE] };
};
ViewerOps.EnumerateViewers[Test];
};
GetMailViewer: PUBLIC ENTRY PROC[name: ROPE] RETURNS [mailViewer: Viewer] = {
Make this an entry so don't have two Get's at once for same mail file
ENABLE UNWIND => NULL;
mailViewer ¬ InternalGetMailViewer[name];
};
InternalGetMailViewer: PUBLIC INTERNAL PROC [name: ROPE] RETURNS [v: Viewer] ~ {
v ¬ FindMailViewer[name];
IF v=NIL THEN {
wDir: ROPE ¬ PeanutProfile.workingDirectory;
shortName: ROPE ¬ name.Concat[".mail"];
longNamePath: PFS.PATH ¬ PFSNames.ExpandName[name: PFS.PathFromRope[shortName], wDir: PFS.PathFromRope[wDir]];
fileName: ROPE ¬ PFS.RopeFromPath[longNamePath];
mailDoc: Node ¬ NIL;
strm: IO.STREAM;
strm ¬ PFS.StreamOpen[longNamePath ! PFS.Error => IF error.code=$unknownFile THEN CONTINUE];
IF strm = NIL THEN {
styleProp: ROPE = "(mail) style";
mailDoc ¬ TextEdit.DocFromNode[TextEdit.FromRope[shortName]];
TiogaOps.PutProp[mailDoc, $Prefix, styleProp];
}
ELSE {
strm.Close[];
mailDoc ¬ TiogaIO.FromFile[PFS.PathFromRope[fileName]].root;
};
v ¬ ViewerTools.MakeNewTextViewer[info: [
name: fileName, file: fileName, label: name, data: mailDoc,
icon: messageSetIcon, iconic: TRUE],
paint: FALSE];
SetMailFileName[v, name];
ViewerOps.PaintViewer[v, all];
[] ¬ ViewerEvents.RegisterEventProc[proc: MessageSetHasBeenOpened,
event: open, filter: v, before: TRUE];
[] ¬ ViewerEvents.RegisterEventProc[proc: MessageSetHasBeenEdited,
event: edit, filter: v, before: TRUE];
[] ¬ ViewerEvents.RegisterEventProc[proc: MessageSetHasBeenSaved,
event: save, filter: v, before: FALSE];
TiogaMenuOps.DefaultMenus[v];
IF TEditProfile.menu1 # levels AND TEditProfile.menu2 # levels AND TEditProfile.menu3 # levels THEN
TEditDocumentPrivate.LevelMenu[v];
TiogaMenuOps.FirstLevelOnly[v];
};
};
MessageSetHasBeenOpened: PROC[viewer: Viewer,
event: ViewerEvents.ViewerEvent, before: BOOL] RETURNS[abort: BOOL ¬ FALSE] = {
IF PeanutProfile.automaticNewMail AND NOT viewer.destroyed THEN SetNewMail[FALSE];
};
MessageSetHasBeenEdited: PROC[viewer: Viewer,
event: ViewerEvents.ViewerEvent, before: BOOL] RETURNS[abort: BOOL ¬ FALSE] = {
IF before THEN {
viewer.icon ¬ dirtyMessageSetIcon;
IF viewer.iconic THEN ViewerOps.PaintViewer[viewer: viewer, hint: all]; };
};
MessageSetHasBeenSaved: PROC [viewer: Viewer,
event: ViewerEvents.ViewerEvent, before: BOOL] RETURNS[abort: BOOL ¬ FALSE] = {
IF NOT before THEN {
viewer.icon ¬ messageSetIcon;
IF viewer.iconic THEN ViewerOps.PaintViewer[viewer: viewer, hint: all]; };
};
CopyMessages: PUBLIC PROC [to: ROPE, delete: BOOL] = {
sourceViewer, destViewer: Viewer;
destDoc, sourceDoc, destLast, sourceLast, afterSource: Node;
start, end: TiogaOps.Location;
level: TiogaOps.SelectionGrain;
caretBefore, pendingDelete: BOOL;
lockedPrimary, lockedSecondary, lockedDest, lockedSource: BOOL ¬ FALSE;
TopParent: PROC [node, root: Node] RETURNS [parent: Node] = {
DO
parent ¬ TiogaOps.Parent[node];
IF parent=root THEN RETURN [node];
node ¬ parent;
ENDLOOP;
};
Cleanup: PROC = {
IF lockedPrimary THEN TiogaOps.UnlockSel[primary];
IF lockedSecondary THEN TiogaOps.UnlockSel[secondary];
IF lockedDest THEN TiogaOps.Unlock[destDoc];
IF lockedSource THEN TiogaOps.Unlock[sourceDoc];
};
destViewer ¬ GetMailViewer[to];
TiogaOps.LockSel[primary]; lockedPrimary ¬ TRUE;
[sourceViewer, start, end, level, caretBefore, pendingDelete] ¬ TiogaOps.GetSelection[];
IF sourceViewer=NIL OR sourceViewer.class.flavor#$Text THEN {
TiogaOps.UnlockSel[primary]; OutputRope["\nSelect message(s)."]; RETURN };
TiogaOps.LockSel[secondary]; lockedSecondary ¬ TRUE;
destDoc ¬ TiogaOps.ViewerDoc[destViewer];
TiogaOps.Lock[destDoc]; lockedDest ¬ TRUE;
sourceDoc ¬ TiogaOps.ViewerDoc[sourceViewer];
IF sourceDoc#destDoc THEN { TiogaOps.Lock[sourceDoc]; lockedSource ¬ TRUE };
destLast ¬ TiogaOps.LastChild[destDoc];
sourceLast ¬ TopParent[end.node, sourceDoc];
afterSource ¬ TiogaOps.Next[sourceLast];
TiogaOps.SelectBranches[ -- source
viewer: sourceViewer, level: branch, caretBefore: FALSE,
pendingDelete: delete, which: primary,
start: TopParent[start.node, sourceDoc], end: sourceLast];
TiogaOps.SelectBranches[ -- destination
viewer: destViewer, start: destLast, end: destLast,
level: branch, caretBefore: FALSE, pendingDelete: FALSE, which: secondary];
TiogaOps.ToSecondary[];
IF NOT delete THEN -- restore original selection
TiogaOps.SetSelection[sourceViewer, start, end, level, caretBefore, pendingDelete, primary]
ELSE IF afterSource # NIL THEN TiogaOps.SelectPoint[sourceViewer, [afterSource,0], primary]
ELSE TiogaOps.CancelSelection[primary];
Cleanup[];
OutputRope[IF delete THEN "\nMoved to " ELSE "\nCopied to "];
OutputRope[to];
};
Start Code
MJSContainers.RegisterClass[$PeanutContainer, mjsClass];
END.