--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/EditTextView.st Thu May 08 10:30:56 2014 +0200
@@ -0,0 +1,8619 @@
+"
+ COPYRIGHT (c) 1989 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+'From Smalltalk/X, Version:6.2.3.0 on 18-02-2014 at 18:37:41' !
+
+"{ Package: 'stx:libwidg' }"
+
+TextView subclass:#EditTextView
+ instanceVariableNames:'cursorLine cursorVisibleLine cursorCol cursorShown
+ prevCursorState readOnly modifiedChannel fixedSize exceptionBlock
+ cursorFgColor cursorBgColor cursorNoFocusFgColor cursorType
+ cursorTypeNoFocus typeOfSelection lastAction replacing
+ showMatchingParenthesis hasKeyboardFocus acceptAction lockUpdates
+ tabMeansNextField autoIndent insertMode editMode trimBlankLines
+ wordWrap replacementWordSelectStyle acceptChannel acceptEnabled
+ st80Mode disableIfInvisible cursorMovementWhenUpdating learnMode
+ learnedMacro cursorLineHolder cursorColHolder tabRequiresControl
+ undoSupport lastStringFromReplaceForNextSearch
+ lastReplacementInfo completionSupport codeAspectHolder'
+ classVariableNames:'DefaultCursorForegroundColor DefaultCursorBackgroundColor
+ DefaultCursorType DefaultCursorNoFocusForegroundColor
+ DefaultCursorTypeNoFocus LastColumnNumberForSort Macros'
+ poolDictionaries:''
+ category:'Views-Text'
+!
+
+Object subclass:#EditAction
+ instanceVariableNames:'userFriendlyInfo'
+ classVariableNames:''
+ poolDictionaries:''
+ privateIn:EditTextView
+!
+
+EditTextView::EditAction subclass:#DeleteRange
+ instanceVariableNames:'line1 col1 line2 col2'
+ classVariableNames:''
+ poolDictionaries:''
+ privateIn:EditTextView
+!
+
+EditTextView::EditAction subclass:#DeleteCharacters
+ instanceVariableNames:'line col1 col2'
+ classVariableNames:''
+ poolDictionaries:''
+ privateIn:EditTextView
+!
+
+Object subclass:#EditMode
+ instanceVariableNames:''
+ classVariableNames:''
+ poolDictionaries:''
+ privateIn:EditTextView
+!
+
+EditTextView::EditMode subclass:#InsertAndSelectMode
+ instanceVariableNames:''
+ classVariableNames:''
+ poolDictionaries:''
+ privateIn:EditTextView::EditMode
+!
+
+EditTextView::EditMode subclass:#InsertMode
+ instanceVariableNames:''
+ classVariableNames:''
+ poolDictionaries:''
+ privateIn:EditTextView::EditMode
+!
+
+EditTextView::EditMode subclass:#OverwriteMode
+ instanceVariableNames:''
+ classVariableNames:'InsertMode OverwriteMode InsertAndSelectMode'
+ poolDictionaries:''
+ privateIn:EditTextView::EditMode
+!
+
+Query subclass:#ExecutingMacroQuery
+ instanceVariableNames:''
+ classVariableNames:''
+ poolDictionaries:''
+ privateIn:EditTextView
+!
+
+Object subclass:#LastReplacementInfo
+ instanceVariableNames:'lastReplacement lastStringToReplace lastReplaceWasMatch
+ lastReplaceIgnoredCase stillCollectingInput previousReplacements'
+ classVariableNames:''
+ poolDictionaries:''
+ privateIn:EditTextView
+!
+
+EditTextView::EditAction subclass:#PasteString
+ instanceVariableNames:'line col string selected'
+ classVariableNames:''
+ poolDictionaries:''
+ privateIn:EditTextView
+!
+
+EditTextView::EditAction subclass:#ReplaceCharacter
+ instanceVariableNames:'line col character'
+ classVariableNames:''
+ poolDictionaries:''
+ privateIn:EditTextView
+!
+
+EditTextView::EditAction subclass:#ReplaceCharacters
+ instanceVariableNames:'line col1 col2 characters'
+ classVariableNames:''
+ poolDictionaries:''
+ privateIn:EditTextView
+!
+
+EditTextView::EditAction subclass:#ReplaceContents
+ instanceVariableNames:'text'
+ classVariableNames:''
+ poolDictionaries:''
+ privateIn:EditTextView
+!
+
+EditTextView::EditAction subclass:#ReplaceLine
+ instanceVariableNames:'line text'
+ classVariableNames:''
+ poolDictionaries:''
+ privateIn:EditTextView
+!
+
+EditTextView::EditAction subclass:#ReplaceLines
+ instanceVariableNames:'line text'
+ classVariableNames:''
+ poolDictionaries:''
+ privateIn:EditTextView
+!
+
+!EditTextView class methodsFor:'documentation'!
+
+copyright
+"
+ COPYRIGHT (c) 1989 by Claus Gittinger
+ All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice. This software may not
+ be provided or otherwise made available to, or used by, any
+ other person. No title to or ownership of the software is
+ hereby transferred.
+"
+!
+
+documentation
+"
+ a view for editable text - adds editing functionality to TextView
+ Also, it adds accept functionality, and defines a new actionBlock:
+ acceptAction to be performed for accept
+
+ If used with a model, this is informed by sending it a changeMsg with
+ the current contents as argument.
+ (however, it is possible to define both changeMsg and acceptAction)
+
+ Please read the historic notice in the ListView class.
+
+ [Instance variables:]
+
+ cursorLine <Number> line where cursor sits (1..)
+
+ cursorVisibleLine <Number> visible line where cursor sits (1..nLinesShown)
+
+ cursorCol <Number> col where cursor sits (1..)
+
+ cursorShown <Boolean> true, if cursor is currently shown
+
+ readOnly <Boolean> true, if text may not be edited
+
+ modifiedChannel <ValueHolder> holding true, if text has been modified.
+ cleared on accept.
+
+ acceptChannel <ValueHolder> holding true, if text has been accepted.
+
+ fixedSize <Boolean> true, if no lines may be added/removed
+
+ exceptionBlock <Block> block to be evaluated when readonly text is about to be modified
+ if it returns true, the modification will be done anyway.
+ if it returns anything else, the modification is not done.
+
+ cursorFgColor <Color> color used for cursor drawing
+ cursorBgColor <Color> color used for cursor drawing
+
+ cursorType <Symbol> how the cursor is drawn; currently implemented
+ are #none, #block (solid-block cursor), #ibeam
+ (vertical bar at insertion point)
+ and #caret (caret below insertion-point).
+ see cursorType: for an up-to-date list.
+
+ cursorTypeNoFocus <Symbol> like above, if view has no focus
+ nil means: hide the cursor.
+
+ undoAction <Block> block which undoes last cut, paste or replace
+ (not yet fully implemented)
+
+ typeOfSelection <Symbol> #paste, if selection created by paste, nil otherwise
+ this affects the next keyPress: if #paste it does not
+ replace; otherwise it replaces the selection.
+
+ lastCut <String> last cut or replaced string
+
+ lastReplacementInfo <LastReplacementInfo> holds the information about the last replace action
+ lastStringToReplace is the string to be replaced by lastReplacement
+ lastReplacement is the string to replace lastStringToReplace
+
+ lastStringFromReplaceForNextSearch <String> string to be taken be the next search action
+ (cleared after a new selection)
+
+ replacing <Boolean> true if entered characters replace last selection
+
+ showMatchingParenthesis <Boolean> if true, shows matching parenthesis
+ when entering one; this is the default.
+
+ hasKeyboardFocus <Boolean> true if this view has the focus
+
+ acceptAction <Block> accept action - evaluated passing the contents as
+ argument
+
+ tabMeansNextField <Boolean> if true, Tab is ignored as input and shifts keyboard
+ focus to the next field. For editTextViews, this is false
+ by default (i.e. tabs can be entered into the text).
+ For some subclasses (inputFields), this may be true.
+
+ trimBlankLines <Boolean> if true, trailing blanks are
+ removed when editing.
+ Default is true.
+
+ wordWrap <Boolean> Currently not used.
+
+ lockUpdates <Boolean> internal, private
+
+ prevCursorState <Boolean> temporary, private
+
+ cursorMovementWhenUpdating
+ <Symbol> defines where the cursor is to be positioned if the
+ model changes its value by some outside activity
+ (i.e. not by user input into the field).
+ Can be one of:
+ #keep / nil -> stay where it was
+ #endOfText -> cursor to the end
+ #endOfLine -> stay in the line, but move to end
+ #beginOfText -> cursor to the beginning
+ #beginOfLine -> stay in the line, but move to begin
+ The default is #beginOfText
+
+
+
+ dropTarget <DropTarget|nil> drop operation descriptor or nil (drop disabled)
+
+
+ userPreference values:
+ userPreferences.st80EditMode
+ <Boolean> if true, cursor positioning is
+ done as in vi or ST80; i.e.
+ wysiwyg mode is somewhat relaxed,
+ in that the cursor cannot be
+ positioned behind a lines end.
+ This is not yet completely implemented.
+ used globals:
+
+ DeleteHistory <Text> last 1000 lines of deleted text
+ (but only if this variable exists already)
+
+ [styleSheet parameters:]
+
+ textCursorForegroundColor <Color> cursor fg color; default: text background
+ textCursorBackgroundColor <Color> cursor bg color; default: text foreground
+ textCursorNoFocusForegroundColor
+ <Color> cursor fg color if no focus; default: cursor fg color
+ textCursorType <Symbol> cursor type; default: #block
+
+ [author:]
+ Claus Gittinger
+
+ [see also:]
+ CodeView Workspace TextView ListView
+ EditField
+"
+!
+
+examples
+"
+ non MVC operation:
+
+ basic setup:
+ [exBegin]
+ |top textView|
+
+ top := StandardSystemView new.
+ top extent:300@200.
+
+ textView := EditTextView new.
+ textView origin:0.0 @ 0.0 corner:1.0 @ 1.0.
+ top addSubView:textView.
+
+ textView contents:('/etc/hosts' asFilename contentsOfEntireFile).
+
+ top open.
+ [exEnd]
+
+
+ with vertical scrollbar:
+ [exBegin]
+ |top scrollView textView|
+
+ top := StandardSystemView new.
+ top extent:300@200.
+
+ scrollView := ScrollableView for:EditTextView.
+ textView := scrollView scrolledView.
+ scrollView origin:0.0 @ 0.0 corner:1.0 @ 1.0.
+ top addSubView:scrollView.
+
+ textView contents:('/etc/hosts' asFilename contentsOfEntireFile).
+
+ top open.
+ [exEnd]
+
+
+ with horizontal & vertical scrollbars:
+ [exBegin]
+ |top scrollView textView|
+
+ top := StandardSystemView new.
+ top extent:300@200.
+
+ scrollView := HVScrollableView for:EditTextView.
+ textView := scrollView scrolledView.
+ scrollView origin:0.0 @ 0.0 corner:1.0 @ 1.0.
+ top addSubView:scrollView.
+
+ textView contents:('/etc/hosts' asFilename contentsOfEntireFile).
+
+ top open.
+ [exEnd]
+
+
+ set the action for accept:
+ [exBegin]
+ |top textView|
+
+ top := StandardSystemView new.
+ top extent:300@200.
+
+ textView := EditTextView new.
+ textView origin:0.0 @ 0.0 corner:1.0 @ 1.0.
+ top addSubView:textView.
+
+ textView contents:('/etc/hosts' asFilename contentsOfEntireFile).
+ textView acceptAction:[:contents |
+ Transcript showCR:'will not overwrite the file with:'.
+ Transcript showCR:contents asString
+ ].
+ top open.
+ [exEnd]
+
+
+
+ non-string (text) items:
+ [exBegin]
+ |top textView list|
+
+ list := '/etc/hosts' asFilename contentsOfEntireFile asStringCollection.
+ 1 to:list size by:2 do:[:nr |
+ list at:nr put:(Text string:(list at:nr)
+ emphasis:(Array with:#bold with:(#color->Color red)))
+ ].
+
+ top := StandardSystemView new.
+ top extent:300@200.
+
+ textView := EditTextView new.
+ textView origin:0.0 @ 0.0 corner:1.0 @ 1.0.
+ top addSubView:textView.
+
+ textView contents:list.
+ top open.
+ [exEnd]
+
+
+
+ MVC operation:
+ (the examples model here is a plug simulating a real model;
+ real world applications would not use a plug ..)
+ [exBegin]
+ |top textView model|
+
+ model := Plug new.
+ model respondTo:#accepted:
+ with:[:newContents |
+ Transcript showCR:'will not overwrite the file with:'.
+ Transcript showCR:newContents asString
+ ].
+ model respondTo:#getList
+ with:['/etc/hosts' asFilename contentsOfEntireFile].
+
+
+ top := StandardSystemView new.
+ top extent:300@200.
+
+ textView := EditTextView new.
+ textView origin:0.0 @ 0.0 corner:1.0 @ 1.0.
+ top addSubView:textView.
+
+ textView listMessage:#getList;
+ model:model;
+ changeMessage:#accepted:;
+ aspect:#list.
+ top open.
+ [exEnd]
+
+
+ two textViews on the same model:
+ [exBegin]
+ |top1 textView1 top2 textView2 model currentContents|
+
+ model := Plug new.
+ model respondTo:#accepted:
+ with:[:newContents |
+ Transcript showCR:'accepted:'.
+ Transcript showCR:newContents asString.
+ currentContents := newContents.
+ model changed:#contents
+ ].
+ model respondTo:#getList
+ with:[Transcript showCR:'query'.
+ currentContents].
+
+
+ top1 := StandardSystemView new.
+ top1 extent:300@200.
+
+ textView1 := EditTextView new.
+ textView1 origin:0.0 @ 0.0 corner:1.0 @ 1.0.
+ top1 addSubView:textView1.
+
+ textView1 listMessage:#getList;
+ model:model;
+ aspect:#contents;
+ changeMessage:#accepted:.
+ top1 open.
+
+ top2 := StandardSystemView new.
+ top2 extent:300@200.
+
+ textView2 := EditTextView new.
+ textView2 origin:0.0 @ 0.0 corner:1.0 @ 1.0.
+ top2 addSubView:textView2.
+
+ textView2 listMessage:#getList;
+ model:model;
+ aspect:#contents;
+ changeMessage:#accepted:.
+ top2 open.
+ [exEnd]
+"
+! !
+
+!EditTextView class methodsFor:'defaults'!
+
+defaultCompletionSupportClass
+ ^ nil
+
+ "Created: / 26-09-2013 / 17:59:27 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+!
+
+st80Mode
+ "return true, if the st80 editing mode is turned on.
+ This setting affects the behavior of the cursor, when positioned
+ beyond the end of a line or the end of the text."
+
+ ^ UserPreferences current st80EditMode
+
+ "
+ EditTextView st80Mode:true
+ EditTextView st80Mode:false
+ "
+
+ "Modified: / 16.1.1998 / 22:54:57 / cg"
+!
+
+st80Mode:aBoolean
+ "turns on/off st80 behavior, where the cursor cannot be positioned
+ beyond the end of a line or the last line"
+
+ UserPreferences current st80EditMode:aBoolean.
+
+ "
+ EditTextView st80Mode:true
+ EditTextView st80Mode:false
+ "
+
+ "Modified: / 16.1.1998 / 22:55:19 / cg"
+!
+
+updateStyleCache
+ "extract values from the styleSheet and cache them in class variables"
+
+ <resource: #style (#'textCursor.foregroundColor' #'textCursor.backgroundColor'
+ #'textCursor.noFocusForegroundColor'
+ #'textCursor.type'
+ #'textCursor.typeNoFocus'
+ #'editText.st80Mode')>
+
+ DefaultCursorForegroundColor := StyleSheet colorAt:'textCursor.foregroundColor'.
+ DefaultCursorBackgroundColor := StyleSheet colorAt:'textCursor.backgroundColor'.
+ DefaultCursorNoFocusForegroundColor := StyleSheet colorAt:'textCursor.noFocusForegroundColor'.
+ DefaultCursorType := StyleSheet at:'textCursor.type' default:#block.
+ DefaultCursorTypeNoFocus := StyleSheet at:'textCursor.typeNoFocus'.
+
+ "
+ self updateStyleCache
+ "
+
+ "Modified: / 20.5.1998 / 04:27:41 / cg"
+! !
+
+!EditTextView class methodsFor:'specs'!
+
+searchReplaceDialogSpec
+ "This resource specification was automatically generated
+ by the UIPainter of ST/X."
+
+ "Do not manually edit this!! If it is corrupted,
+ the UIPainter may not be able to read the specification."
+
+ "
+ UIPainter new openOnClass:DAPASX::ProjectEditorTextView andSelector:#searchReplaceDialogSpec
+ "
+
+ <resource: #canvas>
+
+ ^
+ #(FullSpec
+ name: searchReplaceDialogSpec
+ window:
+ (WindowSpec
+ label: 'String Search and Replace'
+ name: 'String Search and Replace'
+ min: (Point 283 196)
+ max: (Point 283 196)
+ bounds: (Rectangle 0 0 279 192)
+ )
+ component:
+ (SpecCollection
+ collection: (
+ (LabelSpec
+ label: 'Search Pattern:'
+ name: 'label'
+ layout: (LayoutFrame 1 0.0 3 0 -1 1.0 20 0)
+ level: 0
+ translateLabel: true
+ adjust: left
+ )
+ (ComboBoxSpec
+ name: 'patternComboBox'
+ layout: (LayoutFrame 3 0.0 26 0 -3 1.0 48 0)
+ tabable: true
+ model: searchPattern
+ immediateAccept: false
+ acceptOnLeave: true
+ acceptOnReturn: true
+ acceptOnTab: true
+ acceptOnLostFocus: true
+ acceptOnPointerLeave: false
+ autoSelectInitialText: true
+ comboList: patternList
+ )
+ (ComboBoxSpec
+ name: 'replaceComboBox'
+ layout: (LayoutFrame 3 0.0 76 0 -3 1.0 98 0)
+ tabable: true
+ model: replacePattern
+ immediateAccept: false
+ acceptOnLeave: true
+ acceptOnReturn: true
+ acceptOnTab: true
+ acceptOnLostFocus: true
+ acceptOnPointerLeave: false
+ autoSelectInitialText: true
+ comboList: patternList
+ )
+ (CheckBoxSpec
+ label: 'Ignore Case'
+ name: 'ignoreCaseCheckBox'
+ layout: (LayoutFrame 3 0.0 107 0 -3 1.0 130 0)
+ level: 0
+ tabable: true
+ model: ignoreCase
+ translateLabel: true
+ )
+ (VariableVerticalPanelSpec
+ name: 'VariableVerticalPanel1'
+ layout: (LayoutFrame 0 0 -64 1 0 1 -4 1)
+ component:
+ (SpecCollection
+ collection: (
+ (HorizontalPanelViewSpec
+ name: 'HorizontalPanel1'
+ level: 0
+ horizontalLayout: fitSpace
+ verticalLayout: center
+ horizontalSpace: 3
+ verticalSpace: 3
+ ignoreInvisibleComponents: true
+ reverseOrderIfOKAtLeft: true
+ component:
+ (SpecCollection
+ collection: (
+ (ActionButtonSpec
+ label: 'Replace'
+ name: 'replaceButton'
+ level: 2
+ translateLabel: true
+ tabable: true
+ model: replaceAction
+ extent: (Point 134 21)
+ )
+ (ActionButtonSpec
+ label: 'Replace All'
+ name: 'replaceAllButton'
+ level: 2
+ borderWidth: 1
+ translateLabel: true
+ tabable: true
+ model: replaceAllAction
+ extent: (Point 134 21)
+ )
+ )
+
+ )
+ )
+ (HorizontalPanelViewSpec
+ name: 'horizontalPanelView'
+ level: 0
+ horizontalLayout: fitSpace
+ verticalLayout: center
+ horizontalSpace: 3
+ verticalSpace: 3
+ ignoreInvisibleComponents: true
+ reverseOrderIfOKAtLeft: true
+ component:
+ (SpecCollection
+ collection: (
+ (ActionButtonSpec
+ label: 'Cancel'
+ name: 'cancelButton'
+ level: 2
+ translateLabel: true
+ tabable: true
+ model: cancel
+ extent: (Point 88 21)
+ )
+ (ActionButtonSpec
+ label: 'Prev'
+ name: 'prevButton'
+ level: 2
+ translateLabel: true
+ tabable: true
+ model: prevAction
+ extent: (Point 89 21)
+ )
+ (ActionButtonSpec
+ label: 'Next'
+ name: 'nextButton'
+ level: 2
+ borderWidth: 1
+ translateLabel: true
+ tabable: true
+ model: nextAction
+ isDefault: true
+ extent: (Point 88 21)
+ )
+ )
+
+ )
+ )
+ )
+
+ )
+ handles: (Any 0.5 1.0)
+ )
+ (LabelSpec
+ label: 'Replace By:'
+ name: 'ReplaceLabel'
+ layout: (LayoutFrame 1 0.0 53 0 -1 1.0 70 0)
+ level: 0
+ translateLabel: true
+ adjust: left
+ )
+ )
+
+ )
+ )
+
+ "Modified: / 11-10-2006 / 21:05:09 / cg"
+! !
+
+!EditTextView methodsFor:'Compatibility-ST80'!
+
+autoAccept:aBoolean
+ "ignored for now"
+
+ "Created: / 5.6.1998 / 15:30:32 / cg "
+!
+
+continuousAccept:aBoolean
+ "ignored for now"
+
+ "Created: / 19.6.1998 / 00:03:49 / cg"
+!
+
+cutSelection
+ self cut
+
+ "Created: / 31.10.1997 / 03:29:50 / cg"
+!
+
+deselect
+ "remove the selection"
+
+ ^ self unselect
+
+ "Created: / 19.6.1998 / 02:41:54 / cg"
+!
+
+enabled:aBoolean
+
+ self readOnly:aBoolean not
+
+ "Created: / 30.3.1999 / 15:10:23 / stefan"
+ "Modified: / 30.3.1999 / 15:10:53 / stefan"
+!
+
+find:pattern
+ self searchFwd:pattern ifAbsent:nil
+
+ "Created: / 29.1.1999 / 19:09:42 / cg"
+ "Modified: / 29.1.1999 / 19:10:12 / cg"
+!
+
+insert:aString at:aCharacterPosition
+ "insert a string at aCharacterPosition."
+
+ |line col|
+
+ line := self lineOfCharacterPosition:aCharacterPosition.
+ col := aCharacterPosition - (self characterPositionOfLine:line col:1) + 1.
+ col < 1 ifTrue:[
+ col := 1
+ ].
+ self insertString:aString atLine:line col:col.
+
+ "
+ |top v|
+
+ top := StandardSystemView new.
+ top extent:300@300.
+ v := EditTextView origin:0.0@0.0 corner:1.0@1.0 in:top.
+ top openAndWait.
+ v contents:'1234567890\1234567890\1234567890\' withCRs.
+ v insert:'<- hello there' at:5.
+ "
+
+ "Modified: / 5.4.1998 / 17:20:08 / cg"
+!
+
+insertAndSelect:aString at:aCharacterPosition
+ "insert a selected string at aCharacterPosition."
+
+ |line col|
+
+ line := self lineOfCharacterPosition:aCharacterPosition.
+ col := (aCharacterPosition - (self characterPositionOfLine:line col:1) + 1) max:1.
+ self insertString:aString atLine:line col:col.
+ self selectFromLine:line col:col toLine:line col:col + aString size - 1
+ "
+ |v|
+
+ v := EditTextView new openAndWait.
+ v contents:'1234567890\1234567890\1234567890\' withCRs.
+ v insertAndSelect:'<- hello there' at:5.
+ "
+!
+
+pasteSelection
+ self paste
+
+ "Created: / 31.10.1997 / 03:28:53 / cg"
+!
+
+replaceSelectionWith:aString
+ ^ self replaceSelectionBy:aString
+
+ "Created: / 19.6.1998 / 02:42:32 / cg"
+!
+
+selectAt:pos
+ "move the cursor before cursorPosition."
+
+ self cursorToCharacterPosition:pos
+
+ "Modified: / 19.6.1998 / 02:41:28 / cg"
+ "Created: / 19.6.1998 / 02:43:39 / cg"
+!
+
+textHasChanged
+ ^ self modified
+
+ "Created: / 19.6.1998 / 00:09:43 / cg"
+!
+
+textHasChanged:aBoolean
+ "ST-80 compatibility: set/clear the modified flag."
+
+ self modified:aBoolean
+
+ "Created: / 5.2.2000 / 17:07:59 / cg"
+! !
+
+!EditTextView methodsFor:'accessing'!
+
+codeAspect
+ | codeAspect app |
+
+ codeAspect := codeAspectHolder value.
+ codeAspect notNil ifTrue:[^codeAspect].
+ self editedMethod notNil ifTrue:[^SyntaxHighlighter codeAspectMethod].
+
+ "/ Applications should set it explictly, however, to make it behavinh like
+ "/ CodeView2, I kept fetching code here for now.
+ ^((app := self topView application) notNil and:[app respondsTo: #codeAspect])
+ ifTrue:[app codeAspect]
+ ifFalse:[nil]
+
+ "Created: / 27-09-2013 / 09:53:40 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+!
+
+codeAspect: aSymbol
+ codeAspectHolder value: aSymbol
+
+ "Created: / 27-09-2013 / 09:50:52 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+!
+
+completionSupport
+ ^ completionSupport
+!
+
+completionSupport:anEditTextViewCompletionSupport
+ completionSupport := anEditTextViewCompletionSupport.
+!
+
+completionSupportClass
+ ^ self class defaultCompletionSupportClass
+
+ "Created: / 26-09-2013 / 17:54:24 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+!
+
+editedClass
+ |cm|
+
+ cm := self editedMethodOrClass.
+ cm isBehavior ifTrue:[^ cm].
+ cm isMethod ifTrue:[^ cm mclass].
+ ^ nil
+!
+
+editedLanguage
+ ^ nil
+
+ "Created: / 18-09-2013 / 14:16:38 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+!
+
+editedLanguage: aProgrammingLanguage
+ "Sets the edited language. Only defined here to make it polymorph with Workspace"
+
+ "Created: / 27-09-2013 / 10:15:35 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+!
+
+editedMethod
+ |cm|
+
+ cm := self editedMethodOrClass.
+ cm isMethod ifTrue:[^ cm].
+ cm isBehavior ifTrue:[^ nil].
+ ^ nil
+!
+
+editedMethodOrClass
+ ^ nil
+!
+
+editedMethodOrClass: methodOrClass
+ "Sets the edited method or class. Only defined here to make it polymorph with Workspace"
+
+ "Created: / 27-09-2013 / 10:10:14 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+!
+
+hasSelectionOrTextInCursorLine
+ ^ (self selectionOrTextOfCursorLine:false) notNil
+!
+
+selectionOrTextOfCursorLine
+ ^ self selectionOrTextOfCursorLine:true
+!
+
+selectionOrTextOfCursorLine:doSelect
+ |sel lNr line|
+
+ sel := self selectionAsString.
+ sel notNil ifTrue:[^ sel].
+
+ lNr := self cursorLine.
+ line := self listAt:lNr.
+ line notEmptyOrNil ifTrue:[
+ doSelect ifTrue:[
+ self selectLine:lNr.
+ ].
+ ^ line
+ ].
+
+ ^ nil
+! !
+
+!EditTextView methodsFor:'accessing-behavior'!
+
+acceptAction
+ "return the action to be performed on accept (or nil)"
+
+ ^ acceptAction
+!
+
+acceptAction:aBlock
+ "set the action to be performed on accept"
+
+ acceptAction := aBlock
+!
+
+acceptChannel
+ "return the valueHolder holding true if text was accepted.
+ By placing a true into this channel, an accept can also be forced."
+
+ ^ acceptChannel
+
+ "Modified: / 30.1.1998 / 14:17:11 / cg"
+!
+
+acceptChannel:aValueHolder
+ "set the valueHolder holding true if text was accepted.
+ By placing a true into this channel, an accept can also be forced."
+
+ |prev|
+
+ prev := acceptChannel.
+ acceptChannel := aValueHolder.
+ self setupChannel:aValueHolder for:nil withOld:prev
+
+ "Created: / 30.1.1998 / 14:51:09 / cg"
+!
+
+acceptEnabled:aBoolean
+ "enable/disable accept. This greys the corresponding item in the menu"
+
+ acceptEnabled := aBoolean
+
+ "Created: 7.3.1997 / 11:04:34 / cg"
+!
+
+accepted
+ "return true if text was accepted"
+
+ ^ acceptChannel value
+
+ "Created: 14.2.1997 / 16:43:46 / cg"
+!
+
+accepted:aBoolean
+ "set/clear the accepted flag.
+ This may force my current contents to be placed into my model."
+
+ acceptChannel value:aBoolean.
+
+ "Created: / 14.2.1997 / 16:44:01 / cg"
+ "Modified: / 30.1.1998 / 14:20:15 / cg"
+!
+
+autoIndent:aBoolean
+ autoIndent := aBoolean
+
+ "Created: 5.3.1996 / 14:37:50 / cg"
+!
+
+codeAspectHolder
+ ^ codeAspectHolder
+!
+
+codeAspectHolder:something
+ codeAspectHolder := something.
+!
+
+cursorMovementWhenUpdating
+ "return what is be done with the cursor,
+ when I get a new text (via the model or the #contents/#list)
+ Allowed arguments are:
+ #keep / nil -> stay where it was
+ #endOfText -> position cursor to the end
+ #beginOfText -> position cursor to the beginning
+ #endOfLine -> position cursor to the current lines end
+ #beginOfLine -> position cursor to the current lines start
+ The default is #beginOfText.
+ This may be useful for fields which get new values assigned from
+ the program (i.e. not from the user)"
+
+ ^ cursorMovementWhenUpdating
+
+ "Modified: 16.12.1995 / 16:27:55 / cg"
+!
+
+cursorMovementWhenUpdating:aSymbolOrNil
+ "define what should be done with the cursor,
+ when I get a new text (via the model or the #contents/#list)
+ Allowed arguments are:
+ #keep / nil -> stay where it was
+ #endOfText -> position cursor to the end
+ #beginOfText -> position cursor to the beginning
+ #endOfLine -> position cursor to the current lines end
+ #beginOfLine -> position cursor to the current lines start
+ The default is #beginOfText.
+ This may be useful for fields which get new values assigned from
+ the program (i.e. not from the user)"
+
+ cursorMovementWhenUpdating := aSymbolOrNil
+
+ "Modified: 16.12.1995 / 16:27:55 / cg"
+!
+
+disableIfInvisible:aBoolean
+ disableIfInvisible := aBoolean
+!
+
+dontReplaceSelectionOnInput
+ "remember that the current selection was created by a paste operation
+ (as opposed to an explicit selection by the user).
+ This selection will not be replaced by followup user input,
+ so multiple pastes will be possible."
+
+ typeOfSelection := #paste
+!
+
+editModeHolder
+ ^ editMode.
+!
+
+editModeInsert
+ editMode value:EditMode insertMode
+!
+
+editModeInsertAndSelect
+ editMode value:EditMode insertAndSelectMode
+!
+
+editModeOverwrite
+ editMode value:EditMode overwriteMode
+!
+
+exceptionBlock:aBlock
+ "define the action to be triggered when user tries to modify
+ readonly text"
+
+ exceptionBlock := aBlock
+!
+
+fixedSize
+ "make the texts size fixed (no lines may be added).
+ OBSOLETE: use readOnly"
+
+ <resource:#obsolete>
+
+ |menu|
+
+ self obsoleteMethodWarning:'use #readOnly:'.
+ readOnly == true ifFalse:[
+ readOnly := true.
+ (menu := self middleButtonMenu) notNil ifTrue:[
+ menu disableAll:#(cut paste replace indent)
+ ]
+ ]
+
+ "Modified: 14.2.1997 / 17:35:24 / cg"
+!
+
+insertMode:aBoolean
+ editMode value:(aBoolean ifTrue:[EditMode insertMode] ifFalse:[EditMode overwriteMode])
+
+ "Created: 6.3.1996 / 12:24:05 / cg"
+!
+
+insertModeHolder
+ ^ BlockValue
+ with:[:m | m isInsertMode]
+ argument:(editMode).
+
+ "Modified: / 08-03-2007 / 22:58:37 / cg"
+!
+
+isInInsertMode
+ ^ editMode value isInsertMode
+!
+
+isReadOnly
+ "return true, if the text is readonly."
+
+ ^ readOnly value
+
+ "Modified: 14.2.1997 / 17:35:56 / cg"
+!
+
+modeLabelHolder
+ "a valueHolder, which contains 'L' (learnMode), I (insertMode) or empty"
+
+ ^ BlockValue
+ with:[:e :l |
+ self isReadOnly ifTrue:[
+ ''
+ ] ifFalse:[
+ l ifTrue:[ 'L' allBold colorizeAllWith:Color red]
+ ifFalse:[ e infoPrintString]]]
+ argument:(self editModeHolder)
+ argument:(self learnModeHolder).
+
+ "Modified: / 08-03-2007 / 22:58:59 / cg"
+!
+
+modified
+ "return true if text was modified"
+
+ ^ modifiedChannel value
+!
+
+modified:aBoolean
+ "set/clear the modified flag"
+
+ modifiedChannel value:aBoolean
+
+ "Modified: 14.2.1997 / 16:44:05 / cg"
+!
+
+modifiedChannel
+ "return the valueHolder holding true if text was modified"
+
+ ^ modifiedChannel
+!
+
+modifiedChannel:aValueHolder
+ "set the valueHolder holding true if text was modified"
+
+ |prev|
+
+ prev := modifiedChannel.
+ modifiedChannel := aValueHolder.
+ self setupChannel:aValueHolder for:nil withOld:prev
+
+ "Created: / 30.1.1998 / 14:51:32 / cg"
+!
+
+readOnly
+ "make the text readonly.
+ Obsolete because it is obfuscating (looks like a getter)
+ - use #readOnly:"
+
+ <resource:#obsolete>
+
+ self obsoleteMethodWarning:'use #readOnly:'.
+ self readOnly:true.
+
+ "Modified: / 14-02-1997 / 17:35:56 / cg"
+ "Modified (comment): / 02-08-2013 / 16:46:57 / cg"
+!
+
+readOnly:aBoolean
+ "make the text readonly (aBoolean == true) or writable (aBoolean == false).
+ The argument may also be a valueHolder."
+
+ readOnly := aBoolean
+
+ "Created: 14.2.1997 / 17:35:39 / cg"
+!
+
+reallyModifiedChannel
+ "return the valueHolder holding true if text was really modified.
+ For compatibility with views which use the modified flag for syntax highlighting."
+
+ ^ self modifiedChannel
+!
+
+st80Mode:aBoolean
+ "set/clear the st80Mode flag.
+ If on, the cursor wraps at the line end (like in vi or st80);
+ if off, we have the Rand-editor behavior (random access)"
+
+ st80Mode := aBoolean
+
+ "Created: / 09-11-2010 / 13:55:50 / cg"
+!
+
+tabMeansNextField:aBoolean
+ "set/clear tabbing to the next field.
+ If true, Tab is ignored and shifts the keyboard focus.
+ If false, tabs can be entered into the text.
+ The default is true for editTextView, false for single-line
+ input fields."
+
+ tabMeansNextField := aBoolean
+!
+
+tabRequiresControl
+ "returns true, if a focus tabbing requires a control-key to be pressed.
+ The default is true for editTextView, false for other widgets,
+ to allow for easier text entry"
+
+ ^ tabRequiresControl
+!
+
+tabRequiresControl:aBoolean
+ "controls if a focus tabbing requires a control-key to be pressed.
+ The default is true for editTextView, false for other widgets,
+ to allow for easier text entry"
+
+ tabRequiresControl := aBoolean
+! !
+
+!EditTextView methodsFor:'accessing-contents'!
+
+at:lineNr basicPut:aLine
+ "change a line without change notification"
+
+ (self at:lineNr) = aLine ifFalse:[
+ super at:lineNr put:aLine.
+ ].
+!
+
+at:lineNr put:aLine
+ (self at:lineNr) = aLine ifFalse:[
+ super at:lineNr put:aLine.
+ self textChanged
+ ].
+!
+
+characterAfterCursor
+ "return the character one after the cursor - space if beyond line."
+
+ ^ self characterAtLine:cursorLine col:cursorCol+1
+!
+
+characterBeforeCursor
+ "return the character to the left of cursor - space if beyond line, nil if at the beginning."
+
+ cursorCol == 1 ifTrue:[^ nil].
+
+ ^ self characterAtLine:cursorLine col:cursorCol-1
+
+ "Created: / 17.6.1998 / 15:16:41 / cg"
+!
+
+characterUnderCursor
+ "return the character under the cursor - space if beyond line.
+ For non-block cursors, this is the character immediately to the right
+ of the insertion-bar or caret."
+
+ ^ self characterAtLine:cursorLine col:cursorCol
+!
+
+contents
+ "return the contents as a String or Text (i.e. with emphasis)"
+
+ list isNil ifTrue:[^ ''].
+ self removeTrailingBlankLines.
+ ^ super contents.
+
+ "Modified: / 04-07-2006 / 19:22:32 / fm"
+!
+
+contents:something
+ self contents:something keepUndoHistory:false.
+!
+
+contents:something keepUndoHistory:keepUndoHistory
+ super contents:something.
+ keepUndoHistory ifFalse:[
+ undoSupport resetHistories.
+ ].
+!
+
+contentsAsString
+ "return the contents as a String (i.e. without emphasis)"
+
+ list isNil ifTrue:[^ ''].
+ self removeTrailingBlankLines.
+ ^ (list collect:[:each | each isNil ifTrue:['']
+ ifFalse:[each string]
+ ]) asStringWithCRs
+!
+
+cursorCol
+ "return the cursors col (1..).
+ This is the absolute col; NOT the visible col"
+
+ ^ cursorCol
+!
+
+cursorColHolder
+ "return a valueHolder for the cursors column (1..)."
+
+ ^ cursorColHolder
+!
+
+cursorLine
+ "return the cursors line (1..).
+ This is the absolute line; NOT the visible line"
+
+ ^ cursorLine
+!
+
+cursorLineHolder
+ "return a valueHolder for the cursors line (1..).
+ This is the absolute line; NOT the visible line"
+
+ ^ cursorLineHolder
+!
+
+lineStringBeforeCursor
+ "return the line's string before the cursor.
+ Pad with spaces up to the cursor position if beyond the end of line"
+
+ |line|
+
+ line := ((self at:cursorLine) ? '') string.
+ line size < (cursorCol-1) ifTrue:[
+ ^ line paddedTo:(cursorCol-1)
+ ].
+ ^ line copyTo:(cursorCol-1)
+!
+
+list:something
+ "position cursor home when setting contents"
+
+ |prevCursorLine prevCursorCol|
+
+ prevCursorLine := cursorLine.
+ prevCursorCol := cursorCol.
+
+ super list:something.
+
+ (cursorMovementWhenUpdating == #endOfText
+ or:[cursorMovementWhenUpdating == #end]) ifTrue:[
+ ^ self cursorToEndOfText
+ ].
+
+ (cursorMovementWhenUpdating == #endOfLine) ifTrue:[
+ ^ self cursorLine:prevCursorLine col:(self listAt:cursorLine) size + 1.
+ ].
+
+ (cursorMovementWhenUpdating == #beginOfText
+ or:[cursorMovementWhenUpdating == #begin]) ifTrue:[
+ ^ self cursorHome
+ ].
+ (cursorMovementWhenUpdating == #beginOfLine) ifTrue:[
+ ^ self cursorLine:prevCursorLine col:1.
+ ].
+
+ "/ default: stay where it was
+ "/ self cursorLine:prevCursorLine col:prevCursorCol.
+!
+
+setContents:something
+ |selType|
+
+ undoSupport resetHistories.
+
+ selType := typeOfSelection.
+ super setContents:something.
+ typeOfSelection := selType.
+
+ "Created: / 31.3.1998 / 23:35:06 / cg"
+! !
+
+!EditTextView methodsFor:'accessing-dimensions'!
+
+absoluteXOfPosition:positionInText
+ |accumulatedX container|
+
+ accumulatedX := 0.
+ container := self.
+ [ container notNil ] whileTrue:[
+ accumulatedX := accumulatedX + container origin x.
+ container := container isTopView ifFalse:[
+ container container
+ ] ifTrue:[ nil ].
+ ].
+ ^ (self xOfPosition:positionInText) + accumulatedX
+
+ "Created: / 16-02-2010 / 10:05:19 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+!
+
+absoluteYOfCursor
+
+ | accumulatedY container |
+ accumulatedY := 0.
+ container := self.
+ [ container notNil ] whileTrue:[
+ accumulatedY := accumulatedY + container origin y.
+ container := container isTopView
+ ifFalse:[container container]
+ ifTrue:[nil].
+ ].
+ ^(self yOfCursor) + accumulatedY
+
+ "Created: / 27-05-2005 / 07:45:53 / janfrog"
+ "Modified: / 27-05-2005 / 23:03:40 / janfrog"
+!
+
+xOfCursor
+ |point|
+
+ cursorVisibleLine isNil ifTrue:[
+ "/ take the end of the selection, if any
+ (selectionStartLine notNil
+ and:[ self listLineIsVisible:selectionEndLine ])
+ ifTrue:[
+ ^ self xOfCol:selectionEndCol inVisibleLine:selectionEndLine.
+ ].
+
+"/ point := device
+"/ translatePoint:(device pointerPosition)
+"/ fromView:nil
+"/ toView:self.
+"/ ((self bounds) containsPoint:point) ifTrue:[
+"/ ^ point x
+"/ ].
+"/ ^ 0
+ ^ nil
+ ].
+ ^self xOfCol:cursorCol inVisibleLine:cursorVisibleLine.
+
+ "Created: / 27-05-2005 / 07:43:41 / janfrog"
+!
+
+xOfPosition: positionInText
+
+ | line col |
+ line := self lineOfCharacterPosition: positionInText.
+ col := positionInText - (self characterPositionOfLine:line col:1) + 1.
+ ^
+ (self xOfCol:col inVisibleLine:(self listLineToVisibleLine: line))
+ - viewOrigin x.
+
+ "Created: / 16-02-2010 / 10:04:27 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+!
+
+yOfCursor
+
+ ^self yOfVisibleLine:cursorVisibleLine.
+
+ "Created: / 27-05-2005 / 07:43:41 / janfrog"
+!
+
+yOfPosition: positionInText
+
+ | line |
+ line := self lineOfCharacterPosition: positionInText.
+ ^self yOfVisibleLine:(self listLineToVisibleLine: line)
+
+ "Created: / 16-02-2010 / 10:08:21 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+! !
+
+!EditTextView methodsFor:'accessing-look'!
+
+cursorForegroundColor:color1 backgroundColor:color2
+ "set both cursor foreground and cursor background colors"
+
+ |wasOn|
+
+ wasOn := self hideCursor.
+ cursorFgColor := color1 onDevice:self graphicsDevice.
+ cursorBgColor := color2 onDevice:self graphicsDevice.
+ wasOn ifTrue:[self showCursor]
+!
+
+cursorType
+ "return the style of the text cursor.
+ Currently, supported are: #none
+ #block, #frame, #ibeam, #caret, #solidCaret
+ #bigCaret and #bigSolidCaret"
+
+ ^ cursorType
+
+ "Modified: / 5.5.1999 / 14:52:33 / cg"
+!
+
+cursorType:aCursorTypeSymbol
+ "set the style of the text cursor.
+ Currently, supported are: #none
+ #block, #frame, #ibeam, #caret, #solidCaret
+ #bigCaret and #bigSolidCaret"
+
+ cursorType := aCursorTypeSymbol.
+
+ "Created: 21.9.1997 / 13:42:23 / cg"
+ "Modified: 21.9.1997 / 13:43:35 / cg"
+!
+
+cursorTypeNoFocus
+ "return the style of the text cursor when the view has no focus.
+ If left unspecified, this is the same as the regular cursorType."
+
+ ^ cursorTypeNoFocus
+
+ "Created: / 5.5.1999 / 14:52:46 / cg"
+!
+
+cursorTypeNoFocus:aCursorTypeSymbol
+ "set the style of the text cursor when the view has no focus.
+ If left unspecified, this is the same as the regular cursorType."
+
+ cursorTypeNoFocus := aCursorTypeSymbol
+! !
+
+!EditTextView methodsFor:'accessing-replace'!
+
+lastReplacementInfo
+ ^ lastReplacementInfo
+!
+
+lastStringToReplace: aString
+!
+
+previousReplacements
+ "accessor for the code completion"
+
+ ^ lastReplacementInfo previousReplacements
+! !
+
+!EditTextView methodsFor:'change & update'!
+
+accept
+ "accept the current contents by executing the accept-action and/or
+ changeMessage."
+
+ acceptEnabled == false ifTrue:[
+ self beep.
+ ^ self
+ ].
+ (disableIfInvisible == true and:[self reallyRealized not]) ifTrue:[
+ ^ self
+ ].
+
+ lockUpdates := true.
+
+ "/
+ "/ ST-80 way of doing it
+ "/
+ model notNil ifTrue:[
+ self sendChangeMessage:changeMsg with:self argForChangeMessage.
+ acceptChannel value:true withoutNotifying:self.
+ ].
+
+ "/
+ "/ ST/X way of doing things
+ "/ as a historic (and temporary) leftover,
+ "/ the block is called with a stringCollection
+ "/ - not with the actual string
+ "/
+ acceptAction notNil ifTrue:[
+ acceptAction value:self list
+ ].
+
+ lockUpdates := false.
+
+ "Modified: / 30.1.1998 / 14:19:00 / cg"
+!
+
+argForChangeMessage
+ "return the argument to be passed with the change notification.
+ Defined as separate method for easier subclassability."
+
+ ^ self contents
+
+ "Modified: 29.4.1996 / 12:42:14 / cg"
+!
+
+getListFromModel
+ "get my contents from the model.
+ Redefined to ignore updates resulting from my own changes
+ (i.e. if lockUpdates is true)."
+
+ "
+ ignore updates from my own change
+ "
+ lockUpdates ifTrue:[
+ lockUpdates := false.
+ ^ self
+ ].
+ super getListFromModel.
+ undoSupport resetHistories.
+
+ "/ validate the cursorLine
+ (cursorLine notNil
+ and:[ cursorLine > list size ]) ifTrue:[
+ self cursorLine:list size + 1 col:1
+ ].
+!
+
+update:something with:aParameter from:changedObject
+ changedObject == acceptChannel ifTrue:[
+ acceptChannel value == true ifTrue:[
+ self accept.
+ ].
+ ^ self.
+ ].
+ super update:something with:aParameter from:changedObject
+
+ "Created: / 30.1.1998 / 14:15:56 / cg"
+ "Modified: / 1.2.1998 / 13:15:55 / cg"
+! !
+
+!EditTextView methodsFor:'cursor handling'!
+
+basicCursorReturn
+ "move cursor to start of next line; scroll if at end of visible text"
+
+ |wasOn|
+
+ self checkForExistingLine:(cursorLine + 1).
+ cursorVisibleLine notNil ifTrue:[
+ nFullLinesShown notNil ifTrue:[
+ (cursorVisibleLine >= nFullLinesShown) ifTrue:[self scrollDown]
+ ]
+ ].
+
+ wasOn := self hideCursor.
+ self setValidatedCursorLine:(cursorLine + 1) col:1.
+ self makeCursorVisibleAndShowCursor:wasOn.
+
+ "Modified: 22.5.1996 / 18:27:34 / cg"
+!
+
+characterPositionOfCursor
+ ^ self characterPositionOfLine:cursorLine col:cursorCol
+!
+
+cursorBacktab
+ "move cursor to prev tabstop"
+
+ self cursorCol:(self prevTabBefore:cursorCol).
+!
+
+cursorCol:newCol
+ "move cursor to some column in the current line"
+
+ |wasOn|
+
+ (cursorCol == newCol) ifTrue:[^ self].
+
+ wasOn := self hideCursor.
+ self setValidatedCursorCol:newCol.
+ self makeCursorVisibleAndShowCursor:wasOn.
+
+ "Modified: 22.5.1996 / 14:25:53 / cg"
+!
+
+cursorDown
+ "move cursor down; scroll if at end of visible text;
+ beep if at end of physical text."
+
+ |wasOn|
+
+ self cursorDown:1.
+
+ "/ cursor beyond text ?
+ cursorLine > list size ifTrue:[
+ wasOn := self hideCursor.
+ self setValidatedCursorLine:(list size + 1) col:cursorCol.
+ self makeCursorVisibleAndShowCursor:wasOn.
+ self beep.
+ ].
+
+ "Modified: / 10.6.1998 / 17:00:23 / cg"
+!
+
+cursorDown:n
+ "move cursor down by n lines; scroll if at end of visible text"
+
+ |wasOn nv nL|
+
+ (nL := cursorLine) isNil ifTrue:[
+ nL := firstLineShown
+ ].
+
+ self st80EditMode ifTrue:[
+ nL == list size ifTrue:[
+ wasOn := self hideCursor.
+ self setValidatedCursorLine:(list size) col:(self listAt:list size) size + 1.
+ self makeCursorVisibleAndShowCursor:wasOn.
+ self beep.
+ ^ self.
+ ]
+ ].
+
+ cursorVisibleLine notNil ifTrue:[
+ wasOn := self hideCursor.
+ nv := cursorVisibleLine + n - 1.
+ (nv >= nFullLinesShown) ifTrue:[
+ self scrollDown:(nv - nFullLinesShown + 1)
+ ].
+ self setValidatedCursorLine:(cursorLine + n) col:cursorCol.
+ self makeCursorVisibleAndShowCursor:wasOn.
+ ] ifFalse:[
+ self setValidatedCursorLine:(nL + n) col:cursorCol.
+ self makeCursorVisible.
+ ].
+
+ "Modified: / 10.6.1998 / 16:59:17 / cg"
+!
+
+cursorHome
+ "scroll to top AND move cursor to first line of text."
+
+ self cursorLine:1 col:1
+
+"/ |wasOn|
+"/
+"/ wasOn := self hideCursor.
+"/ self scrollToTop.
+"/ cursorLine := cursorVisibleLine := 1.
+"/ cursorCol := self validateCursorCol:1 inLine:cursorLine.
+"/ self makeCursorVisibleAndShowCursor:wasOn.
+
+ "Modified: 22.5.1996 / 18:26:42 / cg"
+!
+
+cursorLeft
+ "move cursor to left"
+
+ (cursorCol ~~ 1) ifTrue:[
+ self cursorCol:(cursorCol - 1)
+ ] ifFalse:[
+ cursorLine ~~ 1 ifTrue:[
+ self st80EditMode ifTrue:[
+ self cursorUp.
+ self cursorToEndOfLine.
+ ]
+ ]
+ ]
+
+ "Modified: / 23.1.1998 / 12:37:13 / cg"
+!
+
+cursorLeft:n
+ "move cursor to left"
+
+ n timesRepeat:[
+ self cursorLeft
+ ].
+!
+
+cursorLine:line col:col
+ "this positions onto physical - not visible - line"
+
+ |wasOn newCol|
+
+ ((line == cursorLine) and:[col == cursorCol]) ifTrue:[^ self].
+
+ wasOn := self hideCursor.
+ self setValidatedCursorLine:line.
+
+ (col < 1) ifTrue:[
+ newCol := 1
+ ] ifFalse:[
+ newCol := col.
+ ].
+ self st80EditMode ifTrue:[
+ (cursorLine == list size
+ and:[cursorLine ~~ line]) ifTrue:[
+ newCol := (self listAt:(list size)) size + 1.
+ ]
+ ].
+ self setValidatedCursorCol:newCol.
+ self makeCursorVisibleAndShowCursor:wasOn.
+
+ "Modified: / 20.6.1998 / 18:19:06 / cg"
+!
+
+cursorMovementAllowed
+ "return true, if the user may move the cursor around
+ (via button-click, or cursor-key with selection).
+ By default, true is returned, but this may be redefined
+ in special subclasses (such as a terminal view), where
+ this is not wanted"
+
+ ^ true
+
+ "Created: / 18.6.1998 / 14:11:16 / cg"
+!
+
+cursorReturn
+ "move cursor to start of next line; scroll if at end of visible text"
+
+ self basicCursorReturn
+!
+
+cursorRight
+ "move cursor to right"
+
+ |l|
+
+ self st80EditMode ifTrue:[
+ l := (self listAt:cursorLine).
+ cursorCol >= (l size + 1) ifTrue:[
+ cursorLine < list size ifTrue:[
+ self cursorReturn.
+ ].
+ ^ self
+ ]
+ ].
+ self cursorCol:(cursorCol + 1)
+
+ "Modified: / 20.6.1998 / 18:19:07 / cg"
+!
+
+cursorRight:n
+ "move cursor to right"
+
+ n timesRepeat:[
+ self cursorRight
+ ].
+!
+
+cursorShown:aBoolean
+ "change cursor visibility
+ return true if cursor was visible before."
+
+ |oldState|
+
+ aBoolean == cursorShown ifTrue:[
+ ^ cursorShown
+ ].
+
+ oldState := cursorShown.
+
+ aBoolean ifTrue:[
+ self drawCursor.
+ ] ifFalse:[
+ (cursorShown and:[shown]) ifTrue: [
+ self undrawCursor.
+ ].
+ ].
+ cursorShown := aBoolean.
+
+ ^ oldState
+
+ "Modified: / 30.3.1999 / 15:32:43 / stefan"
+ "Created: / 30.3.1999 / 15:59:30 / stefan"
+!
+
+cursorTab
+ "move cursor to next tabstop"
+
+ self cursorCol:(self nextTabAfter:cursorCol).
+!
+
+cursorToBeginOfLine
+ "move cursor to the start of the current line"
+
+ |textStart l|
+
+ l := self listAt:cursorLine.
+ textStart := l isNil ifTrue:[1] ifFalse:[l indexOfNonSeparator].
+ cursorCol > textStart ifTrue:[
+ self cursorCol:textStart
+ ] ifFalse:[
+ self cursorCol:1
+ ]
+
+ "Created: / 8.8.2004 / 18:51:21 / janfrog"
+!
+
+cursorToBottom
+ "move cursor to the last line of text (col 1)"
+
+ |wasOn newTop|
+
+ wasOn := self hideCursor.
+
+ newTop := list size - nFullLinesShown.
+ (newTop < 1) ifTrue:[
+ newTop := 1
+ ].
+ self scrollToLine:newTop.
+
+ self setValidatedCursorLine:(list size) col:1.
+
+ self makeCursorVisibleAndShowCursor:wasOn.
+
+ "Modified: 22.5.1996 / 18:27:45 / cg"
+!
+
+cursorToCharacterPosition:pos
+ "compute line/col from character position (1..)
+ and move the cursor onto that char"
+
+ |line col|
+
+ line := self lineOfCharacterPosition:pos.
+ col := pos - (self characterPositionOfLine:line col:1) + 1.
+ self cursorLine:line col:col
+
+ "Created: / 15.1.1998 / 21:55:33 / cg"
+!
+
+cursorToEnd
+ "move cursor down below last line of text"
+
+ |wasOn newTop lineNr line|
+
+ lineNr := list size.
+
+ cursorLine >= lineNr ifTrue:[
+ line := self listAt:cursorLine.
+ (line isEmptyOrNil) ifTrue:[
+ ^ self
+ ]
+ ].
+
+ wasOn := self hideCursor.
+
+ lineNr := lineNr + 1.
+ newTop := lineNr - nFullLinesShown.
+ (newTop < 1) ifTrue:[
+ newTop := 1
+ ].
+ self scrollToLine:newTop.
+
+ self setValidatedCursorLine:lineNr col:1.
+
+ self makeCursorVisibleAndShowCursor:wasOn.
+
+ "Modified: 22.5.1996 / 18:27:53 / cg"
+!
+
+cursorToEndOfLine
+ "move cursor to end of current line"
+
+ |line|
+
+ line := (self listAt:cursorLine).
+ self cursorCol:(line size + 1)
+
+ "Modified: 13.8.1997 / 15:34:02 / cg"
+!
+
+cursorToEndOfText
+ "move cursor to the end of the text (behind the last character in last line)"
+
+ |wasOn newTop lastLineLength pos|
+
+ wasOn := self hideCursor.
+
+ newTop := list size - nFullLinesShown.
+ (newTop < 1) ifTrue:[
+ newTop := 1
+ ].
+ self scrollToLine:newTop.
+
+ self setValidatedCursorLine:list size.
+ lastLineLength := (self listAt:cursorLine) size.
+ pos := (lastLineLength==0) ifTrue:[0] ifFalse:[lastLineLength+1].
+ self setCursorCol:(self validateCursorCol:pos inLine:cursorLine).
+
+ self makeCursorVisibleAndShowCursor:wasOn.
+
+ "Modified: / 15-07-2011 / 20:14:43 / cg"
+!
+
+cursorToEndOfWord
+ "move the cursor to the end of the word"
+
+ (cursorLine > list size) ifTrue:[^ self].
+
+ self wordAtLine:cursorLine col:cursorCol do:[
+ :beginLine :beginCol :endLine :endCol :style |
+
+ self cursorLine:endLine col:endCol+1
+ ]
+
+ "Created: / 28-06-2006 / 19:16:30 / cg"
+!
+
+cursorToFirstVisibleLine
+ "place cursor into the first visible line; do not scroll."
+
+ self cursorLine:(self visibleLineToAbsoluteLine:1) col:1
+!
+
+cursorToLastVisibleLine
+ "place cursor into the first visible line; do not scroll."
+
+ self cursorLine:(self visibleLineToAbsoluteLine:nFullLinesShown) col:1
+!
+
+cursorToNextWord
+ "move the cursor to the beginning of the next word"
+
+ |col line searching|
+
+ (cursorLine > list size) ifTrue:[^ self].
+
+ self
+ wordAtLine:cursorLine col:cursorCol
+ do:[
+ :beginLine :beginCol :endLine :endCol :style |
+
+ line := endLine.
+ col := endCol + 1.
+ searching := true.
+ [searching and:[(self characterAtLine:line col:col) isSeparator]] whileTrue:[
+ self wordAtLine:line col:col do:[
+ :beginLine :beginCol :endLine :endCol :style |
+
+ (line > list size) ifTrue:[
+ "break out"
+ searching := false
+ ] ifFalse:[
+ line := endLine.
+ col := endCol + 1.
+ ]
+ ]
+ ].
+ self cursorLine:line col:col
+ ]
+!
+
+cursorToPreviousWord
+ "move the cursor to the beginning of this or the previous word"
+
+ |col line searching l|
+
+ (cursorLine > list size) ifTrue:[^ self]. "/ this is rubbish
+
+ self wordAtLine:cursorLine col:cursorCol do:[
+ :beginLine :beginCol :endLine :endCol :style |
+
+ line := beginLine.
+ col := beginCol.
+ style == #wordLeft ifTrue:[
+ col := col + 1
+ ].
+
+ (cursorLine == line
+ and:[cursorCol == col]) ifTrue:[
+ searching := true.
+
+ col > 1 ifTrue:[
+ col := col - 1.
+ ].
+
+ [searching] whileTrue:[
+ (col == 1) ifTrue:[
+ line == 1 ifTrue:[
+ searching := false
+ ] ifFalse:[
+ line := line - 1.
+ l := list at:line.
+ col := l size + 1.
+ ]
+ ] ifFalse:[
+ (self characterAtLine:line col:col) isSeparator ifFalse:[
+ self wordAtLine:line col:col do:[
+ :beginLine :beginCol :endLine :endCol :style |
+
+ line := beginLine.
+ col := beginCol.
+ style == #wordLeft ifTrue:[
+ col := col + 1
+ ].
+ searching := false.
+ ]
+ ] ifTrue:[
+ col := col - 1
+ ]
+ ]
+ ]
+ ].
+ self cursorLine:line col:col
+ ]
+
+ "Created: 8.3.1996 / 21:52:48 / cg"
+ "Modified: 8.3.1996 / 22:12:45 / cg"
+!
+
+cursorToTop
+ "move cursor to absolute home"
+
+ self cursorLine:1 col:1
+!
+
+cursorUp
+ "move cursor up; scroll if at start of visible text"
+
+ self cursorUp:1
+!
+
+cursorUp:n
+ "move cursor up n lines; scroll if at start of visible text"
+
+ |wasOn nv nl|
+
+ cursorLine isNil ifTrue:[
+ self setCursorLine:(firstLineShown + nFullLinesShown - 1).
+ ].
+ nl := cursorLine - n.
+ nl < 1 ifTrue:[nl := 1].
+
+ (nl ~~ cursorLine) ifTrue: [
+ wasOn := self hideCursor.
+ cursorVisibleLine notNil ifTrue:[
+ nv := cursorVisibleLine - n.
+ nv < 1 ifTrue:[
+ self scrollUp:(nv negated + 1)
+ ].
+ ].
+ self setValidatedCursorLine:nl col:cursorCol.
+"/ wasOn ifTrue:[self showCursor].
+ self makeCursorVisibleAndShowCursor:wasOn.
+ ]
+
+ "Modified: 22.5.1996 / 18:28:11 / cg"
+!
+
+cursorVisibleLine:visibleLineNr col:colNr
+ "put cursor to visibleline/col"
+
+ |wasOn newCol listLine|
+
+ wasOn := self hideCursor.
+
+ listLine := self visibleLineToAbsoluteLine:visibleLineNr.
+ self setValidatedCursorLine:listLine.
+ cursorVisibleLine := visibleLineNr.
+
+ newCol := colNr.
+ (newCol < 1) ifTrue:[
+ newCol := 1
+ ].
+ self setValidatedCursorCol:newCol.
+
+ self makeCursorVisibleAndShowCursor:wasOn.
+
+ "Modified: / 20.6.1998 / 18:40:28 / cg"
+!
+
+cursorX:x y:y
+ "put cursor to position next to x/y coordinate in view"
+
+ |line col|
+
+ line := self visibleLineOfY:y.
+ col := self colOfX:x inVisibleLine:line.
+ self cursorVisibleLine:line col:col.
+!
+
+drawCursor
+ "draw the cursor if shown and cursor is visible.
+ (but not, if there is a selection - to avoid confusion)"
+
+ shown ifTrue:[
+ cursorVisibleLine notNil ifTrue:[
+ self hasSelection ifFalse:[
+ self drawCursorCharacter
+ ]
+ ]
+ ]
+!
+
+drawCursor:cursorType with:fgColor and:bgColor
+ "draw a cursor; the argument cursorType specifies what type
+ of cursor should be drawn.
+ Currently, supported are: #none,
+ #block, #frame, #ibeam, #caret, #solidCaret
+ #bigCaret and #bigSolidCaret"
+
+ |x y w char y2 x1 x2 oldPaint oldClip|
+
+ self hasSelection ifTrue:[
+ "
+ hide cursor, if there is a selection
+ "
+ ^ super redrawVisibleLine:cursorVisibleLine col:cursorCol.
+ ].
+
+ cursorType == #none ifTrue:[
+ ^ self
+ ].
+
+ cursorType == #block ifTrue:[
+ super drawVisibleLine:cursorVisibleLine col:cursorCol with:fgColor and:bgColor.
+ ^ self
+ ].
+ x := (self xOfCol:cursorCol inVisibleLine:cursorVisibleLine) - viewOrigin x.
+ y := self yOfVisibleLine:cursorVisibleLine.
+
+ oldPaint := self paint. "/ do not clobber GC
+ cursorType == #frame ifTrue:[
+ super redrawVisibleLine:cursorVisibleLine col:cursorCol.
+
+ char := self characterUnderCursor asString.
+ self paint:bgColor.
+ self displayRectangleX:x y:y width:(gc font widthOf:char) height:fontHeight-2.
+ ] ifFalse:[
+ self paint:bgColor.
+ cursorType == #ibeam ifTrue:[
+ x1 := x - 1.
+ y2 := y + fontHeight - lineSpacing.
+ self displayLineFromX:x1 y:y toX:x1 y:y2.
+ self displayLineFromX:x y:y toX:x y:y2.
+ ^ self
+ ].
+
+ cursorType == #Ibeam ifTrue:[
+ x1 := x - 1.
+ y := y + 1.
+ y2 := y + fontHeight - lineSpacing.
+ self displayLineFromX:x1 y:y toX:x1 y:y2.
+ self displayLineFromX:x y:y toX:x y:y2.
+ self displayLineFromX:x1-2 y:y toX:x+2 y:y.
+ self displayLineFromX:x1-2 y:y2 toX:x+2 y:y2.
+ ^ self
+ ].
+
+ y := y + fontHeight - 3.
+ ((cursorType == #bigCaret) or:[cursorType == #bigSolidCaret]) ifTrue:[
+ w := (fontWidth * 2 // 3) max:4.
+ y2 := y + w + (w//2).
+ ] ifFalse:[
+ w := (fontWidth // 2) max:4.
+ y2 := y + w.
+ ].
+ x1 := x - w.
+ x2 := x + w.
+
+ oldClip := self clippingRectangleOrNil.
+ self clippingRectangle:(margin@margin extent:(width-margin) @ (height-margin)).
+
+ cursorType == #caret ifTrue:[
+ self lineWidth:2.
+ self displayLineFromX:x1 y:y2 toX:x y:y.
+ self displayLineFromX:x y:y toX:x2 y:y2.
+ ] ifFalse:[
+ "anything else: solidCaret"
+
+ self fillPolygon:(Array with:(x1 @ y2)
+ with:(x @ y)
+ with:(x2 @ y2))
+ ].
+
+ self clippingRectangle:oldClip
+ ].
+ self paint:oldPaint.
+
+ "Modified: / 15.12.1999 / 22:24:17 / cg"
+!
+
+drawCursorCharacter
+ "draw the cursor.
+ (i.e. the cursor if no selection)
+ - helper for many cursor methods"
+
+ (hasKeyboardFocus
+ and:[self enabled
+ and:[self isReadOnly not]]) ifTrue:[
+ self drawFocusCursor
+ ] ifFalse:[
+ self drawNoFocusCursor
+ ]
+
+ "Modified: / 23.3.1999 / 13:52:48 / cg"
+!
+
+drawFocusCursor
+ "draw the cursor when the focus is in the view."
+
+ self hasSelection ifTrue:[
+ ^ super redrawVisibleLine:cursorVisibleLine col:cursorCol.
+ ].
+ cursorType == #none ifTrue:[
+ ^ self undrawCursor
+ ].
+ self drawCursor:cursorType with:cursorFgColor and:cursorBgColor.
+
+ "Modified: 22.9.1997 / 00:16:38 / cg"
+!
+
+drawNoFocusCursor
+ "draw the cursor for the case when the view has no keyboard focus"
+
+ |cType|
+
+ self hasSelection ifTrue:[
+ ^ super redrawVisibleLine:cursorVisibleLine col:cursorCol.
+ ].
+
+ cType := cursorTypeNoFocus ? cursorType.
+ cType == #none ifTrue:[
+ ^ self undrawCursor
+ ].
+
+ cType == #block ifTrue:[
+ ^ self drawCursor:#frame with:cursorNoFocusFgColor and:cursorBgColor
+ ].
+
+ ^ self drawCursor:cType with:cursorNoFocusFgColor and:cursorNoFocusFgColor.
+
+ "Modified: 22.9.1997 / 00:16:13 / cg"
+!
+
+gotoLine:aLineNumber
+ "position cursor onto line, aLineNumber.
+ Make certain that this line is visible"
+
+ self makeLineVisible:aLineNumber.
+ self unselect.
+ self cursorLine:aLineNumber col:1
+!
+
+hideCursor
+ "make cursor invisible if currently invisible"
+
+ ^ self cursorShown:false
+
+ "Modified: / 30.3.1999 / 16:02:28 / stefan"
+!
+
+makeCursorVisible
+ "scroll text to make cursorline visible
+ (i.e. to have cursorLine in visible area)"
+
+ |line col|
+
+ cursorLine notNil ifTrue:[
+ line := cursorLine.
+ col := cursorCol.
+ "
+ if there is a selection, its better to
+ have its start being visible, instead of the end
+ "
+ (selectionStartLine notNil
+ and:[selectionEndLine notNil]) ifTrue:[
+ expandingTop ~~ false ifTrue:[
+ line := selectionStartLine.
+ col := selectionStartCol.
+ ] ifFalse:[
+ line := selectionEndLine.
+ col := selectionEndCol
+ ]
+ ].
+ self makeLineVisible:line.
+ self makeColVisible:col inLine:line
+ ]
+
+ "Modified: 6.3.1996 / 13:46:46 / cg"
+!
+
+makeCursorVisibleAndShowCursor:flag
+ "scroll to make cursorLine visible;
+ if flag is true, draw the cursor"
+
+ self makeCursorVisible.
+ flag ifTrue:[self showCursor]
+!
+
+setCursorCol:colNr
+ "strictly private: set the cursorCol"
+
+ cursorCol := (colNr max:1).
+ cursorColHolder value:cursorCol.
+!
+
+setCursorLine:lineNr
+ "strictly private: set the cursorLine"
+
+ "/ self assert:(lineNr notNil).
+
+ cursorLine := (lineNr ? 1).
+ cursorLineHolder value:cursorLine.
+ self updateCursorVisibleLine.
+!
+
+setCursorLine:lineNr col:colNr
+ "strictly private: set the cursorLine, col and update the visibleLine"
+
+ self setCursorLine:lineNr.
+ self setCursorCol:colNr.
+!
+
+setValidatedCursorCol:colNr
+ "strictly private: set the cursorCol"
+
+ self setCursorCol:(self validateCursorCol:colNr inLine:cursorLine).
+!
+
+setValidatedCursorLine:lineNr
+ "strictly private: set the cursorLine and update the visibleLine"
+
+ self setCursorLine:(self validateCursorLine:lineNr).
+!
+
+setValidatedCursorLine:lineNr col:colNr
+ "strictly private: set the cursorLine, col and update the visibleLine"
+
+ self setValidatedCursorLine:lineNr.
+ self setValidatedCursorCol:colNr.
+!
+
+showCursor
+ "make cursor visible if currently invisible"
+
+ ^ self cursorShown:true
+
+ "Modified: / 30.3.1999 / 16:02:34 / stefan"
+!
+
+undrawCursor
+ "undraw the cursor (i.e. redraw the character(s) under the cursor)"
+
+ |prevCol line oldClip x y e1 e2 e3|
+
+ cursorVisibleLine notNil ifTrue:[
+ prevCol := cursorCol - 1.
+
+ "/ if there is any italic stuff in the cursor line,
+ "/ redraw it completely (because characters overlap).
+ cursorCol > 1 ifTrue:[
+ (line := self listAt:cursorLine) notNil ifTrue:[
+ line hasChangeOfEmphasis ifTrue:[
+ line size >= (cursorCol-1) ifTrue:[
+ e1 := Text extractEmphasis:#italic from:(line emphasisAt:cursorCol-1).
+ line size >= (cursorCol) ifTrue:[
+ e2 := Text extractEmphasis:#italic from:(line emphasisAt:cursorCol).
+ line size >= (cursorCol+1) ifTrue:[
+ e3 := Text extractEmphasis:#italic from:(line emphasisAt:cursorCol+1)
+ ].
+ ].
+ ].
+ (e1 notNil or:[e2 notNil or:[e3 notNil]]) ifTrue:[
+ ^ super redrawVisibleLine:cursorVisibleLine
+ ]
+ ]
+ ]
+ ].
+
+ ((cursorType == #caret)
+ or:[cursorType == #solidCaret
+ or:[cursorType == #bigSolidCaret
+ or:[cursorType == #bigCaret
+ or:[cursorType == #Ibeam]]]]) ifTrue:[
+ "caret-cursor touches 4 characters"
+ ((cursorCol > 1) and:[fontIsFixedWidth]) ifTrue:[
+ super redrawVisibleLine:cursorVisibleLine-1 from:prevCol to:cursorCol.
+ super redrawVisibleLine:cursorVisibleLine from:prevCol to:cursorCol.
+ super redrawVisibleLine:cursorVisibleLine+1 from:prevCol to:cursorCol.
+ ] ifFalse:[
+ "care for left margin"
+ super redrawVisibleLine:cursorVisibleLine; redrawVisibleLine:cursorVisibleLine+1.
+ ].
+ ^ self
+ ].
+
+ cursorType == #ibeam ifTrue:[
+ "ibeam-cursor touches 2 characters"
+ cursorCol > 1 ifTrue:[
+ super redrawVisibleLine:cursorVisibleLine from:prevCol to:cursorCol.
+ ] ifFalse:[
+ "care for left margin"
+ super redrawVisibleLine:cursorVisibleLine.
+ ].
+ ^ self
+ ].
+
+ "block cursor is simple - just one character under cursor"
+
+ "/ however, if italic characters are involved, we must care
+ "/ for the chars before/after the cursor.
+ "/ We redraw the part of the previous character which got
+ "/ detroyed by the block cursor.
+ "/ (must change the clip, to avoid destroying the prev-prev character)
+
+ line := self visibleAt:cursorVisibleLine.
+ (line notNil and:[line isText]) ifTrue:[
+ cursorCol > 1 ifTrue:[
+ oldClip := self clippingRectangleOrNil.
+ x := (self xOfCol:cursorCol inVisibleLine:cursorVisibleLine) - viewOrigin x.
+ y := self yOfVisibleLine:cursorVisibleLine.
+ self clippingRectangle:(x@y extent:((gc font width * 2) @ fontHeight)).
+ super redrawVisibleLine:cursorVisibleLine from:cursorCol-1 to:cursorCol.
+ self clippingRectangle:oldClip.
+ ^ self.
+ ].
+ ].
+ super redrawVisibleLine:cursorVisibleLine col:cursorCol
+ ]
+
+ "Modified: / 15.12.1999 / 22:25:59 / cg"
+!
+
+updateCursorVisibleLine
+ "strictly private: set the visibleLine from the cursorLine.
+ notice: visibleLine will be set to nil if the cursor is not visible"
+
+ cursorVisibleLine := self listLineToVisibleLine:cursorLine.
+!
+
+validateCursorCol:col inLine:line
+ "check of col is a valid cursor position; return a new col-nr if not.
+ Here, no limits are enforced (and col is returned),
+ but it may be redefined in EditFields or views which dont like the
+ cursor to be positioned behind the end of a textLine (vi/st-80 behavior)"
+
+ |l max|
+
+ "/ in ST80 mode,
+ "/ the cursor may not be positioned beyond the
+ "/ end of a line or beyond the last line of the text
+
+ self st80EditMode ifTrue:[
+ l := (self listAt:line).
+ max := l size + 1.
+ col > max ifTrue:[
+ ^ max
+ ]
+ ].
+ ^ col
+
+ "Created: / 22.5.1996 / 14:25:30 / cg"
+ "Modified: / 20.6.1998 / 18:19:24 / cg"
+!
+
+validateCursorLine:line
+ "check of line is a valid cursor line; return a fixed line-nr if not.
+ Here, no limits are enforced (and line is returned), but it may be
+ redefined in views which dont like the cursor to be positioned
+ behind the end of the text (vi/st-80 behavior), or want to
+ skip reserved regions"
+
+ "/
+ "/ in st80Mode, the cursor may not be positioned
+ "/ beyond the last line
+ "/
+ self st80EditMode ifTrue:[
+ ^ (line min:(list size)) max:1
+ ].
+ ^ line
+
+ "Created: / 22.5.1996 / 18:22:23 / cg"
+ "Modified: / 20.6.1998 / 18:19:26 / cg"
+!
+
+withCursorOffDo:aBlock
+ "evaluate aBlock with cursor off; turn it on afterwards."
+
+ (shown not or:[cursorShown not]) ifTrue:[
+ ^ aBlock value
+ ].
+ self hideCursor.
+ aBlock ensure:[
+ self showCursor
+ ]
+! !
+
+!EditTextView methodsFor:'drag & drop'!
+
+allowDrop:aBoolean
+ "enable/disable drop support"
+
+ aBoolean ifFalse:[
+ dropTarget := nil.
+ ] ifTrue:[
+ dropTarget isNil ifTrue:[
+ dropTarget := DropTarget
+ receiver:self
+ argument:nil
+ dropSelector:#'drop:'
+ canDropSelector:#'canDrop:'
+ ]
+ ].
+!
+
+canDrop:aDropContext
+ "public from d&d.
+ I accept textObjects and fileObjects only."
+
+ "/ cg: disabled to avoid unintended drop (is same as copy-past, anyway)
+ aDropContext sourceWidget == self ifTrue:[^ false].
+ ^ self canDropObjects:aDropContext dropObjects
+
+ "Modified: / 13-10-2006 / 17:41:09 / cg"
+!
+
+canDropObjects:aCollectionOfDropObjects
+ "public from d&d.
+ I accept textObjects and fileObjects only."
+
+ self checkModificationsAllowed ifFalse:[^ false].
+
+ aCollectionOfDropObjects isEmpty ifTrue:[ ^ false ].
+ ^ aCollectionOfDropObjects conform:[:obj| (obj isTextObject or:[obj isFileObject])]
+
+ "Created: / 13-10-2006 / 15:56:57 / cg"
+ "Modified: / 13-10-2006 / 17:41:14 / cg"
+!
+
+drop:aDropContext
+ "public from d&d.
+ drop objects (new API)"
+
+ self dropObjects:(aDropContext dropObjects)
+
+ "Modified: / 13-10-2006 / 17:41:19 / cg"
+!
+
+dropFileObject:aDropObject
+ "drop objects
+ For bw. compatibility, also collections of drop objects are handled (may vanish)"
+
+ |answer text fn pasteWhat sensor dontAskAgainHolder enforcedDropMode app|
+
+ pasteWhat := #name.
+
+ fn := aDropObject asFilename.
+ (fn exists and:[fn isRegularFile]) ifTrue:[
+ enforcedDropMode := UserPreferences current enforcedDropModeForFiles.
+ (enforcedDropMode notNil
+ and:[enforcedDropMode ~~ #name or:[fn fileSize <= (1024*1024)]]) ifTrue:[
+ pasteWhat := enforcedDropMode.
+ ] ifFalse:[
+ sensor := self sensor.
+ (sensor shiftDown or:[sensor ctrlDown]) ifTrue:[
+ pasteWhat := #name.
+ ] ifFalse:[
+ (sensor metaDown) ifTrue:[
+ pasteWhat := #contents.
+ ] ifFalse:[
+ dontAskAgainHolder := false asValue.
+ answer := Dialog
+ confirmWithCancel:(resources
+ stringWithCRs:'Drop the Filename (%1)\or its Contents ?\\Hint: bypass this dialog by pressing SHIFT/CTRL or ALT during the next drop.\SHIFT/CTRL to drop the name, ALT for the contents.'
+ with:fn name allBold)
+ labels:#( 'Cancel' 'Name' 'Contents' )
+ values:#( nil #name #contents )
+ default:#contents
+ check:(resources string:'Do not ask again; instead, always paste the contents of small files.') on:dontAskAgainHolder
+ title:(resources string:'Drop What').
+ answer isNil ifTrue:[ ^ self ].
+
+ dontAskAgainHolder value ifTrue:[
+ UserPreferences current enforcedDropModeForFiles:#contents
+ ].
+ pasteWhat := answer.
+ ]
+ ]
+ ].
+ ].
+
+ pasteWhat == #name ifTrue:[
+ text := fn pathName
+ ] ifFalse:[
+ self withWaitCursorDo:[
+ text := fn contentsOfEntireFile
+ ].
+ (app := self application) notNil ifTrue:[
+ app droppedFile:fn in:self
+ ].
+ ].
+
+ self
+ undoablePaste:text
+ info:'Drop File'.
+
+ "Created: / 13-10-2006 / 17:38:31 / cg"
+ "Modified: / 28-07-2007 / 13:27:09 / cg"
+!
+
+dropObject:aDropObject
+ "drop objects
+ For bw. compatibility, also collections of drop objects are handled (may vanish)"
+
+ |text|
+
+ (aDropObject isFileObject) ifTrue:[
+ self dropFileObject:aDropObject
+ ] ifFalse:[
+ aDropObject isTextObject ifTrue:[
+ text := aDropObject theObject.
+ text isStringCollection ifTrue:[
+ text := text asStringWithoutFinalCR
+ ].
+ ] ifFalse:[
+ text := aDropObject theObject asString
+ ].
+ self
+ undoablePaste:text
+ info:'Drop'.
+ ].
+
+ "Created: / 13-10-2006 / 17:37:05 / cg"
+ "Modified: / 28-07-2007 / 13:26:53 / cg"
+!
+
+dropObjects:aCollectionOfDropObjects
+ "public from d&d.
+ drop objects (old API)"
+
+ aCollectionOfDropObjects do:[:el |
+ self dropObject:el
+ ].
+
+ "Created: / 13-10-2006 / 15:59:40 / cg"
+ "Modified: / 13-10-2006 / 17:41:23 / cg"
+! !
+
+!EditTextView methodsFor:'editing'!
+
+convertSelectionToLowercaseOrUppercaseOrUppercaseFirst
+ "toLower/toUppercaseFirst/toUpper selected text"
+
+ |line1 line2|
+
+ line1 := self selectionStartLine.
+ line2 := self selectionEndLine.
+ line1 isNil ifTrue:[
+ line1 := self perform:#cursorLine ifNotUnderstood:nil.
+ line1 notNil ifTrue:[
+ line2 := line1
+ ]
+ ].
+ line1 notNil ifTrue:[
+ line1 to:line2 do:[:lineNr |
+ |line col1 col2 isAllLower isLowerFirst isAllUpper isUpperFirst
+ makeLowercase makeUppercase makeUppercaseFirst makeLowercaseFirst|
+
+ line := (self listAt:lineNr) copy.
+ line size > 0 ifTrue:[
+ lineNr == line1 ifTrue:[
+ col1 := selectionStartCol.
+ ] ifFalse:[
+ col1 := 1.
+ ].
+ lineNr == line2 ifTrue:[
+ col2 := selectionEndCol.
+ ] ifFalse:[
+ col2 := (self listAt:lineNr) size.
+ ].
+ isAllLower := isAllUpper := isUpperFirst := isLowerFirst := true.
+ col1 to:col2 do:[:col |
+ |ch|
+
+ ch := line at:col.
+ ch isUppercase ifTrue:[
+ isAllLower := false.
+ col == col1 ifTrue:[
+ isLowerFirst := false.
+ ].
+ ] ifFalse:[
+ ch isLowercase ifTrue:[
+ isAllUpper := false.
+ col == col1 ifTrue:[
+ isUpperFirst := false.
+ ].
+ ]
+ ].
+ ].
+
+ makeLowercase := makeUppercase := makeUppercaseFirst := makeLowercaseFirst := false.
+ isLowerFirst ifTrue:[
+ makeUppercaseFirst := true.
+ ] ifFalse:[
+ "/ must remember where we come from - otherwise, we end up
+ "/ in upperFirst - lowerFirst cycle.
+ "/ think about a good place to store this state
+ false "(isUpperFirst and:[isAllUpper not])" ifTrue:[
+ makeLowercaseFirst := true.
+ ] ifFalse:[
+ isAllUpper ifTrue:[
+ makeLowercase := true.
+ ] ifFalse:[
+ makeUppercase := true.
+ ]
+ ]
+ ].
+ makeUppercaseFirst ifTrue:[
+ line at:col1 put:(line at:col1) asUppercase.
+ ] ifFalse:[
+ makeLowercaseFirst ifTrue:[
+ line at:col1 put:(line at:col1) asLowercase.
+ ] ifFalse:[
+ col1 to:col2 do:[:col |
+ |ch|
+
+ ch := line at:col.
+ ch := makeLowercase
+ ifTrue:[ ch asLowercase ]
+ ifFalse:[
+ makeUppercase
+ ifTrue:[ ch asUppercase ]
+ ifFalse:[
+ col == col1
+ ifTrue:[ ch asUppercase ]
+ ifFalse:[ ch asLowercase ]
+ ]
+ ].
+ line at:col put:ch.
+ ].
+ ].
+ ].
+ self withoutRedrawAt:lineNr put:line.
+ self invalidateLine:lineNr.
+ ].
+ ].
+ ]
+
+ "Created: / 14-07-2011 / 11:40:26 / cg"
+!
+
+copyAndDeleteSelection
+ "copy the selection into the pastBuffer and delete it"
+
+ selectionStartLine notNil ifTrue:[
+ self setClipboardText:(self selection).
+ self deleteSelection.
+ ].
+
+ "Created: 27.1.1996 / 16:23:28 / cg"
+!
+
+deleteCharAtCursor
+ "delete single character under cursor; does not merge lines"
+
+ |wasOn|
+
+ wasOn := self hideCursor.
+ self deleteCharAtLine:cursorLine col:cursorCol.
+ wasOn ifTrue:[self showCursor]
+!
+
+deleteCharAtLine:lineNr col:colNr
+ "delete a single character at colNr in line lineNr"
+
+ self st80EditMode ifTrue:[
+ (self listAt:cursorLine) size + 1 = colNr ifTrue:[
+ | wasOn |
+ wasOn := self hideCursor.
+ self
+ cursorReturn;
+ cursorCol:1;
+ deleteCharBeforeCursor.
+ wasOn ifTrue:[ self showCursor].
+ ^ self.
+ ].
+ ].
+
+ self deleteCharsAtLine:lineNr fromCol:colNr toCol:colNr
+!
+
+deleteCharBeforeCursor
+ "delete single character to the left of cursor and move cursor to left"
+
+ |soCol wasOn lineNrAboveCursor ln originalLine prevTab|
+
+ wasOn := self hideCursor.
+ (autoIndent and:[ (tabPositions includes:cursorCol)]) ifTrue:[
+ prevTab := (self prevTabBefore:cursorCol) max:1.
+ ln := originalLine := (list at:cursorLine ifAbsent:'') ? ''.
+ ln size < prevTab ifTrue:[
+ ln := ln , (String new:prevTab withAll:Character space).
+ ].
+ (ln copyTo:prevTab) isBlank ifTrue:[
+ (ln copyFrom:prevTab+1) isBlank ifTrue:[
+ cursorCol > prevTab ifTrue:[
+ self st80EditMode ifTrue:[
+ "/ ensure that there is no conflict here: st80EditMode will
+ "/ not allow a cursor position beyond the end of line,
+ "/ so avoid that cursorLine:col: will force us to the beginning of the line
+ originalLine size < prevTab ifTrue:[
+ self basicListAt:cursorLine put:ln
+ ]
+ ].
+ self cursorLine:cursorLine col:prevTab.
+ wasOn ifTrue:[ self showCursor ].
+ ^ self
+ ].
+ ] ifFalse:[
+ self deleteFromLine:cursorLine col:prevTab toLine:cursorLine col:cursorCol-1.
+ self cursorLine:cursorLine col:prevTab.
+ wasOn ifTrue:[ self showCursor ].
+ ^ self.
+ ]
+ ].
+ ].
+
+"/ (autoIndent
+"/ and:[cursorCol ~~ 1
+"/ and:[cursorLine <= (list size)]])
+"/ ifTrue:[
+"/ soCol := (self leftIndentForLine:cursorLine) + 1.
+"/
+"/ (cursorCol == soCol and:[soCol > 1]) ifTrue:[
+"/ ln := list at:cursorLine.
+"/ (ln notNil and:[(ln indexOfNonSeparatorStartingAt:1) < soCol]) ifTrue:[
+"/ soCol := 1
+"/ ]
+"/ ]
+"/ ] ifFalse:[
+ soCol := 1.
+"/ ].
+
+ (cursorCol ~~ soCol and:[cursorCol ~~ 1]) ifTrue:[
+ "
+ somewhere in the middle of a line
+ "
+ self cursorLeft.
+ self deleteCharAtLine:cursorLine col:cursorCol.
+ ] ifFalse:[
+ "
+ at begin of line - merge with previous line;
+ except for the very first line.
+ "
+ (cursorLine == 1) ifFalse:[
+ lineNrAboveCursor := self validateCursorLine:(cursorLine - 1).
+ lineNrAboveCursor < cursorLine ifTrue:[
+ (lineNrAboveCursor > 0 and:[lineNrAboveCursor > list size]) ifTrue:[
+ "/ we are beyond the end of the text.
+ "/ move the cursor to the previous line.
+ self cursorLine:lineNrAboveCursor col:1.
+ ] ifFalse:[
+ self mergeLine:lineNrAboveCursor removeBlanks:false.
+ ]
+ ]
+ ]
+ ].
+ wasOn ifTrue:[ self showCursor ]
+
+ "Modified: / 16.1.1998 / 22:33:04 / cg"
+!
+
+deleteCharsAtLine:lineNr fromCol:colNr
+ "delete characters from colNr up to the end in line lineNr"
+
+ |line|
+
+ (line := self listAt:lineNr) notNil ifTrue:[
+ self deleteCharsAtLine:lineNr fromCol:colNr toCol:(line size)
+ ]
+
+!
+
+deleteCharsAtLine:lineNr fromCol:startCol toCol:endCol
+ "delete characters from startCol to endCol in line lineNr"
+
+ |deleted|
+
+ deleted := self textFromLine:lineNr col:startCol toLine:lineNr col:endCol.
+ self basicDeleteCharsAtLine:lineNr fromCol:startCol toCol:endCol.
+ self addUndo:(PasteString line:lineNr col:startCol string:deleted info:'delete').
+!
+
+deleteCharsAtLine:lineNr toCol:colNr
+ "delete characters from start up to colNr in line lineNr"
+
+ self deleteCharsAtLine:lineNr fromCol:1 toCol:colNr
+
+
+!
+
+deleteCursorLine
+ "delete the line where the cursor sits"
+
+ self deleteLine:cursorLine
+!
+
+deleteFromCharacterPosition:charPos1 to:charPos2
+ "delete a substring at a character position"
+
+ |line1 col1 line2 col2|
+
+ line1 := self lineOfCharacterPosition:charPos1.
+ col1 := charPos1 - (self characterPositionOfLine:line1 col:1) + 1.
+ col1 == 0 ifTrue:[
+ line1 := line1 - 1.
+ col1 := (self listAt:line1) size + 1.
+ ].
+
+ line2 := self lineOfCharacterPosition:charPos2.
+ col2 := charPos2 - (self characterPositionOfLine:line2 col:1) + 1.
+
+ self deleteFromLine:line1 col:col1 toLine:line2 col:col2.
+!
+
+deleteFromLine:startLine col:startCol toLine:endLine col:endCol
+ "delete all text from startLine/startCol to endLine/endCol -
+ joining lines if nescessary"
+
+ |line newLine lineSize nMore|
+
+ self checkModificationsAllowed ifFalse:[ ^ self].
+ list isNil ifTrue:[^ self].
+ startLine > list size ifTrue:[ ^ self]. "/ deleted space below text
+
+ (startLine == endLine) ifTrue:[
+ "/ delete chars within a line
+ self deleteCharsAtLine:startLine fromCol:startCol toCol:endCol.
+ ^ self
+ ].
+
+ ((startCol == 1) and:[endCol == 0]) ifTrue:[
+ "/ delete full lines only
+ endLine > startLine ifTrue:[
+ self deleteFromLine:startLine toLine:(endLine - 1)
+ ].
+ ^ self
+ ].
+
+ "/ delete right rest of 1st line
+ self deleteCharsAtLine:startLine fromCol:(startCol max:1).
+
+ "/ delete the inner lines ...
+ endLine > (startLine + 1) ifTrue:[
+ self deleteFromLine:(startLine + 1) toLine:(endLine - 1)
+ ].
+
+ (endCol ~~ 0) ifTrue:[
+ "/ delete the left rest of the last line
+
+ self deleteCharsAtLine:(startLine + 1) toCol:endCol.
+
+ "/ must add blanks, if startCol lies beyond end of startLine
+ startLine <= list size ifTrue:[
+ line := list at:startLine.
+ lineSize := line size.
+ (startCol > lineSize) ifTrue:[
+ newLine := line.
+ line isNil ifTrue:[
+ newLine := String new:(startCol - 1)
+ ] ifFalse:[
+ nMore := startCol - 1 - lineSize.
+ nMore > 0 ifTrue:[
+ newLine := line , (line species new:nMore)
+ ]
+ ].
+ newLine ~~ line ifTrue:[
+ self basicListAt:startLine put:newLine.
+ ].
+ "/ TODO: remember old maxwidth of linerange,
+ "/ only clear widthOfWidestLine, if this max
+ "/ length was (one of) the longest.
+ "/ avoids slow delete with huge texts.
+ widthOfWidestLine := nil. "/ i.e. unknown
+ self textChanged.
+ ]
+ ]
+ ].
+
+ "/ merge the left rest of 1st line with right rest of last line into one
+ self mergeLine:startLine removeBlanks:false
+
+ "Modified: / 10-11-1998 / 23:52:59 / cg"
+ "Modified: / 18-03-2011 / 18:25:01 / az"
+!
+
+deleteFromLine:startLineNr toLine:endLineNr
+ "delete some lines"
+
+ |deleted|
+
+ deleted := self textFromLine:startLineNr col:1 toLine:endLineNr+1 col:0.
+ self basicDeleteFromLine:startLineNr toLine:endLineNr.
+ self addUndo:(PasteString line:startLineNr col:1 string:deleted info:'delete').
+!
+
+deleteLine:lineNr
+ "delete line"
+
+ self deleteFromLine:lineNr toLine:lineNr
+
+
+!
+
+deleteLineWithoutRedraw:lineNr
+ "delete line - no redraw;
+ return true, if something was really deleted"
+
+ |deleted ret|
+
+ deleted := self textFromLine:lineNr col:1 toLine:lineNr+1 col:0.
+ ret := self basicDeleteLineWithoutRedraw:lineNr.
+ self addUndo:(PasteString line:lineNr col:1 string:deleted info:'delete').
+ ^ ret.
+!
+
+deleteLinesWithoutRedrawFrom:startLine to:endLine
+ "delete lines - no redraw;
+ return true, if something was really deleted"
+
+ |lastLine|
+
+ self checkModificationsAllowed ifFalse:[^ false].
+
+ (list isNil or:[startLine > list size]) ifTrue:[^ false].
+ (endLine > list size) ifTrue:[
+ lastLine := list size
+ ] ifFalse:[
+ lastLine := endLine
+ ].
+ self basicListRemoveFromIndex:startLine toIndex:lastLine.
+ "/ TODO: remember old maxwidth of linerange,
+ "/ only clear widthOfWidestLine, if this max
+ "/ length was (one of) the longest.
+ "/ avoids slow delete with huge texts.
+ widthOfWidestLine := nil. "/ i.e. unknown
+ self textChanged.
+ ^ true
+
+ "Modified: / 10.11.1998 / 23:55:29 / cg"
+!
+
+deleteSelection
+ "delete the selection"
+
+ |wasOn startLine startCol endLine endCol|
+
+ self checkModificationsAllowed ifFalse:[ ^ self].
+
+ selectionStartLine notNil ifTrue:[
+ wasOn := self hideCursor.
+
+ startLine := selectionStartLine.
+ startCol := selectionStartCol.
+ endLine := selectionEndLine.
+ endCol := selectionEndCol.
+ self unselectWithoutRedraw.
+ self deleteFromLine:startLine col:startCol toLine:endLine col:endCol.
+ self setCursorLine:startLine col:startCol.
+
+ self makeCursorVisibleAndShowCursor:wasOn
+ ]
+!
+
+deleteWordBeforeCursor
+ "delete the word to the left of cursor and move cursor to left"
+
+ |wasOn beginCol beginLine endCol endLine|
+
+ self checkModificationsAllowed ifFalse:[ ^ self].
+
+ wasOn := self hideCursor.
+ self
+ undoableDo:[
+ endCol := cursorCol-1.
+ endLine := cursorLine.
+ self cursorToPreviousWord.
+ beginCol := cursorCol.
+ beginLine := cursorLine.
+ self deleteFromLine:beginLine col:beginCol toLine:endLine col:endCol.
+ ]
+ info:'Delete Word'.
+ wasOn ifTrue:[ self showCursor ].
+
+ "Modified: / 22.2.2000 / 23:59:04 / cg"
+!
+
+insert:aCharacter atLine:lineNr col:colNr
+ "insert a single character at lineNr/colNr;
+ set emphasis to character at current position"
+
+ self basicInsert:aCharacter atLine:lineNr col:colNr.
+ aCharacter ~~ Character cr ifTrue:[
+ self addUndo:(DeleteCharacters line:lineNr col:colNr info:'insert').
+ ]
+!
+
+insertCharAtCursor:aCharacter
+ "insert a single character at cursor-position - advance cursor."
+
+ |wasOn|
+
+ wasOn := self hideCursor.
+ aCharacter == Character tab ifTrue:[
+ "/ needs special care to advance cursor correctly
+ self insertTabAtCursor
+ ] ifFalse:[
+ self insert:aCharacter atLine:cursorLine col:cursorCol.
+ aCharacter == (Character cr) ifTrue:[
+ self basicCursorReturn
+ ] ifFalse:[
+ self cursorRight.
+ ].
+ ].
+ self makeCursorVisibleAndShowCursor:wasOn.
+
+ "Modified: / 12.6.1998 / 21:50:20 / cg"
+!
+
+insertLine:aString before:lineNr
+ "insert the line aString before line lineNr"
+
+ ^ self insertLines:(Array with:aString) from:1 to:1 before:lineNr.
+
+ "Modified: 14.5.1996 / 13:42:54 / cg"
+!
+
+insertLines:aStringCollection before:lineNr
+ "insert a bunch before line lineNr"
+
+ self insertLines:aStringCollection from:1 to:aStringCollection size before:lineNr
+
+ "Modified: 6.9.1995 / 20:51:03 / claus"
+!
+
+insertLines:someText from:start to:end before:lineNr
+ "insert a bunch of lines before line lineNr.
+ The cursor position is left unchanged."
+
+ |text indent visLine w nLines "{ Class: SmallInteger }"
+ srcY "{ Class: SmallInteger }"
+ dstY "{ Class: SmallInteger }" |
+
+ autoIndent ifTrue:[
+ indent := self leftIndentForLine:lineNr.
+
+ text := someText
+ collect:[:ln||line|
+ ln notNil ifTrue:[
+ line := ln withoutLeadingSeparators.
+ (line isEmpty or:[indent == 0]) ifFalse:[
+ line := (String new:indent), line
+ ].
+ line
+ ] ifFalse:[
+ nil
+ ]
+ ].
+ ] ifFalse:[
+ text := someText
+ ].
+
+ visLine := self listLineToVisibleLine:lineNr.
+ (shown not or:[visLine isNil]) ifTrue:[
+ self withoutRedrawInsertLines:text
+ from:start to:end
+ before:lineNr.
+ ] ifFalse:[
+ nLines := end - start + 1.
+ ((visLine + nLines) >= nLinesShown) ifTrue:[
+ self withoutRedrawInsertLines:text
+ from:start to:end
+ before:lineNr.
+ self redrawFromVisibleLine:visLine to:nLinesShown
+ ] ifFalse:[
+ w := self widthForScrollBetween:(lineNr + nLines)
+ and:(firstLineShown + nLines + nLinesShown).
+ srcY := topMargin + ((visLine - 1) * fontHeight).
+ dstY := srcY + (nLines * fontHeight).
+
+ "/
+ "/ scroll ...
+ "/
+ "
+ stupid: must catchExpose before inserting new
+ stuff - since catchExpose may perform redraws
+ "
+ self catchExpose.
+ self withoutRedrawInsertLines:text
+ from:start to:end
+ before:lineNr.
+ self
+ copyFrom:self
+ x:textStartLeft y:srcY
+ toX:textStartLeft y:dstY
+ width:w
+ height:(height - dstY)
+ async:true.
+ self redrawFromVisibleLine:visLine to:(visLine + nLines - 1).
+ self waitForExpose
+ ].
+ ].
+ widthOfWidestLine notNil ifTrue:[
+ text do:[:line |
+ widthOfWidestLine := widthOfWidestLine max:(self widthOfLineString:line).
+ ]
+ ].
+ self textChanged.
+
+ "Modified: 29.1.1997 / 13:02:39 / cg"
+!
+
+insertLines:lines withCR:withCr
+ "insert a bunch of lines at cursor position.
+ Cursor is moved behind insertion.
+ If withCr is true, append cr after last line"
+
+ |start end nLines wasOn|
+
+ lines notNil ifTrue:[
+ nLines := lines size.
+ (nLines == 1) ifTrue:[
+ self insertStringAtCursor:(lines at:1).
+ withCr ifTrue:[
+ self insertCharAtCursor:(Character cr)
+ ]
+ ] ifFalse:[
+ (cursorCol ~~ 1) ifTrue:[
+ self insertStringAtCursor:(lines at:1).
+ self insertCharAtCursor:(Character cr).
+ start := 2
+ ] ifFalse:[
+ start := 1
+ ].
+ withCr ifTrue:[
+ end := nLines
+ ] ifFalse:[
+ end := nLines - 1
+ ].
+ (start <= nLines) ifTrue:[
+ (end >= start) ifTrue:[
+ wasOn := self hideCursor.
+ self insertLines:lines from:start to:end before:cursorLine.
+ self setCursorLine:(cursorLine + (end - start + 1)).
+ wasOn ifTrue:[self showCursor].
+ ]
+ ].
+ withCr ifFalse:[
+ "last line without cr"
+ self insertStringAtCursor:(lines at:nLines)
+ ]
+ ]
+ ]
+
+ "Created: / 18.5.1996 / 15:32:06 / cg"
+ "Modified: / 12.6.1998 / 21:51:16 / cg"
+!
+
+insertLines:lines withCr:withCr
+ "insert a bunch of lines at cursor position. Cursor
+ is moved behind insertion.
+ If withCr is true, append cr after last line"
+
+ <resource:#obsolete>
+
+ self obsoleteMethodWarning:'use #insertLines:withCR:'.
+ self insertLines:lines withCR:withCr.
+
+ "Modified: 31.7.1997 / 23:07:22 / cg"
+!
+
+insertSelectedStringAtCursor:aString
+ "insert the argument, aString at cursor position and select it"
+
+ |startLine startCol|
+
+ startLine := cursorLine.
+ startCol := cursorCol.
+ self insertStringAtCursor:aString.
+ self selectFromLine:startLine col:startCol
+ toLine:cursorLine col:(cursorCol - 1).
+ self makeSelectionVisible.
+!
+
+insertString:aString atCharacterPosition:charPos
+ "insert the argument, aString at a character position"
+
+ |line col|
+
+ line := self lineOfCharacterPosition:charPos.
+ col := charPos - (self characterPositionOfLine:line col:1) + 1.
+ self insertString:aString atLine:line col:col
+!
+
+insertString:aString atLine:lineNr col:colNr
+ "insert the string, aString at line/col;
+ handle cr's correctly"
+
+ |start "{ Class: SmallInteger }"
+ stop "{ Class: SmallInteger }"
+ end "{ Class: SmallInteger }"
+ subString c
+ l "{ Class: SmallInteger }" |
+
+ aString isNil ifTrue:[^ self].
+ (aString includes:(Character cr)) ifFalse:[
+ ^ self insertStringWithoutCRs:aString atLine:lineNr col:colNr
+ ].
+
+ l := lineNr.
+ c := colNr.
+ start := 1.
+ end := aString size.
+ [start <= end] whileTrue:[
+ stop := aString indexOf:(Character cr) startingAt:start.
+ stop == 0 ifTrue:[
+ stop := end + 1
+ ].
+ subString := aString copyFrom:start to:(stop - 1).
+ self insertStringWithoutCRs:subString atLine:l col:c.
+ (stop <= end) ifTrue:[
+ c := c + subString size.
+ self insert:(Character cr) atLine:l col:c.
+ l := l + 1.
+ c := 1
+ ].
+ start := stop + 1
+ ]
+
+ "Modified: / 10.6.1998 / 19:03:59 / cg"
+!
+
+insertStringAtCursor:aString
+ "insert the argument, aString at cursor position
+ handle cr's correctly. A nil argument is interpreted as an empty line."
+
+ aString isNil ifTrue:[
+ "new:"
+ self insertCharAtCursor:(Character cr).
+ ^ self
+ ].
+ (aString includes:(Character cr)) ifFalse:[
+ ^ self insertStringWithoutCRsAtCursor:aString
+ ].
+
+ self insertLines:aString asStringCollection withCR:false.
+
+ "Modified: / 10.6.1998 / 19:03:21 / cg"
+!
+
+insertStringWithoutCRs:aString atLine:lineNr col:colNr
+ "insert aString (which has no crs) at lineNr/colNr"
+
+ self withoutRedrawInsertStringWithoutCRs:aString atLine:lineNr col:colNr.
+ shown ifTrue:[
+ gc font hasOverlappingCharacters ifTrue:[
+ self invalidateLine:lineNr.
+ ] ifFalse:[
+ self redrawLine:lineNr from:colNr
+ ]
+ ]
+
+ "Modified: / 09-11-2010 / 13:43:03 / cg"
+!
+
+insertStringWithoutCRsAtCursor:aString
+ "insert a string (which has no crs) at cursor position
+ - advance cursor"
+
+ |wasOn oldLen newLen deltaLen|
+
+ aString size > 0 ifTrue:[
+ wasOn := self hideCursor.
+ (aString includes:Character tab) ifTrue:[
+ self checkForExistingLine:cursorLine.
+ oldLen := (list at:cursorLine) size.
+ self insertString:aString atLine:cursorLine col:cursorCol.
+ newLen := (list at:cursorLine) size.
+ deltaLen := newLen - oldLen.
+ ] ifFalse:[
+ self insertString:aString atLine:(cursorLine ? 1) col:cursorCol.
+ deltaLen := aString size.
+ ].
+ self setCursorCol:(cursorCol + deltaLen).
+ wasOn ifTrue:[self showCursor]
+ ]
+
+ "Modified: / 10.6.1998 / 20:43:52 / cg"
+!
+
+insertTabAtCursor
+ "insert spaces to next tab"
+
+ |wasOn nextTab|
+
+ wasOn := self hideCursor.
+ nextTab := self nextTabAfter:cursorCol.
+ self insertStringAtCursor:(String new:(nextTab - cursorCol)).
+ self makeCursorVisibleAndShowCursor:wasOn.
+!
+
+joinLines
+ "join lines (remove line-break)"
+
+ self checkModificationsAllowed ifFalse:[ ^ self].
+
+ self
+ undoableDo:[
+ |line col lineLen|
+
+ line := cursorLine.
+ col := cursorCol.
+ lineLen := (list at:line) size.
+ col > lineLen ifTrue:[
+ self insertString:(String new:col-lineLen) atLine:line col:col+1.
+ ] ifFalse:[
+ self deleteCharsAtLine:line fromCol:col toCol:lineLen.
+ ].
+ self mergeLine:line removeBlanks:true.
+ self cursorLine:line col:col.
+ ]
+ info:'Join'
+!
+
+mergeLine:lineNr
+ "merge line lineNr with line lineNr+1"
+
+ self mergeLine:lineNr removeBlanks:true
+
+ "Modified: 9.9.1997 / 09:28:03 / cg"
+!
+
+mergeLine:lineNr removeBlanks:removeBlanks
+ "merge line lineNr with line lineNr+1"
+
+ |len|
+
+ self checkModificationsAllowed ifFalse:[ ^ self].
+
+ len := (self listAt:lineNr) size.
+ self nonUndoableDo:[
+ self basicMergeLine:lineNr removeBlanks:removeBlanks.
+ ].
+ self addUndo:(PasteString new line:lineNr col:len+1 string:(Character cr asString) selected:false).
+!
+
+parenthizeSelectionWith:openingCharacter and:closingCharacter
+ |newSelectionEnd|
+
+ self hasSelection ifFalse:[^ self].
+
+ newSelectionEnd := selectionEndCol.
+
+ (self characterAtLine:selectionStartLine col:selectionStartCol) == openingCharacter ifTrue:[
+ (self characterAtLine:selectionEndLine col:selectionEndCol) == closingCharacter ifTrue:[
+ self deleteCharAtLine:selectionEndLine col:selectionEndCol.
+ newSelectionEnd := newSelectionEnd-1.
+ ].
+ self deleteCharAtLine:selectionStartLine col:selectionStartCol.
+ selectionStartLine == selectionEndLine ifTrue:[
+ newSelectionEnd := newSelectionEnd-1.
+ ]
+ ] ifFalse:[
+ self insert:closingCharacter atLine:selectionEndLine col:selectionEndCol+1.
+ newSelectionEnd := newSelectionEnd+1.
+ self insert:openingCharacter atLine:selectionStartLine col:selectionStartCol.
+ selectionStartLine == selectionEndLine ifTrue:[
+ newSelectionEnd := newSelectionEnd+1.
+ ]
+ ].
+ self
+ selectFromLine:selectionStartLine col:selectionStartCol
+ toLine:selectionEndLine col:newSelectionEnd.
+!
+
+removeTrailingBlankLines
+ "remove all blank lines at end of text"
+
+ |lastLine "{ Class: SmallInteger }"
+ line finished|
+
+ lastLine := list size.
+ finished := false.
+ [finished] whileFalse:[
+ (lastLine <= 1) ifTrue:[
+ finished := true
+ ] ifFalse:[
+ line := list at:lastLine.
+ line notNil ifTrue:[
+ line isBlank ifTrue:[
+ self basicListAt:lastLine put:nil.
+ line := nil
+ ]
+ ].
+ line notNil ifTrue:[
+ finished := true
+ ] ifFalse:[
+ lastLine := lastLine - 1
+ ]
+ ]
+ ].
+ (lastLine ~~ list size) ifTrue:[
+ list grow:lastLine.
+"/ self textChanged
+ ]
+!
+
+replace:aCharacter atLine:lineNr col:colNr
+ "replace a single character at lineNr/colNr"
+
+ |originalChar|
+
+ originalChar := self characterAtLine:lineNr col:colNr.
+ self basicReplace:aCharacter atLine:lineNr col:colNr.
+ self addUndo:(ReplaceCharacters line:lineNr col:colNr character:originalChar info:'replace').
+!
+
+replace:patternArg by:replacePatternArg all:all ignoreCase:ignoreCase
+ |pattern replacePattern|
+
+ pattern := patternArg string.
+ replacePattern := replacePatternArg string.
+ (pattern notEmpty and:[ replacePattern notEmpty ]) ifTrue:[
+ self rememberSearchPattern:pattern.
+ self rememberSearchPattern:replacePattern.
+ LastSearchIgnoredCase := ignoreCase.
+ self
+ undoableDo:[
+ all ifTrue:[
+ self
+ replaceString:pattern
+ to:replacePattern
+ ignoreCase:ignoreCase
+ ] ifFalse:[
+ (self selectionAsString notNil
+ and:[ self selectionAsString sameAs:pattern caseSensitive:ignoreCase not ])
+ ifTrue:[
+ self replaceSelectionBy:replacePattern.
+ self
+ search:pattern
+ ignoreCase:ignoreCase
+ forward:(lastSearchDirection = #forward).
+ ].
+ ]
+ ]
+ info:'Replace'
+ ]
+
+ "Created: / 11-07-2006 / 11:19:57 / fm"
+!
+
+replaceCharAtCursor:aCharacter
+ "replace a single character at cursor-position - advance cursor"
+
+ |wasOn|
+
+ wasOn := self hideCursor.
+ aCharacter == (Character cr) ifTrue:[
+ self cursorReturn
+ ] ifFalse:[
+ self replace:aCharacter atLine:cursorLine col:cursorCol.
+ self cursorRight.
+ ].
+ self makeCursorVisibleAndShowCursor:wasOn.
+
+ "Created: 6.3.1996 / 12:27:42 / cg"
+!
+
+replaceContentsWith:newContents
+ "replace everything"
+
+ |originalContents|
+
+ originalContents := self contents.
+ self contents:newContents keepUndoHistory:true.
+ self addUndo:(ReplaceContents text:originalContents info:'replace').
+!
+
+replaceFromCharacterPosition:charPos1 to:charPos2 with:newString
+ "replace a substring at a character position"
+
+ "/ sigh - insert first, to avoid troible due to shifing-in virtual line ends
+ self insertString:newString atCharacterPosition:charPos1.
+ self deleteFromCharacterPosition:charPos1+newString size to:charPos2+newString size.
+!
+
+replaceLine:lineNr with:newText
+ "replace a line at lineNr"
+
+ |originalLine|
+
+ originalLine := self listAt:lineNr.
+ originalLine isNil ifTrue:[
+ self checkForExistingLine:lineNr
+ ].
+ self list at:lineNr put:newText.
+ self addUndo:(ReplaceLine line:lineNr string:originalLine info:'replace').
+ self invalidateLine:lineNr.
+
+ "Modified: / 12-04-2007 / 09:31:33 / cg"
+!
+
+replaceLines:lines withCR:withCr
+ "replace a bunch of lines at cursor position. Cursor
+ is moved behind replacement.
+ If withCr is true, move to the beginning of the next line
+ after the last line"
+
+ |line col nLines wasOn|
+
+ lines notNil ifTrue:[
+ wasOn := self hideCursor.
+ nLines := lines size.
+ line := cursorLine.
+ col := cursorCol.
+ lines keysAndValuesDo:[:i :l |
+ self replaceString:(l ? '') atLine:line col:col.
+ (i ~~ nLines or:[withCr]) ifTrue:[
+ line := line + 1.
+ col := 1.
+ ] ifFalse:[
+ col := col + (l size).
+ ]
+ ].
+ self cursorLine:line col:col.
+ self makeCursorVisibleAndShowCursor:wasOn.
+ "/ wasOn ifTrue:[self showCursor].
+ ]
+
+ "Created: / 18-05-1996 / 15:32:06 / cg"
+ "Modified: / 25-07-2013 / 17:00:53 / cg"
+!
+
+replaceSelectionBy:something
+ "delete the selection (if any) and insert something, a character or string;
+ leave cursor after insertion"
+
+ self replaceSelectionBy:something keepCursor:false select:false
+!
+
+replaceSelectionBy:something keepCursor:keep
+ "delete the selection (if any) and insert something, a character or string;
+ leave cursor after insertion or leave it, depending on keep"
+
+ self replaceSelectionBy:something keepCursor:keep select:false
+
+ "Modified: 9.10.1996 / 16:14:35 / cg"
+!
+
+replaceSelectionBy:something keepCursor:keep select:selectNewText
+ "delete the selection (if any) and insert something, a character or string;
+ leave cursor after insertion or leave it, depending on keep.
+ If selectNewText is true, select the new text; otherwise deselect"
+
+ |sel l c selStartLine selStartCol|
+
+ l := cursorLine.
+ c := cursorCol.
+
+ sel := self selectionAsString.
+ sel isNil ifTrue:[
+ selStartLine := l.
+ selStartCol := c.
+ ] ifFalse:[
+ selStartLine := selectionStartLine.
+ selStartCol := selectionStartCol.
+
+ self setLastStringToReplace: sel.
+
+ self deleteSelection.
+ replacing := true.
+ lastReplacementInfo rememberReplacement.
+ lastReplacementInfo lastReplacement: ''.
+ lastReplacementInfo stillCollectingInput:true.
+ undoSupport actionInfo:'replace'.
+ ].
+
+ something isCharacter ifTrue:[
+ lastReplacementInfo lastReplacement notNil ifTrue:[
+ lastReplacementInfo stillCollectingInput ifTrue:[
+ lastReplacementInfo lastReplacement: (lastReplacementInfo lastReplacement copyWith:something).
+ ].
+ ].
+ self isInInsertMode ifTrue:[
+ self insertCharAtCursor:something
+ ] ifFalse:[
+ self replaceCharAtCursor:something
+ ]
+ ] ifFalse:[
+ something isString ifTrue:[
+ lastReplacementInfo lastReplacement: something.
+ self isInInsertMode ifTrue:[
+ self insertStringAtCursor:something
+ ] ifFalse:[
+ self replaceStringAtCursor:something
+ ]
+ ] ifFalse:[
+ Transcript showCR:'EditTextView: non String-or-Character in replace'.
+ ].
+ ].
+ keep ifTrue:[
+ self cursorLine:l col:c
+ ].
+ selectNewText ifTrue:[
+ self selectFromLine:selStartLine col:selStartCol toLine:cursorLine col:cursorCol-1
+ ]
+
+ "Modified: 9.10.1996 / 16:14:35 / cg"
+!
+
+replaceString:aString atLine:lineNr col:colNr
+ "replace multiple characters starting at lineNr/colNr.
+ This is not prepared to encounter special chars (except TAB) in the string."
+
+ |originalString|
+
+ self checkModificationsAllowed ifFalse:[ ^ self].
+
+ originalString := self textFromLine:lineNr col:colNr toLine:lineNr col:colNr+aString size-1.
+
+ self basicReplaceString:aString atLine:lineNr col:colNr.
+ self addUndo:(ReplaceCharacters line:lineNr col:colNr characters:originalString info:'replace').
+!
+
+replaceString:aString to:aNewString ignoreCase:ignoreCase
+ |continue count|
+
+ self cursorToTop.
+ self selectFromBeginning.
+ count := 0.
+ continue := true.
+ [ continue ] whileTrue:[
+ (self selectionAsString notNil
+ and:[ self selectionAsString sameAs:aString caseSensitive:ignoreCase not ])
+ ifTrue:[
+ self replaceSelectionBy:aNewString.
+ count := count + 1.
+ ].
+ self
+ searchFwd:aString
+ ignoreCase:ignoreCase
+ ifAbsent:[
+ Dialog information:('%1 has been replaced by %2 %3 times'
+ bindWith:aString with:aNewString with:count).
+ continue := false.
+ ].
+ ].
+
+ "Created: / 10-07-2006 / 16:42:48 / fm"
+!
+
+replaceStringAtCursor:aString
+ "replace multiple characters at cursor-position - advance cursor"
+
+ |wasOn i1 i2|
+
+ wasOn := self hideCursor.
+ (aString includes:Character tab) ifTrue:[
+ "/ need special care for TAB (to move cursor correctly)
+ i1 := 1.
+ [i1 ~~ 0] whileTrue:[
+ i2 := aString indexOf:Character tab startingAt:i1.
+ i2 ~~ 0 ifTrue:[
+ i1 ~~ i2 ifTrue:[
+ self replaceString:(aString copyFrom:i1 to:i2-1) atLine:cursorLine col:cursorCol.
+ self cursorCol:(cursorCol + (i2 - i1)).
+ ].
+ self replaceTABAtCursor.
+ i2 := i2 + 1.
+ ] ifFalse:[
+ self replaceString:(aString copyFrom:i1) atLine:cursorLine col:cursorCol.
+ self cursorCol:(cursorCol + (aString size - i1 + 1)).
+ ].
+ i1 := i2.
+ ]
+ ] ifFalse:[
+ self replaceString:aString atLine:cursorLine col:cursorCol.
+ self cursorCol:(cursorCol + aString size).
+ ].
+ self makeCursorVisibleAndShowCursor:wasOn.
+
+ "Created: / 9.6.1998 / 20:33:20 / cg"
+ "Modified: / 20.6.1998 / 19:41:02 / cg"
+!
+
+replaceTABAtCursor
+ "replace a single character at cursor-position by a TAB character"
+
+ |wasOn nextTab|
+
+ wasOn := self hideCursor.
+ nextTab := self nextTabAfter:cursorCol.
+ self replaceStringAtCursor:(String new:(nextTab - cursorCol)).
+ self makeCursorVisibleAndShowCursor:wasOn.
+
+ "Created: / 12.6.1998 / 21:53:23 / cg"
+!
+
+selectWordBeforeCursor
+ "select the word to the left of cursor"
+
+ |savCursorLine savCursorCol beginCol beginLine endCol endLine|
+
+ savCursorLine := cursorLine.
+ savCursorCol := cursorCol.
+
+ endCol := cursorCol-1.
+ endLine := cursorLine.
+ self cursorToPreviousWord.
+ beginCol := cursorCol.
+ beginLine := cursorLine.
+ self cursorLine:savCursorLine col:savCursorCol.
+ self selectFromLine:beginLine col:beginCol toLine:endLine col:endCol.
+
+ "Created: / 14-06-2011 / 14:46:35 / cg"
+!
+
+splitLine:lineNr before:colNr
+ "split the line linNr before colNr; the right part (from colNr)
+ is cut off and inserted after lineNr; the view is redrawn"
+
+ self basicSplitLine:lineNr before:colNr.
+ self addUndo:(DeleteRange line1:lineNr col1:colNr line2:lineNr+1 col2:0 info:'split').
+!
+
+withoutRedrawAt:lineNr put:aString
+ "replace a line at lineNr"
+
+ |originalLine|
+
+ originalLine := self listAt:lineNr.
+ self addUndo:(ReplaceLine line:lineNr string:originalLine info:'replace').
+ super withoutRedrawAt:lineNr put:aString.
+!
+
+withoutRedrawInsertLine:aString before:lineNr
+ "insert the argument, aString before line lineNr; the string
+ becomes line lineNr; everything else is moved down; the view
+ is not redrawn"
+
+ self basicWithoutRedrawInsertLines:{ aString } from:1 to:1 before:lineNr.
+ self addUndo:(DeleteRange line1:lineNr col1:1 line2:lineNr+1 col2:0 info:'insert').
+!
+
+withoutRedrawInsertLines:lines from:start to:end before:lineNr
+ "insert a bunch of lines before line lineNr; the view is not redrawn"
+
+ self basicWithoutRedrawInsertLines:lines from:start to:end before:lineNr.
+ self isReadOnly ifFalse:[
+ self addUndo:(DeleteRange line1:lineNr col1:1 line2:lineNr+end-start+1 col2:0 info:'insert').
+ ].
+!
+
+withoutRedrawInsertStringWithoutCRs:aString atLine:lineNr col:colNr
+ "insert aString (which has no crs) at lineNr/colNr"
+
+ self basicWithoutRedrawInsertStringWithoutCRs:aString atLine:lineNr col:colNr.
+ self addUndo:(DeleteRange line1:lineNr col1:colNr line2:lineNr col2:colNr+aString size-1 info:'insert').
+!
+
+wrapLines
+ "wrap lines (insert line-break)"
+
+ |lineLength answerString string|
+
+ self checkModificationsAllowed ifFalse:[ ^ self].
+
+ self hasSelection ifFalse:[
+ self selectLine:cursorLine.
+ ].
+ string := self selectionAsString.
+ string isEmptyOrNil ifTrue:[
+ Dialog information:(resources string:'Nothing selected.').
+ ^ self.
+ ].
+
+ answerString := Dialog request:(resources string:'Line length (wrap after how many chars)?') initialAnswer:80.
+ lineLength := Number readFrom:answerString onError:nil.
+ lineLength isNil ifTrue:[^ self].
+ lineLength < 1 ifTrue:[
+ lineLength := 1.
+ ].
+
+ self
+ undoableDo:[
+ |inStream line col lineLen lastGoodCol lastStartCol word|
+
+ line := selectionStartLine.
+ col := selectionStartCol.
+
+ self cutSelection.
+ self cursorLine:line col:col.
+
+ lastGoodCol := col.
+
+ inStream := string readStream.
+ [ inStream atEnd ] whileFalse:[
+ [inStream atEnd not and:[inStream peek isSeparator]] whileTrue:[ inStream next ].
+ word := WriteStream on:(String new:10).
+ [inStream atEnd not and:[inStream peek isSeparator not]] whileTrue:[ word nextPut:inStream next ].
+ (col + 1 + word size > lineLength) ifTrue:[
+ self insertCharAtCursor:(Character cr).
+ col := 1.
+ ] ifFalse:[
+ col ~~ 1 ifTrue:[
+ self insertStringAtCursor:' '.
+ col := col + 1.
+ ]
+ ].
+ self insertStringAtCursor:word contents.
+ col := col + word size.
+ ].
+ ]
+ info:'Wrap'
+
+ "Modified: / 01-03-2012 / 19:56:22 / cg"
+! !
+
+!EditTextView methodsFor:'editing-basic'!
+
+basicDeleteCharsAtLine:lineNr fromCol:startCol toCol:endCol
+ "delete characters from startCol to endCol in line lineNr"
+
+ |line lineSize newLine start stop prevWidth newWidth|
+
+ self unselect.
+
+ cursorLine == lineNr ifTrue:[
+ cursorCol >= startCol ifTrue:[
+ cursorCol >= endCol ifTrue:[
+ cursorCol := startCol.
+ ] ifFalse:[
+ cursorCol := cursorCol - (endCol - startCol + 1).
+ ]
+ ].
+ ].
+
+ line := self listAt:lineNr.
+
+ (self checkModificationsAllowed and:[line notNil]) ifFalse:[^ self].
+
+ lineSize := line size.
+
+ startCol == 0 ifFalse:[ start := startCol ] ifTrue:[ start := 1 ].
+ endCol > lineSize ifFalse:[ stop := endCol ] ifTrue:[ stop := lineSize ].
+
+ stop >= start ifTrue:[
+ start ~~ 1 ifTrue:[ newLine := line copyFrom:1 to:(start-1) ]
+ ifFalse:[ newLine := '' ].
+
+ stop == lineSize ifFalse:[
+ line bitsPerCharacter > newLine bitsPerCharacter ifTrue:[
+ newLine := line string species fromString:newLine.
+ ].
+ newLine := newLine, (line copyFrom:(stop + 1) to:lineSize)
+ ].
+
+ (trimBlankLines and:[newLine isBlank]) ifTrue:[
+ newLine := nil
+ ].
+
+ prevWidth := self widthOfLine:lineNr.
+
+ self basicListAt:lineNr put:newLine.
+
+ (prevWidth = widthOfWidestLine) ifTrue:[
+ "/ remember old width of this line,
+ "/ only clear widthOfWidestLine, if this lines
+ "/ length was (one of) the longest.
+ "/ avoids slow delete with huge texts.
+ widthOfWidestLine := nil. "i.e. unknown"
+
+ "/ scroll left if reqiured
+ viewOrigin x > 0 ifTrue:[
+ newWidth := self widthOfLine:lineNr.
+ newWidth < (viewOrigin x + width) ifTrue:[
+ self scrollHorizontalTo:(newWidth
+ - width
+ + margin + margin
+ + (gc font widthOf:' '))
+ ]
+ ].
+ self textChanged.
+ ] ifFalse:[
+ self textChanged "/ textChangedButNoSizeChange
+ ].
+ gc font hasOverlappingCharacters ifTrue:[
+ self invalidateLine:lineNr.
+ ] ifFalse:[
+ self redrawLine:lineNr from:start.
+ ].
+ ].
+
+ "Modified: / 09-11-2010 / 13:42:45 / cg"
+!
+
+basicDeleteFromLine:startLineNr toLine:endLineNr
+ "delete some lines"
+
+ |wasOn nLines|
+
+ self checkModificationsAllowed ifFalse:[ ^ self].
+ list isNil ifTrue:[^ self].
+
+ wasOn := self hideCursor.
+
+ "/ isnt this the same as:
+ "/ self deleteLinesWithoutRedrawFrom:startLineNr to:endLineNr.
+ startLineNr <= list size ifTrue:[
+ self basicListRemoveFromIndex:startLineNr toIndex:(endLineNr min:list size).
+ ].
+ "/ TODO: remember old maxwidth of linerange,
+ "/ only clear widthOfWidestLine, if this max
+ "/ length was (one of) the longest.
+ "/ avoids slow delete with huge texts.
+ widthOfWidestLine := nil. "/ i.e. unknown
+ self textChanged.
+
+ self redrawFromLine:startLineNr.
+
+ nLines := list size.
+ (firstLineShown >= nLines) ifTrue:[
+ self makeLineVisible:nLines
+ ].
+ wasOn ifTrue:[self showCursor].
+
+ "Modified: / 10-11-1998 / 23:55:05 / cg"
+ "Modified: / 18-03-2011 / 18:26:23 / az"
+!
+
+basicDeleteLineWithoutRedraw:lineNr
+ "delete line - no redraw;
+ return true, if something was really deleted"
+
+ self checkModificationsAllowed ifFalse:[ ^ false].
+
+ (list isNil or:[lineNr > list size]) ifTrue:[^ false].
+ list removeIndex:lineNr.
+ "/ TODO: remember old maxwidth of linerange,
+ "/ only clear widthOfWidestLine, if this max
+ "/ length was (one of) the longest.
+ "/ avoids slow delete with huge texts.
+ widthOfWidestLine := nil. "/ i.e. unknown
+ self textChanged.
+ ^ true
+
+ "Modified: / 10.11.1998 / 23:53:24 / cg"
+!
+
+basicInsert:aCharacter atLine:lineNr col:colNr
+ "insert a single character at lineNr/colNr;
+ set emphasis to character at current position"
+
+ |line lineSize newLine drawCharacterOnly attribute oldClip x y|
+
+ self checkModificationsAllowed ifFalse:[ ^ self].
+
+ aCharacter == (Character cr) ifTrue:[
+ self splitLine:lineNr before:colNr.
+ ^ self
+ ].
+
+ drawCharacterOnly := false.
+ self checkForExistingLine:lineNr.
+ line := list at:lineNr.
+ lineSize := line size.
+
+ self st80EditMode ifFalse:[
+ (trimBlankLines
+ and:[colNr > lineSize
+ and:[aCharacter == Character space]]) ifTrue:[
+ ^ self
+ ]
+ ].
+
+ (lineSize == 0) ifTrue:[
+ newLine := aCharacter asString species new:colNr.
+ drawCharacterOnly := true
+ ] ifFalse: [
+ (colNr > lineSize) ifTrue: [
+ colNr == (lineSize +1) ifTrue:[
+ attribute := line emphasisAt:lineSize
+ ].
+ newLine := line species new:colNr.
+ newLine replaceFrom:1 to:lineSize with:line startingAt:1.
+ drawCharacterOnly := true
+ ] ifFalse: [
+ attribute := line emphasisAt:colNr.
+ newLine := line species new:(lineSize + 1).
+
+ newLine replaceFrom:1 to:(colNr - 1) with:line startingAt:1.
+ newLine replaceFrom:(colNr + 1) to:(lineSize + 1) with:line startingAt:colNr
+ ]
+ ].
+
+ aCharacter asString bitsPerCharacter > newLine bitsPerCharacter ifTrue:[
+ newLine := aCharacter asString species fromString:newLine.
+ line isText ifTrue:[
+ newLine := newLine asText
+ ]
+ ].
+ newLine at:colNr put:aCharacter.
+
+ attribute notNil ifTrue:[
+ newLine emphasisAt:colNr put:attribute.
+ ].
+
+ aCharacter == (Character tab) ifTrue:[
+ newLine := self withTabsExpanded:newLine.
+ drawCharacterOnly := false
+ ].
+
+ self basicListAt:lineNr put:(newLine ifNil:[newLine] ifNotNil:[newLine asSingleByteStringIfPossible]).
+ widthOfWidestLine notNil ifTrue:[
+ widthOfWidestLine := widthOfWidestLine max:(self widthOfLineString:newLine).
+ ].
+ self textChanged.
+ shown ifTrue:[
+ "/ care for italic text - in this case, we must also
+ "/ redraw the character before the insertion in order
+ "/ to fix the slanted piece of the character.
+ "/ (but we must clip, to avoid destoying the character before)
+ (newLine notNil and:[newLine isText]) ifTrue:[
+ colNr > 1 ifTrue:[
+ cursorVisibleLine notNil ifTrue:[
+ oldClip := self clippingRectangleOrNil.
+ x := (self xOfCol:colNr-1 inVisibleLine:cursorVisibleLine) - viewOrigin x.
+ y := self yOfVisibleLine:cursorVisibleLine.
+
+ gc font hasOverlappingCharacters ifTrue:[
+ self invalidateLine:lineNr.
+ ] ifFalse:[
+ drawCharacterOnly ifTrue:[
+ self clippingRectangle:(x@y extent:((gc font width * 2) @ fontHeight)).
+ self redrawLine:lineNr from:colNr-1 to:colNr
+ ] ifFalse:[
+ self clippingRectangle:(x@y extent:((width - x) @ fontHeight)).
+ self redrawLine:lineNr from:colNr-1
+ ].
+ self clippingRectangle:oldClip.
+ ].
+ ].
+ ^ self.
+ ].
+ ].
+ gc font hasOverlappingCharacters ifTrue:[
+ self invalidateLine:lineNr.
+ ] ifFalse:[
+ drawCharacterOnly ifTrue:[
+ self redrawLine:lineNr col:colNr
+ ] ifFalse:[
+ self redrawLine:lineNr from:colNr
+ ]
+ ]
+ ]
+
+ "Modified: / 09-11-2010 / 13:43:18 / cg"
+!
+
+basicListAt:lineNr put:newLine
+ "redefinable for special subclasses (with virtual list)"
+
+ list at:lineNr put:newLine.
+!
+
+basicListRemoveFromIndex:startLineNr toIndex:endLineNr
+ "redefinable for special subclasses (with virtual list)"
+
+ list removeFromIndex:startLineNr toIndex:(endLineNr min:list size).
+!
+
+basicMergeLine:lineNr removeBlanks:removeBlanks
+ "merge line lineNr with line lineNr+1"
+
+ |leftPart rightPart bothParts nextLineNr i|
+
+ (list notNil and:[(list size) >= lineNr]) ifFalse:[
+ "/ empty list or beyond end of text
+ ^ self
+ ].
+ leftPart := self listAt:lineNr.
+
+ leftPart isNil ifTrue:[
+ leftPart := ''.
+ autoIndent ifTrue:[
+ (i := self leftIndentForLine:cursorLine) == 0 ifFalse:[
+ leftPart := String new:i
+ ]
+ ]
+ ].
+ self cursorLine:lineNr col:((leftPart size) + 1).
+ nextLineNr := self validateCursorLine:(lineNr + 1).
+
+ nextLineNr > (list size) ifFalse:[
+ (rightPart := self listAt:nextLineNr) isNil ifTrue:[
+ rightPart := ''
+ ] ifFalse:[
+ removeBlanks ifTrue:[
+ rightPart := rightPart withoutLeadingSeparators.
+ ]
+ ].
+
+ bothParts := leftPart , rightPart.
+ (trimBlankLines and:[bothParts isBlank]) ifTrue:[bothParts := nil].
+ self basicListAt:lineNr put:bothParts.
+ self redrawLine:lineNr.
+ self deleteLine:nextLineNr
+ ]
+
+ "Created: 9.9.1997 / 09:27:38 / cg"
+ "Modified: 9.9.1997 / 09:28:27 / cg"
+!
+
+basicReplace:aCharacter atLine:lineNr col:colNr
+ "replace a single character at lineNr/colNr"
+
+ |line lineSize newLine drawCharacterOnly|
+
+ self checkModificationsAllowed ifFalse:[ ^ self].
+
+ aCharacter == (Character cr) ifTrue:[
+ ^ self
+ ].
+
+ drawCharacterOnly := true.
+ self checkForExistingLine:lineNr.
+ line := list at:lineNr.
+ lineSize := line size.
+
+ (trimBlankLines
+ and:[colNr > lineSize
+ and:[aCharacter == Character space]]) ifTrue:[
+ ^ self
+ ].
+
+ (lineSize == 0) ifTrue:[
+ newLine := aCharacter asString species new:colNr.
+ ] ifFalse: [
+ (colNr > lineSize) ifTrue: [
+ newLine := line species new:colNr.
+ newLine replaceFrom:1 to:lineSize with:line startingAt:1.
+ ] ifFalse: [
+ newLine := line copy.
+ ]
+ ].
+ newLine at:colNr put:aCharacter.
+ aCharacter == (Character tab) ifTrue:[
+ newLine := self withTabsExpanded:newLine.
+ drawCharacterOnly := false
+ ].
+ self basicListAt:lineNr put:(newLine ifNil:[newLine] ifNotNil:[newLine asSingleByteStringIfPossible]).
+ widthOfWidestLine notNil ifTrue:[
+ widthOfWidestLine := widthOfWidestLine max:(self widthOfLineString:newLine).
+ ].
+ self textChanged.
+ shown ifTrue:[
+ gc font hasOverlappingCharacters ifTrue:[
+ self invalidateLine:lineNr.
+ ] ifFalse:[
+ drawCharacterOnly ifTrue:[
+ self redrawLine:lineNr col:colNr
+ ] ifFalse:[
+ self redrawLine:lineNr from:colNr
+ ]
+ ]
+ ]
+
+ "Created: / 06-03-1996 / 12:29:20 / cg"
+ "Modified: / 09-11-2010 / 13:42:54 / cg"
+!
+
+basicReplaceString:aString atLine:lineNr col:colNr
+ "replace multiple characters starting at lineNr/colNr.
+ This is not prepared to encounter special chars (except TAB)
+ in the string."
+
+ |line lineSize newLine endCol|
+
+ self checkModificationsAllowed ifFalse:[ ^ self].
+
+ self checkForExistingLine:lineNr.
+ line := list at:lineNr.
+ lineSize := line size.
+
+ endCol := colNr + aString size - 1.
+ (lineSize == 0) ifTrue:[
+ newLine := aString species new:endCol.
+ ] ifFalse: [
+ (endCol > lineSize) ifTrue: [
+ aString isText ifTrue:[
+ newLine := aString species new:endCol.
+ ] ifFalse:[
+ newLine := line species new:endCol.
+ ].
+ newLine replaceFrom:1 to:lineSize with:line startingAt:1.
+ ] ifFalse: [
+ aString isText ifTrue:[
+ newLine := aString species new:line size.
+ newLine replaceFrom:1 to:lineSize with:line startingAt:1.
+ ] ifFalse:[
+ newLine := line copy.
+ ]
+ ]
+ ].
+ newLine replaceFrom:colNr with:aString.
+ (aString includes:(Character tab)) ifTrue:[
+ newLine := self withTabsExpanded:newLine.
+ ].
+ self basicListAt:lineNr put:(newLine ifNil:[newLine] ifNotNil:[newLine asSingleByteStringIfPossible]).
+ widthOfWidestLine notNil ifTrue:[
+ widthOfWidestLine := widthOfWidestLine max:(self widthOfLineString:newLine).
+ ].
+ self textChanged.
+ shown ifTrue:[
+ gc font hasOverlappingCharacters ifTrue:[
+ self invalidateLine:lineNr.
+ ] ifFalse:[
+ self redrawLine:lineNr from:colNr
+ ]
+ ]
+
+ "Created: / 11-06-1998 / 10:38:32 / cg"
+ "Modified: / 09-11-2010 / 13:42:56 / cg"
+!
+
+basicSplitLine:lineNr before:colNr
+ "split the line linNr before colNr; the right part (from colNr)
+ is cut off and inserted after lineNr; the view is redrawn"
+
+ |line lineSize leftRest rightRest visLine w h mustWait
+ srcY "{ Class: SmallInteger }" |
+
+ list isNil ifTrue:[ ^ self ].
+ lineNr > (list size) ifTrue:[ ^ self ].
+
+ (colNr == 1) ifTrue:[
+ self nonUndoableDo:[
+ self insertLine:nil before:lineNr.
+ ].
+ ^ self
+ ].
+
+ line := list at:lineNr.
+ line notNil ifTrue:[
+ lineSize := line size.
+ (colNr <= lineSize) ifTrue:[
+ rightRest := line copyFrom:colNr to:lineSize.
+ (colNr > 1) ifTrue:[
+ leftRest := line copyTo:(colNr - 1)
+ ]
+ ] ifFalse:[
+ leftRest := line
+ ]
+ ].
+ leftRest notNil ifTrue:[
+ (trimBlankLines and:[leftRest isBlank]) ifTrue:[leftRest := nil]
+ ].
+ self basicListAt:lineNr put:leftRest.
+ self nonUndoableDo:[
+ self withoutRedrawInsertLine:rightRest before:(lineNr + 1).
+ ].
+ visLine := self listLineToVisibleLine:(lineNr).
+ visLine notNil ifTrue:[
+ w := self widthForScrollBetween:lineNr
+ and:(firstLineShown + nLinesShown).
+ srcY := topMargin + (visLine * fontHeight).
+ h := ((nLinesShown - visLine - 1) * fontHeight).
+ (mustWait := (w > 0 and:[h > 0])) ifTrue:[
+ self catchExpose.
+ self
+ copyFrom:self
+ x:textStartLeft y:srcY
+ toX:textStartLeft y:(srcY + fontHeight)
+ width:w
+ height:((nLinesShown - visLine - 1) * fontHeight)
+ async:true.
+ ].
+ self redrawLine:lineNr.
+ self redrawLine:(lineNr + 1).
+ mustWait ifTrue:[self waitForExpose]
+ ].
+ widthOfWidestLine := nil. "/ unknown
+ self textChanged.
+
+ "Modified: / 06-12-2010 / 13:12:55 / cg"
+!
+
+basicWithoutRedrawInsertLines:lines from:start to:end before:lineNr
+ "insert a bunch of lines before line lineNr; the view is not redrawn.
+ Tabs are expanded here with a tab=8 setting (independent of any editor-setting,
+ because the text might have been pasted from an alien view."
+
+ |newLine newLines nLines|
+
+ nLines := end - start + 1.
+ newLines := Array new:(lines size).
+ start to:end do:[:index |
+ newLine := lines at:index.
+ newLine notNil ifTrue:[
+ newLine isString ifTrue:[
+ newLine isBlank ifTrue:[
+ newLine := nil
+ ] ifFalse:[
+ (newLine includes:(Character tab)) ifTrue:[
+ newLine := self withTabs:(ListView tab8Positions) expand:newLine
+ ]
+ ]
+ ]
+ ].
+ newLines at:index put:newLine
+ ].
+ list isNil ifTrue: [
+ list := StringCollection new:(lineNr + nLines + 1)
+ ] ifFalse: [
+ list grow:((list size + nLines) max:(lineNr + nLines - 1))
+ ].
+
+ "I have changed 'replaceFrom:to:with:startingAt:' to correctly handle
+ overlapping copy - if it didn't, we had to use:"
+"
+ index := list size.
+ [index > lineNr] whileTrue: [
+ pIndex := index - 1.
+ list at:index put:(list at:pIndex).
+ index := pIndex
+ ].
+"
+ list replaceFrom:(lineNr + nLines) to:(list size) with:list startingAt:lineNr.
+ list replaceFrom:lineNr to:(lineNr + nLines - 1) with:newLines startingAt:start.
+ self contentsChanged
+
+ "Modified: / 07-10-2011 / 15:55:18 / cg"
+!
+
+basicWithoutRedrawInsertStringWithoutCRs:aString atLine:lineNr col:colNr
+ "insert aString (which has no crs) at lineNr/colNr.
+ Tabs are expanded here with a tab=8 setting (independent of any editor-setting,
+ because the text might have been pasted from an alien view."
+
+ |isText strLen line lineSize newLine stringType sz lineCharWidth stringCharWidth|
+
+ (aString isNil) ifTrue:[ ^ self].
+
+ strLen := aString size.
+ self checkForExistingLine:lineNr.
+
+ stringType := aString string species.
+ isText := aString isText.
+ line := list at:lineNr.
+
+ line notNil ifTrue:[
+ lineSize := line size.
+ line isString ifFalse:[
+ stringType := line species
+ ] ifTrue:[
+ lineCharWidth := line bitsPerCharacter.
+ stringCharWidth := aString bitsPerCharacter.
+ lineCharWidth > stringCharWidth ifTrue:[
+ stringType := line string species
+ ] ifFalse:[
+ stringCharWidth > lineCharWidth ifTrue:[
+ stringType := aString string species
+ ]
+ ].
+ line isText ifTrue:[ isText := true ]
+ ].
+ ] ifFalse:[
+ lineSize := 0
+ ].
+
+ ((colNr == 1) and:[lineSize == 0]) ifTrue: [
+ newLine := aString
+ ] ifFalse:[
+ (lineSize == 0 or:[colNr > lineSize]) ifTrue: [
+ sz := colNr + strLen - 1
+ ] ifFalse:[
+ sz := lineSize + strLen
+ ].
+
+ newLine := stringType new:sz.
+ isText ifTrue:[
+ newLine := Text string:newLine
+ ].
+
+ (lineSize ~~ 0) ifTrue: [
+ (colNr > lineSize) ifTrue: [
+ newLine replaceFrom:1 to:lineSize with:line startingAt:1
+ ] ifFalse: [
+ newLine replaceFrom:1 to:(colNr - 1) with:line startingAt:1.
+ newLine replaceFrom:(colNr + strLen) to:(lineSize + strLen) with:line startingAt:colNr
+ ]
+ ].
+ newLine replaceFrom:(colNr max:1) to:(colNr + strLen - 1) with:aString startingAt:1
+ ].
+
+ (aString includes:(Character tab)) ifTrue:[
+ newLine := self withTabs:(ListView tab8Positions) expand:newLine
+ ].
+
+ self basicListAt:lineNr put:(newLine ifNil:[newLine] ifNotNil:[newLine asSingleByteStringIfPossible]).
+ widthOfWidestLine notNil ifTrue:[
+ widthOfWidestLine := widthOfWidestLine max:(self widthOfLineString:newLine).
+ ].
+ self textChanged.
+
+ "Modified: / 25-01-2012 / 00:37:29 / cg"
+! !
+
+!EditTextView methodsFor:'event handling'!
+
+buttonPress:button x:x y:y
+ "hide the cursor when button is activated"
+
+ hasKeyboardFocus := true.
+ dragIsActive := false.
+
+ completionSupport notNil ifTrue:[
+ "/ also give that guy a chance to close its popup view
+ completionSupport buttonPress:button x:x y:y
+ ].
+
+ cursorShown ifTrue: [
+ self drawCursor
+ ].
+
+ "On X11, be nice and paste PRIMARY when middle click.
+ Note, that middle button on X11 is translated to button
+ 128 in Smalltalk/X - see XWorkstation class>>initializeConstants"
+ (button == #paste and:[self graphicsDevice platformName == #X11]) ifTrue:[
+ self undoableDo:[
+ self paste: (self getClipboardText:#selection).
+ ].
+ ^self.
+ ].
+
+ (button == 1) ifTrue:[
+ self hideCursor
+ ].
+"/ some very old code from times, when a right-click was a paste in X11
+"/
+"/ (button == #paste) ifTrue:[
+"/ self pasteOrReplace.
+"/ ^ self
+"/ ].
+ super buttonPress:button x:x y:y
+
+ "Modified: / 23-03-1999 / 13:51:40 / cg"
+ "Modified (comment): / 17-04-2012 / 21:02:25 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+!
+
+buttonRelease:button x:x y:y
+ "move the cursor to the click-position of previous button press"
+
+ |x1 x2 x2_3 newCursorCol|
+
+ (button == 1) ifTrue:[
+ typeOfSelection := nil.
+
+ dragIsActive ifTrue:[
+ self unselect
+ ].
+ selectionStartLine isNil ifTrue:[
+ clickCol notNil ifTrue:[
+ self cursorMovementAllowed ifTrue:[
+ newCursorCol := clickCol.
+
+ cursorType ~~ #block ifTrue:[
+ clickPos notNil ifTrue:[
+ "/ we do something special, if the text-cursor's type is not a block-cursor
+ "/ (i.e. if its an ibeam).
+ "/ adjust clickCol if the user clicked in the right third of a character.
+ x1 := self xOfCol:clickCol inVisibleLine:clickLine.
+ x2 := self xOfCol:clickCol+1 inVisibleLine:clickLine.
+ x2_3 := x1 + ((x2-x1) * (2/3)).
+ (clickPos x >= x2_3) ifTrue:[ newCursorCol := clickCol+1 ].
+ ].
+ ].
+ self cursorLine:clickLine col:newCursorCol.
+ ].
+ true "self hadSelectionBeforeClick not" ifTrue:[
+ list notEmptyOrNil ifTrue:[
+ UserPreferences current selectAllWhenClickingBeyondEnd ifTrue:[
+ (clickLine >= list size) ifTrue:[
+ (clickLine > (self list size + 2)
+ or:[ clickCol > (list last size + 5) ]) ifTrue:[
+ self selectAll
+ ].
+ ].
+ ]
+ ]
+ ]
+ ]
+ ] ifFalse:[
+ lastStringFromReplaceForNextSearch := nil. "new selection invalidates remembered string"
+ ].
+ self showCursor
+ ].
+ super buttonRelease:button x:x y:y
+
+ "Modified: / 07-03-2012 / 18:48:37 / cg"
+!
+
+cursorKeyPress:key shifted:shifted
+ <resource: #keyboard (#CursorRight #CursorDown #CursorUp #CursorDown)>
+
+ |n|
+
+ self changeTypeOfSelectionTo:nil.
+
+ (key == #CursorRight) ifTrue:[
+ (shifted and:[selectionStartLine isNil]) ifTrue:[
+ selectionStartLine := selectionEndLine := clickStartLine := cursorLine.
+ selectionStartCol := selectionEndCol := clickStartCol := cursorCol.
+ expandingTop := false.
+ self validateNewSelection.
+ self setPrimarySelection.
+ self selectionChanged.
+ self redrawLine:selectionStartLine.
+ ^ self.
+ ].
+
+ selectionStartLine notNil ifTrue:[
+ self cursorMovementAllowed ifTrue:[
+ "/
+ "/ treat the whole selection as cursor
+ "/
+ self setCursorLine:(selectionEndLine ? selectionStartLine).
+ selectionEndCol == 0 ifTrue:[
+ selectionEndCol := 1.
+ ].
+ self setCursorCol:selectionEndCol.
+ shifted ifTrue:[
+ self expandSelectionRight.
+ ^ self
+ ].
+ self unselect; makeCursorVisible.
+ cursorCol == 1 ifTrue:[^ self].
+ ].
+ ].
+ self cursorRight.
+ ^ self
+ ].
+ (key == #CursorDown) ifTrue:[
+ (shifted and:[selectionStartLine isNil]) ifTrue:[
+ selectionStartLine := clickStartLine := cursorLine. selectionEndLine := cursorLine + 1.
+ selectionStartCol := clickStartCol := selectionEndCol := cursorCol.
+ selectionEndCol == 1 ifTrue:[
+ selectionEndCol := 0.
+ ].
+ self validateNewSelection.
+ self selectionChanged.
+ self redrawLine:selectionStartLine.
+ expandingTop := false.
+ self redrawLine:selectionEndLine.
+ ^ self
+ ].
+
+ selectionStartLine notNil ifTrue:[
+ self cursorMovementAllowed ifTrue:[
+ "/
+ "/ treat the whole selection as cursor
+ "/
+ self setCursorLine:(selectionEndLine ? selectionStartLine).
+ self setCursorCol:selectionStartCol.
+ (cursorCol == 0 or:[selectionEndCol == 0]) ifTrue:[
+ self setCursorCol:1.
+ self setCursorLine:(cursorLine - 1).
+ ].
+ self makeCursorVisible.
+
+ shifted ifTrue:[
+ clickLine := cursorLine.
+ clickCol := cursorCol.
+ self expandSelectionDown.
+ ^ self
+ ].
+ self unselect.
+ ].
+ ].
+
+ n := 1 + (self sensor compressKeyPressEventsWithKey:#CursorDown).
+ self cursorDown:n.
+ "/
+ "/ flush keyboard to avoid runaway cursor
+ "/
+ self sensor flushKeyboardFor:self.
+ ^ self
+ ].
+ (key == #CursorLeft or:[key == #CursorUp]) ifTrue:[
+ (shifted and:[selectionStartLine isNil]) ifTrue:[
+ expandingTop := true.
+ key == #CursorLeft ifTrue:[
+ cursorCol > 1 ifTrue:[
+ selectionStartLine := selectionEndLine := clickStartLine := cursorLine.
+ selectionEndCol := clickStartCol := cursorCol-1.
+ selectionStartCol := cursorCol-1.
+ self validateNewSelection.
+ self selectionChanged.
+ self redrawLine:selectionStartLine.
+ ^ self
+ ]
+ ] ifFalse:[
+ cursorLine > 1 ifTrue:[
+ selectionEndLine := clickStartLine := cursorLine.
+ selectionEndCol := selectionStartCol := clickStartCol := cursorCol.
+ selectionStartLine := cursorLine - 1.
+ selectionEndCol == 1 ifTrue:[
+ selectionEndCol := 0.
+ ].
+ self validateNewSelection.
+ self selectionChanged.
+ self redrawFromLine:selectionStartLine to:cursorLine.
+ ^ self
+ ]
+ ]
+ ].
+
+ selectionStartLine notNil ifTrue:[
+ self cursorMovementAllowed ifTrue:[
+ "/
+ "/ treat the whole selection as cursor
+ "/
+ self setCursorLine:selectionStartLine.
+ self setCursorCol:selectionStartCol.
+ (key == #CursorLeft) ifTrue:[
+ self setCursorCol:(cursorCol+1). "/ compensate for followup crsr-left
+ ].
+ self makeCursorVisible.
+
+ shifted ifTrue:[
+ (key == #CursorUp) ifTrue:[
+ clickLine := cursorLine.
+ self expandSelectionUp.
+ ] ifFalse:[
+ self expandSelectionLeft.
+ ].
+ ^ self
+ ].
+ self unselect.
+ ].
+ ].
+ (key == #CursorLeft) ifTrue:[
+ self cursorLeft. ^self
+ ].
+ (key == #CursorUp) ifTrue:[
+ n := 1 + (self sensor compressKeyPressEventsWithKey:#CursorUp).
+ self cursorUp:n.
+ "/
+ "/ flush keyboard to avoid runaway cursor
+ "/
+ self sensor flushKeyboardFor:self.
+ ^ self
+ ].
+ ].
+
+ "Modified: / 17-04-2012 / 21:01:15 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+!
+
+doKeyPress:key x:x y:y
+ "handle keyboard input"
+
+ <resource: #keyboard (#Paste #Insert #PasteFromHistory #Cut #Again #AgainForAll
+ #Replace #Undo #Redo #Accept
+ #Delete #BasicDelete #BackSpace #BasicBackspace
+ #DeleteSpaces #Join
+ #SearchMatchingParent #SelectMatchingParents
+ #SelectWord #ExpandSelectionByWord
+ #SelectToEnd #SelectFromBeginning
+ #SelectLine #ExpandSelectionByLine
+ #BeginOfLine #EndOfLine #NextWord #PreviousWord
+ #CursorRight #CursorDown #CursorLeft #CursorUp
+ #Return #Tab #BackTab #NonInsertingTab #Escape
+ #GotoLine #BeginOfText #EndOfText
+ #InsertLine #DeleteLine
+ #SelectLineFromBeginning
+ #LearnKeyboardMacro #ExecuteKeyboardMacro #ToggleInsertMode
+ #OpenSpecialCharacterWindow
+ #'F*' #'f*')>
+
+ |fKeyMacros shiftPressed ctrlPressed i event macroName
+ immediateCompletion currentUserPrefs|
+
+ currentUserPrefs := UserPreferences current.
+
+ "/ experimental
+ immediateCompletion := currentUserPrefs immediateCodeCompletion.
+ (immediateCompletion
+ or:[currentUserPrefs codeCompletionOnControlKey
+ or:[currentUserPrefs codeCompletionOnTabKey]]) ifTrue:[
+ completionSupport isNil ifTrue:[
+ self initializeCompletionSupport.
+ ].
+ ].
+ "/ JV: why setting it to nil here?
+"/ ifFalse:[
+"/ completionService := nil
+"/ ].
+ completionSupport notNil ifTrue:[
+ (completionSupport handleKeyPress:key x:x y:y) ifTrue:["eaten" ^ self].
+ ].
+
+ key isSymbol ifTrue:[
+ (self graphicsDevice modifierKeys includes:key) ifFalse:[
+ lastReplacementInfo stillCollectingInput:false.
+ ]
+ ].
+ (key == #LearnKeyboardMacro) ifTrue:[
+ lastReplacementInfo stillCollectingInput:false.
+ self toggleLearnMode.
+ ^ self
+ ].
+ (key == #ExecuteKeyboardMacro) ifTrue:[
+ lastReplacementInfo stillCollectingInput:false.
+ self executeLearnedKeyboardMacro.
+ ^ self.
+ ].
+ (key == #Undo) ifTrue:[self undo. ^self].
+ (key == #Redo) ifTrue:[self redo. ^self].
+
+ self learnMode ifTrue:[
+ event := WindowGroup lastEventQuerySignal query.
+ learnedMacro add:event.
+ ].
+
+ (self executekeyboardMacroNamed:key) ifTrue:[
+ "the macro named key exists"
+ ^ self
+ ].
+
+ key isSymbol ifFalse:[
+ "the usual case: key is a character, but maybe a string also (in X11)"
+ self handleNonCommandKey:key.
+ ^ self
+ ].
+
+ event isNil ifTrue:[
+ event := WindowGroup lastEventQuerySignal query.
+ ].
+ shiftPressed := event hasShift.
+ ctrlPressed := event hasCtrl and:[(event rawKey asString startsWith:'Ctrl') not].
+
+ (key == #DeleteWordBeforeCursor) ifTrue:[
+ self deleteWordBeforeCursor.
+ ^ self.
+ ].
+
+ (key == #BackSpace or:[key == #BasicBackspace]) ifTrue:[
+ selectionStartLine notNil ifTrue:[
+ ((key == #BasicBackspace)
+ or:[ currentUserPrefs deleteSetsClipboardText not ])
+ ifTrue:[
+ self deleteSelection.
+ ] ifFalse: [
+ self copyAndDeleteSelection.
+ ].
+ ] ifFalse:[
+ self makeCursorVisible.
+"/ (shiftPressed and:[ ctrlPressed ]) ifTrue:[
+"/ self deleteWordBeforeCursor.
+"/ ] ifFalse:[
+ self deleteCharBeforeCursor.
+"/ ].
+ ].
+ true "immediateCompletion" ifTrue:[
+ completionSupport notNil ifTrue:[ completionSupport postKeyPress:key].
+ ].
+ ^ self
+ ].
+
+ (key == #ToggleAutoIndent) ifTrue:[
+ self autoIndent:(autoIndent not).
+ ^ self.
+ ].
+
+ key == #ToggleInsertMode ifTrue:[
+ self insertMode:(editMode value == EditMode insertMode) not.
+ ^ self.
+ ].
+
+ key == #OpenSpecialCharacterWindow ifTrue:[
+ CharacterSetView notNil ifTrue:[
+ self specialCharacters.
+ ^ self.
+ ]
+ ].
+
+ replacing := false.
+
+ "
+ Fn pastes a key-sequence (but only if not overlayed with
+ another function in the keyboard map)
+
+ see TextView>>:x:y
+ "
+ (key at:1) asLowercase == $f ifTrue:[
+ (('[fF][0-9]' match:key)
+ or:['[fF][0-9][0-9]' match:key]) ifTrue:[
+ shiftPressed ifFalse:[
+ fKeyMacros := currentUserPrefs functionKeySequences.
+ fKeyMacros notNil ifTrue:[
+ (fKeyMacros includesKey:key) ifTrue:[
+ self pasteOrReplace:(fKeyMacros at:key) asStringCollection.
+ ^ self
+ ]
+ ]
+ ]
+ ].
+ ].
+
+ (key == #'Ctrl8' or:[key == #'Ctrl9']) ifTrue:[
+ self parenthizeSelectionWith:$( and:$).
+ ^ self.
+ ].
+ (key == #'Ctrl2') ifTrue:[
+ self parenthizeSelectionWith:$" and:$".
+ ^ self.
+ ].
+ (key == #'Ctrl#') ifTrue:[
+ self parenthizeSelectionWith:$' and:$'.
+ ^ self.
+ ].
+ (key == #'ConvertSelectionToLowercaseOrUppercaseOrUppercaseFirst') ifTrue:[
+ self convertSelectionToLowercaseOrUppercaseOrUppercaseFirst.
+ ^ self.
+ ].
+
+ (key == #Accept) ifTrue:[^ self accept].
+
+ ((key == #Paste) or:[key == #Insert or:[key == #PasteFromHistory]]) ifTrue:[self pasteOrReplace. ^self].
+ (key == #Cut) ifTrue:[self cut. ^self].
+ (key == #Again) ifTrue:[self again. ^self].
+ (key == #AgainForAll) ifTrue:[self multipleAgain. ^self].
+
+ (key == #Join) ifTrue:[self joinLines. ^self].
+ (key == #Replace) ifTrue:[self replace. ^self].
+ (key == #ExpandSelectionByWord) ifTrue:[
+ self makeCursorVisible.
+ self findNextWordAfterSelectionAndAddToSelection.
+ ^ self
+ ].
+ (key == #SelectWord) ifTrue:[
+ self makeCursorVisible.
+ self selectWordUnderCursor.
+ ^ self
+ ].
+
+ (key == #SearchMatchingParent) ifTrue:[self searchForMatchingParenthesis. ^ self].
+ (key == #SelectMatchingParents) ifTrue:[self searchForAndSelectMatchingParenthesis. ^ self].
+ (key == #SelectToEnd) ifTrue:[self selectUpToEnd. ^ self].
+ (key == #SelectFromBeginning) ifTrue:[self selectFromBeginning. ^ self].
+
+" disabled - nobody liked it ...
+ and if you like it, its better done in the keymap.
+
+ (key == #Ctrlb) ifTrue:[self unselect. self cursorLeft. ^ self].
+ (key == #Ctrlf) ifTrue:[self unselect. self cursorRight. ^ self].
+ (key == #Ctrln) ifTrue:[self unselect. self cursorDown. ^ self].
+ (key == #Ctrlp) ifTrue:[self unselect. self cursorUp. ^ self].
+"
+
+ (key == #BeginOfLine) ifTrue:[
+ shiftPressed ifTrue: [
+ "/ "Original St/X code - now use Ctrl modifier"
+ "/ self unselect.
+ "/ self cursorHome.
+ "Jan's modification"
+ "/ self addToSelectionAfter:[ self cursorToBeginOfLine ].
+ "/ Jan's modification modified by his own request ;-))
+ self selectFromBeginOfLine.
+ ] ifFalse: [
+ self unselect.
+ ctrlPressed ifTrue:[
+ self cursorHome.
+ ] ifFalse:[
+ self cursorToBeginOfLine.
+ ]
+ ].
+ ^ self
+ ].
+ (key == #EndOfLine) ifTrue:[
+ shiftPressed ifTrue:[
+ "/ "Original St/X code - now use Ctrl modifier"
+ "/ self unselect.
+ "/ self cursorToBottom
+ " Jan's modification"
+ "/ self addToSelectionAfter:[ self cursorToEndOfLine ] .
+ "/ Jan's modification modified by his own request ;-))
+ self selectToEndOfLine.
+ ] ifFalse:[
+ self unselect.
+ ctrlPressed ifTrue:[
+ self cursorToBottom
+ ] ifFalse:[
+ self cursorToEndOfLine.
+ ]
+ ].
+ ^ self
+ ].
+ (key == #NextWord) ifTrue:[self cursorToNextWord. ^self].
+ (key == #EndOfWord) ifTrue:[self cursorToEndOfWord. ^self].
+ (key == #PreviousWord) ifTrue:[self cursorToPreviousWord. ^self].
+ (key == #GotoLine) ifTrue:[self gotoLine. ^self].
+
+ (key == #CursorRight
+ or:[key == #CursorDown
+ or:[key == #CursorLeft
+ or:[key == #CursorUp]]]) ifTrue:[
+ self cursorKeyPress:key shifted:shiftPressed.
+ ^ self.
+ ].
+
+ (key == #NonInsertingReturn) ifTrue:[
+ self unselect. self cursorReturn.
+ ^ self
+ ].
+
+ (key == #Return) ifTrue:[
+ shiftPressed ifTrue:[
+ self unselect. self cursorReturn.
+ ^ self
+ ].
+
+ self isReadOnly ifTrue:[
+ self unselect; makeCursorVisible.
+ self cursorReturn
+ ] ifFalse:[
+ self isInInsertMode ifFalse:[
+ self cursorReturn.
+ autoIndent == true ifTrue:[
+ i := self leftIndentForLine:(cursorLine + 1).
+ (self listAt:cursorLine) isEmptyOrNil ifTrue:[
+ self cursorCol:(i+1 max:1)
+ ]
+ ]
+ ] ifTrue:[
+ |left right oldIndent|
+
+ "/ old version just unselected ...
+ "/ self unselect; makeCursorVisible.
+
+ "/ new version deletes ...
+ typeOfSelection == #paste ifTrue:[
+ self unselect; makeCursorVisible.
+ ] ifFalse:[
+ self copyAndDeleteSelection.
+ ].
+ left := (self listAt:cursorLine to:cursorCol-1) ? ''.
+ right := (self listAt:cursorLine from:cursorCol) ? ''.
+ self insertCharAtCursor:(Character cr).
+ autoIndent == true ifTrue:[
+ (right isEmpty and:[cursorCol ~~ 1]) ifTrue:[
+ "/ nothing to do.
+ ] ifFalse:[
+ ((self listAt:cursorLine) isEmptyOrNil
+ or:[ cursorCol == 1 ]) ifTrue:[
+ i := (self leftIndentForLine:cursorLine).
+ left := left withoutSeparators.
+ right := right withoutSeparators.
+ (left endsWith:'[') ifTrue:[
+"/ i := i + 4.
+ ] ifFalse:[
+ (false "(left endsWith:']')" or:[(right startsWith:']')]) ifTrue:[
+ i := i - 4.
+ ].
+ ].
+ oldIndent := self leftIndentOfLine:cursorLine.
+ self indentFromLine:cursorLine toLine:cursorLine by:(i-oldIndent).
+ self st80EditMode ifTrue:[
+ (self listAt:cursorLine) size < i ifTrue:[
+ self insertStringAtCursor:(String new:((i-oldIndent) max:0)).
+ ].
+ ].
+ self cursorCol:(i+1 max:1)
+ ].
+ ]
+ ].
+ ].
+ ].
+ ^ self
+ ].
+
+ (key == #NonInsertingTab) ifTrue:[
+ self unselect. self cursorTab.
+ ^ self
+ ].
+ ((key == #BackTab) or:[(key == #Tab)]) ifTrue:[
+ self tabMeansNextField ifTrue:[^ super keyPress:key x:x y:y].
+
+ self hasSelection ifTrue:[
+ selectStyle == #line ifTrue:[
+ ((key == #Tab) and:[shiftPressed not]) ifTrue:[
+ macroName := #IndentBy4.
+ ] ifFalse:[
+ macroName := #UndentBy4.
+ ].
+ macroName notNil ifTrue:[
+ self executekeyboardMacroNamed:macroName.
+ ].
+ ]
+ ].
+
+ self unselect.
+ (key == #Tab) ifTrue:[
+ (shiftPressed or:[self isInInsertMode not]) ifTrue:[
+ self cursorTab.
+ ^ self
+ ].
+ self insertTabAtCursor.
+ ^ self
+ ].
+ self cursorBacktab.
+ ^ self
+ ].
+
+ "/ key == #DeleteSpaces ifTrue:[
+ (key == #Delete) ifTrue:[
+ shiftPressed ifTrue:[
+ [(cursorCol <= (self listAt:cursorLine) size)
+ and:[self characterUnderCursor isSeparator]] whileTrue:[
+ self makeCursorVisible.
+ self deleteCharAtCursor.
+ ].
+ ^ self
+ ]
+ ].
+
+ (key == #Delete
+ or:[key == #BasicDelete]) ifTrue:[
+ selectionStartLine notNil ifTrue:[
+"/ Again function is not supporting Delete action (on purpose, to avoid replacing the next search string)
+"/ To remove text repetetively, use Cut instead.
+"/ self setLastStringToReplace: self selection asStringWithoutFinalCR.
+"/ lastReplacementInfo lastReplacement: nil.
+ ((key == #BasicDelete)
+ or:[currentUserPrefs deleteSetsClipboardText not]) ifTrue:[
+ self deleteSelection.
+ ] ifFalse:[
+ self copyAndDeleteSelection.
+ ].
+ ^ self
+ ].
+ self makeCursorVisible.
+ self deleteCharAtCursor.
+ ^ self
+ ].
+
+ (key == #BeginOfText) ifTrue:[ "i.e. HOME"
+ self unselect.
+ cursorVisibleLine == 1 ifTrue:[
+ self cursorHome.
+ ] ifFalse:[
+ self cursorToFirstVisibleLine
+ ].
+ ^ self
+ ].
+ (key == #EndOfText) ifTrue:[ "i.e. END"
+ self unselect.
+ cursorVisibleLine == nFullLinesShown ifTrue:[
+ self cursorToBottom.
+ ] ifFalse:[
+ self cursorToLastVisibleLine
+ ].
+ ^ self
+ ].
+ ((key == #Escape)
+ or:[key == #SelectLineFromBeginning]) ifTrue:[
+ self makeCursorVisible.
+ self unselect. self selectCursorLineFromBeginning.
+ ^ self
+ ].
+ (key == #SelectLine) ifTrue:[
+ self makeCursorVisible.
+ self unselect. self selectCursorLine.
+ ^ self
+ ].
+ (key == #ExpandSelectionByLine) ifTrue:[
+ "/ self makeCursorVisible.
+ self selectExpandCursorLine.
+ ^ self
+ ].
+ (key == #DeleteLine) ifTrue:[
+ self makeCursorVisible.
+ self unselect. self deleteCursorLine.
+ ^ self
+ ].
+ (key == #InsertLine) ifTrue:[
+ self makeCursorVisible.
+ self unselect. self insertLine:nil before:cursorLine.
+ ^ self
+ ].
+
+ super keyPress:key x:x y:y
+
+ "Modified: / 06-02-1998 / 11:59:59 / stefan"
+ "Modified: / 14-07-2011 / 12:08:28 / cg"
+ "Modified: / 26-09-2013 / 17:52:04 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+!
+
+executeKeyboardMacro:cmdMacro
+ Error handle:[:ex |
+ self warn:'Error in keyboard macro: ' , ex description.
+ ex return.
+ ] do:[
+ AbortOperationRequest handle:[:ex |
+ self warn:'Keyboard macro aborted'.
+ ex return.
+ ] do:[
+ Parser
+ evaluate:cmdMacro asString
+ receiver:self
+ notifying:nil
+ compile:false.
+ ].
+ ].
+!
+
+handleNonCommandKey:keyArg
+ |selStartLineBefore selStartColBefore selEndLineBefore selEndColBefore key|
+
+ self isReadOnly ifTrue:[
+ self flashReadOnly.
+ ^ self.
+ ].
+
+ key := keyArg.
+
+ typeOfSelection == #paste ifTrue:[
+ "pasted selection will NOT be replaced by keystroke"
+ self unselect
+ ].
+
+ selStartLineBefore := selectionStartLine.
+ selStartColBefore := self selectionStartCol.
+ selEndLineBefore := selectionEndLine.
+ selEndColBefore := self selectionEndCol.
+
+ (gc characterEncoding ? #'iso10646-1') ~~ #'iso10646-1' ifTrue:[
+ key isCharacter ifTrue:[
+ key := CharacterEncoder encode:key from:#'iso10646-1' into:gc characterEncoding.
+ ] ifFalse:[
+ key := CharacterEncoder encodeString:key from:#'iso10646-1' into:gc characterEncoding.
+ ].
+ ].
+
+ "replace selection by what is typed in -
+ if word was selected with a space, keep it.
+ if there was no selection, the key's character is inserted"
+
+ editMode value isInsertAndSelectMode ifTrue:[
+ selectionStartLine := selectionStartCol := selectionEndLine := selectionEndCol := nil.
+ ].
+
+ (selectStyle == #wordLeft) ifTrue:[
+ self replaceSelectionBy:(' ' , key asString)
+ ] ifFalse:[
+ (selectStyle == #wordRight) ifTrue:[
+ self replaceSelectionBy:(key asString , ' ').
+ self cursorLeft
+ ] ifFalse:[
+ self replaceSelectionBy:key
+ ]
+ ].
+ selectStyle := nil.
+
+ editMode value isInsertAndSelectMode ifTrue:[
+ selectionStartLine := selStartLineBefore.
+ selectionStartCol := selStartColBefore.
+ selectionEndLine := selEndLineBefore.
+ selectionEndCol := selEndColBefore.
+ ].
+
+ showMatchingParenthesis ifTrue:[
+ "emacs style parenthesis shower"
+ (ExecutingMacroQuery query ? false) ifFalse:[
+ "claus: only do it for closing parenthesis -
+ otherwise its too anoying.
+ "
+"
+ ('()[]{}' includes:key) ifTrue:[
+"
+ (')]}' includes:key) ifTrue:[
+ self
+ searchForMatchingParenthesisFromLine:cursorLine col:(cursorCol - 1)
+ ifFound:[:line :col |
+ |savLine savCol sensor|
+
+ self withCursor:Cursor eye do:[
+ savLine := cursorLine.
+ savCol := cursorCol.
+ self cursorLine:line col:col.
+ self flush.
+
+ "/ want to wait 200ms, but not if another keyPress
+ "/ arrives in the meantime ...
+
+ sensor := self sensor.
+ 5 timesRepeat:[
+ (sensor hasKeyPressEventFor:self) ifFalse:[
+ Processor activeProcess millisecondDelay:40.
+ ]
+ ].
+ self cursorLine:savLine col:savCol
+ ]
+ ]
+ ifNotFound:[self showNotFound]
+ onError:[self beep]
+ ].
+ ]
+ ].
+
+"/ true "autoExpandWhileTyping" ifTrue:[
+"/ self wordAtLine:cursorLine col:cursorCol-1 do:[
+"/ :beginLine :beginCol :endLine :endCol :style |
+"/
+"/ self selectFromLine:beginLine col:beginCol toLine:endLine col:endCol.
+"/ self selection.
+"/ typeOfSelection := #paste.
+"/ ].
+"/ ].
+ editMode value isInsertAndSelectMode ifTrue:[
+ selectionStartLine isNil ifTrue:[
+ self selectFromLine:cursorLine col:cursorCol-1 toLine:cursorLine col:cursorCol-1.
+ ] ifFalse:[
+ self selectFromLine:selectionStartLine col:selectionStartCol toLine:cursorLine col:cursorCol-1.
+ ].
+ ].
+ completionSupport notNil ifTrue:[ completionSupport postKeyPress:keyArg ].
+
+ "Modified (comment): / 25-01-2012 / 00:30:11 / cg"
+!
+
+keyPress:key x:x y:y
+ "handle keyboard input"
+
+ |wasOn|
+
+ wasOn := cursorShown.
+
+ NoModificationError handle:[:ex |
+ self flashReadOnly.
+ (cursorShown not and:[wasOn]) ifTrue:[
+ self makeCursorVisibleAndShowCursor:wasOn.
+ ].
+ ] do:[
+ self undoableDo:[
+ self doKeyPress:key x:x y:y
+ ].
+ ].
+ self repairDamage
+
+ "Modified: / 18-04-2011 / 21:35:27 / cg"
+!
+
+mapped
+ "view was made visible"
+
+ super mapped.
+ self updateCursorVisibleLine.
+!
+
+requestAutoAccept
+ "this is invoked when a dialog closes via accept or cancel.
+ This forces my value to be accepted into my model"
+
+ acceptEnabled == false ifTrue:[
+ "/ nope -
+ ^ false
+ ].
+ self accept.
+ ^ true.
+!
+
+sizeChanged:how
+ "make certain, cursor is visible after the sizechange"
+
+ |cv|
+
+ cv := cursorVisibleLine.
+ super sizeChanged:how.
+ cv notNil ifTrue:[
+ self makeLineVisible:cursorLine
+ ]
+!
+
+unmapped
+ super unmapped.
+
+ completionSupport notNil ifTrue:[
+ completionSupport release.
+ ].
+! !
+
+!EditTextView methodsFor:'focus handling'!
+
+focusOut
+ super focusOut.
+
+ completionSupport notNil ifTrue:[
+ completionSupport release.
+ ].
+!
+
+hasKeyboardFocus:aBoolean
+ "sent by the windowGroup, a delegate or myself to make me show a block cursor
+ (otherwise, I would not know about this)"
+
+ hasKeyboardFocus := aBoolean.
+
+ (cursorShown
+ and:[self enabled
+ and:[self isReadOnly not]]) ifTrue:[
+ self drawCursor
+ ].
+
+ hasKeyboardFocus ifFalse:[
+ completionSupport notNil ifTrue:[
+ "/ this is a hack for Windows:
+ "/ on windows, an activate:false event is first sent to my textView,
+ "/ then an activate is sent to the completion popup.
+ "/ this is done BEFORE the buttonPress event is delivered.
+ "/ therefore, allow for the activate of the completionMenu and it's button event to be processed.
+ "/ before forcing it to be closed...
+ Processor addTimedBlock:[completionSupport "release" editViewLostFocus] afterMilliseconds:200.
+ ].
+ ].
+
+ "Modified (format): / 06-11-2013 / 15:37:31 / cg"
+!
+
+showFocus:explicit
+ "in addition to however my superclass thinks how a focusView is to be
+ displayed, show the cursor when I got the focus"
+
+ self showCursor.
+ self hasKeyboardFocus:true.
+ super showFocus:explicit
+
+ "Modified: 11.12.1996 / 16:56:54 / cg"
+!
+
+showNoFocus:explicit
+ "the view lost the keyboard focus
+ (either explicit, via tabbing; or implicit, by pointer movement)
+ - change any display attributes as req'd."
+
+ super showNoFocus:explicit.
+ completionSupport notNil ifTrue:[
+ completionSupport release.
+ ].
+!
+
+wantsFocusWithPointerEnter
+ "return true, if I want the focus when
+ the mouse pointer enters"
+
+ (UserPreferences current focusFollowsMouse ~~ false
+ and:[(styleSheet at:#'editText.requestFocusOnPointerEnter' default:true)
+ and:[self enabled
+ and:[true "self isReadOnly not"]]]
+ ) ifTrue:[
+ ^ true
+ ].
+
+ ^ false
+! !
+
+!EditTextView methodsFor:'formatting'!
+
+executekeyboardMacroNamed:macroName
+ "try to execute the keyboard macro;
+ return true if that worked, false otherwise"
+
+ |cmdMacro|
+
+ cmdMacro := UserPreferences current functionKeySequences at:macroName ifAbsent:[^ false].
+ self
+ undoableDo:[ self executeKeyboardMacro:cmdMacro ]
+ info: macroName.
+ ^ true
+
+ "
+ EditTextView open
+ contents:'bla';
+ selectAll;
+ executekeyboardMacroNamed:#IndentBy4.
+ EditTextView open
+ contents:'bla';
+ selectAll;
+ executekeyboardMacroNamed:#blabla.
+ "
+
+ "Modified: / 14-02-2012 / 11:17:27 / cg"
+!
+
+indent
+ "indent a line-range - this is done by searching for the
+ last non-empty line before the selection, and changing the indent
+ of the selected line-range based on that line's indent."
+
+ |start end|
+
+ selectionStartLine isNil ifTrue:[^ self].
+
+ start := selectionStartLine.
+ end := selectionEndLine.
+ (selectionEndCol == 0) ifTrue:[
+ end := end - 1
+ ].
+ self unselect.
+ self
+ undoableDo:[self indentFromLine:start toLine:end]
+ info:'Indent'
+!
+
+indentBy4
+ self executekeyboardMacroNamed:#IndentBy4.
+
+ "Modified: / 06-04-2011 / 18:52:40 / cg"
+!
+
+indentFromLine:start toLine:end
+ "indent a line-range - this is done by searching for the
+ last non-empty line before start, and change the indent
+ of the selected line-range based on that line's indent."
+
+ |leftStart delta|
+
+ leftStart := self leftIndentForLine:start.
+ (leftStart == 0) ifTrue:[^ self].
+
+ delta := leftStart - (self leftIndentOfLine:start).
+ (delta == 0) ifTrue:[^ self].
+ self indentFromLine:start toLine:end by:delta
+!
+
+indentFromLine:start toLine:end by:delta
+ "indent a line-range - this is done by searching for the
+ last non-empty line before start, and change the indent
+ of the selected line-range based on that line's indent."
+
+ |d line spaces anyChange|
+
+ (delta == 0) ifTrue:[^ self].
+ (delta > 0) ifTrue:[
+ spaces := String new:delta
+ ].
+
+ anyChange := false.
+ start to:end do:[:lineNr |
+ line := self listAt:lineNr.
+ line notNil ifTrue:[
+ line isBlank ifTrue:[
+ self basicListAt:lineNr put:nil
+ ] ifFalse:[
+ (delta > 0) ifTrue:[
+ line := spaces , line.
+ widthOfWidestLine notNil ifTrue:[
+ widthOfWidestLine := widthOfWidestLine max:(self widthOfLineString:line).
+ ]
+ ] ifFalse:[
+ "check if deletion is ok"
+ d := delta negated + 1.
+
+ line size > d ifTrue:[
+ (line copyTo:(d - 1)) withoutSeparators isEmpty ifTrue:[
+ line := line copyFrom:d
+ ]
+ ].
+ widthOfWidestLine := nil
+ ].
+ self replaceLine:lineNr with:line.
+ anyChange := true.
+ ]
+ ]
+ ].
+
+ anyChange ifTrue:[ self textChanged ].
+
+ "/ self redrawFromLine:start to:end
+
+ "Modified: 5.3.1996 / 14:59:18 / cg"
+!
+
+leftIndentForLine:lineNr
+ "find an appropriate indent for a line.
+ this is done by searching for the last non-empty line before it
+ and returning its indent.
+ cg: changed: only look for the single previous line."
+
+ "SHOULD GO TO ListView"
+
+ |line lnr indent|
+
+ lnr := lineNr.
+
+ "/ [lnr ~~ 1] whileTrue:[
+ (lnr ~~ 1) ifTrue:[
+ lnr := lnr - 1.
+ line := self listAt:lnr.
+
+ line notNil ifTrue:[
+ indent := line indexOfNonSeparatorStartingAt:1.
+ indent ~~ 0 ifTrue:[
+ (line endsWith:$[) ifTrue:[
+ ^ indent + 4 - 1
+ ].
+ ^ indent - 1
+ ]
+ ]
+ ].
+ ^ 0
+
+ "Created: 5.3.1996 / 14:58:53 / cg"
+!
+
+undentBy4
+ self executekeyboardMacroNamed:#UndentBy4.
+
+ "Modified: / 06-04-2011 / 18:52:49 / cg"
+! !
+
+!EditTextView methodsFor:'initialization'!
+
+fetchDeviceResources
+ "fetch device colors, to avoid reallocation at redraw time"
+
+ super fetchDeviceResources.
+
+ cursorFgColor notNil ifTrue:[cursorFgColor := cursorFgColor onDevice:self graphicsDevice].
+ cursorBgColor notNil ifTrue:[cursorBgColor := cursorBgColor onDevice:self graphicsDevice].
+ cursorNoFocusFgColor notNil ifTrue:[cursorNoFocusFgColor := cursorNoFocusFgColor onDevice:self graphicsDevice].
+
+ "Created: 14.1.1997 / 00:15:24 / cg"
+ "Modified: 18.2.1997 / 15:02:46 / cg"
+!
+
+initEvents
+ "enable enter/leave events in addition"
+
+ super initEvents.
+ self enableEnterLeaveEvents
+!
+
+initStyle
+ "initialize style specific stuff"
+
+ super initStyle.
+ "/ lineSpacing := 2. "/ for underwave - also looks better
+ lockUpdates := false.
+
+ cursorFgColor := DefaultCursorForegroundColor.
+ cursorFgColor isNil ifTrue:[cursorFgColor := bgColor].
+ cursorBgColor := DefaultCursorBackgroundColor.
+ cursorBgColor isNil ifTrue:[cursorBgColor := fgColor].
+ cursorType isNil ifTrue:[cursorType := DefaultCursorType].
+ cursorTypeNoFocus isNil ifTrue:[
+ cursorTypeNoFocus := cursorType.
+ DefaultCursorTypeNoFocus notNil ifTrue:[
+ cursorTypeNoFocus := DefaultCursorTypeNoFocus.
+ ]
+ ].
+ cursorNoFocusFgColor := DefaultCursorNoFocusForegroundColor.
+ cursorNoFocusFgColor isNil ifTrue:[
+ cursorType ~~ #block ifTrue:[
+ cursorNoFocusFgColor := cursorBgColor
+ ] ifFalse:[
+ cursorNoFocusFgColor := cursorFgColor
+ ]
+ ].
+
+ "Modified: / 15.12.1999 / 22:27:45 / cg"
+!
+
+initialize
+ "initialize a new EditTextView;
+ setup some instance variables"
+
+ super initialize.
+
+ self level:-1.
+ readOnly := false.
+ fixedSize := false.
+ exceptionBlock := [:errorText | ].
+ cursorShown := prevCursorState := true.
+ cursorLine := 1.
+ cursorVisibleLine := 1.
+ cursorCol := 1.
+ cursorLineHolder := 1 asValue.
+ cursorColHolder := 1 asValue.
+ modifiedChannel := ValueHolder with:false.
+ acceptChannel := ValueHolder with:false.
+ acceptChannel addDependent:self.
+ showMatchingParenthesis := false.
+ hasKeyboardFocus := false.
+ tabMeansNextField := false.
+ autoIndent := false.
+ editMode := EditMode insertMode asValue.
+ learnMode := false asValue.
+ trimBlankLines := self st80EditMode not.
+ cursorMovementWhenUpdating := #beginOfText.
+ lastReplacementInfo := LastReplacementInfo new.
+
+ "/ enable drop by default
+ self allowDrop:true. "/ readOnly tested in #canDrop:
+
+ undoSupport := UndoSupport for:self.
+ codeAspectHolder := nil asValue.
+
+ "Modified: / 27-09-2013 / 09:41:32 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+!
+
+initializeCompletionSupport
+ |supportClass|
+
+ completionSupport isNil ifTrue:[
+ (supportClass := self completionSupportClass) notNil ifTrue:[
+ completionSupport := supportClass for:self.
+ ].
+ ].
+
+ "Created: / 26-09-2013 / 17:51:45 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+!
+
+release
+ completionSupport notNil ifTrue:[
+ completionSupport release
+ ].
+ super release
+! !
+
+!EditTextView methodsFor:'macros'!
+
+executeLearnedKeyboardMacro
+ "replay the characters as learned previously"
+
+ (self learnMode not and:[learnedMacro size > 0]) ifTrue:[
+ ExecutingMacroQuery
+ answer:true
+ do:[
+ learnedMacro do:[:event |
+ WindowGroup lastEventQuerySignal answer:event
+ do:[
+ self
+ dispatchEvent:event
+ withFocusOn:nil
+ delegate:false
+ ]
+ ]
+ ].
+ ] ifFalse:[
+ self flash.
+ ].
+!
+
+learnMode
+ "true if currently learning"
+
+ ^ (learnMode value ? false).
+!
+
+learnMode:aBoolean
+ "toggle the learn-mode"
+
+ |fg bg|
+
+ self learnModeHolder value:aBoolean.
+
+ aBoolean ifTrue:[
+ learnedMacro := OrderedCollection new.
+ fg := Color white.
+ bg := Color black.
+ ] ifFalse:[
+ cursorFgColor := fg := (DefaultCursorForegroundColor ? bgColor).
+ cursorBgColor := bg := (DefaultCursorBackgroundColor ? fgColor).
+ ].
+ self cursorForegroundColor:fg backgroundColor:bg.
+!
+
+learnModeHolder
+ "a holder returning true, if in learn mode"
+
+ learnMode isNil ifTrue:[
+ learnMode := false asValue
+ ].
+ ^ learnMode
+!
+
+rememberLearnedMacroAs: nameString
+ Macros isNil ifTrue:[
+ Macros := Dictionary new.
+ ].
+ Macros at:nameString put:learnedMacro
+!
+
+toggleLearnMode
+ "toggle the learn-mode"
+
+ self learnMode:(self learnMode not).
+! !
+
+!EditTextView methodsFor:'menu & menu actions'!
+
+babelFishTranslate:fromToModeString
+ "translate the selected text and paste it after the selection"
+
+ |original translated|
+
+ original := self selectionAsString.
+ original size == 0 ifTrue:[^ self].
+
+ self withWaitCursorDo:[
+ (HostNameLookupError , SOAP::SoapImplError) handle:[:ex |
+ Dialog warn:('Translation failed - WEB Service error:\\%1.' bindWith:ex description allBold) withCRs
+ ] do:[
+ translated := SOAP::BabelFishClient new translate:original mode:fromToModeString.
+ ]
+ ].
+
+ "/ v pasteOrReplace:translated
+ self cursorLine:(self selectionEndLine) col:(self selectionEndCol + 1).
+ self unselect.
+ self
+ undoablePaste:translated
+ info:'Translate'
+
+ "Modified: / 28-07-2007 / 13:27:21 / cg"
+!
+
+compareWithClipboard
+ "compare the selection against the clipboard contents"
+
+ |t1 t2|
+
+ t2 := self getClipboardText.
+ t2 isEmptyOrNil ifTrue:[
+ Dialog information:'Clipboard is empty.'.
+ ^ self.
+ ].
+
+ self hasSelection ifTrue:[
+ t1 := self selectionAsString.
+ ] ifFalse:[
+ t1 := self contents asString
+ ].
+ t1 := t1 string.
+
+ t1 = t2 ifTrue:[
+ Dialog information:'Strings are equal.'.
+ ^ self.
+ ].
+ DiffTextView
+ openOn:t1 label:'Editor'
+ and:t2 label:'Clipboard'
+!
+
+cut
+ "cut selection into copybuffer"
+
+ self deleteCopyToClipboard:true
+!
+
+defaultForGotoLine
+ "return a default value to show in the gotoLine box"
+
+ cursorLine notNil ifTrue:[
+ ^ cursorLine
+ ].
+ ^ super defaultForGotoLine
+!
+
+deleteCopyToClipboard:toClipboard
+ "cut selection into copybuffer"
+
+ |line col history sel |
+
+ (self checkModificationsAllowed) ifFalse:[
+ self flashReadOnly.
+ ^ self
+ ].
+
+ sel := self selection.
+ sel notNil ifTrue:[
+ self setLastStringToReplace: sel.
+
+ line := selectionStartLine.
+ col := selectionStartCol.
+
+ toClipboard ifTrue:[
+ "
+ remember in CopyBuffer
+ "
+ self setClipboardText:sel. "/ lastString.
+ ].
+
+ "
+ append to DeleteHistory (if there is one)
+ "
+ history := Smalltalk at:#DeleteHistory.
+ history notNil ifTrue:[
+ history addAll:(sel asStringCollection).
+ history size > 1000 ifTrue:[
+ history := history copyFrom:(history size - 1000)
+ ].
+ ].
+
+ "
+ now, delete it
+ "
+ self
+ undoableDo:[self deleteSelection]
+ info:'Delete'.
+ lastReplacementInfo lastReplacement: nil
+ ] ifFalse:[
+ "
+ a cut without selection will search&cut again
+ "
+ self undoableDo:[
+ self again
+ ]
+ ]
+
+ "Modified: / 5.4.1998 / 16:51:53 / cg"
+!
+
+editMenu
+ "return the views middleButtonMenu"
+
+ <resource: #keyboard (#Again #AgainForAll #Copy #Cut #Paste #Accept
+ #Find #GotoLine #SaveAs #Print
+ #PasteFromHistory #Join #Wrap #Undo #Redo
+ #ToggleAutoIndent #ToggleInsertMode
+ #LearnKeyboardMacro #ExecuteKeyboardMacro )>
+ <resource: #programMenu>
+
+ |items m sub translateItems sortItems miscItems toolItems subSub toolSub
+ transSub sortSub what undoIdx redoIdx sensor main mainItems|
+
+ items := #(
+ ('Redo' redo Redo )
+ ('Again (for All)' multipleAgain AgainForAll )
+ ('-' )
+ ('Search...' search Find )
+ ('Goto Line...' gotoLine GotoLine )
+ ('-' )
+ ('Tools' tools )
+ ('Insert Unicode...' insertUnicode )
+ ).
+ CharacterSetView notNil ifTrue:[
+ items := items ,
+ #(
+ ('Special Characters...' specialCharacters OpenSpecialCharacterWindow )
+ ).
+ ].
+ items := items , #(
+ ('-' )
+ ('Save As...' save SaveAs )
+ ('Print' doPrint Print )
+ ('=' )
+ ('Misc' misc ShiftCtrl) ).
+
+ miscItems := #(
+ ('AutoIndent \c' autoIndent: ToggleAutoIndent )
+ ('InsertMode \c' insertMode: ToggleInsertMode )
+ ('-' )
+ ('Paste Previous...' pasteOrReplaceFromHistory PasteFromHistory )
+ ('Join Lines' joinLines Join )
+ ('Wrap Lines...' wrapLines Wrap )
+ ('-' )
+ ('Learn Macro' learnMode: LearnKeyboardMacro)
+ ('Execute Macro' executeLearnedKeyboardMacro ExecuteKeyboardMacro )
+ ('-' )
+ ('Insert File...' insertFile )
+ ('Insert URL Contents...' insertURL )
+ ('Insert new UUID' insertUUID )
+ ('Insert Date && Time' insertDateAndTime )
+ ('-' )
+ ('Insert File as String Literal...' insertFileAsStringLiteral )
+ ('Paste as String Literal' pasteAsStringLiteral )
+ ('-' )
+ ).
+"/ CharacterSetView notNil ifTrue:[
+"/ miscItems := miscItems ,
+"/ #(
+"/ ('Special Characters...' specialCharacters OpenSpecialCharacterWindow )
+"/ ).
+"/ ].
+ miscItems := miscItems ,
+ #(
+ ('Font...' changeFont )
+"/ ('Encoding...' changeEncoding )
+ ).
+
+ translateItems := #(
+ ('English -> German' (babelFishTranslate: 'en_de') )
+ ('English -> French' (babelFishTranslate: 'en_fr') )
+ ('English -> Italian' (babelFishTranslate: 'en_it') )
+ ('English -> Spanish' (babelFishTranslate: 'en_es') )
+ ('English -> Portuguese' (babelFishTranslate: 'en_pt') )
+ ('-' )
+ ('German -> English' (babelFishTranslate: 'de_en') )
+ ('French -> English' (babelFishTranslate: 'fr_en') )
+ ('Italian -> English' (babelFishTranslate: 'it_en') )
+ ('Spanish -> English' (babelFishTranslate: 'es_en') )
+ ('Portuguese -> English' (babelFishTranslate: 'pt_en') )
+ ).
+
+ sortItems := #(
+ ('Lines' (sortSelection:ignoreCase: #lines false) )
+ ('Lines by First Word' (sortSelection:ignoreCase: #linesByFirstWord false) )
+ ('Lines by n''th Word' (sortSelection:ignoreCase: #linesByNthWord false) )
+ ('Lines by n''th Number' (sortSelection:ignoreCase: #linesByNthNumber false) )
+ ('Lines by n''th Hex Number' (sortSelection:ignoreCase: #linesByNthHexNumber false) )
+ ('Words' (sortSelection:ignoreCase: #words false) )
+ ('-' )
+ ('Lines (ignore case)' (sortSelection:ignoreCase: #lines true) )
+ ('Lines by First Word (ignore case)' (sortSelection:ignoreCase: #linesByFirstWord true) )
+ ('Lines by n''th Word (ignore case)' (sortSelection:ignoreCase: #linesByNthWord true) )
+ ('Words (ignore case)' (sortSelection:ignoreCase: #words true) )
+ ('-' )
+ ('By Line Length' (sortSelection:ignoreCase: #linesByLength nil) )
+ ('Reverse' (sortSelection:ignoreCase: #reverse nil) )
+ ).
+
+ toolItems := #(
+ ('Indent' indent )
+ ('Toggle Case' convertSelectionToLowercaseOrUppercaseOrUppercaseFirst ConvertSelectionToLowercaseOrUppercaseOrUppercaseFirst)
+ ('Sort' sort )
+ ('-' )
+ ('Google Spell Check' googleSpellingSuggestion )
+ ('Builtin Spell Check' internalSpellingSuggestion )
+ ('Translate' babelFishTranslate )
+ ('Compare with Clipboard...' compareWithClipboard )
+ ).
+
+ Smalltalk isStandAloneApp ifFalse:[
+ toolItems := toolItems , #(
+ ('-' )
+ ('Open FileBrowser on It' openFileBrowserOnIt )
+ ('Open Workspace with It' openWorkspaceWithIt )
+ ).
+ ].
+
+ sub := PopUpMenu itemList:items resources:resources performer:model.
+ sub receiver:self.
+
+ toolSub := PopUpMenu itemList:toolItems resources:resources performer:model.
+ toolSub receiver:self.
+ sub subMenuAt:#tools put:toolSub.
+
+ transSub := PopUpMenu itemList:translateItems resources:resources performer:model.
+ transSub receiver:self.
+ toolSub subMenuAt:#babelFishTranslate put:transSub.
+
+ sortSub := PopUpMenu itemList:sortItems resources:resources performer:model.
+ sortSub receiver:self.
+ toolSub subMenuAt:#sort put:sortSub.
+
+ subSub := PopUpMenu itemList:miscItems resources:resources performer:model.
+ subSub receiver:self.
+ subSub checkToggleAt:#autoIndent: put:autoIndent.
+ subSub checkToggleAt:#insertMode: put:(self isInInsertMode).
+ subSub checkToggleAt:#learnMode: put:(self learnModeHolder value).
+
+ sub subMenuAt:#misc put:subSub.
+
+ mainItems := #(
+ ('Undo' undo Undo )
+ ('Again' again Again )
+ ('-' )
+ ('Cut' cut Cut )
+ ('Copy' copySelection Copy )
+ ('Paste' pasteOrReplace Paste )
+ ('-' )
+ ('Accept' accept Accept )
+ ('=' )
+ ('More' others Ctrl )
+ ).
+ main := PopUpMenu itemList:mainItems resources:resources.
+ main subMenuAt:#others put:sub.
+
+ sensor := self sensor.
+ (sensor notNil and:[sensor ctrlDown]) ifTrue:[
+ sensor shiftDown ifTrue:[
+ m := subSub
+ ] ifFalse:[
+ m := sub
+ ]
+ ] ifFalse:[
+ m := main
+ ].
+
+ "/ the 'Smalltalk at:' code is here to
+ "/ avoid making the SOAP package a prerequisite for this package (libwidg)
+ (Smalltalk at:#'SOAP::GoogleClient') isNil ifTrue:[
+ "/ GoogleClient new spellingSuggestionOf: 'Smmalltlaak and Soaap'.
+ m disable:#googleSpellingSuggestion
+ ].
+ (Smalltalk at:#'RBSpellChecker') isNil ifTrue:[
+ m disable:#internalSpellingSuggestion
+ ].
+
+ HTTPInterface isNil ifTrue:[
+ m disableAll:#(insertURL)
+ ].
+
+ self isReadOnly ifTrue:[
+ m disableAll:#(accept undo again multipleAgain redo
+ paste pasteOrReplace pasteOrReplaceFromHistory
+ cut indent autoIndent: insertMode:
+ insertFile insertFileAsStringLiteral insertURL
+ babelFishTranslate googleSpellingSuggestion sort
+ convertSelectionToLowercaseOrUppercaseOrUppercaseFirst
+ joinLines wrapLines insertUUID insertDateAndTime pasteAsStringLiteral
+ insertUnicode specialCharacters)
+ ].
+ self hasSelectionForCopy ifFalse:[
+ m disable:#copySelection.
+ ].
+ self hasSelection ifFalse:[
+ m disableAll:#(cut googleSpellingSuggestion babelFishTranslate openFileBrowserOnIt openWorkspaceWithIt sort indent).
+ ] ifTrue:[
+ (Error handle:[:ex |
+ ex return:false
+ ] do:[
+ |fn|
+ fn := self selectionAsString.
+ fn asFilename exists or:[ fn withoutSeparators withoutQuotes asFilename exists ]
+ ]) ifFalse:[
+ m disableAll:#(openFileBrowserOnIt).
+ ]
+ ].
+ self hasUndoAction ifFalse:[
+ m disable:#undo.
+ ] ifTrue:[
+ what := undoSupport undoActionInfo.
+ what notNil ifTrue:[
+ undoIdx := m indexOf:#undo.
+ m labelAt:undoIdx put:(resources string:'Undo (%1)' with:what).
+ ]
+ ].
+ self hasRedoAction ifFalse:[
+ sub disable:#redo.
+ ] ifTrue:[
+ what := undoSupport redoActionInfo.
+ what notNil ifTrue:[
+ redoIdx := sub indexOf:#redo.
+ sub labelAt:redoIdx put:(resources string:'Redo (%1)' with:what).
+ ]
+ ].
+ acceptEnabled == false ifTrue:[
+ m disable:#accept
+ ].
+ ^ m.
+
+ "Modified: / 01-03-2012 / 19:56:58 / cg"
+!
+
+getTextSelectionFromHistory
+ |sel list box history|
+
+ history := self graphicsDevice getCopyBufferHistory copy.
+ list := history collect:[:entry |
+ |text shown|
+
+ text := entry asString string asCollectionOfLines.
+ shown := text detect:[:line| line notEmptyOrNil] ifNone:[' '].
+ text size > 1 ifTrue:[
+ shown := shown,(resources string:' ... [%1 lines]' with:text size).
+ ].
+ shown
+ ].
+
+ box := ListSelectionBox
+ title:(resources string:'Clipboard History')
+ okText:(resources string:'Paste')
+ abortText:(resources string:'Cancel')
+ list:list
+ action:[:idx | idx notNil ifTrue:[sel := history at:idx]].
+ box label:(resources string:'Select Previous Copybuffer String').
+ box useIndex:true.
+ box show.
+ ^ sel.
+
+ "Modified: / 25-08-2010 / 22:02:14 / cg"
+!
+
+getTextSelectionOrTextSelectionFromHistory
+
+ self sensor shiftDown ifTrue:[
+ ^ self getTextSelectionFromHistory
+ ].
+
+ "/ return either the (xterm-) selection or the clipBoard depending on
+ "/ the Ctrl-Key state.
+
+ "/ ouch - this used to be ok for ALT-c / ALT-v,
+ "/ but no longer works with CTRL-c / CTRL-v.
+ ^ self getClipboardText:#clipboard
+
+"/ ^ self
+"/ getClipboardText:(self sensor ctrlDown
+"/ ifTrue:[#selection]
+"/ ifFalse:[#clipboard])
+
+ "Modified: / 13-07-2011 / 14:55:58 / cg"
+!
+
+googleSpellingSuggestion
+ "insert the google-spelling suggestion for the selected text.
+ Requires that the SOAP stuff is loaded and working."
+
+ |text suggestion|
+
+ self withWaitCursorDo:[
+ text := self selection asString string withoutSeparators.
+ text size == 0 ifTrue:[^ self].
+
+ "/ the 'Smalltalk at:' code is here to
+ "/ avoid making the SOAP package a prerequisite for this package (libwidg)
+ (Smalltalk at:#'SOAP::SoapImplError') handle:[:ex |
+ Dialog warn:('Spelling correction failed - WEB Service error:\\%1.' bindWith:ex description allBold) withCRs.
+ ^ self.
+ ] do:[
+ suggestion := (Smalltalk at:#'SOAP::GoogleClient') new spellingSuggestionOf:text.
+ ].
+ suggestion size == 0 ifTrue:[
+ self information:('No spelling suggestion from Google for: ' , text).
+ Transcript showCR:('No spelling suggestion from Google for: ' , text).
+ ^ self.
+ ].
+ ].
+ self
+ undoablePaste:suggestion
+ info:'Spelling Suggestion'.
+
+ "Modified: / 28-07-2007 / 13:25:10 / cg"
+!
+
+insertDateAndTime
+ "insert the curent date and time string"
+
+ typeOfSelection := nil.
+ self
+ undoableDo:[ self pasteOrReplace:(Timestamp now printStringRFC1123Format)]
+ info:'Paste Date and Time'
+!
+
+insertFile
+ "insert contents of a file
+ - ask user for filename using a fileSelectionBox."
+
+ self insertFileAsStringLiteral:false
+!
+
+insertFileAsStringLiteral
+ "insert a file's contents as a string literal.
+ Almost the same as the insert file, but single-quotes are doubled,
+ to make it a legal string literal"
+
+ self insertFileAsStringLiteral:true
+!
+
+insertFileAsStringLiteral:asStringLiteral
+ "insert contents of a file; either as-is or as a string literal.
+ - ask user for filename using a fileSelectionBox."
+
+ |sel selFn file text ok initial|
+
+ ((sel := self selectionAsString) notEmptyOrNil
+ and:[ (selFn := sel asFilename) exists
+ and:[ selFn isRegularFile ]])
+ ifTrue:[
+ initial := selFn pathName.
+ ].
+
+ [
+ |why|
+
+ file := Dialog
+ requestFileName:(resources string:'Insert Contents Of:')
+ default:initial
+ ok:(resources string:'Insert')
+ abort:(resources string:'Cancel')
+ pattern:nil
+ fromDirectory:directoryForFileDialog.
+ file isNil ifTrue:[
+ "cancel"
+ ^ self.
+ ].
+ file := file asFilename.
+ directoryForFileDialog := file.
+
+ ok := file isReadable and:[file isDirectory not].
+ ok ifFalse:[
+ file isReadable ifFalse:[
+ why := '%1 is unreadable.\\Please try again.'
+ ] ifTrue:[
+ why := '%1 is a directory.\\Please try again.'
+ ].
+ Dialog warn:(resources stringWithCRs:why with:file pathName).
+ ].
+ ] doUntil:[ok].
+
+ text := file contentsOfEntireFile.
+ self
+ undoableDo:[ self paste:(asStringLiteral ifTrue:[text storeString] ifFalse:[text]) ]
+ info:'Paste File'
+
+ "Modified: / 28-07-2007 / 13:23:32 / cg"
+!
+
+insertURL
+ "insert contents of a URL
+ - ask user for URL using a dialog."
+
+ self insertURLAsStringLiteral:false
+!
+
+insertURLAsStringLiteral:asStringLiteral
+ "insert contents of a file; either as-is or as a string literal.
+ - ask user for filename using a fileSelectionBox."
+
+ |sel url text response initial|
+
+ (sel := self selectionAsString) notEmptyOrNil
+ ifTrue:[
+ initial := sel.
+ ].
+
+ url := Dialog
+ request:(resources string:'Insert Contents of URL:')
+ initialAnswer:initial
+ okLabel:(resources string:'Insert')
+ title:(resources string:'URL').
+ url isNil ifTrue:[
+ "cancel"
+ ^ self.
+ ].
+ response := HTTPInterface get:url.
+ response isErrorResponse ifTrue:[
+ Dialog warn:(resources string:'Could not fetch the document: %1' with:url).
+ ^ self.
+ ].
+ text := response data asString.
+
+ self
+ undoableDo:[
+ self paste:(asStringLiteral ifTrue:[text storeString] ifFalse:[text])
+ ]
+ info:'Insert Contents of URL'
+!
+
+insertUUID
+ "insert a new UUID's string"
+
+ typeOfSelection := nil.
+ self
+ undoableDo:[ self pasteOrReplace:(UUID genUUID printString)]
+ info:'Paste New UUID'
+
+ "Created: / 28-07-2007 / 13:01:16 / cg"
+!
+
+insertUnicode
+ "open a Dialog requesting an integer value and insert it as unicode character"
+
+ |unicodePoint unicodeChar unicodeString|
+
+ unicodeString := Dialog request:'Enter unicode (U+01FF or decimal number):'.
+ unicodeString size < 2 ifTrue:[
+ ^ self.
+ ].
+ (unicodeString second = $+ and:['Uu' includes:unicodeString first]) ifTrue:[
+ unicodePoint := Integer readFrom:(unicodeString copyFrom:3) radix:16 onError:[^ self].
+ ] ifFalse:[
+ unicodePoint := Integer readFrom:unicodeString onError:[^ self].
+ ].
+
+ unicodeChar := Character value:unicodePoint.
+ self keyPress:unicodeChar x:0 y:0.
+ self keyRelease:unicodeChar x:0 y:0.
+!
+
+internalSpellingSuggestion
+ "insert the internal-spelling suggestion for the selected text.
+ Requires that the RefactoryBrowser/line/spelCheck stuff is loaded."
+
+ |text suggestions best|
+
+ self withWaitCursorDo:[
+ text := self selection asString string withoutSeparators.
+ text size == 0 ifTrue:[^ self].
+
+ suggestions := RBSpellChecker default bestMatchesFor:text.
+ suggestions size == 0 ifTrue:[
+ self information:('No spelling suggestion from builtin checker for: ' , text).
+ Transcript showCR:('No spelling suggestion from builtin checker for: ' , text).
+ ^ self.
+ ].
+ Transcript showCR:suggestions.
+ best := suggestions first.
+ ].
+ self
+ undoablePaste:best
+ info:'Spelling Suggestion'.
+!
+
+openFileBrowserOnFileNamed:fileNameString
+ "open a fileBrowser on the given fileNameString"
+
+ |fn|
+
+ fn := fileNameString asFilename.
+ fn exists ifFalse:[
+ fn := fileNameString withoutSeparators withoutQuotes asFilename.
+ fn exists ifFalse:[
+ ^ self warn:'Oops - file is gone'.
+ ].
+ ].
+ UserPreferences fileBrowserClass openOn:fn
+
+ "Modified: / 06-09-2012 / 14:47:22 / cg"
+!
+
+openFileBrowserOnIt
+ "open a fileBrowser on the selected fileName"
+
+ |fileNameString|
+
+ fileNameString := self selectionAsString.
+ self openFileBrowserOnFileNamed:fileNameString
+
+ "Modified: / 06-09-2012 / 14:47:22 / cg"
+!
+
+openWorkspaceWithIt
+ "open a workspace containing the selected text"
+
+ |text|
+
+ text := self selectionAsString.
+ WorkspaceApplication openWith:text selected:true
+
+ "Created: / 26-05-2007 / 06:05:22 / cg"
+!
+
+paste
+ "paste the copybuffer; if there is a selection, unselect first.
+ Then paste at cursor position."
+
+ self checkModificationsAllowed ifTrue:[
+ self withSelfAndTextForPasteDo:[:me :text |
+ me unselect.
+ me undoablePaste:text
+ ]
+ ]
+!
+
+paste:someText
+ "paste someText at cursor"
+
+ self paste:someText withCR:false
+!
+
+paste:someText withCR:withCR
+ "paste someText at cursor"
+
+ |s nLines startLine startCol l1 l2 c1 c2 codingErrorReported|
+
+ self checkModificationsAllowed ifFalse:[^ self].
+ someText isNil ifTrue:[^ self].
+
+ s := someText.
+ codingErrorReported := false.
+ CharacterEncoderError handle:[:ex |
+ |code msg|
+
+ code := ex parameter.
+ codingErrorReported ifFalse:[
+ msg := 'Cannot represent pasted string in this Views encoding (',gc characterEncoding,').'.
+ code notNil ifTrue:[
+ msg := msg , '\\Reason: No representation for ' , (code radixPrintStringRadix:16).
+ ].
+ Dialog warn:(resources stringWithCRs:msg).
+ codingErrorReported := true.
+ ].
+ ex proceedWith:ex defaultValue
+ ] do:[
+ s isString ifTrue:[
+ s encoding ~~ gc characterEncoding ifTrue:[
+ s := s encodeFrom:(s encoding) into:gc characterEncoding.
+ ].
+
+ s := s asStringCollection.
+ (someText endsWith:Character cr) ifTrue:[
+ "/ s := s copyWith:nil.
+ s := s copyWith:'' "/ an empty line at the end
+
+ ]
+ ] ifFalse:[
+ s isStringCollection ifTrue:[
+ s := s encodeFrom:(s encoding) into:gc characterEncoding.
+ ] ifFalse:[
+ (self
+ confirm:(resources
+ stringWithCRs:'Selection (%1) is not convertable to Text.\\Paste storeString ?'
+ with:s class name)) ifFalse:[^ self].
+ s := StringCollection with:s storeString .
+ "/ ^ self
+ ].
+ ].
+ ].
+
+ (nLines := s size) == 0 ifTrue:[^ self].
+ (nLines == 1 and:[(s at:1) size == 0]) ifTrue:[^ self].
+
+ typeOfSelection := #paste.
+
+ startLine := l1 := cursorLine.
+ startCol := c1 := cursorCol.
+
+ "do not expand tabs into spaces here -
+ they get expanded in basicWithoutRedrawInsertStringWithoutCRs:aString atLine:lineNr col:colNr.
+ Some Subviews want to paste with unexpanded tabs!!"
+
+ self insertLines:s withCR:withCR.
+ l2 := cursorLine.
+ c2 := (cursorCol - 1).
+ self selectFromLine:l1 col:c1 toLine:l2 col:c2.
+ typeOfSelection := #paste. "/ sigh - cleared by #selectFromLine:
+
+ "Modified: / 14-02-1996 / 11:14:14 / stefan"
+ "Modified: / 25-01-2012 / 00:31:30 / cg"
+!
+
+pasteAsStringLiteral
+ "insert clipboard string as a string literal.
+ Almost the same as a normal paste, but single-quotes are doubled,
+ to make it a legal string literal"
+
+ typeOfSelection := nil.
+ self
+ undoableDo:[ self pasteOrReplace:(self getClipboardText storeString) ]
+ info:'Paste as String Literal'
+!
+
+pasteOrReplace
+ "paste the copybuffer; if there is a selection, replace it.
+ otherwise paste at cursor position.
+ Replace is not done for selections which were created by a paste,
+ to allow multiple paste operations in a row."
+
+ self withSelfAndTextForPasteDo:[:me :text | me pasteOrReplace:text]
+!
+
+pasteOrReplace:someText
+ "paste someText; if there is a selection, replace it.
+ otherwise paste at cursor position. Replace is not done
+ for originating by a paste, to allow multiple
+ paste."
+
+ self checkModificationsAllowed ifFalse:[^ self].
+
+ self undoableDo:[
+ ((self hasSelection == true) and:[typeOfSelection ~~ #paste]) ifTrue:[
+ self replace:someText
+ ] ifFalse:[
+ self paste:someText.
+ ]
+ ] info:'Paste/Replace'.
+
+ "Modified: / 30.1.2000 / 02:33:00 / cg"
+!
+
+pasteOrReplaceFromHistory
+ "paste a previous item from the copybuffer history.
+ (i.e. repaste some previously deleted or copied text)"
+
+ |text|
+
+ self checkModificationsAllowed ifFalse:[
+ self flashReadOnly.
+ ^ self
+ ].
+ text := self getTextSelectionFromHistory.
+ text notNil ifTrue:[
+ self pasteOrReplace:text
+ ]
+!
+
+replace
+ "replace the selection by the contents of the copybuffer"
+
+ self hasSelection ifFalse:[^ self].
+ self checkModificationsAllowed ifFalse:[^ self].
+
+ self withSelfAndTextForPasteDo:[:me :text |
+ me undoableDo:[ me replace:text ]
+ info:'Replace'
+ ]
+!
+
+replace:someText
+ "replace the selection by someText"
+
+ |selected selectedString
+ selStartLine selStartCol selEndLine selEndCol|
+
+ self checkModificationsAllowed ifFalse:[^ self].
+
+ self undoableDo:[
+ selected := self selection.
+ selected isNil ifTrue:[
+ ^ self paste:someText
+ ].
+
+ self deleteSelection.
+
+ "take care, if we replace a selection without space by a word selected
+ with one - in this case we usually do not want the space.
+ But, if we replace a word-selected selection by something without a
+ space, we DO want the space added."
+
+ selected size == 1 ifTrue:[
+ selectedString := selected at:1.
+ ].
+
+ someText size == 1 ifTrue:[
+ |cutOffSpace addSpace replacement replacementString|
+
+ cutOffSpace := false.
+ addSpace := false.
+ replacement := someText copyFrom:1.
+
+ selectedString notNil ifTrue:[
+ ((selectedString startsWith:' ') or:[selectedString endsWith:' ']) ifFalse:[
+ "selection has no space"
+
+ ((selectStyle == #wordleft) or:[selectStyle == #wordRight]) ifTrue:[
+ cutOffSpace := true
+ ]
+ ] ifTrue:[
+ addSpace := true
+ ]
+ ].
+ replacementString := replacement at:1.
+ cutOffSpace ifTrue:[
+ (replacementString startsWith:' ') ifTrue:[
+ replacementString := replacementString withoutSpaces
+ ].
+ ] ifFalse:[
+ selectStyle == #wordLeft ifTrue:[
+ "want a space at left"
+ (replacementString startsWith:' ') ifFalse:[
+ replacementString := replacementString withoutSpaces.
+ replacementString := ' ' , replacementString
+ ]
+ ].
+ selectStyle == #wordRight ifTrue:[
+ "want a space at right"
+
+ (replacementString endsWith:' ') ifFalse:[
+ replacementString := replacementString withoutSpaces.
+ replacementString := replacementString , ' '
+ ]
+ ].
+ ].
+ replacement at:1 put: replacementString.
+ self paste:replacement.
+ ] ifFalse:[
+ self paste:someText
+ ].
+ self setLastStringToReplace: selectedString.
+
+ lastReplacementInfo lastReplacement: someText.
+
+ selStartLine := selectionStartLine.
+ selStartCol := self selectionStartCol.
+ selEndLine := selectionEndLine.
+ selEndCol := self selectionEndCol.
+ ]
+ info:'Replace'
+
+ "Modified: / 14.2.1996 / 10:37:02 / stefan"
+ "Modified: / 5.4.1998 / 16:55:28 / cg"
+!
+
+searchReplace
+ "search for a string - show a box to enter searchpattern
+ replace for the found searchpattern or replace all searchpattern found to a new pattern - show a box to enter replacepattern
+ - currently no regular expressions are handled."
+
+ |searchBox patternHolder replacePatternHolder caseHolder flag ign initialString bindings bldr search modal replace action|
+
+ modal := false "(UserPreferences current searchDialogIsModal)".
+ lastSearchDirection := #forward.
+ ign := lastSearchIgnoredCase ? LastSearchIgnoredCase ? true.
+ caseHolder := ign asValue.
+ patternHolder := '' asValue.
+ replacePatternHolder := '' asValue.
+ lastSearchPattern notNil ifTrue:[
+ initialString := lastSearchPattern
+ ].
+ self hasSelectionWithinSingleLine ifTrue:[
+ initialString := self selectionAsString
+ ].
+ initialString isNil ifTrue:[
+ LastSearchPatterns size > 0 ifTrue:[
+ initialString := LastSearchPatterns first
+ ]
+ ].
+ initialString notNil ifTrue:[
+ patternHolder value:initialString.
+ replacePatternHolder value:initialString.
+ ].
+ flag := true.
+ search := [:fwd |
+ self
+ search:patternHolder value
+ ignoreCase:caseHolder value
+ forward:fwd.
+ ].
+ replace := [:all |
+ self
+ replace:patternHolder value
+ by:replacePatternHolder value
+ all:all
+ ignoreCase:caseHolder value
+ ].
+ bindings := IdentityDictionary new.
+ bindings at:#searchPattern put:patternHolder.
+ bindings at:#replacePattern put:replacePatternHolder.
+ modal ifTrue:[
+ bindings at:#nextAction
+ put:[
+ flag := true.
+ action := search.
+ searchBox doAccept.
+ ].
+ bindings at:#prevAction
+ put:[
+ flag := false.
+ action := search.
+ searchBox doAccept.
+ ].
+ bindings at:#replaceAction
+ put:[
+ flag := false.
+ action := replace.
+ searchBox doAccept.
+ ].
+ bindings at:#replaceAllAction
+ put:[
+ flag := true.
+ action := replace.
+ searchBox doAccept.
+ ].
+ ] ifFalse:[
+ bindings at:#nextAction put:[ search value:true. ].
+ bindings at:#prevAction put:[ search value:false. ].
+ bindings at:#replaceAction put:[ replace value:false. ].
+ bindings at:#replaceAllAction put:[ replace value:true. ].
+ ].
+ bindings at:#ignoreCase put:caseHolder.
+ bindings at:#patternList put:LastSearchPatterns.
+ modal ifTrue:[
+ searchBox := SimpleDialog new.
+ ] ifFalse:[
+ searchBox := ApplicationModel new.
+ searchBox createBuilder.
+ ].
+ searchBox resources:(self resources).
+ bldr := searchBox builder.
+ bldr addBindings:bindings.
+ searchBox allButOpenFrom:(self class searchReplaceDialogSpec).
+ (bldr componentAt:#nextButton) cursor:(Cursor thumbsUp).
+ (bldr componentAt:#prevButton) cursor:(Cursor thumbsUp).
+ (bldr componentAt:#cancelButton) cursor:(Cursor thumbsDown).
+ modal ifTrue:[
+ searchBox openDialog.
+ searchBox accepted ifTrue:[
+ action value:flag
+ ].
+ ] ifFalse:[
+ (bldr componentAt:#nextButton) isReturnButton:false.
+ (bldr componentAt:#cancelButton)
+ label:(resources string:'Close');
+ action:[ searchBox closeRequest ].
+
+ "/ searchBox masterApplication:self application.
+
+ self topView beMaster.
+ (searchBox window)
+ beSlave;
+ openInGroup:(self windowGroup).
+
+ "/ searchBox window open.
+
+ searchBox window assignKeyboardFocusToFirstInputField.
+ ]
+
+ "Modified: / 11-07-2006 / 11:20:06 / fm"
+!
+
+showDeleted
+ "open a readonly editor on all deleted text"
+
+ |v|
+
+ v := EditTextView openWith:(Smalltalk at:#DeleteHistory).
+ v readOnly:true.
+ v topView label:'deleted text'.
+!
+
+sort:how ignoreCase:ignoreCase fromLine:start toLine:end
+ "sort/reorder the selected lines.
+ how:
+ #lines
+ #linesByFirstWord
+ #linesByNthWord
+ #linesByNthNumber
+ #linesByNthHexNumber
+ #words
+ #linesByLength
+ #reverse
+ "
+
+ |lines extractor innerExtractor fetcher operation lineWise nStr n s words|
+
+ lineWise := true.
+
+ how == #reverse ifTrue:[
+ operation := [:lines | lines reverse].
+ ] ifFalse:[
+ operation := [:linesOrWords |
+ linesOrWords sort:[:item1 :item2 | (fetcher value:item1) < (fetcher value:item2)]
+ ].
+
+ how == #linesByLength ifTrue:[
+ fetcher := [:l | l size].
+ ] ifFalse:[
+ how == #lines ifTrue:[
+ extractor := [:l | l withoutLeadingSeparators].
+ ] ifFalse:[
+ how == #linesByFirstWord ifTrue:[
+ extractor := [:l | ((l asCollectionOfWords select:[:w | w isEmpty or:[w first isLetterOrDigit]]) at:1 ifAbsent:'')].
+ ] ifFalse:[
+ ((how == #linesByNthWord) or:[ how == #linesByNthNumber or:[ how == #linesByNthHexNumber]]) ifTrue:[
+ nStr := Dialog request:'Word/Column (1..)' initialAnswer:(LastColumnNumberForSort ? 2).
+ nStr isEmptyOrNil ifTrue:[^ self].
+ n := Integer readFrom:nStr onError:[^ self].
+ LastColumnNumberForSort := n.
+ extractor := [:l | ((l string asCollectionOfWords) at:n ifAbsent:'')].
+ how == #linesByNthNumber ifTrue:[
+ innerExtractor := extractor.
+ extractor := [:l | Integer readFrom:(innerExtractor value:l) onError:0]
+ ] ifFalse:[
+ how == #linesByNthHexNumber ifTrue:[
+ innerExtractor := extractor.
+ extractor := [:l |
+ |s|
+ s := innerExtractor value:l.
+ (s startsWith:'16r') ifTrue:[
+ (Integer readSmalltalkSyntaxFrom:s) ? 0
+ ] ifFalse:[
+ Integer readFrom:s radix:16 onError:[ 0 ]
+ ]
+ ]
+ ]
+ ].
+ ] ifFalse:[
+ how == #words ifTrue:[
+ lineWise := false.
+ extractor := [:w | w].
+ ] ifFalse:[
+ self error:'unknown sort criteria: ', how printString.
+ ]
+ ]
+ ]
+ ].
+ ignoreCase ifTrue:[
+ fetcher := [:l | (extractor value:l) asLowercase].
+ ] ifFalse:[
+ fetcher := extractor.
+ ].
+ ].
+ ].
+
+ lineWise ifTrue:[
+ "process the lines of the selection (aka a collection of lines)"
+ start == end ifTrue:[^ self ].
+ lines := (start to:end) collect:[:lineNr | (self listAt:lineNr) ? ''].
+ lines := operation value:lines.
+ (start to:end) with:lines do:[:lineNr :line | self replaceLine:lineNr with:line].
+ ] ifFalse:[
+ "process the whole selection as a string"
+ s := self selectionAsString.
+ words := s asCollectionOfWords.
+ words := operation value:words.
+ s := words asStringCollection asStringWith:Character space.
+ self replace:s.
+ ].
+ self textChanged.
+
+ "Modified: / 31-03-2012 / 10:59:28 / cg"
+!
+
+sortSelection:how ignoreCase:ignoreCase
+ "sort the selected lines"
+
+ |start end|
+
+ selectionStartLine isNil ifTrue:[^ self].
+
+ start := selectionStartLine.
+ end := selectionEndLine.
+ (selectionEndCol == 0) ifTrue:[
+ end := end - 1
+ ].
+
+ self
+ undoableDo:[
+ self sort:how ignoreCase:ignoreCase fromLine:start toLine:end
+ ]
+ info:'Sort'
+
+ "Modified (format): / 15-02-2012 / 16:52:53 / cg"
+!
+
+specialCharacters
+ CharacterSetView
+ openAsInputFor:self
+ label:'Special Character Input'
+ clickLabel:'Click to Insert Character'.
+!
+
+undoablePaste:someText
+ self undoablePaste:someText info:nil.
+
+ "Modified: / 28-07-2007 / 13:25:46 / cg"
+!
+
+undoablePaste:someText info:infoOrNil
+ self
+ undoableDo:[
+ self paste:someText.
+ ]
+ info:infoOrNil
+
+ "Created: / 28-07-2007 / 13:25:30 / cg"
+!
+
+undoablePasteOrReplace:someText info:infoOrNil
+ self
+ undoableDo:[
+ self pasteOrReplace:someText.
+ ]
+ info:infoOrNil
+
+ "Created: / 28-07-2007 / 13:26:16 / cg"
+!
+
+undoablePasteReplacingAll:someText info:infoOrNil
+ self
+ undoableDo:[
+ self selectAll.
+ self pasteOrReplace:someText.
+ ]
+ info:infoOrNil
+
+ "Created: / 28-07-2007 / 13:25:30 / cg"
+!
+
+withSelfAndTextForPasteDo:aBlock
+ "common code for paste/replace of the copybuffer"
+
+ |sel|
+
+ self checkModificationsAllowed ifFalse:[
+ self flashReadOnly.
+ ^ self
+ ].
+
+ sel := self getTextSelectionOrTextSelectionFromHistory.
+ sel notNil ifTrue:[
+ aBlock value:self value:sel.
+ ]
+! !
+
+!EditTextView methodsFor:'private'!
+
+beep
+ UserPreferences current beepInEditor ifTrue:[
+ super beep
+ ]
+!
+
+checkModificationsAllowed
+ "check if the text can be modified (i.e. is not readOnly).
+ evaluate the exceptionBlock if not.
+ This block should be provided by the application or user of the textView,
+ and may show a warnBox or whatever."
+
+ self isReadOnly ifTrue: [
+ exceptionBlock isNil ifTrue:[
+ ^ false
+ ].
+
+ (exceptionBlock value:'Text may not be modified') ~~ true ifTrue:[
+ ^ false
+ ]
+ ].
+ ^ true
+
+ "Modified: / 17.6.1998 / 15:51:10 / cg"
+!
+
+currentSelectionBgColor
+ typeOfSelection == #paste ifTrue:[
+ ^ DefaultAlternativeSelectionBackgroundColor ? selectionBgColor
+ ].
+ ^ super currentSelectionBgColor
+
+ "
+ DefaultAlternativeSelectionBackgroundColor := Color yellow blendWith:Color green
+ "
+!
+
+currentSelectionFgColor
+ typeOfSelection == #paste ifTrue:[
+ ^ DefaultAlternativeSelectionForegroundColor ? selectionFgColor
+ ].
+ ^ super currentSelectionFgColor
+!
+
+resetVariablesBeforeNewSearch
+ "clear the autosearch action, when the first pattern is searched for"
+
+ super resetVariablesBeforeNewSearch.
+
+ "/ new search invalidates remembered string
+ lastStringFromReplaceForNextSearch := nil.
+
+ "Modified (comment): / 07-03-2012 / 23:21:06 / cg"
+!
+
+setLastStringToReplace: aString
+
+ "This method will set the information coming from the last replace into the replacementInfo"
+
+ |lastReplaceIgnoredCase|
+
+ "/ The searchAction is mantained until a cut/replace or a search with a user selection is done
+ self clearSearchAction.
+
+ lastReplacementInfo lastStringToReplace: aString.
+ lastStringFromReplaceForNextSearch := aString.
+
+ "If the replace came after a search, the next replace will have the ignored case from that search action"
+ lastReplaceIgnoredCase := (typeOfSelection == #search)
+ ifTrue: [lastSearchIgnoredCase]
+ ifFalse: [nil].
+ lastReplacementInfo lastReplaceIgnoredCase: lastReplaceIgnoredCase.
+!
+
+st80EditMode
+ ^ st80Mode ? (UserPreferences current st80EditMode)
+!
+
+suppressEmphasisInSelection
+ "selection is shown without emphasis"
+
+ ^ true
+!
+
+textChanged
+ "my text was modified (internally).
+ Sent whenever text has been edited (not to confuse with
+ contentsChanged, which is triggered when the size has changed, and
+ is used to notify scrollers, other views etc.).
+
+ As some authors of this code have been very sloppy in tha past
+ (not sending contentsChanged, but textChanged),
+ we do it here despite what is written above, to ensure that scrollers update correctly."
+
+ self contentsChanged.
+ self modified:true.
+ contentsWasSaved := false
+
+ "Modified: 14.2.1997 / 16:58:38 / cg"
+!
+
+textChangedButNoSizeChange
+ "my text was modified (internally).
+ Sent whenever text has been edited (not to confuse with
+ contentsChanged, which is triggered when the size has changed, and
+ is used to notify scrollers, other views etc.)"
+
+ self modified:true.
+ contentsWasSaved := false
+
+ "Modified: 14.2.1997 / 16:58:38 / cg"
+! !
+
+!EditTextView methodsFor:'queries'!
+
+currentLine
+ "the current line (for relative gotos)"
+
+ ^ cursorLine
+
+ "Created: / 17.5.1998 / 20:07:52 / cg"
+!
+
+hasSearchActionSelection
+
+ ^ typeOfSelection == #searchAction
+!
+
+isKeyboardConsumer
+ "return true, if the receiver is a keyboard consumer;
+ Return true here, redefined from SimpleView."
+
+ ^ self isReadOnly not
+!
+
+specClass
+ "redefined, since the name of my specClass is nonStandard (i.e. not EditTextSpec)"
+
+ self class == EditTextView ifTrue:[^ TextEditorSpec].
+ ^ super specClass
+
+ "Modified: / 31.10.1997 / 19:48:19 / cg"
+!
+
+tabMeansNextField
+ "return true, if a Tab character should shift focus."
+
+ "if not readOnly, I want my tab keys ..."
+
+ ^ self isReadOnly or:[tabMeansNextField]
+
+ "Created: 7.2.1996 / 19:15:31 / cg"
+!
+
+widthOfContents
+ "return the width of the contents in pixels
+ Redefined to add the size of a space (for the cursor).
+ this enables us to scroll one position further than the longest
+ line (and possibly see the cursor behind the line)"
+
+ |w dev|
+
+ w := super widthOfContents.
+ (dev := self graphicsDevice ) isNil ifTrue:[
+ "/ really don't know ...
+ dev := Screen current
+ ].
+ ^ w + (gc font widthOn:dev)
+
+ "Modified: 28.5.1996 / 19:32:25 / cg"
+! !
+
+!EditTextView methodsFor:'realization'!
+
+realize
+ "make the view visible - scroll to make the cursor visible."
+
+ super realize.
+
+ self makeCursorVisible.
+ cursorFgColor := cursorFgColor onDevice:self graphicsDevice.
+ cursorBgColor := cursorBgColor onDevice:self graphicsDevice.
+
+ "Modified: 20.12.1996 / 14:16:05 / cg"
+ "Created: 24.7.1997 / 18:24:12 / cg"
+! !
+
+!EditTextView methodsFor:'redrawing'!
+
+redrawCursorIfBetweenVisibleLine:startVisLine and:endVisLine
+ "redraw the cursor, if it sits in a line range"
+
+ cursorShown ifTrue:[
+ cursorVisibleLine notNil ifTrue:[
+ (cursorVisibleLine between:startVisLine and:endVisLine) ifTrue:[
+ self drawCursorCharacter
+ ]
+ ]
+ ]
+!
+
+redrawCursorIfInVisibleLine:visLine
+ "redraw the cursor, if it sits in visible line"
+
+ cursorShown ifTrue:[
+ (visLine == cursorVisibleLine) ifTrue:[
+ self drawCursorCharacter
+ ]
+ ]
+!
+
+redrawFromVisibleLine:startVisLine to:endVisLine
+ "redraw a visible line range"
+
+ super redrawFromVisibleLine:startVisLine to:endVisLine.
+ self redrawCursorIfBetweenVisibleLine:startVisLine and:endVisLine
+!
+
+redrawVisibleLine:visLine
+ "redraw a visible line"
+
+ super redrawVisibleLine:visLine.
+ self redrawCursorIfInVisibleLine:visLine
+!
+
+redrawVisibleLine:visLine col:colNr
+ "redraw the single character in visibleline at colNr"
+
+ super redrawVisibleLine:visLine col:colNr.
+ cursorShown ifTrue:[
+ (visLine == cursorVisibleLine) ifTrue:[
+ (colNr == cursorCol) ifTrue:[
+ self drawCursorCharacter.
+ ^ self
+ ]
+ ]
+ ].
+
+ "Modified: / 05-11-2007 / 17:35:53 / cg"
+!
+
+redrawVisibleLine:visLine from:startCol
+ "redraw a visible line from startCol to the end of line"
+
+ super redrawVisibleLine:visLine from:startCol.
+ cursorShown ifTrue:[
+ self redrawCursorIfInVisibleLine:visLine
+ ]
+!
+
+redrawVisibleLine:visLine from:startCol to:endCol
+ "redraw a visible line from startCol to endCol"
+
+ super redrawVisibleLine:visLine from:startCol to:endCol.
+ self redrawCursorIfInVisibleLine:visLine
+! !
+
+!EditTextView methodsFor:'scrolling'!
+
+additionalMarginForHorizontalScroll
+ "return the number of pixels by which we may scroll more than the actual
+ width of the document would allow.
+ This is redefined by editable textViews, to allo for the cursor
+ to be visible if it is positioned right behind the longest line of text.
+ The default returned here is 10 pixels, which should be ok for most cursors"
+
+ ^ 10 max:gc font width
+!
+
+halfPageDown
+ "half a page down - to keep cursor on same visible line, it has to be moved
+ within the real text "
+
+ |prevCursorLine|
+
+ prevCursorLine := cursorVisibleLine.
+ super halfPageDown.
+ self cursorVisibleLine:prevCursorLine col:cursorCol
+!
+
+halfPageUp
+ "half a page up - to keep cursor on same visible line, it has to be moved
+ within the real text "
+
+ |prevCursorLine|
+
+ prevCursorLine := cursorVisibleLine.
+ super halfPageUp.
+ self cursorVisibleLine:prevCursorLine col:cursorCol
+!
+
+originChanged:delta
+ "sent after scrolling - have to show the cursor if it was on before"
+
+ super originChanged:delta.
+ "
+ should we move the cursor with the scroll - or leave it ?
+ "
+ self updateCursorVisibleLine.
+ prevCursorState ifTrue:[
+ self showCursor
+ ]
+
+ "Modified: / 17.6.1998 / 16:13:24 / cg"
+!
+
+originWillChange
+ "sent before scrolling - have to hide the cursor"
+
+ prevCursorState := cursorShown.
+ "/ cursorShown := false.
+ cursorShown ifTrue:[
+ self hideCursor
+ ]
+
+ "Modified: / 6.7.1998 / 13:07:23 / cg"
+!
+
+pageDown
+ "page down - to keep cursor on same visible line, it has to be moved
+ within the real text "
+
+ |prevCursorLine|
+
+ prevCursorLine := cursorVisibleLine.
+ super pageDown.
+ self cursorVisibleLine:prevCursorLine col:cursorCol
+!
+
+pageUp
+ "page up - to keep cursor on same visible line, it has to be moved
+ within the real text "
+
+ |prevCursorLine|
+
+ prevCursorLine := cursorVisibleLine.
+ super pageUp.
+ self cursorVisibleLine:prevCursorLine col:cursorCol
+! !
+
+!EditTextView methodsFor:'searching'!
+
+searchBwd:pattern ifAbsent:aBlock
+ "do a backward search"
+
+ self searchBwd:pattern ignoreCase:false ifAbsent:aBlock
+!
+
+searchBwd:pattern ignoreCase:ign ifAbsent:aBlock
+ "do a backward search"
+
+ cursorLine isNil ifTrue:[^ self].
+ super searchBwd:pattern ignoreCase:ign ifAbsent:aBlock
+!
+
+searchBwd:pattern ignoreCase:ign startingAtLine:startLine col:startCol ifAbsent:aBlock
+ "do a backward search"
+
+ cursorLine isNil ifTrue:[^ self].
+
+ self
+ searchBackwardFor:pattern
+ ignoreCase:ign
+ startingAtLine:startLine col:startCol
+ ifFound:[:line :col |
+ self cursorMovementAllowed ifTrue:[
+ self cursorLine:line col:col.
+ ].
+ self showMatch:pattern isMatch:false atLine:line col:col.
+"/ self makeLineVisible:cursorLine
+ typeOfSelection := #search]
+ ifAbsent:aBlock
+
+ "Modified: 9.10.1997 / 13:02:13 / cg"
+!
+
+searchForAndSelectMatchingParenthesis
+ "select characters enclosed by matching parenthesis if one is under cusor"
+
+ self searchForAndSelectMatchingParenthesisFromLine:cursorLine col:cursorCol
+!
+
+searchForMatchingParenthesis
+ "search for a matching parenthesis starting at cursor position.
+ Search for the corresponding character is done forward if its an opening,
+ backwards if its a closing parenthesis.
+ Positions the cursor if found, peeps if not"
+
+ self cursorMovementAllowed ifFalse:[^ self].
+
+ self
+ searchForMatchingParenthesisFromLine:cursorLine col:cursorCol
+ ifFound:[:line :col | self cursorLine:line col:col]
+ ifNotFound:[self showNotFound]
+ onError:[self beep]
+
+ "Modified: 9.10.1997 / 12:56:30 / cg"
+!
+
+searchFwd:pattern ignoreCase:ign match: match startingAtLine:startLine col:startCol ifAbsent:aBlock
+ "do a forward search"
+
+ cursorLine isNil ifTrue:[^ self].
+ self
+ searchForwardFor:pattern
+ ignoreCase:ign
+ match: match
+ startingAtLine:startLine col:startCol
+ ifFound:[:line :col |
+ self cursorMovementAllowed ifTrue:[
+ self cursorLine:line col:col.
+ ].
+ self showMatch:pattern isMatch:match atLine:line col:col.
+"/ self makeLineVisible:cursorLine
+ typeOfSelection := #search
+ ]
+ ifAbsent:aBlock
+
+ "Modified: 9.10.1997 / 12:57:47 / cg"
+ "Created: 9.10.1997 / 13:01:12 / cg"
+!
+
+searchFwd:pattern ignoreCase:ign startingAtLine:startLine col:startCol ifAbsent:aBlock
+ "do a forward search"
+
+ self searchFwd:pattern ignoreCase:ign match: false startingAtLine:startLine col:startCol ifAbsent:aBlock
+!
+
+searchFwd:pattern startingAtLine:startLine col:startCol ifAbsent:aBlock
+ "do a forward search"
+
+ self searchFwd:pattern ignoreCase:false startingAtLine:startLine col:startCol ifAbsent:aBlock
+!
+
+searchPatternForSearchBar
+
+ "Returns the next searchPattern from the user selection or from the autoSearch"
+
+ |searchPattern|
+
+ searchPattern := self searchPatternFromUserSelectionOrReplace.
+ searchPattern isNil
+ ifTrue: [searchPattern := lastSearchPattern]
+ ifFalse: [lastSearchPattern := searchPattern].
+ ^ searchPattern
+!
+
+setSearchPatternWithMatchEscapes: match
+ "set the searchpattern from the selection if there is one, and position
+ cursor to start of pattern"
+
+ |sel|
+
+ "/
+ "/ if the last operation was a replace, set pattern to last
+ "/ original string (for search after again)
+ "/ for cut or delete actions allow lastReplacement with nil
+ "/
+"/ (lastStringFromReplaceForNextSearch notNil
+"/ and:[typeOfSelection ~~ #search]) ifTrue:[
+"/ lastStringFromReplaceForNextSearch isString ifTrue:[
+"/ lastSearchPattern := lastStringFromReplaceForNextSearch.
+"/ ] ifFalse:[
+"/ lastSearchPattern := lastStringFromReplaceForNextSearch asStringWithoutFinalCR.
+"/ ].
+"/ ^ self
+"/ ].
+
+ "/
+ "/ if there is a selection:
+ "/ if there was no previous search, take it as search pattern.
+ "/ if there was a previous search, only take the selection if
+ "/ it did not result from a paste or from the last search itself.
+ "/ (to allow search-paste to be repeated)
+ "/
+ sel := self selection.
+ sel notNil ifTrue:[
+ (lastSearchPattern isNil
+ or:[typeOfSelection ~~ #paste and:[typeOfSelection ~~ #search]]
+ ) ifTrue:[
+ self cursorLine:selectionStartLine col:selectionStartCol.
+ lastSearchPattern := sel asStringWithoutFinalCR.
+ match ifTrue:[lastSearchPattern := lastSearchPattern withMatchEscapes].
+ ]
+ ]
+
+ "Modified: / 07-05-2011 / 17:25:59 / cg"
+!
+
+showMatch:pattern isMatch:isMatch atLine:line col:col
+ super showMatch:pattern isMatch:isMatch atLine:line col:col.
+ typeOfSelection := #search.
+!
+
+startPositionForSearchBackward
+ ^ self startPositionForSearchBackwardBasedOnCursorOrSelection
+!
+
+startPositionForSearchBackwardBasedOnCursorOrSelection
+ |startLine startCol|
+
+ selectionStartLine notNil ifTrue:[
+ startLine := selectionStartLine.
+ startCol := selectionStartCol
+ ] ifFalse:[
+ cursorLine isNil ifTrue:[
+ startLine := list size.
+ startCol := self listAt:startLine size.
+ ] ifFalse:[
+ startLine := cursorLine min:list size.
+ startCol := cursorCol
+ ]
+ ].
+
+ ^ startCol @ cursorLine
+!
+
+startPositionForSearchForward
+ ^ self startPositionForSearchForwardBasedOnCursorOrSelection
+!
+
+startPositionForSearchForwardBasedOnCursorOrSelection
+ |startCol|
+
+ "/ if there is no selection and the cursor is at the origin,
+ "/ assume its the first search and do not skip the very first match
+ startCol := cursorCol.
+ self hasSelection ifTrue:[
+ ^ selectionEndCol @ selectionEndLine.
+ ] ifFalse:[
+ (cursorLine == 1 and:[cursorCol == 1]) ifTrue:[
+ startCol := 0
+ ].
+ startCol := startCol min:(self at:cursorLine) size
+ ].
+
+ ^ startCol @ cursorLine
+
+ "Modified (format): / 24-05-2012 / 13:58:37 / cg"
+! !
+
+!EditTextView methodsFor:'selections'!
+
+addToSelectionAfter:aBlock
+ "Pokud existuje selekce, upravi ji
+ podle aktualni pozice kurzoru a pozice
+ po provedeni blocku.
+ Urceno k implementaci Shift-Home a Shift-End
+ Nejak nevim, jak to presneji popsat :-)"
+
+ |startLine startCol endLine endCol |
+
+ self hasSelection ifTrue: [
+ startLine := selectionStartLine .
+ startCol := selectionStartCol .
+ endLine := selectionEndLine .
+ endCol := selectionEndCol .
+ ] ifFalse: [
+ startLine := endLine := cursorLine .
+ startCol := endCol := cursorCol .
+ ].
+
+ "deselectim a provedu presun kurzoru..."
+ self unselect .
+ aBlock value .
+
+ "funguje dost mizerne, jen na jednom radku..."
+ (startCol - cursorCol) abs <= (endCol - cursorCol) abs
+ ifTrue: [
+ startCol := cursorCol.
+ ] ifFalse: [
+ endCol := cursorCol - 1.
+ ].
+ self selectFromLine:startLine col:startCol toLine: endLine col:endCol .
+!
+
+autoMoveCursorToEndOfSelection
+ "return true, if the cursor should be automatically moved to the
+ end of a selection.
+ Redefined to return false in terminalViews, where the cursor should
+ not be affected by selecting"
+
+ ^ true
+!
+
+changeTypeOfSelectionTo:newType
+ typeOfSelection ~~ newType ifTrue:[
+ typeOfSelection := newType.
+ selectionStartLine notNil ifTrue:[
+ self
+ redrawFromLine:selectionStartLine col:selectionStartCol
+ toLine:selectionEndLine col:selectionEndCol
+ ].
+ ].
+!
+
+findNextWordAfterSelectionAndAddToSelection
+ |selStartCol selStartLine selEndCol selEndLine|
+
+ selectionStartCol isNil ifTrue:[
+ self selectWordUnderCursor.
+ ^ self
+ ].
+
+ selStartCol := selectionStartCol.
+ selEndCol := selectionEndCol.
+ selStartLine := selectionStartLine.
+ selEndLine := selectionEndLine.
+
+ self cursorToNextWord.
+ self selectWordUnderCursor.
+
+ self selectFromLine:selStartLine col:selStartCol toLine:selectionEndLine col:selectionEndCol.
+!
+
+searchPatternFromUserSelectionOrReplace
+
+ |sel searchPattern|
+
+ "/
+ "/ if the last operation was a replace, set pattern to last
+ "/ original string (for search after again)
+ "/
+ (lastStringFromReplaceForNextSearch notNil
+ and:[typeOfSelection ~~ #search]) ifTrue:[
+ lastStringFromReplaceForNextSearch isString ifTrue:[
+ searchPattern := lastStringFromReplaceForNextSearch.
+ ] ifFalse:[
+ searchPattern := lastStringFromReplaceForNextSearch asStringWithoutFinalCR.
+ ].
+ ^ searchPattern
+ ].
+
+ "/
+ "/ if there is a selection:
+ "/ if there was no previous search, take it as search pattern.
+ "/ if there was a previous search, only take the selection if
+ "/ it did not result from a paste.
+ "/ (to allow search-paste to be repeated)
+ "/
+ sel := self selection.
+ sel notNil ifTrue:[
+ typeOfSelection ~~ #search ifTrue:[
+ typeOfSelection ~~ #paste ifTrue:[
+ self cursorLine:selectionStartLine col:selectionStartCol.
+ searchPattern := sel asStringWithoutFinalCR.
+ ]
+ ].
+ ].
+
+ ^ searchPattern
+!
+
+selectAll
+ "select the whole text.
+ redefined to send super selectFrom... since we dont want the
+ cursor to be moved in this case."
+
+ list isNil ifTrue:[
+ self unselect
+ ] ifFalse:[
+ super selectFromLine:1 col:1 toLine:(list size + 1) col:0.
+ typeOfSelection := nil
+ ]
+
+ "Modified: 28.2.1997 / 19:14:54 / cg"
+!
+
+selectAllInitially
+ "select the whole text. This is called only once during the initialization
+ for editFields which are shown in a table or tree.
+ The selectAll is called via this method to allow for easier redefinition and
+ to distinguish auto-select from user-initiated selects."
+
+ self selectAll
+!
+
+selectCursorLine
+ "select cursorline"
+
+ self selectFromLine:cursorLine col:1 toLine:cursorLine+1 col:0
+!
+
+selectCursorLineFromBeginning
+ "select cursorline up to cursor position"
+
+ cursorCol > 1 ifTrue:[
+ self selectFromLine:cursorLine col:1
+ toLine:cursorLine col:(cursorCol-1)
+ ]
+
+ "Modified: 16.8.1996 / 19:14:14 / cg"
+!
+
+selectExpandCursorLine
+ "expand selection by one line or select cursorline"
+
+ selectionStartLine isNil ifTrue:[
+ self selectCursorLine
+ ] ifFalse:[
+ self selectFromLine:selectionStartLine col:selectionStartCol
+ toLine:cursorLine+1 col:0.
+ self makeLineVisible:selectionEndLine
+ ]
+!
+
+selectFromBeginOfLine
+ "select the text from the beginning of the current line to the current cursor position."
+
+ | newCursorCol ln |
+
+ list isNil ifTrue:[
+ self unselect
+ ] ifFalse:[
+ cursorCol > 1 ifTrue:[
+ ln := list at: cursorLine.
+ newCursorCol := ln notEmptyOrNil ifTrue:[ln indexOfNonSeparator] ifFalse:[1].
+ self selectFromLine:cursorLine col:newCursorCol toLine:cursorLine col:cursorCol-1.
+ cursorCol := newCursorCol.
+ typeOfSelection := nil
+ ]
+ ]
+
+ "Created: / 28-06-2011 / 22:47:04 / cg"
+ "Modified: / 18-07-2012 / 17:00:26 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+!
+
+selectFromBeginning
+ "select the text from the beginning to the current cursor position."
+
+ |col|
+
+ list isNil ifTrue:[
+ self unselect
+ ] ifFalse:[
+ cursorCol == 0 ifTrue:[
+ col := 0
+ ] ifFalse:[
+ col := cursorCol - 1
+ ].
+ super selectFromLine:1 col:1 toLine:cursorLine col:col.
+ typeOfSelection := nil
+ ]
+!
+
+selectFromLine:startLine col:startCol toLine:endLine col:endCol
+ "when a range is selected, position the cursor behind the selection
+ for easier editing. Also typeOfSelection is nilled here."
+
+ super selectFromLine:startLine col:startCol toLine:endLine col:endCol.
+ (selectionEndLine notNil and:[self autoMoveCursorToEndOfSelection]) ifTrue:[
+ self cursorLine:selectionEndLine col:(selectionEndCol + 1).
+ ].
+ typeOfSelection := nil
+!
+
+selectToEndOfLine
+ "select the text from the current cursor position to the end of the current line"
+
+ | newCursorCol line |
+
+ list isNil ifTrue:[
+ self unselect
+ ] ifFalse:[
+ cursorCol >= 1 ifTrue:[
+ line := list at: cursorLine.
+ newCursorCol := line size.
+ [ newCursorCol > 1 and:[(line at:newCursorCol) isSeparator] ]
+ whileTrue:[newCursorCol := newCursorCol - 1].
+
+ self selectFromLine:cursorLine col:cursorCol toLine:cursorLine col: newCursorCol.
+ cursorCol := newCursorCol.
+ typeOfSelection := nil
+ ]
+ ]
+
+ "Created: / 28-06-2011 / 23:07:07 / cg"
+ "Modified: / 30-06-2011 / 19:51:21 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+!
+
+selectUpToEnd
+ "select the text from the current cursor position to the end."
+
+ list isNil ifTrue:[
+ self unselect
+ ] ifFalse:[
+ super selectFromLine:cursorLine col:cursorCol toLine:(list size + 1) col:0.
+ typeOfSelection := nil
+ ]
+!
+
+selectWordUnderCursor
+ "select the word under the cursor"
+
+ self selectWordAtLine:cursorLine col:cursorCol
+!
+
+unselect
+ "forget and unhilight selection - must take care of cursor here"
+
+ |wasOn|
+
+ wasOn := self hideCursor.
+ super unselect.
+ typeOfSelection := nil.
+ wasOn ifTrue:[self showCursor]
+! !
+
+!EditTextView methodsFor:'undo & again'!
+
+addUndo:action
+ ^ undoSupport addUndo:action.
+!
+
+again
+ "repeat the last action (which was a cut or replace).
+ If current selection is not last string, search forward to
+ next occurrence of it before repeating the last operation."
+
+ |s l c sel savedSelectStyle startColForSearch
+ lastStringToReplace lastReplaceIgnoredCase lastReplaceWasMatch|
+
+ lastStringToReplace := lastReplacementInfo lastStringToReplace.
+ lastStringToReplace isNil ifTrue:[
+ ^ false
+ ].
+ lastReplaceIgnoredCase := lastReplacementInfo lastReplaceIgnoredCase.
+ lastReplaceWasMatch := lastReplacementInfo lastReplaceWasMatch.
+
+ self undoableDo:[
+ s := lastStringToReplace asString.
+ "remove final cr"
+ (s endsWith:Character cr) ifTrue:[s := s copyButLast:1].
+ "/ s := s withoutSpaces. "XXX - replacing text with spaces ..."
+
+ "set lastStringToReplace as the next search string
+ and set lastReplaceIgnoredCase as the next search ignored case flag"
+ lastStringFromReplaceForNextSearch := s.
+ lastSearchIgnoredCase := lastReplaceIgnoredCase.
+
+ savedSelectStyle := selectStyle.
+ selectStyle := nil.
+
+ sel := self selection.
+
+ "if we are already there (after a find), ommit search"
+
+ (sel notNil and:[sel asString withoutSeparators = s]) ifTrue:[
+ l := selectionStartLine "cursorLine".
+ c := selectionStartCol "cursorCol".
+ self deleteSelection.
+ lastReplacementInfo lastReplacement notNil ifTrue:[
+ self insertLines:lastReplacementInfo lastReplacement asStringCollection withCR:false.
+ self selectFromLine:l col:c toLine:cursorLine col:(cursorCol - 1).
+ typeOfSelection := #paste
+ ].
+ selectStyle := savedSelectStyle.
+ ^ true
+ ].
+
+ sel isEmptyOrNil ifTrue:[
+ startColForSearch := cursorCol - 1
+ ] ifFalse:[
+ startColForSearch := selectionEndCol ? (cursorCol - 1)
+ ].
+ self
+ searchForwardFor:s
+ ignoreCase: lastReplaceIgnoredCase
+ match: lastReplaceWasMatch
+ startingAtLine:cursorLine col:startColForSearch
+ ifFound:
+ [
+ :line :col |
+
+ |repl|
+
+ self selectFromLine:line col:col
+ toLine:line col:(col + s size - 1).
+ self makeLineVisible:line.
+
+ self deleteSelection.
+ lastReplacementInfo lastReplacement notNil ifTrue:[
+ lastReplacementInfo lastReplacement isString ifFalse:[
+ repl := lastReplacementInfo lastReplacement asString "withoutSpaces"
+ ] ifTrue:[
+ repl := lastReplacementInfo lastReplacement "withoutSpaces".
+ ].
+ self insertLines:repl asStringCollection withCR:false.
+ self selectFromLine:line col:col toLine:cursorLine col:(cursorCol - 1).
+ undoSupport actionInfo:'replace'.
+ ].
+ selectStyle := savedSelectStyle.
+ typeOfSelection := #paste.
+ ^ true
+ ]
+ ifAbsent:
+ [
+ self sensor compressKeyPressEventsWithKey:#Again.
+ self showNotFound.
+ selectStyle := savedSelectStyle.
+ ^ false
+ ].
+ ].
+
+ ^ true.
+
+ "Modified: 9.10.1996 / 16:14:11 / cg"
+!
+
+hasRedoAction
+ ^ undoSupport hasRedoAction.
+!
+
+hasUndoAction
+ ^ undoSupport hasUndoAction.
+!
+
+multipleAgain
+ "repeat the last action (which was a cut or replace) until search fails"
+
+ [self again] whileTrue:[]
+!
+
+nonUndoableDo:aBlock
+ undoSupport nonUndoableDo:aBlock.
+!
+
+redo
+ "undo the last undo"
+
+ undoSupport hasRedoAction ifFalse:[
+ self beep
+ ] ifTrue:[
+ undoSupport redo.
+ ]
+!
+
+undo
+ "undo the last edit operation"
+
+ undoSupport hasUndoAction ifFalse:[
+ self beep
+ ] ifTrue:[
+ undoSupport undo.
+ ]
+!
+
+undoableDo:aBlock
+ self undoableDo:aBlock info:nil.
+
+ "Modified: / 28-07-2007 / 13:20:14 / cg"
+!
+
+undoableDo:aBlock info:aString
+ self checkModificationsAllowed ifFalse:[
+ "/ will trigger an error-dialog there (no need for undo-carekeeping)
+ aBlock value.
+ ] ifTrue:[
+ undoSupport undoableDo:aBlock info:aString.
+ ].
+
+ "Modified: / 28-07-2007 / 13:21:00 / cg"
+! !
+
+!EditTextView::EditAction class methodsFor:'instance creation'!
+
+line1:arg1 col1:arg2 line2:arg3 col2:arg4
+ ^ self new line1:arg1 col1:arg2 line2:arg3 col2:arg4
+!
+
+line1:arg1 col1:arg2 line2:arg3 col2:arg4 info:info
+ ^ (self new line1:arg1 col1:arg2 line2:arg3 col2:arg4) info:info
+!
+
+line:arg1 col:arg2 character:arg3
+ ^ self new line:arg1 col:arg2 character:arg3
+!
+
+line:arg1 col:arg2 character:arg3 info:info
+ ^ (self new line:arg1 col:arg2 character:arg3) info:info
+!
+
+line:arg1 col:arg2 characters:arg3 info:info
+ ^ (self new line:arg1 col:arg2 characters:arg3) info:info
+!
+
+line:arg1 col:arg2 info:arg3
+ ^ self new line:arg1 col:arg2 info:arg3
+!
+
+line:arg1 col:arg2 string:arg3
+ ^ self new line:arg1 col:arg2 string:arg3
+!
+
+line:arg1 col:arg2 string:arg3 info:info
+ ^ (self new line:arg1 col:arg2 string:arg3) info:info
+!
+
+line:arg1 string:arg3 info:info
+ ^ (self new line:arg1 string:arg3) info:info
+!
+
+text:arg info:info
+ ^ (self new text:arg) info:info
+! !
+
+!EditTextView::EditAction methodsFor:'accessing'!
+
+info
+ ^ userFriendlyInfo
+!
+
+info:aString
+ userFriendlyInfo := aString
+! !
+
+!EditTextView::EditAction methodsFor:'combining'!
+
+canCombineWithPreviousPasteStringAction: aPasteStringAction
+ ^ false.
+
+ "Created: / 25-09-2006 / 12:16:25 / cg"
+! !
+
+!EditTextView::EditAction methodsFor:'queries'!
+
+canCombineWithNext:nextAction
+ ^ false
+! !
+
+!EditTextView::DeleteRange methodsFor:'accessing'!
+
+line1:line1Arg col1:col1Arg line2:line2Arg col2:col2Arg
+ "set instance variables (automatically generated)"
+
+ self assert:(line1Arg notNil).
+ self assert:(col1Arg notNil).
+ self assert:(line2Arg notNil).
+ self assert:(col2Arg notNil).
+
+ line1 := line1Arg.
+ col1 := col1Arg.
+ line2 := line2Arg.
+ col2 := col2Arg.
+! !
+
+!EditTextView::DeleteRange methodsFor:'execution'!
+
+executeIn:editor
+ editor unselect.
+ editor
+ deleteFromLine:line1
+ col:col1
+ toLine:line2
+ col:col2.
+ editor cursorLine:line1 col:col1.
+! !
+
+!EditTextView::DeleteCharacters methodsFor:'accessing'!
+
+col1
+ ^ col1
+!
+
+col2
+ ^ col2
+!
+
+line
+ ^ line
+!
+
+line:lineArg col1:col1Arg col2:col2Arg
+ "set instance variables (automatically generated)"
+
+ self assert:(lineArg notNil).
+ self assert:(col1Arg notNil).
+ self assert:(col2Arg notNil).
+
+ line := lineArg.
+ col1 := col1Arg.
+ col2 := col2Arg.
+!
+
+line:lineArg col:colArg info:infoArg
+ self assert:(lineArg notNil).
+ self assert:(colArg notNil).
+
+ line := lineArg.
+ col1 := col2 := colArg.
+ self info:infoArg.
+! !
+
+!EditTextView::DeleteCharacters methodsFor:'combining'!
+
+canCombineWithNext:anotherAction
+ ^ anotherAction perform:#canCombineWithPreviousDeleteCharactersAction: with:self ifNotUnderstood:false
+!
+
+canCombineWithPreviousDeleteCharactersAction:previousDeleteAction
+ "I will combine only if we both are single character deletes,
+ and my col-to-delete is the next after anotherDeleteActions col-to-delete.
+ (i.e. single-character typing)"
+
+ previousDeleteAction line == line ifTrue:[
+ previousDeleteAction col2 == (col1-1) ifTrue:[
+ ^ true
+ ].
+ ].
+
+ ^ false
+!
+
+combineWithNext:nextDeleteAction
+ self assert:(line == nextDeleteAction line).
+ self assert:(col2 == (nextDeleteAction col1 - 1)).
+
+ col2 := nextDeleteAction col2.
+ userFriendlyInfo := 'insert ' , (col2 - col1 + 1) printString
+! !
+
+!EditTextView::DeleteCharacters methodsFor:'execution'!
+
+executeIn:editor
+ editor unselect.
+ editor
+ deleteFromLine:line
+ col:col1
+ toLine:line
+ col:col2.
+ editor cursorLine:line col:col1.
+! !
+
+!EditTextView::EditMode class methodsFor:'constants'!
+
+insertAndSelectMode
+ ^ InsertAndSelectMode
+!
+
+insertMode
+ ^ InsertMode
+!
+
+overwriteMode
+ ^ OverwriteMode
+! !
+
+!EditTextView::EditMode class methodsFor:'queries'!
+
+isInsertAndSelectMode
+ ^ false
+!
+
+isInsertMode
+ ^ false
+!
+
+symbolicName
+ self subclassResponsibility
+! !
+
+!EditTextView::EditMode::InsertAndSelectMode class methodsFor:'info'!
+
+infoPrintString
+ ^ 'IS'
+! !
+
+!EditTextView::EditMode::InsertAndSelectMode class methodsFor:'queries'!
+
+isInsertAndSelectMode
+ ^ true
+!
+
+isInsertMode
+ ^ true
+! !
+
+!EditTextView::EditMode::InsertMode class methodsFor:'info'!
+
+infoPrintString
+ ^ 'I'
+! !
+
+!EditTextView::EditMode::InsertMode class methodsFor:'queries'!
+
+isInsertMode
+ ^ true
+! !
+
+!EditTextView::EditMode::OverwriteMode class methodsFor:'info'!
+
+infoPrintString
+ ^ 'O'
+! !
+
+!EditTextView::LastReplacementInfo methodsFor:'accessing'!
+
+lastReplaceIgnoredCase
+ ^ lastReplaceIgnoredCase ? false
+!
+
+lastReplaceIgnoredCase:something
+ lastReplaceIgnoredCase := something.
+!
+
+lastReplaceWasMatch
+ ^ lastReplaceWasMatch ? false
+!
+
+lastReplaceWasMatch:something
+ lastReplaceWasMatch := something.
+!
+
+lastReplacement
+ ^ lastReplacement
+!
+
+lastReplacement:something
+"/Transcript showCR: 'lastReplacement:', something printString.
+ lastReplacement := something.
+!
+
+lastStringToReplace
+ ^ lastStringToReplace
+!
+
+lastStringToReplace:something
+ lastStringToReplace := something.
+!
+
+previousReplacements
+ ^ previousReplacements ? #()
+!
+
+stillCollectingInput
+ ^ stillCollectingInput
+!
+
+stillCollectingInput:aBoolean
+ stillCollectingInput := aBoolean.
+! !
+
+!EditTextView::LastReplacementInfo methodsFor:'history'!
+
+rememberReplacement
+ "remember the previous replacement (called when a new one appears).
+ Mostly for the benefit of the code completion..."
+
+ |oldString newString|
+
+ oldString := lastStringToReplace.
+ newString := lastReplacement.
+ (oldString notEmptyOrNil and:[newString notEmptyOrNil]) ifTrue:[
+ previousReplacements isNil ifTrue:[
+ previousReplacements := OrderedCollection new.
+ ].
+ previousReplacements := previousReplacements reject:[:entry | entry key = oldString].
+ previousReplacements addFirst:(oldString -> newString).
+ previousReplacements size > 20 ifTrue:[
+ previousReplacements removeLast.
+ ]
+ ].
+! !
+
+!EditTextView::PasteString methodsFor:'accessing'!
+
+col
+ ^ col
+
+ "Created: / 25-09-2006 / 12:19:59 / cg"
+!
+
+col2
+ ^ col + string size - 1
+
+ "Created: / 25-09-2006 / 12:20:18 / cg"
+!
+
+line
+ ^ line
+
+ "Created: / 25-09-2006 / 12:21:08 / cg"
+!
+
+line:lineArg col:colArg string:stringArg
+ self assert:(lineArg notNil).
+ self assert:(colArg notNil).
+ self assert:(stringArg notNil).
+
+ line := lineArg.
+ col := colArg.
+ string := stringArg.
+!
+
+line:lineArg col:colArg string:stringArg selected:selectedArg
+ self assert:(lineArg notNil).
+ self assert:(colArg notNil).
+ self assert:(stringArg notNil).
+
+ line := lineArg.
+ col := colArg.
+ string := stringArg.
+ selected := selectedArg.
+!
+
+string
+ ^ string
+
+ "Created: / 25-09-2006 / 12:25:59 / cg"
+! !
+
+!EditTextView::PasteString methodsFor:'combining'!
+
+canCombineWithNext:anotherAction
+ ^ anotherAction canCombineWithPreviousPasteStringAction:self
+
+ "Created: / 25-09-2006 / 12:15:59 / cg"
+!
+
+canCombineWithPreviousPasteStringAction: previousPasteAction
+ "I will combine only if we both are single character inserts,
+ and my col-to-insert is the next after anotherInsertActions end-col.
+ (i.e. single-character deletes)"
+
+ previousPasteAction line == line ifTrue:[
+ previousPasteAction col == (self col2+1) ifTrue:[
+ ^ true
+ ].
+ ].
+
+ ^ false
+
+ "Modified: / 25-09-2006 / 12:22:21 / cg"
+!
+
+combineWithNext:nextPasteAction
+ |s1 s2|
+
+ self assert:(line == nextPasteAction line).
+ self assert:((col - 1) == (nextPasteAction col2)).
+
+ s1 := nextPasteAction string.
+ s1 isString ifFalse:[s1 := s1 asStringWith:nil].
+ s2 := string.
+ s2 isString ifFalse:[s2 := s2 asStringWith:nil].
+
+ string := s1, s2.
+ col := nextPasteAction col.
+ userFriendlyInfo := 'delete ' , string size printString
+
+ "Created: / 25-09-2006 / 12:24:10 / cg"
+! !
+
+!EditTextView::PasteString methodsFor:'execution'!
+
+executeIn:editor
+ editor cursorLine:line col:col.
+ editor paste:string.
+ selected ~~ true ifTrue:[
+ editor unselect
+ ].
+! !
+
+!EditTextView::ReplaceCharacter methodsFor:'accessing'!
+
+col
+ ^ col
+!
+
+col1
+ ^ col
+!
+
+col2
+ ^ col
+!
+
+line
+ ^ line
+!
+
+line:lineArg col:colArg character:characterArg
+ line := lineArg.
+ col := colArg.
+ character := characterArg.
+! !
+
+!EditTextView::ReplaceCharacter methodsFor:'execution'!
+
+executeIn:editor
+ editor
+ replace:character
+ atLine:line
+ col:col.
+ editor cursorLine:line col:col.
+! !
+
+!EditTextView::ReplaceCharacters methodsFor:'accessing'!
+
+characters
+ ^ characters
+!
+
+col1
+ ^ col1
+!
+
+col2
+ ^ col2
+!
+
+line
+ ^ line
+!
+
+line:lineArg col:colArg character:characterArg
+ line := lineArg.
+ col1 := col2 := colArg.
+ characters := characterArg asString.
+!
+
+line:lineArg col:colArg characters:charactersArg
+ line := lineArg.
+ col1 := colArg.
+ characters := charactersArg asString.
+ col2 := col1 + charactersArg size - 1
+! !
+
+!EditTextView::ReplaceCharacters methodsFor:'combining'!
+
+canCombineWithNext:anotherAction
+ ^ anotherAction perform:#canCombineWithPreviousReplaceCharactersAction: with:self ifNotUnderstood:false
+!
+
+canCombineWithPreviousReplaceCharactersAction:previousReplaceAction
+ "I will combine only if we both are single character deletes,
+ and my col-to-delete is the next after anotherDeleteActions col-to-delete.
+ (i.e. single-character typing)"
+
+ previousReplaceAction line == line ifTrue:[
+ previousReplaceAction col2 == (col1-1) ifTrue:[
+ ^ true
+ ].
+ ].
+
+ ^ false
+!
+
+combineWithNext:nextReplaceAction
+ self assert:(line == nextReplaceAction line).
+ self assert:(self col2 == (nextReplaceAction col1 - 1)).
+
+ col2 := nextReplaceAction col2.
+ userFriendlyInfo := 'replace ' , (col2 - col1 + 1) printString.
+ characters := characters , nextReplaceAction characters.
+! !
+
+!EditTextView::ReplaceCharacters methodsFor:'execution'!
+
+executeIn:editor
+ editor
+ replaceString:characters
+ atLine:line
+ col:col1.
+ editor cursorLine:line col:col1.
+! !
+
+!EditTextView::ReplaceContents methodsFor:'accessing'!
+
+text:something
+ text := something.
+! !
+
+!EditTextView::ReplaceContents methodsFor:'execution'!
+
+executeIn:editor
+ editor contents:text
+! !
+
+!EditTextView::ReplaceLine methodsFor:'accessing'!
+
+line:lineArg string:stringArg
+ line := lineArg.
+ text := stringArg.
+! !
+
+!EditTextView::ReplaceLine methodsFor:'execution'!
+
+executeIn:editor
+ editor list at:line put:text.
+ editor invalidateLine:line
+! !
+
+!EditTextView::ReplaceLines methodsFor:'accessing'!
+
+line:lineArg lines:lineCollectionArg
+ line := lineArg.
+ text := lineCollectionArg.
+
+ "Created: / 09-10-2006 / 10:35:22 / cg"
+! !
+
+!EditTextView::ReplaceLines methodsFor:'execution'!
+
+executeIn:editor
+ |lnr|
+
+ lnr := line.
+ text do:[:eachLine |
+ editor list at:lnr put:eachLine.
+ editor invalidateLine:lnr.
+ lnr := lnr + 1.
+ ].
+
+ "Modified: / 09-10-2006 / 10:39:16 / cg"
+! !
+
+!EditTextView class methodsFor:'documentation'!
+
+version
+ ^ '$Header: /cvs/stx/stx/libwidg/EditTextView.st,v 1.604.2.1 2014-05-08 08:30:56 stefan Exp $'
+!
+
+version_CVS
+ ^ '$Header: /cvs/stx/stx/libwidg/EditTextView.st,v 1.604.2.1 2014-05-08 08:30:56 stefan Exp $'
+! !
+