1 Introduction

1.1 What is Tix

1.1.1 Tix for Application Programmers

The acronym Tix stands for Tk Interface Extension. Tix is different things for different people.

If you are a GUI application programmer, that is, if you earn a living by building graphical applications, you will appreciate Tix as a library of mega-widgets: widgets made out of other widgets. To use a crude analogy, if the widgets in the standard TK library are bricks and mortars for a builder, the mega-widgets in the Tix library are walls, windows or even pre-build kitchens. Of course, these ``bigger components'' are themselves made of bricks and mortars, but it will take much less effort to put them together than planting bricks on top of each other.

The Tix widgets not only help you speed up the development of your applications, they also help you in the design process. Since the standard Tk widgets are too primitive, they force you to think of your house as, by using the same analogy, millions of bricks. With the help of the Tix mega-widgets, you can design your application is a more structural and coherent manner.

Moreover, the Tix library provides a rich set of widgets. Figure 1-1 shows all Tix widgets -- there are more than 40 of them! Although the standard Tk library has many useful widgets, they are far from complete. The Tix library provides most of the commonly needed widgets that are missing from standard Tk: FileSelectBox, ComboBox, Control (a.k.a. SpinBox) and an assortment of scroll-able widgets. Tix also includes many more widgets that are generally useful in a wide range of applications: NoteBook, FileEntry, PanedWindow, MDIWindow, etc.

With all these new widgets, you can introduce new interaction techniques into applications, creating more useful and more intuitive user interfaces. You can design your application by choosing the most appropriate widgets to match the special needs of your application and users.

(Figure 1-1) The Class Hierarchy of Tix Widgets

1.1.2 Tix for Widget Developers

On the other hand, if you are a widget developer, Tix provides an object oriented programming environment, the Tix Intrinsics, that is carefully designed for the development of mega-widgets. If you have developed widgets in C, you will know how slow and painful such a process would be. In recognition of the difficulties in widget development, the Tix Intrinsics includes many tools that dramatically cuts down the efforts required to develop new widgets. With the Tix Intrinsics, the rapid prototyping/development of widgets is finally feasible: you can write a new widgets in the matter of hours or even minutes.

With the Tix Intrinsics, you widget code can readily become reusable. Tix also provides a set of rules and mechanisms that allow you to develop widgets that are inter-operable with other widgets.

In Part I of this manual, we will talk about using the Tix widgets. The discussion of writing new Tix widgets will be carried out in Part II.

1.2 Getting Started: the TixControl Widget

Pre-requisites: you should be familiar with Tk widgets and programming, or read the Tk book along with this book

Before delving into the deep philosophy of the Tix widgets, let us first have a quick example to demonstrate the usefulness and convenience of an Tix widget: the TixControl is basically an entry widget that displays a value. Next to the entry, there are two up and down arrow buttons for you to adjust the value inside the entry widget.

1.2.1 Creating a TixControl Widget

The following code demonstrates how to create a TixControl widget and specify its options:

    tixControl .lawyers -label Lawyers: -max 10 -min 0
    .lawyers config -integer true -step 2
This example creates a TixControl widget that let us to select the numbers of lawyers we wish to be allowed in this country. (Figure
1-2 )

Let us examine the options: the -label option specifies a caption for this widget. The -max option specifies the maximum number of lawyers we can choose. The -min option specifies the minimum number of lawyers we can choose: although we would love to enter a negative number, reality dictate that the lower limit must be zero. The -integer option indicates that the number of lawyers must be an integer; that is, we respect the lawyers' rights not to be chopped up into decimal points. Finally, since lawyers seem to go in pairs, we set the -step option to 2, which indicates that when we press the up/down arrow buttons, we want the number of lawyers to go up and down by two each time.

(Figure 1-2) The TixControl Widget

As shown in the example, you can create and manipulate a Tix widget in the same manner as the standard Tk widgets. The options of the widget can be specified during the creation of the widget. Alternatively, they can be changed by the configure widget command. In addition, options can also be specified in the option database or as X resources. Here is an example that produces the same result as the previous code fragment:

    option add *lawyers.max 10
    option add *lawyers.min 0
    tixControl .lawyers -label Lawyers: -integer true
    .lawyers config -step 2

In figure 1-3 , you can see the composition of TixControl: it is made out of a label widget, an entry widget and two button widgets. Widgets that are composed of other widgets, like TixControl, are called mega-widgets. Most widgets in the Tix library are mega-widgets (xx: and as you know this book is about them!).

(Figure 1-3) The Composition of TixControl

1.2.2 Accessing The Value of a TixControl Widget

The TixControl widget allows the user to input a value. There are several ways to read this value in your program. First of all, TixControl stores the current value in the -value option. You can use query the -value option by calling the command

    .c cget -value

this command will return the current value of the tixContro widget .c. The following command sets the value of the widget to a new number (100):

    .c config -value 100
The second way to access the value of TixControl is to use the -variable option. This options instructs the TixControl widget to store the its value into a global variable so that you can read it at any time. Also, by assigning a new value to this global variable, you can change the value of the TixControl widget. Here is an example:

    .c config -variable myvar
    set myvar 100
In some situations, you may want to be informed immediately when the value of the TixControl widget changes. To accomplish this, you can use the -command option. The following line causes the TCL procedure valueChanged to be called whenever the value of .c changes:

    tixControl .c -command valueChanged

Disabling Callbacks Temporarily

Now, if you want to change a value from within the program, you have to disable the callback. The reason is that the callback runs whenever you (as well as the user) makes a change. In particular, if you make a change within the callback procedure and forget to disable the callback, it will recursively call itself and enter an infinite loop. To avoid this problem, you should use the -disablecallback option. Here is an example:

    tixControl .c -command addOne

proc addOne {value} { .c config -disablecallback true .c config -value [incr value] .c config -disablecallback false }

The procedure addOne adjusts the value of .c by one whenever the user enters a new value into .c. Notice that it is necessary to set -disablecallback here or otherwise addOne will be infinitely recursed! That is because addOne is called every time the value changes, either by the user or by the program.

1.2.3 Validating User Inputs

Sometimes it may be necessary to check the user input against certain criteria. For example, you may want to allow only even numbers in a TixControl widget. To do this, you can use the -validatecmd option, which specifies a Tcl command to call whenever the user enters a new value. Here is an example:

    tixControl .c -value 0 -step 2 -validatecmd evenOnly

proc evenOnly {value} { return [expr $value - ($value %2)] }

The value parameter to evenOnly is the new value entered by the user. The evenOnly procedure makes sure that the new value is even by returning a modified, even number. The Tcl command specified by the -validatecmd must return a value which it deems valid and this value will be stored in the -value option of the TixControl widget.

1.3 Accessing The Components Inside Mega Widgets

1.3.1 Subwidgets

As we have seen in section 1.2.1 , the TixControl widget is composed of several widgets: one label widget, one entry widget and two button widgets. These ``widgets inside mega-widgets'' are called subwidgets in the Tix terminology. We will often have the need to access these subwidgets. For example, sometimes we need to change the configuration options of the subwidgets. In other cases, we may need to interact with the subwidgets directly.

1.3.2 Subwidget Names

Each subwidget inside a mega is identified by a subwidget name. Naturally, the label and entry subwidgets inside a TixSelect widget are called label and entry, respectively. The two button widgets are called incr and decr because they are used to increment and decrement the value inside the TixControl widget (see figure 1-4 ).

(Figure 1-4) Subwidgets inside TixControl Widget

1.3.3 The subwidget Method

All Tix mega-widgets support the subwidget method. This method takes at least one argument, the name of a subwidget. When you pass only one argument to this method, it returns the pathname of the subwidget which is identified by that name. For example, if .c is the pathname of a TixControl widget, the command:

  .c subwidget entry
returns the pathname of the entry subwidget, which is .c.frame.entry in this case.

If you call the subwidget method with additional arguments, the widget command of the specified subwidget will be called with these arguments. For example, if .c is, again, the pathname of a TixControl widget, the command:

  .c subwidget entry configure -bg gray
will cause the widget command of the entry subwidget of .c to be called with the arguments configure -bg gray. So actually this command will be translated into the following call:

  .c.frame.entry configure -bg gray
which calls the configure method of the entry subwidget with the arguments -bg gray and changes its background color to gray.

We can call the subwidget method with other types of arguments to access different methods of the specified subwidget. For example, the following call:

  .c subwidget entry icursor end
calls the icursor method of the entry subwidget with the argument end and sets the insert cursor of the entry subwidget to the end of its input string.

1.3.4 Chaining the subwidget Method

Some Tix mega-widgets may have subwidgets that in turn contain subwidgets. For example, the TixExFileSelectDialog (section 5.1.3 ) widget contains a TixExFileSelectBox subwidget called fsbox, which in turn contains a TixComboBox (section 1.4 ) subwidget called dir. If we want to access the dir subwidget, we can just ``chain'' the subwidget method. For example, if we have a TixExFileSelectDialog called .file, the following command will return the pathname of the dir subwidget of the fsbox subwidget of .file:

  .file subwidget fsbox subwidget dir
Moreover, the following command configures the dir subwidget to have a border of the groove type with a border width of 2 pixels:

  .file subwidget fsbox subwidget dir configure -bd 2 -relief groove

The chaining of the subwidget command can be applied for arbitrarily many levels, depending whether your widget has a subwidget that has a subwidget that has a subwidget that has a subwidget ... and so on.

1.3.5 Configuring Subwidget Options Using the -options Switch

As we have seen above, we can use commands like `` subwidget name configure ...'' to set the configuration options of subwidgets. However, this can get quite tedious if we want to configure many options of many subwidgets.

There is a more convenient and terse way to configure the subwidget options without using the subwidget method: the -options switch. All Tix mega-widgets support the -option switch, which can be used during the creation of the mega-widget.

tixControl .income -label "Income: " -variable income -options {
    label.width       8
    label.anchor      e
    entry.width       10
    entry.borderWidth 3
tixControl .age    -label "Age: "    -variable age    -options {
    label.width       8
    label.anchor      e
    entry.width       10
    entry.borderWidth 3
pack .income .age -side top

(Figure 1-5) Using the -options switch

Unaligned Labels

Aligned Labels

(Figure 1-6) Using the -options Switch to Align the Labels

The use of the -options switch is illustrated in program 1-5 , which creates two TixControl widgets for the user to enter his income and age. Because of the different sizes of the labels of these two widgets, if we create them haphazardly, the output may look like fig 1-6 .

To avoid this problem, we set the width of the label subwidgets of the .income and .age widgets to be the same (8 characters wide) and set their -anchor option to e (flushed to right), so that the labels appear to be well-aligned. Program 1-5 also does other things such as setting the entry subwidgets to have a width of 10 characters and a border-width of 3 pixels so that they appear wider and ``deeper''. A better result is shown in figure 1-6 . As we can see from program 1-5 , the value for the -options switch is a list of one or more pairs of

subwidget-option-spec value ..

subwidget-option-spec is in the form subwidget-name . option-name. For example, label.anchor identifies the anchor option of the label subwidget, entry.width identifies the width option of the entry subwidget, and so on.

Notice we must use the name of the option, not the command-line switch of the option. For example, the option that specifies the border-width of the entry subwidget has the command-line switch -borderwidth but its name is borderWidth (notice the capitalization on the name but not on the command-line switch). Therefore, we have used the capitalized version of `` entry.borderWidth 3'' in program 1-5 and not `` entry.borderwidth 3''. To find out the names of the options of the respective subwidgets, please refer to their manual pages.

1.3.6 Configuring Subwidget Options Using the Tk Option Database

The -options switch is good if you want to specify subwidget options for one or a few mega-widgets. If you want to specify the subwidget for many mega-widgets, it is easier to use the Tk Option Database.

Options in the Tk Option Database can be specified using the option command and the pathname of the widget. For all the Tix mega-widgets, it is guaranteed that the pathname of their subwidgets ends with the name of the subwidgets. For example, if we have a mega widget called .a.b.megaw and it has a subwidget whose name is subw, then we can be sure that the pathname of the subwidget will be something like

Therefore, if we want to specify options for it in the Option Database, we can issue commands like:

  option add *a.b.megaw*subw.option1 value1
  option add *a.b.megaw*subw.option2 value2
Notice that it will be wrong to issue the commands as:

  option add *a.b.megaw.subw.option1 value1
  option add *a.b.megaw.subw.option2 value2
because in general we will not know whether the subwidget is an immediate child window of .a.b.megaw (such a decision is left to the mega-widget implementor and may vary in different versions of the same mega-widget).

Program 1-7 demonstrates how the Tk Option Database can be used to achieve the same effect as program 1-5 .

option add *TixControl*label.width       8
option add *TixControl*label.anchor      e
option add *TixControl*entry.width       10
option add *TixControl*entry.borderWidth 3

tixControl .income -label "Income: " -variable income tixControl .age -label "Age: " -variable age

pack .income .age -side top

(Figure 1-7) Using the Tk Option Database in Place of the -options switch

1.3.7 Caution: Restricted Access

In the current implementation of Tix, there is no limits on how you can access the options of the subwidgets. However, many options of the subwidgets may be already used by the mega-widget in special ways. For example, the -textvariable option of the entry subwidget of TixControl may be used to store some private information for the mega widget. Therefore, you should access the options of the subwidgets with great care. In general you should only access those options that affect the appearance of the subwidgets (such as -font or -foreground) and leave everything else intact. () { In future versions of Tix, there will be explicit restrictions on which subwidget options you can access. Errors will be generated if you try to access restricted subwidget options}

1.4 Another Tix Widget: TixComboBox

The TixComboBox widget is very similar to the ComboBox widgets available in MS Windows and Motif 2.0. A TixComboBox consists of an entry widget and a listbox widget. Usually, the ComboBox contains a list of possible values for the user to select. The user may also choose an alternative value by typing it in the entry widget. Figure 1-8 shows two ComboBoxes for the user to choose fonts and character sizes. You can see fro the figure that a listbox is popped down from the ComboBox for fonts for the user to choose among a list of possible fonts.

(Figure 1-8) The TixComboBox Widget

1.4.1 Creating a TixComboBox Widget

tixComboBox .c -label "Animal:" -editable true
.c insert end cat
.c insert end dog
.c insert end pig

(Figure 1-9) Creating a ComboBox

In program 1-9 , we set up a ComboBox .c for the user to select an animal to play with. If the user is just a dull person like you and me, he would just press the arrow button and select a pre-designated animal such as ``dog''. However, if he wants to try something new, he could type ``micky'' or ``sloth'' into the entry widget and he will get to play with his favorite animal.

Of course, sometimes we don't want too many sloths around us and we want to limit the range of the user's selections. In this case we can do one of two things. First, we can set the -editable option to false so that the user cannot type in the entry widget at all. Alternatively, we can use the -validatecmd option (see section 1.4.3 ) to check input the input.

1.4.2 Controlling the Style of the TixComboBox

The TixComboBox widget can appear in many different styles. If we set the -dropdown option to true (which is the default), the listbox will only appear when the user presses the arrow button. When -dropdown is set to false, the listbox is always shown and the arrow button will disappear because it is not needed anymore.

There is also an option called -fancy. It is set to false by default. When set to true, a tick button and a cross button will appear next to the entry widget. The tick button allows you to select again the value that's already in the ComboBox. If you press the cross button, the entry widget will be cleared.

1.4.3 Static Options

The -dropdown and -fancy options are so-called ``static options''. They can only be set during the creation of the ComboBox. Hence this code is valid:

    tixComboBox .c -dropdown true

But the following code will generate an error because it attempts to set the -dropdown option after the ComboBox has already been created.

    TixComboBox .c
    .c config -dropdown true

The restrictions of the static options, although annoying, nevertheless make sense because we don't want our interface to suddenly change its style. If sometimes a button is there and sometimes it disappear all by itself, that will certainly create a lot of confusion and makes the user wonder why he should buy our software. Also, as you will see in chapter 6 , having some static options will make the life of widget writers a lot easier.

Accessing the value of the ComboBox is very similar to accessing the value of the TixControl widget. The ComboBox has these four options, which we discussed in section 1.2.2 : -value, -variable, -command and -validatecmd. You can use these four options to access the user input and respond to user actions in exactly the same way as discussed in section 1.2.2 .

1.4.4 Monitoring the User's Browsing Actions

When the user drags the mouse pointer over the listbox, the listbox item under the pointer will be highlighted and a ``browse event'' will be generated. If you want to keep track of what items the user has browses through, you can use the -browsecmd option. Here is an example:

    tixComboBox .c -browsecmd mybrowse

proc mybrowse {item} { puts "user has browsed $item" }

When the Tcl command specified by the -browsecmd option is called, it will be called with one parameter: the current item that the user has highlighted.

The -browsecmd is useful because it gives the user the possibility of temporarily seeing the results of several choices before committing to a final choice.

For example, we can list a set of image files in a ComboBox. When the user single-clicks on an item on the ComboBox, we want to show a simplified view of that image. After the user has browsed through several images, he can finally decide on which image he wants by double-clicking on that item in the listbox.

The following is some pseudo Tcl code that does this. Please notice that the -browsecmd procedure is called every time the user single-clicks on an item or drags the mouse pointer in the listbox. The -command procedure is only called when the user double-clicks on an item.

tixComboBox .c -dropdown false -browsecmd show_simple -command load_fullsize 
.c insert end "/pkg/images/flowers.gif"
.c insert end "/pkg/images/jimmy.gif"
.c insert end "/pkg/images/ncsa.gif"

proc show_simple {filename} { # Load in a simplified version of $filename }

proc load_fullsize {filename} { # Load in the full size image in $filename }

As we shall see, all Tix widgets that let us do some sort of selections have the -browsecmd option. The -browsecmd option allows us to respond to user events in a simple, straight-forward manner. Of course, you can do the same thing with the Tk bind command, but you don't want to do that unless you are very fond of things like <Control-Shift-ButtonRelease-1> and "%x %X $w %W %w".

1.5 The TixSelect Widget

The TixSelect widget figure 1-10 provides you the same kind of facility that is available with the Tk radiobutton and checkbutton widgets. That is, TixSelect allows the user to select one or a few values out of many choices. However, TixSelect is superior because it allows you to layout the choices in much less space than what is required by the Tk radiobutton widgets. Also, TixSelect supports complicated selection rules. Because of these reasons, TixSelect is a primary choice for implementing toolbar buttons, which often have strict space requirements and complicated selection rules.

(Figure 1-10) The TixSelect Widget

1.5.1 Creating A TixSelect Widget

Program 1-11 shows how to create a TixSelect widget. At line 1 of program 1-11 , we create a TixSelect using the the tixSelect command.

  tixSelect .fruits -label "Fruits: " -orientation horizontal
  .fruits add apple  -text Apple  -width 6
  .fruits add orange -text Orange -width 6
  .fruits add banana -text Banana -width 6
  pack .fruits

(Figure 1-11) Creating a TixSelect Widget

Label and Orientation

As shown in program 1-11 , with the -label option, we can put a label next to the button subwidgets as the caption of the TixSelect widget. We can also control the layout of the button subwidgets using the -orientation option. The -orientation option can have two values: horizontal (the default value) or vertical, and the buttons are lied up accordingly. Figure 1-12 shows the output of a TixSelect widget whose -orientation is set to vertical.

Creating the Button Subwidgets and Configuring Their Appearance

After we have created the TixSelect widget, we can create the button subwidgets inside the TixSelect widget by the add widget command (lines 2-4 of program 1-11 ).

The first argument to the add command is the name of the button subwidget. Additional arguments can be given in option-value pairs to configure the appearance of the button subwidget. These option-value pairs can be any of those accepted by a normal TK button widget. As shown in program 1-11 , we use the -text option to put appropriate text strings over the three button subwidgets.

Notice that we also set the -width option of all the button subwidgets to 6 characters. This way, the three buttons will have the same width. If we didn't set the -width option for the button widgets, they will have different widths, depending on their text string, and the result would look less esthetically pleasing than buttons with same widths.

The output of program 1-11 is shown in figure 1-12

Horizontal Orientation

Vertical Orientation

(Figure 1-12) The TixSelect Widget

Accessing the Button Subwidgets

We have already seen the concept of subwidgets and how they can be accessed in section 1.3.1 --- when we create a Tix mega-widget, some subwidgets are created for us automatically. For example, the label and entry subwidgets inside a TixControl widget. We can access these subwidgets in a multitude of ways, including using the subwidget method.

One thing about the subwidgets we saw in section 1.3.1 is that they are ``static'', meaning they are created when the mega-widget is created and they remain there for the whole lifetime of the mega-widget.

The TixSelect widget takes us to a new concept: dynamic subwidgets are subwidgets that can be created on-the-fly. After we add a new button into the TixSelect widget, we get a new subwidget. The name of this new subwidget is given by the first parameter passed to the add method. As the following code demonstrates, we can access this new subwidget using the subwidget method:

tixSelect .s
.s add apple  -text Apple
.s add orange -text Orange
#   Mmmm..., let's make the widget look more educated
#   by using French words
.s subwidget apple  config -text Pomme
.s subwidget orange config -text Orange

1.5.2 Specifying Selection Rules

For simple selection rules, you can use the -allowzero and -radio options. The -allowzero option specifies whether the user can select none of the choices inside the TixSelect widget. The -radio option controls how many buttons can be selected at once: when set to true, the user can select only one button at a time; when set to false, the user can select as many buttons as he desires.

With these two options, we can write a program using two TixSelect widgets for little Jimmy to fill up his lunch box. On the Sandwich side, we set -radio to true and -allowzero false. That means Jimmy can select one and only one sandwich among beef, cheese or ham sandwiches. On the Veggie side, we want to encourage Jimmy to consume as much veggie as possible, so we set the -allowzero option to false. We also set the -allowzero option to false so that Jimmy cannot get away with eating none of the vegetables (see program 1-13 ).

tixSelect .sandwich -allowzero false -radio true -label "Sandwich :"
.sandwich add beef   -text Beef
.sandwich add cheese -text Cheese
.sandwich add ham    -text Ham

tixSelect .vege -allowzero false -radio false -label "Vegetable :" .vege add bean -text Bean .vege add carrot -text Carrot .vege add lettuce -text Lettuce

(Figure 1-13) Specifying Simple Selection Rules

1.5.3 Accessing the Value of a TixSelect Widget

The value of a TixSelect widget is a list of the names of the button subwidgets that are currently selected. For example, in program 1-11 , if the user has selected the apple button, then the value of the TixSelect widget will be apple. If the user has selected both the apple and the orange buttons, then the value will be the list "apple orange".

The TixSelect widget supports same set of options as the TixControl widget for you to access its value: the -value option stores the current value, which can be queried and modified using the cget and configure methods. You can also use the -variable option to specify a global variable to store the value of the TixSelect widget. The -command option specifies a TCL command to be called whenever the user changes the selection inside a TixSelect widget. This command is called with one argument: the new value of the TixSelect widget. There is also the -disablecallback option which you can use to control whether the command specified by the -command option should be called when the value of the TixSelect changes.

1.5.4 Specifying Complex Selection Rules

If you want to have more complex selection rules for the TixSelect widget, you can use the -validatecmd option. This option works the same as the -validatecmd option of the TixControl widget which we discusses in section 1.2 : it specifies a command to be called every time the user attempts to change the selection inside a TixSelect widget.

In the example program 1-14 , the procedure TwoMax will be called every time the user tries to change the selection of the .fruits widget. TwoMax limits the maximum number of fruits that the user to choose to be 2 by always truncating the value of the TixSelect widget to have no more than two items. If you run this program, you will find out that you can never select a third fruit after you have select two fruits.

  tixSelect .fruits -label "Fruits: " -radio false -validatecmd TwoMax
  .fruits add apple  -text Apple  -width 6
  .fruits add orange -text Orange -width 6
  .fruits add banana -text Banana -width 6
  pack .fruits

proc TwoMax {value} { if {[llength $value] > 2} { return [lrange $value 0 1] } else { return $value } }

(Figure 1-14) Specifying More Complex Selection Rules