DIRECTORY Carets USING [StartCaret, StopCaret], EditSpan USING [CompareNodeOrder, NodeOrder], Imager USING [Color, Context, MaskRectangleI, SetColor], ImagerBackdoor USING [MakeStipple], InputFocus USING [Focus, GetInputFocus, SetInputFocus], Process USING [Detach], Rope USING [ROPE], TextEdit USING [FetchChar, 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, IsDown, ForceDown, LockBothSelections, LockSel, fSel, nilSel, pSel, sSel, UnlockBothSelections, UnlockSel], TEditSelectionPrivate USING [CharPositionInCachedLine], TEditTouchup USING [LockAfterRefresh, UnlockAfterRefresh], ViewerLocks USING [CallUnderWriteLock], ViewerOps USING [AddProp, FetchProp, PaintViewer], ViewerClasses USING [ModifyProc, Viewer]; TEditSelectionImpl: CEDAR PROGRAM IMPORTS Carets, EditSpan, Imager, ImagerBackdoor, InputFocus, Process, TextEdit, TextNode, TEditSelection, TEditSelectionPrivate, TEditTouchup, ViewerLocks, ViewerOps EXPORTS TEditSelection = BEGIN OPEN TEditSelection; ROPE: TYPE = Rope.ROPE; Selection: TYPE = TEditDocument.Selection; Viewer: TYPE = ViewerClasses.Viewer; MakePointSelection: PUBLIC PROC [selection: Selection, pos: TextNode.Location] = { tSel: Selection _ TEditSelection.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]; }; ChangeSelections: PROC [proc: PROC [tSel: Selection], sel: Selection] = { tSel: Selection _ TEditSelection.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 = { DoMakePrimary: PROC [tSel: Selection] = { Deselect[secondary]; MakeSelection[tSel, primary] }; ChangeSelections[DoMakePrimary, sSel] }; MakeSecondary: PUBLIC PROC = { 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] = { inner: PROC = { IF sSel.viewer # NIL THEN Deselect[secondary]; Copy[source: sel, dest: sSel]; }; WithLockedSelection[secondary, inner, "FakeSecondary"]; }; Deselect: PUBLIC PROC [selection: TEditDocument.SelectionId _ primary] = { inner: PROC = { sel: Selection = SELECT selection FROM primary => pSel, secondary => sSel, feedback => fSel, ENDCASE => ERROR; viewer: 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 NOT down THEN { ViewerOps.PaintViewer[viewer, client, FALSE, op]; IF NOT IsDown[selection] THEN ForceDown[selection]; } }; WithLockedSelection[selection, inner, "Deselect"]; }; MakeSelection: PUBLIC PROC [new: Selection _ NIL, selection: TEditDocument.SelectionId _ primary, startValid, endValid: BOOL _ FALSE, forkPaint: BOOL _ TRUE] = { inner: PROC = { sel: Selection; op: ATOM; SELECT selection FROM primary => { if: 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]; }; 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] } }; WithLockedSelection[selection, inner, "MakeSelection"]; }; ShowSelRec: TYPE = RECORD [ process: PROCESS, selection: TEditDocument.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: TEditDocument.SelectionId _ my.selection; sel: Selection _ SELECT selection FROM primary => pSel, secondary => sSel, feedback => fSel, ENDCASE => ERROR; inner: PROC = { viewer: Viewer _ sel.viewer; inner: PROC = { WITH viewer.data SELECT FROM tdd: TEditDocument.TEditDocumentData => { IF NOT TEditTouchup.LockAfterRefresh[tdd, "ShowSel"] THEN RETURN; ViewerOps.PaintViewer[viewer, client, FALSE, op ! UNWIND => TEditTouchup.UnlockAfterRefresh[tdd]]; TEditTouchup.UnlockAfterRefresh[tdd]; }; ENDCASE; }; IF viewer#NIL THEN ViewerLocks.CallUnderWriteLock[inner, viewer]; my.process _ NIL }; WithLockedSelection[selection, inner, "ShowSel"]; }; lightGrey: Imager.Color _ ImagerBackdoor.MakeStipple[stipple: 0208H, xor: TRUE]; darkGrey: Imager.Color _ ImagerBackdoor.MakeStipple[stipple: 0A5A5H, xor: TRUE]; veryDarkGrey: Imager.Color _ ImagerBackdoor.MakeStipple[stipple: 0EBEBH, xor: TRUE]; black: Imager.Color _ ImagerBackdoor.MakeStipple[stipple: 0FFFFH, xor: TRUE]; SelColor: TYPE = {black, lightGrey, darkGrey, veryDarkGrey} _ black; SelBound: TYPE = {solid, line} _ solid; selectionLineWidth: NAT _ 2; feedbackLineWidth: NAT _ 4; feedbackLineRaise: INTEGER _ 1; selectionDescentLimit: NAT _ 7; MarkSelection: PUBLIC PROC [dc: Imager.Context, viewer: Viewer, selection: Selection, id: TEditDocument.SelectionId] = { WITH viewer.data SELECT FROM tdd: TEditDocument.TEditDocumentData => { lines: TEditDocument.LineTable = tdd.lineTable; vHeight: INTEGER = selection.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] = { lineHeight: NAT _ selectionLineWidth; bottom: INTEGER _ vHeight-y-h; IF id=feedback THEN { lineHeight _ feedbackLineWidth; bottom _ bottom - feedbackLineRaise }; SELECT selColor FROM black => Imager.SetColor[dc, black]; lightGrey => Imager.SetColor[dc, lightGrey]; darkGrey => Imager.SetColor[dc, darkGrey]; veryDarkGrey=> Imager.SetColor[dc, veryDarkGrey]; ENDCASE => ERROR; SELECT selBound FROM line => Imager.MaskRectangleI[dc, x, bottom, w, lineHeight]; solid => Imager.MaskRectangleI[dc, x, bottom, w, vHeight-y-bottom]; ENDCASE => ERROR; }; IF selection.end.line<0 OR selection.start.line=LAST[INTEGER] OR (selection.start.line=selection.end.line AND selection.end.clipped) THEN RETURN; -- not visible SELECT TRUE FROM selection.start.line = selection.end.line OR (selection.start.line < 0 AND selection.end.line = 0) => { x, y: INTEGER; IF selection.start.line < 0 OR selection.start.clipped THEN { line: INTEGER _ MAX[0, selection.start.line]; x _ lines[line].xOffset; y _ lines[line].yOffset-lines[line].ascent; } ELSE { x _ selection.start.x; y _ selection.start.y }; IF selection.end.clipped THEN ERROR; -- previous tests imply NOT end.clipped EffectSelect[x, y, selection.end.x-x+selection.end.w, selection.end.h] }; selection.start.line=lines.lastLine AND selection.end.line>lines.lastLine => { EffectSelect[selection.start.x, selection.start.y, lines[selection.start.line].width + lines[selection.start.line].xOffset - selection.start.x, selection.start.h] }; ENDCASE => { sl: INTEGER = MAX[0, MIN[lines.lastLine, selection.start.line]]; el: INTEGER = MAX[0, MIN[lines.lastLine, selection.end.line]]; EffectSelAll: PROC [n: INTEGER] = { EffectSelect[lines[n].xOffset, lines[n].yOffset-lines[n].ascent, lines[n].width, lines[n].ascent+lines[n].descent] }; IF sl=selection.start.line AND NOT selection.start.clipped THEN { EffectSelect[selection.start.x, selection.start.y, (lines[sl].width + lines[sl].xOffset - selection.start.x), selection.start.h] } ELSE { EffectSelAll[sl]; }; FOR n: INTEGER IN (sl..el) DO EffectSelAll[n]; ENDLOOP; SELECT TRUE FROM selection.end.clipped => {}; -- end.line is not actually part of the selection el=selection.end.line => { EffectSelect[lines[el].xOffset, selection.end.y, selection.end.x - lines[el].xOffset + selection.end.w, selection.end.h]; }; ENDCASE => { EffectSelAll[el];-- end.line is off screen, so select all of el }; }; }; ENDCASE; }; ExtendSelection: PUBLIC PROC [dc: Imager.Context, viewer: Viewer, old, new: Selection, id: TEditDocument.SelectionId, updateEnd: BOOL] = { TooHard: PROC = { 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]; }; IF updateEnd THEN { 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: BOOL = old.end.line NOT IN [0..LAST[INTEGER]); sameNode: BOOL = (new.end.pos.node = old.end.pos.node); IF tooHard THEN {TooHard; RETURN}; SELECT TRUE FROM new.end.pos=old.end.pos => {}; (sameNode AND new.end.pos.where>old.end.pos.where) OR (NOT sameNode AND EditSpan.CompareNodeOrder[new.end.pos.node, old.end.pos.node]=after) => { 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; } ENDCASE => { 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]; }; } ELSE { 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: BOOL = old.start.line NOT IN [0..LAST[INTEGER]); sameNode: BOOL = (new.start.pos.node = old.start.pos.node); IF tooHard THEN {TooHard; RETURN}; SELECT TRUE FROM new.start.pos=old.start.pos => {}; (sameNode AND new.start.pos.where { new.end.pos _ DecLoc[old.start.pos]; FixupSelection[new, viewer]; MarkSelection[dc, viewer, new, id]; old.start _ new.start; } ENDCASE => { save: TextNode.Location = new.start.pos; new.end.pos _ DecLoc[save]; new.start _ old.start; FixupSelection[new, viewer, FALSE, TRUE]; MarkSelection[dc, viewer, new, id]; old.start.pos _ save; FixupSelection[old, viewer, TRUE, FALSE]; }; }; Copy[source: old, dest: new]; }; CannotFindIt: PUBLIC ERROR = CODE; ComputeSpanLines: PUBLIC PROC [viewer: Viewer, span: TextNode.Span] RETURNS [start, end: INTEGER, startClipped, endClipped: BOOL] = { [start,startClipped] _ ComputePosLine[viewer, span.start]; SELECT TRUE FROM span.start=span.end => { end _ start; endClipped _ startClipped }; start=LAST[INTEGER] => end _ LAST[INTEGER] -- scrolled off bottom ENDCASE => { firstLine: INTEGER = IF start<=0 THEN 0 ELSE IF startClipped THEN start-1 ELSE start; [end,endClipped] _ ComputePosLine[viewer, span.end, firstLine]; }; }; ComputePosLine: PUBLIC PROC [viewer: Viewer, pos: TextNode.Location, firstLine: INTEGER _ 0] RETURNS [line: INTEGER, clipped: BOOL] = { sp: TEditDocument.SelectionPoint _ ComputePosPoint[viewer, pos, firstLine, TRUE]; RETURN [sp.line, sp.clipped]; }; ComputePosPoint: PUBLIC PROC [viewer: Viewer, pos: TextNode.Location, firstLine: INTEGER _ 0, lineOnly: BOOL _ FALSE] RETURNS [sp: TEditDocument.SelectionPoint] = { WITH viewer.data SELECT FROM tdd: TEditDocument.TEditDocumentData => { foundPos: BOOL _ FALSE; IsPosPastLine: PROC [pos: TextNode.Location, line: INTEGER] RETURNS [BOOL] = { next: TextNode.Offset; next _ lines[line].pos.where+lines[line].nChars; IF 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: BOOL, line: INTEGER] = { found _ FALSE; FOR n: INTEGER IN [firstLine..lines.lastLine] DO SELECT TRUE FROM lines[n].pos.node=pos.node => { IF pos.where < lines[n].pos.where THEN EXIT; -- have gone past it found _ TRUE; line _ n }; found => EXIT; ENDCASE; ENDLOOP }; lines: TEditDocument.LineTable _ tdd.lineTable; sp.pos _ pos; IF lines[0].pos.node = pos.node THEN { IF lines[0].pos.where > pos.where THEN { sp.y _ sp.line _ -LAST[INTEGER]; RETURN; }; }; IF lines[lines.lastLine].pos.node=pos.node AND IsPosPastLine[pos,lines.lastLine] THEN { sp.y _ sp.line _ LAST[INTEGER]; -- after end of text RETURN; }; firstLine _ MIN[lines.lastLine,MAX[firstLine,0]]; [foundPos, sp.line] _ FindPos[pos]; sp.clipped _ FALSE; IF NOT foundPos THEN SELECT EditSpan.CompareNodeOrder[pos.node, lines[0].pos.node] FROM same, before => { sp.line _ sp.y _ -LAST[INTEGER]; RETURN }; -- before beginning disjoint => ERROR CannotFindIt; ENDCASE => -- pos.node comes after first line node IF tdd.clipLevel = TEditDocument.maxClip AND tdd.commentFilter=includeComments THEN { sp.line _ sp.y _ LAST[INTEGER]; RETURN } -- after end ELSE { SELECT EditSpan.CompareNodeOrder[pos.node, lines[lines.lastLine].pos.node] FROM same, after => { sp.line _ sp.y _ LAST[INTEGER]; RETURN }; disjoint => ERROR CannotFindIt; -- may have been deleted or moved 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 NOT foundPos THEN ERROR CannotFindIt; 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+MIN[lines[sp.line].descent, selectionDescentLimit]; [sp.x, sp.w] _ TEditSelectionPrivate.CharPositionInCachedLine[viewer, 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; }; ENDCASE; }; FixupSelection: PUBLIC PROC [selection: Selection, viewer: Viewer, start, end: BOOL _ TRUE] = { IF start THEN selection.start _ ComputePosPoint[viewer, selection.start.pos]; IF end THEN SELECT TRUE FROM selection.start.pos=selection.end.pos => selection.end _ selection.start; selection.start.line=LAST[INTEGER] => { selection.end.line _ LAST[INTEGER]; selection.end.metricsValid _ TRUE; }; ENDCASE => { 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]; }; }; FixupCaret: PUBLIC PROC [selection: Selection] = { lines: TEditDocument.LineTable = selection.data.lineTable; node: TextNode.RefTextNode _ TextNode.NarrowToTextNode[selection.end.pos.node]; size: TextNode.Offset _ TextEdit.Size[node]; PutCaretOnNextLine: PROC = { SELECT selection.end.line FROM IN [0..lines.lastLine) => { nextLine: INTEGER _ selection.end.line+1; selection.caretX _ lines[nextLine].xOffset; selection.caretY _ selection.viewer.ch - lines[nextLine].yOffset - lines[nextLine].descent; }; = lines.lastLine => { selection.caretX _ lines[selection.end.line].xOffset; selection.caretY _ selection.viewer.ch - selection.end.y - selection.end.h - selection.end.h; }; ENDCASE => ERROR; }; IF selection.insertion=before THEN { IF selection.start.clipped THEN selection.caretY _ LAST[INTEGER] -- caret not visible ELSE { selection.caretX _ selection.start.x; selection.caretY _ IF selection.start.line NOT IN [0..lines.lastLine] THEN LAST[INTEGER] ELSE selection.viewer.ch - selection.start.y - selection.start.h; }; } ELSE SELECT TRUE FROM selection.end.clipped => selection.caretY _ LAST[INTEGER]; selection.end.pos.where IN [0..size) AND selection.end.line IN [0..lines.lastLine] AND TextEdit.FetchChar[node,selection.end.pos.where]=[0,15C] => PutCaretOnNextLine[]; ENDCASE => { selection.caretX _ selection.end.x+selection.end.w; selection.caretY _ ( IF selection.end.line NOT IN [0..lines.lastLine] THEN LAST[INTEGER] ELSE selection.viewer.ch - selection.end.y - selection.end.h ); }; }; KillSelection: PUBLIC PROC = { IF InputFocus.GetInputFocus[]#NIL THEN InputFocus.SetInputFocus[]; }; ModifyPSel: PROC [proc: PROC [root: TextNode.Ref, tSel: Selection]] = { inner: PROC = { root: TextNode.Ref _ SelectionRoot[pSel]; IF root#NIL THEN { tSel: Selection _ TEditSelection.Alloc[]; TEditSelection.Copy[source: pSel, dest: tSel]; proc[root, tSel ! UNWIND => Free[tSel]]; MakeSelection[new: tSel]; Free[tSel]; }; }; WithLockedSelection[primary, inner, "CallWithSelLock"]; }; SelectEverything: PUBLIC PROC = { 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] }; WithLockedSelection: PROC [which: TEditDocument.SelectionId, inner: PROC, why: ROPE] = { LockSel[which, why]; inner[ ! UNWIND => UnlockSel[which]]; UnlockSel[which]; }; SelectionRoot: PUBLIC PROC [s: Selection _ pSel] RETURNS [root: TextNode.Ref] = { IF s.viewer=NIL THEN RETURN [TextNode.Root[s.start.pos.node]]; WITH s.viewer.data SELECT FROM tdd: TEditDocument.TEditDocumentData => RETURN [tdd.text]; ENDCASE => RETURN [NIL]; }; InputModify: PUBLIC ViewerClasses.ModifyProc = { WITH self.data SELECT FROM tdd: TEditDocument.TEditDocumentData => { SELECT change FROM set, pop => Carets.StartCaret[self, pSel.caretX, pSel.caretY, primary]; kill => { inner: PROC = {Copy[source: pSel, dest: prop]; Deselect[primary]}; prop: Selection _ NARROW[ViewerOps.FetchProp[self, $SelectionHistory]]; Carets.StopCaret[primary]; IF prop=NIL THEN ViewerOps.AddProp[self, $SelectionHistory, prop _ Create[]]; WithLockedSelection[primary, inner, "InputModify"]; }; push => Carets.StopCaret[primary]; ENDCASE => ERROR; }; ENDCASE; }; END.  TEditSelectionImpl.mesa Copyright c 1985 by Xerox Corporation. All rights reserved. Plass, May 7, 1985 10:55:03 pm PDT Doug Wyatt, April 15, 1985 6:55:52 pm PST Russ Atkinson (RRA) June 10, 1985 11:52:15 pm PDT Michael Plass, August 5, 1985 1:00:48 pm PDT Selection display and Control make a point selection at pos out the the current primary selection make secondary selection be the primary make secondary selection be the primary Take down the selection without changing the input focus. take the selection down from the screen 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. N.B. Also in TEditMouseImpl one liner select from beginning of line select end portion of sl select all of sl select all of the intermediate lines when done, old is valid (it's = pSel, e.g.) and new^ = old^ update right end new end after old end This weird case can happen if old ended with final CR of node and new is in the null line displayed after the CR. new end before old end old ended in null line after final CR, new ends with the final CR update start new start comes before old start new start comes after old start 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 before beginning of text nothing special going on, so would have found it if not after end must think about this harder it really is after the end may have been restored by Undo past this line recompute the xywh fields in the selection fixes selection screen info assuming start.pos and end.pos are correct no need to re-compute scrolled off bottom recompute the xy fields in the caret 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 caret not visible selection actually get taken down in our InputModify Proc expand to include everything Misc functions Κb˜šœ™Jšœ Οmœ1™Jš œžœ žœžœžœžœ˜@J˜—J˜J˜—J˜š  œžœžœ˜Jšœ'™'š  œžœ˜)J˜2J˜—J˜&J˜—J˜š  œžœžœ˜Jšœ'™'š œžœ˜+J˜J˜ J˜—J˜(J˜—J˜š  œžœžœ˜Jšœžœ ˜J˜—J˜š œžœžœ˜ Jšœžœ ˜J˜—J˜š œžœžœ˜Jšœžœ ˜J˜—J˜š  œžœžœ˜/šœžœ˜Jšžœžœžœ˜.J˜J˜—J˜7J˜—J˜š œžœžœ5˜JJšœ9™9šœžœ˜šœžœ ž˜&Jšœ6žœžœ˜G—J˜šœžœžœ ž˜ J˜J˜J˜Jšžœžœ˜—Jšœžœ˜J˜ š žœžœžœžœžœ˜!Jšœ'™'Jšœ&žœ˜1Jšžœžœžœ˜3Jšœ‰™‰J˜—J˜—J˜2J˜—J˜š  œžœžœžœHžœžœ žœžœ˜‘Jšœ-™-šœžœ˜J˜Jšœžœ˜ šžœ ž˜˜ J˜.J˜ J˜Jš žœžœžœžœžœžœ˜>Jšžœžœžœ&˜@J˜—˜ J˜ J˜J˜—˜J˜ J˜J˜—Jšžœžœ˜—Jšžœžœžœžœ˜?šžœžœžœ˜Jšžœžœžœ˜IJšœŠ™ŠJ˜J˜$J˜ Jšžœžœžœ˜8šžœ žœ˜šœ žœžœ ž˜/Jšœ,žœ ˜@—Jšžœžœž˜šžœ"˜)Jšžœžœžœ˜0J˜—J˜—Jšžœ+žœ˜:J˜—J˜—J˜7J˜—J˜Jšœ žœžœ žœ)˜UJšœ žœžœžœ ˜Jšœ žœžœžœ ˜=š œžœžœžœ˜0J˜4šœžœ ž˜&Jšœ6žœžœ˜G—šœžœ˜J˜šœžœ˜šžœ žœž˜˜)Jšžœžœ/žœžœ˜Ašœ&žœ˜/Jšœžœ*˜2—J˜%J˜—Jšžœ˜—J˜—Jšžœžœžœ/˜Ašœ žœ˜J˜——J˜1J˜J˜—JšœJžœ˜PJšœJžœ˜PJšœNžœ˜TJšœGžœ˜MJšœ žœ6˜DJšœ žœ˜'Jšœžœ˜Jšœžœ˜šœžœ˜J˜—šœžœ˜K™J˜—š  œžœžœ^˜xšžœ žœž˜˜)J˜/Jšœ žœ˜'Jš œžœžœ žœžœ˜UJšœžœ žœžœžœ žœžœžœžœ žœ ˜’š  œžœžœ˜,Jšœ žœ˜%Jšœžœ˜Jšžœ žœI˜\šžœ ž˜J˜$J˜,J˜*J˜1Jšžœžœ˜—šžœ ž˜J˜š  œžœžœ˜#˜PJ˜"—J˜—šžœžœžœžœ˜AJšœ™J˜€Kšœ˜—šžœ˜Jšœ™J˜Kšœ˜—šžœžœžœ ž˜Jšœ$™$J˜Jšžœ˜—šžœžœž˜Jšœ‘1˜Nšœ˜J˜yKšœ˜—šžœ˜ Jšœ‘.˜?Kšœ˜——J˜—J˜—Jšžœ˜—J˜—J˜š œžœžœežœ˜ŠJšœ;™;š œžœ˜Jšœ$‘˜3Jšœ‘,˜IJšœ$‘ ˜0J˜J˜—šžœ žœ˜Jšœ™š œžœžœ˜FJ˜>J˜%Jšžœžœžœ˜GJšžœ ˜J˜—Jš œ žœžœžœžœžœ˜7Jšœ žœ)˜7Jšžœ žœ žœ˜"šžœžœž˜J˜š œ žœ&žœžœ žœJ˜‘Jšœ™J˜J˜'Jšžœ žœ'ž˜:J˜Jšœq™qJ˜J˜#J˜J˜—šžœ˜ Jšœ™J˜&J˜J˜Jšžœ žœ'ž˜:J˜JšœA™AJ˜Jšœžœžœ‘ ˜6J˜#J˜J˜Jšœžœžœ˜)J˜——J˜—šžœ˜Jšœ ™ š œžœžœ˜EJ˜Jšžœžœžœ˜7J˜?Jšžœžœ˜'J˜—Jš œ žœžœžœžœžœ˜9Jšœ žœ-˜;Jšžœ žœ žœ˜"šžœžœž˜J˜"š œ žœ*žœžœ žœO˜šJšœ ™ J˜$J˜J˜#J˜J˜—Jšžœ˜ Jšœ™J˜(J˜J˜Jšœžœžœ˜)J˜#J˜Jšœžœžœ˜)J˜—J˜—J˜J˜—J˜Jšœžœžœžœ˜"š  œžœžœ'žœžœžœ˜…J˜:šžœžœž˜J˜Bš œžœžœ žœžœ‘˜AJšžœ˜ —Jšœ žœžœ žœž˜,Jšžœžœ žœ˜(J˜?J˜—J˜—J˜š œžœžœ5žœžœžœ žœ˜‡JšœKžœ˜QJšžœ˜J˜—J˜š œžœžœ5žœžœžœžœ'˜€JšœJ™JJšœN™Nšžœ žœž˜˜)Jšœ žœžœ˜š   œžœ žœžœžœ˜NJ˜J˜0Jš žœžœžœžœžœ‘˜ZJšžœžœ˜J˜—š  œžœžœ žœžœ˜OJšœžœ˜šžœžœžœž˜0šžœžœž˜˜Jšžœ žœžœ‘˜AJšœžœ˜ ˜ J˜——Jšœ žœ˜Jšžœ˜—Jšž˜—J˜—J˜/J˜ šžœžœ˜&šžœ žœ˜(Jšœ™Jšœžœžœ˜ Jšžœ˜J˜—J˜—šžœ)žœ#žœ˜WJšœžœžœ‘˜4Jšžœ˜J˜—Jšœ žœžœ˜1Jšœ1žœ˜7Jšžœžœ ž˜šžœ8ž˜BJšœ$žœžœžœ‘˜PJšœ žœ˜Jšžœ‘'˜2—Jšžœ'žœ"˜Nšžœ˜JšœA™AJšœžœžœ˜Jšžœ˜Jšœ‘ ˜—šžœ˜Jšœ™šžœEž˜O˜Jšœ™Jšœžœžœ˜Jšžœ˜J˜—Jšœ žœ‘!˜AJšžœ‘˜J˜Jšœžœ$˜2Jšœ‘5˜DJš žœžœžœ žœžœ˜?J˜0J˜%Jšžœžœ žœžœ˜(Jšœ™Jšœ žœ˜J˜—J˜—Jš žœ žœ žœžœ‘˜DJ˜4Jšœžœ0˜PJ˜Tšžœžœžœžœ˜Jšœ™Jšžœžœžœžœ˜?Jšžœ)˜-J˜—Jšœžœ˜J˜—Jšžœ˜—J˜—J˜š  œžœžœ4žœžœ˜_Jšœ*™*JšœF™FJšžœžœ@˜MJšžœž˜ šžœžœž˜J˜(Jšœ™J˜ šœžœžœ˜'Jšœ™Jšœžœžœ˜#Jšœžœ˜"J˜—Jšžœ˜ šœ žœ˜Jšžœžœ˜!Jšžœžœžœ˜;Jšžœ˜J˜—J˜FJ˜—J˜—J˜š  œžœžœ˜2Jšœ$™$J˜:J˜OJ˜,š œžœ˜šžœž˜šžœ˜Jšœ žœ˜)J˜+J˜[J˜—˜J˜5J˜]J˜—Jšžœžœ˜—J˜—Jšžœ˜šžœ˜Jšžœ˜šžœžœžœ‘˜:Jšœ;™;JšœH™HJšœ™šžœ˜J˜%—š œžœžœžœžœžœžœ˜XJšžœ=˜A—J˜—J˜—Jšž˜šžœžœž˜J˜Jšœ™Jšœžœžœ˜!Jš œžœ žœžœžœ<˜’J˜Jšžœ˜ J˜3˜Jš žœžœžœžœžœžœ˜CJšžœ8˜šžœžœž˜Jšœ(žœ ˜:Jšžœžœžœ˜—J˜—J˜š  œžœ˜0šžœ žœž˜˜)šžœž˜J˜G˜ Jšœžœ7˜BJšœžœ/˜GJ˜Jšžœžœžœ=˜MJ˜3J˜—J˜"Jšžœžœ˜—J˜—Jšžœ˜—J˜—J˜—Jšžœ˜—…—O„n†