DIRECTORY BiScrollers, Imager, ImagerBackdoor, ImagerFont, ImagerSample, InputFocus, IO, MessageWindow, Pipal, PipalEdit, PipalInt, PipalInteractiveEdit, PipalPaint, PipalReal, PipalTextEditor, Real, Rope, TerminalIO, VFonts, ViewerClasses, ViewerOps; PipalTextEditorImpl: CEDAR PROGRAM IMPORTS BiScrollers, Imager, ImagerBackdoor, ImagerFont, ImagerSample, InputFocus, IO, MessageWindow, Pipal, PipalEdit, PipalInt, PipalInteractiveEdit, PipalPaint, PipalReal, Real, Rope, TerminalIO, VFonts, ViewerOps EXPORTS PipalTextEditor = BEGIN OPEN PipalTextEditor; pipalTextClass: PUBLIC Pipal.Class _ Pipal.RegisterClass[name: "PipalText", type: CODE[PipalTextRec]]; TextDescribe: Pipal.DescribeProc = { text: PipalText _ NARROW [object]; Pipal.PutIndent[out, indent, cr]; IO.PutF[out, "Text [%g]", IO.rope[text.rope]]; }; TextSize: PipalReal.SizeProc ~ { size _ NARROW[object, PipalText].size; }; fixedFont: Imager.Font _ VFonts.defaultFont; feedbackHeight: REAL _ 4.0; fontHeight: REAL _ ImagerFont.FontBoundingBox[fixedFont].descent + ImagerFont.FontBoundingBox[fixedFont].ascent; lineHeight: REAL _ fontHeight + feedbackHeight; TextPaint: PipalPaint.PaintProc ~ { EachRow: RowProc = { Imager.SetXY[context, [0.0, y]]; Imager.ShowRope[context, text.rope, interval.base, interval.size]; }; text: PipalText _ NARROW[object]; Imager.SetColor[context, Imager.black]; Imager.SetFont[context, fixedFont]; [] _ EnumerateRows[text, EachRow]; }; emptySize: PipalReal.Size _ PipalReal.emptySize; CreatePipalText: PUBLIC PROC [rope: Pipal.ROPE, size: PipalReal.Size _ PipalReal.emptySize] RETURNS [text: PipalText] = { text _ NEW[PipalTextRec _ [rope, size]]; IF size=PipalReal.emptySize THEN { extents: ImagerFont.Extents _ ImagerFont.RopeBoundingBox[font: fixedFont, rope: rope]; text.size _ [extents.leftExtent+extents.rightExtent, lineHeight]; }; }; GetSelectionInterval: PUBLIC PROC [text: PipalText, position: PipalReal.Position, grain: SelectionGrain] RETURNS [interval: PipalInt.Interval _ [0, 0], closerRight: BOOL _ FALSE] = { ropeSize: INT _ Rope.Size[text.rope]; IF ropeSize>0 THEN { leftEdge, rightEdge: REAL _ 0.0; location: Location; bbox: PipalReal.Rectangle; [location, bbox] _ CharLocation[text, position]; leftEdge _ bbox.base.x; rightEdge _ bbox.base.x+bbox.size.x; SELECT grain FROM point => { interval.base _ location; interval.size _ 0; }; char => { interval.base _ location; interval.size _ 1; }; word => { start: Location _ location; char: CHAR _ Rope.Fetch[text.rope, location]; IF CharSeparator[char] THEN interval.base _ location ELSE { UNTIL location=0 OR CharSeparator[(char _ Rope.Fetch[text.rope, location-1])] DO location _ location-1; leftEdge _ leftEdge-CharSize[char].x; ENDLOOP; interval.base _ location; location _ start; UNTIL location=ropeSize-1 OR CharSeparator[(char _ Rope.Fetch[text.rope, location+1])] DO location _ location+1; rightEdge _ rightEdge+CharSize[char].x; ENDLOOP; }; interval.size _ location-interval.base+1; }; ENDCASE => ERROR; closerRight _ ABS[rightEdge-position.x]0 THEN DO rightEdge: REAL _ 0.0; firstUnpaintedChar: Location _ start; lastSeparator: Location _ start; DO char: CHAR _ Rope.Fetch[text.rope, firstUnpaintedChar]; rightEdge _ rightEdge + CharSize[char].x; IF rightEdge>=text.size.x THEN EXIT; IF CharSeparator[char] THEN lastSeparator _ firstUnpaintedChar; firstUnpaintedChar _ firstUnpaintedChar + 1; IF firstUnpaintedChar=length THEN EXIT; ENDLOOP; IF firstUnpaintedChar=length THEN lastSeparator _ length-1; IF eachRow[[start, lastSeparator-start+1], y] THEN {quit _ TRUE; EXIT}; IF lastSeparator+1=length THEN EXIT; start _ lastSeparator+1; y _ y-lineHeight; ENDLOOP; }; CharSeparator: PROC [char: CHAR] RETURNS [separator: BOOL] = { separator _ NOT (char IN ['a..'z] OR char IN ['A..'Z] OR char IN ['0..'9]); }; textEditorClass: PUBLIC Pipal.Class _ Pipal.RegisterClass[name: "TextEditor", type: CODE[TextEditorRec]]; TextEditorDescribe: Pipal.DescribeProc = { textEditor: TextEditor _ NARROW[object]; Pipal.PutIndent[out, indent, cr]; IO.PutF[out, "Text Editor"]; }; TextEditorSize: PipalReal.SizeProc ~ { size _ PipalReal.emptySize; }; black: Imager.Color _ ImagerBackdoor.MakeStipple[stipple: 0FFFFH, xor: TRUE]; lightGrey: Imager.Color _ ImagerBackdoor.MakeStipple[stipple: 0208H, xor: TRUE]; darkGrey: Imager.Color _ ImagerBackdoor.MakeStipple[stipple: 0A5A5H, xor: TRUE]; caretH: INTEGER = 6; caretW: INTEGER = 16; CaretBits: TYPE ~ REF CaretBitsRep; CaretBitsRep: TYPE = ARRAY [0..caretH) OF WORD; caretBits: CaretBits _ NEW[CaretBitsRep _ [ 000400B, -- 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 001600B, -- 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 003700B, -- 0 0 0 0 0 1 1 1 1 1 0 0 0 0 0 0 003300B, -- 0 0 0 0 0 1 1 0 1 1 0 0 0 0 0 0 006140B, -- 0 0 0 0 1 1 0 0 0 1 1 0 0 0 0 0 004040B -- 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 ]]; caretMap: ImagerSample.SampleMap _ SampleMapFromCaretBits[caretBits]; SampleMapFromCaretBits: PROC [bits: CaretBits] RETURNS [ImagerSample.SampleMap] ~ TRUSTED { RETURN [ImagerSample.UnsafeNewSampleMap[ box: [min: [s: 0, f: 0], max: [s: caretH, f: caretW]], bitsPerSample: 1, bitsPerLine: BITS[WORD], base: [word: LOOPHOLE[bits], bit: 0], ref: bits, words: SIZE[CaretBitsRep] ]]; }; TextEditorPaint: PipalPaint.PaintProc = { EachRow: RowProc = { y _ y-feedbackHeight; FOR selection: Selections IN Selections DO IF te.selections[selection].valid AND PipalInt.IntersectInterval[interval, te.selections[selection].interval] THEN { sel: Selection _ te.selections[selection]; selectInterval: PipalInt.Interval _ IntersectIntervals[interval, sel.interval]; start: REAL _ ImagerFont.RopeEscapement[fixedFont, text.rope, interval.base, selectInterval.base-interval.base].x; SELECT TRUE FROM selection=primary => Imager.SetColor[context, black]; sel.pendingDelete => Imager.SetColor[context, lightGrey]; ENDCASE => Imager.SetColor[context, darkGrey]; IF selectInterval.size>0 THEN { length: REAL _ ImagerFont.RopeEscapement[fixedFont, text.rope, selectInterval.base, selectInterval.size].x; IF sel.pendingDelete THEN Imager.MaskRectangle[context, [start, y, length, fontHeight]] ELSE Imager.MaskRectangle[context, [start, y, length, feedbackHeight]]; IF (sel.caretAfter AND PipalInt.IsInsideIntervalNumber[selectInterval, sel.interval.base+sel.interval.size]) OR (NOT sel.caretAfter AND PipalInt.IsInsideIntervalNumber[selectInterval, sel.interval.base]) THEN Imager.MaskBitmap[context, caretMap, [0, 7], Imager.defaultScanMode, [IF sel.caretAfter THEN start+length ELSE start, y+feedbackHeight]]; } ELSE Imager.MaskBitmap[context, caretMap, [0, 7], Imager.defaultScanMode, [start, y+feedbackHeight]]; }; ENDLOOP; }; editor: PipalEdit.Editor _ NARROW[object]; text: PipalText _ NARROW[editor.object]; te: TextEditor _ NARROW[editor.state]; PipalPaint.Paint[text, context]; Imager.SetStrokeWidth[context, feedbackHeight]; [] _ EnumerateRows[text, EachRow]; }; Create: PUBLIC PROC [text: PipalText] RETURNS [editor: PipalEdit.Editor] = { editor _ PipalEdit.CreateEditor[text, NEW[TextEditorRec]]; }; InsertRope: PUBLIC PROC [editor: PipalEdit.Editor, characters: Pipal.ROPE] = { text: PipalText _ NARROW[editor.object]; te: TextEditor _ NARROW[editor.state]; [text, te] _ BasicInsert[text, te, characters]; PipalEdit.Do[editor, "insert rope", text, te]; }; InsertChar: PUBLIC PROC [editor: PipalEdit.Editor, character: CHAR] = { text: PipalText _ NARROW[editor.object]; te: TextEditor _ NARROW[editor.state]; [text, te] _ BasicInsert[text, te, Rope.FromChar[character]]; PipalEdit.Do[editor, "insert char", text, te]; }; Copy: PUBLIC PROC [editor: PipalEdit.Editor] = { text: PipalText _ NARROW[editor.object]; te: TextEditor _ NARROW[editor.state]; IF te.selections[secondary].valid THEN { [text, te] _ BasicInsert[text, te, Rope.Substr[text.rope, te.selections[secondary].interval.base, te.selections[secondary].interval.size]]; IF te.selections[secondary].pendingDelete THEN [text, te] _ BasicDelete[text, te, te.selections[secondary].interval]; } ELSE ERROR Pipal.Error[$noSecondarySelection]; PipalEdit.Do[editor, "copy", text, te]; }; Transpose: PUBLIC PROC [editor: PipalEdit.Editor] = { text: PipalText _ NARROW[editor.object]; te: TextEditor _ NEW[TextEditorRec _ NARROW[editor.state, TextEditor]^]; IF NOT te.selections[primary].valid THEN ERROR Pipal.Error[$noPrimarySelection]; IF NOT te.selections[secondary].valid THEN ERROR Pipal.Error[$noSecondarySelection]; { i1: PipalInt.Interval; i2: PipalInt.Interval; switched: BOOL; [i1, i2, switched] _ DecomposeIntervals[te.selections[primary].interval, te.selections[secondary].interval]; text _ CreatePipalText[Rope.Cat[ Rope.Substr[text.rope, 0, i1.base], Rope.Substr[text.rope, i2.base, i2.size], Rope.Substr[text.rope, i1.base+i1.size, i2.base-(i1.base+i1.size)], Rope.Substr[text.rope, i1.base, i1.size], Rope.Substr[text.rope, i2.base+i2.size, Rope.Length[text.rope]-i2.base+i2.size]], text.size]; { difference: INT _ te.selections[primary].interval.size - te.selections[secondary].interval.size; t: Location _ te.selections[primary].interval.base; te.selections[primary].interval.base _ te.selections[secondary].interval.base; te.selections[secondary].interval.base _ t; IF switched THEN te.selections[secondary].interval.base _ te.selections[secondary].interval.base + difference ELSE te.selections[primary].interval.base _ te.selections[primary].interval.base - difference; }; }; PipalEdit.Do[editor, "transpose", text, te]; }; Delete: PUBLIC PROC [editor: PipalEdit.Editor] = { text: PipalText _ NARROW[editor.object]; te: TextEditor _ NEW[TextEditorRec _ NARROW[editor.state, TextEditor]^]; IF NOT te.selections[primary].valid THEN ERROR Pipal.Error[$noPrimarySelection]; te.paste _ Rope.Substr[text.rope, te.selections[primary].interval.base, te.selections[primary].interval.size]; [text, te] _ BasicDelete[text, te, te.selections[primary].interval]; PipalEdit.Do[editor, "delete", text, te]; }; Paste: PUBLIC PROC [editor: PipalEdit.Editor] = { text: PipalText _ NARROW[editor.object]; te: TextEditor _ NARROW[editor.state]; [text, te] _ BasicInsert[text, te, te.paste]; PipalEdit.Do[editor, "paste", text, te]; }; Erase: PUBLIC PROC [editor: PipalEdit.Editor] = { text: PipalText _ NARROW[editor.object]; te: TextEditor _ NARROW[editor.state]; sel: Selection _ te.selections[primary]; loc: Location; IF NOT sel.valid THEN ERROR Pipal.Error[$noPrimarySelection]; loc _ IF sel.caretAfter THEN sel.interval.base+sel.interval.size ELSE sel.interval.base; IF loc=0 THEN ERROR Pipal.Error[$noCharacterToErase]; [text, te] _ BasicDelete[text, te, [loc-1, 1]]; PipalEdit.Do[editor, "erase", text, te]; }; BasicInsert: PROC [oldText: PipalText, oldte: TextEditor, characters: Pipal.ROPE] RETURNS [newText: PipalText, newte: TextEditor] = { size: Location _ Rope.Size[characters]; loc: Location; IF NOT oldte.selections[primary].valid THEN ERROR Pipal.Error[$noPrimarySelection]; IF oldte.selections[primary].pendingDelete THEN { paste: Rope.ROPE _ Rope.Substr[oldText.rope, oldte.selections[primary].interval.base, oldte.selections[primary].interval.size]; [newText, newte] _ BasicDelete[oldText, oldte, oldte.selections[primary].interval]; newte.paste _ paste; } ELSE { newText _ oldText; newte _ NEW[TextEditorRec _ oldte^]; }; loc _ IF newte.selections[primary].caretAfter THEN newte.selections[primary].interval.base + newte.selections[primary].interval.size ELSE newte.selections[primary].interval.base; newText _ CreatePipalText[Rope.Cat[ Rope.Substr[newText.rope, 0, loc], characters, Rope.Substr[newText.rope, loc, Rope.Size[newText.rope]-loc]], newText.size]; FOR selection: Selections IN Selections DO IF newte.selections[selection].valid AND loc<=newte.selections[selection].interval.base THEN newte.selections[selection].interval.base _ newte.selections[selection].interval.base + size; ENDLOOP; }; BasicDelete: PROC [oldText: PipalText, oldte: TextEditor, interval: PipalInt.Interval] RETURNS [newText: PipalText, newte: TextEditor] = { base: Location _ interval.base; size: INT _ interval.size; newText _ CreatePipalText[Rope.Cat[Rope.Substr[oldText.rope, 0, base], Rope.Substr[oldText.rope, base+size, Rope.Size[oldText.rope]-(base+size)]], oldText.size]; newte _ NEW[TextEditorRec _ oldte^]; FOR sel: Selections IN Selections DO IF oldte.selections[sel].valid THEN { IF base=Rope.Size[text.rope]) THEN ERROR; PipalEdit.Do[editor, "select", text, te]; }; CancelSelection: PUBLIC PROC [editor: PipalEdit.Editor, selection: Selections _ primary] = { text: PipalText _ NARROW[editor.object]; te: TextEditor _ NEW[TextEditorRec _ NARROW[editor.state, TextEditor]^]; te.selections[selection].valid _ FALSE; PipalEdit.Do[editor, "cancel selection", text, te]; }; IntersectIntervals: PROC [i1, i2: PipalInt.Interval] RETURNS [i3: PipalInt.Interval] = { i3.base _ MAX[i1.base, i2.base]; i3.size _ MIN[i1.base+i1.size, i2.base+i2.size]-i3.base; IF i3.size<0 THEN i3.size _ 0; }; DecomposeIntervals: PROC [i1, i2: PipalInt.Interval] RETURNS [i3, i4: PipalInt.Interval, switched: BOOL _ FALSE] = { IF i1.base>i2.base THEN { t: PipalInt.Interval _ i1; i1 _ i2; i2 _ t; switched _ TRUE; }; i3.base _ i1.base; i3.size _ MIN[i1.size, i2.base-i1.base]; i4.base _ MAX[i2.base, i1.base+i1.size]; i4.size _ MIN[i2.size, ABS[(i2.base+i2.size)-(i1.base+i1.size)]]; }; RectanglePointDistance: PROC [rectangle: PipalReal.Rectangle, position: PipalReal.Position] RETURNS [distance: REAL] = { distance _ MIN[ PointDistance[position, rectangle.base], PointDistance[position, [rectangle.base.x, rectangle.base.y+rectangle.size.y]], PointDistance[position, [rectangle.base.x+rectangle.size.x, rectangle.base.y]], PointDistance[position, [rectangle.base.x+rectangle.size.x, rectangle.base.y+rectangle.size.y]]]; }; PointDistance: PROC [p1, p2: PipalReal.Position] RETURNS [distance: REAL] = { x1x2: REAL _ ABS[p1.x-p2.x]; y1y2: REAL _ ABS[p1.y-p2.y]; distance _ Real.SqRt[x1x2*x1x2+y1y2*y1y2]; }; InteractiveEditor: PUBLIC PROC [size: PipalReal.Size] RETURNS [viewerData: PipalInteractiveEdit.ViewerData] = { viewerData _ PipalInteractiveEdit.Create[Create[CreatePipalText[NIL, size]], "PipalTextEditor.TIP", EditorNotify, NEW[EditorNotifyDataRec]]; }; EditorNotifyData: TYPE = REF EditorNotifyDataRec; EditorNotifyDataRec: TYPE = RECORD [ coords: PipalReal.Vector _ PipalReal.zeroVector, -- mx, my editState: EditState _ reset, sel: Selections _ primary, selState: SelState _ reset, pdelState: PDelState _ reset, pDel: BOOL _ FALSE, prevPSel: Selection, initialSelect: PipalInt.Interval, mouseColor: MouseColor _ red, editMessage: Rope.ROPE _ NIL]; EditState: TYPE = { reset, -- no edit specified abort, -- edit aborted tolimbo, -- delete primary toprimary, -- copy/move secondary selection to/onto primary tosecondary, -- copy/move primary to/onto secondary toboth -- transpose primary and secondary }; SelState: TYPE = { reset, -- not specified yet primary, -- making a primary selection secondary }; PDelState: TYPE = { reset, -- not specified yet pending, -- making a pending delete selection not }; LevelChange: TYPE = {reduce, expand, same}; MouseColor: TYPE = {red, yellow, blue, dead}; BadMouse: SIGNAL = CODE; MessageArray: TYPE = ARRAY BOOL OF ARRAY BOOL OF Rope.ROPE; toPrimaryMessages: REF MessageArray _ NEW [MessageArray]; toSecondaryMessages: REF MessageArray _ NEW [MessageArray]; EditorNotify: ViewerClasses.NotifyProc = { Complain: PROC [atom: ATOM] = { IO.PutF[TerminalIO.TOS[], "Unimplemented tip table atom %g\n", IO.atom[atom]]; someComplaint _ TRUE; }; AbortSecondary: PROC = { MessageWindow.Append["Make a primary selection first.",TRUE]; MessageWindow.Blink[]; d.editState _ abort; d.mouseColor _ dead }; EditMessage: PROC = { msg: Rope.ROPE _ SELECT d.editState FROM tolimbo => "Select for delete", toprimary => toPrimaryMessages [textEditor.selections[primary].pendingDelete] [textEditor.selections[secondary].pendingDelete], tosecondary => toSecondaryMessages [textEditor.selections[primary].pendingDelete] [textEditor.selections[secondary].pendingDelete], toboth => "Select for transpose", ENDCASE => NIL; IF msg = NIL OR msg = d.editMessage THEN RETURN; MessageWindow.Append[msg,TRUE]; d.editMessage _ msg }; Extend: PROC [saveEnds: BOOL] = { IF d.editState=abort THEN RETURN; IF d.mouseColor # blue THEN { IF d.mouseColor = dead THEN SIGNAL BadMouse; d.mouseColor _ blue; d.prevPSel _ textEditor.selections[primary]; saveEnds _ TRUE; }; { grain: SelectionGrain _ textEditor.selections[d.sel].granularity; new: PipalInt.Interval; closerRight: BOOL; set: PipalInt.Interval; IF saveEnds THEN d.initialSelect _ textEditor.selections[d.sel].interval; [new, closerRight] _ GetSelectionInterval[text, d.coords, grain]; IF d.initialSelect.base<=new.base THEN { set.base _ d.initialSelect.base; set.size _ new.base+new.size-d.initialSelect.base; } ELSE { set.base _ new.base; set.size _ d.initialSelect.base+d.initialSelect.size-new.base; }; SetSelection[editor, set, d.sel, d.pDel, closerRight, grain]; IF d.sel=secondary THEN EditMessage[]; }; }; viewerData: PipalInteractiveEdit.ViewerData _ NARROW [BiScrollers.ClientDataOfViewer[self]]; d: EditorNotifyData _ NARROW[viewerData.data]; editor: PipalEdit.Editor _ viewerData.editor; text: PipalText _ NARROW[editor.object]; textEditor: TextEditor _ NARROW[editor.state]; someComplaint: BOOL _ FALSE; FOR input _ input, input.rest UNTIL input=NIL DO WITH input.first SELECT FROM coords: BiScrollers.ClientCoords => d.coords _ coords^; char: REF CHAR => SELECT char^ FROM IO.BS => Erase[editor ! Pipal.Error => IF reason=$noCharacterToErase THEN {TerminalIO.PutRope["No character to erase\n"]; CONTINUE} ELSE REJECT]; ENDCASE => { newBase: Location _ textEditor.selections[primary].interval.base; IF textEditor.selections[primary].caretAfter THEN newBase _ newBase + textEditor.selections[primary].interval.size; InsertChar[editor, char^]; SetSelection[editor: editor, interval: [newBase+1, 0], selection: primary, pendingDelete: FALSE, caretAfter: FALSE, granularity: point]; }; atom: ATOM => { InputFocus.SetInputFocus[self]; SELECT atom FROM $BlueMouse => { IF d.mouseColor = dead THEN SIGNAL BadMouse; d.mouseColor _ blue; d.prevPSel _ textEditor.selections[primary]; }; $DoEdit => { SELECT d.editState FROM tolimbo => Delete[editor]; reset, abort => NULL; toprimary => {Copy[editor]; CancelSelection[editor, secondary]}; tosecondary => Complain[$DoEditToSecondary]; toboth => Transpose[editor]; ENDCASE => ERROR; d.editState _ reset; d.sel _ primary; d.selState _ reset; d.pdelState _ reset; d.pDel _ FALSE; d.mouseColor _ red; IF d.editMessage # NIL THEN MessageWindow.Clear[]; d.editMessage _ NIL; }; $ForceSelNotPendDel => { d.pdelState _ reset; d.pdelState _ not; d.pDel _ FALSE; }; $ForceSelPendDel => { d.pdelState _ reset; d.pdelState _ pending; d.pDel _ TRUE; }; $RedMouse => { IF d.mouseColor = dead THEN SIGNAL BadMouse; d.mouseColor _ red; d.prevPSel _ textEditor.selections[primary]; }; $SelChar => { IF NOT d.editState=abort THEN { IF d.sel=secondary AND NOT textEditor.selections[primary].valid THEN AbortSecondary[] ELSE { interval: PipalInt.Interval; closerRight: BOOL; [interval, closerRight] _ GetSelectionInterval[text, d.coords, char]; SetSelection[editor, interval, d.sel, d.pDel, closerRight, char]; IF d.editState=tolimbo OR d.sel=secondary THEN EditMessage[]; }; }; }; $SelExpand => textEditor.selections[d.sel].granularity _ SELECT textEditor.selections[d.sel].granularity FROM point => char, char => word, ENDCASE => word; $SelExtend => Extend[FALSE]; $SelNotPendDel => IF d.pdelState=reset THEN { d.pdelState _ not; d.pDel _ FALSE; }; $SelPendDel => IF d.pdelState=reset THEN { d.pdelState _ pending; d.pDel _ TRUE; }; $SelReduce => textEditor.selections[d.sel].granularity _ char; $SelSecondary => IF d.selState=reset THEN { d.selState _ secondary; d.sel _ secondary; }; $SelStartExtend => Extend[TRUE]; $SelUpdate => { IF NOT d.editState=abort THEN { IF d.sel=secondary AND NOT textEditor.selections[primary].valid THEN AbortSecondary[] ELSE { grain: SelectionGrain _ textEditor.selections[d.sel].granularity; interval: PipalInt.Interval; closerRight: BOOL; [interval, closerRight] _ GetSelectionInterval[text, d.coords, grain]; SetSelection[editor, interval, d.sel, d.pDel, closerRight, grain]; IF d.sel=secondary THEN EditMessage[]; }; }; }; $SelWord => { IF NOT d.editState=abort THEN { IF d.sel=secondary AND NOT textEditor.selections[primary].valid THEN AbortSecondary[] ELSE { interval: PipalInt.Interval; closerRight: BOOL; [interval, closerRight] _ GetSelectionInterval[text, d.coords, word]; SetSelection[editor, interval, d.sel, d.pDel, closerRight, word]; IF d.editState=tolimbo OR d.sel=secondary THEN EditMessage[]; }; }; }; $ToLimbo => IF d.editState=reset THEN d.editState _ tolimbo; $ToPrimary => IF d.editState=reset OR d.editState=tolimbo THEN d.editState _ toprimary; $YellowMouse => { IF d.mouseColor = dead THEN SIGNAL BadMouse; d.mouseColor _ yellow; d.prevPSel _ textEditor.selections[primary]; }; ENDCASE => { IO.PutF[TerminalIO.TOS[], "Unknown tip table atom %g\n", IO.atom[atom]]; someComplaint _ TRUE; }; }; ENDCASE; ENDLOOP; IF someComplaint THEN TerminalIO.PutRope["\n"]; ViewerOps.PaintViewer[self, all]; }; Pipal.PutClassMethod[pipalTextClass, Pipal.describeMethod, NEW [Pipal.DescribeProc _ TextDescribe]]; Pipal.PutClassMethod[pipalTextClass, PipalReal.sizeMethod, NEW [PipalReal.SizeProc _ TextSize]]; Pipal.PutClassMethod[pipalTextClass, PipalPaint.paintMethod, NEW [PipalPaint.PaintProc _ TextPaint]]; Pipal.PutClassMethod[textEditorClass, Pipal.describeMethod, NEW [Pipal.DescribeProc _ TextEditorDescribe]]; Pipal.PutClassMethod[textEditorClass, PipalReal.sizeMethod, NEW [PipalReal.SizeProc _ TextEditorSize]]; Pipal.PutClassMethod[textEditorClass, PipalPaint.paintMethod, NEW [PipalPaint.PaintProc _ TextEditorPaint]]; toPrimaryMessages[FALSE][FALSE] _ "Select for copy to caret"; toPrimaryMessages[FALSE][TRUE] _ "Select for move to caret"; toPrimaryMessages[TRUE][FALSE] _ "Select replacement"; toPrimaryMessages[TRUE][TRUE] _ "Select for move onto"; toSecondaryMessages[FALSE][FALSE] _ "Select destination for copy"; toSecondaryMessages[FALSE][TRUE] _ "Select for replacement"; toSecondaryMessages[TRUE][FALSE] _ "Select destination for move"; toSecondaryMessages[TRUE][TRUE] _ "Select destination for move onto"; END. ๔PipalTextEditorImpl.mesa Copyright ำ 1988 by Xerox Corporation. All rights reserved. Barth, February 9, 1988 10:33:36 am PST Text Class Text Editor Class Creation Manipulation Selection PipalInt Orders i1 and i2 so that i3.baseKšœ-œ˜LKšœ˜K˜—Kš œ œœ"œœœœ˜Yš ž œœ%œœœ˜XK˜Kšœœ)˜0Kšœœ˜%šœ œ˜Kšœ œ˜Kšœ%˜%Kšœ ˜ š˜Kšœœ-˜7Kšœ)˜)Kšœœœ˜$Kšœœ$˜?Kšœ,˜,Kšœœœ˜'Kšœ˜—Kšœœ˜;Kšœ,œ œœ˜GKšœœœ˜$Kšœ˜Kšœ˜Kšœ˜—Kšœ˜K˜—š ž œœœœ œ˜>Kš œ œœ œœ œœ ˜LKšœ˜K˜——™K˜šœœ=œ˜iK˜—šžœ˜*Kšœœ ˜(Kšœ!˜!Kšœ˜K˜K˜—šžœ˜&Kšœ˜Kšœ˜K˜—KšœGœ˜MKšœJœ˜PšœJœ˜PK˜—Kšœœ˜šœœ˜K˜—Kšœ œœ˜#š œœœ œœ˜/K˜—šœœ˜+Kšœ ฯc"˜+Kšœ Ÿ"˜+Kšœ Ÿ"˜+Kšœ Ÿ"˜+Kšœ Ÿ"˜+Kšœ Ÿ"˜+K˜K˜—šœE˜EK˜—šžœœœœ˜[šœ"˜(Kšœ7˜7Kšœœœœ˜QKšœœ˜$Kšœ˜—K˜K˜—šžœ˜)šžœ ˜Kšœ˜šœœ ˜*šœ œIœ˜tKšœ*˜*KšœO˜OKšœœg˜ršœœ˜Kšœ5˜5Kšœ9˜9Kšœ'˜.—šœœ˜Kšœœ_˜kKšœœ>˜WKšœC˜GKšœœWœœœEœGœœœ˜ฺK˜—Kšœa˜eK˜—Kšœ˜—K˜—Kšœœ ˜*Kšœœ˜(Kšœœ˜&Kšœ ˜ Kšœ/˜/Kšœ"˜"Kšœ˜K˜——™šžœœœœ˜LKšœ&œ˜:K˜K™——™ šž œœœ.œ˜NKšœœ˜(Kšœœ˜&Kšœ/˜/Kšœ.˜.K˜J˜—šž œœœ'œ˜GKšœœ˜(Kšœœ˜&Kšœ=˜=Kšœ.˜.K˜J˜—šžœœœ˜0Kšœœ˜(Kšœœ˜&šœ œ˜(Kšœ‹˜‹Kšœ(œG˜uK˜—Kšœœ$˜.Kšœ'˜'K˜J˜—šž œœœ˜5Kšœœ˜(Kšœœœ˜HKšœœœœ"˜PKšœœ œœ$˜Tšœ˜Kšœ˜Kšœ˜Kšœ œ˜Kšœl˜lšœ ˜ Kšœ#˜#Kšœ)˜)KšœC˜CKšœ)˜)Kšœ]˜]—šœ˜Kšœ œQ˜`Kšœ3˜3KšœN˜NKšœ+˜+Kšœ œ]˜mKšœZ˜^K˜—K˜—Kšœ,˜,Kšœ˜J˜—šžœœœ˜2Kšœœ˜(Kšœœœ˜HKšœœœœ"˜PKšœn˜nKšœD˜DKšœ)˜)K˜J˜—šžœœœ˜1Kšœœ˜(Kšœœ˜&Kšœ-˜-Kšœ(˜(K˜J˜—šžœœœ˜1Kšœœ˜(Kšœœ˜&Kšœ(˜(K˜Kšœœ œœ"˜=Kšœœœ%œ˜XKšœœœ"˜5Kšœ/˜/Kšœ(˜(K˜J˜—šž œœ;œœ,˜…K˜'K˜Kšœœ!œœ"˜Sšœ)œ˜1Kšœ œo˜KšœS˜SKšœ˜K˜—šœ˜Kšœ˜Kšœœ˜$Kšœ˜—Kšœœ&œSœ)˜ฒšœ#˜#Kšœ"˜"Kšœ ˜ KšœL˜L—šœœ ˜*Kšœ#œ0œ^˜บKšœ˜—K˜J˜—šž œœFœ,˜ŠKšœ˜Kšœœ˜Kšœก˜กKšœœ˜$šœœ ˜$šœœ˜&Kš œ*œ'œ/œ,œ˜ยKšœŽ˜ŽK˜—Kšœ˜—K˜J˜——šœ ™ šž œœœiœœœœ)˜ัKšœœ˜(Kšœœœ˜HKšœ!œ˜&Kšœ-˜-Kšœ7˜7Kšœ1˜1Kšœ3˜3Kš œ+œ-œ œ&œœ˜ Kšœ)˜)K˜K˜—šžœœœ@˜\Kšœœ˜(Kšœœœ˜HKšœ!œ˜'Kšœ3˜3K˜K˜——™šžœœœ˜XKšœ œ˜ Kšœ œ+˜8Kšœ œ ˜K˜K˜—š žœœœ'œœ˜tKšœฉœ™ฎšœœ˜Kšœ+˜+Kšœ œ˜Kšœ˜—K˜Kšœ œ˜(Kšœ œ˜(Kšœ œ œ'˜AK˜K˜——™ šžœœ@œ œ˜xK™Kšœ œ˜Kšœ(˜(KšœO˜OKšœO˜OKšœa˜a—K˜K˜—šž œœœ œ˜MKšœœœ ˜Kšœœœ ˜Kšœ*˜*K˜K˜——™šžœœœœ2˜oKšœ@œ/œ˜ŒKšœ˜K˜—Kšœœœ˜1šœœœ˜$Kšœ1Ÿ ˜:Kšœ˜K˜Kšœ˜Kšœ˜Kšœœœ˜Kšœ˜K˜!Kšœ˜Kšœœœ˜K˜—šœ œ˜KšœŸ˜KšœŸ˜Kšœ Ÿ˜Kšœ Ÿ0˜;Kšœ Ÿ&˜3KšœŸ"˜)K˜K˜—šœ œ˜KšœŸ˜Kšœ Ÿ˜'K˜ K˜K˜—šœ œ˜KšœŸ˜Kšœ Ÿ$˜-K˜K˜K˜—šœ œ˜+K˜—šœ œ˜-K˜—šžœœœ˜K˜—Kšœœœœœœœœœ˜;Kšœœœ˜9šœœœ˜;K˜—šž œ˜*šžœœœ˜Kšœœ)œ ˜NKšœœ˜K˜—šžœœ˜Kšœ7œ˜=K˜K˜)K˜—šž œœ˜šœ œœ ˜(Kšœ˜Kšœ˜Kšœƒ˜ƒKšœ!˜!Kšœœ˜—Kš œœœœœ˜0Kšœœ˜K˜K˜—šžœœ œ˜!Kšœœœ˜!šœœ˜Kšœœœ ˜,K˜Kšœ,˜,Kšœ œ˜Kšœ˜—šœ˜KšœA˜AKšœ˜Kšœ œ˜Kšœ˜Kšœ œ9˜IKšœA˜Ašœ œ˜(Kšœ ˜ Kšœ2˜2Kšœ˜—šœ˜Kšœ˜Kšœ>˜>K˜—Kšœ=˜=Kšœœ˜&K˜—K˜—Kšœ.œ(˜\Kšœœ˜.Kšœ-˜-Kšœœ˜(Kšœœ˜.Kšœœœ˜š œœœœœ œ˜MKšœ7˜7šœœœœ˜#Kšœœ"œœ1œœœ˜‘šœ˜ KšœA˜AKšœ+œB˜sKšœ˜KšœZœœ˜ˆKšœ˜——šœœ˜K˜šœ˜šœ˜Kšœœœ ˜,K˜Kšœ,˜,Kšœ˜—šœ ˜ šœ ˜Kšœ˜Kšœœ˜Kšœ@˜@Kšœ,˜,Kšœ˜Kšœœ˜—Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ œ˜Kšœ˜Kšœœœ˜2Kšœœ˜Kšœ˜—šœ˜Kšœ˜Kšœ˜Kšœ œ˜Kšœ˜—šœ˜Kšœ˜Kšœ˜Kšœ œ˜Kšœ˜—šœ˜Kšœœœ ˜,K˜Kšœ,˜,K˜—šœ ˜ šœœœ˜Kšœœœ&œ˜Ušœ˜Kšœ˜Kšœ œ˜KšœE˜EKšœA˜AKšœœœ˜=K˜—Kšœ˜—K˜—šœ9œ*˜mK˜K˜ Kšœ ˜—Kšœœ˜šœœœ˜-Kšœ˜Kšœ œ˜K˜—šœœœ˜*Kšœ˜Kšœ œ˜K˜—Kšœ>˜>šœœœ˜+Kšœ˜Kšœ˜Kšœ˜—Kšœœ˜ šœ˜šœœœ˜Kšœœœ&œ˜Ušœ˜KšœA˜AKšœ˜Kšœ œ˜KšœF˜FKšœB˜BKšœœ˜&K˜—Kšœ˜—Kšœ˜—šœ ˜ šœœœ˜Kšœœœ&œ˜Ušœ˜Kšœ˜Kšœ œ˜KšœE˜EKšœA˜AKšœœœ˜=Kšœ˜—Kšœ˜—Kšœ˜—Kšœ œœ˜œ+˜lK˜Kšœœœ˜=Kšœœœ˜