1"======================================================================
2|
3|   Smalltalk Tk-based GUI building blocks (basic widget classes).
4|
5|
6 ======================================================================"
7
8"======================================================================
9|
10| Copyright 1999, 2000, 2001, 2002, 2008 Free Software Foundation, Inc.
11| Written by Paolo Bonzini and Robert Collins.
12|
13| This file is part of the GNU Smalltalk class library.
14|
15| The GNU Smalltalk class library is free software; you can redistribute it
16| and/or modify it under the terms of the GNU Lesser General Public License
17| as published by the Free Software Foundation; either version 2.1, or (at
18| your option) any later version.
19|
20| The GNU Smalltalk class library is distributed in the hope that it will be
21| useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
22| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
23| General Public License for more details.
24|
25| You should have received a copy of the GNU Lesser General Public License
26| along with the GNU Smalltalk class library; see the file COPYING.LESSER.
27| If not, write to the Free Software Foundation, 59 Temple Place - Suite
28| 330, Boston, MA 02110-1301, USA.
29|
30 ======================================================================"
31
32
33
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'.
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 create: '-anchor nw -takefocus 0'.
699	self tclEval: 'bind %1 <Configure> "+%1 configure -wraplength %%w"'
700	    with: self connected
701    ]
702
703    initialize: parentWidget [
704	<category: 'private'>
705	super initialize: parentWidget.
706	parentWidget isNil
707	    ifFalse: [self backgroundColor: parentWidget backgroundColor]
708    ]
709
710    setInitialSize [
711	"Make the Tk placer's status, the receiver's properties and the
712	 window status (as returned by winfo) consistent. Occupy the
713	 area indicated by the widget itself, at the top left corner"
714
715	<category: 'private'>
716	self x: 0 y: 0
717    ]
718
719    widgetType [
720	<category: 'private'>
721	^'label'
722    ]
723]
724
725
726
727BPrimitive subclass: BButton [
728    | callback |
729
730    <comment: 'I am a button that a user can click. In fact I am at the head
731of a small hierarchy of objects which exhibit button-like look
732and behavior'>
733    <category: 'Graphics-Windows'>
734
735    BButton class >> new: parent label: label [
736	"Answer a new BButton widget laid inside the given parent widget,
737	 showing by default the `label' String."
738
739	<category: 'instance creation'>
740	^(self new: parent)
741	    label: label;
742	    yourself
743    ]
744
745    backgroundColor [
746	"Answer the value of the backgroundColor option for the widget.
747
748	 Specifies the normal background color to use when displaying the widget."
749
750	<category: 'accessing'>
751	self properties at: #background ifPresent: [:value | ^value].
752	self
753	    tclEval: '%1 cget -background'
754	    with: self connected
755	    with: self container.
756	^self properties at: #background put: self tclResult
757    ]
758
759    backgroundColor: value [
760	"Set the value of the backgroundColor option for the widget.
761
762	 Specifies the normal background color to use when displaying the widget."
763
764	<category: 'accessing'>
765	self
766	    tclEval: '%1 configure -background %3'
767	    with: self connected
768	    with: self container
769	    with: value asTkString.
770	self properties at: #background put: value
771    ]
772
773    callback [
774	"Answer a DirectedMessage that is sent when the receiver is clicked,
775	 or nil if none has been set up."
776
777	<category: 'accessing'>
778	^callback
779    ]
780
781    callback: aReceiver message: aSymbol [
782	"Set up so that aReceiver is sent the aSymbol message (the name of
783	 a zero- or one-argument selector) when the receiver is clicked.
784	 If the method accepts an argument, the receiver is passed."
785
786	<category: 'accessing'>
787	| arguments selector numArgs |
788	selector := aSymbol asSymbol.
789	numArgs := selector numArgs.
790	arguments := #().
791	numArgs = 1 ifTrue: [arguments := Array with: self].
792	callback := DirectedMessage
793		    selector: selector
794		    arguments: arguments
795		    receiver: aReceiver
796    ]
797
798    font [
799	"Answer the value of the font option for the widget.
800
801	 Specifies the font to use when drawing text inside the widget. The font
802	 can be given as either an X font name or a Blox font description string.
803
804	 X font names are given as many fields, each led by a minus, and each of
805	 which can be replaced by an * to indicate a default value is ok:
806	 foundry, family, weight, slant, setwidth, addstyle, pixel size, point size
807	 (the same as pixel size for historical reasons), horizontal resolution,
808	 vertical resolution, spacing, width, charset and character encoding.
809
810	 Blox font description strings have three fields, which must be separated by
811	 a space and of which only the first is mandatory: the font family, the font
812	 size in points (or in pixels if a negative value is supplied), and a number
813	 of styles separated by a space (valid styles are normal, bold, italic,
814	 underline and overstrike). Examples of valid fonts are ``Helvetica 10 Bold'',
815	 ``Times -14'', ``Futura Bold Underline''.  You must enclose the font family
816	 in braces if it is made of two or more words."
817
818	<category: 'accessing'>
819	self properties at: #font ifPresent: [:value | ^value].
820	self
821	    tclEval: '%1 cget -font'
822	    with: self connected
823	    with: self container.
824	^self properties at: #font put: self tclResult
825    ]
826
827    font: value [
828	"Set the value of the font option for the widget.
829
830	 Specifies the font to use when drawing text inside the widget. The font
831	 can be given as either an X font name or a Blox font description string.
832
833	 X font names are given as many fields, each led by a minus, and each of
834	 which can be replaced by an * to indicate a default value is ok:
835	 foundry, family, weight, slant, setwidth, addstyle, pixel size, point size
836	 (the same as pixel size for historical reasons), horizontal resolution,
837	 vertical resolution, spacing, width, charset and character encoding.
838
839	 Blox font description strings have three fields, which must be separated by
840	 a space and of which only the first is mandatory: the font family, the font
841	 size in points (or in pixels if a negative value is supplied), and a number
842	 of styles separated by a space (valid styles are normal, bold, italic,
843	 underline and overstrike). Examples of valid fonts are ``Helvetica 10 Bold'',
844	 ``Times -14'', ``Futura Bold Underline''.  You must enclose the font family
845	 in braces if it is made of two or more words."
846
847	"self tclEval: '%1 configure -font %3'
848	 with: self connected
849	 with: self container
850	 with: (value  asTkString).
851	 self properties at: #font put: value"
852
853	<category: 'accessing'>
854
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 connected getLabel
901    ]
902
903    label: value [
904	"Set the value of the label option for the widget.
905
906	 Specifies a string to be displayed inside the widget. The way in which the
907	 string is displayed depends on the particular widget and may be determined
908	 by other options, such as anchor. For windows, this is the title of the window."
909
910	<category: 'accessing'>
911	self connected setLabel: value
912    ]
913
914    create [
915	<category: 'private'>
916	self connected: GTK.GtkButton new.
917	self connected
918	    connectSignal: 'clicked'
919	    to: self
920	    selector: #onClicked:data:
921	    userData: nil
922    ]
923
924    onClicked: aButton data: userData [
925	<category: 'private'>
926	self invokeCallback
927    ]
928
929    setInitialSize [
930	"Make the Tk placer's status, the receiver's properties and the
931	 window status (as returned by winfo) consistent. Occupy the
932	 area indicated by the widget itself, at the top left corner"
933
934	<category: 'private'>
935
936    ]
937]
938
939
940
941BPrimitive subclass: BForm [
942
943    <comment: 'I am used to group many widgets together.'>
944    <category: 'Graphics-Windows'>
945
946    backgroundColor [
947	"Answer the value of the backgroundColor option for the widget.
948
949	 Specifies the normal background color to use when displaying the widget."
950
951	<category: 'accessing'>
952	| style |
953	style := self container getStyle.
954	'FIXME ok, backGroundColor isn"t trivial to get' printNl
955	"self properties at: #background ifPresent: [ :value | ^value ].
956	 self tclEval: '%1 cget -background'
957	 with: self connected
958	 with: self container.
959	 ^self properties at: #background put: (self tclResult )"
960    ]
961
962    backgroundColor: value [
963	"Set the value of the backgroundColor option for the widget.
964
965	 Specifies the normal background color to use when displaying the widget."
966
967	<category: 'accessing'>
968	| color |
969	value printNl.
970	'fixme implement bg color, will need CStruct Color' printNl
971	"color:=GTK.GdkColor new.
972	 GTK.GdkColor parse: value color: color.
973	 self container modifyBg: GTK.Gtk gtkStateNormal color: (nil)"
974    ]
975
976    defaultHeight [
977	"Answer the value of the defaultHeight option for the widget.
978
979	 Specifies the desired height for the form in pixels. If this option
980	 is less than or equal to zero then the window will not request any size at all."
981
982	<category: 'accessing'>
983	self properties at: #height ifPresent: [:value | ^value].
984	self
985	    tclEval: '%1 cget -height'
986	    with: self connected
987	    with: self container.
988	^self properties at: #height put: self tclResult asNumber
989    ]
990
991    defaultHeight: value [
992	"Set the value of the defaultHeight option for the widget.
993
994	 Specifies the desired height for the form in pixels. If this option
995	 is less than or equal to zero then the window will not request any size at all."
996
997	<category: 'accessing'>
998	self
999	    tclEval: '%1 configure -height %3'
1000	    with: self connected
1001	    with: self container
1002	    with: value printString asTkString.
1003	self properties at: #height put: value
1004    ]
1005
1006    defaultWidth [
1007	"Answer the value of the defaultWidth option for the widget.
1008
1009	 Specifies the desired width for the form in pixels. If this option
1010	 is less than or equal to zero then the window will not request any size at all."
1011
1012	<category: 'accessing'>
1013	self properties at: #width ifPresent: [:value | ^value].
1014	self
1015	    tclEval: '%1 cget -width'
1016	    with: self connected
1017	    with: self container.
1018	^self properties at: #width put: self tclResult asNumber
1019    ]
1020
1021    defaultWidth: value [
1022	"Set the value of the defaultWidth option for the widget.
1023
1024	 Specifies the desired width for the form in pixels. If this option
1025	 is less than or equal to zero then the window will not request any size at all."
1026
1027	<category: 'accessing'>
1028	self
1029	    tclEval: '%1 configure -width %3'
1030	    with: self connected
1031	    with: self container
1032	    with: value printString asTkString.
1033	self properties at: #width put: value
1034    ]
1035
1036    create [
1037	<category: 'private'>
1038	self connected: GTK.GtkPlacer new
1039    ]
1040
1041    addChild: child [
1042	<category: 'private'>
1043	(self connected)
1044	    add: child container;
1045	    moveRel: child container
1046		relX: 0
1047		relY: 0.
1048	^child
1049    ]
1050
1051    child: child height: value [
1052	"Set the given child's height to value.  The default implementation of
1053	 this method uses `rubber-sheet' geometry management as explained in
1054	 the comment to BWidget's #height method.  You should not use this
1055	 method, which is automatically called by the child's #height: method,
1056	 but you might want to override it.  The child's property slots whose
1057	 name ends with `Geom' are reserved for this method. This method
1058	 should never fail -- if it doesn't apply to the kind of geometry
1059	 management that the receiver does, just do nothing."
1060
1061	<category: 'geometry'>
1062	| relative heightParent |
1063	heightParent := self height.
1064	heightParent <= 0 ifTrue: [^self].
1065	relative := value * 32767 // heightParent.
1066	relative := relative min: 32767.
1067	relative := relative max: 0.
1068	self connected
1069	    resizeRel: child container
1070	    relWidth: (child properties at: #widthGeom ifAbsent: [32767])
1071	    relHeight: (child properties at: #heightGeom put: relative)
1072    ]
1073
1074    child: child heightOffset: value [
1075	"Adjust the given child's height by a fixed amount of value pixel.  This
1076	 is meaningful for the default implementation, using `rubber-sheet'
1077	 geometry management as explained in the comment to BWidget's #height and
1078	 #heightOffset: methods.  You should not use this method, which is
1079	 automatically called by the child's #heightOffset: method, but you
1080	 might want to override it.  if it doesn't apply to the kind of
1081	 geometry management that the receiver does, just add value to the
1082	 current height of the widget."
1083
1084	<category: 'geometry'>
1085	self connected
1086	    resize: child container
1087	    width: (child properties at: #widthGeomOfs ifAbsent: [0])
1088	    height: value
1089    ]
1090
1091    child: child inset: pixels [
1092	<category: 'geometry'>
1093	^child
1094	    xOffset: self xOffset + pixels;
1095	    yOffset: self yOffset + pixels;
1096	    widthOffset: self widthOffset - (pixels * 2);
1097	    heightOffset: self heightOffset - (pixels * 2)
1098    ]
1099
1100    child: child stretch: aBoolean [
1101	"This method is only used when on the path from the receiver
1102	 to its toplevel there is a BContainer.  It decides whether child is
1103	 among the widgets that are stretched to fill the entire width of
1104	 the BContainer; if this has not been set for this widget, it
1105	 is propagated along the widget hierarchy."
1106
1107	<category: 'geometry'>
1108	self properties at: #stretch
1109	    ifAbsent:
1110		[self parent isNil ifTrue: [^self].
1111		self parent child: self stretch: aBoolean]
1112    ]
1113
1114    child: child width: value [
1115	"Set the given child's width to value.  The default implementation of
1116	 this method uses `rubber-sheet' geometry management as explained in
1117	 the comment to BWidget's #width method.  You should not use this
1118	 method, which is automatically called by the child's #width: method,
1119	 but you might want to override it.  The child's property slots whose
1120	 name ends with `Geom' are reserved for this method. This method
1121	 should never fail -- if it doesn't apply to the kind of geometry
1122	 management that the receiver does, just do nothing."
1123
1124	<category: 'geometry'>
1125	| relative widthParent |
1126	widthParent := self width.
1127	widthParent <= 0 ifTrue: [^self].
1128	relative := value * 32767 // widthParent.
1129	relative := relative min: 32767.
1130	relative := relative max: 0.
1131	self connected
1132	    resizeRel: child container
1133	    relWidth: (child properties at: #widthGeom put: relative)
1134	    relHeight: (child properties at: #widthGeom ifAbsent: [32767])
1135    ]
1136
1137    child: child widthOffset: value [
1138	"Adjust the given child's width by a fixed amount of value pixel.  This
1139	 is meaningful for the default implementation, using `rubber-sheet'
1140	 geometry management as explained in the comment to BWidget's #width and
1141	 #widthOffset: methods.  You should not use this method, which is
1142	 automatically called by the child's #widthOffset: method, but you
1143	 might want to override it.  if it doesn't apply to the kind of
1144	 geometry management that the receiver does, just add value to the
1145	 current width of the widget."
1146
1147	<category: 'geometry'>
1148	self connected
1149	    resize: child container
1150	    width: value
1151	    height: (child properties at: #widthGeomOfs ifAbsent: [0])
1152    ]
1153
1154    child: child x: value [
1155	"Set the given child's x to value.  The default implementation of
1156	 this method uses `rubber-sheet' geometry management as explained in
1157	 the comment to BWidget's #x method.  You should not use this
1158	 method, which is automatically called by the child's #x: method,
1159	 but you might want to override it.  The child's property slots whose
1160	 name ends with `Geom' are reserved for this method. This method
1161	 should never fail -- if it doesn't apply to the kind of geometry
1162	 management that the receiver does, just do nothing."
1163
1164	<category: 'geometry'>
1165	| relative widthParent |
1166	widthParent := self width.
1167	widthParent <= 0 ifTrue: [^self].
1168	relative := value * 32767 // widthParent.
1169	relative := relative min: 32767.
1170	relative := relative max: 0.
1171	self connected
1172	    moveRel: child container
1173	    relX: (child properties at: #xGeom put: relative)
1174	    relY: (child properties at: #yGeom ifAbsent: [0])
1175    ]
1176
1177    child: child xOffset: value [
1178	"Adjust the given child's x by a fixed amount of value pixel.  This
1179	 is meaningful for the default implementation, using `rubber-sheet'
1180	 geometry management as explained in the comment to BWidget's #x and
1181	 #xOffset: methods.  You should not use this method, which is
1182	 automatically called by the child's #xOffset: method, but you
1183	 might want to override it.  if it doesn't apply to the kind of
1184	 geometry management that the receiver does, just add value to the
1185	 current x of the widget."
1186
1187	<category: 'geometry'>
1188	self connected
1189	    move: child container
1190	    x: value
1191	    y: (child properties at: #yGeomOfs ifAbsent: [0])
1192    ]
1193
1194    child: child y: value [
1195	"Set the given child's y to value.  The default implementation of
1196	 this method uses `rubber-sheet' geometry management as explained in
1197	 the comment to BWidget's #y method.  You should not use this
1198	 method, which is automatically called by the child's #y: method,
1199	 but you might want to override it.  The child's property slots whose
1200	 name ends with `Geom' are reserved for this method. This method
1201	 should never fail -- if it doesn't apply to the kind of geometry
1202	 management that the receiver does, just do nothing."
1203
1204	<category: 'geometry'>
1205	| relative heightParent |
1206	heightParent := self height.
1207	heightParent <= 0 ifTrue: [^self].
1208	relative := value * 32767 // heightParent.
1209	relative := relative min: 32767.
1210	relative := relative max: 0.
1211	self connected
1212	    moveRel: child container
1213	    relX: (child properties at: #xGeom ifAbsent: [0])
1214	    relY: (child properties at: #yGeom put: relative)
1215    ]
1216
1217    child: child yOffset: value [
1218	"Adjust the given child's y by a fixed amount of value pixel.  This
1219	 is meaningful for the default implementation, using `rubber-sheet'
1220	 geometry management as explained in the comment to BWidget's #y and
1221	 #yOffset: methods.  You should not use this method, which is
1222	 automatically called by the child's #yOffset: method, but you
1223	 might want to override it.  if it doesn't apply to the kind of
1224	 geometry management that the receiver does, just add value to the
1225	 current y of the widget."
1226
1227	<category: 'geometry'>
1228	self connected
1229	    move: child container
1230	    x: (child properties at: #xGeomOfs ifAbsent: [0])
1231	    y: value
1232    ]
1233
1234    heightChild: child [
1235	"Answer the given child's height.  The default implementation of this
1236	 method uses `rubber-sheet' geometry management as explained in
1237	 the comment to BWidget's #height method.  You should not use this
1238	 method, which is automatically called by the child's #height method,
1239	 but you might want to override.  The child's property slots whose
1240	 name ends with `Geom' are reserved for this method.  This method
1241	 should never fail -- if it doesn't apply to the kind of geometry
1242	 management that the receiver does, just return 0."
1243
1244	<category: 'geometry'>
1245	^(child properties at: #heightGeom ifAbsentPut: [32767]) * self height
1246	    // 32767
1247    ]
1248
1249    widthChild: child [
1250	"Answer the given child's width.  The default implementation of this
1251	 method uses `rubber-sheet' geometry management as explained in
1252	 the comment to BWidget's #width method.  You should not use this
1253	 method, which is automatically called by the child's #width method,
1254	 but you might want to override.  The child's property slots whose
1255	 name ends with `Geom' are reserved for this method.  This method
1256	 should never fail -- if it doesn't apply to the kind of geometry
1257	 management that the receiver does, just return 0."
1258
1259	<category: 'geometry'>
1260	^(child properties at: #widthGeom ifAbsentPut: [32767]) * self width
1261	    // 32767
1262    ]
1263
1264    xChild: child [
1265	"Answer the given child's x.  The default implementation of this
1266	 method uses `rubber-sheet' geometry management as explained in
1267	 the comment to BWidget's #x method.  You should not use this
1268	 method, which is automatically called by the child's #x method,
1269	 but you might want to override.  The child's property slots whose
1270	 name ends with `Geom' are reserved for this method.  This method
1271	 should never fail -- if it doesn't apply to the kind of geometry
1272	 management that the receiver does, just return 0."
1273
1274	<category: 'geometry'>
1275	^(child properties at: #xGeom ifAbsentPut: [0]) * self width // 32767
1276    ]
1277
1278    yChild: child [
1279	"Answer the given child's y.  The default implementation of this
1280	 method uses `rubber-sheet' geometry management as explained in
1281	 the comment to BWidget's #y method.  You should not use this
1282	 method, which is automatically called by the child's #y method,
1283	 but you might want to override.  The child's property slots whose
1284	 name ends with `Geom' are reserved for this method.  This method
1285	 should never fail -- if it doesn't apply to the kind of geometry
1286	 management that the receiver does, just return 0."
1287
1288	<category: 'geometry'>
1289	^(child properties at: #yGeom ifAbsentPut: [0]) * self height // 32767
1290    ]
1291]
1292
1293
1294
1295BForm subclass: BContainer [
1296    | verticalLayout |
1297
1298    <comment: 'I am used to group many widgets together. I can perform simple
1299management by putting widgets next to each other, from left to
1300right or from top to bottom.'>
1301    <category: 'Graphics-Windows'>
1302
1303    addChild: child [
1304	"The widget identified by child has been added to the receiver.
1305	 This method is public not because you can call it, but because
1306	 it can be useful to override it to perform some initialization on
1307	 the children just added. Answer the new child."
1308
1309	<category: 'accessing'>
1310	self connected
1311	    packStart: child container
1312	    expand: false
1313	    fill: false
1314	    padding: 0.
1315	^child
1316    ]
1317
1318    setVerticalLayout: aBoolean [
1319	"Answer whether the container will align the widgets vertically or
1320	 horizontally.  Horizontal alignment means that widgets are
1321	 packed from left to right, while vertical alignment means that
1322	 widgets are packed from the top to the bottom of the widget.
1323
1324	 Widgets that are set to be ``stretched'' will share all the
1325	 space that is not allocated to non-stretched widgets.
1326
1327	 The layout of the widget can only be set before the first child
1328	 is inserted in the widget."
1329
1330	<category: 'accessing'>
1331	children isEmpty
1332	    ifFalse: [^self error: 'cannot set layout after the first child is created'].
1333	verticalLayout := aBoolean
1334    ]
1335
1336    create [
1337	<category: 'private'>
1338	self verticalLayout
1339	    ifTrue: [self connected: (GTK.GtkVBox new: false spacing: 0)]
1340	    ifFalse: [self connected: (GTK.GtkHBox new: false spacing: 0)]
1341    ]
1342
1343    verticalLayout [
1344	"answer true if objects should be laid out vertically"
1345
1346	<category: 'private'>
1347	verticalLayout isNil ifTrue: [verticalLayout := true].
1348	^verticalLayout
1349    ]
1350
1351    initialize: parentWidget [
1352	"This is called by #new: to initialize the widget (as the name
1353	 says...). The default implementation calls all the other
1354	 methods in the `customization' protocol and some private
1355	 ones that take care of making the receiver's status consistent,
1356	 so you should usually call it instead of doing everything by
1357	 hand. This method is public not because you can call it, but
1358	 because it might be useful to override it. Always answer the
1359	 receiver."
1360
1361	<category: 'private'>
1362	parent := parentWidget.
1363	properties := IdentityDictionary new.
1364	children := OrderedCollection new
1365    ]
1366
1367    child: child height: value [
1368	<category: 'private'>
1369	(child -> value -> (self heightChild: child)) printNl.
1370	^child container setSizeRequest: (self widthChild: child) height: value
1371    ]
1372
1373    child: child heightOffset: value [
1374	<category: 'private'>
1375
1376    ]
1377
1378    child: child inset: value [
1379	<category: 'private'>
1380	| stretch |
1381	stretch := child properties at: #stretchGeom ifAbsent: [false].
1382	self connected
1383	    setChildPacking: child container
1384	    expand: stretch
1385	    fill: stretch
1386	    padding: (child properties at: #paddingGeom put: value)
1387	    packType: GTK.Gtk gtkPackStart
1388    ]
1389
1390    child: child stretch: aBoolean [
1391	<category: 'private'>
1392	child properties at: #stretchGeom put: aBoolean.
1393	self connected
1394	    setChildPacking: child container
1395	    expand: aBoolean
1396	    fill: aBoolean
1397	    padding: (child properties at: #paddingGeom ifAbsent: [0])
1398	    packType: GTK.Gtk gtkPackStart
1399    ]
1400
1401    child: child width: value [
1402	<category: 'private'>
1403	^child container setSizeRequest: value height: (self heightChild: child)
1404    ]
1405
1406    child: child widthOffset: value [
1407	<category: 'private'>
1408
1409    ]
1410
1411    child: child x: value [
1412	<category: 'private'>
1413
1414    ]
1415
1416    child: child xOffset: value [
1417	<category: 'private'>
1418
1419    ]
1420
1421    child: child y: value [
1422	<category: 'private'>
1423
1424    ]
1425
1426    child: child yOffset: value [
1427	<category: 'private'>
1428
1429    ]
1430
1431    heightChild: child [
1432	<category: 'private'>
1433	^child container getSizeRequest at: 2
1434    ]
1435
1436    widthChild: child [
1437	<category: 'private'>
1438	^child container getSizeRequest at: 1
1439    ]
1440
1441    xChild: child [
1442	<category: 'private'>
1443	^child xAbsolute
1444    ]
1445
1446    yChild: child [
1447	<category: 'private'>
1448	^child yAbsolute
1449    ]
1450]
1451
1452
1453
1454BContainer subclass: BRadioGroup [
1455    | value |
1456
1457    <comment: 'I am used to group many mutually-exclusive radio buttons together.
1458In addition, just like every BContainer I can perform simple management
1459by putting widgets next to each other, from left to right or (which is
1460more useful in this particular case...) from top to bottom.'>
1461    <category: 'Graphics-Windows'>
1462
1463    value [
1464	"Answer the index of the button that is currently selected,
1465	 1 being the first button added to the radio button group.
1466	 0 means that no button is selected"
1467
1468	<category: 'accessing'>
1469	^value
1470    ]
1471
1472    value: anInteger [
1473	"Force the value-th button added to the radio button group
1474	 to be the selected one."
1475
1476	<category: 'accessing'>
1477	value = anInteger ifTrue: [^self].
1478	self childrenCount = 0 ifTrue: [^self].
1479	value = 0 ifFalse: [(children at: value) connected setActive: false].
1480	value := anInteger.
1481	anInteger = 0 ifFalse: [(children at: value) connected setActive: true]
1482    ]
1483
1484    addChild: child [
1485	<category: 'private'>
1486	super addChild: child.
1487	child assignedValue: self childrenCount.
1488	self childrenCount = 1 ifTrue: [self value: 1].
1489	child connected
1490	    connectSignal: 'toggled'
1491	    to: self
1492	    selector: #onToggle:data:
1493	    userData: self childrenCount.
1494	^child
1495    ]
1496
1497    onToggle: widget data: userData [
1498	<category: 'private'>
1499	value := userData.
1500	(children at: userData) invokeCallback
1501    ]
1502
1503    group [
1504	"answer the radio group my children are in"
1505
1506	<category: 'private'>
1507	| child |
1508	child := children at: 1.
1509	^child exists ifFalse: [nil] ifTrue: [child connected getGroup]
1510    ]
1511
1512    initialize: parentWidget [
1513	<category: 'private'>
1514	super initialize: parentWidget.
1515	value := 0
1516    ]
1517]
1518
1519
1520
1521BButton subclass: BRadioButton [
1522    | assignedValue |
1523
1524    <comment: 'I am just one in a group of mutually exclusive buttons.'>
1525    <category: 'Graphics-Windows'>
1526
1527    callback: aReceiver message: aSymbol [
1528	"Set up so that aReceiver is sent the aSymbol message (the name of
1529	 a selector accepting at most two arguments) when the receiver is
1530	 clicked.  If the method accepts two arguments, the receiver is
1531	 passed as the first parameter.  If the method accepts one or two
1532	 arguments, true is passed as the last parameter for interoperability
1533	 with BToggle widgets."
1534
1535	<category: 'accessing'>
1536	| arguments selector numArgs |
1537	selector := aSymbol asSymbol.
1538	numArgs := selector numArgs.
1539	arguments := #().
1540	numArgs = 1 ifTrue: [arguments := #(true)].
1541	numArgs = 2
1542	    ifTrue:
1543		[arguments :=
1544			{self.
1545			true}].
1546	callback := DirectedMessage
1547		    selector: selector
1548		    arguments: arguments
1549		    receiver: aReceiver
1550    ]
1551
1552    value [
1553	"Answer whether this widget is the selected one in its radio
1554	 button group."
1555
1556	<category: 'accessing'>
1557	^self parent value = assignedValue
1558    ]
1559
1560    value: aBoolean [
1561	"Answer whether this widget is the selected one in its radio
1562	 button group.  Setting this property to false for a group's
1563	 currently selected button unhighlights all the buttons in that
1564	 group."
1565
1566	<category: 'accessing'>
1567	aBoolean
1568	    ifTrue:
1569		[self parent value: assignedValue.
1570		^self].
1571
1572	"aBoolean is false - unhighlight everything if we're active"
1573	self value ifTrue: [self parent value: 0]
1574    ]
1575
1576    assignedValue: anInteger [
1577	<category: 'private'>
1578	assignedValue := anInteger
1579    ]
1580
1581    create [
1582	<category: 'private'>
1583	self
1584	    connected: (GTK.GtkRadioButton newWithLabel: self parent group label: '')
1585    ]
1586]
1587
1588
1589
1590BButton subclass: BToggle [
1591    | value |
1592
1593    <comment: 'I represent a button whose choice can be included (by checking
1594me) or excluded (by leaving me unchecked).'>
1595    <category: 'Graphics-Windows'>
1596
1597    callback: aReceiver message: aSymbol [
1598	"Set up so that aReceiver is sent the aSymbol message (the name of
1599	 a selector accepting at most two arguments) when the receiver is
1600	 clicked.  If the method accepts two arguments, the receiver is
1601	 passed as the first parameter.  If the method accepts one or two
1602	 arguments, the state of the widget (true if it is selected, false
1603	 if it is not) is passed as the last parameter."
1604
1605	<category: 'accessing'>
1606	| arguments selector numArgs |
1607	selector := aSymbol asSymbol.
1608	numArgs := selector numArgs.
1609	arguments := #().
1610	numArgs = 1 ifTrue: [arguments := {nil}].
1611	numArgs = 2
1612	    ifTrue:
1613		[arguments :=
1614			{self.
1615			nil}].
1616	callback := DirectedMessage
1617		    selector: selector
1618		    arguments: arguments
1619		    receiver: aReceiver
1620    ]
1621
1622    invokeCallback [
1623	"Generate a synthetic callback."
1624
1625	<category: 'accessing'>
1626	self callback isNil ifTrue: [^self].
1627	self callback arguments size > 0
1628	    ifTrue:
1629		[self callback arguments at: self callback arguments size put: self value].
1630	super invokeCallback
1631    ]
1632
1633    value [
1634	"Answer whether the button is in a selected (checked) state."
1635
1636	<category: 'accessing'>
1637	self tclEval: 'return ${var' , self connected , '}'.
1638	^self tclResult = '1'
1639    ]
1640
1641    value: aBoolean [
1642	"Set whether the button is in a selected (checked) state and
1643	 generates a callback accordingly."
1644
1645	<category: 'accessing'>
1646	aBoolean
1647	    ifTrue: [self tclEval: 'set var' , self connected , ' 1']
1648	    ifFalse: [self tclEval: 'set var' , self connected , ' 0']
1649    ]
1650
1651    variable: value [
1652	"Set the value of Tk's variable option for the widget."
1653
1654	<category: 'accessing'>
1655	self
1656	    tclEval: '%1 configure -variable %3'
1657	    with: self connected
1658	    with: self container
1659	    with: value asTkString.
1660	self properties at: #variable put: value
1661    ]
1662
1663    initialize: parentWidget [
1664	<category: 'private'>
1665	| variable |
1666	super initialize: parentWidget.
1667	self tclEval: self connected , ' configure -anchor nw'.
1668	self tclEval: 'variable var' , self connected.
1669	self variable: 'var' , self connected.
1670	self backgroundColor: parentWidget backgroundColor
1671    ]
1672
1673    widgetType [
1674	<category: 'private'>
1675	^'checkbutton'
1676    ]
1677]
1678
1679
1680
1681BPrimitive subclass: BImage [
1682
1683    <comment: 'I can display colorful images.'>
1684    <category: 'Graphics-Windows'>
1685
1686    BImage class >> downArrow [
1687	"Answer the XPM representation of a 12x12 arrow pointing downwards."
1688
1689	<category: 'arrows'>
1690	^'/* XPM */
1691static char * downarrow_xpm[] = {
1692/* width height ncolors chars_per_pixel */
1693"12 12 2 1",
1694/* colors */
1695" 	c None    m None   s None",
1696"o	c black   m black",
1697/* pixels */
1698"            ",
1699"            ",
1700"            ",
1701"            ",
1702"  ooooooo   ",
1703"   ooooo    ",
1704"    ooo     ",
1705"     o      ",
1706"            ",
1707"            ",
1708"            ",
1709"            "};
1710'
1711    ]
1712
1713    BImage class >> leftArrow [
1714	"Answer the XPM representation of a 12x12 arrow pointing leftwards."
1715
1716	<category: 'arrows'>
1717	^'/* XPM */
1718static char * leftarrow_xpm[] = {
1719/* width height ncolors chars_per_pixel */
1720"12 12 2 1",
1721/* colors */
1722" 	c None    m None   s None",
1723"o	c black   m black",
1724/* pixels */
1725"            ",
1726"            ",
1727"       o    ",
1728"      oo    ",
1729"     ooo    ",
1730"    oooo    ",
1731"     ooo    ",
1732"      oo    ",
1733"       o    ",
1734"            ",
1735"            ",
1736"            "};
1737'
1738    ]
1739
1740    BImage class >> upArrow [
1741	"Answer the XPM representation of a 12x12 arrow pointing upwards."
1742
1743	<category: 'arrows'>
1744	^'/* XPM */
1745static char * uparrow_xpm[] = {
1746/* width height ncolors chars_per_pixel */
1747"12 12 2 1",
1748/* colors */
1749" 	c None    m None   s None",
1750"o	c black   m black",
1751/* pixels */
1752"            ",
1753"            ",
1754"            ",
1755"            ",
1756"     o      ",
1757"    ooo     ",
1758"   ooooo    ",
1759"  ooooooo   ",
1760"            ",
1761"            ",
1762"            ",
1763"            "};
1764'
1765    ]
1766
1767    BImage class >> rightArrow [
1768	"Answer the XPM representation of a 12x12 arrow pointing rightwards."
1769
1770	<category: 'arrows'>
1771	^'/* XPM */
1772static char * rightarrow_xpm[] = {
1773/* width height ncolors chars_per_pixel */
1774"12 12 2 1",
1775/* colors */
1776" 	c None    m None   s None",
1777"o	c black   m black",
1778/* pixels */
1779"            ",
1780"            ",
1781"    o       ",
1782"    oo      ",
1783"    ooo     ",
1784"    oooo    ",
1785"    ooo     ",
1786"    oo      ",
1787"    o       ",
1788"            ",
1789"            ",
1790"            "};
1791'
1792    ]
1793
1794    BImage class >> gnu [
1795	"Answer the XPM representation of a 48x48 GNU."
1796
1797	<category: 'GNU'>
1798	^'/* XPM */
1799/*****************************************************************************/
1800/* GNU Emacs bitmap conv. to pixmap by Przemek Klosowski (przemek@nist.gov)  */
1801/*****************************************************************************/
1802static char * image_name [] = {
1803/* width height ncolors chars_per_pixel */
1804"48 48 7 1",
1805/* colors */
1806" 	s mask	c none",
1807"B      c blue",
1808"x      c black",
1809":      c SandyBrown",
1810"+      c SaddleBrown",
1811"o      c grey",
1812".      c white",
1813/* pixels */
1814"                                                ",
1815"                                   x            ",
1816"                                    :x          ",
1817"                                    :::x        ",
1818"                                      ::x       ",
1819"          x                             ::x     ",
1820"         x:                xxx          :::x    ",
1821"        x:           xxx xxx:xxx         x::x   ",
1822"       x::       xxxx::xxx:::::xx        x::x   ",
1823"      x::       x:::::::xx::::::xx       x::x   ",
1824"      x::      xx::::::::x:::::::xx     xx::x   ",
1825"     x::      xx::::::::::::::::::x    xx::xx   ",
1826"    x::x     xx:::::xxx:::::::xxx:xxx xx:::xx   ",
1827"   x:::x    xx:::::xx...xxxxxxxxxxxxxxx:::xx    ",
1828"   x:::x   xx::::::xx..xxx...xxxx...xxxxxxxx    ",
1829"   x:::x   x::::::xx.xxx.......x.x.......xxxx   ",
1830"   x:::xx x:::x::xx.xx..........x.xx.........x  ",
1831"   x::::xx::xx:::x.xx....ooooxoxoxoo.xxx.....x  ",
1832"   xx::::xxxx::xx.xx.xxxx.ooooooo.xxx    xxxx   ",
1833"    xx::::::::xx..x.xxx..ooooooooo.xx           ",
1834"    xxx:::::xxx..xx.xx.xx.xxx.ooooo.xx          ",
1835"      xxx::xx...xx.xx.BBBB..xxooooooxx          ",
1836"       xxxx.....xx.xxBB:BB.xxoooooooxx          ",
1837"        xx.....xx...x.BBBx.xxxooooooxx          ",
1838"       x....xxxx..xx...xxxooooooooooxx          ",
1839"       x..xxxxxx..x.......x..ooooooooxx         ",
1840"       x.x xxx.x.x.x...xxxx.oooooooooxx         ",
1841"        x  xxx.x.x.xx...xx..oooooooooxx         ",
1842"          xx.x..x.x.xx........oooooooox         ",
1843"         xxo.xx.x.x.x.x.......ooooooooox        ",
1844"         xxo..xxxx..x...x.......ooooooox        ",
1845"         xxoo.xx.x..xx...x.......ooo.xxx        ",
1846"         xxoo..x.x.x.x.x.xx.xxxxx.o.xx+xx       ",
1847"         xxoo..x.xx..xx.x.x.x+++xxxxx+++x       ",
1848"         xxooo.x..xxx.x.x.x.x+++++xxx+xxx       ",
1849"          xxoo.xx..x..xx.xxxx++x+++x++xxx       ",
1850"          xxoo..xx.xxx.xxx.xxx++xx+x++xx        ",
1851"           xxooo.xx.xx..xx.xxxx++x+++xxx        ",
1852"           xxooo.xxx.xx.xxxxxxxxx++++xxx        ",
1853"            xxoo...xx.xx.xxxxxx++xxxxxxx        ",
1854"            xxoooo..x..xxx..xxxx+++++xx         ",
1855"             xxoooo..x..xx..xxxx++++xx          ",
1856"              xxxooooox.xx.xxxxxxxxxxx          ",
1857"               xxxooooo..xxx    xxxxx           ",
1858"                xxxxooooxxxx                    ",
1859"                  xxxoooxxx                     ",
1860"                    xxxxx                       ",
1861"                                                "
1862};'
1863    ]
1864
1865    BImage class >> exclaim [
1866	"Answer the XPM representation of a 32x32 exclamation mark icon."
1867
1868	<category: 'icons'>
1869	^'/* XPM */
1870static char * exclaim_xpm[] = {
1871/* width height ncolors chars_per_pixel */
1872"32 32 6 1",
1873/* colors */
1874" 	c None    m None   s None",
1875".	c yellow  m white",
1876"X	c black   m black",
1877"x	c gray50  m black",
1878"o	c gray    m white",
1879"b	c yellow4 m black",
1880/* pixels */
1881"             bbb                ",
1882"            b..oX               ",
1883"           b....oXx             ",
1884"           b.....Xxx            ",
1885"          b......oXxx           ",
1886"          b.......Xxx           ",
1887"         b........oXxx          ",
1888"         b.........Xxx          ",
1889"        b..........oXxx         ",
1890"        b...oXXXo...Xxx         ",
1891"       b....XXXXX...oXxx        ",
1892"       b....XXXXX....Xxx        ",
1893"      b.....XXXXX....oXxx       ",
1894"      b.....XXXXX.....Xxx       ",
1895"     b......XXXXX.....oXxx      ",
1896"     b......bXXXb......Xxx      ",
1897"    b.......oXXXo......oXxx     ",
1898"    b........XXX........Xxx     ",
1899"   b.........bXb........oXxx    ",
1900"   b.........oXo.........Xxx    ",
1901"  b...........X..........oXxx   ",
1902"  b.......................Xxx   ",
1903" b...........oXXo.........oXxx  ",
1904" b...........XXXX..........Xxx  ",
1905"b............XXXX..........oXxx ",
1906"b............oXXo...........Xxx ",
1907"b...........................Xxxx",
1908"b..........................oXxxx",
1909" b........................oXxxxx",
1910"  bXXXXXXXXXXXXXXXXXXXXXXXXxxxxx",
1911"    xxxxxxxxxxxxxxxxxxxxxxxxxxx ",
1912"     xxxxxxxxxxxxxxxxxxxxxxxxx  "};
1913'
1914    ]
1915
1916    BImage class >> info [
1917	"Answer the XPM representation of a 32x32 `information' icon."
1918
1919	<category: 'icons'>
1920	^'/* XPM */
1921static char * info_xpm[] = {
1922/* width height ncolors chars_per_pixel */
1923"32 32 6 1",
1924/* colors */
1925" 	c None    m None   s None",
1926".	c white   m white",
1927"X	c black   m black",
1928"x	c gray50  m black",
1929"o	c gray    m white",
1930"b	c blue    m black",
1931/* pixels */
1932"           xxxxxxxx             ",
1933"        xxxo......oxxx          ",
1934"      xxo............oxx        ",
1935"     xo................ox       ",
1936"    x.......obbbbo.......X      ",
1937"   x........bbbbbb........X     ",
1938"  x.........bbbbbb.........X    ",
1939" xo.........obbbbo.........oX   ",
1940" x..........................Xx  ",
1941"xo..........................oXx ",
1942"x..........bbbbbbb...........Xx ",
1943"x............bbbbb...........Xxx",
1944"x............bbbbb...........Xxx",
1945"x............bbbbb...........Xxx",
1946"x............bbbbb...........Xxx",
1947"xo...........bbbbb..........oXxx",
1948" x...........bbbbb..........Xxxx",
1949" xo..........bbbbb.........oXxxx",
1950"  x........bbbbbbbbb.......Xxxx ",
1951"   X......................Xxxxx ",
1952"    X....................Xxxxx  ",
1953"     Xo................oXxxxx   ",
1954"      XXo............oXXxxxx    ",
1955"       xXXXo......oXXXxxxxx     ",
1956"        xxxXXXo...Xxxxxxxx      ",
1957"          xxxxX...Xxxxxx        ",
1958"             xX...Xxx           ",
1959"               X..Xxx           ",
1960"                X.Xxx           ",
1961"                 XXxx           ",
1962"                  xxx           ",
1963"                   xx           "};
1964'
1965    ]
1966
1967    BImage class >> question [
1968	"Answer the XPM representation of a 32x32 question mark icon."
1969
1970	<category: 'icons'>
1971	^'/* XPM */
1972static char * question_xpm[] = {
1973/* width height ncolors chars_per_pixel */
1974"32 32 6 1",
1975/* colors */
1976" 	c None    m None   s None",
1977".	c white   m white",
1978"X	c black   m black",
1979"x	c gray50  m black",
1980"o	c gray    m white",
1981"b	c blue    m black",
1982/* pixels */
1983"           xxxxxxxx             ",
1984"        xxxo......oxxx          ",
1985"      xxo............oxx        ",
1986"     xo................ox       ",
1987"    x....................X      ",
1988"   x.......obbbbbbo.......X     ",
1989"  x.......obo..bbbbo.......X    ",
1990" xo.......bb....bbbb.......oX   ",
1991" x........bbbb..bbbb........Xx  ",
1992"xo........bbbb.obbbb........oXx ",
1993"x.........obbo.bbbb..........Xx ",
1994"x.............obbb...........Xxx",
1995"x.............bbb............Xxx",
1996"x.............bbo............Xxx",
1997"x.............bb.............Xxx",
1998"xo..........................oXxx",
1999" x...........obbo...........Xxxx",
2000" xo..........bbbb..........oXxxx",
2001"  x..........bbbb..........Xxxx ",
2002"   X.........obbo.........Xxxxx ",
2003"    X....................Xxxxx  ",
2004"     Xo................oXxxxx   ",
2005"      XXo............oXXxxxx    ",
2006"       xXXXo......oXXXxxxxx     ",
2007"        xxxXXXo...Xxxxxxxx      ",
2008"          xxxxX...Xxxxxx        ",
2009"             xX...Xxx           ",
2010"               X..Xxx           ",
2011"                X.Xxx           ",
2012"                 XXxx           ",
2013"                  xxx           ",
2014"                   xx           "};
2015'
2016    ]
2017
2018    BImage class >> stop [
2019	"Answer the XPM representation of a 32x32 `critical stop' icon."
2020
2021	<category: 'icons'>
2022	^'/* XPM */
2023static char * stop_xpm[] = {
2024/* width height ncolors chars_per_pixel */
2025"32 32 5 1",
2026/* colors */
2027" 	c None    m None   s None",
2028".	c red     m white",
2029"o	c DarkRed m black",
2030"X	c white   m black",
2031"x	c gray50  m black",
2032/* pixels */
2033"           oooooooo             ",
2034"        ooo........ooo          ",
2035"       o..............o         ",
2036"     oo................oo       ",
2037"    o....................o      ",
2038"   o......................o     ",
2039"   o......................ox    ",
2040"  o......X..........X......ox   ",
2041" o......XXX........XXX......o   ",
2042" o.....XXXXX......XXXXX.....ox  ",
2043" o......XXXXX....XXXXX......oxx ",
2044"o........XXXXX..XXXXX........ox ",
2045"o.........XXXXXXXXXX.........ox ",
2046"o..........XXXXXXXX..........oxx",
2047"o...........XXXXXX...........oxx",
2048"o...........XXXXXX...........oxx",
2049"o..........XXXXXXXX..........oxx",
2050"o.........XXXXXXXXXX.........oxx",
2051"o........XXXXX..XXXXX........oxx",
2052" o......XXXXX....XXXXX......oxxx",
2053" o.....XXXXX......XXXXX.....oxxx",
2054" o......XXX........XXX......oxx ",
2055"  o......X..........X......oxxx ",
2056"   o......................oxxxx ",
2057"   o......................oxxx  ",
2058"    o....................oxxx   ",
2059"     oo................ooxxxx   ",
2060"      xo..............oxxxxx    ",
2061"       xooo........oooxxxxx     ",
2062"         xxooooooooxxxxxx       ",
2063"          xxxxxxxxxxxxxx        ",
2064"             xxxxxxxx           "};
2065'
2066    ]
2067
2068    BImage class >> new: parent data: aString [
2069	"Answer a new BImage widget laid inside the given parent widget,
2070	 loading data from the given string (Base-64 encoded GIF, XPM,
2071	 PPM are supported)."
2072
2073	<category: 'instance creation'>
2074	^(self new: parent)
2075	    data: aString;
2076	    yourself
2077    ]
2078
2079    BImage class >> new: parent image: aFileStream [
2080	"Answer a new BImage widget laid inside the given parent widget,
2081	 loading data from the given file (GIF, XPM, PPM are supported)."
2082
2083	<category: 'instance creation'>
2084	^(self new: parent)
2085	    image: aFileStream;
2086	    yourself
2087    ]
2088
2089    BImage class >> new: parent size: aPoint [
2090	"Answer a new BImage widget laid inside the given parent widget,
2091	 showing by default a transparent image of aPoint size."
2092
2093	<category: 'instance creation'>
2094	^(self new: parent)
2095	    displayWidth: aPoint x;
2096	    displayHeight: aPoint y;
2097	    blank;
2098	    yourself
2099    ]
2100
2101    BImage class >> directory [
2102	"Answer the Base-64 GIF representation of a `directory folder' icon."
2103
2104	<category: 'small icons'>
2105	^'R0lGODdhEAAQAPIAAAAAAHh4eLi4uPj4+P///wAAAAAAAAAAACwAAAAAEAAQAAADPkixzPOD
2106yADrWE8qC8WN0+BZAmBq1GMOqwigXFXCrGk/cxjjr27fLtout6n9eMIYMTXsFZsogXRKJf6u
2107P0kCADv/'
2108    ]
2109
2110    BImage class >> file [
2111	"Answer the Base-64 GIF representation of a `file' icon."
2112
2113	<category: 'small icons'>
2114	^'R0lGODdhEAAQAPIAAAAAAHh4eLi4uPj4APj4+P///wAAAAAAACwAAAAAEAAQAAADPVi63P4w
2115LkKCtTTnUsXwQqBtAfh910UU4ugGAEucpgnLNY3Gop7folwNOBOeiEYQ0acDpp6pGAFArVqt
2116hQQAO///'
2117    ]
2118
2119    backgroundColor [
2120	"Answer the value of the backgroundColor option for the widget.
2121
2122	 Specifies the normal background color to use when displaying the widget."
2123
2124	<category: 'accessing'>
2125	self properties at: #background ifPresent: [:value | ^value].
2126	self
2127	    tclEval: '%1 cget -background'
2128	    with: self connected
2129	    with: self container.
2130	^self properties at: #background put: self tclResult
2131    ]
2132
2133    backgroundColor: value [
2134	"Set the value of the backgroundColor option for the widget.
2135
2136	 Specifies the normal background color to use when displaying the widget."
2137
2138	<category: 'accessing'>
2139	self
2140	    tclEval: '%1 configure -background %3'
2141	    with: self connected
2142	    with: self container
2143	    with: value asTkString.
2144	self properties at: #background put: value
2145    ]
2146
2147    displayHeight [
2148	"Answer the value of the displayHeight option for the widget.
2149
2150	 Specifies the height of the image in pixels. This is not the height of the
2151	 widget, but specifies the area of the widget that will be taken by the image."
2152
2153	<category: 'accessing'>
2154	self properties at: #displayHeight ifPresent: [:value | ^value].
2155	self
2156	    tclEval: 'img%1 cget -width'
2157	    with: self connected
2158	    with: self container.
2159	^self properties at: #displayHeight put: self tclResult asNumber
2160    ]
2161
2162    displayHeight: value [
2163	"Set the value of the displayHeight option for the widget.
2164
2165	 Specifies the height of the image in pixels. This is not the height of the
2166	 widget, but specifies the area of the widget that will be taken by the image."
2167
2168	<category: 'accessing'>
2169	self
2170	    tclEval: 'img%1 configure -width %3'
2171	    with: self connected
2172	    with: self container
2173	    with: value asFloat printString asTkString.
2174	self properties at: #displayHeight put: value
2175    ]
2176
2177    displayWidth [
2178	"Answer the value of the displayWidth option for the widget.
2179
2180	 Specifies the width of the image in pixels. This is not the width of the
2181	 widget, but specifies the area of the widget that will be taken by the image."
2182
2183	<category: 'accessing'>
2184	self properties at: #displayWidth ifPresent: [:value | ^value].
2185	self
2186	    tclEval: 'img%1 cget -width'
2187	    with: self connected
2188	    with: self container.
2189	^self properties at: #displayWidth put: self tclResult asNumber
2190    ]
2191
2192    displayWidth: value [
2193	"Set the value of the displayWidth option for the widget.
2194
2195	 Specifies the width of the image in pixels. This is not the width of the
2196	 widget, but specifies the area of the widget that will be taken by the image."
2197
2198	<category: 'accessing'>
2199	self
2200	    tclEval: 'img%1 configure -width %3'
2201	    with: self connected
2202	    with: self container
2203	    with: value asFloat printString asTkString.
2204	self properties at: #displayWidth put: value
2205    ]
2206
2207    foregroundColor [
2208	"Answer the value of the foregroundColor option for the widget.
2209
2210	 Specifies the normal foreground color to use when displaying the widget."
2211
2212	<category: 'accessing'>
2213	self properties at: #foreground ifPresent: [:value | ^value].
2214	self
2215	    tclEval: '%1 cget -foreground'
2216	    with: self connected
2217	    with: self container.
2218	^self properties at: #foreground put: self tclResult
2219    ]
2220
2221    foregroundColor: value [
2222	"Set the value of the foregroundColor option for the widget.
2223
2224	 Specifies the normal foreground color to use when displaying the widget."
2225
2226	<category: 'accessing'>
2227	self
2228	    tclEval: '%1 configure -foreground %3'
2229	    with: self connected
2230	    with: self container
2231	    with: value asTkString.
2232	self properties at: #foreground put: value
2233    ]
2234
2235    gamma [
2236	"Answer the value of the gamma option for the widget.
2237
2238	 Specifies that the colors allocated for displaying the image widget
2239	 should be corrected for a non-linear display with the specified gamma exponent
2240	 value. (The intensity produced by most CRT displays is a power function
2241	 of the input value, to a good approximation; gamma is the exponent and
2242	 is typically around 2). The value specified must be greater than zero. The
2243	 default value is one (no correction). In general, values greater than one
2244	 will make the image lighter, and values less than one will make it darker."
2245
2246	<category: 'accessing'>
2247	self properties at: #gamma ifPresent: [:value | ^value].
2248	self
2249	    tclEval: 'img%1 cget -gamma'
2250	    with: self connected
2251	    with: self container.
2252	^self properties at: #gamma put: self tclResult asNumber
2253    ]
2254
2255    gamma: value [
2256	"Set the value of the gamma option for the widget.
2257
2258	 Specifies that the colors allocated for displaying the image widget
2259	 should be corrected for a non-linear display with the specified gamma exponent
2260	 value. (The intensity produced by most CRT displays is a power function
2261	 of the input value, to a good approximation; gamma is the exponent and
2262	 is typically around 2). The value specified must be greater than zero. The
2263	 default value is one (no correction). In general, values greater than one
2264	 will make the image lighter, and values less than one will make it darker."
2265
2266	<category: 'accessing'>
2267	self
2268	    tclEval: 'img%1 configure -gamma %3'
2269	    with: self connected
2270	    with: self container
2271	    with: value asFloat printString asTkString.
2272	self properties at: #gamma put: value
2273    ]
2274
2275    blank [
2276	"Blank the corresponding image"
2277
2278	<category: 'image management'>
2279	self tclEval: 'img' , self connected , ' blank'
2280    ]
2281
2282    data: aString [
2283	"Set the image to be drawn to aString, which can be a GIF
2284	 in Base-64 representation or an X pixelmap."
2285
2286	<category: 'image management'>
2287	self tclEval: 'img' , self connected , ' configure -data '
2288		    , aString asTkImageString
2289    ]
2290
2291    dither [
2292	"Recalculate the dithered image in the window where the
2293	 image is displayed.  The dithering algorithm used in
2294	 displaying images propagates quantization errors from
2295	 one pixel to its neighbors.  If the image data is supplied
2296	 in pieces, the dithered image may not be exactly correct.
2297	 Normally the difference is not noticeable, but if it is a
2298	 problem, this command can be used to fix it."
2299
2300	<category: 'image management'>
2301	self tclEval: 'img' , self connected , ' redither'
2302    ]
2303
2304    fillFrom: origin extent: extent color: color [
2305	"Fill a rectangle with the given origin and extent, using
2306	 the given color."
2307
2308	<category: 'image management'>
2309	self
2310	    fillFrom: origin
2311	    to: origin + extent
2312	    color: color
2313    ]
2314
2315    fillFrom: origin to: corner color: color [
2316	"Fill a rectangle between the given corners, using
2317	 the given color."
2318
2319	<category: 'image management'>
2320	self
2321	    tclEval: 'img%1 put { %2 } -to %3 %4'
2322	    with: self connected
2323	    with: color
2324	    with: origin x printString , ' ' , origin y printString
2325	    with: corner x printString , ' ' , corner y printString
2326    ]
2327
2328    fillRectangle: rectangle color: color [
2329	"Fill a rectangle having the given bounding box, using
2330	 the given color."
2331
2332	<category: 'image management'>
2333	self
2334	    fillFrom: rectangle origin
2335	    to: rectangle corner
2336	    color: color
2337    ]
2338
2339    image: aFileStream [
2340	"Read a GIF or XPM image from aFileStream.  The whole contents
2341	 of the file are read, not only from the file position."
2342
2343	<category: 'image management'>
2344	self
2345	    tclEval: 'img' , self connected , ' read ' , aFileStream name asTkString
2346    ]
2347
2348    imageHeight [
2349	"Specifies the height of the image, in pixels.  This option is useful
2350	 primarily in situations where you wish to build up the contents of
2351	 the image piece by piece.  A value of zero (the default) allows the
2352	 image to expand or shrink vertically to fit the data stored in it."
2353
2354	<category: 'image management'>
2355	self tclEval: 'image height img' , self connected.
2356	^self tclResult asInteger
2357    ]
2358
2359    imageWidth [
2360	"Specifies the width of the image, in pixels.  This option is useful
2361	 primarily in situations where you wish to build up the contents of
2362	 the image piece by piece.  A value of zero (the default) allows the
2363	 image to expand or shrink horizontally to fit the data stored in it."
2364
2365	<category: 'image management'>
2366	self tclEval: 'image width img' , self connected.
2367	^self tclResult asInteger
2368    ]
2369
2370    lineFrom: origin extent: extent color: color [
2371	"Draw a line with the given origin and extent, using
2372	 the given color."
2373
2374	<category: 'image management'>
2375	self
2376	    lineFrom: origin
2377	    to: origin + extent
2378	    color: color
2379    ]
2380
2381    lineFrom: origin to: corner color: color [
2382	<category: 'image management'>
2383	self notYetImplemented
2384    ]
2385
2386    lineFrom: origin toX: endX color: color [
2387	"Draw an horizontal line between the given corners, using
2388	 the given color."
2389
2390	<category: 'image management'>
2391	self
2392	    tclEval: 'img%1 put { %2 } -to %3 %4'
2393	    with: self connected
2394	    with: color
2395	    with: origin x printString , ' ' , origin y printString
2396	    with: endX printString , ' ' , origin y printString
2397    ]
2398
2399    lineInside: rectangle color: color [
2400	"Draw a line having the given bounding box, using
2401	 the given color."
2402
2403	<category: 'image management'>
2404	self
2405	    lineFrom: rectangle origin
2406	    to: rectangle corner
2407	    color: color
2408    ]
2409
2410    lineFrom: origin toY: endY color: color [
2411	"Draw a vertical line between the given corners, using
2412	 the given color."
2413
2414	<category: 'image management'>
2415	self
2416	    tclEval: 'img%1 put { %2 } -to %3 %4'
2417	    with: self connected
2418	    with: color
2419	    with: origin x printString , ' ' , origin y printString
2420	    with: origin x printString , ' ' , endY printString
2421    ]
2422
2423    destroyed [
2424	"Private - The receiver has been destroyed, clear the corresponding
2425	 Tcl image to avoid memory leaks."
2426
2427	<category: 'widget protocol'>
2428	'TODO' printNl.
2429	super destroyed
2430    ]
2431
2432    create [
2433	<category: 'private'>
2434	self tclEval: 'image create photo img' , self connected.
2435	self create: '-anchor nw -image img' , self connected
2436    ]
2437
2438    setInitialSize [
2439	"Make the Tk placer's status, the receiver's properties and the
2440	 window status (as returned by winfo) consistent. Occupy the
2441	 area indicated by the widget itself, at the top left corner"
2442
2443	<category: 'private'>
2444	self x: 0 y: 0
2445    ]
2446
2447    widgetType [
2448	<category: 'private'>
2449	^'label'
2450    ]
2451]
2452
2453
2454
2455BViewport subclass: BList [
2456    | labels items callback gtkmodel connected gtkcolumn |
2457
2458    <comment: 'I represent a list box from which you can choose one or more
2459elements.'>
2460    <category: 'Graphics-Windows'>
2461
2462    add: anObject afterIndex: index [
2463	"Add an element with the given value after another element whose
2464	 index is contained in the index parameter.  The label displayed
2465	 in the widget is anObject's displayString.  Answer anObject."
2466
2467	<category: 'accessing'>
2468	^self
2469	    add: nil
2470	    element: anObject
2471	    afterIndex: index
2472    ]
2473
2474    add: aString element: anObject afterIndex: index [
2475	"Add an element with the aString label after another element whose
2476	 index is contained in the index parameter.  This method allows
2477	 the client to decide autonomously the label that the widget will
2478	 display.
2479
2480	 If anObject is nil, then string is used as the element as well.
2481	 If aString is nil, then the element's displayString is used as
2482	 the label.
2483
2484	 Answer anObject or, if it is nil, aString."
2485
2486	<category: 'accessing'>
2487	| elem label iter |
2488	label := aString isNil ifTrue: [anObject displayString] ifFalse: [aString].
2489	elem := anObject isNil ifTrue: [aString] ifFalse: [anObject].
2490	labels isNil
2491	    ifTrue:
2492		[index > 0
2493		    ifTrue: [^SystemExceptions.IndexOutOfRange signalOn: self withIndex: index].
2494		labels := OrderedCollection with: label.
2495		items := OrderedCollection with: elem]
2496	    ifFalse:
2497		[labels add: label afterIndex: index.
2498		items add: elem afterIndex: index].
2499	iter := self gtkmodel insert: index.
2500	self gtkmodel
2501	    setOop: iter
2502	    column: 0
2503	    value: label.
2504	^elem
2505    ]
2506
2507    addLast: anObject [
2508	"Add an element with the given value at the end of the listbox.
2509	 The label displayed in the widget is anObject's displayString.
2510	 Answer anObject."
2511
2512	<category: 'accessing'>
2513	^self
2514	    add: nil
2515	    element: anObject
2516	    afterIndex: items size
2517    ]
2518
2519    addLast: aString element: anObject [
2520	"Add an element with the given value at the end of the listbox.
2521	 This method allows the client to decide autonomously the label
2522	 that the widget will display.
2523
2524	 If anObject is nil, then string is used as the element as well.
2525	 If aString is nil, then the element's displayString is used as
2526	 the label.
2527
2528	 Answer anObject or, if it is nil, aString."
2529
2530	<category: 'accessing'>
2531	^self
2532	    add: aString
2533	    element: anObject
2534	    afterIndex: items size
2535    ]
2536
2537    associationAt: anIndex [
2538	"Answer an association whose key is the item at the given position
2539	 in the listbox and whose value is the label used to display that
2540	 item."
2541
2542	<category: 'accessing'>
2543	^(items at: anIndex) -> (labels at: anIndex)
2544    ]
2545
2546    at: anIndex [
2547	"Answer the element displayed at the given position in the list
2548	 box."
2549
2550	<category: 'accessing'>
2551	^items at: anIndex
2552    ]
2553
2554    backgroundColor [
2555	"Answer the value of the backgroundColor option for the widget.
2556
2557	 Specifies the normal background color to use when displaying the widget."
2558
2559	<category: 'accessing'>
2560	self properties at: #background ifPresent: [:value | ^value].
2561	self
2562	    tclEval: '%1 cget -background'
2563	    with: self connected
2564	    with: self container.
2565	^self properties at: #background put: self tclResult
2566    ]
2567
2568    backgroundColor: value [
2569	"Set the value of the backgroundColor option for the widget.
2570
2571	 Specifies the normal background color to use when displaying the widget."
2572
2573	<category: 'accessing'>
2574	self
2575	    tclEval: '%1 configure -background %3'
2576	    with: self connected
2577	    with: self container
2578	    with: value asTkString.
2579	self properties at: #background put: value
2580    ]
2581
2582    contents: elementList [
2583	"Set the elements displayed in the listbox, and set the labels
2584	 to be their displayStrings."
2585
2586	<category: 'accessing'>
2587	| newLabels |
2588	newLabels := elementList collect: [:each | each displayString].
2589	^self contents: newLabels elements: elementList
2590    ]
2591
2592    contents: stringCollection elements: elementList [
2593	"Set the elements displayed in the listbox to be those in elementList,
2594	 and set the labels to be the corresponding elements in stringCollection.
2595	 The two collections must have the same size."
2596
2597	<category: 'accessing'>
2598	| stream iter |
2599	(elementList notNil and: [elementList size ~= stringCollection size])
2600	    ifTrue:
2601		[^self
2602		    error: 'label collection must have the same size as element collection'].
2603	labels := stringCollection isNil
2604		    ifTrue:
2605			[elementList asOrderedCollection collect: [:each | each displayString]]
2606		    ifFalse: [stringCollection asOrderedCollection].
2607	items := elementList isNil
2608		    ifTrue: [labels copy]
2609		    ifFalse: [elementList asOrderedCollection].
2610	self gtkmodel clear.
2611	iter := GTK.GtkTreeIter new.
2612	stringCollection do:
2613		[:each |
2614		self gtkmodel append: iter.
2615		self gtkmodel
2616		    setOop: iter
2617		    column: 0
2618		    value: each]
2619    ]
2620
2621    do: aBlock [
2622	"Iterate over each element of the listbox and pass it to aBlock."
2623
2624	<category: 'accessing'>
2625	items do: aBlock
2626    ]
2627
2628    elements [
2629	"Answer the collection of objects that represent the elements
2630	 displayed by the list box."
2631
2632	<category: 'accessing'>
2633	^items copy
2634    ]
2635
2636    elements: elementList [
2637	"Set the elements displayed in the listbox, and set the labels
2638	 to be their displayStrings."
2639
2640	<category: 'accessing'>
2641	| newLabels |
2642	newLabels := elementList collect: [:each | each displayString].
2643	^self contents: newLabels elements: elementList
2644    ]
2645
2646    font [
2647	"Answer the value of the font option for the widget.
2648
2649	 Specifies the font to use when drawing text inside the widget. The font
2650	 can be given as either an X font name or a Blox font description string.
2651
2652	 X font names are given as many fields, each led by a minus, and each of
2653	 which can be replaced by an * to indicate a default value is ok:
2654	 foundry, family, weight, slant, setwidth, addstyle, pixel size, point size
2655	 (the same as pixel size for historical reasons), horizontal resolution,
2656	 vertical resolution, spacing, width, charset and character encoding.
2657
2658	 Blox font description strings have three fields, which must be separated by
2659	 a space and of which only the first is mandatory: the font family, the font
2660	 size in points (or in pixels if a negative value is supplied), and a number
2661	 of styles separated by a space (valid styles are normal, bold, italic,
2662	 underline and overstrike). Examples of valid fonts are ``Helvetica 10 Bold'',
2663	 ``Times -14'', ``Futura Bold Underline''.  You must enclose the font family
2664	 in braces if it is made of two or more words."
2665
2666	<category: 'accessing'>
2667	self properties at: #font ifPresent: [:value | ^value].
2668	self
2669	    tclEval: '%1 cget -font'
2670	    with: self connected
2671	    with: self container.
2672	^self properties at: #font put: self tclResult
2673    ]
2674
2675    font: value [
2676	"Set the value of the font option for the widget.
2677
2678	 Specifies the font to use when drawing text inside the widget. The font
2679	 can be given as either an X font name or a Blox font description string.
2680
2681	 X font names are given as many fields, each led by a minus, and each of
2682	 which can be replaced by an * to indicate a default value is ok:
2683	 foundry, family, weight, slant, setwidth, addstyle, pixel size, point size
2684	 (the same as pixel size for historical reasons), horizontal resolution,
2685	 vertical resolution, spacing, width, charset and character encoding.
2686
2687	 Blox font description strings have three fields, which must be separated by
2688	 a space and of which only the first is mandatory: the font family, the font
2689	 size in points (or in pixels if a negative value is supplied), and a number
2690	 of styles separated by a space (valid styles are normal, bold, italic,
2691	 underline and overstrike). Examples of valid fonts are ``Helvetica 10 Bold'',
2692	 ``Times -14'', ``Futura Bold Underline''.  You must enclose the font family
2693	 in braces if it is made of two or more words."
2694
2695	<category: 'accessing'>
2696	self
2697	    tclEval: '%1 configure -font %3'
2698	    with: self connected
2699	    with: self container
2700	    with: value asTkString.
2701	self properties at: #font put: value
2702    ]
2703
2704    foregroundColor [
2705	"Answer the value of the foregroundColor option for the widget.
2706
2707	 Specifies the normal foreground color to use when displaying the widget."
2708
2709	<category: 'accessing'>
2710	self properties at: #foreground ifPresent: [:value | ^value].
2711	self
2712	    tclEval: '%1 cget -foreground'
2713	    with: self connected
2714	    with: self container.
2715	^self properties at: #foreground put: self tclResult
2716    ]
2717
2718    foregroundColor: value [
2719	"Set the value of the foregroundColor option for the widget.
2720
2721	 Specifies the normal foreground color to use when displaying the widget."
2722
2723	<category: 'accessing'>
2724	self
2725	    tclEval: '%1 configure -foreground %3'
2726	    with: self connected
2727	    with: self container
2728	    with: value asTkString.
2729	self properties at: #foreground put: value
2730    ]
2731
2732    highlightBackground [
2733	"Answer the value of the highlightBackground option for the widget.
2734
2735	 Specifies the background color to use when displaying selected items
2736	 in the widget."
2737
2738	<category: 'accessing'>
2739	self properties at: #selectbackground ifPresent: [:value | ^value].
2740	self
2741	    tclEval: '%1 cget -selectbackground'
2742	    with: self connected
2743	    with: self container.
2744	^self properties at: #selectbackground put: self tclResult
2745    ]
2746
2747    highlightBackground: value [
2748	"Set the value of the highlightBackground option for the widget.
2749
2750	 Specifies the background color to use when displaying selected items
2751	 in the widget."
2752
2753	<category: 'accessing'>
2754	self
2755	    tclEval: '%1 configure -selectbackground %3'
2756	    with: self connected
2757	    with: self container
2758	    with: value asTkString.
2759	self properties at: #selectbackground put: value
2760    ]
2761
2762    highlightForeground [
2763	"Answer the value of the highlightForeground option for the widget.
2764
2765	 Specifies the foreground color to use when displaying selected items
2766	 in the widget."
2767
2768	<category: 'accessing'>
2769	self properties at: #selectforeground ifPresent: [:value | ^value].
2770	self
2771	    tclEval: '%1 cget -selectforeground'
2772	    with: self connected
2773	    with: self container.
2774	^self properties at: #selectforeground put: self tclResult
2775    ]
2776
2777    highlightForeground: value [
2778	"Set the value of the highlightForeground option for the widget.
2779
2780	 Specifies the foreground color to use when displaying selected items
2781	 in the widget."
2782
2783	<category: 'accessing'>
2784	self
2785	    tclEval: '%1 configure -selectforeground %3'
2786	    with: self connected
2787	    with: self container
2788	    with: value asTkString.
2789	self properties at: #selectforeground put: value
2790    ]
2791
2792    index [
2793	"Answer the value of the index option for the widget.
2794
2795	 Indicates the element that has the location cursor. This item will be
2796	 displayed in the highlightForeground color, and with the corresponding
2797	 background color."
2798
2799	<category: 'accessing'>
2800	^self properties at: #index
2801	    ifAbsentPut:
2802		[| iter |
2803		(iter := self connected getSelection getSelected) isNil
2804		    ifTrue: [nil]
2805		    ifFalse: [(self gtkmodel getStringFromIter: iter) asInteger]]
2806    ]
2807
2808    indexAt: point [
2809	"Answer the index of the element that covers the point in the
2810	 listbox window specified by x and y (in pixel coordinates).  If no
2811	 element covers that point, then the closest element to that point
2812	 is used."
2813
2814	<category: 'accessing'>
2815	| pPath ok path index |
2816	pPath := GTK.GtkTreePath type ptrType gcNew.
2817	ok := self
2818		    getPathAtPos: point x
2819		    y: point y
2820		    path: pPath
2821		    column: nil
2822		    cellX: nil
2823		    cellY: nil.
2824	path := pPath value.
2825	index := ok ifTrue: [path getIndices value] ifFalse: [self elements size].
2826	path free.
2827	^index
2828    ]
2829
2830    isSelected: index [
2831	"Answer whether the element indicated by index is currently selected."
2832
2833	<category: 'accessing'>
2834	| selected path |
2835	path := self pathAt: index.
2836	selected := self connected getSelection pathIsSelected: path.
2837	path free.
2838	^selected
2839    ]
2840
2841    labelAt: anIndex [
2842	"Answer the label displayed at the given position in the list
2843	 box."
2844
2845	<category: 'accessing'>
2846	^labels at: anIndex
2847    ]
2848
2849    labels [
2850	"Answer the labels displayed by the list box."
2851
2852	<category: 'accessing'>
2853	^labels copy
2854    ]
2855
2856    labelsDo: aBlock [
2857	"Iterate over each listbox element's label and pass it to aBlock."
2858
2859	<category: 'accessing'>
2860	labels do: aBlock
2861    ]
2862
2863    mode [
2864	"Answer the value of the mode option for the widget.
2865
2866	 Specifies one of several styles for manipulating the selection. The value
2867	 of the option may be either single, browse, multiple, or extended.
2868
2869	 If the selection mode is single or browse, at most one element can be selected in
2870	 the listbox at once. Clicking button 1 on an unselected element selects it and
2871	 deselects any other selected item, while clicking on a selected element
2872	 has no effect. In browse mode it is also possible to drag the selection
2873	 with button 1. That is, moving the mouse while button 1 is pressed keeps
2874	 the item under the cursor selected.
2875
2876	 If the selection mode is multiple or extended, any number of elements may be
2877	 selected at once, including discontiguous ranges. In multiple mode, clicking button
2878	 1 on an element toggles its selection state without affecting any other elements.
2879	 In extended mode, pressing button 1 on an element selects it, deselects
2880	 everything else, and sets the anchor to the element under the mouse; dragging the
2881	 mouse with button 1 down extends the selection to include all the elements between
2882	 the anchor and the element under the mouse, inclusive.
2883
2884	 In extended mode, the selected range can be adjusted by pressing button 1
2885	 with the Shift key down: this modifies the selection to consist of the elements
2886	 between the anchor and the element under the mouse, inclusive. The
2887	 un-anchored end of this new selection can also be dragged with the button
2888	 down. Also in extended mode, pressing button 1 with the Control key down starts a
2889	 toggle operation: the anchor is set to the element under the mouse, and its
2890	 selection state is reversed. The selection state of other elements is not
2891	 changed. If the mouse is dragged with button 1 down, then the selection
2892	 state of all elements between the anchor and the element under the mouse is
2893	 set to match that of the anchor element; the selection state of all other
2894	 elements remains what it was before the toggle operation began.
2895
2896	 Most people will probably want to use browse mode for single selections and
2897	 extended mode for multiple selections; the other modes appear to be useful only in
2898	 special situations."
2899
2900	<category: 'accessing'>
2901	| mode |
2902	^self properties at: #selectmode
2903	    ifAbsentPut:
2904		[mode := self connected getSelection getMode.
2905		mode = GTK.Gtk gtkSelectionSingle
2906		    ifTrue: [#single]
2907		    ifFalse:
2908			[mode = GTK.Gtk gtkSelectionBrowse
2909			    ifTrue: [#browse]
2910			    ifFalse: [mode = GTK.Gtk gtkSelectionExtended ifTrue: [#extended]]]]
2911    ]
2912
2913    mode: value [
2914	"Set the value of the mode option for the widget.
2915
2916	 Specifies one of several styles for manipulating the selection. The value
2917	 of the option may be either single, browse, multiple, or extended.
2918
2919	 If the selection mode is single or browse, at most one element can be selected in
2920	 the listbox at once. Clicking button 1 on an unselected element selects it and
2921	 deselects any other selected item, while clicking on a selected element
2922	 has no effect. In browse mode it is also possible to drag the selection
2923	 with button 1. That is, moving the mouse while button 1 is pressed keeps
2924	 the item under the cursor selected.
2925
2926	 If the selection mode is multiple or extended, any number of elements may be
2927	 selected at once, including discontiguous ranges. In multiple mode, clicking button
2928	 1 on an element toggles its selection state without affecting any other elements.
2929	 In extended mode, pressing button 1 on an element selects it, deselects
2930	 everything else, and sets the anchor to the element under the mouse; dragging the
2931	 mouse with button 1 down extends the selection to include all the elements between
2932	 the anchor and the element under the mouse, inclusive.
2933
2934	 In extended mode, the selected range can be adjusted by pressing button 1
2935	 with the Shift key down: this modifies the selection to consist of the elements
2936	 between the anchor and the element under the mouse, inclusive. The
2937	 un-anchored end of this new selection can also be dragged with the button
2938	 down. Also in extended mode, pressing button 1 with the Control key down starts a
2939	 toggle operation: the anchor is set to the element under the mouse, and its
2940	 selection state is reversed. The selection state of other elements is not
2941	 changed. If the mouse is dragged with button 1 down, then the selection
2942	 state of all elements between the anchor and the element under the mouse is
2943	 set to match that of the anchor element; the selection state of all other
2944	 elements remains what it was before the toggle operation began.
2945
2946	 Most people will probably want to use browse mode for single selections and
2947	 extended mode for multiple selections; the other modes appear to be useful only in
2948	 special situations."
2949
2950	<category: 'accessing'>
2951	| mode |
2952	value = #single
2953	    ifTrue: [mode := GTK.Gtk gtkSelectionSingle]
2954	    ifFalse:
2955		[value = #browse
2956		    ifTrue: [mode := GTK.Gtk gtkSelectionBrowse]
2957		    ifFalse:
2958			[value = #multiple
2959			    ifTrue: [mode := GTK.Gtk gtkSelectionExtended]
2960			    ifFalse:
2961				[value = #extended
2962				    ifTrue: [mode := GTK.Gtk gtkSelectionExtended]
2963				    ifFalse: [^self error: 'invalid value for BList mode']]]].
2964	self connected getSelection setMode: mode.
2965	self properties at: #selectmode put: value
2966    ]
2967
2968    numberOfStrings [
2969	"Answer the number of items in the list box"
2970
2971	<category: 'accessing'>
2972	^labels size
2973    ]
2974
2975    removeAtIndex: index [
2976	"Remove the item at the given index in the list box, answering
2977	 the object associated to the element (i.e. the value that #at:
2978	 would have returned for the given index)"
2979
2980	<category: 'accessing'>
2981	| result |
2982	labels removeAtIndex: index.
2983	result := items removeAtIndex: index.
2984	self gtkmodel remove: (self iterAt: index).
2985	^result
2986    ]
2987
2988    label [
2989	"assign a new label to the list"
2990
2991	<category: 'accessing'>
2992	^self gtkcolumn getTitle
2993    ]
2994
2995    label: aString [
2996	"assign a new label to the list"
2997
2998	<category: 'accessing'>
2999	self gtkcolumn setTitle: aString
3000    ]
3001
3002    size [
3003	"Answer the number of items in the list box"
3004
3005	<category: 'accessing'>
3006	^labels size
3007    ]
3008
3009    itemSelected: receiver at: index [
3010	<category: 'private - examples'>
3011	stdout
3012	    nextPutAll: 'List item ';
3013	    print: index;
3014	    nextPutAll: ' selected!';
3015	    nl.
3016	stdout
3017	    nextPutAll: 'Contents: ';
3018	    nextPutAll: (items at: index);
3019	    nl
3020    ]
3021
3022    gtkcolumn [
3023	"answer the gtk column for the list"
3024
3025	<category: 'private'>
3026	gtkcolumn isNil ifTrue: [self createWidget].
3027	^gtkcolumn
3028    ]
3029
3030    gtkmodel [
3031	"answer the gtk list model"
3032
3033	<category: 'private'>
3034	gtkmodel isNil ifTrue: [self createWidget].
3035	^gtkmodel
3036    ]
3037
3038    onChanged: selection data: userData [
3039	<category: 'private'>
3040	| iter |
3041	(iter := selection getSelected) isNil
3042	    ifFalse: [self invokeCallback: (self gtkmodel getStringFromIter: iter)]
3043    ]
3044
3045    pathAt: anIndex [
3046	<category: 'private'>
3047	^GTK.GtkTreePath newFromIndices: anIndex - 1 varargs: #()
3048    ]
3049
3050    iterAt: anIndex [
3051	<category: 'private'>
3052	^self gtkmodel iterNthChild: nil n: anIndex - 1
3053    ]
3054
3055    create [
3056	<category: 'private'>
3057	| select renderer |
3058	renderer := GTK.GtkCellRendererText new.
3059	'phwoar... should not need the explicit calls, but something is bust in varargs passing'
3060	    printNl.
3061	gtkcolumn := GTK.GtkTreeViewColumn new.
3062	gtkcolumn setTitle: 'List'.
3063	gtkcolumn packStart: renderer expand: true.
3064	gtkcolumn
3065	    addAttribute: renderer
3066	    attribute: 'text'
3067	    column: 0.
3068
3069	"gtkcolumn := GTK.GtkTreeViewColumn newWithAttributes: 'List' cell: renderer varargs: {'text'. 0. nil}."
3070	gtkmodel := GTK.GtkListStore new: 1 varargs: {GTK.GValue gTypeString}.
3071	self connected: (GTK.GtkTreeView newWithModel: self gtkmodel).
3072	(self connected)
3073	    appendColumn: self gtkcolumn;
3074	    setSearchColumn: 0.
3075	select := self connected getSelection.
3076	select setMode: GTK.Gtk gtkSelectionSingle.
3077	select
3078	    connectSignal: 'changed'
3079	    to: self
3080	    selector: #onChanged:data:
3081	    userData: nil
3082    ]
3083
3084    show [
3085	<category: 'private'>
3086	super show.
3087	self container setShadowType: GTK.Gtk gtkShadowIn
3088    ]
3089
3090    needsViewport [
3091	<category: 'private'>
3092	^false
3093    ]
3094
3095    initialize: parentWidget [
3096	<category: 'private'>
3097	super initialize: parentWidget.
3098	self properties at: #index put: nil.
3099	labels := OrderedCollection new
3100    ]
3101
3102    invokeCallback: indexString [
3103	<category: 'private'>
3104	| index |
3105	items isNil ifTrue: [^self].
3106	index := indexString asInteger.
3107	self properties at: #index put: index + 1.
3108	self invokeCallback
3109    ]
3110
3111    callback [
3112	"Answer a DirectedMessage that is sent when the active item in
3113	 the receiver changes, or nil if none has been set up."
3114
3115	<category: 'widget protocol'>
3116	^callback
3117    ]
3118
3119    callback: aReceiver message: aSymbol [
3120	"Set up so that aReceiver is sent the aSymbol message (the name of
3121	 a selector with at most two arguemtnts) when the active item in
3122	 the receiver changegs.  If the method accepts two arguments, the
3123	 receiver is  passed as the first parameter.  If the method accepts
3124	 one or two arguments, the selected index is passed as the last
3125	 parameter."
3126
3127	<category: 'widget protocol'>
3128	| arguments selector numArgs |
3129	selector := aSymbol asSymbol.
3130	numArgs := selector numArgs.
3131	arguments := #().
3132	numArgs = 1 ifTrue: [arguments := {nil}].
3133	numArgs = 2
3134	    ifTrue:
3135		[arguments :=
3136			{self.
3137			nil}].
3138	callback := DirectedMessage
3139		    selector: selector
3140		    arguments: arguments
3141		    receiver: aReceiver
3142    ]
3143
3144    highlight: index [
3145	"Highlight the item at the given position in the listbox."
3146
3147	<category: 'widget protocol'>
3148	index = self index ifTrue: [^self].
3149	(self mode = #single or: [self mode = #browse]) ifTrue: [self unhighlight].
3150	self select: index
3151    ]
3152
3153    invokeCallback [
3154	"Generate a synthetic callback."
3155
3156	<category: 'widget protocol'>
3157	self callback notNil
3158	    ifTrue:
3159		[self callback arguments isEmpty
3160		    ifFalse:
3161			[self callback arguments at: self callback arguments size
3162			    put: (self properties at: #index)].
3163		self callback send]
3164    ]
3165
3166    select: index [
3167	"Highlight the item at the given position in the listbox,
3168	 without unhighlighting other items.  This is meant for
3169	 multiple- or extended-mode listboxes, but can be used
3170	 with other selection mode in particular cases."
3171
3172	<category: 'widget protocol'>
3173	self properties at: #index put: index.
3174	self connected getSelection selectIter: (self iterAt: index)
3175    ]
3176
3177    show: index [
3178	"Ensure that the item at the given position in the listbox is
3179	 visible."
3180
3181	<category: 'widget protocol'>
3182	| path |
3183	path := self pathAt: index.
3184	self connected
3185	    scrollToCell: path
3186	    column: self gtkcolumn
3187	    useAlign: false
3188	    rowAlign: 0.0e
3189	    colAlign: 0.0e.
3190	path free
3191    ]
3192
3193    unhighlight [
3194	"Unhighlight all the items in the listbox."
3195
3196	<category: 'widget protocol'>
3197	self connected getSelection unselectAll
3198    ]
3199
3200    unselect: index [
3201	"Unhighlight the item at the given position in the listbox,
3202	 without affecting the state of the other items."
3203
3204	<category: 'widget protocol'>
3205	self connected getSelection unselectIter: (self iterAt: index)
3206    ]
3207]
3208
3209
3210
3211BForm subclass: BWindow [
3212    | isMapped callback x y width height container uiBox uiManager |
3213
3214    <comment: 'I am the boss. Nothing else could be viewed or interacted with if
3215it wasn''t for me... )):->'>
3216    <category: 'Graphics-Windows'>
3217
3218    TopLevel := nil.
3219
3220    BWindow class >> initializeOnStartup [
3221	<category: 'private - initialization'>
3222	TopLevel := OrderedCollection new
3223    ]
3224
3225    BWindow class >> new [
3226	"Answer a new top-level window."
3227
3228	<category: 'instance creation'>
3229	^TopLevel add: (super new: nil)
3230    ]
3231
3232    BWindow class >> new: label [
3233	"Answer a new top-level window with `label' as its title bar caption."
3234
3235	<category: 'instance creation'>
3236	^self new label: label
3237    ]
3238
3239    BWindow class >> popup: initializationBlock [
3240	<category: 'instance creation'>
3241	self shouldNotImplement
3242    ]
3243
3244    callback [
3245	"Answer a DirectedMessage that is sent to verify whether the
3246	 receiver must be destroyed when the user asks to unmap it."
3247
3248	<category: 'accessing'>
3249	^callback
3250    ]
3251
3252    callback: aReceiver message: aSymbol [
3253	"Set up so that aReceiver is sent the aSymbol message (the name of
3254	 a zero- or one-argument selector) when the user asks to unmap the
3255	 receiver.  If the method accepts an argument, the receiver is passed.
3256
3257	 If the method returns true, the window and its children are
3258	 destroyed (which is the default action, taken if no callback is
3259	 set up).  If the method returns false, the window is left in
3260	 place."
3261
3262	<category: 'accessing'>
3263	| arguments selector numArgs |
3264	selector := aSymbol asSymbol.
3265	numArgs := selector numArgs.
3266	arguments := #().
3267	numArgs = 1 ifTrue: [arguments := Array with: self].
3268	callback := DirectedMessage
3269		    selector: selector
3270		    arguments: arguments
3271		    receiver: aReceiver
3272    ]
3273
3274    invokeCallback [
3275	"Generate a synthetic callback, destroying the window if no
3276	 callback was set up or if the callback method answers true."
3277
3278	<category: 'accessing'>
3279	| result |
3280	result := self callback isNil or: [self callback send].
3281	result
3282	    ifTrue:
3283		[self destroy.
3284		isMapped := false].
3285	^result
3286    ]
3287
3288    label [
3289	"Answer the value of the label option for the widget.
3290
3291	 Specifies a string to be displayed inside the widget. The way in which the
3292	 string is displayed depends on the particular widget and may be determined
3293	 by other options, such as anchor. For windows, this is the title of the
3294	 window."
3295
3296	<category: 'accessing'>
3297	^self container getTitle
3298    ]
3299
3300    label: value [
3301	"Set the value of the label option for the widget.
3302
3303	 Specifies a string to be displayed inside the widget. The way in which the
3304	 string is displayed depends on the particular widget and may be determined
3305	 by other options, such as anchor. For windows, this is the title of the
3306	 window."
3307
3308	<category: 'accessing'>
3309	self container setTitle: value
3310    ]
3311
3312    menu: aBMenuBar [
3313	"Set the value of the menu option for the widget.
3314
3315	 Specifies a menu widget to be used as a menubar."
3316
3317	<category: 'accessing'>
3318	self uiBox
3319	    packStart: aBMenuBar connected
3320	    expand: false
3321	    fill: false
3322	    padding: 0.
3323	self properties at: #menu put: aBMenuBar
3324    ]
3325
3326    resizable [
3327	"Answer the value of the resizable option for the widget.
3328
3329	 Answer whether the user can be resize the window or not. If resizing is
3330	 disabled, then the window's size will be the size from the most recent
3331	 interactive resize or geometry-setting method. If there has been no such
3332	 operation then the window's natural size will be used."
3333
3334	<category: 'accessing'>
3335	^self container getResizable
3336    ]
3337
3338    resizable: value [
3339	"Set the value of the resizable option for the widget.
3340
3341	 Answer whether the user can be resize the window or not. If resizing is
3342	 disabled, then the window's size will be the size from the most recent
3343	 interactive resize or geometry-setting method. If there has been no such
3344	 operation then the window's natural size will be used."
3345
3346	<category: 'accessing'>
3347	^self container setResizable: value
3348    ]
3349
3350    uiBox [
3351	"answer the top level container for this window"
3352
3353	<category: 'accessing'>
3354	^uiBox
3355    ]
3356
3357    uiManager [
3358	<category: 'accessing'>
3359	uiManager isNil ifTrue: [uiManager := GTK.GtkUIManager new].
3360	^uiManager
3361    ]
3362
3363    cacheWindowSize [
3364	"save the window position from gtk"
3365
3366	<category: 'private'>
3367	| px py |
3368	px := CIntType gcNew.
3369	py := CIntType gcNew.
3370	self container getPosition: px rootY: py.
3371	x := px value.
3372	y := py value.
3373	self isMapped
3374	    ifTrue: [self container getSize: px height: py]
3375	    ifFalse: [self container getDefaultSize: px height: py].
3376	width := px value.
3377	height := py value.
3378	self isMapped
3379	    ifTrue: [self container setDefaultSize: width height: height]
3380    ]
3381
3382    container [
3383	<category: 'private'>
3384	container isNil ifTrue: [self error: 'GTK object not created yet'].
3385	^container
3386    ]
3387
3388    container: aWidget [
3389	<category: 'private'>
3390	container := aWidget
3391    ]
3392
3393    initialize: parentWidget [
3394	<category: 'private'>
3395	super initialize: nil.
3396	self isMapped: false.
3397	self createWidget
3398    ]
3399
3400    create [
3401	<category: 'private'>
3402	self container: (GTK.GtkWindow new: GTK.Gtk gtkWindowToplevel).
3403	self container
3404	    connectSignal: 'delete-event'
3405	    to: self
3406	    selector: #onDelete:data:
3407	    userData: nil.
3408	self container
3409	    connectSignal: 'configure-event'
3410	    to: self
3411	    selector: #onConfigure:data:
3412	    userData: nil.
3413	uiBox := GTK.GtkVBox new: false spacing: 0.
3414	self container add: uiBox.
3415
3416	"Create the GtkPlacer"
3417	super create.
3418	uiBox
3419	    packEnd: self connected
3420	    expand: true
3421	    fill: true
3422	    padding: 0
3423    ]
3424
3425    show [
3426	"Do not show the GtkWindow until it is mapped!"
3427
3428	<category: 'private'>
3429	super show.
3430	uiBox show
3431    ]
3432
3433    onConfigure: object data: data [
3434	<category: 'private'>
3435	self cacheWindowSize
3436    ]
3437
3438    onDelete: object data: data [
3439	<category: 'private'>
3440	^self callback notNil and: [self callback send not]
3441    ]
3442
3443    destroyed [
3444	"Private - The receiver has been destroyed, remove it from the
3445	 list of toplevel windows to avoid memory leaks."
3446
3447	<category: 'private'>
3448	super destroyed.
3449	TopLevel remove: self ifAbsent: [].
3450	(TopLevel isEmpty and: [DoDispatchEvents = 1])
3451	    ifTrue: [Blox terminateMainLoop]
3452    ]
3453
3454    isMapped: aBoolean [
3455	<category: 'private'>
3456	isMapped := aBoolean
3457    ]
3458
3459    resetGeometry: xPos y: yPos width: xSize height: ySize [
3460	<category: 'private'>
3461	(x = xPos and: [y = yPos and: [width = xSize and: [height = ySize]]])
3462	    ifTrue: [^self].
3463	self isMapped
3464	    ifFalse: [self container setDefaultSize: xSize height: ySize]
3465	    ifTrue: [self container resize: xSize height: ySize].
3466	x := xPos.
3467	y := yPos.
3468	width := xSize.
3469	height := ySize
3470	"mapped ifTrue: [ self map ]."
3471    ]
3472
3473    resized [
3474	<category: 'private'>
3475	self isMapped ifFalse: [^self].
3476	x := y := width := height := nil
3477    ]
3478
3479    setInitialSize [
3480	<category: 'private'>
3481	self
3482	    x: 0
3483	    y: 0
3484	    width: 300
3485	    height: 300
3486    ]
3487
3488    center [
3489	"Center the window in the screen"
3490
3491	<category: 'widget protocol'>
3492	| screenSize |
3493	screenSize := Blox screenSize.
3494	self x: screenSize x // 2 - (self width // 2)
3495	    y: screenSize y // 2 - (self height // 2)
3496    ]
3497
3498    centerIn: view [
3499	"Center the window in the given widget"
3500
3501	<category: 'widget protocol'>
3502	self x: view x + (view width // 2) - (self parent width // 2)
3503	    y: view x + (view height // 2) - (self parent height // 2)
3504    ]
3505
3506    height [
3507	"Answer the height of the window, as deduced from the geometry
3508	 that the window manager imposed on the window."
3509
3510	<category: 'widget protocol'>
3511	height isNil ifTrue: [self cacheWindowSize].
3512	^height
3513    ]
3514
3515    height: anInteger [
3516	"Ask the window manager to give the given height to the window."
3517
3518	<category: 'widget protocol'>
3519	width isNil ifTrue: [self cacheWindowSize].
3520	self
3521	    resetGeometry: x
3522	    y: y
3523	    width: width
3524	    height: anInteger
3525    ]
3526
3527    heightAbsolute [
3528	"Answer the height of the window, as deduced from the geometry
3529	 that the window manager imposed on the window."
3530
3531	<category: 'widget protocol'>
3532	height isNil ifTrue: [self cacheWindowSize].
3533	^height
3534    ]
3535
3536    heightOffset: value [
3537	<category: 'widget protocol'>
3538	self shouldNotImplement
3539    ]
3540
3541    iconify [
3542	"Map a window and in iconified state.  If a window has not been
3543	 mapped yet, this is achieved by mapping the window in withdrawn
3544	 state first, and then iconifying it."
3545
3546	<category: 'widget protocol'>
3547	self container iconify.
3548	self isMapped: false
3549    ]
3550
3551    isMapped [
3552	"Answer whether the window is mapped"
3553
3554	<category: 'widget protocol'>
3555	isMapped isNil ifTrue: [isMapped := false].
3556	^isMapped
3557    ]
3558
3559    isWindow [
3560	<category: 'widget protocol'>
3561	^true
3562    ]
3563
3564    map [
3565	"Map the window and bring it to the topmost position in the Z-order."
3566
3567	<category: 'widget protocol'>
3568	self container present.
3569	self isMapped: true
3570    ]
3571
3572    modalMap [
3573	"Map the window while establishing an application-local grab for it.
3574	 An event loop is started that ends only after the window has been
3575	 destroyed."
3576
3577	<category: 'widget protocol'>
3578	self container setModal: true.
3579	self map.
3580	Blox dispatchEvents: self.
3581	self container setModal: false
3582    ]
3583
3584    state [
3585	"Set the value of the state option for the window.
3586
3587	 Specifies one of four states for the window: either normal, iconic,
3588	 withdrawn, or (Windows only) zoomed."
3589
3590	<category: 'widget protocol'>
3591	self tclEval: 'wm state ' , self connected.
3592	^self tclResult asSymbol
3593    ]
3594
3595    state: aSymbol [
3596	"Raise an error. To set a BWindow's state, use #map and #unmap."
3597
3598	<category: 'widget protocol'>
3599	self error: 'To set a BWindow''s state, use #map and #unmap.'
3600    ]
3601
3602    unmap [
3603	"Unmap a window, causing it to be forgotten about by the window manager"
3604
3605	<category: 'widget protocol'>
3606	self isMapped ifFalse: [^self].
3607	self hide.
3608	self isMapped: false
3609    ]
3610
3611    width [
3612	"Answer the width of the window, as deduced from the geometry
3613	 that the window manager imposed on the window."
3614
3615	<category: 'widget protocol'>
3616	width isNil ifTrue: [self cacheWindowSize].
3617	^width
3618    ]
3619
3620    width: anInteger [
3621	"Ask the window manager to give the given width to the window."
3622
3623	<category: 'widget protocol'>
3624	height isNil ifTrue: [self cacheWindowSize].
3625	self
3626	    resetGeometry: x
3627	    y: y
3628	    width: anInteger
3629	    height: height
3630    ]
3631
3632    width: xSize height: ySize [
3633	"Ask the window manager to give the given width and height to
3634	 the window."
3635
3636	<category: 'widget protocol'>
3637	self
3638	    resetGeometry: x
3639	    y: y
3640	    width: xSize
3641	    height: ySize
3642    ]
3643
3644    widthAbsolute [
3645	"Answer the width of the window, as deduced from the geometry
3646	 that the window manager imposed on the window."
3647
3648	<category: 'widget protocol'>
3649	width isNil ifTrue: [self cacheWindowSize].
3650	^width
3651    ]
3652
3653    widthOffset: value [
3654	<category: 'widget protocol'>
3655	self shouldNotImplement
3656    ]
3657
3658    window [
3659	<category: 'widget protocol'>
3660	^self
3661    ]
3662
3663    x [
3664	"Answer the x coordinate of the window's top-left corner, as
3665	 deduced from the geometry that the window manager imposed on
3666	 the window."
3667
3668	<category: 'widget protocol'>
3669	x isNil ifTrue: [self cacheWindowSize].
3670	^x
3671    ]
3672
3673    x: anInteger [
3674	"Ask the window manager to move the window's left border
3675	 to the given x coordinate, keeping the size unchanged"
3676
3677	<category: 'widget protocol'>
3678	y isNil ifTrue: [self cacheWindowSize].
3679	self
3680	    resetGeometry: anInteger
3681	    y: y
3682	    width: width
3683	    height: height
3684    ]
3685
3686    x: xPos y: yPos [
3687	"Ask the window manager to move the window's top-left corner
3688	 to the given coordinates, keeping the size unchanged"
3689
3690	<category: 'widget protocol'>
3691	self
3692	    resetGeometry: xPos
3693	    y: yPos
3694	    width: width
3695	    height: height
3696    ]
3697
3698    x: xPos y: yPos width: xSize height: ySize [
3699	"Ask the window manager to give the requested geometry
3700	 to the window."
3701
3702	"XXX gtk deprecates this sort of thing"
3703
3704
3705
3706	<category: 'widget protocol'>
3707	self
3708	    resetGeometry: xPos
3709	    y: yPos
3710	    width: xSize
3711	    height: ySize
3712    ]
3713
3714    xAbsolute [
3715	"Answer the x coordinate of the window's top-left corner, as
3716	 deduced from the geometry that the window manager imposed on
3717	 the window."
3718
3719	<category: 'widget protocol'>
3720	x isNil ifTrue: [self cacheWindowSize].
3721	^x
3722    ]
3723
3724    xOffset: value [
3725	<category: 'widget protocol'>
3726	self shouldNotImplement
3727    ]
3728
3729    y [
3730	"Answer the y coordinate of the window's top-left corner, as
3731	 deduced from the geometry that the window manager imposed on
3732	 the window."
3733
3734	<category: 'widget protocol'>
3735	y isNil ifTrue: [self cacheWindowSize].
3736	^y
3737    ]
3738
3739    y: anInteger [
3740	"Ask the window manager to move the window's left border
3741	 to the given y coordinate, keeping the size unchanged"
3742
3743	<category: 'widget protocol'>
3744	x isNil ifTrue: [self cacheWindowSize].
3745	self
3746	    resetGeometry: x
3747	    y: anInteger
3748	    width: width
3749	    height: height
3750    ]
3751
3752    yAbsolute [
3753	"Answer the y coordinate of the window's top-left corner, as
3754	 deduced from the geometry that the window manager imposed on
3755	 the window."
3756
3757	<category: 'widget protocol'>
3758	y isNil ifTrue: [self cacheWindowSize].
3759	^y
3760    ]
3761
3762    yOffset: value [
3763	<category: 'widget protocol'>
3764	self shouldNotImplement
3765    ]
3766]
3767
3768
3769
3770BWindow subclass: BTransientWindow [
3771
3772    <comment: 'I am almost a boss. I represent a window which is logically linked
3773to another which sits higher in the widget hierarchy, e.g. a dialog
3774box'>
3775    <category: 'Graphics-Windows'>
3776
3777    BTransientWindow class >> new [
3778	<category: 'instance creation'>
3779	self shouldNotImplement
3780    ]
3781
3782    BTransientWindow class >> new: parentWindow [
3783	"Answer a new transient window attached to the given
3784	 parent window and with nothing in its title bar caption."
3785
3786	<category: 'instance creation'>
3787	^(self basicNew)
3788	    initialize: parentWindow;
3789	    yourself
3790    ]
3791
3792    BTransientWindow class >> new: label in: parentWindow [
3793	"Answer a new transient window attached to the given
3794	 parent window and with `label' as its title bar caption."
3795
3796	<category: 'instance creation'>
3797	^(self basicNew)
3798	    initialize: parentWindow;
3799	    label: label;
3800	    yourself
3801    ]
3802
3803    map [
3804	"Map the window and inform the windows manager that the
3805	 receiver is a transient window working on behalf of its
3806	 parent.  The window is also put in its parent window's
3807	 window group: the window manager might use this information,
3808	 for example, to unmap all of the windows in a group when the
3809	 group's leader is iconified."
3810
3811	<category: 'widget protocol'>
3812	self parent isNil
3813	    ifFalse: [self container setTransientFor: self parent container].
3814	super map
3815    ]
3816]
3817
3818
3819
3820BWindow subclass: BPopupWindow [
3821
3822    <comment: 'I am a pseudo-window that has no decorations and no ability to interact
3823with the user.  My main usage, as my name says, is to provide pop-up
3824functionality for other widgets.  Actually there should be no need to
3825directly use me - always rely on the #new and #popup: class methods.'>
3826    <category: 'Graphics-Windows'>
3827
3828    addChild: w [
3829	"Private - The widget identified by child has been added to the
3830	 receiver.  This method is public not because you can call it,
3831	 but because it can be useful to override it to perform some
3832	 initialization on the children just added. Answer the new child."
3833
3834	<category: 'geometry management'>
3835	self uiBox
3836	    packEnd: w
3837	    expand: true
3838	    fill: true
3839	    padding: 1.
3840	w onDestroySend: #destroy to: self
3841    ]
3842
3843    child: child height: value [
3844	"Set the given child's height.  This is done by setting
3845	 its parent window's (that is, our) height."
3846
3847	"Only act after #addChild:"
3848
3849	<category: 'geometry management'>
3850	self childrenCount = 0 ifTrue: [^self].
3851	self height: value
3852    ]
3853
3854    child: child heightOffset: value [
3855	<category: 'geometry management'>
3856	self shouldNotImplement
3857    ]
3858
3859    child: child width: value [
3860	"Set the given child's width.  This is done by setting
3861	 its parent window's (that is, our) width."
3862
3863	"Only act after #addChild:"
3864
3865	<category: 'geometry management'>
3866	self childrenCount = 0 ifTrue: [^self].
3867	self width: value
3868    ]
3869
3870    child: child widthOffset: value [
3871	<category: 'geometry management'>
3872	self shouldNotImplement
3873    ]
3874
3875    child: child x: value [
3876	"Set the x coordinate of the given child's top-left corner.
3877	 This is done by setting its parent window's (that is, our) x."
3878
3879	<category: 'geometry management'>
3880	self x: value
3881    ]
3882
3883    child: child xOffset: value [
3884	<category: 'geometry management'>
3885	self shouldNotImplement
3886    ]
3887
3888    child: child y: value [
3889	"Set the y coordinate of the given child's top-left corner.
3890	 This is done by setting its parent window's (that is, our) y."
3891
3892	<category: 'geometry management'>
3893	self y: value
3894    ]
3895
3896    child: child yOffset: value [
3897	<category: 'geometry management'>
3898	self shouldNotImplement
3899    ]
3900
3901    heightChild: child [
3902	"Answer the given child's height, which is the height that
3903	 was imposed on the popup window."
3904
3905	<category: 'geometry management'>
3906	^self height
3907    ]
3908
3909    widthChild: child [
3910	"Answer the given child's width in pixels, which is the width that
3911	 was imposed on the popup window."
3912
3913	<category: 'geometry management'>
3914	^self width
3915    ]
3916
3917    xChild: child [
3918	"Answer the x coordinate of the given child's top-left corner,
3919	 which is desumed by the position of the popup window."
3920
3921	<category: 'geometry management'>
3922	^self x
3923    ]
3924
3925    yChild: child [
3926	"Answer the y coordinate of the given child's top-left corner,
3927	 which is desumed by the position of the popup window."
3928
3929	<category: 'geometry management'>
3930	^self y
3931    ]
3932
3933    create [
3934	<category: 'private'>
3935	super create.
3936	self container setDecorated: false.
3937	self container setResizable: false
3938    ]
3939
3940    setInitialSize [
3941	<category: 'private'>
3942	self cacheWindowSize
3943    ]
3944]
3945
3946
3947
3948BForm subclass: BDialog [
3949    | callbacks initInfo buttonBox entry |
3950
3951    <comment: 'I am a facility for implementing dialogs with many possible choices
3952and requests. In addition I provide support for a few platform native
3953common dialog boxes, such as choose-a-file and choose-a-color.'>
3954    <category: 'Graphics-Windows'>
3955
3956    BDialog class >> new: parent [
3957	"Answer a new dialog handler (containing a label widget and
3958	 some button widgets) laid out within the given parent window.
3959	 The label widget, when it is created, is empty."
3960
3961	<category: 'instance creation'>
3962	^(self basicNew)
3963	    initInfo: '' -> nil;
3964	    initialize: parent
3965    ]
3966
3967    BDialog class >> new: parent label: aLabel [
3968	"Answer a new dialog handler (containing a label widget and
3969	 some button widgets) laid out within the given parent window.
3970	 The label widget, when it is created, contains aLabel."
3971
3972	<category: 'instance creation'>
3973	^(self basicNew)
3974	    initInfo: aLabel -> nil;
3975	    initialize: parent
3976    ]
3977
3978    BDialog class >> new: parent label: aLabel prompt: aString [
3979	"Answer a new dialog handler (containing a label widget, some
3980	 button widgets, and an edit window showing aString by default)
3981	 laid out within the given parent window.
3982	 The label widget, when it is created, contains aLabel."
3983
3984	<category: 'instance creation'>
3985	^(self basicNew)
3986	    initInfo: aLabel -> aString;
3987	    initialize: parent
3988    ]
3989
3990    BDialog class >> chooseFile: operation parent: parent label: aLabel default: name defaultExtension: ext types: typeList action: action button: button [
3991	<category: 'private'>
3992	| dialog result filename |
3993	'FIXME: implement the default, defaultExtension and typesList portions'
3994	    printNl.
3995	parent map.
3996	dialog := GTK.GtkFileChooserDialog
3997		    new: aLabel
3998		    parent: parent container
3999		    action: action
4000		    varargs:
4001			{GTK.Gtk gtkStockCancel.
4002			GTK.Gtk gtkResponseCancel.
4003			button.
4004			GTK.Gtk gtkResponseAccept.
4005			nil}.
4006	result := dialog run.
4007	^result = GTK.Gtk gtkResponseAccept
4008	    ifFalse:
4009		[dialog destroy.
4010		nil]
4011	    ifTrue:
4012		[filename := dialog getFilename.
4013		filename isEmpty ifTrue: [filename := nil].
4014		dialog destroy.
4015		filename]
4016    ]
4017
4018    BDialog class >> chooseColor: parent label: aLabel default: color [
4019	"Prompt for a color.  The dialog box is created with the given
4020	 parent window and with aLabel as its title bar text, and initially
4021	 it selects the color given in the color parameter.
4022
4023	 If the dialog box is canceled, nil is answered, else the
4024	 selected color is returned as a String with its RGB value."
4025
4026	<category: 'prompters'>
4027	| result |
4028	parent map.
4029	self
4030	    tclEval: 'tk_chooseColor -parent %1 -title %2 -initialcolor %3'
4031	    with: parent container
4032	    with: aLabel asTkString
4033	    with: color asTkString.
4034	result := self tclResult.
4035	result isEmpty ifTrue: [result := nil].
4036	^result
4037    ]
4038
4039    BDialog class >> chooseFileToOpen: parent label: aLabel default: name defaultExtension: ext types: typeList [
4040	"Pop up a dialog box for the user to select a file to open.
4041	 Its purpose is for the user to select an existing file only.
4042	 If the user enters an non-existent file, the dialog box gives
4043	 the user an error prompt and requires the user to give an
4044	 alternative selection or to cancel the selection. If an
4045	 application allows the user to create new files, it should
4046	 do so by providing a separate New menu command.
4047
4048	 If the dialog box is canceled, nil is answered, else the
4049	 selected file name is returned as a String.
4050
4051	 The dialog box is created with the given parent window
4052	 and with aLabel as its title bar text.  The name parameter
4053	 indicates which file is initially selected, and the default
4054	 extension specifies  a string that will be appended to the
4055	 filename if the user enters a filename without an extension.
4056
4057	 The typeList parameter is an array of arrays, like
4058	 #(('Text files' '.txt' '.diz') ('Smalltalk files' '.st')),
4059	 and is used to construct a listbox of file types.  When the user
4060	 chooses a file type in the listbox, only the files of that type
4061	 are listed.  Each item in the array contains a list of strings:
4062	 the first one is the name of the file type described by a particular
4063	 file pattern, and is the text string that appears in the File types
4064	 listbox, while the other ones are the possible extensions that
4065	 belong to this particular file type."
4066
4067	"e.g.
4068	 fileName := BDialog
4069	 chooseFileToOpen: aWindow
4070	 label: 'Open file'
4071	 default: nil
4072	 defaultExtension: 'gif'
4073	 types: #(
4074	 ('Text files'       '.txt' '.diz')
4075	 ('Smalltalk files'  '.st')
4076	 ('C source files'   '.c')
4077	 ('GIF files'	'.gif'))"
4078
4079	<category: 'prompters'>
4080	^self
4081	    chooseFile: 'Open'
4082	    parent: parent
4083	    label: aLabel
4084	    default: name
4085	    defaultExtension: ext
4086	    types: typeList
4087	    action: GTK.Gtk gtkFileChooserActionOpen
4088	    button: GTK.Gtk gtkStockOpen
4089    ]
4090
4091    BDialog class >> chooseFileToSave: parent label: aLabel default: name defaultExtension: ext types: typeList [
4092	"Pop up a dialog box for the user to select a file to save;
4093	 this differs from the file open dialog box in that non-existent
4094	 file names are accepted and existing file names trigger a
4095	 confirmation dialog box, asking the user whether the file
4096	 should be overwritten or not.
4097
4098	 If the dialog box is canceled, nil is answered, else the
4099	 selected file name is returned as a String.
4100
4101	 The dialog box is created with the given parent window
4102	 and with aLabel as its title bar text.  The name parameter
4103	 indicates which file is initially selected, and the default
4104	 extension specifies  a string that will be appended to the
4105	 filename if the user enters a filename without an extension.
4106
4107	 The typeList parameter is an array of arrays, like
4108	 #(('Text files' '.txt' '.diz') ('Smalltalk files' '.st')),
4109	 and is used to construct a listbox of file types.  When the user
4110	 chooses a file type in the listbox, only the files of that type
4111	 are listed.  Each item in the array contains a list of strings:
4112	 the first one is the name of the file type described by a particular
4113	 file pattern, and is the text string that appears in the File types
4114	 listbox, while the other ones are the possible extensions that
4115	 belong to this particular file type."
4116
4117	<category: 'prompters'>
4118	^self
4119	    chooseFile: 'Save'
4120	    parent: parent
4121	    label: aLabel
4122	    default: name
4123	    defaultExtension: ext
4124	    types: typeList
4125	    action: GTK.Gtk gtkFileChooserActionSave
4126	    button: GTK.Gtk gtkStockSave
4127    ]
4128
4129    addButton: aLabel receiver: anObject index: anInt [
4130	"Add a button to the dialog box that, when clicked, will
4131	 cause the #dispatch: method to be triggered in anObject,
4132	 passing anInt as the argument of the callback.  The
4133	 caption of the button is set to aLabel."
4134
4135	<category: 'accessing'>
4136	^self
4137	    addButton: aLabel
4138	    receiver: anObject
4139	    message: #dispatch:
4140	    argument: anInt
4141    ]
4142
4143    addButton: aLabel receiver: anObject message: aSymbol [
4144	"Add a button to the dialog box that, when clicked, will
4145	 cause the aSymbol unary selector to be sent to anObject.
4146	 The caption of the button is set to aLabel."
4147
4148	<category: 'accessing'>
4149	callbacks addLast: (DirectedMessage
4150		    selector: aSymbol
4151		    arguments: #()
4152		    receiver: anObject).
4153	self addButton: aLabel
4154    ]
4155
4156    addButton: aLabel receiver: anObject message: aSymbol argument: arg [
4157	"Add a button to the dialog box that, when clicked, will
4158	 cause the aSymbol one-argument selector to be sent to anObject,
4159	 passing arg as the argument of the callback.  The
4160	 caption of the button is set to aLabel."
4161
4162	<category: 'accessing'>
4163	callbacks addLast: (DirectedMessage
4164		    selector: aSymbol
4165		    arguments: {arg}
4166		    receiver: anObject).
4167	self addButton: aLabel
4168    ]
4169
4170    contents: newText [
4171	"Display newText in the entry widget associated to the dialog box."
4172
4173	<category: 'accessing'>
4174	entry setText: newText
4175    ]
4176
4177    contents [
4178	"Answer the text that is displayed in the entry widget associated
4179	 to the dialog box."
4180
4181	<category: 'accessing'>
4182	^entry getText
4183    ]
4184
4185    addButton: aLabel [
4186	<category: 'private'>
4187	| button |
4188	self buttonBox add: (button := GTK.GtkButton newWithLabel: aLabel).
4189	button show.
4190	button
4191	    connectSignal: 'clicked'
4192	    to: self
4193	    selector: #clicked:data:
4194	    userData: callbacks size
4195    ]
4196
4197    clicked: button data: data [
4198	<category: 'private'>
4199	self invokeCallback: data.
4200	self toplevel destroy
4201    ]
4202
4203    buttonBox [
4204	<category: 'private'>
4205	buttonBox isNil ifTrue: [self create].
4206	^buttonBox
4207    ]
4208
4209    create [
4210	"We do not use BDialog.  Instead, we work in the toplevel's
4211	 uiBox, because Blox makes the BDialog live into a BWindow
4212	 that provides space for other widgets."
4213
4214	<category: 'private'>
4215	| uiBox label separator |
4216	super create.
4217	uiBox := self toplevel uiBox.
4218	buttonBox := GTK.GtkHButtonBox new.
4219	buttonBox setSpacing: 5.
4220	buttonBox setLayout: GTK.Gtk gtkButtonboxEnd.
4221	uiBox
4222	    packEnd: buttonBox
4223	    expand: false
4224	    fill: false
4225	    padding: 5.
4226	buttonBox show.
4227	separator := GTK.GtkHSeparator new.
4228	uiBox
4229	    packEnd: separator
4230	    expand: false
4231	    fill: false
4232	    padding: 0.
4233	separator show.
4234
4235	"Put the GtkPlacer at the end of the list of the end-packed widgets,
4236	 which puts it above our GtkHSeparator and GtkHButtonBox."
4237	uiBox reorderChild: self toplevel connected position: -1.
4238	initInfo isNil ifTrue: [^self].
4239	label := GTK.GtkLabel new: initInfo key.
4240	label setAlignment: 0 yalign: 0.
4241	uiBox
4242	    packStart: label
4243	    expand: false
4244	    fill: false
4245	    padding: 5.
4246	label show.
4247	initInfo value isNil ifTrue: [^self].
4248	entry := GTK.GtkEntry new.
4249	entry setText: initInfo value.
4250	uiBox
4251	    packStart: entry
4252	    expand: false
4253	    fill: false
4254	    padding: 0.
4255	entry show
4256    ]
4257
4258    initInfo: assoc [
4259	<category: 'private'>
4260	initInfo := assoc
4261    ]
4262
4263    initialize: parentWidget [
4264	<category: 'private'>
4265	super initialize: parentWidget.
4266	callbacks := OrderedCollection new
4267    ]
4268
4269    center [
4270	"Center the dialog box's parent window in the screen"
4271
4272	<category: 'widget protocol'>
4273	self parent center
4274    ]
4275
4276    centerIn: view [
4277	"Center the dialog box's parent window in the given widget"
4278
4279	<category: 'widget protocol'>
4280	self parent centerIn: view
4281    ]
4282
4283    invokeCallback: index [
4284	"Generate a synthetic callback corresponding to the index-th
4285	 button being pressed, and destroy the parent window (triggering
4286	 its callback if one was established)."
4287
4288	<category: 'widget protocol'>
4289	(callbacks at: index asInteger) send
4290	"self parent destroy"
4291    ]
4292
4293    loop [
4294	"Map the parent window modally.  In other words, an event loop
4295	 is started that ends only after the window has been destroyed.
4296	 For more information on the treatment of events for modal windows,
4297	 refer to BWindow>>#modalMap."
4298
4299	<category: 'widget protocol'>
4300	self toplevel container showAll.
4301	self toplevel modalMap
4302    ]
4303]
4304
4305
4306
4307BMenuObject subclass: BMenuBar [
4308    | actionGroup uiManager |
4309
4310    <comment: 'I am the Menu Bar, the top widget in a full menu structure.'>
4311    <category: 'Graphics-Windows'>
4312
4313    add: aMenu [
4314	"Add aMenu to the menu bar"
4315
4316	<category: 'accessing'>
4317	aMenu create.
4318	^aMenu
4319    ]
4320
4321    remove: aMenu [
4322	"Remove aMenu from the menu bar"
4323
4324	<category: 'accessing'>
4325	self
4326	    tclEval: 'catch { %1 delete %2 }'
4327	    with: self connected
4328	    with: aMenu connected
4329    ]
4330
4331    uiManager [
4332	<category: 'private'>
4333	uiManager isNil ifTrue: [self create].
4334	^uiManager
4335    ]
4336
4337    create [
4338	<category: 'private'>
4339	uiManager := self parent isNil
4340		    ifTrue: [GTK.GtkUIManager new]
4341		    ifFalse: [self toplevel uiManager].
4342	self uiManager
4343	    addUi: self uiManager newMergeId
4344	    path: '/'
4345	    name: self name
4346	    action: self name
4347	    type: GTK.Gtk gtkUiManagerMenubar
4348	    top: false.
4349	self parent isNil ifFalse: [self parent menu: self].
4350	actionGroup := GTK.GtkActionGroup new: 'MenuActions'.
4351	self uiManager insertActionGroup: actionGroup pos: 0
4352    ]
4353
4354    exists [
4355	<category: 'private'>
4356	^uiManager notNil
4357    ]
4358
4359    name [
4360	"answer the name"
4361
4362	<category: 'private'>
4363	^'MainMenu'
4364    ]
4365
4366    path [
4367	"answer the menu path"
4368
4369	<category: 'private'>
4370	^'/MainMenu'
4371    ]
4372
4373    actionGroup [
4374	"answer an actiongroup that menu entries should go in"
4375
4376	<category: 'private'>
4377	actionGroup isNil ifTrue: [self create].
4378	^actionGroup
4379    ]
4380]
4381
4382
4383
4384BMenuObject subclass: BMenu [
4385    | connected label |
4386
4387    <comment: 'I am a Menu that is part of a menu bar.'>
4388    <category: 'Graphics-Windows'>
4389
4390    BMenu class >> new: parent label: label [
4391	"Add a new menu to the parent window's menu bar, with `label' as
4392	 its caption (for popup menus, parent is the widget over which the
4393	 menu pops up as the right button is pressed)."
4394
4395	<category: 'instance creation'>
4396	^(self basicNew)
4397	    initialize: parent;
4398	    label: label;
4399	    yourself
4400    ]
4401
4402    label [
4403	"Answer the value of the label option for the widget.
4404
4405	 Specifies a string to be displayed inside the widget. The way in which the
4406	 string is displayed depends on the particular widget and may be determined
4407	 by other options, such as anchor. For windows, this is the title of the window."
4408
4409	<category: 'accessing'>
4410	^label
4411    ]
4412
4413    label: value [
4414	"Set the value of the label option for the widget.
4415
4416	 Specifies a string to be displayed inside the widget. The way in which the
4417	 string is displayed depends on the particular widget and may be determined
4418	 by other options, such as anchor. For windows, this is the title of the window."
4419
4420	"TODO: save the merge id we used, remove the ui, and re-add the ui with the new label"
4421
4422	<category: 'accessing'>
4423	label := value
4424    ]
4425
4426    addLine [
4427	"Add a separator item at the end of the menu"
4428
4429	<category: 'callback registration'>
4430	^self addMenuItemFor: #() notifying: self	"self is dummy"
4431    ]
4432
4433    addMenuItemFor: anArray notifying: receiver [
4434	"Add a menu item described by anArray at the end of the menu.
4435	 If anArray is empty, insert a separator line.  If anArray
4436	 has a single item, a menu item is created without a callback.
4437	 If anArray has two or three items, the second one is used as
4438	 the selector sent to receiver, and the third one (if present)
4439	 is passed to the selector."
4440
4441	"Receiver will be sent the callback messages.  anArray
4442	 is something that responds to at: and size.  Possible types are:
4443	 #()		insert a seperator line
4444	 #(name)	        create a menu item with name, but no callback
4445	 #(name symbol)     create a menu item with the given name and
4446	 no parameter callback.
4447	 #(name symbol arg) create a menu item with the given name and
4448	 one parameter callback."
4449
4450	<category: 'callback registration'>
4451	| item |
4452	item := self newMenuItemFor: anArray notifying: receiver.
4453	self exists ifFalse: [self create].
4454	item create
4455    ]
4456
4457    callback: receiver using: selectorPairs [
4458	"Add menu items described by anArray at the end of the menu.
4459	 Each element of selectorPairs must be in the format described
4460	 in BMenu>>#addMenuItemFor:notifying:.  All the callbacks will
4461	 be sent to receiver."
4462
4463	<category: 'callback registration'>
4464	selectorPairs do: [:pair | self addMenuItemFor: pair notifying: receiver]
4465    ]
4466
4467    empty [
4468	"Empty the menu widget; that is, remove all the children"
4469
4470	<category: 'callback registration'>
4471	self tclEval: self connected , ' delete 0 end'.
4472	children := OrderedCollection new.
4473	childrensUnderline := nil
4474    ]
4475
4476    destroy [
4477	"Destroy the menu widget; that is, simply remove ourselves from
4478	 the parent menu bar."
4479
4480	<category: 'callback registration'>
4481	self parent remove: self
4482    ]
4483
4484    addChild: menuItem [
4485	<category: 'private'>
4486	self exists ifFalse: [self create].
4487	menuItem create.
4488	^menuItem
4489    ]
4490
4491    actionGroup [
4492	"answer the menu action group"
4493
4494	<category: 'private'>
4495	^self parent actionGroup
4496    ]
4497
4498    name [
4499	"answer the name the menu should get"
4500
4501	<category: 'private'>
4502	^self label , 'Menu'
4503    ]
4504
4505    menuLabel [
4506	"answer the label the menu should get"
4507
4508	<category: 'private'>
4509	^'_' , self label
4510    ]
4511
4512    path [
4513	"answer the path for the menu"
4514
4515	<category: 'private'>
4516	^self parent path , '/' , self name
4517    ]
4518
4519    uiManager [
4520	"answer the ui manager"
4521
4522	<category: 'private'>
4523	^self parent uiManager
4524    ]
4525
4526    connected [
4527	<category: 'private'>
4528	connected isNil ifTrue: [connected := self uiManager getWidget: self path].
4529	^connected
4530    ]
4531
4532    create [
4533	<category: 'private'>
4534	| s menu u |
4535	self actionGroup addAction: (GTK.GtkAction
4536		    new: self name
4537		    label: self menuLabel
4538		    tooltip: nil
4539		    stockId: nil).
4540	self uiManager
4541	    addUi: self uiManager newMergeId
4542	    path: self parent path
4543	    name: self name
4544	    action: self name
4545	    type: GTK.Gtk gtkUiManagerMenu
4546	    top: false.
4547	self childrenDo: [:each | each create]
4548    ]
4549
4550    onDestroy: object data: data [
4551	<category: 'private'>
4552	self destroyed
4553    ]
4554
4555    exists [
4556	<category: 'private'>
4557	^self connected notNil
4558    ]
4559
4560    initialize: parentWidget [
4561	<category: 'private'>
4562	super initialize: parentWidget.
4563	label := ''
4564    ]
4565
4566    newMenuItemFor: pair notifying: receiver [
4567	<category: 'private'>
4568	| item size |
4569	size := pair size.
4570	pair size = 0 ifTrue: [^BMenuItem new: self].
4571	(size >= 2 and: [pair last isArray])
4572	    ifTrue:
4573		[size := size - 1.
4574		item := BMenu new: self label: (pair at: 1).
4575		pair last
4576		    do: [:each | item add: (item newMenuItemFor: each notifying: receiver)]]
4577	    ifFalse: [item := BMenuItem new: self label: (pair at: 1)].
4578	size = 1 ifTrue: [^item].
4579	size = 2 ifTrue: [^item callback: receiver message: (pair at: 2)].
4580	^item
4581	    callback: receiver
4582	    message: (pair at: 2)
4583	    argument: (pair at: 3)
4584    ]
4585]
4586
4587
4588
4589BMenu subclass: BPopupMenu [
4590    | attachedWidget |
4591
4592    <comment: 'I am a class that provides the ability to show popup menus when the
4593right button (Button 3) is clicked on another window.'>
4594    <category: 'Graphics-Windows'>
4595
4596    PopupMenuBar := nil.
4597    PopupMenus := nil.
4598
4599    BPopupMenu class >> initializeOnStartup [
4600	<category: 'private - accessing'>
4601	PopupMenuBar := nil.
4602	PopupMenus := WeakKeyIdentityDictionary new
4603    ]
4604
4605    BPopupMenu class >> popupMenuBar [
4606	"answer the menubar this menu conceptually exists in"
4607
4608	<category: 'private - accessing'>
4609	PopupMenuBar isNil ifTrue: [PopupMenuBar := BMenuBar new: nil].
4610	^PopupMenuBar
4611    ]
4612
4613    initialize: parentWindow [
4614	"TODO: refactor so that 'self parent' is parentWindow.  Start by
4615	 writing (and using!) a menuBar method in BMenu and overriding it here."
4616
4617	<category: 'private'>
4618	self class popupMenuBar exists ifFalse: [self class popupMenuBar create].
4619	super initialize: self class popupMenuBar.
4620	attachedWidget := parentWindow.
4621	PopupMenus at: parentWindow ifPresent: [:menu | menu destroy].
4622	PopupMenus at: attachedWidget put: self
4623    ]
4624
4625    create [
4626	<category: 'private'>
4627	super create.
4628	attachedWidget connected
4629	    connectSignal: 'button-press-event'
4630	    to: self
4631	    selector: #onPopup:event:data:
4632	    userData: nil
4633    ]
4634
4635    destroyed [
4636	<category: 'private'>
4637	super destroyed.
4638	attachedWidget := nil
4639    ]
4640
4641    onPopup: widget event: event data: data [
4642	<category: 'private'>
4643	| buttonEv |
4644	buttonEv := event castTo: GTK.GdkEventButton type.
4645	buttonEv button value = 3 ifFalse: [^false].
4646	self connected getSubmenu
4647	    popup: nil
4648	    parentMenuItem: nil
4649	    func: nil
4650	    data: nil
4651	    button: 3
4652	    activateTime: buttonEv time value.
4653	^true
4654    ]
4655
4656    popup [
4657	"Generate a synthetic menu popup event"
4658
4659	<category: 'widget protocol'>
4660	self connected getSubmenu
4661	    popup: attachedWidget connected
4662	    parentMenuItem: nil
4663	    func: nil
4664	    data: nil
4665	    button: 0
4666	    activateTime: GTK.Gtk getCurrentEventTime
4667    ]
4668]
4669
4670
4671
4672BMenuObject subclass: BMenuItem [
4673    | index |
4674
4675    <comment: 'I am the tiny and humble Menu Item, a single command choice in the
4676menu structure. But if it wasn''t for me, nothing could be done...
4677eh eh eh!!'>
4678    <category: 'Graphics-Windows'>
4679
4680    BMenuItem class >> new: parent [
4681	"Add a new separator item to the specified menu."
4682
4683	<category: 'instance creation'>
4684	^self basicNew initialize: parent
4685    ]
4686
4687    BMenuItem class >> new: parent label: label [
4688	"Add a new menu item to the specified menu (parent) , with `label'
4689	 as its caption."
4690
4691	<category: 'instance creation'>
4692	^self basicNew initialize: parent label: label
4693    ]
4694
4695    label [
4696	"Answer the value of the label option for the widget.
4697
4698	 Specifies a string to be displayed inside the widget. The way in which the
4699	 string is displayed depends on the particular widget and may be determined
4700	 by other options, such as anchor. For windows, this is the title of the window."
4701
4702	<category: 'accessing'>
4703	^self properties at: #label
4704    ]
4705
4706    label: value [
4707	"Set the value of the label option for the widget.
4708
4709	 Specifies a string to be displayed inside the widget. The way in which the
4710	 string is displayed depends on the particular widget and may be determined
4711	 by other options, such as anchor. For windows, this is the title of the window."
4712
4713	<category: 'accessing'>
4714	(self properties at: #label) isNil
4715	    ifTrue: [^self error: 'no label for separator lines'].
4716	self parent exists
4717	    ifTrue:
4718		[self
4719		    tclEval: self container , ' entryconfigure ' , self connected , ' -label '
4720			    , value asTkString].
4721	self properties at: #label put: value
4722    ]
4723
4724    actionGroup [
4725	"answer the menu action group"
4726
4727	<category: 'private'>
4728	^self parent actionGroup
4729    ]
4730
4731    uiManager [
4732	<category: 'private'>
4733	^self parent uiManager
4734    ]
4735
4736    name [
4737	"answer the name of the item"
4738
4739	<category: 'private'>
4740	^self label
4741    ]
4742
4743    menuLabel [
4744	"answer the gtk label"
4745
4746	<category: 'private'>
4747	^'_' , self name
4748    ]
4749
4750    path [
4751	"answer the gtk uiManager path"
4752
4753	<category: 'private'>
4754	^self parent path , '/' , self name
4755    ]
4756
4757    create [
4758	<category: 'private'>
4759	| s u mergeid action |
4760	self name isNil
4761	    ifTrue:
4762		[mergeid := self uiManager newMergeId.
4763		self properties at: #label put: 'separator' , (mergeid printString: 10).
4764		self uiManager
4765		    addUi: mergeid
4766		    path: self parent path
4767		    name: self name
4768		    action: nil
4769		    type: GTK.Gtk gtkUiManagerSeparator
4770		    top: false]
4771	    ifFalse:
4772		[action := GTK.GtkAction
4773			    new: self name
4774			    label: self menuLabel
4775			    tooltip: 'FIXME'
4776			    stockId: nil.
4777
4778		"FIXME, when to use stock options?  GTK.Gtk gtkStockOpen."
4779		action
4780		    connectSignal: 'activate'
4781		    to: self
4782		    selector: #activated:data:
4783		    userData: nil.
4784
4785		"FIXME when to trigger accelerators"
4786		"self actionGroup addActionWithAccel: foo accelerator: '<control>O'."
4787		self actionGroup addAction: action.
4788		self uiManager
4789		    addUi: self uiManager newMergeId
4790		    path: self parent path
4791		    name: self name
4792		    action: self name
4793		    type: GTK.Gtk gtkUiManagerMenuitem
4794		    top: false]
4795    ]
4796
4797    activated: action data: userData [
4798	<category: 'private'>
4799	self invokeCallback
4800    ]
4801
4802    initialize: parentWidget [
4803	"initialize a separator item"
4804
4805	<category: 'private'>
4806	super initialize: parentWidget.
4807	self properties at: #label put: nil
4808    ]
4809
4810    initialize: parentWidget label: label [
4811	<category: 'private'>
4812	| s |
4813	super initialize: parentWidget.
4814	self properties at: #label put: label.
4815	parent exists ifTrue: [self create]
4816    ]
4817]
4818
4819
4820
4821BMenuItem subclass: BCheckMenuItem [
4822    | status |
4823
4824    <comment: 'I am a menu item which can be toggled between two states, marked
4825and unmarked.'>
4826    <category: 'Graphics-Windows'>
4827
4828    BCheckMenuItem class >> new: parent [
4829	<category: 'instance creation'>
4830	self shouldNotImplement
4831    ]
4832
4833    invokeCallback [
4834	"Generate a synthetic callback"
4835
4836	<category: 'accessing'>
4837	self properties removeKey: #value ifAbsent: [].
4838	self callback isNil ifFalse: [self callback send]
4839    ]
4840
4841    value [
4842	"Answer whether the menu item is in a selected (checked) state."
4843
4844	<category: 'accessing'>
4845	^self properties at: #value ifAbsentPut: [false]
4846    ]
4847
4848    value: aBoolean [
4849	"Set whether the button is in a selected (checked) state and
4850	 generates a callback accordingly."
4851
4852	<category: 'accessing'>
4853	self properties at: #value put: aBoolean.
4854	self tclEval: 'set ' , self variable , self valueString.
4855	self callback isNil ifFalse: [self callback send]
4856    ]
4857
4858    create [
4859	<category: 'private'>
4860	super create.
4861	self
4862	    tclEval: '%1 entryconfigure %2 -onvalue 1 -offvalue 0 -variable %3'
4863	    with: self container
4864	    with: self connected
4865	    with: self variable
4866    ]
4867
4868    destroyed [
4869	"Private - The receiver has been destroyed, clear the corresponding
4870	 Tcl variable to avoid memory leaks."
4871
4872	<category: 'private'>
4873	self tclEval: 'unset ' , self variable.
4874	super destroyed
4875    ]
4876
4877    valueString [
4878	<category: 'private'>
4879	^self value ifTrue: [' 1'] ifFalse: [' 0']
4880    ]
4881
4882    variable [
4883	<category: 'private'>
4884	^'var' , self connected , self container copyWithout: $.
4885    ]
4886
4887    widgetType [
4888	<category: 'private'>
4889	^'checkbutton'
4890    ]
4891]
4892
4893
4894
4895"-------------------------- BEdit class -----------------------------"
4896
4897
4898
4899"-------------------------- BLabel class -----------------------------"
4900
4901
4902
4903Eval [
4904    BLabel initialize
4905]
4906
4907
4908
4909"-------------------------- BButton class -----------------------------"
4910
4911
4912
4913"-------------------------- BForm class -----------------------------"
4914
4915
4916
4917"-------------------------- BContainer class -----------------------------"
4918
4919
4920
4921"-------------------------- BRadioGroup class -----------------------------"
4922
4923
4924
4925"-------------------------- BRadioButton class -----------------------------"
4926
4927
4928
4929"-------------------------- BToggle class -----------------------------"
4930
4931
4932
4933"-------------------------- BImage class -----------------------------"
4934
4935
4936
4937"-------------------------- BList class -----------------------------"
4938
4939
4940
4941"-------------------------- BWindow class -----------------------------"
4942
4943
4944
4945"-------------------------- BTransientWindow class -----------------------------"
4946
4947
4948
4949"-------------------------- BPopupWindow class -----------------------------"
4950
4951
4952
4953"-------------------------- BDialog class -----------------------------"
4954
4955
4956
4957"-------------------------- BMenuBar class -----------------------------"
4958
4959
4960
4961"-------------------------- BMenu class -----------------------------"
4962
4963
4964
4965"-------------------------- BPopupMenu class -----------------------------"
4966
4967
4968
4969"-------------------------- BMenuItem class -----------------------------"
4970
4971
4972
4973"-------------------------- BCheckMenuItem class -----------------------------"
4974
4975