RemindWalnut.mesa
Copyright Ó 1990, 1992 by Xerox Corporation. All rights reserved.
David Goldberg November 13, 1989 8:28:33 pm PST
Peter B. Kessler, January 10, 1990 10:46:47 am PST
Theimer, September 10, 1990 2:12 pm PDT
Last tweaked by Mike Spreitzer April 23, 1992 11:01 am PDT
DIRECTORY BasicTime, IO, Menus, Process, RefText, Remind, RemindEdit, Rope, TextNode, TimeParse, TiogaOps, UserProfile, ViewerClasses, ViewerTools, WalnutRegistry, WalnutWindow;
Adds button to walnut.
RemindWalnut: CEDAR PROGRAM
IMPORTS
RemindEdit, BasicTime, IO, Process, RefText, Rope, TimeParse, TiogaOps, UserProfile, ViewerTools, WalnutRegistry, WalnutWindow
= BEGIN OPEN Remind;
ROPE: TYPE = Rope.ROPE;
WhiteSpace: IO.BreakProc = {
RETURN[SELECT char FROM ' , '\n, '\t, '\l => sepr, ENDCASE => other];
};
PostRemindButton: PROC = TRUSTED {
DO
IF WalnutRegistry.CurrentWalnutState[] # unknown THEN {
walnutUser ¬ TRUE;
UNTIL WalnutRegistry.CurrentWalnutState[] # initializing DO
Process.Pause[Process.SecondsToTicks[5]];
ENDLOOP;
FOR whL: LIST OF WalnutWindow.WalnutHandle ¬ WalnutWindow.GetHandleList[], whL.rest UNTIL whL = NIL DO
[] ¬ WalnutWindow.ReplaceInMsgMenu[wH: whL.first, label: "RemindMe", proc: RemindProc];
ENDLOOP;
EXIT;
};
ELSE IF Basics.IsBound[PeanutWindow.AddCommand] THEN {
PeanutWindow.AddCommand[name: "RemindMe", proc: PeanutRemindProc, fork: FALSE];
peanutUser ← TRUE;
EXIT;
};
Process.Pause[Process.SecondsToTicks[60]];
ENDLOOP;
};
RemindProc: Menus.ClickProc = {
headerSize, msgSize: INT;
timeSpecified: BOOLEAN ¬ FALSE;
viewer: ViewerClasses.Viewer = NARROW[parent];
rootAny: REF ANY ¬ TiogaOps.ViewerDoc[viewer];
root: TextNode.Ref ¬ NARROW[rootAny];
messageName, rootFile, sel, msgText, subject: ROPE ¬ NIL;
dayRope, dateRope, timeRope, titleRope: ROPE ¬ NIL;
startRope: ROPE;
meeting: Meeting;
ps: IO.STREAM;
buffer, fieldTag, fieldBody: REF TEXT;
GatherField: PROC [skipped: INT] RETURNS [ans: ROPE ¬ NIL] ~ {
[] ¬ SkipSpace[ps, TRUE];
IF ps.EndOf[] THEN RETURN;
ans ¬ ps.GetLineRope[];
WHILE (NOT ps.EndOf[]) AND White[ps.PeekChar] DO
this: INT ~ SkipSpace[ps, FALSE];
IF ps.EndOf[] OR this<=skipped THEN RETURN;
ans ¬ ans.Cat["\n", ps.GetLineRope[]];
ENDLOOP;
RETURN};
[messageName, rootFile] ¬ WalnutWindow.GetMsgName[viewer];
sel ¬ ViewerTools.GetSelectionContents[];
meeting ¬ NEW[MeetingRec];
msgText ¬ ViewerTools.GetContents[viewer];
msgSize ¬ msgText.Length[];
ps ¬ IO.RIS[msgText];
buffer ¬ RefText.New[200];
WHILE NOT ps.EndOf[] DO
fieldTag ¬ ps.GetToken[HeaderBreak, buffer].token;
SELECT TRUE FROM
RefText.Equal[fieldTag, "\n", TRUE] => EXIT;
RefText.Equal[fieldTag, "\r", TRUE] => EXIT;
RefText.Equal[fieldTag, "\l", TRUE] => EXIT;
ps.EndOf[] => EXIT;
RefText.Equal[fieldTag, "Subject:", FALSE] => {
[] ¬ SkipSpace[ps, FALSE];
subject ¬ ps.GetLineRope[]};
ENDCASE => fieldBody ¬ ps.GetLine[buffer];
ENDLOOP;
headerSize ¬ ps.GetIndex[];
WHILE NOT ps.EndOf[] DO
skipped: INT;
[fieldTag, skipped] ¬ ps.GetToken[BodyBreak, buffer];
SELECT TRUE FROM
fieldTag.length=1 AND BodyBreak[fieldTag[0]]=break => NULL;
RefText.Equal[fieldTag, "day", FALSE] => dayRope ¬ GatherField[skipped];
RefText.Equal[fieldTag, "date", FALSE] => dateRope ¬ GatherField[skipped];
RefText.Equal[fieldTag, "time", FALSE] => timeRope ¬ GatherField[skipped];
RefText.Equal[fieldTag, "title", FALSE] => titleRope ¬ GatherField[skipped];
ps.EndOf[] => EXIT;
ENDCASE => fieldBody ¬ ps.GetLine[buffer];
ENDLOOP;
meeting.start ¬ BasicTime.nullGMT;
IF sel.Length[] > 1 THEN {
pieces: TimeParse.PiecesType;
selRem: ROPE;
[meeting.start, pieces] ¬ TimeParse.Parse[sel, BasicTime.Now[], heuristic, FALSE, TRUE
!TimeParse.ParseError => GOTO NotHere];
IF pieces=NIL THEN meeting.start ¬ BasicTime.nullGMT;
selRem ¬ RemPieces[sel, pieces];
IF selRem.Length[] > 0 THEN titleRope ¬ selRem;
EXITS NotHere => titleRope ¬ sel};
IF meeting.start = BasicTime.nullGMT THEN {
startRope ¬ Rope.Cat[timeRope, " ", IF dayRope#NIL THEN dayRope ELSE dateRope];
meeting.start ¬ TimeParse.Parse[startRope, BasicTime.Now[], heuristic, FALSE, TRUE
!TimeParse.ParseError => {
meeting.start ¬ BasicTime.Now[];
CONTINUE}].time;
};
meeting.duration ¬ defaultDuration;
meeting.explanation ¬ IF titleRope.Length[]>0 THEN titleRope ELSE subject;
meeting.more ¬ Rope.Cat[rootFile, " ", messageName];
RemindEdit.SpecifyMeeting[meeting, FALSE];
RETURN};
UpdateDefaults: UserProfile.ProfileChangedProc = {
PROC [reason: UserProfile.ProfileChangeReason]
defaultDuration ¬ UserProfile.Number["Remind.Duration", 60];
};
SkipSpace: PROC [stream: IO.STREAM, punctToo: BOOL] RETURNS [n: INT ¬ 0] ~ {
WHILE (NOT stream.EndOf[]) DO
c: CHAR ~ stream.PeekChar[];
SELECT TRUE FROM
White[c] => NULL;
punctToo AND (c='* OR c=':) => NULL;
ENDCASE => EXIT;
n ¬ n + 1;
IF c#stream.GetChar[] THEN ERROR;
ENDLOOP;
RETURN};
White: PROC [char: CHAR] RETURNS [BOOL]
~ {RETURN [char=' OR char='\t]};
HeaderBreak: PROC [char: CHAR] RETURNS [IO.CharClass] --IO.BreakProc-- ~ {
RETURN [SELECT char FROM
' , '\t => sepr,
'\n, '\r, '\l => break,
ENDCASE => other]};
BodyBreak: PROC [char: CHAR] RETURNS [IO.CharClass] --IO.BreakProc-- ~ {
RETURN [SELECT char FROM
':, '*, ' , '\t => sepr,
'\n, '\r, '\l => break,
ENDCASE => other]};
RemPieces: PROC [from: ROPE, pieces: TimeParse.PiecesType] RETURNS [ROPE] ~ {
WHILE pieces#NIL DO
p: TimeParse.PieceType ~ pieces.first­;
flen: INT ~ from.Length[];
resume, dlen: INT;
pieces ¬ pieces.rest;
FOR resume ¬ p.start+p.len, resume+1 WHILE resume<flen DO
SELECT from.Fetch[resume] FROM
' , '\t, '\n, '\r, '\l => NULL;
', => IF resume#p.start+p.len THEN EXIT;
ENDCASE => EXIT;
ENDLOOP;
dlen ¬ resume - p.start;
from ¬ from.Replace[start: p.start, len: dlen, with: NIL];
FOR pl: TimeParse.PiecesType ¬ pieces, pl.rest WHILE pl#NIL DO
IF pl.first.start > p.start THEN pl.first.start ¬ pl.first.start - dlen;
ENDLOOP;
ENDLOOP;
RETURN[from]};
defaultDuration: INT ¬ 0;
walnutUser: BOOLEAN ¬ FALSE;
peanutUser: BOOLEANFALSE;
switchToNew: BOOLEAN ¬ FALSE;
UserProfile.CallWhenProfileChanges[UpdateDefaults];
TRUSTED {Process.Detach[FORK PostRemindButton[]]};
END.