gtk lesson 4: input widgets - city university of new yorksweiss/course_materials/csci493.70/... ·...

27

Upload: ngokhanh

Post on 08-Mar-2018

230 views

Category:

Documents


0 download

TRANSCRIPT

CSci493.70 Graphical User Interface Programming

Lesson 4: An Assortment of Input Widgets

Prof. Stewart Weiss

Lesson 4: An Assortment of Input Widgets

1 Introduction

So far, we have not encountered any widgets that let the user provide input to the application, with theexception of the GtkButton widgets and, in a broad sense, the GtkEventBox. Applications with graphicaluser interfaces provide many di�erent means for the user to provide input: various types of text entrywidgets, buttons that bring up dialog boxes that let the user choose text style (fonts, size, etc.), colors, or�les to open, controls such as checkboxes, radio buttons, combo boxes, slide controls, and spin buttons thatlet the user choose from among a set of �nite alternatives.

This lesson covers several di�erent types of input widgets, including several buttons, the text entry widget,and scales. Because some of these widgets require ancillary background knowledge and some of the demon-stration programs have a few new features, the lesson also introduces concepts related to GLib timers, fonts,color representation, image �le formats, and �le �lters.

2 Buttons

There are a variety of buttons in the GTK+ library. The following classes are derived from GtkButton:

• GtkToggleButton

• GtkColorButton

• GtkFontButton

• GtkLinkButton

• GtkOptionMenu (deprecated, use GtkComboBox instead)

• GtkScaleButton

Toggle buttons are used for creating radio buttons and checkboxes. Color buttons are used for selectingcolors, and font buttons, for selecting fonts. Link buttons are used for connecting to a URL. Scale buttonspop up a scale widget. This kind of widget is commonly used for volume controls in multimedia applications;the GtkVolumeButton is derived from the GtkScaleButton speci�cally for this purpose. It may not seemas if menus can be called buttons, but that is what they are. A menu is a type of control that lets a userchoose from a set of alternative actions. A GtkOptionMenu is a widget that lets a user choose from amongseveral options. It has been deprecated since Gtk+ 2.4 and replaced by the GtkComboBox.

A GtkComboBox derives directly from GtkBin. A GtkComboBox is a widget that allows the user to choosefrom a list of valid choices. The widget consists of a text entry box and a pull down menu from which theuser can select one of a set of prede�ned entries. It also allows the user to type a di�erent option into thetext box. The style in which the selected value is displayed, and the style of the pop-up list is determinedby the current theme. We will cover combo boxes later.

We will begin by going over a few things about basic buttons that have not been covered yet.

1

CSci493.70 Graphical User Interface Programming

Lesson 4: An Assortment of Input Widgets

Prof. Stewart Weiss

2.1 Stock Items

Stock items represent commonly-used menu or toolbar items such as "Open" or "About". Each stock item isidenti�ed by a stock ID, which is a string, such as �gtk-about�. GTK+ de�nes macros to represent thesestrings. For example

#define GTK_STOCK_ABOUT �gtk-about�

Applications should never use the actual strings; they should use the macros, which are pretty easy toguess. Applications can register their own stock items in addition to those built into GTK+. A stock ID isassociated with a GtkStockItem, which contains the user-visible label, keyboard accelerator, among otherthings. When you use a stock item, you have access to these accelerators and labels, which include icons.

A good reason to use stock items when possible is to make your buttons, menu items, and toolbars conformto users' expectations. They will also change their appearance depending upon the themes in the user'senvironment.

Buttons can be created with stock items as their labels. The method is

GtkWidget * gtk_button_new_from_stock (const gchar *stock_id);

The macro name can be used instead of the string.

2.1.1 Example

The following program illustrates the use of buttons with stock items. It also introduces the GtkButtonBoxclass, which is a convenient container for groups of independent buttons (as opposed to radio buttons) thatderives from GtkBox.

i n t main ( i n t argc , char ∗argv [ ] ){

GtkWidget ∗window ;GtkWidget ∗button ;GtkWidget ∗button_box ;GtkWidget ∗ frame ;

gtk_in i t (&argc , &argv ) ;

window = gtk_window_new (GTK_WINDOW_TOPLEVEL) ;gtk_window_set_title (GTK_WINDOW (window ) , "Stock Buttons " ) ;gtk_container_set_border_width (GTK_CONTAINER (window ) , 1 0 ) ;

// Control window width but l e t the he ight grow as neededgtk_widget_set_size_request (window , 250 , −1);

// Create a l ab e l ed frame and a v e r t i c a l button box ;frame = gtk_frame_new("Some Stock Buttons " ) ;button_box = gtk_vbutton_box_new ( ) ;

// Set the a t t r i b u t e s o f the button box : buttons spread// evenly and have a spac ing o f 15 p i x e l s between them ,// with a 10 p i x e l border

2

CSci493.70 Graphical User Interface Programming

Lesson 4: An Assortment of Input Widgets

Prof. Stewart Weiss

gtk_button_box_set_layout (GTK_BUTTON_BOX ( button_box ) ,GTK_BUTTONBOX_SPREAD) ;

gtk_box_set_spacing (GTK_BOX (button_box ) , 1 5 ) ;gtk_container_set_border_width (GTK_CONTAINER ( button_box ) , 1 0 ) ;

// Add the button box to frame and the frame to windowgtk_container_add (GTK_CONTAINER ( frame ) , button_box ) ;gtk_container_add (GTK_CONTAINER (window ) , frame ) ;

// Create a bunch o f buttons from stock ;button = gtk_button_new_from_stock (GTK_STOCK_ABOUT) ;gtk_container_add (GTK_CONTAINER ( button_box ) , button ) ;

button = gtk_button_new_from_stock (GTK_STOCK_OK) ;gtk_container_add (GTK_CONTAINER ( button_box ) , button ) ;

button = gtk_button_new_from_stock (GTK_STOCK_CANCEL) ;gtk_container_add (GTK_CONTAINER ( button_box ) , button ) ;

button = gtk_button_new_from_stock (GTK_STOCK_HELP) ;gtk_container_add (GTK_CONTAINER ( button_box ) , button ) ;

button = gtk_button_new_from_stock (GTK_STOCK_PRINT) ;gtk_container_add (GTK_CONTAINER ( button_box ) , button ) ;

// Don ' t bother with g i v ing the buttons any ac t i on s ;// j u s t the c l o s e boxg_signal_connect (G_OBJECT (window ) , " des t roy " ,

G_CALLBACK ( gtk_main_quit ) , NULL) ;

// Show the widgetsgtk_widget_show_all (window ) ;

/∗In e a r l i e r v e r s i on s o f GTK, the button i c on s d i sp layedautomat i ca l l y . They changed th ing s on us . Now the Gnomes e t t i n g s by d e f au l t w i l l not d i sp l ay button i c on s .Each app has to change the s e t t i n g to d i sp l ay them . Todo th i s , you have to r e t r i e v e GTK' s d e f au l t s e t t i n g s in to aGtkSett ings object , and s e t the "gtk−button−images " propertyto TRUE.

∗/GtkSett ings ∗ de f au l t_s e t t i n g s = gtk_sett ings_get_defau l t ( ) ;g_object_set ( de f au l t_se t t i ng s , "gtk−button−images " , TRUE, NULL) ;

gtk_main ( ) ;r e turn 0 ;

}

Notice the last few lines of the program. To guarantee that the icons will display, your program has to explic-itly set the gtk-button-images property to TRUE in GTK's default settings. This is done by getting a pointerto a GtkSettings object and setting the �gtk-button-images� value to TRUE with the g_object_set()

call.

3

CSci493.70 Graphical User Interface Programming

Lesson 4: An Assortment of Input Widgets

Prof. Stewart Weiss

A GtkButtonBox can be horizontal or vertical, and it has two subclasses for this purpose. This programcreates a vertical button box using

gtk_GtkWidget * gtk_vbutton_box_new (void);

Once it is created, the methods of the parent class are used, analogous to how horizontal and vertical boxesare used. The �rst step is to set how the box will be laid out, using

void gtk_button_box_set_layout (GtkButtonBox *widget,

GtkButtonBoxStyle layout_style);

The GtkButtonBoxStyle is an enumeration that de�nes various ways to distribute the buttons within thebox

typede f enum{GTK_BUTTONBOX_DEFAULT_STYLE, // whatever the d e f au l t i sGTK_BUTTONBOX_SPREAD, // evenly spreadGTK_BUTTONBOX_EDGE, // placed at the edgesGTK_BUTTONBOX_START, // grouped towards the s t a r tGTK_BUTTONBOX_END, // grouped towards the endGTK_BUTTONBOX_CENTER // centered

} GtkButtonBoxStyle ;

The program above spreads the buttons evenly in the space allotted to the box. The gtk_box_set_spacing()method is used to add a 15 pixel spacing between the buttons in the above program. The buttons are addedto the box using the gtk_container_add() method.

2.2 Toggle Buttons

Toggle buttons are not used very much, but they are a parent class of other, more useful buttons, namelycheck buttons and radio buttons, and they are therefore worthy of discussion. A toggle button is always inone of two states; a click switches it from one state to the other. Visually, they will appear depressed in onestate and �popped up� in the other.

To create a new toggle button, use one of three methods:

GtkWidget *gtk_toggle_button_new( void );

GtkWidget *gtk_toggle_button_new_with_label( const gchar *label );

GtkWidget *gtk_toggle_button_new_with_mnemonic( const gchar *label );

These are self-explanatory at this point, assuming you have read the previous chapters.

(to be �lled in more here)

2.3 Check Buttons

Check buttons are derived from toggle buttons, but look a little di�erent. They are what I used to call checkboxes, a small square within which you can click, with a label next to them. These are used for toggling thestate of a two-state property in an application. They are really just a di�erent way to present the togglebutton.

( to be continued ...)

4

CSci493.70 Graphical User Interface Programming

Lesson 4: An Assortment of Input Widgets

Prof. Stewart Weiss

2.4 Radio Buttons

Radio buttons are similar to check buttons except they are grouped so that only one may be selected/de-pressed at a time. They are suitable for making a choice among a small number of alternatives.

( to be continued ...)

3 The Text Entry Widget

A GtkEntry widget is a very powerful, single-line text entry widget, endowed with a large number of methods.It displays a box into which the user can enter and edit text. If the entered text is longer than the width ofthe widget, the widget will scroll so that the cursor position is visible.

An entry is created with

GtkWidget * gtk_entry_new ( void);

The next function alters the text which is currently within the Entry widget.

void gtk_entry_set_text ( GtkEntry *entry,

const gchar *text );

The function gtk_entry_set_text() sets the contents of the Entry widget, replacing the current contents.Note that the class Entry implements the Editable interface (yes, GObject supports Java-like interfaces)which contains some more functions for manipulating the contents.

The text entry widget has the ability to hide the entered text with an invisibility character. As the userenters characters, this character is displayed in the widget instead of what the user types. This feature makesit possible to put the widget into "password mode". To set the visibility property, use

void gtk_entry_set_visibility ( GtkEntry *entry,

gboolean visible);

By default, GTK+ picks the best invisible character that is available in the current font, but it can bechanged with

void gtk_entry_set_invisible_char ( GtkEntry *entry,

gunichar ch);

The gunichar type is GLib's Unicode character representation; to be precise, it is type which can hold anyUTF-32 or UCS-4 character code.

( to be continued ...)

4 The GtkAdjustment Object

A GtkAdjustment is one of those few GTK objects that is not a widget. It represents a discrete value whichhas an associated lower and upper bound, together with step and page increments, and a page size. It isimportant because it is the object that makes spin buttons, scales, sliders, progress bars, and scroll barspossible.

5

CSci493.70 Graphical User Interface Programming

Lesson 4: An Assortment of Input Widgets

Prof. Stewart Weiss

Step and page increments de�ne how the value changes in small �steps� and in large �page� jumps. Thinkabout the di�erent parts of a scrollbar to get an intuition for this. Steps are the changes that take placewhen the arrows at either end of the bar are clicked. Page increments are the changes resulting from clickingin the �trough� of the scrollbar with the left mouse button, or using the Page Up or Page Down key on thekeyboard. The page size is not used by all widgets; it is used in di�erent ways by di�erent widgets. Forexample, for an adjustment object for a scrollbar, it is the size of the visible area that is being scrolled inthe same direction as the scroll. A GtkAdjustment object does not update its value itself. Instead it is leftup to the owner of the GtkAdjustment to control the value.

A GtkAdjustment allows widgets to coordinate their actions through the use of the two signals that theyemit � "value_changed" and "changed". When the value of an adjustment is changed, the �value-changed�signal is emitted. When the adjustment is recon�gured, by changing a bound or the page size, for instance,the �changed� signal is emitted. A recon�guration can occur if the size of a document changes in a widgetcontrolled by a scrollbar; the upper bound increases. If the window is resized, the page_size parameter ischanged. Multiple widgets can react to the signals from a single adjustment, so that while a scrollbar moves,so does the content area of the widget that it controls.

You as a programmer do not change the value of the adjustment directly. An adjustment is always partof some widget called its owner. The owner is what typically calls gtk_adjustment_value_changed() andgtk_adjustment_changed(). For example, when a user slides the thumb of a scale, the scale widget sets theadjustment's new value using gtk_adjustment_value_changed() after which the �value-changed� signal isemitted on the adjustment.

Let us examine the functions for working with adjustments.

To create a new adjustment, use

GtkObject * gtk_adjustment_new (gdouble value,

gdouble lower,

gdouble upper,

gdouble step_increment,

gdouble page_increment,

gdouble page_size);

but cast the result to a GtkAdjustment* because a GtkObject is a resource hog and this type is deprecatedin GTK+3 anyway. The parameters have the following meanings:

value the initial value.lower the minimum value.upper the maximum value.step_increment the step increment.page_increment the page increment.page_size the page size.

The interpretation of these values depends upon the widget that uses them. For example:

GtkAdjustment adjustment;

adjustment = (GtkAdjustment *) gtk_adjustment_new (1.0, 1.0, 100.0, 1.0, 10.0, 0);

This creates an adjustment with range [1.0, 100.0] and initial value 1.0, with steps of 1.0 and 10.0.

You can set and retrieve the adjustment's values with

void gtk_adjustment_set_value (GtkAdjustment *adjustment,

gdouble value);

6

CSci493.70 Graphical User Interface Programming

Lesson 4: An Assortment of Input Widgets

Prof. Stewart Weiss

and

gdouble gtk_adjustment_get_value (GtkAdjustment *adjustment);

There are accessor and mutator methods for each of the individual properties other than the value of theGtkAdjustment object, i.e., lower, upper, and so on. The accessors are of the form

gdouble gtk_adjustment_get_xxx (GtkAdjustment *adjustment);

and the mutators, of the form

void gtk_adjustment_set_xxx (GtkAdjustment *adjustment,

gdouble xxx);

where the xxx is replaced by the property name. Because each time a property is set, a �changed� signalresults, if you plan on changing several at once, it is better to use

GtkObject * gtk_adjustment_configure (GtkAdjustment *adjustment,

gdouble value,

gdouble lower,

gdouble upper,

gdouble step_increment,

gdouble page_increment,

gdouble page_size);

which causes emission of a single �changed� signal. There are no uses of adjustments outside of owningwidgets, so we will move on to spin buttons to illustrate how they are used.

5 The Spin Button

A GtkSpinButton is actually a subclass of GtkEntry. It is a more secure way to allow users to enter numericvalues than a GtkEntry, because if it is set up properly, it handles all input validation. It allows the user toclick on one of two arrows to increment or decrement the displayed value within a �xed range and with �xedsteps. The user can also type a value, but the spin button can be set up so that the value can be checked toensure it is in a given range.

Spin buttons are based upon adjustments. In fact they are created by specifying an adjustment, so the �rststep is usually to create the adjustment and then create the spin button. A spin button is created with

GtkWidget * gtk_spin_button_new (GtkAdjustment *adjustment,

gdouble climb_rate,

guint digits);

where climb_rate controls the acceleration of the button and digits is the number of decimal digits to display.The climb_rate must be between 0.0 and 1.0. The larger the number, the faster the acceleration.

There is a convenience function for creating spin buttons that uses a default adjustment. Consult the APIdocumentation for information about it.

A spin button's parameters can be changed at any time with gtk_spin_button_configure():

7

CSci493.70 Graphical User Interface Programming

Lesson 4: An Assortment of Input Widgets

Prof. Stewart Weiss

void gtk_spin_button_configure (GtkSpinButton *spin_button,

GtkAdjustment *adjustment,

gdouble climb_rate,

guint digits);

There are various functions setting the button's properties, including

void gtk_spin_button_set_digits (GtkSpinButton *spin_button,

guint digits);

void gtk_spin_button_set_increments (GtkSpinButton *spin_button,

gdouble step,

gdouble page);

void gtk_spin_button_set_range (GtkSpinButton *spin_button,

gdouble min,

gdouble max);

and corresponding functions for accessing their values. To retrieve a pointer to the button's current adjust-ment object, use

GtkAdjustment* gtk_spin_button_get_adjustment (GtkSpinButton *spin_button);

and to replace the adjustment being used by a spinner:

void gtk_spin_button_set_adjustment (GtkSpinButton *spin_button,

GtkAdjustment *adjustment);

There are two ways to retrieve the spin button's current value. One returns a gdouble and the other returnsit as an integer, rounding using the usual rules:

gdouble gtk_spin_button_get_value (GtkSpinButton *spin_button);

gint gtk_spin_button_get_value_as_int (GtkSpinButton *spin_button);

A spin button emits a few signals but the most important one is the �value-changed� signal, which is emittedwhenever the value is changed. It would be in the callback for this signal that you would use the currentvalue to update whatever data the spin button controlled.

We will start with a simple program to illustrate the basic functionality.

L i s t i n g spinbutton_demo1 . c// Inc lude d i r e c t i v e s omitted to save spacevoid show_value (GtkSpinButton ∗ sp inner ,

gpo in t e r user_data ){

g in t va lue = gtk_spin_button_get_value_as_int ( sp inner ) ;g_pr int f (" Current va lue i s %d\n" , va lue ) ;

}

i n t main ( i n t argc , char ∗argv [ ] ){

GtkWidget ∗window ;GtkWidget ∗ sp inner ;GtkAdjustment ∗adjustment ;

8

CSci493.70 Graphical User Interface Programming

Lesson 4: An Assortment of Input Widgets

Prof. Stewart Weiss

gtk_in i t (&argc , &argv ) ;

window = gtk_window_new (GTK_WINDOW_TOPLEVEL) ;gtk_window_set_title (GTK_WINDOW (window ) ,

basename ( argv [ 0 ] ) ) ;gtk_widget_set_size_request (window , 250 , 100 ) ;gtk_container_set_border_width (GTK_CONTAINER (window ) , 5 ) ;

g_signal_connect (G_OBJECT (window ) , " des t roy " ,G_CALLBACK ( gtk_main_quit ) , NULL) ;

// Create an adjustment that ranges from 0 to 100 with i n i t i a l// value 50 , a s tep o f 1 and a page increment o f 5 .adjustment = (GtkAdjustment ∗) gtk_adjustment_new (50 . 0 , 0 . 0 ,

100 .0 , 1 . 0 , 5 . 0 , 0 . 0 ) ;

// Create a sp in button with no decimal p l a c e ssp inner = gtk_spin_button_new ( adjustment , 0 . 5 , 2 ) ;g_signal_connect (G_OBJECT ( sp inner ) , " value−changed " ,

G_CALLBACK ( show_value ) , NULL) ;

gtk_container_add (GTK_CONTAINER (window ) , sp inner ) ;gtk_widget_show_all (window ) ;gtk_main ( ) ;r e turn 0 ;

}

The spin button has a climb_rate of 0.5 and displays 2 decimal digits. When the value is changed, theshow_value() callback is executed. This gets the value of the spinner as an integer and displays it on theterminal. When you run this program you will observe a few things.

1. If you hold either arrow key down to accelerate, not all values will be printed on the terminal window.This is because the signals arrive faster than they can be handled and some are thrown away.

2. If you type a number outside of the range of the adjustment, it will replace it with the nearest validvalue.

3. If you type a number with decimal digits, it will accept it, even though it is not a value that can begenerated using only the arrow buttons, and if you then use the arrow buttons, the sequence will beo� by the decimal amount you entered.

4. You can enter non-numeric data. If you do, the current value will be set to the smallest value in itsrange.

5. The width of the spinner is much larger than it needs to be. You can put it into an hbox to control itssize, and add the hbox into the window instead.

You can prevent the spinner from accepting numbers that are not valid, i.e., that cannot be generated bythe arrows alone, by setting the boolean "snap-to-ticks" property to TRUE. By default it is FALSE. There isa function to do this, so that you do not have to use the g_object_set() function:

void gtk_spin_button_set_snap_to_ticks(GtkSpinButton *spin_button,

gboolean snap_to_ticks);

9

CSci493.70 Graphical User Interface Programming

Lesson 4: An Assortment of Input Widgets

Prof. Stewart Weiss

When this property is set to TRUE, the spinner will round invalid, but in-range, values to the nearest �tick�value.

You may not want the spin button to replace the current value if the user types a number that is out ofrange. You may want it to just ignore the out-of-range value and remain at the current value. You canmodify its update policy to do this. The update policy has two values:

GTK_UPDATE_ALWAYS When the button refreshes itself after a value is typed, it always displays the new value,coercing it to a bound if necessary.

GTK_UPDATE_IF_VALID When the button refreshes itself after a value is typed, it only replaces the currentvalue if the new value is within the range of the underlying adjustment.

By default it is set to GTK_UPDATE_ALWAYS.

You can set it to GTK_UPDATE_IF_VALID with

void gtk_spin_button_set_update_policy (GtkSpinButton *spin_button,

GtkSpinButtonUpdatePolicy policy);

passing GTK_UPDATE_IF_VALID as the second parameter.

You can prevent the user from entering non-numeric data by setting the button's �numeric� property toFALSE. The method that does this is

void gtk_spin_button_set_numeric (GtkSpinButton *spin_button,

gboolean numeric);

When this property is set, no non-numeric characters will appear in the spinner's textbox.

6 Scales

Scale widgets, de�ned by the GtkScale class, are widgets that let the user choose a numeric value by slidinga slider along a track called a trough, similar to brightness or volume controls or the playback slider invideo or audio media players. Dragging the slider with the pointer moves it back and forth within thetrough. Clicking in the trough advances the slider towards the location of the click, either completely, orby a designated amount, depending on which mouse button is used. There are both horizontal and verticalscales. Figure 1 shows an example of a horizontal scale. Horizontal scales belong to GtkHScale and verticalscales, to GtkVScale, both derived from GtkScale.

Figure 1: Horizontal scale

6.1 Creating and Adjusting Scales

Vertical and horizontal scales can be created with the following methods:

10

CSci493.70 Graphical User Interface Programming

Lesson 4: An Assortment of Input Widgets

Prof. Stewart Weiss

GtkWidget * gtk_vscale_new ( GtkAdjustment *adjustment );

GtkWidget * gtk_vscale_new_with_range ( gdouble min,

gdouble max,

gdouble step );

GtkWidget * gtk_hscale_new ( GtkAdjustment *adjustment );

GtkWidget * gtk_hscale_new_with_range ( gdouble min,

gdouble max,

gdouble step );

Because horizontal and vertical scales are essentially the same except for orientation, we will limit thediscussion to horizontal scales. As with spin buttons, the di�erent between the two constructors is thatthe gtk_hscale_new_with_range() creates an adjustment implicitly with the values of min, max, and step

provided in the call, whereas gtk_hscale_new() lets the programmer supply the adjustment explicitly.

You have control of several properties of the scale widget. By default, a scale widget displays the currentvalue of the adjustment as the slider position changes. You can turn this on or o� with

void gtk_scale_set_draw_value ( GtkScale *scale,

gboolean draw_value );

The default number of decimal digits for the displayed number is 1. This can be adjusted with

void gtk_scale_set_digits ( GtkScale *scale,

gint digits);

This function a�ects not just the widget, but the adjustment itself. If current value of the adjustment isretrieved, it will match the value that is displayed. A consequence of this is that, if the same adjustmentis embedded in multiple scales, changing the number of displayed digits in one scale changes them in theothers that share the adjustment.

One can also control where the current value of the scale is drawn. The choices are above, below, to the left,and to the right of the trough, using

void gtk_scale_set_value_pos ( GtkScale *scale,

GtkPositionType pos);

where pos can be one of GTK_POS_LEFT, GTK_POS_RIGHT, GTK_POS_TOP, and GTK_POS_BOTTOM. If the currentvalue is positioned on the side of the trough, as opposed to at either end, then as the slider moves, thenumber will move with it.

Another property that scales can have are labeled tick marks along the trough. You can use

void gtk_scale_add_mark ( GtkScale *scale,

gdouble value,

GtkPositionType position,

const gchar *markup);

to put a tick mark above or below a horizontal scale (or to the left or right of a vertical scale.) The value

parameter determines where along the scale to position the mark. It should be a valid value within therange of the adjustment object. The markup parameter is a string that may include Pango markup. Theposition parameter is one of the values mentioned above. For example, to put the tick marks below ahorizontal scale, use GTK_POS_BOTTOM. If you supply a position type that is not along the trough, it willdefault to the top for horizontal scales, and to the left for vertical scales. You can clear the marks withgtk_scale_clear_marks().

The following listing shows the basic steps in creating a scale. This scale does not react to changes in theslider. That will come afterwards.

11

CSci493.70 Graphical User Interface Programming

Lesson 4: An Assortment of Input Widgets

Prof. Stewart Weiss

L i s t i n g scale_demo1 . ci n t main ( i n t argc , char ∗argv [ ] ){

GtkWidget ∗window ;GtkWidget ∗ s c a l e ;GtkObject ∗adjustment ;

gtk_in i t (&argc , &argv ) ;

window = gtk_window_new (GTK_WINDOW_TOPLEVEL) ;gtk_window_set_title (GTK_WINDOW (window ) , basename ( argv [ 0 ] ) ) ;gtk_widget_set_size_request (window , WINWIDTH, WINHEIGHT) ;gtk_container_set_border_width (GTK_CONTAINER (window ) , 2 0 ) ;

g_signal_connect (G_OBJECT (window ) , " des t roy " ,G_CALLBACK ( gtk_main_quit ) , NULL) ;

adjustment = gtk_adjustment_new (50 . 0 , 0 . 0 , 100 .0 , 0 . 1 , 10 . 0 , 0 . 0 ) ;s c a l e = gtk_hscale_new (GTK_ADJUSTMENT ( adjustment ) ) ;

// Set the number o f decimal p l a c e s to d i sp l aygtk_sca le_set_dig i t s (GTK_SCALE ( s c a l e ) , 2 ) ;

// Set the po s i t i o n o f the value with r e sp e c t to the troughgtk_scale_set_value_pos (GTK_SCALE ( s c a l e ) , GTK_POS_TOP) ;gtk_scale_set_draw_value (GTK_SCALE ( s c a l e ) , TRUE) ;gtk_container_add (GTK_CONTAINER(window ) , s c a l e ) ;

gtk_widget_show_all (window ) ;gtk_main ( ) ;

r e turn 0 ;}

6.2 Range Widgets

To react to the changes in value as the slider is moved, you need to connect a callback to a signal fromthe parent class of GtkScale, which is GtkRange. GtkRange is the base class for the classes of widgets thatvisualize adjustment objects, which also includes scrollbars. Scales and scrollbars are similar in function andimplementation. Scrollbars, which are de�ned by the GtkScrollbar class, are also divided into horizontaland vertical subclasses, GtkHScrollbar and GtkVScrollbar. All range widgets, including scrollbars, containa trough and a slider.

GtkRange contains the signals for monitoring the parameters of the adjustment and also provides propertiesand methods for in�uencing the sensitivity of the "steppers". Range widgets use the embedded adjustmentobject to calculate the length of the slider and its position within the trough. As the user manipulates theslider, the range widget will change the value of the adjustment.

The only signal that you need to monitor for basic applications is the �value-changed� signal, which isemitted on the widget whenever the slider moves. The following listing shows a simple example of handlingthis signal.

L i s t i n g scale_demo2 . c

12

CSci493.70 Graphical User Interface Programming

Lesson 4: An Assortment of Input Widgets

Prof. Stewart Weiss

void on_value_changed ( GtkRange ∗ s ca l e ,GtkLabel ∗ l a b e l )

{gchar ∗buf ;buf = g_strdup_printf (" Current volume : %d" ,

( g in t ) gtk_range_get_value ( s c a l e ) ) ;gtk_label_set_text ( l abe l , buf ) ;g_free ( buf ) ;

}

i n t main ( i n t argc , char ∗argv [ ] ){

GtkWidget ∗window ;GtkWidget ∗ s c a l e ;GtkObject ∗adjustment ;GtkWidget ∗vbox ;GtkWidget ∗hbox ;GtkWidget ∗ l a b e l 1 ;GtkWidget ∗ l a b e l 2 ;

g tk_in i t (&argc , &argv ) ;

window = gtk_window_new (GTK_WINDOW_TOPLEVEL) ;gtk_window_set_title (GTK_WINDOW (window ) , basename ( argv [ 0 ] ) ) ;gtk_widget_set_size_request (window , WINWIDTH, WINHEIGHT) ;gtk_container_set_border_width (GTK_CONTAINER (window ) , 2 0 ) ;

g_signal_connect (G_OBJECT (window ) , " des t roy " ,G_CALLBACK ( gtk_main_quit ) , NULL) ;

vbox = gtk_vbox_new(FALSE, 0 ) ;gtk_container_add (GTK_CONTAINER(window ) , vbox ) ;

hbox = gtk_hbox_new(FALSE, 0 ) ;

l a b e l 1 = gtk_label_new ("Volume Level : " ) ;gtk_misc_set_alignment (GTK_MISC( l ab e l 1 ) , 0 , 1 ) ;gtk_box_pack_start (GTK_BOX (hbox ) , l abe l 1 , FALSE, TRUE, 0 ) ;

l a b e l 2 = gtk_label_new (" Current volume : 5 " ) ;gtk_misc_set_alignment (GTK_MISC( l ab e l 2 ) , 1 , 1 ) ;gtk_box_pack_end (GTK_BOX (hbox ) , l abe l2 , FALSE, TRUE, 0 ) ;gtk_box_pack_start (GTK_BOX (vbox ) , hbox , FALSE, TRUE, 0 ) ;

adjustment = gtk_adjustment_new (5 . 0 , 0 . 0 , 10 . 0 ,1 . 0 , 1 . 0 , 0 . 0 ) ;

s c a l e = gtk_hscale_new (GTK_ADJUSTMENT ( adjustment ) ) ;g tk_sca le_set_dig i t s (GTK_SCALE ( s c a l e ) , 0 ) ;gtk_scale_set_value_pos (GTK_SCALE ( s c a l e ) , GTK_POS_TOP) ;gtk_scale_set_draw_value (GTK_SCALE ( s c a l e ) , TRUE) ;gtk_box_pack_start (GTK_BOX (vbox ) , s ca l e , FALSE, TRUE, 0 ) ;

// Connect the value−changed s i g n a l to a ca l l ba ck that

13

CSci493.70 Graphical User Interface Programming

Lesson 4: An Assortment of Input Widgets

Prof. Stewart Weiss

// updates the second l a b e lg_signal_connect (G_OBJECT ( s c a l e ) , "value_changed " ,

G_CALLBACK ( on_value_changed ) ,( gpo in t e r ) l a b e l 2 ) ;

gtk_widget_show_all (window ) ;gtk_main ( ) ;

r e turn 0 ;}

The GtkRange class provides some fairly sophisticated functionality in addition to the basics that you needfor simple applications. One property is the ��ll-level�. If you have downloaded streaming media online, youhave probably seen scales in the media players that contain a slider as well as an indicator that lets youknow how much has been downloaded so far. In GTK+, you can display how much of a �le has downloadedby setting the ��ll-level� of the scale. You can turn on or o� the widget's display of the ��ll-level�, and youcan even prevent the user from positioning the slider beyond the current �ll level. The three functions thatperform these actions respectively, are:

void gtk_range_set_fill_level ( GtkRange *range,

gdouble fill_level);

void gtk_range_set_show_fill_level ( GtkRange *range,

gboolean show_fill_level);

void gtk_range_set_restrict_to_fill_level ( GtkRange *range,

gboolean restrict_to_fill_level);

You set the �ll level to zero initially, and add a timeout to a main event loop. The timeout can be used tocall a function to check on the progress of a download and update the �ll level as it progresses. A timeoutis added to the main event loop with the GLib function

guint g_timeout_add ( guint interval,

GSourceFunc function,

gpointer data);

The interval is expressed in milliseconds. A GSourceFunc is a function whose prototype is

gboolean (*GSourceFunc) ( gpointer user_data);

The function must return TRUE or FALSE. If it returns TRUE, the timeouts continue to interrupt the mainevent loop and the function is called again. If it returns FALSE, the timeout is removed. The data passedto g_timeout_add() in the third argument is passed to the GSourceFunc when it is called. Thus, to have afunction advance_fill() called every half-second, and update the �ll value in a GtkHScale named scale,we would add the instruction

g_timeout_add( 500, (GSourceFunc) advance_fill, scale);

to the main program, and de�ne the GSourceFunc

14

CSci493.70 Graphical User Interface Programming

Lesson 4: An Assortment of Input Widgets

Prof. Stewart Weiss

gboolean advance_f i l l ( GtkRange ∗ s c a l e ){

gdouble f i l l , max ;gchar ∗buf ;GtkLabel ∗ l a b e l ;GtkAdjustment ∗ sca le_adj ;

sca le_adj = gtk_range_get_adjustment ( s c a l e ) ;max = gtk_adjustment_get_upper ( sca le_adj ) ;f i l l = gtk_range_get_f i l l_leve l ( s c a l e ) ;l a b e l = g_object_get_data (G_OBJECT( s c a l e ) , " l a b e l " ) ;

i f ( f i l l < max ) {f i l l = f i l l + INTERVAL;gtk_range_set_f i l l_ leve l ( s ca l e , f i l l ) ;buf = g_strdup_printf ("Remaining : %4.2 f \n" ,max − f i l l ) ;gtk_label_set_text ( l abe l , buf ) ;g_free ( buf ) ;r e turn TRUE;

}e l s e

re turn FALSE;}

See scale_demo3.c for the complete program.

7 Selection Buttons

There are a few convenient buttons built into GTK+. These include the GtkColorButton and GtkFontButton,which are true buttons, and the GtkFileChooserButton, which is not derived from the button class but isa direct child of the GtkHBox class, as are GtkInfoBar and GtkStatusBar. All of these widgets fall into thecategory of selection widgets because they let the user select something or other from the entire domain ofselectable things.

The color button can be used to select a color, which then becomes the currently selected color, out of theset of all possible representable colors. The font button lets the user pick a font, which similarly becomes thecurrently selected font, out of all possible fonts available to the user. The �le chooser button lets the userchoose a �le or a folder, depending on how it is con�gured, either from all possible �les or from a �lteredsubset of �les.

Each of these buttons, when clicked, opens a dialog box that implements the selection capability. The dialogboxes that are opened in response to the color and font buttons are specialized to their particular tasks, andare not customizable or accessible to the programmer. The �le chooser dialog box is a very powerful widgetthat is essentially a �le browser. In addition, it implements the GtkFileChooser interface, which meansthat all of the functionality of this interface is available to the programmer by casting this button into aGtkFileChooser object.

These buttons are convenience buttons in the sense that their functionality exists in more general forms inother widgets.They exist mostly for very simple, or �quick and dirty� applications. Usually you will create aproxy such as a menu item or toolbar button to display a dialog box, and handle that dialog box's responseswith greater control. We will describe the buttons brie�y here, and at the same time introduce some of themore advanced topics that will be covered later.

15

CSci493.70 Graphical User Interface Programming

Lesson 4: An Assortment of Input Widgets

Prof. Stewart Weiss

7.1 The Font Button

The GtkFontButton lets the user choose a font. It displays the currently selected font as its label, and whenclicked, opens a font selection dialog box to change the font. It has methods that control whether the fontlabel is displayed using the font and/or the font size. It also has methods to set or get the title of the dialogbox that is displayed when the button is clicked.

To create a font button use either of the two constructors:

GtkWidget * gtk_font_button_new ( void);

GtkWidget * gtk_font_button_new_with_font ( const gchar *fontname);

the di�erence being that the latter is initialized to contain the given fontname. Methods to set whether ornot to use the font or size in the button label are:

void gtk_font_button_set_use_font ( GtkFontButton *font_button,

gboolean use_font);

void gtk_font_button_set_use_size ( GtkFontButton *font_button,

gboolean use_size);

The font button emits just one signal: �font-set�, which is emitted when the user clicks the OK button in thefont selection dialog box. When the user has selected a font, its name can be retrieved using

const gchar * gtk_font_button_get_font_name ( GtkFontButton *font_button);

Therefore, this function should be called in the signal handler for the �font-set� signal.

This signal is emitted only when the user selects the font ; it is not emitted as the user changes the font withinthe dialog box. The font within the dialog box is a property of the dialog box itself, not the font button. Ifyou wanted the program to react to changes in the value of that property as the user changed the font, youwould have to connect a signal handler to the notify::font-name signal on the dialog. But you do not haveaccess to that dialog when using the GtkFontButton. If your program needs to react to changes outside ofthe dialog, it is best to use the GtkFontSelectionDialog box directly.

The simplest way to show how to use this is with an example that changes the font of a label in a window.Labels have many methods, but what they do not have is a method to change their font, unless you want touse Pango markup to do so. Instead of using the label's own methods, we can use the more general widgetmethod to change the font of the label:

void gtk_widget_modify_font ( GtkWidget *widget,

PangoFontDescription *font_desc);

This function takes a pointer to the widget and a PangoFontDescription pointer. Pango is a libraryfor the layout and rendering of internationalized text. A PangoFontDescription object is derived froma GBoxed object, which descends directly from GObject. It is opaque to the programmer, but it providesmany methods to manipulate fonts. One of the most useful, and the simplest, is a function that returns aPangoFontDescription pointer when given a valid fontname that is available on the host system:

PangoFontDescription * pango_font_description_from_string ( const char *str);

This creates a new font description from a string representation in a speci�c form. The API documentationdescribes this form in detail, but for our purposes now, is is enough to know that the string returned by thegtk_font_button_get_font_name() function is in the correct form to supply to this function. Therefore,a signal handler to change the font of a label passed as user data to the handler could be:

16

CSci493.70 Graphical User Interface Programming

Lesson 4: An Assortment of Input Widgets

Prof. Stewart Weiss

void on_font_changed ( GtkFontButton ∗button ,GtkWidget ∗ l a b e l )

{const gchar ∗ fontname ;PangoFontDescription ∗ font_desc ;

fontname = gtk_font_button_get_font_name ( button ) ;font_desc = pango_font_description_from_string ( fontname ) ;gtk_widget_modify_font ( l abe l , font_desc ) ;

}

This retrieves the fontname as a string, gets a Pango description for it, and modi�es the label. When creatingthe widgets, the label would need an initial display font, which should match the font of the font button. IfPango cannot render the font for one reason or another, it will generate an error message on the standarderror stream. The following listing shows the main program that does this.

#inc lude <g l i b . h>#inc lude <gdk/gdk . h>#inc lude <gtk/gtk . h>#inc lude <s t d l i b . h>#inc lude <l i bg en . h>

#de f i n e WINWIDTH 400#de f i n e WINHEIGHT 300#de f i n e START_FONT "Sans 12"#de f i n e LABEL_TEXT "Cl i ck the button to change the font . "

i n t main ( i n t argc , char ∗argv [ ] ){

GtkWidget ∗window ;GtkWidget ∗ fontbutton ;GtkWidget ∗ l a b e l ;GtkWidget ∗vbox ;GtkWidget ∗hbox ;GtkWidget ∗ hseparator ;PangoFontDescription ∗ i n i t i a l_ f o n t ;

gtk_in i t (&argc , &argv ) ;

window = gtk_window_new (GTK_WINDOW_TOPLEVEL) ;gtk_window_set_title (GTK_WINDOW (window ) , basename ( argv [ 0 ] ) ) ;gtk_container_set_border_width (GTK_CONTAINER (window ) , 2 0 ) ;

g_signal_connect (G_OBJECT (window ) , " des t roy " ,G_CALLBACK ( gtk_main_quit ) , NULL) ;

vbox = gtk_vbox_new(FALSE, 0 ) ;gtk_container_add (GTK_CONTAINER(window ) , vbox ) ;

hbox = gtk_hbox_new( FALSE, 0 ) ;gtk_box_pack_start ( GTK_BOX (vbox ) , hbox , FALSE, FALSE, 1 0 ) ;

hseparator = gtk_hseparator_new ( ) ;gtk_box_pack_start ( GTK_BOX (vbox ) , hseparator , FALSE, FALSE, 1 0 ) ;

l a b e l = gtk_label_new (LABEL_TEXT ) ;

17

CSci493.70 Graphical User Interface Programming

Lesson 4: An Assortment of Input Widgets

Prof. Stewart Weiss

i n i t i a l_ f o n t = pango_font_description_from_string (START_FONT ) ;gtk_widget_modify_font ( l abe l , i n i t i a l_ f o n t ) ;g tk_labe l_set_jus t i f y (GTK_LABEL( l a b e l ) , GTK_JUSTIFY_CENTER) ;gtk_box_pack_start (GTK_BOX(vbox ) , l abe l , TRUE, FALSE, 5 ) ;

fontbutton = gtk_font_button_new_with_font (START_FONT ) ;gtk_font_button_set_tit le (GTK_FONT_BUTTON ( fontbutton ) ,

" S e l e c t a Font " ) ;gtk_font_button_set_use_font (GTK_FONT_BUTTON ( fontbutton ) , TRUE) ;

g_signal_connect (G_OBJECT ( fontbutton ) , " font_set " ,G_CALLBACK ( on_font_changed ) ,( gpo in t e r ) l a b e l ) ;

gtk_box_pack_end ( GTK_BOX (hbox ) , fontbutton , FALSE, FALSE, 0 ) ;g_signal_connect (G_OBJECT(window ) , " des t roy " ,

G_CALLBACK( gtk_main_quit ) , NULL) ;

gtk_widget_show_all (window ) ;gtk_main ( ) ;r e turn 0 ;

}

7.2 The Color Button

The GtkColorButton lets the user choose a color by launching a GtkColorSelectionDialog. The button'slabel is a small rectangular swatch of the currently selected color; it has no text in its label. It has a methodto set or get the title of the dialog box that is displayed when the button is clicked, and a few methods toset or get the current color and alpha channel value. It is created with either of

GtkWidget * gtk_color_button_new ( void);

GtkWidget * gtk_color_button_new_with_color ( const GdkColor *color);

The former creates a button with a black swatch, whereas the second creates a button with the current colordetermined by the GdkColor* argument. The function to retrieve the currently selected color from a colorbutton is

void gtk_color_button_get_color ( GtkColorButton *color_button,

GdkColor *color);

7.2.1 About Color Representations

To use the GtkColorButton you need to know a little about the representation of colors in GDK, which hasan extensive API for dealing with color. A color is stored in a GdkColor structure, which uses the RGB

model to represent a color as independent components of red, green, and blue:

typede f s t r u c t {guint32 p i x e l ;gu int16 red ;guint16 green ;guint16 blue ;

} GdkColor ;

18

CSci493.70 Graphical User Interface Programming

Lesson 4: An Assortment of Input Widgets

Prof. Stewart Weiss

The RGB model is a standard method of representing color, and with 16 bits allocated for each of red, green,and blue, there are in theory 248or roughly 281 trillion possible colors. Programmatically, a color is de�nedby an assignment of values to each of the red, green, and blue members of the GdkColor structure. Largervalues are closer to white. Black is the absence of color, and that is what you get when you zero the membersof this struct. (That is why the swatch on a new button is black when you do not use the second of the twoconstructors above.)

As a convenience, GDK provides a method that lets you specify the color value as a string called the color'sspec, using

gboolean gdk_color_parse ( const gchar *spec,

GdkColor *color);

The string can either one of a large set of standard names from the X11 rgb.txt �le, or it can be a hex valuein the form '#rgb' '#rrggbb' '#rrrgggbbb' or '#rrrrggggbbbb' where 'r', 'g' and 'b' are hex digits of the red,green, and blue components of the color, respectively. Standard color names are a very colorful collection infact, with names such as �baker's chocolate� and �Free Speech Green�.

Although you can visualize a color by its RGB values, that is not what GDK uses. Remember that GDKultimately has to deliver a color to the underlying X11 server, which has to make that color appear on themonitor. Because there are many di�erent types of monitors, there are di�erent hardware representations ofcolor. That is where the pixel value in the GdkColor structure comes into play.

The X11 protocol de�nes a visual as the method of representing a color in hardware. A true-color visual

represents colors by the actual RGB values, which are usually 24 bits or more. If 24 bits are used, 8-bits areused for each of R, G, and B in the pixel. The remaining 8 bits can be used for the alpha channel, whichwill be explained below.

Some systems use colormaps instead, and these are called pseudo-color visuals, because they do not necessar-ily contain the true colors. A colormap is an array whose entries consist of three values each � d bits for red,d bits for green, and d bits for blue. The number N of entries in the array determines how many di�erentcolors can be displayed on the screen at a single time. Colormaps are called palettes in some systems. If d = 8then the palette contains 24-bit color values, which means that there are about 16 million (224) di�erentpossible colors in the palette, even though the palette can display only N of these at a time in a pseudo-colorvisual. When N = 256 for example, the palette can store 256 colors, each of which has 24-bit depth.

(need a picture here)

If a system uses a colormap, then the pixel member of the GdkColor structure contains the index in acolormap of the color de�ned by the red, green, and blue members of the structure1. There is usuallya standard system colormap that is loaded by default. Your application can use the system colormap bycalling gdk_colormap_get_system().

A true-color visual also uses a colormap, but it is used di�erently. In a true-color visual, the three values ineach entry of the array are accessed independently. To be precise, suppose that the colormap has 256 entries,each of which consists of 3 8-bit values, called red, green, and blue, respectively. Rather than thinking of thisas a single array with 3-member structures as its entries, think of it instead as three parallel arrays namedred, green, and blue, each of size 256. Since 256 = 28, 8 bits can be used to index any row of these arrays.The pixel member of the GdkColor structure is decomposed into 8-bit �elds. The high-order 8 bits are theindex into the red array; the next 8 bits are the index into the green array, and the next 8 bits, the indexinto the blue array.

Since the entries in each array are 8 bits each, each of the pixel indices speci�es an 8-bit color value. In otherwords, there are 256 di�erent values for red, 256 for green, and 256 for blue. Since these are independent,there are 256 × 256 × 256 = 28 · 28 · 28 = 224 possible colors. Thus, using a table of the same size as thepseudo-color visual, we can represent 16 million colors using true-color, versus the 256 colors that the tablecan represent using pseudo-color.

1The actual color in the colormap may not match those RGB values exactly.

19

CSci493.70 Graphical User Interface Programming

Lesson 4: An Assortment of Input Widgets

Prof. Stewart Weiss

(need a picture here)

When an application needs to render a color on the screen, it can store it in the colormap, so that it can usethat same color repeatedly, perhaps at a later time. This is called allocating the color. Your application can�ll in the red, green, and blue members of a GdkColor structure, and then ask for it to be allocated. Afterallocation, the pixel member contains a hardware-dependent value that is used to access the colormap.

Most systems allow colormaps to be shared among applications. If a color is shared, then other applicationscan use the colors that are stored in it. If the color could be changed by the application that �rst allocatedit, this would corrupt the color in the other application. Therefore, GDK allows you to mark a color aswriteable or read-only. If it is read-only, your application cannot modify it once it is allocated, and it can beshared. If it is writeable, your application can modify it, and so GDK will not let other applications share it.It is better, of course, to use read-only colors, since the colormap will take up less memory. And of course,when you �nish with it, you can tell GDK to release that color's slot.

7.2.2 Using Color in GDK

We describe a very simple GTK+ application that uses a GtkColorButton. The main window contains aGtkDrawingArea widget, a label, and a color button. To organize things a bit within the window, we use acouple of boxes and an alignment.

A GtkDrawingArea is essentially a blank canvas on which your application can draw. It has its own GDKwindow, which is where your drawing is done. If you draw on this widget though, when the widget is coveredby another window and then exposed again, the drawing will be lost, because the windowing system doesnot redraw this for you. How to use this widget for drawing is the subject of a di�erent lesson. Here we avoidthis problem because we are using this widget only to display the currently selected color as its backgroundcolor. The background color of a widget is drawn by the windowing system.

We create the drawing area widget with

GtkWidget * gtk_drawing_area_new ( void);

which creates an empty canvas for us. We create an initial color using gdk_color_parse(), giving it anRGB text string as the choice. The three steps needed are

#de f i n e STARTCOLOR "baker ' s choco l a t e "

// miss ing s t u f f here o f course// in main ( ) :

GdkColor c o l o r ;gdk_color_parse (STARTCOLOR, &co l o r ) ;

Notice that color is on the program stack, not in dynamic memory. We can then use this GdkColor as thebackground of the drawing area and the starting color of the GtkColorButton that we create. To modifythe background color of a widget, we need the method of the widget base class:

void gtk_widget_modify_bg ( GtkWidget *widget,

GtkStateType state,

const GdkColor *color);

20

CSci493.70 Graphical User Interface Programming

Lesson 4: An Assortment of Input Widgets

Prof. Stewart Weiss

All widgets that have windows can be assigned a background color, which is part of the widget's GtkStylestructure. Every widget can be in one of �ve di�erent states, and the style associated to each state can bedi�erent. Therefore, one can assign a di�erent background color to the widget for each independent state.The GtkStateType argument is an enumerated type that refers to one of the states in which a widget canbe. The gtk_widget_modify_bg() method needs the GtkStateType argument so that it knows to whichstate's background to apply the color. The values of the enumeration are

GTK_STATE_NORMAL State during normal operation.

GTK_STATE_ACTIVE State of a currently active widget, such as a depressed button.

GTK_STATE_PRELIGHT State indicating that the mouse pointer is over the widget and the widget willrespond to mouse clicks.

GTK_STATE_SELECTED State of a selected item, such the selected row in a list.

GTK_STATE_INSENSITIVE State indicating that the widget is unresponsive to user actions.

We will set the background color of the drawing area in its normal state with the call

gtk_widget_modify_bg (drawingarea, GTK_STATE_NORMAL, &color);

and since color also contains our STARTCOLOR value, we can set the color of the button with the call

colorbutton = gtk_color_button_new_with_color (&color);

Then the only remaining important tasks are to de�ne the callback function and to connect it to the �color-set� signal of the color button. The callback is:

void on_color_set ( GtkColorButton ∗button ,GtkWidget ∗widget )

{GdkColor c o l o r ;

/∗ get the s e l e c t e d c o l o r from the d i a l o g −be sure to pass by address ! ∗/

gtk_color_button_get_color ( button , &co l o r ) ;

/∗ change the background co l o r o f the widget ∗/gtk_widget_modify_bg ( widget , GTK_STATE_NORMAL, &co l o r ) ;

}

Notice that the variable color is on the stack and that we have to pass it by address to the gtk_color_button_get_color()method. Notice also that we change the background color of the drawing area widget in two places: �rst inthe main program before we start the gtk_main() loop, and second, in the callback.

Finally, we connect the callback to the �color-set� signal on the color button and pass a pointer to the drawingarea widget as user data:

g_signal_connect (G_OBJECT ( co lo rbut ton ) , " co lo r_set " ,G_CALLBACK ( on_color_set ) ,( gpo in t e r ) drawingarea ) ;

The complete program is called colorbutton_demo1.c and can be found in the buttons demo directory onthe server.

21

CSci493.70 Graphical User Interface Programming

Lesson 4: An Assortment of Input Widgets

Prof. Stewart Weiss

7.3 The File Chooser Button

The GtkFileChooserButton is a widget that lets the user select a �le. It is a �le name with a button tobring up a GtkFileChooserDialog. The user can then use that dialog to change the �le associated withthat button. When this button is used though, multiple �les cannot be selected.

There are two methods to create a new

GtkWidget * gtk_file_chooser_button_new ( const gchar *title,

GtkFileChooserAction action);

GtkWidget * gtk_file_chooser_button_new_with_dialog

( GtkWidget *dialog);

The �rst function creates a �le choosing button that will open a built-in dialog; the second requires that theprogrammer supply a dialog box. As this is our introduction to these buttons and we have not yet covereddialog boxes, we limit discussion to the �rst of the methods.

To create a new �le chooser button, the programmer has to supply a string that will become the title of thedialog box that is opened when the user clicks on the button, and the action that the dialog must perform.The action is whether to open or save a �le, or to open or create a directory. It is speci�ed as a value of theGtkFileChooserAction enumerated type :

GTK_FILE_CHOOSER_ACTION_OPEN Indicates open mode. The �le chooser will only let the user pick anexisting �le.

GTK_FILE_CHOOSER_ACTION_SAVE Indicates save mode. The �le chooser will let the user pick an existing�le, or type in a new �lename.

GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER Indicates an Open mode for selecting folders. The �le chooserwill let the user pick an existing folder.

GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER Indicates a mode for creating a new folder. The �le chooserwill let the user name an existing or new folder.

When the GTK_FILE_CHOOSER_ACTION_OPEN mode is speci�ed, the GtkFileChooserDialog that is openeddisplays the �les in a speci�c directory. The user can control which directory it starts in with

void gtk_file_chooser_set_current_folder ( GtkFileChooser *chooser,

gchar *directory );

In general this sets the current directory of the dialog box. If we want it to start in the user's home directory,a logical choice, we would use the convenient GLib function

const gchar * g_get_home_dir (void);

which returns a string that can be passed to the former method. We could also use the POSIX getenv()

function, as in getenv(�HOME�), which would also give as the path to the home directory.

The button itself has very few methods of its own; its utility is primarily from its being an implementationof the GtkFileChooser interface. Basically, besides the dialog box that it owns, it has three properties:

"focus-on-click" whether or not a click allows it to grab focus."title" the title of the dialog box, as a string."width-chars" the width of the label inside the button, measured in characters.

Each of these can be set or retrieved by a method of the form

22

CSci493.70 Graphical User Interface Programming

Lesson 4: An Assortment of Input Widgets

Prof. Stewart Weiss

void gtk_file_chooser_button_set_xxx (GtkFileChooserButton *button,

gint n_chars);

gint gtk_file_chooser_button_get_xxx (GtkFileChooserButton *button);

where the �xxx� is replaced by the property name, replacing �-� by �_� and where the return type of the getmethod and the type of the second parameter of the set method are matching and one of gboolean, constgchar*, and gint respectively.

Like the font button and the color button, the �le chooser button emits just a single signal, ��le-set�, which isemitted when the user clicks the OK button in the �le selection dialog box. Because the GtkFileChooserButtonimplements the GtkFileChooser interface, it also emits the signals of that class. One can therefore monitorthe �selection-changed� signal instead; the result will be the same, but only if the button is set to select�les. If the button is created with the GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER action, it will not emitthe ��le-set� signal; instead your program must connect to the �selection-changed� signal.

We will demonstrate how to use this button with a simple application that displays two buttons, one to selecta folder, and the other to select a �le. We will call these the folder chooser and the �le chooser respectively.When a folder is selected, the folder chooser button will change the current directory of the �le chooserbutton. When a �le is selected, the �le chooser button will change a label widget in the main program thatdisplays the pathname of the currently selected �le. To make this all the more interesting, we will add �le�lters to the �le chooser button.

7.3.1 The GtkFileFilter

A GtkFileFilter can be used to restrict the �les being shown in a GtkFileChooser. Files can be �lteredbased on their

• name using gtk_file_filter_add_pattern(),

• mime type using gtk_file_filter_add_mime_type(), or

• with a custom �lter function using gtk_file_filter_add_custom().

When �ltering by name using gtk_file_filter_add_pattern(), shell �le globs can be used. For example,the pattern �*.c� will �lter out all �les that do not end in a �.c� extension, and �*.[ch]� will match header�les and C source �les.

Filtering with mime types handles aliasing and sub-classing of mime types. For example, a �lter fortext/plain also matches a �le with mime type application/rtf, since application/rtf is a subclassof text/plain. Also, GtkFileFilter allows wildcards for the subtype of a mime type, so you can �lter forall text �les with �text/*� for example.

The functions to add by name (pattern) or by mime type are:

void gtk_file_filter_add_pattern ( GtkFileFilter *filter,

const gchar *pattern);

void gtk_file_filter_add_mime_type ( GtkFileFilter *filter,

const gchar *mime_type);

The procedure for adding a �lter to a GtkFileChooser has four steps:

1. Create the �lter using

GtkFileFilter * gtk_file_filter_new ( void);

2. Give the �lter a name, using

23

CSci493.70 Graphical User Interface Programming

Lesson 4: An Assortment of Input Widgets

Prof. Stewart Weiss

void gtk_file_filter_set_name ( GtkFileFilter *filter,

const gchar *name);

3. Add one or more patterns or mime/types, one at a time, using one of the above functions, and

4. Add the �lters to the GtkFileChooser with

void gtk_file_chooser_add_filter ( GtkFileChooser *chooser,

GtkFileFilter *filter);

The order in which the �lters are added to the �le chooser will be the order in which they appear in thecombo box in the GtkFileChooserDialog box. The following listing shows a function that adds a numberof �lters to a GtkFileChooserButton's dialog box.

void s e t_up_f i l t e r s ( GtkFileChooser ∗ chooser ){

GtkF i l eF i l t e r ∗ f i l t e r ;

f i l t e r = gtk_f i l e_f i l t e r_new ( ) ;gtk_f i l e_f i l ter_set_name ( f i l t e r , "Al l F i l e s " ) ;g tk_f i l e_f i l t e r_add_pattern ( f i l t e r , "∗" ) ;g tk_f i l e_chooser_add_f i l t e r ( chooser , f i l t e r ) ;

f i l t e r = gtk_f i l e_f i l t e r_new ( ) ;gtk_f i l e_f i l ter_set_name ( f i l t e r , "Text F i l e s " ) ;gtk_file_filter_add_mime_type ( f i l t e r , " t ex t /∗" ) ;g tk_f i l e_chooser_add_f i l t e r ( chooser , f i l t e r ) ;

f i l t e r = gtk_f i l e_f i l t e r_new ( ) ;gtk_f i l e_f i l ter_set_name ( f i l t e r , "Image F i l e s " ) ;gtk_f i le_f i l ter_add_pixbuf_formats ( f i l t e r ) ;g tk_f i l e_chooser_add_f i l t e r ( chooser , f i l t e r ) ;

f i l t e r = gtk_f i l e_f i l t e r_new ( ) ;gtk_f i l e_f i l ter_set_name ( f i l t e r , "C/C++ F i l e s " ) ;g tk_f i l e_f i l t e r_add_pattern ( f i l t e r , " ∗ . [ chC ] " ) ;g tk_f i l e_f i l t e r_add_pattern ( f i l t e r , "∗ . cc " ) ;g tk_f i l e_f i l t e r_add_pattern ( f i l t e r , "∗ . cpp " ) ;g tk_f i l e_f i l t e r_add_pattern ( f i l t e r , "∗ .CC" ) ;gtk_f i l e_chooser_add_f i l t e r ( chooser , f i l t e r ) ;

}

7.3.2 Supported Image File Formats

Notice that GtkFileFilter also has a method to add an image �le �lter:

void gtk_file_filter_add_pixbuf_formats (GtkFileFilter *filter);

This function will add to the given �lter all image formats supported by GdkPixbuf on the given host. If youare curious as to which image �le types are actually supported, you can use the following function, de�nedin the Gdk-PixBuf Reference Manual:

GSList * gdk_pixbuf_get_formats (void);

24

CSci493.70 Graphical User Interface Programming

Lesson 4: An Assortment of Input Widgets

Prof. Stewart Weiss

The following listing is of a command-line program that will print the list of image formats supported byGdkPixbuf on your host machine.

L i s t i n g .#inc lude <gtk/gtk . h>// Appl ies gdk_pixbuf_format_is_writable ( data ) to the GdkPixBufFormat// s t r u c t pointed to by data , and i f i t r e tu rn s true , i t prepends that// po in t e r to the l i s t po inted to by ∗ l i s t . Because the l i s t i t s e l f i s// modi f i ed in the funct ion , the po in t e r to the f r on t o f the l i s t changes .// Therefore , i t i s passed by r e f e r e n c e as a po inter , i . e . , as ∗∗ l i s t , and// so in t h i s funct ion , ∗ l i s t i s the po in t e r i t s e l f . When i t i s f i n i s h e d// ∗ l i s t s t i l l po in t s to the f r on t o f the l i s t .void add_if_writable (GdkPixbufFormat ∗data , GSList ∗∗ l i s t ){

i f ( gdk_pixbuf_format_is_writable ( data ) )∗ l i s t = g_sl ist_prepend (∗ l i s t , data ) ;

}

// This i s a l s o a GFunc funct ion , which i s why i t i s g iven the second// parameter , l i s t , even though i t does not use i t .// I t r e t r i e v e s the name o f the image f i l e format s to r ed in the cur rent// GdkPixbufFormat s t r u c t and p r i n t s i t on standard output .void format_print (GdkPixbufFormat ∗data , GSList ∗∗ l i s t ){

g_print ( "%s\n" , gdk_pixbuf_format_get_name ( data ) ) ;}

i n t main ( i n t argc , char ∗argv [ ] ){

// Get the l i s t o f a l l formats// This func t i on r e tu rn s a Glib s ing ly−l i nked l i s t o f po i n t e r s// to GdkPixbufFormat s t r u c t s . The s t r u c t s are owned by Gdk but the// l i s t o f po i n t e r s must be f r e ed when you are f i n i s h e d with the// l i s t , with g_s l i s t_ f r e e ( ) .GSList ∗ formats = gdk_pixbuf_get_formats ( ) ;

// I n i t i a l i z e an empty GSListGSList ∗writable_formats = NULL;/∗

Produce the l i s t o f wr i t ab l e formats us ing the fo r each algor i thmapply ing a func t i on o f the form

void gfunc ( gpo in t e r data , gpo in t e r user_data )

to each element o f the l i s t , pas s ing in user_data as the secondargument to i t , and the cur rent l i s t element as the f i r s t argumentto i t . For example , in the code below , the GFunc i s add_if_writable ( )and the l i s t i s formats . The data passed as the second argument i sa po in t e r to the l i s t wr i t ab l e formats . add_if_writable ( ) w i l l beapp l i ed to the cur rent l i s t item in format f o r each item in format ,with &writable_formats as i t s second argument .

∗/g_s l i s t_fo reach ( formats , ( GFunc) add_if_writable , &writable_formats ) ;

// Pr int the l i s t o f wr i t ab l e formats us ing the fo r each a lgor i thmg_s l i s t_fo reach ( formats , ( GFunc) format_print , &writable_formats ) ;g_s l i s t_ f r e e ( formats ) ;r e turn 0 ;

25

CSci493.70 Graphical User Interface Programming

Lesson 4: An Assortment of Input Widgets

Prof. Stewart Weiss

}

The main program and callbacks of our demonstration program follow.

void on_folder_changed ( GtkFileChooser ∗ fo lder_chooser ,GtkFileChooser ∗ f i l e_choo s e r )

{gchar ∗ f o l d e r = gtk_fi le_chooser_get_f i lename ( fo lde r_choose r ) ;gtk_f i l e_chooser_set_current_fo lder ( f i l e_choos e r , f o l d e r ) ;

}

void on_file_changed ( GtkFileChooser ∗ f i l e_choose r ,GtkLabel ∗ l a b e l )

{gchar ∗ f i l e = gtk_fi le_chooser_get_f i lename ( f i l e_choo s e r ) ;i f ( NULL == f i l e )

gtk_label_set_text ( l abe l , "no f i l e s e l e c t e d " ) ;e l s e

gtk_label_set_text ( l abe l , f i l e ) ;}

i n t main ( i n t argc , char ∗argv [ ] ){

GtkWidget ∗window ;GtkWidget ∗ f o lde r_choose r ;GtkWidget ∗ f i l e_choo s e r ;GtkWidget ∗ f i l ename_labe l ;GtkWidget ∗ frame ;GtkWidget ∗vbox ;GtkWidget ∗ al ignment ;

gtk_in i t (&argc , &argv ) ;

window = gtk_window_new (GTK_WINDOW_TOPLEVEL) ;gtk_window_set_title (GTK_WINDOW (window ) , basename ( argv [ 0 ] ) ) ;gtk_widget_set_size_request (window , WINWIDTH, WINHEIGHT) ;gtk_container_set_border_width (GTK_CONTAINER (window ) , 2 0 ) ;

g_signal_connect (G_OBJECT (window ) , " des t roy " ,G_CALLBACK ( gtk_main_quit ) , NULL) ;

// Put a vbox in the main window and pack everyth ing in to i tvbox = gtk_vbox_new (FALSE, 5 ) ;gtk_container_add (GTK_CONTAINER (window ) , vbox ) ;

// F i r s t in i s a frame that w i l l conta in a l a b e l . The l a b e l w i l l be// changed by the on_file_changed ca l l b a ck to conta in the abso lu t e// pathname o f the s e l e c t e d f i l e .frame = gtk_frame_new (" Current ly s e l e c t e d f i l e : " ) ;gtk_box_pack_start (GTK_BOX (vbox ) , frame , TRUE, TRUE, 10 ) ;

// The f i l ename_labe l w i l l conta in the path . Turn on l i n e wrap and// wrap on cha ra c t e r s s i n c e the re are no spaces in the path and// i t might get pre t ty long . Add soem padding too .f i l ename_labe l = gtk_label_new ( " " ) ;g tk_labe l_set_jus t i f y ( GTK_LABEL( f i l ename_labe l ) , GTK_JUSTIFY_LEFT) ;gtk_label_set_line_wrap ( GTK_LABEL( f i l ename_labe l ) , TRUE) ;

26

CSci493.70 Graphical User Interface Programming

Lesson 4: An Assortment of Input Widgets

Prof. Stewart Weiss

gtk_label_set_line_wrap_mode ( GTK_LABEL( f i l ename_labe l ) ,PANGO_WRAP_CHAR) ;

gtk_misc_set_alignment ( GTK_MISC( f i l ename_labe l ) , 0 , 0 ) ;gtk_container_add (GTK_CONTAINER ( frame ) , f i l ename_labe l ) ;

// Create the two f i l e c h o o s e r buttons , one to s e l e c t a f o l d e r ,// the other to s e l e c t a f i l e . Make them a f i x ed minimum s i z e so// they don ' t look s t range .f o lde r_choose r = gtk_file_chooser_button_new ("Choose a Folder " ,

GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) ;gtk_widget_set_size_request ( fo lder_chooser , BUTTONWIDTH, −1);

f i l e_choo s e r = gtk_file_chooser_button_new ("Choose a F i l e " ,GTK_FILE_CHOOSER_ACTION_OPEN) ;

gtk_widget_set_size_request ( f i l e_choose r , BUTTONWIDTH, −1);

// Let the s t a r t i n g d i r e c t o r y be the home d i r e c t o r ygtk_f i l e_chooser_set_current_fo lder (GTK_FILE_CHOOSER ( fo lde r_choose r ) ,

g_get_home_dir ( ) ) ;gtk_f i l e_chooser_set_current_fo lder (GTK_FILE_CHOOSER ( f i l e_choo s e r ) ,

g_get_home_dir ( ) ) ;

// Connect the se lect ion_changed s i g n a l to two d i f f e r e n t c a l l b a c k sg_signal_connect (G_OBJECT ( fo lde r_choose r ) , " se lect ion_changed " ,

G_CALLBACK ( on_folder_changed ) ,( gpo in t e r ) f i l e_choo s e r ) ;

g_signal_connect (G_OBJECT ( f i l e_choo s e r ) , " f i l e_ s e t " ,G_CALLBACK ( on_file_changed ) ,( gpo in t e r ) f i l ename_labe l ) ;

s e t_up_f i l t e r s (GTK_FILE_CHOOSER( f i l e_choo s e r ) ) ;

a l ignment = gtk_alignment_new ( 1 , 0 , 0 , 0 ) ;gtk_container_add (GTK_CONTAINER ( al ignment ) , f i l e_choo s e r ) ;gtk_box_pack_end (GTK_BOX (vbox ) , al ignment , FALSE, FALSE, 1 0 ) ;

a l ignment = gtk_alignment_new ( 1 , 0 , 0 , 0 ) ;gtk_container_add (GTK_CONTAINER ( al ignment ) , f o lde r_choose r ) ;gtk_box_pack_end (GTK_BOX (vbox ) , al ignment , FALSE, FALSE, 1 0 ) ;

gtk_widget_show_all (window ) ;

gtk_main ( ) ;r e turn 0 ;

27