RemindDelivery.mesa
Copyright Ó 1990, 1992 by Xerox Corporation. All rights reserved.
David Goldberg November 13, 1989 7:59:49 pm PST
Peter B. Kessler, January 17, 1990 9:11:14 pm PST
Last changed by Pavel on February 12, 1990 5:40:45 pm PST
Kenneth A. Pier, August 11, 1992 5:46 pm PDT
Theimer, September 17, 1990 11:22 pm PDT
Last tweaked by Mike Spreitzer April 30, 1992 11:47 am PDT
Willie-s, May 5, 1992 2:14 pm PDT
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
Implements the remind interface, and also contains the PostReminders proc, which runs in the background and pops up reminders
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;
Each elt is either a (protected)command Meeting or an ActionItem.
The list consists of all (protected)command Meetings, and an ActionItem for every reminder.
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[], to: BasicTime.Now[].Update[secsPerMonth]];
in an attempt to get reminders processed when CalendarTool is first started and a reminder is still active, I have changed ConstructRemindList to as ListMeetings for meetings within one day previous to Now. KAP. August 11, 1992.
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 = {
PROC [ref1: REF ANY, ref2: REF ANY] RETURNS [Comparison]
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};
works entirely off of remindList: never looks in LoganBerry. Calls an ENTRY proc to do the real work
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 {
XXX: put up or blink thing
IF ex.viewer = NIL THEN {
create remind window
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,
label: ShortTime[entry.time],
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 = {
PROC [parent: Viewer, clientData: REF ANYNIL,
mouseButton: MouseButton ← red, shift, control: BOOLFALSE];
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] = {
cmd: Commander.Handle ← CommanderOps.CreateFromStreams[];
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};
Want to open the database when you rollback (no reason database will be the same as when you made the checkpoint). To accomplish this, make sure that database is closed in the checkpoint file.
RegisterChangeProc[NoteChange, NIL, $RemindDelivery, NIL];
ConstructRemindList[];
TRUSTED {
Process.Detach[FORK PostReminders[]];
};
END.