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 lastDisplayExpr: DisplayExpr _ NIL -- last display expression (for undo, etc.) ]; kbBuffer: RECORD[type: {integer, real, variable}, data: ROPE]; SetDisplayExpr: PROC[viewer: Viewer, replacement: DisplayExpr] ~ { state: State _ NARROW[viewer.data]; -- get current state state.lastDisplayExpr _ state.displayExpr; -- old gets current state.displayExpr _ replacement; -- current gets new state.displayBox _ state.displayExpr.Format[normal]; -- current gets new }; UndoViewer: PROC[viewer: Viewer] ~ { state: State _ NARROW[viewer.data]; -- get current state ViewExprOps.UnSelectViewer[viewer]; SetDisplayExpr[viewer, state.lastDisplayExpr]; ViewerOps.PaintViewer[viewer, client]; }; ReplaceInViewer: PROC[viewer: Viewer, old, new: DisplayExpr] ~ { state: State _ NARROW[viewer.data]; replacement: DisplayExpr _ MathDisplayExpr.Replace[state.displayExpr, old, new]; SetDisplayExpr[viewer, replacement]; }; DeleteSelection: PROC[flavor: ATOM, reselect: BOOL _ TRUE] ~ { 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[]]; ReplaceInViewer[selectedViewer, selectedExpr, replacement]; 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.menu _ Menus.CreateMenu[lines: 3]; self.menu.AppendMenuEntry[Menus.CreateEntry[name: "Debug", proc: EnterDebugger, clientData: self.data, guarded: TRUE], 0]; 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["Undo", Undo, self.data], 0]; self.menu.AppendMenuEntry[Menus.CreateEntry["FromRope", ConvertFromRope, self.data], 1]; self.menu.AppendMenuEntry[Menus.CreateEntry["ToRope", ConvertToRope, self.data], 1]; self.menu.AppendMenuEntry[Menus.CreateEntry["ToASRope", ConvertToASRope, self.data], 1]; self.menu.AppendMenuEntry[Menus.CreateEntry["ReplaceWithMatrix", EnterMatrix, self.data], 2]; self.menu.AppendMenuEntry[Menus.CreateEntry["ReplaceWithVector", EnterVector, self.data], 2]; self.menu.AppendMenuEntry[Menus.CreateEntry["ReplaceWithExpr", EnterExpression, self.data], 2]; self.menu.AppendMenuEntry[Menus.CreateEntry["ReplaceWithAtom", EnterAtom, self.data], 2]; self.menu.AppendMenuEntry[Menus.CreateEntry["WrapWithExpr", WrapExpression, self.data], 2]; }; 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[]; }; SelectionColor: PROC[flavor: ATOM] RETURNS[Imager.Color] ~ { RETURN[ SELECT flavor FROM $primary => ImagerColor.ColorFromAtom[$Invert], $copy => ImagerBackdoor.MakeStipple[8421H, TRUE], $move => ImagerBackdoor.MakeStipple[8020H, TRUE], $keyboard => ImagerBackdoor.MakeStipple[00F0H, TRUE], ENDCASE => ImagerColor.ColorFromAtom[$Invert] ]; }; PaintExprViewer: ViewerClasses.PaintProc ~ { scaleFactor: REAL _ fontToViewerSizeRatio; -- scaling factor s: State _ NARROW[self.data]; highlight: LIST OF MathDisplayExpr.Selection _ 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)]]; FOR l: LIST OF ATOM _ LIST[$primary, $copy, $move, $keyboard], l.rest UNTIL l = NIL DO IF ViewExprOps.Active[l.first] THEN { selectedViewer: Viewer _ NIL; selectedExpr: DisplayExpr _ NIL; [selectedViewer, selectedExpr] _ ViewExprOps.GetSelection[l.first]; IF selectedViewer = s.exprViewer THEN highlight _ CONS[[selectedExpr, SelectionColor[l.first]], highlight]; }; ENDLOOP; MathDisplayExpr.Paint[s.displayExpr, context, s.physicalBox, highlight]; }; LexType: TYPE ~ {ucAlpha, lcAlpha, digit, decimal, op, other}; LexChar: PROC[c: CHAR] RETURNS[LexType] ~ { RETURN[ SELECT c FROM IN ['0..'9] => digit, IN ['a..'z] => lcAlpha, IN ['A..'Z] => ucAlpha, '. => decimal, '+, '- , '*, '^, '=, '>, '<, '&, '|, '/, '(, '~, '#, ', => op, ENDCASE => 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, $list]]]; '- => 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 { 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; ReplaceInViewer[selectedViewer, selectedExpr, replacement]; 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]; }; ConvertToASRope: 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]; MessageWindow.Blink[]; RETURN; -- no selection }; [primaryViewer, primaryExpr] _ ViewExprOps.GetSelection[$primary]; v _ ViewerTools.MakeNewTextViewer[info: [name: Rope.Cat["ASRope for ", s.exprViewer.name]]]; ViewerTools.SetContents[v, MathDisplayExpr.ASRopeFromDisplayExpr[primaryExpr]]; }; ConvertToRope: Menus.MenuProc ~ { v: ViewerTools.Viewer _ NIL; s: State _ NARROW[clientData]; primaryViewer: Viewer _ NIL; primaryExpr: DisplayExpr _ NIL; ropeVal: ROPE _ NIL; IF ~ViewExprOps.Active[$primary] THEN { MessageWindow.Append["Make a primary selection first.", TRUE]; MessageWindow.Blink[]; RETURN; -- no selection, so complain }; [primaryViewer, primaryExpr] _ ViewExprOps.GetSelection[$primary]; ropeVal _ MathExpr.RopeFromExpr[MathDisplayExpr.ExprFromDisplayExpr[primaryExpr]]; v _ ViewerTools.MakeNewTextViewer[info: [name: Rope.Cat["Rope for ", s.exprViewer.name]]]; ViewerTools.SetContents[v, ropeVal]; }; ConvertFromRope: Menus.MenuProc ~ { ropeVal: ROPE _ ViewerTools.GetSelectionContents[]; -- get rope from text selection exprVal, primaryExpr: DisplayExpr _ NIL; ok: BOOL _ TRUE; primaryViewer: Viewer _ NIL; IF ~ViewExprOps.Active[$primary] THEN { MessageWindow.Append["Make a primary selection first.", TRUE]; MessageWindow.Blink[]; RETURN; -- no selection, so complain }; [primaryViewer, primaryExpr] _ ViewExprOps.GetSelection[$primary]; exprVal _ MathDisplayExpr.DisplayExprFromExpr[MathExpr.ExprFromRope[ropeVal ! MathExpr.parseError => {ok _ FALSE; CONTINUE}]]; IF ok THEN { ReplaceInViewer[primaryViewer, primaryExpr, exprVal]; ViewExprOps.UnSelect[$primary]; ViewExprOps.FlushPaintQueue[]; -- update viewer contents } ELSE { MessageWindow.Append["Expression Format Error.", TRUE]; MessageWindow.Blink[]; }; }; Break: SIGNAL = CODE; EnterDebugger: Menus.MenuProc ~ { s: State _ NARROW[clientData]; expr: DisplayExpr _ s.displayExpr; SIGNAL Break; }; CopySecondaryToPrimary: PROC[] ~ { 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]; MessageWindow.Blink[]; RETURN; }; [primaryViewer, primaryExpr] _ ViewExprOps.GetSelection[$primary]; [copyViewer, copyExpr] _ ViewExprOps.GetSelection[$copy]; replacement _ MathDisplayExpr.Copy[copyExpr]; ReplaceInViewer[primaryViewer, primaryExpr, replacement]; ViewExprOps.UnSelect[$primary]; ViewExprOps.UnSelect[$copy]; }; MoveSecondaryToPrimary: PROC[] ~ { moveReplacement, primaryExpr, moveExpr: DisplayExpr _ NIL; moveViewer, primaryViewer: Viewer _ NIL; IF ~(ViewExprOps.Active[$primary] AND ViewExprOps.Active[$move]) THEN { MessageWindow.Append["Insufficient Selections for Move.", TRUE]; MessageWindow.Blink[]; RETURN; }; [primaryViewer, primaryExpr] _ ViewExprOps.GetSelection[$primary]; [moveViewer, moveExpr] _ ViewExprOps.GetSelection[$move]; moveReplacement _ MathDisplayExpr.Copy[moveExpr]; IF moveViewer = primaryViewer THEN { state: State _ NARROW[primaryViewer.data]; placeholder: DisplayExpr _ MathDisplayExpr.DisplayExprFromExpr[MathConstructors.MakePlaceHolder[]]; doubleReplacement: DisplayExpr _ MathDisplayExpr.ReplaceN[state.displayExpr, LIST[[old: primaryExpr, new: moveReplacement], [old: moveExpr, new: placeholder]]]; SetDisplayExpr[primaryViewer, doubleReplacement]; -- mutate viewer expression ViewExprOps.PaintEnqueue[primaryViewer]; } ELSE { ReplaceInViewer[primaryViewer, primaryExpr, moveReplacement]; 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]; MessageWindow.Blink[]; RETURN; -- no selection to replace }; nRows _ PopUpMenu.RequestSelection[label: "# Rows", choice: oneToTen]; IF (nRows < 1) THEN RETURN; -- no selection or timeout nCols _ PopUpMenu.RequestSelection[label: "# Cols", choice: oneToTen]; 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]]; ReplaceInViewer[primaryViewer, primaryExpr, replacement]; ViewExprOps.PaintEnqueue[s.exprViewer]; ViewExprOps.FlushPaintQueue[]; }; EnterVector: Menus.MenuProc ~ { primaryViewer: Viewer _ NIL; primaryExpr: DisplayExpr _ NIL; oneToTen: LIST OF ROPE _ LIST["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]; dimension: INT _ 0; elements: LIST OF EXPR _ NIL; replacement: DisplayExpr _ NIL; row: INT _ 0; -- =1 => row vector, =2 => col vector, ELSE => invalid rowVec: BOOL _ FALSE; -- TRUE iff vector is a row vector 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]; MessageWindow.Blink[]; RETURN; -- no selection to replace }; row _ PopUpMenu.RequestSelection[label: "Vector Type", choice: LIST["row", "column"]]; IF (row < 1) OR (row > 2) THEN RETURN; -- no selection or timeout IF row = 1 THEN rowVec _ TRUE; dimension _ PopUpMenu.RequestSelection[label: "Dimension", choice: oneToTen]; IF (dimension < 1) THEN RETURN; -- no selection or timeout FOR i: INT IN [1..dimension] DO elements _ CONS[MathConstructors.MakeInt["0"], elements]; ENDLOOP; replacement _ MathDisplayExpr.DisplayExprFromExpr[MathConstructors.MakeVector[dimension, elements, rowVec]]; ReplaceInViewer[primaryViewer, primaryExpr, replacement]; 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]; MessageWindow.Blink[]; 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]; 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]; MessageWindow.Blink[]; 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]; 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 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]]; ReplaceInViewer[viewer, expr, replacement]; 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[viewer]; IF selectableSibling # NIL THEN ViewExprOps.Select[$primary, viewer, 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; 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]]; ReplaceInViewer[viewer, expr, replacement]; ViewExprOps.PaintEnqueue[viewer]; ViewExprOps.FlushPaintQueue[]; }; EnterAtom: Menus.MenuProc ~ { displayExpr: DisplayExpr _ NIL; choices: LIST OF ROPE _ LIST["greek variable", "infinity", "integer", "real", "variable"]; selection: INT _ 0; ok: BOOL _ TRUE; primaryViewer: Viewer _ NIL; primaryExpr: DisplayExpr _ NIL; textSelection: ROPE _ NIL; integer: INT = 3; real: INT = 4; variable: INT = 5; greekVar: INT = 1; infinity: INT = 2; 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]; MessageWindow.Blink[]; RETURN; -- no selection to replace }; selection _ PopUpMenu.RequestSelection[label: "Choose Atom", choice: choices]; { ENABLE MathConstructors.badFormat, Convert.Error => { MessageWindow.Append["Input format error.", TRUE]; MessageWindow.Blink[]; GOTO abort; }; textSelection _ ViewerTools.GetSelectionContents[]; SELECT selection FROM integer => { displayExpr _ MathDisplayExpr.DisplayExprFromExpr[MathConstructors.MakeInt[textSelection]]; }; real => { displayExpr _ MathDisplayExpr.DisplayExprFromExpr[MathConstructors.MakeReal[Convert.RealFromRope[textSelection]]]; }; variable => { displayExpr _ MathDisplayExpr.DisplayExprFromExpr[MathConstructors.MakeVariable[textSelection]]; }; infinity => { displayExpr _ MathDisplayExpr.DisplayExprFromExpr[MathConstructors.MakeInfinity[]]; }; greekVar => { greekName: ROPE _ NIL; greekChoices: LIST OF ROPE _ LIST["alpha", "beta", "gamma", "delta", "epsilon", "zeta", "eta", "theta", "iota", "kappa", "lambda", "mu", "nu", "xi", "pi", "rho", "sigma", "tau", "upsilon", "phi", "chi", "psi", "omega"]; selection _ PopUpMenu.RequestSelection[label: "Choose Variable", choice: greekChoices]; IF selection < 1 THEN RETURN; -- timeout or no selection FOR l: LIST OF ROPE _ greekChoices, l.rest UNTIL l = NIL DO selection _ selection - 1; IF selection = 0 THEN {greekName _ l.first; EXIT}; ENDLOOP; displayExpr _ MathDisplayExpr.DisplayExprFromExpr[MathConstructors.MakeGreekVar[greekName]]; }; ENDCASE => RETURN; ViewExprOps.UnSelect[$primary]; -- unselect currently active selection ReplaceInViewer[primaryViewer, primaryExpr, displayExpr]; ViewerOps.PaintViewer[s.exprViewer, client]; }; EXITS abort => NULL; }; Undo: Menus.MenuProc ~ { s: State _ NARROW[clientData]; UndoViewer[s.exprViewer]; }; 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 _ displayExpr.Format[normal]; v _ ViewerOps.CreateViewer[flavor: $expr, info: [label: name, name: name, scrollable: TRUE, hscrollable: TRUE]]; s _ NARROW[v.data]; s.displayExpr _ displayExpr; s.lastDisplayExpr _ displayExpr; -- for UNDO 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[]; ReplaceInViewer[viewer, s.displayExpr, MathDisplayExpr.DisplayExprFromExpr[expression]]; 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["Meddle.icons", 0], paint: PaintExprViewer, scroll: VerticalScroll, hscroll: HorizontalScroll ]] ]; }; RegisterExprViewerClass[]; END. !hCarl Waldspurger, August 29, 1986 2:30:34 pm PDT ViewExprImpl.mesa Type Abbreviations From Imported Interfaces Constants Type Definitions ViewerClass $expr Global Data (global only to this module) modifies: viewer effects: Sets and formats display expression for viewer to replacement. intent: ALL assignments to viewer display expressions and boxes should go thru this procedure to keep "UNDO" information valid. modifies: viewer effects: Restores viewer state (expression & box info) to previous state. cancel any current selections in viewer since UNDO may invalidate them replace current expr by last saved expr now paint the viewer so UNDO is seen modifies: viewer effects: Replaces old in the display expression for viewer with new. Reformats the display expression for viewer. effects: Deletes selection of type flavor, if active. Replaces selection with an expression "placeholder". This placeholder remians selected iff reselect is TRUE. local declarations 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: Returns the color associated with flavor. If no such association exists, uses "$Invert" color. 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 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 get viewer & expression associated with primary selection pop up a new text viewer and convert to rope effects: Creates a new text viewer with contents = canonical output form of active seln. make sure there is an active primary selection get viewer & expression associated with primary selection convert expression to rope pop up a new text viewer containing rope effects: Replaces primary selection with expression parsed from linear GetSelection ROPE. local declarations make sure there is an active primary selection get viewer & expression associated with primary selection replace primary selection with exprVal can't do it (parse error), so complain 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 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 same viewer, so use ReplaceN to make two simultaneous replacements different viewers, so do one replacement at a time 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 repaint viewer to reflect replacement make sure that changes are seen effects: Replaces active primary selection with a template for a vector whose size and type is chosen from a series op pop-up menus. Returns without action if any errors occur. local declarations display pop-up menus for vector type and dimension contruct a zero matrix of size dimension replace expr in viewer with replacement 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 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 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 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 constants do pop-up menu get text viewer selection, if any (not all choices use this) local declarations display a second pop-up menu to choose greek letter from match selection (INT) with class name (ROPE -> ATOM) replace active selection repaint viewer to reflect replacement effects: "Undoes" last action on viewer by restoring previously saved state. 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 replace entire display expr for viewer 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šœ0™0Jšœ™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˜DKšœœŸ+˜OK˜K˜——K˜šž)™)K™Kšœ œ(œ˜>K˜—K˜šžœœ/˜CKšœ™KšœI™IKšœ Οzœ;™IK™CK™KšœœŸ˜9K˜Kšœ,Ÿ˜?Kšœ"Ÿ˜5K˜Kšœ6Ÿ˜IK˜—K˜šž œœ˜$Kšœ™KšœJ™JK™KšœœŸ˜9K˜KšœF™FK˜$K˜Kšœ'™'K˜.K˜Kšœ$™$K˜&K˜K˜—K˜šžœœ+˜@Kšœ™KšœE™EKšœ6™6K˜Kšœœ˜#K˜PK˜$K˜K˜K˜—š žœœ œ œœ˜>Kšœ6™6Kšœ>™>KšœA™AK˜Kšœ™Kšœ)œ˜-Kšœœ˜K˜KšœœœŸ'˜SK˜BK˜KšœV˜VK˜;K˜Kšœ6™6Kšœ™Kšœ œ9˜IKšœ˜"K˜—K˜K˜šžœ˜0Kšœ4™4K˜šœ œ ˜K˜K˜ K˜ K˜ Kšœ œ˜Kšœ œ˜Kšœ œ˜K˜—K˜'Kšœpœ˜zK˜KK˜IK˜IK˜XK˜TK˜XK˜]K˜]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˜/Kšœ+œ˜1Kšœ+œ˜1Kšœ/œ˜5Kšœ&˜-K˜——K˜K˜—K˜šžœ˜,Kšœ4™4Kšœ˜Kšœ™Kšœ œŸ˜>Kšœ œ ˜Kšœ œœœ˜3K˜Kš œœœœœœŸ˜RK˜Kšœ3™3K˜$K˜ΨK˜Kšœ'™'šœœœœœ,œœ˜Všœœ˜%Kšœœ˜Kšœœ˜ K˜CKšœœ œ5˜kK˜—Kšœ˜—K˜Kšœ%™%K˜HK˜K˜—K˜Kšœ œ1˜>šžœœœœ ˜+Kšœ'™'šœ˜Kšœ˜ Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ>˜>Kšœ ˜K˜—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šœœ)˜;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˜Kšœ9™9K˜BK˜Kšœ,™,K˜\K˜OK˜—K˜šž œ˜!KšœY™YK™Kšœœ˜Kšœ œ ˜Kšœœ˜Kšœœ˜Kšœ œœ˜K˜Kšœ.™.šœœ˜'Kšœ8œ˜>K˜KšœŸ˜&K˜—K˜Kšœ9™9K˜BK˜Kšœ™K˜RK˜Kšœ(™(K˜ZK˜$K˜—K˜šžœ˜#KšœZ™ZK™Kšœ™Kšœ œ(Ÿ˜TKšœ$œ˜(Kšœœœ˜Kšœœ˜K˜Kšœ.™.šœœ˜'Kšœ8œ˜>K˜KšœŸ˜&K˜—K˜Kšœ9™9K˜BK˜Kšœkœœ˜~K˜šœœ˜ Kšœ&™&K˜5K˜Kšœ Ÿ˜9K˜—šœ˜Kšœ&™&Kšœ1œ˜7K˜K˜—K˜K˜K˜K˜—K˜Kšžœœœ˜šž œ˜!Kšœ6™6K™Kšœ œ ˜K˜"Kšœ˜ K˜—K˜šžœœ˜"Kšœ>™>Kšœ<™K˜KšœŸ˜#K˜—K˜Kšœ)™)K˜FKšœ œœŸ˜7K˜FKšœ œœŸ˜7K˜Kšœ,™,KšœœŸ˜%šœœœ ˜Kš œ œœœœŸ˜<šœœœ ˜Kšœ œ,˜=Kšœ˜—Kšœœ˜Kšœ˜K˜—K˜cK˜Kšœ(™(K˜9K˜Kšœ%™%K˜'K˜Kšœ™K˜K˜—K˜šž œ˜KšœH™HKšœF™FKšœ5™5K™Kšœ™Kšœœ˜Kšœœ˜Kš œ œœœœ4˜QKšœ œ˜Kš œ œœœœ˜Kšœœ˜KšœœŸ6˜EKšœœœŸ#˜:Kšœœœ˜K˜Kšœ œ ˜Kšœdœœ˜všœœ œ˜/Kšœ8œ˜>K˜KšœŸ˜#K˜—K˜Kšœ2™2K™Kšœ?œ˜VKš œ œ œœŸ˜BKšœ œ œ˜K˜K˜MKšœœœŸ˜;K˜Kšœ(™(šœœœ˜Kšœ œ*˜9Kšœ˜K˜—K˜lK˜Kšœ(™(K˜9K˜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™Kšœ™š œœœœ%œœ˜HKšœ œ3˜DKšœ œ˜%Kšœœ˜3Kšœ˜—K˜Kšœ™K˜NKšœœœŸ˜;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™Kšœ™š œœœœ%œœ˜HKšœ œ3˜DKšœ œ˜%Kšœœ˜3Kšœ˜—K˜Kšœ™K˜NKšœœœŸ˜;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šœC™Cš œœœ(œœ˜FKšœœœ˜š œ œœœ!œ œ˜PKšœœœœ˜LKšœ˜—šœœœ˜/Kšœ œF˜UK˜—šœ˜Kšœ œ?˜NK˜—Kšœ˜—K˜K˜^K˜Kšœ(™(K˜+K˜Kšœ™š œœœ1œœ˜Ošœœ˜ KšœOœ˜YKšœŸ˜K˜—Kšœ˜—K˜Kšœ%™%K˜!K˜Kšœ0™0Kšœœœ9œ ˜|K˜Kšœ™K˜K˜—K˜šžœœ œ(˜NKšœ™Kšœ™KšœK™KK˜Kšœ™K˜Kšœ œœœ˜0Kšœœ˜K˜Kšœ™Kšœ.˜.K˜KšœC™Cš œœœ(œœ˜FKšœ œ?˜NKšœ˜—K˜K˜^K˜Kšœ(™(K˜+K˜Kšœ%™%K˜!K˜Kšœ™K˜K˜—˜K˜—K˜šž œ˜Kšœ>™>KšœD™DK˜Kšœ™Kšœœ˜Kš œ œœœœ>˜ZKšœ œ˜Kšœœœ˜Kšœœ˜Kšœœ˜Kšœœœ˜K˜Kšœ ™ Kšœ œ˜Kšœœ˜Kšœ œ˜Kšœ œ˜Kšœ œ˜K˜Kšœ œ ˜Kšœdœœ˜všœœ œ˜/Kšœ8œ˜>K˜KšœŸ˜#K˜—K˜Kšœ™K˜NK˜Kšœ˜šœ/˜5Kšœ,œ˜2K˜Kšœ˜ K˜—K˜Kšœ<™K˜(K˜Kšœ™KšœVœœ˜pK˜Kšœ ™ 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˜XK˜Kšœ%Ÿ)˜NKšœ(Ÿ˜AK˜K˜—K˜š ž œœœœœ˜:Jšœ6™6Jšœ>™>J™Kšœ œ˜Kšœ7˜=K˜K˜K˜—šžœœ˜!Kšœ>™>K˜˜K˜šœ!˜$K˜K˜K˜K˜9K˜/K˜K˜K˜K˜—K˜—K˜K˜—K˜K˜K˜K˜Kšœ˜K˜K˜K˜——…—|Αω