DIRECTORY Atom, Basics, CedarProcess, Convert, Imager, ImagerBackdoor, ImagerColor, ImagerFont, ImagerTransformation, InputFocus, IO, List, MachineDependentPopping, Menus, MessageWindow, PopUpButtons, PopUpButtonsPrivate, PopUpSelection2, Process, ProcessProps, Real, RefTab, Rope, TEditSelection, TEditSplit, TIPUser, UserProfile, Vector2, VFonts, ViewerClasses, ViewerOps, ViewerPrivate, ViewerSpecs, ViewerTools; PopUpButtonsImpl: CEDAR MONITOR IMPORTS Atom, Basics, CedarProcess, Convert, Imager, ImagerBackdoor, ImagerColor, ImagerFont, ImagerTransformation, InputFocus, IO, List, MachineDependentPopping, MessageWindow, PopUpButtons, PopUpSelection2, Process, ProcessProps, Real, RefTab, Rope, TEditSelection, TEditSplit, TIPUser, UserProfile, Vector2, VFonts, ViewerOps, ViewerPrivate, ViewerSpecs, ViewerTools EXPORTS PopUpButtons = BEGIN OPEN MDP:MachineDependentPopping, ViewerClasses, PUS:PopUpSelection2, PopUpButtonsPrivate, PopUpButtons; LORA: TYPE = LIST OF REF ANY; ATOMList: TYPE = LIST OF ATOM; LOR: TYPE = LIST OF ROPE; Instance: TYPE ~ REF InstancePrivate; InstancePrivate: PUBLIC TYPE = PopUpButtonsPrivate.InstancePrivate; leftMargin: INTEGER _ 3; rightMargin: INTEGER _ 2; bottomMargin: INTEGER _ 0; topMargin: INTEGER _ 1; Class: TYPE = REF ClassPrivate; ClassPrivate: PUBLIC TYPE = PopUpButtonsPrivate.ClassPrivate; dontPaint: PUBLIC Imager.Color _ ImagerColor.ColorFromStipple[ word: 0, function: [xor, null]]; noHelp: PUBLIC REF HelpPrivate[none] _ NEW [HelpPrivate[none] _ [none[]]]; undef: CharData = [ALL[0], 0]; maxHeight: NAT = 5; CharData: TYPE = RECORD [ bits: ARRAY [0 .. maxHeight) OF WORD, width: [0 .. Basics.bitsPerWord] ]; Char: TYPE = CHAR['A .. 'Z]; BitFont: TYPE = REF BitFontPrivate; BitFontPrivate: TYPE = RECORD [ height, hSep, vSep: NAT, chars: ARRAY Char OF CharData _ ALL[undef] ]; idTransform: Imager.Transformation ~ ImagerTransformation.Translate[[0.0, 0.0]]; smallBF: BitFont _ MakeSmallFont[]; MakeSmallFont: PROC RETURNS [smallBF: BitFont] = { Set: PROC [char: CHAR, width: NAT, asRope: ROPE] = { smallBF.chars[char].width _ width; FOR row: NAT IN [0 .. 4] DO FOR col: NAT IN [0 .. width) DO SELECT asRope.Fetch[row*(width+1) + col + 1] FROM '. => NULL; 'X => smallBF.chars[char].bits[row] _ Basics.BITOR[ smallBF.chars[char].bits[row], Basics.BITSHIFT[1, Basics.bitsPerWord - col - 1]]; ENDCASE => ERROR; ENDLOOP; ENDLOOP; }; smallBF _ NEW [BitFontPrivate _ [height: 5, hSep: 1, vSep: 1]]; Set['A, 3, " .X. X.X XXX X.X X.X"]; Set['C, 4, " .XXX X... X... X... .XXX"]; Set['F, 4, " XXXX X... XXX. X... X..."]; Set['H, 4, " X..X X..X XXXX X..X X..X"]; Set['I, 3, " XXX .X. .X. .X. XXX"]; Set['L, 3, " X.. X.. X.. X.. XXX"]; Set['N, 4, " X..X XX.X XXXX X.XX X..X"]; Set['P, 4, " XXX. X..X XXX. X... X..."]; Set['R, 4, " XXX. X..X XXX. X.X. X..X"]; Set['S, 4, " .XXX X... .XX. ...X XXX."]; Set['T, 3, " XXX .X. .X. .X. .X."]; }; mouseButtonIcons: ARRAY Menus.MouseButton OF PUS.Image _ [MakeMouseIcon[red], MakeMouseIcon[yellow], MakeMouseIcon[blue]]; sayPlain: PUS.Image _ MakeWords[LIST["PLAIN"]]; sayShf: PUS.Image _ MakeWords[LIST["SHIFT"]]; sayCtl: PUS.Image _ MakeWords[LIST["CNTRL"]]; sayBoth: PUS.Image _ MakeWords[LIST["CNTRL", "SHIFT"]]; MakeMouseIcon: PROC [mb: Menus.MouseButton] RETURNS [image: PUS.Image] = { image _ NEW [PUS.ImagePrivate _ [ size: mbiSize[it].Add[[2*(mbiSize[other].x + mbiSep + mbiw), 2*mbiw]], Draw: DrawMouseButtonIcon, data: NEW [Menus.MouseButton _ mb] ]]; }; mbib: REAL = 1.0; mbiw: REAL = 1.0; mbiSize: ARRAY {it, other} OF Vector2.VEC = [[5, 11], [4, 9]]; mbiSep: REAL = 1.0; DrawMouseButtonIcon: PROC [image: PUS.Image, context: Imager.Context, bounds: Imager.Rectangle, state: VisibleState] --PUS.Drawer-- = { rmb: REF Menus.MouseButton = NARROW[image.data]; y: REAL = bounds.y + bounds.h/2 + mbiw; x: REAL _ bounds.x + (bounds.w - image.size.x)/2 + mbiw; FOR mb: Menus.MouseButton IN Menus.MouseButton DO size: Imager.VEC = IF mb = rmb^ THEN mbiSize[it] ELSE mbiSize[other]; context.SetColor[Imager.black]; context.MaskRectangle[[x, y-size.y/2, size.x, size.y]]; IF mb # rmb^ THEN { context.SetColor[Imager.white]; context.MaskRectangle[[x+mbib, y-size.y/2+mbib, size.x-2*mbib, size.y-2*mbib]]; }; x _ x + size.x + mbiSep; ENDLOOP; }; ww: REAL = 1.0; MakeWords: PROC [wl: LOR] RETURNS [image: PUS.Image] = { image _ NEW [PUS.ImagePrivate _ [ size: [0, 0], Draw: DrawWords, data: wl]]; FOR words: LOR _ wl, words.rest WHILE words # NIL DO word: ROPE = words.first; width: NAT _ 0; FOR i: NAT IN [0 .. NAT[word.Length[]]) DO char: CHAR = word.Fetch[i]; IF i > 0 THEN width _ width + smallBF.hSep; width _ width + smallBF.chars[char].width; ENDLOOP; image.size.x _ MAX[image.size.x, width]; image.size.y _ image.size.y + smallBF.height; IF words.rest # NIL THEN image.size.y _ image.size.y + smallBF.vSep; ENDLOOP; image.size _ image.size.Add[[2*ww, 2*ww]]; }; DrawWords: PROC [image: PUS.Image, context: Imager.Context, bounds: Imager.Rectangle, state: VisibleState] --PUS.Drawer-- = { wl: LOR = NARROW[image.data]; y: INTEGER _ Real.Round[bounds.y + (image.size.y + bounds.h)/2]; context.SetColor[Imager.black]; FOR words: LOR _ wl, words.rest WHILE words # NIL DO word: ROPE = words.first; x: INTEGER _ Real.Round[bounds.x + (bounds.w - image.size.x)/2]; FOR i: NAT IN [0 .. NAT[word.Length[]]) DO char: CHAR = word.Fetch[i]; TRUSTED {ImagerBackdoor.MaskBits[context: context, base: @smallBF.chars[char].bits[0], wordsPerLine: 1, sMin: 0, fMin: 0, sSize: smallBF.height, fSize: smallBF.chars[char].width, tx: x, ty: y]}; x _ x + smallBF.chars[char].width + smallBF.hSep; ENDLOOP; y _ y - smallBF.vSep - smallBF.height; ENDLOOP; }; topMouse: PUS.Label _ MakeLabel[horiz, LIST[mouseButtonIcons[red], mouseButtonIcons[yellow], mouseButtonIcons[blue]]]; leftMouse: PUS.Label _ MakeLabel[vert, LIST[mouseButtonIcons[red], mouseButtonIcons[yellow], mouseButtonIcons[blue]]]; leftCtlShf: PUS.Label _ MakeLabel[vert, LIST[sayPlain, sayShf, sayCtl, sayBoth]]; topShf: PUS.Label _ MakeLabel[horiz, LIST[sayPlain, sayShf]]; leftShf: PUS.Label _ MakeLabel[vert, LIST[sayPlain, sayShf]]; leftCtl: PUS.Label _ MakeLabel[vert, LIST[sayPlain, sayCtl]]; MakeLabel: PROC [dim: Dim, images: PUSImageList] RETURNS [label: PUS.Label] = { il: ImageLabel = NEW [ImageLabelPrivate _ [dim, images]]; label _ NEW [PUS.LabelPrivate _ [ minSpacing: 0, minWidth: 0, Draw: DrawLabel, data: il]]; FOR list: PUSImageList _ images, list.rest WHILE list # NIL DO image: PUS.Image = list.first; Maxin: PROC [minSpacing, minWidth: NAT, s, w: REAL] RETURNS [newMinSpacing, newMinWidth: NAT] = { newMinSpacing _ MAX[minSpacing, NAT[Ceiling[s]]]; newMinWidth _ MAX[minWidth, NAT[Ceiling[w]]]; }; SELECT dim FROM horiz => [label.minSpacing, label.minWidth] _ Maxin[label.minSpacing, label.minWidth, image.size.x, image.size.y]; vert => [label.minSpacing, label.minWidth] _ Maxin[label.minSpacing, label.minWidth, image.size.y, image.size.x]; ENDCASE => ERROR; ENDLOOP; }; PUSImageList: TYPE = LIST OF PUS.Image; ImageLabel: TYPE = REF ImageLabelPrivate; ImageLabelPrivate: TYPE = RECORD [ dim: Dim, images: PUSImageList ]; Dim: TYPE = {horiz, vert}; DrawLabel: PROC [context: Imager.Context, org: Imager.VEC, n, spacing, width: NAT, data: REF ANY] = { il: ImageLabel = NARROW[data]; offset: Imager.VEC = SELECT il.dim FROM horiz => [0, 0], vert => [-width, -spacing], ENDCASE => ERROR; dOrg: Imager.VEC = SELECT il.dim FROM horiz => [spacing, 0], vert => [0, -spacing], ENDCASE => ERROR; bounds: Imager.Rectangle _ SELECT il.dim FROM horiz => [0, 0, spacing, width], vert => [0, 0, width, spacing], ENDCASE => ERROR; org _ org.Add[offset]; FOR list: PUSImageList _ il.images, list.rest WHILE list # NIL AND n > 0 DO image: PUS.Image = list.first; bounds.x _ org.x; bounds.y _ org.y; image.Draw[image, context, bounds, [FALSE, FALSE, FALSE]]; org _ org.Add[dOrg]; n _ n - 1; ENDLOOP; }; ImageForRope: PUBLIC PROC [rope: ROPE, colors: Colors _ NIL, font: Imager.Font _ NIL, align: Align _ bottomLeft] RETURNS [image: Image] ~ {RETURN PUS.ImageForRope[rope, colors, font, align]}; QuaClass: PUBLIC PROC [ra: REF ANY] RETURNS [MaybeClass] ~ { WITH ra SELECT FROM x: Class => RETURN [[TRUE, x]]; ENDCASE => RETURN [[FALSE, NIL]]}; MakeClass: PUBLIC PROC [spec: ClassSpec] RETURNS [class: Class] = { helpLen: NAT ~ IF spec.help#NIL AND spec.help.kind=none THEN 0 ELSE 1; choicesLen: NAT ~ ChoiceListLength[spec.choices]; choices: PUS.ChoiceS = NEW [PUS.ChoiceSequence[helpLen + choicesLen]]; columns: NAT; left, top: PUS.Label _ NIL; i: NAT _ 0; headImage: PUS.Image _ IF spec.image#NIL THEN QuaTransformed[spec.image].i ELSE NIL; DO m, n: NAT _ 1; Times: PROC [k: NAT] = {m _ n; n _ n*k}; IF spec.decodeMouseButton THEN Times[3]; IF spec.decodeShift THEN Times[2]; IF spec.decodeControl THEN Times[2]; IF choicesLen > m OR n = 1 THEN EXIT; IF spec.decodeControl THEN spec.decodeControl _ FALSE ELSE IF spec.decodeShift THEN spec.decodeShift _ FALSE ELSE IF spec.decodeMouseButton THEN spec.decodeMouseButton _ FALSE ELSE ERROR; ENDLOOP; IF helpLen=1 THEN { choices[i] _ [helpImage, helpDoc]; i _ i + 1; }; FOR cl: ChoiceList _ spec.choices, cl.rest WHILE cl # NIL DO IF cl.first = nullChoice THEN choices[i] _ PUS.nullChoice ELSE { image: Image _ cl.first.image; IF image = NIL THEN image _ PUS.ImageForRope[KeyText[cl.first.key]]; choices[i] _ [ image: image, doc: cl.first.doc]; IF headImage=NIL THEN headImage _ choices[i].image; }; i _ i + 1; ENDLOOP; SELECT TRUE FROM spec.decodeMouseButton AND (spec.decodeShift OR spec.decodeControl) => { columns _ 3; top _ topMouse; left _ SELECT TRUE FROM spec.decodeShift AND spec.decodeControl => leftCtlShf, spec.decodeShift => leftShf, spec.decodeControl => leftCtl, ENDCASE => ERROR; }; spec.decodeMouseButton => { columns _ 1; left _ leftMouse; }; spec.decodeShift AND spec.decodeControl => { columns _ 2; top _ topShf; left _ leftCtl; }; ENDCASE => { columns _ 1; left _ IF spec.decodeShift THEN leftShf ELSE IF spec.decodeControl THEN leftCtl ELSE NIL; }; class _ NEW [ClassPrivate _ [ spec: spec, menu: PUS.Create[choices: choices, doc: spec.doc, allMayBeUp: FALSE, header: IF spec.headMenu THEN headImage ELSE NIL, left: left, top: top, fullRows: helpLen, columns: columns], wDir: MDP.GetWDir[], helpCount: helpLen, choiceCount: choicesLen ]]; }; GetSpec: PUBLIC PROC [class: Class] RETURNS [spec: ClassSpec] = { spec _ class.spec}; AmbushClass: PUBLIC PROC [class: Class, spec: ClassSpec] = { class^ _ MakeClass[spec]^; }; KeyText: PROC [key: REF ANY] RETURNS [text: ROPE] = { WITH key SELECT FROM rt: REF TEXT => text _ Rope.FromRefText[rt]; r: ROPE => text _ r; a: ATOM => text _ Atom.GetPName[a]; ri: REF INT => text _ Convert.RopeFromInt[ri^]; ri: REF INTEGER => text _ Convert.RopeFromInt[ri^]; ri: REF NAT => text _ Convert.RopeFromInt[ri^]; rc: REF CARDINAL => text _ Convert.RopeFromCard[rc^]; rc: REF LONG CARDINAL => text _ Convert.RopeFromCard[rc^]; rr: REF REAL => text _ Convert.RopeFromReal[rr^]; l: LORA => { text _ NIL; FOR rs: LORA _ l, rs.rest WHILE rs # NIL DO IF text # NIL THEN text _ text.Concat[", "]; text _ text.Concat[KeyText[rs.first]]; ENDLOOP; }; l: ROPEList => { text _ NIL; FOR rs: ROPEList _ l, rs.rest WHILE rs # NIL DO IF text # NIL THEN text _ text.Concat[", "]; text _ text.Concat[rs.first]; ENDLOOP; }; l: ATOMList => { text _ NIL; FOR rs: ATOMList _ l, rs.rest WHILE rs # NIL DO IF text # NIL THEN text _ text.Concat[", "]; text _ text.Concat[Atom.GetPName[rs.first]]; ENDLOOP; }; ENDCASE => ERROR; }; ChoicesDocs: PROC [choices: ChoiceList] RETURNS [ropes: ROPEList] = { tail: ROPEList _ ropes _ NIL; FOR choices _ choices, choices.rest WHILE choices # NIL DO this: ROPEList = LIST[choices.first.doc]; IF tail # NIL THEN tail.rest _ this ELSE ropes _ this; tail _ this; ENDLOOP; ropes _ ropes; }; ChoiceListLength: PROC [list: ChoiceList] RETURNS [length: NAT _ 0] = { FOR list _ list, list.rest WHILE list # NIL DO length _ length + 1 ENDLOOP; }; defaultFont: PUBLIC Imager.Font _ VFonts.DefaultFont[NIL]; defaultColors: PUBLIC Colors _ PUS.defaultColors; inverseColors: PUBLIC Colors _ PUS.inverseColors; QuaRopeImage: PUBLIC PROC [i: Image] RETURNS [MaybeRope] ~ { xfm: Imager.Transformation; [[, i, xfm]] _ QuaTransformed[i]; WITH i.data SELECT FROM ri: RopeImage => RETURN [[TRUE, ri.text, ri.colors, ri.font, ri.align, xfm]]; ENDCASE => RETURN [[FALSE, NIL, NIL, NIL, [-1.0, -1.0], NIL]]}; TransformedImage: TYPE ~ REF TransformedImagePrivate; TransformedImagePrivate: TYPE ~ RECORD [i: Image, t: Imager.Transformation]; TransformImage: PUBLIC PROC [i: Image, t: Imager.Transformation] RETURNS [Image] ~ { ti: TransformedImage ~ NEW [TransformedImagePrivate _ [i, t]]; tr: Imager.Rectangle ~ t.TransformRectangle[[0, 0, i.size.x, i.size.y]]; RETURN [NEW [ImagePrivate _ [size: [tr.w, tr.h], Draw: DrawTransformed, data: ti]]]}; QuaTransformed: PUBLIC PROC [gi: Image] RETURNS [MaybeTransformed] ~ { WITH gi.data SELECT FROM ti: TransformedImage => RETURN [[TRUE, ti.i, ti.t]]; ENDCASE => RETURN [[FALSE, gi, idTransform]]}; DrawTransformed: PROC [image: Image, context: Imager.Context, bounds: Imager.Rectangle, state: VisibleState] ~ { ti: TransformedImage ~ NARROW[image.data]; tBounds: Imager.Rectangle ~ ti.t.InverseTransformRectangle[bounds]; DoSaved: PROC ~ { context.ConcatT[ti.t]; ti.i.Draw[ti.i, context, tBounds, state]; RETURN}; context.DoSave[DoSaved]; RETURN}; helpFont: ImagerFont.Font _ ImagerFont.Find["Xerox/TiogaFonts/Tioga10I"]; helpImage: PUS.Image _ PUS.ImageForRope[rope: "Help", font: helpFont, align: center]; helpDoc: ROPE _ "Click here for more documentation (if you're lucky)"; Instantiate: PUBLIC PROC [class: Class, viewerInfo: ViewerClasses.ViewerRec _ [], instanceData: REF ANY _ NIL, image: Image _ NIL, help: Help _ NIL, paint: BOOL _ TRUE] RETURNS [button: Viewer] = { specdImage: Image ~ image; specdHelp: Help ~ help; bordW: NAT ~ IF viewerInfo.border THEN 2*ViewerSpecs.windowBorderSize ELSE 0; inst: Instance ~ NEW [InstancePrivate _ [ spec: [class, instanceData, viewerInfo.name, specdImage, NIL, specdHelp, NIL, GetInerhitedProcessProps[]], packageGlobalFrame: NIL, state: IF class.spec.guarded THEN guarded ELSE armed, Paint: ViewerPaintButton, InTest: InViewer ]]; inst.packageGlobalFrame _ MDP.GetCaller2sGlobalFrame[]; image _ inst.spec.image _ ComputeImage[image, class, inst.spec.name]; IF viewerInfo.name = NIL THEN viewerInfo.name _ inst.spec.name _ QuaRopeImage[image].rope; IF viewerInfo.ww=0 THEN viewerInfo.ww _ Real.Round[image.size.x + bordW]; IF viewerInfo.wh=0 THEN viewerInfo.wh _ Real.Round[image.size.y + bordW]; IF viewerInfo.parent = NIL AND viewerInfo.wx=0 AND viewerInfo.wy=0 THEN { m: Viewer ~ ViewerPrivate.messageWindow; ViewerOps.MoveViewer[m, m.wx, m.wy, m.ww-viewerInfo.ww, m.wh, FALSE]; viewerInfo.wx _ m.wx + m.ww; viewerInfo.wy _ m.wy; viewerInfo.wh _ m.wh; viewerInfo.column _ static; }; viewerInfo.data _ inst; RETURN[ViewerOps.CreateViewer[$PopUpButton, viewerInfo, paint]]; }; GeneralInstantiate: PUBLIC PROC [class: Class, Paint: PaintProc, InTest: InTestProc, name: ROPE _ NIL, instanceData: REF ANY _ NIL, image: Image _ NIL, help: Help _ NIL] RETURNS [Instance] = { specdImage: Image ~ image; specdHelp: Help ~ help; inst: Instance ~ NEW [InstancePrivate _ [ spec: [class, instanceData, name, specdImage, NIL, specdHelp, NIL, GetInerhitedProcessProps[]], packageGlobalFrame: MDP.GetCaller2sGlobalFrame[], state: IF class.spec.guarded THEN guarded ELSE armed, Paint: Paint, InTest: InTest ]]; image _ inst.spec.image _ ComputeImage[image, class, name]; IF name = NIL THEN inst.spec.name _ QuaRopeImage[image].rope; RETURN[inst]}; DefaultSize: PUBLIC PROC [viewerName: ROPE _ NIL, class: Class _ NIL, image: Image _ NIL, border: BOOL _ TRUE] RETURNS [ww, wh: INTEGER] ~ { bordW: NAT ~ IF border THEN 2*ViewerSpecs.windowBorderSize ELSE 0; image _ ComputeImage[image, class, viewerName]; ww _ Real.Round[image.size.x + bordW]; wh _ Real.Round[image.size.y + bordW]; }; ComputeImage: PROC [specdImage: Image, class: Class, viewerName: ROPE] RETURNS [use: Image] ~ { use _ specdImage; IF use=NIL AND class#NIL THEN use _ class.spec.image; IF use=NIL AND viewerName#NIL THEN use _ PUS.ImageForRope[viewerName]; IF use=NIL AND class#NIL AND class.spec.choices#NIL THEN { use _ IF class.spec.choices.first.image # NIL THEN class.spec.choices.first.image ELSE PUS.ImageForRope[KeyText[class.spec.choices.first.key]]; }; IF use=NIL THEN use _ PUS.ImageForRope["The turkey client didn't specify an image for this button"]; }; UnionHelp: PUBLIC PROC [a, b: Help] RETURNS [Help] ~ { hu: REF HelpUnion ~ NEW [HelpUnion _ [a, b]]; IF a.kind=none THEN RETURN [b]; IF b.kind=none THEN RETURN [a]; RETURN [NEW [HelpPrivate[proc] _ [proc[UnionHelpProc, hu]]]]}; HelpUnion: TYPE ~ RECORD [a, b: Help]; UnionHelpProc: PROC [view: View, instSpec: InstanceSpec, data: REF ANY] --HelpProc-- ~ { hu: REF HelpUnion ~ NARROW[data]; DoHelp[view, instSpec, hu.a]; DoHelp[view, instSpec, hu.b]; RETURN}; GetHelp: PROC [inst: Instance] RETURNS [help: Help] ~ { IF inst.spec.help#NIL THEN RETURN [inst.spec.help]; IF inst.spec.specdHelp#NIL THEN RETURN [inst.spec.help _ inst.spec.specdHelp]; IF inst.spec.class.spec.help#NIL THEN RETURN [inst.spec.help _ inst.spec.class.spec.help]; IF inst.packageName=NIL THEN inst.packageName _ GuessPackageName[inst.packageGlobalFrame]; IF inst.packageName.Length[] # 0 THEN { fileName: ROPE ~ inst.packageName.Cat["Doc.Tioga"]; IF MDP.GetFullFilename[fileName, inst.spec.class.wDir].full#NIL THEN { RETURN [inst.spec.help _ NEW [HelpPrivate[docs] _ [docs[LIST[[fileName, NIL]]]]]]; }; }; RETURN [inst.spec.help _ noHelp]; }; DeduceHelp: PUBLIC PROC [buttonName, installationDir, clientPackageName: ROPE _ NIL, clientPackageGlobalFrame: POINTER _ NIL] RETURNS [Help] ~ { IF clientPackageName=NIL AND clientPackageGlobalFrame#NIL THEN clientPackageName _ GuessPackageName[clientPackageGlobalFrame]; IF clientPackageName.Length#0 THEN { fileName: ROPE ~ clientPackageName.Cat["Doc.Tioga"]; really: ROPE ~ MDP.GetFullFilename[fileName, installationDir].ohneVersion; IF really#NIL THEN RETURN [NEW [HelpPrivate[docs] _ [docs[LIST[[really, NIL]]]]]]; }; RETURN [noHelp]}; QuaInstance: PUBLIC PROC [ra: REF ANY] RETURNS [MaybeInstance] ~ { WITH ra SELECT FROM x: Instance => RETURN [[TRUE, x]]; ENDCASE => RETURN [[FALSE, NIL]]}; ViewerQuaInstance: PUBLIC PROC [v: Viewer] RETURNS [MaybeInstance] ~ { WITH v.data SELECT FROM x: Instance => RETURN [[TRUE, x]]; ENDCASE => RETURN [[FALSE, NIL]]}; InstanceToSpec: PUBLIC PROC [inst: Instance] RETURNS [InstanceSpec] ~ {RETURN [inst.spec]}; ViewerToSpec: PUBLIC PROC [button: Viewer] RETURNS [is: InstanceSpec] = { inst: Instance = NARROW[button.data]; [] _ GetHelp[inst]; is _ inst.spec; }; InstanceToState: PUBLIC PROC [inst: Instance] RETURNS [VisibleState] ~ { RETURN [[inst.highlight, inst.executingCount > 0, inst.state # armed]]}; AmbushGeneralInstance: PUBLIC PROC [view: View, inst: Instance, class: Class _ NIL, instanceData: REF ANY _ NIL, image: Image _ NIL, help: Help _ NIL, specInstanceData, specImage, specHelp, specProcessProps: BOOL _ FALSE, paint: BOOL _ TRUE] ~ { IF class # NIL THEN inst.spec.class _ class; IF specInstanceData OR instanceData # NIL THEN inst.spec.instanceData _ instanceData; IF specImage OR image # NIL THEN inst.spec.specdImage _ image; IF specHelp OR help # NIL THEN inst.spec.specdHelp _ help; IF specProcessProps THEN inst.spec.processProps _ GetInerhitedProcessProps[]; inst.spec.image _ ComputeImage[inst.spec.specdImage, inst.spec.class, inst.spec.name]; inst.spec.help _ NIL; IF paint THEN PaintButton[view, NIL, inst]; }; GetInerhitedProcessProps: PROC RETURNS [inherited: PropList] = { inherited _ NIL; FOR props: PropList _ ProcessProps.GetPropList[], props.rest WHILE props # NIL DO SELECT props.first.key FROM $EvalHead, $CommanderHandle => NULL; $WorkingDirectory => inherited _ List.PutAssoc[props.first.key, props.first.val, inherited]; ENDCASE => unrecognized _ List.PutAssoc[props.first.key, props.first.val, unrecognized]; ENDLOOP; inherited _ inherited; }; unrecognized: PropList _ NIL; ButtonPaint: PROC [self: Viewer, context: Imager.Context, whatChanged: REF, clear: BOOL] RETURNS [quit: BOOL _ FALSE] --ViewerClasses.PaintProc-- = { inst: Instance ~ NARROW[self.data]; IF inst # NIL THEN { image: Image ~ inst.spec.image; state: VisibleState _ [ highlight: inst.highlight, executing: inst.executingCount > 0, guarded: inst.state # armed]; really: BOOL _ TRUE; WITH whatChanged SELECT FROM Decide: REF PaintDecider => [really, state] _ Decide[inst]; ENDCASE => NULL; IF really THEN { image.Draw[image, context, [0, 0, self.cw, self.ch], state]; context _ context} ELSE self _ self; context _ context} ELSE self _ self; RETURN}; RawNotify: PUBLIC ENTRY PROC [view: View, instance: Instance, actionQueue, action: REF ANY] = { ENABLE UNWIND => NULL; input: LIST OF REF ANY ~ MDP.MatchEvent[actionQueue, action]; InnerButtonNotify[view, input, instance]; RETURN}; ButtonNotify: ENTRY PROC [self: Viewer, input: LIST OF REF ANY] --ViewerClasses.NotifyProc-- = { ENABLE UNWIND => NULL; IF self#NIL THEN {inst: Instance ~ NARROW[self.data]; InnerButtonNotify[self, input, inst]} ELSE InnerButtonNotify[curView, input, curButt]; RETURN}; PaintButton: PROC [view: View, paintHint: ATOM, inst: Instance] = { IF inst # NIL AND inst.Paint # NIL THEN { inst.Paint[view, IF paintHint # $Increment THEN AlwaysPaint ELSE ConditionallyPaint, inst]; view _ view} ELSE paintHint _ paintHint; RETURN}; ConditionallyPaint: PROC [decisionData: REF ANY] RETURNS [paint: BOOL, state: VisibleState] ~ { inst: Instance ~ NARROW[decisionData]; state _ [ highlight: inst.highlight, executing: inst.executingCount > 0, guarded: inst.state # armed]; IF inst.shownVS # state THEN { inst.shownVS _ state; paint _ TRUE} ELSE paint _ FALSE; RETURN}; AlwaysPaint: PROC [decisionData: REF ANY] RETURNS [paint: BOOL, state: VisibleState] ~ { inst: Instance ~ NARROW[decisionData]; state _ [ highlight: inst.highlight, executing: inst.executingCount > 0, guarded: inst.state # armed]; inst.shownVS _ state; paint _ TRUE; RETURN}; ViewerPaintButton: PROC [view: View, Decide: PaintDecider, decisionData: REF ANY] --PaintProc-- ~ { ViewerOps.PaintViewer[NARROW[view], client, FALSE, NEW [PaintDecider _ Decide]]; RETURN}; InnerButtonNotify: INTERNAL PROC [view: View, input: LIST OF REF ANY, inst: Instance] = { ENABLE UNWIND => { IF curButt # NIL THEN curButt.depressed _ curButt.highlight _ FALSE; popOrDoc _ FALSE; BROADCAST startCondition; BROADCAST timeout; InputFocus.ReleaseButtons[]; }; viewAsViewer: Viewer ~ WITH view SELECT FROM x: Viewer => x, ENDCASE => NIL; button: Menus.MouseButton _ red; shift, control: BOOL _ FALSE; mouse: TIPUser.TIPScreenCoords _ NIL; IF popping OR inst = NIL THEN RETURN; FOR list: LIST OF REF ANY _ input, list.rest UNTIL list = NIL DO WITH list.first SELECT FROM x: ATOM => SELECT x FROM $Blue => button _ blue; $Control => control _ TRUE; $Hit => { IF inst # curButt THEN { IF curButt#NIL AND curButt.depressed THEN ERROR; curView _ view; curButt _ inst; }; IF inst.depressed THEN SELECT inst.state FROM guarded => { popOrDoc _ FALSE; inst.depressed _ inst.highlight _ FALSE; inst.state _ arming; BROADCAST timeout; InputFocus.ReleaseButtons[]; PaintButton[view, $Increment, inst]; TRUSTED {Process.Detach[FORK ArmButtonProc[inst, view]]}; IF inst.spec.class.spec.disarmMsg#NIL THEN ViewerPrivate.Document[inst.spec.class.spec.disarmMsg, viewAsViewer, inst.spec.instanceData, button, shift, control]; }; arming => NULL; armed => { popOrDoc _ FALSE; inst.depressed _ inst.highlight _ FALSE; IF inst.spec.class.spec.guarded THEN inst.state _ guarded; BROADCAST timeout; InputFocus.ReleaseButtons[]; PaintButton[view, $Increment, inst]; IF inst.spec.class.spec.disableDecoding THEN TRUSTED {Process.Detach[FORK Bitch["Quick-clicking of this button disabled; down-click and hold to get menu"]]} ELSE IF inst.spec.class.spec.fork THEN TRUSTED {Process.Detach[FORK ButtonPusher[view, inst, firstChoice+inst.spec.class.helpCount, TRUE]]} ELSE ButtonPusher[view, inst, firstChoice+inst.spec.class.helpCount, FALSE]; }; ENDCASE => ERROR; }; $Mark => IF ~inst.depressed THEN { IF curButt#NIL AND curButt.depressed THEN ERROR; popOrDoc _ inst.state = armed; curView _ view; curButt _ inst; firstChoice _ Decode[inst, button, shift, control]; NOTIFY startCondition; inst.depressed _ TRUE; inst.highlight _ (firstChoice < inst.spec.class.choiceCount AND IthChoice[inst.spec.class.spec.choices, firstChoice] # nullChoice) OR inst.spec.class.choiceCount = 0; InputFocus.CaptureButtons[ButtonNotify, buttonClass.tipTable, viewAsViewer]; PaintButton[view, $Increment, inst]; } ELSE { stillInButton: BOOL; IF inst # curButt THEN ERROR; stillInButton _ inst.InTest[view, mouse]; IF stillInButton THEN RETURN; popOrDoc _ FALSE; inst.depressed _ inst.highlight _ FALSE; BROADCAST timeout; InputFocus.ReleaseButtons[]; PaintButton[view, $Increment, inst]; }; $Red => button _ red; $Shift => shift _ TRUE; $Yellow => button _ yellow; ENDCASE => NULL; z: TIPUser.TIPScreenCoords => mouse _ z; ENDCASE => ERROR; ENDLOOP; }; InViewer: PROC [view: View, coords: TIPUser.TIPScreenCoords] RETURNS [in: BOOL] --InTestProc-- ~ { viewer: Viewer ~ NARROW[view]; vIn: Viewer; [vIn, in] _ ViewerOps.MouseInViewer[coords]; in _ in AND vIn=viewer; RETURN}; armingTime: Process.Milliseconds _ 100; -- cover removal time. armedTime: Process.Milliseconds _ 5000; -- unguarded interval. ArmButtonProc: ENTRY PROC [inst: Instance, view: View] = { ButtonWait[inst, armingTime]; IF inst.state = arming THEN { inst.state _ armed; PaintButton[view, $Increment, inst]; ButtonWait[inst, armedTime]; }; IF inst.state # guarded THEN { inst.state _ guarded; PaintButton[view, $Increment, inst]; }; }; ButtonWait: INTERNAL PROC[inst: Instance, ticks: Process.Milliseconds] = TRUSTED { buttonWaitCondition: CONDITION; Process.SetTimeout[LONG[@buttonWaitCondition], Process.MsecToTicks[ticks]]; WAIT buttonWaitCondition; }; popOrDoc: BOOL _ FALSE; --do we want to (pop or MessageWindow[help])? popping: BOOL _ FALSE; --are we popping up a menu? curView: View _ NIL; curButt: Instance _ NIL; firstChoice: NAT _ 0; --what's coded by shift keys when button "entered". startCondition: CONDITION; timeout: CONDITION; timeoutMSec: NAT _ 0; desiredTimeoutMSec: NAT _ 400; FullInst: TYPE = REF FullInstPrivate; FullInstPrivate: TYPE = RECORD [v: View, i: Instance]; Popper: PROC = { x: INT _ 1; do: {Pop, Msg}; msg: ROPE; fi: FullInst _ NIL; default: NAT; WaitToPop: ENTRY PROC = { ENABLE UNWIND => NULL; DO WHILE NOT popOrDoc DO WAIT startCondition ENDLOOP; IF timeoutMSec # desiredTimeoutMSec THEN TRUSTED {Process.SetTimeout[@timeout, Process.MsecToTicks[timeoutMSec _ desiredTimeoutMSec]]}; WAIT timeout; IF popOrDoc THEN { popOrDoc _ FALSE; IF curButt.spec.class.spec.choices = NIL THEN {do _ Msg; msg _ curButt.spec.class.spec.doc; } ELSE {do _ Pop; popping _ TRUE; curButt.depressed _ curButt.highlight _ FALSE; InputFocus.ReleaseButtons[]; PaintButton[curView, $Increment, curButt]; fi _ NEW [FullInstPrivate _ [v: curView, i: curButt]]; default _ firstChoice + 1 + curButt.spec.class.helpCount; }; RETURN; } ELSE x _ x; ENDLOOP; }; CedarProcess.SetPriority[excited]; DO WaitToPop[]; SELECT do FROM Pop => { {ENABLE UNWIND => FinishPop[]; IF fi.i.spec.class.spec.fork THEN { i: NAT; [i,] _ PUS.Pop[menu: fi.i.spec.class.menu, default: default]; IF i > 0 THEN TRUSTED {Process.Detach[FORK ButtonPusher[fi.v, fi.i, i-1, TRUE]]}; } ELSE { [] _ PUS.Pop[menu: fi.i.spec.class.menu, default: default, InNotifier: ConsumeSelection, notifyData: fi]; }; }; FinishPop[]; }; Msg => MessageWindow.Append[msg, TRUE]; ENDCASE => ERROR; ENDLOOP; }; FinishPop: ENTRY PROC = {popping _ FALSE}; ConsumeSelection: PROC [i: INT, mb: MouseButton, data: REF ANY] = { fi: FullInst = NARROW[data]; IF i > 0 THEN ButtonPusher[fi.v, fi.i, i-1, FALSE]; }; ForkPopper: PROC = TRUSTED {Process.Detach[FORK Popper[]]}; ButtonPusher: PROC [view: View, inst: Instance, i: NAT, forked: BOOL] = { Doit: PROC = { j: INT ~ i-inst.spec.class.helpCount; choice: Choice _ nullChoice; SELECT TRUE FROM i=0 AND inst.spec.class.helpCount=1 => { -- try and print out help IF view # NIL THEN { -- no help for non-viewer buttons (for now) IF forked THEN GetAndDoHelp[view, inst] ELSE TRUSTED {Process.Detach[FORK GetAndDoHelp[view, inst]]}; }; }; (j { inst.executingCount _ inst.executingCount + 1; PaintButton[view, $Increment, inst]; IF forked THEN CedarProcess.SetPriority[normal]; inst.spec.class.spec.proc[view, inst.spec.instanceData, inst.spec.class.spec.classData, choice.key ! ABORTED => CONTINUE]; inst.executingCount _ MAX[inst.executingCount - 1, 0]; PaintButton[view, $Increment, inst]; }; ENDCASE => NULL; }; ProcessProps.AddPropList[inst.spec.processProps, Doit]; }; GetAndDoHelp: PROC [view: View, inst: Instance] ~ { CedarProcess.SetPriority[normal]; DoHelp[view, inst.spec, GetHelp[inst]]; RETURN}; Decode: PROC [inst: Instance, mouseButton: Menus.MouseButton, shift, control: BOOL] RETURNS [i: NAT] = { IF inst.spec.class.choiceCount IN [0 .. 1] THEN RETURN [0]; {Add: PROC [base, digit: NAT] = INLINE {i _ i*base + digit}; decodeMouseButton: BOOL = inst.spec.class.spec.decodeMouseButton; decodeShift: BOOL = inst.spec.class.spec.decodeShift; decodeControl: BOOL = inst.spec.class.spec.decodeControl; i _ IF decodeControl AND control THEN 1 ELSE 0; IF decodeShift THEN Add[2, IF shift THEN 1 ELSE 0]; IF decodeMouseButton THEN Add[3, SELECT mouseButton FROM red => 0, yellow => 1, blue => 2, ENDCASE => ERROR]; }}; IthChoice: PROC [list: ChoiceList, i: NAT] RETURNS [ith: Choice] = { IF list = NIL THEN RETURN [nullChoice]; THROUGH [0 .. i) DO list _ list.rest ENDLOOP; ith _ list.first; }; DoHelp: PUBLIC PROC [view: View, instSpec: InstanceSpec, help: Help] ~ { WITH help SELECT FROM x: REF HelpPrivate[none] => Bitch["Sorry, this button is helpless"]; x: REF HelpPrivate[docs] => { FOR hl: HelpDocList _ x.docs, hl.rest WHILE hl # NIL DO hd: HelpDocument ~ hl.first; fullFileName: ROPE ~ MDP.GetFullFilename[hd.filename, instSpec.class.wDir].full; usedViewers: RefTab.Ref ~ RefTab.Create[]; first: BOOL _ TRUE; aDocViewer: Viewer _ NIL; LookForFree: PROC RETURNS [found: BOOL] ~ { v: Viewer _ aDocViewer; DO IF NOT usedViewers.Fetch[v].found THEN {aDocViewer _ v; RETURN [TRUE]}; v _ v.link; IF v=NIL OR v=aDocViewer THEN EXIT; ENDLOOP; RETURN [FALSE]; }; SearchFor: PROC [rope: ROPE] ~ { IF first THEN { aDocViewer _ ViewerTools.FindExistingViewer[fullFileName]; IF aDocViewer=NIL THEN aDocViewer _ ViewerTools.MakeNewTextViewer[info: [name: fullFileName, file: fullFileName, iconic: FALSE, column: right]]; first _ FALSE; } ELSE { IF NOT LookForFree[] THEN { TEditSplit.Split[aDocViewer]; IF NOT LookForFree[] THEN ERROR; Process.PauseMsec[splitMsec]; }; }; IF NOT usedViewers.Store[aDocViewer, $T] THEN ERROR; IF aDocViewer.iconic THEN ViewerOps.OpenIcon[aDocViewer]; TEditSelection.FindRope[viewer: aDocViewer, rope: rope, word: TRUE, id: feedback]; Process.PauseMsec[settleMSec]; RETURN}; IF fullFileName.Length[] = 0 THEN {Bitch["Couldn't resolve file name hint %g", [rope[hd.filename]]]; LOOP}; IF hd.searches = NIL THEN { target: ROPE _ instSpec.name; IF target.Length[]=0 THEN target _ QuaRopeImage[instSpec.image].rope; {stop: INT _ target.Length[]; WHILE stop > 0 AND target.Fetch[stop-1] IN [0C .. ' ] DO stop _ stop-1 ENDLOOP; IF stop > 0 AND target.Fetch[stop-1]=': THEN stop _ stop-1; {start: INT _ 0; WHILE start < stop AND target.Fetch[start] IN [0C .. ' ] DO start _ start + 1 ENDLOOP; IF stop > start THEN SearchFor[target.Substr[start: start, len: stop-start].Concat[":"]] ELSE Bitch["Couldn't determine what to search for in %g", [rope[hd.filename]]]; }}} ELSE { FOR ss: ROPEList _ hd.searches, ss.rest WHILE ss # NIL DO SearchFor[ss.first] ENDLOOP; }; ENDLOOP; }; x: REF HelpPrivate[proc] => x.Proc[view, instSpec, x.data]; ENDCASE => ERROR; RETURN}; settleMSec: NAT _ 500; splitMsec: NAT _ 100; Bitch: PROC [msg: ROPE, parm: IO.Value _ [null[]]] ~ { MessageWindow.Append[message: IO.PutFR[msg, parm], clearFirst: TRUE]; MessageWindow.Blink[]; }; Ceiling: PROC [r: REAL] RETURNS [i: INT] = { d: INT = Real.Fix[r]+1; i _ Real.Fix[r-d]+d; }; NoteProfile: PROC [reason: UserProfile.ProfileChangeReason] --UserProfile.ProfileChangedProc-- = { n: INT = UserProfile.Number["PopUpButtons.Delay", 400]; desiredTimeoutMSec _ IF n IN [1 .. LAST[NAT]] THEN n ELSE 400; }; buttonClass: ViewerClasses.ViewerClass _ NEW[ViewerClasses.ViewerClassRec _ [ paint: ButtonPaint, notify: ButtonNotify, tipTable: TIPUser.InstantiateNewTIPTable["PopUpButton.tip"], cursor: bullseye ]]; MDP.SetTipTable[buttonClass.tipTable]; UserProfile.CallWhenProfileChanges[NoteProfile]; ViewerOps.RegisterViewerClass[$PopUpButton, buttonClass]; -- plug in to Viewers TRUSTED { Process.InitializeCondition[@startCondition, Process.SecondsToTicks[100]]; Process.InitializeCondition[@timeout, Process.MsecToTicks[timeoutMSec _ desiredTimeoutMSec]]; Process.EnableAborts[@timeout]; Process.EnableAborts[@startCondition]; }; ForkPopper[]; END. ฒPopUpButtonsImpl.mesa Demers, January 5, 1987 12:21:07 pm PST Last tweaked by Mike Spreitzer on September 8, 1989 6:03:03 pm PDT Bier, July 17, 1989 10:16:56 pm PDT TRUSTED {context.MaskBits[base: @smallBF.chars[char].bits[0], wordsPerLine: 1, sMin: 0, fMin: 0, sSize: smallBF.height, fSize: smallBF.chars[char].width, tx: x, ty: y]}; assert: state=arming Monitor Invariant: inst.depressed for only one inst, which = curButt. curButt.depressed <=> CapturedButtons[curView]. curButt.highlight = (curButt.depressed & (no choices OR firstChoice interesting)). popOrDoc => curButt.depressed. Monitored data: --also anyInstance.depressed, anyInstance.highlight, and anyInstance.state-- ส*– "cedar" style˜codešœ™K™'K™BK™#—K˜Kšฯk œyœ›˜ŸK˜šฯnœœ˜Jšœyœ๏˜๑Kšœ ˜K˜—K˜Kšœœœ)œ4˜nK˜Kš œœœœœœ˜Kš œ œœœœ˜Kš œœœœœ˜K˜Kšœ œœ˜%Kšœœœ'˜CK˜Kšœ œ˜Kšœ œ˜Kšœœ˜Kšœ œ˜K˜Kšœœœ˜Kšœœœ$˜=K˜Kšœ œN˜_Kšœœœœ ˜JK˜Kšœœ˜Kšœ œ˜šœ œœ˜Kšœœœœ˜%Kšœ ˜ K˜—K˜Kšœœœ ˜Kšœ œœ˜#šœœœ˜Kšœœ˜Kšœœœ œ˜*K˜—K˜K˜PK˜#K˜šž œœœ˜2š žœœœ œ œ˜4Kšœ"˜"šœœœ ˜šœœœ˜šœ'˜1Kšœœ˜ šœ-œ˜3Kšœ˜Kšœœ#˜2—Kšœœ˜—Kšœ˜—Kšœ˜—K˜—Kšœ œ2˜?Kšฯf#˜#KšŸ(˜(KšŸ(˜(KšŸ(˜(KšŸ#˜#KšŸ#˜#KšŸ(˜(KšŸ(˜(KšŸ(˜(KšŸ(˜(KšŸ#˜#K˜—K˜KšœœœœJ˜zKšœ œœ ˜/Kšœœœ ˜-Kšœœœ ˜-Kšœ œœ˜7K˜šž œœœ œ ˜Jšœœœ˜!KšœF˜FKšžœ˜Kšœœ˜"K˜—K˜—K˜Kšœœ˜Kšœœ˜Kšœ œ œ œ˜>Kšœœ˜K˜šžœœ œPฯcœ˜‡Kšœœœ ˜0Kšœœ ˜'Kšœœ1˜8šœœ˜1Kš œ œœ œ œ˜EK˜K˜7šœ œ˜K˜K˜OK˜—K˜Kšœ˜—K˜—K˜Kšœœ˜K˜š ž œœœœ œ ˜8šœœœ˜!K˜ Kšžœ ˜K˜ —š œœœ œ˜4Kšœœ˜Kšœœ˜š œœœœ˜*Kšœœ˜Kšœœ˜+K˜*Kšœ˜—Kšœœ˜(Kšœ-˜-Kšœœœ,˜DKšœ˜—K˜*K˜—K˜šž œœ œP œ˜}Kšœœœ ˜Kšœœ6˜@K˜š œœœ œ˜4Kšœœ˜Kšœœ6˜@š œœœœ˜*Kšœœ˜Kšœข™ฉKšœป˜ยKšœ1˜1Kšœ˜—Kšœ&˜&Kšœ˜—K˜—K˜Kšœ œi˜vKšœ œh˜vKšœ œB˜QKšœœœ˜=Kšœ œœ˜=Kšœ œœ˜=K˜šž œœ"œ œ ˜OKšœœ%˜9šœœœ˜!Kšœ˜Kšœ ˜ Kšžœ ˜Kšœ ˜ —šœ(œœ˜>Kšœœ˜š žœœœœœœ˜aKšœœ œ˜1Kšœœ œ˜-K˜—šœ˜Kšœr˜rKšœq˜qKšœœ˜—Kšœ˜—K˜—K˜Kš œœœœœ˜'Kšœ œœ˜)šœœœ˜"K˜ Kšœ˜K˜—K˜Kšœœ˜K˜š ž œœ'œœœœ˜eKšœœ˜šœœœ˜'Kšœ˜Kšœ˜Kšœœ˜—šฯgœ œœ˜%Kšœ˜Kšœ˜Kšœœ˜—šœœ˜-Kšœ ˜ Kšœ˜Kšœœ˜—K˜š œ+œœœ˜KKšœœ˜K˜K˜Kšœ$œœœ˜:Kšœกœ˜K˜ Kšœ˜—K˜K˜—šž œœœœœœœœœ*˜ฟK˜—š žœœœœœœ˜<šœœ˜Kšœ œœ˜Kšœœœœ˜"—K˜—šž œœœœ˜CKš œ œœ œœœœ˜FKšœ œ"˜1Kšœ œ œœ'˜FKšœ œ˜ Kšœ œ œ˜Kšœœ˜ Kš œ œ œ œœœœ˜Tš˜Kšœœ˜Kšžœœœ˜(Kšœœ ˜(Kšœœ ˜"Kšœœ ˜$Kšœœœœ˜%Kšœœ˜5Kšœœœ˜6Kšœœœ˜BKšœœ˜ Kšœ˜—šœ œ˜Kšœ"˜"K˜ K˜—šœ(œœ˜KšœH˜HKšœœ&žœ ˜UK˜—šžœœœ œ˜Fšœ œ˜Kšœœœ˜4Kšœœœ˜.—K˜—šžœœ[˜pKšœœ ˜*KšœC˜Cšžœœ˜K˜Kšœ)˜)Kšœ˜—K˜Kšœ˜K˜—KšœI˜IKšœ œ œ;˜UKšœ œ9˜FK˜šž œœ˜šœ˜Kšœ)˜)Kšœœœœ˜Kšœœ˜Kšœ œ˜Kšœœœ˜—Kšœ˜K˜K˜Kš œœœœ œ˜Mšœœ˜)Kšœ9œ œ˜jKšœœ˜Kšœœœ œ˜5Kšžœ˜Kšžœ ˜Kšœ˜—Kšœœ˜7KšœE˜EKšœœœ=˜ZKšœœ2˜IKšœœ2˜Iš œœœœœ˜IKšœ(˜(Kšœ>œ˜EK˜K˜K˜K˜Kšœ˜—K˜Kšœ:˜@Kšœ˜K˜—šžœœœžœ žœœœœœœœœœ˜ภK˜K˜šœœ˜)Kšœ.œ œ˜_Kšœœ˜1Kšœœœ œ˜5Kšžœ˜ Kšžœ˜Kšœ˜—Kšœ;˜;Kšœœœ+˜=Kšœ˜K˜—šž œœœœœœœ œœœ œ˜ŒKš œœœœ œ˜BKšœ/˜/Kšœ&˜&Kšœ&˜&K˜K˜—šž œœ/œœ˜_Kšœ˜Kš œœœœœ˜5Kš œœœ œœœ˜Fšœœœœœœœ˜:Kš œœ"œœ œœ5˜K˜—KšœœœœK˜dK˜K˜—šž œœœœ ˜6Kšœœ œ˜-Kšœ œœ˜Kšœ œœ˜Kšœœ3˜>K˜—Kšœ œœ˜&K˜š ž œœ,œœ  œ˜XKšœœ œ˜!Kšœ˜Kšœ˜Kšœ˜K˜—šžœœœ˜7Kšœœœœ˜3Kšœœœœ(˜NKšœœœœ.˜ZKšœœœ>˜Zšœœ˜'Kšœ œ%˜3šœœ6œœ˜FKšœœœ œ˜RK˜—K˜—Kšœ˜!K˜K˜—šž œœœ2œœœœœ ˜Kš œœœœœ@˜~šœœ˜$Kšœ œ&˜4Kšœœœ8˜JKšœœœœœœ œ˜RK˜—Kšœ ˜K˜—š ž œœœœœœ˜Bšœœ˜Kšœœœ˜"Kšœœœœ˜"—K˜—šžœœœ œ˜Fšœœ˜Kšœœœ˜"Kšœœœœ˜"—K˜—šžœœœœ˜CKšœœ˜K˜—šž œœœœ˜IKšœœ˜%Kšœ˜K˜K˜K˜—šžœœœœ˜HKšœB˜HK˜—šžœœœ-œœœœœœ;œœ œœ˜๕Kšœ œœ˜,Kšœœœœ'˜UKšœ œ œœ˜>Kšœ œœœ˜:Kšœœ5˜MKšœV˜VKšœœ˜Kšœœœ˜+K˜K˜—šžœœœ˜@Kšœ œ˜šœ:œ œ˜Qšœ˜Kšœœ˜$Kšœ\˜\KšœQ˜X—Kšœ˜—K˜K˜Kšœœ˜K˜—šž œœ6œ œœœœ œ˜•Kšœœ ˜#šœœœ˜K˜šœ˜Kšœ˜Kšœ#˜#Kšœ˜—Kšœœœ˜šœ œ˜Kšžœœ0˜;Kšœœ˜—šœœ˜K˜Kšœ( ˜>K˜šž œœœ!˜:Kšœ™K˜šœœ˜Kšœ˜Kšœ$˜$K˜K˜—šœœ˜K˜Kšœ$˜$Kšœ˜—Kšœ˜K˜—šž œœœ0œ˜RKšœ œ˜Kšœœ4˜KKšœ˜Kšœ˜K˜—™K™2Kšœ/™/Kšœ  œA™RKšœ™K™—™Kšœ œœ -˜EKšœ œœ ˜2Kšœœ˜šœœ˜Kš Lœ™M—Kšœ œ 3˜IKšœ œ˜Kšœ  œ˜Kšœ œ˜—K˜Kšœœ˜K˜Kšœ œœ˜%Kšœœœ˜6K˜šžœœ˜Kšœœ˜ K˜Kšœœ˜ Kšœœ˜Kšœ œ˜ šž œœœ˜Kšœœœ˜š˜Kš œœ œœœ˜2Kšœ"œœW˜‡Kšœ ˜ šœ œ˜Kšœ œ˜šœ#˜(šœ ˜Kšœ"˜"K˜—šœ ˜Kšœ œ˜Kšœ(œ˜.K˜Kšœ ขœ˜*Kšœœขœ˜6Kšœ9˜9K˜——Kšœ˜K˜—Kšœ˜ Kšœ˜—Kšœ˜K˜—Kšœ"˜"š˜Kšœ ˜ šœ˜šœ˜Kšœœœ˜šœœ˜#Kšœœ˜Kšœœ3˜=Kš œœœœขœ œ˜QK˜—šœ˜Kšœœ3ž œ$˜iK˜—Kšœ˜Kšœ ˜ Kšœ˜—Kšœ!œ˜'Kšœœ˜—Kšœ˜—K˜K˜—Kšž œœœœ˜*K˜š žœœœœœ˜CKšœœ˜Kšœœขœ œ˜3K˜—K˜šž œœœœ ˜;K˜—šž œœ!œ œ˜Išžœœ˜Kšœœ˜%K˜šœœ˜šœœ" ˜Bšœœœ +˜@Kšœœ˜'Kšœœœ˜=K˜—K˜—šœœEœ%˜ŽKšœ.˜.Kšœ$˜$Kšœœ"˜0Kšœeœœ˜zKšœœ˜6Kšœ$˜$K˜—Kšœœ˜—K˜—Kšœ7˜7Kšœ˜K˜—šž œœ!˜3Kšœ!˜!Kšœ'˜'Kšœ˜K˜—š žœœBœœœ˜hKšœœ œœ˜;Kš œžœœœœ˜˜Dšœœ˜šœ#œœ˜7Kšœ˜Kšœœœ8˜PK˜*Kšœœœ˜Kšœœ˜šž œœœ œ˜+Kšœ˜š˜Kš œœœœœ˜GK˜ Kš œœœœœ˜#Kšœ˜—Kšœœ˜K˜—šž œœœ˜ šœœ˜Kšœ:˜:Kšœ œœcœ˜Kšœœ˜K˜—šœ˜šœœœ˜Kšœ˜Kšœœœœ˜ Kšœ˜K˜—K˜—Kšœœ#œœ˜4Kšœœ ˜9K•StartOfExpansionห[viewer: ViewerClasses.Viewer, rope: ROPE, findWhere: TEditSelection.FindWhere _ anywhere, def: BOOL _ FALSE, word: BOOL _ FALSE, id: TEditDocument.SelectionId _ primary, case: BOOL _ TRUE]šœ>œ˜RKšœ˜Kšœ˜—KšœœDœ˜kšœœœ˜Kšœœ˜Kšœœ,˜EKšœœ˜Kš œ œœ œœ˜OKšœ œœ˜;Kšœœ˜Kš œœœ œœ˜VKšœœEœK˜จK˜—šœ˜Kš œ%œœœœ˜VK˜—Kšœ˜—K˜—Kšœœ5˜;Kšœœ˜—Kšœ˜Kšœ œ˜Kšœ œ˜K˜—šžœœœœ˜6K–-[message: ROPE, clearFirst: BOOL _ FALSE]šœœœ˜EKšœ˜K˜—K˜š žœœœœœ˜,Kšœœ˜K˜K˜K˜—šž œœ+ "œ˜bKšœœ1˜7Kš œœœœœœœ˜>K˜K˜—šœ)œ!˜MKšœ˜Kšœ˜K˜