DIRECTORY Carets USING [StartCaret, StopCaret], Graphics USING [black, Context, DrawBox, SetColor, SetPaintMode, SetStipple], 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], TEditFormat USING [LineInfo], TEditSelection USING [Alloc, Copy, Create, Free, GetCachedLineInfo, 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, Graphics, 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] }; lightGrey: CARDINAL = 0208H; darkGrey: CARDINAL = 0A5A5H; veryDarkGrey: CARDINAL _ 0EBEBH; SelColor: TYPE = {black, lightGrey, darkGrey, veryDarkGrey} _ black; SelBound: TYPE = {solid, line} _ solid; feedbackLineWidth: CARDINAL _ 4; feedbackLineRaise: INTEGER _ 1; MarkSelection: PUBLIC PROC [dc: Graphics.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] = BEGIN OPEN Graphics; lineHeight: CARDINAL _ 2; bottom: INTEGER _ vHeight-y-h; IF id=feedback THEN { lineHeight _ feedbackLineWidth; bottom _ bottom - feedbackLineRaise }; SELECT selColor FROM black => SetColor[dc, black]; darkGrey => SetStipple[dc, darkGrey]; lightGrey => SetStipple[dc, lightGrey]; veryDarkGrey=> SetStipple[dc, veryDarkGrey]; ENDCASE => ERROR; SELECT selBound FROM solid => DrawBox[dc, [x, bottom, x+w, vHeight-y]]; ENDCASE => DrawBox[dc, [x, bottom, x+w, bottom+lineHeight]]; END; IF end.line<0 OR start.line=LAST[INTEGER] OR (start.line=end.line AND end.clipped) THEN RETURN; -- not visible [] _ Graphics.SetPaintMode[dc, invert]; 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; [] _ Graphics.SetPaintMode[dc, opaque]; }; ENDCASE; }; NormalizeLineIndex: PROC [lines: TEditDocument.LineTable, line: INTEGER] RETURNS [INTEGER] = INLINE { RETURN [MAX[0,MIN[lines.lastLine,line]]] }; ExtendSelection: PUBLIC PROC [dc: Graphics.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; [lineInfo, nChars] _ GetCachedLineInfo[viewer, tdd, sp.line]; IF lineInfo[0]>=LAST[INTEGER] THEN BEGIN -- empty line sp.x _ lines[sp.line].xOffset; sp.w _ 0; END ELSE IF pos.where>lines[sp.line].pos.where+nChars THEN BEGIN -- past this line IF sp.line=lines.lastLine THEN sp.line _ sp.y _ LAST[INTEGER] ELSE {sp.x _ lines[sp.line].width; sp.w _ 0}; END ELSE BEGIN cx: INTEGER _ lines[sp.line].xOffset; cw: INTEGER _ lineInfo[0]; last: INTEGER = pos.where-lines[sp.line].pos.where; FOR n: INTEGER IN [1..last] DO cx _ cx + cw; IF (cw_lineInfo[n]) = LAST[INTEGER] THEN BEGIN IF pos.where > lines[sp.line].pos.where+n AND pos.where # TextEdit.Size[TextNode.NarrowToTextNode[pos.node]] THEN sp.line _ LAST[INTEGER]; cw _ 0; EXIT; END; ENDLOOP; sp.x _ cx; sp.w _ cw; END; 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. J-- TEditSelectionImpl.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, May 2, 1983 1:19 pm -- 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 -- off end; selection past end of screen -- 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šÏcD™DJšÏk1™1J™*Jšž ˜ Jšœžœ˜%Jšœ žœ?˜MJšœ žœ˜-Jšœ žœ'˜7Jšœžœ ˜Jšœ žœ˜)Jšœ žœ˜!šœ žœL˜ZJ˜I—šœžœ7˜JJšœK˜K—Jšœ žœ ˜Jšœžœ›˜¯Jšœ žœ(˜:Jšœ žœ#˜2Jšœžœ˜)J˜Jšœž ˜!J˜Jšžœm˜tJšžœž˜J˜Jšžœ-˜1J˜Jš+˜+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˜—Jšœ žœ ˜Jšœ žœ ˜Jšœžœ ˜ Jšœ žœ6˜DJšœ žœ˜'Jšœžœ˜ Jšœžœ˜J˜šŸ œžœžœ5˜OJ˜*šžœ žœž˜šœ˜Jšžœ ˜J˜/Jšœ žœ ˜Jš œžœžœ žœžœ˜Uš œžœ žœžœžœ žœ˜TJšžœžœžœ žœ ˜=J˜—šŸ œžœžœ˜*Jšžœžœ ˜Jšœ žœ˜Jšœžœ˜Jšžœ žœI˜\šžœ ž˜J˜J˜&Jšœ(˜(Jšœ,˜,Jšžœžœ˜—šžœ ž˜Jšœ3˜3Jšžœ5˜<—Jšžœ˜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˜—šŸœžœžœ5˜QJšœ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˜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šžœ˜—Jšžœ˜—J˜ J˜ Jšžœ˜—Jšœžœ˜Jšžœ˜J˜—šŸœžœžœ5˜PJšœ žœžœž˜#Jš-™-JšI™IJ˜Jšžœžœ@˜MJ˜šžœžœž˜šžœ'ž˜-Jšœ ˜8—š žœžœžœžœžœ˜FJšœžœžœ žœ˜G—šžœ˜šœ žœžœžœ˜6Jšžœžœžœžœ˜V—J˜H—Jšžœ˜—Jšžœ˜J˜—š Ÿ œžœžœžœžœ ˜FJš'™'J˜0J˜EJ˜,šŸœžœž˜ šžœ ž˜šžœž˜Jšœ žœ˜J˜!J˜GJšžœ˜—šœž˜J˜!J˜+Jšžœ˜—Jšžœžœ˜—Jšžœ˜—šžœžœž˜Jš žœžœ žœžœ˜Ašžœžœžœž˜=šœžœžœ%˜JJšžœ˜——šžœž˜ J˜š œ žœ žœžœžœžœžœ˜DJšžœ˜#—Jšžœ˜—Jšž˜—šžœž˜ Jš žœ žœ žœžœ˜?š žœžœžœ žœ žœž˜JJšœ+žœ˜B—šžœž˜ J˜š œ žœ žœžœžœžœžœ˜BJšžœ˜—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˜—…—QnY