{ import: Object } { import: Text } { import: TextBuffer } { import: Signal } { import: events } { import: TextLayout } { import: Range } { import: Shell } { import: Scroll } { import: Frame } CaretBox : Box () CaretBox glyph [ ^ Glyph withFont: Font default ucs4: 0 ] CaretBox kerningFor: nextBox [ ^extent x negated, 0 ] TextView : Box (model buffer controller selection caretBox hadjustment vadjustment) TextView new [ self := super new. self extent: 10, 10. self layout: ParagraphLayout. self foreground: ColorBlack. selection := 0 to: 0. caretBox := CaretBox new. caretBox extent: 2, 20. caretBox border: ColorBlue. buffer := TextArrayBuffer new. controller := TextController withView: self. hadjustment := Adjustment new. vadjustment := Adjustment new. buffer when: #onReplaced do: self >>> #onReplaced:. buffer contents: ''. self keyDown: controller. self buttonDown: controller. self motion: controller. self buttonUp: controller. ] TextView buffer [ ^buffer ] TextView selection [ ^selection ] TextView selection: aRange [ selection := aRange ] TextView font [ ^self propertyAt: #font ifAbsent: [ Font default ] ] TextView model [ ^model ] TextView model: anObject [ model := anObject. self textContents: anObject contents. ] TextView textContents [ ^buffer contents ] TextView textContents: aString [ buffer contents: aString. self selection: (0 to: 0). self updateCaret. ] TextView addScrollPane [ | pane | pane := ScrollPane new. pane hscrollBarPolicy: #never. pane add: self. hadjustment := pane hscrollBar model. vadjustment := pane vscrollBar model. ^ pane ] TextView save [ model ifNotNil: [ model contents: self textContents ]] TextView cancel [ model ifNotNil: [ self textContents: model contents ]] TextView onReplaced: diff [ [self notEmpty] whileTrue: [ contents removeLast ]. buffer doWithHead: [ :head :rows | | line | line := TextBox new. (buffer lineFrom: head) do: [:char | char = $\n ifTrue: [ char := 32 ]. char = $\r ifTrue: [ char := 32 ]. char = $\t ifTrue: [ char := 32 ]. line add: (GlyphBox withGlyph: (self font glyphAt: char))]. line layout: (self propertyAt: #paragraphLayout ifAbsent: [KerningLayout]). self add: line. ]. self updateCaret. self layoutContents. hadjustment pageSize: hadjustment pageSize outerSize: self extent x. vadjustment pageSize: vadjustment pageSize outerSize: self extent y. self damaged. ] TextView updateCaret [ | range caretLine | "caretLine is somewhat dirty way to know cursor position" self damaged. caretBox removeFromList. range := selection sorted. self doWithLineAndOffset: [ :line :char :x :y :offset | char background: ((range first <= offset and: [offset < range last]) ifTrue: [ ColorGray75 ] ifFalse: [ nil ]). offset = selection last ifTrue: [ caretBox extent: 2, char extent y. line add: caretBox inFrontOf: char. caretLine := line]]. selection last = buffer size ifTrue: [ self contents last addBack: caretBox. caretLine := self contents last ]. self layoutContents. caretLine ifNotNil: [vadjustment moveTo: (caretLine position y size: caretBox height)]. ] Adjustment moveTo: aRange [ aRange first < inner first ifTrue: [ ^self inner: (aRange first size: inner size) outer: outer ]. inner last < aRange last ifTrue: [ ^self inner: (aRange last - inner size size: inner size) outer: outer ]. ] TextView doWithLineAndOffset: aBlock [ | x y offset | x := y := offset := 0. self do: [ :line | line do: [ :char | char = caretBox ifFalse: [ aBlock value: line value: char value: x value: y value: offset. x := x + 1. offset := offset + 1 ]]. y := y + 1. x := 0 ]. ] TextView indexAtPoint: aPoint [ self doWithLineAndOffset: [ :line :char :x :y :offset | (char includesGlobalPoint: aPoint) ifTrue: [ ^offset ]. char globalPosition y > aPoint y ifTrue: [ ^offset - 1 ] ]. ^ buffer size. ] TextView getPosition: offset [ self doWithLineAndOffset: [ :line :char :x :y :i | offset = i ifTrue: [ ^x, y ]]. ^ (self contents last contents size - 1), (self contents size - 1) ] TextView getOffset: point [ "todo: Convert from physical coordinate in (cols, rows) to offset. (-1, y) means last line, and (x, -1) means end of the line. If the value is larger than size, maximum size is chosen." self doWithLineAndOffset: [ :line :char :x :y :offset | point = (x, y) ifTrue: [ ^offset ]. point y < y ifTrue: [ ^offset - 1]]. ^ buffer size. ] TextView selectionContents [ ^ buffer from: selection sorted first size: selection size ] "----------------" TextController : EventHandler (view isSelecting) TextController withView: aTextView [ self := self new. view := aTextView. isSelecting := false. ] TextController caret [ ^view selection last ] TextController keyDown :anEvent [ (anEvent altPressed and: [anEvent key = $i]) ifTrue: [^ self inspectIt]. (anEvent altPressed and: [anEvent key = $p]) ifTrue: [^ self printIt]. (anEvent altPressed and: [anEvent key = $d]) ifTrue: [^ self doIt]. (anEvent altPressed and: [anEvent key = $s]) ifTrue: [^ self save]. (anEvent altPressed and: [anEvent key = $b]) ifTrue: [^ self browseIt]. (anEvent altPressed and: [anEvent key = $l]) ifTrue: [^ self cancel]. anEvent key = KeyEvent UP ifTrue: [^ self up: anEvent]. anEvent key = KeyEvent DOWN ifTrue: [^ self down: anEvent]. anEvent key = KeyEvent BACKSPACE ifTrue: [^ self backspace ]. anEvent key = KeyEvent RIGHT ifTrue: [^ self right: anEvent]. anEvent key = KeyEvent LEFT ifTrue: [^ self left: anEvent]. anEvent ucs4 <= 0 ifTrue: [^anEvent]. anEvent ucs4 = $\r ifTrue: [^ self insert: $\n ]. anEvent ucs4 < 256 ifTrue: [^ self insert: anEvent ucs4 ]. ] TextController save [ view save ] TextController cancel [ view cancel ] TextController backspace [ self deleteSelection ifTrue: [ ^self ]. view selection: (self caret - 1 size: 0). view buffer replaceFrom: self caret size: 1 with: ''. ] TextController insert: anInteger [ self append: (String with: anInteger) ] TextController append: aString [ | caret | self deleteSelection. caret := self caret. view selection: (self caret + aString size size: 0). view buffer replaceFrom: caret size: 0 with: aString. ] TextController deleteSelection [ view selection size = 0 ifTrue: [ ^false ]. view buffer replaceFrom: view selection sorted first size: view selection sorted size with: ''. view selection: (view selection sorted first size: 0). view updateCaret. ^ true. ] TextController right: event [ self point: self caret + 1 event: event ] TextController left: event [ self point: self caret - 1 event: event ] TextController up: event [ | p offset | p := view getPosition: self caret. offset := view getOffset: p x, (0 max: p y - 1). self point: offset event: event. ] TextController down: event [ | p offset | p := view getPosition: self caret. offset := view getOffset: p x, (p y + 1). self point: offset event: event. ] TextController point: offset event: event [ offset := offset max: 0. offset := offset min: view buffer size. view selection: ((isSelecting or: [event notNil and: [event shiftPressed]]) ifTrue: [ view selection first to: offset ] ifFalse: [ offset to: offset ]). view updateCaret ] TextController buttonDown :event [ event button = ButtonEvent RIGHT ifTrue: [ | handler | event handler: event hand window. "*** HACK ***" handler := MenuHandler new label: 'print it' action: [:anEvent | self printIt ]; label: 'do it' action: [:anEvent | self doIt ]; label: 'inspect' action: [:anEvent | self inspectIt ]; label: 'workspace' action: [:anEvent | Window default openWorkspace ]. ^ handler buttonDown :event]. isSelecting := false. self point: (view indexAtPoint: event position) event: nil. isSelecting := true. ] TextController motion :event [ isSelecting ifTrue: [ self point: (view indexAtPoint: event position) event: nil]. ] TextController buttonUp :event [ isSelecting := false. ] TextController selectLine [ | pos offset head | view selection size > 0 ifTrue: [^self]. pos := view getPosition: self caret. offset := view getOffset: 0, pos y. head := self caret - pos x. view selection: (head to: (head + (view buffer lineFrom: offset) size)). view updateCaret. ] TextController evaluateSelection [ | input resultStream result | self selectLine. input := view selectionContents. resultStream := WriteStream on: (String new: 8). result := Shell new eval: input to: resultStream. " result := resultStream contents." isSelecting := false. ^ result ] TextController doIt [ self evaluateSelection ] TextController printIt [ | result | result := self evaluateSelection printString. self clearSelection. self append: result. view selection: (self caret - result size to: self caret). view updateCaret. ] TextController guessSelectedObject [ "Return the object if evaluated pointer looks an object, otherwize number" | result oop | result := self evaluateSelection. { if ((int)v_result & 0x10) v_oop= (oop) ((int) v_result >> 2); else v_oop= (oop) ((int) v_result >> 1); }. ^oop ] TextController inspectIt [ self guessSelectedObject inspect ] TextController browseIt [ self guessSelectedObject browse ] TextController clearSelection [ self point: self caret event: nil. isSelecting := false. ]