DIRECTORY EditSpan USING [CompareNodeOrder, InsertTextNode, NodeOrder, Place], NodeStyle USING [Ref], NodeStyleOps USING [Alloc, ApplyAll, Free, OfStyle], Rope USING [Size], RopeEdit USING [AlphaNumericChar, BlankChar], RopeReader USING [Backwards, Get, GetIndex, GetRopeReader, FreeRopeReader, Ref, SetCharForEndOfRope, SetPosition], Scaled USING [FromInt], TextEdit USING [FetchChar, FetchLooks, GetRope, Offset, RefTextNode, Size], TextLooks USING [Looks, noLooks], TextNode USING [EndPos, FirstChild, LastWithin, Location, Offset, Parent, Ref, RefTextNode, Root], TEditDocument USING [BeforeAfter, LineTable, PunctuationPosition, SelectionGrain, SelectionPoint, Selection, SelectionId, SelectionRec, TEditDocumentData], TEditFormat USING [Allocate, CharPosition, FormatLine, LineInfo, Resolve], TEditInput USING [currentEvent], TEditProfile USING [selectionCaret, wordPunctuation, ySelectFudge], TEditSelection USING [Alloc, Free, Copy, Deselect, FixupSelection, fSel, LevelChange, LockSel, pSel, sSel, MakeSelection, SelectEverything, UnlockSel], TEditSelectionPrivate USING [], TEditTouchup USING [LockAfterRefresh, UnlockAfterRefresh], ViewerClasses USING [Viewer], ViewerGroupLocks USING [CallRootUnderWriteLock], ViewerOps USING [FetchProp]; TEditMouseImpl: CEDAR MONITOR IMPORTS EditSpan, NodeStyleOps, Rope, RopeEdit, RopeReader, Scaled, TEditInput, TEditProfile, TEditFormat, TEditSelection, TEditTouchup, TextEdit, TextNode, ViewerGroupLocks, ViewerOps EXPORTS TEditSelection, TEditSelectionPrivate = BEGIN DoSelect: PROC [ proc: PROC [tSel, refSel: TEditDocument.Selection, rightOfLine: BOOL], viewer: ViewerClasses.Viewer, tdd: TEditDocument.TEditDocumentData, x, y: INTEGER, sel: TEditDocument.SelectionId] = { action: PROC = { refSel: TEditDocument.Selection = SELECT sel FROM primary => TEditSelection.pSel, secondary => TEditSelection.sSel, feedback => TEditSelection.fSel, ENDCASE => ERROR; tSel: TEditDocument.Selection; rightOfLine: BOOL _ FALSE; tSel _ TEditSelection.Alloc[]; rightOfLine _ ResolveToChar[tSel, viewer, tdd, x, y]; proc[tSel, refSel, rightOfLine]; TEditSelection.Free[tSel] }; WithProperLocks[action, sel, viewer, tdd]; }; WithProperLocks: PROC [action: PROC, sel: TEditDocument.SelectionId, viewer: ViewerClasses.Viewer, tdd: TEditDocument.TEditDocumentData] = { inner: PROC = { WITH viewer.data SELECT FROM vtdd: TEditDocument.TEditDocumentData => IF vtdd = tdd THEN { IF TEditTouchup.LockAfterRefresh[tdd, "DoSelect"] THEN { action[ ! UNWIND => TEditTouchup.UnlockAfterRefresh[tdd]]; TEditTouchup.UnlockAfterRefresh[tdd]; }; }; ENDCASE; }; TEditSelection.LockSel[sel, "DoSelect"]; ViewerGroupLocks.CallRootUnderWriteLock[inner, viewer ! UNWIND => TEditSelection.UnlockSel[sel]]; TEditSelection.UnlockSel[sel] ; }; SelectPoint: PUBLIC PROC [ viewer: ViewerClasses.Viewer, tdd: TEditDocument.TEditDocumentData, x,y: INTEGER, sel: TEditDocument.SelectionId, pDel: BOOLEAN] = { DoSelectPoint: PROC [tSel, refSel: TEditDocument.Selection, rightOfLine: BOOL] = { newInsertion: TEditDocument.BeforeAfter; IF refSel.viewer#viewer OR refSel.granularity#point THEN TEditSelection.Deselect[selection: sel]; tSel.end _ tSel.start; newInsertion _ SetInsertion[tSel, x, tSel.start.line, rightOfLine, tdd]; IF refSel.viewer#viewer OR refSel.start#tSel.start OR refSel.granularity#point OR refSel.insertion#newInsertion OR refSel.pendingDelete#pDel THEN { tSel.granularity _ point; tSel.viewer _ viewer; tSel.data _ tdd; tSel.insertion _ newInsertion; tSel.pendingDelete _ pDel; SetSelLooks[tSel]; TEditSelection.MakeSelection[tSel, sel, TRUE, TRUE, FALSE]; }; }; DoSelect[DoSelectPoint, viewer, tdd, x, y, sel]; }; SelectChar: PUBLIC PROC [ viewer: ViewerClasses.Viewer, tdd: TEditDocument.TEditDocumentData, x, y: INTEGER, sel: TEditDocument.SelectionId, pDel: BOOLEAN] = { DoSelectChar: PROC [tSel, refSel: TEditDocument.Selection, rightOfLine: BOOL] = { newInsertion: TEditDocument.BeforeAfter; newGrain: TEditDocument.SelectionGrain; startValid, endValid: BOOLEAN _ TRUE; tSel.end _ tSel.start; IF rightOfLine AND tdd.tsInfo#NIL AND tSel.end.line=tdd.lineTable.lastLine AND tdd.lineTable.lines[tSel.end.line].end=eon THEN { -- make point selection at end tSel.end.pos.where _ tSel.start.pos.where _ TextEdit.Size[tSel.start.pos.node]; newGrain _ point; newInsertion _ before; startValid _ endValid _ FALSE; } ELSE { newInsertion _ SetInsertion[tSel, x, tSel.start.line, rightOfLine, tdd]; newGrain _ char; }; IF refSel.viewer#viewer OR refSel.start#tSel.start OR refSel.end#tSel.end OR refSel.granularity#newGrain OR refSel.pendingDelete#pDel OR refSel.insertion#newInsertion THEN { tSel.granularity _ newGrain; tSel.viewer _ viewer; tSel.data _ tdd; tSel.insertion _ newInsertion; tSel.pendingDelete _ pDel; SetSelLooks[tSel]; TEditSelection.MakeSelection[tSel, sel, startValid, endValid, FALSE]; }; }; DoSelect[DoSelectChar, viewer, tdd, x, y, sel]; }; SelectWord: PUBLIC PROC [ viewer: ViewerClasses.Viewer, tdd: TEditDocument.TEditDocumentData, x, y: INTEGER, sel: TEditDocument.SelectionId, pDel: BOOLEAN] = { DoSelectWord: PROC [tSel, refSel: TEditDocument.Selection, rightOfLine: BOOL] = { start, end: TextEdit.Offset; punc: TEditDocument.PunctuationPosition; newInsertion: TEditDocument.BeforeAfter; newGrain: TEditDocument.SelectionGrain; startValid, endValid: BOOLEAN _ TRUE; hitLine: INTEGER _ tSel.start.line; [start, end, punc] _ ExpandToWord[tSel.start.pos]; tSel.viewer _ viewer; tSel.data _ tdd; tSel.start.pos.where _ start; tSel.end.pos _ [tSel.start.pos.node, end]; TEditSelection.FixupSelection[tSel, viewer]; IF rightOfLine AND tdd.tsInfo#NIL AND tSel.end.line=tdd.lineTable.lastLine AND tdd.lineTable.lines[tSel.end.line].end = eon THEN { tSel.end.pos.where _ tSel.start.pos.where _ TextEdit.Size[tSel.start.pos.node]; newGrain _ point; newInsertion _ before; startValid _ endValid _ FALSE; } ELSE { newInsertion _ SetInsertion[tSel, x, hitLine, rightOfLine, tdd]; newGrain _ word; }; IF refSel.viewer#tSel.viewer OR refSel.granularity#newGrain OR tSel.start.pos#refSel.start.pos OR tSel.end.pos#refSel.end.pos OR newInsertion#refSel.insertion OR refSel.pendingDelete#pDel THEN { tSel.granularity _ newGrain; tSel.punctuation _ punc; tSel.insertion _ newInsertion; tSel.pendingDelete _ pDel; SetSelLooks[tSel]; TEditSelection.MakeSelection[tSel, sel, startValid, endValid, FALSE]; }; }; DoSelect[DoSelectWord, viewer, tdd, x, y, sel]; }; SelectNode: PUBLIC PROC [ viewer: ViewerClasses.Viewer, tdd: TEditDocument.TEditDocumentData, x,y: INTEGER, sel: TEditDocument.SelectionId, pDel: BOOLEAN] = { DoSelectNode: PROC [tSel, refSel: TEditDocument.Selection, rightOfLine: BOOL] = { hitLine: INTEGER _ tSel.start.line; newInsertion: TEditDocument.BeforeAfter; tSel.viewer _ viewer; tSel.data _ tdd; tSel.start.pos _ [tSel.start.pos.node, 0]; tSel.end.pos _ [tSel.start.pos.node, MAX[TextEdit.Size[tSel.start.pos.node],1]-1]; TEditSelection.FixupSelection[tSel, viewer]; newInsertion _ SetInsertion[tSel, x, hitLine, FALSE, tdd]; IF refSel.viewer#viewer OR refSel.start.pos.node#tSel.start.pos.node OR refSel.granularity#node OR refSel.pendingDelete#pDel OR refSel.insertion#newInsertion THEN { tSel.granularity _ node; tSel.pendingDelete _ pDel; tSel.insertion _ newInsertion; SetSelLooks[tSel]; TEditSelection.MakeSelection[new: tSel, selection: sel, forkPaint: FALSE]; }; }; DoSelect[DoSelectNode, viewer, tdd, x, y, sel]; }; SelectBranch: PUBLIC PROC [ viewer: ViewerClasses.Viewer, tdd: TEditDocument.TEditDocumentData, x,y: INTEGER, sel: TEditDocument.SelectionId, pDel: BOOLEAN] = { DoSelectBranch: PROC [tSel, refSel: TEditDocument.Selection, rightOfLine: BOOL] = { hitLine: INTEGER _ tSel.start.line; newInsertion: TEditDocument.BeforeAfter; tSel.viewer _ viewer; tSel.data _ tdd; tSel.start.pos _ [tSel.start.pos.node, 0]; tSel.end.pos.node _ TextNode.LastWithin[tSel.start.pos.node]; tSel.end.pos.where _ TextNode.EndPos[tSel.end.pos.node]; TEditSelection.FixupSelection[tSel, viewer]; newInsertion _ SetInsertion[tSel, x, hitLine, FALSE, tdd]; IF refSel.viewer#viewer OR refSel.start.pos.node#tSel.start.pos.node OR refSel.granularity#branch OR refSel.pendingDelete#pDel OR refSel.insertion#newInsertion THEN { tSel.granularity _ branch; tSel.pendingDelete _ pDel; tSel.insertion _ newInsertion; SetSelLooks[tSel]; TEditSelection.MakeSelection[new: tSel, selection: sel, forkPaint: FALSE]; }; }; DoSelect[DoSelectBranch, viewer, tdd, x, y, sel]; }; ComputeBeforeAfter: PROC [sel: TEditDocument.Selection, new: TextNode.Location, x, y: INTEGER] RETURNS [TEditDocument.BeforeAfter] = { OPEN sel; sOS: BOOLEAN = (start.line IN [0..LAST[INTEGER])); eOS: BOOLEAN = (end.line IN [0..LAST[INTEGER])); easy: BOOLEAN = (start.pos.node=end.pos.node) AND (start.pos.node=new.node); RETURN[SELECT TRUE FROM NOT (sOS OR eOS) => SELECT TRUE FROM end.line<0 => after, start.line>0 => before, ENDCASE => IF y>viewer.ch/2 THEN after ELSE before, NOT sOS => after, NOT eOS => before, easy AND new.where<=start.pos.where => before, easy AND new.where>=end.pos.where => after, easy => IF Dist[sel, before, x, y] < Dist[sel, after, x, y] THEN before ELSE after, EditSpan.CompareNodeOrder[new.node, start.pos.node]#after => before, EditSpan.CompareNodeOrder[new.node, end.pos.node]#before => after, ENDCASE => IF Dist[sel, before, x, y] < Dist[sel, after, x, y] THEN before ELSE after ]; }; Dist: PROC [sel: TEditDocument.Selection, dir: TEditDocument.BeforeAfter, x, y: INTEGER] RETURNS [LONG INTEGER] = { SQR: PROC [n: LONG INTEGER] RETURNS [LONG INTEGER] = INLINE {RETURN[n*n]}; RETURN[IF dir=before THEN SQR[x-sel.start.x]+SQR[y-sel.start.y] ELSE SQR[x-sel.end.x]+SQR[y-sel.end.y]]; }; CompareLoc: PROC [loc1, loc2: TextNode.Location] RETURNS [order: EditSpan.NodeOrder] = { IF loc1.node=loc2.node THEN RETURN [SELECT loc1.where FROM < loc2.where => before, = loc2.where => same, ENDCASE => after]; RETURN [EditSpan.CompareNodeOrder[loc1.node,loc2.node]] }; initStart, initEnd: TextNode.Location; initTDD: TEditDocument.TEditDocumentData; Extend: PUBLIC PROC [ viewer: ViewerClasses.Viewer, tdd: TEditDocument.TEditDocumentData, x, y: INTEGER, sel: TEditDocument.SelectionId, pDel: BOOLEAN, changeLevel: TEditSelection.LevelChange, saveEnds: BOOLEAN] = { DoExtend: PROC [tSel, refSel: TEditDocument.Selection, rightOfLine: BOOL] = { end: TEditDocument.BeforeAfter; ok: BOOLEAN; sp: TEditDocument.SelectionPoint; -- the place we're extending to IF refSel.viewer=NIL THEN RETURN; -- no selection to extend ok _ refSel.pendingDelete=pDel; end _ refSel.insertion; IF refSel.viewer#viewer THEN { refTDD: TEditDocument.TEditDocumentData = NARROW[refSel.viewer.data]; IF refTDD = NIL OR refTDD.text#tdd.text THEN RETURN; ok _ FALSE -- extending into a different viewer, so must recalculate both ends }; IF refSel.granularity=point THEN ok _ FALSE; IF saveEnds OR tdd#initTDD THEN { initTDD _ tdd; initStart _ refSel.start.pos; initEnd _ refSel.end.pos }; sp _ tSel.start; IF end=after THEN { IF CompareLoc[tSel.start.pos,refSel.start.pos]=before THEN { end _ before; SELECT CompareLoc[refSel.start.pos,initEnd] FROM same, before => refSel.end.pos _ initEnd; ENDCASE => initEnd _ refSel.end.pos; -- didn't get saved ok _ FALSE } } ELSE { IF CompareLoc[tSel.start.pos,refSel.end.pos]=after THEN { end _ after; SELECT CompareLoc[initStart,refSel.end.pos] FROM same, before => refSel.start.pos _ initStart; ENDCASE => initStart _ refSel.start.pos; -- didn't get saved ok _ FALSE } }; IF ok AND changeLevel=same AND ((end=before AND sp.pos=refSel.start.pos) OR (end=after AND sp.pos=refSel.end.pos)) THEN RETURN; -- no change TEditSelection.Copy[source: refSel, dest: tSel]; tSel.viewer _ viewer; -- in case we're extending into a different one IF tSel.granularity=point AND end=before AND tSel.end.pos.where > 0 AND (sp.pos.node # tSel.end.pos.node OR sp.pos.where < tSel.end.pos.where) THEN { tSel.end.pos.where _ tSel.end.pos.where-1; -- so don't include char after caret ok _ FALSE }; SELECT changeLevel FROM same => NULL; reduce => { ok _ FALSE; tSel.granularity _ SELECT tSel.granularity FROM branch => node, node => word, word => char, ENDCASE => char }; expand => { ok _ FALSE; tSel.granularity _ SELECT tSel.granularity FROM point => char, char => word, word => node, ENDCASE => branch }; ENDCASE => ERROR; SELECT tSel.granularity FROM branch, node => { IF end=after THEN { IF tSel.granularity=branch THEN sp.pos.node _ TextNode.LastWithin[sp.pos.node]; IF ok AND sp.pos.node=tSel.end.pos.node THEN RETURN; tSel.end.pos _ [sp.pos.node, MAX[TextEdit.Size[sp.pos.node],1]-1]; } ELSE { IF ok AND sp.pos.node=tSel.start.pos.node THEN RETURN; tSel.start.pos _ [sp.pos.node, 0]; }; }; word => { prev, start, endPos: TextEdit.Offset; node: TextNode.Ref = sp.pos.node; prevNode: TextNode.Ref; punc: TEditDocument.PunctuationPosition; [start, endPos, punc] _ ExpandToWord[sp.pos, end=before]; IF end=after THEN { prevNode _ tSel.end.pos.node; tSel.end.pos.node _ node; IF tSel.punctuation # leading THEN tSel.punctuation _ punc ELSE IF punc = trailing THEN endPos _ endPos-1; prev _ tSel.end.pos.where; IF (tSel.end.pos.where_endPos)=prev AND ok AND node=prevNode THEN RETURN; } ELSE { prevNode _ tSel.start.pos.node; tSel.start.pos.node _ node; IF tSel.punctuation # trailing THEN tSel.punctuation _ punc ELSE IF punc = leading THEN start _ start+1; prev _ tSel.start.pos.where; IF (tSel.start.pos.where_start)=prev AND ok AND node=prevNode THEN RETURN; }; }; char, point => { IF end=after THEN { IF tSel.end=sp AND ok AND tSel.granularity=char THEN RETURN; tSel.end _ sp } ELSE { IF tSel.start=sp AND ok AND tSel.granularity=char THEN RETURN; tSel.start _ sp }; tSel.granularity _ char; }; ENDCASE => ERROR; -- no other selection flavors tSel.insertion _ end; tSel.pendingDelete _ pDel; SetSelLooks[tSel]; TEditSelection.MakeSelection[tSel, sel, end=after AND ok, end=before AND ok, FALSE] }; DoSelect[DoExtend, viewer, tdd, x, y, sel]; }; SetSelLooks: PUBLIC PROC [sel: TEditDocument.Selection] = { loc: TextNode.Location ~ IF sel.insertion = before THEN sel.start.pos ELSE sel.end.pos; size: INT _ TextEdit.Size[loc.node]; sel.looks _ ( IF size <= 0 THEN TextLooks.noLooks ELSE IF loc.where >= size THEN TextEdit.FetchLooks[loc.node, size-1] ELSE TextEdit.FetchLooks[loc.node, loc.where] ) }; Update: PUBLIC PROC [ viewer: ViewerClasses.Viewer, tdd: TEditDocument.TEditDocumentData, x,y: INTEGER, sel: TEditDocument.SelectionId, pDel: BOOLEAN] = { refSel: TEditDocument.Selection = IF sel=primary THEN TEditSelection.pSel ELSE IF sel=secondary THEN TEditSelection.sSel ELSE ERROR; SELECT refSel.granularity FROM word => SelectWord[viewer, tdd, x, y, sel, pDel]; node => SelectNode[viewer, tdd, x, y, sel, pDel]; branch => SelectBranch[viewer, tdd, x, y, sel, pDel]; ENDCASE => SelectChar[viewer, tdd, x, y, sel, pDel]; }; ExpandToWord: PROC [pos: TextNode.Location, frontOnly: BOOLEAN _ FALSE] RETURNS [start, end: TextEdit.Offset, punc: TEditDocument.PunctuationPosition _ none] = { refChar, char: CHARACTER; alpha: BOOLEAN; node: TextNode.RefTextNode = pos.node; lastOffset: TextEdit.Offset _ TextEdit.Size[node]-1; ropeReader: RopeReader.Ref _ RopeReader.GetRopeReader[]; start _ end _ pos.where; RopeReader.SetPosition[ropeReader, TextEdit.GetRope[node], end]; RopeReader.SetCharForEndOfRope[ropeReader, 15C]; -- so we get a return at the end refChar _ RopeReader.Get[ropeReader]; IF refChar=15C THEN { RopeReader.FreeRopeReader[ropeReader]; RETURN }; -- CR is a word alpha _ RopeEdit.AlphaNumericChar[refChar]; char _ RopeReader.Get[ropeReader]; WHILE ((alpha AND RopeEdit.AlphaNumericChar[char]) OR char=refChar) AND end0 DO char _ RopeReader.Backwards[ropeReader]; start _ start-1; ENDLOOP; IF TEditProfile.wordPunctuation AND punc=none AND alpha AND char=40C THEN { punc _ leading; start _ start-1; }; RopeReader.FreeRopeReader[ropeReader]; }; SetInsertion: PROC [sel: TEditDocument.Selection, x, line: INTEGER, rightOfLine: BOOLEAN, tdd: TEditDocument.TEditDocumentData] RETURNS [TEditDocument.BeforeAfter] = { node: TextNode.RefTextNode _ sel.start.pos.node; size: TextNode.Offset _ TextEdit.Size[node]; IF sel.start.line=sel.end.line AND sel.start.pos.where>=size THEN RETURN [before]; SELECT TEditProfile.selectionCaret FROM before => RETURN[ IF sel.start.pos=sel.end.pos AND sel.granularity=char AND rightOfLine AND tdd.lineTable.lines[line].end=eon THEN after ELSE before]; after => RETURN[ IF sel.start.pos.where=0 AND sel.start.pos=sel.end.pos AND sel.granularity=char AND x-sel.start.x <= sel.end.x+sel.end.w-x THEN before ELSE after]; ENDCASE; IF sel.start.line=line THEN { IF sel.end.line#line THEN RETURN [before]; IF sel.start.pos.where>=size THEN RETURN [before]; IF rightOfLine THEN { BlankAt: PROC [offset: INT] RETURNS [BOOL] ~ { set: NAT; char: CHAR; [set, char] _ TextEdit.FetchChar[node, offset]; RETURN [set=0 AND RopeEdit.BlankChar[char]]; }; RETURN [IF sel.start.pos=sel.end.pos -- single char selection AND sel.start.pos.where+1 < size -- not last char in node AND BlankAt[sel.start.pos.where] THEN before ELSE after]; }; RETURN[IF x-sel.start.x <= sel.end.x+sel.end.w-x THEN before ELSE after]; } ELSE IF sel.end.line=line THEN RETURN[after]; RETURN[IF line-sel.start.line <= sel.end.line-line THEN before ELSE after]; }; selectionDescentLimit: NAT _ 7; ResolveToChar: PUBLIC ENTRY PROC [selection: TEditDocument.Selection, viewer: ViewerClasses.Viewer, tdd: TEditDocument.TEditDocumentData, x, y: INTEGER] RETURNS [rightOfLine: BOOLEAN] = TRUSTED { lines: TEditDocument.LineTable; line: INTEGER _ 0; lastLine: INTEGER; lines _ tdd.lineTable; line _ lastLine _ lines.lastLine; y _ y-TEditProfile.ySelectFudge; -- for people who like to point below the target IF y > (lines[lastLine].yOffset+lines[lastLine].descent) THEN { -- off end of text x _ LAST[INTEGER]; } ELSE FOR n: INTEGER IN (0..lastLine] DO -- horrible linear search! IF lines[n].yOffset >= y THEN { bottomOfUpper: INTEGER _ lines[n-1].yOffset+lines[n-1].descent; topOfLower: INTEGER _ lines[n].yOffset-lines[n].ascent; break: INTEGER _ MIN[MAX[(bottomOfUpper+topOfLower)/2, lines[n-1].yOffset], lines[n].yOffset-1]; line _ IF y < break THEN n-1 ELSE n; EXIT; }; ENDLOOP; selection.start.y _ lines[line].yOffset-lines[line].ascent; selection.start.h _ lines[line].ascent+MIN[lines[line].descent, selectionDescentLimit]; selection.start.line _ line; GetLine[viewer, line]; [selection.start.pos, selection.start.x, selection.start.w, rightOfLine] _ TEditFormat.Resolve[lineInfo, x]; }; GrowSelectionToBlanks: PUBLIC PROC = { Blank: PROC [char: CHAR] RETURNS [BOOLEAN] = { RETURN [RopeEdit.BlankChar[char]] }; GrowSelectionToSomething[Blank, Blank] }; GrowSelectionToSomething: PUBLIC PROC [left, right: PROC [CHAR] RETURNS [BOOLEAN]] = { tSel: TEditDocument.Selection; start, end: TextNode.RefTextNode; startPos, endPos, endLen: TextNode.Offset; ropeReader: RopeReader.Ref; IF TEditSelection.pSel=NIL OR TEditSelection.pSel.viewer=NIL THEN RETURN; tSel _ TEditSelection.Alloc[]; TEditSelection.Copy[source: TEditSelection.pSel, dest: tSel]; start _ tSel.start.pos.node; ropeReader _ RopeReader.GetRopeReader[]; RopeReader.SetPosition[ropeReader, TextEdit.GetRope[start], tSel.start.pos.where]; DO -- find first blank to left of start of selection loc: TextNode.Offset = RopeReader.GetIndex[ropeReader]; IF loc = 0 THEN { startPos _ 0; EXIT }; IF left[RopeReader.Backwards[ropeReader]] THEN { startPos _ loc; EXIT }; ENDLOOP; tSel.start.pos.where _ startPos; end _ tSel.end.pos.node; endPos _ IF tSel.granularity=point THEN tSel.end.pos.where ELSE tSel.end.pos.where+1; endLen _ Rope.Size[TextEdit.GetRope[end]]; RopeReader.SetPosition[ropeReader, TextEdit.GetRope[end], endPos]; DO -- find first blank to right of end of selection loc: TextNode.Offset = RopeReader.GetIndex[ropeReader]; IF loc = endLen THEN { endPos _ endLen; EXIT }; IF right[RopeReader.Get[ropeReader]] THEN { endPos _ loc; EXIT }; ENDLOOP; RopeReader.FreeRopeReader[ropeReader]; tSel.end.pos.where _ endPos-1; TEditSelection.MakeSelection[new: tSel]; TEditSelection.Free[tSel] }; GrowSelection: PUBLIC PROC = { tSel: TEditDocument.Selection; IF TEditSelection.pSel=NIL OR TEditSelection.pSel.viewer=NIL THEN RETURN; tSel _ TEditSelection.Alloc[]; TEditSelection.Copy[source: TEditSelection.pSel, dest: tSel]; SELECT tSel.granularity FROM point => { tSel.granularity _ char; }; char => { start, end: TextEdit.Offset; tSel.granularity _ word; [start, end, ----] _ ExpandToWord[tSel.start.pos]; tSel.start.pos.where _ start; tSel.end.pos _ [tSel.start.pos.node, end]; }; word => { tSel.start.pos.where _ 0; tSel.end.pos.where _ TextNode.EndPos[tSel.end.pos.node]; tSel.granularity _ IF tSel.start.pos.node=tSel.end.pos.node AND TextNode.FirstChild[tSel.end.pos.node]=NIL THEN branch ELSE node; }; node => { tSel.granularity _ branch; tSel.end.pos.node _ TextNode.LastWithin[tSel.end.pos.node]; tSel.end.pos.where _ TextNode.EndPos[tSel.end.pos.node]; }; branch => { parent: TextNode.Ref _ TextNode.Parent[tSel.start.pos.node]; IF TextNode.Parent[parent]=NIL THEN { -- at the root TEditSelection.SelectEverything; RETURN }; tSel.start.pos _ [parent, 0]; tSel.end.pos.node _ TextNode.LastWithin[parent]; tSel.end.pos.where _ TextNode.EndPos[tSel.end.pos.node]; }; ENDCASE => ERROR; TEditSelection.MakeSelection[new: tSel]; TEditSelection.Free[tSel] }; InsertionPoint: PUBLIC PROC [s: TEditDocument.Selection _ TEditSelection.pSel] RETURNS [ip: TextNode.Location] = { nodeSel: BOOLEAN ~ s.granularity = node OR s.granularity = branch; viewer: ViewerClasses.Viewer ~ s.viewer; IF viewer = NIL OR s.data # viewer.data THEN RETURN [[NIL, 0]]; -- in case of vanishing viewers. IF nodeSel AND s.start.pos.node=NIL THEN { place: EditSpan.Place ~ IF s.insertion=before THEN before ELSE after; node: TextNode.Ref ~ IF place = before THEN s.start.pos.node ELSE s.end.pos.node; ip _ [EditSpan.InsertTextNode[root: TextNode.Root[node], old: node, where: place, inherit: TRUE, event: TEditInput.currentEvent], 0]; } ELSE { IF s.insertion=before THEN {ip _ s.start.pos} ELSE { size: INT ~ TextEdit.Size[s.end.pos.node]; ip _ [s.end.pos.node, MIN[MAX[s.end.pos.where+1, 0], size]]; }; }; }; GetSelectionGrain: PUBLIC PROC [sel: TEditDocument.Selection] RETURNS [TEditDocument.SelectionGrain] = { IF sel.granularity = node OR sel.granularity = branch THEN RETURN [node]; IF sel.granularity = point AND sel.start.pos = sel.end.pos THEN RETURN [point]; RETURN [IF sel.granularity=word THEN word ELSE char]; }; lineInfoViewer: ViewerClasses.Viewer _ NIL; lineInfoLine: INTEGER; lineInfo: TEditFormat.LineInfo _ TEditFormat.Allocate[]; InvalidateLineCache: PUBLIC ENTRY PROC = { lineInfoViewer _ NIL }; GetLine: INTERNAL PROC [viewer: ViewerClasses.Viewer, line: INTEGER] = { WITH viewer.data SELECT FROM tdd: TEditDocument.TEditDocumentData => { lines: TEditDocument.LineTable = tdd.lineTable; IF viewer#lineInfoViewer OR line#lineInfoLine THEN { style: NodeStyle.Ref _ NodeStyleOps.Alloc[]; pos: TextNode.Location = lines[line].pos; kind: NodeStyleOps.OfStyle ~ IF ViewerOps.FetchProp[viewer, $StyleKind] = $Print THEN print ELSE screen; NodeStyleOps.ApplyAll[style, lines[line].pos.node, kind]; TEditFormat.FormatLine[ lineInfo: lineInfo, node: pos.node, startOffset: pos.where, nodeStyle: style, lineWidth: Scaled.FromInt[viewer.cw], kind: kind ]; lineInfoViewer _ viewer; lineInfoLine _ line; NodeStyleOps.Free[style]; }; }; ENDCASE => NULL; }; CharPositionInCachedLine: PUBLIC ENTRY PROC [viewer: ViewerClasses.Viewer, line: INTEGER, pos: TextNode.Location] RETURNS [x, width: INTEGER] = { GetLine[viewer, line]; [x, width] _ TEditFormat.CharPosition[lineInfo, pos.where]; }; END.  TEditMouseImpl.mesa Copyright c 1985 by Xerox Corporation. All rights reserved. Edited by Paxton on May 31, 1983 9:18 am Michael Plass, August 5, 1985 1:13:32 pm PDT Russ Atkinson (RRA) June 17, 1985 2:52:45 pm PDT Doug Wyatt, June 20, 1985 4:56:06 pm PDT ---- Mouse hit primitives ------ make point selection at end of typescript can't extend into another document changing the end of the selection switch ends changing the start of the selection switch ends point => SelectPoint[viewer, tdd, x, y, changeSel, sel, changePDel, pDel]; This ensures caret before in empty nodes. caret goes before unless making single character selection to right of last character in node caret goes after unless making single character selection to left of middle of first character in node ---- Screen to viewbox transforms ------ N.B. Also in TEditSelectionImpl info is returned in selection.start -------------- -------------- Must create an insertion point ---- Misc functions ------ a one line cache for the current line being resolved Κ°˜codešœ™Kšœ Οmœ1™žœ˜EK˜—K˜—K˜/K˜K˜—š   œžœžœMžœ(žœ˜Ÿš  œžœ6žœ˜QK˜K˜(K˜(K˜'Kšœžœžœ˜%Kšœ žœ˜#K˜2K˜K˜K˜K˜*K˜,š žœ žœ žœžœ&žœ.žœ˜‚Kšœ)™)K˜OKšœAžœ˜GK˜—šžœ˜K˜@K˜K˜—šžœžœžœ!žœžœžœžœ˜ΒKšœ˜K˜K˜K˜K˜Kšœ>žœ˜EK˜—K˜—K˜/K˜K˜—š   œžœžœLžœ(žœ˜žš  œžœ6žœ˜QKšœ žœ˜#K˜(K˜K˜K˜*˜$Kšžœ*˜-—K˜,Kšœ.žœ˜:Kšžœžœ+ž˜GKšœžœž˜7šœžœ˜$K˜K˜K˜K˜KšœCžœ˜JK˜—K˜—K˜/K˜K˜—š   œžœžœLžœ(žœ˜ š œžœ6žœ˜SKšœ žœ˜#K˜(K˜K˜K˜*K˜=K˜8K˜,Kšœ.žœ˜:Kšžœžœ+ž˜GKšœžœž˜9šœžœ˜$K˜K˜K˜K˜KšœCžœ˜JK˜—K˜—K˜1K˜K˜—š œžœ>žœžœ ˜†Kšžœ˜ Kš œžœžœžœžœ˜2Kš œžœ žœžœžœ˜0Kšœžœ ˜-Kšžœ˜šžœžœžœž˜š žœžœ žœžœž˜$K˜K˜Kšžœžœžœžœ˜4—Kšžœ˜Kšžœ˜Kšœžœ&˜.Kšœžœ#˜+Kšœžœ1žœžœ˜SK˜DK˜BKšžœžœ2žœžœ˜UK˜—K˜K˜—š  œžœFžœžœžœžœ˜sKšžœžœžœžœžœžœžœžœžœ˜Jšžœžœ ž˜Kšžœžœ˜%Kšžœžœžœ˜(—K˜K˜—š  œžœ!žœ ˜Xš žœžœžœžœ ž˜:K˜K˜Kšžœ ˜—Kšžœ2˜8K˜K˜—K˜&K˜)š  œžœžœMžœ(žœ5žœ˜Χš œžœ6žœ˜MK˜Kšœžœ˜ Kšœ"‘˜AKš žœžœžœžœ‘˜;K˜K˜šžœžœ˜Kšœ*žœ˜Eš žœ žœžœžœžœ˜4Kšœ"™"—Kšœžœ‘C˜NK˜—Kšžœžœžœ˜,šžœ žœ žœ˜!K˜K˜K˜K˜—K˜šžœ ˜ šžœ˜Kšœ!™!šžœ4žœ˜—˜K˜——K˜K˜—Kšžœžœ‘˜/—K˜K˜K˜Kšœ2žœžœžœ˜TK˜—K˜+K˜K˜—š  œžœžœ#˜;Kšœžœžœžœ ˜WKšœžœ˜$šœ ˜ Kšžœ žœ˜$Kšžœžœžœ&˜DKšžœ)˜-K˜—K˜K˜—š  œžœžœLžœ(žœ˜šKšœ"žœ žœžœžœžœžœžœ˜„šžœž˜KšœJ™JK˜1K˜1K˜5Kšžœ-˜4—K˜K˜—š   œžœ%žœžœžœR˜‘Kšœž œ˜Kšœžœ˜K˜&K˜4K˜8K˜K˜@Kšœ1‘ ˜QK˜%Kšžœ žœ*žœ‘˜VK˜+K˜"K˜š žœ žœ"žœžœž˜YK˜"K˜ Kšžœ˜—Kšžœžœ žœžœ ˜Ešžœ˜K˜K˜ K˜—K˜BK˜(š žœ žœ"žœžœ ž˜RK˜(K˜Kšžœ˜—š žœžœ žœžœ žœ˜KK˜!K˜—K˜&K˜K˜—š   œžœ)žœžœ'žœ ˜§K˜0K˜,Kšžœžœžœžœ ˜RKšœ)™)šžœž˜'šœ žœ˜Kšœ]™]Kš žœžœžœ žœ#žœžœ ˜„—šœ žœ˜Kšœf™fKš žœžœžœžœ(žœžœ˜“—Kšžœ˜—šžœžœ˜Kšžœžœžœ ˜*Kšžœžœžœ ˜2šžœ žœ˜š  œžœ žœžœžœ˜.Kšœžœžœ˜Kšœ/˜/Kšžœžœ˜,Kšœ˜—šžœžœ‘˜=Kšžœ‘˜9Kšžœ˜ Kšžœžœ˜—Kšœ˜—Kšžœžœ(žœžœ˜IK˜—Kšžœžœžœžœ˜-Kšžœžœ*žœžœ˜KK˜K˜K˜—Kšœ(™(šœžœ˜K™K™—š  œžœžœžœpžœžœžœžœ˜ΓK˜Kšœžœ˜šœ žœ˜Kšœ#™#—K˜K˜!Kšœ!‘0˜Qšžœ7žœ‘˜RKšœžœžœ˜K˜—š žœžœžœžœžœ‘˜Bšžœžœ˜Kšœžœ)˜?Kšœ žœ$˜7KšœžœžœžœH˜`Kšœžœ žœžœ˜$Kšžœ˜Kšœ˜—Kšžœ˜—K˜;Kšœ'žœ-˜WK˜K˜K˜lK˜K˜—Kšœ™š œžœžœ˜&š  œžœžœžœžœ˜.Kšžœ˜"K˜—K˜'K˜K˜—š œžœžœžœžœžœžœ˜VK˜K˜!K˜*K˜Kš žœžœžœžœžœžœ˜IK˜K˜=K˜K˜(K˜Ršžœ‘1˜4K˜7Kšžœ žœžœ˜'Kšžœ(žœžœ˜HKšžœ˜—K˜ K˜Kšœ žœžœžœ˜UK˜*K˜Bšžœ‘0˜3K˜7Kšžœžœžœ˜/Kšžœ#žœžœ˜AKšžœ˜—K˜&K˜K˜(K˜K˜K˜—š  œžœžœ˜K˜Kš žœžœžœžœžœžœ˜IK˜K˜=šžœž˜˜ K˜K˜—˜ K˜K˜Kšœ ‘œ!˜2K˜K˜*K˜—˜ K˜K˜8Kšœžœ'ž˜?Kšœ'žœžœžœ˜AK˜—˜ K˜K˜;K˜8K˜—˜ K˜<šžœžœžœ‘˜4Kšœ!žœ˜(K˜—K˜K˜0K˜8K˜—Kšžœžœ˜—K˜(K˜K˜K˜—Kšœ™š œžœžœ4žœ˜rKšœ žœžœ˜BK˜(Kš žœ žœžœžœžœžœ‘ ˜`šžœ žœžœžœ˜*Kšœ™Kšœžœžœžœ˜EKšœžœžœžœ˜QK•StartOfExpansion„[root: TextNode.Ref, old: TextNode.Ref, where: EditSpan.Place _ after, inherit: BOOL _ FALSE, event: EditSpan.Event _ NIL]šœ[žœ&˜…Kšœ˜—šžœ˜Kšžœžœ˜-šžœ˜Kšœžœ!˜*Kšœžœžœ˜