1"======================================================================
2|
3|   Smalltalk Tk-based GUI building blocks (basic widget classes).
4|
5|
6 ======================================================================"
7
8"======================================================================
9|
10| Copyright 1999, 2000, 2001, 2002, 2009 Free Software Foundation, Inc.
11| Written by Paolo Bonzini.
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
34BPrimitive subclass: BEdit [
35    | callback |
36
37    <comment: 'I am a widget showing one line of modifiable text.'>
38    <category: 'Graphics-Windows'>
39
40    Initialized := nil.
41
42    BEdit class >> new: parent contents: aString [
43	"Answer a new BEdit widget laid inside the given parent widget,
44	 with a default content of aString"
45
46	<category: 'instance creation'>
47	^(self new: parent)
48	    contents: aString;
49	    yourself
50    ]
51
52    BEdit class >> initializeOnStartup [
53	<category: 'private'>
54	Initialized := false
55    ]
56
57    backgroundColor [
58	"Answer the value of the backgroundColor option for the widget.
59
60	 Specifies the normal background color to use when displaying the widget."
61
62	<category: 'accessing'>
63	self properties at: #background ifPresent: [:value | ^value].
64	self
65	    tclEval: '%1 cget -background'
66	    with: self connected
67	    with: self container.
68	^self properties at: #background put: self tclResult
69    ]
70
71    backgroundColor: value [
72	"Set 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
78	    tclEval: '%1 configure -background %3'
79	    with: self connected
80	    with: self container
81	    with: value asTkString.
82	self properties at: #background put: value
83    ]
84
85    callback [
86	"Answer a DirectedMessage that is sent when the receiver is modified,
87	 or nil if none has been set up."
88
89	<category: 'accessing'>
90	^callback
91    ]
92
93    callback: aReceiver message: aSymbol [
94	"Set up so that aReceiver is sent the aSymbol message (the name of
95	 a zero- or one-argument selector) when the receiver is modified.
96	 If the method accepts an argument, the receiver is passed."
97
98	<category: 'accessing'>
99	| arguments selector numArgs |
100	selector := aSymbol asSymbol.
101	numArgs := selector numArgs.
102	arguments := #().
103	numArgs = 1 ifTrue: [arguments := Array with: self].
104	callback := DirectedMessage
105		    selector: selector
106		    arguments: arguments
107		    receiver: aReceiver
108    ]
109
110    contents [
111	"Return the contents of the widget"
112
113	<category: 'accessing'>
114	self tclEval: 'return ${var' , self connected , '}'.
115	^self tclResult
116    ]
117
118    contents: newText [
119	"Set the contents of the widget"
120
121	<category: 'accessing'>
122	self tclEval: 'set var' , self connected , ' ' , newText asTkString
123    ]
124
125    font [
126	"Answer the value of the font option for the widget.
127
128	 Specifies the font to use when drawing text inside the widget. The font
129	 can be given as either an X font name or a Blox font description string.
130
131	 X font names are given as many fields, each led by a minus, and each of
132	 which can be replaced by an * to indicate a default value is ok:
133	 foundry, family, weight, slant, setwidth, addstyle, pixel size, point size
134	 (the same as pixel size for historical reasons), horizontal resolution,
135	 vertical resolution, spacing, width, charset and character encoding.
136
137	 Blox font description strings have three fields, which must be separated by
138	 a space and of which only the first is mandatory: the font family, the font
139	 size in points (or in pixels if a negative value is supplied), and a number
140	 of styles separated by a space (valid styles are normal, bold, italic,
141	 underline and overstrike). Examples of valid fonts are ``Helvetica 10 Bold'',
142	 ``Times -14'', ``Futura Bold Underline''.  You must enclose the font family
143	 in braces if it is made of two or more words."
144
145	<category: 'accessing'>
146	self properties at: #font ifPresent: [:value | ^value].
147	self
148	    tclEval: '%1 cget -font'
149	    with: self connected
150	    with: self container.
151	^self properties at: #font put: self tclResult
152    ]
153
154    font: value [
155	"Set the value of the font option for the widget.
156
157	 Specifies the font to use when drawing text inside the widget. The font
158	 can be given as either an X font name or a Blox font description string.
159
160	 X font names are given as many fields, each led by a minus, and each of
161	 which can be replaced by an * to indicate a default value is ok:
162	 foundry, family, weight, slant, setwidth, addstyle, pixel size, point size
163	 (the same as pixel size for historical reasons), horizontal resolution,
164	 vertical resolution, spacing, width, charset and character encoding.
165
166	 Blox font description strings have three fields, which must be separated by
167	 a space and of which only the first is mandatory: the font family, the font
168	 size in points (or in pixels if a negative value is supplied), and a number
169	 of styles separated by a space (valid styles are normal, bold, italic,
170	 underline and overstrike). Examples of valid fonts are ``Helvetica 10 Bold'',
171	 ``Times -14'', ``Futura Bold Underline''.  You must enclose the font family
172	 in braces if it is made of two or more words."
173
174	<category: 'accessing'>
175	self
176	    tclEval: '%1 configure -font %3'
177	    with: self connected
178	    with: self container
179	    with: value asTkString.
180	self properties at: #font put: value
181    ]
182
183    foregroundColor [
184	"Answer the value of the foregroundColor option for the widget.
185
186	 Specifies the normal foreground color to use when displaying the widget."
187
188	<category: 'accessing'>
189	self properties at: #foreground ifPresent: [:value | ^value].
190	self
191	    tclEval: '%1 cget -foreground'
192	    with: self connected
193	    with: self container.
194	^self properties at: #foreground put: self tclResult
195    ]
196
197    foregroundColor: value [
198	"Set the value of the foregroundColor option for the widget.
199
200	 Specifies the normal foreground color to use when displaying the widget."
201
202	<category: 'accessing'>
203	self
204	    tclEval: '%1 configure -foreground %3'
205	    with: self connected
206	    with: self container
207	    with: value asTkString.
208	self properties at: #foreground put: value
209    ]
210
211    selectBackground [
212	"Answer the value of the selectBackground option for the widget.
213
214	 Specifies the background color to use when displaying selected parts
215	 of the widget."
216
217	<category: 'accessing'>
218	self properties at: #selectbackground ifPresent: [:value | ^value].
219	self
220	    tclEval: '%1 cget -selectbackground'
221	    with: self connected
222	    with: self container.
223	^self properties at: #selectbackground put: self tclResult
224    ]
225
226    selectBackground: value [
227	"Set the value of the selectBackground option for the widget.
228
229	 Specifies the background color to use when displaying selected parts
230	 of the widget."
231
232	<category: 'accessing'>
233	self
234	    tclEval: '%1 configure -selectbackground %3'
235	    with: self connected
236	    with: self container
237	    with: value asTkString.
238	self properties at: #selectbackground put: value
239    ]
240
241    selectForeground [
242	"Answer the value of the selectForeground option for the widget.
243
244	 Specifies the foreground color to use when displaying selected parts
245	 of the widget."
246
247	<category: 'accessing'>
248	self properties at: #selectforeground ifPresent: [:value | ^value].
249	self
250	    tclEval: '%1 cget -selectforeground'
251	    with: self connected
252	    with: self container.
253	^self properties at: #selectforeground put: self tclResult
254    ]
255
256    selectForeground: value [
257	"Set the value of the selectForeground option for the widget.
258
259	 Specifies the foreground color to use when displaying selected parts
260	 of the widget."
261
262	<category: 'accessing'>
263	self
264	    tclEval: '%1 configure -selectforeground %3'
265	    with: self connected
266	    with: self container
267	    with: value asTkString.
268	self properties at: #selectforeground put: value
269    ]
270
271    create [
272	"Private - Set up the widget and Tcl hooks to get callbacks from
273	 it."
274
275	<category: 'private'>
276	self create: ' -width 0 -font {' , self class defaultFont , '}'.
277	Initialized ifFalse: [self defineCallbackProcedure].
278	self
279	    tclEval: '
280	set var%1 {}
281	bind %1 <<Changed>> {callback %2 invokeCallback}
282	trace variable var%1 w doEditCallback
283	%1 configure -textvariable var%1 -highlightthickness 0 -takefocus 1'
284	    with: self connected
285	    with: self asOop printString
286    ]
287
288    defineCallbackProcedure [
289	"Private - Set up a Tcl hook to generate Changed events for entry widgets"
290
291	<category: 'private'>
292	Initialized := true.
293	self
294	    tclEval: '
295      proc doEditCallback { name el op } {
296	regsub ^var $name {} widgetName
297	event generate $widgetName <<Changed>>
298      }'
299    ]
300
301    setInitialSize [
302	"Make the Tk placer's status, the receiver's properties and the
303	 window status (as returned by winfo) consistent. Occupy the
304	 height indicated by the widget itself and the whole of the
305	 parent's width, at the top left corner"
306
307	<category: 'private'>
308	self
309	    x: 0 y: 0;
310	    width: self parent width
311    ]
312
313    widgetType [
314	<category: 'private'>
315	^'entry'
316    ]
317
318    destroyed [
319	"Private - The receiver has been destroyed, clear the corresponding
320	 Tcl variable to avoid memory leaks."
321
322	<category: 'widget protocol'>
323	self tclEval: 'unset var' , self connected.
324	super destroyed
325    ]
326
327    hasSelection [
328	"Answer whether there is selected text in the widget"
329
330	<category: 'widget protocol'>
331	self tclEval: self connected , ' selection present'.
332	^self tclResult = '1'
333    ]
334
335    insertAtEnd: aString [
336	"Clear the selection and append aString at the end of the
337	 widget."
338
339	<category: 'widget protocol'>
340	self
341	    tclEval: '%1 selection clear
342	%1 insert end %2
343	%1 see end'
344	    with: self connected
345	    with: aString asTkString
346    ]
347
348    insertText: aString [
349	"Insert aString in the widget at the current insertion point,
350	 replacing the currently selected text (if any)."
351
352	<category: 'widget protocol'>
353	self
354	    tclEval: 'catch { %1 delete sel.first sel.last }
355	%1 insert insert %2
356	%1 see insert'
357	    with: self connected
358	    with: aString asTkString
359    ]
360
361    invokeCallback [
362	"Generate a synthetic callback."
363
364	<category: 'widget protocol'>
365	self callback isNil ifFalse: [self callback send]
366    ]
367
368    nextPut: aCharacter [
369	"Clear the selection and append aCharacter at the end of the
370	 widget."
371
372	<category: 'widget protocol'>
373	self insertAtEnd: (String with: aCharacter)
374    ]
375
376    nextPutAll: aString [
377	"Clear the selection and append aString at the end of the
378	 widget."
379
380	<category: 'widget protocol'>
381	self insertAtEnd: aString
382    ]
383
384    nl [
385	"Clear the selection and append a linefeed character at the
386	 end of the widget."
387
388	<category: 'widget protocol'>
389	self insertAtEnd: Character nl asString
390    ]
391
392    replaceSelection: aString [
393	"Insert aString in the widget at the current insertion point,
394	 replacing the currently selected text (if any), and leaving
395	 the text selected."
396
397	<category: 'widget protocol'>
398	self
399	    tclEval: 'catch {
400	  %1 icursor sel.first
401	  %1 delete sel.first sel.last
402	}
403	%1 insert insert %2
404	%1 select insert [expr %3 + [%1 index insert]]
405	%1 see insert'
406	    with: self connected
407	    with: aString asTkString
408	    with: aString size printString
409    ]
410
411    selectAll [
412	"Select the whole contents of the widget."
413
414	<category: 'widget protocol'>
415	self tclEval: self connected , ' selection range 0 end'
416    ]
417
418    selectFrom: first to: last [
419	"Sets the selection to include the characters starting with the one
420	 indexed by first (the very first character in the widget having
421	 index 1) and ending with the one just before last.  If last
422	 refers to the same character as first or an earlier one, then the
423	 widget's selection is cleared."
424
425	<category: 'widget protocol'>
426	self
427	    tclEval: '%1 selection range %2 %3'
428	    with: self connected
429	    with: (first - 1) printString
430	    with: (last - 1) printString
431    ]
432
433    selection [
434	"Answer an empty string if the widget has no selection, else answer
435	 the currently selected text"
436
437	<category: 'widget protocol'>
438	| stream first |
439	self
440	    tclEval: 'if [%1 selection present] {
441	   return [string range ${var%1} [%1 index sel.first] [%1 index sel.last]]"
442	 }'
443	    with: self connected.
444	^self tclResult
445    ]
446
447    selectionRange [
448	"Answer nil if the widget has no selection, else answer
449	 an Interval object whose first item is the index of the
450	 first character in the selection, and whose last item is the
451	 index of the character just after the last one in the
452	 selection."
453
454	<category: 'widget protocol'>
455	| stream first |
456	self
457	    tclEval: 'if [%1 selection present] {
458	   return "[%1 index sel.first] [%1 index sel.last]"
459	 }'
460	    with: self connected.
461	stream := ReadStream on: self tclResult.
462	stream atEnd ifTrue: [^nil].
463	first := (stream upTo: $ ) asInteger + 1.
464	^first to: stream upToEnd asInteger + 1
465    ]
466
467    space [
468	"Clear the selection and append a space at the end of the
469	 widget."
470
471	<category: 'widget protocol'>
472	self insertAtEnd: ' '
473    ]
474]
475
476
477
478BPrimitive subclass: BLabel [
479
480    <comment: 'I am a label showing static text.'>
481    <category: 'Graphics-Windows'>
482
483    AnchorPoints := nil.
484
485    BLabel class >> initialize [
486	"Private - Initialize the receiver's class variables."
487
488	<category: 'initialization'>
489	(AnchorPoints := IdentityDictionary new: 15)
490	    at: #topLeft put: 'nw';
491	    at: #topCenter put: 'n';
492	    at: #topRight put: 'ne';
493	    at: #leftCenter put: 'w';
494	    at: #center put: 'center';
495	    at: #rightCenter put: 'e';
496	    at: #bottomLeft put: 'sw';
497	    at: #bottomCenter put: 's';
498	    at: #bottomRight put: 'se'
499    ]
500
501    BLabel class >> new: parent label: label [
502	"Answer a new BLabel widget laid inside the given parent widget,
503	 showing by default the `label' String."
504
505	<category: 'instance creation'>
506	^(self new: parent)
507	    label: label;
508	    yourself
509    ]
510
511    alignment [
512	"Answer the value of the anchor option for the widget.
513
514	 Specifies how the information in a widget (e.g. text or a bitmap) is to be
515	 displayed in the widget. Must be one of the symbols #topLeft, #topCenter,
516	 #topRight, #leftCenter, #center, #rightCenter, #bottomLeft, #bottomCenter,
517	 #bottomRight. For example, #topLeft means display the information such that
518	 its top-left corner is at the top-left corner of the widget."
519
520	<category: 'accessing'>
521	^self properties at: #alignment ifAbsent: [#topLeft]
522    ]
523
524    alignment: aSymbol [
525	"Set the value of the anchor option for the widget.
526
527	 Specifies how the information in a widget (e.g. text or a bitmap) is to be
528	 displayed in the widget. Must be one of the symbols #topLeft, #topCenter,
529	 #topRight, #leftCenter, #center, #rightCenter, #bottomLeft, #bottomCenter,
530	 #bottomRight. For example, #topLeft means display the information such that
531	 its top-left corner is at the top-left corner of the widget."
532
533	<category: 'accessing'>
534	self anchor: (AnchorPoints at: aSymbol).
535	self properties at: #alignment put: aSymbol
536    ]
537
538    backgroundColor [
539	"Answer the value of the backgroundColor option for the widget.
540
541	 Specifies the normal background color to use when displaying the widget."
542
543	<category: 'accessing'>
544	self properties at: #background ifPresent: [:value | ^value].
545	self
546	    tclEval: '%1 cget -background'
547	    with: self connected
548	    with: self container.
549	^self properties at: #background put: self tclResult
550    ]
551
552    backgroundColor: value [
553	"Set the value of the backgroundColor option for the widget.
554
555	 Specifies the normal background color to use when displaying the widget."
556
557	<category: 'accessing'>
558	self
559	    tclEval: '%1 configure -background %3'
560	    with: self connected
561	    with: self container
562	    with: value asTkString.
563	self properties at: #background put: value
564    ]
565
566    font [
567	"Answer the value of the font option for the widget.
568
569	 Specifies the font to use when drawing text inside the widget. The font
570	 can be given as either an X font name or a Blox font description string.
571
572	 X font names are given as many fields, each led by a minus, and each of
573	 which can be replaced by an * to indicate a default value is ok:
574	 foundry, family, weight, slant, setwidth, addstyle, pixel size, point size
575	 (the same as pixel size for historical reasons), horizontal resolution,
576	 vertical resolution, spacing, width, charset and character encoding.
577
578	 Blox font description strings have three fields, which must be separated by
579	 a space and of which only the first is mandatory: the font family, the font
580	 size in points (or in pixels if a negative value is supplied), and a number
581	 of styles separated by a space (valid styles are normal, bold, italic,
582	 underline and overstrike). Examples of valid fonts are ``Helvetica 10 Bold'',
583	 ``Times -14'', ``Futura Bold Underline''.  You must enclose the font family
584	 in braces if it is made of two or more words."
585
586	<category: 'accessing'>
587	self properties at: #font ifPresent: [:value | ^value].
588	self
589	    tclEval: '%1 cget -font'
590	    with: self connected
591	    with: self container.
592	^self properties at: #font put: self tclResult
593    ]
594
595    font: value [
596	"Set the value of the font option for the widget.
597
598	 Specifies the font to use when drawing text inside the widget. The font
599	 can be given as either an X font name or a Blox font description string.
600
601	 X font names are given as many fields, each led by a minus, and each of
602	 which can be replaced by an * to indicate a default value is ok:
603	 foundry, family, weight, slant, setwidth, addstyle, pixel size, point size
604	 (the same as pixel size for historical reasons), horizontal resolution,
605	 vertical resolution, spacing, width, charset and character encoding.
606
607	 Blox font description strings have three fields, which must be separated by
608	 a space and of which only the first is mandatory: the font family, the font
609	 size in points (or in pixels if a negative value is supplied), and a number
610	 of styles separated by a space (valid styles are normal, bold, italic,
611	 underline and overstrike). Examples of valid fonts are ``Helvetica 10 Bold'',
612	 ``Times -14'', ``Futura Bold Underline''.  You must enclose the font family
613	 in braces if it is made of two or more words."
614
615	<category: 'accessing'>
616	self
617	    tclEval: '%1 configure -font %3'
618	    with: self connected
619	    with: self container
620	    with: value asTkString.
621	self properties at: #font put: value
622    ]
623
624    foregroundColor [
625	"Answer the value of the foregroundColor option for the widget.
626
627	 Specifies the normal foreground color to use when displaying the widget."
628
629	<category: 'accessing'>
630	self properties at: #foreground ifPresent: [:value | ^value].
631	self
632	    tclEval: '%1 cget -foreground'
633	    with: self connected
634	    with: self container.
635	^self properties at: #foreground put: self tclResult
636    ]
637
638    foregroundColor: value [
639	"Set the value of the foregroundColor option for the widget.
640
641	 Specifies the normal foreground color to use when displaying the widget."
642
643	<category: 'accessing'>
644	self
645	    tclEval: '%1 configure -foreground %3'
646	    with: self connected
647	    with: self container
648	    with: value asTkString.
649	self properties at: #foreground put: value
650    ]
651
652    label [
653	"Answer the value of the label option for the widget.
654
655	 Specifies a string to be displayed inside the widget. The way in which the
656	 string is displayed depends on the particular widget and may be determined
657	 by other options, such as anchor. For windows, this is the title of the window."
658
659	<category: 'accessing'>
660	self properties at: #text ifPresent: [:value | ^value].
661	self
662	    tclEval: '%1 cget -text'
663	    with: self connected
664	    with: self container.
665	^self properties at: #text put: self tclResult
666    ]
667
668    label: value [
669	"Set the value of the label option for the widget.
670
671	 Specifies a string to be displayed inside the widget. The way in which the
672	 string is displayed depends on the particular widget and may be determined
673	 by other options, such as anchor. For windows, this is the title of the window."
674
675	<category: 'accessing'>
676	self
677	    tclEval: '%1 configure -text %3'
678	    with: self connected
679	    with: self container
680	    with: value asTkString.
681	self properties at: #text put: value
682    ]
683
684    anchor: value [
685	"Private - Set the value of the Tk anchor option for the widget."
686
687	<category: 'private'>
688	self
689	    tclEval: '%1 configure -anchor %3'
690	    with: self connected
691	    with: self container
692	    with: value asTkString.
693	self properties at: #anchor put: value
694    ]
695
696    create [
697	<category: 'private'>
698	self
699	    create: '-anchor nw -takefocus 0 -font {' , self class defaultFont , '}'.
700	self tclEval: 'bind %1 <Configure> "+%1 configure -wraplength %%w"'
701	    with: self connected
702    ]
703
704    initialize: parentWidget [
705	<category: 'private'>
706	super initialize: parentWidget.
707	parentWidget isNil
708	    ifFalse: [self backgroundColor: parentWidget backgroundColor]
709    ]
710
711    setInitialSize [
712	"Make the Tk placer's status, the receiver's properties and the
713	 window status (as returned by winfo) consistent. Occupy the
714	 area indicated by the widget itself, at the top left corner"
715
716	<category: 'private'>
717	self x: 0 y: 0
718    ]
719
720    widgetType [
721	<category: 'private'>
722	^'label'
723    ]
724]
725
726
727
728BPrimitive subclass: BButton [
729    | callback |
730
731    <comment: 'I am a button that a user can click. In fact I am at the head
732of a small hierarchy of objects which exhibit button-like look
733and behavior'>
734    <category: 'Graphics-Windows'>
735
736    BButton class >> new: parent label: label [
737	"Answer a new BButton widget laid inside the given parent widget,
738	 showing by default the `label' String."
739
740	<category: 'instance creation'>
741	^(self new: parent)
742	    label: label;
743	    yourself
744    ]
745
746    backgroundColor [
747	"Answer the value of the backgroundColor option for the widget.
748
749	 Specifies the normal background color to use when displaying the widget."
750
751	<category: 'accessing'>
752	self properties at: #background ifPresent: [:value | ^value].
753	self
754	    tclEval: '%1 cget -background'
755	    with: self connected
756	    with: self container.
757	^self properties at: #background put: self tclResult
758    ]
759
760    backgroundColor: value [
761	"Set the value of the backgroundColor option for the widget.
762
763	 Specifies the normal background color to use when displaying the widget."
764
765	<category: 'accessing'>
766	self
767	    tclEval: '%1 configure -background %3'
768	    with: self connected
769	    with: self container
770	    with: value asTkString.
771	self properties at: #background put: value
772    ]
773
774    callback [
775	"Answer a DirectedMessage that is sent when the receiver is clicked,
776	 or nil if none has been set up."
777
778	<category: 'accessing'>
779	^callback
780    ]
781
782    callback: aReceiver message: aSymbol [
783	"Set up so that aReceiver is sent the aSymbol message (the name of
784	 a zero- or one-argument selector) when the receiver is clicked.
785	 If the method accepts an argument, the receiver is passed."
786
787	<category: 'accessing'>
788	| arguments selector numArgs |
789	selector := aSymbol asSymbol.
790	numArgs := selector numArgs.
791	arguments := #().
792	numArgs = 1 ifTrue: [arguments := Array with: self].
793	callback := DirectedMessage
794		    selector: selector
795		    arguments: arguments
796		    receiver: aReceiver
797    ]
798
799    font [
800	"Answer the value of the font option for the widget.
801
802	 Specifies the font to use when drawing text inside the widget. The font
803	 can be given as either an X font name or a Blox font description string.
804
805	 X font names are given as many fields, each led by a minus, and each of
806	 which can be replaced by an * to indicate a default value is ok:
807	 foundry, family, weight, slant, setwidth, addstyle, pixel size, point size
808	 (the same as pixel size for historical reasons), horizontal resolution,
809	 vertical resolution, spacing, width, charset and character encoding.
810
811	 Blox font description strings have three fields, which must be separated by
812	 a space and of which only the first is mandatory: the font family, the font
813	 size in points (or in pixels if a negative value is supplied), and a number
814	 of styles separated by a space (valid styles are normal, bold, italic,
815	 underline and overstrike). Examples of valid fonts are ``Helvetica 10 Bold'',
816	 ``Times -14'', ``Futura Bold Underline''.  You must enclose the font family
817	 in braces if it is made of two or more words."
818
819	<category: 'accessing'>
820	self properties at: #font ifPresent: [:value | ^value].
821	self
822	    tclEval: '%1 cget -font'
823	    with: self connected
824	    with: self container.
825	^self properties at: #font put: self tclResult
826    ]
827
828    font: value [
829	"Set the value of the font option for the widget.
830
831	 Specifies the font to use when drawing text inside the widget. The font
832	 can be given as either an X font name or a Blox font description string.
833
834	 X font names are given as many fields, each led by a minus, and each of
835	 which can be replaced by an * to indicate a default value is ok:
836	 foundry, family, weight, slant, setwidth, addstyle, pixel size, point size
837	 (the same as pixel size for historical reasons), horizontal resolution,
838	 vertical resolution, spacing, width, charset and character encoding.
839
840	 Blox font description strings have three fields, which must be separated by
841	 a space and of which only the first is mandatory: the font family, the font
842	 size in points (or in pixels if a negative value is supplied), and a number
843	 of styles separated by a space (valid styles are normal, bold, italic,
844	 underline and overstrike). Examples of valid fonts are ``Helvetica 10 Bold'',
845	 ``Times -14'', ``Futura Bold Underline''.  You must enclose the font family
846	 in braces if it is made of two or more words."
847
848	<category: 'accessing'>
849	self
850	    tclEval: '%1 configure -font %3'
851	    with: self connected
852	    with: self container
853	    with: value asTkString.
854	self properties at: #font put: value
855    ]
856
857    foregroundColor [
858	"Answer the value of the foregroundColor option for the widget.
859
860	 Specifies the normal foreground color to use when displaying the widget."
861
862	<category: 'accessing'>
863	self properties at: #foreground ifPresent: [:value | ^value].
864	self
865	    tclEval: '%1 cget -foreground'
866	    with: self connected
867	    with: self container.
868	^self properties at: #foreground put: self tclResult
869    ]
870
871    foregroundColor: value [
872	"Set the value of the foregroundColor option for the widget.
873
874	 Specifies the normal foreground color to use when displaying the widget."
875
876	<category: 'accessing'>
877	self
878	    tclEval: '%1 configure -foreground %3'
879	    with: self connected
880	    with: self container
881	    with: value asTkString.
882	self properties at: #foreground put: value
883    ]
884
885    invokeCallback [
886	"Generate a synthetic callback"
887
888	<category: 'accessing'>
889	self callback isNil ifFalse: [self callback send]
890    ]
891
892    label [
893	"Answer the value of the label option for the widget.
894
895	 Specifies a string to be displayed inside the widget. The way in which the
896	 string is displayed depends on the particular widget and may be determined
897	 by other options, such as anchor. For windows, this is the title of the window."
898
899	<category: 'accessing'>
900	self properties at: #text ifPresent: [:value | ^value].
901	self
902	    tclEval: '%1 cget -text'
903	    with: self connected
904	    with: self container.
905	^self properties at: #text put: self tclResult
906    ]
907
908    label: value [
909	"Set the value of the label option for the widget.
910
911	 Specifies a string to be displayed inside the widget. The way in which the
912	 string is displayed depends on the particular widget and may be determined
913	 by other options, such as anchor. For windows, this is the title of the window."
914
915	<category: 'accessing'>
916	self
917	    tclEval: '%1 configure -text %3'
918	    with: self connected
919	    with: self container
920	    with: value asTkString.
921	self properties at: #text put: value
922    ]
923
924    create [
925	<category: 'private'>
926	self
927	    create: '-highlightthickness 0 -takefocus 1 -command {callback %1 invokeCallback} -font {%2}'
928		    %
929			{self asOop.
930			self class defaultFont}
931    ]
932
933    setInitialSize [
934	"Make the Tk placer's status, the receiver's properties and the
935	 window status (as returned by winfo) consistent. Occupy the
936	 area indicated by the widget itself, at the top left corner"
937
938	<category: 'private'>
939	self x: 0 y: 0
940    ]
941
942    widgetType [
943	<category: 'private'>
944	^'button'
945    ]
946]
947
948
949
950BPrimitive subclass: BForm [
951
952    <comment: 'I am used to group many widgets together. I leave the heavy
953task of managing their position to the user.'>
954    <category: 'Graphics-Windows'>
955
956    backgroundColor [
957	"Answer the value of the backgroundColor option for the widget.
958
959	 Specifies the normal background color to use when displaying the widget."
960
961	<category: 'accessing'>
962	self properties at: #background ifPresent: [:value | ^value].
963	self
964	    tclEval: '%1 cget -background'
965	    with: self connected
966	    with: self container.
967	^self properties at: #background put: self tclResult
968    ]
969
970    backgroundColor: value [
971	"Set the value of the backgroundColor option for the widget.
972
973	 Specifies the normal background color to use when displaying the widget."
974
975	<category: 'accessing'>
976	self
977	    tclEval: '%1 configure -background %3'
978	    with: self connected
979	    with: self container
980	    with: value asTkString.
981	self properties at: #background put: value
982    ]
983
984    defaultHeight [
985	"Answer the value of the defaultHeight option for the widget.
986
987	 Specifies the desired height for the form in pixels. If this option
988	 is less than or equal to zero then the window will not request any size at all."
989
990	<category: 'accessing'>
991	self properties at: #height ifPresent: [:value | ^value].
992	self
993	    tclEval: '%1 cget -height'
994	    with: self connected
995	    with: self container.
996	^self properties at: #height put: self tclResult asNumber
997    ]
998
999    defaultHeight: value [
1000	"Set the value of the defaultHeight option for the widget.
1001
1002	 Specifies the desired height for the form in pixels. If this option
1003	 is less than or equal to zero then the window will not request any size at all."
1004
1005	<category: 'accessing'>
1006	self
1007	    tclEval: '%1 configure -height %3'
1008	    with: self connected
1009	    with: self container
1010	    with: value printString asTkString.
1011	self properties at: #height put: value
1012    ]
1013
1014    defaultWidth [
1015	"Answer the value of the defaultWidth option for the widget.
1016
1017	 Specifies the desired width for the form in pixels. If this option
1018	 is less than or equal to zero then the window will not request any size at all."
1019
1020	<category: 'accessing'>
1021	self properties at: #width ifPresent: [:value | ^value].
1022	self
1023	    tclEval: '%1 cget -width'
1024	    with: self connected
1025	    with: self container.
1026	^self properties at: #width put: self tclResult asNumber
1027    ]
1028
1029    defaultWidth: value [
1030	"Set the value of the defaultWidth option for the widget.
1031
1032	 Specifies the desired width for the form in pixels. If this option
1033	 is less than or equal to zero then the window will not request any size at all."
1034
1035	<category: 'accessing'>
1036	self
1037	    tclEval: '%1 configure -width %3'
1038	    with: self connected
1039	    with: self container
1040	    with: value printString asTkString.
1041	self properties at: #width put: value
1042    ]
1043
1044    create [
1045	<category: 'private'>
1046	self create: '-highlightthickness 0 -takefocus 0'
1047    ]
1048
1049    initialize: parentWidget [
1050	<category: 'private'>
1051	super initialize: parentWidget.
1052	parentWidget isNil
1053	    ifFalse: [self backgroundColor: parentWidget backgroundColor]
1054    ]
1055
1056    widgetType [
1057	<category: 'private'>
1058	^'frame'
1059    ]
1060]
1061
1062
1063
1064BForm subclass: BContainer [
1065    | side fill |
1066
1067    <comment: 'I am used to group many widgets together. I can perform simple
1068management by putting widgets next to each other, from left to
1069right or from top to bottom.'>
1070    <category: 'Graphics-Windows'>
1071
1072    setVerticalLayout: aBoolean [
1073	"Answer whether the container will align the widgets vertically or
1074	 horizontally.  Horizontal alignment means that widgets are
1075	 packed from left to right, while vertical alignment means that
1076	 widgets are packed from the top to the bottom of the widget.
1077
1078	 Widgets that are set to be ``stretched'' will share all the
1079	 space that is not allocated to non-stretched widgets.
1080
1081	 The layout of the widget can only be set before the first child
1082	 is inserted in the widget."
1083
1084	<category: 'accessing'>
1085	children isEmpty
1086	    ifFalse: [^self error: 'cannot set layout after the first child is created'].
1087	fill := aBoolean ifTrue: [' -fill x'] ifFalse: [' -fill y'].
1088	side := aBoolean ifTrue: [' -side top'] ifFalse: [' -side left']
1089    ]
1090
1091    addChild: child [
1092	"Private - The widget identified by child has been added to the
1093	 receiver.  This method is public not because you can call it,
1094	 but because it can be useful to override it, not forgetting the
1095	 call to either the superclass implementation or #basicAddChild:,
1096	 to perform some initialization on the children just added. Answer
1097	 the new child."
1098
1099	<category: 'private'>
1100	side isNil ifTrue: [self setVerticalLayout: true].
1101	self tclEval: 'pack ' , child container , ' -anchor nw ' , side , fill.
1102	^self basicAddChild: child
1103    ]
1104
1105    child: child height: value [
1106	<category: 'private'>
1107
1108    ]
1109
1110    child: child heightOffset: value [
1111	<category: 'private'>
1112
1113    ]
1114
1115    child: child stretch: aBoolean [
1116	<category: 'private'>
1117	| fillMethod |
1118	fillMethod := aBoolean
1119		    ifTrue: [' -expand 1 -fill both']
1120		    ifFalse: [' -expand 0 ' , fill].
1121	self tclEval: 'pack ' , child container , fillMethod
1122    ]
1123
1124    child: child width: value [
1125	<category: 'private'>
1126
1127    ]
1128
1129    child: child widthOffset: value [
1130	<category: 'private'>
1131
1132    ]
1133
1134    child: child x: value [
1135	<category: 'private'>
1136
1137    ]
1138
1139    child: child xOffset: value [
1140	<category: 'private'>
1141
1142    ]
1143
1144    child: child y: value [
1145	<category: 'private'>
1146
1147    ]
1148
1149    child: child yOffset: value [
1150	<category: 'private'>
1151
1152    ]
1153
1154    heightChild: child [
1155	<category: 'private'>
1156	| w |
1157	w := self toplevel.
1158	Blox idle.
1159	w isMapped
1160	    ifTrue: [self tclEval: 'winfo height ' , child container]
1161	    ifFalse: [self tclEval: 'winfo reqheight ' , child container].
1162	^self tclResult asInteger
1163    ]
1164
1165    setInitialSize [
1166	"Make the Tk placer's status, the receiver's properties and the
1167	 window status (as returned by winfo) consistent. Occupy the
1168	 area indicated by the widget itself, at the top left corner"
1169
1170	<category: 'private'>
1171	self x: 0 y: 0.
1172
1173	"A hack..."
1174	self parent isNil ifTrue: [^self].
1175	(self parent isKindOf: BContainer)
1176	    ifFalse: [self tclEval: 'pack propagate ' , self container , ' 0']
1177    ]
1178
1179    widthChild: child [
1180	<category: 'private'>
1181	| w |
1182	w := self toplevel.
1183	Blox idle.
1184	w isMapped
1185	    ifTrue: [self tclEval: 'winfo width ' , child container]
1186	    ifFalse: [self tclEval: 'winfo reqwidth ' , child container].
1187	^self tclResult asInteger
1188    ]
1189
1190    xChild: child [
1191	<category: 'private'>
1192	^child xAbsolute
1193    ]
1194
1195    yChild: child [
1196	<category: 'private'>
1197	^child yAbsolute
1198    ]
1199]
1200
1201
1202
1203BContainer subclass: BRadioGroup [
1204    | lastValue lastAssignedValue |
1205
1206    <comment: 'I am used to group many mutually-exclusive radio buttons together.
1207In addition, just like every BContainer I can perform simple management
1208by putting widgets next to each other, from left to right or (which is
1209more useful in this particular case...) from top to bottom.'>
1210    <category: 'Graphics-Windows'>
1211
1212    value [
1213	"Answer the index of the button that is currently selected,
1214	 1 being the first button added to the radio button group.
1215	 0 means that no button is selected"
1216
1217	<category: 'accessing'>
1218	self tclEval: 'return ${var' , self connected , '}'.
1219	^self tclResult asInteger
1220    ]
1221
1222    value: value [
1223	"Force the value-th button added to the radio button group
1224	 to be the selected one."
1225
1226	<category: 'accessing'>
1227	self tclEval: 'set var' , self connected , ' ' , value printString
1228    ]
1229
1230    initialize: parentWidget [
1231	<category: 'private'>
1232	super initialize: parentWidget.
1233	lastAssignedValue := lastValue := 0.
1234	self tclEval: 'set ' , self variable , ' 1'
1235    ]
1236
1237    lastValue [
1238	<category: 'private'>
1239	^lastValue
1240    ]
1241
1242    lastValue: value [
1243	<category: 'private'>
1244	lastValue := value
1245    ]
1246
1247    newButtonValue [
1248	<category: 'private'>
1249	^lastAssignedValue := lastAssignedValue + 1
1250    ]
1251
1252    variable [
1253	<category: 'private'>
1254	^'var' , self connected
1255    ]
1256
1257    destroyed [
1258	"Private - The receiver has been destroyed, clear the corresponding
1259	 Tcl variable to avoid memory leaks."
1260
1261	<category: 'widget protocol'>
1262	self tclEval: 'unset var' , self connected.
1263	super destroyed
1264    ]
1265]
1266
1267
1268
1269BButton subclass: BRadioButton [
1270    | variableValue |
1271
1272    <comment: 'I am just one in a group of mutually exclusive buttons.'>
1273    <category: 'Graphics-Windows'>
1274
1275    callback: aReceiver message: aSymbol [
1276	"Set up so that aReceiver is sent the aSymbol message (the name of
1277	 a selector accepting at most two arguments) when the receiver is
1278	 clicked.  If the method accepts two arguments, the receiver is
1279	 passed as the first parameter.  If the method accepts one or two
1280	 arguments, true is passed as the last parameter for interoperability
1281	 with BToggle widgets."
1282
1283	<category: 'accessing'>
1284	| arguments selector numArgs |
1285	selector := aSymbol asSymbol.
1286	numArgs := selector numArgs.
1287	arguments := #().
1288	numArgs = 1 ifTrue: [arguments := #(true)].
1289	numArgs = 2
1290	    ifTrue:
1291		[arguments :=
1292			{self.
1293			true}].
1294	callback := DirectedMessage
1295		    selector: selector
1296		    arguments: arguments
1297		    receiver: aReceiver
1298    ]
1299
1300    value [
1301	"Answer whether this widget is the selected one in its radio
1302	 button group."
1303
1304	<category: 'accessing'>
1305	^self parent value = variableValue
1306    ]
1307
1308    value: aBoolean [
1309	"Answer whether this widget is the selected one in its radio
1310	 button group.  Setting this property to false for a group's
1311	 currently selected button unhighlights all the buttons in that
1312	 group."
1313
1314	<category: 'accessing'>
1315	aBoolean
1316	    ifTrue:
1317		[self parent value: variableValue.
1318		^self].
1319
1320	"aBoolean is false - unhighlight everything if we're active"
1321	self value ifTrue: [self parent value: 0]
1322    ]
1323
1324    initialize: parentWidget [
1325	<category: 'private'>
1326	super initialize: parentWidget.
1327	variableValue := self parent newButtonValue.
1328	self
1329	    tclEval: self connected , ' configure -anchor nw';
1330	    variableValue: variableValue;
1331	    variable: self parent variable;
1332	    backgroundColor: parentWidget backgroundColor.
1333	variableValue = 1 ifTrue: [self parent value: 1]
1334    ]
1335
1336    variable: value [
1337	"Set the value of Tk's variable option for the widget."
1338
1339	<category: 'private'>
1340	self
1341	    tclEval: '%1 configure -variable %3'
1342	    with: self connected
1343	    with: self container
1344	    with: value asTkString.
1345	self properties at: #variable put: value
1346    ]
1347
1348    variableValue: value [
1349	"Set the value of Tk's value option for the widget."
1350
1351	<category: 'private'>
1352	self
1353	    tclEval: '%1 configure -value %3'
1354	    with: self connected
1355	    with: self container
1356	    with: value printString asTkString.
1357	self properties at: #value put: value
1358    ]
1359
1360    widgetType [
1361	<category: 'private'>
1362	^'radiobutton'
1363    ]
1364]
1365
1366
1367
1368BButton subclass: BToggle [
1369    | value variableReturn |
1370
1371    <comment: 'I represent a button whose choice can be included (by checking
1372me) or excluded (by leaving me unchecked).'>
1373    <category: 'Graphics-Windows'>
1374
1375    callback: aReceiver message: aSymbol [
1376	"Set up so that aReceiver is sent the aSymbol message (the name of
1377	 a selector accepting at most two arguments) when the receiver is
1378	 clicked.  If the method accepts two arguments, the receiver is
1379	 passed as the first parameter.  If the method accepts one or two
1380	 arguments, the state of the widget (true if it is selected, false
1381	 if it is not) is passed as the last parameter."
1382
1383	<category: 'accessing'>
1384	| arguments selector numArgs |
1385	selector := aSymbol asSymbol.
1386	numArgs := selector numArgs.
1387	arguments := #().
1388	numArgs = 1 ifTrue: [arguments := {nil}].
1389	numArgs = 2
1390	    ifTrue:
1391		[arguments :=
1392			{self.
1393			nil}].
1394	callback := DirectedMessage
1395		    selector: selector
1396		    arguments: arguments
1397		    receiver: aReceiver
1398    ]
1399
1400    invokeCallback [
1401	"Generate a synthetic callback."
1402
1403	<category: 'accessing'>
1404	self callback isNil ifTrue: [^self].
1405	self callback arguments size > 0
1406	    ifTrue:
1407		[self callback arguments at: self callback arguments size put: self value].
1408	super invokeCallback
1409    ]
1410
1411    value [
1412	"Answer whether the button is in a selected (checked) state."
1413
1414	<category: 'accessing'>
1415	self tclEval: 'return ${var' , self connected , '}'.
1416	^self tclResult = '1'
1417    ]
1418
1419    value: aBoolean [
1420	"Set whether the button is in a selected (checked) state and
1421	 generates a callback accordingly."
1422
1423	<category: 'accessing'>
1424	aBoolean
1425	    ifTrue: [self tclEval: 'set var' , self connected , ' 1']
1426	    ifFalse: [self tclEval: 'set var' , self connected , ' 0']
1427    ]
1428
1429    variable: value [
1430	"Set the value of Tk's variable option for the widget."
1431
1432	<category: 'accessing'>
1433	self
1434	    tclEval: '%1 configure -variable %3'
1435	    with: self connected
1436	    with: self container
1437	    with: value asTkString.
1438	self properties at: #variable put: value
1439    ]
1440
1441    initialize: parentWidget [
1442	<category: 'private'>
1443	| variable |
1444	super initialize: parentWidget.
1445	self tclEval: self connected , ' configure -anchor nw'.
1446	self tclEval: 'variable var' , self connected.
1447	self variable: 'var' , self connected.
1448	self backgroundColor: parentWidget backgroundColor
1449    ]
1450
1451    widgetType [
1452	<category: 'private'>
1453	^'checkbutton'
1454    ]
1455]
1456
1457
1458
1459BPrimitive subclass: BImage [
1460
1461    <comment: 'I can display colorful images.'>
1462    <category: 'Graphics-Windows'>
1463
1464    BImage class >> downArrow [
1465	"Answer the XPM representation of a 12x12 arrow pointing downwards."
1466
1467	<category: 'arrows'>
1468	^'/* XPM */
1469static char * downarrow_xpm[] = {
1470/* width height ncolors chars_per_pixel */
1471"12 12 2 1",
1472/* colors */
1473" 	c None    m None   s None",
1474"o	c black   m black",
1475/* pixels */
1476"            ",
1477"            ",
1478"            ",
1479"            ",
1480"  ooooooo   ",
1481"   ooooo    ",
1482"    ooo     ",
1483"     o      ",
1484"            ",
1485"            ",
1486"            ",
1487"            "};
1488'
1489    ]
1490
1491    BImage class >> leftArrow [
1492	"Answer the XPM representation of a 12x12 arrow pointing leftwards."
1493
1494	<category: 'arrows'>
1495	^'/* XPM */
1496static char * leftarrow_xpm[] = {
1497/* width height ncolors chars_per_pixel */
1498"12 12 2 1",
1499/* colors */
1500" 	c None    m None   s None",
1501"o	c black   m black",
1502/* pixels */
1503"            ",
1504"            ",
1505"       o    ",
1506"      oo    ",
1507"     ooo    ",
1508"    oooo    ",
1509"     ooo    ",
1510"      oo    ",
1511"       o    ",
1512"            ",
1513"            ",
1514"            "};
1515'
1516    ]
1517
1518    BImage class >> upArrow [
1519	"Answer the XPM representation of a 12x12 arrow pointing upwards."
1520
1521	<category: 'arrows'>
1522	^'/* XPM */
1523static char * uparrow_xpm[] = {
1524/* width height ncolors chars_per_pixel */
1525"12 12 2 1",
1526/* colors */
1527" 	c None    m None   s None",
1528"o	c black   m black",
1529/* pixels */
1530"            ",
1531"            ",
1532"            ",
1533"            ",
1534"     o      ",
1535"    ooo     ",
1536"   ooooo    ",
1537"  ooooooo   ",
1538"            ",
1539"            ",
1540"            ",
1541"            "};
1542'
1543    ]
1544
1545    BImage class >> rightArrow [
1546	"Answer the XPM representation of a 12x12 arrow pointing rightwards."
1547
1548	<category: 'arrows'>
1549	^'/* XPM */
1550static char * rightarrow_xpm[] = {
1551/* width height ncolors chars_per_pixel */
1552"12 12 2 1",
1553/* colors */
1554" 	c None    m None   s None",
1555"o	c black   m black",
1556/* pixels */
1557"            ",
1558"            ",
1559"    o       ",
1560"    oo      ",
1561"    ooo     ",
1562"    oooo    ",
1563"    ooo     ",
1564"    oo      ",
1565"    o       ",
1566"            ",
1567"            ",
1568"            "};
1569'
1570    ]
1571
1572    BImage class >> gnu [
1573	"Answer the XPM representation of a 48x48 GNU."
1574
1575	<category: 'GNU'>
1576	^'/* XPM */
1577/*****************************************************************************/
1578/* GNU Emacs bitmap conv. to pixmap by Przemek Klosowski (przemek@nist.gov)  */
1579/*****************************************************************************/
1580static char * image_name [] = {
1581/* width height ncolors chars_per_pixel */
1582"48 48 7 1",
1583/* colors */
1584" 	s mask	c none",
1585"B      c blue",
1586"x      c black",
1587":      c SandyBrown",
1588"+      c SaddleBrown",
1589"o      c grey",
1590".      c white",
1591/* pixels */
1592"                                                ",
1593"                                   x            ",
1594"                                    :x          ",
1595"                                    :::x        ",
1596"                                      ::x       ",
1597"          x                             ::x     ",
1598"         x:                xxx          :::x    ",
1599"        x:           xxx xxx:xxx         x::x   ",
1600"       x::       xxxx::xxx:::::xx        x::x   ",
1601"      x::       x:::::::xx::::::xx       x::x   ",
1602"      x::      xx::::::::x:::::::xx     xx::x   ",
1603"     x::      xx::::::::::::::::::x    xx::xx   ",
1604"    x::x     xx:::::xxx:::::::xxx:xxx xx:::xx   ",
1605"   x:::x    xx:::::xx...xxxxxxxxxxxxxxx:::xx    ",
1606"   x:::x   xx::::::xx..xxx...xxxx...xxxxxxxx    ",
1607"   x:::x   x::::::xx.xxx.......x.x.......xxxx   ",
1608"   x:::xx x:::x::xx.xx..........x.xx.........x  ",
1609"   x::::xx::xx:::x.xx....ooooxoxoxoo.xxx.....x  ",
1610"   xx::::xxxx::xx.xx.xxxx.ooooooo.xxx    xxxx   ",
1611"    xx::::::::xx..x.xxx..ooooooooo.xx           ",
1612"    xxx:::::xxx..xx.xx.xx.xxx.ooooo.xx          ",
1613"      xxx::xx...xx.xx.BBBB..xxooooooxx          ",
1614"       xxxx.....xx.xxBB:BB.xxoooooooxx          ",
1615"        xx.....xx...x.BBBx.xxxooooooxx          ",
1616"       x....xxxx..xx...xxxooooooooooxx          ",
1617"       x..xxxxxx..x.......x..ooooooooxx         ",
1618"       x.x xxx.x.x.x...xxxx.oooooooooxx         ",
1619"        x  xxx.x.x.xx...xx..oooooooooxx         ",
1620"          xx.x..x.x.xx........oooooooox         ",
1621"         xxo.xx.x.x.x.x.......ooooooooox        ",
1622"         xxo..xxxx..x...x.......ooooooox        ",
1623"         xxoo.xx.x..xx...x.......ooo.xxx        ",
1624"         xxoo..x.x.x.x.x.xx.xxxxx.o.xx+xx       ",
1625"         xxoo..x.xx..xx.x.x.x+++xxxxx+++x       ",
1626"         xxooo.x..xxx.x.x.x.x+++++xxx+xxx       ",
1627"          xxoo.xx..x..xx.xxxx++x+++x++xxx       ",
1628"          xxoo..xx.xxx.xxx.xxx++xx+x++xx        ",
1629"           xxooo.xx.xx..xx.xxxx++x+++xxx        ",
1630"           xxooo.xxx.xx.xxxxxxxxx++++xxx        ",
1631"            xxoo...xx.xx.xxxxxx++xxxxxxx        ",
1632"            xxoooo..x..xxx..xxxx+++++xx         ",
1633"             xxoooo..x..xx..xxxx++++xx          ",
1634"              xxxooooox.xx.xxxxxxxxxxx          ",
1635"               xxxooooo..xxx    xxxxx           ",
1636"                xxxxooooxxxx                    ",
1637"                  xxxoooxxx                     ",
1638"                    xxxxx                       ",
1639"                                                "
1640};'
1641    ]
1642
1643    BImage class >> exclaim [
1644	"Answer the XPM representation of a 32x32 exclamation mark icon."
1645
1646	<category: 'icons'>
1647	^'/* XPM */
1648static char * exclaim_xpm[] = {
1649/* width height ncolors chars_per_pixel */
1650"32 32 6 1",
1651/* colors */
1652" 	c None    m None   s None",
1653".	c yellow  m white",
1654"X	c black   m black",
1655"x	c gray50  m black",
1656"o	c gray    m white",
1657"b	c yellow4 m black",
1658/* pixels */
1659"             bbb                ",
1660"            b..oX               ",
1661"           b....oXx             ",
1662"           b.....Xxx            ",
1663"          b......oXxx           ",
1664"          b.......Xxx           ",
1665"         b........oXxx          ",
1666"         b.........Xxx          ",
1667"        b..........oXxx         ",
1668"        b...oXXXo...Xxx         ",
1669"       b....XXXXX...oXxx        ",
1670"       b....XXXXX....Xxx        ",
1671"      b.....XXXXX....oXxx       ",
1672"      b.....XXXXX.....Xxx       ",
1673"     b......XXXXX.....oXxx      ",
1674"     b......bXXXb......Xxx      ",
1675"    b.......oXXXo......oXxx     ",
1676"    b........XXX........Xxx     ",
1677"   b.........bXb........oXxx    ",
1678"   b.........oXo.........Xxx    ",
1679"  b...........X..........oXxx   ",
1680"  b.......................Xxx   ",
1681" b...........oXXo.........oXxx  ",
1682" b...........XXXX..........Xxx  ",
1683"b............XXXX..........oXxx ",
1684"b............oXXo...........Xxx ",
1685"b...........................Xxxx",
1686"b..........................oXxxx",
1687" b........................oXxxxx",
1688"  bXXXXXXXXXXXXXXXXXXXXXXXXxxxxx",
1689"    xxxxxxxxxxxxxxxxxxxxxxxxxxx ",
1690"     xxxxxxxxxxxxxxxxxxxxxxxxx  "};
1691'
1692    ]
1693
1694    BImage class >> info [
1695	"Answer the XPM representation of a 32x32 `information' icon."
1696
1697	<category: 'icons'>
1698	^'/* XPM */
1699static char * info_xpm[] = {
1700/* width height ncolors chars_per_pixel */
1701"32 32 6 1",
1702/* colors */
1703" 	c None    m None   s None",
1704".	c white   m white",
1705"X	c black   m black",
1706"x	c gray50  m black",
1707"o	c gray    m white",
1708"b	c blue    m black",
1709/* pixels */
1710"           xxxxxxxx             ",
1711"        xxxo......oxxx          ",
1712"      xxo............oxx        ",
1713"     xo................ox       ",
1714"    x.......obbbbo.......X      ",
1715"   x........bbbbbb........X     ",
1716"  x.........bbbbbb.........X    ",
1717" xo.........obbbbo.........oX   ",
1718" x..........................Xx  ",
1719"xo..........................oXx ",
1720"x..........bbbbbbb...........Xx ",
1721"x............bbbbb...........Xxx",
1722"x............bbbbb...........Xxx",
1723"x............bbbbb...........Xxx",
1724"x............bbbbb...........Xxx",
1725"xo...........bbbbb..........oXxx",
1726" x...........bbbbb..........Xxxx",
1727" xo..........bbbbb.........oXxxx",
1728"  x........bbbbbbbbb.......Xxxx ",
1729"   X......................Xxxxx ",
1730"    X....................Xxxxx  ",
1731"     Xo................oXxxxx   ",
1732"      XXo............oXXxxxx    ",
1733"       xXXXo......oXXXxxxxx     ",
1734"        xxxXXXo...Xxxxxxxx      ",
1735"          xxxxX...Xxxxxx        ",
1736"             xX...Xxx           ",
1737"               X..Xxx           ",
1738"                X.Xxx           ",
1739"                 XXxx           ",
1740"                  xxx           ",
1741"                   xx           "};
1742'
1743    ]
1744
1745    BImage class >> question [
1746	"Answer the XPM representation of a 32x32 question mark icon."
1747
1748	<category: 'icons'>
1749	^'/* XPM */
1750static char * question_xpm[] = {
1751/* width height ncolors chars_per_pixel */
1752"32 32 6 1",
1753/* colors */
1754" 	c None    m None   s None",
1755".	c white   m white",
1756"X	c black   m black",
1757"x	c gray50  m black",
1758"o	c gray    m white",
1759"b	c blue    m black",
1760/* pixels */
1761"           xxxxxxxx             ",
1762"        xxxo......oxxx          ",
1763"      xxo............oxx        ",
1764"     xo................ox       ",
1765"    x....................X      ",
1766"   x.......obbbbbbo.......X     ",
1767"  x.......obo..bbbbo.......X    ",
1768" xo.......bb....bbbb.......oX   ",
1769" x........bbbb..bbbb........Xx  ",
1770"xo........bbbb.obbbb........oXx ",
1771"x.........obbo.bbbb..........Xx ",
1772"x.............obbb...........Xxx",
1773"x.............bbb............Xxx",
1774"x.............bbo............Xxx",
1775"x.............bb.............Xxx",
1776"xo..........................oXxx",
1777" x...........obbo...........Xxxx",
1778" xo..........bbbb..........oXxxx",
1779"  x..........bbbb..........Xxxx ",
1780"   X.........obbo.........Xxxxx ",
1781"    X....................Xxxxx  ",
1782"     Xo................oXxxxx   ",
1783"      XXo............oXXxxxx    ",
1784"       xXXXo......oXXXxxxxx     ",
1785"        xxxXXXo...Xxxxxxxx      ",
1786"          xxxxX...Xxxxxx        ",
1787"             xX...Xxx           ",
1788"               X..Xxx           ",
1789"                X.Xxx           ",
1790"                 XXxx           ",
1791"                  xxx           ",
1792"                   xx           "};
1793'
1794    ]
1795
1796    BImage class >> stop [
1797	"Answer the XPM representation of a 32x32 `critical stop' icon."
1798
1799	<category: 'icons'>
1800	^'/* XPM */
1801static char * stop_xpm[] = {
1802/* width height ncolors chars_per_pixel */
1803"32 32 5 1",
1804/* colors */
1805" 	c None    m None   s None",
1806".	c red     m white",
1807"o	c DarkRed m black",
1808"X	c white   m black",
1809"x	c gray50  m black",
1810/* pixels */
1811"           oooooooo             ",
1812"        ooo........ooo          ",
1813"       o..............o         ",
1814"     oo................oo       ",
1815"    o....................o      ",
1816"   o......................o     ",
1817"   o......................ox    ",
1818"  o......X..........X......ox   ",
1819" o......XXX........XXX......o   ",
1820" o.....XXXXX......XXXXX.....ox  ",
1821" o......XXXXX....XXXXX......oxx ",
1822"o........XXXXX..XXXXX........ox ",
1823"o.........XXXXXXXXXX.........ox ",
1824"o..........XXXXXXXX..........oxx",
1825"o...........XXXXXX...........oxx",
1826"o...........XXXXXX...........oxx",
1827"o..........XXXXXXXX..........oxx",
1828"o.........XXXXXXXXXX.........oxx",
1829"o........XXXXX..XXXXX........oxx",
1830" o......XXXXX....XXXXX......oxxx",
1831" o.....XXXXX......XXXXX.....oxxx",
1832" o......XXX........XXX......oxx ",
1833"  o......X..........X......oxxx ",
1834"   o......................oxxxx ",
1835"   o......................oxxx  ",
1836"    o....................oxxx   ",
1837"     oo................ooxxxx   ",
1838"      xo..............oxxxxx    ",
1839"       xooo........oooxxxxx     ",
1840"         xxooooooooxxxxxx       ",
1841"          xxxxxxxxxxxxxx        ",
1842"             xxxxxxxx           "};
1843'
1844    ]
1845
1846    BImage class >> new: parent data: aString [
1847	"Answer a new BImage widget laid inside the given parent widget,
1848	 loading data from the given string (Base-64 encoded GIF, XPM,
1849	 PPM are supported)."
1850
1851	<category: 'instance creation'>
1852	^(self new: parent)
1853	    data: aString;
1854	    yourself
1855    ]
1856
1857    BImage class >> new: parent image: aFileStream [
1858	"Answer a new BImage widget laid inside the given parent widget,
1859	 loading data from the given file (GIF, XPM, PPM are supported)."
1860
1861	<category: 'instance creation'>
1862	^(self new: parent)
1863	    image: aFileStream;
1864	    yourself
1865    ]
1866
1867    BImage class >> new: parent size: aPoint [
1868	"Answer a new BImage widget laid inside the given parent widget,
1869	 showing by default a transparent image of aPoint size."
1870
1871	<category: 'instance creation'>
1872	^(self new: parent)
1873	    displayWidth: aPoint x;
1874	    displayHeight: aPoint y;
1875	    blank;
1876	    yourself
1877    ]
1878
1879    BImage class >> directory [
1880	"Answer the Base-64 GIF representation of a `directory folder' icon."
1881
1882	<category: 'small icons'>
1883	^'R0lGODdhEAAQAPIAAAAAAHh4eLi4uPj4+P///wAAAAAAAAAAACwAAAAAEAAQAAADPkixzPOD
1884yADrWE8qC8WN0+BZAmBq1GMOqwigXFXCrGk/cxjjr27fLtout6n9eMIYMTXsFZsogXRKJf6u
1885P0kCADv/'
1886    ]
1887
1888    BImage class >> file [
1889	"Answer the Base-64 GIF representation of a `file' icon."
1890
1891	<category: 'small icons'>
1892	^'R0lGODdhEAAQAPIAAAAAAHh4eLi4uPj4APj4+P///wAAAAAAACwAAAAAEAAQAAADPVi63P4w
1893LkKCtTTnUsXwQqBtAfh910UU4ugGAEucpgnLNY3Gop7folwNOBOeiEYQ0acDpp6pGAFArVqt
1894hQQAO///'
1895    ]
1896
1897    backgroundColor [
1898	"Answer the value of the backgroundColor option for the widget.
1899
1900	 Specifies the normal background color to use when displaying the widget."
1901
1902	<category: 'accessing'>
1903	self properties at: #background ifPresent: [:value | ^value].
1904	self
1905	    tclEval: '%1 cget -background'
1906	    with: self connected
1907	    with: self container.
1908	^self properties at: #background put: self tclResult
1909    ]
1910
1911    backgroundColor: value [
1912	"Set the value of the backgroundColor option for the widget.
1913
1914	 Specifies the normal background color to use when displaying the widget."
1915
1916	<category: 'accessing'>
1917	self
1918	    tclEval: '%1 configure -background %3'
1919	    with: self connected
1920	    with: self container
1921	    with: value asTkString.
1922	self properties at: #background put: value
1923    ]
1924
1925    displayHeight [
1926	"Answer the value of the displayHeight option for the widget.
1927
1928	 Specifies the height of the image in pixels. This is not the height of the
1929	 widget, but specifies the area of the widget that will be taken by the image."
1930
1931	<category: 'accessing'>
1932	self properties at: #displayHeight ifPresent: [:value | ^value].
1933	self
1934	    tclEval: 'img%1 cget -width'
1935	    with: self connected
1936	    with: self container.
1937	^self properties at: #displayHeight put: self tclResult asNumber
1938    ]
1939
1940    displayHeight: value [
1941	"Set the value of the displayHeight option for the widget.
1942
1943	 Specifies the height of the image in pixels. This is not the height of the
1944	 widget, but specifies the area of the widget that will be taken by the image."
1945
1946	<category: 'accessing'>
1947	self
1948	    tclEval: 'img%1 configure -width %3'
1949	    with: self connected
1950	    with: self container
1951	    with: value asFloat printString asTkString.
1952	self properties at: #displayHeight put: value
1953    ]
1954
1955    displayWidth [
1956	"Answer the value of the displayWidth option for the widget.
1957
1958	 Specifies the width of the image in pixels. This is not the width of the
1959	 widget, but specifies the area of the widget that will be taken by the image."
1960
1961	<category: 'accessing'>
1962	self properties at: #displayWidth ifPresent: [:value | ^value].
1963	self
1964	    tclEval: 'img%1 cget -width'
1965	    with: self connected
1966	    with: self container.
1967	^self properties at: #displayWidth put: self tclResult asNumber
1968    ]
1969
1970    displayWidth: value [
1971	"Set the value of the displayWidth option for the widget.
1972
1973	 Specifies the width of the image in pixels. This is not the width of the
1974	 widget, but specifies the area of the widget that will be taken by the image."
1975
1976	<category: 'accessing'>
1977	self
1978	    tclEval: 'img%1 configure -width %3'
1979	    with: self connected
1980	    with: self container
1981	    with: value asFloat printString asTkString.
1982	self properties at: #displayWidth put: value
1983    ]
1984
1985    foregroundColor [
1986	"Answer the value of the foregroundColor option for the widget.
1987
1988	 Specifies the normal foreground color to use when displaying the widget."
1989
1990	<category: 'accessing'>
1991	self properties at: #foreground ifPresent: [:value | ^value].
1992	self
1993	    tclEval: '%1 cget -foreground'
1994	    with: self connected
1995	    with: self container.
1996	^self properties at: #foreground put: self tclResult
1997    ]
1998
1999    foregroundColor: value [
2000	"Set the value of the foregroundColor option for the widget.
2001
2002	 Specifies the normal foreground color to use when displaying the widget."
2003
2004	<category: 'accessing'>
2005	self
2006	    tclEval: '%1 configure -foreground %3'
2007	    with: self connected
2008	    with: self container
2009	    with: value asTkString.
2010	self properties at: #foreground put: value
2011    ]
2012
2013    gamma [
2014	"Answer the value of the gamma option for the widget.
2015
2016	 Specifies that the colors allocated for displaying the image widget
2017	 should be corrected for a non-linear display with the specified gamma exponent
2018	 value. (The intensity produced by most CRT displays is a power function
2019	 of the input value, to a good approximation; gamma is the exponent and
2020	 is typically around 2). The value specified must be greater than zero. The
2021	 default value is one (no correction). In general, values greater than one
2022	 will make the image lighter, and values less than one will make it darker."
2023
2024	<category: 'accessing'>
2025	self properties at: #gamma ifPresent: [:value | ^value].
2026	self
2027	    tclEval: 'img%1 cget -gamma'
2028	    with: self connected
2029	    with: self container.
2030	^self properties at: #gamma put: self tclResult asNumber
2031    ]
2032
2033    gamma: value [
2034	"Set the value of the gamma option for the widget.
2035
2036	 Specifies that the colors allocated for displaying the image widget
2037	 should be corrected for a non-linear display with the specified gamma exponent
2038	 value. (The intensity produced by most CRT displays is a power function
2039	 of the input value, to a good approximation; gamma is the exponent and
2040	 is typically around 2). The value specified must be greater than zero. The
2041	 default value is one (no correction). In general, values greater than one
2042	 will make the image lighter, and values less than one will make it darker."
2043
2044	<category: 'accessing'>
2045	self
2046	    tclEval: 'img%1 configure -gamma %3'
2047	    with: self connected
2048	    with: self container
2049	    with: value asFloat printString asTkString.
2050	self properties at: #gamma put: value
2051    ]
2052
2053    blank [
2054	"Blank the corresponding image"
2055
2056	<category: 'image management'>
2057	self tclEval: 'img' , self connected , ' blank'
2058    ]
2059
2060    data: aString [
2061	"Set the image to be drawn to aString, which can be a GIF
2062	 in Base-64 representation or an X pixelmap."
2063
2064	<category: 'image management'>
2065	self tclEval: 'img' , self connected , ' configure -data '
2066		    , aString asTkImageString
2067    ]
2068
2069    dither [
2070	"Recalculate the dithered image in the window where the
2071	 image is displayed.  The dithering algorithm used in
2072	 displaying images propagates quantization errors from
2073	 one pixel to its neighbors.  If the image data is supplied
2074	 in pieces, the dithered image may not be exactly correct.
2075	 Normally the difference is not noticeable, but if it is a
2076	 problem, this command can be used to fix it."
2077
2078	<category: 'image management'>
2079	self tclEval: 'img' , self connected , ' redither'
2080    ]
2081
2082    fillFrom: origin extent: extent color: color [
2083	"Fill a rectangle with the given origin and extent, using
2084	 the given color."
2085
2086	<category: 'image management'>
2087	self
2088	    fillFrom: origin
2089	    to: origin + extent
2090	    color: color
2091    ]
2092
2093    fillFrom: origin to: corner color: color [
2094	"Fill a rectangle between the given corners, using
2095	 the given color."
2096
2097	<category: 'image management'>
2098	self
2099	    tclEval: 'img%1 put { %2 } -to %3 %4'
2100	    with: self connected
2101	    with: color
2102	    with: origin x printString , ' ' , origin y printString
2103	    with: corner x printString , ' ' , corner y printString
2104    ]
2105
2106    fillRectangle: rectangle color: color [
2107	"Fill a rectangle having the given bounding box, using
2108	 the given color."
2109
2110	<category: 'image management'>
2111	self
2112	    fillFrom: rectangle origin
2113	    to: rectangle corner
2114	    color: color
2115    ]
2116
2117    image: aFileStream [
2118	"Read a GIF or XPM image from aFileStream.  The whole contents
2119	 of the file are read, not only from the file position."
2120
2121	<category: 'image management'>
2122	self
2123	    tclEval: 'img' , self connected , ' read ' , aFileStream name asTkString
2124    ]
2125
2126    imageHeight [
2127	"Specifies the height of the image, in pixels.  This option is useful
2128	 primarily in situations where you wish to build up the contents of
2129	 the image piece by piece.  A value of zero (the default) allows the
2130	 image to expand or shrink vertically to fit the data stored in it."
2131
2132	<category: 'image management'>
2133	self tclEval: 'image height img' , self connected.
2134	^self tclResult asInteger
2135    ]
2136
2137    imageWidth [
2138	"Specifies the width of the image, in pixels.  This option is useful
2139	 primarily in situations where you wish to build up the contents of
2140	 the image piece by piece.  A value of zero (the default) allows the
2141	 image to expand or shrink horizontally to fit the data stored in it."
2142
2143	<category: 'image management'>
2144	self tclEval: 'image width img' , self connected.
2145	^self tclResult asInteger
2146    ]
2147
2148    lineFrom: origin extent: extent color: color [
2149	"Draw a line with the given origin and extent, using
2150	 the given color."
2151
2152	<category: 'image management'>
2153	self
2154	    lineFrom: origin
2155	    to: origin + extent
2156	    color: color
2157    ]
2158
2159    lineFrom: origin to: corner color: color [
2160	<category: 'image management'>
2161	self notYetImplemented
2162    ]
2163
2164    lineFrom: origin toX: endX color: color [
2165	"Draw an horizontal line between the given corners, using
2166	 the given color."
2167
2168	<category: 'image management'>
2169	self
2170	    tclEval: 'img%1 put { %2 } -to %3 %4'
2171	    with: self connected
2172	    with: color
2173	    with: origin x printString , ' ' , origin y printString
2174	    with: endX printString , ' ' , origin y printString
2175    ]
2176
2177    lineInside: rectangle color: color [
2178	"Draw a line having the given bounding box, using
2179	 the given color."
2180
2181	<category: 'image management'>
2182	self
2183	    lineFrom: rectangle origin
2184	    to: rectangle corner
2185	    color: color
2186    ]
2187
2188    lineFrom: origin toY: endY color: color [
2189	"Draw a vertical line between the given corners, using
2190	 the given color."
2191
2192	<category: 'image management'>
2193	self
2194	    tclEval: 'img%1 put { %2 } -to %3 %4'
2195	    with: self connected
2196	    with: color
2197	    with: origin x printString , ' ' , origin y printString
2198	    with: origin x printString , ' ' , endY printString
2199    ]
2200
2201    destroyed [
2202	"Private - The receiver has been destroyed, clear the corresponding
2203	 Tcl image to avoid memory leaks."
2204
2205	<category: 'widget protocol'>
2206	primitive isNil
2207	    ifFalse: [self tclEval: 'image delete img' , self connected].
2208	super destroyed
2209    ]
2210
2211    create [
2212	<category: 'private'>
2213	self tclEval: 'image create photo img' , self connected.
2214	self create: '-anchor nw -image img' , self connected
2215    ]
2216
2217    setInitialSize [
2218	"Make the Tk placer's status, the receiver's properties and the
2219	 window status (as returned by winfo) consistent. Occupy the
2220	 area indicated by the widget itself, at the top left corner"
2221
2222	<category: 'private'>
2223	self x: 0 y: 0
2224    ]
2225
2226    widgetType [
2227	<category: 'private'>
2228	^'label'
2229    ]
2230]
2231
2232
2233
2234BViewport subclass: BList [
2235    | labels items callback |
2236
2237    <comment: 'I represent a list box from which you can choose one or more
2238elements.'>
2239    <category: 'Graphics-Windows'>
2240
2241    add: anObject afterIndex: index [
2242	"Add an element with the given value after another element whose
2243	 index is contained in the index parameter.  The label displayed
2244	 in the widget is anObject's displayString.  Answer anObject."
2245
2246	<category: 'accessing'>
2247	^self
2248	    add: nil
2249	    element: anObject
2250	    afterIndex: index
2251    ]
2252
2253    add: aString element: anObject afterIndex: index [
2254	"Add an element with the aString label after another element whose
2255	 index is contained in the index parameter.  This method allows
2256	 the client to decide autonomously the label that the widget will
2257	 display.
2258
2259	 If anObject is nil, then string is used as the element as well.
2260	 If aString is nil, then the element's displayString is used as
2261	 the label.
2262
2263	 Answer anObject or, if it is nil, aString."
2264
2265	<category: 'accessing'>
2266	| elem label |
2267	label := aString isNil ifTrue: [anObject displayString] ifFalse: [aString].
2268	elem := anObject isNil ifTrue: [aString] ifFalse: [anObject].
2269	labels isNil
2270	    ifTrue:
2271		[index > 0
2272		    ifTrue: [^SystemExceptions.IndexOutOfRange signalOn: self withIndex: index].
2273		labels := OrderedCollection with: label.
2274		items := OrderedCollection with: elem]
2275	    ifFalse:
2276		[labels add: label afterIndex: index.
2277		items add: elem afterIndex: index].
2278	self tclEval: self connected , ' insert ' , index printString , ' '
2279		    , label asTkString.
2280	^elem
2281    ]
2282
2283    addLast: anObject [
2284	"Add an element with the given value at the end of the listbox.
2285	 The label displayed in the widget is anObject's displayString.
2286	 Answer anObject."
2287
2288	<category: 'accessing'>
2289	^self
2290	    add: nil
2291	    element: anObject
2292	    afterIndex: items size
2293    ]
2294
2295    addLast: aString element: anObject [
2296	"Add an element with the given value at the end of the listbox.
2297	 This method allows the client to decide autonomously the label
2298	 that the widget will display.
2299
2300	 If anObject is nil, then string is used as the element as well.
2301	 If aString is nil, then the element's displayString is used as
2302	 the label.
2303
2304	 Answer anObject or, if it is nil, aString."
2305
2306	<category: 'accessing'>
2307	^self
2308	    add: aString
2309	    element: anObject
2310	    afterIndex: items size
2311    ]
2312
2313    associationAt: anIndex [
2314	"Answer an association whose key is the item at the given position
2315	 in the listbox and whose value is the label used to display that
2316	 item."
2317
2318	<category: 'accessing'>
2319	^(items at: anIndex) -> (labels at: anIndex)
2320    ]
2321
2322    at: anIndex [
2323	"Answer the element displayed at the given position in the list
2324	 box."
2325
2326	<category: 'accessing'>
2327	^items at: anIndex
2328    ]
2329
2330    backgroundColor [
2331	"Answer the value of the backgroundColor option for the widget.
2332
2333	 Specifies the normal background color to use when displaying the widget."
2334
2335	<category: 'accessing'>
2336	self properties at: #background ifPresent: [:value | ^value].
2337	self
2338	    tclEval: '%1 cget -background'
2339	    with: self connected
2340	    with: self container.
2341	^self properties at: #background put: self tclResult
2342    ]
2343
2344    backgroundColor: value [
2345	"Set the value of the backgroundColor option for the widget.
2346
2347	 Specifies the normal background color to use when displaying the widget."
2348
2349	<category: 'accessing'>
2350	self
2351	    tclEval: '%1 configure -background %3'
2352	    with: self connected
2353	    with: self container
2354	    with: value asTkString.
2355	self properties at: #background put: value
2356    ]
2357
2358    contents: elementList [
2359	"Set the elements displayed in the listbox, and set the labels
2360	 to be their displayStrings."
2361
2362	<category: 'accessing'>
2363	| newLabels |
2364	newLabels := elementList collect: [:each | each displayString].
2365	^self contents: newLabels elements: elementList
2366    ]
2367
2368    contents: stringCollection elements: elementList [
2369	"Set the elements displayed in the listbox to be those in elementList,
2370	 and set the labels to be the corresponding elements in stringCollection.
2371	 The two collections must have the same size."
2372
2373	<category: 'accessing'>
2374	| stream |
2375	(elementList notNil and: [elementList size ~= stringCollection size])
2376	    ifTrue:
2377		[^self
2378		    error: 'label collection must have the same size as element collection'].
2379	labels := stringCollection isNil
2380		    ifTrue:
2381			[elementList asOrderedCollection collect: [:each | each displayString]]
2382		    ifFalse: [stringCollection asOrderedCollection].
2383	items := elementList isNil
2384		    ifTrue: [labels copy]
2385		    ifFalse: [elementList asOrderedCollection].
2386	self tclEval: self connected , ' delete 0 end'.
2387	stream := WriteStream on: (String new: 1000).
2388	stream
2389	    nextPutAll: self connected;
2390	    nextPutAll: ' insert 0'.
2391	stringCollection do:
2392		[:each |
2393		stream space.
2394		stream nextPutAll: each asTkString].
2395	self tclEval: stream contents
2396    ]
2397
2398    do: aBlock [
2399	"Iterate over each element of the listbox and pass it to aBlock."
2400
2401	<category: 'accessing'>
2402	items do: aBlock
2403    ]
2404
2405    elements [
2406	"Answer the collection of objects that represent the elements
2407	 displayed by the list box."
2408
2409	<category: 'accessing'>
2410	^items copy
2411    ]
2412
2413    elements: elementList [
2414	"Set the elements displayed in the listbox, and set the labels
2415	 to be their displayStrings."
2416
2417	<category: 'accessing'>
2418	| newLabels |
2419	newLabels := elementList collect: [:each | each displayString].
2420	^self contents: newLabels elements: elementList
2421    ]
2422
2423    font [
2424	"Answer the value of the font option for the widget.
2425
2426	 Specifies the font to use when drawing text inside the widget. The font
2427	 can be given as either an X font name or a Blox font description string.
2428
2429	 X font names are given as many fields, each led by a minus, and each of
2430	 which can be replaced by an * to indicate a default value is ok:
2431	 foundry, family, weight, slant, setwidth, addstyle, pixel size, point size
2432	 (the same as pixel size for historical reasons), horizontal resolution,
2433	 vertical resolution, spacing, width, charset and character encoding.
2434
2435	 Blox font description strings have three fields, which must be separated by
2436	 a space and of which only the first is mandatory: the font family, the font
2437	 size in points (or in pixels if a negative value is supplied), and a number
2438	 of styles separated by a space (valid styles are normal, bold, italic,
2439	 underline and overstrike). Examples of valid fonts are ``Helvetica 10 Bold'',
2440	 ``Times -14'', ``Futura Bold Underline''.  You must enclose the font family
2441	 in braces if it is made of two or more words."
2442
2443	<category: 'accessing'>
2444	self properties at: #font ifPresent: [:value | ^value].
2445	self
2446	    tclEval: '%1 cget -font'
2447	    with: self connected
2448	    with: self container.
2449	^self properties at: #font put: self tclResult
2450    ]
2451
2452    font: value [
2453	"Set the value of the font option for the widget.
2454
2455	 Specifies the font to use when drawing text inside the widget. The font
2456	 can be given as either an X font name or a Blox font description string.
2457
2458	 X font names are given as many fields, each led by a minus, and each of
2459	 which can be replaced by an * to indicate a default value is ok:
2460	 foundry, family, weight, slant, setwidth, addstyle, pixel size, point size
2461	 (the same as pixel size for historical reasons), horizontal resolution,
2462	 vertical resolution, spacing, width, charset and character encoding.
2463
2464	 Blox font description strings have three fields, which must be separated by
2465	 a space and of which only the first is mandatory: the font family, the font
2466	 size in points (or in pixels if a negative value is supplied), and a number
2467	 of styles separated by a space (valid styles are normal, bold, italic,
2468	 underline and overstrike). Examples of valid fonts are ``Helvetica 10 Bold'',
2469	 ``Times -14'', ``Futura Bold Underline''.  You must enclose the font family
2470	 in braces if it is made of two or more words."
2471
2472	<category: 'accessing'>
2473	self
2474	    tclEval: '%1 configure -font %3'
2475	    with: self connected
2476	    with: self container
2477	    with: value asTkString.
2478	self properties at: #font put: value
2479    ]
2480
2481    foregroundColor [
2482	"Answer the value of the foregroundColor option for the widget.
2483
2484	 Specifies the normal foreground color to use when displaying the widget."
2485
2486	<category: 'accessing'>
2487	self properties at: #foreground ifPresent: [:value | ^value].
2488	self
2489	    tclEval: '%1 cget -foreground'
2490	    with: self connected
2491	    with: self container.
2492	^self properties at: #foreground put: self tclResult
2493    ]
2494
2495    foregroundColor: value [
2496	"Set the value of the foregroundColor option for the widget.
2497
2498	 Specifies the normal foreground color to use when displaying the widget."
2499
2500	<category: 'accessing'>
2501	self
2502	    tclEval: '%1 configure -foreground %3'
2503	    with: self connected
2504	    with: self container
2505	    with: value asTkString.
2506	self properties at: #foreground put: value
2507    ]
2508
2509    highlightBackground [
2510	"Answer the value of the highlightBackground option for the widget.
2511
2512	 Specifies the background color to use when displaying selected items
2513	 in the widget."
2514
2515	<category: 'accessing'>
2516	self properties at: #selectbackground ifPresent: [:value | ^value].
2517	self
2518	    tclEval: '%1 cget -selectbackground'
2519	    with: self connected
2520	    with: self container.
2521	^self properties at: #selectbackground put: self tclResult
2522    ]
2523
2524    highlightBackground: value [
2525	"Set the value of the highlightBackground option for the widget.
2526
2527	 Specifies the background color to use when displaying selected items
2528	 in the widget."
2529
2530	<category: 'accessing'>
2531	self
2532	    tclEval: '%1 configure -selectbackground %3'
2533	    with: self connected
2534	    with: self container
2535	    with: value asTkString.
2536	self properties at: #selectbackground put: value
2537    ]
2538
2539    highlightForeground [
2540	"Answer the value of the highlightForeground option for the widget.
2541
2542	 Specifies the foreground color to use when displaying selected items
2543	 in the widget."
2544
2545	<category: 'accessing'>
2546	self properties at: #selectforeground ifPresent: [:value | ^value].
2547	self
2548	    tclEval: '%1 cget -selectforeground'
2549	    with: self connected
2550	    with: self container.
2551	^self properties at: #selectforeground put: self tclResult
2552    ]
2553
2554    highlightForeground: value [
2555	"Set the value of the highlightForeground option for the widget.
2556
2557	 Specifies the foreground color to use when displaying selected items
2558	 in the widget."
2559
2560	<category: 'accessing'>
2561	self
2562	    tclEval: '%1 configure -selectforeground %3'
2563	    with: self connected
2564	    with: self container
2565	    with: value asTkString.
2566	self properties at: #selectforeground put: value
2567    ]
2568
2569    index [
2570	"Answer the value of the index option for the widget.
2571
2572	 Indicates the element that has the location cursor. This item will be
2573	 displayed in the highlightForeground color, and with the corresponding
2574	 background color."
2575
2576	<category: 'accessing'>
2577	self properties at: #index ifPresent: [:value | ^value].
2578	self
2579	    tclEval: '%1 index active'
2580	    with: self connected
2581	    with: self container.
2582	^self properties at: #index put: self tclResult asInteger
2583    ]
2584
2585    indexAt: point [
2586	"Answer the index of the element that covers the point in the
2587	 listbox window specified by x and y (in pixel coordinates).  If no
2588	 element covers that point, then the closest element to that point
2589	 is used."
2590
2591	<category: 'accessing'>
2592	self
2593	    tclEval: self connected , ' index @%1,%2'
2594	    with: point x printString
2595	    with: point y printString.
2596	^self tclResult asInteger + 1
2597    ]
2598
2599    isSelected: index [
2600	"Answer whether the element indicated by index is currently selected."
2601
2602	<category: 'accessing'>
2603	self tclEval: self connected , ' selection includes ' , index printString.
2604	^self tclResult = '1'
2605    ]
2606
2607    label [
2608	"Return nil, it is here for Gtk+ support"
2609
2610	<category: 'accessing'>
2611	^nil
2612    ]
2613
2614    label: aString [
2615	"Do nothing, it is here for Gtk+ support"
2616
2617	<category: 'accessing'>
2618
2619    ]
2620
2621    labelAt: anIndex [
2622	"Answer the label displayed at the given position in the list
2623	 box."
2624
2625	<category: 'accessing'>
2626	^labels at: anIndex
2627    ]
2628
2629    labels [
2630	"Answer the labels displayed by the list box."
2631
2632	<category: 'accessing'>
2633	^labels copy
2634    ]
2635
2636    labelsDo: aBlock [
2637	"Iterate over each listbox element's label and pass it to aBlock."
2638
2639	<category: 'accessing'>
2640	labels do: aBlock
2641    ]
2642
2643    mode [
2644	"Answer the value of the mode option for the widget.
2645
2646	 Specifies one of several styles for manipulating the selection. The value
2647	 of the option may be either single, browse, multiple, or extended.
2648
2649	 If the selection mode is single or browse, at most one element can be selected in
2650	 the listbox at once. Clicking button 1 on an unselected element selects it and
2651	 deselects any other selected item, while clicking on a selected element
2652	 has no effect. In browse mode it is also possible to drag the selection
2653	 with button 1. That is, moving the mouse while button 1 is pressed keeps
2654	 the item under the cursor selected.
2655
2656	 If the selection mode is multiple or extended, any number of elements may be
2657	 selected at once, including discontiguous ranges. In multiple mode, clicking button
2658	 1 on an element toggles its selection state without affecting any other elements.
2659	 In extended mode, pressing button 1 on an element selects it, deselects
2660	 everything else, and sets the anchor to the element under the mouse; dragging the
2661	 mouse with button 1 down extends the selection to include all the elements between
2662	 the anchor and the element under the mouse, inclusive.
2663
2664	 In extended mode, the selected range can be adjusted by pressing button 1
2665	 with the Shift key down: this modifies the selection to consist of the elements
2666	 between the anchor and the element under the mouse, inclusive. The
2667	 un-anchored end of this new selection can also be dragged with the button
2668	 down. Also in extended mode, pressing button 1 with the Control key down starts a
2669	 toggle operation: the anchor is set to the element under the mouse, and its
2670	 selection state is reversed. The selection state of other elements is not
2671	 changed. If the mouse is dragged with button 1 down, then the selection
2672	 state of all elements between the anchor and the element under the mouse is
2673	 set to match that of the anchor element; the selection state of all other
2674	 elements remains what it was before the toggle operation began.
2675
2676	 Most people will probably want to use browse mode for single selections and
2677	 extended mode for multiple selections; the other modes appear to be useful only in
2678	 special situations."
2679
2680	<category: 'accessing'>
2681	self properties at: #selectmode ifPresent: [:value | ^value].
2682	self
2683	    tclEval: '%1 cget -selectmode'
2684	    with: self connected
2685	    with: self container.
2686	^self properties at: #selectmode put: self tclResult asSymbol
2687    ]
2688
2689    mode: value [
2690	"Set the value of the mode option for the widget.
2691
2692	 Specifies one of several styles for manipulating the selection. The value
2693	 of the option may be either single, browse, multiple, or extended.
2694
2695	 If the selection mode is single or browse, at most one element can be selected in
2696	 the listbox at once. Clicking button 1 on an unselected element selects it and
2697	 deselects any other selected item, while clicking on a selected element
2698	 has no effect. In browse mode it is also possible to drag the selection
2699	 with button 1. That is, moving the mouse while button 1 is pressed keeps
2700	 the item under the cursor selected.
2701
2702	 If the selection mode is multiple or extended, any number of elements may be
2703	 selected at once, including discontiguous ranges. In multiple mode, clicking button
2704	 1 on an element toggles its selection state without affecting any other elements.
2705	 In extended mode, pressing button 1 on an element selects it, deselects
2706	 everything else, and sets the anchor to the element under the mouse; dragging the
2707	 mouse with button 1 down extends the selection to include all the elements between
2708	 the anchor and the element under the mouse, inclusive.
2709
2710	 In extended mode, the selected range can be adjusted by pressing button 1
2711	 with the Shift key down: this modifies the selection to consist of the elements
2712	 between the anchor and the element under the mouse, inclusive. The
2713	 un-anchored end of this new selection can also be dragged with the button
2714	 down. Also in extended mode, pressing button 1 with the Control key down starts a
2715	 toggle operation: the anchor is set to the element under the mouse, and its
2716	 selection state is reversed. The selection state of other elements is not
2717	 changed. If the mouse is dragged with button 1 down, then the selection
2718	 state of all elements between the anchor and the element under the mouse is
2719	 set to match that of the anchor element; the selection state of all other
2720	 elements remains what it was before the toggle operation began.
2721
2722	 Most people will probably want to use browse mode for single selections and
2723	 extended mode for multiple selections; the other modes appear to be useful only in
2724	 special situations."
2725
2726	<category: 'accessing'>
2727	self
2728	    tclEval: '%1 configure -selectmode %3'
2729	    with: self connected
2730	    with: self container
2731	    with: value asTkString.
2732	self properties at: #selectmode put: value
2733    ]
2734
2735    numberOfStrings [
2736	"Answer the number of items in the list box"
2737
2738	<category: 'accessing'>
2739	^labels size
2740    ]
2741
2742    removeAtIndex: index [
2743	"Remove the item at the given index in the list box, answering
2744	 the object associated to the element (i.e. the value that #at:
2745	 would have returned for the given index)"
2746
2747	<category: 'accessing'>
2748	| result |
2749	labels removeAtIndex: index.
2750	result := items removeAtIndex: index.
2751	self tclEval: self connected , 'delete ' , index printString.
2752	^result
2753    ]
2754
2755    size [
2756	"Answer the number of items in the list box"
2757
2758	<category: 'accessing'>
2759	^labels size
2760    ]
2761
2762    itemSelected: receiver at: index [
2763	<category: 'private - examples'>
2764	stdout
2765	    nextPutAll: 'List item ';
2766	    print: index;
2767	    nextPutAll: ' selected!';
2768	    nl.
2769	stdout
2770	    nextPutAll: 'Contents: ';
2771	    nextPutAll: (items at: index);
2772	    nl
2773    ]
2774
2775    create [
2776	<category: 'private'>
2777	self
2778	    create: '-highlightthickness 0 -takefocus 1 \
2779		 -exportselection no -font {'
2780			, self class defaultFont , '}';
2781	    horizontal: true;
2782	    vertical: true.
2783
2784	"Tcl hack to get the callback upon activate. See analogous
2785	 trick for text boxes in BText>>#initialize:."
2786	self
2787	    tclEval: '
2788      rename %1 .%1
2789      bind %1 <<ListboxSelect>> { callback %2 invokeCallback: [%1 index active] }
2790      proc %1 args {
2791	if [regexp {^activate} [lindex $args 0]] {
2792	  callback %2 invokeCallback: [%1 index [lindex $args 1]]
2793	}
2794	uplevel .%1 $args
2795      }'
2796	    with: self connected
2797	    with: self asOop printString
2798    ]
2799
2800    initialize: parentWidget [
2801	<category: 'private'>
2802	super initialize: parentWidget.
2803	self properties at: #index put: nil.
2804	labels := OrderedCollection new
2805    ]
2806
2807    invokeCallback: indexString [
2808	<category: 'private'>
2809	| index |
2810	items isNil ifTrue: [^self].
2811	index := indexString asInteger.
2812	self properties at: #index put: index + 1.
2813	self invokeCallback
2814    ]
2815
2816    widgetType [
2817	<category: 'private'>
2818	^'listbox'
2819    ]
2820
2821    callback [
2822	"Answer a DirectedMessage that is sent when the active item in
2823	 the receiver changes, or nil if none has been set up."
2824
2825	<category: 'widget protocol'>
2826	^callback
2827    ]
2828
2829    callback: aReceiver message: aSymbol [
2830	"Set up so that aReceiver is sent the aSymbol message (the name of
2831	 a selector with at most two arguemtnts) when the active item in
2832	 the receiver changegs.  If the method accepts two arguments, the
2833	 receiver is  passed as the first parameter.  If the method accepts
2834	 one or two arguments, the selected index is passed as the last
2835	 parameter."
2836
2837	<category: 'widget protocol'>
2838	| arguments selector numArgs |
2839	selector := aSymbol asSymbol.
2840	numArgs := selector numArgs.
2841	arguments := #().
2842	numArgs = 1 ifTrue: [arguments := {nil}].
2843	numArgs = 2
2844	    ifTrue:
2845		[arguments :=
2846			{self.
2847			nil}].
2848	callback := DirectedMessage
2849		    selector: selector
2850		    arguments: arguments
2851		    receiver: aReceiver
2852    ]
2853
2854    highlight: index [
2855	"Highlight the item at the given position in the listbox."
2856
2857	<category: 'widget protocol'>
2858	index = self index ifTrue: [^self].
2859	(self mode = #single or: [self mode = #browse]) ifTrue: [self unhighlight].
2860	self select: index
2861    ]
2862
2863    invokeCallback [
2864	"Generate a synthetic callback."
2865
2866	<category: 'widget protocol'>
2867	self callback notNil
2868	    ifTrue:
2869		[self callback arguments isEmpty
2870		    ifFalse:
2871			[self callback arguments at: self callback arguments size
2872			    put: (self properties at: #index)].
2873		self callback send]
2874    ]
2875
2876    select: index [
2877	"Highlight the item at the given position in the listbox,
2878	 without unhighlighting other items.  This is meant for
2879	 multiple- or extended-mode listboxes, but can be used
2880	 with other selection mode in particular cases."
2881
2882	<category: 'widget protocol'>
2883	self properties at: #index put: index.
2884	self
2885	    tclEval: '%1 selection set %2
2886	%1 activate %2
2887	%1 see %2'
2888	    with: self connected
2889	    with: (index - 1) printString
2890    ]
2891
2892    show: index [
2893	"Ensure that the item at the given position in the listbox is
2894	 visible."
2895
2896	<category: 'widget protocol'>
2897	self tclEval: self connected , ' see ' , (index - 1) printString
2898    ]
2899
2900    unhighlight [
2901	"Unhighlight all the items in the listbox."
2902
2903	<category: 'widget protocol'>
2904	self tclEval: self connected , ' selection clear 0 end'
2905    ]
2906
2907    unselect: index [
2908	"Unhighlight the item at the given position in the listbox,
2909	 without affecting the state of the other items."
2910
2911	<category: 'widget protocol'>
2912	self
2913	    tclEval: self connected , ' selection clear ' , (index - 1) printString
2914    ]
2915]
2916
2917
2918
2919BForm subclass: BWindow [
2920    | isMapped callback x y width height |
2921
2922    <comment: 'I am the boss. Nothing else could be viewed or interacted with if
2923it wasn''t for me... )):->'>
2924    <category: 'Graphics-Windows'>
2925
2926    TopLevel := nil.
2927    Grab := nil.
2928
2929    BWindow class >> initializeOnStartup [
2930	<category: 'private - initialization'>
2931	self tclEval: 'wm withdraw .'.
2932	TopLevel := OrderedCollection new.
2933	Grab := nil
2934    ]
2935
2936    BWindow class >> new [
2937	"Answer a new top-level window."
2938
2939	<category: 'instance creation'>
2940	^TopLevel add: (super new: nil)
2941    ]
2942
2943    BWindow class >> new: label [
2944	"Answer a new top-level window with `label' as its title bar caption."
2945
2946	<category: 'instance creation'>
2947	^self new label: label
2948    ]
2949
2950    BWindow class >> popup: initializationBlock [
2951	<category: 'instance creation'>
2952	self shouldNotImplement
2953    ]
2954
2955    callback [
2956	"Answer a DirectedMessage that is sent to verify whether the
2957	 receiver must be destroyed when the user asks to unmap it."
2958
2959	<category: 'accessing'>
2960	^callback
2961    ]
2962
2963    callback: aReceiver message: aSymbol [
2964	"Set up so that aReceiver is sent the aSymbol message (the name of
2965	 a zero- or one-argument selector) when the user asks to unmap the
2966	 receiver.  If the method accepts an argument, the receiver is passed.
2967
2968	 If the method returns true, the window and its children are
2969	 destroyed (which is the default action, taken if no callback is
2970	 set up).  If the method returns false, the window is left in
2971	 place."
2972
2973	<category: 'accessing'>
2974	| arguments selector numArgs |
2975	selector := aSymbol asSymbol.
2976	numArgs := selector numArgs.
2977	arguments := #().
2978	numArgs = 1 ifTrue: [arguments := Array with: self].
2979	callback := DirectedMessage
2980		    selector: selector
2981		    arguments: arguments
2982		    receiver: aReceiver
2983    ]
2984
2985    invokeCallback [
2986	"Generate a synthetic callback, destroying the window if no
2987	 callback was set up or if the callback method answers true."
2988
2989	<category: 'accessing'>
2990	| result |
2991	result := self callback isNil or: [self callback send].
2992	result ifTrue: [self destroy].
2993	isMapped := result not
2994    ]
2995
2996    label [
2997	"Answer the value of the label option for the widget.
2998
2999	 Specifies a string to be displayed inside the widget. The way in which the
3000	 string is displayed depends on the particular widget and may be determined
3001	 by other options, such as anchor. For windows, this is the title of the window."
3002
3003	<category: 'accessing'>
3004	self properties at: #label ifPresent: [:value | ^value].
3005	self
3006	    tclEval: 'wm title %1'
3007	    with: self connected
3008	    with: self container.
3009	^self properties at: #label put: self tclResult
3010    ]
3011
3012    label: value [
3013	"Set the value of the label option for the widget.
3014
3015	 Specifies a string to be displayed inside the widget. The way in which the
3016	 string is displayed depends on the particular widget and may be determined
3017	 by other options, such as anchor. For windows, this is the title of the window."
3018
3019	<category: 'accessing'>
3020	self
3021	    tclEval: 'wm title %1 %3'
3022	    with: self connected
3023	    with: self container
3024	    with: value asTkString.
3025	self properties at: #label put: value
3026    ]
3027
3028    menu: value [
3029	"Set the value of the menu option for the widget.
3030
3031	 Specifies a menu widget to be used as a menubar. On the Macintosh, the
3032	 menubar will be displayed accross the top of the main monitor. On Microsoft
3033	 Windows and all UNIX platforms, the menu will appear accross the toplevel
3034	 window as part of the window dressing maintained by the window manager."
3035
3036	<category: 'accessing'>
3037	self
3038	    tclEval: '%1 configure -menu %3'
3039	    with: self connected
3040	    with: self container
3041	    with: value container asTkString.
3042	self properties at: #menu put: value
3043    ]
3044
3045    resizable [
3046	"Answer the value of the resizable option for the widget.
3047
3048	 Answer whether the user can be resize the window or not. If resizing is
3049	 disabled, then the window's size will be the size from the most recent
3050	 interactive resize or geometry-setting method. If there has been no such
3051	 operation then the window's natural size will be used."
3052
3053	<category: 'accessing'>
3054	self properties at: #resizable ifPresent: [:value | ^value].
3055	self
3056	    tclEval: 'wm resizable %1'
3057	    with: self connected
3058	    with: self container.
3059	^self properties at: #resizable put: self tclResult = '{1 1}'
3060    ]
3061
3062    resizable: value [
3063	"Set the value of the resizable option for the widget.
3064
3065	 Answer whether the user can be resize the window or not. If resizing is
3066	 disabled, then the window's size will be the size from the most recent
3067	 interactive resize or geometry-setting method. If there has been no such
3068	 operation then the window's natural size will be used."
3069
3070	<category: 'accessing'>
3071	self
3072	    tclEval: 'wm resizable %1 %3 %3'
3073	    with: self connected
3074	    with: self container
3075	    with: value asCBooleanValue printString asTkString.
3076	self properties at: #resizable put: value
3077    ]
3078
3079    cacheWindowSize [
3080	<category: 'private'>
3081	| stream |
3082	self tclEval: 'update; wm geometry ' , self container.
3083	stream := ReadStream on: self tclResult.
3084	width := (stream upTo: $x) asInteger.
3085	height := (stream upTo: $+) asInteger.
3086	x := (stream upTo: $+) asInteger.
3087	y := stream upToEnd asInteger
3088    ]
3089
3090    create [
3091	<category: 'private'>
3092	self create: '-takefocus 0'
3093    ]
3094
3095    create: options [
3096	<category: 'private'>
3097	super create: options.
3098	self isMapped: false.
3099	self
3100	    bind: '<Configure>'
3101	    to: #resized
3102	    of: self
3103	    parameters: ''.
3104	self
3105	    tclEval: '
3106	wm withdraw %1
3107	wm protocol %1 WM_DELETE_WINDOW { callback %2 invokeCallback }'
3108	    with: self connected
3109	    with: self asOop printString
3110    ]
3111
3112    destroyed [
3113	"Private - The receiver has been destroyed, remove it from the
3114	 list of toplevel windows to avoid memory leaks."
3115
3116	<category: 'private'>
3117	super destroyed.
3118	TopLevel remove: self ifAbsent: []
3119    ]
3120
3121    isMapped: aBoolean [
3122	<category: 'private'>
3123	isMapped := aBoolean
3124    ]
3125
3126    resetGeometry: pattern x: xPos y: yPos width: xSize height: ySize [
3127	<category: 'private'>
3128	| s mapped |
3129	(x = xPos and: [y = yPos and: [width = xSize and: [height = ySize]]])
3130	    ifTrue: [^self].
3131	s := WriteStream on: (String new: 50).
3132	(mapped := self isMapped)
3133	    ifTrue:
3134		[s
3135		    nextPutAll: 'wm withdraw ' , self connected;
3136		    nl.
3137		self isMapped: false].
3138	s
3139	    nextPutAll: 'wm geometry ';
3140	    nextPutAll: self connected;
3141	    space;
3142	    nextPutAll: pattern;
3143	    nl;
3144	    nextPutAll: 'update'.
3145	self
3146	    tclEval: s contents
3147	    with: xSize printString
3148	    with: ySize printString
3149	    with: xPos printString
3150	    with: yPos printString.
3151	x := xPos.
3152	y := yPos.
3153	width := xSize.
3154	height := ySize.
3155	mapped ifTrue: [self map]
3156    ]
3157
3158    resized [
3159	<category: 'private'>
3160	self isMapped ifFalse: [^self].
3161	x := y := width := height := nil
3162    ]
3163
3164    setInitialSize [
3165	<category: 'private'>
3166	self
3167	    x: 20
3168	    y: 20
3169	    width: 300
3170	    height: 300
3171    ]
3172
3173    widgetType [
3174	<category: 'private'>
3175	^'toplevel'
3176    ]
3177
3178    center [
3179	"Center the window in the screen"
3180
3181	<category: 'widget protocol'>
3182	| screenSize |
3183	screenSize := Blox screenSize.
3184	self x: screenSize x // 2 - (self width // 2)
3185	    y: screenSize y // 2 - (self height // 2)
3186    ]
3187
3188    centerIn: view [
3189	"Center the window in the given widget"
3190
3191	<category: 'widget protocol'>
3192	self x: view x + (view width // 2) - (self parent width // 2)
3193	    y: view x + (view height // 2) - (self parent height // 2)
3194    ]
3195
3196    height [
3197	"Answer the height of the window, as deduced from the geometry
3198	 that the window manager imposed on the window."
3199
3200	<category: 'widget protocol'>
3201	height isNil ifTrue: [self cacheWindowSize].
3202	^height
3203    ]
3204
3205    height: anInteger [
3206	"Ask the window manager to give the given height to the window."
3207
3208	<category: 'widget protocol'>
3209	width isNil ifTrue: [self cacheWindowSize].
3210	self
3211	    resetGeometry: '=%1x%2'
3212	    x: x
3213	    y: y
3214	    width: width
3215	    height: anInteger
3216    ]
3217
3218    heightAbsolute [
3219	"Answer the height of the window, as deduced from the geometry
3220	 that the window manager imposed on the window."
3221
3222	<category: 'widget protocol'>
3223	height isNil ifTrue: [self cacheWindowSize].
3224	^height
3225    ]
3226
3227    heightOffset: value [
3228	<category: 'widget protocol'>
3229	self shouldNotImplement
3230    ]
3231
3232    iconify [
3233	"Map a window and in iconified state.  If a window has not been
3234	 mapped yet, this is achieved by mapping the window in withdrawn
3235	 state first, and then iconifying it."
3236
3237	<category: 'widget protocol'>
3238	self isMapped ifFalse: [self tclEval: 'wm withdraw ' , self connected].
3239	self tclEval: 'wm iconify ' , self connected.
3240	self isMapped: false
3241    ]
3242
3243    isMapped [
3244	"Answer whether the window is mapped"
3245
3246	<category: 'widget protocol'>
3247	^isMapped
3248    ]
3249
3250    isWindow [
3251	<category: 'widget protocol'>
3252	^true
3253    ]
3254
3255    map [
3256	"Map the window and bring it to the topmost position in the Z-order."
3257
3258	<category: 'widget protocol'>
3259	self isMapped ifTrue: [^self].
3260	self tclEval: '
3261	wm deiconify %1
3262	focus [ tk_focusNext %1 ]'
3263	    with: self container.
3264	self isMapped: true
3265    ]
3266
3267    modalMap [
3268	"Map the window while establishing an application-local grab for it.
3269	 An event loop is started that ends only after the window has been
3270	 destroyed.
3271
3272	 When a grab is set for a particular window, all pointer events are
3273	 restructed to the grab window and its descendants in Blox's window
3274	 hierarchy.  Whenever the pointer is within the grab window's subtree,
3275	 the pointer will behave exactly the same as if there had been no grab
3276	 grab at all and all events will be reported in the normal fashion.
3277	 When the pointer is outside the window's tree, button presses and
3278	 releases and mouse motion events are reported to the grabbing window,
3279	 and window entry and window exit events are ignored. In other words,
3280	 windows outside the grab subtree will be visible on the screen but
3281	 they will be insensitive until the grab is released.  The
3282	 tree of windows underneath the grab window can include top-level windows,
3283	 in which case all of those top-level windows and their descendants will
3284	 continue to receive mouse events during the grab.  Keyboard events (key
3285	 presses and key releases) are delivered as usual:  the window manager
3286	 controls which application receives keyboard events, and
3287	 if they are sent to any window in the grabbing application then
3288	 they are redirected to the window owning the focus."
3289
3290	<category: 'widget protocol'>
3291	| previousGrab terminate |
3292	previousGrab := Grab.
3293	Grab := self connected.
3294	self
3295	    map;
3296	    tclEval: 'grab set ' , Grab.
3297	Blox dispatchEvents: self.
3298	previousGrab isNil
3299	    ifTrue: [self tclEval: 'grab release ' , Grab]
3300	    ifFalse: [self tclEval: 'grab set ' , previousGrab].
3301	Grab := previousGrab
3302    ]
3303
3304    state [
3305	"Set the value of the state option for the window.
3306
3307	 Specifies one of four states for the window: either normal, iconic,
3308	 withdrawn, or (Windows only) zoomed."
3309
3310	<category: 'widget protocol'>
3311	self tclEval: 'wm state ' , self connected.
3312	^self tclResult asSymbol
3313    ]
3314
3315    state: aSymbol [
3316	"Raise an error. To set a BWindow's state, use #map and #unmap."
3317
3318	<category: 'widget protocol'>
3319	self error: 'To set a BWindow''s state, use #map and #unmap.'
3320    ]
3321
3322    unmap [
3323	"Unmap a window, causing it to be forgotten about by the window manager"
3324
3325	<category: 'widget protocol'>
3326	self isMapped ifFalse: [^self].
3327	self tclEval: 'wm withdraw ' , self connected.
3328	self isMapped: false
3329    ]
3330
3331    width [
3332	"Answer the width of the window, as deduced from the geometry
3333	 that the window manager imposed on the window."
3334
3335	<category: 'widget protocol'>
3336	width isNil ifTrue: [self cacheWindowSize].
3337	^width
3338    ]
3339
3340    width: anInteger [
3341	"Ask the window manager to give the given width to the window."
3342
3343	<category: 'widget protocol'>
3344	height isNil ifTrue: [self cacheWindowSize].
3345	self
3346	    resetGeometry: '=%1x%2'
3347	    x: x
3348	    y: y
3349	    width: anInteger
3350	    height: height
3351    ]
3352
3353    width: xSize height: ySize [
3354	"Ask the window manager to give the given width and height to
3355	 the window."
3356
3357	<category: 'widget protocol'>
3358	self
3359	    resetGeometry: '=%1x%2'
3360	    x: x
3361	    y: y
3362	    width: xSize
3363	    height: ySize
3364    ]
3365
3366    widthAbsolute [
3367	"Answer the width of the window, as deduced from the geometry
3368	 that the window manager imposed on the window."
3369
3370	<category: 'widget protocol'>
3371	width isNil ifTrue: [self cacheWindowSize].
3372	^width
3373    ]
3374
3375    widthOffset: value [
3376	<category: 'widget protocol'>
3377	self shouldNotImplement
3378    ]
3379
3380    window [
3381	<category: 'widget protocol'>
3382	^self
3383    ]
3384
3385    x [
3386	"Answer the x coordinate of the window's top-left corner, as
3387	 deduced from the geometry that the window manager imposed on
3388	 the window."
3389
3390	<category: 'widget protocol'>
3391	x isNil ifTrue: [self cacheWindowSize].
3392	^x
3393    ]
3394
3395    x: anInteger [
3396	"Ask the window manager to move the window's left border
3397	 to the given x coordinate, keeping the size unchanged"
3398
3399	<category: 'widget protocol'>
3400	y isNil ifTrue: [self cacheWindowSize].
3401	self
3402	    resetGeometry: '+%3+%4'
3403	    x: anInteger
3404	    y: y
3405	    width: width
3406	    height: height
3407    ]
3408
3409    x: xPos y: yPos [
3410	"Ask the window manager to move the window's top-left corner
3411	 to the given coordinates, keeping the size unchanged"
3412
3413	<category: 'widget protocol'>
3414	self
3415	    resetGeometry: '+%3+%4'
3416	    x: xPos
3417	    y: yPos
3418	    width: width
3419	    height: height
3420    ]
3421
3422    x: xPos y: yPos width: xSize height: ySize [
3423	"Ask the window manager to give the requested geometry
3424	 to the window."
3425
3426	<category: 'widget protocol'>
3427	self
3428	    resetGeometry: '=%1x%2+%3+%4'
3429	    x: xPos
3430	    y: yPos
3431	    width: xSize
3432	    height: ySize
3433    ]
3434
3435    xAbsolute [
3436	"Answer the x coordinate of the window's top-left corner, as
3437	 deduced from the geometry that the window manager imposed on
3438	 the window."
3439
3440	<category: 'widget protocol'>
3441	x isNil ifTrue: [self cacheWindowSize].
3442	^x
3443    ]
3444
3445    xOffset: value [
3446	<category: 'widget protocol'>
3447	self shouldNotImplement
3448    ]
3449
3450    y [
3451	"Answer the y coordinate of the window's top-left corner, as
3452	 deduced from the geometry that the window manager imposed on
3453	 the window."
3454
3455	<category: 'widget protocol'>
3456	y isNil ifTrue: [self cacheWindowSize].
3457	^y
3458    ]
3459
3460    y: anInteger [
3461	"Ask the window manager to move the window's left border
3462	 to the given y coordinate, keeping the size unchanged"
3463
3464	<category: 'widget protocol'>
3465	x isNil ifTrue: [self cacheWindowSize].
3466	self
3467	    resetGeometry: '+%3+%4'
3468	    x: x
3469	    y: anInteger
3470	    width: width
3471	    height: height
3472    ]
3473
3474    yAbsolute [
3475	"Answer the y coordinate of the window's top-left corner, as
3476	 deduced from the geometry that the window manager imposed on
3477	 the window."
3478
3479	<category: 'widget protocol'>
3480	y isNil ifTrue: [self cacheWindowSize].
3481	^y
3482    ]
3483
3484    yOffset: value [
3485	<category: 'widget protocol'>
3486	self shouldNotImplement
3487    ]
3488]
3489
3490
3491
3492BWindow subclass: BTransientWindow [
3493
3494    <comment: 'I am almost a boss. I represent a window which is logically linked
3495to another which sits higher in the widget hierarchy, e.g. a dialog
3496box'>
3497    <category: 'Graphics-Windows'>
3498
3499    BTransientWindow class >> new [
3500	<category: 'instance creation'>
3501	self shouldNotImplement
3502    ]
3503
3504    BTransientWindow class >> new: parentWindow [
3505	"Answer a new transient window attached to the given
3506	 parent window and with nothing in its title bar caption."
3507
3508	<category: 'instance creation'>
3509	^(self basicNew)
3510	    initialize: parentWindow;
3511	    yourself
3512    ]
3513
3514    BTransientWindow class >> new: label in: parentWindow [
3515	"Answer a new transient window attached to the given
3516	 parent window and with `label' as its title bar caption."
3517
3518	<category: 'instance creation'>
3519	^(self basicNew)
3520	    initialize: parentWindow;
3521	    label: label;
3522	    yourself
3523    ]
3524
3525    setWidgetName: parentWidget [
3526	<category: 'private'>
3527	| unique |
3528	unique := '.w' , (self asOop printString: 36).
3529	parentWidget isNil ifTrue: [^unique].
3530	^parentWidget parent isNil
3531	    ifTrue: [unique]
3532	    ifFalse: [parentWidget parent container , unique]
3533    ]
3534
3535    map [
3536	"Map the window and inform the windows manager that the
3537	 receiver is a transient window working on behalf of its
3538	 parent.  The window is also put in its parent window's
3539	 window group: the window manager might use this information,
3540	 for example, to unmap all of the windows in a group when the
3541	 group's leader is iconified."
3542
3543	<category: 'widget protocol'>
3544	super map.
3545	self parent isNil ifTrue: [^self].
3546	self
3547	    tclEval: 'wm transient ' , self connected , ' ' , self parent connected.
3548	self
3549	    tclEval: 'wm group     ' , self connected , ' ' , self parent connected
3550    ]
3551]
3552
3553
3554
3555BWindow subclass: BPopupWindow [
3556
3557    <comment: 'I am a pseudo-window that has no decorations and no ability to interact
3558with the user.  My main usage, as my name says, is to provide pop-up
3559functionality for other widgets.  Actually there should be no need to
3560directly use me - always rely on the #new and #popup: class methods.'>
3561    <category: 'Graphics-Windows'>
3562
3563    addChild: w [
3564	"Private - The widget identified by child has been added to the
3565	 receiver.  This method is public not because you can call it,
3566	 but because it can be useful to override it, not forgetting the
3567	 call to either the superclass implementation or #basicAddChild:,
3568	 to perform some initialization on the children just added. Answer
3569	 the new child."
3570
3571	<category: 'geometry management'>
3572	self tclEval: 'place forget ' , w container.
3573	self
3574	    tclEval: 'pack ' , w container , ' -fill both -side left -padx 1 -pady 1'.
3575	w onDestroySend: #destroy to: self.
3576	^self basicAddChild: w
3577    ]
3578
3579    child: child height: value [
3580	"Set the given child's height.  This is done by setting
3581	 its parent window's (that is, our) height."
3582
3583	"Only act after #addChild:"
3584
3585	<category: 'geometry management'>
3586	self childrenCount = 0 ifTrue: [^self].
3587	self tclEval: 'pack ' , child container , ' -expand 1'.
3588	self height: value
3589    ]
3590
3591    child: child heightOffset: value [
3592	<category: 'geometry management'>
3593	self shouldNotImplement
3594    ]
3595
3596    child: child width: value [
3597	"Set the given child's width.  This is done by setting
3598	 its parent window's (that is, our) width."
3599
3600	"Only act after #addChild:"
3601
3602	<category: 'geometry management'>
3603	self childrenCount = 0 ifTrue: [^self].
3604	self tclEval: 'pack ' , child container , ' -expand 1'.
3605	self width: value
3606    ]
3607
3608    child: child widthOffset: value [
3609	<category: 'geometry management'>
3610	self shouldNotImplement
3611    ]
3612
3613    child: child x: value [
3614	"Set the x coordinate of the given child's top-left corner.
3615	 This is done by setting its parent window's (that is, our) x."
3616
3617	<category: 'geometry management'>
3618	self x: value
3619    ]
3620
3621    child: child xOffset: value [
3622	<category: 'geometry management'>
3623	self shouldNotImplement
3624    ]
3625
3626    child: child y: value [
3627	"Set the y coordinate of the given child's top-left corner.
3628	 This is done by setting its parent window's (that is, our) y."
3629
3630	<category: 'geometry management'>
3631	self y: value
3632    ]
3633
3634    child: child yOffset: value [
3635	<category: 'geometry management'>
3636	self shouldNotImplement
3637    ]
3638
3639    heightChild: child [
3640	"Answer the given child's height, which is the height that
3641	 was imposed on the popup window."
3642
3643	<category: 'geometry management'>
3644	^self height
3645    ]
3646
3647    widthChild: child [
3648	"Answer the given child's width in pixels, which is the width that
3649	 was imposed on the popup window."
3650
3651	<category: 'geometry management'>
3652	^self width
3653    ]
3654
3655    xChild: child [
3656	"Answer the x coordinate of the given child's top-left corner,
3657	 which is desumed by the position of the popup window."
3658
3659	<category: 'geometry management'>
3660	^self x
3661    ]
3662
3663    yChild: child [
3664	"Answer the y coordinate of the given child's top-left corner,
3665	 which is desumed by the position of the popup window."
3666
3667	<category: 'geometry management'>
3668	^self y
3669    ]
3670
3671    create [
3672	<category: 'private'>
3673	self
3674	    create: '-takefocus 0 -background black';
3675	    tclEval: 'wm overrideredirect ' , self connected , ' 1';
3676	    resizable: false
3677    ]
3678
3679    setInitialSize [
3680	<category: 'private'>
3681	self cacheWindowSize
3682    ]
3683]
3684
3685
3686
3687BForm subclass: BDialog [
3688    | callbacks initInfo |
3689
3690    <comment: 'I am a facility for implementing dialogs with many possible choices
3691and requests. In addition I provide support for a few platform native
3692common dialog boxes, such as choose-a-file and choose-a-color.'>
3693    <category: 'Graphics-Windows'>
3694
3695    BDialog class >> new: parent [
3696	"Answer a new dialog handler (containing a label widget and
3697	 some button widgets) laid out within the given parent window.
3698	 The label widget, when it is created, is empty."
3699
3700	<category: 'instance creation'>
3701	^(self basicNew)
3702	    initInfo: '' -> nil;
3703	    initialize: parent
3704    ]
3705
3706    BDialog class >> new: parent label: aLabel [
3707	"Answer a new dialog handler (containing a label widget and
3708	 some button widgets) laid out within the given parent window.
3709	 The label widget, when it is created, contains aLabel."
3710
3711	<category: 'instance creation'>
3712	^(self basicNew)
3713	    initInfo: aLabel -> nil;
3714	    initialize: parent
3715    ]
3716
3717    BDialog class >> new: parent label: aLabel prompt: aString [
3718	"Answer a new dialog handler (containing a label widget, some
3719	 button widgets, and an edit window showing aString by default)
3720	 laid out within the given parent window.
3721	 The label widget, when it is created, contains aLabel."
3722
3723	<category: 'instance creation'>
3724	^(self basicNew)
3725	    initInfo: aLabel -> aString;
3726	    initialize: parent
3727    ]
3728
3729    BDialog class >> chooseFile: operation parent: parent label: aLabel default: name defaultExtension: ext types: typeList [
3730	<category: 'private'>
3731	| stream strictMotif file |
3732	stream := WriteStream on: String new.
3733	stream
3734	    nextPutAll: 'tk_get';
3735	    nextPutAll: operation;
3736	    nextPutAll: 'File -parent ';
3737	    nextPutAll: parent container;
3738	    nextPutAll: ' -title ';
3739	    nextPutAll: aLabel asTkString;
3740	    nextPutAll: ' -defaultextension ';
3741	    nextPutAll: ext asTkString;
3742	    nextPutAll: ' -filetypes {'.
3743	typeList do:
3744		[:each |
3745		stream
3746		    nextPut: ${;
3747		    nextPutAll: (each at: 1) asTkString;
3748		    nextPutAll: ' {'.
3749		each size > 1
3750		    ifTrue:
3751			[each
3752			    from: 2
3753			    to: each size
3754			    do:
3755				[:type |
3756				stream
3757				    nextPutAll: type;
3758				    space]].
3759		stream nextPutAll: '}} '].
3760	stream nextPutAll: '{"All files" * }}'.
3761	(name notNil and: [name notEmpty])
3762	    ifTrue:
3763		[stream
3764		    nextPutAll: ' -initialfile ';
3765		    nextPutAll: name asTkString].
3766	strictMotif := BText emacsLike.
3767	BText emacsLike: Blox platform ~= 'unix'.
3768	parent map.
3769	self tclEval: stream contents.
3770	file := self tclResult.
3771	file isEmpty ifTrue: [file := nil].
3772	BText emacsLike: strictMotif.
3773	^file
3774    ]
3775
3776    BDialog class >> chooseColor: parent label: aLabel default: color [
3777	"Prompt for a color.  The dialog box is created with the given
3778	 parent window and with aLabel as its title bar text, and initially
3779	 it selects the color given in the color parameter.
3780
3781	 If the dialog box is canceled, nil is answered, else the
3782	 selected color is returned as a String with its RGB value."
3783
3784	<category: 'prompters'>
3785	| result |
3786	parent map.
3787	self
3788	    tclEval: 'tk_chooseColor -parent %1 -title %2 -initialcolor %3'
3789	    with: parent container
3790	    with: aLabel asTkString
3791	    with: color asTkString.
3792	result := self tclResult.
3793	result isEmpty ifTrue: [result := nil].
3794	^result
3795    ]
3796
3797    BDialog class >> chooseFileToOpen: parent label: aLabel default: name defaultExtension: ext types: typeList [
3798	"Pop up a dialog box for the user to select a file to open.
3799	 Its purpose is for the user to select an existing file only.
3800	 If the user enters an non-existent file, the dialog box gives
3801	 the user an error prompt and requires the user to give an
3802	 alternative selection or to cancel the selection. If an
3803	 application allows the user to create new files, it should
3804	 do so by providing a separate New menu command.
3805
3806	 If the dialog box is canceled, nil is answered, else the
3807	 selected file name is returned as a String.
3808
3809	 The dialog box is created with the given parent window
3810	 and with aLabel as its title bar text.  The name parameter
3811	 indicates which file is initially selected, and the default
3812	 extension specifies  a string that will be appended to the
3813	 filename if the user enters a filename without an extension.
3814
3815	 The typeList parameter is an array of arrays, like
3816	 #(('Text files' '.txt' '.diz') ('Smalltalk files' '.st')),
3817	 and is used to construct a listbox of file types.  When the user
3818	 chooses a file type in the listbox, only the files of that type
3819	 are listed.  Each item in the array contains a list of strings:
3820	 the first one is the name of the file type described by a particular
3821	 file pattern, and is the text string that appears in the File types
3822	 listbox, while the other ones are the possible extensions that
3823	 belong to this particular file type."
3824
3825	"e.g.
3826	 fileName := BDialog
3827	 chooseFileToOpen: aWindow
3828	 label: 'Open file'
3829	 default: nil
3830	 defaultExtension: 'gif'
3831	 types: #(
3832	 ('Text files'       '.txt' '.diz')
3833	 ('Smalltalk files'  '.st')
3834	 ('C source files'   '.c')
3835	 ('GIF files'	'.gif'))"
3836
3837	<category: 'prompters'>
3838	^self
3839	    chooseFile: 'Open'
3840	    parent: parent
3841	    label: aLabel
3842	    default: name
3843	    defaultExtension: ext
3844	    types: typeList
3845    ]
3846
3847    BDialog class >> chooseFileToSave: parent label: aLabel default: name defaultExtension: ext types: typeList [
3848	"Pop up a dialog box for the user to select a file to save;
3849	 this differs from the file open dialog box in that non-existent
3850	 file names are accepted and existing file names trigger a
3851	 confirmation dialog box, asking the user whether the file
3852	 should be overwritten or not.
3853
3854	 If the dialog box is canceled, nil is answered, else the
3855	 selected file name is returned as a String.
3856
3857	 The dialog box is created with the given parent window
3858	 and with aLabel as its title bar text.  The name parameter
3859	 indicates which file is initially selected, and the default
3860	 extension specifies  a string that will be appended to the
3861	 filename if the user enters a filename without an extension.
3862
3863	 The typeList parameter is an array of arrays, like
3864	 #(('Text files' '.txt' '.diz') ('Smalltalk files' '.st')),
3865	 and is used to construct a listbox of file types.  When the user
3866	 chooses a file type in the listbox, only the files of that type
3867	 are listed.  Each item in the array contains a list of strings:
3868	 the first one is the name of the file type described by a particular
3869	 file pattern, and is the text string that appears in the File types
3870	 listbox, while the other ones are the possible extensions that
3871	 belong to this particular file type."
3872
3873	<category: 'prompters'>
3874	^self
3875	    chooseFile: 'Save'
3876	    parent: parent
3877	    label: aLabel
3878	    default: name
3879	    defaultExtension: ext
3880	    types: typeList
3881    ]
3882
3883    addButton: aLabel receiver: anObject index: anInt [
3884	"Add a button to the dialog box that, when clicked, will
3885	 cause the #dispatch: method to be triggered in anObject,
3886	 passing anInt as the argument of the callback.  The
3887	 caption of the button is set to aLabel."
3888
3889	<category: 'accessing'>
3890	^self
3891	    addButton: aLabel
3892	    receiver: anObject
3893	    message: #dispatch:
3894	    argument: anInt
3895    ]
3896
3897    addButton: aLabel receiver: anObject message: aSymbol [
3898	"Add a button to the dialog box that, when clicked, will
3899	 cause the aSymbol unary selector to be sent to anObject.
3900	 The caption of the button is set to aLabel."
3901
3902	<category: 'accessing'>
3903	callbacks addLast: (DirectedMessage
3904		    selector: aSymbol
3905		    arguments: #()
3906		    receiver: anObject).
3907	self addButton: aLabel
3908    ]
3909
3910    addButton: aLabel receiver: anObject message: aSymbol argument: arg [
3911	"Add a button to the dialog box that, when clicked, will
3912	 cause the aSymbol one-argument selector to be sent to anObject,
3913	 passing arg as the argument of the callback.  The
3914	 caption of the button is set to aLabel."
3915
3916	<category: 'accessing'>
3917	callbacks addLast: (DirectedMessage
3918		    selector: aSymbol
3919		    arguments: {arg}
3920		    receiver: anObject).
3921	self addButton: aLabel
3922    ]
3923
3924    contents: newText [
3925	"Display newText in the entry widget associated to the dialog box."
3926
3927	<category: 'accessing'>
3928	self tclEval: 'set var' , self connected , ' ' , newText asTkString
3929    ]
3930
3931    contents [
3932	"Answer the text that is displayed in the entry widget associated
3933	 to the dialog box."
3934
3935	<category: 'accessing'>
3936	self tclEval: 'return ${var' , self connected , '}'.
3937	^self tclResult
3938    ]
3939
3940    addButton: aLabel [
3941	<category: 'private'>
3942	self
3943	    tclEval: 'button %1.buttons.b%2 -text %3 -highlightthickness 0 -takefocus 1 -command {
3944	callback %4 "invokeCallback:" %2
3945	destroy %1
3946    }
3947    pack %1.buttons.b%2 -side left -expand 1'
3948	    with: self container
3949	    with: callbacks size printString
3950	    with: aLabel asTkString
3951	    with: self asOop printString
3952    ]
3953
3954    create [
3955	<category: 'private'>
3956	super create.
3957	self
3958	    tclEval: '
3959	label %1.msg -padx 5 -pady 5 -anchor nw -text '
3960		    , initInfo key asTkString
3961			, '
3962	place %1.msg -x 0.0 -y 0.0 -relwidth 1.0
3963	bind %1.msg <Configure> { %1.msg configure -wraplength %%w }
3964	%1.msg configure -background [ %1 cget -background ]
3965	frame %1.buttons -highlightthickness 0 -takefocus 0
3966	%1.buttons configure -background [ %1 cget -background ]
3967	place %1.buttons -anchor sw -x 0.0 -rely 1.0 -relwidth 1.0 -height 14m
3968	lower %1.buttons
3969	lower %1.msg'
3970	    with: self connected.
3971	initInfo value isNil ifTrue: [^self].
3972	self
3973	    tclEval: '
3974	set var%1 %2
3975	entry %1.text -textvariable var%1 -highlightthickness 0 -takefocus 1
3976	place %1.text -in %1.msg -x 5 -y 5 -width -10 -rely 1.0 -relwidth 1.0
3977	raise %1.text'
3978	    with: self connected
3979	    with: initInfo value asTkString
3980    ]
3981
3982    initInfo: assoc [
3983	<category: 'private'>
3984	initInfo := assoc
3985    ]
3986
3987    initialize: parentWidget [
3988	<category: 'private'>
3989	super initialize: parentWidget.
3990	callbacks := OrderedCollection new
3991    ]
3992
3993    center [
3994	"Center the dialog box's parent window in the screen"
3995
3996	<category: 'widget protocol'>
3997	self parent center
3998    ]
3999
4000    centerIn: view [
4001	"Center the dialog box's parent window in the given widget"
4002
4003	<category: 'widget protocol'>
4004	self parent centerIn: view
4005    ]
4006
4007    destroyed [
4008	"Private - The receiver has been destroyed, clear the corresponding
4009	 Tcl variable to avoid memory leaks."
4010
4011	<category: 'widget protocol'>
4012	self tclEval: 'catch { unset var' , self connected , '}'.
4013	super destroyed
4014    ]
4015
4016    invokeCallback: index [
4017	"Generate a synthetic callback corresponding to the index-th
4018	 button being pressed, and destroy the parent window (triggering
4019	 its callback if one was established)."
4020
4021	<category: 'widget protocol'>
4022	(callbacks at: index asInteger) send.
4023	self parent destroy
4024    ]
4025
4026    loop [
4027	"Map the parent window modally.  In other words, an event loop
4028	 is started that ends only after the window has been destroyed.
4029	 For more information on the treatment of events for modal windows,
4030	 refer to BWindow>>#modalMap."
4031
4032	"self parent width: (self parent width min: 200)."
4033
4034	<category: 'widget protocol'>
4035	self parent modalMap
4036    ]
4037]
4038
4039
4040
4041BMenuObject subclass: BMenuBar [
4042
4043    <comment: 'I am the Menu Bar, the top widget in a full menu structure.'>
4044    <category: 'Graphics-Windows'>
4045
4046    add: aMenu [
4047	"Add aMenu to the menu bar"
4048
4049	<category: 'accessing'>
4050	aMenu create.
4051	^self addChild: aMenu
4052    ]
4053
4054    remove: aMenu [
4055	"Remove aMenu from the menu bar"
4056
4057	<category: 'accessing'>
4058	self
4059	    tclEval: 'catch { %1 delete %2 }'
4060	    with: self connected
4061	    with: aMenu connected
4062    ]
4063
4064    connected [
4065	<category: 'private'>
4066	^primitive
4067    ]
4068
4069    container [
4070	<category: 'private'>
4071	^primitive
4072    ]
4073
4074    initialize: parentWidget [
4075	<category: 'private'>
4076	super initialize: parentWidget.
4077	primitive := self parent isNil
4078		    ifTrue: ['.popup']
4079		    ifFalse: [self parent container , '.menu'].
4080
4081	"BMenuBar is NOT a BPrimitive, so it has to explicitly create itself"
4082	self
4083	    tclEval: 'menu ' , self connected , ' -font {' , self class defaultFont
4084		    , '} -tearoff 0'.
4085	self parent isNil ifFalse: [self parent menu: self]
4086    ]
4087]
4088
4089
4090
4091BMenuObject subclass: BMenu [
4092    | label exists |
4093
4094    <comment: 'I am a Menu that is part of a menu bar.'>
4095    <category: 'Graphics-Windows'>
4096
4097    BMenu class >> new: parent label: label [
4098	"Add a new menu to the parent window's menu bar, with `label' as
4099	 its caption (for popup menus, parent is the widget over which the
4100	 menu pops up as the right button is pressed)."
4101
4102	<category: 'instance creation'>
4103	^(self basicNew)
4104	    initialize: parent;
4105	    label: label;
4106	    yourself
4107    ]
4108
4109    label [
4110	"Answer the value of the label option for the widget.
4111
4112	 Specifies a string to be displayed inside the widget. The way in which the
4113	 string is displayed depends on the particular widget and may be determined
4114	 by other options, such as anchor. For windows, this is the title of the window."
4115
4116	<category: 'accessing'>
4117	^label
4118    ]
4119
4120    label: value [
4121	"Set the value of the label option for the widget.
4122
4123	 Specifies a string to be displayed inside the widget. The way in which the
4124	 string is displayed depends on the particular widget and may be determined
4125	 by other options, such as anchor. For windows, this is the title of the window."
4126
4127	<category: 'accessing'>
4128	label := value.
4129	exists
4130	    ifTrue:
4131		[self
4132		    tclEval: ' %1 configure -title %2'
4133		    with: self connected
4134		    with: value asTkString]
4135    ]
4136
4137    addLine [
4138	"Add a separator item at the end of the menu"
4139
4140	<category: 'callback registration'>
4141	^self addMenuItemFor: #() notifying: self	"self is dummy"
4142    ]
4143
4144    addMenuItemFor: anArray notifying: receiver [
4145	"Add a menu item described by anArray at the end of the menu.
4146	 If anArray is empty, insert a separator line.  If anArray
4147	 has a single item, a menu item is created without a callback.
4148	 If anArray has two or three items, the second one is used as
4149	 the selector sent to receiver, and the third one (if present)
4150	 is passed to the selector."
4151
4152	"Receiver will be sent the callback messages.  anArray
4153	 is something that responds to at: and size.  Possible types are:
4154	 #()		insert a seperator line
4155	 #(name)	        create a menu item with name, but no callback
4156	 #(name symbol)     create a menu item with the given name and
4157	 no parameter callback.
4158	 #(name symbol arg) create a menu item with the given name and
4159	 one parameter callback."
4160
4161	<category: 'callback registration'>
4162	| item |
4163	item := self newMenuItemFor: anArray notifying: receiver.
4164	exists ifTrue: [item create]
4165    ]
4166
4167    callback: receiver using: selectorPairs [
4168	"Add menu items described by anArray at the end of the menu.
4169	 Each element of selectorPairs must be in the format described
4170	 in BMenu>>#addMenuItemFor:notifying:.  All the callbacks will
4171	 be sent to receiver."
4172
4173	<category: 'callback registration'>
4174	selectorPairs do: [:pair | self addMenuItemFor: pair notifying: receiver]
4175    ]
4176
4177    empty [
4178	"Empty the menu widget; that is, remove all the children"
4179
4180	<category: 'callback registration'>
4181	self tclEval: self connected , ' delete 0 end'.
4182	children := OrderedCollection new.
4183	childrensUnderline := nil
4184    ]
4185
4186    destroy [
4187	"Destroy the menu widget; that is, simply remove ourselves from
4188	 the parent menu bar."
4189
4190	<category: 'callback registration'>
4191	self parent remove: self
4192    ]
4193
4194    addChild: menuItem [
4195	<category: 'private'>
4196	menuItem menuIndex: self childrenCount.
4197	super addChild: menuItem.
4198	self exists ifTrue: [menuItem create].
4199	^menuItem
4200    ]
4201
4202    connected [
4203	<category: 'private'>
4204	^primitive
4205    ]
4206
4207    container [
4208	<category: 'private'>
4209	^primitive
4210    ]
4211
4212    create [
4213	<category: 'private'>
4214	| s |
4215	s := WriteStream on: (String new: 80).
4216	s
4217	    nextPutAll: 'menu ';
4218	    nextPutAll: self connected;
4219	    nextPutAll: ' -tearoff 0 -postcommand { callback ';
4220	    print: self asOop;
4221	    nextPutAll: ' invokeCallback }';
4222	    nl;
4223	    nextPutAll: self parent container;
4224	    nextPutAll: ' add cascade -label ';
4225	    nextPutAll: self label asTkString;
4226	    nextPutAll: ' -menu ';
4227	    nextPutAll: self connected;
4228	    nextPutAll: ' -underline ';
4229	    print: (self parent underline: self label).
4230	self tclEval: s contents.
4231
4232	"Set the title for torn-off menus"
4233	self label: self label.
4234	self childrenDo: [:each | each create].
4235	exists := true
4236    ]
4237
4238    exists [
4239	<category: 'private'>
4240	^exists
4241    ]
4242
4243    initialize: parentWidget [
4244	<category: 'private'>
4245	super initialize: parentWidget.
4246	label := ''.
4247	exists := false.
4248	primitive := '%1.w%2' %
4249			{self parent container.
4250			self asOop printString: 36}
4251    ]
4252
4253    newMenuItemFor: pair notifying: receiver [
4254	<category: 'private'>
4255	| item size |
4256	size := pair size.
4257	pair size = 0 ifTrue: [^BMenuItem new: self].
4258	(size >= 2 and: [pair last isArray])
4259	    ifTrue:
4260		[size := size - 1.
4261		item := BMenu new: self label: (pair at: 1).
4262		pair last
4263		    do: [:each | item add: (item newMenuItemFor: each notifying: receiver)]]
4264	    ifFalse: [item := BMenuItem new: self label: (pair at: 1)].
4265	size = 1 ifTrue: [^item].
4266	size = 2 ifTrue: [^item callback: receiver message: (pair at: 2)].
4267	^item
4268	    callback: receiver
4269	    message: (pair at: 2)
4270	    argument: (pair at: 3)
4271    ]
4272]
4273
4274
4275
4276BMenu subclass: BPopupMenu [
4277
4278    <comment: 'I am a class that provides the ability to show popup menus when the
4279right button (Button 3) is clicked on another window.'>
4280    <category: 'Graphics-Windows'>
4281
4282    PopupMenuBar := nil.
4283    PopupMenus := nil.
4284
4285    BPopupMenu class >> initializeOnStartup [
4286	<category: 'private - accessing'>
4287	PopupMenuBar := nil.
4288	PopupMenus := WeakKeyIdentityDictionary new
4289    ]
4290
4291    BPopupMenu class >> popupMenuBar [
4292	<category: 'private - accessing'>
4293	PopupMenuBar isNil ifTrue: [PopupMenuBar := BMenuBar new: nil].
4294	^PopupMenuBar
4295    ]
4296
4297    initialize: parentWindow [
4298	<category: 'private'>
4299	super initialize: self class popupMenuBar.
4300	self parent add: self.
4301	PopupMenus at: self parent ifPresent: [:menu | menu destroy].
4302	PopupMenus at: self parent put: self.
4303	parentWindow
4304	    bind: '<Button-3>'
4305	    to: #popup:y:
4306	    of: self
4307	    parameters: '%X %Y'.
4308	parentWindow
4309	    bind: '<Shift-F10>'
4310	    to: #popup:y:
4311	    of: self
4312	    parameters: '[expr 2+[winfo rootx %W]] [expr 2+[winfo rooty %W]]'
4313    ]
4314
4315    popup: x y: y [
4316	"Note that x and y are strings!"
4317
4318	<category: 'private'>
4319	self tclEval: 'tk_popup ' , self connected , ' ' , x , ' ' , y
4320    ]
4321
4322    popup [
4323	"Generate a synthetic menu popup event"
4324
4325	<category: 'widget protocol'>
4326	self tclEval: 'event generate %1 <Shift-F10>' with: self parent connected
4327    ]
4328]
4329
4330
4331
4332BMenuObject subclass: BMenuItem [
4333    | index createCode |
4334
4335    <comment: 'I am the tiny and humble Menu Item, a single command choice in the
4336menu structure. But if it wasn''t for me, nothing could be done...
4337eh eh eh!!'>
4338    <category: 'Graphics-Windows'>
4339
4340    BMenuItem class >> new: parent [
4341	"Add a new separator item to the specified menu."
4342
4343	<category: 'instance creation'>
4344	^self basicNew initialize: parent
4345    ]
4346
4347    BMenuItem class >> new: parent label: label [
4348	"Add a new menu item to the specified menu (parent) , with `label'
4349	 as its caption."
4350
4351	<category: 'instance creation'>
4352	^self basicNew initialize: parent label: label
4353    ]
4354
4355    label [
4356	"Answer the value of the label option for the widget.
4357
4358	 Specifies a string to be displayed inside the widget. The way in which the
4359	 string is displayed depends on the particular widget and may be determined
4360	 by other options, such as anchor. For windows, this is the title of the window."
4361
4362	<category: 'accessing'>
4363	^self properties at: #label
4364    ]
4365
4366    label: value [
4367	"Set the value of the label option for the widget.
4368
4369	 Specifies a string to be displayed inside the widget. The way in which the
4370	 string is displayed depends on the particular widget and may be determined
4371	 by other options, such as anchor. For windows, this is the title of the window."
4372
4373	<category: 'accessing'>
4374	(self properties at: #label) isNil
4375	    ifTrue: [^self error: 'no label for separator lines'].
4376	self parent exists
4377	    ifTrue:
4378		[self
4379		    tclEval: self container , ' entryconfigure ' , self connected , ' -label '
4380			    , value asTkString].
4381	self properties at: #label put: value
4382    ]
4383
4384    connected [
4385	<category: 'private'>
4386	^index
4387    ]
4388
4389    container [
4390	<category: 'private'>
4391	^self parent container
4392    ]
4393
4394    create [
4395	<category: 'private'>
4396	| label |
4397	label := self label ifNil: [''] ifNotNil: [:lab | lab asTkString].
4398	self
4399	    tclEval: createCode
4400	    with: label
4401	    with: self widgetType.
4402	createCode := ''	"free some memory"
4403    ]
4404
4405    initialize: parentWidget [
4406	<category: 'private'>
4407	super initialize: parentWidget.
4408	createCode := self container , ' add separator'.
4409	self properties at: #label put: nil.
4410	parent addChild: self
4411    ]
4412
4413    initialize: parentWidget label: label [
4414	<category: 'private'>
4415	| s |
4416	super initialize: parentWidget.
4417	s := WriteStream on: (String new: 80).
4418	s
4419	    nextPutAll: self container;
4420	    nextPutAll: ' add %2 -label %1 -font {' , self class defaultFont
4421			, '} -underline ';
4422	    print: (self parent underline: label);
4423	    nextPutAll: ' -command { callback ';
4424	    print: self asOop;
4425	    nextPutAll: ' invokeCallback }'.
4426	createCode := s contents.
4427	self properties at: #label put: label.
4428	parent addChild: self.
4429	parent exists ifTrue: [self create]
4430    ]
4431
4432    menuIndex: anIndex [
4433	<category: 'private'>
4434	index := anIndex printString
4435    ]
4436
4437    widgetType [
4438	<category: 'private'>
4439	^'command'
4440    ]
4441]
4442
4443
4444
4445BMenuItem subclass: BCheckMenuItem [
4446    | status |
4447
4448    <comment: 'I am a menu item which can be toggled between two states, marked
4449and unmarked.'>
4450    <category: 'Graphics-Windows'>
4451
4452    BCheckMenuItem class >> new: parent [
4453	<category: 'instance creation'>
4454	self shouldNotImplement
4455    ]
4456
4457    invokeCallback [
4458	"Generate a synthetic callback"
4459
4460	<category: 'accessing'>
4461	self properties removeKey: #value ifAbsent: [].
4462	self callback isNil ifFalse: [self callback send]
4463    ]
4464
4465    value [
4466	"Answer whether the menu item is in a selected (checked) state."
4467
4468	<category: 'accessing'>
4469	^self properties at: #value ifAbsentPut: [false]
4470    ]
4471
4472    value: aBoolean [
4473	"Set whether the button is in a selected (checked) state and
4474	 generates a callback accordingly."
4475
4476	<category: 'accessing'>
4477	self properties at: #value put: aBoolean.
4478	self tclEval: 'set ' , self variable , self valueString.
4479	self callback isNil ifFalse: [self callback send]
4480    ]
4481
4482    create [
4483	<category: 'private'>
4484	super create.
4485	self
4486	    tclEval: '%1 entryconfigure %2 -onvalue 1 -offvalue 0 -variable %3'
4487	    with: self container
4488	    with: self connected
4489	    with: self variable
4490    ]
4491
4492    destroyed [
4493	"Private - The receiver has been destroyed, clear the corresponding
4494	 Tcl variable to avoid memory leaks."
4495
4496	<category: 'private'>
4497	self tclEval: 'unset ' , self variable.
4498	super destroyed
4499    ]
4500
4501    valueString [
4502	<category: 'private'>
4503	^self value ifTrue: [' 1'] ifFalse: [' 0']
4504    ]
4505
4506    variable [
4507	<category: 'private'>
4508	^'var' , self connected , self container copyWithout: $.
4509    ]
4510
4511    widgetType [
4512	<category: 'private'>
4513	^'checkbutton'
4514    ]
4515]
4516
4517
4518
4519"-------------------------- BEdit class -----------------------------"
4520
4521
4522
4523"-------------------------- BLabel class -----------------------------"
4524
4525
4526
4527Eval [
4528    BLabel initialize
4529]
4530
4531
4532
4533"-------------------------- BButton class -----------------------------"
4534
4535
4536
4537"-------------------------- BForm class -----------------------------"
4538
4539
4540
4541"-------------------------- BContainer class -----------------------------"
4542
4543
4544
4545"-------------------------- BRadioGroup class -----------------------------"
4546
4547
4548
4549"-------------------------- BRadioButton class -----------------------------"
4550
4551
4552
4553"-------------------------- BToggle class -----------------------------"
4554
4555
4556
4557"-------------------------- BImage class -----------------------------"
4558
4559
4560
4561"-------------------------- BList class -----------------------------"
4562
4563
4564
4565"-------------------------- BWindow class -----------------------------"
4566
4567
4568
4569"-------------------------- BTransientWindow class -----------------------------"
4570
4571
4572
4573"-------------------------- BPopupWindow class -----------------------------"
4574
4575
4576
4577"-------------------------- BDialog class -----------------------------"
4578
4579
4580
4581"-------------------------- BMenuBar class -----------------------------"
4582
4583
4584
4585"-------------------------- BMenu class -----------------------------"
4586
4587
4588
4589"-------------------------- BPopupMenu class -----------------------------"
4590
4591
4592
4593"-------------------------- BMenuItem class -----------------------------"
4594
4595
4596
4597"-------------------------- BCheckMenuItem class -----------------------------"
4598
4599