-- /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; Commands: TYPE = { DoIt, Erase, Reset}; calForm: PUBLIC CalForm; curYear: Years _ BasicTime.Unpack[ BasicTime.Now[]].year; curSelection: PUBLIC CurSelection; editedEv: PUBLIC REF Hickory.EventTuple _ NIL; DestroyViewer: PUBLIC INTERNAL PROC = BEGIN 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 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 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 CreateCalForm: PUBLIC INTERNAL PROC [ mode: CalStorage.Mode, ev: REF Hickory.EventTuple _ NIL] = BEGIN 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 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 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 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 groups: Hickory.GroupSet _ [ NIL, NIL]; evl: Hickory.EventList; ok: BOOLEAN _ TRUE; [ 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 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: BOOLEAN _ TRUE] = BEGIN 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 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 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 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 IF repType IN [ Hourly..Yearly] THEN CalSupport.SetButton[ calForm.row5.menuTable.buttons[ CalSupport.R2I[ repType]-1]]; END; -- RefillRepeatMenu RefillGroupMenu: INTERNAL PROCEDURE = BEGIN 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 HickoryChange: PUBLIC INTERNAL PROCEDURE [ reason: Hickory.Reason, ev: Hickory.EventTuple, data: RopeSets.RopeSet] = BEGIN 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 curSelection _ SetSelection[]; END. fGlobal Types Global variables Utility Operations is called when calViewer was destroyed... Must set all ptr to NIL so GC gets the garbage to set the selection and also protData.curDate 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.. Building the form on the screen... 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. here we change the title of the viewer a bit here we draw the form on the screen. Either we build it from scratch or we fill in the viewers that are already existing. Menu Procedures to translate the form and enter it into hickory... to reset current selection and clear form if cmd = Erase. If reset and we are editing an existing event, reinitialize form. to undo the current contents. to graphically erase selected groups... to refill the form without drawing it from scratch. to update the year, month and day tables to adjust the repetiton menu when we are editing an old event. to refill the group menu. Assume that calForm.row6.groups knows about all the groups in hickory. curSelection contains groups currently selected groups. in the case hickory is modified we fix the calendar form here here we deal with changes to hickory. Initially สy˜J˜)J˜\J˜\J˜:J˜šฯk ˜ Jšœ œœ#˜EJšœœ˜!Jšœ œ(˜6J˜Jšœœํ˜๚J˜Jšœ œ˜#Jšœ œd˜tJšœ œ˜Jšœœy˜†Jšœœ˜Jšœœ ˜Jšœœœ˜Jšœ œ`˜nJšœœ ˜Jšœœ˜)Jšœ œ4˜CJšœ œ˜!Jšœœ˜&J˜—J˜šœ œ˜Jšœ˜Jšœ‹˜’Jšœ˜Jšœ'˜-J˜—šœœœ(˜4J˜—šœ ™ J˜J˜'J˜—šœ™J˜Jšœ œ ˜J˜9Jšœœ˜"Jšœ œœœ˜.J˜—šœ™J˜š ฯn œœœœ˜+JšœX™Xšœœœ˜%J˜,Jšœœ˜Jšœ˜—Jšœ˜Jšœ œ˜šœœ˜Jšœœ˜Jš œœœœœœ˜KJšœœ˜Jš œœœœœœ˜LJšœœ˜šœœœ˜'š œœœœฯc ˜4Jšœœ˜ Jšœ˜—Jšœ˜—JšœŸ˜ —Jšœœœ˜7Jšœœœ˜7Jšœœœ˜7Jšœœœ˜7šœœ˜"Jšœœ˜ Jš œœœœœœ˜9Jšœ˜—Jšœœœ˜7Jšœœœœ˜@šœœ˜"Jšœœ˜ Jš œœœœœœ˜9Jšœ˜—Jšœœ˜Jšœœ œ˜EJšœœ˜JšœŸ˜J˜—šž œ œœœœœœ˜…šœ.™.šœœ˜šœœœ˜J˜.J˜J˜&Jšœ&˜&J˜(Jšœ ˜ J˜J˜Jš˜—šœ˜ J˜0J˜J˜J˜+Jšœœ˜J˜Jšœ œ˜J˜ Jšœ˜—JšœŸ˜ —Jšœ˜ JšœŸ˜J˜——š žœ œœ œ˜dJšœY™YJšœS™SJšœ ™ J˜J˜J˜]Jšœ œœ˜Jšœ œ"˜2Jšœ ˜JšœŸ˜J˜——šœ"™"J˜š ž œœœœœœ˜fJšœW™WJ™J˜*J˜'J˜J˜J˜(J˜7š œœœ œœ˜)Jšœ˜Jšœ œ˜Jš˜—š œœœœŸ˜8Jšœœ"˜>Jšœ>˜>Jšœ˜Jšœ˜—J˜JšœAœœœœœœ œ˜ขJšœ0˜0Jšœ˜J˜—šž œœ œ#œ˜JJšœ,™,J˜Jšœœ1˜MJšœ.˜2JšœŸ˜—J˜šž œœ œ^˜‚JšœR™RJ™&Jšœœœ!˜@šœ˜ Jšœ8˜8JšœJ˜JJšœD˜Dš œœœœŸ˜4Jšœœ˜ Jšœ˜Jš˜—Jšœœ˜$Jšœจœ˜ฎJšœ˜—šœŸ˜J˜———šœ™J™šœŸqœ˜ŽJ˜Jšœ˜J˜—šœŸqœ˜ŽJ˜Jšœ˜J˜—šœŸqœ˜Jšœ ˜ Jšœ˜—J˜šžœœ œ˜/Jšœœœ˜šœ˜J˜J˜Jšœ˜Jšœ˜—JšœŸ˜ J˜—šžœœ œ˜-Jšœ2™2Jšœœœ˜'J˜Jšœœœ˜JšœFœœ˜Yšœœ˜J˜-Jšœ˜—JšœŸ˜J˜—šž œœ œ˜6JšœU™UJ™%J˜*J˜š œ œ œœ˜+Jšœœ˜Jšœ˜Jš˜—šœœŸ˜!Jšœœ˜ J˜KJ˜$Jšœ˜—Jšœ8˜8JšœJ˜JJšœC˜CJšœŸ ˜J˜—š ž œœ œ!œœ˜UJšœ™Jšœ ˜ J˜šœœ˜Jšœ-œ˜3Jšœ-œ˜3Jšœ-œ˜3Jšœ-œ˜3Jšœ-œ˜3Jšœ1œ˜7Jšœ1œ˜7Jšœ˜—J˜;J˜JšœŸ ˜J˜—šžœœœ&˜RJšœ˜šœ œ˜$JšœG˜G—JšœŸ˜ J˜—šžœœ œ˜/Jšœ'™'Jšœ˜J˜šœœœœ˜&Jšœ.˜.Jšœ˜—JšœŸ˜J˜—š ž œœ œ œ3˜cJšœ4™4Jšœ ˜ Jšœ œ˜J˜J˜Jšœ œœœ˜J˜ J˜1Jšœ-œ˜3Jšœ2œ˜8Jšœ3œ˜9Jšœ8œ˜?J˜!J˜šœ$˜*JšœUœ˜Z—Jšœ2œ˜<šœ"˜(JšœSœ˜X—Jšœ2œ˜™>šœœ˜%JšœS˜S—JšœŸ˜J˜—šžœœ œœ˜,JšœI™IJšœN™NJšœ˜Jšœœ˜J˜Jš œœœœœ˜FJš œ œœœŸ˜7Jšœ(œŸ'˜WJšœ˜J˜šœœœ˜%šœœœ˜Jšœ2œ˜9šœ7˜=J˜,—Jšœ0˜4J˜ Jš˜—šœ˜ Jšœ,œŸ˜