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 ANY ← NIL,
mouseButton: MouseButton ← red, shift, control: BOOL ← FALSE];
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.