DIRECTORY Carets USING [StartCaret, StopCaret], Imager USING [black, Context, IntRectangle, SetColor, XOR, MaskBits], EditSpan USING [CompareNodeOrder, NodeOrder], InputFocus USING [Focus, GetInputFocus, SetInputFocus], Process USING [Detach], TextEdit USING [FetchChar, Offset, Size], TextLooks USING [Looks, noLooks], TextNode USING [FirstChild, ForwardClipped, LastWithin, Level, Location, NarrowToTextNode, Offset, Parent, Ref, RefTextNode, Root, Span, StepBackward, StepForward], TEditDocument USING [BeforeAfter, LineTable, maxClip, PunctuationPosition, SelectionGrain, SelectionId, SelectionPoint, Selection, TEditDocumentData], TEditSelection USING [Alloc, Copy, Create, Free, CharPositionInCachedLine, IsDown, ForceDown, LockBothSelections, LockSel, fSel, nilSel, pSel, sSel, UnlockBothSelections, UnlockSel], TEditTouchup USING [LockAfterRefresh, UnlockAfterRefresh], ViewerOps USING [AddProp, FetchProp, PaintViewer], ViewerClasses USING [ModifyProc, Viewer]; TEditSelectionImpl: CEDAR PROGRAM IMPORTS Carets, EditSpan, Imager, InputFocus, Process, TextEdit, TextNode, TEditSelection, TEditTouchup, ViewerOps EXPORTS TEditSelection = BEGIN OPEN TEditDocument, TEditSelection, TEditTouchup; ------ Selection Display and Control ------ MakePointSelection: PUBLIC PROC [selection: Selection, pos: TextNode.Location] = BEGIN tSel: Selection _ Alloc[]; Copy[source: selection, dest: tSel]; tSel.start.pos _ tSel.end.pos _ pos; tSel.granularity _ point; tSel.pendingDelete _ FALSE; tSel.insertion _ before; MakeSelection[selection: primary, new: tSel]; Free[tSel]; END; ChangeSelections: PROC [proc: PROC [tSel: Selection], sel: Selection] = { tSel: Selection _ Alloc[]; LockBothSelections["ChangeBothSelections"]; { ENABLE UNWIND => UnlockBothSelections[]; Copy[source: sel, dest: tSel]; proc[tSel] }; UnlockBothSelections[]; Free[tSel] }; PushOrExchangeSelections: PUBLIC PROC = { DoPush: PROC [tSel: Selection] = { MakeSelection[IF sSel.viewer=NIL THEN NIL ELSE sSel, primary]; MakeSelection[IF tSel.viewer=NIL THEN NIL ELSE tSel, secondary] }; ChangeSelections[DoPush, pSel] }; MakePrimary: PUBLIC PROC = { -- make secondary selection be the primary DoMakePrimary: PROC [tSel: Selection] = { Deselect[secondary]; MakeSelection[tSel, primary] }; ChangeSelections[DoMakePrimary, sSel] }; MakeSecondary: PUBLIC PROC = { -- make secondary selection be the primary DoMakeSecondary: PROC [tSel: Selection] = { Deselect[primary]; MakeSelection[tSel, secondary] }; ChangeSelections[DoMakeSecondary, pSel] }; CancelPrimary: PUBLIC PROC = { MakeSelection[NIL, primary] }; CancelSecondary: PUBLIC PROC = { MakeSelection[NIL, secondary] }; CancelFeedback: PUBLIC PROC = { MakeSelection[NIL, feedback] }; FakeSecondary: PUBLIC PROC [sel: Selection] = { LockSel[secondary, "FakeSecondary"]; { ENABLE UNWIND => UnlockSel[secondary]; IF sSel.viewer # NIL THEN Deselect[secondary]; Copy[source: sel, dest: sSel] }; UnlockSel[secondary] }; Deselect: PUBLIC PROC [selection: SelectionId _ primary] = { LockSel[selection, "Deselect"]; { ENABLE UNWIND => UnlockSel[selection]; sel: Selection = SELECT selection FROM primary => pSel, secondary => sSel, feedback => fSel, ENDCASE => ERROR; viewer: ViewerClasses.Viewer = sel.viewer; op: ATOM = SELECT selection FROM primary => $TakeDownPSel, secondary => $TakeDownSSel, feedback => $TakeDownFSel, ENDCASE => ERROR; down: BOOL = IsDown[selection]; Copy[source: nilSel, dest: sel]; IF viewer#NIL AND ~down THEN { -- take the selection down from the screen ViewerOps.PaintViewer[viewer, client, FALSE, op]; IF ~IsDown[selection] THEN ForceDown[selection]; }}; UnlockSel[selection] }; MakeSelection: PUBLIC PROC [new: Selection _ NIL, selection: SelectionId _ primary, startValid, endValid: BOOLEAN _ FALSE, forkPaint: BOOL _ TRUE] = { sel: Selection; op: ATOM; LockSel[selection, "MakeSelection"]; { ENABLE UNWIND => UnlockSel[selection]; SELECT selection FROM primary => BEGIN if: ViewerClasses.Viewer = InputFocus.GetInputFocus[].owner; sel _ pSel; op _ $ShowPSel; IF new=NIL THEN {IF if#NIL THEN InputFocus.SetInputFocus[NIL]} ELSE IF if#new.viewer THEN InputFocus.SetInputFocus[new.viewer]; END; feedback => { sel _ fSel; op _ $ShowFSel }; secondary => { sel _ sSel; op _ $ShowSSel }; ENDCASE => ERROR; IF new=NIL OR new.viewer # sel.viewer THEN Deselect[selection]; IF new#NIL THEN { IF selection#feedback AND fSel.viewer=new.viewer THEN Deselect[feedback]; Copy[source: new, dest: sel]; sel.start.metricsValid _ startValid; sel.end.metricsValid _ endValid; IF sel.granularity=point THEN sel.pendingDelete _ FALSE; IF forkPaint THEN { showSel: REF ShowSelRec = SELECT selection FROM primary => showPSel, secondary => showSSel, ENDCASE => showFSel; IF showSel.process = NIL THEN TRUSTED {Process.Detach[showSel.process _ FORK ShowSel[showSel, op ! ABORTED => CONTINUE]]} } ELSE ViewerOps.PaintViewer[sel.viewer, client, FALSE, op] }}; UnlockSel[selection] }; ShowSelRec: TYPE = RECORD [ process: PROCESS, selection: SelectionId ]; showPSel: REF ShowSelRec _ NEW[ShowSelRec _ [NIL, primary]]; showSSel: REF ShowSelRec _ NEW[ShowSelRec _ [NIL, secondary]]; showFSel: REF ShowSelRec _ NEW[ShowSelRec _ [NIL, feedback]]; ShowSel: PROC [my: REF ShowSelRec, op: ATOM] = { selection: SelectionId _ my.selection; sel: Selection _ SELECT selection FROM primary => pSel, secondary => sSel, feedback => fSel, ENDCASE => ERROR; LockSel[selection, "ShowSel"]; { ENABLE UNWIND => UnlockSel[selection]; viewer: ViewerClasses.Viewer _ sel.viewer; IF viewer#NIL THEN { tdd: TEditDocumentData _ NARROW[viewer.data]; IF tdd # NIL AND LockAfterRefresh[tdd, "ShowSel"] THEN { ViewerOps.PaintViewer[viewer, client, FALSE, op]; UnlockAfterRefresh[tdd] }}; my.process _ NIL }; UnlockSel[selection] }; MakeStipple: PROC [s: CARDINAL] RETURNS [a: ARRAY [0..4) OF CARDINAL] ~ { FOR i: NAT DECREASING IN [0..4) DO a[i] _ (s MOD 16) * 1111h; s _ s / 16; ENDLOOP; }; blackStipple: ARRAY [0..4) OF CARDINAL _ MakeStipple[LAST[CARDINAL]]; lightGreyStipple: ARRAY [0..4) OF CARDINAL _ MakeStipple[00208H]; darkGreyStipple: ARRAY [0..4) OF CARDINAL _ MakeStipple[0A5A5H]; veryDarkGreyStipple: ARRAY [0..4) OF CARDINAL _ MakeStipple[0EBEBH]; SelColor: TYPE = {black, lightGrey, darkGrey, veryDarkGrey} _ black; SelBound: TYPE = {solid, line} _ solid; feedbackLineThickness: CARDINAL _ 4; feedbackLineDrop: INTEGER _ 1; lineThickness: CARDINAL _ 2; MarkSelection: PUBLIC PROC [dc: Imager.Context, viewer: ViewerClasses.Viewer, selection: Selection, id: SelectionId] = { WITH viewer.data SELECT FROM tdd: TEditDocumentData=> { OPEN selection; lines: TEditDocument.LineTable = tdd.lineTable; vHeight: INTEGER = viewer.ch; selBound: SelBound = IF selection.pendingDelete AND id#feedback THEN solid ELSE line; selColor: SelColor = IF id=primary THEN black ELSE IF id=feedback THEN veryDarkGrey ELSE IF selection.pendingDelete THEN lightGrey ELSE darkGrey; EffectSelect: PROC [x, y, w, h: INTEGER] = TRUSTED BEGIN area: Imager.IntRectangle _ [x, vHeight-y-h, w, h]; lineHeight: CARDINAL _ 2; stipple: LONG POINTER _ SELECT selColor FROM black => @blackStipple, veryDarkGrey=> @veryDarkGreyStipple, darkGrey => @darkGreyStipple, lightGrey => @lightGreyStipple, ENDCASE => ERROR; IF id=feedback THEN { area.h _ feedbackLineThickness; area.y _ area.y - feedbackLineDrop } ELSE IF NOT selection.pendingDelete THEN { area.h _ lineThickness }; Imager.MaskBits[dc, stipple, 1, [0, 0, 16, 4], area]; END; IF end.line<0 OR start.line=LAST[INTEGER] OR (start.line=end.line AND end.clipped) THEN RETURN; -- not visible Imager.SetColor[dc, Imager.XOR]; IF start.line = end.line OR (start.line < 0 AND end.line = 0) THEN { -- one liner x, y: INTEGER; IF start.line < 0 OR start.clipped THEN { -- select from beginning of line line: INTEGER _ MAX[0,start.line]; x _ lines[line].xOffset; y _ lines[line].yOffset-lines[line].ascent } ELSE { x _ start.x; y _ start.y }; IF end.clipped THEN ERROR; -- previous tests imply ~end.clipped EffectSelect[x, y, end.x-x+end.w, end.h] } ELSE IF start.line=lines.lastLine AND end.line>lines.lastLine THEN -- one liner EffectSelect[start.x, start.y, lines[start.line].width + lines[start.line].xOffset - start.x, start.h] ELSE BEGIN sl: INTEGER = NormalizeLineIndex[lines, start.line]; el: INTEGER = NormalizeLineIndex[lines, end.line]; EffectSelAll: PROC [n: INTEGER] = INLINE { EffectSelect[lines[n].xOffset, lines[n].yOffset-lines[n].ascent, lines[n].width, lines[n].ascent+lines[n].descent] }; IF sl=start.line AND ~start.clipped THEN -- select end portion of sl EffectSelect[start.x, start.y, (lines[sl].width + lines[sl].xOffset - start.x), start.h] ELSE EffectSelAll[sl];-- select all of sl FOR n: INTEGER IN (sl..el) DO -- select all of the intermediate lines EffectSelAll[n]; ENDLOOP; IF end.clipped THEN NULL -- end.line is not actually part of the selection ELSE IF el=end.line THEN -- end.line is on the screen, so select initial part of el EffectSelect[lines[el].xOffset, end.y, end.x - lines[el].xOffset + end.w, end.h] ELSE EffectSelAll[el];-- end.line is off screen, so select all of el END; Imager.SetColor[dc, Imager.black]; }; ENDCASE; }; NormalizeLineIndex: PROC [lines: TEditDocument.LineTable, line: INTEGER] RETURNS [INTEGER] = INLINE { RETURN [MAX[0,MIN[lines.lastLine,line]]] }; ExtendSelection: PUBLIC PROC [dc: Imager.Context, viewer: ViewerClasses.Viewer, old, new: Selection, id: SelectionId, updateEnd: BOOLEAN] = BEGIN TooHard: PROC = BEGIN MarkSelection[dc, viewer, old, id]; -- take it down FixupSelection[new, viewer]; -- get position info about the new selection MarkSelection[dc, viewer, new, id]; -- put it up Copy[source: new, dest: old]; END; IF updateEnd THEN BEGIN -- update right end BumpLoc: PROC [loc: TextNode.Location] RETURNS [TextNode.Location] = { n: TextNode.RefTextNode = TextNode.NarrowToTextNode[loc.node]; where: TextNode.Offset _ loc.where+1; IF where >= TextEdit.Size[n] THEN RETURN [[TextNode.StepForward[n],0]]; RETURN [[n,where]]; }; tooHard: BOOLEAN = old.end.line NOT IN [0..LAST[INTEGER]); sameNode: BOOLEAN = (new.end.pos.node = old.end.pos.node); IF tooHard THEN {TooHard; RETURN}; IF new.end.pos=old.end.pos THEN NULL ELSE IF (sameNode AND new.end.pos.where>old.end.pos.where) OR (~sameNode AND EditSpan.CompareNodeOrder[new.end.pos.node, old.end.pos.node]=after) THEN BEGIN -- new end after old end new.start _ old.end; new.start.pos _ BumpLoc[new.start.pos]; IF sameNode AND new.start.pos.node # new.end.pos.node THEN new.start.pos _ new.end.pos; FixupSelection[new, viewer]; MarkSelection[dc, viewer, new, id]; old.end _ new.end; END ELSE BEGIN -- new end before old end save: TextNode.Location = new.end.pos; new.start _ new.end; new.start.pos _ BumpLoc[save]; IF sameNode AND new.start.pos.node # new.end.pos.node THEN new.start.pos _ old.end.pos; new.end _ old.end; FixupSelection[new, viewer, TRUE, FALSE]; -- fix start MarkSelection[dc, viewer, new, id]; old.end _ new.start; old.end.pos _ save; FixupSelection[old, viewer, FALSE, TRUE]; END; END ELSE BEGIN -- update start DecLoc: PROC [loc: TextNode.Location] RETURNS [TextNode.Location] = { n: TextNode.RefTextNode; IF loc.where > 0 THEN RETURN [[loc.node, loc.where-1]]; n _ TextNode.NarrowToTextNode[TextNode.StepBackward[loc.node]]; RETURN [[n,MAX[TextEdit.Size[n],1]-1]] }; tooHard: BOOLEAN = old.start.line NOT IN [0..LAST[INTEGER]); sameNode: BOOLEAN = (new.start.pos.node = old.start.pos.node); IF tooHard THEN {TooHard; RETURN}; IF new.start.pos=old.start.pos THEN NULL ELSE IF (sameNode AND new.start.pos.where= next AND lines[line].end # eon THEN RETURN [TRUE]; -- it is past this line RETURN [FALSE] }; FindPos: PROC [pos: TextNode.Location] RETURNS [found: BOOLEAN, line: INTEGER] = { found _ FALSE; FOR n: INTEGER IN [firstLine..lines.lastLine] DO IF lines[n].pos.node=pos.node THEN { IF pos.where < lines[n].pos.where THEN EXIT; -- have gone past it found _ TRUE; line _ n } ELSE IF found THEN EXIT; -- have gone past pos.node ENDLOOP}; IF tdd = NIL THEN RETURN; lines _ tdd.lineTable; sp.pos _ pos; IF (lines[0].pos.node=pos.node AND lines[0].pos.where>pos.where) THEN BEGIN sp.y _ sp.line _ -LAST[INTEGER]; -- before beginning of text RETURN; END; IF lines[lines.lastLine].pos.node=pos.node AND IsPosPastLine[pos,lines.lastLine] THEN BEGIN sp.y _ sp.line _ LAST[INTEGER]; -- after end of text RETURN; END; firstLine _ MIN[lines.lastLine,MAX[firstLine,0]]; [foundPos, sp.line] _ FindPos[pos]; sp.clipped _ FALSE; IF ~foundPos THEN SELECT EditSpan.CompareNodeOrder[pos.node, lines[0].pos.node] FROM same => ERROR; disjoint => ERROR CannotFindIt; before => { sp.line _ sp.y _ -LAST[INTEGER]; RETURN }; -- before beginning ENDCASE => -- pos.node comes after first line node IF tdd.clipLevel = maxClip AND tdd.commentFilter=includeComments THEN { sp.line _ sp.y _ LAST[INTEGER]; RETURN } -- after end ELSE { -- must think about this harder SELECT EditSpan.CompareNodeOrder[pos.node, lines[lines.lastLine].pos.node] FROM same => ERROR; disjoint => ERROR CannotFindIt; -- may have been deleted or moved after => { -- it really is after the end sp.line _ sp.y _ LAST[INTEGER]; RETURN } ENDCASE => { -- it got clipped n: TextNode.Ref _ pos.node; delta: INTEGER _ TextNode.Level[n]-tdd.clipLevel; firstLine _ 0; -- need to let FindPos look at all the previous lines FOR i:INTEGER IN [0..delta) DO n _ TextNode.Parent[n]; ENDLOOP; n _ TextNode.ForwardClipped[n,tdd.clipLevel].nx; [foundPos, sp.line] _ FindPos[[n,0]]; IF ~foundPos THEN ERROR CannotFindIt; -- may have been restored by Undo sp.clipped _ TRUE; }; }; IF lineOnly OR sp.clipped THEN RETURN; -- otherwise, compute metrics sp.y _ lines[sp.line].yOffset-lines[sp.line].ascent; sp.h _ lines[sp.line].ascent+lines[sp.line].descent; [sp.x, sp.w] _ CharPositionInCachedLine[viewer, tdd, sp.line, pos]; IF sp.x = LAST[INTEGER] THEN { IF sp.line=lines.lastLine THEN sp.line _ sp.y _ LAST[INTEGER] ELSE {sp.x _ lines[sp.line].width; sp.w _ 0}; }; sp.metricsValid _ TRUE; END; FixupSelection: PUBLIC PROC [selection: Selection, viewer: ViewerClasses.Viewer, start, end: BOOLEAN _ TRUE] = BEGIN IF start THEN selection.start _ ComputePosPoint[viewer, selection.start.pos]; IF end THEN BEGIN IF selection.start.pos=selection.end.pos THEN selection.end _ selection.start -- no need to re-compute ELSE IF selection.start.line=LAST[INTEGER] THEN -- scrolled off bottom {selection.end.line _ LAST[INTEGER]; selection.end.metricsValid _ TRUE} ELSE { firstLine: INTEGER = IF selection.start.line<=0 THEN 0 ELSE IF selection.start.clipped THEN selection.start.line-1 ELSE selection.start.line; selection.end _ ComputePosPoint[viewer, selection.end.pos, firstLine] }; END; END; FixupCaret: PUBLIC PROC [selection: Selection] = BEGIN OPEN selection; lines: TEditDocument.LineTable = data.lineTable; node: TextNode.RefTextNode _ TextNode.NarrowToTextNode[end.pos.node]; size: TextNode.Offset _ TextEdit.Size[node]; PutCaretOnNextLine: PROC = BEGIN SELECT end.line FROM IN [0..lines.lastLine) => BEGIN nextLine: INTEGER _ end.line+1; caretX _ lines[nextLine].xOffset; caretY _ viewer.ch - lines[nextLine].yOffset - lines[nextLine].descent; END; = lines.lastLine => BEGIN caretX _ lines[end.line].xOffset; caretY _ viewer.ch - end.y - end.h - end.h; END; ENDCASE => ERROR; END; IF insertion=before THEN BEGIN IF start.clipped THEN caretY _ LAST[INTEGER] -- caret not visible --ELSE IF start.line=lines.lastLine AND granularity=point AND --start.pos.where=size AND size>0 AND TextEdit.FetchChar[node, size-1]=15C --THEN PutCaretOnNextLine ELSE BEGIN caretX _ start.x; caretY _ IF start.line NOT IN [0..lines.lastLine] THEN LAST[INTEGER] ELSE viewer.ch - start.y - start.h; END; END ELSE BEGIN IF end.clipped THEN caretY _ LAST[INTEGER] -- caret not visible ELSE IF end.pos.where IN [0..size) AND end.line IN [0..lines.lastLine] AND TextEdit.FetchChar[node,end.pos.where]=15C THEN PutCaretOnNextLine ELSE BEGIN caretX _ end.x+end.w; caretY _ IF end.line NOT IN [0..lines.lastLine] THEN LAST[INTEGER] ELSE viewer.ch - end.y - end.h; END END; END; KillSelection: PUBLIC PROCEDURE = BEGIN IF InputFocus.GetInputFocus[]#NIL THEN InputFocus.SetInputFocus[]; END; ModifyPSel: PROC [proc: PROC [root: TextNode.Ref, tSel: Selection]] = { tSel: Selection; { ENABLE UNWIND => { UnlockSel[primary]; IF tSel # NIL THEN Free[tSel] }; root: TextNode.Ref; LockSel[primary, "CallWithSelLock"]; IF (root _ SelectionRoot[pSel])=NIL THEN { UnlockSel[primary]; RETURN }; tSel _ Alloc[]; TEditSelection.Copy[source: pSel, dest: tSel]; proc[root, tSel]; MakeSelection[new: tSel] }; Free[tSel]; tSel _ NIL; UnlockSel[primary] }; SelectEverything: PUBLIC PROC = { -- expand to include everything DoSelEverything: PROC [root: TextNode.Ref, tSel: Selection] = { root _ TextNode.Root[tSel.start.pos.node]; tSel.start.pos _ [TextNode.FirstChild[root], 0]; tSel.end.pos.node _ TextNode.LastWithin[root]; tSel.end.pos.where _ TextEdit.Size[TextNode.NarrowToTextNode[tSel.end.pos.node]]-1; tSel.granularity _ branch }; ModifyPSel[DoSelEverything] }; PendingDeleteSelection: PUBLIC PROC = { DoPDel: PROC [root: TextNode.Ref, tSel: Selection] = { tSel.pendingDelete _ TRUE }; ModifyPSel[DoPDel] }; NotPendingDeleteSelection: PUBLIC PROC = { DoNotPDel: PROC [root: TextNode.Ref, tSel: Selection] = { tSel.pendingDelete _ FALSE }; ModifyPSel[DoNotPDel] }; CaretBeforeSelection: PUBLIC PROC = { DoCaretBeforeSelection: PROC [root: TextNode.Ref, tSel: Selection] = { tSel.insertion _ before }; ModifyPSel[DoCaretBeforeSelection] }; CaretAfterSelection: PUBLIC PROC = { DoCaretAfterSelection: PROC [root: TextNode.Ref, tSel: Selection] = { IF tSel.granularity # point THEN tSel.insertion _ after }; ModifyPSel[DoCaretAfterSelection] }; ------ Misc functions ------ SelectionRoot: PUBLIC PROC [s: Selection _ pSel] RETURNS [root: TextNode.Ref] = { tdd: TEditDocument.TEditDocumentData; IF s.viewer=NIL THEN RETURN [TextNode.Root[s.start.pos.node]]; tdd _ NARROW[s.viewer.data]; IF tdd = NIL THEN RETURN [NIL]; RETURN [tdd.text] }; InputModify: PUBLIC ViewerClasses.ModifyProc = BEGIN tdd: TEditDocumentData = NARROW[self.data]; IF tdd = NIL THEN RETURN; SELECT change FROM set, pop => Carets.StartCaret[self, pSel.caretX, pSel.caretY, primary]; kill => BEGIN prop: TEditDocument.Selection _ NARROW[ViewerOps.FetchProp[self, $SelectionHistory]]; Carets.StopCaret[primary]; IF prop=NIL THEN ViewerOps.AddProp[self, $SelectionHistory, prop _ Create[]]; LockSel[primary, "InputModify"]; { ENABLE UNWIND => UnlockSel[primary]; Copy[source: pSel, dest: prop]; Deselect[primary] }; UnlockSel[primary]; END; push => Carets.StopCaret[primary]; ENDCASE => ERROR; END; END. 2TEditSelectionImpl.mesa Edited by Paxton on May 23, 1983 12:03 pm Last Edited by: Maxwell, January 6, 1983 11:35 am Last Edited by: Plass, October 12, 1983 9:21 am -- make a point selection at pos out the the current primary selection Take down the selection without changing the input focus. If the selection was not in a visible viewer, PaintViewer didn't call our paint proc, so the state didn't change. Force change directly. Changes input focus as well as the selection. When called to make a non-feedback selection is the viewer containing the feedback selection, automatically cancel the feedback selection. -- when done, old is valid (it's = pSel, e.g.) and new^ = old^ This weird case can happen if old ended with final CR of node and new is in the null line displayed after the CR. old ended in null line after final CR, new ends with the final CR -- this must work correctly even if the node was filtered out for some reason -- if pos was filtered, return line where it would have been and set clipped=TRUE -- nothing special going on, so would have found it if not after end past this line -- recompute the xywh fields in the selection -- fixes selection screen info assuming start.pos and end.pos are correct -- recompute the xy fields in the caret -- selection actually get taken down in our InputModify Proc ʘJšœA™AJšœ1™1J™/JšÏk ˜ Jšœœ˜%Jšœœ*œ ˜EJšœ œ˜-Jšœ œ'˜7Jšœœ ˜Jšœ œ˜)Jšœ œ˜!šœ œL˜ZJ˜I—šœœ7˜JJšœK˜K—Jšœœ¢˜¶Jšœ œ(˜:Jšœ œ#˜2Jšœœ˜)J˜Jšœ ˜!J˜Jšœk˜rJšœ˜J˜Jšœ-˜1J˜JšÏc+˜+J˜šÏnœœœ2˜VJšžF™FJšœ˜Jšœ$˜$J˜$J˜Jšœœ˜J˜J˜-J˜ Jšœ˜J˜—šŸœœœ'˜IJšœ˜Jšœ+˜+šœœœ˜+Jšœ,˜,—Jšœ%˜%J˜—šŸœœœ˜)šŸœœ˜"Jš œœ œœœœ˜>Jš œœ œœœœ˜B—Jšœ!˜!J˜—šŸ œœœž*˜GšŸ œœ˜)J˜4—Jšœ(˜(J˜—šŸ œœœž*˜IšŸœœ˜+J˜4—Jšœ*˜*J˜—š Ÿ œœœœœ œ˜=J˜—š Ÿœœœœœ œ˜AJ˜—š Ÿœœœœœ œ˜?J˜—šŸ œœœ˜/Jšœ$˜$šœœœ˜(Jšœœœ˜.Jšœ ˜ —Jšœ˜J˜—šŸœœœ'˜Jšœœœ&˜@Jš˜—Jšœ+˜+Jšœ,˜,Jšœœ˜—Jšœœœœ˜?šœœœ˜šœœœ˜IJ™Š—Jšœ˜J˜$J˜ Jšœœœ˜8šœ œ˜šœ œœ ˜/Jšœ,œ ˜@—šœœ˜šœ"˜)Jšœœœ˜3———Jšœ+œ ˜=——Jšœ˜—J˜Jšœ œœ œ˜GJšœ œœœ ˜Jšœ œœœ ˜=J˜šŸœœœœ˜0Jšœ&˜&šœœ ˜&Jšœ6œœ˜G—J˜šœœœ˜(Jšœ*˜*šœœœ˜Jšœœ ˜-šœœœ"œ˜8Jšœ&œ˜1Jšœ˜——Jšœ œ˜—Jšœœ˜J˜—šŸ œœœœœœœ˜Iš œœ œœ˜"Jšœ œ ˜Jšœ ˜ Jšœ˜—Jšœ˜—Jš œœœœœœ˜EJš œœœœÏfœ˜AJš œœœœ œ˜@Jš œœœœ œ˜DJšœ œ6˜DJšœ œ˜'Jšœœ˜$Jšœœ˜Jšœœ˜J˜šŸ œœœ3˜MJ˜*šœ œ˜šœ˜Jšœ ˜J˜/Jšœ œ ˜Jš œœœ œœ˜Uš œœ œœœ œ˜TJšœœœ œ ˜=J˜—šŸ œœœ˜*Jšœ˜ Jšœ3˜3Jšœ œ˜šœ œœ˜šœ ˜Jšœ˜Jšœ$˜$Jšœ˜Jšœ ˜ Jšœœ˜——šœ œ˜Jšœ˜Jšœ"˜"Jšœ˜—šœœœœ˜*Jšœ˜Jšœ˜—Jšœ5˜5Jšœ˜J˜—š œ œ œœ˜,Jšœœœœž˜AJ˜—Jšœœ˜ J˜š œœœœž ˜QJšœœ˜šœœœž ˜JJšœœœ˜"J˜J˜,—Jšœ˜"Jšœ œœž$˜?J˜(J˜—š œœœœž ˜OJ˜f—šœ˜ Jšœœ)˜4Jšœœ'˜2šŸ œœœœ˜*˜PJ˜$——šœœœž˜D˜1J˜&——Jšœž˜)š œœœ œž'˜EJšœœ˜—Jšœ œœž1˜Jšœœ œž:˜SJ˜P—Jšœž.˜DJšœ˜—J˜Jšœ"˜"J˜—Jšœ˜—Jšœ˜J˜—šŸœœ(œ˜HJšœœœ˜Jšœœœ˜+J˜—šŸœœœ3˜OJšœ1œ˜AJšž>™>J˜šŸœœ˜Jšœ$ž˜3Jšœž,˜IJšœ$ž ˜0J˜Jšœ˜J˜—šœ œœž˜+šŸœœœ˜FJ˜>J˜%Jšœœœ˜GJšœ ˜J˜—Jš œ œœœœœ˜:Jšœ œ)˜:Jšœ œ œ˜"Jšœœ˜$šœœ œ&˜=šœ ˜JšœE˜I—Jšœž˜J˜J˜'šœ œ'˜:Jšœœ˜J™q—Jšœ˜Jšœ#˜#J˜Jš˜—šœœž˜$J˜&J˜J˜šœ œ'œ˜WJ™A—J˜Jšœœœž ˜6J˜#J˜J˜Jšœœœ˜)Jšœ˜—Jš˜—šœœž˜šŸœœœ˜EJ˜Jšœœœ˜7J˜?Jšœœ˜)—Jš œ œœœœœ˜Jšœ œ œ˜"Jšœœ˜(šœœ œ*˜Ašœ ˜JšœJ˜N—Jšœž#˜)J˜$J˜J˜#J˜Jš˜—šœœž"˜-J˜(J˜J˜Jšœœœ˜)J˜#J˜Jšœœœ˜)Jšœ˜—Jšœ˜—J˜Jšœ˜J˜—Jšœœœœ˜"J˜šŸœœœ4˜QJšœœœ˜HJ˜:Jšœœ+˜FJšœœœœœœœž˜Kšœ˜šœ œœ œ˜,Jšœœ œ˜(—J˜A—Jšœ˜J˜—šŸœœœ˜JšœAœ˜MJšœœ œ˜1Jšœ=œ˜CJšœ˜Jšœ˜J˜—šŸœœœ˜J˜5Jšœ œœœ˜2Jšœ˜$J˜JšžM™MJšžQ™QJ˜Jšœ œœ˜Jšœœ˜-J˜J˜š Ÿ œœ œœœ˜QJ˜J˜0Jš œœœœœž˜ZJšœœ˜J˜—šŸœœ˜&Jšœ œœ˜+Jšœœ˜šœœœ˜0šœœ˜$Jšœ œœž˜AJšœœ ˜—Jš œœœœž˜3—Jšœ˜ J˜—Jšœœœœ˜J˜J˜ J˜šœ˜"Jšœœ˜(Jšœœœž˜Jšœœ˜Jš œœœœœ˜Jšœ˜J˜—šœ œ˜4Jšœœ ˜+Jšœœœœ˜Jšœ˜J˜Gšœ˜ ˜Jšœ/˜5—J˜Jšœœœ=˜MJšœ ˜ šœœœ˜&Jšœ4˜4—Jšœ˜Jšœ˜—J˜"Jšœœ˜Jšœ˜J˜—Jšœ˜J˜J˜—…—Oðm)