DIRECTORY ImagerFont, Imager, ImagerTransformation, ImagerColor USING [ColorFromAtom], ImagerBackdoor USING [MakeStipple], Commander, Convert, ViewerOps, Menus, PopUpMenu USING [RequestSelection], MessageWindow USING [Append, Blink], ViewerTools, ViewerClasses, Rope, Real USING [RoundI], TIPUser, Icons, InputFocus, MathExpr, MathDisplayExpr, MathDB, MathBox, MathTypes, MathConstructors, ViewExprOps, ViewExpr; ViewExprImpl: CEDAR PROGRAM IMPORTS ViewerOps, TIPUser, Menus, MathDisplayExpr, MathBox, MathConstructors, PopUpMenu, MathExpr, MathDB, Convert, MessageWindow, ViewerTools, Rope, Real, InputFocus, ViewExprOps, ImagerColor, ImagerBackdoor, Icons EXPORTS ViewExpr ~ BEGIN ROPE: TYPE ~ Rope.ROPE; BOX: TYPE ~ MathBox.BOX; Viewer: TYPE ~ ViewerClasses.Viewer; Argument: TYPE ~ MathExpr.Argument; CompoundClass: TYPE ~ MathExpr.CompoundClass; EXPR: TYPE ~ MathExpr.EXPR; DisplayExpr: TYPE ~ MathDisplayExpr.DisplayExpr; fontToViewerSizeRatio: REAL = 75.0; -- magnify from TeX font units to Viewer pixels State: TYPE ~ REF StateRec; StateRec: TYPE ~ RECORD [ exprViewer: Viewer, -- viewer containing expression offsetX, offsetY: REAL, -- offsets into imager context of expr lower left-hand corner scale: REAL, -- magnification factor (1.0 = default) displayExpr: DisplayExpr, -- expression displayBox: BOX, -- bounding box for expression physicalBox: BOX -- physical mapping between expression and viewer ]; kbBuffer: RECORD[type: {integer, real, variable}, data: ROPE]; DeleteSelection: PROC[flavor: ATOM, reselect: BOOL _ TRUE] ~ { state: State; replacement, selectedExpr: DisplayExpr _ NIL; selectedViewer: Viewer _ NIL; IF ~ViewExprOps.Active[flavor] THEN RETURN; -- return w/o action if seln not active [selectedViewer, selectedExpr] _ ViewExprOps.GetSelection[flavor]; replacement _ MathDisplayExpr.DisplayExprFromExpr[MathConstructors.MakePlaceHolder[]]; state _ NARROW[selectedViewer.data]; MathDisplayExpr.Replace[selectedExpr, replacement ! MathDisplayExpr.replaceAll => {state.displayExpr _ replacement; CONTINUE}]; state.displayBox _ state.displayExpr.Format[normal]; IF reselect THEN ViewExprOps.Select[flavor, selectedViewer, replacement] ELSE ViewExprOps.UnSelect[flavor]; }; InitializeExprViewer: ViewerClasses.InitProc ~ { self.data _ NEW[StateRec _ [ exprViewer: self, offsetX: 0.0, offsetY: 0.0, scale: 1.0, displayExpr: NIL, displayBox: NIL, physicalBox: NIL]]; self.scrollable _ TRUE; self.hscrollable _ TRUE; self.menu _ Menus.CreateMenu[lines: 2]; self.menu.AppendMenuEntry[Menus.CreateEntry["Scale", Scale, self.data], 0]; self.menu.AppendMenuEntry[Menus.CreateEntry["Home", Home, self.data], 0]; self.menu.AppendMenuEntry[Menus.CreateEntry["ToRope", ConvertToRope, self.data], 0]; self.menu.AppendMenuEntry[Menus.CreateEntry["Debug", EnterDebugger, self.data], 0]; self.menu.AppendMenuEntry[Menus.CreateEntry["ReplaceWithMatrix", EnterMatrix, self.data], 1]; self.menu.AppendMenuEntry[Menus.CreateEntry["ReplaceWithExpr", EnterExpression, self.data], 1]; self.menu.AppendMenuEntry[Menus.CreateEntry["ReplaceWithAtom", EnterAtom, self.data], 1]; self.menu.AppendMenuEntry[Menus.CreateEntry["WrapWithExpr", WrapExpression, self.data], 1]; }; DestroyExprViewer: ViewerClasses.DestroyProc ~ { s: State _ NARROW[self.data]; ViewExprOps.UnSelectViewer[self]; ViewExprOps.PaintDequeue[self]; s.exprViewer _ NIL; -- break up circular ref for GC }; NotifyExprViewer: ViewerClasses.NotifyProc ~ { s: State _ NARROW[self.data]; replacement: DisplayExpr _ NIL; c: CHAR; [] _ InputFocus.SetInputFocus[self]; WITH input.first SELECT FROM refC: REF CHAR => { c _ refC^; ParseKBChar[c]; }; xy: TIPUser.TIPScreenCoords => { SELECT input.rest.first FROM $PrimarySelect => { ViewExprOps.Select[$primary, s.exprViewer, MathDisplayExpr.DisplayExprFromCoords[s.displayExpr, xy.mouseX, xy.mouseY ! MathDisplayExpr.noSelection => {ViewExprOps.UnSelect[$primary]; CONTINUE}]]; }; $MoveSelect => { IF ~ViewExprOps.Active[$primary] THEN { MessageWindow.Append["Make a primary selection first.", TRUE]; MessageWindow.Blink[]; RETURN; }; ViewExprOps.Select[$move, s.exprViewer, MathDisplayExpr.DisplayExprFromCoords[s.displayExpr, xy.mouseX, xy.mouseY ! MathDisplayExpr.noSelection => {ViewExprOps.UnSelect[$move]; CONTINUE}]]; }; $CopySelect => { IF ~ViewExprOps.Active[$primary] THEN { MessageWindow.Append["Make a primary selection first.", TRUE]; MessageWindow.Blink[]; RETURN; }; ViewExprOps.Select[$copy, s.exprViewer, MathDisplayExpr.DisplayExprFromCoords[s.displayExpr, xy.mouseX, xy.mouseY ! MathDisplayExpr.noSelection => {ViewExprOps.UnSelect[$copy]; CONTINUE}]]; }; $PrimaryParentSelect, $MoveParentSelect, $CopyParentSelect => { flavor: ATOM; selectedExpr: DisplayExpr _ NIL; selectedViewer: Viewer _ NIL; SELECT input.rest.first FROM $PrimaryParentSelect => flavor _ $primary; $MoveParentSelect => flavor _ $move; $CopyParentSelect => flavor _ $copy; ENDCASE => ERROR; IF ~ViewExprOps.Active[flavor] THEN RETURN; [selectedViewer, selectedExpr] _ ViewExprOps.GetSelection[flavor]; ViewExprOps.Select[flavor, selectedViewer, MathDisplayExpr.SelectableParent[selectedExpr ! MathDisplayExpr.noSelection => {CONTINUE}]]; }; $PrimaryChildSelect, $MoveChildSelect, $CopyChildSelect => { flavor: ATOM; selectedExpr: DisplayExpr _ NIL; selectedViewer: Viewer _ NIL; SELECT input.rest.first FROM $PrimaryChildSelect => flavor _ $primary; $MoveChildSelect => flavor _ $move; $CopyChildSelect => flavor _ $copy; ENDCASE => ERROR; IF ~ViewExprOps.Active[flavor] THEN RETURN; [selectedViewer, selectedExpr] _ ViewExprOps.GetSelection[flavor]; ViewExprOps.Select[flavor, selectedViewer, MathDisplayExpr.SelectableChild[selectedExpr ! MathDisplayExpr.noSelection => {CONTINUE}]]; }; $PrimarySiblingSelect, $MoveSiblingSelect, $CopySiblingSelect => { flavor: ATOM; selectedExpr: DisplayExpr _ NIL; selectedViewer: Viewer _ NIL; SELECT input.rest.first FROM $PrimarySiblingSelect => flavor _ $primary; $MoveSiblingSelect => flavor _ $move; $CopySiblingSelect => flavor _ $copy; ENDCASE => ERROR; IF ~ViewExprOps.Active[flavor] THEN RETURN; [selectedViewer, selectedExpr] _ ViewExprOps.GetSelection[flavor]; ViewExprOps.Select[flavor, selectedViewer, MathDisplayExpr.SelectableSibling[selectedExpr ! MathDisplayExpr.noSelection => {CONTINUE}]]; }; $KBPrimaryDelete => { IF ViewExprOps.Active[$primary] THEN DeleteSelection[$primary] ELSE DeleteSelection[$keyboard]; kbBuffer.data _ ""; -- reset keyboard buffer contents }; $DoPendingCopy => { IF ViewExprOps.Active[$copy] THEN CopySecondaryToPrimary[]; }; $DoPendingMove => { IF ViewExprOps.Active[$move] THEN MoveSecondaryToPrimary[]; }; $KBPrimaryParentSelect => { selectedViewer: Viewer _ NIL; selectedExpr: DisplayExpr _ NIL; IF ViewExprOps.Active[$primary] THEN { [selectedViewer, selectedExpr] _ ViewExprOps.GetSelection[$primary]; ViewExprOps.Select[$primary, selectedViewer, MathDisplayExpr.SelectableParent[selectedExpr ! MathDisplayExpr.noSelection => {CONTINUE}]]; } ELSE IF ViewExprOps.Active[$keyboard] THEN { [selectedViewer, selectedExpr] _ ViewExprOps.GetSelection[$keyboard]; ViewExprOps.UnSelect[$keyboard]; ViewExprOps.Select[$primary, selectedViewer, MathDisplayExpr.SelectableParent[selectedExpr ! MathDisplayExpr.noSelection => {CONTINUE}]]; }; }; ENDCASE => RETURN; -- take no action given unrecognized TIP actions }; ENDCASE => ERROR; ViewExprOps.FlushPaintQueue[]; }; PaintExprViewer: ViewerClasses.PaintProc ~ { scaleFactor: REAL _ fontToViewerSizeRatio; -- scaling factor s: State _ NARROW[self.data]; highlight: LIST OF MathDisplayExpr.Selection _ NIL; selectedViewer: Viewer _ NIL; selectedExpr: DisplayExpr _ NIL; IF (s.displayExpr = NIL) OR (s.displayBox = NIL) THEN RETURN; -- nothing to paint scaleFactor _ scaleFactor * s.scale; s.physicalBox _ MathBox.ChangeOffset[MathBox.Scale[s.displayBox, [scaleFactor, scaleFactor]], [s.offsetX + (scaleFactor * s.displayBox.Extents.leftExtent), s.offsetY + (scaleFactor * s.displayBox.Extents.descent)]]; IF ViewExprOps.Active[$primary] THEN { [selectedViewer, selectedExpr] _ ViewExprOps.GetSelection[$primary]; highlight _ CONS[[expr: selectedExpr, color: ImagerColor.ColorFromAtom[$Invert]], highlight]; }; IF ViewExprOps.Active[$copy] THEN { [selectedViewer, selectedExpr] _ ViewExprOps.GetSelection[$copy]; highlight _ CONS[[expr: selectedExpr, color: ImagerBackdoor.MakeStipple[8421H, TRUE]], highlight]; }; IF ViewExprOps.Active[$move] THEN { [selectedViewer, selectedExpr] _ ViewExprOps.GetSelection[$move]; highlight _ CONS[[expr: selectedExpr, color: ImagerBackdoor.MakeStipple[8020H, TRUE]], highlight]; }; IF ViewExprOps.Active[$keyboard] THEN { [selectedViewer, selectedExpr] _ ViewExprOps.GetSelection[$keyboard]; highlight _ CONS[[expr: selectedExpr, color: ImagerBackdoor.MakeStipple[00F0H, TRUE]], highlight]; }; MathDisplayExpr.Paint[s.displayExpr, context, s.physicalBox, highlight]; }; LexType: TYPE ~ {ucAlpha, lcAlpha, digit, decimal, op, other}; LexChar: PROC[c: CHAR] RETURNS[LexType] ~ { SELECT c FROM IN ['0..'9] => RETURN[digit]; IN ['a..'z] => RETURN[lcAlpha]; IN ['A..'Z] => RETURN[ucAlpha]; '. => RETURN[decimal]; '+, '- , '*, '^, '=, '>, '<, '&, '|, '/, '(, '~, '# => RETURN[op]; ENDCASE => RETURN[other]; }; ParseAction: TYPE ~ REF ParseActionRep; ReplaceAction: TYPE ~ replace ParseActionRep; WrapAction: TYPE ~ wrap ParseActionRep; NoAction: TYPE ~ none ParseActionRep; ParseActionRep: TYPE ~ RECORD [ SELECT type:* FROM replace => [ oldSelect: ATOM, -- old selection flavor which contains expr to replace construct: {integer, real, variable}, -- new expression to be constructed fromRope: ROPE, -- data for construction newSelect: ATOM -- selection flavor to select as after replacement ], wrap => [ selection: ATOM, -- old selection flavor class: ATOM -- class for template wrapper ], none => NULL -- nothing ENDCASE ]; ParseKBChar: PROC[c: CHAR] ~ { lexC: LexType _ LexChar[c]; -- lexical type of character c ropeC: ROPE _ Convert.RopeFromChar[c, FALSE]; -- rope equivalent of char c action: ParseAction _ NIL; IF lexC = op THEN { wrapAround: ATOM _ $keyboard; -- wrap operation around keyboard selection IF ViewExprOps.Active[$primary] THEN wrapAround _ $primary; -- or primary if active SELECT c FROM '+ => action _ NEW[WrapAction _ [wrap[wrapAround, $sum]]]; '- => action _ NEW[WrapAction _ [wrap[wrapAround, $difference]]]; '* => action _ NEW[WrapAction _ [wrap[wrapAround, $product]]]; '^ => action _ NEW[WrapAction _ [wrap[wrapAround, $pow]]]; '= => action _ NEW[WrapAction _ [wrap[wrapAround, $eqFormula]]]; '> => action _ NEW[WrapAction _ [wrap[wrapAround, $gtFormula]]]; '< => action _ NEW[WrapAction _ [wrap[wrapAround, $ltFormula]]]; '& => action _ NEW[WrapAction _ [wrap[wrapAround, $and]]]; '| => action _ NEW[WrapAction _ [wrap[wrapAround, $or]]]; '~ => action _ NEW[WrapAction _ [wrap[wrapAround, $not]]]; '/ => action _ NEW[WrapAction _ [wrap[wrapAround, $fraction]]]; '( => action _ NEW[WrapAction _ [wrap[wrapAround, $paren]]]; '# => action _ NEW[WrapAction _ [wrap[wrapAround, $notEqFormula]]]; ENDCASE => ERROR; } ELSE { SELECT ViewExprOps.Active[$keyboard] FROM FALSE => { SELECT lexC FROM digit => { kbBuffer.type _ integer; kbBuffer.data _ ropeC; action _ NEW[ReplaceAction _ [replace[$primary, integer, kbBuffer.data, $keyboard]]]; }; lcAlpha, ucAlpha => { kbBuffer.type _ variable; kbBuffer.data _ ropeC; action _ NEW[ReplaceAction _ [replace[$primary, variable, kbBuffer.data, $keyboard]]]; }; ENDCASE => action _ NEW[NoAction]; }; TRUE => { SELECT kbBuffer.type FROM integer => { SELECT lexC FROM digit => { kbBuffer.data _ kbBuffer.data.Cat[ropeC]; action _ NEW[ReplaceAction _[replace[$keyboard, integer, kbBuffer.data, $keyboard]]]; }; decimal => { kbBuffer.data _ kbBuffer.data.Cat[ropeC]; kbBuffer.type _ real; action _ NEW[ReplaceAction _ [replace[$keyboard, real, kbBuffer.data, $keyboard]]]; }; ENDCASE => action _ NEW[NoAction]; }; real => { SELECT lexC FROM digit => { kbBuffer.data _ kbBuffer.data.Cat[ropeC]; action _ NEW[ReplaceAction _ [replace[$keyboard, real, kbBuffer.data, $keyboard]]]; }; ENDCASE => action _ NEW[NoAction]; }; variable => { SELECT lexC FROM ucAlpha, lcAlpha => { kbBuffer.data _ kbBuffer.data.Cat[ropeC]; action _ NEW[ReplaceAction _ [replace[$keyboard, variable, kbBuffer.data, $keyboard]]]; }; ENDCASE => action _ NEW[NoAction]; }; ENDCASE => ERROR; }; ENDCASE => ERROR; }; -- end of ELSE WITH action SELECT FROM r: REF ReplaceAction => { selectedViewer: Viewer _ NIL; selectedExpr, replacement: DisplayExpr _ NIL; ok: BOOL _ TRUE; [selectedViewer, selectedExpr] _ ViewExprOps.GetSelection[r.oldSelect ! ViewExprOps.noSelection => {ok _ FALSE; CONTINUE}]; IF ok THEN { state: State _ NARROW[selectedViewer.data]; SELECT r.construct FROM integer => { replacement _ MathDisplayExpr.DisplayExprFromExpr[MathConstructors.MakeInt[r.fromRope]]; }; real => { replacement _ MathDisplayExpr.DisplayExprFromExpr[MathConstructors.MakeReal[Convert.RealFromRope[r.fromRope]]]; }; variable => { replacement _ MathDisplayExpr.DisplayExprFromExpr[MathConstructors.MakeVariable[r.fromRope]]; }; ENDCASE => ERROR; MathDisplayExpr.Replace[selectedExpr, replacement ! MathDisplayExpr.replaceAll => {state.displayExpr _ replacement; CONTINUE}]; state.displayBox _ state.displayExpr.Format[normal]; ViewExprOps.UnSelect[r.oldSelect]; ViewExprOps.Select[r.newSelect, selectedViewer, replacement]; }; -- end of "if ok" }; -- end of "replace Parse action choice" w: REF WrapAction => { selectedViewer: Viewer _ NIL; selectedExpr: DisplayExpr _ NIL; ok: BOOL _ TRUE; [selectedViewer, selectedExpr] _ ViewExprOps.GetSelection[w.selection ! ViewExprOps.noSelection => {ok _ FALSE; CONTINUE}]; IF ok THEN WrapTemplateAround[w.class, selectedViewer, selectedExpr]; }; -- end of "wrap Parse action choice" n: REF NoAction => NULL; -- do nothing; no action ENDCASE => ERROR; }; Scale: Menus.MenuProc ~ { s: State _ NARROW[clientData]; SELECT mouseButton FROM red => {s.scale _ s.scale * 1.5}; yellow => {s.scale _ 1.0}; blue => {s.scale _ s.scale / 1.5}; ENDCASE => ERROR; ViewerOps.PaintViewer[s.exprViewer, client]; }; ConvertToRope: Menus.MenuProc ~ { v: ViewerTools.Viewer _ NIL; s: State _ NARROW[clientData]; primaryViewer: Viewer _ NIL; primaryExpr: DisplayExpr _ NIL; IF ~ViewExprOps.Active[$primary] THEN { MessageWindow.Append["Make a primary selection first.", TRUE]; RETURN; -- no selection }; [primaryViewer, primaryExpr] _ ViewExprOps.GetSelection[$primary]; v _ ViewerTools.MakeNewTextViewer[info: [name: "Rope"]]; ViewerTools.SetContents[v, MathDisplayExpr.ASRopeFromDisplayExpr[primaryExpr]]; }; Break: SIGNAL = CODE; EnterDebugger: Menus.MenuProc ~ { s: State _ NARROW[clientData]; expr: DisplayExpr _ s.displayExpr; SIGNAL Break; }; CopySecondaryToPrimary: PROC[] ~ { primaryState, secondaryState: State; replacement, primaryExpr, copyExpr: DisplayExpr _ NIL; primaryViewer, copyViewer: Viewer _ NIL; IF ~(ViewExprOps.Active[$primary] AND ViewExprOps.Active[$copy]) THEN { MessageWindow.Append["Insufficient Selections for Copy.", TRUE]; RETURN; }; [primaryViewer, primaryExpr] _ ViewExprOps.GetSelection[$primary]; [copyViewer, copyExpr] _ ViewExprOps.GetSelection[$copy]; primaryState _ NARROW[primaryViewer.data]; secondaryState _ NARROW[copyViewer.data]; replacement _ MathDisplayExpr.Copy[copyExpr]; MathDisplayExpr.Replace[primaryExpr, replacement ! MathDisplayExpr.replaceAll => {primaryState.displayExpr _ replacement; CONTINUE}]; primaryState.displayBox _ primaryState.displayExpr.Format[normal]; ViewExprOps.UnSelect[$primary]; ViewExprOps.UnSelect[$copy]; }; MoveSecondaryToPrimary: PROC[] ~ { primaryState, secondaryState: State; replacement, primaryExpr, moveExpr: DisplayExpr _ NIL; moveViewer, primaryViewer: Viewer _ NIL; IF ~(ViewExprOps.Active[$primary] AND ViewExprOps.Active[$move]) THEN { MessageWindow.Append["Insufficient Selections for Move.", TRUE]; RETURN; }; [primaryViewer, primaryExpr] _ ViewExprOps.GetSelection[$primary]; [moveViewer, moveExpr] _ ViewExprOps.GetSelection[$move]; primaryState _ NARROW[primaryViewer.data]; secondaryState _ NARROW[moveViewer.data]; replacement _ MathDisplayExpr.Copy[moveExpr]; MathDisplayExpr.Replace[primaryExpr, replacement ! MathDisplayExpr.replaceAll => {primaryState.displayExpr _ replacement; CONTINUE}]; primaryState.displayBox _ primaryState.displayExpr.Format[normal]; ViewExprOps.UnSelect[$primary]; DeleteSelection[$move, FALSE]; }; EnterMatrix: Menus.MenuProc ~ { primaryViewer: Viewer _ NIL; primaryExpr: DisplayExpr _ NIL; oneToTen: LIST OF ROPE _ LIST["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]; nRows, nCols: INT _ 0; rows: LIST OF LIST OF EXPR _ NIL; replacement: DisplayExpr _ NIL; ok: BOOL _ TRUE; s: State _ NARROW[clientData]; [primaryViewer, primaryExpr] _ ViewExprOps.GetSelection[$primary ! ViewExprOps.noSelection => {ok _ FALSE; CONTINUE}]; IF ~ok OR (primaryViewer # s.exprViewer) THEN { MessageWindow.Append["Make a primary selection first.", TRUE]; RETURN; -- no selection to replace }; nRows _ PopUpMenu.RequestSelection[label: "# Rows", choice: oneToTen, timeOut: 10]; IF (nRows < 1) THEN RETURN; -- no selection or timeout nCols _ PopUpMenu.RequestSelection[label: "# Cols", choice: oneToTen, timeOut: 10]; IF (nCols < 1) THEN RETURN; -- no selection or timeout rows _ NIL; -- cons up list of rows FOR r:INT IN [1..nRows] DO currentRow: LIST OF EXPR _ NIL; -- cons up list of elements FOR c:INT IN [1..nCols] DO currentRow _ CONS[MathConstructors.MakeInt["0"], currentRow]; ENDLOOP; rows _ CONS[currentRow, rows]; ENDLOOP; replacement _ MathDisplayExpr.DisplayExprFromExpr[MathConstructors.MakeMatrix[nRows, nCols, rows]]; MathDisplayExpr.Replace[primaryExpr, replacement ! MathDisplayExpr.replaceAll => {s.displayExpr _ replacement; CONTINUE}]; s.displayBox _ s.displayExpr.Format[normal]; ViewExprOps.PaintEnqueue[s.exprViewer]; ViewExprOps.FlushPaintQueue[]; }; EnterExpression: Menus.MenuProc ~ { exprClassName: ATOM; choices: LIST OF ROPE _ NIL; choiceClassNames: LIST OF ATOM _ NIL; selection: INT _ 0; ok: BOOL _ TRUE; primaryViewer: Viewer _ NIL; primaryExpr: DisplayExpr _ NIL; s: State _ NARROW[clientData]; [primaryViewer, primaryExpr] _ ViewExprOps.GetSelection[$primary ! ViewExprOps.noSelection => {ok _ FALSE; CONTINUE}]; IF ~ok OR (primaryViewer # s.exprViewer) THEN { MessageWindow.Append["Make a primary selection first.", TRUE]; RETURN; -- no selection to replace }; FOR l: LIST OF ATOM _ MathDB.CompoundClassNames, l.rest UNTIL l = NIL DO description: ROPE _ MathDB.LookupCompoundClass[l.first].description; choices _ CONS[description, choices]; choiceClassNames _ CONS[l.first, choiceClassNames]; ENDLOOP; selection _ PopUpMenu.RequestSelection[label: "Choose Expr", choice: choices, timeOut: 10]; IF (selection < 1) THEN RETURN; -- no selection or timeout FOR l: LIST OF ATOM _ choiceClassNames, l.rest UNTIL l = NIL DO selection _ selection - 1; IF selection = 0 THEN {exprClassName _ l.first; EXIT}; ENDLOOP; IF (selection # 0) THEN RETURN; -- should never occur if pop-up works ok ReplaceWithTemplate[exprClassName, primaryViewer, primaryExpr]; }; WrapExpression: Menus.MenuProc ~ { choices: LIST OF ROPE _ NIL; choiceClassNames: LIST OF ATOM _ NIL; selection: INT _ 0; exprClassName: ATOM; ok: BOOL _ TRUE; primaryViewer: Viewer _ NIL; primaryExpr: DisplayExpr _ NIL; s: State _ NARROW[clientData]; [primaryViewer, primaryExpr] _ ViewExprOps.GetSelection[$primary ! ViewExprOps.noSelection => {ok _ FALSE; CONTINUE}]; IF ~ok OR (primaryViewer # s.exprViewer) THEN { MessageWindow.Append["Make a primary selection first.", TRUE]; RETURN; -- no selection to replace }; FOR l: LIST OF ATOM _ MathDB.CompoundClassNames, l.rest UNTIL l = NIL DO description: ROPE _ MathDB.LookupCompoundClass[l.first].description; choices _ CONS[description, choices]; choiceClassNames _ CONS[l.first, choiceClassNames]; ENDLOOP; selection _ PopUpMenu.RequestSelection[label: "Choose Expr", choice: choices, timeOut: 10]; IF (selection < 1) THEN RETURN; -- no selection or timeout FOR l: LIST OF ATOM _ choiceClassNames, l.rest UNTIL l = NIL DO selection _ selection - 1; IF selection = 0 THEN {exprClassName _ l.first; EXIT}; ENDLOOP; IF (selection # 0) THEN RETURN; -- should never occur if pop-up works ok WrapTemplateAround[exprClassName, primaryViewer, primaryExpr]; }; WrapTemplateAround: PROC [class: ATOM, viewer: Viewer, expr: DisplayExpr] ~ { exprClass: CompoundClass; -- class data associated with class argExprs: LIST OF MathExpr.TaggedMathExpr _ NIL; replacement, selectableSibling: DisplayExpr _ NIL; hotTag: ATOM; -- tag of "hot" argument s: State _ NARROW[viewer.data]; exprClass _ MathDB.LookupCompoundClass[class]; FOR l: LIST OF Argument _ exprClass.arguments, l.rest UNTIL l = NIL DO hot: BOOL _ FALSE; FOR aliases: LIST OF ATOM _ l.first.aliases, aliases.rest UNTIL aliases = NIL DO IF aliases.first = $aliasHot THEN {hotTag _ l.first.name; hot _ TRUE; EXIT}; ENDLOOP; IF hot AND (expr.Class[] # $placeholder) THEN { argExprs _ CONS[[l.first.name, MathDisplayExpr.ExprFromDisplayExpr[expr]], argExprs]; } ELSE { argExprs _ CONS[[l.first.name, MathConstructors.MakePlaceHolder[]], argExprs]; }; ENDLOOP; replacement _ MathDisplayExpr.DisplayExprFromExpr[MathExpr.MakeCompoundExpr[class, argExprs]]; MathDisplayExpr.Replace[expr, replacement ! MathDisplayExpr.replaceAll => {s.displayExpr _ replacement; CONTINUE}]; s.displayBox _ s.displayExpr.Format[normal]; FOR l: LIST OF DisplayExpr _ replacement.GetSubExprs[], l.rest UNTIL l = NIL DO IF l.first.Tag[] = hotTag THEN { selectableSibling _ l.first.SelectableSibling[! MathDisplayExpr.noSelection => CONTINUE]; EXIT; -- break out of FOR loop }; ENDLOOP; ViewExprOps.PaintEnqueue[s.exprViewer]; IF selectableSibling # NIL THEN ViewExprOps.Select[$primary, s.exprViewer, selectableSibling] ELSE ViewExprOps.UnSelect[$primary]; ViewExprOps.FlushPaintQueue[]; }; ReplaceWithTemplate: PROC [class: ATOM, viewer: Viewer, expr: DisplayExpr] ~ { exprClass: CompoundClass; argExprs: LIST OF MathExpr.TaggedMathExpr _ NIL; replacement: DisplayExpr _ NIL; s: State _ NARROW[viewer.data]; exprClass _ MathDB.LookupCompoundClass[class]; FOR l: LIST OF Argument _ exprClass.arguments, l.rest UNTIL l = NIL DO argExprs _ CONS[[l.first.name, MathConstructors.MakePlaceHolder[]], argExprs]; ENDLOOP; replacement _ MathDisplayExpr.DisplayExprFromExpr[MathExpr.MakeCompoundExpr[class, argExprs]]; MathDisplayExpr.Replace[expr, replacement ! MathDisplayExpr.replaceAll => {s.displayExpr _ replacement; CONTINUE}]; s.displayBox _ s.displayExpr.Format[normal]; ViewExprOps.PaintEnqueue[s.exprViewer]; ViewExprOps.FlushPaintQueue[]; }; EnterAtom: Menus.MenuProc ~ { displayExpr: DisplayExpr _ NIL; choices: LIST OF ROPE _ LIST["integer", "real", "variable"]; selection: INT _ 0; ok: BOOL _ TRUE; primaryViewer: Viewer _ NIL; primaryExpr: DisplayExpr _ NIL; s: State _ NARROW[clientData]; [primaryViewer, primaryExpr] _ ViewExprOps.GetSelection[$primary ! ViewExprOps.noSelection => {ok _ FALSE; CONTINUE}]; IF ~ok OR (primaryViewer # s.exprViewer) THEN { MessageWindow.Append["Make a primary selection first.", TRUE]; RETURN; -- no selection to replace }; selection _ PopUpMenu.RequestSelection[label: "Choose Atom", choice: choices, timeOut: 10]; { ENABLE MathConstructors.badFormat, Convert.Error => { MessageWindow.Append["Input format error.", TRUE]; MessageWindow.Blink[]; GOTO abort; }; SELECT selection FROM 1 => { displayExpr _ MathDisplayExpr.DisplayExprFromExpr[MathConstructors.MakeInt[ViewerTools.GetSelectionContents[]]]; }; 2 => { displayExpr _ MathDisplayExpr.DisplayExprFromExpr[MathConstructors.MakeReal[Convert.RealFromRope[ViewerTools.GetSelectionContents[]]]]; }; 3 => { displayExpr _ MathDisplayExpr.DisplayExprFromExpr[MathConstructors.MakeVariable[ViewerTools.GetSelectionContents[]]]; }; ENDCASE => RETURN; ViewExprOps.UnSelect[$primary]; -- unselect currently active selection MathDisplayExpr.Replace[primaryExpr, displayExpr ! MathDisplayExpr.replaceAll => { s.displayExpr _ displayExpr; CONTINUE}]; s.displayBox _ s.displayExpr.Format[normal]; ViewerOps.PaintViewer[s.exprViewer, client]; }; EXITS abort => NULL; }; Home: Menus.MenuProc ~ { s: State _ NARROW[clientData]; s.offsetX _ 0.0; s.offsetY _ 0.0; ViewerOps.PaintViewer[s.exprViewer, client]; }; VerticalScroll: ViewerClasses.ScrollProc ~ { s: State _ NARROW[self.data]; -- get viewer state info topPercentage: INTEGER _ 100 + Real.RoundI[((-self.ch + (s.physicalBox.Offset[].y - s.physicalBox.Extents[].descent)) / s.physicalBox.Height[]) * 100]; bottomPercentage: INTEGER _ 100 - Real.RoundI[((s.physicalBox.Extents[].descent - s.physicalBox.Offset[].y) / s.physicalBox.Height[]) * 100]; SELECT op FROM query => { RETURN[top: MIN[100, MAX[0, topPercentage]], bottom: MAX[0, MIN[100, bottomPercentage]]]; }; up => { s.offsetY _ s.offsetY + amount; ViewerOps.PaintViewer[s.exprViewer, client]; }; down => { s.offsetY _ s.offsetY - amount; ViewerOps.PaintViewer[s.exprViewer, client]; }; thumb => RETURN[]; ENDCASE => ERROR; }; HorizontalScroll: ViewerClasses.HScrollProc ~ { s: State _ NARROW[self.data]; -- get viewer state info leftPercentage: INTEGER _ Real.RoundI[((s.physicalBox.Extents[].leftExtent - s.physicalBox.Offset[].x) / s.physicalBox.Width[]) * 100]; rightPercentage: INTEGER _ Real.RoundI[((self.cw - (s.physicalBox.Offset[].x - s.physicalBox.Extents[].leftExtent)) / s.physicalBox.Width[]) * 100]; SELECT op FROM query => { RETURN[left: MIN[100, MAX[0, leftPercentage]], right: MAX[0, MIN[100, rightPercentage]]]; }; left => { s.offsetX _ s.offsetX - amount; ViewerOps.PaintViewer[s.exprViewer, client]; }; right => { s.offsetX _ s.offsetX + amount; ViewerOps.PaintViewer[s.exprViewer, client]; }; thumb => RETURN[]; ENDCASE => ERROR; }; Create: PUBLIC PROC[expr: EXPR _ NIL, name: ROPE] RETURNS[Viewer] ~ { v: Viewer _ NIL; -- expression viewer instance s: State; -- expression viewer state expression: EXPR _ expr; -- intial expression to display in viewer displayExpr: DisplayExpr _ NIL; -- display form of expr displayBox: BOX _ NIL; -- box for displayExpr IF expression = NIL THEN expression _ MathConstructors.MakePlaceHolder[]; displayExpr _ MathDisplayExpr.DisplayExprFromExpr[expression]; displayBox _ MathDisplayExpr.Format[displayExpr, normal]; v _ ViewerOps.CreateViewer[flavor: $expr, info: [label: name, name: name]]; s _ NARROW[v.data]; s.displayExpr _ displayExpr; s.displayBox _ displayBox; ViewerOps.PaintViewer[v, all]; RETURN[v]; }; SetContents: PUBLIC PROC[viewer: Viewer, expr: EXPR] ~ { s: State _ NARROW[viewer.data]; expression: EXPR _ expr; IF expression = NIL THEN expression _ MathConstructors.MakePlaceHolder[]; s.displayExpr _ MathDisplayExpr.DisplayExprFromExpr[expression]; s.displayBox _ MathDisplayExpr.Format[s.displayExpr, normal]; ViewExprOps.UnSelectViewer[viewer]; -- destroy any references to old contents ViewerOps.PaintViewer[viewer, client]; -- repaint updated viewer }; GetContents: PUBLIC PROC[viewer: Viewer] RETURNS[ROPE] ~ { s: State _ NARROW[viewer.data]; RETURN[MathDisplayExpr.ASRopeFromDisplayExpr[s.displayExpr]]; }; RegisterExprViewerClass: PROC ~ { ViewerOps.RegisterViewerClass[ $expr, NEW[ViewerClasses.ViewerClassRec _ [ init: InitializeExprViewer, destroy: DestroyExprViewer, notify: NotifyExprViewer, tipTable: TIPUser.InstantiateNewTIPTable["ViewExpr.tip"], icon: Icons.NewIconFromFile["MathEditor.icons", 0], paint: PaintExprViewer, scroll: VerticalScroll, hscroll: HorizontalScroll ]] ]; }; RegisterExprViewerClass[]; END. ‚Carl Waldspurger, August 19, 1986 11:30:07 pm PDT ViewExprImpl.mesa Type Abbreviations From Imported Interfaces Constants Type Definitions ViewerClass $expr Global Data (global only to this module) effects: Deletes selection of type flavor, if active. Replaces selection with an expression "placeholder". This placeholder remians selected iff reselect is TRUE. local declarations reformat entire display expr following select will repaint viewer to reflect change select placeholder replacement effects: Initializes an expression viewer instance. effects: Peforms orderly termination of an expression viewer. if self contains any selections, unselect them remove self from paint queue effects: Performs actions based on parsed TIP events. grab the input focus so keyboard events get parsed copy char data & parse it can't make a move selection w/o an existing primary selection can't make a copy selection w/o an existing primary selection local declarations make sure selection of flavor already exists (if not, return) local declarations make sure selection of flavor already exists (if not, return) local declarations make sure selection of flavor already exists (if not, return) extend active primary or keyboard selection to its parent as a primary selection local declarations flush any pending paint requests effects: Repaints contents of an expression viewer. local declarations modify scale factor by user-specified growth factor cons up list of selections to highlight spray the bits onto screen via imager effects: Returns the lexical type of c this pointless extra level of indirection proves that variants LOSE BIG TIME in Cedar oh, what I'd give for CLU oneof's... modifies: kbBuffer, message window, $keyboard selection effects: Parses c w.r.t contents of kbBuffer. Updates kbBuffer, message window, and $keyboard selection (viewer & expr). local declarations first check to see if c is an operator type newly active keyboard; replace $primary with keystroke already active keyboard; extend current selection perform parse action local declarations actually do the replace reformat display expr unselect the old primary selection select will repaint viewer to reflect change (replacement) local declarations effects: Changes viewer scale factor. If selected with left button, scale _ scale * 1.5, right button, scale _ scale / 1.5, middle button, scale _ 1.0 effects: Creates a new text viewer with contents = AS value of active selection effects: SIGNALS Break to enter the symbolic debugger. effects: If both primary and secondary selections are active, replaces primary selection by secondary selection. local declarations reformat primary expression select ops below will repaint viewers to reflect replacement effects: If both primary and move selections are active, replaces primary selection by move selection and deletes secondary selection. local declarations reformat primary expression select ops below will repaint viewers to reflect replacement effects: Replaces active primary selection with a template for a matrix whose size is chosen from a series op pop-up menus. Returns without action if any errors occur. local declarations display pop-up menu for # rows then #cols contruct a zero matrix of size nRows x nCols replace expr in viewer with replacement reformat entire display expr repaint viewer to reflect replacement make sure that changes are seen effects: Replaces active primary selection with a template for an expression type which is chosen from a pop-up menu. Returns without action if any errors occur. local declarations display pop-up menu of all possible compound expression classes construct list of choices do pop-up menu find selection class name match selection (INT) with class name effects: Wraps around primarySelection a template for an expression type which is chosen from a pop-up menu. Returns without action if any errors occur. local declarations display pop-up menu of all possible compound expression classes construct list of choices do pop-up menu find selection class name match selection (INT) with class name (ROPE -> ATOM) perform the wrap requires: viewer contains expr modifies: viewer.data effects: Wraps a template for an expression of compound class "class" around expr. The primary selection is changed to be a sibling of the "hot" argument. local declarations lookup classname in database create expressions for each argument to make LIST OF TaggedMathExpr replace expr in viewer with replacement reformat entire display expr find selectable sibling repaint viewer to reflect replacement if possible, select sibling as primary selection make sure that changes are seen requires: viewer contains expr modifies: viewer.data effects: Replaces expr in viewer with a template for an class expression. local declarations lookup classname in database create expressions for each argument to make LIST OF TaggedMathExpr replace expr in viewer with replacement reformat entire display expr repaint viewer to reflect replacement make sure that changes are seen effects: Replaces active selection with an atomic expression. Returns without action if any errors (e.g. parsing) occur. local declarations do pop-up menu replace active selection reformat entire display expr repaint viewer to reflect replacement effects: Repaints viewer after zeroing all offsets from scrolling. effects: Performs verical scrolling of an expression viewer. compute current top & bottom percentage visible metrics effects: Performs horizontal scrolling of an expression viewer. compute current left & right percentage visible metrics effects: Creates a new instance of an expression viewer with banner name. Viewer will contain math expression expr. If expr = NIL, then Viewer will contain an empty "placeholder". If expr to display is defaulted, use empty placeholder instantiate new viewer set its state paint new viewer return the newly spawned viewer modifies: viewer effects: Changes the contents of math expression viewer viewer to be expr. If expr to display is defaulted, use empty placeholder effects: Returns the contents of viewer as a ROPE in a format understood by the AlgebraStructures parser. effects: Registers a new viewer class for expression viewers. Κ˜Jšœ1™1Jšœ™J˜codešΟk ˜ Kšœ ˜ Kšœ˜Kšœ˜Kšœ œ˜"Kšœœ˜#Kšœ ˜ Kšœ˜Kšœ ˜ Kšœ˜Kšœ œ˜#Kšœœ˜$Kšœ ˜ Kšœ˜Kšœ˜Kšœœ ˜Kšœ˜K˜K˜ Kšœ ˜ K˜K˜Kšœ˜Kšœ ˜ K˜K˜ Kšœ ˜ K˜K˜—šΟn œœ˜KšœΡ˜ΨKšœ ˜K˜K˜Kšž+™+˜Kšœœœ˜Kšœœ œ˜Kšœœ˜$Kšœ œ˜#Kšœœ˜-Kšœœ œ˜Kšœ œ˜0—K˜šž ™ K˜Kšœœ Οc/˜TK˜K˜—Kšž™˜Kšž™K™Kšœœœ ˜šœ œœ˜KšœŸ˜4KšœœŸ=˜VKšœœŸ'˜5KšœŸ ˜(Kšœ œŸ˜0Kšœ œŸ1˜CK˜K˜——K˜šž)™)K™Kšœ œ(œ˜>K˜—K˜š žœœ œ œœ˜>Kšœ6™6Kšœ>™>KšœA™AK˜Kšœ™K˜ Kšœ)œ˜-Kšœœ˜K˜KšœœœŸ'˜SK˜BK˜KšœV˜VK˜Kšœœ˜$Kšœtœ˜K˜Kšœ™K˜4K˜Kšœ6™6Kšœ™Kšœ œ9œ˜kK˜—K˜K˜šžœ˜0Kšœ4™4K˜šœ œ ˜K˜K˜ K˜ K˜ Kšœ œ˜Kšœ œ˜Kšœ œ˜K˜—Kšœœ˜Kšœœ˜K˜K˜'K˜KK˜IK˜TK˜SK˜]K˜_K˜YK˜[K˜K˜—K˜šžœ˜0Kšœ>™>K˜Kšœ œ ˜K˜Kšœ.™.K˜!K™Kšœ™K˜KšœœŸ˜4K˜—K˜šžœ˜/Kšœ6™6K˜Kšœ œ ˜Kšœœ˜Kšœœ˜K˜Kšœ2™2K˜$K˜Kšœ œ˜˜šœœœ˜Kšœ™K˜ K˜K˜K˜—K˜ ˜Kšœ˜˜šœ˜Kšœ·œ˜ΓK˜—šœ˜Kšœ=™=šœœ˜'Kšœ8œ˜>K˜Kšœ˜Kšœ˜K˜—Kšœ±œ˜½K˜—šœ˜Kšœ=™=šœœ˜'Kšœ8œ˜>K˜Kšœ˜Kšœ˜K˜—Kšœ±œ˜½K˜—K˜K˜˜?Kšœ™Kšœœ˜ Kšœœ˜ Kšœœ˜K˜šœ˜K˜*K˜$K˜$Kšœœ˜K˜—Kšœ=™=Kšœœœ˜+K˜K˜BK˜Kšœ{œ˜‡K˜—K˜˜Kšœ œ ˜Kšœ œœœ˜3Kšœœ˜Kšœœ˜ K˜Kš œœœœœœŸ˜RK˜Kšœ3™3K˜$K˜ΨK˜Kšœ'™'šœœ˜&K˜DKšœ œM˜]K˜—šœœ˜#K˜AKšœ œ?œ˜bK˜—šœœ˜#K˜AKšœ œ?œ˜bK˜—šœœ˜'K˜EKšœ œ?œ˜bK˜—K˜K˜Kšœ%™%K˜HK˜—K˜Kšœ œ1˜>šžœœœœ ˜+Kšœ'™'šœ˜ Kšœ œ˜Kšœ œ ˜Kšœ œ ˜Kšœœ ˜Kšœ7œ˜BKšœœ˜—K˜K˜—K˜KšœU™UKšœ$™$K™Kšœ œœ˜'Kšœœ˜.Kšœ œ˜(Kšœ œ˜&šœœœ˜šœ˜˜ Kšœ œŸ6˜HKšœ'Ÿ#˜JKšœ œŸ˜)Kšœ œŸ2˜BK˜—˜ Kšœ œŸ˜)KšœœŸ˜*K˜—KšœœŸ ˜Kš˜—K˜K˜—šž œœœ˜Kšœ7™7Kšœ.™.KšœT™TK™Kšœ™KšœŸ˜;KšœœœŸ˜KKšœœ˜K˜Kšœ+™+šœ œ˜Kšœ œŸ+˜JKšœœŸ˜Sšœ˜ Kšœœ(˜:Kšœœ/˜AKšœœ,˜>Kšœœ(˜:Kšœœ.˜@Kšœœ.˜@Kšœœ.˜@Kšœœ(˜:Kšœœ'˜9Kšœœ(˜:Kšœœ-˜?Kšœœ*˜™>Kšœ6™6Kšœ œ ˜šœ ˜K˜!K˜K˜"Kšœœ˜—K˜,K˜—K˜šž œ˜!KšœP™PK˜Kšœœ˜Kšœ œ ˜Kšœœ˜Kšœœ˜K˜šœœ˜'Kšœ8œ˜>KšœŸ˜K˜—K˜K˜BK˜K˜8K˜OK˜—K˜Kšžœœœ˜šž œ˜!Kšœ6™6K™Kšœ œ ˜K˜"Kšœ˜ K˜—K˜šžœœ˜"Kšœ>™>Kšœ<™KšœŸ˜#K˜—K˜Kšœ)™)K˜SKšœ œœŸ˜7K˜SKšœ œœŸ˜7K˜Kšœ,™,KšœœŸ˜%šœœœ ˜Kš œ œœœœŸ˜<šœœœ ˜Kšœ œ,˜=Kšœ˜—Kšœœ˜Kšœ˜K˜—K˜cK˜Kšœ(™(Kšœoœ˜zK˜Kšœ™K˜,K˜Kšœ%™%K˜'K˜Kšœ™K˜K˜K˜˜K˜——šžœ˜#KšœR™RKšœ-™-K™5K˜Kšœ™Kšœœ˜Kš œ œœœœ˜Kš œœœœœ˜%Kšœ œ˜Kšœœœ˜Kšœœ˜Kšœœ˜K˜Kšœ œ ˜Kšœdœœ˜všœœ œ˜/Kšœ8œ˜>KšœŸ˜#K˜—K˜Kšœ?™?K™Kšœ™š œœœœ%œœ˜HKšœ œ3˜DKšœ œ˜%Kšœœ˜3Kšœ˜—K˜Kšœ™K˜[KšœœœŸ˜;K˜Kšœ™š œœœœœœ˜?Kšœ&™&K˜Kšœœœ˜6Kšœ˜—K˜KšœœœŸ(˜JK˜K˜?K˜—K˜šžœ˜"KšœI™IKšœ-™-K™5K˜Kšœ™Kš œ œœœœ˜Kš œœœœœ˜%Kšœ œ˜Kšœœ˜Kšœœœ˜Kšœœ˜Kšœœ˜K˜Kšœ œ ˜Kšœdœœ˜všœœ œ˜/Kšœ8œ˜>KšœŸ˜#K˜—K˜Kšœ?™?K™Kšœ™š œœœœ%œœ˜HKšœ œ3˜DKšœ œ˜%Kšœœ˜3Kšœ˜—K˜Kšœ™K˜[KšœœœŸ˜;K˜Kšœ™š œœœœœœ˜?Kšœ4™4K˜Kšœœœ˜6Kšœ˜—K˜KšœœœŸ(˜JK˜Kšœ™K˜>K˜—K˜šžœœ œ(˜MKšœ™Kšœ™KšœS™SKšœQ™QK˜Kšœ™KšœŸ#˜>Kšœ œœœ˜0Kšœ.œ˜2KšœœŸ˜'K˜Kšœ œ˜ K˜Kšœ™Kšœ.˜.K˜KšœC™Cš œœœ(œœ˜FKšœœœ˜š œ œœœ!œ œ˜PKšœœœœ˜LKšœ˜—šœœœ˜/Kšœ œF˜UK˜—šœ˜Kšœ œ?˜NK˜—Kšœ˜—K˜K˜^K˜Kšœ(™(Kšœhœ˜sK˜Kšœ™K˜,K˜Kšœ™š œœœ1œœ˜Ošœœ˜ KšœOœ˜YKšœŸ˜K˜—Kšœ˜—K˜Kšœ%™%K˜'K˜Kšœ0™0Kšœœœ?œ ˜‚K˜Kšœ™K˜K˜—K˜šžœœ œ(˜NKšœ™Kšœ™KšœK™KK˜Kšœ™K˜Kšœ œœœ˜0Kšœœ˜K˜Kšœ œ˜ K˜Kšœ™Kšœ.˜.K˜KšœC™Cš œœœ(œœ˜FKšœ œ?˜NKšœ˜—K˜K˜^K˜Kšœ(™(Kšœhœ˜sK˜Kšœ™K˜,K˜Kšœ%™%K˜'K˜Kšœ™K˜K˜—˜K˜—K˜šž œ˜Kšœ>™>KšœD™DK˜Kšœ™Kšœœ˜Kš œ œœœœ ˜KšœŸ˜#K˜—K˜Kšœ™K˜[K˜Kšœ˜šœ/˜5Kšœ,œ˜2K˜Kšœ˜ K˜—šœ ˜˜K˜pK˜—˜K˜‡K˜—˜Kšœu˜uK˜—Kšœœ˜—K˜Kšœ™Kšœ!Ÿ&˜GKšœpœ˜{K˜Kšœ™K˜,K˜Kšœ%™%K˜,K˜š˜Kšœ œ˜—K˜K˜K˜K˜—šžœ˜KšœC™CK˜Kšœ œ ˜K˜K˜K˜,K˜K˜—K˜šžœ˜,Kšœ=™=K˜Kšœ œŸ˜7K˜Kšœ7™7Kšœœ˜—Kšœœt˜K˜šœ˜šœ ˜ Kš œœœœœ˜[K˜—šœ˜Kšœ˜K˜,K˜—˜ K˜K˜,K˜—Kšœ œ˜Kšœœ˜—K˜K˜K˜—šžœ˜/Kšœ@™@K˜Kšœ œŸ˜7K˜Kšœ7™7Kšœœp˜‡Kšœœ|˜”K˜šœ˜šœ ˜ Kš œœœœœ˜ZK˜—˜ K˜K˜,K˜—˜ K˜K˜,K˜—Kšœ œ˜Kšœœ˜—K˜K˜K˜—šžœœœœœœœ ˜EKšœJ™JK™HK™5K™ Kšœ œŸ˜/Kšœ Ÿ˜%Kšœ œ Ÿ)˜CKšœœŸ˜7Kšœ œœŸ˜.K˜Kšœ6™6Kšœœœ1˜IK˜K˜>K˜9K˜Kšœ™KšœK˜KK˜Kšœ ™ Kšœœ ˜K˜K˜K˜Kšœ™K˜K˜Kšœ™Kšœ˜ K˜K˜—K˜šž œœœœ˜8Jšœ™Jšœ@™@Jšœ™J™Kšœ œ˜Kšœ œ˜K˜Kšœ6™6Kšœœœ1˜IK˜K˜@K˜=K˜Kšœ%Ÿ)˜NKšœ(Ÿ˜AK˜K˜—K˜š ž œœœœœ˜:Jšœ6™6Jšœ>™>J™Kšœ œ˜Kšœ7˜=K˜K˜K˜—šžœœ˜!Kšœ>™>K˜˜K˜šœ!˜$K˜K˜K˜K˜9K˜3K˜K˜K˜K˜—K˜—K˜K˜—K˜K˜K˜K˜Kšœ˜K˜K˜K˜——…—kϊ€˜