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 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<old.start.pos.where) OR (~sameNode AND EditSpan.CompareNodeOrder[new.start.pos.node, old.start.pos.node]=before) THEN BEGIN -- new start comes before old start new.end.pos _ DecLoc[old.start.pos]; FixupSelection[new, viewer]; MarkSelection[dc, viewer, new, id]; old.start _ new.start; END ELSE BEGIN -- new start comes after old start 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]; END; END; Copy[source: old, dest: new]; END; CannotFindIt: PUBLIC ERROR = CODE; ComputeSpanLines: PUBLIC PROC [viewer: ViewerClasses.Viewer, span: TextNode.Span] RETURNS [start, end: INTEGER, startClipped, endClipped: BOOLEAN] = BEGIN [start,startClipped] _ ComputePosLine[viewer, span.start]; IF span.start=span.end THEN { end _ start; endClipped _ startClipped } ELSE IF start=LAST[INTEGER] THEN end _ LAST[INTEGER] -- scrolled off bottom ELSE { firstLine: INTEGER = IF start<=0 THEN 0 ELSE IF startClipped THEN start-1 ELSE start; [end,endClipped] _ ComputePosLine[viewer, span.end, firstLine] }; END; ComputePosLine: PUBLIC PROC [ viewer: ViewerClasses.Viewer, pos: TextNode.Location, firstLine: INTEGER _ 0] RETURNS [line: INTEGER, clipped: BOOLEAN] = BEGIN sp: SelectionPoint _ ComputePosPoint[viewer, pos, firstLine, TRUE]; RETURN [sp.line, sp.clipped]; END; ComputePosPoint: PUBLIC PROC [ viewer: ViewerClasses.Viewer, pos: TextNode.Location, firstLine: INTEGER _ 0, lineOnly: BOOLEAN _ FALSE] RETURNS [sp: SelectionPoint] = BEGIN foundPos: BOOLEAN _ FALSE; tdd: TEditDocumentData = NARROW[viewer.data]; lines: TEditDocument.LineTable; lineInfo: TEditFormat.LineInfo; nChars: INTEGER; IsPosPastLine: PROC [pos: TextNode.Location, line: INTEGER] RETURNS [BOOLEAN] = { 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: 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]; FOR n: INTEGER IN [1..pos.where-lines[sp.line].pos.where] 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. ���N��-- TEditSelectionImpl.mesa Edited by Paxton on December 28, 1982 2:23 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šÏcH™HJšÏ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šœ9™9J˜šœžœžœ˜(šœžœž˜&Jšœ6žœžœ˜G—Jšœ*˜*šœžœžœž˜ Jšœ˜Jšœ˜Jšœ˜Jšžœžœ˜—Jšœžœ˜Jšœ ˜ š žœžœžœžœ*˜IJšœ&žœ˜1šžœžœ˜0J™‰—Jšœ˜——J˜—J˜�šŸ œžœž˜šœžœ#˜8Jš œžœžœ žœžœ˜B—J™-Jšœ˜Jšœžœ˜ J˜$šœžœžœ˜(šžœž˜šœž˜J˜<Jšœ˜Jšžœžœžœžœžœžœžœ˜>Jšžœžœžœ&˜@Jšž˜—Jšœ+˜+Jšœ,˜,Jšžœžœ˜—Jšžœžœžœžœ˜?šžœžœžœž˜šžœžœžœ˜IJ™Š—Jšœ˜J˜$J˜ šžœžœ˜šœ žœžœž˜/Jšœ,žœ ˜@—šžœžœž˜šžœ"˜)Jšžœžœžœ˜3———Jšžœ+žœ ˜=——Jšœ˜—J˜�Jšœžœžœžœ˜GJšœ žœžœžœ˜<Jšœ žœžœžœ˜>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šžœ žœžœ˜"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šœžœžœ˜4Jšžœ˜Jšžœ˜J˜�—Jšœžœžœ˜1J˜�Jšœ1žœ˜7J˜�šžœž˜šžœ8ž˜BJšœžœ˜Jšœžœ˜Jšœžœžœžœ˜Jšžœ'˜2šžœžœ#žœ˜GJšD™DJšœžœžœžœ˜5—šžœ˜&šžœEž˜OJšœžœ˜Jšœžœ!˜Ašœ˜(Jšœžœžœžœ˜(—šžœ˜J˜šœžœ$˜2Jšœ5˜DJš žœžœžœžœžœ˜?J˜0—Jšœ%˜%Jšžœžœžœ!˜GJšœ žœ˜J˜——J˜J˜�————Jš žœ žœžœžœ˜DJ˜�J˜4J˜4J˜=šžœžœžœžœžœ ˜6J˜J˜ Jšž˜—š žœžœ+žœžœ˜NJšžœžœžœžœ˜=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šžœžœžœ˜BJšžœ˜J˜�—šŸ œžœžœ+˜GJšœ˜šžœžœžœžœžœžœ˜IJšœ˜Jšœ$˜$Jš žœžœžœžœžœ˜HJšœ˜J˜.Jšœ˜J˜—Jšœžœ˜-J˜�—šŸœžœžœ˜AšŸœžœ*˜?J˜*J˜0J˜.J˜SJ˜—šœ˜J˜�——šŸœžœžœ˜'šŸœžœ*˜6Jšœžœ˜—Jšœ˜—J˜�šŸœžœžœ˜*šŸ œžœ*˜9Jšœžœ˜—Jšœ˜J˜�—šŸœžœžœ˜%šŸœžœ*˜FJ˜—Jšœ%˜%J˜�—šŸœžœžœ˜$šŸœžœ*˜EJšžœžœ˜:—Jšœ$˜$J˜�—Jš˜J˜�šŸ œžœžœžœ˜QJ˜%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˜—�…—����PÀ��mó��