1"====================================================================== 2| 3| Smalltalk Tk-based GUI building blocks (text widget). 4| 5| 6 ======================================================================" 7 8"====================================================================== 9| 10| Copyright 1999, 2000, 2001, 2002 Free Software Foundation, Inc. 11| Written by Paolo Bonzini and Robert Collins. 12| 13| This file is part of the GNU Smalltalk class library. 14| 15| The GNU Smalltalk class library is free software; you can redistribute it 16| and/or modify it under the terms of the GNU Lesser General Public License 17| as published by the Free Software Foundation; either version 2.1, or (at 18| your option) any later version. 19| 20| The GNU Smalltalk class library is distributed in the hope that it will be 21| useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 22| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 23| General Public License for more details. 24| 25| You should have received a copy of the GNU Lesser General Public License 26| along with the GNU Smalltalk class library; see the file COPYING.LESSER. 27| If not, write to the Free Software Foundation, 59 Temple Place - Suite 28| 330, Boston, MA 02110-1301, USA. 29| 30 ======================================================================" 31 32 33 34BViewport subclass: BText [ 35 | callback tagInfo images gtkbuffer | 36 37 <comment: ' 38I represent a text viewer with pretty good formatting options.'> 39 <category: 'Graphics-Windows'> 40 41 BText class >> emacsLike [ 42 "Answer whether we are using Emacs or Motif key bindings." 43 44 <category: 'accessing'> 45 'FIXME: emacsLike should die?' printNl. 46 ^false 47 "self tclEval: 'return $tk_strictMotif'. 48 ^self tclResult = '0'" 49 ] 50 51 BText class >> emacsLike: aBoolean [ 52 "Set whether we are using Emacs or Motif key bindings." 53 54 <category: 'accessing'> 55 'FIXME: emacsLike should die?' printNl 56 "self tclEval: 57 'set tk_strictMotif ', (aBoolean ifTrue: [ '0' ] ifFalse: [ '1' ])." 58 ] 59 60 BText class >> newReadOnly: parent [ 61 "Answer a new read-only text widget (read-only is achieved simply 62 by setting its state to be disabled)" 63 64 <category: 'instance creation'> 65 | ctl | 66 ctl := self new: parent. 67 ctl tclEval: ctl connected , ' configure -state disabled'. 68 ^ctl 69 ] 70 71 backgroundColor [ 72 "Answer the value of the backgroundColor option for the widget. 73 74 Specifies the normal background color to use when displaying the widget." 75 76 <category: 'accessing'> 77 self properties at: #background ifPresent: [:value | ^value]. 78 self 79 tclEval: '%1 cget -background' 80 with: self connected 81 with: self container. 82 ^self properties at: #background put: self tclResult 83 ] 84 85 backgroundColor: value [ 86 "Set the value of the backgroundColor option for the widget. 87 88 Specifies the normal background color to use when displaying the widget." 89 90 <category: 'accessing'> 91 self 92 tclEval: '%1 configure -background %3' 93 with: self connected 94 with: self container 95 with: value asTkString. 96 self properties at: #background put: value 97 ] 98 99 callback [ 100 "Answer a DirectedMessage that is sent when the receiver is modified, 101 or nil if none has been set up." 102 103 <category: 'accessing'> 104 ^callback 105 ] 106 107 callback: aReceiver message: aSymbol [ 108 "Set up so that aReceiver is sent the aSymbol message (the name of 109 a zero- or one-argument selector) when the receiver is modified. 110 If the method accepts an argument, the receiver is passed." 111 112 <category: 'accessing'> 113 | arguments selector numArgs | 114 selector := aSymbol asSymbol. 115 numArgs := selector numArgs. 116 arguments := #(). 117 numArgs = 1 ifTrue: [arguments := Array with: self]. 118 callback := DirectedMessage 119 selector: selector 120 arguments: arguments 121 receiver: aReceiver 122 ] 123 124 contents [ 125 "Return the contents of the widget" 126 127 <category: 'accessing'> 128 | bounds | 129 bounds := self gtkbuffer getBounds. 130 ^(bounds at: 1) getVisibleText: (bounds at: 2) 131 ] 132 133 contents: aString [ 134 "Set the contents of the widget" 135 136 <category: 'accessing'> 137 self gtkbuffer setText: aString 138 ] 139 140 font [ 141 "Answer the value of the font option for the widget. 142 143 Specifies the font to use when drawing text inside the widget. The font 144 can be given as either an X font name or a Blox font description string. 145 146 X font names are given as many fields, each led by a minus, and each of 147 which can be replaced by an * to indicate a default value is ok: 148 foundry, family, weight, slant, setwidth, addstyle, pixel size, point size 149 (the same as pixel size for historical reasons), horizontal resolution, 150 vertical resolution, spacing, width, charset and character encoding. 151 152 Blox font description strings have three fields, which must be separated by 153 a space and of which only the first is mandatory: the font family, the font 154 size in points (or in pixels if a negative value is supplied), and a number 155 of styles separated by a space (valid styles are normal, bold, italic, 156 underline and overstrike). Examples of valid fonts are ``Helvetica 10 Bold'', 157 ``Times -14'', ``Futura Bold Underline''. You must enclose the font family 158 in braces if it is made of two or more words." 159 160 <category: 'accessing'> 161 self properties at: #font ifPresent: [:value | ^value]. 162 self 163 tclEval: '%1 cget -font' 164 with: self connected 165 with: self container. 166 ^self properties at: #font put: self tclResult 167 ] 168 169 font: value [ 170 "Set the value of the font option for the widget. 171 172 Specifies the font to use when drawing text inside the widget. The font 173 can be given as either an X font name or a Blox font description string. 174 175 X font names are given as many fields, each led by a minus, and each of 176 which can be replaced by an * to indicate a default value is ok: 177 foundry, family, weight, slant, setwidth, addstyle, pixel size, point size 178 (the same as pixel size for historical reasons), horizontal resolution, 179 vertical resolution, spacing, width, charset and character encoding. 180 181 Blox font description strings have three fields, which must be separated by 182 a space and of which only the first is mandatory: the font family, the font 183 size in points (or in pixels if a negative value is supplied), and a number 184 of styles separated by a space (valid styles are normal, bold, italic, 185 underline and overstrike). Examples of valid fonts are ``Helvetica 10 Bold'', 186 ``Times -14'', ``Futura Bold Underline''. You must enclose the font family 187 in braces if it is made of two or more words." 188 189 "Change default font throughout the widget" 190 191 <category: 'accessing'> 192 self connected modifyFont: (GTK.PangoFontDescription fromString: value). 193 self properties at: #font put: value 194 ] 195 196 foregroundColor [ 197 "Answer the value of the foregroundColor option for the widget. 198 199 Specifies the normal foreground color to use when displaying the widget." 200 201 <category: 'accessing'> 202 self properties at: #foreground ifPresent: [:value | ^value]. 203 self 204 tclEval: '%1 cget -foreground' 205 with: self connected 206 with: self container. 207 ^self properties at: #foreground put: self tclResult 208 ] 209 210 foregroundColor: value [ 211 "Set the value of the foregroundColor option for the widget. 212 213 Specifies the normal foreground color to use when displaying the widget." 214 215 <category: 'accessing'> 216 self 217 tclEval: '%1 configure -foreground %3' 218 with: self connected 219 with: self container 220 with: value asTkString. 221 self properties at: #foreground put: value 222 ] 223 224 getSelection [ 225 "Answer an empty string if the widget has no selection, else answer 226 the currently selected text" 227 228 <category: 'accessing'> 229 | bounds | 230 bounds := self gtkbuffer getSelectionBounds. 231 ^(bounds at: 1) getVisibleText: (bounds at: 2) 232 ] 233 234 selectBackground [ 235 "Answer the value of the selectBackground option for the widget. 236 237 Specifies the background color to use when displaying selected parts 238 of the widget." 239 240 <category: 'accessing'> 241 self properties at: #selectbackground ifPresent: [:value | ^value]. 242 self 243 tclEval: '%1 cget -selectbackground' 244 with: self connected 245 with: self container. 246 ^self properties at: #selectbackground put: self tclResult 247 ] 248 249 selectBackground: value [ 250 "Set the value of the selectBackground option for the widget. 251 252 Specifies the background color to use when displaying selected parts 253 of the widget." 254 255 <category: 'accessing'> 256 self 257 tclEval: '%1 configure -selectbackground %3' 258 with: self connected 259 with: self container 260 with: value asTkString. 261 self properties at: #selectbackground put: value 262 ] 263 264 selectForeground [ 265 "Answer the value of the selectForeground option for the widget. 266 267 Specifies the foreground color to use when displaying selected parts 268 of the widget." 269 270 <category: 'accessing'> 271 self properties at: #selectforeground ifPresent: [:value | ^value]. 272 self 273 tclEval: '%1 cget -selectforeground' 274 with: self connected 275 with: self container. 276 ^self properties at: #selectforeground put: self tclResult 277 ] 278 279 selectForeground: value [ 280 "Set the value of the selectForeground option for the widget. 281 282 Specifies the foreground color to use when displaying selected parts 283 of the widget." 284 285 <category: 'accessing'> 286 self 287 tclEval: '%1 configure -selectforeground %3' 288 with: self connected 289 with: self container 290 with: value asTkString. 291 self properties at: #selectforeground put: value 292 ] 293 294 wrap [ 295 "Answer the value of the wrap option for the widget. 296 297 Specifies how to handle lines in the text that are too long to be displayed 298 in a single line of the text's window. The value must be #none or #char or 299 #word. A wrap mode of none means that each line of text appears as exactly 300 one line on the screen; extra characters that do not fit on the screen are 301 not displayed. In the other modes each line of text will be broken up into 302 several screen lines if necessary to keep all the characters visible. In 303 char mode a screen line break may occur after any character; in word mode a 304 line break will only be made at word boundaries." 305 306 <category: 'accessing'> 307 self properties at: #wrap ifPresent: [:value | ^value]. 308 self 309 tclEval: '%1 cget -wrap' 310 with: self connected 311 with: self container. 312 ^self properties at: #wrap put: self tclResult asSymbol 313 ] 314 315 wrap: value [ 316 "Set the value of the wrap option for the widget. 317 318 Specifies how to handle lines in the text that are too long to be displayed 319 in a single line of the text's window. The value must be #none or #char or 320 #word. A wrap mode of none means that each line of text appears as exactly 321 one line on the screen; extra characters that do not fit on the screen are 322 not displayed. In the other modes each line of text will be broken up into 323 several screen lines if necessary to keep all the characters visible. In 324 char mode a screen line break may occur after any character; in word mode a 325 line break will only be made at word boundaries." 326 327 <category: 'accessing'> 328 self 329 tclEval: '%1 configure -wrap %3' 330 with: self connected 331 with: self container 332 with: value asTkString. 333 self properties at: #wrap put: value 334 ] 335 336 insertAtEnd: aString attribute: attr [ 337 "Clear the selection and append aString at the end of the 338 widget. Use the given attributes to format the text." 339 340 <category: 'attributes'> 341 | start tmpMark end | 342 attr isNil ifTrue: [^self insertAtEnd: aString]. 343 end := self gtkbuffer getEndIter. 344 tmpMark := self gtkbuffer 345 createMark: 'temporary' 346 where: end 347 leftGravity: true. 348 self gtkbuffer beginUserAction. 349 self gtkbuffer insert: end text: aString. 350 start := self gtkbuffer getIterAtMark: tmpMark. 351 end := self gtkbuffer getEndIter. 352 self gtkbuffer placeCursor: end. 353 self 354 setAttributes: attr 355 start: start 356 end: end. 357 self gtkbuffer endUserAction 358 ] 359 360 insertText: aString attribute: attr [ 361 "Insert aString in the widget at the current insertion point, 362 replacing the currently selected text (if any). Use the 363 given attributes to format the text." 364 365 <category: 'attributes'> 366 | bounds start end tmpMark | 367 attr isNil ifTrue: [^self insertText: aString]. 368 369 "We need a temporary mark to save the beginning of the selection." 370 bounds := self gtkbuffer getSelectionBounds. 371 tmpMark := self gtkbuffer 372 createMark: 'temporary' 373 where: (bounds at: 1) 374 leftGravity: true. 375 (self gtkbuffer) 376 beginUserAction; 377 deleteSelection: false defaultEditable: true; 378 insertAtCursor: aString. 379 start := self gtkbuffer getIterAtMark: tmpMark. 380 end := self gtkbuffer getIterAtMark: self gtkbuffer getInsert. 381 self 382 setAttributes: attr 383 start: start 384 end: end. 385 self gtkbuffer endUserAction 386 ] 387 388 removeAttributes [ 389 "Remove any kind of formatting from the text in the widget" 390 391 <category: 'attributes'> 392 tagInfo isNil ifTrue: [^self]. 393 self removeAttributesInside: 394 {self gtkbuffer getStartIter. 395 self gtkbuffer getEndIter} 396 ] 397 398 removeAttributesFrom: aPoint to: endPoint [ 399 "Remove any kind of formatting from the text in the widget 400 between the given endpoints. The two endpoints are Point 401 objects in which both coordinates are 1-based: the first 402 line is line 1, and the first character in the first line 403 is character 1." 404 405 <category: 'attributes'> 406 tagInfo isNil ifTrue: [^self]. 407 self removeAttributesInside: (self from: aPoint to: endPoint) 408 ] 409 410 setAttributes: attr from: aPoint to: endPoint [ 411 "Add the formatting given by attr to the text in the widget 412 between the given endpoints. The two endpoints are Point 413 objects in which both coordinates are 1-based: the first 414 line is line 1, and the first character in the first line 415 is character 1." 416 417 <category: 'attributes'> 418 | range tag tags tagtable | 419 attr isNil ifTrue: [^self]. 420 range := self from: aPoint to: endPoint. 421 self 422 setAttributes: attr 423 start: (range at: 1) 424 end: (range at: 2) 425 ] 426 427 child: child height: value [ 428 "Set the height of the given child to be `value' pixels." 429 430 <category: 'geometry management'> 431 | width height | 432 height := self at: #heightGeom put: value asInteger. 433 width := self at: #widthGeom ifAbsentPut: [self widthAbsolute] 434 "self 435 tclEval: 'wm geometry %1 =%2x%3' 436 with: child container 437 with: width printString 438 with: height printString" 439 ] 440 441 child: child heightOffset: value [ 442 "Adjust the height of the given child to be given by `value' 443 more pixels." 444 445 <category: 'geometry management'> 446 self child: child height: (self heightChild: child) + value 447 ] 448 449 child: child width: value [ 450 "Set the width of the given child to be `value' pixels." 451 452 <category: 'geometry management'> 453 | width height | 454 width := self at: #widthGeom put: value asInteger. 455 height := self at: #heightGeom ifAbsentPut: [child heightAbsolute] 456 "self 457 tclEval: 'wm geometry %1 =%2x%3' 458 with: child container 459 with: width printString 460 with: height printString" 461 ] 462 463 child: child widthOffset: value [ 464 "Adjust the width of the given child to be given by `value' 465 more pixels." 466 467 <category: 'geometry management'> 468 self child: child width: (self widthChild: child) + value 469 ] 470 471 child: child x: value [ 472 "Never fail and do nothing, the children stay where 473 the text ended at the time each child was added in 474 the widget" 475 476 <category: 'geometry management'> 477 478 ] 479 480 child: child xOffset: value [ 481 <category: 'geometry management'> 482 self shouldNotImplement 483 ] 484 485 child: child y: value [ 486 "Never fail and do nothing, the children stay where 487 the text ended at the time each child was added in 488 the widget" 489 490 <category: 'geometry management'> 491 492 ] 493 494 child: child yOffset: value [ 495 <category: 'geometry management'> 496 self shouldNotImplement 497 ] 498 499 heightChild: child [ 500 "Answer the given child's height in pixels." 501 502 <category: 'geometry management'> 503 ^child at: #heightGeom ifAbsentPut: [child heightAbsolute] 504 ] 505 506 widthChild: child [ 507 "Answer the given child's width in pixels." 508 509 <category: 'geometry management'> 510 ^child at: #widthGeom ifAbsentPut: [child widthAbsolute] 511 ] 512 513 xChild: child [ 514 "Answer the given child's top-left border's x coordinate. 515 We always answer 0 since the children actually move when 516 the text widget scrolls" 517 518 <category: 'geometry management'> 519 ^0 520 ] 521 522 yChild: child [ 523 "Answer the given child's top-left border's y coordinate. 524 We always answer 0 since the children actually move when 525 the text widget scrolls" 526 527 <category: 'geometry management'> 528 ^0 529 ] 530 531 insertImage: anObject [ 532 "Insert an image where the insertion point currently lies in the widget. 533 anObject can be a String containing image data (either Base-64 encoded 534 GIF data, XPM data, or PPM data), or the result or registering an image 535 with #registerImage:" 536 537 <category: 'images'> 538 | key | 539 key := self registerImage: anObject. 540 self 541 tclEval: '%1 image create insert -align baseline -image %2' 542 with: self connected 543 with: key value. 544 ^key 545 ] 546 547 insertImage: anObject at: position [ 548 "Insert an image at the given position in the widget. The 549 position is a Point object in which both coordinates are 1-based: 550 the first line is line 1, and the first character in the first 551 line is character 1. 552 553 anObject can be a String containing image data (either Base-64 encoded 554 GIF data, XPM data, or PPM data), or the result or registering an image 555 with #registerImage:" 556 557 <category: 'images'> 558 | key | 559 key := self registerImage: anObject. 560 self 561 tclEval: '%1 image create %2.%3 -align baseline -image %4' 562 with: self connected 563 with: position y printString 564 with: (position x - 1) printString 565 with: key value. 566 ^key 567 ] 568 569 insertImageAtEnd: anObject [ 570 "Insert an image at the end of the widgets text. 571 anObject can be a String containing image data (either Base-64 encoded 572 GIF data, XPM data, or PPM data), or the result or registering an image 573 with #registerImage:" 574 575 <category: 'images'> 576 | key | 577 key := self registerImage: anObject. 578 self 579 tclEval: '%1 image create end -align baseline -image %2' 580 with: self connected 581 with: key value. 582 ^key 583 ] 584 585 registerImage: anObject [ 586 "Register an image (whose data is in anObject, a String including 587 Base-64 encoded GIF data, XPM data, or PPM data) to be used 588 in the widget. If the same image must be used a lot of times, 589 it is better to register it once and then pass the result of 590 #registerImage: to the image insertion methods. 591 592 Registered image are private within each BText widget. Registering 593 an image with a widget and using it with another could give 594 unpredictable results." 595 596 <category: 'images'> 597 | imageName | 598 anObject class == ValueHolder ifTrue: [^anObject]. 599 self tclEval: 'image create photo -data ' , anObject asTkImageString. 600 images isNil ifTrue: [images := OrderedCollection new]. 601 imageName := images add: self tclResult. 602 ^ValueHolder value: imageName 603 ] 604 605 insertAtEnd: aString [ 606 "Clear the selection and append aString at the end of the 607 widget." 608 609 <category: 'inserting text'> 610 (self gtkbuffer) 611 insert: self gtkbuffer getEndIter text: aString; 612 placeCursor: self gtkbuffer getEndIter 613 ] 614 615 insertText: aString [ 616 "Insert aString in the widget at the current insertion point, 617 replacing the currently selected text (if any)." 618 619 <category: 'inserting text'> 620 (self gtkbuffer) 621 beginUserAction; 622 deleteSelection: false defaultEditable: true; 623 insertAtCursor: aString; 624 endUserAction 625 ] 626 627 insertSelectedText: aString [ 628 "Insert aString in the widget at the current insertion point, 629 leaving the currently selected text (if any) in place, and 630 selecting the text." 631 632 <category: 'inserting text'> 633 | bounds selBound tmpMark | 634 selBound := self gtkbuffer getSelectionBound. 635 bounds := self gtkbuffer getSelectionBounds. 636 637 "We need a temporary mark to keep the beginning of the selection 638 where it is." 639 tmpMark := self gtkbuffer 640 createMark: 'temporary' 641 where: (bounds at: 1) 642 leftGravity: true. 643 (self gtkbuffer) 644 beginUserAction; 645 placeCursor: (bounds at: 2); 646 insertAtCursor: aString; 647 moveMark: selBound where: (self gtkbuffer getIterAtMark: tmpMark); 648 endUserAction; 649 deleteMark: tmpMark 650 ] 651 652 insertText: aString at: position [ 653 "Insert aString in the widget at the given position, 654 replacing the currently selected text (if any). The 655 position is a Point object in which both coordinates are 1-based: 656 the first line is line 1, and the first character in the first 657 line is character 1." 658 659 <category: 'inserting text'> 660 self 661 tclEval: '%1 delete sel.first sel.last 662 %1 insert %2.%3 %4 663 %1 see insert' 664 with: self connected 665 with: position y printString 666 with: (position x - 1) printString 667 with: aString asTkString 668 ] 669 670 insertTextSelection: aString [ 671 "Insert aString in the widget after the current selection, 672 leaving the currently selected text (if any) intact." 673 674 <category: 'inserting text'> 675 | bounds selBound tmpMark | 676 selBound := self gtkbuffer getSelectionBound. 677 bounds := self gtkbuffer getSelectionBounds. 678 679 "We need a temporary mark to put the beginning of the selection 680 where the selection used to end." 681 tmpMark := self gtkbuffer 682 createMark: 'temporary' 683 where: (bounds at: 2) 684 leftGravity: true. 685 (self gtkbuffer) 686 beginUserAction; 687 placeCursor: (bounds at: 2); 688 insertAtCursor: aString; 689 moveMark: selBound where: (self gtkbuffer getIterAtMark: tmpMark); 690 endUserAction; 691 deleteMark: tmpMark 692 ] 693 694 invokeCallback [ 695 "Generate a synthetic callback." 696 697 <category: 'inserting text'> 698 self callback isNil ifFalse: [self callback send] 699 ] 700 701 nextPut: aCharacter [ 702 "Clear the selection and append aCharacter at the end of the 703 widget." 704 705 <category: 'inserting text'> 706 self insertAtEnd: (String with: aCharacter) 707 ] 708 709 nextPutAll: aString [ 710 "Clear the selection and append aString at the end of the 711 widget." 712 713 <category: 'inserting text'> 714 self insertAtEnd: aString 715 ] 716 717 nl [ 718 "Clear the selection and append a linefeed character at the 719 end of the widget." 720 721 <category: 'inserting text'> 722 self insertAtEnd: Character nl asString 723 ] 724 725 refuseTabs [ 726 "Arrange so that Tab characters, instead of being inserted 727 in the widget, traverse the widgets in the parent window." 728 729 <category: 'inserting text'> 730 self 731 tclEval: ' 732 bind %1 <Tab> { 733 focus [tk_focusNext %W] 734 break 735 } 736 bind %1 <Shift-Tab> { 737 focus [tk_focusPrev %W] 738 break 739 }' 740 with: self connected 741 ] 742 743 replaceSelection: aString [ 744 "Insert aString in the widget at the current insertion point, 745 replacing the currently selected text (if any), and leaving 746 the text selected." 747 748 <category: 'inserting text'> 749 | bounds | 750 bounds := self gtkbuffer getSelectionBounds. 751 self gtkbuffer delete: (bounds at: 1) end: (bounds at: 2). 752 self gtkbuffer insertAtCursor: aString 753 ] 754 755 searchString: aString [ 756 "Search aString in the widget. If it is not found, 757 answer zero, else answer the 1-based line number 758 and move the insertion point to the place where 759 the string was found." 760 761 <category: 'inserting text'> 762 | result | 763 self 764 tclEval: self connected , ' search ' , aString asTkString , ' 1.0 end'. 765 result := self tclResult. 766 result isEmpty ifTrue: [^0]. 767 self 768 tclEval: ' 769 %1 mark set insert %2 770 %1 see insert' 771 with: self connected 772 with: result. 773 774 "Sending asInteger removes the column" 775 ^result asInteger 776 ] 777 778 space [ 779 "Clear the selection and append a space at the end of the 780 widget." 781 782 <category: 'inserting text'> 783 self insertAtEnd: ' ' 784 ] 785 786 charsInLine: number [ 787 "Answer how many characters are there in the number-th line" 788 789 <category: 'position & lines'> 790 | iter | 791 iter := self gtkbuffer getIterAtLine: number. 792 iter forwardToLineEnd. 793 ^1 + iter getLineOffset 794 ] 795 796 currentColumn [ 797 "Answer the 1-based column number where the insertion point 798 currently lies." 799 800 <category: 'position & lines'> 801 | mark iter | 802 mark := self gtkbuffer getInsert. 803 iter := self gtkbuffer getIterAtMark: mark. 804 ^1 + iter getLineOffset 805 ] 806 807 currentLine [ 808 "Answer the 1-based line number where the insertion point 809 currently lies." 810 811 <category: 'position & lines'> 812 | mark iter | 813 mark := self gtkbuffer getInsert. 814 iter := self gtkbuffer getIterAtMark: mark. 815 ^1 + iter getLine 816 ] 817 818 currentPosition [ 819 "Answer a Point representing where the insertion point 820 currently lies. Both coordinates in the answer are 1-based: 821 the first line is line 1, and the first character in the first 822 line is character 1." 823 824 <category: 'position & lines'> 825 | mark iter | 826 mark := self gtkbuffer getInsert. 827 iter := self gtkbuffer getIterAtMark: mark. 828 ^(1 + iter getLine) @ (1 + iter getLineOffset) 829 ] 830 831 currentPosition: aPoint [ 832 "Move the insertion point to the position given by aPoint. 833 Both coordinates in aPoint are interpreted as 1-based: 834 the first line is line 1, and the first character in the first 835 line is character 1." 836 837 <category: 'position & lines'> 838 | iter | 839 iter := self gtkbuffer getIterAtLineOffset: aPoint y - 1 840 charOffset: aPoint x - 1. 841 self gtkbuffer placeCursor: iter 842 ] 843 844 gotoLine: line end: aBoolean [ 845 "If aBoolean is true, move the insertion point to the last 846 character of the line-th line (1 being the first line 847 in the widget); if aBoolean is false, move it to the start 848 of the line-th line." 849 850 <category: 'position & lines'> 851 | iter | 852 iter := self gtkbuffer getIterAtLine: line - 1. 853 aBoolean ifTrue: [iter forwardToLineEnd]. 854 self gtkbuffer placeCursor: iter 855 ] 856 857 indexAt: point [ 858 "Answer the position of the character that covers the 859 pixel whose coordinates within the text's window are 860 given by the supplied Point object." 861 862 <category: 'position & lines'> 863 self 864 tclEval: self connected , ' index @%1,%2' 865 with: point x printString 866 with: point y printString. 867 ^self parseResult 868 ] 869 870 lineAt: number [ 871 "Answer the number-th line of text in the widget" 872 873 <category: 'position & lines'> 874 | start end | 875 start := self gtkbuffer getIterAtLine: number - 1. 876 end := self gtkbuffer getIterAtLine: number - 1. 877 end forwardToLineEnd. 878 ^start getVisibleText: end 879 ] 880 881 numberOfLines [ 882 "Answer the number of lines in the widget" 883 884 <category: 'position & lines'> 885 ^self gtkbuffer getLineCount 886 ] 887 888 selectFrom: first to: last [ 889 "Select the text between the given endpoints. The two endpoints 890 are Point objects in which both coordinates are 1-based: the 891 first line is line 1, and the first character in the first line 892 is character 1." 893 894 <category: 'position & lines'> 895 | bounds | 896 bounds := self from: first to: last. 897 self gtkbuffer selectRange: (bounds at: 1) bound: (bounds at: 2) 898 ] 899 900 setToEnd [ 901 "Move the insertion point to the end of the widget" 902 903 <category: 'position & lines'> 904 self tclEval: ' 905 %1 mark set insert end-1c 906 %1 see end' 907 with: self connected 908 ] 909 910 addChild: child [ 911 <category: 'private'> 912 self 913 tclEval: '%1 window create end -window %2' 914 with: self connected 915 with: child container 916 ] 917 918 setAttributes: attr start: startTextIter end: endTextIter [ 919 <category: 'private'> 920 | tags | 921 tagInfo isNil ifTrue: [tagInfo := BTextTags new: self]. 922 tags := attr tags: tagInfo. 923 tags do: 924 [:each | 925 self gtkbuffer 926 applyTag: each 927 start: startTextIter 928 end: endTextIter] 929 ] 930 931 gtkbuffer [ 932 "answer the gtk text buffer" 933 934 <category: 'private'> 935 gtkbuffer isNil ifTrue: [self createWidget]. 936 ^gtkbuffer 937 ] 938 939 onChanged: userData data: unused [ 940 <category: 'private'> 941 self invokeCallback 942 ] 943 944 create [ 945 "initialise a Text widget" 946 947 <category: 'private'> 948 self connected: GTK.GtkTextView new. 949 gtkbuffer := self connected getBuffer. 950 self gtkbuffer 951 connectSignal: 'changed' 952 to: self 953 selector: #onChanged:data: 954 userData: nil 955 ] 956 957 defineTag: name as: options [ 958 <category: 'private'> 959 options class = String 960 ifTrue: 961 [options printNl. 962 0 unconverted defineTag call]. 963 "FIXME/TODO: use g_object_set_property and recreate createTag" 964 self gtkbuffer createTag: name varargs: options 965 ] 966 967 destroyed [ 968 <category: 'private'> 969 super destroyed. 970 images isNil ifTrue: [^self]. 971 images do: [:name | self tclEval: 'image delete ' , name]. 972 images := nil 973 ] 974 975 from: aPoint to: endPoint [ 976 <category: 'private'> 977 | start end | 978 start := self gtkbuffer getIterAtLineOffset: aPoint y - 1 979 charOffset: aPoint x - 1. 980 end := self gtkbuffer getIterAtLineOffset: endPoint y - 1 981 charOffset: endPoint x - 1. 982 ^ 983 {start. 984 end} 985 ] 986 987 removeAttributesInside: range [ 988 <category: 'private'> 989 | start end | 990 start := range at: 1. 991 end := range at: 2. 992 self gtkbuffer removeAllTags: start end: end 993 ] 994 995 tag: name bind: event to: aSymbol of: anObject parameters: params [ 996 <category: 'private'> 997 self 998 bind: event 999 to: aSymbol 1000 of: anObject 1001 parameters: params 1002 prefix: '%1 tag bind %2' % 1003 {self connected. 1004 name} 1005 ] 1006] 1007 1008 1009 1010BEventTarget subclass: BTextBindings [ 1011 | list tagName | 1012 1013 <comment: 'This object is used to assign event handlers to particular sections of 1014text in a BText widget. To use it, you simply have to add event handlers 1015to it, and then create a BTextAttributes object that refers to it.'> 1016 <category: 'Graphics-Windows'> 1017 1018 BTextBindings class >> new [ 1019 "Create a new instance of the receiver." 1020 1021 <category: 'instance creation'> 1022 ^self basicNew initialize 1023 ] 1024 1025 defineTagFor: aBText [ 1026 <category: 'private - BTextTags protocol'> 1027 list do: [:each | each sendTo: aBText] 1028 ] 1029 1030 tagName [ 1031 <category: 'private - BTextTags protocol'> 1032 ^tagName 1033 ] 1034 1035 initialize [ 1036 <category: 'private'> 1037 tagName := 'ev' , (Time millisecondClockValue printString: 36). 1038 list := OrderedCollection new 1039 ] 1040 1041 primBind: event to: aSymbol of: anObject parameters: params [ 1042 <category: 'private'> 1043 | args | 1044 (args := Array new: 5) 1045 at: 1 put: tagName; 1046 at: 2 put: event; 1047 at: 3 put: aSymbol; 1048 at: 4 put: anObject; 1049 at: 5 put: params. 1050 list add: (Message selector: #tag:bind:to:of:parameters: arguments: args) 1051 ] 1052] 1053 1054 1055 1056Object subclass: BTextAttributes [ 1057 | bgColor fgColor font styles events | 1058 1059 <category: 'Graphics-Windows'> 1060 <comment: ' 1061I help you creating wonderful, colorful BTexts.'> 1062 1063 BTextAttributes class >> backgroundColor: color [ 1064 "Create a new BTextAttributes object resulting in text 1065 with the given background color." 1066 1067 <category: 'instance-creation shortcuts'> 1068 ^self new backgroundColor: color 1069 ] 1070 1071 BTextAttributes class >> black [ 1072 "Create a new BTextAttributes object resulting in black text." 1073 1074 <category: 'instance-creation shortcuts'> 1075 ^self new foregroundColor: 'black' 1076 ] 1077 1078 BTextAttributes class >> blue [ 1079 "Create a new BTextAttributes object resulting in blue text." 1080 1081 <category: 'instance-creation shortcuts'> 1082 ^self new foregroundColor: 'blue' 1083 ] 1084 1085 BTextAttributes class >> center [ 1086 "Create a new BTextAttributes object resulting in centered 1087 paragraphs." 1088 1089 <category: 'instance-creation shortcuts'> 1090 ^self new center 1091 ] 1092 1093 BTextAttributes class >> cyan [ 1094 "Create a new BTextAttributes object resulting in cyan text." 1095 1096 <category: 'instance-creation shortcuts'> 1097 ^self new foregroundColor: 'cyan' 1098 ] 1099 1100 BTextAttributes class >> darkCyan [ 1101 "Create a new BTextAttributes object resulting in dark cyan text." 1102 1103 <category: 'instance-creation shortcuts'> 1104 ^self new foregroundColor: 'PureDarkCyan' 1105 ] 1106 1107 BTextAttributes class >> darkGreen [ 1108 "Create a new BTextAttributes object resulting in dark green text." 1109 1110 <category: 'instance-creation shortcuts'> 1111 ^self new foregroundColor: 'PureDarkGreen' 1112 ] 1113 1114 BTextAttributes class >> darkMagenta [ 1115 "Create a new BTextAttributes object resulting in dark purple text." 1116 1117 <category: 'instance-creation shortcuts'> 1118 ^self new foregroundColor: 'PureDarkMagenta' 1119 ] 1120 1121 BTextAttributes class >> events: aBTextBindings [ 1122 "Create a new BTextAttributes object for text that responds to 1123 events according to the callbacks established in aBTextBindings." 1124 1125 <category: 'instance-creation shortcuts'> 1126 ^self new events: aBTextBindings 1127 ] 1128 1129 BTextAttributes class >> font: font [ 1130 "Create a new BTextAttributes object resulting in text with the given font. 1131 The font can be given as either an X font name or a Blox font description 1132 string. 1133 1134 X font names are given as many fields, each led by a minus, and each of 1135 which can be replaced by an * to indicate a default value is ok: 1136 foundry, family, weight, slant, setwidth, addstyle, pixel size, point size 1137 (the same as pixel size for historical reasons), horizontal resolution, 1138 vertical resolution, spacing, width, charset and character encoding. 1139 1140 Blox font description strings have three fields, which must be separated by 1141 a space and of which only the first is mandatory: the font family, the font 1142 size in points (or in pixels if a negative value is supplied), and a number 1143 of styles separated by a space (valid styles are normal, bold, italic, 1144 underline and overstrike). Examples of valid fonts are ``Helvetica 10 Bold'', 1145 ``Times -14'', ``Futura Bold Underline''. You must enclose the font family 1146 in braces if it is made of two or more words." 1147 1148 <category: 'instance-creation shortcuts'> 1149 ^self new font: font 1150 ] 1151 1152 BTextAttributes class >> foregroundColor: color [ 1153 "Create a new BTextAttributes object resulting in text 1154 with the given foreground color." 1155 1156 <category: 'instance-creation shortcuts'> 1157 ^self new foregroundColor: color 1158 ] 1159 1160 BTextAttributes class >> green [ 1161 "Create a new BTextAttributes object resulting in green text." 1162 1163 <category: 'instance-creation shortcuts'> 1164 ^self new foregroundColor: 'green' 1165 ] 1166 1167 BTextAttributes class >> magenta [ 1168 "Create a new BTextAttributes object resulting in magenta text." 1169 1170 <category: 'instance-creation shortcuts'> 1171 ^self new foregroundColor: 'magenta' 1172 ] 1173 1174 BTextAttributes class >> red [ 1175 "Create a new BTextAttributes object resulting in red text." 1176 1177 <category: 'instance-creation shortcuts'> 1178 ^self new foregroundColor: 'red' 1179 ] 1180 1181 BTextAttributes class >> strikeout [ 1182 "Create a new BTextAttributes object resulting in struck-out text." 1183 1184 <category: 'instance-creation shortcuts'> 1185 ^self new strikeout 1186 ] 1187 1188 BTextAttributes class >> underline [ 1189 "Create a new BTextAttributes object resulting in underlined text." 1190 1191 <category: 'instance-creation shortcuts'> 1192 ^self new underline 1193 ] 1194 1195 BTextAttributes class >> yellow [ 1196 "Create a new BTextAttributes object resulting in yellow text." 1197 1198 <category: 'instance-creation shortcuts'> 1199 ^self new foregroundColor: 'yellow' 1200 ] 1201 1202 BTextAttributes class >> white [ 1203 "Create a new BTextAttributes object resulting in white text." 1204 1205 <category: 'instance-creation shortcuts'> 1206 ^self new foregroundColor: 'white' 1207 ] 1208 1209 black [ 1210 "Set the receiver so that applying it results in black text." 1211 1212 <category: 'colors'> 1213 self foregroundColor: 'black' 1214 ] 1215 1216 blue [ 1217 "Set the receiver so that applying it results in blue text." 1218 1219 <category: 'colors'> 1220 self foregroundColor: 'blue' 1221 ] 1222 1223 cyan [ 1224 "Set the receiver so that applying it results in cyan text." 1225 1226 <category: 'colors'> 1227 self foregroundColor: 'cyan' 1228 ] 1229 1230 darkCyan [ 1231 "Set the receiver so that applying it results in dark cyan text." 1232 1233 <category: 'colors'> 1234 self foregroundColor: 'PureDarkCyan' 1235 ] 1236 1237 darkGreen [ 1238 "Set the receiver so that applying it results in dark green text." 1239 1240 <category: 'colors'> 1241 self foregroundColor: 'PureDarkGreen' 1242 ] 1243 1244 darkMagenta [ 1245 "Set the receiver so that applying it results in dark magenta text." 1246 1247 <category: 'colors'> 1248 self foregroundColor: 'PureDarkMagenta' 1249 ] 1250 1251 green [ 1252 "Set the receiver so that applying it results in green text." 1253 1254 <category: 'colors'> 1255 self foregroundColor: 'green' 1256 ] 1257 1258 magenta [ 1259 "Set the receiver so that applying it results in magenta text." 1260 1261 <category: 'colors'> 1262 self foregroundColor: 'magenta' 1263 ] 1264 1265 red [ 1266 "Set the receiver so that applying it results in red text." 1267 1268 <category: 'colors'> 1269 self foregroundColor: 'red' 1270 ] 1271 1272 white [ 1273 "Set the receiver so that applying it results in white text." 1274 1275 <category: 'colors'> 1276 self foregroundColor: 'white' 1277 ] 1278 1279 yellow [ 1280 "Set the receiver so that applying it results in black text." 1281 1282 <category: 'colors'> 1283 self foregroundColor: 'yellow' 1284 ] 1285 1286 hasStyle: aSymbol [ 1287 <category: 'private'> 1288 ^styles notNil and: [styles includes: aSymbol] 1289 ] 1290 1291 style: aSymbol [ 1292 <category: 'private'> 1293 styles isNil ifTrue: [styles := Set new]. 1294 styles add: aSymbol 1295 ] 1296 1297 tags: aBTextTags [ 1298 <category: 'private'> 1299 | s tagTable | 1300 tagTable := aBTextTags tagTable. 1301 s := OrderedCollection new. 1302 fgColor isNil 1303 ifFalse: [s add: (tagTable lookup: (aBTextTags fgColor: fgColor))]. 1304 bgColor isNil 1305 ifFalse: [s add: (tagTable lookup: (aBTextTags bgColor: bgColor))]. 1306 font isNil ifFalse: [s add: (tagTable lookup: (aBTextTags font: font))]. 1307 events isNil 1308 ifFalse: [s add: (tagTable lookup: (aBTextTags events: events))]. 1309 styles isNil 1310 ifFalse: [styles do: [:each | s add: (tagTable lookup: each)]]. 1311 ^s 1312 ] 1313 1314 backgroundColor [ 1315 "Answer the value of the backgroundColor option for the text. 1316 1317 Specifies the background color to use when displaying text with 1318 these attributes. nil indicates that the default value is not 1319 overridden." 1320 1321 <category: 'setting attributes'> 1322 ^bgColor 1323 ] 1324 1325 backgroundColor: color [ 1326 "Set the value of the backgroundColor option for the text. 1327 1328 Specifies the background color to use when displaying text with 1329 these attributes. nil indicates that the default value is not 1330 overridden." 1331 1332 <category: 'setting attributes'> 1333 bgColor := color 1334 ] 1335 1336 center [ 1337 "Center the text to which these attributes are applied" 1338 1339 <category: 'setting attributes'> 1340 self style: #STYLEcenter 1341 ] 1342 1343 events [ 1344 "Answer the event bindings which apply to text subject to these 1345 attributes" 1346 1347 <category: 'setting attributes'> 1348 ^events 1349 ] 1350 1351 events: aBTextBindings [ 1352 "Set the event bindings which apply to text subject to these 1353 attributes" 1354 1355 <category: 'setting attributes'> 1356 events := aBTextBindings 1357 ] 1358 1359 font [ 1360 "Answer the value of the font option for the text. 1361 The font can be given as either an X font name or a Blox font description 1362 string, or nil if you want the widget's default font to apply. 1363 1364 X font names are given as many fields, each led by a minus, and each of 1365 which can be replaced by an * to indicate a default value is ok: 1366 foundry, family, weight, slant, setwidth, addstyle, pixel size, point size 1367 (the same as pixel size for historical reasons), horizontal resolution, 1368 vertical resolution, spacing, width, charset and character encoding. 1369 1370 Blox font description strings have three fields, which must be separated by 1371 a space and of which only the first is mandatory: the font family, the font 1372 size in points (or in pixels if a negative value is supplied), and a number 1373 of styles separated by a space (valid styles are normal, bold, italic, 1374 underline and overstrike). Examples of valid fonts are ``Helvetica 10 Bold'', 1375 ``Times -14'', ``Futura Bold Underline''. You must enclose the font family 1376 in braces if it is made of two or more words." 1377 1378 <category: 'setting attributes'> 1379 ^font 1380 ] 1381 1382 font: fontName [ 1383 "Set the value of the font option for the text. 1384 The font can be given as either an X font name or a Blox font description 1385 string, or nil if you want the widget's default font to apply. 1386 1387 X font names are given as many fields, each led by a minus, and each of 1388 which can be replaced by an * to indicate a default value is ok: 1389 foundry, family, weight, slant, setwidth, addstyle, pixel size, point size 1390 (the same as pixel size for historical reasons), horizontal resolution, 1391 vertical resolution, spacing, width, charset and character encoding. 1392 1393 Blox font description strings have three fields, which must be separated by 1394 a space and of which only the first is mandatory: the font family, the font 1395 size in points (or in pixels if a negative value is supplied), and a number 1396 of styles separated by a space (valid styles are normal, bold, italic, 1397 underline and overstrike). Examples of valid fonts are ``Helvetica 10 Bold'', 1398 ``Times -14'', ``Futura Bold Underline''. You must enclose the font family 1399 in braces if it is made of two or more words." 1400 1401 <category: 'setting attributes'> 1402 font := fontName 1403 ] 1404 1405 foregroundColor [ 1406 "Answer the value of the foregroundColor option for the text. 1407 1408 Specifies the foreground color to use when displaying text with 1409 these attributes. nil indicates that the default value is not 1410 overridden." 1411 1412 <category: 'setting attributes'> 1413 ^fgColor 1414 ] 1415 1416 foregroundColor: color [ 1417 "Set the value of the foregroundColor option for the text. 1418 1419 Specifies the foreground color to use when displaying text with 1420 these attributes. nil indicates that the default value is not 1421 overridden." 1422 1423 <category: 'setting attributes'> 1424 fgColor := color 1425 ] 1426 1427 isCentered [ 1428 "Answer whether the text to which these attributes are applied 1429 is centered" 1430 1431 <category: 'setting attributes'> 1432 ^self hasStyle: #STYLEcenter 1433 ] 1434 1435 isStruckout [ 1436 "Answer whether the text to which these attributes are applied 1437 is struckout" 1438 1439 <category: 'setting attributes'> 1440 ^self hasStyle: #STYLEstrikeout 1441 ] 1442 1443 isUnderlined [ 1444 "Answer whether the text to which these attributes are applied 1445 is underlined" 1446 1447 <category: 'setting attributes'> 1448 ^self hasStyle: #STYLEunderline 1449 ] 1450 1451 strikeout [ 1452 "Strike out the text to which these attributes are applied" 1453 1454 <category: 'setting attributes'> 1455 self style: #STYLEstrikeout 1456 ] 1457 1458 underline [ 1459 "Underline the text to which these attributes are applied" 1460 1461 <category: 'setting attributes'> 1462 self style: #STYLEunderline 1463 ] 1464] 1465 1466 1467 1468Object subclass: BTextTags [ 1469 | client tags | 1470 1471 <category: 'Graphics-Windows'> 1472 <comment: 'I am a private class. I sit between a BText and BTextAttributes, helping 1473the latter in telling the former which attributes to use.'> 1474 1475 BTextTags class >> new [ 1476 <category: 'private - instance creation'> 1477 self shouldNotImplement 1478 ] 1479 1480 BTextTags class >> new: client [ 1481 <category: 'private - instance creation'> 1482 ^super new initialize: client 1483 ] 1484 1485 bgColor: color [ 1486 <category: 'private - BTextAttributes protocol'> 1487 ^'b_' , (self color: color) 1488 ] 1489 1490 events: aBTextBindings [ 1491 <category: 'private - BTextAttributes protocol'> 1492 | tagName | 1493 tagName := aBTextBindings tagName. 1494 (tags includes: tagName) 1495 ifFalse: 1496 [tags add: tagName. 1497 aBTextBindings defineTagFor: client]. 1498 ^tagName 1499 ] 1500 1501 fgColor: color [ 1502 <category: 'private - BTextAttributes protocol'> 1503 ^'f_' , (self color: color) 1504 ] 1505 1506 font: font [ 1507 <category: 'private - BTextAttributes protocol'> 1508 | tagName | 1509 tagName := WriteStream on: (String new: 20). 1510 font substrings do: 1511 [:each | 1512 tagName 1513 nextPutAll: each; 1514 nextPut: $_]. 1515 tagName := tagName contents. 1516 (tags includes: tagName) 1517 ifFalse: 1518 [tags add: tagName. 1519 'FIXME fonts.. ' display. 1520 font printNl. 1521 client defineTag: tagName 1522 as: 1523 {'font'. 1524 font. 1525 nil}]. 1526 ^tagName 1527 ] 1528 1529 color: color [ 1530 <category: 'private'> 1531 | tagName | 1532 tagName := (color at: 1) = $# 1533 ifTrue: 1534 [(color copy) 1535 at: 1 put: $_; 1536 yourself] 1537 ifFalse: [color asLowercase]. 1538 (tags includes: tagName) 1539 ifFalse: 1540 [tags add: tagName. 1541 client defineTag: 'f_' , tagName 1542 as: 1543 {'foreground'. 1544 color. 1545 nil}. 1546 client defineTag: 'b_' , tagName 1547 as: 1548 {'background'. 1549 color. 1550 nil}]. 1551 ^tagName 1552 ] 1553 1554 initialize: clientBText [ 1555 "initialise for use with clientBText" 1556 1557 <category: 'private'> 1558 client := clientBText. 1559 tags := Set new. 1560 client defineTag: 'STYLEstrikeout' 1561 as: 1562 {'strikethrough'. 1563 true. 1564 nil}. 1565 client defineTag: 'STYLEunderline' 1566 as: 1567 {'underline'. 1568 GTK.Pango pangoUnderlineSingle. 1569 nil}. 1570 client defineTag: 'STYLEcenter' 1571 as: 1572 {'justification'. 1573 GTK.Gtk gtkJustifyCenter. 1574 nil} 1575 ] 1576 1577 tagTable [ 1578 <category: 'private'> 1579 ^client gtkbuffer getTagTable 1580 ] 1581] 1582 1583 1584 1585"-------------------------- BText class -----------------------------" 1586 1587 1588 1589"-------------------------- BTextBindings class -----------------------------" 1590 1591 1592 1593"-------------------------- BTextAttributes class -----------------------------" 1594 1595 1596 1597"-------------------------- BTextTags class -----------------------------" 1598 1599