diff options
-rw-r--r-- | ChangeLog | 13 | ||||
-rw-r--r-- | ChangeLog.pre-2-0 | 13 | ||||
-rw-r--r-- | ChangeLog.pre-2-10 | 13 | ||||
-rw-r--r-- | ChangeLog.pre-2-2 | 13 | ||||
-rw-r--r-- | ChangeLog.pre-2-4 | 13 | ||||
-rw-r--r-- | ChangeLog.pre-2-6 | 13 | ||||
-rw-r--r-- | ChangeLog.pre-2-8 | 13 | ||||
-rw-r--r-- | docs/gtk_tut.sgml | 2069 | ||||
-rw-r--r-- | docs/tutorial/gtk_tut.sgml | 2069 | ||||
-rw-r--r-- | examples/menu/menufactory.c | 145 | ||||
-rw-r--r-- | examples/menu/menufactory.h | 9 | ||||
-rw-r--r-- | examples/menu/mfmain.c | 12 | ||||
-rw-r--r-- | examples/menu/mfmain.h | 6 | ||||
-rw-r--r-- | examples/rangewidgets/Makefile | 8 | ||||
-rw-r--r-- | examples/rangewidgets/rangewidgets.c | 287 |
15 files changed, 4051 insertions, 645 deletions
@@ -1,3 +1,16 @@ +Thu Aug 13 09:11:11 BST 1998 Tony Gale <gale@gtk.org> + + * docs/gtk_tut.sgml: + - Tidy up of the menufactory example from + Andy Kahn <kahn@zk3.dec.com> + - New section on Range Widgets from + David Huggins-Daines <bn711@freenet.carleton.ca> + - 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 + Tue Jul 21 12:42:01 1998 Owen Taylor <otaylor@redhat.com> * gdk/gdk.h gdk/gdkfont.c: Added gdk_text/string_extents() - diff --git a/ChangeLog.pre-2-0 b/ChangeLog.pre-2-0 index a6cad6a487..2e9d069ca1 100644 --- a/ChangeLog.pre-2-0 +++ b/ChangeLog.pre-2-0 @@ -1,3 +1,16 @@ +Thu Aug 13 09:11:11 BST 1998 Tony Gale <gale@gtk.org> + + * docs/gtk_tut.sgml: + - Tidy up of the menufactory example from + Andy Kahn <kahn@zk3.dec.com> + - New section on Range Widgets from + David Huggins-Daines <bn711@freenet.carleton.ca> + - 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 + Tue Jul 21 12:42:01 1998 Owen Taylor <otaylor@redhat.com> * gdk/gdk.h gdk/gdkfont.c: Added gdk_text/string_extents() - diff --git a/ChangeLog.pre-2-10 b/ChangeLog.pre-2-10 index a6cad6a487..2e9d069ca1 100644 --- a/ChangeLog.pre-2-10 +++ b/ChangeLog.pre-2-10 @@ -1,3 +1,16 @@ +Thu Aug 13 09:11:11 BST 1998 Tony Gale <gale@gtk.org> + + * docs/gtk_tut.sgml: + - Tidy up of the menufactory example from + Andy Kahn <kahn@zk3.dec.com> + - New section on Range Widgets from + David Huggins-Daines <bn711@freenet.carleton.ca> + - 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 + Tue Jul 21 12:42:01 1998 Owen Taylor <otaylor@redhat.com> * gdk/gdk.h gdk/gdkfont.c: Added gdk_text/string_extents() - diff --git a/ChangeLog.pre-2-2 b/ChangeLog.pre-2-2 index a6cad6a487..2e9d069ca1 100644 --- a/ChangeLog.pre-2-2 +++ b/ChangeLog.pre-2-2 @@ -1,3 +1,16 @@ +Thu Aug 13 09:11:11 BST 1998 Tony Gale <gale@gtk.org> + + * docs/gtk_tut.sgml: + - Tidy up of the menufactory example from + Andy Kahn <kahn@zk3.dec.com> + - New section on Range Widgets from + David Huggins-Daines <bn711@freenet.carleton.ca> + - 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 + Tue Jul 21 12:42:01 1998 Owen Taylor <otaylor@redhat.com> * gdk/gdk.h gdk/gdkfont.c: Added gdk_text/string_extents() - diff --git a/ChangeLog.pre-2-4 b/ChangeLog.pre-2-4 index a6cad6a487..2e9d069ca1 100644 --- a/ChangeLog.pre-2-4 +++ b/ChangeLog.pre-2-4 @@ -1,3 +1,16 @@ +Thu Aug 13 09:11:11 BST 1998 Tony Gale <gale@gtk.org> + + * docs/gtk_tut.sgml: + - Tidy up of the menufactory example from + Andy Kahn <kahn@zk3.dec.com> + - New section on Range Widgets from + David Huggins-Daines <bn711@freenet.carleton.ca> + - 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 + Tue Jul 21 12:42:01 1998 Owen Taylor <otaylor@redhat.com> * gdk/gdk.h gdk/gdkfont.c: Added gdk_text/string_extents() - diff --git a/ChangeLog.pre-2-6 b/ChangeLog.pre-2-6 index a6cad6a487..2e9d069ca1 100644 --- a/ChangeLog.pre-2-6 +++ b/ChangeLog.pre-2-6 @@ -1,3 +1,16 @@ +Thu Aug 13 09:11:11 BST 1998 Tony Gale <gale@gtk.org> + + * docs/gtk_tut.sgml: + - Tidy up of the menufactory example from + Andy Kahn <kahn@zk3.dec.com> + - New section on Range Widgets from + David Huggins-Daines <bn711@freenet.carleton.ca> + - 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 + Tue Jul 21 12:42:01 1998 Owen Taylor <otaylor@redhat.com> * gdk/gdk.h gdk/gdkfont.c: Added gdk_text/string_extents() - diff --git a/ChangeLog.pre-2-8 b/ChangeLog.pre-2-8 index a6cad6a487..2e9d069ca1 100644 --- a/ChangeLog.pre-2-8 +++ b/ChangeLog.pre-2-8 @@ -1,3 +1,16 @@ +Thu Aug 13 09:11:11 BST 1998 Tony Gale <gale@gtk.org> + + * docs/gtk_tut.sgml: + - Tidy up of the menufactory example from + Andy Kahn <kahn@zk3.dec.com> + - New section on Range Widgets from + David Huggins-Daines <bn711@freenet.carleton.ca> + - 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 + Tue Jul 21 12:42:01 1998 Owen Taylor <otaylor@redhat.com> * gdk/gdk.h gdk/gdkfont.c: Added gdk_text/string_extents() - diff --git a/docs/gtk_tut.sgml b/docs/gtk_tut.sgml index d519ddbfc6..a948db7915 100644 --- a/docs/gtk_tut.sgml +++ b/docs/gtk_tut.sgml @@ -10,7 +10,7 @@ name="<imain@gtk.org>"></tt>, Tony Gale <tt><htmlurl url="mailto:gale@gtk.org" name="<gale@gtk.org>"></tt> -<date>July 25th, 1998 +<date>August 13th, 1998 <!-- ***************************************************************** --> <sect>Introduction @@ -482,14 +482,17 @@ static gint button_press_event (GtkWidget *widget, Note that we can declare the second argument as type <tt/GdkEventButton/ as we know what type of event will occur for this function to be called. -<!-- Need an Annex with all the event types in it - TRG --> - -<!-- Need to check this - TRG The value returned from this function indicates whether the event should -be processed further by the GTK event handling mechanism. Returning +be propagated further by the GTK event handling mechanism. Returning TRUE indicates that the event has been handled, and that it should not -propogate further. Returning FALSE continues the normal event handling. ---> +propagate further. Returning FALSE continues the normal event handling. +See the section on +<ref id="sec_Adv_Events_and_Signals" +name="Advanced Event and Signal Handling"> for more details on this +propagation process. + +For details on the GdkEvent data types, see the appendix entitled +<ref id="sec_GDK_Event_Types" name="GDK Event Types">. <!-- ----------------------------------------------------------------- --> <sect1>Stepping Through Hello World @@ -2086,6 +2089,775 @@ removes the need for a variable to hold the list of buttons: <!-- TODO: checout out gtk_radio_button_new_from_widget function - TRG --> <!-- ***************************************************************** --> +<sect>Range Widgets +<!-- ***************************************************************** --> +<p> +The category of range widgets includes the ubiquitous <em>scrollbar</em> +widget and the less common <em>scale</em> 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. + +<!-- ----------------------------------------------------------------- --> +<sect1>The Scale Widgets +<p> +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. + +<!-- ----------------------------------------------------------------- --> +<sect2>Creating a Scale Widget +<p> +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 +<tt><gtk/gtkvscale.h></tt> and +<tt><gtk/gtkhscale.h></tt>, create vertical and +horizontal scale widgets, respectively: + +<tscreen><verb> +GtkWidget* gtk_vscale_new( GtkAdjustment *adjustment ); + +GtkWidget* gtk_hscale_new( GtkAdjustment *adjustment ); +</verb></tscreen> + +<tt/adjustment/ can either be an adjustment which has +already been created with <tt/gtk_adjustment_new()/, or +<tt/NULL/, in which case, an anonymous GtkAdjustment is +created with all of its values set to <tt/0.0/. If you're +thoroughly confused by now, see <ref +id="sec_Range_GtkAdjustment" name="The Adjustment Object"> +below for an explanation of what exactly the <tt/adjustment/ +argument does and how to create and manipulate it. + +<!-- ----------------------------------------------------------------- --> +<sect2>Functions, Signals, and Macros +<p> +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: + +<tscreen><verb> +void gtk_scale_set_draw_value( GtkScale *scale, + gint draw_value ); +</verb></tscreen> + +As you might have guessed, <tt/draw_value/ is either +<tt/TRUE/ or <tt/FALSE/, with predictable consequences for +either one. + +The value displayed by a scale widget is rounded to one +decimal point by default (as is the <tt/value/ field in its +GtkAdjustment... but I digress). You can change this with: + +<tscreen><verb> +void gtk_scale_set_digits( GtkScale *scale, + gint digits); +</verb></tscreen> + +where <tt/digits/ is the number of decimal places you want. +You can set <tt/digits/ to anything you like, but no more +than 13 decimal places will actually be drawn on screen. +This probably isn't too horribly restrictive. + +Finally, the value can be drawn in different positions +relative to the trough: + +<tscreen><verb> +void gtk_scale_set_value_pos( GtkScale *scale, + GtkPositionType pos ); +</verb></tscreen> + +If you've read the section on the notebook widget, then you +know what the possible values of <tt/pos/ are. They are +defined as type <tt>GtkPositionType</tt> and can take one +of the following values: + +<itemize> +<item>GTK_POS_LEFT +<item>GTK_POS_RIGHT +<item>GTK_POS_TOP +<item>GTK_POS_BOTTOM +</itemize> + +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 +<tt><gtk/gtkscale.h></tt>. 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. + +<!-- ----------------------------------------------------------------- --> +<sect1>The Scrollbar Widgets +<p> +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: + +<tscreen><verb> +GtkWidget* gtk_hscrollbar_new( GtkAdjustment *adjustment ); + +GtkWidget* gtk_vscrollbar_new( GtkAdjustment *adjustment ); +</verb></tscreen> + +and that's about it (if you don't believe me, look in the +header files!). Again, <tt/adjustment/ can either be a +pointer to an existing GtkAdjustment, or NULL, in which case +one will be created for you. + +<!-- ----------------------------------------------------------------- --> +<sect1>The Adjustment Object<label id="sec_Range_GtkAdjustment"> +<p> +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 +<tt/adjustment/ object. + +Every range widget contains a pointer to a GtkAdjustment +object. You'll usually create one of these in order to pass +it to the <tt/gtk_*_new()/ function which creates a range +widget, or some compound widget that uses range widgets, such +as GtkScrolledWindow or GtkCList. + +Aside from specifying some characteristics related to the +range widget's appearance and behaviour, the GtkAdjustment you +pass to this function becomes "attached" to the newly-created +range widget and from that point on will always contain the +numerical value corresponding to the position of the slider +(unless, at some point in the future, you set a new adjustment +for the range widget). + +One adjustment object can be shared between many range +widgets. Reusing the same adjustment object across several +range widgets will cause them all to change when one of them +is changed. + +<!-- ----------------------------------------------------------------- --> +<sect2>Creating a GtkAdjustment +<p> +You create an adjustment using: + +<tscreen><verb> +GtkObject* gtk_adjustment_new( gfloat value, + gfloat lower, + gfloat upper, + gfloat step_increment, + gfloat page_increment, + gfloat page_size ); +</verb></tscreen> + +It may or may not be obvious by now that the values given to +<tt/gtk_adjustment_new()/ are simply arbitrary floating-point +values. The mapping between these values and the on-screen +size of the range widget and its constituent parts is +handled by the range widget. Thus, you're free to use +whatever numbers are most meaningful to your program. + +The <tt/value/ argument is the initial value you want to +give to the adjustment. The <tt/lower/ argument specifies +the lowest value which the adjustment can hold, or, in other +words, the lowest value which the user can select using the +range widget which uses this adjustment. The +<tt/step_increment/ argument specifies the "smaller" of the +two increments by which the user can change the value, while +the <tt/page_increment/ is the "larger" one. <ref +id="sec_Range_Bindings" name="Key and Mouse Bindings"> below +describes the default key and mouse bindings for range +widgets, and how they relate to these increments. The +<tt/page_size/ argument is only relevant for scrollbars. +Its most obvious effect is that it determines the size of +the slider; however, you should set it based on the "size" +of the visible area of whatever you're scrolling. + +As an example, say you're writing a text editor. You might +want to have the value of the vertical scrollbar beside the +editing area correspond to the line number +of the first visible line in the editing area. In that +case, you might call <tt/gtk_adjustment_new()/ like this: + +<tscreen><verb> +GtkObject *adj; + +adj = gtk_adjustment_new (0, first_line, last_line, 1, + window_height - 2, window_height); +</verb></tscreen> + +where <tt/window_height/ is the number of visible lines in +the window. + +Finally, with regard to the <tt/upper/ argument to +<tt/gtk_adjustment_new/, you'll notice that, since the value +of the adjustment corresponds to the <em/first/ visible line +in the window, the maximum value in the adjustment is not +actually <tt/last_line/, but rather <tt>last_line - +window_height</tt> (or, in more general terms, <tt>upper - +page_height</tt>). 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 +<tt/page_size/ to <tt/0.0/ for adjustments that are only +going to be used for scale widgets. + +<!-- ----------------------------------------------------------------- --> +<sect2>Inside the GtkAdjustment object +<p> +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 <tt/struct _GtkAdjustment/ itself: + +<tscreen><verb> +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); +}; +</verb></tscreen> + +The first thing you should know is that there aren't any +handy-dandy macros or accessor functions for getting the +<tt/value/ out of a GtkAdjustment, so you'll have to (horror +of horrors) do it like a <em/real/ C programmer. Don't +worry - the <tt>GTK_ADJUSTMENT (Object)</tt> 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 +<em/set/ these fields directly. To set <tt/value/, you can +use: + +<tscreen><verb> +void gtk_adjustment_set_value( GtkAdjustment *adjustment, + gfloat value ); +</verb></tscreen> + +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 +<tt/gtk_range_set_adjustment()/, as detailed in <ref +id="sec_Range_Functions" name="Common Functions, Signals, +and Macros"> 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 <ref id="sec_Range_UpdatePolicy" +name="Update Policies"> 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 <tt/gtk_adjustment_set_value()/. So, for +example, if you have a scale widget, and you want to change +the rotation of a picture whenever its value changes, you +would create a callback like this: + +<tscreen><verb> +void cb_rotate_picture (GtkAdjustment *adj, GtkWidget *picture) +{ + set_picture_rotation (picture, adj->value); +... +</verb></tscreen> + +and connect it to the scale widget's adjustment like this: + +<tscreen><verb> +gtk_signal_connect (GTK_OBJECT (adj), "value_changed", + GTK_SIGNAL_FUNC (cb_rotate_picture), picture); +</verb></tscreen> + +The "changed" signal is somewhat more elusive. It is never +emitted directly due to the <em/user's/ actions. Rather, +programs or other widgets should emit it on a GtkAdjustment +when they modify any of its fields directly. This will +force any range widgets that use this adjustment to +recalculate and redraw if necessary. This is useful if you +have a number of range widgets using the same GtkAdjustment, +and don't want to call <tt/gtk_range_set_adjustment()/ for +all of them. It's also handy if you are going to be +continuously changing these values, such as in our +hypothetical text editor, where the <tt/upper/ field will +have to change every time a new line is added, and you don't +want the extra overhead of creating a new GtkAdjustment +object every time. + +<!-- ----------------------------------------------------------------- --> +<sect1>Common Functions, Signals, and Macros<label id="sec_Range_Functions"> +<p> +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. + +<!-- ----------------------------------------------------------------- --> +<sect2>Update Policies<label id="sec_Range_UpdatePolicy"> +<p> +The "update policy" of a range widget defines at what points +during user interaction it will change the <tt/value/ field +of its GtkAdjustment and emit the "value_changed" signal on +this GtkAdjustment. The update policies, defined in +<tt><gtk/gtkenums.h></tt> as the <tt>enum +GtkUpdateType</tt>, are: + +<itemize> +<item>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. +</item> + +<item>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. +</item> + +<item>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. +</item> +</itemize> + +The update policy of a range widget can be set by casting it +using the <tt>GTK_RANGE (Widget)</tt> macro and passing it +to this function: + +<tscreen><verb> +void gtk_range_set_update_policy( GtkRange *range, + GtkUpdateType policy ); +</verb></tscreen> + +<!-- ----------------------------------------------------------------- --> +<sect2>Getting and setting adjustments +<p> +Getting and setting the adjustment for a range widget "on +the fly" is done, predictably, with: + +<tscreen><verb> +GtkAdjustment* gtk_range_get_adjustment( GtkRange *range ); + +void gtk_range_set_adjustment( GtkRange *range, + GtkAdjustment *adjustment ); +</verb> +</tscreen> + +<tt/gtk_range_get_adjustment()/ returns a pointer to the +adjustment to which <tt/range/ is connected. + +<tt/gtk_range_set_adjustment()/ does absolutely nothing if +you pass it the adjustment that <tt/range/ is already using, +regardless of whether you changed any of its fields or not. +If you pass it a new GtkAdjustment, it will unreference the +old one if it exists (possibly destroying it), connect the +appropriate signals to the new one, and call the private +function <tt/gtk_range_adjustment_changed()/, which will (or +at least, is supposed to...) recalculate the size and/or +position of the slider and redraw if necessary. As +mentioned above, if you wish to reuse the same +GtkAdjustment, when you modify its values directly, you +should emit the "changed" signal on it, like this: + +<tscreen><verb> +gtk_signal_emit_by_name (GTK_OBJECT (adjustment), "changed"); +</verb></tscreen> + +<!-- ----------------------------------------------------------------- --> +<sect1>Key and Mouse bindings<label id="sec_Range_Bindings"> +<p> +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 <tt/page_increment/ to be added or +subtracted from its <tt/value/, and the slider to be moved +accordingly. Clicking button 2 in the trough will jump the +slider to the point at which the button was clicked. +Clicking any button on a scrollbar's arrows will cause its +adjustment's value to change <tt/step_increment/ at a time. + +The key bindings, by contrast, are slightly different +between horizontal and vertical range widgets, for obvious +reasons. They are also not quite the same for scale widgets +as they are for scrollbars, for somewhat less obvious +reasons (possibly to avoid confusion between the keys for +horizontal and vertical scrollbars in scrolled windows, +where both operate on the same area). + +<!-- ----------------------------------------------------------------- --> +<sect2>Vertical Range Widgets +<p> +All vertical range widgets can be operated with the up and +down arrow keys, as well as with the <tt/Page Up/ and +<tt/Page Down/ keys. The arrows move the slider up and +down by <tt/step_increment/, while <tt/Page Up/ and +<tt/Page Down/ move it by <tt/page_increment/. + +The user can also move the slider all the way to one end +or the other of the trough using the keyboard. With the +GtkVScale widget, this is done with the <tt/Home/ and +<tt/End/ keys, whereas with the GtkVScrollbar widget, this +is done by typing <tt>Control-Page Up</tt> and +<tt>Control-Page Down</tt>. + +<!-- ----------------------------------------------------------------- --> +<sect2>Horizontal Range Widgets +<p> +The left and right arrow keys work as you might expect in +these widgets, moving the slider back and forth by +<tt/step_increment/. The <tt/Home/ and <tt/End/ keys move +the slider to the ends of the trough. For the GtkHScale +widget, moving the slider by <tt/page_increment/ is +accomplished with <tt>Control-Left</tt> and +<tt>Control-Right</tt>, while for GtkHScrollbar, it's done +with <tt>Control-Home</tt> and <tt>Control-End</tt>. + +<!-- ----------------------------------------------------------------- --> +<sect1>Example<label id="sec_Range_Example"></heading> +<p> +This example is a somewhat modified version of the "range +widgets" test from <tt/testgtk.c/. It basically puts up a +window with three range widgets all connected to the same +adjustment, and a couple of controls for adjusting some of the +parameters for scale widgets mentioned above, so you can see +how they affect the way these widgets work for the user. + +<tscreen><verb> +/* example-start rangewidgets rangewidgets.c */ + +#include <gtk/gtk.h> + +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 */ +</verb></tscreen> + +<!-- ***************************************************************** --> <sect> Miscallaneous Widgets <!-- ***************************************************************** --> @@ -6328,7 +7100,7 @@ tree, and connects all the signals for the relevant objects, so you can see when they are emitted. <tscreen><verb> -/* example-start tree tree.c */ +/* example-start tree tree.h */ #include <gtk/gtk.h> @@ -6872,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 */ </verb></tscreen> @@ -6893,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 @@ -6910,131 +7678,39 @@ void menus_create(GtkMenuEntry * entries, int nmenu_entries); static GtkMenuEntry menu_items[] = { - {"<Main>/File/New", "<control>N", NULL, NULL}, - {"<Main>/File/Open", "<control>O", NULL, NULL}, - {"<Main>/File/Save", "<control>S", NULL, NULL}, - {"<Main>/File/Save as", NULL, NULL, NULL}, - {"<Main>/File/<separator>", NULL, NULL, NULL}, - {"<Main>/File/Quit", "<control>Q", file_quit_cmd_callback, "OK, I'll quit"}, - {"<Main>/Options/Test", NULL, NULL, NULL} + {"<Main>/File/New", "<control>N", print_hello, NULL}, + {"<Main>/File/Open", "<control>O", print_hello, NULL}, + {"<Main>/File/Save", "<control>S", print_hello, NULL}, + {"<Main>/File/Save as", NULL, NULL, NULL}, + {"<Main>/File/<separator>", NULL, NULL, NULL}, + {"<Main>/File/Quit", "<control>Q", file_quit_cmd_callback, "OK, I'll quit"}, + {"<Main>/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) -{ - if (initialize) - menus_init(); - - if (menubar) - *menubar = subfactory[0]->widget; - if (table) - *table = subfactory[0]->table; -} - -void menus_init(void) +static void +print_hello(GtkWidget *widget, gpointer data) { - 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], "<Main>"); - menus_create(menu_items, nmenu_items); - } + printf("hello!\n"); } -void menus_create(GtkMenuEntry * entries, int nmenu_entries) +void get_main_menu(GtkWidget *window, GtkWidget ** menubar) { - 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); - } -} + int nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]); + GtkMenuFactory *factory; + GtkMenuFactory *subfactory; -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, "<control>"); - if (modifiers & GDK_SHIFT_MASK) - strcat(accel, "<shift>"); - if (modifiers & GDK_MOD1_MASK) - strcat(accel, "<alt>"); - - 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; -} + factory = gtk_menu_factory_new(GTK_MENU_FACTORY_MENU_BAR); + subfactory = gtk_menu_factory_new(GTK_MENU_FACTORY_MENU_BAR); -static void menus_remove_accel(GtkWidget * widget, gchar * signal_name, gchar * path) -{ - char *t; + gtk_menu_factory_add_subfactory(factory, subfactory, "<Main>"); + gtk_menu_factory_add_entries(factory, menu_items, nmenu_items); + gtk_window_add_accelerator_table(GTK_WINDOW(window), subfactory->table); - if (entry_ht) { - t = g_hash_table_lookup(entry_ht, path); - g_free(t); - - g_hash_table_insert(entry_ht, path, g_strdup("")); - } + if (menubar) + *menubar = subfactory->widget; } -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 */ </verb></tscreen> @@ -7058,6 +7734,7 @@ void file_quit_cmd_callback(GtkWidget *widget, gpointer data); #endif /* __cplusplus */ #endif /* __MFMAIN_H__ */ + /* example-end */ </verb></tscreen> @@ -7071,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); @@ -7094,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); @@ -7114,6 +7787,7 @@ void file_quit_cmd_callback (GtkWidget *widget, gpointer data) g_print ("%s\n", (char *) data); gtk_exit(0); } + /* example-end */ </verb></tscreen> @@ -7572,18 +8246,12 @@ widget, please consider writing a tutorial on it so others may benifit from your time. <!-- ----------------------------------------------------------------- --> -<sect1> Adjustments -<p> -<!-- ----------------------------------------------------------------- --> <sect1> Toolbar <p> <!-- ----------------------------------------------------------------- --> <sect1> Fixed Container <p> <!-- ----------------------------------------------------------------- --> -<sect1> Range Controls -<p> -<!-- ----------------------------------------------------------------- --> <sect1> Curves <p> <!-- ----------------------------------------------------------------- --> @@ -8283,6 +8951,169 @@ gtk_idle_add will be called whenever the opportunity arises. As with the others, returning FALSE will stop the idle function from being called. <!-- ***************************************************************** --> +<sect>Advanced Event and Signal Handling<label id="sec_Adv_Events_and_Signals"> +<!-- ***************************************************************** --> + +<!-- ----------------------------------------------------------------- --> +<sect1>Signal Functions + +<!-- ----------------------------------------------------------------- --> +<sect2>Connecting and Disconnecting Signal Handlers +<p> + +<tscreen><verb> +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 ); +</verb></tscreen> + +<!-- ----------------------------------------------------------------- --> +<sect2>Blocking and Unblocking Signal Handlers +<p> +<tscreen><verb> +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 ); +</verb></tscreen> + +<!-- ----------------------------------------------------------------- --> +<sect2>Emitting and Stopping Signals +<p> +<tscreen><verb> +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 ); +</verb></tscreen> + +<!-- ----------------------------------------------------------------- --> +<sect1>Signal Emission and Propagation +<p> +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 <em>last</em> 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: +<itemize> +<item>Start with the widget where the event occured. + +<item>Emit the generic "event" signal. If that signal handler returns +a value of TRUE, stop all processing. + +<item>Otherwise, emit a specific, "button_press_event" signal. If that +returns TRUE, stop all processing. + +<item>Otherwise, go to the widget's parent, and repeat the above steps. + +<item>Contimue until some signal handler returns TRUE, or until the +top-level widget is reached. +</itemize> + +Some consequences of the above are: +<itemize> +<item>Your handler's return value will have no effect if there is a +default handler, unless you connect with gtk_signal_connect_after(). + +<item>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. +</itemize> + +<!-- ***************************************************************** --> <sect>Managing Selections <!-- ***************************************************************** --> @@ -11636,6 +12467,12 @@ name="rajat@ix.netcom.com"</tt> for the excellent job on the Pixmap tutorial. <item>Michael K. Johnson <tt><htmlurl url="mailto:johnsonm@redhat.com" name="johnsonm@redhat.com"></tt> for info and code for popup menus. +<item>David Huggins-Daines <tt><htmlurl url="mailto:bn711@freenet.carleton.ca" +name="bn711@freenet.carleton.ca"></tt> for the Range Widgets and Tree Widget +sections. + +<item>Stefan Mars <tt><htmlurl url="mailto:mars@lysator.liu.se" +name="mars@lysator.liu.se"></tt> for the GtkCList section </itemize> <p> And to all of you who commented and helped refine this document. @@ -11675,6 +12512,439 @@ not make any guarentee that the information is even accurate. <!-- ***************************************************************** --> <!-- ***************************************************************** --> +<sect> GDK Event Types<label id="sec_GDK_Event_Types"> +<!-- ***************************************************************** --> +<p> +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. + +<itemize> +<item> GdkEvent + <itemize> + <item>drag_end_event + </itemize> + +<item> GdkEventType + +<item> GdkEventAny + <itemize> + <item>delete_event + <item>destroy_event + <item>map_event + <item>unmap_event + <item>no_expose_event + </itemize> + +<item> GdkEventExpose + <itemize> + <item>expose_event + </itemize> + +<item> GdkEventNoExpose + +<item> GdkEventVisibility + +<item> GdkEventMotion + <itemize> + <item>motion_notify_event + </itemize> + +<item> GdkEventButton + <itemize> + <item>button_press_event + <item>button_release_event + </itemize> + +<item> GdkEventKey + <itemize> + <item>key_press_event + <item>key_release_event + </itemize> + +<item> GdkEventCrossing + <itemize> + <item>enter_notify_event + <item>leave_notify_event + </itemize> + +<item> GdkEventFocus + <itemize> + <item>focus_in_event + <item>focus_out_event + </itemize> + +<item> GdkEventConfigure + <itemize> + <item>configure_event + </itemize> + +<item> GdkEventProperty + <itemize> + <item>property_notify_event + </itemize> + +<item> GdkEventSelection + <itemize> + <item>selection_clear_event + <item>selection_request_event + <item>selection_notify_event + </itemize> + +<item> GdkEventProximity + <itemize> + <item>proximity_in_event + <item>proximity_out_event + </itemize> + +<item> GdkEventDragBegin + <itemize> + <item>drag_begin_event + </itemize> + +<item> GdkEventDragRequest + <itemize> + <item>drag_request_event + </itemize> + +<item> GdkEventDropEnter + <itemize> + <item>drop_enter_event + </itemize> + +<item> GdkEventDropLeave + <itemize> + <item>drop_leave_event + </itemize> + +<item> GdkEventDropDataAvailable + <itemize> + <item>drop_data_available_event + </itemize> + +<item> GdkEventClient + <itemize> + <item>client_event + </itemize> + +<item> GdkEventOther + <itemize> + <item>other_event + </itemize> +</itemize> + +The data type <tt/GdkEventType/ is a special data type that is used by +all the other data types as an indicator of the data type being passed +to the signal handler. As you will see below, each of the event data +structures has a member of this type. It is defined as an enumeration +type as follows: + +<tscreen><verb> +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; +</verb></tscreen> + +The other event type that is different from the others is +<tt/GdkEvent/ itself. This is a union of all the other +data types, which allows it to be cast to a specific +event data type within a signal handler. + +<!-- Just a big list for now, needs expanding upon - TRG --> +So, the event data types are defined as follows: + +<tscreen><verb> +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; +}; +</verb></tscreen> + +<!-- ***************************************************************** --> <sect> Code Examples <!-- ***************************************************************** --> <p> @@ -11682,10 +12952,12 @@ Below are the code examples that are used in the above text which are not included in complete form elsewhere. <!-- ----------------------------------------------------------------- --> -<sect1> Scribble +<sect1>Tictactoe +<!-- ----------------------------------------------------------------- --> +<sect2>tictactoe.h <p> <tscreen><verb> -/* example-start scribble-simple scribble-simple.c */ +/* example-start tictactoe tictactoe.h */ /* GTK - The GIMP Toolkit * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald @@ -11705,165 +12977,267 @@ which are not included in complete form elsewhere. * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ +#ifndef __TICTACTOE_H__ +#define __TICTACTOE_H__ -#include <gtk/gtk.h> -/* Backing pixmap for drawing area */ -static GdkPixmap *pixmap = NULL; +#include <gdk/gdk.h> +#include <gtk/gtkvbox.h> -/* Create a new backing pixmap of the appropriate size */ -static gint -configure_event (GtkWidget *widget, GdkEventConfigure *event) + +#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 { - if (pixmap) - gdk_pixmap_unref(pixmap); + GtkVBox vbox; + + GtkWidget *buttons[3][3]; +}; - 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); +struct _TictactoeClass +{ + GtkVBoxClass parent_class; - return TRUE; + void (* tictactoe) (Tictactoe *ttt); +}; + +guint tictactoe_get_type (void); +GtkWidget* tictactoe_new (void); +void tictactoe_clear (Tictactoe *ttt); + +#ifdef __cplusplus } +#endif /* __cplusplus */ -/* Redraw the screen from the backing pixmap */ -static gint -expose_event (GtkWidget *widget, GdkEventExpose *event) +#endif /* __TICTACTOE_H__ */ + +/* example-end */ +</verb></tscreen> + +<!-- ----------------------------------------------------------------- --> +<sect2>tictactoe.c +<p> +<tscreen><verb> +/* 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 () { - 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); + static guint ttt_type = 0; - return FALSE; + 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; } -/* Draw a rectangle on the screen */ static void -draw_brush (GtkWidget *widget, gdouble x, gdouble y) +tictactoe_class_init (TictactoeClass *class) { - GdkRectangle update_rect; + GtkObjectClass *object_class; - 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); -} + 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); -static gint -button_press_event (GtkWidget *widget, GdkEventButton *event) -{ - if (event->button == 1 && pixmap != NULL) - draw_brush (widget, event->x, event->y); - return TRUE; + gtk_object_class_add_signals (object_class, tictactoe_signals, LAST_SIGNAL); + + class->tictactoe = NULL; } -static gint -motion_notify_event (GtkWidget *widget, GdkEventMotion *event) +static void +tictactoe_init (Tictactoe *ttt) { - 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); + GtkWidget *table; + gint i,j; - return TRUE; + 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]); + } } -void -quit () +GtkWidget* +tictactoe_new () { - gtk_exit (0); + return GTK_WIDGET ( gtk_type_new (tictactoe_get_type ())); } -int -main (int argc, char *argv[]) +void +tictactoe_clear (Tictactoe *ttt) { - GtkWidget *window; - GtkWidget *drawing_area; - GtkWidget *vbox; - - GtkWidget *button; + int i,j; - gtk_init (&argc, &argv); + 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); + } +} - window = gtk_window_new (GTK_WINDOW_TOPLEVEL); - gtk_widget_set_name (window, "Test Input"); +static void +tictactoe_toggle (GtkWidget *widget, Tictactoe *ttt) +{ + int i,k; - vbox = gtk_vbox_new (FALSE, 0); - gtk_container_add (GTK_CONTAINER (window), vbox); - gtk_widget_show (vbox); + 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 } }; - gtk_signal_connect (GTK_OBJECT (window), "destroy", - GTK_SIGNAL_FUNC (quit), NULL); + int success, found; - /* Create the drawing area */ + for (k=0; k<8; k++) + { + success = TRUE; + found = FALSE; - 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); + 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; + } + } +} - gtk_widget_show (drawing_area); +/* example-end */ +</verb></tscreen> - /* Signals used to handle backing pixmap */ +<!-- ----------------------------------------------------------------- --> +<sect2>ttt_test.c +<p> +<tscreen><verb> +/* example-start tictactoe ttt_test.c */ - 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); +#include <gtk/gtk.h> +#include "tictactoe.h" - /* Event signals */ +void +win (GtkWidget *widget, gpointer data) +{ + g_print ("Yay!\n"); + tictactoe_clear (TICTACTOE (widget)); +} - 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); +int +main (int argc, char *argv[]) +{ + GtkWidget *window; + GtkWidget *ttt; + + gtk_init (&argc, &argv); - 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); + 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); - /* .. And a quit button */ - button = gtk_button_new_with_label ("Quit"); - gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0); + ttt = tictactoe_new (); + + gtk_container_add (GTK_CONTAINER (window), ttt); + gtk_widget_show (ttt); - gtk_signal_connect_object (GTK_OBJECT (button), "clicked", - GTK_SIGNAL_FUNC (gtk_widget_destroy), - GTK_OBJECT (window)); - gtk_widget_show (button); + gtk_signal_connect (GTK_OBJECT (ttt), "tictactoe", + GTK_SIGNAL_FUNC (win), NULL); gtk_widget_show (window); - + gtk_main (); - + return 0; } + /* example-end */ </verb></tscreen> @@ -12565,4 +13939,191 @@ gtk_dial_adjustment_value_changed (GtkAdjustment *adjustment, } /* example-end */ </verb></tscreen> + +<!-- ----------------------------------------------------------------- --> +<sect1> Scribble +<p> +<tscreen><verb> +/* 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 <gtk/gtk.h> + +/* 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 */ +</verb></tscreen> + </article> diff --git a/docs/tutorial/gtk_tut.sgml b/docs/tutorial/gtk_tut.sgml index d519ddbfc6..a948db7915 100644 --- a/docs/tutorial/gtk_tut.sgml +++ b/docs/tutorial/gtk_tut.sgml @@ -10,7 +10,7 @@ name="<imain@gtk.org>"></tt>, Tony Gale <tt><htmlurl url="mailto:gale@gtk.org" name="<gale@gtk.org>"></tt> -<date>July 25th, 1998 +<date>August 13th, 1998 <!-- ***************************************************************** --> <sect>Introduction @@ -482,14 +482,17 @@ static gint button_press_event (GtkWidget *widget, Note that we can declare the second argument as type <tt/GdkEventButton/ as we know what type of event will occur for this function to be called. -<!-- Need an Annex with all the event types in it - TRG --> - -<!-- Need to check this - TRG The value returned from this function indicates whether the event should -be processed further by the GTK event handling mechanism. Returning +be propagated further by the GTK event handling mechanism. Returning TRUE indicates that the event has been handled, and that it should not -propogate further. Returning FALSE continues the normal event handling. ---> +propagate further. Returning FALSE continues the normal event handling. +See the section on +<ref id="sec_Adv_Events_and_Signals" +name="Advanced Event and Signal Handling"> for more details on this +propagation process. + +For details on the GdkEvent data types, see the appendix entitled +<ref id="sec_GDK_Event_Types" name="GDK Event Types">. <!-- ----------------------------------------------------------------- --> <sect1>Stepping Through Hello World @@ -2086,6 +2089,775 @@ removes the need for a variable to hold the list of buttons: <!-- TODO: checout out gtk_radio_button_new_from_widget function - TRG --> <!-- ***************************************************************** --> +<sect>Range Widgets +<!-- ***************************************************************** --> +<p> +The category of range widgets includes the ubiquitous <em>scrollbar</em> +widget and the less common <em>scale</em> 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. + +<!-- ----------------------------------------------------------------- --> +<sect1>The Scale Widgets +<p> +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. + +<!-- ----------------------------------------------------------------- --> +<sect2>Creating a Scale Widget +<p> +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 +<tt><gtk/gtkvscale.h></tt> and +<tt><gtk/gtkhscale.h></tt>, create vertical and +horizontal scale widgets, respectively: + +<tscreen><verb> +GtkWidget* gtk_vscale_new( GtkAdjustment *adjustment ); + +GtkWidget* gtk_hscale_new( GtkAdjustment *adjustment ); +</verb></tscreen> + +<tt/adjustment/ can either be an adjustment which has +already been created with <tt/gtk_adjustment_new()/, or +<tt/NULL/, in which case, an anonymous GtkAdjustment is +created with all of its values set to <tt/0.0/. If you're +thoroughly confused by now, see <ref +id="sec_Range_GtkAdjustment" name="The Adjustment Object"> +below for an explanation of what exactly the <tt/adjustment/ +argument does and how to create and manipulate it. + +<!-- ----------------------------------------------------------------- --> +<sect2>Functions, Signals, and Macros +<p> +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: + +<tscreen><verb> +void gtk_scale_set_draw_value( GtkScale *scale, + gint draw_value ); +</verb></tscreen> + +As you might have guessed, <tt/draw_value/ is either +<tt/TRUE/ or <tt/FALSE/, with predictable consequences for +either one. + +The value displayed by a scale widget is rounded to one +decimal point by default (as is the <tt/value/ field in its +GtkAdjustment... but I digress). You can change this with: + +<tscreen><verb> +void gtk_scale_set_digits( GtkScale *scale, + gint digits); +</verb></tscreen> + +where <tt/digits/ is the number of decimal places you want. +You can set <tt/digits/ to anything you like, but no more +than 13 decimal places will actually be drawn on screen. +This probably isn't too horribly restrictive. + +Finally, the value can be drawn in different positions +relative to the trough: + +<tscreen><verb> +void gtk_scale_set_value_pos( GtkScale *scale, + GtkPositionType pos ); +</verb></tscreen> + +If you've read the section on the notebook widget, then you +know what the possible values of <tt/pos/ are. They are +defined as type <tt>GtkPositionType</tt> and can take one +of the following values: + +<itemize> +<item>GTK_POS_LEFT +<item>GTK_POS_RIGHT +<item>GTK_POS_TOP +<item>GTK_POS_BOTTOM +</itemize> + +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 +<tt><gtk/gtkscale.h></tt>. 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. + +<!-- ----------------------------------------------------------------- --> +<sect1>The Scrollbar Widgets +<p> +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: + +<tscreen><verb> +GtkWidget* gtk_hscrollbar_new( GtkAdjustment *adjustment ); + +GtkWidget* gtk_vscrollbar_new( GtkAdjustment *adjustment ); +</verb></tscreen> + +and that's about it (if you don't believe me, look in the +header files!). Again, <tt/adjustment/ can either be a +pointer to an existing GtkAdjustment, or NULL, in which case +one will be created for you. + +<!-- ----------------------------------------------------------------- --> +<sect1>The Adjustment Object<label id="sec_Range_GtkAdjustment"> +<p> +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 +<tt/adjustment/ object. + +Every range widget contains a pointer to a GtkAdjustment +object. You'll usually create one of these in order to pass +it to the <tt/gtk_*_new()/ function which creates a range +widget, or some compound widget that uses range widgets, such +as GtkScrolledWindow or GtkCList. + +Aside from specifying some characteristics related to the +range widget's appearance and behaviour, the GtkAdjustment you +pass to this function becomes "attached" to the newly-created +range widget and from that point on will always contain the +numerical value corresponding to the position of the slider +(unless, at some point in the future, you set a new adjustment +for the range widget). + +One adjustment object can be shared between many range +widgets. Reusing the same adjustment object across several +range widgets will cause them all to change when one of them +is changed. + +<!-- ----------------------------------------------------------------- --> +<sect2>Creating a GtkAdjustment +<p> +You create an adjustment using: + +<tscreen><verb> +GtkObject* gtk_adjustment_new( gfloat value, + gfloat lower, + gfloat upper, + gfloat step_increment, + gfloat page_increment, + gfloat page_size ); +</verb></tscreen> + +It may or may not be obvious by now that the values given to +<tt/gtk_adjustment_new()/ are simply arbitrary floating-point +values. The mapping between these values and the on-screen +size of the range widget and its constituent parts is +handled by the range widget. Thus, you're free to use +whatever numbers are most meaningful to your program. + +The <tt/value/ argument is the initial value you want to +give to the adjustment. The <tt/lower/ argument specifies +the lowest value which the adjustment can hold, or, in other +words, the lowest value which the user can select using the +range widget which uses this adjustment. The +<tt/step_increment/ argument specifies the "smaller" of the +two increments by which the user can change the value, while +the <tt/page_increment/ is the "larger" one. <ref +id="sec_Range_Bindings" name="Key and Mouse Bindings"> below +describes the default key and mouse bindings for range +widgets, and how they relate to these increments. The +<tt/page_size/ argument is only relevant for scrollbars. +Its most obvious effect is that it determines the size of +the slider; however, you should set it based on the "size" +of the visible area of whatever you're scrolling. + +As an example, say you're writing a text editor. You might +want to have the value of the vertical scrollbar beside the +editing area correspond to the line number +of the first visible line in the editing area. In that +case, you might call <tt/gtk_adjustment_new()/ like this: + +<tscreen><verb> +GtkObject *adj; + +adj = gtk_adjustment_new (0, first_line, last_line, 1, + window_height - 2, window_height); +</verb></tscreen> + +where <tt/window_height/ is the number of visible lines in +the window. + +Finally, with regard to the <tt/upper/ argument to +<tt/gtk_adjustment_new/, you'll notice that, since the value +of the adjustment corresponds to the <em/first/ visible line +in the window, the maximum value in the adjustment is not +actually <tt/last_line/, but rather <tt>last_line - +window_height</tt> (or, in more general terms, <tt>upper - +page_height</tt>). 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 +<tt/page_size/ to <tt/0.0/ for adjustments that are only +going to be used for scale widgets. + +<!-- ----------------------------------------------------------------- --> +<sect2>Inside the GtkAdjustment object +<p> +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 <tt/struct _GtkAdjustment/ itself: + +<tscreen><verb> +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); +}; +</verb></tscreen> + +The first thing you should know is that there aren't any +handy-dandy macros or accessor functions for getting the +<tt/value/ out of a GtkAdjustment, so you'll have to (horror +of horrors) do it like a <em/real/ C programmer. Don't +worry - the <tt>GTK_ADJUSTMENT (Object)</tt> 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 +<em/set/ these fields directly. To set <tt/value/, you can +use: + +<tscreen><verb> +void gtk_adjustment_set_value( GtkAdjustment *adjustment, + gfloat value ); +</verb></tscreen> + +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 +<tt/gtk_range_set_adjustment()/, as detailed in <ref +id="sec_Range_Functions" name="Common Functions, Signals, +and Macros"> 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 <ref id="sec_Range_UpdatePolicy" +name="Update Policies"> 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 <tt/gtk_adjustment_set_value()/. So, for +example, if you have a scale widget, and you want to change +the rotation of a picture whenever its value changes, you +would create a callback like this: + +<tscreen><verb> +void cb_rotate_picture (GtkAdjustment *adj, GtkWidget *picture) +{ + set_picture_rotation (picture, adj->value); +... +</verb></tscreen> + +and connect it to the scale widget's adjustment like this: + +<tscreen><verb> +gtk_signal_connect (GTK_OBJECT (adj), "value_changed", + GTK_SIGNAL_FUNC (cb_rotate_picture), picture); +</verb></tscreen> + +The "changed" signal is somewhat more elusive. It is never +emitted directly due to the <em/user's/ actions. Rather, +programs or other widgets should emit it on a GtkAdjustment +when they modify any of its fields directly. This will +force any range widgets that use this adjustment to +recalculate and redraw if necessary. This is useful if you +have a number of range widgets using the same GtkAdjustment, +and don't want to call <tt/gtk_range_set_adjustment()/ for +all of them. It's also handy if you are going to be +continuously changing these values, such as in our +hypothetical text editor, where the <tt/upper/ field will +have to change every time a new line is added, and you don't +want the extra overhead of creating a new GtkAdjustment +object every time. + +<!-- ----------------------------------------------------------------- --> +<sect1>Common Functions, Signals, and Macros<label id="sec_Range_Functions"> +<p> +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. + +<!-- ----------------------------------------------------------------- --> +<sect2>Update Policies<label id="sec_Range_UpdatePolicy"> +<p> +The "update policy" of a range widget defines at what points +during user interaction it will change the <tt/value/ field +of its GtkAdjustment and emit the "value_changed" signal on +this GtkAdjustment. The update policies, defined in +<tt><gtk/gtkenums.h></tt> as the <tt>enum +GtkUpdateType</tt>, are: + +<itemize> +<item>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. +</item> + +<item>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. +</item> + +<item>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. +</item> +</itemize> + +The update policy of a range widget can be set by casting it +using the <tt>GTK_RANGE (Widget)</tt> macro and passing it +to this function: + +<tscreen><verb> +void gtk_range_set_update_policy( GtkRange *range, + GtkUpdateType policy ); +</verb></tscreen> + +<!-- ----------------------------------------------------------------- --> +<sect2>Getting and setting adjustments +<p> +Getting and setting the adjustment for a range widget "on +the fly" is done, predictably, with: + +<tscreen><verb> +GtkAdjustment* gtk_range_get_adjustment( GtkRange *range ); + +void gtk_range_set_adjustment( GtkRange *range, + GtkAdjustment *adjustment ); +</verb> +</tscreen> + +<tt/gtk_range_get_adjustment()/ returns a pointer to the +adjustment to which <tt/range/ is connected. + +<tt/gtk_range_set_adjustment()/ does absolutely nothing if +you pass it the adjustment that <tt/range/ is already using, +regardless of whether you changed any of its fields or not. +If you pass it a new GtkAdjustment, it will unreference the +old one if it exists (possibly destroying it), connect the +appropriate signals to the new one, and call the private +function <tt/gtk_range_adjustment_changed()/, which will (or +at least, is supposed to...) recalculate the size and/or +position of the slider and redraw if necessary. As +mentioned above, if you wish to reuse the same +GtkAdjustment, when you modify its values directly, you +should emit the "changed" signal on it, like this: + +<tscreen><verb> +gtk_signal_emit_by_name (GTK_OBJECT (adjustment), "changed"); +</verb></tscreen> + +<!-- ----------------------------------------------------------------- --> +<sect1>Key and Mouse bindings<label id="sec_Range_Bindings"> +<p> +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 <tt/page_increment/ to be added or +subtracted from its <tt/value/, and the slider to be moved +accordingly. Clicking button 2 in the trough will jump the +slider to the point at which the button was clicked. +Clicking any button on a scrollbar's arrows will cause its +adjustment's value to change <tt/step_increment/ at a time. + +The key bindings, by contrast, are slightly different +between horizontal and vertical range widgets, for obvious +reasons. They are also not quite the same for scale widgets +as they are for scrollbars, for somewhat less obvious +reasons (possibly to avoid confusion between the keys for +horizontal and vertical scrollbars in scrolled windows, +where both operate on the same area). + +<!-- ----------------------------------------------------------------- --> +<sect2>Vertical Range Widgets +<p> +All vertical range widgets can be operated with the up and +down arrow keys, as well as with the <tt/Page Up/ and +<tt/Page Down/ keys. The arrows move the slider up and +down by <tt/step_increment/, while <tt/Page Up/ and +<tt/Page Down/ move it by <tt/page_increment/. + +The user can also move the slider all the way to one end +or the other of the trough using the keyboard. With the +GtkVScale widget, this is done with the <tt/Home/ and +<tt/End/ keys, whereas with the GtkVScrollbar widget, this +is done by typing <tt>Control-Page Up</tt> and +<tt>Control-Page Down</tt>. + +<!-- ----------------------------------------------------------------- --> +<sect2>Horizontal Range Widgets +<p> +The left and right arrow keys work as you might expect in +these widgets, moving the slider back and forth by +<tt/step_increment/. The <tt/Home/ and <tt/End/ keys move +the slider to the ends of the trough. For the GtkHScale +widget, moving the slider by <tt/page_increment/ is +accomplished with <tt>Control-Left</tt> and +<tt>Control-Right</tt>, while for GtkHScrollbar, it's done +with <tt>Control-Home</tt> and <tt>Control-End</tt>. + +<!-- ----------------------------------------------------------------- --> +<sect1>Example<label id="sec_Range_Example"></heading> +<p> +This example is a somewhat modified version of the "range +widgets" test from <tt/testgtk.c/. It basically puts up a +window with three range widgets all connected to the same +adjustment, and a couple of controls for adjusting some of the +parameters for scale widgets mentioned above, so you can see +how they affect the way these widgets work for the user. + +<tscreen><verb> +/* example-start rangewidgets rangewidgets.c */ + +#include <gtk/gtk.h> + +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 */ +</verb></tscreen> + +<!-- ***************************************************************** --> <sect> Miscallaneous Widgets <!-- ***************************************************************** --> @@ -6328,7 +7100,7 @@ tree, and connects all the signals for the relevant objects, so you can see when they are emitted. <tscreen><verb> -/* example-start tree tree.c */ +/* example-start tree tree.h */ #include <gtk/gtk.h> @@ -6872,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 */ </verb></tscreen> @@ -6893,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 @@ -6910,131 +7678,39 @@ void menus_create(GtkMenuEntry * entries, int nmenu_entries); static GtkMenuEntry menu_items[] = { - {"<Main>/File/New", "<control>N", NULL, NULL}, - {"<Main>/File/Open", "<control>O", NULL, NULL}, - {"<Main>/File/Save", "<control>S", NULL, NULL}, - {"<Main>/File/Save as", NULL, NULL, NULL}, - {"<Main>/File/<separator>", NULL, NULL, NULL}, - {"<Main>/File/Quit", "<control>Q", file_quit_cmd_callback, "OK, I'll quit"}, - {"<Main>/Options/Test", NULL, NULL, NULL} + {"<Main>/File/New", "<control>N", print_hello, NULL}, + {"<Main>/File/Open", "<control>O", print_hello, NULL}, + {"<Main>/File/Save", "<control>S", print_hello, NULL}, + {"<Main>/File/Save as", NULL, NULL, NULL}, + {"<Main>/File/<separator>", NULL, NULL, NULL}, + {"<Main>/File/Quit", "<control>Q", file_quit_cmd_callback, "OK, I'll quit"}, + {"<Main>/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) -{ - if (initialize) - menus_init(); - - if (menubar) - *menubar = subfactory[0]->widget; - if (table) - *table = subfactory[0]->table; -} - -void menus_init(void) +static void +print_hello(GtkWidget *widget, gpointer data) { - 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], "<Main>"); - menus_create(menu_items, nmenu_items); - } + printf("hello!\n"); } -void menus_create(GtkMenuEntry * entries, int nmenu_entries) +void get_main_menu(GtkWidget *window, GtkWidget ** menubar) { - 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); - } -} + int nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]); + GtkMenuFactory *factory; + GtkMenuFactory *subfactory; -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, "<control>"); - if (modifiers & GDK_SHIFT_MASK) - strcat(accel, "<shift>"); - if (modifiers & GDK_MOD1_MASK) - strcat(accel, "<alt>"); - - 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; -} + factory = gtk_menu_factory_new(GTK_MENU_FACTORY_MENU_BAR); + subfactory = gtk_menu_factory_new(GTK_MENU_FACTORY_MENU_BAR); -static void menus_remove_accel(GtkWidget * widget, gchar * signal_name, gchar * path) -{ - char *t; + gtk_menu_factory_add_subfactory(factory, subfactory, "<Main>"); + gtk_menu_factory_add_entries(factory, menu_items, nmenu_items); + gtk_window_add_accelerator_table(GTK_WINDOW(window), subfactory->table); - if (entry_ht) { - t = g_hash_table_lookup(entry_ht, path); - g_free(t); - - g_hash_table_insert(entry_ht, path, g_strdup("")); - } + if (menubar) + *menubar = subfactory->widget; } -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 */ </verb></tscreen> @@ -7058,6 +7734,7 @@ void file_quit_cmd_callback(GtkWidget *widget, gpointer data); #endif /* __cplusplus */ #endif /* __MFMAIN_H__ */ + /* example-end */ </verb></tscreen> @@ -7071,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); @@ -7094,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); @@ -7114,6 +7787,7 @@ void file_quit_cmd_callback (GtkWidget *widget, gpointer data) g_print ("%s\n", (char *) data); gtk_exit(0); } + /* example-end */ </verb></tscreen> @@ -7572,18 +8246,12 @@ widget, please consider writing a tutorial on it so others may benifit from your time. <!-- ----------------------------------------------------------------- --> -<sect1> Adjustments -<p> -<!-- ----------------------------------------------------------------- --> <sect1> Toolbar <p> <!-- ----------------------------------------------------------------- --> <sect1> Fixed Container <p> <!-- ----------------------------------------------------------------- --> -<sect1> Range Controls -<p> -<!-- ----------------------------------------------------------------- --> <sect1> Curves <p> <!-- ----------------------------------------------------------------- --> @@ -8283,6 +8951,169 @@ gtk_idle_add will be called whenever the opportunity arises. As with the others, returning FALSE will stop the idle function from being called. <!-- ***************************************************************** --> +<sect>Advanced Event and Signal Handling<label id="sec_Adv_Events_and_Signals"> +<!-- ***************************************************************** --> + +<!-- ----------------------------------------------------------------- --> +<sect1>Signal Functions + +<!-- ----------------------------------------------------------------- --> +<sect2>Connecting and Disconnecting Signal Handlers +<p> + +<tscreen><verb> +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 ); +</verb></tscreen> + +<!-- ----------------------------------------------------------------- --> +<sect2>Blocking and Unblocking Signal Handlers +<p> +<tscreen><verb> +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 ); +</verb></tscreen> + +<!-- ----------------------------------------------------------------- --> +<sect2>Emitting and Stopping Signals +<p> +<tscreen><verb> +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 ); +</verb></tscreen> + +<!-- ----------------------------------------------------------------- --> +<sect1>Signal Emission and Propagation +<p> +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 <em>last</em> 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: +<itemize> +<item>Start with the widget where the event occured. + +<item>Emit the generic "event" signal. If that signal handler returns +a value of TRUE, stop all processing. + +<item>Otherwise, emit a specific, "button_press_event" signal. If that +returns TRUE, stop all processing. + +<item>Otherwise, go to the widget's parent, and repeat the above steps. + +<item>Contimue until some signal handler returns TRUE, or until the +top-level widget is reached. +</itemize> + +Some consequences of the above are: +<itemize> +<item>Your handler's return value will have no effect if there is a +default handler, unless you connect with gtk_signal_connect_after(). + +<item>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. +</itemize> + +<!-- ***************************************************************** --> <sect>Managing Selections <!-- ***************************************************************** --> @@ -11636,6 +12467,12 @@ name="rajat@ix.netcom.com"</tt> for the excellent job on the Pixmap tutorial. <item>Michael K. Johnson <tt><htmlurl url="mailto:johnsonm@redhat.com" name="johnsonm@redhat.com"></tt> for info and code for popup menus. +<item>David Huggins-Daines <tt><htmlurl url="mailto:bn711@freenet.carleton.ca" +name="bn711@freenet.carleton.ca"></tt> for the Range Widgets and Tree Widget +sections. + +<item>Stefan Mars <tt><htmlurl url="mailto:mars@lysator.liu.se" +name="mars@lysator.liu.se"></tt> for the GtkCList section </itemize> <p> And to all of you who commented and helped refine this document. @@ -11675,6 +12512,439 @@ not make any guarentee that the information is even accurate. <!-- ***************************************************************** --> <!-- ***************************************************************** --> +<sect> GDK Event Types<label id="sec_GDK_Event_Types"> +<!-- ***************************************************************** --> +<p> +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. + +<itemize> +<item> GdkEvent + <itemize> + <item>drag_end_event + </itemize> + +<item> GdkEventType + +<item> GdkEventAny + <itemize> + <item>delete_event + <item>destroy_event + <item>map_event + <item>unmap_event + <item>no_expose_event + </itemize> + +<item> GdkEventExpose + <itemize> + <item>expose_event + </itemize> + +<item> GdkEventNoExpose + +<item> GdkEventVisibility + +<item> GdkEventMotion + <itemize> + <item>motion_notify_event + </itemize> + +<item> GdkEventButton + <itemize> + <item>button_press_event + <item>button_release_event + </itemize> + +<item> GdkEventKey + <itemize> + <item>key_press_event + <item>key_release_event + </itemize> + +<item> GdkEventCrossing + <itemize> + <item>enter_notify_event + <item>leave_notify_event + </itemize> + +<item> GdkEventFocus + <itemize> + <item>focus_in_event + <item>focus_out_event + </itemize> + +<item> GdkEventConfigure + <itemize> + <item>configure_event + </itemize> + +<item> GdkEventProperty + <itemize> + <item>property_notify_event + </itemize> + +<item> GdkEventSelection + <itemize> + <item>selection_clear_event + <item>selection_request_event + <item>selection_notify_event + </itemize> + +<item> GdkEventProximity + <itemize> + <item>proximity_in_event + <item>proximity_out_event + </itemize> + +<item> GdkEventDragBegin + <itemize> + <item>drag_begin_event + </itemize> + +<item> GdkEventDragRequest + <itemize> + <item>drag_request_event + </itemize> + +<item> GdkEventDropEnter + <itemize> + <item>drop_enter_event + </itemize> + +<item> GdkEventDropLeave + <itemize> + <item>drop_leave_event + </itemize> + +<item> GdkEventDropDataAvailable + <itemize> + <item>drop_data_available_event + </itemize> + +<item> GdkEventClient + <itemize> + <item>client_event + </itemize> + +<item> GdkEventOther + <itemize> + <item>other_event + </itemize> +</itemize> + +The data type <tt/GdkEventType/ is a special data type that is used by +all the other data types as an indicator of the data type being passed +to the signal handler. As you will see below, each of the event data +structures has a member of this type. It is defined as an enumeration +type as follows: + +<tscreen><verb> +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; +</verb></tscreen> + +The other event type that is different from the others is +<tt/GdkEvent/ itself. This is a union of all the other +data types, which allows it to be cast to a specific +event data type within a signal handler. + +<!-- Just a big list for now, needs expanding upon - TRG --> +So, the event data types are defined as follows: + +<tscreen><verb> +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; +}; +</verb></tscreen> + +<!-- ***************************************************************** --> <sect> Code Examples <!-- ***************************************************************** --> <p> @@ -11682,10 +12952,12 @@ Below are the code examples that are used in the above text which are not included in complete form elsewhere. <!-- ----------------------------------------------------------------- --> -<sect1> Scribble +<sect1>Tictactoe +<!-- ----------------------------------------------------------------- --> +<sect2>tictactoe.h <p> <tscreen><verb> -/* example-start scribble-simple scribble-simple.c */ +/* example-start tictactoe tictactoe.h */ /* GTK - The GIMP Toolkit * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald @@ -11705,165 +12977,267 @@ which are not included in complete form elsewhere. * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ +#ifndef __TICTACTOE_H__ +#define __TICTACTOE_H__ -#include <gtk/gtk.h> -/* Backing pixmap for drawing area */ -static GdkPixmap *pixmap = NULL; +#include <gdk/gdk.h> +#include <gtk/gtkvbox.h> -/* Create a new backing pixmap of the appropriate size */ -static gint -configure_event (GtkWidget *widget, GdkEventConfigure *event) + +#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 { - if (pixmap) - gdk_pixmap_unref(pixmap); + GtkVBox vbox; + + GtkWidget *buttons[3][3]; +}; - 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); +struct _TictactoeClass +{ + GtkVBoxClass parent_class; - return TRUE; + void (* tictactoe) (Tictactoe *ttt); +}; + +guint tictactoe_get_type (void); +GtkWidget* tictactoe_new (void); +void tictactoe_clear (Tictactoe *ttt); + +#ifdef __cplusplus } +#endif /* __cplusplus */ -/* Redraw the screen from the backing pixmap */ -static gint -expose_event (GtkWidget *widget, GdkEventExpose *event) +#endif /* __TICTACTOE_H__ */ + +/* example-end */ +</verb></tscreen> + +<!-- ----------------------------------------------------------------- --> +<sect2>tictactoe.c +<p> +<tscreen><verb> +/* 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 () { - 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); + static guint ttt_type = 0; - return FALSE; + 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; } -/* Draw a rectangle on the screen */ static void -draw_brush (GtkWidget *widget, gdouble x, gdouble y) +tictactoe_class_init (TictactoeClass *class) { - GdkRectangle update_rect; + GtkObjectClass *object_class; - 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); -} + 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); -static gint -button_press_event (GtkWidget *widget, GdkEventButton *event) -{ - if (event->button == 1 && pixmap != NULL) - draw_brush (widget, event->x, event->y); - return TRUE; + gtk_object_class_add_signals (object_class, tictactoe_signals, LAST_SIGNAL); + + class->tictactoe = NULL; } -static gint -motion_notify_event (GtkWidget *widget, GdkEventMotion *event) +static void +tictactoe_init (Tictactoe *ttt) { - 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); + GtkWidget *table; + gint i,j; - return TRUE; + 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]); + } } -void -quit () +GtkWidget* +tictactoe_new () { - gtk_exit (0); + return GTK_WIDGET ( gtk_type_new (tictactoe_get_type ())); } -int -main (int argc, char *argv[]) +void +tictactoe_clear (Tictactoe *ttt) { - GtkWidget *window; - GtkWidget *drawing_area; - GtkWidget *vbox; - - GtkWidget *button; + int i,j; - gtk_init (&argc, &argv); + 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); + } +} - window = gtk_window_new (GTK_WINDOW_TOPLEVEL); - gtk_widget_set_name (window, "Test Input"); +static void +tictactoe_toggle (GtkWidget *widget, Tictactoe *ttt) +{ + int i,k; - vbox = gtk_vbox_new (FALSE, 0); - gtk_container_add (GTK_CONTAINER (window), vbox); - gtk_widget_show (vbox); + 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 } }; - gtk_signal_connect (GTK_OBJECT (window), "destroy", - GTK_SIGNAL_FUNC (quit), NULL); + int success, found; - /* Create the drawing area */ + for (k=0; k<8; k++) + { + success = TRUE; + found = FALSE; - 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); + 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; + } + } +} - gtk_widget_show (drawing_area); +/* example-end */ +</verb></tscreen> - /* Signals used to handle backing pixmap */ +<!-- ----------------------------------------------------------------- --> +<sect2>ttt_test.c +<p> +<tscreen><verb> +/* example-start tictactoe ttt_test.c */ - 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); +#include <gtk/gtk.h> +#include "tictactoe.h" - /* Event signals */ +void +win (GtkWidget *widget, gpointer data) +{ + g_print ("Yay!\n"); + tictactoe_clear (TICTACTOE (widget)); +} - 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); +int +main (int argc, char *argv[]) +{ + GtkWidget *window; + GtkWidget *ttt; + + gtk_init (&argc, &argv); - 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); + 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); - /* .. And a quit button */ - button = gtk_button_new_with_label ("Quit"); - gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0); + ttt = tictactoe_new (); + + gtk_container_add (GTK_CONTAINER (window), ttt); + gtk_widget_show (ttt); - gtk_signal_connect_object (GTK_OBJECT (button), "clicked", - GTK_SIGNAL_FUNC (gtk_widget_destroy), - GTK_OBJECT (window)); - gtk_widget_show (button); + gtk_signal_connect (GTK_OBJECT (ttt), "tictactoe", + GTK_SIGNAL_FUNC (win), NULL); gtk_widget_show (window); - + gtk_main (); - + return 0; } + /* example-end */ </verb></tscreen> @@ -12565,4 +13939,191 @@ gtk_dial_adjustment_value_changed (GtkAdjustment *adjustment, } /* example-end */ </verb></tscreen> + +<!-- ----------------------------------------------------------------- --> +<sect1> Scribble +<p> +<tscreen><verb> +/* 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 <gtk/gtk.h> + +/* 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 */ +</verb></tscreen> + </article> diff --git a/examples/menu/menufactory.c b/examples/menu/menufactory.c index 9f72e1e925..a7f7819594 100644 --- a/examples/menu/menufactory.c +++ b/examples/menu/menufactory.c @@ -1,17 +1,11 @@ -/* This file extracted from the GTK tutorial. */ - -/* menufactory.c */ +/* example-start menu menufactory.c */ #include <gtk/gtk.h> #include <strings.h> #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 @@ -24,128 +18,37 @@ void menus_create(GtkMenuEntry * entries, int nmenu_entries); static GtkMenuEntry menu_items[] = { - {"<Main>/File/New", "<control>N", NULL, NULL}, - {"<Main>/File/Open", "<control>O", NULL, NULL}, - {"<Main>/File/Save", "<control>S", NULL, NULL}, - {"<Main>/File/Save as", NULL, NULL, NULL}, - {"<Main>/File/<separator>", NULL, NULL, NULL}, - {"<Main>/File/Quit", "<control>Q", file_quit_cmd_callback, "OK, I'll quit"}, - {"<Main>/Options/Test", NULL, NULL, NULL} + {"<Main>/File/New", "<control>N", print_hello, NULL}, + {"<Main>/File/Open", "<control>O", print_hello, NULL}, + {"<Main>/File/Save", "<control>S", print_hello, NULL}, + {"<Main>/File/Save as", NULL, NULL, NULL}, + {"<Main>/File/<separator>", NULL, NULL, NULL}, + {"<Main>/File/Quit", "<control>Q", file_quit_cmd_callback, "OK, I'll quit"}, + {"<Main>/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(); - - if (menubar) - *menubar = subfactory[0]->widget; - if (table) - *table = subfactory[0]->table; + printf("hello!\n"); } -void menus_init(void) +void get_main_menu(GtkWidget *window, GtkWidget ** menubar) { - 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], "<Main>"); - menus_create(menu_items, nmenu_items); - } -} + int nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]); + GtkMenuFactory *factory; + GtkMenuFactory *subfactory; -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); - } -} + factory = gtk_menu_factory_new(GTK_MENU_FACTORY_MENU_BAR); + subfactory = gtk_menu_factory_new(GTK_MENU_FACTORY_MENU_BAR); -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, "<control>"); - if (modifiers & GDK_SHIFT_MASK) - strcat(accel, "<shift>"); - if (modifiers & GDK_MOD1_MASK) - strcat(accel, "<alt>"); + gtk_menu_factory_add_subfactory(factory, subfactory, "<Main>"); + gtk_menu_factory_add_entries(factory, menu_items, nmenu_items); + gtk_window_add_accelerator_table(GTK_WINDOW(window), subfactory->table); - 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("")); - } + if (menubar) + *menubar = subfactory->widget; } -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 */ diff --git a/examples/menu/menufactory.h b/examples/menu/menufactory.h index e1569dae4c..acd04684e8 100644 --- a/examples/menu/menufactory.h +++ b/examples/menu/menufactory.h @@ -1,6 +1,4 @@ -/* This file extracted from the GTK tutorial. */ - -/* menufactory.h */ +/* example-start menu menufactory.h */ #ifndef __MENUFACTORY_H__ #define __MENUFACTORY_H__ @@ -9,11 +7,12 @@ 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 */ diff --git a/examples/menu/mfmain.c b/examples/menu/mfmain.c index cbe0c58408..5777632ee9 100644 --- a/examples/menu/mfmain.c +++ b/examples/menu/mfmain.c @@ -1,21 +1,16 @@ -/* This file extracted from the GTK tutorial. */ - -/* mfmain.c */ +/* example-start menu mfmain.c */ #include <gtk/gtk.h> #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); @@ -30,8 +25,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); @@ -50,3 +44,5 @@ void file_quit_cmd_callback (GtkWidget *widget, gpointer data) g_print ("%s\n", (char *) data); gtk_exit(0); } + +/* example-end */ diff --git a/examples/menu/mfmain.h b/examples/menu/mfmain.h index fe481b0c1f..83fc0e3a48 100644 --- a/examples/menu/mfmain.h +++ b/examples/menu/mfmain.h @@ -1,6 +1,4 @@ -/* This file extracted from the GTK tutorial. */ - -/* mfmain.h */ +/* example-start menu mfmain.h */ #ifndef __MFMAIN_H__ #define __MFMAIN_H__ @@ -17,3 +15,5 @@ void file_quit_cmd_callback(GtkWidget *widget, gpointer data); #endif /* __cplusplus */ #endif /* __MFMAIN_H__ */ + +/* 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 <gtk/gtk.h> + +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 */ |