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ó��