<<>> <> <> <> <> <> <> <> <> <> <<>> DIRECTORY Basics, BasicTime, CommanderOps USING [DoCommandRope], Convert, Icons USING [IconFlavor, NewIconFromFile], Idle USING [IsIdle], List USING [Sort, CompareProc, LORA, Remove], Menus USING [AppendMenuEntry, ClickProc, CreateEntry, CreateMenu, GetLine, GetNumberOfLines, Menu, SetLine], Process USING [Detach, Pause, SecondsToTicks], Remind, RemindReset, Rope, ViewerClasses USING [Viewer], ViewerOps USING [BlinkViewer, DestroyViewer], ViewerTools USING [MakeNewTextViewer]; RemindDelivery: CEDAR MONITOR IMPORTS Basics, BasicTime, CommanderOps, Convert, Icons, Idle, List, Menus, Process, Remind, Rope, ViewerOps, ViewerTools EXPORTS RemindReset = BEGIN <> <<>> OPEN Remind; ROPE: TYPE ~ Rope.ROPE; Extras: TYPE = REF ExtrasRec; ExtrasRec: TYPE = RECORD[ ringing: BOOLEAN ¬ FALSE, viewer: ViewerClasses.Viewer ¬ NIL]; ActionItem: TYPE ~ REF ActionItemPrivate; ActionItemPrivate: TYPE ~ RECORD [reminder: Reminder, meeting: Meeting]; secsPerMonth: INT ~ 86400*30; secsPerDay: INT ~ 60*60*24; insideMonitorCnt: INT ¬ 0; remindList: List.LORA ¬ NIL; <> <> remindIcon: Icons.IconFlavor ¬ Icons.NewIconFromFile["Remind.icons", 0]; Reset: PUBLIC ENTRY PROC [data: REF ANY] ~ { remindList _ NIL; NoteChange[data]; }; NoteChange: PROC [data: REF ANY] ~ TRUSTED {Process.Detach[FORK ConstructRemindList[]]}; ConstructRemindList: ENTRY PROC = { ENABLE UNWIND => NULL; newMeetList: LIST OF Meeting; newRemindList: List.LORA ¬ NIL; mt: Meeting; <> <> newMeetList ¬ ListMeetings[from: BasicTime.Now[].Update[-secsPerDay], to: BasicTime.Now[].Update[secsPerMonth]]; FOR ls: LIST OF Meeting ¬ newMeetList, ls.rest UNTIL ls = NIL DO FOR rl: RemindList ¬ ls.first.reminders, rl.rest WHILE rl#NIL DO rmd: REF ReminderRecord[alert]; lead: INT; WITH rl.first SELECT FROM x: REF ReminderRecord[alert] => rmd ¬ x; x: REF ReminderRecord[mail] => LOOP; ENDCASE => ERROR; lead ¬ BasicTime.Period[rmd.start, ls.first.start]; FOR rlOld: List.LORA ¬ remindList, rlOld.rest UNTIL rlOld = NIL DO WITH rlOld.first SELECT FROM x: ActionItem => IF x.meeting.uniqID=ls.first.uniqID THEN WITH x.reminder SELECT FROM y: REF ReminderRecord[alert] => IF BasicTime.Period[y.start, x.meeting.start]=lead THEN {rl.first.other ¬ y.other; EXIT}; y: REF ReminderRecord[mail] => NULL; ENDCASE => ERROR; x: Meeting => NULL; ENDCASE => ERROR; REPEAT FINISHED => rl.first.other ¬ NEW[ExtrasRec]; ENDLOOP; newRemindList ¬ CONS[NEW[ActionItemPrivate ¬ [rmd, ls.first]], newRemindList]; ENDLOOP; SELECT ls.first.type FROM command, protectedCmd => newRemindList ¬ CONS[ls.first, newRemindList]; none, meeting, seminar => NULL; ENDCASE => ERROR; ENDLOOP; remindList ¬ List.Sort[newRemindList, CompareUsingStartReminding]; }; CompareUsingStartReminding: List.CompareProc = { <> t1: BasicTime.GMT ~ StartFromRlElt[ref1]; t2: BasicTime.GMT ~ StartFromRlElt[ref2]; RETURN[Basics.CompareInt[0, BasicTime.Period[t1, t2]]]}; StartFromRlElt: PROC [rle: REF ANY] RETURNS [BasicTime.GMT] ~ { WITH rle SELECT FROM x: Meeting => RETURN[x.start]; x: ActionItem => WITH x.reminder SELECT FROM y: REF ReminderRecord[alert] => RETURN[y.start]; y: REF ReminderRecord[mail] => RETURN[y.when]; ENDCASE => ERROR; ENDCASE => ERROR}; <> PostReminders: PROC = { wait: INT; wait ¬ 30; DO IF NOT Idle.IsIdle[] AND insideMonitorCnt < 3 THEN TRUSTED{Process.Detach[FORK DoPostReminders[]]}; Process.Pause[Process.SecondsToTicks[wait]]; ENDLOOP; }; DoPostReminders: ENTRY PROC = { ENABLE UNWIND => NULL; start, stop, now: BasicTime.GMT; ex: Extras; str: ROPE; viewer: ViewerClasses.Viewer; remindMenu: Menus.Menu; mt: Meeting; rmd: REF ReminderRecord[alert]; cur: List.LORA; insideMonitorCnt ¬ insideMonitorCnt + 1; now ¬ BasicTime.Now[]; FOR cur ¬ remindList, cur.rest WHILE cur#NIL DO WITH cur.first SELECT FROM x: Meeting => {mt ¬ x; start ¬ mt.start}; x: ActionItem => { mt ¬ x.meeting; WITH x.reminder SELECT FROM y: REF ReminderRecord[alert] => {rmd ¬ y; start ¬ y.start; stop ¬ y.stop}; y: REF ReminderRecord[mail] => LOOP; ENDCASE => ERROR}; ENDCASE => ERROR; IF BasicTime.Period[now, start] > 0 THEN EXIT; IF cur.first=mt THEN { IF NOT (mt.type=command OR mt.type=protectedCmd) THEN ERROR; DoCommand[mt]; IF mt.repeat = once THEN remindList ¬ List.Remove[cur.first, remindList] ELSE { UpdateMeeting[mt, FirstOccurrenceAfter[mt.repeat, mt.nth, mt.start, now], FALSE]; rmd.other ¬ NEW[ExtrasRec]; }; } ELSE { ex ¬ NARROW[rmd.other]; IF BasicTime.Period[stop, now] > 0 THEN { IF mt.repeat = once THEN remindList ¬ List.Remove[cur.first, remindList] ELSE { newStart: BasicTime.GMT ~ FirstOccurrenceAfter[mt.repeat, mt.nth, start, now]; diff: INT ~ BasicTime.Period[start, newStart]; rmd.start ¬ newStart; rmd.stop ¬ BasicTime.Update[rmd.stop, diff]; rmd.other ¬ NEW[ExtrasRec]; }; } ELSE { <> IF ex.viewer = NIL THEN { <> str ¬ Rope.Cat["Time: ", Convert.RopeFromTime[mt.start], "\nDuration: ", Convert.RopeFromCard[mt.duration], " minutes\n"]; viewer ¬ ViewerTools.MakeNewTextViewer[ info: [ data: IF mt.repeat = once THEN Rope.Cat[str, "\n", mt.explanation] ELSE Rope.Cat[str, "Repeat: ", RopeFromRepetition[mt.repeat], "\n\n", mt.explanation], icon: remindIcon, <> name: mt.explanation] ]; remindMenu ¬ Menus.CreateMenu[]; Menus.AppendMenuEntry[menu: remindMenu, entry: Menus.CreateEntry["Blinker", ToggleBlinking, ex]]; Menus.SetLine[menu: viewer.menu, line: Menus.GetNumberOfLines[viewer.menu], entryList: Menus.GetLine[remindMenu, 0]]; ex.viewer ¬ viewer; ex.ringing ¬ TRUE; TRUSTED{Process.Detach[FORK BlinkIcon[ex, stop]]}; }; }; }; ENDLOOP; remindList ¬ List.Sort[remindList, CompareUsingStartReminding]; insideMonitorCnt ¬ insideMonitorCnt - 1; RETURN}; ToggleBlinking: Menus.ClickProc = { <> ex: Extras ¬ NARROW[clientData]; ex.ringing ¬ NOT ex.ringing; RETURN}; BlinkIcon: PROC[ex: Extras, stop: BasicTime.GMT] = { WHILE BasicTime.Period[BasicTime.Now[], stop] >= 0 DO Process.Pause[Process.SecondsToTicks[3]]; IF ex.ringing THEN ViewerOps.BlinkViewer[ex.viewer]; ENDLOOP; ViewerOps.DestroyViewer[ex.viewer]; }; DoCommand: PROC [mt: Meeting] = { <> out: ROPE; viewer: ViewerClasses.Viewer; [out,] ¬ CommanderOps.DoCommandRope[mt.explanation, NIL, NIL]; viewer ¬ ViewerTools.MakeNewTextViewer[info: [data: out, name: "Calendar command"]]; }; UpdateMeeting: PROC[meeting: Meeting, newTime: BasicTime.GMT, remindersToo: BOOL] = { diff: INT ¬ BasicTime.Period[meeting.start, newTime]; meeting.start ¬ newTime; IF remindersToo THEN FOR rl: RemindList ¬ meeting.reminders, rl.rest WHILE rl#NIL DO WITH rl.first SELECT FROM x: REF ReminderRecord[alert] => { x.start ¬ BasicTime.Update[x.start, diff]; x.stop ¬ BasicTime.Update[x.stop, diff]}; x: REF ReminderRecord[mail] => { x.when ¬ BasicTime.Update[x.when, diff]}; ENDCASE => ERROR; ENDLOOP; RETURN}; <> RegisterChangeProc[NoteChange, NIL, $RemindDelivery, NIL]; ConstructRemindList[]; TRUSTED { Process.Detach[FORK PostReminders[]]; }; END.