/ivy/binding/calendar/calBrowserImpl.mesa
Last edited by: Binding, August 16, 1984 3:29:57 pm PDT
DIRECTORY
Buttons USING [ Button, SetDisplayStyle],
BasicTime USING [ GMT, Unpacked, Unpack, DayOfWeek, MonthOfYear, Now, Update],
Calendar USING [ Days, Weekdays, Months, Years, Date, ZoomLevel],
CalBrowser,
CalForm USING [ CreateCalForm],
CalNuts USING [ ResetNutInfo],
CalStorage USING [ protData, GetEventsOfDay, StoreViewer, RetrieveViewer, Mode, FindEvent],
CalSupport USING [ MakeMenu, DisplayMsg, PreviousDay, NextDay, GetMonthInfo, GetMonthName, FormWidth, FormHeight, CharWidth, RowHeight, I2M, GetRowCol, CreateTextViewer, ExtractDate, M2I, GetDayName, CreateButton, ClearButton, GetIconFlavor],
CalWalnut USING [ DisplayMessage],
Containers USING [ Create, Container],
Hickory USING [ EventList, Event, Reason, EventTuple, ForgetEvent],
IO USING [ int, rope, PutFR],
Labels USING [ Label, SetDisplayStyle, Create],
Menus USING [ MenuProc],
Rope USING [ ROPE, Concat, Length, FromChar, Substr, Equal],
RopeSets USING [ RopeSet],
ViewerClasses USING [ Viewer, ViewerRec],
ViewerOps USING [ DestroyViewer, PaintViewer, MoveViewer, SetMenu],
VTables USING [ VTable, Create, SetTableEntry, Install, NullBorder, FullBorder, Border, GetTableEntry],
ViewerTools USING [ SetContents]
;
CalBrowserImpl: CEDAR MONITOR
LOCKS CalStorage.protData
IMPORTS BasicTime, CalStorage, CalSupport, IO, Rope, ViewerOps, VTables, CalForm, Containers, Buttons, Labels, ViewerTools, Hickory, CalWalnut, CalNuts
EXPORTS CalBrowser
SHARES CalSupport, CalForm, CalStorage, CalWalnut, CalNuts
= BEGIN OPEN Calendar, CalStorage.protData;
Constants
RowHeight: CARDINAL ← CalSupport.RowHeight;
ButtonHeight: CARDINAL ← RowHeight - 4;
CharWidth: CARDINAL = CalSupport.CharWidth;
FormHeight: CARDINAL ← CalSupport.FormHeight;
FormWidth: CARDINAL ← CalSupport.FormWidth;
Delta: CARDINAL = 10000; -- to move viewer into outer space
Local types
EventList: TYPE = Hickory.EventList;
Commands: TYPE = { Enter, Query, Edit, Forget, ShowMsg};
EventButton: TYPE = RECORD [
index: CARDINAL ← 0,
ev: Hickory.Event ← NIL,
button: Buttons.Button ← NIL
];
EventButtonList: TYPE = LIST OF EventButton;
Data structure to describe the different viewers
DayGrid: TYPE = RECORD [
Format: Rope.ROPE ← " %13g %-3g %-55g %-10g",
TimeWidth: CARDINAL ← 13, -- must match with above rope...!!
TextWidth: CARDINAL ← 55, -- units are nbr of chars
PlaceWidth: CARDINAL ← 10,
TimeFormat: Rope.ROPE ← "%02g:%02g — %02g:%02g",
TimeFormat2: Rope.ROPE ← " %02g:%02g ",
buttons: EventButtonList ← NIL
];
MonthGrid: TYPE = RECORD [
Rows: CARDINAL ← 6,
Cols: CARDINAL ← 7,
DayFieldWidth: CARDINAL ← 0, -- how wide is a day field in table ( pixels)
DayViewers: ARRAY Days OF VTables.VTable,
DateFormat: Rope.ROPE ← "%3g %02g" -- for date of day
];
YearGrid: TYPE = RECORD [
Rows: CARDINAL ← 8, -- 4 month tables and 4 title tables...
Cols: CARDINAL ← 3, -- 3 months in a row
MonthViewers: ARRAY Months OF VTables.VTable,
SmallRows: CARDINAL ← 4 + 7*4,
DateFormat: Rope.ROPE ← "%02g" -- for date of day
];
Global module variables
dayGrid: DayGrid;
monthGrid: MonthGrid;
yearGrid: YearGrid;
zoomLevel: ZoomLevel;
cont: ViewerClasses.Viewer; -- the container for the browser view
selectedEvent: REF Hickory.EventTuple ← NIL;
selectedButton: REF EventButton;
Support stuff
FindEventButton: PROCEDURE [ list: EventButtonList, index: CARDINAL] RETURNS [ rec: EventButton] = BEGIN
IF list = NIL THEN ERROR;
WHILE list # NIL DO
IF list.first.index = index THEN RETURN[ list.first];
list ← list.rest;
ENDLOOP;
END; -- FindEventButton
DeleteEventButton: PROCEDURE [ list: EventButtonList, index: CARDINAL] RETURNS [ truncatedList: EventButtonList] = BEGIN
here we delete the record...
prev, cur: EventButtonList;
prev ← NIL; cur ← list;
WHILE cur # NIL AND cur.first.index # index DO
prev ← cur; cur ← cur.rest;
ENDLOOP;
IF prev = NIL THEN BEGIN
list ← cur.rest; cur.rest ← NIL;
END
ELSE IF cur # NIL THEN BEGIN
prev.rest ← cur.rest; cur.rest ← NIL;
END
ELSE ERROR;
RETURN[ list];
END; -- DeleteEventButton
CreateLabel: PROCEDURE [ title: Rope.ROPE, myParent: ViewerClasses.Viewer, hi, wi: CARDINAL, xOff, yOff: CARDINAL ← 0, displayStyle: ATOM ← $BlackOnWhite] RETURNS [ label: Labels.Label] = BEGIN
to create a button...
view: ViewerClasses.ViewerRec;
BEGIN OPEN view;
name ← title;
parent ← myParent;
wx ← xOff; wy ← yOff;
ww ← wi;
wh ← hi;
iconic ← FALSE;
scrollable ← FALSE;
border ← FALSE;
END; -- OPEN
label ← Labels.Create[ info: view, paint: FALSE];
Labels.SetDisplayStyle[ label, displayStyle, FALSE];
RETURN[ label];
END; -- CreateLabel
CreateContainer: PROCEDURE [ myParent: ViewerClasses.Viewer, scroll: BOOLEANFALSE] RETURNS [ cont: ViewerClasses.Viewer] = BEGIN
here we build the container of the calViewer, while in Browse mode.
view: ViewerClasses.ViewerRec;
BEGIN OPEN view;
ww ← FormWidth;
wh ← FormHeight;
scrollable ← scroll;
iconic ← FALSE;
border ← TRUE;
parent ← myParent; -- calViewer...
END; -- OPEN
cont ← Containers.Create[ view, FALSE];
RETURN[ cont];
END; -- CreateContainer
ResetIcon: PROCEDURE [ newMode: CalStorage.Mode, date: Date, oldZoom, newZoom: ZoomLevel] = BEGIN
here we figure out how to change the icon of calViewer...
OPEN CalSupport;
IF curMode # Browse THEN -- we just entered browsing mode..
calViewer.icon ← CalSupport.GetIconFlavor[ Browse, newZoom, date.Day]
ELSE BEGIN -- we were in browsing mode..
IF oldZoom = newZoom AND newZoom = Day THEN
calViewer.icon ← CalSupport.GetIconFlavor[ Browse, newZoom, date.Day]
ELSE IF oldZoom # newZoom THEN
calViewer.icon ← CalSupport.GetIconFlavor[ Browse, newZoom, date.Day]
END;
END; -- ResetIcon
Menu prcodedures ( are EXTERNAL)
Enter: Menus.MenuProc -- [parent: REF ANY, clientData: REF ANY ← NIL, mouseButton: MouseButton ← red, shift, control: BOOL ← FALSE] -- = BEGIN
to enter CalForm...
IF mouseButton = red THEN DoCmd[ Enter];
END; -- Reset
ShowMsg: Menus.MenuProc -- [parent: REF ANY, clientData: REF ANY ← NIL, mouseButton: MouseButton ← red, shift, control: BOOL ← FALSE] -- = BEGIN
to enter CalForm...
IF mouseButton = red THEN DoCmd[ ShowMsg];
END; -- Reset
Edit: Menus.MenuProc -- [parent: REF ANY, clientData: REF ANY ← NIL, mouseButton: MouseButton ← red, shift, control: BOOL ← FALSE] -- = BEGIN
to edit a specific event through CalForm...
IF mouseButton = red THEN DoCmd[ Edit]
END; -- Reset
Forget: Menus.MenuProc -- [parent: REF ANY, clientData: REF ANY ← NIL, mouseButton: MouseButton ← red, shift, control: BOOL ← FALSE] -- = BEGIN
to forget a specific event ...
IF mouseButton = red THEN DoCmd[ Forget];
END; -- Reset
Print: Menus.MenuProc -- [parent: REF ANY, clientData: REF ANY ← NIL, mouseButton: MouseButton ← red, shift, control: BOOL ← FALSE] -- = BEGIN
to print the current contents of the calendar viewer onto hard press
IF mouseButton = red THEN CalSupport.DisplayMsg[ "Print: Not yet implemented!"];
END; -- Print
Next: Menus.MenuProc -- [parent: REF ANY, clientData: REF ANY ← NIL, mouseButton: MouseButton ← red, shift, control: BOOL ← FALSE] -- = BEGIN
to move to next day, month or year
IF mouseButton = red THEN ShowNextOrPrevious[ TRUE];
END; -- Next
Previous: Menus.MenuProc -- [parent: REF ANY, clientData: REF ANY ← NIL, mouseButton: MouseButton ← red, shift, control: BOOL ← FALSE] -- = BEGIN
to move to previous day, month or year
IF mouseButton = red THEN ShowNextOrPrevious[ FALSE];
END; -- Previous
ZoomOut: Menus.MenuProc -- [parent: REF ANY, clientData: REF ANY ← NIL, mouseButton: MouseButton ← red, shift, control: BOOL ← FALSE] -- = BEGIN
to either pass from day -> month or month -> year
zoom: REF ZoomLevel = NARROW[ clientData];
IF mouseButton = red THEN DoZoomOut[ zoom^]; -- ISTYPE[ clientData, ZoomLevel]....
END; -- ZoomOut
ZoomIn: Menus.MenuProc -- [parent: REF ANY, clientData: REF ANY ← NIL, mouseButton: MouseButton ← red, shift, control: BOOL ← FALSE] -- = BEGIN
to either pass from month -> day or year -> month
IF mouseButton = yellow THEN DoZoomIn[ clientData]; -- can't NARROW here because it's either Days or Months
END; -- ZoomIn
SelectEvent: Menus.MenuProc -- [parent: REF ANY, clientData: REF ANY ← NIL, mouseButton: MouseButton ← red, shift, control: BOOL ← FALSE] -- = BEGIN
To select an event from day viewer
index: REF CARDINALNARROW[ clientData];
IF mouseButton = red THEN DoSelection[ index^];
END; -- SelectEvent
Entry points for menu procedures and supporters ( ENTRY or INTERNAL)
DoSelection: ENTRY PROC [ butIndex: CARDINAL] = BEGIN
to record the user's mouse click on a particular event
ENABLE UNWIND => NULL;
but: EventButton;
but ← FindEventButton[ list: dayGrid.buttons, index: butIndex];
IF selectedButton # NIL AND Rope.Equal[ but.ev, selectedButton^.ev] THEN BEGIN
undo selection
Buttons.SetDisplayStyle[ but.button, $BlackOnWhite, TRUE];
selectedEvent ← NIL; selectedButton ← NIL;
END
ELSE BEGIN
IF but.button # NIL THEN Buttons.SetDisplayStyle[ but.button, $BlackOnGrey, TRUE];
IF selectedButton # NIL THEN BEGIN
but: EventButton ← FindEventButton[ list: dayGrid.buttons, index: selectedButton.index];
IF but.button # NIL THEN Buttons.SetDisplayStyle[ but.button, $BlackOnWhite, TRUE];
END;
selectedButton ← NEW[ EventButton ← but];
selectedEvent ← CalStorage.FindEvent[ curDate, selectedButton.ev];
END;
END; --DoSelection
DoCmd: ENTRY PROCEDURE [ cmd: Commands] = BEGIN
when enter button clicked... here we leave the Browser module...
ENABLE UNWIND => NULL;
SELECT cmd FROM
Enter => BEGIN
HideContainer[]; -- destroying day viewer, saving month & year viewers
CalForm.CreateCalForm[ mode: EnterEvent, ev: NIL];
END;
Edit => BEGIN
IF selectedEvent = NIL THEN CalSupport.DisplayMsg[ "No event selected!"]
ELSE BEGIN
HideContainer[]; -- destroying day viewer, saving month & year viewers
CalForm.CreateCalForm[ mode: EnterEvent, ev: selectedEvent];
END;
END;
Forget => BEGIN
IF selectedEvent = NIL THEN CalSupport.DisplayMsg[ "No event selected!"]
ELSE BEGIN
Hickory.ForgetEvent[ selectedEvent^.Key, TRUE];
END
END;
ShowMsg => BEGIN
IF selectedEvent = NIL THEN CalSupport.DisplayMsg[ "No event selected!"]
ELSE BEGIN
CalWalnut.DisplayMessage[ selectedEvent.Message];
CalSupport.ClearButton[ selectedButton.button];
END;
END;
ENDCASE;
selectedEvent ← NIL;
END; -- DoEnterCmd
HideContainer: INTERNAL PROCEDURE = BEGIN
when we change modes ( Reset..) we must hide away the cont or destroy it.
IF cont # NIL AND zoomLevel # Day THEN BEGIN
ViewerOps.MoveViewer[ viewer: cont, x: cont.wx+Delta, y: cont.wy+Delta, w: cont.ww, h: cont.wh, paint: FALSE];
cont ← NIL;
END
ELSE BEGIN
ViewerOps.DestroyViewer[ viewer: cont, paint: FALSE];
dayGrid.buttons ← NIL;
END;
END;
ShowNextOrPrevious: ENTRY PROCEDURE [ next: BOOLEAN] = BEGIN
either shows next or previous [ day | month | year ]...
ENABLE UNWIND => NULL;
newDate: Date ← curDate;
IF next THEN BEGIN -- have to move forward in time...
SELECT zoomLevel FROM
Day => newDate ← CalSupport.NextDay[ newDate];
Month =>
IF newDate.Month = December THEN BEGIN
newDate.Month ← January; newDate.Year ← SUCC[ newDate.Year]
END
ELSE newDate.Month ← SUCC[ newDate.Month];
Year => newDate.Year ← SUCC[ newDate.Year];
ENDCASE;
END
ELSE BEGIN -- next = FALSE: have to go backward in time
SELECT zoomLevel FROM
Day => newDate ← CalSupport.PreviousDay[ newDate];
Month =>
IF newDate.Month # January THEN newDate.Month ← PRED[ newDate.Month]
ELSE BEGIN
newDate.Month ← December; newDate.Year ← PRED[ newDate.Year]
END;
Year => newDate.Year ← PRED[ newDate.Year];
ENDCASE;
END;
curDate ← newDate;
DisplayEvents[ zoomLevel, curDate];
END; -- ShowNextOrPrevious
DoZoomOut: ENTRY PROCEDURE [ newZoom: ZoomLevel] = BEGIN
ENABLE UNWIND => NULL;
DisplayEvents[ newZoom, curDate];
END; -- DoZoomOut
DoZoomIn: ENTRY PROCEDURE [ clientData: REF ANY] = BEGIN
zoom from year -> month or month -> day, depending on the clientData
ENABLE UNWIND => NULL;
IF ISTYPE[ clientData, REF Days] THEN BEGIN -- month -> day
rDay: REF Days = NARROW[ clientData];
curDate.Day ← rDay^;
DisplayEvents[ Day, curDate];
END
ELSE IF ISTYPE[ clientData, REF Months] THEN BEGIN -- year -> month
rMonth: REF Months = NARROW[ clientData];
curDate.Month ← rMonth^;
DisplayEvents[ Month, curDate];
END
ELSE ERROR;
END; -- DoZoomIn
DisplayEvents and its numerous supporters ( all INTERNAL to monitor)
DisplayEvents: PUBLIC INTERNAL PROCEDURE [ zoom: ZoomLevel, date: Calendar.Date] = BEGIN
this is the big one: we attempt to create a nice viewer depending on the zooming
level and fill it with info from the events cached in calendar
curMode ← Browse;
ResetIcon[ curMode, date, zoomLevel, zoom];
CalNuts.ResetNutInfo[ zoom, Browse];
curDate ← date;
selectedEvent ← NIL;
AdjustCaption[ zoom, date];
SetMenu[ zoom, calViewer];
cont ← RetrieveCalBrowser[ zoom, zoomLevel, date, cont];
zoomLevel ← zoom;
IF cont = NIL THEN BEGIN
SELECT zoom FROM
Day => BEGIN
cont ← CreateContainer[ calViewer, TRUE];
DisplayDay[ cont, date];
END;
Month => BEGIN
cont ← CreateContainer[ myParent: calViewer];
DisplayMonth[ cont, date];
CalStorage.StoreViewer[ date, Month, cont];
END;
Year => BEGIN
cont ← CreateContainer[ myParent: calViewer];
DisplayYear[ cont, date];
CalStorage.StoreViewer[ date, Year, cont];
END;
ENDCASE => ERROR;
ViewerOps.PaintViewer[ viewer: calViewer, hint: all];
END
ELSE --remember calViewer is top level viewer....! It also appears that moving calViewer
maintains coord appropriately
ViewerOps.MoveViewer[ viewer: cont, x: cont.wx-Delta, y: cont.wy-Delta, w: cont.ww, h: cont.wh, paint: TRUE];
END; -- DisplayEvents
RetrieveCalBrowser: INTERNAL PROCEDURE [ newZoom, oldZoom: ZoomLevel, date: Date, oldViewer: ViewerClasses.Viewer] RETURNS [ newViewer: ViewerClasses.Viewer] = BEGIN
IF oldViewer # NIL THEN
IF oldZoom # Day THEN -- hide it away
ViewerOps.MoveViewer[ viewer: oldViewer, x: oldViewer.wx+Delta, y: oldViewer.wy+Delta, w: oldViewer.ww, h: oldViewer.wh]
ELSE BEGIN
ViewerOps.DestroyViewer[ oldViewer, FALSE]; -- don't bother saving it
dayGrid.buttons ← NIL;
END;
SELECT newZoom FROM
Day => newViewer ← NIL;
Month => newViewer ← CalStorage.RetrieveViewer[ date, Month];
Year => newViewer ← CalStorage.RetrieveViewer[ date, Year];
ENDCASE;
RETURN[ newViewer];
END; -- RetrieveCalBrowser
SetMenu: INTERNAL PROCEDURE[ zoomLevel: ZoomLevel, viewer: ViewerClasses.Viewer] = BEGIN
here we set the menu of calViewer according to zoom level
SELECT zoomLevel FROM
Day => BEGIN
rz: REF ZoomLevel ← NEW[ ZoomLevel ← Month];
ViewerOps.SetMenu[ viewer: viewer, menu: CalSupport.MakeMenu[ LIST[ "Next", "Previous", "Month", "Print", "Forget", "Edit", "Enter", "ShowMsg"], LIST[ Next, Previous, ZoomOut, Print, Forget, Edit, Enter, ShowMsg], LIST[ NIL, NIL, rz, NIL, NIL, NIL, NIL, NIL]], paint: TRUE];
END; -- Day
Month => BEGIN
rz: REF ZoomLevel ← NEW[ ZoomLevel ← Year];
ViewerOps.SetMenu[ viewer: viewer, menu: CalSupport.MakeMenu[ LIST[ "Next", "Previous", "Year", "Print", "Enter"], LIST[ Next, Previous, ZoomOut, Print, Enter], LIST[ NIL, NIL, rz, NIL, NIL]], paint: FALSE];
END; -- Month
Year => BEGIN
ViewerOps.SetMenu[ viewer: viewer, menu: CalSupport.MakeMenu[ LIST[ "Next", "Previous", "Print", "Enter"], LIST[ Next, Previous, Print, Enter], LIST[ NIL, NIL, NIL, NIL]], paint: FALSE];
END; -- Year
ENDCASE;
END; -- SetMenu
AdjustCaption: INTERNAL PROCEDURE [ zoomLevel: ZoomLevel, curDate: Date] = BEGIN
here we change the title of the viewer a bit
newTitle: Rope.ROPE ← "Calendar: ";
now: BasicTime.GMT ← BasicTime.Now[];
time: BasicTime.Unpacked ← BasicTime.Unpack[ now];
SELECT zoomLevel FROM
Day =>
newTitle ← Rope.Concat[ newTitle, IO.PutFR[ "%g, %g %2g, %4g", IO.rope[ CalSupport.GetDayName[ curDate]], IO.rope[ CalSupport.GetMonthName[ curDate.Month]], IO.int[ curDate.Day], IO.int[ curDate.Year]]];
Month =>
newTitle ← Rope.Concat[ newTitle, IO.PutFR[ "%g %4g", IO.rope[ CalSupport.GetMonthName[ curDate.Month]], IO.int[ curDate.Year]]];
Year =>
newTitle ← Rope.Concat[ newTitle, IO.PutFR[ "%4g", IO.int[ curDate.Year]]];
ENDCASE;
calViewer.name ← newTitle;
END; -- AdjustCaption
DisplayDay: INTERNAL PROCEDURE [ cont: ViewerClasses.Viewer, date: Date] = BEGIN
list all the events of the day in the text viewer, container is clean
OPEN dayGrid;
evl: EventList ← NIL;
rowCount: CARDINAL ← 0;
but: Buttons.Button;
msg, txtRope, tr, plRope: Rope.ROPE;
time: BasicTime.Unpacked;
buttonLabel: Rope.ROPE;
IF cont = NIL THEN RETURN;
evl ← CalStorage.GetEventsOfDay[ date, Day];
IF evl = NIL THEN BEGIN
[] ← CreateLabel[ myParent: cont, hi: ButtonHeight, wi: cont.cw, title: "Sorry, no events on this day...", displayStyle: $BlackOnWhite];
RETURN;
END;
buttons ← NIL;
FOR l: EventList ← evl, l.rest UNTIL l = NIL DO
time ← BasicTime.Unpack[ l.first.EventTime];
txtRope ← l.first.Text;
plRope ← l.first.Place;
IF l.first.Duration # 0 THEN BEGIN
duration: LONG CARDINAL ← l.first.Duration;
endTime: BasicTime.Unpacked;
duration ← duration*60;
endTime ← BasicTime.Unpack[ BasicTime.Update[ l.first.EventTime, duration]];
tr ← IO.PutFR[ TimeFormat, IO.int[ time.hour], IO.int[ time.minute], IO.int[ endTime.hour], IO.int[ endTime.minute]];
END
ELSE tr ← IO.PutFR[ TimeFormat2, IO.int[ time.hour], IO.int[ time.minute]];
IF l.first.Message # NIL THEN msg ← " M" ELSE msg ← " ";
IF Rope.Length[ txtRope] > TextWidth THEN txtRope ← Rope.Substr[ txtRope, 0, TextWidth];
IF Rope.Length[ plRope] > PlaceWidth THEN plRope ← Rope.Substr[ plRope, 0, PlaceWidth];
buttonLabel ← IO.PutFR[ Format, IO.rope[ tr], IO.rope[ msg], IO.rope[ txtRope], IO.rope[ plRope]];
but ← CalSupport.CreateButton[ myParent: cont, hi: ButtonHeight, wi: cont.cw, yOff: rowCount*ButtonHeight, xOff: 0, proc: SelectEvent, clientData: NEW[ CARDINAL ← rowCount], title: buttonLabel, displayStyle: $BlackOnWhite];
buttons ← CONS[ [ rowCount, l.first.Key, but], buttons];
rowCount ← rowCount + 1;
ENDLOOP;
END; -- DisplayDay
TitleMonthGrid: INTERNAL PROCEDURE [ monthTable: VTables.VTable] RETURNS [ newMonthTable: VTables.VTable] = BEGIN
to label the first row of the month grid when we build the monthly grid for a year.
OPEN monthGrid;
width: INT← monthTable.cw/Cols;
newMonthTable ← monthTable;
FOR d: Weekdays IN [ Monday..Sunday] DO
dr: Rope.ROPE; col: NAT;
SELECT d FROM
Monday => BEGIN dr ← "Mo"; col ← 1; END;
Tuesday => BEGIN dr ← "Tu"; col ← 2; END;
Wednesday => BEGIN dr ← "We"; col ← 3; END;
Thursday => BEGIN dr ← "Th"; col ← 4; END;
Friday => BEGIN dr ← "Fr"; col ← 5 ;END;
Saturday => BEGIN dr ← "Sa"; col ← 6; END;
Sunday => BEGIN dr ← "Su"; col ← 0; END;
ENDCASE;
VTables.SetTableEntry[ table: newMonthTable, row: 0, column: col, name: dr, border: [ FALSE, TRUE, FALSE, TRUE], w: width];
ENDLOOP;
END; -- TitleMonthGrid
CreateDayViewer: INTERNAL PROCEDURE [ width, height: INT, myParent: ViewerClasses.Viewer, butLabel: Rope.ROPE, butProc: Menus.MenuProc, dayOfMonth: Days, contents: Rope.ROPE] RETURNS [ table: VTables.VTable] = BEGIN
build a table with 2 rows: the first is a button and labeled with the name and date of the
day. Second row is a text viewer, whose content are all the event texts... of that day
button, txtViewer: ViewerClasses.Viewer;
table ← VTables.Create[ rows: 2, columns: 1, parent: myParent, w: width, h: height];
VTables.SetTableEntry[ table: table, row: 0, column: 0, flavor: $Button, proc: butProc, clientData: NEW[ Days ← dayOfMonth], name: butLabel, w: width, border: [ FALSE, TRUE, FALSE, FALSE]];
VTables.Install[ table, FALSE];
button ← VTables.GetTableEntry[ table, 0, 0];
txtViewer ← CalSupport.CreateTextViewer[ width: width, height: height - button.wh, myParent: table, scrolling: TRUE, cont: contents, edit: FALSE];
VTables.SetTableEntry[ table: table, row: 1, column: 0, flavor: $Viewer, clientData: txtViewer, border: [ TRUE, TRUE, FALSE, TRUE], h: height - button.wh, w: width]; -- ouff
VTables.Install[ table, FALSE];
END; -- CreateDayViewer
FillInDayGrid: INTERNAL PROCEDURE [ table: VTables.VTable, date: Date] RETURNS [ newTable: VTables.VTable] = BEGIN
to fill a monthly grid with the day viewers
OPEN monthGrid;
width, height: INT;
nbrOfDays: Days;
firstDay: Weekdays;
cont: Rope.ROPE;
DayName: INTERNAL PROCEDURE [ col: INT, i: Days] RETURNS [ namedDay: Rope.ROPE] = INLINE BEGIN
r: Rope.ROPE;
SELECT col FROM
1 => r ← "Mon";
2 => r ← "Tue";
3 => r ← "Wed";
4 => r ← "Thu";
5 => r ← "Fri";
6 => r ← "Sat";
0 => r ← "Sun";
ENDCASE;
RETURN[ IO.PutFR[ monthGrid.DateFormat, IO.rope[ r], IO.int[ i]]];
END; -- DayName
[ nbrOfDays, firstDay] ← CalSupport.GetMonthInfo[ curDate.Month, curDate.Year];
width ← table.cw/Cols;
height ← table.ch/Rows;
FOR d: Days IN [ 1.. nbrOfDays] DO
row, col: INT;
dayViewer: ViewerClasses.Viewer;
[ row, col] ← CalSupport.GetRowCol[ d, firstDay];
cont ← DayContents[ [ d, date.Month, date.Year], width];
dayViewer ← CreateDayViewer[ width, height, table, DayName[ col, d], ZoomIn, d, cont];
monthGrid.DayViewers[ d] ← dayViewer;
ENDLOOP;
RETURN[ table];
END; -- FillInDayGrid
CreateMonthGrid: INTERNAL PROCEDURE [ myParent: ViewerClasses.Viewer, height, width: INT, zoom: ZoomLevel, date: Date] RETURNS [ table: VTables.VTable] = BEGIN
we create a table to display the month. If zoom = Year we must include a title row for the
days. Monthly grids don't have the title row to make all table entries of same height.
monthTable: VTables.VTable;
IF zoomLevel = Year THEN
monthTable ← VTables.Create[ columns: monthGrid.Cols, rows: monthGrid.Rows+1, parent: myParent, h: height, w: width]
ELSE
monthTable ← VTables.Create[ columns: monthGrid.Cols, rows: monthGrid.Rows, parent: myParent, h: height, w: width];
IF monthTable = NIL THEN BEGIN
CalSupport.DisplayMsg[ "Failure to create month grid!"];
RETURN;
END;
IF zoomLevel = Year THEN monthTable ← TitleMonthGrid[ monthTable]
ELSE monthTable ← FillInDayGrid[ monthTable, date];
VTables.Install[ monthTable, FALSE];
RETURN[ monthTable];
END; -- CreateMonthGrid
DayContents: INTERNAL PROCEDURE [ date: Date, fieldWidth: INT ← 0] RETURNS [ entry: Rope.ROPE] = BEGIN
depending on zooomLevel, we fill in the day entry. Currently when at zoom = Month
we show all event texts. If at zoom = Year, simply put a '*' if some event scheduled on
that day.
fieldWidth is in Pixels!
OPEN monthGrid;
evl: EventList ← CalStorage.GetEventsOfDay[ date, Day];
length: INT ← fieldWidth/CharWidth-2; -- estimate how many chars fit on a line
IF evl = NIL THEN RETURN[ NIL];
IF evl = NIL THEN RETURN[ NIL];
entry ← Rope.Concat[ entry, " "]; -- text viewer's first char...
FOR l: EventList ← evl, l.rest UNTIL l = NIL DO
IF fieldWidth = 0 THEN
entry ← Rope.Concat[ entry, l.first.Text]
ELSE IF Rope.Length[ l.first.Text] > length THEN BEGIN
entry ← Rope.Concat[ entry, Rope.Substr[ base: l.first.Text, len: length]];
entry ← Rope.Concat[ entry, "..."];
END
ELSE entry ← Rope.Concat[ entry, l.first.Text];
entry ← Rope.Concat[ entry, Rope.FromChar[ '\n]];
ENDLOOP;
RETURN[ entry];
END; -- DayContents
FillMonthGrid: INTERNAL PROCEDURE [ date: Date, monthTable: VTables.VTable, zoom: ZoomLevel] = BEGIN
to fill in the computed contents into the monthly grid table
OPEN monthGrid;
wid, hi: INT; -- h, w for entry
row, col: INTEGER;
noLeftBorder: VTables.Border ← [ TRUE, TRUE, FALSE, TRUE];
noRightBorder: VTables.Border ← [ TRUE, TRUE, TRUE, FALSE];
firstDay: Weekdays;
nbrOfDays: Days;
GetBorder: INTERNAL PROCEDURE [ index: INT] RETURNS [ border: VTables.Border] = INLINE BEGIN
IF index = 0 THEN RETURN[ noLeftBorder]
ELSE IF index = Rows-1 THEN RETURN[ noRightBorder]
ELSE RETURN[ VTables.FullBorder];
END; -- GetBorder
IF zoom = Year THEN BEGIN
wid ← monthTable.cw/monthGrid.Cols;
hi ← RowHeight;
END
ELSE BEGIN OPEN monthGrid;
wid ← FormWidth/Cols;
hi ← FormHeight/Rows;
END;
[ nbrOfDays, firstDay] ← CalSupport.GetMonthInfo[ date.Month, date.Year];
[ row, col] ← CalSupport.GetRowCol[ 1, firstDay];
IF col # 0 THEN -- fill first row partially
FOR c: INT IN [ 0..col-1] DO
VTables.SetTableEntry[ table: monthTable, row: IF zoom = Year THEN 1 ELSE 0, column: c, w: wid, h: hi, border: GetBorder[ c]];
ENDLOOP;
[ row, col] ← CalSupport.GetRowCol[ nbrOfDays, firstDay];
IF col # 6 THEN -- fill last row partially
FOR c: INT IN [ col+1..monthGrid.Cols) DO
VTables.SetTableEntry[ table: monthTable, row: IF zoom = Year THEN row + 1 ELSE row, column: c, w: wid, h: hi, border: GetBorder[ c]];
ENDLOOP;
VTables.Install[ monthTable, FALSE];
FOR i: Days IN [ 1..nbrOfDays] DO
[ row, col] ← CalSupport.GetRowCol[ i, firstDay];
remember title is row 0 when we have zoom = Year
IF zoomLevel = Month THEN BEGIN
simply insert day viewers, whose contents is already set!
dayViewer: VTables.VTable ← monthGrid.DayViewers[ i];
VTables.SetTableEntry[ table: monthTable, row: row, column: col, flavor: $Viewer, w: wid, h: hi, clientData: dayViewer, border: GetBorder[ col]];
END
ELSE BEGIN -- zoomLevel = Year; proc registered with title table...
evl: EventList ← CalStorage.GetEventsOfDay[ [ i, date.Month, date.Year], Year];
VTables.SetTableEntry[ table: monthTable, row: row+1, column: col, name: IO.PutFR[ "%02g", IO.int[ i]], w: wid, h: hi, border: GetBorder[ col], proc: NIL, displayStyle: IF evl = NIL THEN $BlackOnWhite ELSE $BlackOnGrey];
END;
VTables.Install[ monthTable, FALSE];
ENDLOOP;
END; -- FillMonthGrid
DisplayMonth: INTERNAL PROCEDURE [ cont: ViewerClasses.Viewer, date: Date] = BEGIN
displays the month into container.
OPEN monthGrid;
table : VTables.VTable ← CreateMonthGrid[ cont, cont.ch, cont.cw, Month, date];
-- above call sets monthGrid.dayViewers
FillMonthGrid[ date, table, Month];
END; -- DisplayMonth
CreateTitleTable: INTERNAL PROCEDURE [ myParent: ViewerClasses.Viewer, height, width: INT, mo: Months] RETURNS [ table: VTables.VTable] = BEGIN
creates a month title table to be contained in the year table and installs it
table ← VTables.Create[ columns: 1, rows: 1, parent: myParent, w: width, h: height];
VTables.SetTableEntry[ table: table, row: 0, column: 0, name: CalSupport.GetMonthName[ mo], proc: ZoomIn, clientData: NEW[ Months ← mo], w: width, h: height, border: VTables.NullBorder];
VTables.Install[ table, FALSE];
END; -- CreateTitleTable
CreateYearGrid: INTERNAL PROCEDURE [ myParent: ViewerClasses.Viewer, date: Date] RETURNS [ yearTable: VTables.VTable] = BEGIN
create the year grid i..e a table [ 4, 3] of tables [ 6, 7]
OPEN yearGrid;
table: VTables.VTable;
wid: INT; -- height is constant
yearTable ← VTables.Create[ columns: Cols, rows: Rows, parent: myParent, h: myParent.ch, w: myParent.cw];
wid ← yearTable.cw/Cols; -- width of a month table within year grid.
FOR l: INT IN [ 0..Rows-1] DO
FOR k: INT IN [ 0..Cols-1] DO
OPEN yearGrid;
mo: Months ← CalSupport.I2M[ (l/2)*Cols+k];
IF l MOD 2 = 0 THEN table ← CreateTitleTable[ yearTable, RowHeight, wid, mo]
ELSE BEGIN
table ← CreateMonthGrid[ yearTable, ( monthGrid.Rows+1)*RowHeight, wid, Year, [ date.Day, mo, date.Year]];
yearGrid.MonthViewers[ mo] ← table;
END;
VTables.SetTableEntry[ table: yearTable, row: l, column: k, flavor: $Viewer, clientData: table, proc: NIL];
VTables.Install[ table, FALSE];
ENDLOOP;
ENDLOOP;
VTables.Install[ table: yearTable, paint: FALSE];
RETURN[ yearTable];
END; -- CreateYearGrid
DisplayYear: INTERNAL PROCEDURE [ cont: ViewerClasses.Viewer, date: Date] = BEGIN
display the year in yearly grid...
table: VTables.VTable ← CreateYearGrid[ cont, date];
FOR curMonth: Months IN [ January..December] DO
curMonthTable: VTables.VTable ← yearGrid.MonthViewers[ curMonth];
FillMonthGrid[ [ date.Day, curMonth, date.Year], curMonthTable, Year];
ENDLOOP;
VTables.Install[ table: table, paint: FALSE];
END; -- DisplayYear
Updating the browser viewers when hickory changes.
HickoryChange: PUBLIC INTERNAL PROCEDURE [ reason: Hickory.Reason, oldEvl, newEvl: Hickory.EventList, data: RopeSets.RopeSet] = BEGIN
having updated calStorage, we now update the viewers of the browser...
evl is the list of all events that have somehow changed. Assume that calStorage
is up to date, except viewer contents.
prevDate: Date ← [ 0, January, 1968]; -- fix same day only once
dayViewerFixed: BOOLEANFALSE;
IF calViewer = NIL OR calViewer.destroyed THEN RETURN;
IF reason = Edit OR reason = Destroy OR reason = Forget THEN -- get rid of old stuff
FOR l: Hickory.EventList ← oldEvl, l.rest UNTIL l = NIL DO
date: Date ← CalSupport.ExtractDate[ l.first.EventTime];
viewer: ViewerClasses.Viewer;
IF date # prevDate THEN BEGIN
viewer ← CalStorage.RetrieveViewer[ date, Month];
IF viewer # NIL THEN CleanMonthViewer[ viewer, date];
IF curDate = date AND zoomLevel = Day AND curMode = Browse THEN BEGIN
we should be having a day viewer on the screen..
FixDayViewer[ date]; dayViewerFixed ← TRUE;
END;
END;
prevDate ← date;
ENDLOOP;
IF reason = Edit OR reason = NewEvent OR reason = UnForget THEN -- insert new stuff
FOR l: Hickory.EventList ← newEvl, l.rest UNTIL l = NIL DO
date: Date ← CalSupport.ExtractDate[ l.first.EventTime];
viewer: ViewerClasses.Viewer;
IF date # prevDate THEN BEGIN
viewer ← CalStorage.RetrieveViewer[ date, Month];
IF viewer # NIL THEN FixMonthViewer[ viewer, date];
viewer ← CalStorage.RetrieveViewer[ date, Year];
IF viewer # NIL THEN FixYearViewer[ viewer, date];
IF NOT dayViewerFixed AND curDate = date AND zoomLevel = Day
AND curMode = Browse THEN BEGIN
we should be having a day viewer on the screen..
FixDayViewer[ date];
END;
END;
prevDate ← date;
ENDLOOP;
END; -- HickoryChange
CleanMonthViewer: INTERNAL PROCEDURE [ monthViewer: Containers.Container, date: Date] = BEGIN
here we clean the day viewers of the month of events that are no longer scheduled.
monthTable: VTables.VTable ← monthViewer^.child; -- there ought to be only one
row, col: CARDINAL;
firstDay: Weekdays;
nbrOfDays: Days;
dayViewer, txtViewer: ViewerClasses.Viewer;
IF monthTable = NIL THEN RETURN;
[ nbrOfDays, firstDay] ← CalSupport.GetMonthInfo[ date.Month, date.Year];
[ row, col] ← CalSupport.GetRowCol[ date.Day, firstDay];
dayViewer ← VTables.GetTableEntry[ table: monthTable, row: row, column: col];
txtViewer ← VTables.GetTableEntry[ table: dayViewer, row: 1, column: 0];
ViewerTools.SetContents[ viewer: txtViewer, contents: DayContents[ date, dayViewer.cw]];
END; -- FixMonthViewer
FixMonthViewer: INTERNAL PROCEDURE [ monthViewer: Containers.Container, date: Date] = BEGIN
here we insert newly schedule events into the day viewers of the month.
monthTable: VTables.VTable ← monthViewer^.child; -- there ought to be only one
row, col: CARDINAL;
firstDay: Weekdays;
nbrOfDays: Days;
dayViewer, txtViewer: ViewerClasses.Viewer;
IF monthTable = NIL THEN RETURN;
[ nbrOfDays, firstDay] ← CalSupport.GetMonthInfo[ date.Month, date.Year];
[ row, col] ← CalSupport.GetRowCol[ date.Day, firstDay];
dayViewer ← VTables.GetTableEntry[ table: monthTable, row: row, column: col];
txtViewer ← VTables.GetTableEntry[ table: dayViewer, row: 1, column: 0];
ViewerTools.SetContents[ viewer: txtViewer, contents: DayContents[ date, dayViewer.cw]];
END; -- FixMonthViewer
FixYearViewer: INTERNAL PROCEDURE [ yearViewer: Containers.Container, date: Date] = BEGIN
and here the year viewer is updated
yearTable: VTables.VTable ← yearViewer^.child; -- there ought to be only one
row, col: CARDINAL;
firstDay: Weekdays;
nbrOfDays: Days;
label: Labels.Label;
monthTable: VTables.VTable;
evl: Hickory.EventList;
IF yearTable = NIL THEN RETURN;
[ nbrOfDays, firstDay] ← CalSupport.GetMonthInfo[ date.Month, date.Year];
row ← ( CalSupport.M2I[ date.Month])/yearGrid.Cols;
col ← ( CalSupport.M2I[ date.Month]) MOD yearGrid.Cols;
row ← 2*row + 1; -- month button rows...
monthTable ← VTables.GetTableEntry[ table: yearTable, row: row, column: col];
[ row, col] ← CalSupport.GetRowCol[ date.Day, firstDay];
row ← row + 1; -- day title row
label ← VTables.GetTableEntry[ table: monthTable, row: row, column: col];
evl ← CalStorage.GetEventsOfDay[ date, Day];
IF evl = NIL THEN Labels.SetDisplayStyle[ label: label, style: $BlackOnWhite]
ELSE Labels.SetDisplayStyle[ label: label, style: $BlackOnGrey];
END; -- FixYearViewer
FixDayViewer: INTERNAL PROCEDURE [ date: Date] = BEGIN
assume that day viewer is visible on screen. Day viewer is top level container
For now we destroy entire viewer and recreate it. It's hard to get repeated events right,
that's why. But there might be smarter and faster ways..
IF cont # NIL THEN BEGIN
ViewerOps.DestroyViewer[ viewer: cont, paint: TRUE];
cont ← NIL; dayGrid.buttons ← NIL;
END;
cont ← CreateContainer[ calViewer, TRUE];
DisplayDay[ cont, date];
ViewerOps.PaintViewer[ cont, all];
END; -- FixDayViewer
INITIALLY
zoomLevel ← unspecified;
END.utto