<<>> <> <> <> <> <> <> DIRECTORY BasicTime, IO, Menus, Process, RefText, Remind, RemindEdit, Rope, TextNode, TimeParse, TiogaOps, UserProfile, ViewerClasses, ViewerTools, WalnutRegistry, WalnutWindow; <> 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; }; <> <> <> <> <<};>> 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 = { <> 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 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; <> switchToNew: BOOLEAN ¬ FALSE; UserProfile.CallWhenProfileChanges[UpdateDefaults]; TRUSTED {Process.Detach[FORK PostRemindButton[]]}; END.