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, Size, XChar], TextLooks USING [Looks, noLooks], TextNode USING [EndPos, FirstChild, LastWithin, Location, Parent, Ref, 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] = INLINE --gfi saver-- { 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: INT; 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]; }; 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: INT; 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: INT, punc: TEditDocument.PunctuationPosition _ none] = { refChar, char: CHARACTER; alpha: BOOLEAN; node: TextNode.Ref = pos.node; lastOffset: INT _ TextEdit.Size[node]-1; ropeReader: RopeReader.Ref _ RopeReader.GetRopeReader[]; start _ end _ pos.where; RopeReader.SetPosition[ropeReader, node.rope, 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.Ref _ sel.start.pos.node; size: INT _ 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] ~ INLINE --gfi saver-- { char: TextEdit.XChar ~ TextEdit.FetchChar[node, offset]; RETURN [char.set=0 AND RopeEdit.BlankChar[VAL[char.code]]]; }; 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.Ref; startPos, endPos, endLen: INT; 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, start.rope, tSel.start.pos.where]; DO -- find first blank to left of start of selection loc: INT = 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[end.rope]; RopeReader.SetPosition[ropeReader, end.rope, endPos]; DO -- find first blank to right of end of selection loc: INT = 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: INT; 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 Σ 1985, 1988 by Xerox Corporation. All rights reserved. Edited by Paxton on May 31, 1983 9:18 am Michael Plass, October 15, 1987 5:15:06 pm PDT Russ Atkinson (RRA) June 17, 1985 2:52:45 pm PDT Doug Wyatt, February 17, 1988 6:06:29 pm PST ---- Mouse hit primitives ------ make point selection at end of typescript 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 ]; }; 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šœB™BKšœ(™(K™.K™0K™,—K™šΟk ˜ Kšœ œ6˜DKšœ œ˜Kšœ œ"˜4Kšœœ˜Kšœ œ˜-Kšœ œb˜rKšœœ ˜Kšœ œ&˜4Kšœ œ˜!Kšœ œ?˜MKšœœˆ˜›Kšœ œ9˜JKšœ œ˜ Kšœ œ1˜CKšœœƒ˜—Kšœœ˜Kšœ œ(˜:Kšœœ ˜Kšœœ˜0Kšœ œ ˜—K˜šΠblœœ˜Kšœ±˜ΈKšœ&˜-Kšœ˜K˜—šœ ™ K˜š Οnœœ œ6œMœ%˜Ξšœœ˜šœ"˜"šœ˜Kšœ˜Kšœ!˜!Kšœ ˜ Kšœœ˜——K˜Kšœ œœ˜K˜K˜5K˜ K˜K˜—Kšœ*˜*K˜K˜—š Ÿœœ œhœΟc œ˜‘šœœ˜šœ œ˜šœ(˜(šœ œ˜šœ0œ˜8˜Kšœœ*˜2—Kšœ%˜%K˜—K˜——Kšœ˜—K˜—Kšœ(˜(šœ6˜6Kšœœ#˜+—Kšœ˜K˜K˜—š Ÿ œœœLœ(œ˜ŸšŸ œœ6œ˜RK˜(Kšœœœ)˜aK˜K˜HKšœœœ˜Nšœœœ˜DK˜K˜K˜K˜K˜K˜Kšœ(œœœ˜;K˜—K˜—K˜0K˜K˜—š Ÿ œœœMœ(œ˜ŸšŸ œœ6œ˜QK˜(K˜'Kšœœœ˜%K˜Kšœ œ œœ%˜Jšœ,œ ˜TK˜OKšœ˜Kšœ˜Kšœœ˜K˜—šœ˜K˜HK˜K˜—Kšœœœ˜LKšœœ˜;šœœ˜$K˜K˜K˜K˜K˜K˜Kšœ>œ˜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˜—š Ÿ œœ%œœœœ5˜•Kšœ œ˜Kšœœ˜K˜Kšœ œ˜(K˜8K˜K˜3Kšœ1  ˜QK˜%Kšœ œ*œ ˜VK˜+K˜"K˜š œ œ"œœ˜YK˜"K˜ Kšœ˜—Kšœœ œœ ˜Ešœ˜K˜K˜ K˜—K˜5K˜(š œ œ"œœ ˜RK˜(K˜Kšœ˜—š œœ œœ œ˜KK˜!K˜—K˜&K˜K˜—š Ÿ œœ)œœ'œ ˜§K˜(Kšœœ˜ Kšœœœœ ˜RKšœ)™)šœ˜'šœ œ˜Kšœ]™]Kš œœœ œ#œœ ˜„—šœ œ˜Kšœf™fKš œœœœ(œœ˜“—Kšœ˜—šœœ˜Kšœœœ ˜*Kšœœœ ˜2šœ œ˜šŸœœ œœœœ  œ˜CKšœ8˜8Kšœ œœ˜;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˜Ešœ 1˜4Kšœœ#˜+Kšœ œœ˜'Kšœ(œœ˜HKšœ˜—K˜ K˜Kšœ œœœ˜UK˜K˜5šœ 0˜3Kšœœ#˜+Kšœœœ˜/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šœœœ˜