DIRECTORY Convert USING [RopeFromInt], Graph USING [CaretIndex, ColorIndex, Entity, FontIndex, EntityList, EntityRec, NumberOfColors, NumberOfFonts, ROPE, Text, TextRec, Texts, UnitLineWidth, ValueList, Viewer, XY], GraphCarets USING [Move, TurnOff, TurnOn], GraphConvert USING [RopeOfSlope, ValueListFromRope], GraphCleanUp USING [CleanUpVL], GraphPrivate USING [AddEntityButton, AddTextButton, EntityHashSize, GraphAtomProc, GraphHandle, PaintAction, PaintAll, PaintEntity, PaintGrids, PaintTarget, PaintText, systemColor], GraphUtil USING [BlinkMsg, EntityFromId, GetIntField, GetRealField, InitSegAll, RaiseError, RealToScreenI, SetColor, TextFromId, UpdateSegAll, UpdateSegEnd], Imager USING [Error], ImagerFont USING [Find, Font, Scale], Rope USING [Cat, Concat], VFonts USING [EstablishFont], ViewerTools USING [GetContents, SetContents]; GraphUpdate: CEDAR PROGRAM IMPORTS Convert, GraphCarets, GraphCleanUp, GraphConvert, GraphPrivate, GraphUtil, Imager, ImagerFont, Rope, VFonts, ViewerTools EXPORTS GraphPrivate = { OPEN Graph, GraphPrivate, GraphUtil; Update: PUBLIC GraphAtomProc = { IF handle # NIL THEN IF handle.controller # NIL THEN { OPEN handle; msg: ROPE; i: INT; -- r: REAL; SELECT atom FROM $Divisions => UpdateDivisions[handle]; $Bounds => UpdateBounds[handle]; $Carets => UpdateCarets[handle]; $Targets => UpdateTargets[handle]; $Grids => UpdateGrids[handle]; $Color => { [msg, i] _ GetIntField[controller.colorIndex]; IF msg = NIL THEN IF i IN ColorIndex THEN UpdateColor[handle, i] ELSE BlinkMsg[Rope.Cat[ "Index must be in [0..", Convert.RopeFromInt[NumberOfColors], ")"]]; }; $Font => { [msg, i]_ GetIntField[controller.fontIndex]; IF msg = NIL THEN IF i IN FontIndex THEN UpdateFont[handle, i] ELSE BlinkMsg[Rope.Cat[ "Index must be in [0..", Convert.RopeFromInt[NumberOfFonts], ")"]]; }; $Text => { [msg, i] _ GetIntField[controller.textId]; IF msg = NIL THEN { text: Text _ TextFromId[handle.allTexts, i]; UpdateText[handle, text, i]; -- text = nil => add it. }; }; $XEntry => UpdateX[handle]; $Entity => { [msg, i] _ GetIntField[controller.entityId]; IF msg = NIL THEN { entity: Entity _ EntityFromId[entityHash, i]; UpdateEntity[handle, entity, i]; }; }; ENDCASE => RaiseError[$UnknownAtom, "in Update"]; }; }; -- Update UpdateDivisions: PROC[handle: GraphHandle] = { OPEN handle; graph.auto[divisions] _ controller.auto[divisions]; graph.division[x] _ UpdateFromIntField[controller.divX, graph.division[x]]; graph.division[y] _ UpdateFromIntField[controller.divY, graph.division[x]]; IF graph.division[x] < 1 OR graph.division[y] < 1 THEN BlinkMsg["x or y divisions < 1."] ELSE IF chart.viewer # NIL THEN PaintAll[handle, TRUE]; -- paint everything. }; -- SetDivisionFields UpdateBounds: PROC[handle: GraphHandle] = { OPEN handle; graph.auto[bounds] _ controller.auto[bounds]; graph.bounds _ [ xmin: UpdateFromRealField[controller.xmin, graph.bounds.xmin], ymin: UpdateFromRealField[controller.ymin, graph.bounds.ymin], xmax: UpdateFromRealField[controller.xmax, graph.bounds.xmax], ymax: UpdateFromRealField[controller.ymax, graph.bounds.ymax] ]; IF NOT graph.auto[bounds] AND (graph.bounds.xmin <= graph.bounds.xmax OR graph.bounds.ymin <= graph.bounds.ymax) THEN BlinkMsg["Illegal bounds. (xmin <= xmax or ymin <= ymax.)"] ELSE IF chart.viewer # NIL THEN PaintAll[handle, TRUE]; }; -- UpdateBounds UpdateCarets: PROC[handle: GraphHandle] = { OPEN handle; UpdateCaret: PROC[index: CaretIndex] = { vx: Viewer _ controller.caretPlace[index][x]; vy: Viewer _ controller.caretPlace[index][y]; rx: REAL _ UpdateFromRealField[vx, graph.caret[index].place.x]; ry: REAL _ UpdateFromRealField[vy, graph.caret[index].place.y]; sx: INTEGER _ RealToScreenI[handle, rx, x]; sy: INTEGER _ RealToScreenI[handle, ry, y]; graph.caret[index]^ _ [on: controller.caretOn[index], place: [rx, ry]]; IF chart.viewer # NIL THEN { GraphCarets.Move[handle, sx, sy, index]; IF graph.caret[index].on THEN GraphCarets.TurnOn[handle, index] ELSE GraphCarets.TurnOff[handle, index]; }; }; -- UpdateCaret FOR i: CaretIndex IN CaretIndex DO UpdateCaret[primary] ENDLOOP; ViewerTools.SetContents[controller.slope, IF (graph.showSlope _ controller.slopeOn) THEN GraphConvert.RopeOfSlope[graph.caret[primary].place, graph.caret[secondary].place] ELSE NIL]; }; -- UpdateCarets UpdateTargets: PROC[handle: GraphHandle] = { OPEN handle; UpdateTarget: PROC[xy: XY] = { IF chart.viewer # NIL AND graph.target[xy].on THEN PaintTarget[handle, erase, xy]; graph.target[xy].value _ UpdateFromRealField[ controller.targetValue[xy], graph.target[xy].value]; graph.target[xy].width _ UpdateFromRealField[ controller.targetWidth[xy], graph.target[xy].width]; graph.target[xy].colorIndex _ UpdateFromIntField[ controller.targetColor[xy], graph.target[xy].colorIndex]; IF graph.target[xy].colorIndex NOT IN ColorIndex THEN { graph.target[xy].colorIndex _ 13; BlinkMsg["Color index out of range. Default is used."]; }; IF (graph.target[xy].on _ controller.targetOn[xy]) THEN IF chart.viewer # NIL THEN PaintTarget[handle, paint, xy]; }; UpdateTarget[x]; UpdateTarget[y]; }; -- UpdateTargets UpdateGrids: PROC[handle: GraphHandle] = { OPEN handle; UpdateGrid: PROC[wasOn, isOn: BOOL, xy: XY] = { IF wasOn # isOn THEN { IF chart.viewer # NIL THEN { IF wasOn THEN PaintGrids[handle, erase, xy]; PaintGrids[handle, paint, xy]; -- paint grids or ticks. }; graph.grids[xy] _ isOn; }; }; UpdateGrid[graph.grids[x], controller.gridOn[x], x]; UpdateGrid[graph.grids[y], controller.gridOn[y], y]; }; -- UpdateGrids UpdateColor: PROC[handle: GraphHandle, index: ColorIndex] = { OPEN handle; GetRGB: PROC[v: Viewer, r: REAL] RETURNS [rgb: REAL] = { rgb _ UpdateFromRealField[v, r]; IF rgb > 1.0 OR rgb < 0.0 THEN { rgb _ MAX[0.0, MIN[1.0, rgb]]; BlinkMsg["color value limited by [0..1]."]; }; }; graph.color[index].R _ GetRGB[controller.red, graph.color[index].R]; graph.color[index].G _ GetRGB[controller.green, graph.color[index].G]; graph.color[index].B _ GetRGB[controller.blue, graph.color[index].B]; systemColor[index] _ SetColor[index, graph.color[index]]; }; -- UpdateColor UpdateFont: PROC[handle: GraphHandle, index: FontIndex] = { OPEN handle; PaintTextsOfThisFont: PROC [action: PaintAction] = { IF chart.viewer # NIL THEN FOR ts: Texts _ graph.texts, ts.rest UNTIL ts = NIL DO text: Text _ ts.first; IF text.fontIndex = index THEN PaintText[handle, action, text]; ENDLOOP; }; -- PaintTextsOfThisFont msg: ROPE; vFontSize: INT; pFontScale: REAL; vFont, pFont: ImagerFont.Font _ NIL; fontFamily: ROPE _ ViewerTools.GetContents[controller.fontFamily]; IF fontFamily = NIL THEN {BlinkMsg["Specify font family."]; RETURN}; [msg, vFontSize] _ GetIntField[controller.vFontSize]; IF msg # NIL THEN RETURN; [msg, pFontScale] _ GetRealField[controller.pFontScale]; IF msg # NIL THEN RETURN; vFont _ VFonts.EstablishFont[fontFamily, vFontSize, controller.boldOn, controller.italicOn, TRUE]; -- true means default on failure. pFont _ ImagerFont.Scale[ ImagerFont.Find[Rope.Cat[ "Xerox/PressFonts/", fontFamily, Rope.Concat[IF controller.boldOn THEN "-b" ELSE "-m", IF controller.italicOn THEN "ir" ELSE "rr"]]], pFontScale ! Imager.Error => {pFont _ NIL; CONTINUE}]; IF pFont = NIL THEN BlinkMsg["Can't find font."] ELSE { PaintTextsOfThisFont[erase]; graph.font[index] _ [fontFamily, controller.boldOn, controller.italicOn, vFontSize, pFontScale]; imagerFonts[screen][index] _ vFont; imagerFonts[interpress][index] _ pFont; PaintTextsOfThisFont[paint]; }; }; -- UpdateFont UpdateText: PROC[handle: GraphHandle, text: Text, id: INT] = { OPEN handle; rope: ROPE _ ViewerTools.GetContents[controller.textContent]; msg: ROPE _ NIL; add: BOOL _ text = NIL; int: INT; GetTextPlace: PROC [v: Viewer, original: REAL] RETURNS [r: REAL] = { r _ UpdateFromRealField[v, original]; IF r > 3.0 OR r < - 3.0 THEN { r _ MAX[-3.0, MIN[3.0, r]]; BlinkMsg["Text coordinates limited by [-3..3]"]; }; }; -- GetTextPlace IF rope = NIL THEN {BlinkMsg["name field is empty."]; RETURN}; IF add THEN text _ NEW[TextRec _ []] ELSE IF chart.viewer # NIL THEN PaintText[handle, erase, text]; int _ UpdateFromIntField[controller.textFont, text.fontIndex]; IF int NOT IN FontIndex THEN msg _ "Illegal font index." ELSE text.fontIndex _ int; int _ UpdateFromIntField[controller.textColor, text.colorIndex]; IF int NOT IN ColorIndex THEN msg _ msg.Cat[" Illegal color index."] ELSE text.colorIndex _ int; IF msg # NIL THEN {BlinkMsg[msg]; text _ NIL; RETURN}; text^ _ [ text: rope, place: [GetTextPlace[controller.textPlaceX, text.place.x], GetTextPlace[controller.textPlaceY, text.place.y]], fontIndex: text.fontIndex, colorIndex: text.colorIndex, justifX: controller.justifX, justifY: controller.justifY, id: id ]; IF chart.viewer # NIL THEN PaintText[handle, paint, text]; IF add THEN { handle.allTexts _ CONS[text, handle.allTexts]; graph.texts _ CONS[text, graph.texts]; [] _ AddTextButton[handle, text]; }; }; -- UpdateText UpdateX: PROC [handle: GraphHandle] = { OPEN handle; -- proc no use for now. length, pos: INT; vList: ValueList; [vList, length, pos] _ GraphConvert.ValueListFromRope[ ViewerTools.GetContents[controller.xValues]]; IF pos # 0 THEN RETURN; IF vList = NIL THEN {BlinkMsg["The values field is empty."]; RETURN}; IF length # entityGroupList.first.length THEN { BlinkMsg[Rope.Cat["There should be ", Convert.RopeFromInt[entityGroupList.first.length], "numbers in the values field."]]; RETURN}; UpdateSegEnd[entityGroupList.first.x, vList]; IF chart.viewer # NIL THEN PaintAll[handle]; }; -- UpdateX UpdateEntity: PROC [handle: GraphHandle, entity: Entity, id: INT] = { OPEN handle; msg: ROPE _ NIL; add: BOOL _ entity = NIL; vList: ValueList; length, pos, int: INT _ 0; painted: BOOL _ FALSE; [vList, length, pos] _ GraphConvert.ValueListFromRope[ ViewerTools.GetContents[controller.entityValues]]; IF pos # 0 THEN RETURN; IF vList = NIL THEN {BlinkMsg["Error in the values field."]; RETURN}; IF length # entityGroupList.first.length THEN { BlinkMsg[Rope.Cat["There should be ", Convert.RopeFromInt[entityGroupList.first.length], "numbers in the values field."]]; RETURN}; IF add THEN entity _ NEW[EntityRec _ []] ELSE { FOR el: EntityList _ graph.entityList, el.rest UNTIL el = NIL DO IF el.first = entity THEN painted _ TRUE; ENDLOOP; IF chart.viewer # NIL AND painted THEN PaintEntity[handle, erase, entity]; }; int _ UpdateFromIntField[controller.entityColor, entity.colorIndex]; IF int NOT IN ColorIndex THEN {BlinkMsg[" Illegal color index."]; RETURN}; entity^ _ [ name: ViewerTools.GetContents[controller.entityName], colorIndex: int, mark: controller.mark, width: UpdateFromRealField[controller.entityWidth, entity.width], oldValues: IF add THEN vList ELSE entity.oldValues, segments: entity.segments, group: IF entity.group = NIL THEN entityGroupList.first ELSE entity.group, -- for now. id: id ]; IF add THEN { -- add it on handle and on table. index: INT[0..EntityHashSize) _ id MOD EntityHashSize; entityHash[index] _ CONS[entity, entityHash[index]]; entity.group.ys.first.entityList _ CONS[entity, entity.group.ys.first.entityList]; -- for now. [] _ AddEntityButton[handle, entity]; -- for now. InitSegAll[entity]; } ELSE UpdateSegAll[entity, vList]; vList _ GraphCleanUp.CleanUpVL[vList]; IF add OR NOT painted THEN { -- add it on graph graph.entityList _ CONS[entity, graph.entityList]; }; IF chart.viewer # NIL THEN PaintEntity[handle, paint, entity]; }; -- UpdateEntity UpdateFromIntField: PROC[field: Viewer, origianl: INT _ 0, showMsg: BOOL _ TRUE] RETURNS [int: INT _ 0] = { msg: ROPE; [msg, int] _ GetIntField[field, showMsg]; IF msg # NIL THEN int _ origianl; }; -- UpdateFromIntField UpdateFromRealField: PROC[field: Viewer, origianl: REAL _ 0.0, showMsg: BOOL _ TRUE] RETURNS [real: REAL _ 0.0] = { msg: ROPE; [msg, real] _ GetRealField[field, showMsg]; IF msg # NIL THEN real _ origianl; }; -- UpdateFromRealField }. LOG. SChen, created at October 9, 1985 8:57:21 pm PDT. GraphUpdate.mesa, Copyright c 1985 by Xerox Corporation. All rights reserved. Last Edited by: Sweetsun Chen, October 22, 1985 3:43:23 pm PDT If handle = nil or controller = nil then noop; otherwise: updates internal values based on users input on panel and, if chart.viewer # nil, also updates display. $CurveGroup => { [msg, i] _ GetIntField[controller.entityId]; IF msg = NIL THEN { eg: EntityGroup _ EntityGroupFromId[handle.entityGroupList, i]; UpdateEntityGroup[handle, eg, i]; }; }; $Crosssection => { [msg, r] _ GetRealField[controller.entityId]; IF msg = NIL THEN UpdateCrosssection[controller, x]; }; no fields on panel have to do with blinking. eGroup: EntityGroup; [msg, groupId] _ GetIntField[controller.entityGroupId, "group id"]; IF msg # NIL THEN RETURN; eGroup _ EntityGroupFromId[handle.entityGroupList, groupId]; IF eGroup = NIL THEN { BlinkMsg[Rope.Cat["There is no group with id = ", Convert.RopeFromInt[groupId], "."]]; RETURN; }; UpdateEntityGroup: PROC[handle: GraphHandle, eg: EntityGroup] = { OPEN handle; }; -- UpdateEntityGroup UpdateCrosssection: PROC[handle: GraphHandle, x: REAL] = { OPEN handle; rope, tRope: ROPE; ok: BOOL; y: REAL; FOR egl: EntityGroupList _ graph.entityGroupList, egl.rest UNTIL egl = NIL DO xEntity: Entity _ egl.first.x; rope _ IF rope = NIL THEN "[" ELSE rope.Concat[", ["]; FOR el: EntityList _ egl.first.entityList, el.rest UNTIL el = NIL DO IF el.first = xEntity THEN LOOP; [ok, y] _ Crosssection[xEntity.newValues, el.first, x]; tRope _ IF ok THEN Convert.RopeFromReal[y] ELSE "(*)"; rope _ IF rope = NIL THEN tRope ELSE rope.Cat[" ", tRope]; ENDLOOP; rope.Concat["]"]; ENDLOOP; ViewerTools.SetContent[controller.yIds, rope]; }; -- UpdateCrosssection caller should make sure that field is not nil. caller should make sure that field is not nil. Κ“˜JšœΟmœ1™Nšœ™Icode™.—J˜šΟk ˜ Jšœžœ˜Jšœžœcžœ:žœ˜°Jšœ žœ˜*Jšœ žœ"˜4Jšœ žœ ˜Jšœ žœ£˜΅Jšœ žœŽ˜Jšœžœ ˜Jšœ žœ˜%Jšœžœ˜Jšœžœ˜Jšœ žœ˜-—J˜šœ žœž˜Jšžœy˜€Jšžœžœ ˜=J˜—šœžœ˜ ™9J™g—šžœ žœžœžœžœžœžœ˜CJšœžœžœΟcΠikŸ˜šžœž˜Jšœ&˜&Jšœ ˜ Jšœ ˜ Jšœ"˜"Jšœ˜šœ ˜ Jšœ.˜.šžœžœž˜Jšžœžœ žœ˜.šžœ˜JšœD˜D——J˜—˜ Jšœ,˜,šžœžœžœ˜Jšžœžœ žœ˜,šžœ˜JšœC˜C——J˜—šœ ˜ Jšœ*˜*šžœžœžœ˜Jšœ,˜,JšœŸ˜5J˜—J˜—Jšœ˜šœ ˜ Jšœ,˜,šžœžœžœ˜Jšœ-˜-Jšœ ˜ J˜—J˜—™Jšœ,™,šžœžœžœ™Jšœ?™?Jšœ!™!J™—J™—šœ™Jšœ-™-Jšžœžœžœ#™4J™—Jšžœ*˜1—J˜—JšœŸ ˜ J˜šΟnœžœžœ˜;Jšœ3˜3JšœK˜KJšœK˜KJšžœžœžœ"˜XJš žœžœžœžœžœŸ˜LJšœŸ˜—J˜š‘ œžœžœ˜8Jšœ-˜-šœ˜Jšœ>˜>Jšœ>˜>Jšœ>˜>Jšœ=˜=J˜—Jš žœžœžœ)žœ(žœ<˜±Jš žœžœžœžœžœ˜7JšœŸ˜—J˜š‘ œžœžœ˜8š‘ œžœ˜(Jšœ-˜-Jšœ-˜-Jšœžœ7˜?Jšœžœ7˜?Jšœžœ ˜+Jšœžœ ˜+JšœG˜Gšžœžœžœ˜Jšœ(˜(Jšžœžœ"˜?Jšžœ$˜(J™,J˜—JšœŸ˜—J˜Jšžœžœ žœžœ˜@J˜šœ)˜)šžœ(ž˜.JšœR˜R—Jšžœžœ˜ —JšœŸ˜—J˜š‘ œžœžœ˜9š‘ œžœžœ˜Jšžœžœžœžœ ˜Ršœ-˜-Jšœ4˜4—šœ-˜-Jšœ4˜4—šœ1˜1Jšœ9˜9—šžœžœžœ žœ˜7Jšœ!˜!J˜7J˜—šžœ1ž˜7Jšžœžœžœ ˜:—J˜—Jšœ˜Jšœ˜JšœŸ˜—J˜š‘ œžœžœ˜7š‘ œžœžœžœ˜/šžœžœ˜šžœžœžœ˜Jšžœžœ˜,JšœŸ˜7J˜—J˜J˜—J˜—Jšœ4˜4Jšœ4˜4JšœŸ˜—J˜š‘ œžœ-žœ˜Jš ‘œžœžœžœžœ˜8Jšœ ˜ šžœ žœ žœ˜ Jšœžœžœ ˜J˜+J˜—J˜—JšœD˜DJšœF˜FJšœE˜EJšœ9˜9JšœŸ˜—J˜š‘ œžœ,žœ˜Hš‘œžœ˜4š žœžœžœžœ"žœžœž˜QJ˜Jšžœžœ!˜?Jšžœ˜—JšœŸ˜—Jšœžœ˜ Jšœ žœ˜Jšœ žœ˜Jšœ žœ˜$Jšœ žœ2˜BJšžœžœžœ$žœ˜DJšœ5˜5Jšžœžœžœžœ˜Jšœ8˜8Jšžœžœžœžœ˜Jšœ\žœŸ!˜„šœ˜šœ˜Jšœ˜Jšœ ˜ šœ žœžœžœ˜5Jšžœžœžœ ˜.——Jšœ ˜ Jšœžœžœ˜+—Jšžœ žœžœ˜0šžœ˜J˜Jšœ`˜`J˜#J˜'J˜J˜—JšœŸ ˜—J˜š‘ œžœ&žœžœ˜KJšœžœ3˜=Jšœžœžœ˜Jšœžœ žœ˜Jšœžœ˜ š ‘ œžœžœžœžœ˜DJšœ%˜%šžœ žœ žœ˜Jšœžœžœ ˜J˜0J˜—JšœŸ˜—Jšžœžœžœ$žœ˜>Jšžœžœžœ˜$Jšžœžœžœžœ ˜?Jšœ>˜>Jšžœžœžœ žœ˜8Jšžœ˜Jšœ@˜@Jšžœžœžœ žœ'˜DJšžœ˜Jš žœžœžœžœžœ˜6˜ J˜ šœ:˜:Jšœ3˜3—Jšœ˜Jšœ˜Jšœ˜Jšœ˜J˜J˜—Jšžœžœžœ ˜:šžœžœ˜ Jšœžœ˜.Jšœžœ˜&J˜!J˜—JšœŸ ˜—J˜š‘œžœžœ Ÿ˜LJšœ žœ˜J˜šœ6˜6Jšœ-˜-—Jšžœ žœžœ˜Jšžœ žœžœ*žœ˜Ešžœ'žœ˜/šœ%˜%Jšœ2˜2Jšœ!˜!—Jšžœ˜—Jšœ-˜-Jšžœžœžœ˜,JšœŸ ˜ —J˜š‘ œžœ*žœžœ˜RJšœžœžœ˜Jšœžœ žœ˜J˜Jšœžœ˜Jšœ žœžœ˜˜J™JšœC™CJšžœžœžœžœ™Jšœ<™<šžœ žœžœ™J™VJšžœ™J™——šœ6˜6Jšœ2˜2—Jšžœ žœžœ˜Jšžœ žœžœ*žœ˜Ešžœ'žœ˜/šœ%˜%Jšœ2˜2Jšœ!˜!—Jšžœ˜—Jšžœžœ žœ˜(šžœ˜šžœ,žœžœž˜@Jšžœžœ žœ˜)Jšžœ˜—Jšžœžœžœ žœ$˜JJ˜—JšœD˜DJš žœžœžœ žœ%žœ˜Jšœ ˜ Jšœ5˜5Jšœ˜Jšœ˜JšœA˜AJšœ žœžœžœ˜3Jšœ˜Jš œžœžœžœžœŸ ˜VJšœ˜J˜—šžœžœŸ!˜/Jšœžœžœ˜6Jšœžœ˜4Jšœ#žœ,Ÿ ˜^Jšœ&Ÿ ˜1J˜J˜—Jšžœ˜!J˜&š žœžœžœ žœŸ˜/Jšœžœ˜2J˜—Jšžœžœžœ$˜>JšœŸ˜—J˜š‘œžœ+žœ™NJšœŸ™—J˜š‘œžœžœžœ™GJšœ žœ™Jšœžœ™ Jšœžœ™šžœ8žœžœž™MJšœ™Jš œžœžœžœžœ™6šžœ0žœžœž™DJšžœžœžœ™ Jšœ7™7Jšœžœžœžœ™6Jš œžœžœžœžœ™:Jšžœ™—J™Jšžœ™—Jšœ.™.JšœŸ™—J˜š‘œžœžœžœžœžœžœ ˜kJ™.Jšœžœ˜ Jšœ)˜)Jšžœžœžœ˜!JšœŸ˜—J˜š‘œžœžœžœžœžœžœ ˜sJ™.Jšœžœ˜ Jšœ+˜+Jšžœžœžœ˜"JšœŸ˜——J˜J˜šžœ˜J˜1——…—.nD