-- /ivy/binding/calendar/calFormImpl.mesa
-- creating the calendar form on the display and registering actions that are implemented by
-- CalFormImplB.mesa. Also dealing with resetting and erasing the form, once it was created.
-- Last edited by: Binding, August 16, 1984 3:32:57 pm PDT
DIRECTORY
BasicTime USING [ Unpack, Now, GMT, nullGMT, earliestGMT, latestGMT],
Buttons USING [ Button, ReLabel],
Calendar USING [ Date, Years, Months, Days, Weekdays],
CalEnter USING [ EnterEvents],
CalForm USING [ RepetitionDescriptor, FormWidth, Delta, FormHeight, YearTable, MonthTable, DayTable, Row1, Row234, Row5, Row6, Row7, MenuTable, CalForm, CurSelection, TranslateSelection, SyntaxError, CreateCalFormCont, FillDayTable, ExtractTimeSpec],
CalNuts USING [ ResetNutInfo],
CalStorage USING [ protData, Mode],
CalSupport USING [ MakeMenu, GetMonthInfo, M2I, R2I, GetRowCol, ExtractDate, ClearButton, SetButton, GetIconFlavor],
Containers USING [ Container],
Hickory USING [ RepetitionType, ListGroups, ProtectionType, GroupSet, Event, EventTuple, EventList, SameEvent, Reason, RestoreEvents],
Labels USING [ Set, Label],
Menus USING [ MenuProc],
Rope USING [ ROPE],
RopeSets USING [ RopeSetEl, IsValueInSet, InsertValueIntoSet, DeleteValueFromSet, Difference, Union, RopeSet],
Tempus USING [ MakeRope],
ViewerClasses USING [ Viewer, ViewerRec],
ViewerOps USING [ PaintViewer, MoveViewer, SetMenu, DestroyViewer],
ViewerTools USING [ SetContents],
VTables USING [ VTable, GetTableEntry]
;
CalFormImpl: CEDAR MONITOR
LOCKS CalStorage.protData
IMPORTS BasicTime, CalStorage, CalSupport, Hickory, ViewerOps, CalForm, RopeSets, Tempus, ViewerTools, Buttons, CalEnter, Labels, VTables, CalNuts
EXPORTS CalForm
SHARES CalSupport, CalForm, CalEnter, CalNuts
= BEGIN OPEN CalStorage.protData, Calendar, CalForm;
Global Types
Commands: TYPE = { DoIt, Erase, Reset};
Global variables
calForm: PUBLIC CalForm;
curYear: Years ← BasicTime.Unpack[ BasicTime.Now[]].year;
curSelection: PUBLIC CurSelection;
editedEv: PUBLIC REF Hickory.EventTuple ← NIL;
Utility Operations
DestroyViewer: PUBLIC INTERNAL PROC = BEGIN
is called when calViewer was destroyed... Must set all ptr to NIL so GC gets the garbage
IF calForm.container # NIL THEN BEGIN
ViewerOps.DestroyViewer[ calForm.container];
calForm.container ← NIL;
END;
curSelection ← SetSelection[];
editedEv ← NIL;
BEGIN OPEN calForm.row1;
yearTable.table ← NIL;
FOR i: INT IN [ 1..yearTable.cols] DO yearTable.buttons[ i] ← NIL; ENDLOOP;
monthTable.table ← NIL;
FOR i: INT IN [ 1..monthTable.cols] DO yearTable.buttons[ i] ← NIL; ENDLOOP;
dayTable.table ← NIL;
FOR l: INT IN [ 0.. dayTable.cols-1] DO
FOR i: INT IN [ 0.. dayTable.rows-2] DO -- title row
dayTable.buttons[ i] [ l] ← NIL;
ENDLOOP;
ENDLOOP;
END; -- row1
calForm.row2.table ← NIL; calForm.row2.txtViewer ← NIL;
calForm.row3.table ← NIL; calForm.row3.txtViewer ← NIL;
calForm.row4.table ← NIL; calForm.row4.txtViewer ← NIL;
calForm.row5.table ← NIL; calForm.row5.txtViewer ← NIL;
BEGIN OPEN calForm.row5.menuTable;
table ← NIL;
FOR i: INT IN [ 0..cols-1) DO buttons [i] ← NIL; ENDLOOP;
END;
calForm.row6.table ← NIL; calForm.row6.txtViewer ← NIL;
calForm.row6.groups ← [ NIL, NIL]; calForm.row6.groupHead ← NIL;
BEGIN OPEN calForm.row6.menuTable;
table ← NIL;
FOR i: INT IN [ 0..cols-1) DO buttons [i] ← NIL; ENDLOOP;
END;
calForm.row7.table ← NIL;
calForm.row7.txtViewers[ 1] ← NIL; calForm.row7.txtViewers[ 2] ← NIL;
calForm.row8.table ← NIL;
END; -- DestroyViewer
SetSelection: PROCEDURE [ ev: REF Hickory.EventTuple ← NIL, gr: Hickory.GroupSet ← [ NIL, NIL]] RETURNS [ sel: CurSelection] = BEGIN
to set the selection and also protData.curDate
BEGIN OPEN sel;
IF ev # NIL THEN BEGIN
date ← CalSupport.ExtractDate[ ev.EventTime];
curDate ← date;
repetition.RepeatType ← ev.RepeatType;
repetition.RepeatTime ← ev.RepeatTime;
repetition.RepeatUntil ← ev.RepeatUntil;
groups ← gr;
protection ← ev.Protection;
reminder ← ev.Remind;
END
ELSE BEGIN
date ← CalSupport.ExtractDate[ BasicTime.Now[]];
curDate ← date;
repetition.RepeatType ← None;
repetition.RepeatUntil ← BasicTime.nullGMT;
repetition.RepeatTime ← NIL;
protection ← Private;
reminder ← TRUE;
groups ← gr;
END;
END; -- OPEN
RETURN[ sel];
END; -- SetSelection
GetFirstOccurrence: PROCEDURE [ key: Hickory.Event] RETURNS [ tuple: REF Hickory.EventTuple] = BEGIN
when we are editing a repeated event, make sure we are editing the first occurrence of it
otherwise confusion results at later stages. Note that this is a somewhat arbitrary
semantics..
evl: Hickory.EventList;
evl ← Hickory.RestoreEvents[ from: BasicTime.earliestGMT, to: BasicTime.latestGMT, key: key];
IF evl.rest # NIL THEN ERROR
ELSE tuple ← NEW[ Hickory.EventTuple ← evl.first];
RETURN[ tuple];
END; -- GetFirstOccurrence
Building the form on the screen...
CreateCalForm: PUBLIC INTERNAL PROC [ mode: CalStorage.Mode, ev: REF Hickory.EventTuple ← NIL] = BEGIN
here we start our effort to build the form on the screen. IF ev = NIL we start with new
event, else we are editing one.
oldSelection: CurSelection ← curSelection;
iconFile: Rope.ROPE ← "Calendar.icons";
curMode ← mode;
CalNuts.ResetNutInfo[ mode: EnterEvent];
calViewer.icon ← CalSupport.GetIconFlavor[ EnterEvent];
IF ev = NIL AND editedEv # NIL THEN BEGIN
curSelection ← SetSelection[];
editedEv ← NIL;
END
ELSE IF ev # NIL THEN BEGIN -- editing an existing event
IF ev.RepeatType # None THEN ev ← GetFirstOccurrence[ ev.Key];
curSelection ← SetSelection[ ev, Hickory.ListGroups[ ev.Key]];
editedEv ← ev;
END;
AdjustCaption[ calViewer];
ViewerOps.SetMenu[ viewer: calViewer, menu: CalSupport.MakeMenu[ LIST[ "DoIt", "Erase", "Reset"], LIST[ DoIt, Erase, Reset], LIST[ NIL, NIL, NIL]], paint: FALSE];
DrawCalForm[ calViewer, editedEv, oldSelection];
END;
AdjustCaption: INTERNAL PROCEDURE [ viewer: ViewerClasses.Viewer] = BEGIN
here we change the title of the viewer a bit
IF curMode = EnterEvent THEN viewer.name ← "Calendar: entering/editing event"
ELSE viewer.name ← "Calendar: querying data base";
END; -- AdjustCaption
DrawCalForm: INTERNAL PROCEDURE [ myParent: ViewerClasses.Viewer, ev: REF Hickory.EventTuple, oldSelection: CurSelection] = BEGIN
here we draw the form on the screen. Either we build it from scratch or we fill in
the viewers that are already existing.
IF calForm.container = NIL THEN CreateCalFormCont[ myParent, ev]
ELSE BEGIN
UpdateDateTables[ oldSelection.date, curSelection.date];
UpdateProtectionButton[ oldSelection.protection, curSelection.protection];
UpdateReminderButton[ oldSelection.reminder, curSelection.reminder];
IF ev # NIL THEN BEGIN -- editing an existing event
EraseForm[ oldSelection, FALSE];
RefillForm[ ev, curSelection];
END
ELSE EraseForm[ oldSelection, TRUE];
ViewerOps.MoveViewer[ viewer: calForm.container, x: calForm.container.wx-Delta, y: calForm.container.wy-Delta, w: calForm.container.ww, h: calForm.container.wh, paint: TRUE];
END;
END; -- DrawCalForm
Menu Procedures
Reset: Menus.MenuProc -- [parent: REF ANY, clientData: REF ANY ← NIL, mouseButton: MouseButton ← red, shift, control: BOOL ← FALSE] -- = BEGIN
DoCmd[ Reset];
END;
Erase: Menus.MenuProc -- [parent: REF ANY, clientData: REF ANY ← NIL, mouseButton: MouseButton ← red, shift, control: BOOL ← FALSE] -- = BEGIN
DoCmd[ Erase];
END;
DoIt: Menus.MenuProc -- [parent: REF ANY, clientData: REF ANY ← NIL, mouseButton: MouseButton ← red, shift, control: BOOL ← FALSE] -- = BEGIN
DoCmd[ DoIt];
END;
DoCmd: ENTRY PROCEDURE [ cmd: Commands] = BEGIN
ENABLE UNWIND => NULL;
SELECT cmd FROM
Reset => ResetForm[ Reset];
Erase => ResetForm[ Erase];
DoIt => TranslateAndEnter[];
ENDCASE;
END; -- DoCmd
TranslateAndEnter: INTERNAL PROCEDURE = BEGIN
to translate the form and enter it into hickory...
groups: Hickory.GroupSet ← [ NIL, NIL];
evl: Hickory.EventList;
ok: BOOLEANTRUE;
[ evl, groups] ← TranslateSelection[ editedEv ! SyntaxError => { ok ← FALSE; CONTINUE }];
IF ok THEN BEGIN
CalEnter.EnterEvents[ editedEv, evl, groups];
END;
END; -- TranslateAndEnter
ResetForm: INTERNAL PROCEDURE [ cmd: Commands] = BEGIN
to reset current selection and clear form if cmd = Erase. If reset and we are editing
an existing event, reinitialize form.
oldSelection: CurSelection ← curSelection;
IF cmd = Erase OR editedEv = NIL THEN BEGIN
EraseForm[ curSelection, TRUE];
curSelection ← SetSelection[];
END
ELSE BEGIN -- Reset to edited ev
EraseForm[ curSelection, FALSE];
curSelection ← SetSelection[ editedEv, Hickory.ListGroups[ editedEv^.Key]];
RefillForm[ editedEv, curSelection];
END;
UpdateDateTables[ oldSelection.date, curSelection.date];
UpdateProtectionButton[ oldSelection.protection, curSelection.protection];
UpdateReminderButton[oldSelection.reminder, curSelection.reminder];
END; -- ResetForm
EraseForm: INTERNAL PROCEDURE [ selection: CurSelection, all: BOOLEANTRUE] = BEGIN
to undo the current contents.
OPEN calForm;
IF all THEN BEGIN
ViewerTools.SetContents[ row2.txtViewer, "", TRUE];
ViewerTools.SetContents[ row3.txtViewer, "", TRUE];
ViewerTools.SetContents[ row4.txtViewer, "", TRUE];
ViewerTools.SetContents[ row5.txtViewer, "", TRUE];
ViewerTools.SetContents[ row6.txtViewer, "", TRUE];
ViewerTools.SetContents[ row7.txtViewers[1], "", TRUE];
ViewerTools.SetContents[ row7.txtViewers[2], "", TRUE];
END;
EraseRepetitionSelection[ selection.repetition.RepeatType];
EraseSelectedGroups[];
END; -- EraseForm
EraseRepetitionSelection: INTERNAL PROC [ repType: Hickory.RepetitionType] = BEGIN
OPEN calForm.row5;
IF repType IN [ Hourly..Yearly] THEN
CalSupport.ClearButton[menuTable.buttons[ CalSupport.R2I[ repType]-1]];
END; -- EraseRepetitionSelection
EraseSelectedGroups: INTERNAL PROCEDURE = BEGIN
to graphically erase selected groups...
OPEN calForm.row6;
FOR i: INT IN [ 0..menuTable.cols) DO
CalSupport.ClearButton[menuTable.buttons[ i]];
ENDLOOP;
END; -- EraseSelectedGroups
RefillForm: INTERNAL PROCEDURE [ evPtr: REF Hickory.EventTuple, curSelection: CurSelection] = BEGIN
to refill the form without drawing it from scratch.
OPEN calForm;
tr: Rope.ROPE;
ev: Hickory.EventTuple;
IF evPtr = NIL THEN RETURN;
ev ← evPtr^;
tr ← ExtractTimeSpec[ ev.EventTime, ev.Duration];
ViewerTools.SetContents[ row2.txtViewer, tr, TRUE];
ViewerTools.SetContents[ row3.txtViewer, ev.Text, TRUE];
ViewerTools.SetContents[ row4.txtViewer, ev.Place, TRUE];
ViewerTools.SetContents[ row5.txtViewer, ev.RepeatTime, TRUE];
RefillRepeatMenu[ ev.RepeatType];
RefillGroupMenu[];
IF ev.RepeatUntil # BasicTime.nullGMT THEN
ViewerTools.SetContents[ row7.txtViewers[1], Tempus.MakeRope[ time: ev.RepeatUntil], TRUE]
ELSE ViewerTools.SetContents[ row7.txtViewers[1], "", TRUE];
IF ev.KeepUntil # BasicTime.nullGMT THEN
ViewerTools.SetContents[ row7.txtViewers[2], Tempus.MakeRope[ time: ev.KeepUntil], TRUE]
ELSE ViewerTools.SetContents[ row7.txtViewers[2], "", TRUE];
END; -- RefillForm
UpdateDateTables: INTERNAL PROCEDURE [ oldDate, newDate: Date] = BEGIN
to update the year, month and day tables
OPEN calForm.row1;
oldRow, newRow, oldCol, newCol: CARDINAL;
IF oldDate = newDate THEN RETURN;
IF oldDate.Year # newDate.Year THEN BEGIN
OPEN yearTable;
oldRow ← oldDate.Year - curYear + ( rows-1)/2;
newRow ← newDate.Year - curYear + ( rows-1)/2;
CalSupport.ClearButton[ buttons[ oldRow]];
CalSupport.SetButton[ buttons[ newRow]];
END; -- yearTable
IF oldDate.Month # newDate.Month THEN BEGIN
OPEN monthTable;
oldRow ← CalSupport.M2I[ oldDate.Month] + 1;
newRow ← CalSupport.M2I[ newDate.Month] + 1;
CalSupport.ClearButton[ buttons[ oldRow]];
CalSupport.SetButton[ buttons[ newRow]];
END; -- monthTable
IF oldDate.Month # newDate.Month OR oldDate.Year # newDate.Year THEN BEGIN
FillDayTable[ dayTable.table, newDate];
ViewerOps.PaintViewer[ dayTable.table, all];
END
ELSE IF oldDate.Day # newDate.Day THEN BEGIN -- only need to change two buttons
OPEN dayTable;
firstDay: Weekdays;
nbrOfDays: Days;
[ nbrOfDays, firstDay] ← CalSupport.GetMonthInfo[ oldDate.Month, oldDate.Year];
[ oldRow, oldCol] ← CalSupport.GetRowCol[ oldDate.Day, firstDay];
[ newRow, newCol] ← CalSupport.GetRowCol[ newDate.Day, firstDay];
CalSupport.ClearButton[ buttons[ oldRow] [ oldCol]];
CalSupport.SetButton[ buttons[ newRow] [ newCol]];
END; -- daytable
END; -- UpdateDateTables
UpdateProtectionButton: INTERNAL PROC [ oldProt, newProt: Hickory.ProtectionType] = BEGIN
OPEN calForm.row8;
label: Labels.Label;
IF oldProt = newProt THEN RETURN;
label ← VTables.GetTableEntry[ table: calForm.row8.table, row: 0, column: 1];
IF newProt = Private THEN Labels.Set[ label, " Private"]
ELSE Labels.Set[ label, " Private"];
END; -- UpdateProtectionButton
UpdateReminderButton: INTERNAL PROC [ oldRem, newRem: BOOLEAN] = BEGIN
OPEN calForm.row8;
label: Labels.Label;
IF oldRem = newRem THEN RETURN;
label ← VTables.GetTableEntry[ table: calForm.row8.table, row: 0, column: 3];
IF newRem THEN Labels.Set[ label, " Yes"]
ELSE Labels.Set[ label, " No"];
END; -- UpdateReminderButton
RefillRepeatMenu: INTERNAL PROCEDURE [ repType: Hickory.RepetitionType] = BEGIN
to adjust the repetiton menu when we are editing an old event.
IF repType IN [ Hourly..Yearly] THEN
CalSupport.SetButton[ calForm.row5.menuTable.buttons[ CalSupport.R2I[ repType]-1]];
END; -- RefillRepeatMenu
RefillGroupMenu: INTERNAL PROCEDURE = BEGIN
to refill the group menu. Assume that calForm.row6.groups knows about all
the groups in hickory. curSelection contains groups currently selected groups.
OPEN calForm.row6;
gr: REF RopeSets.RopeSetEl;
IF calForm.container = NIL OR calForm.container.destroyed THEN RETURN;
IF txtViewer = NIL THEN RETURN; -- row6 not initialized
ViewerTools.SetContents[ txtViewer, "", TRUE]; -- all known hickory groups are in menu
groupHead ← groups.Head;
gr ← groupHead;
FOR i: INT IN [ 0..menuTable.cols) DO
IF gr # NIL THEN BEGIN
Buttons.ReLabel[ menuTable.buttons[ i], gr.Value, FALSE];
IF RopeSets.IsValueInSet[ gr.Value, curSelection.groups] THEN
CalSupport.SetButton[ menuTable.buttons[ i]]
ELSE CalSupport.ClearButton[ menuTable.buttons[ i]];
gr ← gr.Next;
END
ELSE BEGIN
Buttons.ReLabel[ menuTable.buttons[ i], "", FALSE]; -- erase
CalSupport.ClearButton[ menuTable.buttons[ i]];
END;
ENDLOOP;
END; -- RefillGroupMenu
in the case hickory is modified we fix the calendar form here
HickoryChange: PUBLIC INTERNAL PROCEDURE [ reason: Hickory.Reason, ev: Hickory.EventTuple, data: RopeSets.RopeSet] = BEGIN
here we deal with changes to hickory.
SELECT reason FROM
Destroy => IF editedEv # NIL AND Hickory.SameEvent[ ev.Key, editedEv.Key] THEN
editedEv.Key ← NIL;
NewEvent => NULL;
Edit => IF editedEv # NIL AND Hickory.SameEvent[ ev.Key, editedEv.Key] THEN
editedEv ← NEW [ Hickory.EventTuple ← ev];
NewGroup => IF data.Head # NIL AND NOT RopeSets.IsValueInSet[ data.Head^.Value, calForm.row6.groups] THEN BEGIN
calForm.row6.groups ← RopeSets.InsertValueIntoSet[ data.Head^.Value, calForm.row6.groups];
RefillGroupMenu[];
END;
GroupDestroy => IF data.Head # NIL AND RopeSets.IsValueInSet[ data.Head^.Value, calForm.row6.groups] AND NOT RopeSets.IsValueInSet [ data.Head^.Value, curSelection.groups] THEN BEGIN
calForm.row6.groups ← RopeSets.DeleteValueFromSet[ data.Head^.Value, calForm.row6.groups];
RefillGroupMenu[];
END;
InsertionToGroup => IF editedEv # NIL AND Hickory.SameEvent[ ev.Key, editedEv^.Key] THEN BEGIN
curSelection.groups ← RopeSets.Union[ data, curSelection.groups];
RefillGroupMenu[];
END;
RemoveFromGroup => IF editedEv # NIL AND Hickory.SameEvent[ ev.Key, editedEv^.Key] THEN BEGIN
curSelection.groups ← RopeSets.Difference[ curSelection.groups, data];
RefillGroupMenu[];
END;
ENDCASE;
END; -- HickoryChange
Initially
curSelection ← SetSelection[];
END.