diff --git a/ChangeLog b/ChangeLog index 6f12a029e0..31a1899cff 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,18 @@ +Thu Aug 13 09:11:11 BST 1998 Tony Gale + + * docs/gtk_tut.sgml: + - Tidy up of the menufactory example from + Andy Kahn + - New section on Range Widgets from + David Huggins-Daines + - Started a new section on 'Advanced Event and Signal + Handling' - used an email from Owen. + - New appendix on Gdk Event Types + - Added the tictactoe full example code to the + 'Code Examples' appendix + * Add the range widgets example. Re-add the text and tree + examples, as cvs has lost them. + Tue Aug 11 20:52:58 1998 Tim Janik * gtk/gtktypeutils.c (gtk_type_class_init): relookup nodes after diff --git a/ChangeLog.pre-2-0 b/ChangeLog.pre-2-0 index 6f12a029e0..31a1899cff 100644 --- a/ChangeLog.pre-2-0 +++ b/ChangeLog.pre-2-0 @@ -1,3 +1,18 @@ +Thu Aug 13 09:11:11 BST 1998 Tony Gale + + * docs/gtk_tut.sgml: + - Tidy up of the menufactory example from + Andy Kahn + - New section on Range Widgets from + David Huggins-Daines + - Started a new section on 'Advanced Event and Signal + Handling' - used an email from Owen. + - New appendix on Gdk Event Types + - Added the tictactoe full example code to the + 'Code Examples' appendix + * Add the range widgets example. Re-add the text and tree + examples, as cvs has lost them. + Tue Aug 11 20:52:58 1998 Tim Janik * gtk/gtktypeutils.c (gtk_type_class_init): relookup nodes after diff --git a/ChangeLog.pre-2-10 b/ChangeLog.pre-2-10 index 6f12a029e0..31a1899cff 100644 --- a/ChangeLog.pre-2-10 +++ b/ChangeLog.pre-2-10 @@ -1,3 +1,18 @@ +Thu Aug 13 09:11:11 BST 1998 Tony Gale + + * docs/gtk_tut.sgml: + - Tidy up of the menufactory example from + Andy Kahn + - New section on Range Widgets from + David Huggins-Daines + - Started a new section on 'Advanced Event and Signal + Handling' - used an email from Owen. + - New appendix on Gdk Event Types + - Added the tictactoe full example code to the + 'Code Examples' appendix + * Add the range widgets example. Re-add the text and tree + examples, as cvs has lost them. + Tue Aug 11 20:52:58 1998 Tim Janik * gtk/gtktypeutils.c (gtk_type_class_init): relookup nodes after diff --git a/ChangeLog.pre-2-2 b/ChangeLog.pre-2-2 index 6f12a029e0..31a1899cff 100644 --- a/ChangeLog.pre-2-2 +++ b/ChangeLog.pre-2-2 @@ -1,3 +1,18 @@ +Thu Aug 13 09:11:11 BST 1998 Tony Gale + + * docs/gtk_tut.sgml: + - Tidy up of the menufactory example from + Andy Kahn + - New section on Range Widgets from + David Huggins-Daines + - Started a new section on 'Advanced Event and Signal + Handling' - used an email from Owen. + - New appendix on Gdk Event Types + - Added the tictactoe full example code to the + 'Code Examples' appendix + * Add the range widgets example. Re-add the text and tree + examples, as cvs has lost them. + Tue Aug 11 20:52:58 1998 Tim Janik * gtk/gtktypeutils.c (gtk_type_class_init): relookup nodes after diff --git a/ChangeLog.pre-2-4 b/ChangeLog.pre-2-4 index 6f12a029e0..31a1899cff 100644 --- a/ChangeLog.pre-2-4 +++ b/ChangeLog.pre-2-4 @@ -1,3 +1,18 @@ +Thu Aug 13 09:11:11 BST 1998 Tony Gale + + * docs/gtk_tut.sgml: + - Tidy up of the menufactory example from + Andy Kahn + - New section on Range Widgets from + David Huggins-Daines + - Started a new section on 'Advanced Event and Signal + Handling' - used an email from Owen. + - New appendix on Gdk Event Types + - Added the tictactoe full example code to the + 'Code Examples' appendix + * Add the range widgets example. Re-add the text and tree + examples, as cvs has lost them. + Tue Aug 11 20:52:58 1998 Tim Janik * gtk/gtktypeutils.c (gtk_type_class_init): relookup nodes after diff --git a/ChangeLog.pre-2-6 b/ChangeLog.pre-2-6 index 6f12a029e0..31a1899cff 100644 --- a/ChangeLog.pre-2-6 +++ b/ChangeLog.pre-2-6 @@ -1,3 +1,18 @@ +Thu Aug 13 09:11:11 BST 1998 Tony Gale + + * docs/gtk_tut.sgml: + - Tidy up of the menufactory example from + Andy Kahn + - New section on Range Widgets from + David Huggins-Daines + - Started a new section on 'Advanced Event and Signal + Handling' - used an email from Owen. + - New appendix on Gdk Event Types + - Added the tictactoe full example code to the + 'Code Examples' appendix + * Add the range widgets example. Re-add the text and tree + examples, as cvs has lost them. + Tue Aug 11 20:52:58 1998 Tim Janik * gtk/gtktypeutils.c (gtk_type_class_init): relookup nodes after diff --git a/ChangeLog.pre-2-8 b/ChangeLog.pre-2-8 index 6f12a029e0..31a1899cff 100644 --- a/ChangeLog.pre-2-8 +++ b/ChangeLog.pre-2-8 @@ -1,3 +1,18 @@ +Thu Aug 13 09:11:11 BST 1998 Tony Gale + + * docs/gtk_tut.sgml: + - Tidy up of the menufactory example from + Andy Kahn + - New section on Range Widgets from + David Huggins-Daines + - Started a new section on 'Advanced Event and Signal + Handling' - used an email from Owen. + - New appendix on Gdk Event Types + - Added the tictactoe full example code to the + 'Code Examples' appendix + * Add the range widgets example. Re-add the text and tree + examples, as cvs has lost them. + Tue Aug 11 20:52:58 1998 Tim Janik * gtk/gtktypeutils.c (gtk_type_class_init): relookup nodes after diff --git a/docs/gtk_tut.sgml b/docs/gtk_tut.sgml index fe93a7cbac..b17bb492c1 100644 --- a/docs/gtk_tut.sgml +++ b/docs/gtk_tut.sgml @@ -10,7 +10,7 @@ name="<imain@gtk.org>">, Tony Gale -June 24th, 1998 +August 13th, 1998 Introduction @@ -70,6 +70,9 @@ You can also view other sources of GTK information on http://www.gtk.org/ GTK uses GNU autoconf for configuration. Once untar'd, type ./configure --help to see a list of options. +Th GTK source distribution also contains the complete source to all of the +examples used in this tutorial, along with Makefiles to aid compilation. + To begin our introduction to GTK, we'll start with the simplest program possible. This program will create a 200x200 pixel window and has no way of exiting except to be @@ -272,6 +275,9 @@ directories for the compiler to look in, and gtk-config --libs will output the list of libraries for the compiler to link with and the directories to find them in. +Note that the type of single quote used in the compile command above +is significant. + The libraries that are usually linked in are: The GTK library (-lgtk), the widget library, based on top of GDK. @@ -476,14 +482,17 @@ static gint button_press_event (GtkWidget *widget, Note that we can declare the second argument as type - - +propagate further. Returning FALSE continues the normal event handling. +See the section on + for more details on this +propagation process. + +For details on the GdkEvent data types, see the appendix entitled +. Stepping Through Hello World @@ -985,6 +994,7 @@ yourself and play with it. /* example-start packbox packbox.c */ +#include #include "gtk/gtk.h" void @@ -2078,6 +2088,775 @@ removes the need for a variable to hold the list of buttons: + +Range Widgets + +

+The category of range widgets includes the ubiquitous scrollbar +widget and the less common scale widget. Though these two +types of widgets are typically used for vastly different +purposes, they are quite similar in function and implementation. +Range widgets allow the user to visually manipulate a value +within a specified range (hence the name). + +All range widgets share a set of common graphic elements, each +of which has its own X window and receives events. They all +contain a "trough" and a "slider" (what is sometimes called a +"thumbwheel" in other GUI environments). Dragging the slider +with the pointer moves it back and forth within the trough, +while clicking in the trough advances the slider towards the +location of the click, either completely, or by a designated +amount (called a "page"), depending on which button was used. + + +The Scale Widgets +

+Scale widgets are used to set an explicitly numeric parameter +which has a visual correlate, and which the user might be +expected to adjust primarily by sight. For example, the +GtkColorSelection compound widget contains scale widgets which +control the components of the colour being selected. +Typically, the precise value of the number is less important +here than its side-effects, and thus the user should be spared +the effort of reaching for the keyboard. + + +Creating a Scale Widget +

+There are actually two types of scale widget: GtkHScale +widgets, which are horizontal, and GtkVScale widgets, which + +are vertical. (Most programmers seem to favour horizontal +scale widgets). Since they work essentially the same way, +there's no need to treat them separately here. The +following functions, defined in +<gtk/gtkvscale.h> and +<gtk/gtkhscale.h>, create vertical and +horizontal scale widgets, respectively: + + +GtkWidget* gtk_vscale_new( GtkAdjustment *adjustment ); + +GtkWidget* gtk_hscale_new( GtkAdjustment *adjustment ); + + + +below for an explanation of what exactly the +Functions, Signals, and Macros +

+Scale widgets can display their current value as a number +beside the trough. The default behaviour is to show the +value, but you can change this with this function: + + +void gtk_scale_set_draw_value( GtkScale *scale, + gint draw_value ); + + +As you might have guessed, +void gtk_scale_set_digits( GtkScale *scale, + gint digits); + + +where +void gtk_scale_set_value_pos( GtkScale *scale, + GtkPositionType pos ); + + +If you've read the section on the notebook widget, then you +know what the possible values of GtkPositionType and can take one +of the following values: + + +GTK_POS_LEFT +GTK_POS_RIGHT +GTK_POS_TOP +GTK_POS_BOTTOM + + +If you position the value on the "side" +of the trough (e.g. on the top or bottom of a horizontal +scale widget), then it will follow the slider up and down +the trough. + +All the preceding functions are defined in +<gtk/gtkscale.h>. The other signals and +functions defined in the header files for the scale widgets +are either not useful for anyone other than writers of scale +widgets, or are the standard GTK+ type-casting macros and +functions. + + +The Scrollbar Widgets +

+These are your standard, run-of-the-mill scrollbars. As with +the scale widgets, there are separate types for horizontal and +vertical scrollbars. There really isn't much to say about +these. You create them with the following functions: + + +GtkWidget* gtk_hscrollbar_new( GtkAdjustment *adjustment ); + +GtkWidget* gtk_vscrollbar_new( GtkAdjustment *adjustment ); + + +and that's about it (if you don't believe me, look in the +header files!). Again, +The Adjustment Object

+As you might have noticed, there really isn't much to the +various range widgets themselves from the programmer's point +of view. Most of your program's interaction with these +widgets will take place by way of the heretofore mysterious + +Creating a GtkAdjustment +

+You create an adjustment using: + + +GtkObject* gtk_adjustment_new( gfloat value, + gfloat lower, + gfloat upper, + gfloat step_increment, + gfloat page_increment, + gfloat page_size ); + + +It may or may not be obvious by now that the values given to + below +describes the default key and mouse bindings for range +widgets, and how they relate to these increments. The + +GtkObject *adj; + +adj = gtk_adjustment_new (0, first_line, last_line, 1, + window_height - 2, window_height); + + +where last_line - +window_height (or, in more general terms, upper - +page_height). This is a little confusing at first, but +it makes sense if you think about it in terms of what the +user expects to see in a scrolled window when the +scrollbar's slider is moved all the way to the end of the +trough. + +Since the size of the slider on scale widgets is invariable, +to avoid excessive confusion, it's a good idea to set the + +Inside the GtkAdjustment object +

+OK, you say, that's nice, but how do I get at all these +values, and, more importantly, how do I know when the user +has moved the slider around? To answer these questions and +more, let's start by taking a look at +struct _GtkAdjustment +{ + GtkData data; + + gfloat lower; + gfloat upper; + gfloat value; + gfloat step_increment; + gfloat page_increment; + gfloat page_size; +}; + +struct _GtkAdjustmentClass +{ + GtkDataClass parent_class; + + void (* changed) (GtkAdjustment *adjustment); + void (* value_changed) (GtkAdjustment *adjustment); +}; + + +The first thing you should know is that there aren't any +handy-dandy macros or accessor functions for getting the +GTK_ADJUSTMENT (Object) macro does +run-time type checking (as do all the GTK+ type-casting +macros, actually). On the other hand, unless you're writing +a new type of range widget, you probably don't want to + +void gtk_adjustment_set_value( GtkAdjustment *adjustment, + gfloat value ); + + +If you need to change the other fields, and you don't intend +to do this very frequently, it's best to create a new +GtkAdjustment and set it with + below. + +You might have noticed that, while adjustments are not +widgets, they are still a "subclass" of GtkObject. +Therefore, they can (and do) emit signals of their own. + +The various widgets that use the GtkAdjustment object will +emit the "value_changed" signal on an adjustment whenever +they change its value (see below for more detail). This +happens both when user input causes the slider to move on a +range widget, as well as when the program explicitly changes +the value with +void cb_rotate_picture (GtkAdjustment *adj, GtkWidget *picture) +{ + set_picture_rotation (picture, adj->value); +... + + +and connect it to the scale widget's adjustment like this: + + +gtk_signal_connect (GTK_OBJECT (adj), "value_changed", + GTK_SIGNAL_FUNC (cb_rotate_picture), picture); + + +The "changed" signal is somewhat more elusive. It is never +emitted directly due to the +Common Functions, Signals, and Macros

+The GtkRange widget class is fairly complicated internally, +but, like all the "base class" widgets, most of its complexity +is only interesting if you want to hack on it. Also, almost +all of the functions and signals it defines are only really +used in writing derived widgets. There are, however, a few +useful functions and concepts that are defined in gtkrange.h +and are common to all range widgets. + + +Update Policies

+The "update policy" of a range widget defines at what points +during user interaction it will change the <gtk/gtkenums.h> as the enum +GtkUpdateType, are: + + +GTK_UPDATE_POLICY_CONTINUOUS - This is the default. +The "value_changed" signal is emitted continuously, +i.e. whenever the slider is moved by even the tiniest +amount. + + +GTK_UPDATE_POLICY_DISCONTINUOUS - The +"value_changed" signal is only emitted once the slider +has stopped moving and the user has released the mouse +button. + + +GTK_UPDATE_POLICY_DELAYED - The "value_change" +signal is emitted when the user releases the mouse button, +or if the slider stops moving for a short period of +time. + + + +The update policy of a range widget can be set by casting it +using the GTK_RANGE (Widget) macro and passing it +to this function: + + +void gtk_range_set_update_policy( GtkRange *range, + GtkUpdateType policy ); + + + +Getting and setting adjustments +

+Getting and setting the adjustment for a range widget "on +the fly" is done, predictably, with: + + +GtkAdjustment* gtk_range_get_adjustment( GtkRange *range ); + +void gtk_range_set_adjustment( GtkRange *range, + GtkAdjustment *adjustment ); + + + + +gtk_signal_emit_by_name (GTK_OBJECT (adjustment), "changed"); + + + +Key and Mouse bindings

+All of the GTK+ range widgets react to mouse clicks in more +or less the same way. Clicking button 1 in the trough will +cause its adjustment's +Vertical Range Widgets +

+All vertical range widgets can be operated with the up and +down arrow keys, as well as with the Control-Page Up and +Control-Page Down. + + +Horizontal Range Widgets +

+The left and right arrow keys work as you might expect in +these widgets, moving the slider back and forth by +Control-Left and +Control-Right, while for GtkHScrollbar, it's done +with Control-Home and Control-End. + + +Example

+This example is a somewhat modified version of the "range +widgets" test from +/* example-start rangewidgets rangewidgets.c */ + +#include + +GtkWidget *hscale, *vscale; + +void cb_pos_menu_select (GtkWidget *item, GtkPositionType pos) +{ + /* set the value position on both scale widgets */ + gtk_scale_set_value_pos (GTK_SCALE (hscale), pos); + gtk_scale_set_value_pos (GTK_SCALE (vscale), pos); +} + +void cb_update_menu_select (GtkWidget *item, GtkUpdateType policy) +{ + /* set the update policy for both scale widgets */ + gtk_range_set_update_policy (GTK_RANGE (hscale), policy); + gtk_range_set_update_policy (GTK_RANGE (vscale), policy); +} + +void cb_digits_scale (GtkAdjustment *adj) +{ + /* set the number of decimal places to which adj->vaule is rounded + */ + gtk_scale_set_digits (GTK_SCALE (hscale), (gint) adj->value); + gtk_scale_set_digits (GTK_SCALE (vscale), (gint) adj->value); +} + +void cb_page_size (GtkAdjustment *get, GtkAdjustment *set) +{ + /* set the page size and page increment size of the sample + adjustment to the value specified by the "Page Size" scale */ + set->page_size = get->value; + set->page_increment = get->value; + /* now emit the "changed" signal to reconfigure all the widgets that + are attached to this adjustment */ + gtk_signal_emit_by_name (GTK_OBJECT (set), "changed"); +} + +void cb_draw_value (GtkToggleButton *button) +{ + /* turn the value display on the scale widgets off or on depending + on the state of the checkbutton */ + gtk_scale_set_draw_value (GTK_SCALE (hscale), button->active); + gtk_scale_set_draw_value (GTK_SCALE (vscale), button->active); +} + +/* convenience functions */ + +GtkWidget *make_menu_item (gchar *name, GtkSignalFunc callback, + gpointer data) +{ + GtkWidget *item; + + item = gtk_menu_item_new_with_label (name); + gtk_signal_connect (GTK_OBJECT (item), "activate", + callback, data); + gtk_widget_show (item); + + return item; +} + +void scale_set_default_values (GtkScale *scale) +{ + gtk_range_set_update_policy (GTK_RANGE (scale), + GTK_UPDATE_CONTINUOUS); + gtk_scale_set_digits (scale, 1); + gtk_scale_set_value_pos (scale, GTK_POS_TOP); + gtk_scale_set_draw_value (scale, TRUE); +} + +/* makes the sample window */ + +void create_range_controls (void) +{ + GtkWidget *window; + GtkWidget *box1, *box2, *box3; + GtkWidget *button; + GtkWidget *scrollbar; + GtkWidget *separator; + GtkWidget *opt, *menu, *item; + GtkWidget *label; + GtkWidget *scale; + GtkObject *adj1, *adj2; + + /* standard window-creating stuff */ + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_signal_connect (GTK_OBJECT (window), "destroy", + GTK_SIGNAL_FUNC(gtk_main_quit), + NULL); + gtk_window_set_title (GTK_WINDOW (window), "range controls"); + + box1 = gtk_vbox_new (FALSE, 0); + gtk_container_add (GTK_CONTAINER (window), box1); + gtk_widget_show (box1); + + box2 = gtk_hbox_new (FALSE, 10); + gtk_container_border_width (GTK_CONTAINER (box2), 10); + gtk_box_pack_start (GTK_BOX (box1), box2, TRUE, TRUE, 0); + gtk_widget_show (box2); + + /* value, lower, upper, step_increment, page_increment, page_size */ + /* note that the page_size value only makes a difference for + scrollbar widgets, and the highest value you'll get is actually + (upper - page_size). */ + adj1 = gtk_adjustment_new (0.0, 0.0, 101.0, 0.1, 1.0, 1.0); + + vscale = gtk_vscale_new (GTK_ADJUSTMENT (adj1)); + scale_set_default_values (GTK_SCALE (vscale)); + gtk_box_pack_start (GTK_BOX (box2), vscale, TRUE, TRUE, 0); + gtk_widget_show (vscale); + + box3 = gtk_vbox_new (FALSE, 10); + gtk_box_pack_start (GTK_BOX (box2), box3, TRUE, TRUE, 0); + gtk_widget_show (box3); + + /* reuse the same adjustment */ + hscale = gtk_hscale_new (GTK_ADJUSTMENT (adj1)); + gtk_widget_set_usize (GTK_WIDGET (hscale), 200, 30); + scale_set_default_values (GTK_SCALE (hscale)); + gtk_box_pack_start (GTK_BOX (box3), hscale, TRUE, TRUE, 0); + gtk_widget_show (hscale); + + /* reuse the same adjustment again */ + scrollbar = gtk_hscrollbar_new (GTK_ADJUSTMENT (adj1)); + /* notice how this causes the scales to always be updated + continuously when the scrollbar is moved */ + gtk_range_set_update_policy (GTK_RANGE (scrollbar), + GTK_UPDATE_CONTINUOUS); + gtk_box_pack_start (GTK_BOX (box3), scrollbar, TRUE, TRUE, 0); + gtk_widget_show (scrollbar); + + box2 = gtk_hbox_new (FALSE, 10); + gtk_container_border_width (GTK_CONTAINER (box2), 10); + gtk_box_pack_start (GTK_BOX (box1), box2, TRUE, TRUE, 0); + gtk_widget_show (box2); + + /* a checkbutton to control whether the value is displayed or not */ + button = gtk_check_button_new_with_label + ("Display value on scale widgets"); + gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (button), TRUE); + gtk_signal_connect (GTK_OBJECT (button), "toggled", GTK_SIGNAL_FUNC + (cb_draw_value), NULL); + gtk_box_pack_start (GTK_BOX (box2), button, TRUE, TRUE, 0); + gtk_widget_show (button); + + box2 = gtk_hbox_new (FALSE, 10); + gtk_container_border_width (GTK_CONTAINER (box2), 10); + + /* an option menu to change the position of the value */ + label = gtk_label_new ("Scale Value Position:"); + gtk_box_pack_start (GTK_BOX (box2), label, FALSE, FALSE, 0); + gtk_widget_show (label); + + opt = gtk_option_menu_new(); + menu = gtk_menu_new(); + + item = make_menu_item ("Top", GTK_SIGNAL_FUNC (cb_pos_menu_select), + GINT_TO_POINTER (GTK_POS_TOP)); + gtk_menu_append (GTK_MENU (menu), item); + + item = make_menu_item ("Bottom", GTK_SIGNAL_FUNC (cb_pos_menu_select), + GINT_TO_POINTER (GTK_POS_BOTTOM)); + gtk_menu_append (GTK_MENU (menu), item); + + item = make_menu_item ("Left", GTK_SIGNAL_FUNC (cb_pos_menu_select), + GINT_TO_POINTER (GTK_POS_LEFT)); + gtk_menu_append (GTK_MENU (menu), item); + + item = make_menu_item ("Right", GTK_SIGNAL_FUNC (cb_pos_menu_select), + GINT_TO_POINTER (GTK_POS_RIGHT)); + gtk_menu_append (GTK_MENU (menu), item); + + gtk_option_menu_set_menu (GTK_OPTION_MENU (opt), menu); + gtk_box_pack_start (GTK_BOX (box2), opt, TRUE, TRUE, 0); + gtk_widget_show (opt); + + gtk_box_pack_start (GTK_BOX (box1), box2, TRUE, TRUE, 0); + gtk_widget_show (box2); + + box2 = gtk_hbox_new (FALSE, 10); + gtk_container_border_width (GTK_CONTAINER (box2), 10); + + /* yet another option menu, this time for the update policy of the + scale widgets */ + label = gtk_label_new ("Scale Update Policy:"); + gtk_box_pack_start (GTK_BOX (box2), label, FALSE, FALSE, 0); + gtk_widget_show (label); + + opt = gtk_option_menu_new(); + menu = gtk_menu_new(); + + item = make_menu_item ("Continuous", + GTK_SIGNAL_FUNC (cb_update_menu_select), + GINT_TO_POINTER (GTK_UPDATE_CONTINUOUS)); + gtk_menu_append (GTK_MENU (menu), item); + + item = make_menu_item ("Discontinuous", + GTK_SIGNAL_FUNC (cb_update_menu_select), + GINT_TO_POINTER (GTK_UPDATE_DISCONTINUOUS)); + gtk_menu_append (GTK_MENU (menu), item); + + item = make_menu_item ("Delayed", + GTK_SIGNAL_FUNC (cb_update_menu_select), + GINT_TO_POINTER (GTK_UPDATE_DELAYED)); + gtk_menu_append (GTK_MENU (menu), item); + + gtk_option_menu_set_menu (GTK_OPTION_MENU (opt), menu); + gtk_box_pack_start (GTK_BOX (box2), opt, TRUE, TRUE, 0); + gtk_widget_show (opt); + + gtk_box_pack_start (GTK_BOX (box1), box2, TRUE, TRUE, 0); + gtk_widget_show (box2); + + box2 = gtk_hbox_new (FALSE, 10); + gtk_container_border_width (GTK_CONTAINER (box2), 10); + + /* a GtkHScale widget for adjusting the number of digits on the + sample scales. */ + label = gtk_label_new ("Scale Digits:"); + gtk_box_pack_start (GTK_BOX (box2), label, FALSE, FALSE, 0); + gtk_widget_show (label); + + adj2 = gtk_adjustment_new (1.0, 0.0, 5.0, 1.0, 1.0, 0.0); + gtk_signal_connect (GTK_OBJECT (adj2), "value_changed", + GTK_SIGNAL_FUNC (cb_digits_scale), NULL); + scale = gtk_hscale_new (GTK_ADJUSTMENT (adj2)); + gtk_scale_set_digits (GTK_SCALE (scale), 0); + gtk_box_pack_start (GTK_BOX (box2), scale, TRUE, TRUE, 0); + gtk_widget_show (scale); + + gtk_box_pack_start (GTK_BOX (box1), box2, TRUE, TRUE, 0); + gtk_widget_show (box2); + + box2 = gtk_hbox_new (FALSE, 10); + gtk_container_border_width (GTK_CONTAINER (box2), 10); + + /* And, one last GtkHScale widget for adjusting the page size of the + scrollbar. */ + label = gtk_label_new ("Scrollbar Page Size:"); + gtk_box_pack_start (GTK_BOX (box2), label, FALSE, FALSE, 0); + gtk_widget_show (label); + + adj2 = gtk_adjustment_new (1.0, 1.0, 101.0, 1.0, 1.0, 0.0); + gtk_signal_connect (GTK_OBJECT (adj2), "value_changed", + GTK_SIGNAL_FUNC (cb_page_size), adj1); + scale = gtk_hscale_new (GTK_ADJUSTMENT (adj2)); + gtk_scale_set_digits (GTK_SCALE (scale), 0); + gtk_box_pack_start (GTK_BOX (box2), scale, TRUE, TRUE, 0); + gtk_widget_show (scale); + + gtk_box_pack_start (GTK_BOX (box1), box2, TRUE, TRUE, 0); + gtk_widget_show (box2); + + separator = gtk_hseparator_new (); + gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 0); + gtk_widget_show (separator); + + box2 = gtk_vbox_new (FALSE, 10); + gtk_container_border_width (GTK_CONTAINER (box2), 10); + gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, TRUE, 0); + gtk_widget_show (box2); + + button = gtk_button_new_with_label ("Quit"); + gtk_signal_connect_object (GTK_OBJECT (button), "clicked", + GTK_SIGNAL_FUNC(gtk_main_quit), + NULL); + gtk_box_pack_start (GTK_BOX (box2), button, TRUE, TRUE, 0); + GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); + gtk_widget_grab_default (button); + gtk_widget_show (button); + + gtk_widget_show (window); +} + +int main (int argc, char *argv[]) +{ + gtk_init(&argc, &argv); + + create_range_controls(); + + gtk_main(); + + return 0; +} + +/* example-end */ + + Miscallaneous Widgets @@ -5705,7 +6484,804 @@ Please see the GtkList example on this, which covers the usage of a GtkListItem as well. -Menu Widgets + Tree Widget

+ +The purpose of tree widgets is to display hierarchically-organized +data. The GtkTree widget itself is a vertical container for widgets +of type GtkTreeItem. GtkTree itself is not terribly different from +GtkList - both are derived directly from GtkContainer, and the +GtkContainer methods work in the same way on GtkTree widgets as on +GtkList widgets. The difference is that GtkTree widgets can be nested +within other GtkTree widgets. We'll see how to do this shortly. + +The GtkTree widget has its own window, and defaults to a white +background, as does GtkList. Also, most of the GtkTree methods work +in the same way as the corresponding GtkList ones. However, GtkTree +is not derived from GtkList, so you cannot use them interchangeably. + + Creating a Tree +

+A GtkTree is created in the usual way, using: + + +GtkWidget* gtk_tree_new( void ); + + +Like the GtkList widget, a GtkTree will simply keep growing as more +items are added to it, as well as when subtrees are expanded. +For this reason, they are almost always packed into a +GtkScrolledWindow. You might want to use gtk_widget_set_usize() on +the scrolled window to ensure that it is big enough to see the tree's +items, as the default size for GtkScrolledWindow is quite small. + +Now that you have a tree, you'll probably want to add some items to +it. below +explains the gory details of GtkTreeItem. For now, it'll suffice to +create one, using: + + +GtkWidget* gtk_tree_item_new_with_label( gchar *label ); + + +You can then add it to the tree using one of the following (see + +below for more options): + + +void gtk_tree_append( GtkTree *tree, + GtkWidget *tree_item ); + +void gtk_tree_prepend( GtkTree *tree, + GtkWidget *tree_item ); + + +Note that you must add items to a GtkTree one at a time - there is no +equivalent to gtk_list_*_items(). + + Adding a Subtree +

+A subtree is created like any other GtkTree widget. A subtree is added +to another tree beneath a tree item, using: + + +void gtk_tree_item_set_subtree( GtkTreeItem *tree_item, + GtkWidget *subtree ); + + +You do not need to call gtk_widget_show() on a subtree before or after +adding it to a GtkTreeItem. However, you must have added the +GtkTreeItem in question to a parent tree before calling +gtk_tree_item_set_subtree(). This is because, technically, the parent +of the subtree is not the GtkTreeItem which "owns" it, but +rather the GtkTree which holds that GtkTreeItem. + +When you add a subtree to a GtkTreeItem, a plus or minus sign appears +beside it, which the user can click on to "expand" or "collapse" it, +meaning, to show or hide its subtree. GtkTreeItems are collapsed by +default. Note that when you collapse a GtkTreeItem, any selected +items in its subtree remain selected, which may not be what the user +expects. + + Handling the Selection List +

+As with GtkList, the GtkTree type has a selection field, and +it is possible to control the behaviour of the tree (somewhat) by +setting the selection type using: + + +void gtk_tree_set_selection_mode( GtkTree *tree, + GtkSelectionMode mode ); + + +The semantics associated with the various selection modes are +described in the section on the GtkList widget. As with the GtkList +widget, the "select_child", "unselect_child" (not really - see below for an explanation), +and "selection_changed" signals are emitted when list items are +selected or unselected. However, in order to take advantage of these +signals, you need to know which GtkTree widget they will be +emitted by, and where to find the list of selected items. + +This is a source of potential confusion. The best way to explain this +is that though all GtkTree widgets are created equal, some are more +equal than others. All GtkTree widgets have their own X window, and +can therefore receive events such as mouse clicks (if their +GtkTreeItems or their children don't catch them first!). However, to +make GTK_SELECTION_SINGLE and GTK_SELECTION_BROWSE selection types +behave in a sane manner, the list of selected items is specific to the +topmost GtkTree widget in a hierarchy, known as the "root tree". + +Thus, accessing the selectionfield directly in an arbitrary +GtkTree widget is not a good idea unless you know it's the +root tree. Instead, use the GTK_TREE_SELECTION (Tree) macro, which +gives the root tree's selection list as a GList pointer. Of course, +this list can include items that are not in the subtree in question if +the selection type is GTK_SELECTION_MULTIPLE. + +Finally, the "select_child" (and "unselect_child", in theory) signals +are emitted by all trees, but the "selection_changed" signal is only +emitted by the root tree. Consequently, if you want to handle the +"select_child" signal for a tree and all its subtrees, you will have +to call gtk_signal_connect() for every subtree. + + Tree Widget Internals +

+The GtkTree's struct definition looks like this: + + +struct _GtkTree +{ + GtkContainer container; + + GList *children; + + GtkTree* root_tree; /* owner of selection list */ + GtkWidget* tree_owner; + GList *selection; + guint level; + guint indent_value; + guint current_indent; + guint selection_mode : 2; + guint view_mode : 1; + guint view_line : 1; +}; + + +The perils associated with accessing the selection field +directly have already been mentioned. The other important fields of +the struct can also be accessed with handy macros or class functions. +GTK_TREE_IS_ROOT_TREE (Tree) returns a boolean value which indicates +whether a tree is the root tree in a GtkTree hierarchy, while +GTK_TREE_ROOT_TREE (Tree) returns the root tree, an object of type +GtkTree (so, remember to cast it using GTK_WIDGET (Tree) if you want +to use one of the gtk_widget_*() functions on it). + +Instead of directly accessing the children field of a GtkTree widget, +it's probably best to cast it using GTK_CONTAINER (Tree), and pass it +to the gtk_container_children() function. This creates a duplicate of +the original list, so it's advisable to free it up using g_list_free() +after you're done with it, or to iterate on it destructively, like +this: + + +children = gtk_container_children (GTK_CONTAINER (tree)); +while (children) { + do_something_nice (GTK_TREE_ITEM (children->data)); + children = g_list_remove_link (children, children); +} + + +The tree_owner field is defined only in subtrees, where it +points to the GtkTreeItem widget which holds the tree in question. +The level field indicates how deeply nested a particular tree +is; root trees have level 0, and each successive level of subtrees has +a level one greater than the parent level. This field is set only +after a GtkTree widget is actually mapped (i.e. drawn on the screen). + + Signals

+ +void selection_changed( GtkTree *tree ); + + +This signal will be emitted whenever the selection field of a +GtkTree has changed. This happens when a child of the GtkTree is +selected or deselected. + + +void select_child( GtkTree *tree, + GtkWidget *child ); + + +This signal is emitted when a child of the GtkTree is about to get +selected. This happens on calls to gtk_tree_select_item(), +gtk_tree_select_child(), on all button presses and calls to +gtk_tree_item_toggle() and gtk_item_toggle(). It may sometimes be +indirectly triggered on other occasions where children get added to or +removed from the GtkTree. + + +void unselect_child (GtkTree *tree, + GtkWidget *child); + + +This signal is emitted when a child of the GtkTree is about to get +deselected. As of GTK+ 1.0.4, this seems to only occur on calls to +gtk_tree_unselect_item() or gtk_tree_unselect_child(), and perhaps on +other occasions, but not when a button press deselects a +child, nor on emission of the "toggle" signal by gtk_item_toggle(). + + Functions and Macros

+ +guint gtk_tree_get_type( void ); + + +Returns the `GtkTree' type identifier. + + +GtkWidget* gtk_tree_new( void ); + + +Create a new GtkTree object. The new widget is returned as a pointer to a +GtkWidget object. NULL is returned on failure. + + +void gtk_tree_append( GtkTree *tree, + GtkWidget *tree_item ); + + +Append a tree item to a GtkTree. + + +void gtk_tree_prepend( GtkTree *tree, + GtkWidget *tree_item ); + + +Prepend a tree item to a GtkTree. + + +void gtk_tree_insert( GtkTree *tree, + GtkWidget *tree_item, + gint position ); + + +Insert a tree item into a GtkTree at the position in the list +specified by position. + + +void gtk_tree_remove_items( GtkTree *tree, + GList *items ); + + +Remove a list of items (in the form of a GList *) from a GtkTree. +Note that removing an item from a tree dereferences (and thus usually) +destroys it and its subtree, if it has one, and all +subtrees in that subtree. If you want to remove only one item, you +can use gtk_container_remove(). + + +void gtk_tree_clear_items( GtkTree *tree, + gint start, + gint end ); + + +Remove the items from position start to position end +from a GtkTree. The same warning about dereferencing applies here, as +gtk_tree_clear_items() simply constructs a list and passes it to +gtk_tree_remove_items(). + + +void gtk_tree_select_item( GtkTree *tree, + gint item ); + + +Emits the "select_item" signal for the child at position +item, thus selecting the child (unless you unselect it in a +signal handler...) + + +void gtk_tree_unselect_item( GtkTree *tree, + gint item ); + + +Emits the "unselect_item" signal for the child at position +item, thus unselecting the child. + + +void gtk_tree_select_child( GtkTree *tree, + GtkWidget *tree_item ); + + +Emits the "select_item" signal for the child tree_item, thus +selecting it. + + +void gtk_tree_unselect_child( GtkTree *tree, + GtkWidget *tree_item ); + + +Emits the "unselect_item" signal for the child tree_item, +thus unselecting it. + + +gint gtk_tree_child_position( GtkTree *tree, + GtkWidget *child ); + + +Returns the position in the tree of child, unless +child is not in the tree, in which case it returns -1. + + +void gtk_tree_set_selection_mode( GtkTree *tree, + GtkSelectionMode mode ); + + +Sets the selection mode, which can be one of GTK_SELECTION_SINGLE (the +default), GTK_SELECTION_BROWSE, GTK_SELECTION_MULTIPLE, or +GTK_SELECTION_EXTENDED. This is only defined for root trees, which +makes sense, since the root tree "owns" the selection. Setting it for +subtrees has no effect at all; the value is simply ignored. + + +void gtk_tree_set_view_mode( GtkTree *tree, + GtkTreeViewMode mode ); + + +Sets the "view mode", which can be either GTK_TREE_VIEW_LINE (the +default) or GTK_TREE_VIEW_ITEM. The view mode propagates from a tree +to its subtrees, and can't be set exclusively to a subtree (this is +not exactly true - see the example code comments). + +The term "view mode" is rather ambiguous - basically, it controls the +way the hilight is drawn when one of a tree's children is selected. +If it's GTK_TREE_VIEW_LINE, the entire GtkTreeItem widget is +hilighted, while for GTK_TREE_VIEW_ITEM, only the child widget +(i.e. usually the label) is hilighted. + + +void gtk_tree_set_view_lines( GtkTree *tree, + guint flag ); + + +Controls whether connecting lines between tree items are drawn. +flag is either TRUE, in which case they are, or FALSE, in +which case they aren't. + + +GtkTree *GTK_TREE (gpointer obj); + + +Cast a generic pointer to `GtkTree *'. + + +GtkTreeClass *GTK_TREE_CLASS (gpointer class); + + +Cast a generic pointer to `GtkTreeClass*'. + + +gint GTK_IS_TREE (gpointer obj); + + +Determine if a generic pointer refers to a `GtkTree' object. + + +gint GTK_IS_ROOT_TREE (gpointer obj) + + +Determine if a generic pointer refers to a `GtkTree' object +and is a root tree. Though this will accept any pointer, the +results of passing it a pointer that does not refer to a GtkTree are +undefined and possibly harmful. + + +GtkTree *GTK_TREE_ROOT_TREE (gpointer obj) + + +Return the root tree of a pointer to a `GtkTree' object. The above +warning applies. + + +GList *GTK_TREE_SELECTION( gpointer obj) + + +Return the selection list of the root tree of a `GtkTree' object. The +above warning applies here, too. + + Tree Item Widget

+The GtkTreeItem widget, like GtkListItem, is derived from GtkItem, +which in turn is derived from GtkBin. Therefore, the item itself is a +generic container holding exactly one child widget, which can be of +any type. The GtkTreeItem widget has a number of extra fields, but +the only one we need be concerned with is the subtree field. + +The definition for the GtkTreeItem struct looks like this: + + +struct _GtkTreeItem +{ + GtkItem item; + + GtkWidget *subtree; + GtkWidget *pixmaps_box; + GtkWidget *plus_pix_widget, *minus_pix_widget; + + GList *pixmaps; /* pixmap node for this items color depth */ + + guint expanded : 1; +}; + + +The pixmaps_box field is a GtkEventBox which catches clicks +on the plus/minus symbol which controls expansion and collapsing. The +pixmaps field points to an internal data structure. Since +you can always obtain the subtree of a GtkTreeItem in a (relatively) +type-safe manner with the GTK_TREE_ITEM_SUBTREE (Item) macro, it's +probably advisable never to touch the insides of a GtkTreeItem unless +you really know what you're doing. + +Since it is directly derived from a GtkItem it can be treated as such +by using the GTK_ITEM (TreeItem) macro. A GtkTreeItem usually holds a +label, so the convenience function gtk_list_item_new_with_label() is +provided. The same effect can be achieved using code like the +following, which is actually copied verbatim from +gtk_tree_item_new_with_label(): + + +tree_item = gtk_tree_item_new (); +label_widget = gtk_label_new (label); +gtk_misc_set_alignment (GTK_MISC (label_widget), 0.0, 0.5); + +gtk_container_add (GTK_CONTAINER (tree_item), label_widget); +gtk_widget_show (label_widget); + + +As one is not forced to add a GtkLabel to a GtkTreeItem, you could +also add a GtkHBox or a GtkArrow, or even a GtkNotebook (though your +app will likely be quite unpopular in this case) to the GtkTreeItem. + +If you remove all the items from a subtree, it will be destroyed and +unparented, unless you reference it beforehand, and the GtkTreeItem +which owns it will be collapsed. So, if you want it to stick around, +do something like the following: + + +gtk_widget_ref (tree); +owner = GTK_TREE(tree)->tree_owner; +gtk_container_remove (GTK_CONTAINER(tree), item); +if (tree->parent == NULL){ + gtk_tree_item_expand (GTK_TREE_ITEM(owner)); + gtk_tree_item_set_subtree (GTK_TREE_ITEM(owner), tree); +} +else + gtk_widget_unref (tree); + + +Finally, drag-n-drop does work with GtkTreeItems. You just +have to make sure that the GtkTreeItem you want to make into a drag +item or a drop site has not only been added to a GtkTree, but that +each successive parent widget has a parent itself, all the way back to +a toplevel or dialog window, when you call gtk_widget_dnd_drag_set() +or gtk_widget_dnd_drop_set(). Otherwise, strange things will happen. + + Signals +

+GtkTreeItem inherits the "select", "deselect", and "toggle" signals +from GtkItem. In addition, it adds two signals of its own, "expand" +and "collapse". + + +void select( GtkItem *tree_item ); + + +This signal is emitted when an item is about to be selected, either +after it has been clicked on by the user, or when the program calls +gtk_tree_item_select(), gtk_item_select(), or gtk_tree_select_child(). + + +void deselect( GtkItem *tree_item ); + + +This signal is emitted when an item is about to be unselected, either +after it has been clicked on by the user, or when the program calls +gtk_tree_item_deselect() or gtk_item_deselect(). In the case of +GtkTreeItems, it is also emitted by gtk_tree_unselect_child(), and +sometimes gtk_tree_select_child(). + + +void toggle( GtkItem *tree_item ); + + +This signal is emitted when the program calls gtk_item_toggle(). The +effect it has when emitted on a GtkTreeItem is to call +gtk_tree_select_child() (and never gtk_tree_unselect_child()) on the +item's parent tree, if the item has a parent tree. If it doesn't, +then the highlight is reversed on the item. + + +void expand( GtkTreeItem *tree_item ); + + +This signal is emitted when the tree item's subtree is about to be +expanded, that is, when the user clicks on the plus sign next to the +item, or when the program calls gtk_tree_item_expand(). + + +void collapse( GtkTreeItem *tree_item ); + + +This signal is emitted when the tree item's subtree is about to be +collapsed, that is, when the user clicks on the minus sign next to the +item, or when the program calls gtk_tree_item_collapse(). + + Functions and Macros +

+ +guint gtk_tree_item_get_type( void ); + + +Returns the `GtkTreeItem' type identifier. + + +GtkWidget* gtk_tree_item_new( void ); + + +Create a new GtkTreeItem object. The new widget is returned as a pointer +to a GtkWidget object. NULL is returned on failure. + + +GtkWidget* gtk_tree_item_new_with_label (gchar *label); + + +Create a new GtkTreeItem object, having a single GtkLabel as +the sole child. The new widget is returned as a pointer to a +GtkWidget object. NULL is returned on failure. + + +void gtk_tree_item_select( GtkTreeItem *tree_item ); + + +This function is basicaly a wrapper around a call to +gtk_item_select (GTK_ITEM (tree_item)) which will emit the +select signal. + + +void gtk_tree_item_deselect( GtkTreeItem *tree_item ); + + +This function is basicaly a wrapper around a call to +gtk_item_deselect (GTK_ITEM (tree_item)) which will emit the +deselect signal. + + +void gtk_tree_item_set_subtree( GtkTreeItem *tree_item, + GtkWidget *subtree ); + + +This function adds subtree to tree_item, showing it if tree_item is +expanded, or hiding it if tree_item is collapsed. Again, remember +that the tree_item must have already been added to a tree for this to +work. + + +void gtk_tree_item_remove_subtree( GtkTreeItem *tree_item ); + + +This removes all of tree_item's subtree's children (thus unreferencing +and destroying it, any of its children's subtrees, and so on...), then +removes the subtree itself, and hides the plus/minus sign. + + +void gtk_tree_item_expand( GtkTreeItem *tree_item ); + + +This emits the "expand" signal on tree_item, which expands it. + + +void gtk_tree_item_collapse( GtkTreeItem *tree_item ); + + +This emits the "collapse" signal on tree_item, which collapses it. + + +GtkTreeItem *GTK_TREE_ITEM (gpointer obj) + + +Cast a generic pointer to `GtkTreeItem*'. + + +GtkTreeItemClass *GTK_TREE_ITEM_CLASS (gpointer obj) + + +Cast a generic pointer to `GtkTreeItemClass'. + + +gint GTK_IS_TREE_ITEM (gpointer obj) + + +Determine if a generic pointer refers to a `GtkTreeItem' object. + + +GtkWidget GTK_TREE_ITEM_SUBTREE (gpointer obj) + + +Return's a tree item's subtree (obj should point to a `GtkTreeItem' +object). + + Tree Example +

+This is somewhat like the tree example in testgtk.c, but a lot less +complete (although much better commented). It puts up a window with a +tree, and connects all the signals for the relevant objects, so you +can see when they are emitted. + + +/* example-start tree tree.c */ + +#include + +/* for all the GtkItem:: and GtkTreeItem:: signals */ +static void cb_itemsignal (GtkWidget *item, gchar *signame) +{ + gchar *name; + GtkLabel *label; + + /* It's a GtkBin, so it has one child, which we know to be a + label, so get that */ + label = GTK_LABEL (GTK_BIN (item)->child); + /* Get the text of the label */ + gtk_label_get (label, &name); + /* Get the level of the tree which the item is in */ + g_print ("%s called for item %s->%p, level %d\n", signame, name, + item, GTK_TREE (item->parent)->level); +} + +/* Note that this is never called */ +static void cb_unselect_child (GtkWidget *root_tree, GtkWidget *child, + GtkWidget *subtree) +{ + g_print ("unselect_child called for root tree %p, subtree %p, child %p\n", + root_tree, subtree, child); +} + +/* Note that this is called every time the user clicks on an item, + whether it is already selected or not. */ +static void cb_select_child (GtkWidget *root_tree, GtkWidget *child, + GtkWidget *subtree) +{ + g_print ("select_child called for root tree %p, subtree %p, child %p\n", + root_tree, subtree, child); +} + +static void cb_selection_changed (GtkWidget *tree) +{ + GList *i; + + g_print ("selection_change called for tree %p\n", tree); + g_print ("selected objects are:\n"); + + i = GTK_TREE_SELECTION(tree); + while (i){ + gchar *name; + GtkLabel *label; + GtkWidget *item; + + /* Get a GtkWidget pointer from the list node */ + item = GTK_WIDGET (i->data); + label = GTK_LABEL (GTK_BIN (item)->child); + gtk_label_get (label, &name); + g_print ("\t%s on level %d\n", name, GTK_TREE + (item->parent)->level); + i = i->next; + } +} + +int main (int argc, char *argv[]) +{ + GtkWidget *window, *scrolled_win, *tree; + static gchar *itemnames[] = {"Foo", "Bar", "Baz", "Quux", + "Maurice"}; + gint i; + + gtk_init (&argc, &argv); + + /* a generic toplevel window */ + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_signal_connect (GTK_OBJECT(window), "delete_event", + GTK_SIGNAL_FUNC (gtk_main_quit), NULL); + gtk_container_border_width (GTK_CONTAINER(window), 5); + + /* A generic scrolled window */ + scrolled_win = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + gtk_widget_set_usize (scrolled_win, 150, 200); + gtk_container_add (GTK_CONTAINER(window), scrolled_win); + gtk_widget_show (scrolled_win); + + /* Create the root tree */ + tree = gtk_tree_new(); + g_print ("root tree is %p\n", tree); + /* connect all GtkTree:: signals */ + gtk_signal_connect (GTK_OBJECT(tree), "select_child", + GTK_SIGNAL_FUNC(cb_select_child), tree); + gtk_signal_connect (GTK_OBJECT(tree), "unselect_child", + GTK_SIGNAL_FUNC(cb_unselect_child), tree); + gtk_signal_connect (GTK_OBJECT(tree), "selection_changed", + GTK_SIGNAL_FUNC(cb_selection_changed), tree); + /* Add it to the scrolled window */ + gtk_container_add (GTK_CONTAINER(scrolled_win), tree); + /* Set the selection mode */ + gtk_tree_set_selection_mode (GTK_TREE(tree), + GTK_SELECTION_MULTIPLE); + /* Show it */ + gtk_widget_show (tree); + + for (i = 0; i < 5; i++){ + GtkWidget *subtree, *item; + gint j; + + /* Create a tree item */ + item = gtk_tree_item_new_with_label (itemnames[i]); + /* Connect all GtkItem:: and GtkTreeItem:: signals */ + gtk_signal_connect (GTK_OBJECT(item), "select", + GTK_SIGNAL_FUNC(cb_itemsignal), "select"); + gtk_signal_connect (GTK_OBJECT(item), "deselect", + GTK_SIGNAL_FUNC(cb_itemsignal), "deselect"); + gtk_signal_connect (GTK_OBJECT(item), "toggle", + GTK_SIGNAL_FUNC(cb_itemsignal), "toggle"); + gtk_signal_connect (GTK_OBJECT(item), "expand", + GTK_SIGNAL_FUNC(cb_itemsignal), "expand"); + gtk_signal_connect (GTK_OBJECT(item), "collapse", + GTK_SIGNAL_FUNC(cb_itemsignal), "collapse"); + /* Add it to the parent tree */ + gtk_tree_append (GTK_TREE(tree), item); + /* Show it - this can be done at any time */ + gtk_widget_show (item); + /* Create this item's subtree */ + subtree = gtk_tree_new(); + g_print ("-> item %s->%p, subtree %p\n", itemnames[i], item, + subtree); + + /* This is still necesary if you want these signals to be called + for the subtree's children. Note that selection_change will be + signalled for the root tree regardless. */ + gtk_signal_connect (GTK_OBJECT(subtree), "select_child", + GTK_SIGNAL_FUNC(cb_select_child), subtree); + gtk_signal_connect (GTK_OBJECT(subtree), "unselect_child", + GTK_SIGNAL_FUNC(cb_unselect_child), subtree); + /* This has absolutely no effect, because it is completely ignored + in subtrees */ + gtk_tree_set_selection_mode (GTK_TREE(subtree), + GTK_SELECTION_SINGLE); + /* Neither does this, but for a rather different reason - the + view_mode and view_line values of a tree are propagated to + subtrees when they are mapped. So, setting it later on would + actually have a (somewhat unpredictable) effect */ + gtk_tree_set_view_mode (GTK_TREE(subtree), GTK_TREE_VIEW_ITEM); + /* Set this item's subtree - note that you cannot do this until + AFTER the item has been added to its parent tree! */ + gtk_tree_item_set_subtree (GTK_TREE_ITEM(item), subtree); + + for (j = 0; j < 5; j++){ + GtkWidget *subitem; + + /* Create a subtree item, in much the same way */ + subitem = gtk_tree_item_new_with_label (itemnames[j]); + /* Connect all GtkItem:: and GtkTreeItem:: signals */ + gtk_signal_connect (GTK_OBJECT(subitem), "select", + GTK_SIGNAL_FUNC(cb_itemsignal), "select"); + gtk_signal_connect (GTK_OBJECT(subitem), "deselect", + GTK_SIGNAL_FUNC(cb_itemsignal), "deselect"); + gtk_signal_connect (GTK_OBJECT(subitem), "toggle", + GTK_SIGNAL_FUNC(cb_itemsignal), "toggle"); + gtk_signal_connect (GTK_OBJECT(subitem), "expand", + GTK_SIGNAL_FUNC(cb_itemsignal), "expand"); + gtk_signal_connect (GTK_OBJECT(subitem), "collapse", + GTK_SIGNAL_FUNC(cb_itemsignal), "collapse"); + g_print ("-> -> item %s->%p\n", itemnames[j], subitem); + /* Add it to its parent tree */ + gtk_tree_append (GTK_TREE(subtree), subitem); + /* Show it */ + gtk_widget_show (subitem); + } + } + + /* Show the window and loop endlessly */ + gtk_widget_show (window); + gtk_main(); + return 0; +} +/* example-end */ + + + +Menu Widget

There are two ways to create menus, there's the easy way, and there's the @@ -6068,14 +7644,14 @@ of the global variables used in the menufactory.c file. extern "C" { #endif /* __cplusplus */ -void get_main_menu (GtkWidget **menubar, GtkAcceleratorTable **table); -void menus_create(GtkMenuEntry *entries, int nmenu_entries); +void get_main_menu (GtkWidget *, GtkWidget **menubar); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* __MENUFACTORY_H__ */ + /* example-end */ @@ -6089,11 +7665,7 @@ And here is the menufactory.c file. #include "mfmain.h" - -static void menus_remove_accel(GtkWidget * widget, gchar * signal_name, gchar * path); -static gint menus_install_accel(GtkWidget * widget, gchar * signal_name, gchar key, gchar modifiers, gchar * path); -void menus_init(void); -void menus_create(GtkMenuEntry * entries, int nmenu_entries); +static void print_hello(GtkWidget *widget, gpointer data); /* this is the GtkMenuEntry structure used to create new menus. The @@ -6106,131 +7678,39 @@ void menus_create(GtkMenuEntry * entries, int nmenu_entries); static GtkMenuEntry menu_items[] = { - {"

/File/New", "N", NULL, NULL}, - {"
/File/Open", "O", NULL, NULL}, - {"
/File/Save", "S", NULL, NULL}, - {"
/File/Save as", NULL, NULL, NULL}, - {"
/File/", NULL, NULL, NULL}, - {"
/File/Quit", "Q", file_quit_cmd_callback, "OK, I'll quit"}, - {"
/Options/Test", NULL, NULL, NULL} + {"
/File/New", "N", print_hello, NULL}, + {"
/File/Open", "O", print_hello, NULL}, + {"
/File/Save", "S", print_hello, NULL}, + {"
/File/Save as", NULL, NULL, NULL}, + {"
/File/", NULL, NULL, NULL}, + {"
/File/Quit", "Q", file_quit_cmd_callback, "OK, I'll quit"}, + {"
/Options/Test", NULL, NULL, NULL} }; -/* calculate the number of menu_item's */ -static int nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]); -static int initialize = TRUE; -static GtkMenuFactory *factory = NULL; -static GtkMenuFactory *subfactory[1]; -static GHashTable *entry_ht = NULL; - -void get_main_menu(GtkWidget ** menubar, GtkAcceleratorTable ** table) +static void +print_hello(GtkWidget *widget, gpointer data) { - if (initialize) - menus_init(); + printf("hello!\n"); +} + +void get_main_menu(GtkWidget *window, GtkWidget ** menubar) +{ + int nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]); + GtkMenuFactory *factory; + GtkMenuFactory *subfactory; + + factory = gtk_menu_factory_new(GTK_MENU_FACTORY_MENU_BAR); + subfactory = gtk_menu_factory_new(GTK_MENU_FACTORY_MENU_BAR); + + gtk_menu_factory_add_subfactory(factory, subfactory, "
"); + gtk_menu_factory_add_entries(factory, menu_items, nmenu_items); + gtk_window_add_accelerator_table(GTK_WINDOW(window), subfactory->table); if (menubar) - *menubar = subfactory[0]->widget; - if (table) - *table = subfactory[0]->table; + *menubar = subfactory->widget; } -void menus_init(void) -{ - if (initialize) { - initialize = FALSE; - - factory = gtk_menu_factory_new(GTK_MENU_FACTORY_MENU_BAR); - subfactory[0] = gtk_menu_factory_new(GTK_MENU_FACTORY_MENU_BAR); - - gtk_menu_factory_add_subfactory(factory, subfactory[0], "
"); - menus_create(menu_items, nmenu_items); - } -} - -void menus_create(GtkMenuEntry * entries, int nmenu_entries) -{ - char *accelerator; - int i; - - if (initialize) - menus_init(); - - if (entry_ht) - for (i = 0; i < nmenu_entries; i++) { - accelerator = g_hash_table_lookup(entry_ht, entries[i].path); - if (accelerator) { - if (accelerator[0] == '\0') - entries[i].accelerator = NULL; - else - entries[i].accelerator = accelerator; - } - } - gtk_menu_factory_add_entries(factory, entries, nmenu_entries); - - for (i = 0; i < nmenu_entries; i++) - if (entries[i].widget) { - gtk_signal_connect(GTK_OBJECT(entries[i].widget), "install_accelerator", - (GtkSignalFunc) menus_install_accel, - entries[i].path); - gtk_signal_connect(GTK_OBJECT(entries[i].widget), "remove_accelerator", - (GtkSignalFunc) menus_remove_accel, - entries[i].path); - } -} - -static gint menus_install_accel(GtkWidget * widget, gchar * signal_name, gchar key, gchar modifiers, gchar * path) -{ - char accel[64]; - char *t1, t2[2]; - - accel[0] = '\0'; - if (modifiers & GDK_CONTROL_MASK) - strcat(accel, ""); - if (modifiers & GDK_SHIFT_MASK) - strcat(accel, ""); - if (modifiers & GDK_MOD1_MASK) - strcat(accel, ""); - - t2[0] = key; - t2[1] = '\0'; - strcat(accel, t2); - - if (entry_ht) { - t1 = g_hash_table_lookup(entry_ht, path); - g_free(t1); - } else - entry_ht = g_hash_table_new(g_str_hash, g_str_equal); - - g_hash_table_insert(entry_ht, path, g_strdup(accel)); - - return TRUE; -} - -static void menus_remove_accel(GtkWidget * widget, gchar * signal_name, gchar * path) -{ - char *t; - - if (entry_ht) { - t = g_hash_table_lookup(entry_ht, path); - g_free(t); - - g_hash_table_insert(entry_ht, path, g_strdup("")); - } -} - -void menus_set_sensitive(char *path, int sensitive) -{ - GtkMenuPath *menu_path; - - if (initialize) - menus_init(); - - menu_path = gtk_menu_factory_find(factory, path); - if (menu_path) - gtk_widget_set_sensitive(menu_path->widget, sensitive); - else - g_warning("Unable to set sensitivity for menu which doesn't exist: %s", path); -} /* example-end */ @@ -6254,6 +7734,7 @@ void file_quit_cmd_callback(GtkWidget *widget, gpointer data); #endif /* __cplusplus */ #endif /* __MFMAIN_H__ */ + /* example-end */ @@ -6267,15 +7748,12 @@ And mfmain.c #include "mfmain.h" #include "menufactory.h" - int main(int argc, char *argv[]) { GtkWidget *window; GtkWidget *main_vbox; GtkWidget *menubar; - GtkAcceleratorTable *accel; - gtk_init(&argc, &argv); window = gtk_window_new(GTK_WINDOW_TOPLEVEL); @@ -6290,8 +7768,7 @@ int main(int argc, char *argv[]) gtk_container_add(GTK_CONTAINER(window), main_vbox); gtk_widget_show(main_vbox); - get_main_menu(&menubar, &accel); - gtk_window_add_accelerator_table(GTK_WINDOW(window), accel); + get_main_menu(window, &menubar); gtk_box_pack_start(GTK_BOX(main_vbox), menubar, FALSE, TRUE, 0); gtk_widget_show(menubar); @@ -6310,6 +7787,7 @@ void file_quit_cmd_callback (GtkWidget *widget, gpointer data) g_print ("%s\n", (char *) data); gtk_exit(0); } + /* example-end */ @@ -6562,6 +8040,194 @@ Shift whilst using cursor movement will extend the selection. Ctrl-V Paste from clipboard + +A GtkText Example +

+ +/* example-start text text.c */ + +/* text.c */ + +#include +#include + +void text_toggle_editable (GtkWidget *checkbutton, + GtkWidget *text) +{ + gtk_text_set_editable(GTK_TEXT(text), + GTK_TOGGLE_BUTTON(checkbutton)->active); +} + +void text_toggle_word_wrap (GtkWidget *checkbutton, + GtkWidget *text) +{ + gtk_text_set_word_wrap(GTK_TEXT(text), + GTK_TOGGLE_BUTTON(checkbutton)->active); +} + +void close_application( GtkWidget *widget, gpointer data ) +{ + gtk_main_quit(); +} + +int main (int argc, char *argv[]) +{ + GtkWidget *window; + GtkWidget *box1; + GtkWidget *box2; + GtkWidget *hbox; + GtkWidget *button; + GtkWidget *check; + GtkWidget *separator; + GtkWidget *table; + GtkWidget *vscrollbar; + GtkWidget *text; + GdkColormap *cmap; + GdkColor colour; + GdkFont *fixed_font; + + FILE *infile; + + gtk_init (&argc, &argv); + + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_widget_set_usize (window, 600, 500); + gtk_window_set_policy (GTK_WINDOW(window), TRUE, TRUE, FALSE); + gtk_signal_connect (GTK_OBJECT (window), "destroy", + GTK_SIGNAL_FUNC(close_application), + NULL); + gtk_window_set_title (GTK_WINDOW (window), "Text Widget Example"); + gtk_container_border_width (GTK_CONTAINER (window), 0); + + + box1 = gtk_vbox_new (FALSE, 0); + gtk_container_add (GTK_CONTAINER (window), box1); + gtk_widget_show (box1); + + + box2 = gtk_vbox_new (FALSE, 10); + gtk_container_border_width (GTK_CONTAINER (box2), 10); + gtk_box_pack_start (GTK_BOX (box1), box2, TRUE, TRUE, 0); + gtk_widget_show (box2); + + + table = gtk_table_new (2, 2, FALSE); + gtk_table_set_row_spacing (GTK_TABLE (table), 0, 2); + gtk_table_set_col_spacing (GTK_TABLE (table), 0, 2); + gtk_box_pack_start (GTK_BOX (box2), table, TRUE, TRUE, 0); + gtk_widget_show (table); + + /* Create the GtkText widget */ + text = gtk_text_new (NULL, NULL); + gtk_text_set_editable (GTK_TEXT (text), TRUE); + gtk_table_attach (GTK_TABLE (table), text, 0, 1, 0, 1, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0); + gtk_widget_show (text); + + /* Add a vertical scrollbar to the GtkText widget */ + vscrollbar = gtk_vscrollbar_new (GTK_TEXT (text)->vadj); + gtk_table_attach (GTK_TABLE (table), vscrollbar, 1, 2, 0, 1, + GTK_FILL, GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0); + gtk_widget_show (vscrollbar); + + /* Get the system colour map and allocate the colour red */ + cmap = gdk_colormap_get_system(); + colour.red = 0xffff; + colour.green = 0; + colour.blue = 0; + if (!gdk_color_alloc(cmap, &colour)) { + g_error("couldn't allocate colour"); + } + + /* Load a fixed font */ + fixed_font = gdk_font_load ("-misc-fixed-medium-r-*-*-*-140-*-*-*-*-*-*"); + + /* Realizing a widget creates a window for it, ready for us to insert some text */ + gtk_widget_realize (text); + + /* Freeze the text widget, ready for multiple updates */ + gtk_text_freeze (GTK_TEXT (text)); + + /* Insert some coloured text */ + gtk_text_insert (GTK_TEXT (text), NULL, &text->style->black, NULL, + "Supports ", -1); + gtk_text_insert (GTK_TEXT (text), NULL, &colour, NULL, + "colored ", -1); + gtk_text_insert (GTK_TEXT (text), NULL, &text->style->black, NULL, + "text and different ", -1); + gtk_text_insert (GTK_TEXT (text), fixed_font, &text->style->black, NULL, + "fonts\n\n", -1); + + /* Load the file text.c into the text window */ + + infile = fopen("text.c", "r"); + + if (infile) { + char buffer[1024]; + int nchars; + + while (1) + { + nchars = fread(buffer, 1, 1024, infile); + gtk_text_insert (GTK_TEXT (text), fixed_font, NULL, + NULL, buffer, nchars); + + if (nchars < 1024) + break; + } + + fclose (infile); + } + + /* Thaw the text widget, allowing the updates to become visible */ + gtk_text_thaw (GTK_TEXT (text)); + + hbox = gtk_hbutton_box_new (); + gtk_box_pack_start (GTK_BOX (box2), hbox, FALSE, FALSE, 0); + gtk_widget_show (hbox); + + check = gtk_check_button_new_with_label("Editable"); + gtk_box_pack_start (GTK_BOX (hbox), check, FALSE, FALSE, 0); + gtk_signal_connect (GTK_OBJECT(check), "toggled", + GTK_SIGNAL_FUNC(text_toggle_editable), text); + gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(check), TRUE); + gtk_widget_show (check); + check = gtk_check_button_new_with_label("Wrap Words"); + gtk_box_pack_start (GTK_BOX (hbox), check, FALSE, TRUE, 0); + gtk_signal_connect (GTK_OBJECT(check), "toggled", + GTK_SIGNAL_FUNC(text_toggle_word_wrap), text); + gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(check), FALSE); + gtk_widget_show (check); + + separator = gtk_hseparator_new (); + gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 0); + gtk_widget_show (separator); + + box2 = gtk_vbox_new (FALSE, 10); + gtk_container_border_width (GTK_CONTAINER (box2), 10); + gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, TRUE, 0); + gtk_widget_show (box2); + + button = gtk_button_new_with_label ("close"); + gtk_signal_connect (GTK_OBJECT (button), "clicked", + GTK_SIGNAL_FUNC(close_application), + NULL); + gtk_box_pack_start (GTK_BOX (box2), button, TRUE, TRUE, 0); + GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); + gtk_widget_grab_default (button); + gtk_widget_show (button); + + gtk_widget_show (window); + + gtk_main (); + + return 0; +} +/* example-end */ + + + Undocumented Widgets @@ -6579,9 +8245,6 @@ When you do come to understand all the functions of a new undocumented widget, please consider writing a tutorial on it so others may benifit from your time. - - Adjustments -

Toolbar

@@ -6589,9 +8252,6 @@ from your time. Fixed Container

- Range Controls -

- Curves

@@ -7290,6 +8950,169 @@ the ones above. The function pointed to by the first argument to gtk_idle_add will be called whenever the opportunity arises. As with the others, returning FALSE will stop the idle function from being called. + +Advanced Event and Signal Handling

+ + +guint gtk_signal_connect( GtkObject *object, + const gchar *name, + GtkSignalFunc func, + gpointer func_data ); + +guint gtk_signal_connect_after( GtkObject *object, + const gchar *name, + GtkSignalFunc func, + gpointer func_data ); + +guint gtk_signal_connect_object( GtkObject *object, + const gchar *name, + GtkSignalFunc func, + GtkObject *slot_object ); + +guint gtk_signal_connect_object_after( GtkObject *object, + const gchar *name, + GtkSignalFunc func, + GtkObject *slot_object ); + +guint gtk_signal_connect_full( GtkObject *object, + const gchar *name, + GtkSignalFunc func, + GtkCallbackMarshal marshal, + gpointer data, + GtkDestroyNotify destroy_func, + gint object_signal, + gint after ); + +guint gtk_signal_connect_interp( GtkObject *object, + const gchar *name, + GtkCallbackMarshal func, + gpointer data, + GtkDestroyNotify destroy_func, + gint after ); + +void gtk_signal_connect_object_while_alive( GtkObject *object, + const gchar *signal, + GtkSignalFunc func, + GtkObject *alive_object ); + +void gtk_signal_connect_while_alive( GtkObject *object, + const gchar *signal, + GtkSignalFunc func, + gpointer func_data, + GtkObject *alive_object ); + +void gtk_signal_disconnect( GtkObject *object, + guint handler_id ); + +void gtk_signal_disconnect_by_func( GtkObject *object, + GtkSignalFunc func, + gpointer data ); + + + +Blocking and Unblocking Signal Handlers +

+ +void gtk_signal_handler_block( GtkObject *object, + guint handler_id); + +void gtk_signal_handler_block_by_func( GtkObject *object, + GtkSignalFunc func, + gpointer data ); + +void gtk_signal_handler_block_by_data( GtkObject *object, + gpointer data ); + +void gtk_signal_handler_unblock( GtkObject *object, + guint handler_id ); + +void gtk_signal_handler_unblock_by_func( GtkObject *object, + GtkSignalFunc func, + gpointer data ); + +void gtk_signal_handler_unblock_by_data( GtkObject *object, + gpointer data ); + + + +Emitting and Stopping Signals +

+ +void gtk_signal_emit( GtkObject *object, + guint signal_id, + ... ); + +void gtk_signal_emit_by_name( GtkObject *object, + const gchar *name, + ... ); + +void gtk_signal_emitv( GtkObject *object, + guint signal_id, + GtkArg *params ); + +void gtk_signal_emitv_by_name( GtkObject *object, + const gchar *name, + GtkArg *params ); + +guint gtk_signal_n_emissions( GtkObject *object, + guint signal_id ); + +guint gtk_signal_n_emissions_by_name( GtkObject *object, + const gchar *name ); + +void gtk_signal_emit_stop( GtkObject *object, + guint signal_id ); + +void gtk_signal_emit_stop_by_name( GtkObject *object, + const gchar *name ); + + + +Signal Emission and Propagation +

+Signal emission is the process wherby GTK+ runs all handlers for a +specific object and signal. + +First, note that the return value from a signal emission is the +return value of the last handler executed. Since event signals +are all of type GTK_RUN_LAST, this will be the default (GTK+ supplied) +default handler, unless you connect with gtk_signal_connect_after(). + +The way an event (say GTK_BUTTON_PRESS) is handled, is: + +Start with the widget where the event occured. + +Emit the generic "event" signal. If that signal handler returns +a value of TRUE, stop all processing. + +Otherwise, emit a specific, "button_press_event" signal. If that +returns TRUE, stop all processing. + +Otherwise, go to the widget's parent, and repeat the above steps. + +Contimue until some signal handler returns TRUE, or until the +top-level widget is reached. + + +Some consequences of the above are: + +Your handler's return value will have no effect if there is a +default handler, unless you connect with gtk_signal_connect_after(). + +To prevent the default handler from being run, you need to connect +with gtk_signal_connect() and use gtk_signal_emit_stop_by_name() - the +return value only affects whether the signal is propagated, not the +current emission. + + Managing Selections @@ -10027,9 +11850,8 @@ static gint configure_event (GtkWidget *widget, GdkEventConfigure *event) { if (pixmap) - { - gdk_pixmap_destroy(pixmap); - } + gdk_pixmap_unref(pixmap); + pixmap = gdk_pixmap_new(widget->window, widget->allocation.width, widget->allocation.height, @@ -10054,7 +11876,7 @@ of the pixmap onto the screen (we determine the area we need to redraw by using the event->area field of the exposure event): -/* Refill the screen from the backing pixmap */ +/* Redraw the screen from the backing pixmap */ static gint expose_event (GtkWidget *widget, GdkEventExpose *event) { @@ -10645,6 +12467,12 @@ name="rajat@ix.netcom.com" for the excellent job on the Pixmap tutorial. Michael K. Johnson for info and code for popup menus. +David Huggins-Daines for the Range Widgets and Tree Widget +sections. + +Stefan Mars for the GtkCList section

And to all of you who commented and helped refine this document. @@ -10678,4 +12506,1624 @@ to ensure that you have the most up to date information available. purpose. This is simply provided as a free resource. As such, the authors and maintainers of the information provided within can not make any guarentee that the information is even accurate. + + + + + + + GDK Event Types

+The follwing data types are passed into event handlers by GTK+. For +each data type listed, the signals that use this data type are listed. + + + GdkEvent + + drag_end_event + + + GdkEventType + + GdkEventAny + + delete_event + destroy_event + map_event + unmap_event + no_expose_event + + + GdkEventExpose + + expose_event + + + GdkEventNoExpose + + GdkEventVisibility + + GdkEventMotion + + motion_notify_event + + + GdkEventButton + + button_press_event + button_release_event + + + GdkEventKey + + key_press_event + key_release_event + + + GdkEventCrossing + + enter_notify_event + leave_notify_event + + + GdkEventFocus + + focus_in_event + focus_out_event + + + GdkEventConfigure + + configure_event + + + GdkEventProperty + + property_notify_event + + + GdkEventSelection + + selection_clear_event + selection_request_event + selection_notify_event + + + GdkEventProximity + + proximity_in_event + proximity_out_event + + + GdkEventDragBegin + + drag_begin_event + + + GdkEventDragRequest + + drag_request_event + + + GdkEventDropEnter + + drop_enter_event + + + GdkEventDropLeave + + drop_leave_event + + + GdkEventDropDataAvailable + + drop_data_available_event + + + GdkEventClient + + client_event + + + GdkEventOther + + other_event + + + +The data type +typedef enum +{ + GDK_NOTHING = -1, + GDK_DELETE = 0, + GDK_DESTROY = 1, + GDK_EXPOSE = 2, + GDK_MOTION_NOTIFY = 3, + GDK_BUTTON_PRESS = 4, + GDK_2BUTTON_PRESS = 5, + GDK_3BUTTON_PRESS = 6, + GDK_BUTTON_RELEASE = 7, + GDK_KEY_PRESS = 8, + GDK_KEY_RELEASE = 9, + GDK_ENTER_NOTIFY = 10, + GDK_LEAVE_NOTIFY = 11, + GDK_FOCUS_CHANGE = 12, + GDK_CONFIGURE = 13, + GDK_MAP = 14, + GDK_UNMAP = 15, + GDK_PROPERTY_NOTIFY = 16, + GDK_SELECTION_CLEAR = 17, + GDK_SELECTION_REQUEST = 18, + GDK_SELECTION_NOTIFY = 19, + GDK_PROXIMITY_IN = 20, + GDK_PROXIMITY_OUT = 21, + GDK_DRAG_BEGIN = 22, + GDK_DRAG_REQUEST = 23, + GDK_DROP_ENTER = 24, + GDK_DROP_LEAVE = 25, + GDK_DROP_DATA_AVAIL = 26, + GDK_CLIENT_EVENT = 27, + GDK_VISIBILITY_NOTIFY = 28, + GDK_NO_EXPOSE = 29, + GDK_OTHER_EVENT = 9999 /* Deprecated, use filters instead */ +} GdkEventType; + + +The other event type that is different from the others is + +So, the event data types are defined as follows: + + +struct _GdkEventAny +{ + GdkEventType type; + GdkWindow *window; + gint8 send_event; +}; + +struct _GdkEventExpose +{ + GdkEventType type; + GdkWindow *window; + gint8 send_event; + GdkRectangle area; + gint count; /* If non-zero, how many more events follow. */ +}; + +struct _GdkEventNoExpose +{ + GdkEventType type; + GdkWindow *window; + gint8 send_event; + /* XXX: does anyone need the X major_code or minor_code fields? */ +}; + +struct _GdkEventVisibility +{ + GdkEventType type; + GdkWindow *window; + gint8 send_event; + GdkVisibilityState state; +}; + +struct _GdkEventMotion +{ + GdkEventType type; + GdkWindow *window; + gint8 send_event; + guint32 time; + gdouble x; + gdouble y; + gdouble pressure; + gdouble xtilt; + gdouble ytilt; + guint state; + gint16 is_hint; + GdkInputSource source; + guint32 deviceid; + gdouble x_root, y_root; +}; + +struct _GdkEventButton +{ + GdkEventType type; + GdkWindow *window; + gint8 send_event; + guint32 time; + gdouble x; + gdouble y; + gdouble pressure; + gdouble xtilt; + gdouble ytilt; + guint state; + guint button; + GdkInputSource source; + guint32 deviceid; + gdouble x_root, y_root; +}; + +struct _GdkEventKey +{ + GdkEventType type; + GdkWindow *window; + gint8 send_event; + guint32 time; + guint state; + guint keyval; + gint length; + gchar *string; +}; + +struct _GdkEventCrossing +{ + GdkEventType type; + GdkWindow *window; + gint8 send_event; + GdkWindow *subwindow; + GdkNotifyType detail; +}; + +struct _GdkEventFocus +{ + GdkEventType type; + GdkWindow *window; + gint8 send_event; + gint16 in; +}; + +struct _GdkEventConfigure +{ + GdkEventType type; + GdkWindow *window; + gint8 send_event; + gint16 x, y; + gint16 width; + gint16 height; +}; + +struct _GdkEventProperty +{ + GdkEventType type; + GdkWindow *window; + gint8 send_event; + GdkAtom atom; + guint32 time; + guint state; +}; + +struct _GdkEventSelection +{ + GdkEventType type; + GdkWindow *window; + gint8 send_event; + GdkAtom selection; + GdkAtom target; + GdkAtom property; + guint32 requestor; + guint32 time; +}; + +/* This event type will be used pretty rarely. It only is important + for XInput aware programs that are drawing their own cursor */ + +struct _GdkEventProximity +{ + GdkEventType type; + GdkWindow *window; + gint8 send_event; + guint32 time; + GdkInputSource source; + guint32 deviceid; +}; + +struct _GdkEventDragRequest +{ + GdkEventType type; + GdkWindow *window; + gint8 send_event; + guint32 requestor; + union { + struct { + guint protocol_version:4; + guint sendreply:1; + guint willaccept:1; + guint delete_data:1; /* Do *not* delete if link is sent, only + if data is sent */ + guint senddata:1; + guint reserved:22; + } flags; + glong allflags; + } u; + guint8 isdrop; /* This gdk event can be generated by a couple of + X events - this lets the app know whether the + drop really occurred or we just set the data */ + + GdkPoint drop_coords; + gchar *data_type; + guint32 timestamp; +}; + +struct _GdkEventDragBegin +{ + GdkEventType type; + GdkWindow *window; + gint8 send_event; + union { + struct { + guint protocol_version:4; + guint reserved:28; + } flags; + glong allflags; + } u; +}; + +struct _GdkEventDropEnter +{ + GdkEventType type; + GdkWindow *window; + gint8 send_event; + guint32 requestor; + union { + struct { + guint protocol_version:4; + guint sendreply:1; + guint extended_typelist:1; + guint reserved:26; + } flags; + glong allflags; + } u; +}; + +struct _GdkEventDropLeave +{ + GdkEventType type; + GdkWindow *window; + gint8 send_event; + guint32 requestor; + union { + struct { + guint protocol_version:4; + guint reserved:28; + } flags; + glong allflags; + } u; +}; + +struct _GdkEventDropDataAvailable +{ + GdkEventType type; + GdkWindow *window; + gint8 send_event; + guint32 requestor; + union { + struct { + guint protocol_version:4; + guint isdrop:1; + guint reserved:25; + } flags; + glong allflags; + } u; + gchar *data_type; /* MIME type */ + gulong data_numbytes; + gpointer data; + guint32 timestamp; + GdkPoint coords; +}; + +struct _GdkEventClient +{ + GdkEventType type; + GdkWindow *window; + gint8 send_event; + GdkAtom message_type; + gushort data_format; + union { + char b[20]; + short s[10]; + long l[5]; + } data; +}; + +struct _GdkEventOther +{ + GdkEventType type; + GdkWindow *window; + gint8 send_event; + GdkXEvent *xevent; +}; + + + + Code Examples + +

+Below are the code examples that are used in the above text +which are not included in complete form elsewhere. + + +Tictactoe + +tictactoe.h +

+ +/* example-start tictactoe tictactoe.h */ + +/* GTK - The GIMP Toolkit + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#ifndef __TICTACTOE_H__ +#define __TICTACTOE_H__ + + +#include +#include + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define TICTACTOE(obj) GTK_CHECK_CAST (obj, tictactoe_get_type (), Tictactoe) +#define TICTACTOE_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, tictactoe_get_type (), TictactoeClass) +#define IS_TICTACTOE(obj) GTK_CHECK_TYPE (obj, tictactoe_get_type ()) + + +typedef struct _Tictactoe Tictactoe; +typedef struct _TictactoeClass TictactoeClass; + +struct _Tictactoe +{ + GtkVBox vbox; + + GtkWidget *buttons[3][3]; +}; + +struct _TictactoeClass +{ + GtkVBoxClass parent_class; + + void (* tictactoe) (Tictactoe *ttt); +}; + +guint tictactoe_get_type (void); +GtkWidget* tictactoe_new (void); +void tictactoe_clear (Tictactoe *ttt); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __TICTACTOE_H__ */ + +/* example-end */ + + + +tictactoe.c +

+ +/* example-start tictactoe tictactoe.c */ + +/* GTK - The GIMP Toolkit + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#include "gtk/gtksignal.h" +#include "gtk/gtktable.h" +#include "gtk/gtktogglebutton.h" +#include "tictactoe.h" + +enum { + TICTACTOE_SIGNAL, + LAST_SIGNAL +}; + +static void tictactoe_class_init (TictactoeClass *klass); +static void tictactoe_init (Tictactoe *ttt); +static void tictactoe_toggle (GtkWidget *widget, Tictactoe *ttt); + +static gint tictactoe_signals[LAST_SIGNAL] = { 0 }; + +guint +tictactoe_get_type () +{ + static guint ttt_type = 0; + + if (!ttt_type) + { + GtkTypeInfo ttt_info = + { + "Tictactoe", + sizeof (Tictactoe), + sizeof (TictactoeClass), + (GtkClassInitFunc) tictactoe_class_init, + (GtkObjectInitFunc) tictactoe_init, + (GtkArgSetFunc) NULL, + (GtkArgGetFunc) NULL + }; + + ttt_type = gtk_type_unique (gtk_vbox_get_type (), &ttt_info); + } + + return ttt_type; +} + +static void +tictactoe_class_init (TictactoeClass *class) +{ + GtkObjectClass *object_class; + + object_class = (GtkObjectClass*) class; + + tictactoe_signals[TICTACTOE_SIGNAL] = gtk_signal_new ("tictactoe", + GTK_RUN_FIRST, + object_class->type, + GTK_SIGNAL_OFFSET (TictactoeClass, tictactoe), + gtk_signal_default_marshaller, GTK_TYPE_NONE, 0); + + + gtk_object_class_add_signals (object_class, tictactoe_signals, LAST_SIGNAL); + + class->tictactoe = NULL; +} + +static void +tictactoe_init (Tictactoe *ttt) +{ + GtkWidget *table; + gint i,j; + + table = gtk_table_new (3, 3, TRUE); + gtk_container_add (GTK_CONTAINER(ttt), table); + gtk_widget_show (table); + + for (i=0;i<3; i++) + for (j=0;j<3; j++) + { + ttt->buttons[i][j] = gtk_toggle_button_new (); + gtk_table_attach_defaults (GTK_TABLE(table), ttt->buttons[i][j], + i, i+1, j, j+1); + gtk_signal_connect (GTK_OBJECT (ttt->buttons[i][j]), "toggled", + GTK_SIGNAL_FUNC (tictactoe_toggle), ttt); + gtk_widget_set_usize (ttt->buttons[i][j], 20, 20); + gtk_widget_show (ttt->buttons[i][j]); + } +} + +GtkWidget* +tictactoe_new () +{ + return GTK_WIDGET ( gtk_type_new (tictactoe_get_type ())); +} + +void +tictactoe_clear (Tictactoe *ttt) +{ + int i,j; + + for (i=0;i<3;i++) + for (j=0;j<3;j++) + { + gtk_signal_handler_block_by_data (GTK_OBJECT(ttt->buttons[i][j]), ttt); + gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (ttt->buttons[i][j]), + FALSE); + gtk_signal_handler_unblock_by_data (GTK_OBJECT(ttt->buttons[i][j]), ttt); + } +} + +static void +tictactoe_toggle (GtkWidget *widget, Tictactoe *ttt) +{ + int i,k; + + static int rwins[8][3] = { { 0, 0, 0 }, { 1, 1, 1 }, { 2, 2, 2 }, + { 0, 1, 2 }, { 0, 1, 2 }, { 0, 1, 2 }, + { 0, 1, 2 }, { 0, 1, 2 } }; + static int cwins[8][3] = { { 0, 1, 2 }, { 0, 1, 2 }, { 0, 1, 2 }, + { 0, 0, 0 }, { 1, 1, 1 }, { 2, 2, 2 }, + { 0, 1, 2 }, { 2, 1, 0 } }; + + int success, found; + + for (k=0; k<8; k++) + { + success = TRUE; + found = FALSE; + + for (i=0;i<3;i++) + { + success = success && + GTK_TOGGLE_BUTTON(ttt->buttons[rwins[k][i]][cwins[k][i]])->active; + found = found || + ttt->buttons[rwins[k][i]][cwins[k][i]] == widget; + } + + if (success && found) + { + gtk_signal_emit (GTK_OBJECT (ttt), + tictactoe_signals[TICTACTOE_SIGNAL]); + break; + } + } +} + +/* example-end */ + + + +ttt_test.c +

+ +/* example-start tictactoe ttt_test.c */ + +#include +#include "tictactoe.h" + +void +win (GtkWidget *widget, gpointer data) +{ + g_print ("Yay!\n"); + tictactoe_clear (TICTACTOE (widget)); +} + +int +main (int argc, char *argv[]) +{ + GtkWidget *window; + GtkWidget *ttt; + + gtk_init (&argc, &argv); + + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + + gtk_window_set_title (GTK_WINDOW (window), "Aspect Frame"); + + gtk_signal_connect (GTK_OBJECT (window), "destroy", + GTK_SIGNAL_FUNC (gtk_exit), NULL); + + gtk_container_border_width (GTK_CONTAINER (window), 10); + + ttt = tictactoe_new (); + + gtk_container_add (GTK_CONTAINER (window), ttt); + gtk_widget_show (ttt); + + gtk_signal_connect (GTK_OBJECT (ttt), "tictactoe", + GTK_SIGNAL_FUNC (win), NULL); + + gtk_widget_show (window); + + gtk_main (); + + return 0; +} + +/* example-end */ + + + + GtkDial + + + gtkdial.h +

+ +/* example-start gtkdial gtkdial.h */ + +/* GTK - The GIMP Toolkit + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#ifndef __GTK_DIAL_H__ +#define __GTK_DIAL_H__ + + +#include +#include +#include + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +#define GTK_DIAL(obj) GTK_CHECK_CAST (obj, gtk_dial_get_type (), GtkDial) +#define GTK_DIAL_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, gtk_dial_get_type (), GtkDialClass) +#define GTK_IS_DIAL(obj) GTK_CHECK_TYPE (obj, gtk_dial_get_type ()) + + +typedef struct _GtkDial GtkDial; +typedef struct _GtkDialClass GtkDialClass; + +struct _GtkDial +{ + GtkWidget widget; + + /* update policy (GTK_UPDATE_[CONTINUOUS/DELAYED/DISCONTINUOUS]) */ + guint policy : 2; + + /* Button currently pressed or 0 if none */ + guint8 button; + + /* Dimensions of dial components */ + gint radius; + gint pointer_width; + + /* ID of update timer, or 0 if none */ + guint32 timer; + + /* Current angle */ + gfloat angle; + + /* Old values from adjustment stored so we know when something changes */ + gfloat old_value; + gfloat old_lower; + gfloat old_upper; + + /* The adjustment object that stores the data for this dial */ + GtkAdjustment *adjustment; +}; + +struct _GtkDialClass +{ + GtkWidgetClass parent_class; +}; + + +GtkWidget* gtk_dial_new (GtkAdjustment *adjustment); +guint gtk_dial_get_type (void); +GtkAdjustment* gtk_dial_get_adjustment (GtkDial *dial); +void gtk_dial_set_update_policy (GtkDial *dial, + GtkUpdateType policy); + +void gtk_dial_set_adjustment (GtkDial *dial, + GtkAdjustment *adjustment); +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GTK_DIAL_H__ */ +/* example-end */ + + + + gtkdial.c +

+ +/* example-start gtkdial gtkdial.c */ + +/* GTK - The GIMP Toolkit + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#include +#include +#include +#include + +#include "gtkdial.h" + +#define SCROLL_DELAY_LENGTH 300 +#define DIAL_DEFAULT_SIZE 100 + +/* Forward declararations */ + +static void gtk_dial_class_init (GtkDialClass *klass); +static void gtk_dial_init (GtkDial *dial); +static void gtk_dial_destroy (GtkObject *object); +static void gtk_dial_realize (GtkWidget *widget); +static void gtk_dial_size_request (GtkWidget *widget, + GtkRequisition *requisition); +static void gtk_dial_size_allocate (GtkWidget *widget, + GtkAllocation *allocation); +static gint gtk_dial_expose (GtkWidget *widget, + GdkEventExpose *event); +static gint gtk_dial_button_press (GtkWidget *widget, + GdkEventButton *event); +static gint gtk_dial_button_release (GtkWidget *widget, + GdkEventButton *event); +static gint gtk_dial_motion_notify (GtkWidget *widget, + GdkEventMotion *event); +static gint gtk_dial_timer (GtkDial *dial); + +static void gtk_dial_update_mouse (GtkDial *dial, gint x, gint y); +static void gtk_dial_update (GtkDial *dial); +static void gtk_dial_adjustment_changed (GtkAdjustment *adjustment, + gpointer data); +static void gtk_dial_adjustment_value_changed (GtkAdjustment *adjustment, + gpointer data); + +/* Local data */ + +static GtkWidgetClass *parent_class = NULL; + +guint +gtk_dial_get_type () +{ + static guint dial_type = 0; + + if (!dial_type) + { + GtkTypeInfo dial_info = + { + "GtkDial", + sizeof (GtkDial), + sizeof (GtkDialClass), + (GtkClassInitFunc) gtk_dial_class_init, + (GtkObjectInitFunc) gtk_dial_init, + (GtkArgSetFunc) NULL, + (GtkArgGetFunc) NULL, + }; + + dial_type = gtk_type_unique (gtk_widget_get_type (), &dial_info); + } + + return dial_type; +} + +static void +gtk_dial_class_init (GtkDialClass *class) +{ + GtkObjectClass *object_class; + GtkWidgetClass *widget_class; + + object_class = (GtkObjectClass*) class; + widget_class = (GtkWidgetClass*) class; + + parent_class = gtk_type_class (gtk_widget_get_type ()); + + object_class->destroy = gtk_dial_destroy; + + widget_class->realize = gtk_dial_realize; + widget_class->expose_event = gtk_dial_expose; + widget_class->size_request = gtk_dial_size_request; + widget_class->size_allocate = gtk_dial_size_allocate; + widget_class->button_press_event = gtk_dial_button_press; + widget_class->button_release_event = gtk_dial_button_release; + widget_class->motion_notify_event = gtk_dial_motion_notify; +} + +static void +gtk_dial_init (GtkDial *dial) +{ + dial->button = 0; + dial->policy = GTK_UPDATE_CONTINUOUS; + dial->timer = 0; + dial->radius = 0; + dial->pointer_width = 0; + dial->angle = 0.0; + dial->old_value = 0.0; + dial->old_lower = 0.0; + dial->old_upper = 0.0; + dial->adjustment = NULL; +} + +GtkWidget* +gtk_dial_new (GtkAdjustment *adjustment) +{ + GtkDial *dial; + + dial = gtk_type_new (gtk_dial_get_type ()); + + if (!adjustment) + adjustment = (GtkAdjustment*) gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0); + + gtk_dial_set_adjustment (dial, adjustment); + + return GTK_WIDGET (dial); +} + +static void +gtk_dial_destroy (GtkObject *object) +{ + GtkDial *dial; + + g_return_if_fail (object != NULL); + g_return_if_fail (GTK_IS_DIAL (object)); + + dial = GTK_DIAL (object); + + if (dial->adjustment) + gtk_object_unref (GTK_OBJECT (dial->adjustment)); + + if (GTK_OBJECT_CLASS (parent_class)->destroy) + (* GTK_OBJECT_CLASS (parent_class)->destroy) (object); +} + +GtkAdjustment* +gtk_dial_get_adjustment (GtkDial *dial) +{ + g_return_val_if_fail (dial != NULL, NULL); + g_return_val_if_fail (GTK_IS_DIAL (dial), NULL); + + return dial->adjustment; +} + +void +gtk_dial_set_update_policy (GtkDial *dial, + GtkUpdateType policy) +{ + g_return_if_fail (dial != NULL); + g_return_if_fail (GTK_IS_DIAL (dial)); + + dial->policy = policy; +} + +void +gtk_dial_set_adjustment (GtkDial *dial, + GtkAdjustment *adjustment) +{ + g_return_if_fail (dial != NULL); + g_return_if_fail (GTK_IS_DIAL (dial)); + + if (dial->adjustment) + { + gtk_signal_disconnect_by_data (GTK_OBJECT (dial->adjustment), (gpointer) dial); + gtk_object_unref (GTK_OBJECT (dial->adjustment)); + } + + dial->adjustment = adjustment; + gtk_object_ref (GTK_OBJECT (dial->adjustment)); + + gtk_signal_connect (GTK_OBJECT (adjustment), "changed", + (GtkSignalFunc) gtk_dial_adjustment_changed, + (gpointer) dial); + gtk_signal_connect (GTK_OBJECT (adjustment), "value_changed", + (GtkSignalFunc) gtk_dial_adjustment_value_changed, + (gpointer) dial); + + dial->old_value = adjustment->value; + dial->old_lower = adjustment->lower; + dial->old_upper = adjustment->upper; + + gtk_dial_update (dial); +} + +static void +gtk_dial_realize (GtkWidget *widget) +{ + GtkDial *dial; + GdkWindowAttr attributes; + gint attributes_mask; + + g_return_if_fail (widget != NULL); + g_return_if_fail (GTK_IS_DIAL (widget)); + + GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED); + dial = GTK_DIAL (widget); + + attributes.x = widget->allocation.x; + attributes.y = widget->allocation.y; + attributes.width = widget->allocation.width; + attributes.height = widget->allocation.height; + attributes.wclass = GDK_INPUT_OUTPUT; + attributes.window_type = GDK_WINDOW_CHILD; + attributes.event_mask = gtk_widget_get_events (widget) | + GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | + GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK | + GDK_POINTER_MOTION_HINT_MASK; + attributes.visual = gtk_widget_get_visual (widget); + attributes.colormap = gtk_widget_get_colormap (widget); + + attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP; + widget->window = gdk_window_new (widget->parent->window, &attributes, attributes_mask); + + widget->style = gtk_style_attach (widget->style, widget->window); + + gdk_window_set_user_data (widget->window, widget); + + gtk_style_set_background (widget->style, widget->window, GTK_STATE_ACTIVE); +} + +static void +gtk_dial_size_request (GtkWidget *widget, + GtkRequisition *requisition) +{ + requisition->width = DIAL_DEFAULT_SIZE; + requisition->height = DIAL_DEFAULT_SIZE; +} + +static void +gtk_dial_size_allocate (GtkWidget *widget, + GtkAllocation *allocation) +{ + GtkDial *dial; + + g_return_if_fail (widget != NULL); + g_return_if_fail (GTK_IS_DIAL (widget)); + g_return_if_fail (allocation != NULL); + + widget->allocation = *allocation; + dial = GTK_DIAL (widget); + + if (GTK_WIDGET_REALIZED (widget)) + { + + gdk_window_move_resize (widget->window, + allocation->x, allocation->y, + allocation->width, allocation->height); + + } + dial->radius = MIN(allocation->width,allocation->height) * 0.45; + dial->pointer_width = dial->radius / 5; +} + +static gint +gtk_dial_expose (GtkWidget *widget, + GdkEventExpose *event) +{ + GtkDial *dial; + GdkPoint points[3]; + gdouble s,c; + gdouble theta; + gint xc, yc; + gint tick_length; + gint i; + + g_return_val_if_fail (widget != NULL, FALSE); + g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE); + g_return_val_if_fail (event != NULL, FALSE); + + if (event->count > 0) + return FALSE; + + dial = GTK_DIAL (widget); + + gdk_window_clear_area (widget->window, + 0, 0, + widget->allocation.width, + widget->allocation.height); + + xc = widget->allocation.width/2; + yc = widget->allocation.height/2; + + /* Draw ticks */ + + for (i=0; i<25; i++) + { + theta = (i*M_PI/18. - M_PI/6.); + s = sin(theta); + c = cos(theta); + + tick_length = (i%6 == 0) ? dial->pointer_width : dial->pointer_width/2; + + gdk_draw_line (widget->window, + widget->style->fg_gc[widget->state], + xc + c*(dial->radius - tick_length), + yc - s*(dial->radius - tick_length), + xc + c*dial->radius, + yc - s*dial->radius); + } + + /* Draw pointer */ + + s = sin(dial->angle); + c = cos(dial->angle); + + + points[0].x = xc + s*dial->pointer_width/2; + points[0].y = yc + c*dial->pointer_width/2; + points[1].x = xc + c*dial->radius; + points[1].y = yc - s*dial->radius; + points[2].x = xc - s*dial->pointer_width/2; + points[2].y = yc - c*dial->pointer_width/2; + + gtk_draw_polygon (widget->style, + widget->window, + GTK_STATE_NORMAL, + GTK_SHADOW_OUT, + points, 3, + TRUE); + + return FALSE; +} + +static gint +gtk_dial_button_press (GtkWidget *widget, + GdkEventButton *event) +{ + GtkDial *dial; + gint dx, dy; + double s, c; + double d_parallel; + double d_perpendicular; + + g_return_val_if_fail (widget != NULL, FALSE); + g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE); + g_return_val_if_fail (event != NULL, FALSE); + + dial = GTK_DIAL (widget); + + /* Determine if button press was within pointer region - we + do this by computing the parallel and perpendicular distance of + the point where the mouse was pressed from the line passing through + the pointer */ + + dx = event->x - widget->allocation.width / 2; + dy = widget->allocation.height / 2 - event->y; + + s = sin(dial->angle); + c = cos(dial->angle); + + d_parallel = s*dy + c*dx; + d_perpendicular = fabs(s*dx - c*dy); + + if (!dial->button && + (d_perpendicular < dial->pointer_width/2) && + (d_parallel > - dial->pointer_width)) + { + gtk_grab_add (widget); + + dial->button = event->button; + + gtk_dial_update_mouse (dial, event->x, event->y); + } + + return FALSE; +} + +static gint +gtk_dial_button_release (GtkWidget *widget, + GdkEventButton *event) +{ + GtkDial *dial; + + g_return_val_if_fail (widget != NULL, FALSE); + g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE); + g_return_val_if_fail (event != NULL, FALSE); + + dial = GTK_DIAL (widget); + + if (dial->button == event->button) + { + gtk_grab_remove (widget); + + dial->button = 0; + + if (dial->policy == GTK_UPDATE_DELAYED) + gtk_timeout_remove (dial->timer); + + if ((dial->policy != GTK_UPDATE_CONTINUOUS) && + (dial->old_value != dial->adjustment->value)) + gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed"); + } + + return FALSE; +} + +static gint +gtk_dial_motion_notify (GtkWidget *widget, + GdkEventMotion *event) +{ + GtkDial *dial; + GdkModifierType mods; + gint x, y, mask; + + g_return_val_if_fail (widget != NULL, FALSE); + g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE); + g_return_val_if_fail (event != NULL, FALSE); + + dial = GTK_DIAL (widget); + + if (dial->button != 0) + { + x = event->x; + y = event->y; + + if (event->is_hint || (event->window != widget->window)) + gdk_window_get_pointer (widget->window, &x, &y, &mods); + + switch (dial->button) + { + case 1: + mask = GDK_BUTTON1_MASK; + break; + case 2: + mask = GDK_BUTTON2_MASK; + break; + case 3: + mask = GDK_BUTTON3_MASK; + break; + default: + mask = 0; + break; + } + + if (mods & mask) + gtk_dial_update_mouse (dial, x,y); + } + + return FALSE; +} + +static gint +gtk_dial_timer (GtkDial *dial) +{ + g_return_val_if_fail (dial != NULL, FALSE); + g_return_val_if_fail (GTK_IS_DIAL (dial), FALSE); + + if (dial->policy == GTK_UPDATE_DELAYED) + gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed"); + + return FALSE; +} + +static void +gtk_dial_update_mouse (GtkDial *dial, gint x, gint y) +{ + gint xc, yc; + gfloat old_value; + + g_return_if_fail (dial != NULL); + g_return_if_fail (GTK_IS_DIAL (dial)); + + xc = GTK_WIDGET(dial)->allocation.width / 2; + yc = GTK_WIDGET(dial)->allocation.height / 2; + + old_value = dial->adjustment->value; + dial->angle = atan2(yc-y, x-xc); + + if (dial->angle < -M_PI/2.) + dial->angle += 2*M_PI; + + if (dial->angle < -M_PI/6) + dial->angle = -M_PI/6; + + if (dial->angle > 7.*M_PI/6.) + dial->angle = 7.*M_PI/6.; + + dial->adjustment->value = dial->adjustment->lower + (7.*M_PI/6 - dial->angle) * + (dial->adjustment->upper - dial->adjustment->lower) / (4.*M_PI/3.); + + if (dial->adjustment->value != old_value) + { + if (dial->policy == GTK_UPDATE_CONTINUOUS) + { + gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed"); + } + else + { + gtk_widget_draw (GTK_WIDGET(dial), NULL); + + if (dial->policy == GTK_UPDATE_DELAYED) + { + if (dial->timer) + gtk_timeout_remove (dial->timer); + + dial->timer = gtk_timeout_add (SCROLL_DELAY_LENGTH, + (GtkFunction) gtk_dial_timer, + (gpointer) dial); + } + } + } +} + +static void +gtk_dial_update (GtkDial *dial) +{ + gfloat new_value; + + g_return_if_fail (dial != NULL); + g_return_if_fail (GTK_IS_DIAL (dial)); + + new_value = dial->adjustment->value; + + if (new_value < dial->adjustment->lower) + new_value = dial->adjustment->lower; + + if (new_value > dial->adjustment->upper) + new_value = dial->adjustment->upper; + + if (new_value != dial->adjustment->value) + { + dial->adjustment->value = new_value; + gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed"); + } + + dial->angle = 7.*M_PI/6. - (new_value - dial->adjustment->lower) * 4.*M_PI/3. / + (dial->adjustment->upper - dial->adjustment->lower); + + gtk_widget_draw (GTK_WIDGET(dial), NULL); +} + +static void +gtk_dial_adjustment_changed (GtkAdjustment *adjustment, + gpointer data) +{ + GtkDial *dial; + + g_return_if_fail (adjustment != NULL); + g_return_if_fail (data != NULL); + + dial = GTK_DIAL (data); + + if ((dial->old_value != adjustment->value) || + (dial->old_lower != adjustment->lower) || + (dial->old_upper != adjustment->upper)) + { + gtk_dial_update (dial); + + dial->old_value = adjustment->value; + dial->old_lower = adjustment->lower; + dial->old_upper = adjustment->upper; + } +} + +static void +gtk_dial_adjustment_value_changed (GtkAdjustment *adjustment, + gpointer data) +{ + GtkDial *dial; + + g_return_if_fail (adjustment != NULL); + g_return_if_fail (data != NULL); + + dial = GTK_DIAL (data); + + if (dial->old_value != adjustment->value) + { + gtk_dial_update (dial); + + dial->old_value = adjustment->value; + } +} +/* example-end */ + + + + Scribble +

+ +/* example-start scribble-simple scribble-simple.c */ + +/* GTK - The GIMP Toolkit + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include + +/* Backing pixmap for drawing area */ +static GdkPixmap *pixmap = NULL; + +/* Create a new backing pixmap of the appropriate size */ +static gint +configure_event (GtkWidget *widget, GdkEventConfigure *event) +{ + if (pixmap) + gdk_pixmap_unref(pixmap); + + pixmap = gdk_pixmap_new(widget->window, + widget->allocation.width, + widget->allocation.height, + -1); + gdk_draw_rectangle (pixmap, + widget->style->white_gc, + TRUE, + 0, 0, + widget->allocation.width, + widget->allocation.height); + + return TRUE; +} + +/* Redraw the screen from the backing pixmap */ +static gint +expose_event (GtkWidget *widget, GdkEventExpose *event) +{ + gdk_draw_pixmap(widget->window, + widget->style->fg_gc[GTK_WIDGET_STATE (widget)], + pixmap, + event->area.x, event->area.y, + event->area.x, event->area.y, + event->area.width, event->area.height); + + return FALSE; +} + +/* Draw a rectangle on the screen */ +static void +draw_brush (GtkWidget *widget, gdouble x, gdouble y) +{ + GdkRectangle update_rect; + + update_rect.x = x - 5; + update_rect.y = y - 5; + update_rect.width = 10; + update_rect.height = 10; + gdk_draw_rectangle (pixmap, + widget->style->black_gc, + TRUE, + update_rect.x, update_rect.y, + update_rect.width, update_rect.height); + gtk_widget_draw (widget, &update_rect); +} + +static gint +button_press_event (GtkWidget *widget, GdkEventButton *event) +{ + if (event->button == 1 && pixmap != NULL) + draw_brush (widget, event->x, event->y); + + return TRUE; +} + +static gint +motion_notify_event (GtkWidget *widget, GdkEventMotion *event) +{ + int x, y; + GdkModifierType state; + + if (event->is_hint) + gdk_window_get_pointer (event->window, &x, &y, &state); + else + { + x = event->x; + y = event->y; + state = event->state; + } + + if (state & GDK_BUTTON1_MASK && pixmap != NULL) + draw_brush (widget, x, y); + + return TRUE; +} + +void +quit () +{ + gtk_exit (0); +} + +int +main (int argc, char *argv[]) +{ + GtkWidget *window; + GtkWidget *drawing_area; + GtkWidget *vbox; + + GtkWidget *button; + + gtk_init (&argc, &argv); + + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_widget_set_name (window, "Test Input"); + + vbox = gtk_vbox_new (FALSE, 0); + gtk_container_add (GTK_CONTAINER (window), vbox); + gtk_widget_show (vbox); + + gtk_signal_connect (GTK_OBJECT (window), "destroy", + GTK_SIGNAL_FUNC (quit), NULL); + + /* Create the drawing area */ + + drawing_area = gtk_drawing_area_new (); + gtk_drawing_area_size (GTK_DRAWING_AREA (drawing_area), 200, 200); + gtk_box_pack_start (GTK_BOX (vbox), drawing_area, TRUE, TRUE, 0); + + gtk_widget_show (drawing_area); + + /* Signals used to handle backing pixmap */ + + gtk_signal_connect (GTK_OBJECT (drawing_area), "expose_event", + (GtkSignalFunc) expose_event, NULL); + gtk_signal_connect (GTK_OBJECT(drawing_area),"configure_event", + (GtkSignalFunc) configure_event, NULL); + + /* Event signals */ + + gtk_signal_connect (GTK_OBJECT (drawing_area), "motion_notify_event", + (GtkSignalFunc) motion_notify_event, NULL); + gtk_signal_connect (GTK_OBJECT (drawing_area), "button_press_event", + (GtkSignalFunc) button_press_event, NULL); + + gtk_widget_set_events (drawing_area, GDK_EXPOSURE_MASK + | GDK_LEAVE_NOTIFY_MASK + | GDK_BUTTON_PRESS_MASK + | GDK_POINTER_MOTION_MASK + | GDK_POINTER_MOTION_HINT_MASK); + + /* .. And a quit button */ + button = gtk_button_new_with_label ("Quit"); + gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0); + + gtk_signal_connect_object (GTK_OBJECT (button), "clicked", + GTK_SIGNAL_FUNC (gtk_widget_destroy), + GTK_OBJECT (window)); + gtk_widget_show (button); + + gtk_widget_show (window); + + gtk_main (); + + return 0; +} +/* example-end */ + + diff --git a/docs/tutorial/gtk_tut.sgml b/docs/tutorial/gtk_tut.sgml index fe93a7cbac..b17bb492c1 100644 --- a/docs/tutorial/gtk_tut.sgml +++ b/docs/tutorial/gtk_tut.sgml @@ -10,7 +10,7 @@ name="<imain@gtk.org>">, Tony Gale -June 24th, 1998 +August 13th, 1998 Introduction @@ -70,6 +70,9 @@ You can also view other sources of GTK information on http://www.gtk.org/ GTK uses GNU autoconf for configuration. Once untar'd, type ./configure --help to see a list of options. +Th GTK source distribution also contains the complete source to all of the +examples used in this tutorial, along with Makefiles to aid compilation. + To begin our introduction to GTK, we'll start with the simplest program possible. This program will create a 200x200 pixel window and has no way of exiting except to be @@ -272,6 +275,9 @@ directories for the compiler to look in, and gtk-config --libs will output the list of libraries for the compiler to link with and the directories to find them in. +Note that the type of single quote used in the compile command above +is significant. + The libraries that are usually linked in are: The GTK library (-lgtk), the widget library, based on top of GDK. @@ -476,14 +482,17 @@ static gint button_press_event (GtkWidget *widget, Note that we can declare the second argument as type - - +propagate further. Returning FALSE continues the normal event handling. +See the section on + for more details on this +propagation process. + +For details on the GdkEvent data types, see the appendix entitled +. Stepping Through Hello World @@ -985,6 +994,7 @@ yourself and play with it. /* example-start packbox packbox.c */ +#include #include "gtk/gtk.h" void @@ -2078,6 +2088,775 @@ removes the need for a variable to hold the list of buttons: + +Range Widgets + +

+The category of range widgets includes the ubiquitous scrollbar +widget and the less common scale widget. Though these two +types of widgets are typically used for vastly different +purposes, they are quite similar in function and implementation. +Range widgets allow the user to visually manipulate a value +within a specified range (hence the name). + +All range widgets share a set of common graphic elements, each +of which has its own X window and receives events. They all +contain a "trough" and a "slider" (what is sometimes called a +"thumbwheel" in other GUI environments). Dragging the slider +with the pointer moves it back and forth within the trough, +while clicking in the trough advances the slider towards the +location of the click, either completely, or by a designated +amount (called a "page"), depending on which button was used. + + +The Scale Widgets +

+Scale widgets are used to set an explicitly numeric parameter +which has a visual correlate, and which the user might be +expected to adjust primarily by sight. For example, the +GtkColorSelection compound widget contains scale widgets which +control the components of the colour being selected. +Typically, the precise value of the number is less important +here than its side-effects, and thus the user should be spared +the effort of reaching for the keyboard. + + +Creating a Scale Widget +

+There are actually two types of scale widget: GtkHScale +widgets, which are horizontal, and GtkVScale widgets, which + +are vertical. (Most programmers seem to favour horizontal +scale widgets). Since they work essentially the same way, +there's no need to treat them separately here. The +following functions, defined in +<gtk/gtkvscale.h> and +<gtk/gtkhscale.h>, create vertical and +horizontal scale widgets, respectively: + + +GtkWidget* gtk_vscale_new( GtkAdjustment *adjustment ); + +GtkWidget* gtk_hscale_new( GtkAdjustment *adjustment ); + + + +below for an explanation of what exactly the +Functions, Signals, and Macros +

+Scale widgets can display their current value as a number +beside the trough. The default behaviour is to show the +value, but you can change this with this function: + + +void gtk_scale_set_draw_value( GtkScale *scale, + gint draw_value ); + + +As you might have guessed, +void gtk_scale_set_digits( GtkScale *scale, + gint digits); + + +where +void gtk_scale_set_value_pos( GtkScale *scale, + GtkPositionType pos ); + + +If you've read the section on the notebook widget, then you +know what the possible values of GtkPositionType and can take one +of the following values: + + +GTK_POS_LEFT +GTK_POS_RIGHT +GTK_POS_TOP +GTK_POS_BOTTOM + + +If you position the value on the "side" +of the trough (e.g. on the top or bottom of a horizontal +scale widget), then it will follow the slider up and down +the trough. + +All the preceding functions are defined in +<gtk/gtkscale.h>. The other signals and +functions defined in the header files for the scale widgets +are either not useful for anyone other than writers of scale +widgets, or are the standard GTK+ type-casting macros and +functions. + + +The Scrollbar Widgets +

+These are your standard, run-of-the-mill scrollbars. As with +the scale widgets, there are separate types for horizontal and +vertical scrollbars. There really isn't much to say about +these. You create them with the following functions: + + +GtkWidget* gtk_hscrollbar_new( GtkAdjustment *adjustment ); + +GtkWidget* gtk_vscrollbar_new( GtkAdjustment *adjustment ); + + +and that's about it (if you don't believe me, look in the +header files!). Again, +The Adjustment Object

+As you might have noticed, there really isn't much to the +various range widgets themselves from the programmer's point +of view. Most of your program's interaction with these +widgets will take place by way of the heretofore mysterious + +Creating a GtkAdjustment +

+You create an adjustment using: + + +GtkObject* gtk_adjustment_new( gfloat value, + gfloat lower, + gfloat upper, + gfloat step_increment, + gfloat page_increment, + gfloat page_size ); + + +It may or may not be obvious by now that the values given to + below +describes the default key and mouse bindings for range +widgets, and how they relate to these increments. The + +GtkObject *adj; + +adj = gtk_adjustment_new (0, first_line, last_line, 1, + window_height - 2, window_height); + + +where last_line - +window_height (or, in more general terms, upper - +page_height). This is a little confusing at first, but +it makes sense if you think about it in terms of what the +user expects to see in a scrolled window when the +scrollbar's slider is moved all the way to the end of the +trough. + +Since the size of the slider on scale widgets is invariable, +to avoid excessive confusion, it's a good idea to set the + +Inside the GtkAdjustment object +

+OK, you say, that's nice, but how do I get at all these +values, and, more importantly, how do I know when the user +has moved the slider around? To answer these questions and +more, let's start by taking a look at +struct _GtkAdjustment +{ + GtkData data; + + gfloat lower; + gfloat upper; + gfloat value; + gfloat step_increment; + gfloat page_increment; + gfloat page_size; +}; + +struct _GtkAdjustmentClass +{ + GtkDataClass parent_class; + + void (* changed) (GtkAdjustment *adjustment); + void (* value_changed) (GtkAdjustment *adjustment); +}; + + +The first thing you should know is that there aren't any +handy-dandy macros or accessor functions for getting the +GTK_ADJUSTMENT (Object) macro does +run-time type checking (as do all the GTK+ type-casting +macros, actually). On the other hand, unless you're writing +a new type of range widget, you probably don't want to + +void gtk_adjustment_set_value( GtkAdjustment *adjustment, + gfloat value ); + + +If you need to change the other fields, and you don't intend +to do this very frequently, it's best to create a new +GtkAdjustment and set it with + below. + +You might have noticed that, while adjustments are not +widgets, they are still a "subclass" of GtkObject. +Therefore, they can (and do) emit signals of their own. + +The various widgets that use the GtkAdjustment object will +emit the "value_changed" signal on an adjustment whenever +they change its value (see below for more detail). This +happens both when user input causes the slider to move on a +range widget, as well as when the program explicitly changes +the value with +void cb_rotate_picture (GtkAdjustment *adj, GtkWidget *picture) +{ + set_picture_rotation (picture, adj->value); +... + + +and connect it to the scale widget's adjustment like this: + + +gtk_signal_connect (GTK_OBJECT (adj), "value_changed", + GTK_SIGNAL_FUNC (cb_rotate_picture), picture); + + +The "changed" signal is somewhat more elusive. It is never +emitted directly due to the +Common Functions, Signals, and Macros

+The GtkRange widget class is fairly complicated internally, +but, like all the "base class" widgets, most of its complexity +is only interesting if you want to hack on it. Also, almost +all of the functions and signals it defines are only really +used in writing derived widgets. There are, however, a few +useful functions and concepts that are defined in gtkrange.h +and are common to all range widgets. + + +Update Policies

+The "update policy" of a range widget defines at what points +during user interaction it will change the <gtk/gtkenums.h> as the enum +GtkUpdateType, are: + + +GTK_UPDATE_POLICY_CONTINUOUS - This is the default. +The "value_changed" signal is emitted continuously, +i.e. whenever the slider is moved by even the tiniest +amount. + + +GTK_UPDATE_POLICY_DISCONTINUOUS - The +"value_changed" signal is only emitted once the slider +has stopped moving and the user has released the mouse +button. + + +GTK_UPDATE_POLICY_DELAYED - The "value_change" +signal is emitted when the user releases the mouse button, +or if the slider stops moving for a short period of +time. + + + +The update policy of a range widget can be set by casting it +using the GTK_RANGE (Widget) macro and passing it +to this function: + + +void gtk_range_set_update_policy( GtkRange *range, + GtkUpdateType policy ); + + + +Getting and setting adjustments +

+Getting and setting the adjustment for a range widget "on +the fly" is done, predictably, with: + + +GtkAdjustment* gtk_range_get_adjustment( GtkRange *range ); + +void gtk_range_set_adjustment( GtkRange *range, + GtkAdjustment *adjustment ); + + + + +gtk_signal_emit_by_name (GTK_OBJECT (adjustment), "changed"); + + + +Key and Mouse bindings

+All of the GTK+ range widgets react to mouse clicks in more +or less the same way. Clicking button 1 in the trough will +cause its adjustment's +Vertical Range Widgets +

+All vertical range widgets can be operated with the up and +down arrow keys, as well as with the Control-Page Up and +Control-Page Down. + + +Horizontal Range Widgets +

+The left and right arrow keys work as you might expect in +these widgets, moving the slider back and forth by +Control-Left and +Control-Right, while for GtkHScrollbar, it's done +with Control-Home and Control-End. + + +Example

+This example is a somewhat modified version of the "range +widgets" test from +/* example-start rangewidgets rangewidgets.c */ + +#include + +GtkWidget *hscale, *vscale; + +void cb_pos_menu_select (GtkWidget *item, GtkPositionType pos) +{ + /* set the value position on both scale widgets */ + gtk_scale_set_value_pos (GTK_SCALE (hscale), pos); + gtk_scale_set_value_pos (GTK_SCALE (vscale), pos); +} + +void cb_update_menu_select (GtkWidget *item, GtkUpdateType policy) +{ + /* set the update policy for both scale widgets */ + gtk_range_set_update_policy (GTK_RANGE (hscale), policy); + gtk_range_set_update_policy (GTK_RANGE (vscale), policy); +} + +void cb_digits_scale (GtkAdjustment *adj) +{ + /* set the number of decimal places to which adj->vaule is rounded + */ + gtk_scale_set_digits (GTK_SCALE (hscale), (gint) adj->value); + gtk_scale_set_digits (GTK_SCALE (vscale), (gint) adj->value); +} + +void cb_page_size (GtkAdjustment *get, GtkAdjustment *set) +{ + /* set the page size and page increment size of the sample + adjustment to the value specified by the "Page Size" scale */ + set->page_size = get->value; + set->page_increment = get->value; + /* now emit the "changed" signal to reconfigure all the widgets that + are attached to this adjustment */ + gtk_signal_emit_by_name (GTK_OBJECT (set), "changed"); +} + +void cb_draw_value (GtkToggleButton *button) +{ + /* turn the value display on the scale widgets off or on depending + on the state of the checkbutton */ + gtk_scale_set_draw_value (GTK_SCALE (hscale), button->active); + gtk_scale_set_draw_value (GTK_SCALE (vscale), button->active); +} + +/* convenience functions */ + +GtkWidget *make_menu_item (gchar *name, GtkSignalFunc callback, + gpointer data) +{ + GtkWidget *item; + + item = gtk_menu_item_new_with_label (name); + gtk_signal_connect (GTK_OBJECT (item), "activate", + callback, data); + gtk_widget_show (item); + + return item; +} + +void scale_set_default_values (GtkScale *scale) +{ + gtk_range_set_update_policy (GTK_RANGE (scale), + GTK_UPDATE_CONTINUOUS); + gtk_scale_set_digits (scale, 1); + gtk_scale_set_value_pos (scale, GTK_POS_TOP); + gtk_scale_set_draw_value (scale, TRUE); +} + +/* makes the sample window */ + +void create_range_controls (void) +{ + GtkWidget *window; + GtkWidget *box1, *box2, *box3; + GtkWidget *button; + GtkWidget *scrollbar; + GtkWidget *separator; + GtkWidget *opt, *menu, *item; + GtkWidget *label; + GtkWidget *scale; + GtkObject *adj1, *adj2; + + /* standard window-creating stuff */ + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_signal_connect (GTK_OBJECT (window), "destroy", + GTK_SIGNAL_FUNC(gtk_main_quit), + NULL); + gtk_window_set_title (GTK_WINDOW (window), "range controls"); + + box1 = gtk_vbox_new (FALSE, 0); + gtk_container_add (GTK_CONTAINER (window), box1); + gtk_widget_show (box1); + + box2 = gtk_hbox_new (FALSE, 10); + gtk_container_border_width (GTK_CONTAINER (box2), 10); + gtk_box_pack_start (GTK_BOX (box1), box2, TRUE, TRUE, 0); + gtk_widget_show (box2); + + /* value, lower, upper, step_increment, page_increment, page_size */ + /* note that the page_size value only makes a difference for + scrollbar widgets, and the highest value you'll get is actually + (upper - page_size). */ + adj1 = gtk_adjustment_new (0.0, 0.0, 101.0, 0.1, 1.0, 1.0); + + vscale = gtk_vscale_new (GTK_ADJUSTMENT (adj1)); + scale_set_default_values (GTK_SCALE (vscale)); + gtk_box_pack_start (GTK_BOX (box2), vscale, TRUE, TRUE, 0); + gtk_widget_show (vscale); + + box3 = gtk_vbox_new (FALSE, 10); + gtk_box_pack_start (GTK_BOX (box2), box3, TRUE, TRUE, 0); + gtk_widget_show (box3); + + /* reuse the same adjustment */ + hscale = gtk_hscale_new (GTK_ADJUSTMENT (adj1)); + gtk_widget_set_usize (GTK_WIDGET (hscale), 200, 30); + scale_set_default_values (GTK_SCALE (hscale)); + gtk_box_pack_start (GTK_BOX (box3), hscale, TRUE, TRUE, 0); + gtk_widget_show (hscale); + + /* reuse the same adjustment again */ + scrollbar = gtk_hscrollbar_new (GTK_ADJUSTMENT (adj1)); + /* notice how this causes the scales to always be updated + continuously when the scrollbar is moved */ + gtk_range_set_update_policy (GTK_RANGE (scrollbar), + GTK_UPDATE_CONTINUOUS); + gtk_box_pack_start (GTK_BOX (box3), scrollbar, TRUE, TRUE, 0); + gtk_widget_show (scrollbar); + + box2 = gtk_hbox_new (FALSE, 10); + gtk_container_border_width (GTK_CONTAINER (box2), 10); + gtk_box_pack_start (GTK_BOX (box1), box2, TRUE, TRUE, 0); + gtk_widget_show (box2); + + /* a checkbutton to control whether the value is displayed or not */ + button = gtk_check_button_new_with_label + ("Display value on scale widgets"); + gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (button), TRUE); + gtk_signal_connect (GTK_OBJECT (button), "toggled", GTK_SIGNAL_FUNC + (cb_draw_value), NULL); + gtk_box_pack_start (GTK_BOX (box2), button, TRUE, TRUE, 0); + gtk_widget_show (button); + + box2 = gtk_hbox_new (FALSE, 10); + gtk_container_border_width (GTK_CONTAINER (box2), 10); + + /* an option menu to change the position of the value */ + label = gtk_label_new ("Scale Value Position:"); + gtk_box_pack_start (GTK_BOX (box2), label, FALSE, FALSE, 0); + gtk_widget_show (label); + + opt = gtk_option_menu_new(); + menu = gtk_menu_new(); + + item = make_menu_item ("Top", GTK_SIGNAL_FUNC (cb_pos_menu_select), + GINT_TO_POINTER (GTK_POS_TOP)); + gtk_menu_append (GTK_MENU (menu), item); + + item = make_menu_item ("Bottom", GTK_SIGNAL_FUNC (cb_pos_menu_select), + GINT_TO_POINTER (GTK_POS_BOTTOM)); + gtk_menu_append (GTK_MENU (menu), item); + + item = make_menu_item ("Left", GTK_SIGNAL_FUNC (cb_pos_menu_select), + GINT_TO_POINTER (GTK_POS_LEFT)); + gtk_menu_append (GTK_MENU (menu), item); + + item = make_menu_item ("Right", GTK_SIGNAL_FUNC (cb_pos_menu_select), + GINT_TO_POINTER (GTK_POS_RIGHT)); + gtk_menu_append (GTK_MENU (menu), item); + + gtk_option_menu_set_menu (GTK_OPTION_MENU (opt), menu); + gtk_box_pack_start (GTK_BOX (box2), opt, TRUE, TRUE, 0); + gtk_widget_show (opt); + + gtk_box_pack_start (GTK_BOX (box1), box2, TRUE, TRUE, 0); + gtk_widget_show (box2); + + box2 = gtk_hbox_new (FALSE, 10); + gtk_container_border_width (GTK_CONTAINER (box2), 10); + + /* yet another option menu, this time for the update policy of the + scale widgets */ + label = gtk_label_new ("Scale Update Policy:"); + gtk_box_pack_start (GTK_BOX (box2), label, FALSE, FALSE, 0); + gtk_widget_show (label); + + opt = gtk_option_menu_new(); + menu = gtk_menu_new(); + + item = make_menu_item ("Continuous", + GTK_SIGNAL_FUNC (cb_update_menu_select), + GINT_TO_POINTER (GTK_UPDATE_CONTINUOUS)); + gtk_menu_append (GTK_MENU (menu), item); + + item = make_menu_item ("Discontinuous", + GTK_SIGNAL_FUNC (cb_update_menu_select), + GINT_TO_POINTER (GTK_UPDATE_DISCONTINUOUS)); + gtk_menu_append (GTK_MENU (menu), item); + + item = make_menu_item ("Delayed", + GTK_SIGNAL_FUNC (cb_update_menu_select), + GINT_TO_POINTER (GTK_UPDATE_DELAYED)); + gtk_menu_append (GTK_MENU (menu), item); + + gtk_option_menu_set_menu (GTK_OPTION_MENU (opt), menu); + gtk_box_pack_start (GTK_BOX (box2), opt, TRUE, TRUE, 0); + gtk_widget_show (opt); + + gtk_box_pack_start (GTK_BOX (box1), box2, TRUE, TRUE, 0); + gtk_widget_show (box2); + + box2 = gtk_hbox_new (FALSE, 10); + gtk_container_border_width (GTK_CONTAINER (box2), 10); + + /* a GtkHScale widget for adjusting the number of digits on the + sample scales. */ + label = gtk_label_new ("Scale Digits:"); + gtk_box_pack_start (GTK_BOX (box2), label, FALSE, FALSE, 0); + gtk_widget_show (label); + + adj2 = gtk_adjustment_new (1.0, 0.0, 5.0, 1.0, 1.0, 0.0); + gtk_signal_connect (GTK_OBJECT (adj2), "value_changed", + GTK_SIGNAL_FUNC (cb_digits_scale), NULL); + scale = gtk_hscale_new (GTK_ADJUSTMENT (adj2)); + gtk_scale_set_digits (GTK_SCALE (scale), 0); + gtk_box_pack_start (GTK_BOX (box2), scale, TRUE, TRUE, 0); + gtk_widget_show (scale); + + gtk_box_pack_start (GTK_BOX (box1), box2, TRUE, TRUE, 0); + gtk_widget_show (box2); + + box2 = gtk_hbox_new (FALSE, 10); + gtk_container_border_width (GTK_CONTAINER (box2), 10); + + /* And, one last GtkHScale widget for adjusting the page size of the + scrollbar. */ + label = gtk_label_new ("Scrollbar Page Size:"); + gtk_box_pack_start (GTK_BOX (box2), label, FALSE, FALSE, 0); + gtk_widget_show (label); + + adj2 = gtk_adjustment_new (1.0, 1.0, 101.0, 1.0, 1.0, 0.0); + gtk_signal_connect (GTK_OBJECT (adj2), "value_changed", + GTK_SIGNAL_FUNC (cb_page_size), adj1); + scale = gtk_hscale_new (GTK_ADJUSTMENT (adj2)); + gtk_scale_set_digits (GTK_SCALE (scale), 0); + gtk_box_pack_start (GTK_BOX (box2), scale, TRUE, TRUE, 0); + gtk_widget_show (scale); + + gtk_box_pack_start (GTK_BOX (box1), box2, TRUE, TRUE, 0); + gtk_widget_show (box2); + + separator = gtk_hseparator_new (); + gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 0); + gtk_widget_show (separator); + + box2 = gtk_vbox_new (FALSE, 10); + gtk_container_border_width (GTK_CONTAINER (box2), 10); + gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, TRUE, 0); + gtk_widget_show (box2); + + button = gtk_button_new_with_label ("Quit"); + gtk_signal_connect_object (GTK_OBJECT (button), "clicked", + GTK_SIGNAL_FUNC(gtk_main_quit), + NULL); + gtk_box_pack_start (GTK_BOX (box2), button, TRUE, TRUE, 0); + GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); + gtk_widget_grab_default (button); + gtk_widget_show (button); + + gtk_widget_show (window); +} + +int main (int argc, char *argv[]) +{ + gtk_init(&argc, &argv); + + create_range_controls(); + + gtk_main(); + + return 0; +} + +/* example-end */ + + Miscallaneous Widgets @@ -5705,7 +6484,804 @@ Please see the GtkList example on this, which covers the usage of a GtkListItem as well. -Menu Widgets + Tree Widget

+ +The purpose of tree widgets is to display hierarchically-organized +data. The GtkTree widget itself is a vertical container for widgets +of type GtkTreeItem. GtkTree itself is not terribly different from +GtkList - both are derived directly from GtkContainer, and the +GtkContainer methods work in the same way on GtkTree widgets as on +GtkList widgets. The difference is that GtkTree widgets can be nested +within other GtkTree widgets. We'll see how to do this shortly. + +The GtkTree widget has its own window, and defaults to a white +background, as does GtkList. Also, most of the GtkTree methods work +in the same way as the corresponding GtkList ones. However, GtkTree +is not derived from GtkList, so you cannot use them interchangeably. + + Creating a Tree +

+A GtkTree is created in the usual way, using: + + +GtkWidget* gtk_tree_new( void ); + + +Like the GtkList widget, a GtkTree will simply keep growing as more +items are added to it, as well as when subtrees are expanded. +For this reason, they are almost always packed into a +GtkScrolledWindow. You might want to use gtk_widget_set_usize() on +the scrolled window to ensure that it is big enough to see the tree's +items, as the default size for GtkScrolledWindow is quite small. + +Now that you have a tree, you'll probably want to add some items to +it. below +explains the gory details of GtkTreeItem. For now, it'll suffice to +create one, using: + + +GtkWidget* gtk_tree_item_new_with_label( gchar *label ); + + +You can then add it to the tree using one of the following (see + +below for more options): + + +void gtk_tree_append( GtkTree *tree, + GtkWidget *tree_item ); + +void gtk_tree_prepend( GtkTree *tree, + GtkWidget *tree_item ); + + +Note that you must add items to a GtkTree one at a time - there is no +equivalent to gtk_list_*_items(). + + Adding a Subtree +

+A subtree is created like any other GtkTree widget. A subtree is added +to another tree beneath a tree item, using: + + +void gtk_tree_item_set_subtree( GtkTreeItem *tree_item, + GtkWidget *subtree ); + + +You do not need to call gtk_widget_show() on a subtree before or after +adding it to a GtkTreeItem. However, you must have added the +GtkTreeItem in question to a parent tree before calling +gtk_tree_item_set_subtree(). This is because, technically, the parent +of the subtree is not the GtkTreeItem which "owns" it, but +rather the GtkTree which holds that GtkTreeItem. + +When you add a subtree to a GtkTreeItem, a plus or minus sign appears +beside it, which the user can click on to "expand" or "collapse" it, +meaning, to show or hide its subtree. GtkTreeItems are collapsed by +default. Note that when you collapse a GtkTreeItem, any selected +items in its subtree remain selected, which may not be what the user +expects. + + Handling the Selection List +

+As with GtkList, the GtkTree type has a selection field, and +it is possible to control the behaviour of the tree (somewhat) by +setting the selection type using: + + +void gtk_tree_set_selection_mode( GtkTree *tree, + GtkSelectionMode mode ); + + +The semantics associated with the various selection modes are +described in the section on the GtkList widget. As with the GtkList +widget, the "select_child", "unselect_child" (not really - see below for an explanation), +and "selection_changed" signals are emitted when list items are +selected or unselected. However, in order to take advantage of these +signals, you need to know which GtkTree widget they will be +emitted by, and where to find the list of selected items. + +This is a source of potential confusion. The best way to explain this +is that though all GtkTree widgets are created equal, some are more +equal than others. All GtkTree widgets have their own X window, and +can therefore receive events such as mouse clicks (if their +GtkTreeItems or their children don't catch them first!). However, to +make GTK_SELECTION_SINGLE and GTK_SELECTION_BROWSE selection types +behave in a sane manner, the list of selected items is specific to the +topmost GtkTree widget in a hierarchy, known as the "root tree". + +Thus, accessing the selectionfield directly in an arbitrary +GtkTree widget is not a good idea unless you know it's the +root tree. Instead, use the GTK_TREE_SELECTION (Tree) macro, which +gives the root tree's selection list as a GList pointer. Of course, +this list can include items that are not in the subtree in question if +the selection type is GTK_SELECTION_MULTIPLE. + +Finally, the "select_child" (and "unselect_child", in theory) signals +are emitted by all trees, but the "selection_changed" signal is only +emitted by the root tree. Consequently, if you want to handle the +"select_child" signal for a tree and all its subtrees, you will have +to call gtk_signal_connect() for every subtree. + + Tree Widget Internals +

+The GtkTree's struct definition looks like this: + + +struct _GtkTree +{ + GtkContainer container; + + GList *children; + + GtkTree* root_tree; /* owner of selection list */ + GtkWidget* tree_owner; + GList *selection; + guint level; + guint indent_value; + guint current_indent; + guint selection_mode : 2; + guint view_mode : 1; + guint view_line : 1; +}; + + +The perils associated with accessing the selection field +directly have already been mentioned. The other important fields of +the struct can also be accessed with handy macros or class functions. +GTK_TREE_IS_ROOT_TREE (Tree) returns a boolean value which indicates +whether a tree is the root tree in a GtkTree hierarchy, while +GTK_TREE_ROOT_TREE (Tree) returns the root tree, an object of type +GtkTree (so, remember to cast it using GTK_WIDGET (Tree) if you want +to use one of the gtk_widget_*() functions on it). + +Instead of directly accessing the children field of a GtkTree widget, +it's probably best to cast it using GTK_CONTAINER (Tree), and pass it +to the gtk_container_children() function. This creates a duplicate of +the original list, so it's advisable to free it up using g_list_free() +after you're done with it, or to iterate on it destructively, like +this: + + +children = gtk_container_children (GTK_CONTAINER (tree)); +while (children) { + do_something_nice (GTK_TREE_ITEM (children->data)); + children = g_list_remove_link (children, children); +} + + +The tree_owner field is defined only in subtrees, where it +points to the GtkTreeItem widget which holds the tree in question. +The level field indicates how deeply nested a particular tree +is; root trees have level 0, and each successive level of subtrees has +a level one greater than the parent level. This field is set only +after a GtkTree widget is actually mapped (i.e. drawn on the screen). + + Signals

+ +void selection_changed( GtkTree *tree ); + + +This signal will be emitted whenever the selection field of a +GtkTree has changed. This happens when a child of the GtkTree is +selected or deselected. + + +void select_child( GtkTree *tree, + GtkWidget *child ); + + +This signal is emitted when a child of the GtkTree is about to get +selected. This happens on calls to gtk_tree_select_item(), +gtk_tree_select_child(), on all button presses and calls to +gtk_tree_item_toggle() and gtk_item_toggle(). It may sometimes be +indirectly triggered on other occasions where children get added to or +removed from the GtkTree. + + +void unselect_child (GtkTree *tree, + GtkWidget *child); + + +This signal is emitted when a child of the GtkTree is about to get +deselected. As of GTK+ 1.0.4, this seems to only occur on calls to +gtk_tree_unselect_item() or gtk_tree_unselect_child(), and perhaps on +other occasions, but not when a button press deselects a +child, nor on emission of the "toggle" signal by gtk_item_toggle(). + + Functions and Macros

+ +guint gtk_tree_get_type( void ); + + +Returns the `GtkTree' type identifier. + + +GtkWidget* gtk_tree_new( void ); + + +Create a new GtkTree object. The new widget is returned as a pointer to a +GtkWidget object. NULL is returned on failure. + + +void gtk_tree_append( GtkTree *tree, + GtkWidget *tree_item ); + + +Append a tree item to a GtkTree. + + +void gtk_tree_prepend( GtkTree *tree, + GtkWidget *tree_item ); + + +Prepend a tree item to a GtkTree. + + +void gtk_tree_insert( GtkTree *tree, + GtkWidget *tree_item, + gint position ); + + +Insert a tree item into a GtkTree at the position in the list +specified by position. + + +void gtk_tree_remove_items( GtkTree *tree, + GList *items ); + + +Remove a list of items (in the form of a GList *) from a GtkTree. +Note that removing an item from a tree dereferences (and thus usually) +destroys it and its subtree, if it has one, and all +subtrees in that subtree. If you want to remove only one item, you +can use gtk_container_remove(). + + +void gtk_tree_clear_items( GtkTree *tree, + gint start, + gint end ); + + +Remove the items from position start to position end +from a GtkTree. The same warning about dereferencing applies here, as +gtk_tree_clear_items() simply constructs a list and passes it to +gtk_tree_remove_items(). + + +void gtk_tree_select_item( GtkTree *tree, + gint item ); + + +Emits the "select_item" signal for the child at position +item, thus selecting the child (unless you unselect it in a +signal handler...) + + +void gtk_tree_unselect_item( GtkTree *tree, + gint item ); + + +Emits the "unselect_item" signal for the child at position +item, thus unselecting the child. + + +void gtk_tree_select_child( GtkTree *tree, + GtkWidget *tree_item ); + + +Emits the "select_item" signal for the child tree_item, thus +selecting it. + + +void gtk_tree_unselect_child( GtkTree *tree, + GtkWidget *tree_item ); + + +Emits the "unselect_item" signal for the child tree_item, +thus unselecting it. + + +gint gtk_tree_child_position( GtkTree *tree, + GtkWidget *child ); + + +Returns the position in the tree of child, unless +child is not in the tree, in which case it returns -1. + + +void gtk_tree_set_selection_mode( GtkTree *tree, + GtkSelectionMode mode ); + + +Sets the selection mode, which can be one of GTK_SELECTION_SINGLE (the +default), GTK_SELECTION_BROWSE, GTK_SELECTION_MULTIPLE, or +GTK_SELECTION_EXTENDED. This is only defined for root trees, which +makes sense, since the root tree "owns" the selection. Setting it for +subtrees has no effect at all; the value is simply ignored. + + +void gtk_tree_set_view_mode( GtkTree *tree, + GtkTreeViewMode mode ); + + +Sets the "view mode", which can be either GTK_TREE_VIEW_LINE (the +default) or GTK_TREE_VIEW_ITEM. The view mode propagates from a tree +to its subtrees, and can't be set exclusively to a subtree (this is +not exactly true - see the example code comments). + +The term "view mode" is rather ambiguous - basically, it controls the +way the hilight is drawn when one of a tree's children is selected. +If it's GTK_TREE_VIEW_LINE, the entire GtkTreeItem widget is +hilighted, while for GTK_TREE_VIEW_ITEM, only the child widget +(i.e. usually the label) is hilighted. + + +void gtk_tree_set_view_lines( GtkTree *tree, + guint flag ); + + +Controls whether connecting lines between tree items are drawn. +flag is either TRUE, in which case they are, or FALSE, in +which case they aren't. + + +GtkTree *GTK_TREE (gpointer obj); + + +Cast a generic pointer to `GtkTree *'. + + +GtkTreeClass *GTK_TREE_CLASS (gpointer class); + + +Cast a generic pointer to `GtkTreeClass*'. + + +gint GTK_IS_TREE (gpointer obj); + + +Determine if a generic pointer refers to a `GtkTree' object. + + +gint GTK_IS_ROOT_TREE (gpointer obj) + + +Determine if a generic pointer refers to a `GtkTree' object +and is a root tree. Though this will accept any pointer, the +results of passing it a pointer that does not refer to a GtkTree are +undefined and possibly harmful. + + +GtkTree *GTK_TREE_ROOT_TREE (gpointer obj) + + +Return the root tree of a pointer to a `GtkTree' object. The above +warning applies. + + +GList *GTK_TREE_SELECTION( gpointer obj) + + +Return the selection list of the root tree of a `GtkTree' object. The +above warning applies here, too. + + Tree Item Widget

+The GtkTreeItem widget, like GtkListItem, is derived from GtkItem, +which in turn is derived from GtkBin. Therefore, the item itself is a +generic container holding exactly one child widget, which can be of +any type. The GtkTreeItem widget has a number of extra fields, but +the only one we need be concerned with is the subtree field. + +The definition for the GtkTreeItem struct looks like this: + + +struct _GtkTreeItem +{ + GtkItem item; + + GtkWidget *subtree; + GtkWidget *pixmaps_box; + GtkWidget *plus_pix_widget, *minus_pix_widget; + + GList *pixmaps; /* pixmap node for this items color depth */ + + guint expanded : 1; +}; + + +The pixmaps_box field is a GtkEventBox which catches clicks +on the plus/minus symbol which controls expansion and collapsing. The +pixmaps field points to an internal data structure. Since +you can always obtain the subtree of a GtkTreeItem in a (relatively) +type-safe manner with the GTK_TREE_ITEM_SUBTREE (Item) macro, it's +probably advisable never to touch the insides of a GtkTreeItem unless +you really know what you're doing. + +Since it is directly derived from a GtkItem it can be treated as such +by using the GTK_ITEM (TreeItem) macro. A GtkTreeItem usually holds a +label, so the convenience function gtk_list_item_new_with_label() is +provided. The same effect can be achieved using code like the +following, which is actually copied verbatim from +gtk_tree_item_new_with_label(): + + +tree_item = gtk_tree_item_new (); +label_widget = gtk_label_new (label); +gtk_misc_set_alignment (GTK_MISC (label_widget), 0.0, 0.5); + +gtk_container_add (GTK_CONTAINER (tree_item), label_widget); +gtk_widget_show (label_widget); + + +As one is not forced to add a GtkLabel to a GtkTreeItem, you could +also add a GtkHBox or a GtkArrow, or even a GtkNotebook (though your +app will likely be quite unpopular in this case) to the GtkTreeItem. + +If you remove all the items from a subtree, it will be destroyed and +unparented, unless you reference it beforehand, and the GtkTreeItem +which owns it will be collapsed. So, if you want it to stick around, +do something like the following: + + +gtk_widget_ref (tree); +owner = GTK_TREE(tree)->tree_owner; +gtk_container_remove (GTK_CONTAINER(tree), item); +if (tree->parent == NULL){ + gtk_tree_item_expand (GTK_TREE_ITEM(owner)); + gtk_tree_item_set_subtree (GTK_TREE_ITEM(owner), tree); +} +else + gtk_widget_unref (tree); + + +Finally, drag-n-drop does work with GtkTreeItems. You just +have to make sure that the GtkTreeItem you want to make into a drag +item or a drop site has not only been added to a GtkTree, but that +each successive parent widget has a parent itself, all the way back to +a toplevel or dialog window, when you call gtk_widget_dnd_drag_set() +or gtk_widget_dnd_drop_set(). Otherwise, strange things will happen. + + Signals +

+GtkTreeItem inherits the "select", "deselect", and "toggle" signals +from GtkItem. In addition, it adds two signals of its own, "expand" +and "collapse". + + +void select( GtkItem *tree_item ); + + +This signal is emitted when an item is about to be selected, either +after it has been clicked on by the user, or when the program calls +gtk_tree_item_select(), gtk_item_select(), or gtk_tree_select_child(). + + +void deselect( GtkItem *tree_item ); + + +This signal is emitted when an item is about to be unselected, either +after it has been clicked on by the user, or when the program calls +gtk_tree_item_deselect() or gtk_item_deselect(). In the case of +GtkTreeItems, it is also emitted by gtk_tree_unselect_child(), and +sometimes gtk_tree_select_child(). + + +void toggle( GtkItem *tree_item ); + + +This signal is emitted when the program calls gtk_item_toggle(). The +effect it has when emitted on a GtkTreeItem is to call +gtk_tree_select_child() (and never gtk_tree_unselect_child()) on the +item's parent tree, if the item has a parent tree. If it doesn't, +then the highlight is reversed on the item. + + +void expand( GtkTreeItem *tree_item ); + + +This signal is emitted when the tree item's subtree is about to be +expanded, that is, when the user clicks on the plus sign next to the +item, or when the program calls gtk_tree_item_expand(). + + +void collapse( GtkTreeItem *tree_item ); + + +This signal is emitted when the tree item's subtree is about to be +collapsed, that is, when the user clicks on the minus sign next to the +item, or when the program calls gtk_tree_item_collapse(). + + Functions and Macros +

+ +guint gtk_tree_item_get_type( void ); + + +Returns the `GtkTreeItem' type identifier. + + +GtkWidget* gtk_tree_item_new( void ); + + +Create a new GtkTreeItem object. The new widget is returned as a pointer +to a GtkWidget object. NULL is returned on failure. + + +GtkWidget* gtk_tree_item_new_with_label (gchar *label); + + +Create a new GtkTreeItem object, having a single GtkLabel as +the sole child. The new widget is returned as a pointer to a +GtkWidget object. NULL is returned on failure. + + +void gtk_tree_item_select( GtkTreeItem *tree_item ); + + +This function is basicaly a wrapper around a call to +gtk_item_select (GTK_ITEM (tree_item)) which will emit the +select signal. + + +void gtk_tree_item_deselect( GtkTreeItem *tree_item ); + + +This function is basicaly a wrapper around a call to +gtk_item_deselect (GTK_ITEM (tree_item)) which will emit the +deselect signal. + + +void gtk_tree_item_set_subtree( GtkTreeItem *tree_item, + GtkWidget *subtree ); + + +This function adds subtree to tree_item, showing it if tree_item is +expanded, or hiding it if tree_item is collapsed. Again, remember +that the tree_item must have already been added to a tree for this to +work. + + +void gtk_tree_item_remove_subtree( GtkTreeItem *tree_item ); + + +This removes all of tree_item's subtree's children (thus unreferencing +and destroying it, any of its children's subtrees, and so on...), then +removes the subtree itself, and hides the plus/minus sign. + + +void gtk_tree_item_expand( GtkTreeItem *tree_item ); + + +This emits the "expand" signal on tree_item, which expands it. + + +void gtk_tree_item_collapse( GtkTreeItem *tree_item ); + + +This emits the "collapse" signal on tree_item, which collapses it. + + +GtkTreeItem *GTK_TREE_ITEM (gpointer obj) + + +Cast a generic pointer to `GtkTreeItem*'. + + +GtkTreeItemClass *GTK_TREE_ITEM_CLASS (gpointer obj) + + +Cast a generic pointer to `GtkTreeItemClass'. + + +gint GTK_IS_TREE_ITEM (gpointer obj) + + +Determine if a generic pointer refers to a `GtkTreeItem' object. + + +GtkWidget GTK_TREE_ITEM_SUBTREE (gpointer obj) + + +Return's a tree item's subtree (obj should point to a `GtkTreeItem' +object). + + Tree Example +

+This is somewhat like the tree example in testgtk.c, but a lot less +complete (although much better commented). It puts up a window with a +tree, and connects all the signals for the relevant objects, so you +can see when they are emitted. + + +/* example-start tree tree.c */ + +#include + +/* for all the GtkItem:: and GtkTreeItem:: signals */ +static void cb_itemsignal (GtkWidget *item, gchar *signame) +{ + gchar *name; + GtkLabel *label; + + /* It's a GtkBin, so it has one child, which we know to be a + label, so get that */ + label = GTK_LABEL (GTK_BIN (item)->child); + /* Get the text of the label */ + gtk_label_get (label, &name); + /* Get the level of the tree which the item is in */ + g_print ("%s called for item %s->%p, level %d\n", signame, name, + item, GTK_TREE (item->parent)->level); +} + +/* Note that this is never called */ +static void cb_unselect_child (GtkWidget *root_tree, GtkWidget *child, + GtkWidget *subtree) +{ + g_print ("unselect_child called for root tree %p, subtree %p, child %p\n", + root_tree, subtree, child); +} + +/* Note that this is called every time the user clicks on an item, + whether it is already selected or not. */ +static void cb_select_child (GtkWidget *root_tree, GtkWidget *child, + GtkWidget *subtree) +{ + g_print ("select_child called for root tree %p, subtree %p, child %p\n", + root_tree, subtree, child); +} + +static void cb_selection_changed (GtkWidget *tree) +{ + GList *i; + + g_print ("selection_change called for tree %p\n", tree); + g_print ("selected objects are:\n"); + + i = GTK_TREE_SELECTION(tree); + while (i){ + gchar *name; + GtkLabel *label; + GtkWidget *item; + + /* Get a GtkWidget pointer from the list node */ + item = GTK_WIDGET (i->data); + label = GTK_LABEL (GTK_BIN (item)->child); + gtk_label_get (label, &name); + g_print ("\t%s on level %d\n", name, GTK_TREE + (item->parent)->level); + i = i->next; + } +} + +int main (int argc, char *argv[]) +{ + GtkWidget *window, *scrolled_win, *tree; + static gchar *itemnames[] = {"Foo", "Bar", "Baz", "Quux", + "Maurice"}; + gint i; + + gtk_init (&argc, &argv); + + /* a generic toplevel window */ + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_signal_connect (GTK_OBJECT(window), "delete_event", + GTK_SIGNAL_FUNC (gtk_main_quit), NULL); + gtk_container_border_width (GTK_CONTAINER(window), 5); + + /* A generic scrolled window */ + scrolled_win = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + gtk_widget_set_usize (scrolled_win, 150, 200); + gtk_container_add (GTK_CONTAINER(window), scrolled_win); + gtk_widget_show (scrolled_win); + + /* Create the root tree */ + tree = gtk_tree_new(); + g_print ("root tree is %p\n", tree); + /* connect all GtkTree:: signals */ + gtk_signal_connect (GTK_OBJECT(tree), "select_child", + GTK_SIGNAL_FUNC(cb_select_child), tree); + gtk_signal_connect (GTK_OBJECT(tree), "unselect_child", + GTK_SIGNAL_FUNC(cb_unselect_child), tree); + gtk_signal_connect (GTK_OBJECT(tree), "selection_changed", + GTK_SIGNAL_FUNC(cb_selection_changed), tree); + /* Add it to the scrolled window */ + gtk_container_add (GTK_CONTAINER(scrolled_win), tree); + /* Set the selection mode */ + gtk_tree_set_selection_mode (GTK_TREE(tree), + GTK_SELECTION_MULTIPLE); + /* Show it */ + gtk_widget_show (tree); + + for (i = 0; i < 5; i++){ + GtkWidget *subtree, *item; + gint j; + + /* Create a tree item */ + item = gtk_tree_item_new_with_label (itemnames[i]); + /* Connect all GtkItem:: and GtkTreeItem:: signals */ + gtk_signal_connect (GTK_OBJECT(item), "select", + GTK_SIGNAL_FUNC(cb_itemsignal), "select"); + gtk_signal_connect (GTK_OBJECT(item), "deselect", + GTK_SIGNAL_FUNC(cb_itemsignal), "deselect"); + gtk_signal_connect (GTK_OBJECT(item), "toggle", + GTK_SIGNAL_FUNC(cb_itemsignal), "toggle"); + gtk_signal_connect (GTK_OBJECT(item), "expand", + GTK_SIGNAL_FUNC(cb_itemsignal), "expand"); + gtk_signal_connect (GTK_OBJECT(item), "collapse", + GTK_SIGNAL_FUNC(cb_itemsignal), "collapse"); + /* Add it to the parent tree */ + gtk_tree_append (GTK_TREE(tree), item); + /* Show it - this can be done at any time */ + gtk_widget_show (item); + /* Create this item's subtree */ + subtree = gtk_tree_new(); + g_print ("-> item %s->%p, subtree %p\n", itemnames[i], item, + subtree); + + /* This is still necesary if you want these signals to be called + for the subtree's children. Note that selection_change will be + signalled for the root tree regardless. */ + gtk_signal_connect (GTK_OBJECT(subtree), "select_child", + GTK_SIGNAL_FUNC(cb_select_child), subtree); + gtk_signal_connect (GTK_OBJECT(subtree), "unselect_child", + GTK_SIGNAL_FUNC(cb_unselect_child), subtree); + /* This has absolutely no effect, because it is completely ignored + in subtrees */ + gtk_tree_set_selection_mode (GTK_TREE(subtree), + GTK_SELECTION_SINGLE); + /* Neither does this, but for a rather different reason - the + view_mode and view_line values of a tree are propagated to + subtrees when they are mapped. So, setting it later on would + actually have a (somewhat unpredictable) effect */ + gtk_tree_set_view_mode (GTK_TREE(subtree), GTK_TREE_VIEW_ITEM); + /* Set this item's subtree - note that you cannot do this until + AFTER the item has been added to its parent tree! */ + gtk_tree_item_set_subtree (GTK_TREE_ITEM(item), subtree); + + for (j = 0; j < 5; j++){ + GtkWidget *subitem; + + /* Create a subtree item, in much the same way */ + subitem = gtk_tree_item_new_with_label (itemnames[j]); + /* Connect all GtkItem:: and GtkTreeItem:: signals */ + gtk_signal_connect (GTK_OBJECT(subitem), "select", + GTK_SIGNAL_FUNC(cb_itemsignal), "select"); + gtk_signal_connect (GTK_OBJECT(subitem), "deselect", + GTK_SIGNAL_FUNC(cb_itemsignal), "deselect"); + gtk_signal_connect (GTK_OBJECT(subitem), "toggle", + GTK_SIGNAL_FUNC(cb_itemsignal), "toggle"); + gtk_signal_connect (GTK_OBJECT(subitem), "expand", + GTK_SIGNAL_FUNC(cb_itemsignal), "expand"); + gtk_signal_connect (GTK_OBJECT(subitem), "collapse", + GTK_SIGNAL_FUNC(cb_itemsignal), "collapse"); + g_print ("-> -> item %s->%p\n", itemnames[j], subitem); + /* Add it to its parent tree */ + gtk_tree_append (GTK_TREE(subtree), subitem); + /* Show it */ + gtk_widget_show (subitem); + } + } + + /* Show the window and loop endlessly */ + gtk_widget_show (window); + gtk_main(); + return 0; +} +/* example-end */ + + + +Menu Widget

There are two ways to create menus, there's the easy way, and there's the @@ -6068,14 +7644,14 @@ of the global variables used in the menufactory.c file. extern "C" { #endif /* __cplusplus */ -void get_main_menu (GtkWidget **menubar, GtkAcceleratorTable **table); -void menus_create(GtkMenuEntry *entries, int nmenu_entries); +void get_main_menu (GtkWidget *, GtkWidget **menubar); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* __MENUFACTORY_H__ */ + /* example-end */ @@ -6089,11 +7665,7 @@ And here is the menufactory.c file. #include "mfmain.h" - -static void menus_remove_accel(GtkWidget * widget, gchar * signal_name, gchar * path); -static gint menus_install_accel(GtkWidget * widget, gchar * signal_name, gchar key, gchar modifiers, gchar * path); -void menus_init(void); -void menus_create(GtkMenuEntry * entries, int nmenu_entries); +static void print_hello(GtkWidget *widget, gpointer data); /* this is the GtkMenuEntry structure used to create new menus. The @@ -6106,131 +7678,39 @@ void menus_create(GtkMenuEntry * entries, int nmenu_entries); static GtkMenuEntry menu_items[] = { - {"

/File/New", "N", NULL, NULL}, - {"
/File/Open", "O", NULL, NULL}, - {"
/File/Save", "S", NULL, NULL}, - {"
/File/Save as", NULL, NULL, NULL}, - {"
/File/", NULL, NULL, NULL}, - {"
/File/Quit", "Q", file_quit_cmd_callback, "OK, I'll quit"}, - {"
/Options/Test", NULL, NULL, NULL} + {"
/File/New", "N", print_hello, NULL}, + {"
/File/Open", "O", print_hello, NULL}, + {"
/File/Save", "S", print_hello, NULL}, + {"
/File/Save as", NULL, NULL, NULL}, + {"
/File/", NULL, NULL, NULL}, + {"
/File/Quit", "Q", file_quit_cmd_callback, "OK, I'll quit"}, + {"
/Options/Test", NULL, NULL, NULL} }; -/* calculate the number of menu_item's */ -static int nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]); -static int initialize = TRUE; -static GtkMenuFactory *factory = NULL; -static GtkMenuFactory *subfactory[1]; -static GHashTable *entry_ht = NULL; - -void get_main_menu(GtkWidget ** menubar, GtkAcceleratorTable ** table) +static void +print_hello(GtkWidget *widget, gpointer data) { - if (initialize) - menus_init(); + printf("hello!\n"); +} + +void get_main_menu(GtkWidget *window, GtkWidget ** menubar) +{ + int nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]); + GtkMenuFactory *factory; + GtkMenuFactory *subfactory; + + factory = gtk_menu_factory_new(GTK_MENU_FACTORY_MENU_BAR); + subfactory = gtk_menu_factory_new(GTK_MENU_FACTORY_MENU_BAR); + + gtk_menu_factory_add_subfactory(factory, subfactory, "
"); + gtk_menu_factory_add_entries(factory, menu_items, nmenu_items); + gtk_window_add_accelerator_table(GTK_WINDOW(window), subfactory->table); if (menubar) - *menubar = subfactory[0]->widget; - if (table) - *table = subfactory[0]->table; + *menubar = subfactory->widget; } -void menus_init(void) -{ - if (initialize) { - initialize = FALSE; - - factory = gtk_menu_factory_new(GTK_MENU_FACTORY_MENU_BAR); - subfactory[0] = gtk_menu_factory_new(GTK_MENU_FACTORY_MENU_BAR); - - gtk_menu_factory_add_subfactory(factory, subfactory[0], "
"); - menus_create(menu_items, nmenu_items); - } -} - -void menus_create(GtkMenuEntry * entries, int nmenu_entries) -{ - char *accelerator; - int i; - - if (initialize) - menus_init(); - - if (entry_ht) - for (i = 0; i < nmenu_entries; i++) { - accelerator = g_hash_table_lookup(entry_ht, entries[i].path); - if (accelerator) { - if (accelerator[0] == '\0') - entries[i].accelerator = NULL; - else - entries[i].accelerator = accelerator; - } - } - gtk_menu_factory_add_entries(factory, entries, nmenu_entries); - - for (i = 0; i < nmenu_entries; i++) - if (entries[i].widget) { - gtk_signal_connect(GTK_OBJECT(entries[i].widget), "install_accelerator", - (GtkSignalFunc) menus_install_accel, - entries[i].path); - gtk_signal_connect(GTK_OBJECT(entries[i].widget), "remove_accelerator", - (GtkSignalFunc) menus_remove_accel, - entries[i].path); - } -} - -static gint menus_install_accel(GtkWidget * widget, gchar * signal_name, gchar key, gchar modifiers, gchar * path) -{ - char accel[64]; - char *t1, t2[2]; - - accel[0] = '\0'; - if (modifiers & GDK_CONTROL_MASK) - strcat(accel, ""); - if (modifiers & GDK_SHIFT_MASK) - strcat(accel, ""); - if (modifiers & GDK_MOD1_MASK) - strcat(accel, ""); - - t2[0] = key; - t2[1] = '\0'; - strcat(accel, t2); - - if (entry_ht) { - t1 = g_hash_table_lookup(entry_ht, path); - g_free(t1); - } else - entry_ht = g_hash_table_new(g_str_hash, g_str_equal); - - g_hash_table_insert(entry_ht, path, g_strdup(accel)); - - return TRUE; -} - -static void menus_remove_accel(GtkWidget * widget, gchar * signal_name, gchar * path) -{ - char *t; - - if (entry_ht) { - t = g_hash_table_lookup(entry_ht, path); - g_free(t); - - g_hash_table_insert(entry_ht, path, g_strdup("")); - } -} - -void menus_set_sensitive(char *path, int sensitive) -{ - GtkMenuPath *menu_path; - - if (initialize) - menus_init(); - - menu_path = gtk_menu_factory_find(factory, path); - if (menu_path) - gtk_widget_set_sensitive(menu_path->widget, sensitive); - else - g_warning("Unable to set sensitivity for menu which doesn't exist: %s", path); -} /* example-end */ @@ -6254,6 +7734,7 @@ void file_quit_cmd_callback(GtkWidget *widget, gpointer data); #endif /* __cplusplus */ #endif /* __MFMAIN_H__ */ + /* example-end */ @@ -6267,15 +7748,12 @@ And mfmain.c #include "mfmain.h" #include "menufactory.h" - int main(int argc, char *argv[]) { GtkWidget *window; GtkWidget *main_vbox; GtkWidget *menubar; - GtkAcceleratorTable *accel; - gtk_init(&argc, &argv); window = gtk_window_new(GTK_WINDOW_TOPLEVEL); @@ -6290,8 +7768,7 @@ int main(int argc, char *argv[]) gtk_container_add(GTK_CONTAINER(window), main_vbox); gtk_widget_show(main_vbox); - get_main_menu(&menubar, &accel); - gtk_window_add_accelerator_table(GTK_WINDOW(window), accel); + get_main_menu(window, &menubar); gtk_box_pack_start(GTK_BOX(main_vbox), menubar, FALSE, TRUE, 0); gtk_widget_show(menubar); @@ -6310,6 +7787,7 @@ void file_quit_cmd_callback (GtkWidget *widget, gpointer data) g_print ("%s\n", (char *) data); gtk_exit(0); } + /* example-end */ @@ -6562,6 +8040,194 @@ Shift whilst using cursor movement will extend the selection. Ctrl-V Paste from clipboard + +A GtkText Example +

+ +/* example-start text text.c */ + +/* text.c */ + +#include +#include + +void text_toggle_editable (GtkWidget *checkbutton, + GtkWidget *text) +{ + gtk_text_set_editable(GTK_TEXT(text), + GTK_TOGGLE_BUTTON(checkbutton)->active); +} + +void text_toggle_word_wrap (GtkWidget *checkbutton, + GtkWidget *text) +{ + gtk_text_set_word_wrap(GTK_TEXT(text), + GTK_TOGGLE_BUTTON(checkbutton)->active); +} + +void close_application( GtkWidget *widget, gpointer data ) +{ + gtk_main_quit(); +} + +int main (int argc, char *argv[]) +{ + GtkWidget *window; + GtkWidget *box1; + GtkWidget *box2; + GtkWidget *hbox; + GtkWidget *button; + GtkWidget *check; + GtkWidget *separator; + GtkWidget *table; + GtkWidget *vscrollbar; + GtkWidget *text; + GdkColormap *cmap; + GdkColor colour; + GdkFont *fixed_font; + + FILE *infile; + + gtk_init (&argc, &argv); + + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_widget_set_usize (window, 600, 500); + gtk_window_set_policy (GTK_WINDOW(window), TRUE, TRUE, FALSE); + gtk_signal_connect (GTK_OBJECT (window), "destroy", + GTK_SIGNAL_FUNC(close_application), + NULL); + gtk_window_set_title (GTK_WINDOW (window), "Text Widget Example"); + gtk_container_border_width (GTK_CONTAINER (window), 0); + + + box1 = gtk_vbox_new (FALSE, 0); + gtk_container_add (GTK_CONTAINER (window), box1); + gtk_widget_show (box1); + + + box2 = gtk_vbox_new (FALSE, 10); + gtk_container_border_width (GTK_CONTAINER (box2), 10); + gtk_box_pack_start (GTK_BOX (box1), box2, TRUE, TRUE, 0); + gtk_widget_show (box2); + + + table = gtk_table_new (2, 2, FALSE); + gtk_table_set_row_spacing (GTK_TABLE (table), 0, 2); + gtk_table_set_col_spacing (GTK_TABLE (table), 0, 2); + gtk_box_pack_start (GTK_BOX (box2), table, TRUE, TRUE, 0); + gtk_widget_show (table); + + /* Create the GtkText widget */ + text = gtk_text_new (NULL, NULL); + gtk_text_set_editable (GTK_TEXT (text), TRUE); + gtk_table_attach (GTK_TABLE (table), text, 0, 1, 0, 1, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0); + gtk_widget_show (text); + + /* Add a vertical scrollbar to the GtkText widget */ + vscrollbar = gtk_vscrollbar_new (GTK_TEXT (text)->vadj); + gtk_table_attach (GTK_TABLE (table), vscrollbar, 1, 2, 0, 1, + GTK_FILL, GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0); + gtk_widget_show (vscrollbar); + + /* Get the system colour map and allocate the colour red */ + cmap = gdk_colormap_get_system(); + colour.red = 0xffff; + colour.green = 0; + colour.blue = 0; + if (!gdk_color_alloc(cmap, &colour)) { + g_error("couldn't allocate colour"); + } + + /* Load a fixed font */ + fixed_font = gdk_font_load ("-misc-fixed-medium-r-*-*-*-140-*-*-*-*-*-*"); + + /* Realizing a widget creates a window for it, ready for us to insert some text */ + gtk_widget_realize (text); + + /* Freeze the text widget, ready for multiple updates */ + gtk_text_freeze (GTK_TEXT (text)); + + /* Insert some coloured text */ + gtk_text_insert (GTK_TEXT (text), NULL, &text->style->black, NULL, + "Supports ", -1); + gtk_text_insert (GTK_TEXT (text), NULL, &colour, NULL, + "colored ", -1); + gtk_text_insert (GTK_TEXT (text), NULL, &text->style->black, NULL, + "text and different ", -1); + gtk_text_insert (GTK_TEXT (text), fixed_font, &text->style->black, NULL, + "fonts\n\n", -1); + + /* Load the file text.c into the text window */ + + infile = fopen("text.c", "r"); + + if (infile) { + char buffer[1024]; + int nchars; + + while (1) + { + nchars = fread(buffer, 1, 1024, infile); + gtk_text_insert (GTK_TEXT (text), fixed_font, NULL, + NULL, buffer, nchars); + + if (nchars < 1024) + break; + } + + fclose (infile); + } + + /* Thaw the text widget, allowing the updates to become visible */ + gtk_text_thaw (GTK_TEXT (text)); + + hbox = gtk_hbutton_box_new (); + gtk_box_pack_start (GTK_BOX (box2), hbox, FALSE, FALSE, 0); + gtk_widget_show (hbox); + + check = gtk_check_button_new_with_label("Editable"); + gtk_box_pack_start (GTK_BOX (hbox), check, FALSE, FALSE, 0); + gtk_signal_connect (GTK_OBJECT(check), "toggled", + GTK_SIGNAL_FUNC(text_toggle_editable), text); + gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(check), TRUE); + gtk_widget_show (check); + check = gtk_check_button_new_with_label("Wrap Words"); + gtk_box_pack_start (GTK_BOX (hbox), check, FALSE, TRUE, 0); + gtk_signal_connect (GTK_OBJECT(check), "toggled", + GTK_SIGNAL_FUNC(text_toggle_word_wrap), text); + gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(check), FALSE); + gtk_widget_show (check); + + separator = gtk_hseparator_new (); + gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 0); + gtk_widget_show (separator); + + box2 = gtk_vbox_new (FALSE, 10); + gtk_container_border_width (GTK_CONTAINER (box2), 10); + gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, TRUE, 0); + gtk_widget_show (box2); + + button = gtk_button_new_with_label ("close"); + gtk_signal_connect (GTK_OBJECT (button), "clicked", + GTK_SIGNAL_FUNC(close_application), + NULL); + gtk_box_pack_start (GTK_BOX (box2), button, TRUE, TRUE, 0); + GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); + gtk_widget_grab_default (button); + gtk_widget_show (button); + + gtk_widget_show (window); + + gtk_main (); + + return 0; +} +/* example-end */ + + + Undocumented Widgets @@ -6579,9 +8245,6 @@ When you do come to understand all the functions of a new undocumented widget, please consider writing a tutorial on it so others may benifit from your time. - - Adjustments -

Toolbar

@@ -6589,9 +8252,6 @@ from your time. Fixed Container

- Range Controls -

- Curves

@@ -7290,6 +8950,169 @@ the ones above. The function pointed to by the first argument to gtk_idle_add will be called whenever the opportunity arises. As with the others, returning FALSE will stop the idle function from being called. + +Advanced Event and Signal Handling

+ + +guint gtk_signal_connect( GtkObject *object, + const gchar *name, + GtkSignalFunc func, + gpointer func_data ); + +guint gtk_signal_connect_after( GtkObject *object, + const gchar *name, + GtkSignalFunc func, + gpointer func_data ); + +guint gtk_signal_connect_object( GtkObject *object, + const gchar *name, + GtkSignalFunc func, + GtkObject *slot_object ); + +guint gtk_signal_connect_object_after( GtkObject *object, + const gchar *name, + GtkSignalFunc func, + GtkObject *slot_object ); + +guint gtk_signal_connect_full( GtkObject *object, + const gchar *name, + GtkSignalFunc func, + GtkCallbackMarshal marshal, + gpointer data, + GtkDestroyNotify destroy_func, + gint object_signal, + gint after ); + +guint gtk_signal_connect_interp( GtkObject *object, + const gchar *name, + GtkCallbackMarshal func, + gpointer data, + GtkDestroyNotify destroy_func, + gint after ); + +void gtk_signal_connect_object_while_alive( GtkObject *object, + const gchar *signal, + GtkSignalFunc func, + GtkObject *alive_object ); + +void gtk_signal_connect_while_alive( GtkObject *object, + const gchar *signal, + GtkSignalFunc func, + gpointer func_data, + GtkObject *alive_object ); + +void gtk_signal_disconnect( GtkObject *object, + guint handler_id ); + +void gtk_signal_disconnect_by_func( GtkObject *object, + GtkSignalFunc func, + gpointer data ); + + + +Blocking and Unblocking Signal Handlers +

+ +void gtk_signal_handler_block( GtkObject *object, + guint handler_id); + +void gtk_signal_handler_block_by_func( GtkObject *object, + GtkSignalFunc func, + gpointer data ); + +void gtk_signal_handler_block_by_data( GtkObject *object, + gpointer data ); + +void gtk_signal_handler_unblock( GtkObject *object, + guint handler_id ); + +void gtk_signal_handler_unblock_by_func( GtkObject *object, + GtkSignalFunc func, + gpointer data ); + +void gtk_signal_handler_unblock_by_data( GtkObject *object, + gpointer data ); + + + +Emitting and Stopping Signals +

+ +void gtk_signal_emit( GtkObject *object, + guint signal_id, + ... ); + +void gtk_signal_emit_by_name( GtkObject *object, + const gchar *name, + ... ); + +void gtk_signal_emitv( GtkObject *object, + guint signal_id, + GtkArg *params ); + +void gtk_signal_emitv_by_name( GtkObject *object, + const gchar *name, + GtkArg *params ); + +guint gtk_signal_n_emissions( GtkObject *object, + guint signal_id ); + +guint gtk_signal_n_emissions_by_name( GtkObject *object, + const gchar *name ); + +void gtk_signal_emit_stop( GtkObject *object, + guint signal_id ); + +void gtk_signal_emit_stop_by_name( GtkObject *object, + const gchar *name ); + + + +Signal Emission and Propagation +

+Signal emission is the process wherby GTK+ runs all handlers for a +specific object and signal. + +First, note that the return value from a signal emission is the +return value of the last handler executed. Since event signals +are all of type GTK_RUN_LAST, this will be the default (GTK+ supplied) +default handler, unless you connect with gtk_signal_connect_after(). + +The way an event (say GTK_BUTTON_PRESS) is handled, is: + +Start with the widget where the event occured. + +Emit the generic "event" signal. If that signal handler returns +a value of TRUE, stop all processing. + +Otherwise, emit a specific, "button_press_event" signal. If that +returns TRUE, stop all processing. + +Otherwise, go to the widget's parent, and repeat the above steps. + +Contimue until some signal handler returns TRUE, or until the +top-level widget is reached. + + +Some consequences of the above are: + +Your handler's return value will have no effect if there is a +default handler, unless you connect with gtk_signal_connect_after(). + +To prevent the default handler from being run, you need to connect +with gtk_signal_connect() and use gtk_signal_emit_stop_by_name() - the +return value only affects whether the signal is propagated, not the +current emission. + + Managing Selections @@ -10027,9 +11850,8 @@ static gint configure_event (GtkWidget *widget, GdkEventConfigure *event) { if (pixmap) - { - gdk_pixmap_destroy(pixmap); - } + gdk_pixmap_unref(pixmap); + pixmap = gdk_pixmap_new(widget->window, widget->allocation.width, widget->allocation.height, @@ -10054,7 +11876,7 @@ of the pixmap onto the screen (we determine the area we need to redraw by using the event->area field of the exposure event): -/* Refill the screen from the backing pixmap */ +/* Redraw the screen from the backing pixmap */ static gint expose_event (GtkWidget *widget, GdkEventExpose *event) { @@ -10645,6 +12467,12 @@ name="rajat@ix.netcom.com" for the excellent job on the Pixmap tutorial. Michael K. Johnson for info and code for popup menus. +David Huggins-Daines for the Range Widgets and Tree Widget +sections. + +Stefan Mars for the GtkCList section

And to all of you who commented and helped refine this document. @@ -10678,4 +12506,1624 @@ to ensure that you have the most up to date information available. purpose. This is simply provided as a free resource. As such, the authors and maintainers of the information provided within can not make any guarentee that the information is even accurate. + + + + + + + GDK Event Types

+The follwing data types are passed into event handlers by GTK+. For +each data type listed, the signals that use this data type are listed. + + + GdkEvent + + drag_end_event + + + GdkEventType + + GdkEventAny + + delete_event + destroy_event + map_event + unmap_event + no_expose_event + + + GdkEventExpose + + expose_event + + + GdkEventNoExpose + + GdkEventVisibility + + GdkEventMotion + + motion_notify_event + + + GdkEventButton + + button_press_event + button_release_event + + + GdkEventKey + + key_press_event + key_release_event + + + GdkEventCrossing + + enter_notify_event + leave_notify_event + + + GdkEventFocus + + focus_in_event + focus_out_event + + + GdkEventConfigure + + configure_event + + + GdkEventProperty + + property_notify_event + + + GdkEventSelection + + selection_clear_event + selection_request_event + selection_notify_event + + + GdkEventProximity + + proximity_in_event + proximity_out_event + + + GdkEventDragBegin + + drag_begin_event + + + GdkEventDragRequest + + drag_request_event + + + GdkEventDropEnter + + drop_enter_event + + + GdkEventDropLeave + + drop_leave_event + + + GdkEventDropDataAvailable + + drop_data_available_event + + + GdkEventClient + + client_event + + + GdkEventOther + + other_event + + + +The data type +typedef enum +{ + GDK_NOTHING = -1, + GDK_DELETE = 0, + GDK_DESTROY = 1, + GDK_EXPOSE = 2, + GDK_MOTION_NOTIFY = 3, + GDK_BUTTON_PRESS = 4, + GDK_2BUTTON_PRESS = 5, + GDK_3BUTTON_PRESS = 6, + GDK_BUTTON_RELEASE = 7, + GDK_KEY_PRESS = 8, + GDK_KEY_RELEASE = 9, + GDK_ENTER_NOTIFY = 10, + GDK_LEAVE_NOTIFY = 11, + GDK_FOCUS_CHANGE = 12, + GDK_CONFIGURE = 13, + GDK_MAP = 14, + GDK_UNMAP = 15, + GDK_PROPERTY_NOTIFY = 16, + GDK_SELECTION_CLEAR = 17, + GDK_SELECTION_REQUEST = 18, + GDK_SELECTION_NOTIFY = 19, + GDK_PROXIMITY_IN = 20, + GDK_PROXIMITY_OUT = 21, + GDK_DRAG_BEGIN = 22, + GDK_DRAG_REQUEST = 23, + GDK_DROP_ENTER = 24, + GDK_DROP_LEAVE = 25, + GDK_DROP_DATA_AVAIL = 26, + GDK_CLIENT_EVENT = 27, + GDK_VISIBILITY_NOTIFY = 28, + GDK_NO_EXPOSE = 29, + GDK_OTHER_EVENT = 9999 /* Deprecated, use filters instead */ +} GdkEventType; + + +The other event type that is different from the others is + +So, the event data types are defined as follows: + + +struct _GdkEventAny +{ + GdkEventType type; + GdkWindow *window; + gint8 send_event; +}; + +struct _GdkEventExpose +{ + GdkEventType type; + GdkWindow *window; + gint8 send_event; + GdkRectangle area; + gint count; /* If non-zero, how many more events follow. */ +}; + +struct _GdkEventNoExpose +{ + GdkEventType type; + GdkWindow *window; + gint8 send_event; + /* XXX: does anyone need the X major_code or minor_code fields? */ +}; + +struct _GdkEventVisibility +{ + GdkEventType type; + GdkWindow *window; + gint8 send_event; + GdkVisibilityState state; +}; + +struct _GdkEventMotion +{ + GdkEventType type; + GdkWindow *window; + gint8 send_event; + guint32 time; + gdouble x; + gdouble y; + gdouble pressure; + gdouble xtilt; + gdouble ytilt; + guint state; + gint16 is_hint; + GdkInputSource source; + guint32 deviceid; + gdouble x_root, y_root; +}; + +struct _GdkEventButton +{ + GdkEventType type; + GdkWindow *window; + gint8 send_event; + guint32 time; + gdouble x; + gdouble y; + gdouble pressure; + gdouble xtilt; + gdouble ytilt; + guint state; + guint button; + GdkInputSource source; + guint32 deviceid; + gdouble x_root, y_root; +}; + +struct _GdkEventKey +{ + GdkEventType type; + GdkWindow *window; + gint8 send_event; + guint32 time; + guint state; + guint keyval; + gint length; + gchar *string; +}; + +struct _GdkEventCrossing +{ + GdkEventType type; + GdkWindow *window; + gint8 send_event; + GdkWindow *subwindow; + GdkNotifyType detail; +}; + +struct _GdkEventFocus +{ + GdkEventType type; + GdkWindow *window; + gint8 send_event; + gint16 in; +}; + +struct _GdkEventConfigure +{ + GdkEventType type; + GdkWindow *window; + gint8 send_event; + gint16 x, y; + gint16 width; + gint16 height; +}; + +struct _GdkEventProperty +{ + GdkEventType type; + GdkWindow *window; + gint8 send_event; + GdkAtom atom; + guint32 time; + guint state; +}; + +struct _GdkEventSelection +{ + GdkEventType type; + GdkWindow *window; + gint8 send_event; + GdkAtom selection; + GdkAtom target; + GdkAtom property; + guint32 requestor; + guint32 time; +}; + +/* This event type will be used pretty rarely. It only is important + for XInput aware programs that are drawing their own cursor */ + +struct _GdkEventProximity +{ + GdkEventType type; + GdkWindow *window; + gint8 send_event; + guint32 time; + GdkInputSource source; + guint32 deviceid; +}; + +struct _GdkEventDragRequest +{ + GdkEventType type; + GdkWindow *window; + gint8 send_event; + guint32 requestor; + union { + struct { + guint protocol_version:4; + guint sendreply:1; + guint willaccept:1; + guint delete_data:1; /* Do *not* delete if link is sent, only + if data is sent */ + guint senddata:1; + guint reserved:22; + } flags; + glong allflags; + } u; + guint8 isdrop; /* This gdk event can be generated by a couple of + X events - this lets the app know whether the + drop really occurred or we just set the data */ + + GdkPoint drop_coords; + gchar *data_type; + guint32 timestamp; +}; + +struct _GdkEventDragBegin +{ + GdkEventType type; + GdkWindow *window; + gint8 send_event; + union { + struct { + guint protocol_version:4; + guint reserved:28; + } flags; + glong allflags; + } u; +}; + +struct _GdkEventDropEnter +{ + GdkEventType type; + GdkWindow *window; + gint8 send_event; + guint32 requestor; + union { + struct { + guint protocol_version:4; + guint sendreply:1; + guint extended_typelist:1; + guint reserved:26; + } flags; + glong allflags; + } u; +}; + +struct _GdkEventDropLeave +{ + GdkEventType type; + GdkWindow *window; + gint8 send_event; + guint32 requestor; + union { + struct { + guint protocol_version:4; + guint reserved:28; + } flags; + glong allflags; + } u; +}; + +struct _GdkEventDropDataAvailable +{ + GdkEventType type; + GdkWindow *window; + gint8 send_event; + guint32 requestor; + union { + struct { + guint protocol_version:4; + guint isdrop:1; + guint reserved:25; + } flags; + glong allflags; + } u; + gchar *data_type; /* MIME type */ + gulong data_numbytes; + gpointer data; + guint32 timestamp; + GdkPoint coords; +}; + +struct _GdkEventClient +{ + GdkEventType type; + GdkWindow *window; + gint8 send_event; + GdkAtom message_type; + gushort data_format; + union { + char b[20]; + short s[10]; + long l[5]; + } data; +}; + +struct _GdkEventOther +{ + GdkEventType type; + GdkWindow *window; + gint8 send_event; + GdkXEvent *xevent; +}; + + + + Code Examples + +

+Below are the code examples that are used in the above text +which are not included in complete form elsewhere. + + +Tictactoe + +tictactoe.h +

+ +/* example-start tictactoe tictactoe.h */ + +/* GTK - The GIMP Toolkit + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#ifndef __TICTACTOE_H__ +#define __TICTACTOE_H__ + + +#include +#include + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define TICTACTOE(obj) GTK_CHECK_CAST (obj, tictactoe_get_type (), Tictactoe) +#define TICTACTOE_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, tictactoe_get_type (), TictactoeClass) +#define IS_TICTACTOE(obj) GTK_CHECK_TYPE (obj, tictactoe_get_type ()) + + +typedef struct _Tictactoe Tictactoe; +typedef struct _TictactoeClass TictactoeClass; + +struct _Tictactoe +{ + GtkVBox vbox; + + GtkWidget *buttons[3][3]; +}; + +struct _TictactoeClass +{ + GtkVBoxClass parent_class; + + void (* tictactoe) (Tictactoe *ttt); +}; + +guint tictactoe_get_type (void); +GtkWidget* tictactoe_new (void); +void tictactoe_clear (Tictactoe *ttt); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __TICTACTOE_H__ */ + +/* example-end */ + + + +tictactoe.c +

+ +/* example-start tictactoe tictactoe.c */ + +/* GTK - The GIMP Toolkit + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#include "gtk/gtksignal.h" +#include "gtk/gtktable.h" +#include "gtk/gtktogglebutton.h" +#include "tictactoe.h" + +enum { + TICTACTOE_SIGNAL, + LAST_SIGNAL +}; + +static void tictactoe_class_init (TictactoeClass *klass); +static void tictactoe_init (Tictactoe *ttt); +static void tictactoe_toggle (GtkWidget *widget, Tictactoe *ttt); + +static gint tictactoe_signals[LAST_SIGNAL] = { 0 }; + +guint +tictactoe_get_type () +{ + static guint ttt_type = 0; + + if (!ttt_type) + { + GtkTypeInfo ttt_info = + { + "Tictactoe", + sizeof (Tictactoe), + sizeof (TictactoeClass), + (GtkClassInitFunc) tictactoe_class_init, + (GtkObjectInitFunc) tictactoe_init, + (GtkArgSetFunc) NULL, + (GtkArgGetFunc) NULL + }; + + ttt_type = gtk_type_unique (gtk_vbox_get_type (), &ttt_info); + } + + return ttt_type; +} + +static void +tictactoe_class_init (TictactoeClass *class) +{ + GtkObjectClass *object_class; + + object_class = (GtkObjectClass*) class; + + tictactoe_signals[TICTACTOE_SIGNAL] = gtk_signal_new ("tictactoe", + GTK_RUN_FIRST, + object_class->type, + GTK_SIGNAL_OFFSET (TictactoeClass, tictactoe), + gtk_signal_default_marshaller, GTK_TYPE_NONE, 0); + + + gtk_object_class_add_signals (object_class, tictactoe_signals, LAST_SIGNAL); + + class->tictactoe = NULL; +} + +static void +tictactoe_init (Tictactoe *ttt) +{ + GtkWidget *table; + gint i,j; + + table = gtk_table_new (3, 3, TRUE); + gtk_container_add (GTK_CONTAINER(ttt), table); + gtk_widget_show (table); + + for (i=0;i<3; i++) + for (j=0;j<3; j++) + { + ttt->buttons[i][j] = gtk_toggle_button_new (); + gtk_table_attach_defaults (GTK_TABLE(table), ttt->buttons[i][j], + i, i+1, j, j+1); + gtk_signal_connect (GTK_OBJECT (ttt->buttons[i][j]), "toggled", + GTK_SIGNAL_FUNC (tictactoe_toggle), ttt); + gtk_widget_set_usize (ttt->buttons[i][j], 20, 20); + gtk_widget_show (ttt->buttons[i][j]); + } +} + +GtkWidget* +tictactoe_new () +{ + return GTK_WIDGET ( gtk_type_new (tictactoe_get_type ())); +} + +void +tictactoe_clear (Tictactoe *ttt) +{ + int i,j; + + for (i=0;i<3;i++) + for (j=0;j<3;j++) + { + gtk_signal_handler_block_by_data (GTK_OBJECT(ttt->buttons[i][j]), ttt); + gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (ttt->buttons[i][j]), + FALSE); + gtk_signal_handler_unblock_by_data (GTK_OBJECT(ttt->buttons[i][j]), ttt); + } +} + +static void +tictactoe_toggle (GtkWidget *widget, Tictactoe *ttt) +{ + int i,k; + + static int rwins[8][3] = { { 0, 0, 0 }, { 1, 1, 1 }, { 2, 2, 2 }, + { 0, 1, 2 }, { 0, 1, 2 }, { 0, 1, 2 }, + { 0, 1, 2 }, { 0, 1, 2 } }; + static int cwins[8][3] = { { 0, 1, 2 }, { 0, 1, 2 }, { 0, 1, 2 }, + { 0, 0, 0 }, { 1, 1, 1 }, { 2, 2, 2 }, + { 0, 1, 2 }, { 2, 1, 0 } }; + + int success, found; + + for (k=0; k<8; k++) + { + success = TRUE; + found = FALSE; + + for (i=0;i<3;i++) + { + success = success && + GTK_TOGGLE_BUTTON(ttt->buttons[rwins[k][i]][cwins[k][i]])->active; + found = found || + ttt->buttons[rwins[k][i]][cwins[k][i]] == widget; + } + + if (success && found) + { + gtk_signal_emit (GTK_OBJECT (ttt), + tictactoe_signals[TICTACTOE_SIGNAL]); + break; + } + } +} + +/* example-end */ + + + +ttt_test.c +

+ +/* example-start tictactoe ttt_test.c */ + +#include +#include "tictactoe.h" + +void +win (GtkWidget *widget, gpointer data) +{ + g_print ("Yay!\n"); + tictactoe_clear (TICTACTOE (widget)); +} + +int +main (int argc, char *argv[]) +{ + GtkWidget *window; + GtkWidget *ttt; + + gtk_init (&argc, &argv); + + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + + gtk_window_set_title (GTK_WINDOW (window), "Aspect Frame"); + + gtk_signal_connect (GTK_OBJECT (window), "destroy", + GTK_SIGNAL_FUNC (gtk_exit), NULL); + + gtk_container_border_width (GTK_CONTAINER (window), 10); + + ttt = tictactoe_new (); + + gtk_container_add (GTK_CONTAINER (window), ttt); + gtk_widget_show (ttt); + + gtk_signal_connect (GTK_OBJECT (ttt), "tictactoe", + GTK_SIGNAL_FUNC (win), NULL); + + gtk_widget_show (window); + + gtk_main (); + + return 0; +} + +/* example-end */ + + + + GtkDial + + + gtkdial.h +

+ +/* example-start gtkdial gtkdial.h */ + +/* GTK - The GIMP Toolkit + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#ifndef __GTK_DIAL_H__ +#define __GTK_DIAL_H__ + + +#include +#include +#include + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +#define GTK_DIAL(obj) GTK_CHECK_CAST (obj, gtk_dial_get_type (), GtkDial) +#define GTK_DIAL_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, gtk_dial_get_type (), GtkDialClass) +#define GTK_IS_DIAL(obj) GTK_CHECK_TYPE (obj, gtk_dial_get_type ()) + + +typedef struct _GtkDial GtkDial; +typedef struct _GtkDialClass GtkDialClass; + +struct _GtkDial +{ + GtkWidget widget; + + /* update policy (GTK_UPDATE_[CONTINUOUS/DELAYED/DISCONTINUOUS]) */ + guint policy : 2; + + /* Button currently pressed or 0 if none */ + guint8 button; + + /* Dimensions of dial components */ + gint radius; + gint pointer_width; + + /* ID of update timer, or 0 if none */ + guint32 timer; + + /* Current angle */ + gfloat angle; + + /* Old values from adjustment stored so we know when something changes */ + gfloat old_value; + gfloat old_lower; + gfloat old_upper; + + /* The adjustment object that stores the data for this dial */ + GtkAdjustment *adjustment; +}; + +struct _GtkDialClass +{ + GtkWidgetClass parent_class; +}; + + +GtkWidget* gtk_dial_new (GtkAdjustment *adjustment); +guint gtk_dial_get_type (void); +GtkAdjustment* gtk_dial_get_adjustment (GtkDial *dial); +void gtk_dial_set_update_policy (GtkDial *dial, + GtkUpdateType policy); + +void gtk_dial_set_adjustment (GtkDial *dial, + GtkAdjustment *adjustment); +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GTK_DIAL_H__ */ +/* example-end */ + + + + gtkdial.c +

+ +/* example-start gtkdial gtkdial.c */ + +/* GTK - The GIMP Toolkit + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#include +#include +#include +#include + +#include "gtkdial.h" + +#define SCROLL_DELAY_LENGTH 300 +#define DIAL_DEFAULT_SIZE 100 + +/* Forward declararations */ + +static void gtk_dial_class_init (GtkDialClass *klass); +static void gtk_dial_init (GtkDial *dial); +static void gtk_dial_destroy (GtkObject *object); +static void gtk_dial_realize (GtkWidget *widget); +static void gtk_dial_size_request (GtkWidget *widget, + GtkRequisition *requisition); +static void gtk_dial_size_allocate (GtkWidget *widget, + GtkAllocation *allocation); +static gint gtk_dial_expose (GtkWidget *widget, + GdkEventExpose *event); +static gint gtk_dial_button_press (GtkWidget *widget, + GdkEventButton *event); +static gint gtk_dial_button_release (GtkWidget *widget, + GdkEventButton *event); +static gint gtk_dial_motion_notify (GtkWidget *widget, + GdkEventMotion *event); +static gint gtk_dial_timer (GtkDial *dial); + +static void gtk_dial_update_mouse (GtkDial *dial, gint x, gint y); +static void gtk_dial_update (GtkDial *dial); +static void gtk_dial_adjustment_changed (GtkAdjustment *adjustment, + gpointer data); +static void gtk_dial_adjustment_value_changed (GtkAdjustment *adjustment, + gpointer data); + +/* Local data */ + +static GtkWidgetClass *parent_class = NULL; + +guint +gtk_dial_get_type () +{ + static guint dial_type = 0; + + if (!dial_type) + { + GtkTypeInfo dial_info = + { + "GtkDial", + sizeof (GtkDial), + sizeof (GtkDialClass), + (GtkClassInitFunc) gtk_dial_class_init, + (GtkObjectInitFunc) gtk_dial_init, + (GtkArgSetFunc) NULL, + (GtkArgGetFunc) NULL, + }; + + dial_type = gtk_type_unique (gtk_widget_get_type (), &dial_info); + } + + return dial_type; +} + +static void +gtk_dial_class_init (GtkDialClass *class) +{ + GtkObjectClass *object_class; + GtkWidgetClass *widget_class; + + object_class = (GtkObjectClass*) class; + widget_class = (GtkWidgetClass*) class; + + parent_class = gtk_type_class (gtk_widget_get_type ()); + + object_class->destroy = gtk_dial_destroy; + + widget_class->realize = gtk_dial_realize; + widget_class->expose_event = gtk_dial_expose; + widget_class->size_request = gtk_dial_size_request; + widget_class->size_allocate = gtk_dial_size_allocate; + widget_class->button_press_event = gtk_dial_button_press; + widget_class->button_release_event = gtk_dial_button_release; + widget_class->motion_notify_event = gtk_dial_motion_notify; +} + +static void +gtk_dial_init (GtkDial *dial) +{ + dial->button = 0; + dial->policy = GTK_UPDATE_CONTINUOUS; + dial->timer = 0; + dial->radius = 0; + dial->pointer_width = 0; + dial->angle = 0.0; + dial->old_value = 0.0; + dial->old_lower = 0.0; + dial->old_upper = 0.0; + dial->adjustment = NULL; +} + +GtkWidget* +gtk_dial_new (GtkAdjustment *adjustment) +{ + GtkDial *dial; + + dial = gtk_type_new (gtk_dial_get_type ()); + + if (!adjustment) + adjustment = (GtkAdjustment*) gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0); + + gtk_dial_set_adjustment (dial, adjustment); + + return GTK_WIDGET (dial); +} + +static void +gtk_dial_destroy (GtkObject *object) +{ + GtkDial *dial; + + g_return_if_fail (object != NULL); + g_return_if_fail (GTK_IS_DIAL (object)); + + dial = GTK_DIAL (object); + + if (dial->adjustment) + gtk_object_unref (GTK_OBJECT (dial->adjustment)); + + if (GTK_OBJECT_CLASS (parent_class)->destroy) + (* GTK_OBJECT_CLASS (parent_class)->destroy) (object); +} + +GtkAdjustment* +gtk_dial_get_adjustment (GtkDial *dial) +{ + g_return_val_if_fail (dial != NULL, NULL); + g_return_val_if_fail (GTK_IS_DIAL (dial), NULL); + + return dial->adjustment; +} + +void +gtk_dial_set_update_policy (GtkDial *dial, + GtkUpdateType policy) +{ + g_return_if_fail (dial != NULL); + g_return_if_fail (GTK_IS_DIAL (dial)); + + dial->policy = policy; +} + +void +gtk_dial_set_adjustment (GtkDial *dial, + GtkAdjustment *adjustment) +{ + g_return_if_fail (dial != NULL); + g_return_if_fail (GTK_IS_DIAL (dial)); + + if (dial->adjustment) + { + gtk_signal_disconnect_by_data (GTK_OBJECT (dial->adjustment), (gpointer) dial); + gtk_object_unref (GTK_OBJECT (dial->adjustment)); + } + + dial->adjustment = adjustment; + gtk_object_ref (GTK_OBJECT (dial->adjustment)); + + gtk_signal_connect (GTK_OBJECT (adjustment), "changed", + (GtkSignalFunc) gtk_dial_adjustment_changed, + (gpointer) dial); + gtk_signal_connect (GTK_OBJECT (adjustment), "value_changed", + (GtkSignalFunc) gtk_dial_adjustment_value_changed, + (gpointer) dial); + + dial->old_value = adjustment->value; + dial->old_lower = adjustment->lower; + dial->old_upper = adjustment->upper; + + gtk_dial_update (dial); +} + +static void +gtk_dial_realize (GtkWidget *widget) +{ + GtkDial *dial; + GdkWindowAttr attributes; + gint attributes_mask; + + g_return_if_fail (widget != NULL); + g_return_if_fail (GTK_IS_DIAL (widget)); + + GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED); + dial = GTK_DIAL (widget); + + attributes.x = widget->allocation.x; + attributes.y = widget->allocation.y; + attributes.width = widget->allocation.width; + attributes.height = widget->allocation.height; + attributes.wclass = GDK_INPUT_OUTPUT; + attributes.window_type = GDK_WINDOW_CHILD; + attributes.event_mask = gtk_widget_get_events (widget) | + GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | + GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK | + GDK_POINTER_MOTION_HINT_MASK; + attributes.visual = gtk_widget_get_visual (widget); + attributes.colormap = gtk_widget_get_colormap (widget); + + attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP; + widget->window = gdk_window_new (widget->parent->window, &attributes, attributes_mask); + + widget->style = gtk_style_attach (widget->style, widget->window); + + gdk_window_set_user_data (widget->window, widget); + + gtk_style_set_background (widget->style, widget->window, GTK_STATE_ACTIVE); +} + +static void +gtk_dial_size_request (GtkWidget *widget, + GtkRequisition *requisition) +{ + requisition->width = DIAL_DEFAULT_SIZE; + requisition->height = DIAL_DEFAULT_SIZE; +} + +static void +gtk_dial_size_allocate (GtkWidget *widget, + GtkAllocation *allocation) +{ + GtkDial *dial; + + g_return_if_fail (widget != NULL); + g_return_if_fail (GTK_IS_DIAL (widget)); + g_return_if_fail (allocation != NULL); + + widget->allocation = *allocation; + dial = GTK_DIAL (widget); + + if (GTK_WIDGET_REALIZED (widget)) + { + + gdk_window_move_resize (widget->window, + allocation->x, allocation->y, + allocation->width, allocation->height); + + } + dial->radius = MIN(allocation->width,allocation->height) * 0.45; + dial->pointer_width = dial->radius / 5; +} + +static gint +gtk_dial_expose (GtkWidget *widget, + GdkEventExpose *event) +{ + GtkDial *dial; + GdkPoint points[3]; + gdouble s,c; + gdouble theta; + gint xc, yc; + gint tick_length; + gint i; + + g_return_val_if_fail (widget != NULL, FALSE); + g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE); + g_return_val_if_fail (event != NULL, FALSE); + + if (event->count > 0) + return FALSE; + + dial = GTK_DIAL (widget); + + gdk_window_clear_area (widget->window, + 0, 0, + widget->allocation.width, + widget->allocation.height); + + xc = widget->allocation.width/2; + yc = widget->allocation.height/2; + + /* Draw ticks */ + + for (i=0; i<25; i++) + { + theta = (i*M_PI/18. - M_PI/6.); + s = sin(theta); + c = cos(theta); + + tick_length = (i%6 == 0) ? dial->pointer_width : dial->pointer_width/2; + + gdk_draw_line (widget->window, + widget->style->fg_gc[widget->state], + xc + c*(dial->radius - tick_length), + yc - s*(dial->radius - tick_length), + xc + c*dial->radius, + yc - s*dial->radius); + } + + /* Draw pointer */ + + s = sin(dial->angle); + c = cos(dial->angle); + + + points[0].x = xc + s*dial->pointer_width/2; + points[0].y = yc + c*dial->pointer_width/2; + points[1].x = xc + c*dial->radius; + points[1].y = yc - s*dial->radius; + points[2].x = xc - s*dial->pointer_width/2; + points[2].y = yc - c*dial->pointer_width/2; + + gtk_draw_polygon (widget->style, + widget->window, + GTK_STATE_NORMAL, + GTK_SHADOW_OUT, + points, 3, + TRUE); + + return FALSE; +} + +static gint +gtk_dial_button_press (GtkWidget *widget, + GdkEventButton *event) +{ + GtkDial *dial; + gint dx, dy; + double s, c; + double d_parallel; + double d_perpendicular; + + g_return_val_if_fail (widget != NULL, FALSE); + g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE); + g_return_val_if_fail (event != NULL, FALSE); + + dial = GTK_DIAL (widget); + + /* Determine if button press was within pointer region - we + do this by computing the parallel and perpendicular distance of + the point where the mouse was pressed from the line passing through + the pointer */ + + dx = event->x - widget->allocation.width / 2; + dy = widget->allocation.height / 2 - event->y; + + s = sin(dial->angle); + c = cos(dial->angle); + + d_parallel = s*dy + c*dx; + d_perpendicular = fabs(s*dx - c*dy); + + if (!dial->button && + (d_perpendicular < dial->pointer_width/2) && + (d_parallel > - dial->pointer_width)) + { + gtk_grab_add (widget); + + dial->button = event->button; + + gtk_dial_update_mouse (dial, event->x, event->y); + } + + return FALSE; +} + +static gint +gtk_dial_button_release (GtkWidget *widget, + GdkEventButton *event) +{ + GtkDial *dial; + + g_return_val_if_fail (widget != NULL, FALSE); + g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE); + g_return_val_if_fail (event != NULL, FALSE); + + dial = GTK_DIAL (widget); + + if (dial->button == event->button) + { + gtk_grab_remove (widget); + + dial->button = 0; + + if (dial->policy == GTK_UPDATE_DELAYED) + gtk_timeout_remove (dial->timer); + + if ((dial->policy != GTK_UPDATE_CONTINUOUS) && + (dial->old_value != dial->adjustment->value)) + gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed"); + } + + return FALSE; +} + +static gint +gtk_dial_motion_notify (GtkWidget *widget, + GdkEventMotion *event) +{ + GtkDial *dial; + GdkModifierType mods; + gint x, y, mask; + + g_return_val_if_fail (widget != NULL, FALSE); + g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE); + g_return_val_if_fail (event != NULL, FALSE); + + dial = GTK_DIAL (widget); + + if (dial->button != 0) + { + x = event->x; + y = event->y; + + if (event->is_hint || (event->window != widget->window)) + gdk_window_get_pointer (widget->window, &x, &y, &mods); + + switch (dial->button) + { + case 1: + mask = GDK_BUTTON1_MASK; + break; + case 2: + mask = GDK_BUTTON2_MASK; + break; + case 3: + mask = GDK_BUTTON3_MASK; + break; + default: + mask = 0; + break; + } + + if (mods & mask) + gtk_dial_update_mouse (dial, x,y); + } + + return FALSE; +} + +static gint +gtk_dial_timer (GtkDial *dial) +{ + g_return_val_if_fail (dial != NULL, FALSE); + g_return_val_if_fail (GTK_IS_DIAL (dial), FALSE); + + if (dial->policy == GTK_UPDATE_DELAYED) + gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed"); + + return FALSE; +} + +static void +gtk_dial_update_mouse (GtkDial *dial, gint x, gint y) +{ + gint xc, yc; + gfloat old_value; + + g_return_if_fail (dial != NULL); + g_return_if_fail (GTK_IS_DIAL (dial)); + + xc = GTK_WIDGET(dial)->allocation.width / 2; + yc = GTK_WIDGET(dial)->allocation.height / 2; + + old_value = dial->adjustment->value; + dial->angle = atan2(yc-y, x-xc); + + if (dial->angle < -M_PI/2.) + dial->angle += 2*M_PI; + + if (dial->angle < -M_PI/6) + dial->angle = -M_PI/6; + + if (dial->angle > 7.*M_PI/6.) + dial->angle = 7.*M_PI/6.; + + dial->adjustment->value = dial->adjustment->lower + (7.*M_PI/6 - dial->angle) * + (dial->adjustment->upper - dial->adjustment->lower) / (4.*M_PI/3.); + + if (dial->adjustment->value != old_value) + { + if (dial->policy == GTK_UPDATE_CONTINUOUS) + { + gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed"); + } + else + { + gtk_widget_draw (GTK_WIDGET(dial), NULL); + + if (dial->policy == GTK_UPDATE_DELAYED) + { + if (dial->timer) + gtk_timeout_remove (dial->timer); + + dial->timer = gtk_timeout_add (SCROLL_DELAY_LENGTH, + (GtkFunction) gtk_dial_timer, + (gpointer) dial); + } + } + } +} + +static void +gtk_dial_update (GtkDial *dial) +{ + gfloat new_value; + + g_return_if_fail (dial != NULL); + g_return_if_fail (GTK_IS_DIAL (dial)); + + new_value = dial->adjustment->value; + + if (new_value < dial->adjustment->lower) + new_value = dial->adjustment->lower; + + if (new_value > dial->adjustment->upper) + new_value = dial->adjustment->upper; + + if (new_value != dial->adjustment->value) + { + dial->adjustment->value = new_value; + gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed"); + } + + dial->angle = 7.*M_PI/6. - (new_value - dial->adjustment->lower) * 4.*M_PI/3. / + (dial->adjustment->upper - dial->adjustment->lower); + + gtk_widget_draw (GTK_WIDGET(dial), NULL); +} + +static void +gtk_dial_adjustment_changed (GtkAdjustment *adjustment, + gpointer data) +{ + GtkDial *dial; + + g_return_if_fail (adjustment != NULL); + g_return_if_fail (data != NULL); + + dial = GTK_DIAL (data); + + if ((dial->old_value != adjustment->value) || + (dial->old_lower != adjustment->lower) || + (dial->old_upper != adjustment->upper)) + { + gtk_dial_update (dial); + + dial->old_value = adjustment->value; + dial->old_lower = adjustment->lower; + dial->old_upper = adjustment->upper; + } +} + +static void +gtk_dial_adjustment_value_changed (GtkAdjustment *adjustment, + gpointer data) +{ + GtkDial *dial; + + g_return_if_fail (adjustment != NULL); + g_return_if_fail (data != NULL); + + dial = GTK_DIAL (data); + + if (dial->old_value != adjustment->value) + { + gtk_dial_update (dial); + + dial->old_value = adjustment->value; + } +} +/* example-end */ + + + + Scribble +

+ +/* example-start scribble-simple scribble-simple.c */ + +/* GTK - The GIMP Toolkit + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include + +/* Backing pixmap for drawing area */ +static GdkPixmap *pixmap = NULL; + +/* Create a new backing pixmap of the appropriate size */ +static gint +configure_event (GtkWidget *widget, GdkEventConfigure *event) +{ + if (pixmap) + gdk_pixmap_unref(pixmap); + + pixmap = gdk_pixmap_new(widget->window, + widget->allocation.width, + widget->allocation.height, + -1); + gdk_draw_rectangle (pixmap, + widget->style->white_gc, + TRUE, + 0, 0, + widget->allocation.width, + widget->allocation.height); + + return TRUE; +} + +/* Redraw the screen from the backing pixmap */ +static gint +expose_event (GtkWidget *widget, GdkEventExpose *event) +{ + gdk_draw_pixmap(widget->window, + widget->style->fg_gc[GTK_WIDGET_STATE (widget)], + pixmap, + event->area.x, event->area.y, + event->area.x, event->area.y, + event->area.width, event->area.height); + + return FALSE; +} + +/* Draw a rectangle on the screen */ +static void +draw_brush (GtkWidget *widget, gdouble x, gdouble y) +{ + GdkRectangle update_rect; + + update_rect.x = x - 5; + update_rect.y = y - 5; + update_rect.width = 10; + update_rect.height = 10; + gdk_draw_rectangle (pixmap, + widget->style->black_gc, + TRUE, + update_rect.x, update_rect.y, + update_rect.width, update_rect.height); + gtk_widget_draw (widget, &update_rect); +} + +static gint +button_press_event (GtkWidget *widget, GdkEventButton *event) +{ + if (event->button == 1 && pixmap != NULL) + draw_brush (widget, event->x, event->y); + + return TRUE; +} + +static gint +motion_notify_event (GtkWidget *widget, GdkEventMotion *event) +{ + int x, y; + GdkModifierType state; + + if (event->is_hint) + gdk_window_get_pointer (event->window, &x, &y, &state); + else + { + x = event->x; + y = event->y; + state = event->state; + } + + if (state & GDK_BUTTON1_MASK && pixmap != NULL) + draw_brush (widget, x, y); + + return TRUE; +} + +void +quit () +{ + gtk_exit (0); +} + +int +main (int argc, char *argv[]) +{ + GtkWidget *window; + GtkWidget *drawing_area; + GtkWidget *vbox; + + GtkWidget *button; + + gtk_init (&argc, &argv); + + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_widget_set_name (window, "Test Input"); + + vbox = gtk_vbox_new (FALSE, 0); + gtk_container_add (GTK_CONTAINER (window), vbox); + gtk_widget_show (vbox); + + gtk_signal_connect (GTK_OBJECT (window), "destroy", + GTK_SIGNAL_FUNC (quit), NULL); + + /* Create the drawing area */ + + drawing_area = gtk_drawing_area_new (); + gtk_drawing_area_size (GTK_DRAWING_AREA (drawing_area), 200, 200); + gtk_box_pack_start (GTK_BOX (vbox), drawing_area, TRUE, TRUE, 0); + + gtk_widget_show (drawing_area); + + /* Signals used to handle backing pixmap */ + + gtk_signal_connect (GTK_OBJECT (drawing_area), "expose_event", + (GtkSignalFunc) expose_event, NULL); + gtk_signal_connect (GTK_OBJECT(drawing_area),"configure_event", + (GtkSignalFunc) configure_event, NULL); + + /* Event signals */ + + gtk_signal_connect (GTK_OBJECT (drawing_area), "motion_notify_event", + (GtkSignalFunc) motion_notify_event, NULL); + gtk_signal_connect (GTK_OBJECT (drawing_area), "button_press_event", + (GtkSignalFunc) button_press_event, NULL); + + gtk_widget_set_events (drawing_area, GDK_EXPOSURE_MASK + | GDK_LEAVE_NOTIFY_MASK + | GDK_BUTTON_PRESS_MASK + | GDK_POINTER_MOTION_MASK + | GDK_POINTER_MOTION_HINT_MASK); + + /* .. And a quit button */ + button = gtk_button_new_with_label ("Quit"); + gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0); + + gtk_signal_connect_object (GTK_OBJECT (button), "clicked", + GTK_SIGNAL_FUNC (gtk_widget_destroy), + GTK_OBJECT (window)); + gtk_widget_show (button); + + gtk_widget_show (window); + + gtk_main (); + + return 0; +} +/* example-end */ + + diff --git a/examples/rangewidgets/Makefile b/examples/rangewidgets/Makefile new file mode 100644 index 0000000000..1259faef45 --- /dev/null +++ b/examples/rangewidgets/Makefile @@ -0,0 +1,8 @@ + +CC = gcc + +rangewidgets: rangewidgets.c + $(CC) `gtk-config --cflags` `gtk-config --libs` rangewidgets.c -o rangewidgets + +clean: + rm -f *.o rangewidgets diff --git a/examples/rangewidgets/rangewidgets.c b/examples/rangewidgets/rangewidgets.c new file mode 100644 index 0000000000..d59c8b2737 --- /dev/null +++ b/examples/rangewidgets/rangewidgets.c @@ -0,0 +1,287 @@ +/* example-start rangewidgets rangewidgets.c */ + +#include + +GtkWidget *hscale, *vscale; + +void cb_pos_menu_select (GtkWidget *item, GtkPositionType pos) +{ + /* set the value position on both scale widgets */ + gtk_scale_set_value_pos (GTK_SCALE (hscale), pos); + gtk_scale_set_value_pos (GTK_SCALE (vscale), pos); +} + +void cb_update_menu_select (GtkWidget *item, GtkUpdateType policy) +{ + /* set the update policy for both scale widgets */ + gtk_range_set_update_policy (GTK_RANGE (hscale), policy); + gtk_range_set_update_policy (GTK_RANGE (vscale), policy); +} + +void cb_digits_scale (GtkAdjustment *adj) +{ + /* set the number of decimal places to which adj->vaule is rounded + */ + gtk_scale_set_digits (GTK_SCALE (hscale), (gint) adj->value); + gtk_scale_set_digits (GTK_SCALE (vscale), (gint) adj->value); +} + +void cb_page_size (GtkAdjustment *get, GtkAdjustment *set) +{ + /* set the page size and page increment size of the sample + adjustment to the value specified by the "Page Size" scale */ + set->page_size = get->value; + set->page_increment = get->value; + /* now emit the "changed" signal to reconfigure all the widgets that + are attached to this adjustment */ + gtk_signal_emit_by_name (GTK_OBJECT (set), "changed"); +} + +void cb_draw_value (GtkToggleButton *button) +{ + /* turn the value display on the scale widgets off or on depending + on the state of the checkbutton */ + gtk_scale_set_draw_value (GTK_SCALE (hscale), button->active); + gtk_scale_set_draw_value (GTK_SCALE (vscale), button->active); +} + +/* convenience functions */ + +GtkWidget *make_menu_item (gchar *name, GtkSignalFunc callback, + gpointer data) +{ + GtkWidget *item; + + item = gtk_menu_item_new_with_label (name); + gtk_signal_connect (GTK_OBJECT (item), "activate", + callback, data); + gtk_widget_show (item); + + return item; +} + +void scale_set_default_values (GtkScale *scale) +{ + gtk_range_set_update_policy (GTK_RANGE (scale), + GTK_UPDATE_CONTINUOUS); + gtk_scale_set_digits (scale, 1); + gtk_scale_set_value_pos (scale, GTK_POS_TOP); + gtk_scale_set_draw_value (scale, TRUE); +} + +/* makes the sample window */ + +void create_range_controls (void) +{ + GtkWidget *window; + GtkWidget *box1, *box2, *box3; + GtkWidget *button; + GtkWidget *scrollbar; + GtkWidget *separator; + GtkWidget *opt, *menu, *item; + GtkWidget *label; + GtkWidget *scale; + GtkObject *adj1, *adj2; + + /* standard window-creating stuff */ + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_signal_connect (GTK_OBJECT (window), "destroy", + GTK_SIGNAL_FUNC(gtk_main_quit), + NULL); + gtk_window_set_title (GTK_WINDOW (window), "range controls"); + + box1 = gtk_vbox_new (FALSE, 0); + gtk_container_add (GTK_CONTAINER (window), box1); + gtk_widget_show (box1); + + box2 = gtk_hbox_new (FALSE, 10); + gtk_container_border_width (GTK_CONTAINER (box2), 10); + gtk_box_pack_start (GTK_BOX (box1), box2, TRUE, TRUE, 0); + gtk_widget_show (box2); + + /* value, lower, upper, step_increment, page_increment, page_size */ + /* note that the page_size value only makes a difference for + scrollbar widgets, and the highest value you'll get is actually + (upper - page_size). */ + adj1 = gtk_adjustment_new (0.0, 0.0, 101.0, 0.1, 1.0, 1.0); + + vscale = gtk_vscale_new (GTK_ADJUSTMENT (adj1)); + scale_set_default_values (GTK_SCALE (vscale)); + gtk_box_pack_start (GTK_BOX (box2), vscale, TRUE, TRUE, 0); + gtk_widget_show (vscale); + + box3 = gtk_vbox_new (FALSE, 10); + gtk_box_pack_start (GTK_BOX (box2), box3, TRUE, TRUE, 0); + gtk_widget_show (box3); + + /* reuse the same adjustment */ + hscale = gtk_hscale_new (GTK_ADJUSTMENT (adj1)); + gtk_widget_set_usize (GTK_WIDGET (hscale), 200, 30); + scale_set_default_values (GTK_SCALE (hscale)); + gtk_box_pack_start (GTK_BOX (box3), hscale, TRUE, TRUE, 0); + gtk_widget_show (hscale); + + /* reuse the same adjustment again */ + scrollbar = gtk_hscrollbar_new (GTK_ADJUSTMENT (adj1)); + /* notice how this causes the scales to always be updated + continuously when the scrollbar is moved */ + gtk_range_set_update_policy (GTK_RANGE (scrollbar), + GTK_UPDATE_CONTINUOUS); + gtk_box_pack_start (GTK_BOX (box3), scrollbar, TRUE, TRUE, 0); + gtk_widget_show (scrollbar); + + box2 = gtk_hbox_new (FALSE, 10); + gtk_container_border_width (GTK_CONTAINER (box2), 10); + gtk_box_pack_start (GTK_BOX (box1), box2, TRUE, TRUE, 0); + gtk_widget_show (box2); + + /* a checkbutton to control whether the value is displayed or not */ + button = gtk_check_button_new_with_label + ("Display value on scale widgets"); + gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (button), TRUE); + gtk_signal_connect (GTK_OBJECT (button), "toggled", GTK_SIGNAL_FUNC + (cb_draw_value), NULL); + gtk_box_pack_start (GTK_BOX (box2), button, TRUE, TRUE, 0); + gtk_widget_show (button); + + box2 = gtk_hbox_new (FALSE, 10); + gtk_container_border_width (GTK_CONTAINER (box2), 10); + + /* an option menu to change the position of the value */ + label = gtk_label_new ("Scale Value Position:"); + gtk_box_pack_start (GTK_BOX (box2), label, FALSE, FALSE, 0); + gtk_widget_show (label); + + opt = gtk_option_menu_new(); + menu = gtk_menu_new(); + + item = make_menu_item ("Top", GTK_SIGNAL_FUNC (cb_pos_menu_select), + GINT_TO_POINTER (GTK_POS_TOP)); + gtk_menu_append (GTK_MENU (menu), item); + + item = make_menu_item ("Bottom", GTK_SIGNAL_FUNC (cb_pos_menu_select), + GINT_TO_POINTER (GTK_POS_BOTTOM)); + gtk_menu_append (GTK_MENU (menu), item); + + item = make_menu_item ("Left", GTK_SIGNAL_FUNC (cb_pos_menu_select), + GINT_TO_POINTER (GTK_POS_LEFT)); + gtk_menu_append (GTK_MENU (menu), item); + + item = make_menu_item ("Right", GTK_SIGNAL_FUNC (cb_pos_menu_select), + GINT_TO_POINTER (GTK_POS_RIGHT)); + gtk_menu_append (GTK_MENU (menu), item); + + gtk_option_menu_set_menu (GTK_OPTION_MENU (opt), menu); + gtk_box_pack_start (GTK_BOX (box2), opt, TRUE, TRUE, 0); + gtk_widget_show (opt); + + gtk_box_pack_start (GTK_BOX (box1), box2, TRUE, TRUE, 0); + gtk_widget_show (box2); + + box2 = gtk_hbox_new (FALSE, 10); + gtk_container_border_width (GTK_CONTAINER (box2), 10); + + /* yet another option menu, this time for the update policy of the + scale widgets */ + label = gtk_label_new ("Scale Update Policy:"); + gtk_box_pack_start (GTK_BOX (box2), label, FALSE, FALSE, 0); + gtk_widget_show (label); + + opt = gtk_option_menu_new(); + menu = gtk_menu_new(); + + item = make_menu_item ("Continuous", + GTK_SIGNAL_FUNC (cb_update_menu_select), + GINT_TO_POINTER (GTK_UPDATE_CONTINUOUS)); + gtk_menu_append (GTK_MENU (menu), item); + + item = make_menu_item ("Discontinuous", + GTK_SIGNAL_FUNC (cb_update_menu_select), + GINT_TO_POINTER (GTK_UPDATE_DISCONTINUOUS)); + gtk_menu_append (GTK_MENU (menu), item); + + item = make_menu_item ("Delayed", + GTK_SIGNAL_FUNC (cb_update_menu_select), + GINT_TO_POINTER (GTK_UPDATE_DELAYED)); + gtk_menu_append (GTK_MENU (menu), item); + + gtk_option_menu_set_menu (GTK_OPTION_MENU (opt), menu); + gtk_box_pack_start (GTK_BOX (box2), opt, TRUE, TRUE, 0); + gtk_widget_show (opt); + + gtk_box_pack_start (GTK_BOX (box1), box2, TRUE, TRUE, 0); + gtk_widget_show (box2); + + box2 = gtk_hbox_new (FALSE, 10); + gtk_container_border_width (GTK_CONTAINER (box2), 10); + + /* a GtkHScale widget for adjusting the number of digits on the + sample scales. */ + label = gtk_label_new ("Scale Digits:"); + gtk_box_pack_start (GTK_BOX (box2), label, FALSE, FALSE, 0); + gtk_widget_show (label); + + adj2 = gtk_adjustment_new (1.0, 0.0, 5.0, 1.0, 1.0, 0.0); + gtk_signal_connect (GTK_OBJECT (adj2), "value_changed", + GTK_SIGNAL_FUNC (cb_digits_scale), NULL); + scale = gtk_hscale_new (GTK_ADJUSTMENT (adj2)); + gtk_scale_set_digits (GTK_SCALE (scale), 0); + gtk_box_pack_start (GTK_BOX (box2), scale, TRUE, TRUE, 0); + gtk_widget_show (scale); + + gtk_box_pack_start (GTK_BOX (box1), box2, TRUE, TRUE, 0); + gtk_widget_show (box2); + + box2 = gtk_hbox_new (FALSE, 10); + gtk_container_border_width (GTK_CONTAINER (box2), 10); + + /* And, one last GtkHScale widget for adjusting the page size of the + scrollbar. */ + label = gtk_label_new ("Scrollbar Page Size:"); + gtk_box_pack_start (GTK_BOX (box2), label, FALSE, FALSE, 0); + gtk_widget_show (label); + + adj2 = gtk_adjustment_new (1.0, 1.0, 101.0, 1.0, 1.0, 0.0); + gtk_signal_connect (GTK_OBJECT (adj2), "value_changed", + GTK_SIGNAL_FUNC (cb_page_size), adj1); + scale = gtk_hscale_new (GTK_ADJUSTMENT (adj2)); + gtk_scale_set_digits (GTK_SCALE (scale), 0); + gtk_box_pack_start (GTK_BOX (box2), scale, TRUE, TRUE, 0); + gtk_widget_show (scale); + + gtk_box_pack_start (GTK_BOX (box1), box2, TRUE, TRUE, 0); + gtk_widget_show (box2); + + separator = gtk_hseparator_new (); + gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 0); + gtk_widget_show (separator); + + box2 = gtk_vbox_new (FALSE, 10); + gtk_container_border_width (GTK_CONTAINER (box2), 10); + gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, TRUE, 0); + gtk_widget_show (box2); + + button = gtk_button_new_with_label ("Quit"); + gtk_signal_connect_object (GTK_OBJECT (button), "clicked", + GTK_SIGNAL_FUNC(gtk_main_quit), + NULL); + gtk_box_pack_start (GTK_BOX (box2), button, TRUE, TRUE, 0); + GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); + gtk_widget_grab_default (button); + gtk_widget_show (button); + + gtk_widget_show (window); +} + +int main (int argc, char *argv[]) +{ + gtk_init(&argc, &argv); + + create_range_controls(); + + gtk_main(); + + return 0; +} + +/* example-end */ diff --git a/examples/text/Makefile b/examples/text/Makefile new file mode 100644 index 0000000000..b1e501a48f --- /dev/null +++ b/examples/text/Makefile @@ -0,0 +1,8 @@ + +CC = gcc + +text: text.c + $(CC) `gtk-config --cflags` `gtk-config --libs` text.c -o text + +clean: + rm -f *.o text diff --git a/examples/text/text.c b/examples/text/text.c new file mode 100644 index 0000000000..b6a0210182 --- /dev/null +++ b/examples/text/text.c @@ -0,0 +1,181 @@ +/* example-start text text.c */ + +/* text.c */ + +#include +#include + +void text_toggle_editable (GtkWidget *checkbutton, + GtkWidget *text) +{ + gtk_text_set_editable(GTK_TEXT(text), + GTK_TOGGLE_BUTTON(checkbutton)->active); +} + +void text_toggle_word_wrap (GtkWidget *checkbutton, + GtkWidget *text) +{ + gtk_text_set_word_wrap(GTK_TEXT(text), + GTK_TOGGLE_BUTTON(checkbutton)->active); +} + +void close_application( GtkWidget *widget, gpointer data ) +{ + gtk_main_quit(); +} + +int main (int argc, char *argv[]) +{ + GtkWidget *window; + GtkWidget *box1; + GtkWidget *box2; + GtkWidget *hbox; + GtkWidget *button; + GtkWidget *check; + GtkWidget *separator; + GtkWidget *table; + GtkWidget *vscrollbar; + GtkWidget *text; + GdkColormap *cmap; + GdkColor colour; + GdkFont *fixed_font; + + FILE *infile; + + gtk_init (&argc, &argv); + + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_widget_set_usize (window, 600, 500); + gtk_window_set_policy (GTK_WINDOW(window), TRUE, TRUE, FALSE); + gtk_signal_connect (GTK_OBJECT (window), "destroy", + GTK_SIGNAL_FUNC(close_application), + NULL); + gtk_window_set_title (GTK_WINDOW (window), "Text Widget Example"); + gtk_container_border_width (GTK_CONTAINER (window), 0); + + + box1 = gtk_vbox_new (FALSE, 0); + gtk_container_add (GTK_CONTAINER (window), box1); + gtk_widget_show (box1); + + + box2 = gtk_vbox_new (FALSE, 10); + gtk_container_border_width (GTK_CONTAINER (box2), 10); + gtk_box_pack_start (GTK_BOX (box1), box2, TRUE, TRUE, 0); + gtk_widget_show (box2); + + + table = gtk_table_new (2, 2, FALSE); + gtk_table_set_row_spacing (GTK_TABLE (table), 0, 2); + gtk_table_set_col_spacing (GTK_TABLE (table), 0, 2); + gtk_box_pack_start (GTK_BOX (box2), table, TRUE, TRUE, 0); + gtk_widget_show (table); + + /* Create the GtkText widget */ + text = gtk_text_new (NULL, NULL); + gtk_text_set_editable (GTK_TEXT (text), TRUE); + gtk_table_attach (GTK_TABLE (table), text, 0, 1, 0, 1, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0); + gtk_widget_show (text); + + /* Add a vertical scrollbar to the GtkText widget */ + vscrollbar = gtk_vscrollbar_new (GTK_TEXT (text)->vadj); + gtk_table_attach (GTK_TABLE (table), vscrollbar, 1, 2, 0, 1, + GTK_FILL, GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0); + gtk_widget_show (vscrollbar); + + /* Get the system colour map and allocate the colour red */ + cmap = gdk_colormap_get_system(); + colour.red = 0xffff; + colour.green = 0; + colour.blue = 0; + if (!gdk_color_alloc(cmap, &colour)) { + g_error("couldn't allocate colour"); + } + + /* Load a fixed font */ + fixed_font = gdk_font_load ("-misc-fixed-medium-r-*-*-*-140-*-*-*-*-*-*"); + + /* Realizing a widget creates a window for it, ready for us to insert some text */ + gtk_widget_realize (text); + + /* Freeze the text widget, ready for multiple updates */ + gtk_text_freeze (GTK_TEXT (text)); + + /* Insert some coloured text */ + gtk_text_insert (GTK_TEXT (text), NULL, &text->style->black, NULL, + "Supports ", -1); + gtk_text_insert (GTK_TEXT (text), NULL, &colour, NULL, + "colored ", -1); + gtk_text_insert (GTK_TEXT (text), NULL, &text->style->black, NULL, + "text and different ", -1); + gtk_text_insert (GTK_TEXT (text), fixed_font, &text->style->black, NULL, + "fonts\n\n", -1); + + /* Load the file text.c into the text window */ + + infile = fopen("text.c", "r"); + + if (infile) { + char buffer[1024]; + int nchars; + + while (1) + { + nchars = fread(buffer, 1, 1024, infile); + gtk_text_insert (GTK_TEXT (text), fixed_font, NULL, + NULL, buffer, nchars); + + if (nchars < 1024) + break; + } + + fclose (infile); + } + + /* Thaw the text widget, allowing the updates to become visible */ + gtk_text_thaw (GTK_TEXT (text)); + + hbox = gtk_hbutton_box_new (); + gtk_box_pack_start (GTK_BOX (box2), hbox, FALSE, FALSE, 0); + gtk_widget_show (hbox); + + check = gtk_check_button_new_with_label("Editable"); + gtk_box_pack_start (GTK_BOX (hbox), check, FALSE, FALSE, 0); + gtk_signal_connect (GTK_OBJECT(check), "toggled", + GTK_SIGNAL_FUNC(text_toggle_editable), text); + gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(check), TRUE); + gtk_widget_show (check); + check = gtk_check_button_new_with_label("Wrap Words"); + gtk_box_pack_start (GTK_BOX (hbox), check, FALSE, TRUE, 0); + gtk_signal_connect (GTK_OBJECT(check), "toggled", + GTK_SIGNAL_FUNC(text_toggle_word_wrap), text); + gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(check), FALSE); + gtk_widget_show (check); + + separator = gtk_hseparator_new (); + gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 0); + gtk_widget_show (separator); + + box2 = gtk_vbox_new (FALSE, 10); + gtk_container_border_width (GTK_CONTAINER (box2), 10); + gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, TRUE, 0); + gtk_widget_show (box2); + + button = gtk_button_new_with_label ("close"); + gtk_signal_connect (GTK_OBJECT (button), "clicked", + GTK_SIGNAL_FUNC(close_application), + NULL); + gtk_box_pack_start (GTK_BOX (box2), button, TRUE, TRUE, 0); + GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); + gtk_widget_grab_default (button); + gtk_widget_show (button); + + gtk_widget_show (window); + + gtk_main (); + + return 0; +} +/* example-end */ diff --git a/examples/tree/Makefile b/examples/tree/Makefile new file mode 100644 index 0000000000..4f9c70a02b --- /dev/null +++ b/examples/tree/Makefile @@ -0,0 +1,8 @@ + +CC = gcc + +tree: tree.c + $(CC) `gtk-config --cflags` `gtk-config --libs` tree.c -o tree + +clean: + rm -f *.o tree diff --git a/examples/tree/tree.c b/examples/tree/tree.c new file mode 100644 index 0000000000..3b19f8db38 --- /dev/null +++ b/examples/tree/tree.c @@ -0,0 +1,178 @@ +/* example-start tree tree.c */ + +#include + +/* for all the GtkItem:: and GtkTreeItem:: signals */ +static void cb_itemsignal (GtkWidget *item, gchar *signame) +{ + gchar *name; + GtkLabel *label; + + /* It's a GtkBin, so it has one child, which we know to be a + label, so get that */ + label = GTK_LABEL (GTK_BIN (item)->child); + /* Get the text of the label */ + gtk_label_get (label, &name); + /* Get the level of the tree which the item is in */ + g_print ("%s called for item %s->%p, level %d\n", signame, name, + item, GTK_TREE (item->parent)->level); +} + +/* Note that this is never called */ +static void cb_unselect_child (GtkWidget *root_tree, GtkWidget *child, + GtkWidget *subtree) +{ + g_print ("unselect_child called for root tree %p, subtree %p, child %p\n", + root_tree, subtree, child); +} + +/* Note that this is called every time the user clicks on an item, + whether it is already selected or not. */ +static void cb_select_child (GtkWidget *root_tree, GtkWidget *child, + GtkWidget *subtree) +{ + g_print ("select_child called for root tree %p, subtree %p, child %p\n", + root_tree, subtree, child); +} + +static void cb_selection_changed (GtkWidget *tree) +{ + GList *i; + + g_print ("selection_change called for tree %p\n", tree); + g_print ("selected objects are:\n"); + + i = GTK_TREE_SELECTION(tree); + while (i){ + gchar *name; + GtkLabel *label; + GtkWidget *item; + + /* Get a GtkWidget pointer from the list node */ + item = GTK_WIDGET (i->data); + label = GTK_LABEL (GTK_BIN (item)->child); + gtk_label_get (label, &name); + g_print ("\t%s on level %d\n", name, GTK_TREE + (item->parent)->level); + i = i->next; + } +} + +int main (int argc, char *argv[]) +{ + GtkWidget *window, *scrolled_win, *tree; + static gchar *itemnames[] = {"Foo", "Bar", "Baz", "Quux", + "Maurice"}; + gint i; + + gtk_init (&argc, &argv); + + /* a generic toplevel window */ + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_signal_connect (GTK_OBJECT(window), "delete_event", + GTK_SIGNAL_FUNC (gtk_main_quit), NULL); + gtk_container_border_width (GTK_CONTAINER(window), 5); + + /* A generic scrolled window */ + scrolled_win = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + gtk_widget_set_usize (scrolled_win, 150, 200); + gtk_container_add (GTK_CONTAINER(window), scrolled_win); + gtk_widget_show (scrolled_win); + + /* Create the root tree */ + tree = gtk_tree_new(); + g_print ("root tree is %p\n", tree); + /* connect all GtkTree:: signals */ + gtk_signal_connect (GTK_OBJECT(tree), "select_child", + GTK_SIGNAL_FUNC(cb_select_child), tree); + gtk_signal_connect (GTK_OBJECT(tree), "unselect_child", + GTK_SIGNAL_FUNC(cb_unselect_child), tree); + gtk_signal_connect (GTK_OBJECT(tree), "selection_changed", + GTK_SIGNAL_FUNC(cb_selection_changed), tree); + /* Add it to the scrolled window */ + gtk_container_add (GTK_CONTAINER(scrolled_win), tree); + /* Set the selection mode */ + gtk_tree_set_selection_mode (GTK_TREE(tree), + GTK_SELECTION_MULTIPLE); + /* Show it */ + gtk_widget_show (tree); + + for (i = 0; i < 5; i++){ + GtkWidget *subtree, *item; + gint j; + + /* Create a tree item */ + item = gtk_tree_item_new_with_label (itemnames[i]); + /* Connect all GtkItem:: and GtkTreeItem:: signals */ + gtk_signal_connect (GTK_OBJECT(item), "select", + GTK_SIGNAL_FUNC(cb_itemsignal), "select"); + gtk_signal_connect (GTK_OBJECT(item), "deselect", + GTK_SIGNAL_FUNC(cb_itemsignal), "deselect"); + gtk_signal_connect (GTK_OBJECT(item), "toggle", + GTK_SIGNAL_FUNC(cb_itemsignal), "toggle"); + gtk_signal_connect (GTK_OBJECT(item), "expand", + GTK_SIGNAL_FUNC(cb_itemsignal), "expand"); + gtk_signal_connect (GTK_OBJECT(item), "collapse", + GTK_SIGNAL_FUNC(cb_itemsignal), "collapse"); + /* Add it to the parent tree */ + gtk_tree_append (GTK_TREE(tree), item); + /* Show it - this can be done at any time */ + gtk_widget_show (item); + /* Create this item's subtree */ + subtree = gtk_tree_new(); + g_print ("-> item %s->%p, subtree %p\n", itemnames[i], item, + subtree); + + /* This is still necesary if you want these signals to be called + for the subtree's children. Note that selection_change will be + signalled for the root tree regardless. */ + gtk_signal_connect (GTK_OBJECT(subtree), "select_child", + GTK_SIGNAL_FUNC(cb_select_child), subtree); + gtk_signal_connect (GTK_OBJECT(subtree), "unselect_child", + GTK_SIGNAL_FUNC(cb_unselect_child), subtree); + /* This has absolutely no effect, because it is completely ignored + in subtrees */ + gtk_tree_set_selection_mode (GTK_TREE(subtree), + GTK_SELECTION_SINGLE); + /* Neither does this, but for a rather different reason - the + view_mode and view_line values of a tree are propagated to + subtrees when they are mapped. So, setting it later on would + actually have a (somewhat unpredictable) effect */ + gtk_tree_set_view_mode (GTK_TREE(subtree), GTK_TREE_VIEW_ITEM); + /* Set this item's subtree - note that you cannot do this until + AFTER the item has been added to its parent tree! */ + gtk_tree_item_set_subtree (GTK_TREE_ITEM(item), subtree); + + for (j = 0; j < 5; j++){ + GtkWidget *subitem; + + /* Create a subtree item, in much the same way */ + subitem = gtk_tree_item_new_with_label (itemnames[j]); + /* Connect all GtkItem:: and GtkTreeItem:: signals */ + gtk_signal_connect (GTK_OBJECT(subitem), "select", + GTK_SIGNAL_FUNC(cb_itemsignal), "select"); + gtk_signal_connect (GTK_OBJECT(subitem), "deselect", + GTK_SIGNAL_FUNC(cb_itemsignal), "deselect"); + gtk_signal_connect (GTK_OBJECT(subitem), "toggle", + GTK_SIGNAL_FUNC(cb_itemsignal), "toggle"); + gtk_signal_connect (GTK_OBJECT(subitem), "expand", + GTK_SIGNAL_FUNC(cb_itemsignal), "expand"); + gtk_signal_connect (GTK_OBJECT(subitem), "collapse", + GTK_SIGNAL_FUNC(cb_itemsignal), "collapse"); + g_print ("-> -> item %s->%p\n", itemnames[j], subitem); + /* Add it to its parent tree */ + gtk_tree_append (GTK_TREE(subtree), subitem); + /* Show it */ + gtk_widget_show (subitem); + } + } + + /* Show the window and loop endlessly */ + gtk_widget_show (window); + gtk_main(); + return 0; +} +/* example-end */