"====================================================================== | | Smalltalk Tk-based GUI building blocks (text widget). | | $Revision: 1.7.5$ | $Date: 2000/05/28 16:56:52$ | $Author: pb$ | ======================================================================" "====================================================================== | | Copyright 1988-92, 1994-95, 1999, 2000 Free Software Foundation, Inc. | Written by Paolo Bonzini. | | This file is part of the GNU Smalltalk class library. | | The GNU Smalltalk class library is free software; you can redistribute it | and/or modify it under the terms of the GNU Lesser General Public License | as published by the Free Software Foundation; either version 2.1, or (at | your option) any later version. | | The GNU Smalltalk class library is distributed in the hope that it will be | useful, but WITHOUT ANY WARRANTY; without even the implied warranty of | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser | General Public License for more details. | | You should have received a copy of the GNU Lesser General Public License | along with the GNU Smalltalk class library; see the file COPYING.LESSER. | If not, write to the Free Software Foundation, 59 Temple Place - Suite | 330, Boston, MA 02111-1307, USA. | ======================================================================" "-------------------------- Main text widget class ------------------" BViewport subclass: #BText instanceVariableNames: 'callback tagInfo images' classVariableNames: '' poolDictionaries: '' category: 'Graphics-Windows' ! BText comment: ' I represent a text viewer with pretty good formatting options.'! BText "#char, #word, #none" defineAccessor: 'wrap' option: 'wrap' fromStringCode: 'asSymbol'; defineMutator: 'wrap' option: 'wrap' toStringCode: ''; defineColorProtocolWithActivePrefix: 'select' tk: 'select'! !BText class methodsFor: 'accessing'! emacsLike self tclEval: 'return $tk_strictMotif'. ^self tclResult = '1' ! emacsLike: aBoolean self tclEval: 'set tk_strictMotif ', (aBoolean ifTrue: [ '0' ] ifFalse: [ '1' ]). ! ! !BText class methodsFor: 'instance creation'! newReadOnly: parent | ctl | ctl := self new: parent. ctl tclEval: ctl connected, ' configure -state disabled'. ^ctl ! ! !BText methodsFor: 'geometry management'! xChild: child ^0! child: child x: value "Never fail"! child: child xOffset: value ^0! yChild: child ^0! child: child y: value ^0! child: child yOffset: value self shouldNotImplement! widthChild: child "Answer the given child's width in pixels." ^child at: #widthGeom ifAbsentPut: [ child widthAbsolute ]! child: child width: value | width height | width := self at: #widthGeom put: value asInteger. height := self at: #heightGeom ifAbsentPut: [ child heightAbsolute ]. "self tclEval: 'wm geometry %1 =%2x%3' with: child container with: width printString with: height printString"! child: child widthOffset: value self child: child width: (self widthChild: child) + value! heightChild: child "Answer the given child's height." ^child at: #heightGeom ifAbsentPut: [ child heightAbsolute ]! child: child height: value | width height | height := self at: #heightGeom put: value asInteger. width := self at: #widthGeom ifAbsentPut: [ self widthAbsolute ]. "self tclEval: 'wm geometry %1 =%2x%3' with: child container with: width printString with: height printString"! child: child heightOffset: value self child: child height: (self heightChild: child) + value! ! !BText methodsFor: 'accessing'! callback ^callback ! callback: aReceiver message: aString callback := DirectedMessage selector: aString asSymbol arguments: #() receiver: aReceiver ! contents self tclEval: self connected, ' get 1.0 end-1c'. ^self tclResult ! contents: aString self tclEval: '%1 delete 1.0 end %1 insert 1.0 %2 %1 see 1.0' with: self connected with: aString asTkString. ! getSelection | result | self tclEval: ' if { [%1 tag ranges sel] == {} } then { return {} } %1 get sel.first sel.last' with: self connected. result := self tclResult. ^result isEmpty ifTrue: [ nil ] ifFalse: [ result ] ! ! !BText methodsFor: 'position & lines'! numberOfLines | stream | self tclEval: self connected, ' index end-1c'. stream := ReadStream on: self tclResult. ^(stream upTo: $.) asInteger ! charsInLine: number | stream | self tclEval: self connected, ' index ', number printString, ' lineend'. stream := ReadStream on: self tclResult. stream skipTo: $. . ^stream upToEnd asInteger + 1 ! indexAt: point self tclEval: self connected, ' index @%1,%2' with: point x printString with: point y printString. ^self parseResult ! lineAt: number self tclEval: '%1 get %2.0 %2.0 lineend' with: self connected with: number printString. ^self tclResult ! setToEnd self tclEval: ' %1 mark set insert end-1c %1 see end' with: self connected ! selectFrom: first to: last self tclEval: '%1 tag remove sel 1.0 end %1 tag add sel %2' with: self connected with: (self from: first to: last) ! currentPosition self tclEval: self connected, ' index insert'. ^self parseResult ! currentPosition: aPoint self tclEval: ' %1 mark set insert %2.%3 %1 see insert' with: self connected with: aPoint y printString with: (aPoint x - 1) printString. ! currentColumn | stream | self tclEval: self connected, ' index insert'. stream := ReadStream on: self tclResult. stream skipTo: $. . ^stream upToEnd asInteger + 1 ! currentLine | stream | self tclEval: self connected, ' index insert'. stream := ReadStream on: self tclResult. ^(stream upTo: $.) asInteger ! gotoLine: line end: aBoolean | code | code := aBoolean ifTrue: [ '%1 mark set insert "%2.0 -1l lineend"' ] ifFalse: [ '%1 mark set insert %2.0' ]. self tclEval: code with: self connected with: line printString. self tclEval: self connected, ' see insert'. ^1 ! ! !BText methodsFor: 'attributes'! removeAttributes tagInfo isNil ifTrue: [ ^self ]. self removeAttributesInside: '1.0 end'. tagInfo initialize: self. ! removeAttributesFrom: aPoint to: endPoint tagInfo isNil ifTrue: [ ^self ]. self removeAttributesInside: (self from: aPoint to: endPoint) ! setAttributes: attr from: aPoint to: endPoint attr isNil ifTrue: [ ^self ]. tagInfo isNil ifTrue: [ tagInfo := BTextTags new: self ]. self tclEval: 'foreach tag %2 { %1 tag add $tag %3 }' with: self connected with: (attr tags: tagInfo) with: (self from: aPoint to: endPoint). ! insertText: aString attribute: attr attr isNil ifTrue: [ ^self insertText: aString ]. tagInfo isNil ifTrue: [ tagInfo := BTextTags new: self ]. self tclEval: '%1 delete sel.first sel.last %1 insert insert %2 %3 %1 see insert' with: self connected with: aString asTkString with: (attr tags: tagInfo). ! insertAtEnd: aString attribute: attr attr isNil ifTrue: [ ^self insertAtEnd: aString ]. tagInfo isNil ifTrue: [ tagInfo := BTextTags new: self ]. self tclEval: '%1 tag remove sel 1.0 end %1 insert end %2%3' with: self connected with: aString asTkString with: (attr tags: tagInfo). ! ! !BText methodsFor: 'images'! insertImage: anObject | key | key := self registerImage: anObject. self tclEval: '%1 image create insert -align baseline -image %2' with: self connected with: key value. ^key ! insertImageAtEnd: anObject | key | key := self registerImage: anObject. self tclEval: '%1 image create end -align baseline -image %2' with: self connected with: key value. ^key ! insertImage: anObject at: position | key | key := self registerImage: anObject. self tclEval: '%1 image create %2.%3 -align baseline -image %4' with: self connected with: position y printString with: (position x - 1) printString with: key value. ^key ! registerImage: anObject | imageName | anObject class == ValueHolder ifTrue: [ ^anObject ]. self tclEval: 'image create photo -data ', anObject asTkImageString. images isNil ifTrue: [ images := OrderedCollection new ]. imageName := images add: self tclResult. ^ValueHolder value: imageName ! ! !BText methodsFor: 'inserting text'! insertText: aString at: position self tclEval: '%1 delete sel.first sel.last %1 insert %2.%3 %4 %1 see insert' with: self connected with: position y printString with: (position x - 1) printString with: aString asTkString ! insertText: aString self tclEval: 'catch { %1 delete sel.first sel.last } %1 insert insert %2 %1 see insert' with: self connected with: aString asTkString ! insertSelectedText: aString self tclEval: '%1 tag remove sel 1.0 end %1 insert insert %2 { sel } %1 see insert' with: self connected with: aString asTkString ! insertTextSelection: aString self tclEval: 'catch { %1 mark set insert sel.last } %1 tag remove sel 1.0 end %1 insert insert %2 { sel } %1 see insert' with: self connected with: aString asTkString ! nextPut: aCharacter self insertAtEnd: (String with: aCharacter) ! nextPutAll: aString self insertAtEnd: aString ! nl self insertAtEnd: Character nl asString ! insertAtEnd: aString self tclEval: '%1 tag remove sel 1.0 end %1 insert end %2' with: self connected with: aString asTkString ! invokeCallback self callback isNil ifFalse: [ self callback send ] ! refuseTabs self tclEval: ' bind %1 { focus [tk_focusNext %W] break } bind %1 { focus [tk_focusPrev %W] break }' with: self connected ! replaceSelection: aString self tclEval: 'catch { %1 delete sel.first sel.last } %1 insert insert %2 { sel } %1 see insert' with: self connected with: aString asTkString ! searchString: aString | result | self tclEval: self connected, ' search ', aString asTkString, ' 1.0 end'. result := self tclResult. result isEmpty ifTrue: [ ^0 ]. self tclEval: ' %1 mark set insert %2 %1 see insert' with: self connected with: result. "Sending asInteger removes the column" ^result asInteger ! space self insertAtEnd: ' ' ! ! !BText methodsFor: 'private'! addChild: child self tclEval: '%1 window create end -window %2' with: self connected with: child container. ^self basicAddChild: child ! create self create: ' -bg white -wrap word -state normal -highlightthickness 0 -takefocus 1'; horizontal: true; vertical: true. "This hack gets the callback upon insert or delete; see Tk FAQ by Jeffrey Hobbs (jeff.hobbs@acm.org)" self tclEval: ' rename %1 .%1 proc %1 args { if [regexp {^(ins|del).*} [lindex $args 0]] { callback %2 invokeCallback } uplevel .%1 $args }' with: self connected with: self asOop printString. ! defineTag: name as: options self tclEval: '%1 tag configure %2 %3 %1 tag raise sel %2' with: self connected with: name with: options ! destroyed super destroyed. images isNil ifTrue: [ ^self ]. images do: [ :name | self tclEval: 'image delete ', name. ]. images := nil ! from: aPoint to: endPoint ^'%1.%2 %3.%4' bindWith: aPoint y printString with: (aPoint x - 1) printString with: endPoint y printString with: (endPoint x - 1) printString ! parseResult | stream y | stream := ReadStream on: self tclResult. y := (stream upTo: $.) asInteger. ^stream upToEnd asInteger + 1 @ y ! removeAttributesInside: range self tclEval: 'foreach tag [ %1 tag names ] { if { $tag != "sel" } then { %1 tag remove $tag %2 } }' with: self connected with: range. ! tag: name bind: event to: aSymbol of: anObject parameters: params self bind: event to: aSymbol of: anObject parameters: params prefix: ('%1 tag bind %2' bindWith: self connected with: name) ! widgetType ^'text ' ! ! "--------------------------- BTextBindings --------------------------" BEventTarget subclass: #BTextBindings instanceVariableNames: 'list tagName' classVariableNames: '' poolDictionaries: '' category: 'Graphics-Windows'! !BTextBindings class methodsFor: 'instance creation'! new ^self basicNew initialize ! ! !BTextBindings methodsFor: 'BTextTags protocol'! defineTagFor: aBText list do: [ :each | each sendTo: aBText ] ! tagName ^tagName ! ! !BTextBindings methodsFor: 'private'! initialize tagName := 'ev', (Time millisecondClockValue printString: 36). list := OrderedCollection new. ! primBind: event to: aSymbol of: anObject parameters: params | args | (args := Array new: 5) at: 1 put: tagName; at: 2 put: event; at: 3 put: aSymbol; at: 4 put: anObject; at: 5 put: params. list add: (Message selector: #tag:bind:to:of:parameters: arguments: args) ! ! "-------------------------- BTextAttributes -------------------------" Object subclass: #BTextAttributes instanceVariableNames: 'bgColor fgColor font styles events' classVariableNames: '' poolDictionaries: '' category: 'Graphics-Windows'! BTextAttributes comment: ' I help you creating wonderful, colorful BTexts.'! !BTextAttributes class methodsFor: 'instance-creation shortcuts'! backgroundColor: color ^self new backgroundColor: color ! foregroundColor: color ^self new foregroundColor: color ! events: aBTextBindings ^self new events: aBTextBindings ! font: font ^self new font: font ! strikeout ^self new strikeout ! underline ^self new underline ! center ^self new center ! red ^self new foregroundColor: 'red' ! blue ^self new foregroundColor: 'blue' ! darkGreen ^self new foregroundColor: 'PureDarkGreen' ! green ^self new foregroundColor: 'green' ! darkCyan ^self new foregroundColor: 'PureDarkCyan' ! cyan ^self new foregroundColor: 'cyan' ! darkMagenta ^self new foregroundColor: 'PureDarkMagenta' ! magenta ^self new foregroundColor: 'magenta' ! yellow ^self new foregroundColor: 'yellow' ! white ^self new foregroundColor: 'white' ! black ^self new foregroundColor: 'black' ! ! !BTextAttributes methodsFor: 'setting attributes'! backgroundColor ^bgColor ! backgroundColor: color bgColor := color ! foregroundColor ^bgColor ! foregroundColor: color fgColor := color ! events ^events ! events: aBTextBindings events := aBTextBindings ! font ^font ! font: fontName font := fontName ! isStruckout ^self hasStyle: #STYLEstrikeout ! strikeout self style: #STYLEstrikeout ! isUnderlined ^self hasStyle: #STYLEunderline ! underline self style: #STYLEunderline ! isCentered self hasStyle: #STYLEcenter ! center self style: #STYLEcenter ! ! !BTextAttributes methodsFor: 'colors'! red self foregroundColor: 'red' ! blue self foregroundColor: 'blue' ! darkGreen self foregroundColor: 'PureDarkGreen' ! green self foregroundColor: 'green' ! darkCyan self foregroundColor: 'PureDarkCyan' ! cyan self foregroundColor: 'cyan' ! darkMagenta self foregroundColor: 'PureDarkMagenta' ! magenta self foregroundColor: 'magenta' ! yellow self foregroundColor: 'yellow' ! white self foregroundColor: 'white' ! black self foregroundColor: 'black' ! ! !BTextAttributes methodsFor: 'private'! hasStyle: aSymbol ^styles notNil and: [ styles includes: aSymbol ] ! style: aSymbol styles isNil ifTrue: [ styles := Set new ]. styles add: aSymbol ! tags: aBTextTags | s | s := WriteStream on: (String new: 20). s nextPutAll: ' {'. fgColor isNil ifFalse: [ s nextPutAll: (aBTextTags fgColor: fgColor) ]. bgColor isNil ifFalse: [ s nextPutAll: (aBTextTags bgColor: bgColor) ]. font isNil ifFalse: [ s nextPutAll: (aBTextTags font: font) ]. events isNil ifFalse: [ s nextPutAll: (aBTextTags events: events) ]. styles isNil ifFalse: [ styles do: [ :each | s nextPut: $ ; nextPutAll: each ] ]. s nextPut: $}. ^s contents ! ! "-------------------------- BTextTags - private ---------------------" Object subclass: #BTextTags instanceVariableNames: 'client tags' classVariableNames: '' poolDictionaries: '' category: 'Graphics-Windows'! BTextTags comment: ' I am a private class. I sit between a BText and BTextAttributes, helping the latter in telling the former which attributes to use.'! !BTextTags class methodsFor: 'instance creation'! new self shouldNotImplement ! new: client ^super new initialize: client ! ! !BTextTags methodsFor: 'BTextAttributes protocol'! bgColor: color ^' b_', (self color: color) ! fgColor: color ^' f_', (self color: color) ! events: aBTextBindings | tagName | tagName := aBTextBindings tagName. (tags includes: tagName) ifFalse: [ tags add: tagName. aBTextBindings defineTagFor: client. ]. ^' ', tagName ! font: font | tagName | tagName := WriteStream on: (String new: 20). font substrings do: [ :each | tagName nextPutAll: each; nextPut: $_ ]. tagName := tagName contents. (tags includes: tagName) ifFalse: [ tags add: tagName. client defineTag: tagName as: ' -font {', font, '}' ]. ^' ', tagName ! ! !BTextTags methodsFor: 'private'! color: color | tagName | tagName := (color at: 1) = $# ifTrue: [ color copy at: 1 put: $_; yourself ] ifFalse: [ color asLowercase ]. (tags includes: tagName) ifFalse: [ tags add: tagName. client defineTag: 'f_', tagName as: ' -foreground ', color. client defineTag: 'b_', tagName as: ' -background ', color ]. ^tagName ! initialize: clientBText client := clientBText. tags := Set new. client defineTag: 'STYLEstrikeout' as: ' -overstrike 1'. client defineTag: 'STYLEunderline' as: ' -underline 1'. client defineTag: 'STYLEcenter' as: ' -justify center'. ! !