summaryrefslogtreecommitdiff
path: root/docs/tutorial
diff options
context:
space:
mode:
authorPST 1998 Shawn T. Amundson <amundson@gimp.org>1998-03-12 18:23:11 +0000
committerShawn Amundson <amundson@src.gnome.org>1998-03-12 18:23:11 +0000
commitaffaf4f9d265522c71f1a9ba4b359094c00dc18a (patch)
tree2ed9182335ff959014320e46f7b1e1b76d27c39a /docs/tutorial
parent4f3495f955fb0798c18325c4d061891019e6b4f5 (diff)
downloadgdk-pixbuf-affaf4f9d265522c71f1a9ba4b359094c00dc18a.tar.gz
I just remembered this commit failed before because
of problems with the cvs server... connection timed out. Wed Mar 11 14:36:48 PST 1998 Shawn T. Amundson <amundson@gimp.org> * gtk/docs/: added tutorial, changed some files around to make more sense.
Diffstat (limited to 'docs/tutorial')
-rw-r--r--docs/tutorial/gtk_tut.sgml9092
-rw-r--r--docs/tutorial/gtk_tut_it.sgml8340
2 files changed, 17432 insertions, 0 deletions
diff --git a/docs/tutorial/gtk_tut.sgml b/docs/tutorial/gtk_tut.sgml
new file mode 100644
index 000000000..1406c5e5d
--- /dev/null
+++ b/docs/tutorial/gtk_tut.sgml
@@ -0,0 +1,9092 @@
+<!doctype linuxdoc system>
+
+<!-- This is the tutorial marked up in SGML
+ (just to show how to write a comment)
+-->
+
+<article>
+<title>GTK Tutorial
+<author>Ian Main <tt><htmlurl url="mailto:slow@intergate.bc.ca"
+ name="&lt;slow@intergate.bc.ca&gt;"></tt>,
+Tony Gale <tt><htmlurl url="mailto:gale@gimp.org"
+ name="&lt;gale@gimp.org&gt;"></tt
+<date>March 8th, 1998
+
+<!-- ***************************************************************** -->
+<sect>Introduction
+<!-- ***************************************************************** -->
+<p>
+GTK (GIMP Toolkit) was originally developed as a toolkit for the GIMP
+(General Image Manipulation Program). GTK is built on top of GDK (GIMP
+Drawing Kit) which is basically wrapper around the Xlib functions. It's
+called the GIMP toolkit because it was original written for developing
+the GIMP, but has now been used in several free software projects. The
+authors are
+<itemize>
+<item> Peter Mattis <tt><htmlurl url="mailto:petm@xcf.berkeley.edu"
+ name="petm@xcf.berkeley.edu"></tt>
+<item> Spencer Kimball <tt><htmlurl url="mailto:spencer@xcf.berkeley.edu"
+ name="spencer@xcf.berkeley.edu"></tt>
+<item> Josh MacDonald <tt><htmlurl url="mailto:jmacd@xcf.berkeley.edu"
+ name="jmacd@xcf.berkeley.edu"></tt>
+</itemize>
+
+<p>
+GTK is essentially an object oriented application programmers interface (API).
+Although written completely in
+C, it is implemented using the idea of classes and callback functions
+(pointers to functions).
+<p>
+There is also a third component called glib which contains a few
+replacements for some standard calls, as well as some additional functions
+for handling linked lists etc. The replacement functions are used to
+increase GTK's portability, as some of the functions implemented
+here are not available or are nonstandard on other unicies such as
+g_strerror(). Some also contain enhancements to the libc versions such as
+g_malloc has enhanced debugging utilities.
+<p>
+This tutorial is an attempt to document as much as possible of GTK, it is by
+no means complete. This
+tutorial assumes a good understanding of C, and how to create C programs.
+It would be a great benefit for the reader to have previous X programming
+experience, but it shouldn't be necessary. If you are learning GTK as your
+first widget set, please comment on how you found this tutorial, and what
+you had troubles with.
+Note that there is also a C++ API for GTK (GTK--) in the works, so if you
+prefer to use C++, you should look into this instead. There's also an
+Objective C wrapper, and guile bindings available, but I don't follow these.
+<p>
+I would very much like to hear any problems you have learning GTK from this
+document, and would appreciate input as to how it may be improved.
+
+<!-- ***************************************************************** -->
+<sect>Getting Started
+<!-- ***************************************************************** -->
+
+<p>
+The first thing to do of course, is download the GTK source and install
+it. You can always get the latest version from ftp.gimp.org in /pub/gtk.
+You can also view other sources of GTK information on http://www.gimp.org/gtk
+GTK uses GNU autoconf for
+configuration. Once untar'd, type ./configure --help to see a list of options.
+<p>
+To begin our introduction to GTK, we'll start with the simplest program
+possible. This program will
+create a 200x200 pixel window and has no way of exiting except to be
+killed using the shell.
+
+<tscreen><verb>
+#include <gtk/gtk.h>
+
+int main (int argc, char *argv[])
+{
+ GtkWidget *window;
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_widget_show (window);
+
+ gtk_main ();
+
+ return 0;
+}
+</verb></tscreen>
+
+All programs will of course include the gtk/gtk.h which declares the
+variables, functions, structures etc. that will be used in your GTK
+application.
+<p>
+The next line:
+
+<tscreen><verb>
+gtk_init (&amp;argc, &amp;argv);
+</verb></tscreen>
+
+calls the function gtk_init(gint *argc, gchar ***argv) which will be
+called in all GTK applications. This sets up a few things for us such
+as the default visual and color map and then proceeds to call
+gdk_init(gint *argc, gchar ***argv). This function initializes the
+library for use, sets up default signal handlers, and checks the
+arguments passed to your application on the command line, looking for one
+of the following:
+
+<itemize>
+<item> <tt/--display/
+<item> <tt/--debug-level/
+<item> <tt/--no-xshm/
+<item> <tt/--sync/
+<item> <tt/--show-events/
+<item> <tt/--no-show-events/
+</itemize>
+<p>
+It removes these from the argument list, leaving anything it does
+not recognize for your application to parse or ignore. This creates a set
+of standard arguments excepted by all GTK applications.
+<p>
+The next two lines of code create and display a window.
+
+<tscreen><verb>
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_widget_show (window);
+</verb></tscreen>
+
+The GTK_WINDOW_TOPLEVEL argument specifies that we want the window to
+undergo window manager decoration and placement. Rather than create a
+window of 0x0 size, a window without children is set to 200x200 by default
+so you can still manipulate it.
+<p>
+The gtk_widget_show() function, lets GTK know that we are done setting the
+attributes of this widget, and it can display it.
+<p>
+The last line enters the GTK main processing loop.
+
+<tscreen><verb>
+gtk_main ();
+</verb></tscreen>
+
+gtk_main() is another call you will see in every GTK application. When
+control reaches this point, GTK will sleep waiting for X events (such as
+button or key presses), timeouts, or file IO notifications to occur.
+In our simple example however, events are ignored.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Hello World in GTK
+<p>
+OK, now for a program with a widget (a button). It's the classic hello
+world ala GTK.
+
+<tscreen><verb>
+/* helloworld.c */
+
+#include <gtk/gtk.h>
+
+/* this is a callback function. the data arguments are ignored in this example..
+ * More on callbacks below. */
+void hello (GtkWidget *widget, gpointer data)
+{
+ g_print ("Hello World\n");
+}
+
+gint delete_event(GtkWidget *widget, gpointer data)
+{
+ g_print ("delete event occured\n");
+ /* if you return TRUE in the "delete_event" signal handler,
+ * GTK will emit the "destroy" signal. Returning FALSE means
+ * you don't want the window to be destroyed.
+ * This is useful for popping up 'are you sure you want to quit ?'
+ * type dialogs. */
+
+ /* Change FALSE to TRUE and the main window will be destroyed with
+ * a "delete_event". */
+
+ return (FALSE);
+}
+
+/* another callback */
+void destroy (GtkWidget *widget, gpointer data)
+{
+ gtk_main_quit ();
+}
+
+int main (int argc, char *argv[])
+{
+ /* GtkWidget is the storage type for widgets */
+ GtkWidget *window;
+ GtkWidget *button;
+
+ /* this is called in all GTK applications. arguments are parsed from
+ * the command line and are returned to the application. */
+ gtk_init (&amp;argc, &amp;argv);
+
+ /* create a new window */
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+ /* when the window is given the "delete_event" signal (this is given
+ * by the window manager (usually the 'close' option, or on the
+ * titlebar), we ask it to call the delete_event () function
+ * as defined above. The data passed to the callback
+ * function is NULL and is ignored in the callback. */
+ gtk_signal_connect (GTK_OBJECT (window), "delete_event",
+ GTK_SIGNAL_FUNC (delete_event), NULL);
+
+ /* here we connect the "destroy" event to a signal handler.
+ * This event occurs when we call gtk_widget_destroy() on the window,
+ * or if we return 'TRUE' in the "delete_event" callback. */
+ gtk_signal_connect (GTK_OBJECT (window), "destroy",
+ GTK_SIGNAL_FUNC (destroy), NULL);
+
+ /* sets the border width of the window. */
+ gtk_container_border_width (GTK_CONTAINER (window), 10);
+
+ /* creates a new button with the label "Hello World". */
+ button = gtk_button_new_with_label ("Hello World");
+
+ /* When the button receives the "clicked" signal, it will call the
+ * function hello() passing it NULL as it's argument. The hello() function is
+ * defined above. */
+ gtk_signal_connect (GTK_OBJECT (button), "clicked",
+ GTK_SIGNAL_FUNC (hello), NULL);
+
+ /* This will cause the window to be destroyed by calling
+ * gtk_widget_destroy(window) when "clicked. Again, the destroy
+ * signal could come from here, or the window manager. */
+ gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
+ GTK_SIGNAL_FUNC (gtk_widget_destroy),
+ GTK_OBJECT (window));
+
+ /* this packs the button into the window (a gtk container). */
+ gtk_container_add (GTK_CONTAINER (window), button);
+
+ /* the final step is to display this newly created widget... */
+ gtk_widget_show (button);
+
+ /* and the window */
+ gtk_widget_show (window);
+
+ /* all GTK applications must have a gtk_main(). Control ends here
+ * and waits for an event to occur (like a key press or mouse event). */
+ gtk_main ();
+
+ return 0;
+}
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Compiling Hello World
+<p>
+To compile use:
+
+<tscreen><verb>
+gcc -Wall -g helloworld.c -o hello_world -L/usr/X11R6/lib \
+ -lgtk -lgdk -lglib -lX11 -lXext -lm
+</verb></tscreen>
+<p>
+The libraries above must all be in your default search paths, if not, add
+-L&lt;library directory&gt; and gcc will look in these directories for
+the needed
+libraries. For instance, on my Debian Linux system, I have to add
+<tt>-L/usr/X11R6/lib</> for it to find the X11 libraries.
+<p>
+The order of the libraries are significant. The linker has to know what
+functions it needs from a library before it processes it.
+<p>
+The libraries we are linking in are:
+<itemize>
+<item>The GTK library (-lgtk), the widget library, based on top of GDK.
+<item>The GDK library (-lgdk), the Xlib wrapper.
+<item>The glib library (-lglib), containing miscellaneous functions, only
+g_print() is used in this particular example. GTK is built on top
+of glib so you will always require this library. See the section on
+<ref id="sec_glib" name="glib"> for details.
+<item>The Xlib library (-lX11) which is used by GDK.
+<item>The Xext library (-lXext). This contains code for shared memory
+pixmaps and other X extensions.
+<item>The math library (-lm). This is used by GTK for various purposes.
+</itemize>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Theory of Signals and Callbacks
+<p>
+Before we look in detail at hello world, we'll discuss events and callbacks.
+GTK is an event driven toolkit, which means it will sleep in
+gtk_main until an event occurs and control is passed to the appropriate
+function.
+<p>
+This passing of control is done using the idea of "signals". When an
+event occurs, such as the press of a mouse button, the
+appropriate signal will be "emitted" by the widget that was pressed.
+This is how GTK does
+most of its useful work. To make a button perform an action,
+we set up a signal handler to catch these
+signals and call the appropriate function. This is done by using a
+function such as:
+
+<tscreen><verb>
+gint gtk_signal_connect (GtkObject *object,
+ gchar *name,
+ GtkSignalFunc func,
+ gpointer func_data);
+</verb></tscreen>
+<p>
+Where the first argument is the widget which will be emitting the signal, and
+the second, the name of the signal you wish to catch. The third is the function
+you wish to be called when it is caught, and the fourth, the data you wish
+to have passed to this function.
+<p>
+The function specified in the third argument is called a "callback
+function", and should be of the form:
+
+<tscreen><verb>
+void callback_func(GtkWidget *widget, gpointer *callback_data);
+</verb></tscreen>
+<p>
+Where the first argument will be a pointer to the widget that emitted the signal, and
+the second, a pointer to the data given as the last argument to the
+gtk_signal_connect() function as shown above.
+<p>
+Another call used in the hello world example, is:
+
+<tscreen><verb>
+gint gtk_signal_connect_object (GtkObject *object,
+ gchar *name,
+ GtkSignalFunc func,
+ GtkObject *slot_object);
+</verb></tscreen>
+<p>
+gtk_signal_connect_object() is the same as gtk_signal_connect() except that
+the callback function only uses one argument, a
+pointer to a GTK
+object. So when using this function to connect signals, the callback should be of
+the form:
+
+<tscreen><verb>
+void callback_func (GtkObject *object);
+</verb></tscreen>
+<p>
+Where the object is usually a widget. We usually don't setup callbacks for
+gtk_signal_connect_object however. They are usually used
+to call a GTK function that accept a single widget or object as an
+argument, as is the case in our hello world example.
+
+The purpose of having two functions to connect signals is simply to allow
+the callbacks to have a different number of arguments. Many functions in
+the GTK library accept only a single GtkWidget pointer as an argument, so you
+want to use the gtk_signal_connect_object() for these, whereas for your
+functions, you may need to have additional data supplied to the callbacks.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Stepping Through Hello World
+<p>
+Now that we know the theory behind this, lets clarify by walking through
+the example hello world program.
+<p>
+Here is the callback function that will be called when the button is
+"clicked". We ignore both the widget and the data in this example, but it
+is not hard to do things with them. The next example will use the data
+argument to tell us which button was pressed.
+
+<tscreen><verb>
+void hello (GtkWidget *widget, gpointer *data)
+{
+ g_print ("Hello World\n");
+}
+</verb></tscreen>
+
+<p>
+This callback is a bit special. The "delete_event" occurs when the
+window manager sends this event to the application. We have a choice here
+as to what to do about these events. We can ignore them, make some sort of
+response, or simply quit the application.
+
+The value you return in this callback lets GTK know what action to take.
+By returning FALSE, we let it know that we don't want to have the "destroy"
+signal emitted, keeping our application running. By returning TRUE, we
+ask that "destroy" is emitted, which in turn will call our "destroy"
+signal handler.
+
+<tscreen><verb>
+gint delete_event(GtkWidget *widget, gpointer data)
+{
+ g_print ("delete event occured\n");
+
+ return (FALSE);
+}
+</verb></tscreen>
+
+<p>
+Here is another callback function which just quits by calling
+gtk_main_quit(). Not really much to say about this, it is pretty self
+explanatory.
+<tscreen><verb>
+void destroy (GtkWidget *widget, gpointer *data)
+{
+ gtk_main_quit ();
+}
+</verb></tscreen>
+<p>
+I assume you know about the main() function... yes, as with other
+applications, all GTK applications will also have one of these.
+<tscreen><verb>
+int main (int argc, char *argv[])
+{
+</verb></tscreen>
+<p>
+This next part, declares a pointer to a structure of type GtkWidget. These
+are used below to create a window and a button.
+<tscreen><verb>
+ GtkWidget *window;
+ GtkWidget *button;
+</verb></tscreen>
+<p>
+Here is our gtk_init again. As before, this initializes the toolkit, and
+parses the arguments found on the command line. Any argument it
+recognizes from the command line, it removes from the list, and modifies
+argc and argv to make it look like they never existed, allowing your
+application to parse the remaining arguments.
+<tscreen><verb>
+ gtk_init (&amp;argc, &amp;argv);
+</verb></tscreen>
+<p>
+Create a new window. This is fairly straight forward. Memory is allocated
+for the GtkWidget *window structure so it now points to a valid structure.
+It sets up a new window, but it is not displayed until below where we call
+gtk_widget_show(window) near the end of our program.
+<tscreen><verb>
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+</verb></tscreen>
+<p>
+Here is an example of connecting a signal handler to an object, in this case, the
+window. Here, the "destroy" signal is caught. This is emitted when we use
+the window manager to kill the window (and we return TRUE in the
+"delete_event" handler), or when we use the
+gtk_widget_destroy() call passing in the window widget as the object to
+destroy. By setting this up, we handle both cases with a single call.
+Here, it just calls the destroy() function defined above with a NULL
+argument, which quits GTK for us.
+<p>
+The GTK_OBJECT and GTK_SIGNAL_FUNC are macros that perform type casting and
+checking for us, as well as aid the readability of the code.
+<tscreen><verb>
+ gtk_signal_connect (GTK_OBJECT (window), "destroy",
+ GTK_SIGNAL_FUNC (destroy), NULL);
+</verb></tscreen>
+<p>
+This next function is used to set an attribute of a container object.
+This just sets the window
+so it has a blank area along the inside of it 10 pixels wide where no
+widgets will go. There are other similar functions which we will look at
+in the section on
+<ref id="sec_setting_widget_attributes" name="Setting Widget Attributes">
+<p>
+And again, GTK_CONTAINER is a macro to perform type casting.
+<tscreen><verb>
+ gtk_container_border_width (GTK_CONTAINER (window), 10);
+</verb></tscreen>
+<p>
+This call creates a new button. It allocates space for a new GtkWidget
+structure in memory, initializes it, and makes the button pointer point to
+it. It will have the label "Hello World" on it when displayed.
+<tscreen><verb>
+ button = gtk_button_new_with_label ("Hello World");
+</verb></tscreen>
+<p>
+Here, we take this button, and make it do something useful. We attach a
+signal handler to it so when it emits the "clicked" signal, our hello()
+function is called. The data is ignored, so we simply pass in NULL to the
+hello() callback function. Obviously, the "clicked" signal is emitted when
+we click the button with our mouse pointer.
+
+<tscreen><verb>
+ gtk_signal_connect (GTK_OBJECT (button), "clicked",
+ GTK_SIGNAL_FUNC (hello), NULL);
+</verb></tscreen>
+<p>
+We are also going to use this button to exit our program. This will
+illustrate how the "destroy"
+signal may come from either the window manager, or our program. When the
+button is "clicked", same as above, it calls the first hello() callback function,
+and then this one in the order they are set up. You may have as many
+callback function as you need, and all will be executed in the order you
+connected them. Because the gtk_widget_destroy() function accepts only a
+GtkWidget *widget as an argument, we use the gtk_signal_connect_object()
+function here instead of straight gtk_signal_connect().
+
+<tscreen><verb>
+ gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
+ GTK_SIGNAL_FUNC (gtk_widget_destroy),
+ GTK_OBJECT (window));
+</verb></tscreen>
+<p>
+This is a packing call, which will be explained in depth later on. But it
+is fairly easy to understand. It simply tells GTK that the button is to be
+placed in the window where it will be displayed.
+<tscreen><verb>
+ gtk_container_add (GTK_CONTAINER (window), button);
+</verb></tscreen>
+<p>
+Now that we have everything setup the way we want it to be. With all the
+signal handlers in place, and the button placed in the window where it
+should be, we ask GTK to "show" the widgets on the screen. The window
+widget is shown last so the whole window will pop up at once rather than
+seeing the window pop up, and then the button form inside of it. Although
+with such simple example, you'd never notice.
+<tscreen><verb>
+ gtk_widget_show (button);
+
+ gtk_widget_show (window);
+</verb></tscreen>
+<p>
+And of course, we call gtk_main() which waits for events to come from the X
+server and will call on the widgets to emit signals when these events come.
+<tscreen><verb>
+ gtk_main ();
+</verb></tscreen>
+And the final return. Control returns here after gtk_quit() is called.
+<tscreen><verb>
+ return 0;
+</verb></tscreen>
+<p>
+Now, when we click the mouse button on a GTK button, the
+widget emits a "clicked" signal. In order for us to use this information, our
+program sets up a signal handler to catch that signal, which dispatches the function
+of our choice. In our example, when the button we created is "clicked", the
+hello() function is called with a NULL
+argument, and then the next handler for this signal is called. This calls
+the gtk_widget_destroy() function, passing it the window widget as it's
+argument, destroying the window widget. This causes the window to emit the
+"destroy" signal, which is
+caught, and calls our destroy() callback function, which simply exits GTK.
+<p>
+Another course of events, is to use the window manager to kill the window.
+This will cause the "delete_event" to be emitted. This will call our
+"delete_event" handler. If we return FALSE here, the window will be left as
+is and nothing will happen. Returning TRUE will cause GTK to emit the
+"destroy" signal which of course, calls the "destroy" callback, exiting GTK.
+<p>
+Note that these signals are not the same as the Unix system
+signals, and are not implemented using them, although the terminology is
+almost identical.
+
+<!-- ***************************************************************** -->
+<sect>Moving On
+<!-- ***************************************************************** -->
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Data Types
+<p>
+There are a few things you probably noticed in the previous examples that
+need explaining. The
+gint, gchar etc. that you see are typedefs to int and char respectively. This is done
+to get around that nasty dependency on the size of simple data types when doing calculations.
+A good example is "gint32" which will be
+typedef'd to a 32 bit integer for any given platform, whether it be the 64 bit
+alpha, or the 32 bit i386. The
+typedefs are very straight forward and intuitive. They are all defined in
+glib/glib.h (which gets included from gtk.h).
+<p>
+You'll also notice the ability to use GtkWidget when the function calls for a GtkObject.
+GTK is an object oriented design, and a widget is an object.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>More on Signal Handlers
+<p>
+Lets take another look at the gtk_signal_connect declaration.
+
+<tscreen><verb>
+gint gtk_signal_connect (GtkObject *object, gchar *name,
+ GtkSignalFunc func, gpointer func_data);
+</verb></tscreen>
+
+Notice the gint return value ? This is a tag that identifies your callback
+function. As said above, you may have as many callbacks per signal and per
+object as you need, and each will be executed in turn, in the order they were attached.
+This tag allows you to remove this callback from the list by using:
+<tscreen><verb>
+void gtk_signal_disconnect (GtkObject *object,
+ gint id);
+</verb></tscreen>
+So, by passing in the widget you wish to remove the handler from, and the
+tag or id returned by one of the signal_connect functions, you can
+disconnect a signal handler.
+<p>
+Another function to remove all the signal handers from an object is:
+<tscreen><verb>
+gtk_signal_handlers_destroy (GtkObject *object);
+</verb></tscreen>
+<p>
+This call is fairly self explanatory. It simply removes all the current
+signal handlers from the object passed in as the first argument.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>An Upgraded Hello World
+<p>
+Let's take a look at a slightly improved hello world with better examples
+of callbacks. This will also introduce us to our next topic, packing
+widgets.
+
+<tscreen><verb>
+/* helloworld2.c */
+
+#include <gtk/gtk.h>
+
+/* Our new improved callback. The data passed to this function is printed
+ * to stdout. */
+void callback (GtkWidget *widget, gpointer *data)
+{
+ g_print ("Hello again - %s was pressed\n", (char *) data);
+}
+
+/* another callback */
+void delete_event (GtkWidget *widget, gpointer *data)
+{
+ gtk_main_quit ();
+}
+
+int main (int argc, char *argv[])
+{
+ /* GtkWidget is the storage type for widgets */
+ GtkWidget *window;
+ GtkWidget *button;
+ GtkWidget *box1;
+
+ /* this is called in all GTK applications. arguments are parsed from
+ * the command line and are returned to the application. */
+ gtk_init (&amp;argc, &amp;argv);
+
+ /* create a new window */
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+ /* this is a new call, this just sets the title of our
+ * new window to "Hello Buttons!" */
+ gtk_window_set_title (GTK_WINDOW (window), "Hello Buttons!");
+
+ /* Here we just set a handler for delete_event that immediately
+ * exits GTK. */
+ gtk_signal_connect (GTK_OBJECT (window), "delete_event",
+ GTK_SIGNAL_FUNC (delete_event), NULL);
+
+
+ /* sets the border width of the window. */
+ gtk_container_border_width (GTK_CONTAINER (window), 10);
+
+ /* we create a box to pack widgets into. this is described in detail
+ * in the "packing" section below. The box is not really visible, it
+ * is just used as a tool to arrange widgets. */
+ box1 = gtk_hbox_new(FALSE, 0);
+
+ /* put the box into the main window. */
+ gtk_container_add (GTK_CONTAINER (window), box1);
+
+ /* creates a new button with the label "Button 1". */
+ button = gtk_button_new_with_label ("Button 1");
+
+ /* Now when the button is clicked, we call the "callback" function
+ * with a pointer to "button 1" as it's argument */
+ gtk_signal_connect (GTK_OBJECT (button), "clicked",
+ GTK_SIGNAL_FUNC (callback), (gpointer) "button 1");
+
+ /* instead of gtk_container_add, we pack this button into the invisible
+ * box, which has been packed into the window. */
+ gtk_box_pack_start(GTK_BOX(box1), button, TRUE, TRUE, 0);
+
+ /* always remember this step, this tells GTK that our preparation for
+ * this button is complete, and it can be displayed now. */
+ gtk_widget_show(button);
+
+ /* do these same steps again to create a second button */
+ button = gtk_button_new_with_label ("Button 2");
+
+ /* call the same callback function with a different argument,
+ * passing a pointer to "button 2" instead. */
+ gtk_signal_connect (GTK_OBJECT (button), "clicked",
+ GTK_SIGNAL_FUNC (callback), (gpointer) "button 2");
+
+ gtk_box_pack_start(GTK_BOX(box1), button, TRUE, TRUE, 0);
+
+ /* The order in which we show the buttons is not really important, but I
+ * recommend showing the window last, so it all pops up at once. */
+ gtk_widget_show(button);
+
+ gtk_widget_show(box1);
+
+ gtk_widget_show (window);
+
+ /* rest in gtk_main and wait for the fun to begin! */
+ gtk_main ();
+
+ return 0;
+}
+</verb></tscreen>
+<p>
+Compile this program using the same linking arguments as our first example.
+You'll notice this time there is no easy way to exit the program, you have to use
+your window manager or command line to kill it. A good exercise for the
+reader would be to insert a third "Quit" button that will exit the
+program. You may also wish to play with the options to
+gtk_box_pack_start() while reading the next section.
+Try resizing the window, and observe the behavior.
+<p>
+Just as a side note, there is another useful define for gtk_window_new() -
+GTK_WINDOW_DIALOG. This interacts with the window manager a little
+differently and should be used for transient windows.
+
+<!-- ***************************************************************** -->
+<sect>Packing Widgets
+<!-- ***************************************************************** -->
+
+<p>
+When creating an application, you'll want to put more than one button
+inside a window. Our first hello world example only used one widget so we
+could simply use a gtk_container_add call to "pack" the widget into the
+window. But when you want to put more than one widget into a window, how
+do you control where that widget is positioned ? This is where packing
+comes in.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Theory of Packing Boxes
+<p>
+Most packing is done by creating boxes as in the example above. These are
+invisible widget containers that we can pack our widgets into and come in
+two forms, a horizontal box, and a vertical box. When packing widgets
+into a horizontal box, the objects are inserted horizontally from left to
+right or right to left depending on the call used. In a vertical box,
+widgets are packed from top to bottom or vice versa. You may use any
+combination of boxes inside or beside other boxes to create the desired
+effect.
+<p>
+To create a new horizontal box, we use a call to gtk_hbox_new(), and for
+vertical boxes, gtk_vbox_new(). The gtk_box_pack_start() and
+gtk_box_pack_end() functions are used to place objects inside of these
+containers. The gtk_box_pack_start() function will start at the top and
+work its way down in a vbox, and pack left to right in an hbox.
+gtk_box_pack_end() will do the opposite, packing from bottom to top in a
+vbox, and right to left in an hbox. Using these functions allow us to
+right justify or left justify our widgets and may be mixed in any way to
+achieve the desired effect. We will use gtk_box_pack_start() in most of
+our examples. An object may be another container or a widget. And in
+fact, many widgets are actually containers themselves including the
+button, but we usually only use a label inside a button.
+<p>
+By using these calls, GTK knows where you want to place your widgets so it
+can do automatic resizing and other nifty things. there's also a number
+of options as to how your widgets should be packed. As you can imagine,
+this method gives us a quite a bit of flexibility when placing and
+creating widgets.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Details of Boxes
+<p>
+Because of this flexibility, packing boxes in GTK can be confusing at
+first. There are a lot of options, and it's not immediately obvious how
+they all fit together. In the end however, there are basically five
+different styles you can get.
+
+<p>
+<? <CENTER> >
+<?
+<IMG SRC="packbox1.gif" VSPACE="15" HSPACE="10" WIDTH="528" HEIGHT="235"
+ALT="Box Packing Example Image">
+>
+<? </CENTER> >
+
+Each line contains one horizontal box (hbox) with several buttons. The
+call to gtk_box_pack is shorthand for the call to pack each of the buttons
+into the hbox. Each of the buttons is packed into the hbox the same way
+(i.e. same arguments to the gtk_box_pack_start () function).
+<p>
+This is the declaration of the gtk_box_pack_start function.
+
+<tscreen><verb>
+void gtk_box_pack_start (GtkBox *box,
+ GtkWidget *child,
+ gint expand,
+ gint fill,
+ gint padding);
+</verb></tscreen>
+
+The first argument is the box you are packing the object into, the second
+is this object. The objects will all be buttons for now, so we'll be
+packing buttons into boxes.
+<p>
+The expand argument to gtk_box_pack_start() or gtk_box_pack_end() controls
+whether the widgets are laid out in the box to fill in all the extra space
+in the box so the box is expanded to fill the area alloted to it (TRUE).
+Or the box is shrunk to just fit the widgets (FALSE). Setting expand to
+FALSE will allow you to do right and left
+justifying of your widgets. Otherwise, they will all expand to fit in the
+box, and the same effect could be achieved by using only one of
+gtk_box_pack_start or pack_end functions.
+<p>
+The fill argument to the gtk_box_pack functions control whether the extra
+space is allocated to the objects themselves (TRUE), or as extra padding
+in the box around these objects (FALSE). It only has an effect if the
+expand argument is also TRUE.
+<p>
+When creating a new box, the function looks like this:
+
+<tscreen><verb>
+GtkWidget * gtk_hbox_new (gint homogeneous,
+ gint spacing);
+</verb></tscreen>
+
+The homogeneous argument to gtk_hbox_new (and the same for gtk_vbox_new)
+controls whether each object in the box has the same size (i.e. the same
+width in an hbox, or the same height in a vbox). If it is set, the expand
+argument to the gtk_box_pack routines is always turned on.
+<p>
+What's the difference between spacing (set when the box is created) and
+padding (set when elements are packed)? Spacing is added between objects,
+and padding is added on either side of an object. The following figure
+should make it clearer:
+
+<? <CENTER> >
+<?
+<IMG ALIGN="center" SRC="packbox2.gif" WIDTH="509" HEIGHT="213"
+VSPACE="15" HSPACE="10" ALT="Box Packing Example Image">
+>
+<? </CENTER> >
+
+Here is the code used to create the above images. I've commented it fairly
+heavily so hopefully you won't have any problems following it. Compile it yourself
+and play with it.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Packing Demonstration Program
+<p>
+<tscreen><verb>
+/* packbox.c */
+
+#include "gtk/gtk.h"
+
+void
+delete_event (GtkWidget *widget, gpointer *data)
+{
+ gtk_main_quit ();
+}
+
+/* Make a new hbox filled with button-labels. Arguments for the
+ * variables we're interested are passed in to this function.
+ * We do not show the box, but do show everything inside. */
+GtkWidget *make_box (gint homogeneous, gint spacing,
+ gint expand, gint fill, gint padding)
+{
+ GtkWidget *box;
+ GtkWidget *button;
+ char padstr[80];
+
+ /* create a new hbox with the appropriate homogeneous and spacing
+ * settings */
+ box = gtk_hbox_new (homogeneous, spacing);
+
+ /* create a series of buttons with the appropriate settings */
+ button = gtk_button_new_with_label ("gtk_box_pack");
+ gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
+ gtk_widget_show (button);
+
+ button = gtk_button_new_with_label ("(box,");
+ gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
+ gtk_widget_show (button);
+
+ button = gtk_button_new_with_label ("button,");
+ gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
+ gtk_widget_show (button);
+
+ /* create a button with the label depending on the value of
+ * expand. */
+ if (expand == TRUE)
+ button = gtk_button_new_with_label ("TRUE,");
+ else
+ button = gtk_button_new_with_label ("FALSE,");
+
+ gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
+ gtk_widget_show (button);
+
+ /* This is the same as the button creation for "expand"
+ * above, but uses the shorthand form. */
+ button = gtk_button_new_with_label (fill ? "TRUE," : "FALSE,");
+ gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
+ gtk_widget_show (button);
+
+ sprintf (padstr, "%d);", padding);
+
+ button = gtk_button_new_with_label (padstr);
+ gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
+ gtk_widget_show (button);
+
+ return box;
+}
+
+int
+main (int argc, char *argv[])
+{
+ GtkWidget *window;
+ GtkWidget *button;
+ GtkWidget *box1;
+ GtkWidget *box2;
+ GtkWidget *separator;
+ GtkWidget *label;
+ GtkWidget *quitbox;
+ int which;
+
+ /* Our init, don't forget this! :) */
+ gtk_init (&amp;argc, &amp;argv);
+
+ if (argc != 2) {
+ fprintf (stderr, "usage: packbox num, where num is 1, 2, or 3.\n");
+ /* this just does cleanup in GTK, and exits with an exit status of 1. */
+ gtk_exit (1);
+ }
+
+ which = atoi (argv[1]);
+
+ /* Create our window */
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+ /* You should always remember to connect the destroy signal to the
+ * main window. This is very important for proper intuitive
+ * behavior */
+ gtk_signal_connect (GTK_OBJECT (window), "delete_event",
+ GTK_SIGNAL_FUNC (delete_event), NULL);
+ gtk_container_border_width (GTK_CONTAINER (window), 10);
+
+ /* We create a vertical box (vbox) to pack the horizontal boxes into.
+ * This allows us to stack the horizontal boxes filled with buttons one
+ * on top of the other in this vbox. */
+ box1 = gtk_vbox_new (FALSE, 0);
+
+ /* which example to show. These correspond to the pictures above. */
+ switch (which) {
+ case 1:
+ /* create a new label. */
+ label = gtk_label_new ("gtk_hbox_new (FALSE, 0);");
+
+ /* Align the label to the left side. We'll discuss this function and
+ * others in the section on Widget Attributes. */
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
+
+ /* Pack the label into the vertical box (vbox box1). Remember that
+ * widgets added to a vbox will be packed one on top of the other in
+ * order. */
+ gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 0);
+
+ /* show the label */
+ gtk_widget_show (label);
+
+ /* call our make box function - homogeneous = FALSE, spacing = 0,
+ * expand = FALSE, fill = FALSE, padding = 0 */
+ box2 = make_box (FALSE, 0, FALSE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
+ gtk_widget_show (box2);
+
+ /* call our make box function - homogeneous = FALSE, spacing = 0,
+ * expand = FALSE, fill = FALSE, padding = 0 */
+ box2 = make_box (FALSE, 0, TRUE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
+ gtk_widget_show (box2);
+
+ /* Args are: homogeneous, spacing, expand, fill, padding */
+ box2 = make_box (FALSE, 0, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
+ gtk_widget_show (box2);
+
+ /* creates a separator, we'll learn more about these later,
+ * but they are quite simple. */
+ separator = gtk_hseparator_new ();
+
+ /* pack the separator into the vbox. Remember each of these
+ * widgets are being packed into a vbox, so they'll be stacked
+ * vertically. */
+ gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);
+ gtk_widget_show (separator);
+
+ /* create another new label, and show it. */
+ label = gtk_label_new ("gtk_hbox_new (TRUE, 0);");
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
+ gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ /* Args are: homogeneous, spacing, expand, fill, padding */
+ box2 = make_box (TRUE, 0, TRUE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
+ gtk_widget_show (box2);
+
+ /* Args are: homogeneous, spacing, expand, fill, padding */
+ box2 = make_box (TRUE, 0, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
+ gtk_widget_show (box2);
+
+ /* another new separator. */
+ separator = gtk_hseparator_new ();
+ /* The last 3 arguments to gtk_box_pack_start are: expand, fill, padding. */
+ gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);
+ gtk_widget_show (separator);
+
+ break;
+
+ case 2:
+
+ /* create a new label, remember box1 is a vbox as created
+ * near the beginning of main() */
+ label = gtk_label_new ("gtk_hbox_new (FALSE, 10);");
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
+ gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ /* Args are: homogeneous, spacing, expand, fill, padding */
+ box2 = make_box (FALSE, 10, TRUE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
+ gtk_widget_show (box2);
+
+ /* Args are: homogeneous, spacing, expand, fill, padding */
+ box2 = make_box (FALSE, 10, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
+ gtk_widget_show (box2);
+
+ separator = gtk_hseparator_new ();
+ /* The last 3 arguments to gtk_box_pack_start are: expand, fill, padding. */
+ gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);
+ gtk_widget_show (separator);
+
+ label = gtk_label_new ("gtk_hbox_new (FALSE, 0);");
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
+ gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ /* Args are: homogeneous, spacing, expand, fill, padding */
+ box2 = make_box (FALSE, 0, TRUE, FALSE, 10);
+ gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
+ gtk_widget_show (box2);
+
+ /* Args are: homogeneous, spacing, expand, fill, padding */
+ box2 = make_box (FALSE, 0, TRUE, TRUE, 10);
+ gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
+ gtk_widget_show (box2);
+
+ separator = gtk_hseparator_new ();
+ /* The last 3 arguments to gtk_box_pack_start are: expand, fill, padding. */
+ gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);
+ gtk_widget_show (separator);
+ break;
+
+ case 3:
+
+ /* This demonstrates the ability to use gtk_box_pack_end() to
+ * right justify widgets. First, we create a new box as before. */
+ box2 = make_box (FALSE, 0, FALSE, FALSE, 0);
+ /* create the label that will be put at the end. */
+ label = gtk_label_new ("end");
+ /* pack it using gtk_box_pack_end(), so it is put on the right side
+ * of the hbox created in the make_box() call. */
+ gtk_box_pack_end (GTK_BOX (box2), label, FALSE, FALSE, 0);
+ /* show the label. */
+ gtk_widget_show (label);
+
+ /* pack box2 into box1 (the vbox remember ? :) */
+ gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
+ gtk_widget_show (box2);
+
+ /* a separator for the bottom. */
+ separator = gtk_hseparator_new ();
+ /* this explicitly sets the separator to 400 pixels wide by 5 pixels
+ * high. This is so the hbox we created will also be 400 pixels wide,
+ * and the "end" label will be separated from the other labels in the
+ * hbox. Otherwise, all the widgets in the hbox would be packed as
+ * close together as possible. */
+ gtk_widget_set_usize (separator, 400, 5);
+ /* pack the separator into the vbox (box1) created near the start
+ * of main() */
+ gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);
+ gtk_widget_show (separator);
+ }
+
+ /* Create another new hbox.. remember we can use as many as we need! */
+ quitbox = gtk_hbox_new (FALSE, 0);
+
+ /* Our quit button. */
+ button = gtk_button_new_with_label ("Quit");
+
+ /* setup the signal to destroy the window. Remember that this will send
+ * the "destroy" signal to the window which will be caught by our signal
+ * handler as defined above. */
+ gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
+ GTK_SIGNAL_FUNC (gtk_main_quit),
+ GTK_OBJECT (window));
+ /* pack the button into the quitbox.
+ * The last 3 arguments to gtk_box_pack_start are: expand, fill, padding. */
+ gtk_box_pack_start (GTK_BOX (quitbox), button, TRUE, FALSE, 0);
+ /* pack the quitbox into the vbox (box1) */
+ gtk_box_pack_start (GTK_BOX (box1), quitbox, FALSE, FALSE, 0);
+
+ /* pack the vbox (box1) which now contains all our widgets, into the
+ * main window. */
+ gtk_container_add (GTK_CONTAINER (window), box1);
+
+ /* and show everything left */
+ gtk_widget_show (button);
+ gtk_widget_show (quitbox);
+
+ gtk_widget_show (box1);
+ /* Showing the window last so everything pops up at once. */
+ gtk_widget_show (window);
+
+ /* And of course, our main function. */
+ gtk_main ();
+
+ /* control returns here when gtk_main_quit() is called, but not when
+ * gtk_exit is used. */
+
+ return 0;
+}
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Packing Using Tables
+<p>
+Let's take a look at another way of packing - Tables. These can be
+extremely useful in certain situations.
+
+Using tables, we create a grid that we can place widgets in. The widgets
+may take up as many spaces as we specify.
+
+The first thing to look at of course, is the gtk_table_new function:
+
+<tscreen><verb>
+GtkWidget* gtk_table_new (gint rows,
+ gint columns,
+ gint homogeneous);
+</verb></tscreen>
+<p>
+The first argument is the number of rows to make in the table, while the
+second, obviously, the number of columns.
+
+The homogeneous argument has to do with how the table's boxes are sized. If homogeneous
+is TRUE, the table boxes are resized to the size of the largest widget in the table.
+If homogeneous is FALSE, the size of a table boxes is dictated by the tallest widget
+in its same row, and the widest widget in its column.
+
+The rows and columnts are laid out starting with 0 to n, where n was the
+number specified in the call to gtk_table_new. So, if you specify rows = 2 and
+columns = 2, the layout would look something like this:
+
+<tscreen><verb>
+ 0 1 2
+0+----------+----------+
+ | | |
+1+----------+----------+
+ | | |
+2+----------+----------+
+</verb></tscreen>
+<p>
+Note that the coordinate system starts in the upper left hand corner. To place a
+widget into a box, use the following function:
+
+<tscreen><verb>
+void gtk_table_attach (GtkTable *table,
+ GtkWidget *child,
+ gint left_attach,
+ gint right_attach,
+ gint top_attach,
+ gint bottom_attach,
+ gint xoptions,
+ gint yoptions,
+ gint xpadding,
+ gint ypadding);
+</verb></tscreen>
+<p>
+Where the first argument ("table") is the table you've created and the second
+("child") the widget you wish to place in the table.
+
+The left and right attach
+arguments specify where to place the widget, and how many boxes to use. If you want
+a button in the lower right table entry
+of our 2x2 table, and want it to fill that entry ONLY. left_attach would be = 1,
+right_attach = 2, top_attach = 1, bottom_attach = 2.
+
+Now, if you wanted a widget to take up the whole
+top row of our 2x2 table, you'd use left_attach = 0, right_attach =2, top_attach = 0,
+bottom_attach = 1.
+
+The xoptions and yoptions are used to specify packing options and may be OR'ed
+together to allow multiple options.
+
+These options are:
+<itemize>
+<item>GTK_FILL - If the table box is larger than the widget, and GTK_FILL is
+specified, the widget will expand to use all the room available.
+
+<item>GTK_SHRINK - If the table widget was allocated less space then was
+requested (usually by the user resizing the window), then the widgets would
+normally just be pushed off the bottom of
+the window and disappear. If GTK_SHRINK is specified, the widgets will
+shrink with the table.
+
+<item>GTK_EXPAND - This will cause the table to expand to use up any remaining
+space in the window.
+</itemize>
+
+Padding is just like in boxes, creating a clear area around the widget
+specified in pixels.
+
+gtk_table_attach() has a LOT of options. So, there's a shortcut:
+
+<tscreen><verb>
+void gtk_table_attach_defaults (GtkTable *table,
+ GtkWidget *widget,
+ gint left_attach,
+ gint right_attach,
+ gint top_attach,
+ gint bottom_attach);
+</verb></tscreen>
+
+The X and Y options default to GTK_FILL | GTK_EXPAND, and X and Y padding
+are set to 0. The rest of the arguments are identical to the previous
+function.
+
+We also have gtk_table_set_row_spacing() and gtk_table_set_col_spacing(). This places
+spacing between the rows at the specified row or column.
+
+<tscreen><verb>
+void gtk_table_set_row_spacing (GtkTable *table,
+ gint row,
+ gint spacing);
+</verb></tscreen>
+and
+<tscreen><verb>
+void gtk_table_set_col_spacing (GtkTable *table,
+ gint column,
+ gint spacing);
+</verb></tscreen>
+
+Note that for columns, the space goes to the right of the column, and for rows,
+the space goes below the row.
+
+You can also set a consistent spacing of all rows and/or columns with:
+
+<tscreen><verb>
+void gtk_table_set_row_spacings (GtkTable *table,
+ gint spacing);
+</verb></tscreen>
+<p>
+And,
+<tscreen><verb>
+void gtk_table_set_col_spacings (GtkTable *table,
+ gint spacing);
+</verb></tscreen>
+<p>
+Note that with these calls, the last row and last column do not get any spacing
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Table Packing Example
+<p>
+Here we make a window with three buttons in a 2x2 table.
+The first two buttons will be placed in the upper row.
+A third, quit button, is placed in the lower row, spanning both columns.
+Which means it should look something like this:
+<p>
+<? <CENTER> >
+<?
+<IMG SRC="table.gif" VSPACE="15" HSPACE="10"
+ALT="Table Packing Example Image" WIDTH="180" HEIGHT="120">
+>
+<? </CENTER> >
+
+Here's the source code:
+
+<tscreen><verb>
+/* table.c */
+#include <gtk/gtk.h>
+
+/* our callback.
+ * the data passed to this function is printed to stdout */
+void callback (GtkWidget *widget, gpointer *data)
+{
+ g_print ("Hello again - %s was pressed\n", (char *) data);
+}
+
+/* this callback quits the program */
+void delete_event (GtkWidget *widget, gpointer *data)
+{
+ gtk_main_quit ();
+}
+
+int main (int argc, char *argv[])
+{
+ GtkWidget *window;
+ GtkWidget *button;
+ GtkWidget *table;
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ /* create a new window */
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+ /* set the window title */
+ gtk_window_set_title (GTK_WINDOW (window), "Table");
+
+ /* set a handler for delete_event that immediately
+ * exits GTK. */
+ gtk_signal_connect (GTK_OBJECT (window), "delete_event",
+ GTK_SIGNAL_FUNC (delete_event), NULL);
+
+ /* sets the border width of the window. */
+ gtk_container_border_width (GTK_CONTAINER (window), 20);
+
+ /* create a 2x2 table */
+ table = gtk_table_new (2, 2, TRUE);
+
+ /* put the table in the main window */
+ gtk_container_add (GTK_CONTAINER (window), table);
+
+ /* create first button */
+ button = gtk_button_new_with_label ("button 1");
+
+ /* when the button is clicked, we call the "callback" function
+ * with a pointer to "button 1" as it's argument */
+ gtk_signal_connect (GTK_OBJECT (button), "clicked",
+ GTK_SIGNAL_FUNC (callback), (gpointer) "button 1");
+
+
+ /* insert button 1 into the upper left quadrant of the table */
+ gtk_table_attach_defaults (GTK_TABLE(table), button, 0, 1, 0, 1);
+
+ gtk_widget_show (button);
+
+ /* create second button */
+
+ button = gtk_button_new_with_label ("button 2");
+
+ /* when the button is clicked, we call the "callback" function
+ * with a pointer to "button 2" as it's argument */
+ gtk_signal_connect (GTK_OBJECT (button), "clicked",
+ GTK_SIGNAL_FUNC (callback), (gpointer) "button 2");
+ /* insert button 2 into the upper right quadrant of the table */
+ gtk_table_attach_defaults (GTK_TABLE(table), button, 1, 2, 0, 1);
+
+ gtk_widget_show (button);
+
+ /* create "Quit" button */
+ button = gtk_button_new_with_label ("Quit");
+
+ /* when the button is clicked, we call the "delete_event" function
+ * and the program exits */
+ gtk_signal_connect (GTK_OBJECT (button), "clicked",
+ GTK_SIGNAL_FUNC (delete_event), NULL);
+
+ /* insert the quit button into the both
+ * lower quadrants of the table */
+ gtk_table_attach_defaults (GTK_TABLE(table), button, 0, 2, 1, 2);
+
+ gtk_widget_show (button);
+
+ gtk_widget_show (table);
+ gtk_widget_show (window);
+
+ gtk_main ();
+
+ return 0;
+}
+</verb></tscreen>
+
+You can compile this program with something like:
+
+<tscreen><verb>
+gcc -g -Wall -ansi -o table table.c -L/usr/X11R6/lib \
+ -lgdk -lgtk -lglib -lX11 -lXext -lm
+</verb></tscreen>
+
+<!-- ***************************************************************** -->
+<sect>Widget Overview
+<!-- ***************************************************************** -->
+
+<p>
+The general steps to creating a widget in GTK are:
+<enum>
+<item> gtk_*_new - one of various functions to create a new widget. These
+are all detailed in this section.
+
+<item> Connect all signals we wish to use to the appropriate handlers.
+
+<item> Set the attributes of the widget.
+
+<item> Pack the widget into a container using the appropriate call such as
+gtk_container_add() or gtk_box_pack_start().
+
+<item> gtk_widget_show() the widget.
+</enum>
+<p>
+gtk_widget_show() lets GTK know that we are done setting the attributes
+of the widget, and it is ready to be displayed. You may also use
+gtk_widget_hide to make it disappear again. The order in which you
+show the widgets is not important, but I suggest showing the window
+last so the whole window pops up at once rather than seeing the individual
+widgets come up on the screen as they're formed. The children of a widget
+(a window is a widget too)
+will not be displayed until the window itself is shown using the
+gtk_widget_show() function.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Casting
+<p>
+You'll notice as you go on, that GTK uses a type casting system. This is
+always done using macros that both test the ability to cast the given item,
+and perform the cast. Some common ones you will see are:
+
+<itemize>
+<item> GTK_WIDGET(widget)
+<item> GTK_OBJECT(object)
+<item> GTK_SIGNAL_FUNC(function)
+<item> GTK_CONTAINER(container)
+<item> GTK_WINDOW(window)
+<item> GTK_BOX(box)
+</itemize>
+
+These are all used to cast arguments in functions. You'll see them in the
+examples, and can usually tell when to use them simply by looking at the
+function's declaration.
+
+As you can see below in the class hierarchy, all GtkWidgets are derived from
+the GtkObject base class. This means you can use an widget in any place the
+function asks for an object - simply use the GTK_OBJECT() macro.
+
+For example:
+
+<tscreen><verb>
+gtk_signal_connect(GTK_OBJECT(button), "clicked",
+ GTK_SIGNAL_FUNC(callback_function), callback_data);
+</verb></tscreen>
+
+This casts the button into an object, and provides a cast for the function
+pointer to the callback.
+
+Many widgets are also containers. If you look in the class hierarchy below,
+you'll notice that many widgets drive from the GtkContainer class. Any one
+of those widgets may use with the GTK_CONTAINER macro to
+pass them to functions that ask for containers.
+
+Unfortunately, these macros are not extensively covered in the tutorial, but I
+recomend taking a look through the GTK header files. It can be very
+educational. In fact, it's not difficult to learn how a widget works just
+by looking at the function declarations.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Widget Hierarchy
+<p>
+For your reference, here is the class hierarchy tree used to implement widgets.
+
+<tscreen><verb>
+ GtkObject
+ +GtkData
+ | +GtkAdjustment
+ | `GtkTooltips
+ `GtkWidget
+ +GtkContainer
+ | +GtkBin
+ | | +GtkAlignment
+ | | +GtkEventBox
+ | | +GtkFrame
+ | | | `GtkAspectFrame
+ | | +GtkHandleBox
+ | | +GtkItem
+ | | | +GtkListItem
+ | | | +GtkMenuItem
+ | | | | `GtkCheckMenuItem
+ | | | | `GtkRadioMenuItem
+ | | | `GtkTreeItem
+ | | +GtkViewport
+ | | `GtkWindow
+ | | +GtkColorSelectionDialog
+ | | +GtkDialog
+ | | | `GtkInputDialog
+ | | `GtkFileSelection
+ | +GtkBox
+ | | +GtkButtonBox
+ | | | +GtkHButtonBox
+ | | | `GtkVButtonBox
+ | | +GtkHBox
+ | | | +GtkCombo
+ | | | `GtkStatusbar
+ | | `GtkVBox
+ | | +GtkColorSelection
+ | | `GtkGammaCurve
+ | +GtkButton
+ | | +GtkOptionMenu
+ | | `GtkToggleButton
+ | | `GtkCheckButton
+ | | `GtkRadioButton
+ | +GtkCList
+ | +GtkFixed
+ | +GtkList
+ | +GtkMenuShell
+ | | +GtkMenuBar
+ | | `GtkMenu
+ | +GtkNotebook
+ | +GtkPaned
+ | | +GtkHPaned
+ | | `GtkVPaned
+ | +GtkScrolledWindow
+ | +GtkTable
+ | +GtkToolbar
+ | `GtkTree
+ +GtkDrawingArea
+ | `GtkCurve
+ +GtkEditable
+ | +GtkEntry
+ | | `GtkSpinButton
+ | `GtkText
+ +GtkMisc
+ | +GtkArrow
+ | +GtkImage
+ | +GtkLabel
+ | | `GtkTipsQuery
+ | `GtkPixmap
+ +GtkPreview
+ +GtkProgressBar
+ +GtkRange
+ | +GtkScale
+ | | +GtkHScale
+ | | `GtkVScale
+ | `GtkScrollbar
+ | +GtkHScrollbar
+ | `GtkVScrollbar
+ +GtkRuler
+ | +GtkHRuler
+ | `GtkVRuler
+ `GtkSeparator
+ +GtkHSeparator
+ `GtkVSeparator
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Widgets Without Windows
+<p>
+The following widgets do not have an associated window. If you want to
+capture events, you'll have to use the GtkEventBox. See the section on
+<ref id="sec_The_EventBox_Widget" name="The EventBox Widget">
+
+<tscreen><verb>
+GtkAlignment
+GtkArrow
+GtkBin
+GtkBox
+GtkImage
+GtkItem
+GtkLabel
+GtkPaned
+GtkPixmap
+GtkScrolledWindow
+GtkSeparator
+GtkTable
+GtkViewport
+GtkAspectFrame
+GtkFrame
+GtkVPaned
+GtkHPaned
+GtkVBox
+GtkHBox
+GtkVSeparator
+GtkHSeparator
+</verb></tscreen>
+<p>
+We'll further our exploration of GTK by examining each widget in turn,
+creating a few simple functions to display them. Another good source is
+the testgtk.c program that comes with GTK. It can be found in
+gtk/testgtk.c.
+
+<!-- ***************************************************************** -->
+<sect>The Button Widget
+<!-- ***************************************************************** -->
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Normal Buttons
+<p>
+We've almost seen all there is to see of the button widget. It's pretty
+simple. There is however two ways to create a button. You can use the
+gtk_button_new_with_label() to create a button with a label, or use
+gtk_button_new() to create a blank button. It's then up to you to pack a
+label or pixmap into this new button. To do this, create a new box, and
+then pack your objects into this box using the usual gtk_box_pack_start,
+and then use gtk_container_add to pack the box into the button.
+<p>
+Here's an example of using gtk_button_new to create a button with a
+picture and a label in it. I've broken the code to create a box up from
+the rest so you can use it in your programs.
+
+<tscreen><verb>
+/* buttons.c */
+
+#include <gtk/gtk.h>
+
+/* create a new hbox with an image and a label packed into it
+ * and return the box.. */
+
+GtkWidget *xpm_label_box (GtkWidget *parent, gchar *xpm_filename, gchar *label_text)
+{
+ GtkWidget *box1;
+ GtkWidget *label;
+ GtkWidget *pixmapwid;
+ GdkPixmap *pixmap;
+ GdkBitmap *mask;
+ GtkStyle *style;
+
+ /* create box for xpm and label */
+ box1 = gtk_hbox_new (FALSE, 0);
+ gtk_container_border_width (GTK_CONTAINER (box1), 2);
+
+ /* get style of button.. I assume it's to get the background color.
+ * if someone knows the real reason, please enlighten me. */
+ style = gtk_widget_get_style(parent);
+
+ /* now on to the xpm stuff.. load xpm */
+ pixmap = gdk_pixmap_create_from_xpm (parent->window, &amp;mask,
+ &amp;style->bg[GTK_STATE_NORMAL],
+ xpm_filename);
+ pixmapwid = gtk_pixmap_new (pixmap, mask);
+
+ /* create label for button */
+ label = gtk_label_new (label_text);
+
+ /* pack the pixmap and label into the box */
+ gtk_box_pack_start (GTK_BOX (box1),
+ pixmapwid, FALSE, FALSE, 3);
+
+ gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 3);
+
+ gtk_widget_show(pixmapwid);
+ gtk_widget_show(label);
+
+ return (box1);
+}
+
+/* our usual callback function */
+void callback (GtkWidget *widget, gpointer *data)
+{
+ g_print ("Hello again - %s was pressed\n", (char *) data);
+}
+
+
+int main (int argc, char *argv[])
+{
+ /* GtkWidget is the storage type for widgets */
+ GtkWidget *window;
+ GtkWidget *button;
+ GtkWidget *box1;
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ /* create a new window */
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+ gtk_window_set_title (GTK_WINDOW (window), "Pixmap'd Buttons!");
+
+ /* It's a good idea to do this for all windows. */
+ gtk_signal_connect (GTK_OBJECT (window), "destroy",
+ GTK_SIGNAL_FUNC (gtk_exit), NULL);
+
+ gtk_signal_connect (GTK_OBJECT (window), "delete_event",
+ GTK_SIGNAL_FUNC (gtk_exit), NULL);
+
+
+ /* sets the border width of the window. */
+ gtk_container_border_width (GTK_CONTAINER (window), 10);
+ gtk_widget_realize(window);
+
+ /* create a new button */
+ button = gtk_button_new ();
+
+ /* You should be getting used to seeing most of these functions by now */
+ gtk_signal_connect (GTK_OBJECT (button), "clicked",
+ GTK_SIGNAL_FUNC (callback), (gpointer) "cool button");
+
+ /* this calls our box creating function */
+ box1 = xpm_label_box(window, "info.xpm", "cool button");
+
+ /* pack and show all our widgets */
+ gtk_widget_show(box1);
+
+ gtk_container_add (GTK_CONTAINER (button), box1);
+
+ gtk_widget_show(button);
+
+ gtk_container_add (GTK_CONTAINER (window), button);
+
+ gtk_widget_show (window);
+
+ /* rest in gtk_main and wait for the fun to begin! */
+ gtk_main ();
+
+ return 0;
+}
+</verb></tscreen>
+
+The xpm_label_box function could be used to pack xpm's and labels into any
+widget that can be a container.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Toggle Buttons
+<p>
+Toggle buttons are very similar to normal buttons, except they will always
+be in one of two states, alternated by a click. They may be depressed, and
+when you click again, they will pop back up. Click again, and they will pop
+back down.
+
+Toggle buttons are the basis for check buttons and radio buttons, as such,
+many of the calls used for toggle buttons are inherited by radio and check
+buttons. I will point these out when we come to them.
+
+Creating a new toggle button:
+
+<tscreen><verb>
+GtkWidget* gtk_toggle_button_new (void);
+
+GtkWidget* gtk_toggle_button_new_with_label (gchar *label);
+</verb></tscreen>
+<p>
+As you can imagine, these work identically to the normal button widget
+calls. The first creates a blank toggle button, and the second, a button
+with a label widget already packed into it.
+<p>
+To retrieve the state of the toggle widget, including radio and check
+buttons, we use a macro as shown in our example below. This tests the state
+of the toggle in a callback. The signal of interest emitted to us by toggle
+buttons (the toggle button, check button, and radio button widgets), is the
+"toggled" signal. To check the state of these buttons, set up a signal
+handler to catch the toggled signal, and use the macro to determine it's
+state. The callback will look something like:
+
+<tscreen><verb>
+void toggle_button_callback (GtkWidget *widget, gpointer data)
+{
+ if (GTK_TOGGLE_BUTTON (widget)->active)
+ {
+ /* If control reaches here, the toggle button is down */
+
+ } else {
+
+ /* If control reaches here, the toggle button is up */
+ }
+}
+</verb></tscreen>
+
+<!--
+
+COMMENTED!
+
+<tscreen><verb>
+guint gtk_toggle_button_get_type (void);
+</verb></tscreen>
+<p>
+No idea... they all have this, but I dunno what it is :)
+
+
+<tscreen><verb>
+void gtk_toggle_button_set_mode (GtkToggleButton *toggle_button,
+ gint draw_indicator);
+</verb></tscreen>
+<p>
+No idea.
+-->
+
+<tscreen><verb>
+void gtk_toggle_button_set_state (GtkToggleButton *toggle_button,
+ gint state);
+</verb></tscreen>
+<p>
+The above call can be used to set the state of the toggle button, and it's
+children the radio and check buttons. Passing
+in your created button as the first argument, and a TRUE or FALSE
+for the second state argument to specify whether it should be up (released) or
+down (depressed). Default is up, or FALSE.
+
+Note that when you use the gtk_toggle_button_set_state() function, and the
+state is actually changed, it causes
+the "clicked" signal to be emitted from the button.
+
+<tscreen><verb>
+void gtk_toggle_button_toggled (GtkToggleButton *toggle_button);
+</verb></tscreen>
+<p>
+This simply toggles the button, and emits the "toggled" signal.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Check Buttons
+<p>
+Check buttons inherent many properties and functions from the the toggle buttons above,
+but look a little
+different. Rather than being buttons with text inside them, they are small
+squares with the text to the right of them. These are often seen for
+toggling options on and off in applications.
+
+The two creation functions are the same as for the normal button.
+
+<tscreen><verb>
+GtkWidget* gtk_check_button_new (void);
+
+GtkWidget* gtk_check_button_new_with_label (gchar *label);
+</verb></tscreen>
+
+The new_with_label function creates a check button with a label beside it.
+
+Checking the state of the check button is identical to that of the toggle
+button.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Radio Buttons
+<p>
+Radio buttons are similar to check buttons except they are grouped so that
+only one may be selected/depressed at a time. This is good for places in
+your application where you need to select from a short list of options.
+
+Creating a new radio button is done with one of these calls:
+
+<tscreen><verb>
+GtkWidget* gtk_radio_button_new (GSList *group);
+
+GtkWidget* gtk_radio_button_new_with_label (GSList *group,
+ gchar *label);
+</verb></tscreen>
+<p>
+You'll notice the extra argument to these calls. They require a group to
+perform they're duty properly. The first call should pass NULL as the first
+argument. Then create a group using:
+
+<tscreen><verb>
+GSList* gtk_radio_button_group (GtkRadioButton *radio_button);
+</verb></tscreen>
+
+<p>
+The important thing to remember is that gtk_radio_button_group must be
+called for each new button added to the group, with the previous button
+passed in as an argument. The result is then passed into the call to
+gtk_radio_button_new or gtk_radio_button_new_with_label. This allows a
+chain of buttons to be established. The example below should make this
+clear.
+
+It is also a good idea to explicitly set which button should be the
+default depressed button with:
+
+<tscreen><verb>
+void gtk_toggle_button_set_state (GtkToggleButton *toggle_button,
+ gint state);
+</verb></tscreen>
+<p>
+This is described in the section on toggle buttons, and works in exactly the
+same way.
+<p>
+The following example creates a radio button group with three buttons.
+
+<tscreen><verb>
+/* radiobuttons.c */
+
+#include <gtk/gtk.h>
+#include <glib.h>
+
+void close_application( GtkWidget *widget, gpointer *data ) {
+ gtk_main_quit();
+}
+
+main(int argc,char *argv[])
+{
+ static GtkWidget *window = NULL;
+ GtkWidget *box1;
+ GtkWidget *box2;
+ GtkWidget *button;
+ GtkWidget *separator;
+ GSList *group;
+
+ gtk_init(&amp;argc,&amp;argv);
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+ gtk_signal_connect (GTK_OBJECT (window), "delete_event",
+ GTK_SIGNAL_FUNC(close_application),
+ NULL);
+
+ gtk_window_set_title (GTK_WINDOW (window), "radio buttons");
+ gtk_container_border_width (GTK_CONTAINER (window), 0);
+
+ box1 = gtk_vbox_new (FALSE, 0);
+ gtk_container_add (GTK_CONTAINER (window), box1);
+ gtk_widget_show (box1);
+
+ box2 = gtk_vbox_new (FALSE, 10);
+ gtk_container_border_width (GTK_CONTAINER (box2), 10);
+ gtk_box_pack_start (GTK_BOX (box1), box2, TRUE, TRUE, 0);
+ gtk_widget_show (box2);
+
+ button = gtk_radio_button_new_with_label (NULL, "button1");
+ gtk_box_pack_start (GTK_BOX (box2), button, TRUE, TRUE, 0);
+ gtk_widget_show (button);
+
+ group = gtk_radio_button_group (GTK_RADIO_BUTTON (button));
+ button = gtk_radio_button_new_with_label(group, "button2");
+ gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (button), TRUE);
+ gtk_box_pack_start (GTK_BOX (box2), button, TRUE, TRUE, 0);
+ gtk_widget_show (button);
+
+ group = gtk_radio_button_group (GTK_RADIO_BUTTON (button));
+ button = gtk_radio_button_new_with_label(group, "button3");
+ gtk_box_pack_start (GTK_BOX (box2), button, TRUE, TRUE, 0);
+ gtk_widget_show (button);
+
+ separator = gtk_hseparator_new ();
+ gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 0);
+ gtk_widget_show (separator);
+
+ box2 = gtk_vbox_new (FALSE, 10);
+ gtk_container_border_width (GTK_CONTAINER (box2), 10);
+ gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, TRUE, 0);
+ gtk_widget_show (box2);
+
+ button = gtk_button_new_with_label ("close");
+ gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
+ GTK_SIGNAL_FUNC(close_application),
+ GTK_OBJECT (window));
+ gtk_box_pack_start (GTK_BOX (box2), button, TRUE, TRUE, 0);
+ GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
+ gtk_widget_grab_default (button);
+ gtk_widget_show (button);
+ gtk_widget_show (window);
+
+ gtk_main();
+ return(0);
+}
+</verb></tscreen>
+
+You can shorten this slightly by using the following syntax, which
+removes the need for a variable to hold the list of buttons:
+
+<tscreen><verb>
+ button2 = gtk_radio_button_new_with_label(
+ gtk_radio_button_group (GTK_RADIO_BUTTON (button1)),
+ "button2");
+</verb></tscreen>
+
+<!-- ***************************************************************** -->
+<sect> Miscallaneous Widgets
+<!-- ***************************************************************** -->
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Labels
+<p>
+Labels are used a lot in GTK, and are relatively simple. Labels emit no
+signals as they do not have an associated X window. If you need to catch
+signals, or do clipping, use the EventBox widget.
+
+To create a new label, use:
+
+<tscreen><verb>
+GtkWidget* gtk_label_new (char *str);
+</verb></tscreen>
+
+Where the sole argument is the string you wish the label to display.
+
+To change the label's text after creation, use the function:
+
+<tscreen><verb>
+void gtk_label_set (GtkLabel *label,
+ char *str);
+</verb></tscreen>
+<p>
+Where the first argument is the label you created previously (casted using
+the GTK_LABEL() macro), and the second is the new string.
+
+The space needed for the new string will be automatically adjusted if needed.
+
+To retrieve the current string, use:
+
+<tscreen><verb>
+void gtk_label_get (GtkLabel *label,
+ char **str);
+</verb></tscreen>
+
+Where the first arguement is the label you've created, and the second, the
+return for the string.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>The Tooltips Widget
+<p>
+These are the little text strings that pop up when you leave your pointer
+over a button or other widget for a few seconds. They are easy to use, so I
+will just explain them without giving an example. If you want to see some
+code, take a look at the testgtk.c program distributed with GDK.
+<p>
+Some widgets (such as the label) will not work with tooltips.
+<p>
+The first call you will use to create a new tooltip. You only need to do
+this once in a given function. The GtkTooltip this function returns can be
+used to create multiple tooltips.
+
+<tscreen><verb>
+GtkTooltips *gtk_tooltips_new (void);
+</verb></tscreen>
+
+Once you have created a new tooltip, and the widget you wish to use it on,
+simply use this call to set it.
+
+<tscreen><verb>
+void gtk_tooltips_set_tips (GtkTooltips *tooltips,
+ GtkWidget *widget,
+ gchar *tips_text);
+</verb></tscreen>
+
+The first argument is the tooltip you've already created, followed by the
+widget you wish to have this tooltip pop up for, and the text you wish it to
+say.
+<p>
+Here's a short example:
+
+<tscreen><verb>
+GtkTooltips *tooltips;
+GtkWidget *button;
+...
+tooltips = gtk_tooltips_new ();
+button = gtk_button_new_with_label ("button 1");
+...
+gtk_tooltips_set_tips (tooltips, button, "This is button 1");
+</verb></tscreen>
+
+
+There are other calls used with tooltips. I will just list them with a
+brief description of what they do.
+
+<tscreen><verb>
+void gtk_tooltips_destroy (GtkTooltips *tooltips);
+</verb></tscreen>
+
+Destroy the created tooltips.
+
+<tscreen><verb>
+void gtk_tooltips_enable (GtkTooltips *tooltips);
+</verb></tscreen>
+
+Enable a disabled set of tooltips.
+
+<tscreen><verb>
+void gtk_tooltips_disable (GtkTooltips *tooltips);
+</verb></tscreen>
+
+Disable an enabled set of tooltips.
+
+<tscreen><verb>
+void gtk_tooltips_set_delay (GtkTooltips *tooltips,
+ gint delay);
+
+</verb></tscreen>
+Sets how many milliseconds you have to hold you pointer over the widget before the
+tooltip will pop up. The default is 1000 milliseconds or 1 second.
+
+<tscreen><verb>
+void gtk_tooltips_set_tips (GtkTooltips *tooltips,
+ GtkWidget *widget,
+ gchar *tips_text);
+</verb></tscreen>
+
+Change the tooltip text of an already created tooltip.
+
+<tscreen><verb>
+void gtk_tooltips_set_colors (GtkTooltips *tooltips,
+ GdkColor *background,
+ GdkColor *foreground);
+</verb></tscreen>
+
+Set the foreground and background color of the tooltips. Again, I have no
+idea how to specify the colors.
+<p>
+And that's all the functions associated with tooltips. More than you'll
+ever want to know :)
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Progress Bars
+<p>
+Progress bars are used to show the status of an operation. They are pretty
+easy to use, as you will see with the code below. But first lets start out
+with the call to create a new progress bar.
+
+<tscreen><verb>
+GtkWidget *gtk_progress_bar_new (void);
+</verb></tscreen>
+
+Now that the progress bar has been created we can use it.
+
+<tscreen><verb>
+void gtk_progress_bar_update (GtkProgressBar *pbar, gfloat percentage);
+</verb></tscreen>
+
+The first argument is the progress bar you wish to operate on, and the second
+argument is the amount 'completed', meaning the amount the progress bar has
+been filled from 0-100% (a real number between 0 and 1).
+
+Progress Bars are usually used with timeouts or other such functions (see
+section on <ref id="sec_timeouts" name="Timeouts, I/O and Idle Functions">)
+to give the illusion of multitasking. All will employ
+the gtk_progress_bar_update function in the same manner.
+
+Here is an example of the progress bar, updated using timeouts. This
+code also shows you how to reset the Progress Bar.
+
+<tscreen><verb>
+/* progressbar.c */
+
+#include <gtk/gtk.h>
+
+static int ptimer = 0;
+int pstat = TRUE;
+
+/* This function increments and updates the progress bar, it also resets
+ the progress bar if pstat is FALSE */
+gint progress (gpointer data)
+{
+ gfloat pvalue;
+
+ /* get the current value of the progress bar */
+ pvalue = GTK_PROGRESS_BAR (data)->percentage;
+
+ if ((pvalue >= 1.0) || (pstat == FALSE)) {
+ pvalue = 0.0;
+ pstat = TRUE;
+ }
+ pvalue += 0.01;
+
+ gtk_progress_bar_update (GTK_PROGRESS_BAR (data), pvalue);
+
+ return TRUE;
+}
+
+/* This function signals a reset of the progress bar */
+void progress_r (void)
+{
+ pstat = FALSE;
+}
+
+void destroy (GtkWidget *widget, gpointer *data)
+{
+ gtk_main_quit ();
+}
+
+int main (int argc, char *argv[])
+{
+ GtkWidget *window;
+ GtkWidget *button;
+ GtkWidget *label;
+ GtkWidget *table;
+ GtkWidget *pbar;
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+ gtk_signal_connect (GTK_OBJECT (window), "delete_event",
+ GTK_SIGNAL_FUNC (destroy), NULL);
+
+ gtk_container_border_width (GTK_CONTAINER (window), 10);
+
+ table = gtk_table_new(3,2,TRUE);
+ gtk_container_add (GTK_CONTAINER (window), table);
+
+ label = gtk_label_new ("Progress Bar Example");
+ gtk_table_attach_defaults(GTK_TABLE(table), label, 0,2,0,1);
+ gtk_widget_show(label);
+
+ /* Create a new progress bar, pack it into the table, and show it */
+ pbar = gtk_progress_bar_new ();
+ gtk_table_attach_defaults(GTK_TABLE(table), pbar, 0,2,1,2);
+ gtk_widget_show (pbar);
+
+ /* Set the timeout to handle automatic updating of the progress bar */
+ ptimer = gtk_timeout_add (100, progress, pbar);
+
+ /* This button signals the progress bar to be reset */
+ button = gtk_button_new_with_label ("Reset");
+ gtk_signal_connect (GTK_OBJECT (button), "clicked",
+ GTK_SIGNAL_FUNC (progress_r), NULL);
+ gtk_table_attach_defaults(GTK_TABLE(table), button, 0,1,2,3);
+ gtk_widget_show(button);
+
+ button = gtk_button_new_with_label ("Cancel");
+ gtk_signal_connect (GTK_OBJECT (button), "clicked",
+ GTK_SIGNAL_FUNC (destroy), NULL);
+
+ gtk_table_attach_defaults(GTK_TABLE(table), button, 1,2,2,3);
+ gtk_widget_show (button);
+
+ gtk_widget_show(table);
+ gtk_widget_show(window);
+
+ gtk_main ();
+
+ return 0;
+}
+</verb></tscreen>
+
+In this small program there are four areas that concern the general operation
+of Progress Bars, we will look at them in the order they are called.
+
+<tscreen><verb>
+pbar = gtk_progress_bar_new ();
+</verb></tscreen>
+
+This code creates a new progress bar, called pbar.
+
+<tscreen><verb>
+ptimer = gtk_timeout_add (100, progress, pbar);
+</verb></tscreen>
+
+This code, uses timeouts to enable a constant time interval, timeouts are
+not necessary in the use of Progress Bars.
+
+<tscreen><verb>
+pvalue = GTK_PROGRESS_BAR (data)->percentage;
+</verb></tscreen>
+
+This code assigns the current value of the percentage bar to pvalue.
+
+<tscreen><verb>
+gtk_progress_bar_update (GTK_PROGRESS_BAR (data), pvalue);
+</verb></tscreen>
+
+Finally, this code updates the progress bar with the value of pvalue
+
+And that is all there is to know about Progress Bars, enjoy.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Dialogs
+<p>
+
+The Dialog widget is very simple, and is actually just a window with a few
+things pre-packed into it for you. The structure for a Dialog is:
+
+<tscreen><verb>
+struct GtkDialog
+{
+ GtkWindow window;
+
+ GtkWidget *vbox;
+ GtkWidget *action_area;
+};
+</verb></tscreen>
+
+So you see, it simple creates a window, and then packs a vbox into the top,
+then a seperator, and then an hbox for the "action_area".
+
+The Dialog widget can be used for pop-up messages to the user, and
+other similar tasks. It is really basic, and there is only one
+function for the dialog box, which is:
+
+<tscreen><verb>
+GtkWidget* gtk_dialog_new (void);
+</verb></tscreen>
+
+So to create a new dialog box, use,
+
+<tscreen><verb>
+GtkWidget window;
+window = gtk_dialog_new ();
+</verb></tscreen>
+
+This will create the dialog box, and it is now up to you to use it.
+you could pack a button in the action_area by doing something like so:
+
+<tscreen><verb>
+button = ...
+gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->action_area), button,
+ TRUE, TRUE, 0);
+gtk_widget_show (button);
+</verb></tscreen>
+
+And you could add to the vbox area by packing, for instance, a label
+in it, try something like this:
+
+<tscreen><verb>
+label = gtk_label_new ("Dialogs are groovy");
+gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->vbox), label, TRUE,
+ TRUE, 0);
+gtk_widget_show (label);
+</verb></tscreen>
+
+As an example in using the dialog box, you could put two buttons in
+the action_area, a Cancel button and an Ok button, and a label in the vbox
+area, asking the user a question or giving an error etc. Then you could
+attach a different signal to each of the buttons and perform the
+operation the user selects.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Pixmaps
+<p>
+Pixmaps are data structures that contain pictures. These pictures can be
+used in various places, but most visibly as icons on the X-Windows desktop,
+or as cursors. A bitmap is a 2-color pixmap.
+
+To use pixmaps in GTK, we must first build a GdkPixmap structure using
+routines from the GDK layer. Pixmaps can either be created from in-memory
+data, or from data read from a file. We'll go through each of the calls
+to create a pixmap.
+
+<tscreen><verb>
+GdkPixmap *gdk_bitmap_create_from_data( GdkWindow *window,
+ gchar *data,
+ gint width,
+ gint height );
+</verb></tscreen>
+<p>
+This routine is used to create a single-plane pixmap (2 colors) from data in
+memory. Each bit of the data represents whether that pixel is off or on.
+Width and height are in pixels. The GdkWindow pointer is to the current
+window, since a pixmap resources are meaningful only in the context of the
+screen where it is to be displayed.
+
+<tscreen><verb>
+GdkPixmap* gdk_pixmap_create_from_data( GdkWindow *window,
+ gchar *data,
+ gint width,
+ gint height,
+ gint depth,
+ GdkColor *fg,
+ GdkColor *bg );
+</verb></tscreen>
+
+This is used to create a pixmap of the given depth (number of colors) from
+the bitmap data specified. fg and bg are the foreground and background
+color to use.
+
+<tscreen><verb>
+GdkPixmap* gdk_pixmap_create_from_xpm( GdkWindow *window,
+ GdkBitmap **mask,
+ GdkColor *transparent_color,
+ const gchar *filename );
+</verb></tscreen>
+
+XPM format is a readable pixmap representation for the X Window System. It
+is widely used and many different utilities are available for creating image
+files in this format. The file specified by filename must contain an image
+in that format and it is loaded into the pixmap structure. The mask specifies
+what bits of the pixmap are opaque. All other bits are colored using the
+color specified by transparent_color. An example using this follows below.
+
+<tscreen><verb>
+GdkPixmap* gdk_pixmap_create_from_xpm_d (GdkWindow *window,
+ GdkBitmap **mask,
+ GdkColor *transparent_color,
+ gchar **data);
+</verb></tscreen>
+
+Small images can be incorporated into a program as data in the XPM format.
+A pixmap is created using this data, instead of reading it from a file.
+An example of such data is
+
+<tscreen><verb>
+/* XPM */
+static const char * xpm_data[] = {
+"16 16 3 1",
+" c None",
+". c #000000000000",
+"X c #FFFFFFFFFFFF",
+" ",
+" ...... ",
+" .XXX.X. ",
+" .XXX.XX. ",
+" .XXX.XXX. ",
+" .XXX..... ",
+" .XXXXXXX. ",
+" .XXXXXXX. ",
+" .XXXXXXX. ",
+" .XXXXXXX. ",
+" .XXXXXXX. ",
+" .XXXXXXX. ",
+" .XXXXXXX. ",
+" ......... ",
+" ",
+" "};
+</verb></tscreen>
+
+<tscreen><verb>
+void gdk_pixmap_destroy( GdkPixmap *pixmap );
+</verb></tscreen>
+<p>
+When we're done using a pixmap and not likely to reuse it again soon,
+it is a good idea to release the resource using gdk_pixmap_destroy. Pixmaps
+should be considered a precious resource.
+
+
+Once we've created a pixmap, we can display it as a GTK widget. We must
+create a pixmap widget to contain the GDK pixmap. This is done using
+
+<tscreen><verb>
+GtkWidget* gtk_pixmap_new( GdkPixmap *pixmap,
+ GdkBitmap *mask );
+</verb></tscreen>
+<p>
+The other pixmap widget calls are
+
+<tscreen><verb>
+guint gtk_pixmap_get_type( void );
+void gtk_pixmap_set( GtkPixmap *pixmap,
+ GdkPixmap *val,
+ GdkBitmap *mask);
+void gtk_pixmap_get( GtkPixmap *pixmap,
+ GdkPixmap **val,
+ GdkBitmap **mask);
+</verb></tscreen>
+<p>
+gtk_pixmap_set is used to change the pixmap that the widget is currently
+managing. Val is the pixmap created using GDK.
+
+The following is an example of using a pixmap in a button.
+
+<tscreen><verb>
+/* pixmap.c */
+
+#include <gtk/gtk.h>
+
+
+/* XPM data of Open-File icon */
+static const char * xpm_data[] = {
+"16 16 3 1",
+" c None",
+". c #000000000000",
+"X c #FFFFFFFFFFFF",
+" ",
+" ...... ",
+" .XXX.X. ",
+" .XXX.XX. ",
+" .XXX.XXX. ",
+" .XXX..... ",
+" .XXXXXXX. ",
+" .XXXXXXX. ",
+" .XXXXXXX. ",
+" .XXXXXXX. ",
+" .XXXXXXX. ",
+" .XXXXXXX. ",
+" .XXXXXXX. ",
+" ......... ",
+" ",
+" "};
+
+
+/* when invoked (via signal delete_event), terminates the application.
+ */
+void close_application( GtkWidget *widget, gpointer *data ) {
+ gtk_main_quit();
+}
+
+
+/* is invoked when the button is clicked. It just prints a message.
+ */
+void button_clicked( GtkWidget *widget, gpointer *data ) {
+ printf( "button clicked\n" );
+}
+
+int main( int argc, char *argv[] )
+{
+ /* GtkWidget is the storage type for widgets */
+ GtkWidget *window, *pixmapwid, *button;
+ GdkPixmap *pixmap;
+ GdkBitmap *mask;
+ GtkStyle *style;
+
+ /* create the main window, and attach delete_event signal to terminating
+ the application */
+ gtk_init( &amp;argc, &amp;argv );
+ window = gtk_window_new( GTK_WINDOW_TOPLEVEL );
+ gtk_signal_connect( GTK_OBJECT (window), "delete_event",
+ GTK_SIGNAL_FUNC (close_application), NULL );
+ gtk_container_border_width( GTK_CONTAINER (window), 10 );
+ gtk_widget_show( window );
+
+ /* now for the pixmap from gdk */
+ style = gtk_widget_get_style( window );
+ pixmap = gdk_pixmap_create_from_xpm_d( window->window, &amp;mask,
+ &amp;style->bg[GTK_STATE_NORMAL],
+ (gchar **)xpm_data );
+
+ /* a pixmap widget to contain the pixmap */
+ pixmapwid = gtk_pixmap_new( pixmap, mask );
+ gtk_widget_show( pixmapwid );
+
+ /* a button to contain the pixmap widget */
+ button = gtk_button_new();
+ gtk_container_add( GTK_CONTAINER(button), pixmapwid );
+ gtk_container_add( GTK_CONTAINER(window), button );
+ gtk_widget_show( button );
+
+ gtk_signal_connect( GTK_OBJECT(button), "clicked",
+ GTK_SIGNAL_FUNC(button_clicked), NULL );
+
+ /* show the window */
+ gtk_main ();
+
+ return 0;
+}
+</verb></tscreen>
+
+
+To load a file from an XPM data file called icon0.xpm in the current
+directory, we would have created the pixmap thus
+
+<tscreen><verb>
+ /* load a pixmap from a file */
+ pixmap = gdk_pixmap_create_from_xpm( window->window, &amp;mask,
+ &amp;style->bg[GTK_STATE_NORMAL],
+ "./icon0.xpm" );
+ pixmapwid = gtk_pixmap_new( pixmap, mask );
+ gtk_widget_show( pixmapwid );
+ gtk_container_add( GTK_CONTAINER(window), pixmapwid );
+</verb></tscreen>
+
+
+
+Using Shapes
+<p>
+A disadvantage of using pixmaps is that the displayed object is always
+rectangular, regardless of the image. We would like to create desktops
+and applications with icons that have more natural shapes. For example,
+for a game interface, we would like to have round buttons to push. The
+way to do this is using shaped windows.
+
+A shaped window is simply a pixmap where the background pixels are
+transparent. This way, when the background image is multi-colored, we
+don't overwrite it with a rectangular, non-matching border around our
+icon. The following example displays a full wheelbarrow image on the
+desktop.
+
+<tscreen><verb>
+/* wheelbarrow.c */
+
+#include <gtk/gtk.h>
+
+/* XPM */
+static char * WheelbarrowFull_xpm[] = {
+"48 48 64 1",
+" c None",
+". c #DF7DCF3CC71B",
+"X c #965875D669A6",
+"o c #71C671C671C6",
+"O c #A699A289A699",
+"+ c #965892489658",
+"@ c #8E38410330C2",
+"# c #D75C7DF769A6",
+"$ c #F7DECF3CC71B",
+"% c #96588A288E38",
+"&amp; c #A69992489E79",
+"* c #8E3886178E38",
+"= c #104008200820",
+"- c #596510401040",
+"; c #C71B30C230C2",
+": c #C71B9A699658",
+"> c #618561856185",
+", c #20811C712081",
+"< c #104000000000",
+"1 c #861720812081",
+"2 c #DF7D4D344103",
+"3 c #79E769A671C6",
+"4 c #861782078617",
+"5 c #41033CF34103",
+"6 c #000000000000",
+"7 c #49241C711040",
+"8 c #492445144924",
+"9 c #082008200820",
+"0 c #69A618611861",
+"q c #B6DA71C65144",
+"w c #410330C238E3",
+"e c #CF3CBAEAB6DA",
+"r c #71C6451430C2",
+"t c #EFBEDB6CD75C",
+"y c #28A208200820",
+"u c #186110401040",
+"i c #596528A21861",
+"p c #71C661855965",
+"a c #A69996589658",
+"s c #30C228A230C2",
+"d c #BEFBA289AEBA",
+"f c #596545145144",
+"g c #30C230C230C2",
+"h c #8E3882078617",
+"j c #208118612081",
+"k c #38E30C300820",
+"l c #30C2208128A2",
+"z c #38E328A238E3",
+"x c #514438E34924",
+"c c #618555555965",
+"v c #30C2208130C2",
+"b c #38E328A230C2",
+"n c #28A228A228A2",
+"m c #41032CB228A2",
+"M c #104010401040",
+"N c #492438E34103",
+"B c #28A2208128A2",
+"V c #A699596538E3",
+"C c #30C21C711040",
+"Z c #30C218611040",
+"A c #965865955965",
+"S c #618534D32081",
+"D c #38E31C711040",
+"F c #082000000820",
+" ",
+" .XoO ",
+" +@#$%o&amp; ",
+" *=-;#::o+ ",
+" >,<12#:34 ",
+" 45671#:X3 ",
+" +89<02qwo ",
+"e* >,67;ro ",
+"ty> 459@>+&amp;&amp; ",
+"$2u+ ><ipas8* ",
+"%$;=* *3:.Xa.dfg> ",
+"Oh$;ya *3d.a8j,Xe.d3g8+ ",
+" Oh$;ka *3d$a8lz,,xxc:.e3g54 ",
+" Oh$;kO *pd$%svbzz,sxxxxfX..&amp;wn> ",
+" Oh$@mO *3dthwlsslszjzxxxxxxx3:td8M4 ",
+" Oh$@g&amp; *3d$XNlvvvlllm,mNwxxxxxxxfa.:,B* ",
+" Oh$@,Od.czlllllzlmmqV@V#V@fxxxxxxxf:%j5&amp; ",
+" Oh$1hd5lllslllCCZrV#r#:#2AxxxxxxxxxcdwM* ",
+" OXq6c.%8vvvllZZiqqApA:mq:Xxcpcxxxxxfdc9* ",
+" 2r<6gde3bllZZrVi7S@SV77A::qApxxxxxxfdcM ",
+" :,q-6MN.dfmZZrrSS:#riirDSAX@Af5xxxxxfevo",
+" +A26jguXtAZZZC7iDiCCrVVii7Cmmmxxxxxx%3g",
+" *#16jszN..3DZZZZrCVSA2rZrV7Dmmwxxxx&amp;en",
+" p2yFvzssXe:fCZZCiiD7iiZDiDSSZwwxx8e*>",
+" OA1<jzxwwc:$d%NDZZZZCCCZCCZZCmxxfd.B ",
+" 3206Bwxxszx%et.eaAp77m77mmmf3&amp;eeeg* ",
+" @26MvzxNzvlbwfpdettttttttttt.c,n&amp; ",
+" *;16=lsNwwNwgsvslbwwvccc3pcfu<o ",
+" p;<69BvwwsszslllbBlllllllu<5+ ",
+" OS0y6FBlvvvzvzss,u=Blllj=54 ",
+" c1-699Blvlllllu7k96MMMg4 ",
+" *10y8n6FjvllllB<166668 ",
+" S-kg+>666<M<996-y6n<8* ",
+" p71=4 m69996kD8Z-66698&amp;&amp; ",
+" &amp;i0ycm6n4 ogk17,0<6666g ",
+" N-k-<> >=01-kuu666> ",
+" ,6ky&amp; &amp;46-10ul,66, ",
+" Ou0<> o66y<ulw<66&amp; ",
+" *kk5 >66By7=xu664 ",
+" <<M4 466lj<Mxu66o ",
+" *>> +66uv,zN666* ",
+" 566,xxj669 ",
+" 4666FF666> ",
+" >966666M ",
+" oM6668+ ",
+" *4 ",
+" ",
+" "};
+
+
+/* when invoked (via signal delete_event), terminates the application.
+ */
+void close_application( GtkWidget *widget, gpointer *data ) {
+ gtk_main_quit();
+}
+
+int main (int argc, char *argv[])
+{
+ /* GtkWidget is the storage type for widgets */
+ GtkWidget *window, *pixmap, *fixed;
+ GdkPixmap *gdk_pixmap;
+ GdkBitmap *mask;
+ GtkStyle *style;
+ GdkGC *gc;
+
+ /* create the main window, and attach delete_event signal to terminate
+ the application. Note that the main window will not have a titlebar
+ since we're making it a popup. */
+ gtk_init (&amp;argc, &amp;argv);
+ window = gtk_window_new( GTK_WINDOW_POPUP );
+ gtk_signal_connect (GTK_OBJECT (window), "delete_event",
+ GTK_SIGNAL_FUNC (close_application), NULL);
+ gtk_widget_show (window);
+
+ /* now for the pixmap and the pixmap widget */
+ style = gtk_widget_get_default_style();
+ gc = style->black_gc;
+ gdk_pixmap = gdk_pixmap_create_from_xpm_d( window->window, &amp;mask,
+ &amp;style->bg[GTK_STATE_NORMAL],
+ WheelbarrowFull_xpm );
+ pixmap = gtk_pixmap_new( gdk_pixmap, mask );
+ gtk_widget_show( pixmap );
+
+ /* To display the pixmap, we use a fixed widget to place the pixmap */
+ fixed = gtk_fixed_new();
+ gtk_widget_set_usize( fixed, 200, 200 );
+ gtk_fixed_put( GTK_FIXED(fixed), pixmap, 0, 0 );
+ gtk_container_add( GTK_CONTAINER(window), fixed );
+ gtk_widget_show( fixed );
+
+ /* This masks out everything except for the image itself */
+ gtk_widget_shape_combine_mask( window, mask, 0, 0 );
+
+ /* show the window */
+ gtk_widget_set_uposition( window, 20, 400 );
+ gtk_widget_show( window );
+ gtk_main ();
+
+ return 0;
+}
+</verb></tscreen>
+<p>
+To make the wheelbarrow image sensitive, we could attach the button press
+event signal to make it do something. The following few lines would make
+the picture sensitive to a mouse button being pressed which makes the
+application terminate.
+
+<tscreen><verb>
+gtk_widget_set_events( window,
+ gtk_widget_get_events( window ) |
+ GDK_BUTTON_PRESS_MASK );
+
+gtk_signal_connect( GTK_OBJECT(window), "button_press_event",
+ GTK_SIGNAL_FUNC(close_application), NULL );
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Rulers
+<p>
+Ruler widgets are used to indicate the location of the mouse pointer
+in a given window. A window can have a vertical ruler spanning across
+the width and a horizontal ruler spanning down the height. A small
+triangular indicator on the ruler shows the exact location of the
+pointer relative to the ruler.
+
+A ruler must first be created. Horizontal and vertical rulers are
+created using
+
+<tscreen><verb>
+GtkWidget *gtk_hruler_new(void); /* horizontal ruler */
+GtkWidget *gtk_vruler_new(void); /* vertical ruler */
+</verb></tscreen>
+
+Once a ruler is created, we can define the unit of measurement. Units
+of measure for rulers can be GTK_PIXELS, GTK_INCHES or
+GTK_CENTIMETERS. This is set using
+
+<tscreen><verb>
+void gtk_ruler_set_metric( GtkRuler *ruler,
+ GtkMetricType metric );
+</verb></tscreen>
+
+The default measure is GTK_PIXELS.
+
+<tscreen><verb>
+gtk_ruler_set_metric( GTK_RULER(ruler), GTK_PIXELS );
+</verb></tscreen>
+
+Other important characteristics of a ruler are how to mark the units
+of scale and where the position indicator is initially placed. These
+are set for a ruler using
+
+<tscreen><verb>
+void gtk_ruler_set_range (GtkRuler *ruler,
+ gfloat lower,
+ gfloat upper,
+ gfloat position,
+ gfloat max_size);
+</verb></tscreen>
+
+The lower and upper arguments define the extents of the ruler, and
+max_size is the largest possible number that will be displayed.
+Position defines the initial position of the pointer indicator within
+the ruler.
+
+A vertical ruler can span an 800 pixel wide window thus
+
+<tscreen><verb>
+gtk_ruler_set_range( GTK_RULER(vruler), 0, 800, 0, 800);
+</verb></tscreen>
+
+The markings displayed on the ruler will be from 0 to 800, with
+a number for every 100 pixels. If instead we wanted the ruler to
+range from 7 to 16, we would code
+
+<tscreen><verb>
+gtk_ruler_set_range( GTK_RULER(vruler), 7, 16, 0, 20);
+</verb></tscreen>
+
+The indicator on the ruler is a small triangular mark that indicates
+the position of the pointer relative to the ruler. If the ruler is
+used to follow the mouse pointer, the motion_notify_event signal
+should be connected to the motion_notify_event method of the ruler.
+To follow all mouse movements within a window area, we would use
+
+<tscreen><verb>
+#define EVENT_METHOD(i, x) GTK_WIDGET_CLASS(GTK_OBJECT(i)->klass)->x
+
+gtk_signal_connect_object( GTK_OBJECT(area), "motion_notify_event",
+ (GtkSignalFunc)EVENT_METHOD(ruler, motion_notify_event),
+ GTK_OBJECT(ruler) );
+</verb></tscreen>
+
+The following example creates a drawing area with a horizontal ruler
+above it and a vertical ruler to the left of it. The size of the
+drawing area is 600 pixels wide by 400 pixels high. The horizontal
+ruler spans from 7 to 13 with a mark every 100 pixels, while the
+vertical ruler spans from 0 to 400 with a mark every 100 pixels.
+Placement of the drawing area and the rulers are done using a table.
+
+<tscreen><verb>
+/* rulers.c */
+
+#include <gtk/gtk.h>
+
+#define EVENT_METHOD(i, x) GTK_WIDGET_CLASS(GTK_OBJECT(i)->klass)->x
+
+#define XSIZE 600
+#define YSIZE 400
+
+/* this routine gets control when the close button is clicked
+ */
+void close_application( GtkWidget *widget, gpointer *data ) {
+ gtk_main_quit();
+}
+
+
+/* the main routine
+ */
+int main( int argc, char *argv[] ) {
+ GtkWidget *window, *table, *area, *hrule, *vrule;
+
+ /* initialize gtk and create the main window */
+ gtk_init( &amp;argc, &amp;argv );
+
+ window = gtk_window_new( GTK_WINDOW_TOPLEVEL );
+ gtk_signal_connect (GTK_OBJECT (window), "delete_event",
+ GTK_SIGNAL_FUNC( close_application ), NULL);
+ gtk_container_border_width (GTK_CONTAINER (window), 10);
+
+ /* create a table for placing the ruler and the drawing area */
+ table = gtk_table_new( 3, 2, FALSE );
+ gtk_container_add( GTK_CONTAINER(window), table );
+
+ area = gtk_drawing_area_new();
+ gtk_drawing_area_size( (GtkDrawingArea *)area, XSIZE, YSIZE );
+ gtk_table_attach( GTK_TABLE(table), area, 1, 2, 1, 2,
+ GTK_EXPAND|GTK_FILL, GTK_FILL, 0, 0 );
+ gtk_widget_set_events( area, GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK );
+
+ /* The horizontal ruler goes on top. As the mouse moves across the drawing area,
+ a motion_notify_event is passed to the appropriate event handler for the ruler. */
+ hrule = gtk_hruler_new();
+ gtk_ruler_set_metric( GTK_RULER(hrule), GTK_PIXELS );
+ gtk_ruler_set_range( GTK_RULER(hrule), 7, 13, 0, 20 );
+ gtk_signal_connect_object( GTK_OBJECT(area), "motion_notify_event",
+ (GtkSignalFunc)EVENT_METHOD(hrule, motion_notify_event),
+ GTK_OBJECT(hrule) );
+ /* GTK_WIDGET_CLASS(GTK_OBJECT(hrule)->klass)->motion_notify_event, */
+ gtk_table_attach( GTK_TABLE(table), hrule, 1, 2, 0, 1,
+ GTK_EXPAND|GTK_SHRINK|GTK_FILL, GTK_FILL, 0, 0 );
+
+ /* The vertical ruler goes on the left. As the mouse moves across the drawing area,
+ a motion_notify_event is passed to the appropriate event handler for the ruler. */
+ vrule = gtk_vruler_new();
+ gtk_ruler_set_metric( GTK_RULER(vrule), GTK_PIXELS );
+ gtk_ruler_set_range( GTK_RULER(vrule), 0, YSIZE, 10, YSIZE );
+ gtk_signal_connect_object( GTK_OBJECT(area), "motion_notify_event",
+ (GtkSignalFunc)
+ GTK_WIDGET_CLASS(GTK_OBJECT(vrule)->klass)->motion_notify_event,
+ GTK_OBJECT(vrule) );
+ gtk_table_attach( GTK_TABLE(table), vrule, 0, 1, 1, 2,
+ GTK_FILL, GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0 );
+
+ /* now show everything */
+ gtk_widget_show( area );
+ gtk_widget_show( hrule );
+ gtk_widget_show( vrule );
+ gtk_widget_show( table );
+ gtk_widget_show( window );
+ gtk_main();
+
+ return 0;
+}
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Statusbars
+<p>
+Statusbars are simple widgets used to display a text message. They keep a stack
+of the messages pushed onto them, so that popping the current message
+will re-display the previous text message.
+
+In order to allow different parts of an application to use the same statusbar to display
+messages, the statusbar widget issues Context Identifiers which are used to identify
+different 'users'. The message on top of the stack is the one displayed, no matter what context
+it is in. Messages are stacked in last-in-first-out order, not context identifier order.
+
+A statusbar is created with a call to:
+<tscreen><verb>
+GtkWidget* gtk_statusbar_new (void);
+</verb></tscreen>
+
+A new Context Identifier is requested using a call to the following function with a short
+textual description of the context:
+<tscreen><verb>
+guint gtk_statusbar_get_context_id (GtkStatusbar *statusbar,
+ const gchar *context_description);
+</verb></tscreen>
+
+There are three functions that can operate on statusbars.
+<tscreen><verb>
+guint gtk_statusbar_push (GtkStatusbar *statusbar,
+ guint context_id,
+ gchar *text);
+
+void gtk_statusbar_pop (GtkStatusbar *statusbar)
+ guint context_id);
+void gtk_statusbar_remove (GtkStatusbar *statusbar,
+ guint context_id,
+ guint message_id);
+</verb></tscreen>
+
+The first, gtk_statusbar_push, is used to add a new message to the statusbar.
+It returns a Message Identifier, which can be passed later to the function gtk_statusbar_remove
+to remove the message with the given Message and Context Identifiers from the statusbar's stack.
+
+The function gtk_statusbar_pop removes the message highest in the stack with the given
+Context Identifier.
+
+The following example creates a statusbar and two buttons, one for pushing items
+onto the statusbar, and one for popping the last item back off.
+
+<tscreen><verb>
+/* statusbar.c */
+
+#include <gtk/gtk.h>
+#include <glib.h>
+
+GtkWidget *status_bar;
+
+void push_item (GtkWidget *widget, gpointer *data)
+{
+ static int count = 1;
+ char buff[20];
+
+ g_snprintf(buff, 20, "Item %d", count++);
+ gtk_statusbar_push( GTK_STATUSBAR(status_bar), (guint) &amp;data, buff);
+
+ return;
+}
+
+void pop_item (GtkWidget *widget, gpointer *data)
+{
+ gtk_statusbar_pop( GTK_STATUSBAR(status_bar), (guint) &amp;data );
+ return;
+}
+
+int main (int argc, char *argv[])
+{
+
+ GtkWidget *window;
+ GtkWidget *vbox;
+ GtkWidget *button;
+
+ int context_id;
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ /* create a new window */
+ window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_widget_set_usize( GTK_WIDGET (window), 200, 100);
+ gtk_window_set_title(GTK_WINDOW (window), "GTK Statusbar Example");
+ gtk_signal_connect(GTK_OBJECT (window), "delete_event",
+ (GtkSignalFunc) gtk_exit, NULL);
+
+ vbox = gtk_vbox_new(FALSE, 1);
+ gtk_container_add(GTK_CONTAINER(window), vbox);
+ gtk_widget_show(vbox);
+
+ status_bar = gtk_statusbar_new();
+ gtk_box_pack_start (GTK_BOX (vbox), status_bar, TRUE, TRUE, 0);
+ gtk_widget_show (status_bar);
+
+ context_id = gtk_statusbar_get_context_id( GTK_STATUSBAR(status_bar), "Statusbar example");
+
+ button = gtk_button_new_with_label("push item");
+ gtk_signal_connect(GTK_OBJECT(button), "clicked",
+ GTK_SIGNAL_FUNC (push_item), &amp;context_id);
+ gtk_box_pack_start(GTK_BOX(vbox), button, TRUE, TRUE, 2);
+ gtk_widget_show(button);
+
+ button = gtk_button_new_with_label("pop last item");
+ gtk_signal_connect(GTK_OBJECT(button), "clicked",
+ GTK_SIGNAL_FUNC (pop_item), &amp;context_id);
+ gtk_box_pack_start(GTK_BOX(vbox), button, TRUE, TRUE, 2);
+ gtk_widget_show(button);
+
+ /* always display the window as the last step so it all splashes on
+ * the screen at once. */
+ gtk_widget_show(window);
+
+ gtk_main ();
+
+ return 0;
+}
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Text Entries
+<p>
+The Entry widget allows text to be typed and displayed in a single line text box.
+The text may be set with functions calls that allow new text to replace,
+prepend or append the current contents of the Entry widget.
+
+There are two functions for creating Entry widgets:
+<tscreen><verb>
+GtkWidget* gtk_entry_new (void);
+
+GtkWidget* gtk_entry_new_with_max_length (guint16 max);
+</verb></tscreen>
+
+The first just creates a new Entry widget, whilst the second creates a new Entry and
+sets a limit on the length of the text within the Entry..
+
+The maximum length of the text within an entry widget may be changed by a call to the following
+function. If the current text is longer than this maximum, then it is upto us to alter the Entries
+contents appropriately.
+
+<tscreen><verb>
+void gtk_entry_set_max_length (GtkEntry *entry,
+ guint16 max);
+</verb></tscreen>
+
+There are several functions for altering the text which is currently within the Entry widget.
+<tscreen><verb>
+void gtk_entry_set_text (GtkEntry *entry,
+ const gchar *text);
+void gtk_entry_append_text (GtkEntry *entry,
+ const gchar *text);
+void gtk_entry_prepend_text (GtkEntry *entry,
+ const gchar *text);
+</verb></tscreen>
+
+The function gtk_entry_set_text sets the contents of the Entry widget, replacing the
+current contents. The functions gtk_entry_append_text and gtk_entry_prepend_text allow
+the current contents to be appended and prepended to.
+
+The next function allows the current insertion point to be set.
+<tscreen><verb>
+void gtk_entry_set_position (GtkEntry *entry,
+ gint position);
+</verb></tscreen>
+
+The contents of the Entry can be retrieved by using a call to the following function. This
+is useful in the callback functions described below.
+<tscreen><verb>
+gchar* gtk_entry_get_text (GtkEntry *entry);
+</verb></tscreen>
+
+If we don't want the contents of the Entry to be changed by someone typing into it, we
+can change it's edittable state.
+<tscreen><verb>
+void gtk_entry_set_editable (GtkEntry *entry,
+ gboolean editable);
+</verb></tscreen>
+
+This function allows us to toggle the edittable state of the Entry widget by passing in
+TRUE or FALSE values for the editable argument.
+
+If we are using the Entry where we don't want the text entered to be visible, for
+example when a password is being entered, we can use the following function, which
+also takes a boolean flag.
+<tscreen><verb>
+void gtk_entry_set_visibility (GtkEntry *entry,
+ gboolean visible);
+</verb></tscreen>
+
+A region of the text may be set as selected by using the following function. This would
+most often be used after setting some default text in an Entry, making it easy for the user
+to remove it.
+<tscreen><verb>
+void gtk_entry_select_region (GtkEntry *entry,
+ gint start,
+ gint end);
+</verb></tscreen>
+
+If we want to catch when the user has entered text, we can connect to the
+<tt/activate/ or <tt/changed/ signal. Activate is raised when the user hits
+the enter key within the Entry widget. Changed is raised when the text changes at all,
+e.g. for every character entered or removed.
+
+The following code is an example of using an Entry widget.
+<tscreen><verb>
+/* entry.c */
+
+#include <gtk/gtk.h>
+
+void enter_callback(GtkWidget *widget, GtkWidget *entry)
+{
+ gchar *entry_text;
+ entry_text = gtk_entry_get_text(GTK_ENTRY(entry));
+ printf("Entry contents: %s\n", entry_text);
+}
+
+void entry_toggle_editable (GtkWidget *checkbutton,
+ GtkWidget *entry)
+{
+ gtk_entry_set_editable(GTK_ENTRY(entry),
+ GTK_TOGGLE_BUTTON(checkbutton)->active);
+}
+
+void entry_toggle_visibility (GtkWidget *checkbutton,
+ GtkWidget *entry)
+{
+ gtk_entry_set_visibility(GTK_ENTRY(entry),
+ GTK_TOGGLE_BUTTON(checkbutton)->active);
+}
+
+int main (int argc, char *argv[])
+{
+
+ GtkWidget *window;
+ GtkWidget *vbox, *hbox;
+ GtkWidget *entry;
+ GtkWidget *button;
+ GtkWidget *check;
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ /* create a new window */
+ window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_widget_set_usize( GTK_WIDGET (window), 200, 100);
+ gtk_window_set_title(GTK_WINDOW (window), "GTK Entry");
+ gtk_signal_connect(GTK_OBJECT (window), "delete_event",
+ (GtkSignalFunc) gtk_exit, NULL);
+
+ vbox = gtk_vbox_new (FALSE, 0);
+ gtk_container_add (GTK_CONTAINER (window), vbox);
+ gtk_widget_show (vbox);
+
+ entry = gtk_entry_new_with_max_length (50);
+ gtk_signal_connect(GTK_OBJECT(entry), "activate",
+ GTK_SIGNAL_FUNC(enter_callback),
+ entry);
+ gtk_entry_set_text (GTK_ENTRY (entry), "hello");
+ gtk_entry_append_text (GTK_ENTRY (entry), " world");
+ gtk_entry_select_region (GTK_ENTRY (entry),
+ 0, GTK_ENTRY(entry)->text_length);
+ gtk_box_pack_start (GTK_BOX (vbox), entry, TRUE, TRUE, 0);
+ gtk_widget_show (entry);
+
+ hbox = gtk_hbox_new (FALSE, 0);
+ gtk_container_add (GTK_CONTAINER (vbox), hbox);
+ gtk_widget_show (hbox);
+
+ check = gtk_check_button_new_with_label("Editable");
+ gtk_box_pack_start (GTK_BOX (hbox), check, TRUE, TRUE, 0);
+ gtk_signal_connect (GTK_OBJECT(check), "toggled",
+ GTK_SIGNAL_FUNC(entry_toggle_editable), entry);
+ gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(check), TRUE);
+ gtk_widget_show (check);
+
+ check = gtk_check_button_new_with_label("Visible");
+ gtk_box_pack_start (GTK_BOX (hbox), check, TRUE, TRUE, 0);
+ gtk_signal_connect (GTK_OBJECT(check), "toggled",
+ GTK_SIGNAL_FUNC(entry_toggle_visibility), entry);
+ gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(check), TRUE);
+ gtk_widget_show (check);
+
+ button = gtk_button_new_with_label ("Close");
+ gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
+ GTK_SIGNAL_FUNC(gtk_exit),
+ GTK_OBJECT (window));
+ gtk_box_pack_start (GTK_BOX (vbox), button, TRUE, TRUE, 0);
+ GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
+ gtk_widget_grab_default (button);
+ gtk_widget_show (button);
+
+ gtk_widget_show(window);
+
+ gtk_main();
+ return(0);
+}
+</verb></tscreen>
+
+<!-- ***************************************************************** -->
+<sect> Container Widgets
+<!-- ***************************************************************** -->
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Notebooks
+<p>
+The NoteBook Widget is a collection of 'pages' that overlap each other,
+each page contains different information. This widget has become more common
+lately in GUI programming, and it is a good way to show blocks similar
+information that warrant separation in their display.
+
+The first function call you will need to know, as you can probably
+guess by now, is used to create a new notebook widget.
+
+<tscreen><verb>
+GtkWidget* gtk_notebook_new (void);
+</verb></tscreen>
+
+Once the notebook has been created, there are 12 functions that
+operate on the notebook widget. Let's look at them individually.
+
+The first one we will look at is how to position the page indicators.
+These page indicators or 'tabs' as they are referred to, can be positioned
+in four ways; top, bottom, left, or right.
+
+<tscreen><verb>
+void gtk_notebook_set_tab_pos (GtkNotebook *notebook, GtkPositionType pos);
+</verb></tscreen>
+
+GtkPostionType will be one of the following, and they are pretty self explanatory.
+<itemize>
+<item> GTK_POS_LEFT
+<item> GTK_POS_RIGHT
+<item> GTK_POS_TOP
+<item> GTK_POS_BOTTOM
+</itemize>
+
+GTK_POS_TOP is the default.
+
+Next we will look at how to add pages to the notebook. There are three
+ways to add pages to the NoteBook. Let's look at the first two together as
+they are quite similar.
+
+<tscreen><verb>
+void gtk_notebook_append_page (GtkNotebook *notebook, GtkWidget *child, GtkWidget *tab_label);
+
+void gtk_notebook_prepend_page (GtkNotebook *notebook, GtkWidget *child, GtkWidget *tab_label);
+</verb></tscreen>
+
+These functions add pages to the notebook by inserting them from the
+back of the notebook (append), or the front of the notebook (prepend).
+*child is the widget that is placed within the notebook page, and *tab_label is
+the label for the page being added.
+
+The final function for adding a page to the notebook contains all of
+the properties of the previous two, but it allows you to specify what position
+you want the page to be in the notebook.
+
+<tscreen><verb>
+void gtk_notebook_insert_page (GtkNotebook *notebook, GtkWidget *child, GtkWidget *tab_label, gint position);
+</verb></tscreen>
+
+The parameters are the same as _append_ and _prepend_ except it
+contains an extra parameter, position. This parameter is used to specify what
+place this page will inserted to.
+
+Now that we know how to add a page, lets see how we can remove a page
+from the notebook.
+
+<tscreen><verb>
+void gtk_notebook_remove_page (GtkNotebook *notebook, gint page_num);
+</verb></tscreen>
+
+This function takes the page specified by page_num and removes it from
+the widget *notebook.
+
+To find out what the current page is in a notebook use the function:
+
+<tscreen><verb>
+gint gtk_notebook_current_page (GtkNotebook *notebook);
+</verb></tscreen>
+
+These next two functions are simple calls to move the notebook page
+forward or backward. Simply provide the respective function call with the
+notebook widget you wish to operate on. Note: When the NoteBook is currently
+on the last page, and gtk_notebook_next_page is called, the notebook will
+wrap back to the first page. Likewise, if the NoteBook is on the first page,
+and gtk_notebook_prev_page is called, the notebook will wrap to the last page.
+
+<tscreen><verb>
+void gtk_notebook_next_page (GtkNoteBook *notebook);
+void gtk_notebook_prev_page (GtkNoteBook *notebook);
+</verb></tscreen>
+
+This next function sets the 'active' page. If you wish the
+notebook to be opened to page 5 for example, you would use this function.
+Without using this function, the notebook defaults to the first page.
+
+<tscreen><verb>
+void gtk_notebook_set_page (GtkNotebook *notebook, gint page_num);
+</verb></tscreen>
+
+The next two functions add or remove the notebook page tabs and the
+notebook border respectively.
+
+<tscreen><verb>
+void gtk_notebook_set_show_tabs (GtkNotebook *notebook, gint show_tabs);
+void gtk_notebook_set_show_border (GtkNotebook *notebook, gint show_border);
+</verb></tscreen>
+
+show_tabs and show_border can both be either TRUE or FALSE (0 or 1).
+
+Now lets look at an example, it is expanded from the testgtk.c code
+that comes with the GTK distribution, and it shows all 13 functions. This
+small program, creates a window with a notebook and six buttons. The notebook
+contains 11 pages, added in three different ways, appended, inserted, and
+prepended. The buttons allow you rotate the tab positions, add/remove the tabs
+and border, remove a page, change pages in both a forward and backward manner,
+and exit the program.
+
+<tscreen><verb>
+/* notebook.c */
+
+#include <gtk/gtk.h>
+
+/* This function rotates the position of the tabs */
+void rotate_book (GtkButton *button, GtkNotebook *notebook)
+{
+ gtk_notebook_set_tab_pos (notebook, (notebook->tab_pos +1) %4);
+}
+
+/* Add/Remove the page tabs and the borders */
+void tabsborder_book (GtkButton *button, GtkNotebook *notebook)
+{
+ gint tval = FALSE;
+ gint bval = FALSE;
+ if (notebook->show_tabs == 0)
+ tval = TRUE;
+ if (notebook->show_border == 0)
+ bval = TRUE;
+
+ gtk_notebook_set_show_tabs (notebook, tval);
+ gtk_notebook_set_show_border (notebook, bval);
+}
+
+/* Remove a page from the notebook */
+void remove_book (GtkButton *button, GtkNotebook *notebook)
+{
+ gint page;
+
+ page = gtk_notebook_current_page(notebook);
+ gtk_notebook_remove_page (notebook, page);
+ /* Need to refresh the widget --
+ This forces the widget to redraw itself. */
+ gtk_widget_draw(GTK_WIDGET(notebook), NULL);
+}
+
+void delete (GtkWidget *widget, gpointer *data)
+{
+ gtk_main_quit ();
+}
+
+int main (int argc, char *argv[])
+{
+ GtkWidget *window;
+ GtkWidget *button;
+ GtkWidget *table;
+ GtkWidget *notebook;
+ GtkWidget *frame;
+ GtkWidget *label;
+ GtkWidget *checkbutton;
+ int i;
+ char bufferf[32];
+ char bufferl[32];
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+ gtk_signal_connect (GTK_OBJECT (window), "delete_event",
+ GTK_SIGNAL_FUNC (delete), NULL);
+
+ gtk_container_border_width (GTK_CONTAINER (window), 10);
+
+ table = gtk_table_new(2,6,TRUE);
+ gtk_container_add (GTK_CONTAINER (window), table);
+
+ /* Create a new notebook, place the position of the tabs */
+ notebook = gtk_notebook_new ();
+ gtk_notebook_set_tab_pos (GTK_NOTEBOOK (notebook), GTK_POS_TOP);
+ gtk_table_attach_defaults(GTK_TABLE(table), notebook, 0,6,0,1);
+ gtk_widget_show(notebook);
+
+ /* lets append a bunch of pages to the notebook */
+ for (i=0; i < 5; i++) {
+ sprintf(bufferf, "Append Frame %d", i+1);
+ sprintf(bufferl, "Page %d", i+1);
+
+ frame = gtk_frame_new (bufferf);
+ gtk_container_border_width (GTK_CONTAINER (frame), 10);
+ gtk_widget_set_usize (frame, 100, 75);
+ gtk_widget_show (frame);
+
+ label = gtk_label_new (bufferf);
+ gtk_container_add (GTK_CONTAINER (frame), label);
+ gtk_widget_show (label);
+
+ label = gtk_label_new (bufferl);
+ gtk_notebook_append_page (GTK_NOTEBOOK (notebook), frame, label);
+ }
+
+
+ /* now lets add a page to a specific spot */
+ checkbutton = gtk_check_button_new_with_label ("Check me please!");
+ gtk_widget_set_usize(checkbutton, 100, 75);
+ gtk_widget_show (checkbutton);
+
+ label = gtk_label_new ("Add spot");
+ gtk_container_add (GTK_CONTAINER (checkbutton), label);
+ gtk_widget_show (label);
+ label = gtk_label_new ("Add page");
+ gtk_notebook_insert_page (GTK_NOTEBOOK (notebook), checkbutton, label, 2);
+
+ /* Now finally lets prepend pages to the notebook */
+ for (i=0; i < 5; i++) {
+ sprintf(bufferf, "Prepend Frame %d", i+1);
+ sprintf(bufferl, "PPage %d", i+1);
+
+ frame = gtk_frame_new (bufferf);
+ gtk_container_border_width (GTK_CONTAINER (frame), 10);
+ gtk_widget_set_usize (frame, 100, 75);
+ gtk_widget_show (frame);
+
+ label = gtk_label_new (bufferf);
+ gtk_container_add (GTK_CONTAINER (frame), label);
+ gtk_widget_show (label);
+
+ label = gtk_label_new (bufferl);
+ gtk_notebook_prepend_page (GTK_NOTEBOOK(notebook), frame, label);
+ }
+
+ /* Set what page to start at (page 4) */
+ gtk_notebook_set_page (GTK_NOTEBOOK(notebook), 3);
+
+
+ /* create a bunch of buttons */
+ button = gtk_button_new_with_label ("close");
+ gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
+ GTK_SIGNAL_FUNC (delete), NULL);
+ gtk_table_attach_defaults(GTK_TABLE(table), button, 0,1,1,2);
+ gtk_widget_show(button);
+
+ button = gtk_button_new_with_label ("next page");
+ gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
+ (GtkSignalFunc) gtk_notebook_next_page,
+ GTK_OBJECT (notebook));
+ gtk_table_attach_defaults(GTK_TABLE(table), button, 1,2,1,2);
+ gtk_widget_show(button);
+
+ button = gtk_button_new_with_label ("prev page");
+ gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
+ (GtkSignalFunc) gtk_notebook_prev_page,
+ GTK_OBJECT (notebook));
+ gtk_table_attach_defaults(GTK_TABLE(table), button, 2,3,1,2);
+ gtk_widget_show(button);
+
+ button = gtk_button_new_with_label ("tab position");
+ gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
+ (GtkSignalFunc) rotate_book, GTK_OBJECT(notebook));
+ gtk_table_attach_defaults(GTK_TABLE(table), button, 3,4,1,2);
+ gtk_widget_show(button);
+
+ button = gtk_button_new_with_label ("tabs/border on/off");
+ gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
+ (GtkSignalFunc) tabsborder_book,
+ GTK_OBJECT (notebook));
+ gtk_table_attach_defaults(GTK_TABLE(table), button, 4,5,1,2);
+ gtk_widget_show(button);
+
+ button = gtk_button_new_with_label ("remove page");
+ gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
+ (GtkSignalFunc) remove_book,
+ GTK_OBJECT(notebook));
+ gtk_table_attach_defaults(GTK_TABLE(table), button, 5,6,1,2);
+ gtk_widget_show(button);
+
+ gtk_widget_show(table);
+ gtk_widget_show(window);
+
+ gtk_main ();
+
+ return 0;
+}
+</verb></tscreen>
+<p>
+Hopefully this helps you on your way with creating notebooks for your
+GTK applications.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Scrolled Windows
+<p>
+Scrolled windows are used to create a scrollable area inside a real window.
+You may insert any types of widgets to these scrolled windows, and they will
+all be accessable regardless of the size by using the scrollbars.
+
+The following function is used to create a new scolled window.
+
+<tscreen><verb>
+GtkWidget* gtk_scrolled_window_new (GtkAdjustment *hadjustment,
+ GtkAdjustment *vadjustment);
+</verb></tscreen>
+<p>
+Where the first argument is the adjustment for the horizontal
+direction, and the second, the adjustment for the vertical direction.
+These are almost always set to NULL.
+
+<tscreen><verb>
+void gtk_scrolled_window_set_policy (GtkScrolledWindow *scrolled_window,
+ GtkPolicyType hscrollbar_policy,
+ GtkPolicyType vscrollbar_policy);
+</verb></tscreen>
+
+This sets the policy to be used with respect to the scrollbars.
+The first arguement is the scrolled window you wish to change. The second
+sets the policiy for the horizontal scrollbar, and the third,
+the vertical scrollbar.
+
+The policy may be one of GTK_POLICY AUTOMATIC, or GTK_POLICY_ALWAYS.
+GTK_POLICY_AUTOMATIC will automatically decide whether you need
+scrollbars, wheras GTK_POLICY_ALWAYS will always leave the scrollbars
+there.
+
+Here is a simple example that packs 100 toggle buttons into a scrolled window.
+I've only commented on the parts that may be new to you.
+
+<tscreen><verb>
+/* scrolledwin.c */
+
+#include <gtk/gtk.h>
+
+void destroy(GtkWidget *widget, gpointer *data)
+{
+ gtk_main_quit();
+}
+
+int main (int argc, char *argv[])
+{
+ static GtkWidget *window;
+ GtkWidget *scrolled_window;
+ GtkWidget *table;
+ GtkWidget *button;
+ char buffer[32];
+ int i, j;
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ /* Create a new dialog window for the scrolled window to be
+ * packed into. A dialog is just like a normal window except it has a
+ * vbox and a horizontal seperator packed into it. It's just a shortcut
+ * for creating dialogs */
+ window = gtk_dialog_new ();
+ gtk_signal_connect (GTK_OBJECT (window), "destroy",
+ (GtkSignalFunc) destroy, NULL);
+ gtk_window_set_title (GTK_WINDOW (window), "dialog");
+ gtk_container_border_width (GTK_CONTAINER (window), 0);
+ gtk_widget_set_usize(window, 300, 300);
+
+ /* create a new scrolled window. */
+ scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+
+ gtk_container_border_width (GTK_CONTAINER (scrolled_window), 10);
+
+ /* the policy is one of GTK_POLICY AUTOMATIC, or GTK_POLICY_ALWAYS.
+ * GTK_POLICY_AUTOMATIC will automatically decide whether you need
+ * scrollbars, wheras GTK_POLICY_ALWAYS will always leave the scrollbars
+ * there. The first one is the horizontal scrollbar, the second,
+ * the vertical. */
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
+ /* The dialog window is created with a vbox packed into it. */
+ gtk_box_pack_start (GTK_BOX (GTK_DIALOG(window)->vbox), scrolled_window,
+ TRUE, TRUE, 0);
+ gtk_widget_show (scrolled_window);
+
+ /* create a table of 10 by 10 squares. */
+ table = gtk_table_new (10, 10, FALSE);
+
+ /* set the spacing to 10 on x and 10 on y */
+ gtk_table_set_row_spacings (GTK_TABLE (table), 10);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 10);
+
+ /* pack the table into the scrolled window */
+ gtk_container_add (GTK_CONTAINER (scrolled_window), table);
+ gtk_widget_show (table);
+
+ /* this simply creates a grid of toggle buttons on the table
+ * to demonstrate the scrolled window. */
+ for (i = 0; i < 10; i++)
+ for (j = 0; j < 10; j++) {
+ sprintf (buffer, "button (%d,%d)\n", i, j);
+ button = gtk_toggle_button_new_with_label (buffer);
+ gtk_table_attach_defaults (GTK_TABLE (table), button,
+ i, i+1, j, j+1);
+ gtk_widget_show (button);
+ }
+
+ /* Add a "close" button to the bottom of the dialog */
+ button = gtk_button_new_with_label ("close");
+ gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
+ (GtkSignalFunc) gtk_widget_destroy,
+ GTK_OBJECT (window));
+
+ /* this makes it so the button is the default. */
+
+ GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
+ gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->action_area), button, TRUE, TRUE, 0);
+
+ /* This grabs this button to be the default button. Simply hitting
+ * the "Enter" key will cause this button to activate. */
+ gtk_widget_grab_default (button);
+ gtk_widget_show (button);
+
+ gtk_widget_show (window);
+
+ gtk_main();
+
+ return(0);
+}
+</verb></tscreen>
+<p>
+Try playing with resizing the window. You'll notice how the scrollbars
+react. You may also wish to use the gtk_widget_set_usize() call to set the default
+size of the window or other widgets.
+
+<!-- ***************************************************************** -->
+<sect> List Widgets
+<!-- ***************************************************************** -->
+
+<p>
+The GtkList widget is designed to act as a vertical container for widgets
+that should be of the type GtkListItem.
+
+A GtkList widget has its own window to receive events and it's own
+background color which is usualy white. As it is directly derived from a
+GtkContainer it can be treated as such by using the GTK_CONTAINER(List)
+macro, see the GtkContainer widget for more on this.
+One should already be familar whith the usage of a GList and its
+related functions g_list_*() to be able to use the GtkList widget to
+its fully extends.
+
+There is one field inside the structure definition of the GtkList widget
+that will be of greater interest to us, this is:
+
+<tscreen><verb>
+struct _GtkList
+{
+ ...
+ GList *selection;
+ guint selection_mode;
+ ...
+};
+</verb></tscreen>
+
+The selection field of a GtkList points to a linked list of all items
+that are cureently selected, or `NULL' if the selection is empty.
+So to learn about the current selection we read the GTK_LIST()->selection
+field, but do not modify it since the internal fields are maintained by
+the gtk_list_*() functions.
+
+The selection_mode of the GtkList determines the selection facilities
+of a GtkList and therefore the contents of the GTK_LIST()->selection
+field:
+
+The selection_mode may be one of the following:
+<itemize>
+<item> GTK_SELECTION_SINGLE - The selection is either `NULL'
+ or contains a GList* pointer
+ for a single selected item.
+
+<item> GTK_SELECTION_BROWSE - The selection is `NULL' if the list
+ contains no widgets or insensitive
+ ones only, otherwise it contains
+ a GList pointer for one GList
+ structure, and therefore exactly
+ one list item.
+
+<item> GTK_SELECTION_MULTIPLE - The selection is `NULL' if no list
+ items are selected or a GList pointer
+ for the first selected item. That
+ in turn points to a GList structure
+ for the second selected item and so
+ on.
+
+<item> GTK_SELECTION_EXTENDED - The selection is always `NULL'.
+</itemize>
+<p>
+The default is GTK_SELECTION_MULTIPLE.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Signals
+<p>
+<tscreen><verb>
+void selection_changed (GtkList *LIST)
+</verb></tscreen>
+
+This signal will be invoked whenever a the selection field
+of a GtkList has changed. This happens when a child of
+the GtkList got selected or unselected.
+
+<tscreen><verb>
+void select_child (GtkList *LIST, GtkWidget *CHILD)
+</verb></tscreen>
+
+This signal is invoked when a child of the GtkList is about
+to get selected. This happens mainly on calls to
+gtk_list_select_item(), gtk_list_select_child(), button presses
+and sometimes indirectly triggered on some else occasions where
+children get added to or removed from the GtkList.
+
+<tscreen><verb>
+void unselect_child (GtkList *LIST, GtkWidget *CHILD)
+</verb></tscreen>
+
+This signal is invoked when a child of the GtkList is about
+to get unselected. This happens mainly on calls to
+gtk_list_unselect_item(), gtk_list_unselect_child(), button presses
+and sometimes indirectly triggered on some else occasions where
+children get added to or removed from the GtkList.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Functions
+<p>
+<tscreen><verb>
+guint gtk_list_get_type (void)
+</verb></tscreen>
+
+Returns the `GtkList' type identifier.
+
+<tscreen><verb>
+GtkWidget* gtk_list_new (void)
+</verb></tscreen>
+
+Create a new `GtkList' object. The new widget is
+returned as a pointer to a `GtkWidget' object.
+`NULL' is returned on failure.
+
+<tscreen><verb>
+void gtk_list_insert_items (GtkList *LIST, GList *ITEMS, gint POSITION)
+</verb></tscreen>
+
+Insert list items into the LIST, starting at POSITION.
+ITEMS is a doubly linked list where each nodes data
+pointer is expected to point to a newly created GtkListItem.
+The GList nodes of ITEMS are taken over by the LIST.
+
+<tscreen><verb>
+void gtk_list_append_items (GtkList *LIST, GList *ITEMS)
+</verb></tscreen>
+
+Insert list items just like gtk_list_insert_items() at the end
+of the LIST. The GList nodes of ITEMS are taken over by the LIST.
+
+<tscreen><verb>
+void gtk_list_prepend_items (GtkList *LIST, GList *ITEMS)
+</verb></tscreen>
+
+Insert list items just like gtk_list_insert_items() at the very
+beginning of the LIST. The GList nodes of ITEMS are taken over
+by the LIST.
+
+<tscreen><verb>
+void gtk_list_remove_items (GtkList *LIST, GList *ITEMS)
+</verb></tscreen>
+
+Remove list items from the LIST. ITEMS is a doubly linked
+list where each nodes data pointer is expected to point to a
+direct child of LIST. It is the callers responsibility to make a
+call to g_list_free(ITEMS) afterwards. Also the caller has to
+destroy the list items himself.
+
+<tscreen><verb>
+void gtk_list_clear_items (GtkList *LIST, gint START, gint END)
+</verb></tscreen>
+
+Remove and destroy list items from the LIST. a widget is affected if
+its current position within LIST is in the range specified by START
+and END.
+
+<tscreen><verb>
+void gtk_list_select_item (GtkList *LIST, gint ITEM)
+</verb></tscreen>
+
+Invoke the select_child signal for a list item
+specified through its current position within LIST.
+
+<tscreen><verb>
+void gtk_list_unselect_item (GtkList *LIST, gint ITEM)
+</verb></tscreen>
+
+Invoke the unselect_child signal for a list item
+specified through its current position within LIST.
+
+<tscreen><verb>
+void gtk_list_select_child (GtkList *LIST, GtkWidget *CHILD)
+</verb></tscreen>
+
+Invoke the select_child signal for the specified CHILD.
+
+<tscreen><verb>
+void gtk_list_unselect_child (GtkList *LIST, GtkWidget *CHILD)
+</verb></tscreen>
+
+Invoke the unselect_child signal for the specified CHILD.
+
+<tscreen><verb>
+gint gtk_list_child_position (GtkList *LIST, GtkWidget *CHILD)
+</verb></tscreen>
+
+Return the position of CHILD within LIST. `-1' is returned on failure.
+
+<tscreen><verb>
+void gtk_list_set_selection_mode (GtkList *LIST, GtkSelectionMode MODE)
+</verb></tscreen>
+
+Set LIST to the selection mode MODE wich can be of GTK_SELECTION_SINGLE,
+GTK_SELECTION_BROWSE, GTK_SELECTION_MULTIPLE or GTK_SELECTION_EXTENDED.
+
+<tscreen><verb>
+GtkList* GTK_LIST (gpointer OBJ)
+</verb></tscreen>
+
+Cast a generic pointer to `GtkList*'. *Note Standard Macros::, for
+more info.
+
+<tscreen><verb>
+GtkListClass* GTK_LIST_CLASS (gpointer CLASS)
+</verb></tscreen>
+
+Cast a generic pointer to `GtkListClass*'. *Note Standard Macros::,
+for more info.
+
+<tscreen><verb>
+gint GTK_IS_LIST (gpointer OBJ)
+</verb></tscreen>
+
+Determine if a generic pointer refers to a `GtkList' object. *Note
+Standard Macros::, for more info.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Example
+<p>
+Following is an example program that will print out the changes
+of the selection of a GtkList, and lets you "arrest" list items
+into a prison by selecting them with the rightmost mouse button:
+
+<tscreen><verb>
+/* list.c */
+
+/* include the gtk+ header files
+ * include stdio.h, we need that for the printf() function
+ */
+#include <gtk/gtk.h>
+#include <stdio.h>
+
+/* this is our data identification string to store
+ * data in list items
+ */
+const gchar *list_item_data_key="list_item_data";
+
+
+/* prototypes for signal handler that we are going to connect
+ * to the GtkList widget
+ */
+static void sigh_print_selection (GtkWidget *gtklist,
+ gpointer func_data);
+static void sigh_button_event (GtkWidget *gtklist,
+ GdkEventButton *event,
+ GtkWidget *frame);
+
+
+/* main function to set up the user interface */
+
+gint main (int argc, gchar *argv[])
+{
+ GtkWidget *separator;
+ GtkWidget *window;
+ GtkWidget *vbox;
+ GtkWidget *scrolled_window;
+ GtkWidget *frame;
+ GtkWidget *gtklist;
+ GtkWidget *button;
+ GtkWidget *list_item;
+ GList *dlist;
+ guint i;
+ gchar buffer[64];
+
+
+ /* initialize gtk+ (and subsequently gdk) */
+
+ gtk_init(&amp;argc, &amp;argv);
+
+
+ /* create a window to put all the widgets in
+ * connect gtk_main_quit() to the "destroy" event of
+ * the window to handle window manager close-window-events
+ */
+ window=gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title(GTK_WINDOW(window), "GtkList Example");
+ gtk_signal_connect(GTK_OBJECT(window),
+ "destroy",
+ GTK_SIGNAL_FUNC(gtk_main_quit),
+ NULL);
+
+
+ /* inside the window we need a box to arrange the widgets
+ * vertically */
+ vbox=gtk_vbox_new(FALSE, 5);
+ gtk_container_border_width(GTK_CONTAINER(vbox), 5);
+ gtk_container_add(GTK_CONTAINER(window), vbox);
+ gtk_widget_show(vbox);
+
+ /* this is the scolled window to put the GtkList widget inside */
+ scrolled_window=gtk_scrolled_window_new(NULL, NULL);
+ gtk_widget_set_usize(scrolled_window, 250, 150);
+ gtk_container_add(GTK_CONTAINER(vbox), scrolled_window);
+ gtk_widget_show(scrolled_window);
+
+ /* create the GtkList widget
+ * connect the sigh_print_selection() signal handler
+ * function to the "selection_changed" signal of the GtkList
+ * to print out the selected items each time the selection
+ * has changed */
+ gtklist=gtk_list_new();
+ gtk_container_add(GTK_CONTAINER(scrolled_window), gtklist);
+ gtk_widget_show(gtklist);
+ gtk_signal_connect(GTK_OBJECT(gtklist),
+ "selection_changed",
+ GTK_SIGNAL_FUNC(sigh_print_selection),
+ NULL);
+
+ /* we create a "Prison" to put a list item in ;)
+ */
+ frame=gtk_frame_new("Prison");
+ gtk_widget_set_usize(frame, 200, 50);
+ gtk_container_border_width(GTK_CONTAINER(frame), 5);
+ gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT);
+ gtk_container_add(GTK_CONTAINER(vbox), frame);
+ gtk_widget_show(frame);
+
+ /* connect the sigh_button_event() signal handler to the GtkList
+ * wich will handle the "arresting" of list items
+ */
+ gtk_signal_connect(GTK_OBJECT(gtklist),
+ "button_release_event",
+ GTK_SIGNAL_FUNC(sigh_button_event),
+ frame);
+
+ /* create a separator
+ */
+ separator=gtk_hseparator_new();
+ gtk_container_add(GTK_CONTAINER(vbox), separator);
+ gtk_widget_show(separator);
+
+ /* finaly create a button and connect it´s "clicked" signal
+ * to the destroyment of the window
+ */
+ button=gtk_button_new_with_label("Close");
+ gtk_container_add(GTK_CONTAINER(vbox), button);
+ gtk_widget_show(button);
+ gtk_signal_connect_object(GTK_OBJECT(button),
+ "clicked",
+ GTK_SIGNAL_FUNC(gtk_widget_destroy),
+ GTK_OBJECT(window));
+
+
+ /* now we create 5 list items, each having it´s own
+ * label and add them to the GtkList using gtk_container_add()
+ * also we query the text string from the label and
+ * associate it with the list_item_data_key for each list item
+ */
+ for (i=0; i<5; i++) {
+ GtkWidget *label;
+ gchar *string;
+
+ sprintf(buffer, "ListItemContainer with Label #%d", i);
+ label=gtk_label_new(buffer);
+ list_item=gtk_list_item_new();
+ gtk_container_add(GTK_CONTAINER(list_item), label);
+ gtk_widget_show(label);
+ gtk_container_add(GTK_CONTAINER(gtklist), list_item);
+ gtk_widget_show(list_item);
+ gtk_label_get(GTK_LABEL(label), &amp;string);
+ gtk_object_set_data(GTK_OBJECT(list_item),
+ list_item_data_key,
+ string);
+ }
+ /* here, we are creating another 5 labels, this time
+ * we use gtk_list_item_new_with_label() for the creation
+ * we can´t query the text string from the label because
+ * we don´t have the labels pointer and therefore
+ * we just associate the list_item_data_key of each
+ * list item with the same text string
+ * for adding of the list items we put them all into a doubly
+ * linked list (GList), and then add them by a single call to
+ * gtk_list_append_items()
+ * because we use g_list_prepend() to put the items into the
+ * doubly linked list, their order will be descending (instead
+ * of ascending when using g_list_append())
+ */
+ dlist=NULL;
+ for (; i<10; i++) {
+ sprintf(buffer, "List Item with Label %d", i);
+ list_item=gtk_list_item_new_with_label(buffer);
+ dlist=g_list_prepend(dlist, list_item);
+ gtk_widget_show(list_item);
+ gtk_object_set_data(GTK_OBJECT(list_item),
+ list_item_data_key,
+ "ListItem with integrated Label");
+ }
+ gtk_list_append_items(GTK_LIST(gtklist), dlist);
+
+ /* finaly we want to see the window, don´t we? ;)
+ */
+ gtk_widget_show(window);
+
+ /* fire up the main event loop of gtk
+ */
+ gtk_main();
+
+ /* we get here after gtk_main_quit() has been called which
+ * happens if the main window gets destroyed
+ */
+ return 0;
+}
+
+/* this is the signal handler that got connected to button
+ * press/release events of the GtkList
+ */
+void
+sigh_button_event (GtkWidget *gtklist,
+ GdkEventButton *event,
+ GtkWidget *frame)
+{
+ /* we only do something if the third (rightmost mouse button
+ * was released
+ */
+ if (event->type==GDK_BUTTON_RELEASE &amp;&amp;
+ event->button==3) {
+ GList *dlist, *free_list;
+ GtkWidget *new_prisoner;
+
+ /* fetch the currently selected list item which
+ * will be our next prisoner ;)
+ */
+ dlist=GTK_LIST(gtklist)->selection;
+ if (dlist)
+ new_prisoner=GTK_WIDGET(dlist->data);
+ else
+ new_prisoner=NULL;
+
+ /* look for already prisoned list items, we
+ * will put them back into the list
+ * remember to free the doubly linked list that
+ * gtk_container_children() returns
+ */
+ dlist=gtk_container_children(GTK_CONTAINER(frame));
+ free_list=dlist;
+ while (dlist) {
+ GtkWidget *list_item;
+
+ list_item=dlist->data;
+
+ gtk_widget_reparent(list_item, gtklist);
+
+ dlist=dlist->next;
+ }
+ g_list_free(free_list);
+
+ /* if we have a new prisoner, remove him from the
+ * GtkList and put him into the frame "Prison"
+ * we need to unselect the item before
+ */
+ if (new_prisoner) {
+ GList static_dlist;
+
+ static_dlist.data=new_prisoner;
+ static_dlist.next=NULL;
+ static_dlist.prev=NULL;
+
+ gtk_list_unselect_child(GTK_LIST(gtklist),
+ new_prisoner);
+ gtk_widget_reparent(new_prisoner, frame);
+ }
+ }
+}
+
+/* this is the signal handler that gets called if GtkList
+ * emits the "selection_changed" signal
+ */
+void
+sigh_print_selection (GtkWidget *gtklist,
+ gpointer func_data)
+{
+ GList *dlist;
+
+ /* fetch the doubly linked list of selected items
+ * of the GtkList, remember to treat this as read-only!
+ */
+ dlist=GTK_LIST(gtklist)->selection;
+
+ /* if there are no selected items there is nothing more
+ * to do than just telling the user so
+ */
+ if (!dlist) {
+ g_print("Selection cleared\n");
+ return;
+ }
+ /* ok, we got a selection and so we print it
+ */
+ g_print("The selection is a ");
+
+ /* get the list item from the doubly linked list
+ * and then query the data associated with list_item_data_key
+ * we then just print it
+ */
+ while (dlist) {
+ GtkObject *list_item;
+ gchar *item_data_string;
+
+ list_item=GTK_OBJECT(dlist->data);
+ item_data_string=gtk_object_get_data(list_item,
+ list_item_data_key);
+ g_print("%s ", item_data_string);
+
+ dlist=dlist->next;
+ }
+ g_print("\n");
+}
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> List Item Widget
+<p>
+The GtkListItem widget is designed to act as a container holding up
+to one child, providing functions for selection/deselection just like
+the GtkList widget requires them for its children.
+
+A GtkListItem has its own window to receive events and has its own
+background color which is usualy white.
+
+As it is directly derived from a
+GtkItem it can be treated as such by using the GTK_ITEM(ListItem)
+macro, see the GtkItem widget for more on this.
+Usualy a GtkListItem just holds a label to identify e.g. a filename
+within a GtkList -- therefore the convenient function
+gtk_list_item_new_with_label() is provided. The same effect can be
+achieved by creating a GtkLabel on its own, setting its alignment
+to xalign=0 and yalign=0.5 with a subsequent container addition
+to the GtkListItem.
+
+As one is not forced to add a GtkLabel to a GtkListItem, you could
+also add a GtkVBox or a GtkArrow etc. to the GtkListItem.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Signals
+<p>
+A GtkListItem does not create new signals on its own, but inherits
+the signals of a GtkItem. *Note GtkItem::, for more info.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Functions
+<p>
+
+<tscreen><verb>
+guint gtk_list_item_get_type (void)
+</verb></tscreen>
+
+Returns the `GtkListItem' type identifier.
+
+<tscreen><verb>
+GtkWidget* gtk_list_item_new (void)
+</verb></tscreen>
+
+Create a new `GtkListItem' object. The new widget is
+returned as a pointer to a `GtkWidget' object.
+`NULL' is returned on failure.
+
+<tscreen><verb>
+GtkWidget* gtk_list_item_new_with_label (gchar *LABEL)
+</verb></tscreen>
+
+Create a new `GtkListItem' object, having a single GtkLabel as
+the sole child. The new widget is returned as a pointer to a
+`GtkWidget' object.
+`NULL' is returned on failure.
+
+<tscreen><verb>
+void gtk_list_item_select (GtkListItem *LIST_ITEM)
+</verb></tscreen>
+
+This function is basicaly a wrapper around a call to
+gtk_item_select (GTK_ITEM (list_item)) which will emit the
+select signal.
+*Note GtkItem::, for more info.
+
+<tscreen><verb>
+void gtk_list_item_deselect (GtkListItem *LIST_ITEM)
+</verb></tscreen>
+
+This function is basicaly a wrapper around a call to
+gtk_item_deselect (GTK_ITEM (list_item)) which will emit the
+deselect signal.
+*Note GtkItem::, for more info.
+
+<tscreen><verb>
+GtkListItem* GTK_LIST_ITEM (gpointer OBJ)
+</verb></tscreen>
+
+Cast a generic pointer to `GtkListItem*'. *Note Standard Macros::,
+for more info.
+
+<tscreen><verb>
+GtkListItemClass* GTK_LIST_ITEM_CLASS (gpointer CLASS)
+</verb></tscreen>
+
+Cast a generic pointer to `GtkListItemClass*'. *Note Standard
+Macros::, for more info.
+
+<tscreen><verb>
+gint GTK_IS_LIST_ITEM (gpointer OBJ)
+</verb></tscreen>
+
+Determine if a generic pointer refers to a `GtkListItem' object.
+*Note Standard Macros::, for more info.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Example
+<p>
+Please see the GtkList example on this, which covers the usage of a
+GtkListItem as well.
+
+<!-- ***************************************************************** -->
+<sect> File Selections
+<!-- ***************************************************************** -->
+<p>
+The file selection widget is a quick and simple way to display a File
+dialog box. It comes complete with Ok, Cancel, and Help buttons, a great way
+to cut down on programming time.
+
+To create a new file selection box use:
+
+<tscreen><verb>
+GtkWidget* gtk_file_selection_new (gchar *title);
+</verb></tscreen>
+
+To set the filename, for example to bring up a specific directory, or
+give a default filename, use this function:
+
+<tscreen><verb>
+void gtk_file_selection_set_filename (GtkFileSelection *filesel, gchar *filename);
+</verb></tscreen>
+
+To grab the text that the user has entered or clicked on, use this
+function:
+
+<tscreen><verb>
+gchar* gtk_file_selection_get_filename (GtkFileSelection *filesel);
+</verb></tscreen>
+
+There are also pointers to the widgets contained within the file
+selection widget. These are:
+
+<itemize>
+<item>dir_list
+<item>file_list
+<item>selection_entry
+<item>selection_text
+<item>main_vbox
+<item>ok_button
+<item>cancel_button
+<item>help_button
+</itemize>
+
+Most likely you will want to use the ok_button, cancel_button, and
+help_button pointers in signaling their use.
+
+Included here is an example stolen from testgtk.c, modified to run
+on it's own. As you will see, there is nothing much to creating a file
+selection widget. While, in this example, the Help button appears on the
+screen, it does nothing as there is not a signal attached to it.
+
+<tscreen><verb>
+/* filesel.c */
+
+#include <gtk/gtk.h>
+
+/* Get the selected filename and print it to the console */
+void file_ok_sel (GtkWidget *w, GtkFileSelection *fs)
+{
+ g_print ("%s\n", gtk_file_selection_get_filename (GTK_FILE_SELECTION (fs)));
+}
+
+void destroy (GtkWidget *widget, gpointer *data)
+{
+ gtk_main_quit ();
+}
+
+int main (int argc, char *argv[])
+{
+ GtkWidget *filew;
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ /* Create a new file selection widget */
+ filew = gtk_file_selection_new ("File selection");
+
+ gtk_signal_connect (GTK_OBJECT (filew), "destroy",
+ (GtkSignalFunc) destroy, &amp;filew);
+ /* Connect the ok_button to file_ok_sel function */
+ gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (filew)->ok_button),
+ "clicked", (GtkSignalFunc) file_ok_sel, filew );
+
+ /* Connect the cancel_button to destroy the widget */
+ gtk_signal_connect_object (GTK_OBJECT (GTK_FILE_SELECTION (filew)->cancel_button),
+ "clicked", (GtkSignalFunc) gtk_widget_destroy,
+ GTK_OBJECT (filew));
+
+ /* Lets set the filename, as if this were a save dialog, and we are giving
+ a default filename */
+ gtk_file_selection_set_filename (GTK_FILE_SELECTION(filew),
+ "penguin.png");
+
+ gtk_widget_show(filew);
+ gtk_main ();
+ return 0;
+}
+</verb></tscreen>
+
+<!-- ***************************************************************** -->
+<sect>Menu Widgets
+<!-- ***************************************************************** -->
+
+<p>
+There are two ways to create menus, there's the easy way, and there's the
+hard way. Both have their uses, but you can usually use the menufactory
+(the easy way). The "hard" way is to create all the menus using the calls
+directly. The easy way is to use the gtk_menu_factory calls. This is
+much simpler, but there are advantages and disadvantages to each approach.
+
+The menufactory is much easier to use, and to add new menus to, although
+writing a few wrapper functions to create menus using the manual method
+could go a long way towards usability. With the menufactory, it is not
+possible to add images or the character '/' to the menus.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Manual Menu Creation
+<p>
+In the true tradition of teaching, we'll show you the hard
+way first. <tt>:)</>
+<p>
+There are three widgets that go into making a menubar and submenus:
+<itemize>
+<item>a menu item, which is what the user wants to select, e.g. 'Save'
+<item>a menu, which acts as a container for the menu items, and
+<item>a menubar, which is a container for each of the individual menus,
+</itemize>
+
+This is slightly complicated by the fact that menu item widgets are used for two different things. They are
+both the widets that are packed into the menu, and the widget that is packed into the menubar, which,
+when selected, activiates the menu.
+
+Let's look at the functions that are used to create menus and menubars.
+This first function is used to create a new menubar.
+
+<tscreen><verb>
+GtkWidget *gtk_menu_bar_new(void);
+</verb></tscreen>
+
+This rather self explanatory function creates a new menubar. You use
+gtk_container_add to pack this into a window, or the box_pack functions to
+pack it into a box - the same as buttons.
+
+<tscreen><verb>
+GtkWidget *gtk_menu_new();
+</verb></tscreen>
+
+This function returns a pointer to a new menu, it is never actually shown
+(with gtk_widget_show), it is just a container for the menu items. Hopefully this will
+become more clear when you look at the example below.
+<p>
+The next two calls are used to create menu items that are packed into
+the menu (and menubar).
+
+<tscreen><verb>
+GtkWidget *gtk_menu_item_new();
+</verb></tscreen>
+
+and
+
+<tscreen><verb>
+GtkWidget *gtk_menu_item_new_with_label(const char *label);
+</verb></tscreen>
+
+These calls are used to create the menu items that are to be displayed.
+Remember to differentiate between a "menu" as created with gtk_menu_new
+and a "menu item" as created by the gtk_menu_item_new functions. The
+menu item will be an actual button with an associated action,
+whereas a menu will be a container holding menu items.
+
+The gtk_menu_new_with_label and gtk_menu_new functions are just as you'd expect after
+reading about the buttons. One creates a new menu item with a label
+already packed into it, and the other just creates a blank menu item.
+
+Once you've created a menu item you have to put it into a menu. This is done using the function
+gtk_menu_append. In order to capture when the item is selected by the user, we need to connect
+to the <tt/activate/ signal in the usual way.
+So, if we wanted to create a standard <tt/File/ menu, with the options <tt/Open/,
+<tt/Save/ and <tt/Quit/ the code would look something like
+
+<tscreen><verb>
+file_menu = gtk_menu_new(); /* Don't need to show menus */
+
+/* Create the menu items */
+open_item = gtk_menu_item_new_with_label("Open");
+save_item = gtk_menu_item_new_with_label("Save");
+quit_item = gtk_menu_item_new_with_label("Quit");
+
+/* Add them to the menu */
+gtk_menu_append( GTK_MENU(file_menu), open_item);
+gtk_menu_append( GTK_MENU(file_menu), save_item);
+gtk_menu_append( GTK_MENU(file_menu), quit_item);
+
+/* Attach the callback functions to the activate signal */
+gtk_signal_connect_object( GTK_OBJECT(open_items), "activate",
+ GTK_SIGNAL_FUNC(menuitem_response), (gpointer) "file.open");
+gtk_signal_connect_object( GTK_OBJECT(save_items), "activate",
+ GTK_SIGNAL_FUNC(menuitem_response), (gpointer) "file.save");
+
+/* We can attach the Quit menu item to our exit function */
+gtk_signal_connect_object( GTK_OBJECT(quit_items), "activate",
+ GTK_SIGNAL_FUNC(destroy), (gpointer) "file.quit");
+
+/* We do need to show menu items */
+gtk_widget_show( open_item );
+gtk_widget_show( save_item );
+gtk_widget_show( quit_item );
+</verb></tscreen>
+
+At this point we have our menu. Now we need to create a menubar and a menu item for the <tt/File/ entry,
+to which we add our menu. The code looks like this
+
+<tscreen><verb>
+menu_bar = gtk_menu_bar_new();
+gtk_container_add( GTK_CONTAINER(window), menu_bar);
+gtk_widget_show( menu_bar );
+
+file_item = gtk_menu_item_new_with_label("File");
+gtk_widget_show(file_item);
+</verb></tscreen>
+
+Now we need to associate the menu with <tt/file_item/. This is done with the function
+
+<tscreen>
+void gtk_menu_item_set_submenu( GtkMenuItem *menu_item,
+ GtkWidget *submenu);
+</tscreen>
+
+So, our example would continue with
+
+<tscreen><verb>
+gtk_menu_item_set_submenu( GTK_MENU_ITEM(file_item), file_menu);
+</verb></tscreen>
+
+All that is left to do is to add the menu to the menubar, which is accomplished using the function
+
+<tscreen>
+void gtk_menu_bar_append( GtkMenuBar *menu_bar, GtkWidget *menu_item);
+</tscreen>
+
+which in our case looks like this:
+
+<tscreen><verb>
+gtk_menu_bar_append( menu_bar, file_item );
+</verb></tscreen>
+
+If we wanted the menu right justified on the menubar, such as help menus often are, we can
+use the following function (again on <tt/file_item/ in the current example) before attaching
+it to the menubar.
+<tscreen><verb>
+void gtk_menu_item_right_justify (GtkMenuItem *menu_item);
+</verb></tscreen>
+
+Here is a summary of the steps needed to create a menu bar with menus attached:
+<itemize>
+<item> Create a new menu using gtk_menu_new()
+<item> Use multiple calls to gtk_menu_item_new() for each item you wish to have on
+ your menu. And use gtk_menu_append() to put each of these new items on
+ to the menu.
+<item> Create a menu item using gtk_menu_item_new(). This will be the root of
+ the menu, the text appearing here will be on the menubar itself.
+<item> Use gtk_menu_item_set_submenu() to attach the menu to
+ the root menu item (The one created in the above step).
+<item> Create a new menubar using gtk_menu_bar_new. This step only needs
+ to be done once when creating a series of menus on one menu bar.
+<item> Use gtk_menu_bar_append to put the root menu onto the menubar.
+</itemize>
+<p>
+Creating a popup menu is nearly the same. The difference is that the
+menu is not posted `automatically' by a menubar, but explicitly
+by calling the function gtk_menu_popup() from a button-press event, for example.
+Take these steps:
+<itemize>
+<item>Create an event handling function. It needs to have the prototype
+<tscreen>
+static gint handler(GtkWidget *widget, GdkEvent *event);
+</tscreen>
+and it will use the event to find out where to pop up the menu.
+<item>In the event handler, if event is a mouse button press, treat
+<tt>event</tt> as a button event (which it is) and use it as
+shown in the sample code to pass information to gtk_menu_popup().
+<item>Bind that event handler to a widget with
+<tscreen>
+gtk_signal_connect_object(GTK_OBJECT(widget), "event",
+ GTK_SIGNAL_FUNC (handler), GTK_OBJECT(menu));
+</tscreen>
+where <tt>widget</tt> is the widget you are binding to, <tt>handler</tt>
+is the handling function, and <tt>menu</tt> is a menu created with
+gtk_menu_new(). This can be a menu which is also posted by a menu bar,
+as shown in the sample code.
+</itemize>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Manual Menu Example
+<p>
+That should about do it. Let's take a look at an example to help clarify.
+
+<tscreen><verb>
+/* menu.c */
+
+#include <gtk/gtk.h>
+
+static gint button_press (GtkWidget *, GdkEvent *);
+static void menuitem_response (gchar *);
+
+int main (int argc, char *argv[])
+{
+
+ GtkWidget *window;
+ GtkWidget *menu;
+ GtkWidget *menu_bar;
+ GtkWidget *root_menu;
+ GtkWidget *menu_items;
+ GtkWidget *vbox;
+ GtkWidget *button;
+ char buf[128];
+ int i;
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ /* create a new window */
+ window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_widget_set_usize( GTK_WIDGET (window), 200, 100);
+ gtk_window_set_title(GTK_WINDOW (window), "GTK Menu Test");
+ gtk_signal_connect(GTK_OBJECT (window), "delete_event",
+ (GtkSignalFunc) gtk_exit, NULL);
+
+ /* Init the menu-widget, and remember -- never
+ * gtk_show_widget() the menu widget!!
+ * This is the menu that holds the menu items, the one that
+ * will pop up when you click on the "Root Menu" in the app */
+ menu = gtk_menu_new();
+
+ /* Next we make a little loop that makes three menu-entries for "test-menu".
+ * Notice the call to gtk_menu_append. Here we are adding a list of
+ * menu items to our menu. Normally, we'd also catch the "clicked"
+ * signal on each of the menu items and setup a callback for it,
+ * but it's omitted here to save space. */
+
+ for(i = 0; i < 3; i++)
+ {
+ /* Copy the names to the buf. */
+ sprintf(buf, "Test-undermenu - %d", i);
+
+ /* Create a new menu-item with a name... */
+ menu_items = gtk_menu_item_new_with_label(buf);
+
+ /* ...and add it to the menu. */
+ gtk_menu_append(GTK_MENU (menu), menu_items);
+
+ /* Do something interesting when the menuitem is selected */
+ gtk_signal_connect_object(GTK_OBJECT(menu_items), "activate",
+ GTK_SIGNAL_FUNC(menuitem_response), (gpointer) g_strdup(buf));
+
+ /* Show the widget */
+ gtk_widget_show(menu_items);
+ }
+
+ /* This is the root menu, and will be the label
+ * displayed on the menu bar. There won't be a signal handler attached,
+ * as it only pops up the rest of the menu when pressed. */
+ root_menu = gtk_menu_item_new_with_label("Root Menu");
+
+ gtk_widget_show(root_menu);
+
+ /* Now we specify that we want our newly created "menu" to be the menu
+ * for the "root menu" */
+ gtk_menu_item_set_submenu(GTK_MENU_ITEM (root_menu), menu);
+
+ /* A vbox to put a menu and a button in: */
+ vbox = gtk_vbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(window), vbox);
+ gtk_widget_show(vbox);
+
+ /* Create a menu-bar to hold the menus and add it to our main window */
+ menu_bar = gtk_menu_bar_new();
+ gtk_box_pack_start(GTK_BOX(vbox), menu_bar, FALSE, FALSE, 2);
+ gtk_widget_show(menu_bar);
+
+ /* Create a button to which to attach menu as a popup */
+ button = gtk_button_new_with_label("press me");
+ gtk_signal_connect_object(GTK_OBJECT(button), "event",
+ GTK_SIGNAL_FUNC (button_press), GTK_OBJECT(menu));
+ gtk_box_pack_end(GTK_BOX(vbox), button, TRUE, TRUE, 2);
+ gtk_widget_show(button);
+
+ /* And finally we append the menu-item to the menu-bar -- this is the
+ * "root" menu-item I have been raving about =) */
+ gtk_menu_bar_append(GTK_MENU_BAR (menu_bar), root_menu);
+
+ /* always display the window as the last step so it all splashes on
+ * the screen at once. */
+ gtk_widget_show(window);
+
+ gtk_main ();
+
+ return 0;
+}
+
+
+
+/* Respond to a button-press by posting a menu passed in as widget.
+ *
+ * Note that the "widget" argument is the menu being posted, NOT
+ * the button that was pressed.
+ */
+
+static gint button_press (GtkWidget *widget, GdkEvent *event)
+{
+
+ if (event->type == GDK_BUTTON_PRESS) {
+ GdkEventButton *bevent = (GdkEventButton *) event;
+ gtk_menu_popup (GTK_MENU(widget), NULL, NULL, NULL, NULL,
+ bevent->button, bevent->time);
+ /* Tell calling code that we have handled this event; the buck
+ * stops here. */
+ return TRUE;
+ }
+
+ /* Tell calling code that we have not handled this event; pass it on. */
+ return FALSE;
+}
+
+
+/* Print a string when a menu item is selected */
+
+static void menuitem_response (gchar *string)
+{
+ printf("%s\n", string);
+}
+</verb></tscreen>
+
+You may also set a menu item to be insensitive and, using an accelerator
+table, bind keys to menu functions.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Using GtkMenuFactory
+<p>
+Now that we've shown you the hard way, here's how you do it using the
+gtk_menu_factory calls.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Menu Factory Example
+<p>
+Here is an example using the GTK menu factory. This is the first file,
+menufactory.h. We keep a separate menufactory.c and mfmain.c because of the global variables used
+in the menufactory.c file.
+
+<tscreen><verb>
+/* menufactory.h */
+
+#ifndef __MENUFACTORY_H__
+#define __MENUFACTORY_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+void get_main_menu (GtkWidget **menubar, GtkAcceleratorTable **table);
+void menus_create(GtkMenuEntry *entries, int nmenu_entries);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __MENUFACTORY_H__ */
+</verb></tscreen>
+<p>
+And here is the menufactory.c file.
+
+<tscreen><verb>
+/* 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);
+
+
+/* this is the GtkMenuEntry structure used to create new menus. The
+ * first member is the menu definition string. The second, the
+ * default accelerator key used to access this menu function with
+ * the keyboard. The third is the callback function to call when
+ * this menu item is selected (by the accelerator key, or with the
+ * mouse.) The last member is the data to pass to your callback function.
+ */
+
+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}
+};
+
+/* 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)
+{
+ 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);
+ }
+}
+
+void menus_create(GtkMenuEntry * entries, int nmenu_entries)
+{
+ char *accelerator;
+ int i;
+
+ if (initialize)
+ menus_init();
+
+ if (entry_ht)
+ for (i = 0; i < nmenu_entries; i++) {
+ accelerator = g_hash_table_lookup(entry_ht, entries[i].path);
+ if (accelerator) {
+ if (accelerator[0] == '\0')
+ entries[i].accelerator = NULL;
+ else
+ entries[i].accelerator = accelerator;
+ }
+ }
+ gtk_menu_factory_add_entries(factory, entries, nmenu_entries);
+
+ for (i = 0; i < nmenu_entries; i++)
+ if (entries[i].widget) {
+ gtk_signal_connect(GTK_OBJECT(entries[i].widget), "install_accelerator",
+ (GtkSignalFunc) menus_install_accel,
+ entries[i].path);
+ gtk_signal_connect(GTK_OBJECT(entries[i].widget), "remove_accelerator",
+ (GtkSignalFunc) menus_remove_accel,
+ entries[i].path);
+ }
+}
+
+static gint menus_install_accel(GtkWidget * widget, gchar * signal_name, gchar key, gchar modifiers, gchar * path)
+{
+ char accel[64];
+ char *t1, t2[2];
+
+ accel[0] = '\0';
+ if (modifiers & GDK_CONTROL_MASK)
+ strcat(accel, "<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;
+}
+
+static void menus_remove_accel(GtkWidget * widget, gchar * signal_name, gchar * path)
+{
+ char *t;
+
+ if (entry_ht) {
+ t = g_hash_table_lookup(entry_ht, path);
+ g_free(t);
+
+ g_hash_table_insert(entry_ht, path, g_strdup(""));
+ }
+}
+
+void menus_set_sensitive(char *path, int sensitive)
+{
+ GtkMenuPath *menu_path;
+
+ if (initialize)
+ menus_init();
+
+ menu_path = gtk_menu_factory_find(factory, path);
+ if (menu_path)
+ gtk_widget_set_sensitive(menu_path->widget, sensitive);
+ else
+ g_warning("Unable to set sensitivity for menu which doesn't exist: %s", path);
+}
+
+</verb></tscreen>
+<p>
+And here's the mfmain.h
+
+<tscreen><verb>
+/* mfmain.h */
+
+#ifndef __MFMAIN_H__
+#define __MFMAIN_H__
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+void file_quit_cmd_callback(GtkWidget *widget, gpointer data);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __MFMAIN_H__ */
+</verb></tscreen>
+<p>
+And mfmain.c
+
+<tscreen><verb>
+/* 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(&amp;argc, &amp;argv);
+
+ window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_signal_connect(GTK_OBJECT(window), "destroy",
+ GTK_SIGNAL_FUNC(file_quit_cmd_callback),
+ "WM destroy");
+ gtk_window_set_title(GTK_WINDOW(window), "Menu Factory");
+ gtk_widget_set_usize(GTK_WIDGET(window), 300, 200);
+
+ main_vbox = gtk_vbox_new(FALSE, 1);
+ gtk_container_border_width(GTK_CONTAINER(main_vbox), 1);
+ gtk_container_add(GTK_CONTAINER(window), main_vbox);
+ gtk_widget_show(main_vbox);
+
+ get_main_menu(&amp;menubar, &amp;accel);
+ gtk_window_add_accelerator_table(GTK_WINDOW(window), accel);
+ gtk_box_pack_start(GTK_BOX(main_vbox), menubar, FALSE, TRUE, 0);
+ gtk_widget_show(menubar);
+
+ gtk_widget_show(window);
+ gtk_main();
+
+ return(0);
+}
+
+/* This is just to demonstrate how callbacks work when using the
+ * menufactory. Often, people put all the callbacks from the menus
+ * in a separate file, and then have them call the appropriate functions
+ * from there. Keeps it more organized. */
+void file_quit_cmd_callback (GtkWidget *widget, gpointer data)
+{
+ g_print ("%s\n", (char *) data);
+ gtk_exit(0);
+}
+</verb></tscreen>
+<p>
+And a makefile so it'll be easier to compile it.
+
+<tscreen><verb>
+# Makefile.mf
+
+CC = gcc
+PROF = -g
+C_FLAGS = -Wall $(PROF) -L/usr/local/include -DDEBUG
+L_FLAGS = $(PROF) -L/usr/X11R6/lib -L/usr/local/lib
+L_POSTFLAGS = -lgtk -lgdk -lglib -lXext -lX11 -lm
+PROGNAME = menufactory
+
+O_FILES = menufactory.o mfmain.o
+
+$(PROGNAME): $(O_FILES)
+ rm -f $(PROGNAME)
+ $(CC) $(L_FLAGS) -o $(PROGNAME) $(O_FILES) $(L_POSTFLAGS)
+
+.c.o:
+ $(CC) -c $(C_FLAGS) $<
+
+clean:
+ rm -f core *.o $(PROGNAME) nohup.out
+distclean: clean
+ rm -f *~
+</verb></tscreen>
+<p>
+For now, there's only this example. An explanation and lots 'o' comments
+will follow later.
+
+<!-- ***************************************************************** -->
+<sect> Undocumented Widgets
+<!-- ***************************************************************** -->
+
+<p>
+These all require authors! :) Please consider contributing to our tutorial.
+
+If you must use one of these widgets that are undocumented, I strongly
+suggest you take a look at their respective header files in the GTK distro.
+GTK's function names are very descriptive. Once you have an understanding
+of how things work, it's not easy to figure out how to use a widget simply
+by looking at it's function declarations. This, along with a few examples
+from others' code, and it should be no problem.
+
+When you do come to understand all the functions of a new undocumented
+widget, please consider writing a tutorial on it so others may benifit from
+your time.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Color Selections
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Range Controls
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Text Boxes
+<p>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Previews
+<p>
+
+(This may need to be rewritten to follow the style of the rest of the tutorial)
+
+<tscreen><verb>
+
+Previews serve a number of purposes in GIMP/GTK. The most important one is
+this. High quality images may take up to tens of megabytes of memory - easy!
+Any operation on an image that big is bound to take a long time. If it takes
+you 5-10 trial-and-errors (i.e. 10-20 steps, since you have to revert after
+you make an error) to choose the desired modification, it make take you
+literally hours to make the right one - if you don't run out of memory
+first. People who have spent hours in color darkrooms know the feeling.
+Previews to the rescue!
+
+But the annoyance of the delay is not the only issue. Oftentimes it is
+helpful to compare the Before and After versions side-by-side or at least
+back-to-back. If you're working with big images and 10 second delays,
+obtaining the Before and After impressions is, to say the least, difficult.
+For 30M images (4"x6", 600dpi, 24 bit) the side-by-side comparison is right
+out for most people, while back-to-back is more like back-to-1001, 1002,
+..., 1010-back! Previews to the rescue!
+
+But there's more. Previews allow for side-by-side pre-previews. In other
+words, you write a plug-in (e.g. the filterpack simulation) which would have
+a number of here's-what-it-would-look-like-if-you-were-to-do-this previews.
+An approach like this acts as a sort of a preview palette and is very
+effective fow subtle changes. Let's go previews!
+
+There's more. For certain plug-ins real-time image-specific human
+intervention maybe necessary. In the SuperNova plug-in, for example, the
+user is asked to enter the coordinates of the center of the future
+supernova. The easiest way to do this, really, is to present the user with a
+preview and ask him to intereactively select the spot. Let's go previews!
+
+Finally, a couple of misc uses. One can use previews even when not working
+with big images. For example, they are useful when rendering compicated
+patterns. (Just check out the venerable Diffraction plug-in + many other
+ones!) As another example, take a look at the colormap rotation plug-in
+(work in progress). You can also use previews for little logo's inside you
+plug-ins and even for an image of yourself, The Author. Let's go previews!
+
+When Not to Use Previews
+
+Don't use previews for graphs, drawing etc. GDK is much faster for that. Use
+previews only for rendered images!
+
+Let's go previews!
+
+You can stick a preview into just about anything. In a vbox, an hbox, a
+table, a button, etc. But they look their best in tight frames around them.
+Previews by themselves do not have borders and look flat without them. (Of
+course, if the flat look is what you want...) Tight frames provide the
+necessary borders.
+
+ [Image][Image]
+
+Previews in many ways are like any other widgets in GTK (whatever that
+means) except they possess an addtional feature: they need to be filled with
+some sort of an image! First, we will deal exclusively with the GTK aspect
+of previews and then we'll discuss how to fill them.
+
+GtkWidget *preview!
+
+Without any ado:
+
+ /* Create a preview widget,
+ set its size, an show it */
+GtkWidget *preview;
+preview=gtk_preview_new(GTK_PREVIEW_COLOR)
+ /*Other option:
+ GTK_PREVIEW_GRAYSCALE);*/
+gtk_preview_size (GTK_PREVIEW (preview), WIDTH, HEIGHT);
+gtk_widget_show(preview);
+my_preview_rendering_function(preview);
+
+Oh yeah, like I said, previews look good inside frames, so how about:
+
+GtkWidget *create_a_preview(int Width,
+ int Height,
+ int Colorfulness)
+{
+ GtkWidget *preview;
+ GtkWidget *frame;
+
+ frame = gtk_frame_new(NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
+ gtk_container_border_width (GTK_CONTAINER(frame),0);
+ gtk_widget_show(frame);
+
+ preview=gtk_preview_new (Colorfulness?GTK_PREVIEW_COLOR
+ :GTK_PREVIEW_GRAYSCALE);
+ gtk_preview_size (GTK_PREVIEW (preview), Width, Height);
+ gtk_container_add(GTK_CONTAINER(frame),preview);
+ gtk_widget_show(preview);
+
+ my_preview_rendering_function(preview);
+ return frame;
+}
+
+That's my basic preview. This routine returns the "parent" frame so you can
+place it somewhere else in your interface. Of course, you can pass the
+parent frame to this routine as a parameter. In many situations, however,
+the contents of the preview are changed continually by your application. In
+this case you may want to pass a pointer to the preview to a
+"create_a_preview()" and thus have control of it later.
+
+One more important note that may one day save you a lot of time. Sometimes
+it is desirable to label you preview. For example, you may label the preview
+containing the original image as "Original" and the one containing the
+modified image as "Less Original". It might occure to you to pack the
+preview along with the appropriate label into a vbox. The unexpected caveat
+is that if the label is wider than the preview (which may happen for a
+variety of reasons unforseeable to you, from the dynamic decision on the
+size of the preview to the size of the font) the frame expands and no longer
+fits tightly over the preview. The same problem can probably arise in other
+situations as well.
+
+ [Image]
+
+The solution is to place the preview and the label into a 2x1 table and by
+attaching them with the following paramters (this is one possible variations
+of course. The key is no GTK_FILL in the second attachment):
+
+gtk_table_attach(GTK_TABLE(table),label,0,1,0,1,
+ 0,
+ GTK_EXPAND|GTK_FILL,
+ 0,0);
+gtk_table_attach(GTK_TABLE(table),frame,0,1,1,2,
+ GTK_EXPAND,
+ GTK_EXPAND,
+ 0,0);
+
+
+And here's the result:
+
+ [Image]
+
+Misc
+
+Making a preview clickable is achieved most easily by placing it in a
+button. It also adds a nice border around the preview and you may not even
+need to place it in a frame. See the Filter Pack Simulation plug-in for an
+example.
+
+This is pretty much it as far as GTK is concerned.
+
+Filling In a Preview
+
+In order to familiarize ourselves with the basics of filling in previews,
+let's create the following pattern (contrived by trial and error):
+
+ [Image]
+
+void
+my_preview_rendering_function(GtkWidget *preview)
+{
+#define SIZE 100
+#define HALF (SIZE/2)
+
+ guchar *row=(guchar *) malloc(3*SIZE); /* 3 bits per dot */
+ gint i, j; /* Coordinates */
+ double r, alpha, x, y;
+
+ if (preview==NULL) return; /* I usually add this when I want */
+ /* to avoid silly crashes. You */
+ /* should probably make sure that */
+ /* everything has been nicely */
+ /* initialized! */
+ for (j=0; j < ABS(cos(2*alpha)) ) { /* Are we inside the shape? */
+ /* glib.h contains ABS(x). */
+ row[i*3+0] = sqrt(1-r)*255; /* Define Red */
+ row[i*3+1] = 128; /* Define Green */
+ row[i*3+2] = 224; /* Define Blue */
+ } /* "+0" is for alignment! */
+ else {
+ row[i*3+0] = r*255;
+ row[i*3+1] = ABS(sin((float)i/SIZE*2*PI))*255;
+ row[i*3+2] = ABS(sin((float)j/SIZE*2*PI))*255;
+ }
+ }
+ gtk_preview_draw_row( GTK_PREVIEW(preview),row,0,j,SIZE);
+ /* Insert "row" into "preview" starting at the point with */
+ /* coordinates (0,j) first column, j_th row extending SIZE */
+ /* pixels to the right */
+ }
+
+ free(row); /* save some space */
+ gtk_widget_draw(preview,NULL); /* what does this do? */
+ gdk_flush(); /* or this? */
+}
+
+Non-GIMP users can have probably seen enough to do a lot of things already.
+For the GIMP users I have a few pointers to add.
+
+Image Preview
+
+It is probably wize to keep a reduced version of the image around with just
+enough pixels to fill the preview. This is done by selecting every n'th
+pixel where n is the ratio of the size of the image to the size of the
+preview. All further operations (including filling in the previews) are then
+performed on the reduced number of pixels only. The following is my
+implementation of reducing the image. (Keep in mind that I've had only basic
+C!)
+
+(UNTESTED CODE ALERT!!!)
+
+typedef struct {
+ gint width;
+ gint height;
+ gint bbp;
+ guchar *rgb;
+ guchar *mask;
+} ReducedImage;
+
+enum {
+ SELECTION_ONLY,
+ SELCTION_IN_CONTEXT,
+ ENTIRE_IMAGE
+};
+
+ReducedImage *Reduce_The_Image(GDrawable *drawable,
+ GDrawable *mask,
+ gint LongerSize,
+ gint Selection)
+{
+ /* This function reduced the image down to the the selected preview size */
+ /* The preview size is determine by LongerSize, i.e. the greater of the */
+ /* two dimentions. Works for RGB images only! */
+ gint RH, RW; /* Reduced height and reduced width */
+ gint width, height; /* Width and Height of the area being reduced */
+ gint bytes=drawable->bpp;
+ ReducedImage *temp=(ReducedImage *)malloc(sizeof(ReducedImage));
+
+ guchar *tempRGB, *src_row, *tempmask, *src_mask_row,R,G,B;
+ gint i, j, whichcol, whichrow, x1, x2, y1, y2;
+ GPixelRgn srcPR, srcMask;
+ gint NoSelectionMade=TRUE; /* Assume that we're dealing with the entire */
+ /* image. */
+
+ gimp_drawable_mask_bounds (drawable->id, &amp;x1, &amp;y1, &amp;x2, &amp;y2);
+ width = x2-x1;
+ height = y2-y1;
+ /* If there's a SELECTION, we got its bounds!)
+
+ if (width != drawable->width &amp;&amp; height != drawable->height)
+ NoSelectionMade=FALSE;
+ /* Become aware of whether the user has made an active selection */
+ /* This will become important later, when creating a reduced mask. */
+
+ /* If we want to preview the entire image, overrule the above! */
+ /* Of course, if no selection has been made, this does nothing! */
+ if (Selection==ENTIRE_IMAGE) {
+ x1=0;
+ x2=drawable->width;
+ y1=0;
+ y2=drawable->height;
+ }
+
+ /* If we want to preview a selection with some surronding area we */
+ /* have to expand it a little bit. Consider it a bit of a riddle. */
+ if (Selection==SELECTION_IN_CONTEXT) {
+ x1=MAX(0, x1-width/2.0);
+ x2=MIN(drawable->width, x2+width/2.0);
+ y1=MAX(0, y1-height/2.0);
+ y2=MIN(drawable->height, y2+height/2.0);
+ }
+
+ /* How we can determine the width and the height of the area being */
+ /* reduced. */
+ width = x2-x1;
+ height = y2-y1;
+
+ /* The lines below determine which dimension is to be the longer */
+ /* side. The idea borrowed from the supernova plug-in. I suspect I */
+ /* could've thought of it myself, but the truth must be told. */
+ /* Plagiarism stinks! */
+ if (width>height) {
+ RW=LongerSize;
+ RH=(float) height * (float) LongerSize/ (float) width;
+ }
+ else {
+ RH=LongerSize;
+ RW=(float)width * (float) LongerSize/ (float) height;
+ }
+
+ /* The intire image is stretched into a string! */
+ tempRGB = (guchar *) malloc(RW*RH*bytes);
+ tempmask = (guchar *) malloc(RW*RH);
+
+ gimp_pixel_rgn_init (&amp;srcPR, drawable, x1, y1, width, height, FALSE, FALSE);
+ gimp_pixel_rgn_init (&amp;srcMask, mask, x1, y1, width, height, FALSE, FALSE);
+
+ /* Grab enough to save a row of image and a row of mask. */
+ src_row = (guchar *) malloc (width*bytes);
+ src_mask_row = (guchar *) malloc (width);
+
+ for (i=0; i < RH; i++) {
+ whichrow=(float)i*(float)height/(float)RH;
+ gimp_pixel_rgn_get_row (&amp;srcPR, src_row, x1, y1+whichrow, width);
+ gimp_pixel_rgn_get_row (&amp;srcMask, src_mask_row, x1, y1+whichrow, width);
+
+ for (j=0; j < RW; j++) {
+ whichcol=(float)j*(float)width/(float)RW;
+
+ /* No selection made = each point is completely selected! */
+ if (NoSelectionMade)
+ tempmask[i*RW+j]=255;
+ else
+ tempmask[i*RW+j]=src_mask_row[whichcol];
+
+ /* Add the row to the one long string which now contains the image! */
+ tempRGB[i*RW*bytes+j*bytes+0]=src_row[whichcol*bytes+0];
+ tempRGB[i*RW*bytes+j*bytes+1]=src_row[whichcol*bytes+1];
+ tempRGB[i*RW*bytes+j*bytes+2]=src_row[whichcol*bytes+2];
+
+ /* Hold on to the alpha as well */
+ if (bytes==4)
+ tempRGB[i*RW*bytes+j*bytes+3]=src_row[whichcol*bytes+3];
+ }
+ }
+ temp->bpp=bytes;
+ temp->width=RW;
+ temp->height=RH;
+ temp->rgb=tempRGB;
+ temp->mask=tempmask;
+ return temp;
+}
+
+The following is a preview function which used the same ReducedImage type!
+Note that it uses fakes transparancy (if one is present by means of
+fake_transparancy which is defined as follows:
+
+gint fake_transparency(gint i, gint j)
+{
+ if ( ((i%20)- 10) * ((j%20)- 10)>0 )
+ return 64;
+ else
+ return 196;
+}
+
+Now here's the preview function:
+
+void
+my_preview_render_function(GtkWidget *preview,
+ gint changewhat,
+ gint changewhich)
+{
+ gint Inten, bytes=drawable->bpp;
+ gint i, j, k;
+ float partial;
+ gint RW=reduced->width;
+ gint RH=reduced->height;
+ guchar *row=malloc(bytes*RW);;
+
+
+ for (i=0; i < RH; i++) {
+ for (j=0; j < RW; j++) {
+
+ row[j*3+0] = reduced->rgb[i*RW*bytes + j*bytes + 0];
+ row[j*3+1] = reduced->rgb[i*RW*bytes + j*bytes + 1];
+ row[j*3+2] = reduced->rgb[i*RW*bytes + j*bytes + 2];
+
+ if (bytes==4)
+ for (k=0; k<3; k++) {
+ float transp=reduced->rgb[i*RW*bytes+j*bytes+3]/255.0;
+ row[3*j+k]=transp*a[3*j+k]+(1-transp)*fake_transparency(i,j);
+ }
+ }
+ gtk_preview_draw_row( GTK_PREVIEW(preview),row,0,i,RW);
+ }
+
+ free(a);
+ gtk_widget_draw(preview,NULL);
+ gdk_flush();
+}
+
+Applicable Routines
+
+guint gtk_preview_get_type (void);
+/* No idea */
+void gtk_preview_uninit (void);
+/* No idea */
+GtkWidget* gtk_preview_new (GtkPreviewType type);
+/* Described above */
+void gtk_preview_size (GtkPreview *preview,
+ gint width,
+ gint height);
+/* Allows you to resize an existing preview. */
+/* Apparantly there's a bug in GTK which makes */
+/* this process messy. A way to clean up a mess */
+/* is to manually resize the window containing */
+/* the preview after resizing the preview. */
+
+void gtk_preview_put (GtkPreview *preview,
+ GdkWindow *window,
+ GdkGC *gc,
+ gint srcx,
+ gint srcy,
+ gint destx,
+ gint desty,
+ gint width,
+ gint height);
+/* No idea */
+
+void gtk_preview_put_row (GtkPreview *preview,
+ guchar *src,
+ guchar *dest,
+ gint x,
+ gint y,
+ gint w);
+/* No idea */
+
+void gtk_preview_draw_row (GtkPreview *preview,
+ guchar *data,
+ gint x,
+ gint y,
+ gint w);
+/* Described in the text */
+
+void gtk_preview_set_expand (GtkPreview *preview,
+ gint expand);
+/* No idea */
+
+/* No clue for any of the below but */
+/* should be standard for most widgets */
+void gtk_preview_set_gamma (double gamma);
+void gtk_preview_set_color_cube (guint nred_shades,
+ guint ngreen_shades,
+ guint nblue_shades,
+ guint ngray_shades);
+void gtk_preview_set_install_cmap (gint install_cmap);
+void gtk_preview_set_reserved (gint nreserved);
+GdkVisual* gtk_preview_get_visual (void);
+GdkColormap* gtk_preview_get_cmap (void);
+GtkPreviewInfo* gtk_preview_get_info (void);
+
+That's all, folks!
+
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Curves
+<p>
+
+<!-- ***************************************************************** -->
+<sect>The EventBox Widget<label id="sec_The_EventBox_Widget">
+<!-- ***************************************************************** -->
+
+<p>
+Some gtk widgets don't have associated X windows, so they just draw on
+thier parents. Because of this, they cannot recieve events
+and if they are incorrectly sized, they don't clip so you can get
+messy overwritting etc. If you require more from these widgets, the
+EventBox is for you.
+
+At first glance, the EventBox widget might appear to be totally
+useless. It draws nothing on the screen and responds to no
+events. However, it does serve a function - it provides an X window for
+its child widget. This is important as many GTK widgets do not
+have an associated X window. Not having an X window saves memory and
+improves performance, but also has some drawbacks. A widget without an
+X window cannot receive events, and does not perform any clipping on
+it's contents. Although the name ``EventBox'' emphasizes the
+event-handling function, the widget also can be used for clipping.
+(And more ... see the example below.)
+
+<p>
+To create a new EventBox widget, use:
+
+<tscreen><verb>
+GtkWidget* gtk_event_box_new (void);
+</verb></tscreen>
+
+<p>
+A child widget can then be added to this EventBox:
+
+<tscreen><verb>
+gtk_container_add (GTK_CONTAINER(event_box), widget);
+</verb></tscreen>
+
+<p>
+The following example demonstrates both uses of an EventBox - a label
+is created that clipped to a small box, and set up so that a
+mouse-click on the label causes the program to exit.
+
+<tscreen><verb>
+/* eventbox.c */
+
+#include <gtk/gtk.h>
+
+int
+main (int argc, char *argv[])
+{
+ GtkWidget *window;
+ GtkWidget *event_box;
+ GtkWidget *label;
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+ gtk_window_set_title (GTK_WINDOW (window), "Event Box");
+
+ gtk_signal_connect (GTK_OBJECT (window), "destroy",
+ GTK_SIGNAL_FUNC (gtk_exit), NULL);
+
+ gtk_container_border_width (GTK_CONTAINER (window), 10);
+
+ /* Create an EventBox and add it to our toplevel window */
+
+ event_box = gtk_event_box_new ();
+ gtk_container_add (GTK_CONTAINER(window), event_box);
+ gtk_widget_show (event_box);
+
+ /* Create a long label */
+
+ label = gtk_label_new ("Click here to quit, quit, quit, quit, quit");
+ gtk_container_add (GTK_CONTAINER (event_box), label);
+ gtk_widget_show (label);
+
+ /* Clip it short. */
+ gtk_widget_set_usize (label, 110, 20);
+
+ /* And bind an action to it */
+ gtk_widget_set_events (event_box, GDK_BUTTON_PRESS_MASK);
+ gtk_signal_connect (GTK_OBJECT(event_box), "button_press_event",
+ GTK_SIGNAL_FUNC (gtk_exit), NULL);
+
+ /* Yet one more thing you need an X window for ... */
+
+ gtk_widget_realize (event_box);
+ gdk_window_set_cursor (event_box->window, gdk_cursor_new (GDK_HAND1));
+
+ gtk_widget_show (window);
+
+ gtk_main ();
+
+ return 0;
+}
+</verb></tscreen>
+
+<!-- ***************************************************************** -->
+<sect>Setting Widget Attributes<label id="sec_setting_widget_attributes">
+<!-- ***************************************************************** -->
+
+<p>
+This describes the functions used to operate on widgets. These can be used
+to set style, padding, size etc.
+
+(Maybe I should make a whole section on accelerators.)
+
+<tscreen><verb>
+void gtk_widget_install_accelerator (GtkWidget *widget,
+ GtkAcceleratorTable *table,
+ gchar *signal_name,
+ gchar key,
+ guint8 modifiers);
+
+void gtk_widget_remove_accelerator (GtkWidget *widget,
+ GtkAcceleratorTable *table,
+ gchar *signal_name);
+
+void gtk_widget_activate (GtkWidget *widget);
+
+void gtk_widget_set_name (GtkWidget *widget,
+ gchar *name);
+gchar* gtk_widget_get_name (GtkWidget *widget);
+
+void gtk_widget_set_sensitive (GtkWidget *widget,
+ gint sensitive);
+
+void gtk_widget_set_style (GtkWidget *widget,
+ GtkStyle *style);
+
+GtkStyle* gtk_widget_get_style (GtkWidget *widget);
+
+GtkStyle* gtk_widget_get_default_style (void);
+
+void gtk_widget_set_uposition (GtkWidget *widget,
+ gint x,
+ gint y);
+void gtk_widget_set_usize (GtkWidget *widget,
+ gint width,
+ gint height);
+
+void gtk_widget_grab_focus (GtkWidget *widget);
+
+void gtk_widget_show (GtkWidget *widget);
+
+void gtk_widget_hide (GtkWidget *widget);
+</verb></tscreen>
+
+<!-- ***************************************************************** -->
+<sect>Timeouts, IO and Idle Functions<label id="sec_timeouts">
+<!-- ***************************************************************** -->
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Timeouts
+<p>
+You may be wondering how you make GTK do useful work when in gtk_main.
+Well, you have several options. Using the following functions you can
+create a timeout function that will be called every "interval" milliseconds.
+
+<tscreen><verb>
+gint gtk_timeout_add (guint32 interval,
+ GtkFunction function,
+ gpointer data);
+</verb></tscreen>
+
+The first argument is the number of milliseconds
+between calls to your function. The second argument is the function
+you wish to have called, and
+the third, the data passed to this callback function. The return value is
+an integer "tag" which may be used to stop the timeout by calling:
+
+<tscreen><verb>
+void gtk_timeout_remove (gint tag);
+</verb></tscreen>
+
+You may also stop the timeout function by returning zero or FALSE from
+your callback function. Obviously this means if you want your function to
+continue to be called, it should return a non-zero value, ie TRUE.
+
+The declaration of your callback should look something like this:
+
+<tscreen><verb>
+gint timeout_callback (gpointer data);
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Monitoring IO
+<p>
+Another nifty feature of GTK, is the ability to have it check for data on a
+file descriptor for you (as returned by open(2) or socket(2)). This is
+especially useful for networking applications. The function:
+
+<tscreen><verb>
+gint gdk_input_add (gint source,
+ GdkInputCondition condition,
+ GdkInputFunction function,
+ gpointer data);
+</verb></tscreen>
+
+Where the first argument is the file descriptor you wish to have watched,
+and the second specifies what you want GDK to look for. This may be one of:
+<p>
+GDK_INPUT_READ - Call your function when there is data ready for reading on
+your file descriptor.
+<p>
+GDK_INPUT_WRITE - Call your function when the file descriptor is ready for
+writing.
+<p>
+As I'm sure you've figured out already, the third argument is the function
+you wish to have called when the above conditions are satisfied, and the
+fourth is the data to pass to this function.
+<p>
+The return value is a tag that may be used to stop GDK from monitoring this
+file descriptor using the following function.
+<p>
+<tscreen><verb>
+void gdk_input_remove (gint tag);
+</verb></tscreen>
+<p>
+The callback function should be declared:
+<p>
+<tscreen><verb>
+void input_callback (gpointer data, gint source,
+ GdkInputCondition condition);
+</verb></tscreen>
+<p>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Idle Functions
+<p>
+What if you have a function you want called when nothing else is
+happening ?
+
+<tscreen><verb>
+gint gtk_idle_add (GtkFunction function,
+ gpointer data);
+</verb></tscreen>
+
+This causes GTK to call the specified function whenever nothing else is
+happening.
+
+<tscreen><verb>
+void gtk_idle_remove (gint tag);
+</verb></tscreen>
+<p>
+I won't explain the meaning of the arguments as they follow very much like
+the ones above. The function pointed to by the first argument to
+gtk_idle_add will be called whenever the opportunity arises. As with the
+others, returning FALSE will stop the idle function from being called.
+
+<!-- ***************************************************************** -->
+<sect>Managing Selections
+<!-- ***************************************************************** -->
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Overview
+
+<p>
+
+One type of interprocess communication supported by GTK is
+<em>selections</em>. A selection identifies a chunk of data, for
+instance, a portion of text, selected by the user in some fashion, for
+instance, by dragging with the mouse. Only one application on a
+display, (he <em>owner</em>_ can own a particular selection at one
+time, so when a selection is claimed by one application, the previous
+owner must indicate to the user that selection has been
+relinquished. Other applications can request the contents of a
+selection in different forms, called <em>targets</em>. There can be
+any number of selections, but most X applications only handle one, the
+<em>primary selection</em>.
+
+<p>
+In most cases, it isn't necessary for a GTK application to deal with
+selections itself. The standard widgets, such as the Entry widget,
+already have the capability to claim the selection when appropriate
+(e.g., when the user drags over text), and to retrieve the contents of
+the selection owned by another widget, or another application (e.g.,
+when the user clicks the second mouse button). However, there may be
+cases in which you want to give other widgets the ability to supply
+the selection, or you wish to retrieve targets not supported by
+default.
+
+<p>
+A fundamental concept needed to understand selection handling is that
+of the <em>atom</em>. An atom is an integer that uniquely identifies a
+string (on a certain display). Certain atoms are predefined by the X
+server, and in some cases there are constants in in <tt>gtk.h</tt>
+corresponding to these atoms. For instance the constant
+<tt>GDK_PRIMARY_SELECTION</tt> corresponds to the string "PRIMARY".
+In other cases, you should use the functions
+<tt>gdk_atom_intern()</tt>, to get the atom corresponding to a string,
+and <tt>gdk_atom_name()</tt>, to get the name of an atom. Both
+selections and targets are identifed by atoms.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Retrieving the selection
+
+<p>
+
+Retrieving the selection is an asynchronous process. To start the
+process, you call:
+
+<tscreen><verb>
+gint gtk_selection_convert (GtkWidget *widget,
+ GdkAtom selection,
+ GdkAtom target,
+ guint32 time)
+</verb</tscreen>
+
+This <em>converts</em> the selection into the form specified by
+<tt/target/. If it all possible, the time field should be the time
+from the event that triggered the selection. This helps make sure that
+events occur in the order that the user requested them.
+ However, if it is not available (for instance, if the conversion was
+triggered by a "clicked" signal), then you can use the constant
+<tt>GDK_CURRENT_TIME</tt>.
+
+<p>
+When the selection owner responds to the request, a
+"selection_received" signal is sent to your application. The handler
+for this signal receives a pointer to a <tt>GtkSelectionData</tt>
+structure, which is defined as:
+
+<tscreen><verb>
+struct _GtkSelectionData
+{
+ GdkAtom selection;
+ GdkAtom target;
+ GdkAtom type;
+ gint format;
+ guchar *data;
+ gint length;
+};
+</verb></tscreen>
+
+<tt>selection</tt> and <tt>target</tt> are the values you gave in your
+<tt>gtk_selection_convert()</tt> call. <tt>type</tt> is an atom that
+identifies the type of data returned by the selection owner. Some
+possible values are "STRING", a string of latin-1 characters, "ATOM",
+a series of atoms, "INTEGER", an integer, etc. Most targets can only
+return one type. <tt/format/ gives the length of the units (for
+instance characters) in bits. Usually, you don't care about this when
+receiving data. <tt>data</tt> is a pointer to the returned data, and
+<tt>length</tt> gives the length of the returned data, in bytes. If
+<tt>length</tt> is negative, then an error occurred and the selection
+could not be retrieved. This might happen if no application owned the
+selection, or if you requested a target that the application didn't
+support. The buffer is actually guaranteed to be one byte longer than
+<tt>length</tt>; the extra byte will always be zero, so it isn't
+necessary to make a copy of strings just to null terminate them.
+
+<p>
+In the following example, we retrieve the special target "TARGETS",
+which is a list of all targets into which the selection can be
+converted.
+
+<tscreen><verb>
+/* gettargets.c */
+
+#include <gtk/gtk.h>
+
+void selection_received (GtkWidget *widget,
+ GtkSelectionData *selection_data,
+ gpointer data);
+
+/* Signal handler invoked when user clicks on the "Get Targets" button */
+void
+get_targets (GtkWidget *widget, gpointer data)
+{
+ static GdkAtom targets_atom = GDK_NONE;
+
+ /* Get the atom corresonding to the string "TARGETS" */
+ if (targets_atom == GDK_NONE)
+ targets_atom = gdk_atom_intern ("TARGETS", FALSE);
+
+ /* And request the "TARGETS" target for the primary selection */
+ gtk_selection_convert (widget, GDK_SELECTION_PRIMARY, targets_atom,
+ GDK_CURRENT_TIME);
+}
+
+/* Signal handler called when the selections owner returns the data */
+void
+selection_received (GtkWidget *widget, GtkSelectionData *selection_data,
+ gpointer data)
+{
+ GdkAtom *atoms;
+ GList *item_list;
+ int i;
+
+ /* **** IMPORTANT **** Check to see if retrieval succeeded */
+ if (selection_data->length < 0)
+ {
+ g_print ("Selection retrieval failed\n");
+ return;
+ }
+ /* Make sure we got the data in the expected form */
+ if (selection_data->type != GDK_SELECTION_TYPE_ATOM)
+ {
+ g_print ("Selection \"TARGETS\" was not returned as atoms!\n");
+ return;
+ }
+
+ /* Print out the atoms we received */
+ atoms = (GdkAtom *)selection_data->data;
+
+ item_list = NULL;
+ for (i=0; i<selection_data->length/sizeof(GdkAtom); i++)
+ {
+ char *name;
+ name = gdk_atom_name (atoms[i]);
+ if (name != NULL)
+ g_print ("%s\n",name);
+ else
+ g_print ("(bad atom)\n");
+ }
+
+ return;
+}
+
+int
+main (int argc, char *argv[])
+{
+ GtkWidget *window;
+ GtkWidget *button;
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ /* Create the toplevel window */
+
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title (GTK_WINDOW (window), "Event Box");
+ gtk_container_border_width (GTK_CONTAINER (window), 10);
+
+ gtk_signal_connect (GTK_OBJECT (window), "destroy",
+ GTK_SIGNAL_FUNC (gtk_exit), NULL);
+
+ /* Create a button the user can click to get targets */
+
+ button = gtk_button_new_with_label ("Get Targets");
+ gtk_container_add (GTK_CONTAINER (window), button);
+
+ gtk_signal_connect (GTK_OBJECT(button), "clicked",
+ GTK_SIGNAL_FUNC (get_targets), NULL);
+ gtk_signal_connect (GTK_OBJECT(button), "selection_received",
+ GTK_SIGNAL_FUNC (selection_received), NULL);
+
+ gtk_widget_show (button);
+ gtk_widget_show (window);
+
+ gtk_main ();
+
+ return 0;
+}
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Supplying the selection
+
+<p>
+
+Supplying the selection is a bit more complicated. You must register
+handlers that will be called when your selection is requested. For
+each selection/target pair you will handle, you make a call to:
+
+<tscreen><verb>
+void gtk_selection_add_handler (GtkWidget *widget,
+ GdkAtom selection,
+ GdkAtom target,
+ GtkSelectionFunction function,
+ GtkRemoveFunction remove_func,
+ gpointer data);
+</verb></tscreen>
+
+<tt/widget/, <tt/selection/, and <tt/target/ identify the requests
+this handler will manage. <tt/remove_func/ if not
+NULL, will be called when the signal handler is removed. This is
+useful, for instance, for interpreted languages which need to
+keep track of a reference count for <tt/data/.
+
+<p>
+The callback function has the signature:
+
+<tscreen><verb>
+typedef void (*GtkSelectionFunction) (GtkWidget *widget,
+ GtkSelectionData *selection_data,
+ gpointer data);
+
+</verb></tscreen>
+
+The GtkSelectionData is the same as above, but this time, we're
+responsible for filling in the fields <tt/type/, <tt/format/,
+<tt/data/, and <tt/length/. (The <tt/format/ field is actually
+important here - the X server uses it to figure out whether the data
+needs to be byte-swapped or not. Usually it will be 8 - <em/i.e./ a
+character - or 32 - <em/i.e./ a. integer.) This is done by calling the
+function:
+
+<tscreen><verb>
+void gtk_selection_data_set (GtkSelectionData *selection_data,
+ GdkAtom type,
+ gint format,
+ guchar *data,
+ gint length);
+</verb></tscreen>
+
+This function takes care of properly making a copy of the data so that
+you don't have to worry about keeping it around. (You should not fill
+in the fields of the GtkSelectionData structure by hand.)
+
+<p>
+When prompted by the user, you claim ownership of the selection by
+calling:
+
+<tscreen><verb>
+gint gtk_selection_owner_set (GtkWidget *widget,
+ GdkAtom selection,
+ guint32 time);
+</verb></tscreen>
+
+If another application claims ownership of the selection, you will
+receive a "selection_clear_event".
+
+As an example of supplying the selection, the following program adds
+selection functionality to a toggle button. When the toggle button is
+depressed, the program claims the primary selection. The only target
+supported (aside from certain targets like "TARGETS" supplied by GTK
+itself), is the "STRING" target. When this target is requested, a
+string representation of the time is returned.
+
+<tscreen><verb>
+/* setselection.c */
+
+#include <gtk/gtk.h>
+#include <time.h>
+
+/* Callback when the user toggles the selection */
+void
+selection_toggled (GtkWidget *widget, gint *have_selection)
+{
+ if (GTK_TOGGLE_BUTTON(widget)->active)
+ {
+ *have_selection = gtk_selection_owner_set (widget,
+ GDK_SELECTION_PRIMARY,
+ GDK_CURRENT_TIME);
+ /* if claiming the selection failed, we return the button to
+ the out state */
+ if (!*have_selection)
+ gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON(widget), FALSE);
+ }
+ else
+ {
+ if (*have_selection)
+ {
+ /* Before clearing the selection by setting the owner to NULL,
+ we check if we are the actual owner */
+ if (gdk_selection_owner_get (GDK_SELECTION_PRIMARY) == widget->window)
+ gtk_selection_owner_set (NULL, GDK_SELECTION_PRIMARY,
+ GDK_CURRENT_TIME);
+ *have_selection = FALSE;
+ }
+ }
+}
+
+/* Called when another application claims the selection */
+gint
+selection_clear (GtkWidget *widget, GdkEventSelection *event,
+ gint *have_selection)
+{
+ *have_selection = FALSE;
+ gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON(widget), FALSE);
+
+ return TRUE;
+}
+
+/* Supplies the current time as the selection. */
+void
+selection_handle (GtkWidget *widget,
+ GtkSelectionData *selection_data,
+ gpointer data)
+{
+ gchar *timestr;
+ time_t current_time;
+
+ current_time = time (NULL);
+ timestr = asctime (localtime(&amp;current_time));
+ /* When we return a single string, it should not be null terminated.
+ That will be done for us */
+
+ gtk_selection_data_set (selection_data, GDK_SELECTION_TYPE_STRING,
+ 8, timestr, strlen(timestr));
+}
+
+int
+main (int argc, char *argv[])
+{
+ GtkWidget *window;
+
+ GtkWidget *selection_button;
+
+ static int have_selection = FALSE;
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ /* Create the toplevel window */
+
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title (GTK_WINDOW (window), "Event Box");
+ gtk_container_border_width (GTK_CONTAINER (window), 10);
+
+ gtk_signal_connect (GTK_OBJECT (window), "destroy",
+ GTK_SIGNAL_FUNC (gtk_exit), NULL);
+
+ /* Create a toggle button to act as the selection */
+
+ selection_button = gtk_toggle_button_new_with_label ("Claim Selection");
+ gtk_container_add (GTK_CONTAINER (window), selection_button);
+ gtk_widget_show (selection_button);
+
+ gtk_signal_connect (GTK_OBJECT(selection_button), "toggled",
+ GTK_SIGNAL_FUNC (selection_toggled), &amp;have_selection);
+ gtk_signal_connect (GTK_OBJECT(selection_button), "selection_clear_event",
+ GTK_SIGNAL_FUNC (selection_clear), &amp;have_selection);
+
+ gtk_selection_add_handler (selection_button, GDK_SELECTION_PRIMARY,
+ GDK_SELECTION_TYPE_STRING,
+ selection_handle, NULL);
+
+ gtk_widget_show (selection_button);
+ gtk_widget_show (window);
+
+ gtk_main ();
+
+ return 0;
+}
+</verb></tscreen>
+
+
+<!-- ***************************************************************** -->
+<sect>glib<label id="sec_glib">
+<!-- ***************************************************************** -->
+
+<p>
+glib provides many useful functions and definitions available for use
+when creating GDK
+and GTK applications. I will list them all here with a brief explanation.
+Many are duplicates of standard libc functions so I won't go into
+detail on those. This is mostly to be used as a reference, so you know what is
+available for use.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Definitions
+<p>
+Definitions for the extremes of many of the standard types are:
+
+<tscreen><verb>
+G_MINFLOAT
+G_MAXFLOAT
+G_MINDOUBLE
+G_MAXDOUBLE
+G_MINSHORT
+G_MAXSHORT
+G_MININT
+G_MAXINT
+G_MINLONG
+G_MAXLONG
+</verb></tscreen>
+
+Also, the following typedefs. The ones left unspecified are dynamically set
+depending on the architecture. Remember to avoid counting on the size of a
+pointer if you want to be portable! Eg, a pointer on an Alpha is 8 bytes, but 4
+on Intel.
+
+<tscreen><verb>
+char gchar;
+short gshort;
+long glong;
+int gint;
+char gboolean;
+
+unsigned char guchar;
+unsigned short gushort;
+unsigned long gulong;
+unsigned int guint;
+
+float gfloat;
+double gdouble;
+long double gldouble;
+
+void* gpointer;
+
+gint8
+guint8
+gint16
+guint16
+gint32
+guint32
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Doubly Linked Lists
+<p>
+The following functions are used to create, manage, and destroy doubly
+linked lists. I assume you know what linked lists are, as it is beyond the scope
+of this document to explain them. Of course, it's not required that you
+know these for general use of GTK, but they are nice to know.
+
+<tscreen><verb>
+GList* g_list_alloc (void);
+
+void g_list_free (GList *list);
+
+void g_list_free_1 (GList *list);
+
+GList* g_list_append (GList *list,
+ gpointer data);
+
+GList* g_list_prepend (GList *list,
+ gpointer data);
+
+GList* g_list_insert (GList *list,
+ gpointer data,
+ gint position);
+
+GList* g_list_remove (GList *list,
+ gpointer data);
+
+GList* g_list_remove_link (GList *list,
+ GList *link);
+
+GList* g_list_reverse (GList *list);
+
+GList* g_list_nth (GList *list,
+ gint n);
+
+GList* g_list_find (GList *list,
+ gpointer data);
+
+GList* g_list_last (GList *list);
+
+GList* g_list_first (GList *list);
+
+gint g_list_length (GList *list);
+
+void g_list_foreach (GList *list,
+ GFunc func,
+ gpointer user_data);
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Singly Linked Lists
+<p>
+Many of the above functions for singly linked lists are identical to the
+above. Here is a complete list:
+<tscreen><verb>
+GSList* g_slist_alloc (void);
+
+void g_slist_free (GSList *list);
+
+void g_slist_free_1 (GSList *list);
+
+GSList* g_slist_append (GSList *list,
+ gpointer data);
+
+GSList* g_slist_prepend (GSList *list,
+ gpointer data);
+
+GSList* g_slist_insert (GSList *list,
+ gpointer data,
+ gint position);
+
+GSList* g_slist_remove (GSList *list,
+ gpointer data);
+
+GSList* g_slist_remove_link (GSList *list,
+ GSList *link);
+
+GSList* g_slist_reverse (GSList *list);
+
+GSList* g_slist_nth (GSList *list,
+ gint n);
+
+GSList* g_slist_find (GSList *list,
+ gpointer data);
+
+GSList* g_slist_last (GSList *list);
+
+gint g_slist_length (GSList *list);
+
+void g_slist_foreach (GSList *list,
+ GFunc func,
+ gpointer user_data);
+
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Memory Management
+<p>
+<tscreen><verb>
+gpointer g_malloc (gulong size);
+</verb></tscreen>
+
+This is a replacement for malloc(). You do not need to check the return
+vaule as it is done for you in this function.
+
+<tscreen><verb>
+gpointer g_malloc0 (gulong size);
+</verb></tscreen>
+
+Same as above, but zeroes the memory before returning a pointer to it.
+
+<tscreen><verb>
+gpointer g_realloc (gpointer mem,
+ gulong size);
+</verb></tscreen>
+
+Relocates "size" bytes of memory starting at "mem". Obviously, the memory should have been
+previously allocated.
+
+<tscreen><verb>
+void g_free (gpointer mem);
+</verb></tscreen>
+
+Frees memory. Easy one.
+
+<tscreen><verb>
+void g_mem_profile (void);
+</verb></tscreen>
+
+Dumps a profile of used memory, but requries that you add #define
+MEM_PROFILE to the top of glib/gmem.c and re-make and make install.
+
+<tscreen><verb>
+void g_mem_check (gpointer mem);
+</verb></tscreen>
+
+Checks that a memory location is valid. Requires you add #define
+MEM_CHECK to the top of gmem.c and re-make and make install.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Timers
+<p>
+Timer functions..
+
+<tscreen><verb>
+GTimer* g_timer_new (void);
+
+void g_timer_destroy (GTimer *timer);
+
+void g_timer_start (GTimer *timer);
+
+void g_timer_stop (GTimer *timer);
+
+void g_timer_reset (GTimer *timer);
+
+gdouble g_timer_elapsed (GTimer *timer,
+ gulong *microseconds);
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>String Handling
+<p>
+A whole mess of string handling functions. They all look very interesting, and
+probably better for many purposes than the standard C string functions, but
+require documentation.
+
+<tscreen><verb>
+GString* g_string_new (gchar *init);
+void g_string_free (GString *string,
+ gint free_segment);
+
+GString* g_string_assign (GString *lval,
+ gchar *rval);
+
+GString* g_string_truncate (GString *string,
+ gint len);
+
+GString* g_string_append (GString *string,
+ gchar *val);
+
+GString* g_string_append_c (GString *string,
+ gchar c);
+
+GString* g_string_prepend (GString *string,
+ gchar *val);
+
+GString* g_string_prepend_c (GString *string,
+ gchar c);
+
+void g_string_sprintf (GString *string,
+ gchar *fmt,
+ ...);
+
+void g_string_sprintfa (GString *string,
+ gchar *fmt,
+ ...);
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Utility and Error Functions
+<p>
+<tscreen><verb>
+gchar* g_strdup (const gchar *str);
+</verb></tscreen>
+
+Replacement strdup function. Copies the
+original strings contents to newly allocated memory, and returns a pointer to it.
+
+<tscreen><verb>
+gchar* g_strerror (gint errnum);
+</verb></tscreen>
+
+I recommend using this for all error messages. It's much nicer, and more
+portable than perror() or others. The output is usually of the form:
+
+<tscreen><verb>
+program name:function that failed:file or further description:strerror
+</verb></tscreen>
+
+Here's an example of one such call used in our hello_world program:
+
+<tscreen><verb>
+g_print("hello_world:open:%s:%s\n", filename, g_strerror(errno));
+</verb></tscreen>
+
+<tscreen><verb>
+void g_error (gchar *format, ...);
+</verb></tscreen>
+
+Prints an error message. The format is just like printf, but it
+prepends "** ERROR **: " to your message, and exits the program.
+Use only for fatal errors.
+
+<tscreen><verb>
+void g_warning (gchar *format, ...);
+</verb></tscreen>
+
+Same as above, but prepends "** WARNING **: ", and does not exit the
+program.
+
+<tscreen><verb>
+void g_message (gchar *format, ...);
+</verb></tscreen>
+
+Prints "message: " prepended to the string you pass in.
+
+<tscreen><verb>
+void g_print (gchar *format, ...);
+</verb></tscreen>
+
+Replacement for printf().
+
+And our last function:
+
+<tscreen><verb>
+gchar* g_strsignal (gint signum);
+</verb></tscreen>
+
+Prints out the name of the Unix system signal given the signal number.
+Useful in generic signal handling functions.
+
+All of the above are more or less just stolen from glib.h. If anyone cares
+to document any function, just send me an email!
+
+<!-- ***************************************************************** -->
+<sect>GTK's rc Files
+<!-- ***************************************************************** -->
+
+<p>
+GTK has it's own way of dealing with application defaults, by using rc
+files. These can be used to set the colors of just about any widget, and
+can also be used to tile pixmaps onto the background of some widgets.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Functions For rc Files
+<p>
+When your application starts, you should include a call to:
+<tscreen><verb>
+void gtk_rc_parse (char *filename);
+</verb></tscreen>
+<p>
+Passing in the filename of your rc file. This will cause GTK to parse this
+file, and use the style settings for the widget types defined there.
+<p>
+If you wish to have a special set of widgets that can take on a different
+style from others, or any other logical division of widgets, use a call to:
+<tscreen><verb>
+void gtk_widget_set_name (GtkWidget *widget,
+ gchar *name);
+</verb></tscreen>
+<p>
+Passing your newly created widget as the first argument, and the name
+you wish to give it as the second. This will allow you to change the
+attributes of this widget by name through the rc file.
+<p>
+If we use a call something like this:
+
+<tscreen><verb>
+button = gtk_button_new_with_label ("Special Button");
+gtk_widget_set_name (button, "special button");
+</verb></tscreen>
+<p>
+Then this button is given the name "special button" and may be addressed by
+name in the rc file as "special button.GtkButton". [<--- Verify ME!]
+<p>
+The example rc file below, sets the properties of the main window, and lets
+all children of that main window inherit the style described by the "main
+button" style. The code used in the application is:
+
+<tscreen><verb>
+window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+gtk_widget_set_name (window, "main window");
+</verb></tscreen>
+<p>
+And then the style is defined in the rc file using:
+
+<tscreen><verb>
+widget "main window.*GtkButton*" style "main_button"
+</verb></tscreen>
+<p>
+Which sets all the GtkButton widgets in the "main window" to the
+"main_buttons" style as defined in the rc file.
+<p>
+As you can see, this is a fairly powerful and flexible system. Use your
+imagination as to how best to take advantage of this.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>GTK's rc File Format
+<p>
+The format of the GTK file is illustrated in the example below. This is
+the testgtkrc file from the GTK distribution, but I've added a
+few comments and things. You may wish to include this explanation
+your application to allow the user to fine tune his application.
+<p>
+There are several directives to change the attributes of a widget.
+<itemize>
+<item>fg - Sets the foreground color of a widget.
+<item>bg - Sets the background color of a widget.
+<item>bg_pixmap - Sets the background of a widget to a tiled pixmap.
+<item>font - Sets the font to be used with the given widget.
+</itemize>
+<p>
+In addition to this, there are several states a widget can be in, and you
+can set different colors, pixmaps and fonts for each state. These states are:
+<itemize>
+<item>NORMAL - The normal state of a widget, without the mouse over top of
+it, and not being pressed etc.
+<item>PRELIGHT - When the mouse is over top of the widget, colors defined
+using this state will be in effect.
+<item>ACTIVE - When the widget is pressed or clicked it will be active, and
+the attributes assigned by this tag will be in effect.
+<item>INSENSITIVE - When a widget is set insensitive, and cannot be
+activated, it will take these attributes.
+<item>SELECTED - When an object is selected, it takes these attributes.
+</itemize>
+<p>
+When using the "fg" and "bg" keywords to set the colors of widgets, the
+format is:
+<tscreen><verb>
+fg[<STATE>] = { Red, Green, Blue }
+</verb></tscreen>
+<p>
+Where STATE is one of the above states (PRELIGHT, ACTIVE etc), and the Red,
+Green and Blue are values in the range of 0 - 1.0, { 1.0, 1.0, 1.0 } being
+white.
+They must be in float form, or they will register as 0, so a straight
+"1" will not work, it must
+be "1.0". A straight "0" is fine because it doesn't matter if it's not
+recognized. Unrecognized values are set to 0.
+<p>
+bg_pixmap is very similar to the above, except the colors are replaced by a
+filename.
+
+pixmap_path is a list of paths seperated by ":"'s. These paths will be
+searched for any pixmap you specify.
+
+<p>
+The font directive is simply:
+<tscreen><verb>
+font = "<font name>"
+</verb></tscreen>
+<p>
+Where the only hard part is figuring out the font string. Using xfontsel or
+similar utility should help.
+<p>
+The "widget_class" sets the style of a class of widgets. These classes are
+listed in the widget overview on the class hierarchy.
+<p>
+The "widget" directive sets a specificaly named set of widgets to a
+given style, overriding any style set for the given widget class.
+These widgets are registered inside the application using the
+gtk_widget_set_name() call. This allows you to specify the attributes of a
+widget on a per widget basis, rather than setting the attributes of an
+entire widget class. I urge you to document any of these special widgets so
+users may customize them.
+<p>
+When the keyword "<tt>parent</>" is used as an attribute, the widget will take on
+the attributes of it's parent in the application.
+<p>
+When defining a style, you may assign the attributes of a previously defined
+style to this new one.
+<tscreen><verb>
+style "main_button" = "button"
+{
+ font = "-adobe-helvetica-medium-r-normal--*-100-*-*-*-*-*-*"
+ bg[PRELIGHT] = { 0.75, 0, 0 }
+}
+</verb></tscreen>
+<p>
+This example takes the "button" style, and creates a new "main_button" style
+simply by changing the font and prelight background color of the "button"
+style.
+<p>
+Of course, many of these attributes don't apply to all widgets. It's a
+simple matter of common sense really. Anything that could apply, should.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Example rc file
+<p>
+
+<tscreen><verb>
+# pixmap_path "<dir 1>:<dir 2>:<dir 3>:..."
+#
+pixmap_path "/usr/include/X11R6/pixmaps:/home/imain/pixmaps"
+#
+# style <name> [= <name>]
+# {
+# <option>
+# }
+#
+# widget <widget_set> style <style_name>
+# widget_class <widget_class_set> style <style_name>
+
+
+# Here is a list of all the possible states. Note that some do not apply to
+# certain widgets.
+#
+# NORMAL - The normal state of a widget, without the mouse over top of
+# it, and not being pressed etc.
+#
+# PRELIGHT - When the mouse is over top of the widget, colors defined
+# using this state will be in effect.
+#
+# ACTIVE - When the widget is pressed or clicked it will be active, and
+# the attributes assigned by this tag will be in effect.
+#
+# INSENSITIVE - When a widget is set insensitive, and cannot be
+# activated, it will take these attributes.
+#
+# SELECTED - When an object is selected, it takes these attributes.
+#
+# Given these states, we can set the attributes of the widgets in each of
+# these states using the following directives.
+#
+# fg - Sets the foreground color of a widget.
+# fg - Sets the background color of a widget.
+# bg_pixmap - Sets the background of a widget to a tiled pixmap.
+# font - Sets the font to be used with the given widget.
+#
+
+# This sets a style called "button". The name is not really important, as
+# it is assigned to the actual widgets at the bottom of the file.
+
+style "window"
+{
+ #This sets the padding around the window to the pixmap specified.
+ #bg_pixmap[<STATE>] = "<pixmap filename>"
+ bg_pixmap[NORMAL] = "warning.xpm"
+}
+
+style "scale"
+{
+ #Sets the foreground color (font color) to red when in the "NORMAL"
+ #state.
+
+ fg[NORMAL] = { 1.0, 0, 0 }
+
+ #Sets the background pixmap of this widget to that of it's parent.
+ bg_pixmap[NORMAL] = "<parent>"
+}
+
+style "button"
+{
+ # This shows all the possible states for a button. The only one that
+ # doesn't apply is the SELECTED state.
+
+ fg[PRELIGHT] = { 0, 1.0, 1.0 }
+ bg[PRELIGHT] = { 0, 0, 1.0 }
+ bg[ACTIVE] = { 1.0, 0, 0 }
+ fg[ACTIVE] = { 0, 1.0, 0 }
+ bg[NORMAL] = { 1.0, 1.0, 0 }
+ fg[NORMAL] = { .99, 0, .99 }
+ bg[INSENSITIVE] = { 1.0, 1.0, 1.0 }
+ fg[INSENSITIVE] = { 1.0, 0, 1.0 }
+}
+
+# In this example, we inherit the attributes of the "button" style and then
+# override the font and background color when prelit to create a new
+# "main_button" style.
+
+style "main_button" = "button"
+{
+ font = "-adobe-helvetica-medium-r-normal--*-100-*-*-*-*-*-*"
+ bg[PRELIGHT] = { 0.75, 0, 0 }
+}
+
+style "toggle_button" = "button"
+{
+ fg[NORMAL] = { 1.0, 0, 0 }
+ fg[ACTIVE] = { 1.0, 0, 0 }
+
+ # This sets the background pixmap of the toggle_button to that of it's
+ # parent widget (as defined in the application).
+ bg_pixmap[NORMAL] = "<parent>"
+}
+
+style "text"
+{
+ bg_pixmap[NORMAL] = "marble.xpm"
+ fg[NORMAL] = { 1.0, 1.0, 1.0 }
+}
+
+style "ruler"
+{
+ font = "-adobe-helvetica-medium-r-normal--*-80-*-*-*-*-*-*"
+}
+
+# pixmap_path "~/.pixmaps"
+
+# These set the widget types to use the styles defined above.
+# The widget types are listed in the class hierarchy, but could probably be
+# just listed in this document for the users reference.
+
+widget_class "GtkWindow" style "window"
+widget_class "GtkDialog" style "window"
+widget_class "GtkFileSelection" style "window"
+widget_class "*Gtk*Scale" style "scale"
+widget_class "*GtkCheckButton*" style "toggle_button"
+widget_class "*GtkRadioButton*" style "toggle_button"
+widget_class "*GtkButton*" style "button"
+widget_class "*Ruler" style "ruler"
+widget_class "*GtkText" style "text"
+
+# This sets all the buttons that are children of the "main window" to
+# the main_buton style. These must be documented to be taken advantage of.
+widget "main window.*GtkButton*" style "main_button"
+</verb></tscreen>
+
+<!-- ***************************************************************** -->
+<sect>Writing Your Own Widgets
+<!-- ***************************************************************** -->
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Overview
+<p>
+Although the GTK distribution comes with many types of widgets that
+should cover most basic needs, there may come a time when you need to
+create your own new widget type. Since GTK uses widget inheretence
+extensively, and there is already a widget that
+is close to what you want, it is often possible to make a useful new widget type in
+just a few lines of code. But before starting work on a new widget, check
+around first to make sure that someone has not already written
+it. This will prevent duplication of effort and keep the number of
+GTK widgets out there to a minimum, which will help keep both the code
+and the interface of different applications consistent. As a flip side
+to this, once you finish your widget, announce it to the world so
+other people can benefit. The best place to do this is probably the
+<tt>gtk-list</tt>.
+
+Complete sources for the example widgets are available at the place you
+got this tutorial, or from:
+
+<htmlurl url="http://www.msc.cornell.edu/~otaylor/gtk-gimp/tutorial"
+name="http://www.msc.cornell.edu/~otaylor/gtk-gimp/tutorial">
+
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> The Anatomy Of A Widget
+
+<p>
+In order to create a new widget, it is important to have an
+understanding of how GTK objects work. This section is just meant as a
+brief overview. See the reference documentation for the details.
+
+<p>
+GTK widgets are implemented in an object oriented fashion. However,
+they are implemented in standard C. This greatly improves portability
+and stability over using current generation C++ compilers; however,
+it does mean that the widget writer has to pay attention to some of
+the implementation details. The information common to all instances of
+one class of widgets (e.g., to all Button widgets) is stored in the
+<em>class structure</em>. There is only one copy of this in
+which is stored information about the class's signals
+(which act like virtual functions in C). To support inheritance, the
+first field in the class structure must be a copy of the parent's
+class structure. The declaration of the class structure of GtkButtton
+looks like:
+
+<tscreen><verb>
+struct _GtkButtonClass
+{
+ GtkContainerClass parent_class;
+
+ void (* pressed) (GtkButton *button);
+ void (* released) (GtkButton *button);
+ void (* clicked) (GtkButton *button);
+ void (* enter) (GtkButton *button);
+ void (* leave) (GtkButton *button);
+};
+</verb></tscreen>
+
+<p>
+When a button is treated as a container (for instance, when it is
+resized), its class structure can be casted to GtkContainerClass, and
+the relevant fields used to handle the signals.
+
+<p>
+There is also a structure for each widget that is created on a
+per-instance basis. This structure has fields to store information that
+is different for each instance of the widget. We'll call this
+structure the <em>object structure</em>. For the Button class, it looks
+like:
+
+<tscreen><verb>
+struct _GtkButton
+{
+ GtkContainer container;
+
+ GtkWidget *child;
+
+ guint in_button : 1;
+ guint button_down : 1;
+};
+</verb></tscreen>
+
+<p>
+Note that, similar to the class structure, the first field is the
+object structure of the parent class, so that this structure can be
+casted to the parent class's object structure as needed.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Creating a Composite widget
+
+<!-- ----------------------------------------------------------------- -->
+<sect2> Introduction
+
+<p>
+One type of widget that you may be interested in creating is a
+widget that is merely an aggregate of other GTK widgets. This type of
+widget does nothing that couldn't be done without creating new
+widgets, but provides a convenient way of packaging user interface
+elements for reuse. The FileSelection and ColorSelection widgets in
+the standard distribution are examples of this type of widget.
+
+<p>
+The example widget that we'll create in this section is the Tictactoe
+widget, a 3x3 array of toggle buttons which triggers a signal when all
+three buttons in a row, column, or on one of the diagonals are
+depressed.
+
+<!-- ----------------------------------------------------------------- -->
+<sect2> Choosing a parent class
+
+<p>
+The parent class for a composite widget is typically the container
+class that holds all of the elements of the composite widget. For
+example, the parent class of the FileSelection widget is the
+Dialog class. Since our buttons will be arranged in a table, it
+might seem natural to make our parent class the GtkTable
+class. Unfortunately, this turns out not to work. The creation of a
+widget is divided among two functions - a <tt/WIDGETNAME_new()/
+function that the user calls, and a <tt/WIDGETNAME_init()/ function
+which does the basic work of initializing the widget which is
+independent of the arguments passed to the <tt/_new()/
+function. Descendent widgets only call the <tt/_init/ function of
+their parent widget. But this division of labor doesn't work well for
+tables, which when created, need to know the number of rows and
+columns in the table. Unless we want to duplicate most of the
+functionality of <tt/gtk_table_new()/ in our Tictactoe widget, we had
+best avoid deriving it from GtkTable. For that reason, we derive it
+from GtkVBox instead, and stick our table inside the VBox.
+
+<!-- ----------------------------------------------------------------- -->
+<sect2> The header file
+
+<p>
+Each widget class has a header file which declares the object and
+class structures for that widget, along with public functions.
+A couple of features are worth pointing out. To prevent duplicate
+definitions, we wrap the entire header file in:
+
+<tscreen><verb>
+#ifndef __TICTACTOE_H__
+#define __TICTACTOE_H__
+.
+.
+.
+#endif /* __TICTACTOE_H__ */
+</verb></tscreen>
+
+And to keep C++ programs that include the header file happy, in:
+
+<tscreen><verb>
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+.
+.
+.
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+</verb></tscreen>
+
+Along with the functions and structures, we declare three standard
+macros in our header file, <tt/TICTACTOE(obj)/,
+<tt/TICTACTOE_CLASS(klass)/, and <tt/IS_TICTACTOE(obj)/, which cast a
+pointer into a pointer to the the object or class structure, and check
+if an object is a Tictactoe widget respectively.
+
+<p>
+Here is the complete header file:
+
+<tscreen><verb>
+/* tictactoe.h */
+
+#ifndef __TICTACTOE_H__
+#define __TICTACTOE_H__
+
+#include <gdk/gdk.h>
+#include <gtk/gtkvbox.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#define TICTACTOE(obj) GTK_CHECK_CAST (obj, tictactoe_get_type (), Tictactoe)
+#define TICTACTOE_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, tictactoe_get_type (), TictactoeClass)
+#define IS_TICTACTOE(obj) GTK_CHECK_TYPE (obj, tictactoe_get_type ())
+
+
+typedef struct _Tictactoe Tictactoe;
+typedef struct _TictactoeClass TictactoeClass;
+
+struct _Tictactoe
+{
+ GtkVBox vbox;
+
+ GtkWidget *buttons[3][3];
+};
+
+struct _TictactoeClass
+{
+ GtkVBoxClass parent_class;
+
+ void (* tictactoe) (Tictactoe *ttt);
+};
+
+guint tictactoe_get_type (void);
+GtkWidget* tictactoe_new (void);
+void tictactoe_clear (Tictactoe *ttt);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __TICTACTOE_H__ */
+
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect2> The <tt/_get_type()/ function.
+
+<p>
+We now continue on to the implementation of our widget. A core
+function for every widget is the function
+<tt/WIDGETNAME_get_type()/. This function, when first called, tells
+GTK about the widget class, and gets an ID that uniquely identifies
+the widget class. Upon subsequent calls, it just returns the ID.
+
+<tscreen><verb>
+guint
+tictactoe_get_type ()
+{
+ static guint ttt_type = 0;
+
+ if (!ttt_type)
+ {
+ GtkTypeInfo ttt_info =
+ {
+ "Tictactoe",
+ sizeof (Tictactoe),
+ sizeof (TictactoeClass),
+ (GtkClassInitFunc) tictactoe_class_init,
+ (GtkObjectInitFunc) tictactoe_init,
+ (GtkArgFunc) NULL,
+ };
+
+ ttt_type = gtk_type_unique (gtk_vbox_get_type (), &amp;ttt_info);
+ }
+
+ return ttt_type;
+}
+</verb></tscreen>
+
+<p>
+The GtkTypeInfo structure has the following definition:
+
+<tscreen><verb>
+struct _GtkTypeInfo
+{
+ gchar *type_name;
+ guint object_size;
+ guint class_size;
+ GtkClassInitFunc class_init_func;
+ GtkObjectInitFunc object_init_func;
+ GtkArgFunc arg_func;
+};
+</verb></tscreen>
+
+<p>
+The fields of this structure are pretty self-explanatory. We'll ignore
+the <tt/arg_func/ field here: It has an important, but as yet largely
+unimplemented, role in allowing widget options to be conveniently set
+from interpreted languages. Once GTK has a correctly filled in copy of
+this structure, it knows how to create objects of a particular widget
+type.
+
+<!-- ----------------------------------------------------------------- -->
+<sect2> The <tt/_class_init()/ function
+
+<p>
+The <tt/WIDGETNAME_class_init()/ function initializes the fields of
+the widget's class structure, and sets up any signals for the
+class. For our Tictactoe widget it looks like:
+
+<tscreen><verb>
+
+enum {
+ TICTACTOE_SIGNAL,
+ LAST_SIGNAL
+};
+
+static gint tictactoe_signals[LAST_SIGNAL] = { 0 };
+
+static void
+tictactoe_class_init (TictactoeClass *class)
+{
+ GtkObjectClass *object_class;
+
+ object_class = (GtkObjectClass*) class;
+
+ tictactoe_signals[TICTACTOE_SIGNAL] = gtk_signal_new ("tictactoe",
+ GTK_RUN_FIRST,
+ object_class->type,
+ GTK_SIGNAL_OFFSET (TictactoeClass, tictactoe),
+ gtk_signal_default_marshaller, GTK_ARG_NONE, 0);
+
+
+ gtk_object_class_add_signals (object_class, tictactoe_signals, LAST_SIGNAL);
+
+ class->tictactoe = NULL;
+}
+</verb></tscreen>
+
+<p>
+Our widget has just one signal, the ``tictactoe'' signal that is
+invoked when a row, column, or diagonal is completely filled in. Not
+every composite widget needs signals, so if you are reading this for
+the first time, you may want to skip to the next section now, as
+things are going to get a bit complicated.
+
+The function:
+
+<tscreen><verb>
+gint gtk_signal_new (gchar *name,
+ GtkSignalRunType run_type,
+ gint object_type,
+ gint function_offset,
+ GtkSignalMarshaller marshaller,
+ GtkArgType return_val,
+ gint nparams,
+ ...);
+</verb></tscreen>
+
+Creates a new signal. The parameters are:
+
+<itemize>
+<item> <tt/name/: The name of the signal.
+<item> <tt/run_type/: Whether the default handler runs before or after
+user handlers. Usually this will be <tt/GTK_RUN_FIRST/, or <tt/GTK_RUN_LAST/,
+although there are other possibilities.
+<item> <tt/object_type/: The ID of the object that this signal applies
+to. (It will also apply to that objects descendents)
+<item> <tt/function_offset/: The offset within the class structure of
+a pointer to the default handler.
+<item> <tt/marshaller/: A function that is used to invoke the signal
+handler. For signal handlers that have no arguments other than the
+object that emitted the signal and user data, we can use the
+presupplied marshaller function <tt/gtk_signal_default_marshaller/.
+<item> <tt/return_val/: The type of the return val.
+<item> <tt/nparams/: The number of parameters of the signal handler
+(other than the two default ones mentioned above)
+<item> <tt/.../: The types of the parameters.
+</itemize>
+
+When specifying types, the <tt/GtkArgType/ enumeration is used:
+
+<tscreen><verb>
+typedef enum
+{
+ GTK_ARG_INVALID,
+ GTK_ARG_NONE,
+ GTK_ARG_CHAR,
+ GTK_ARG_SHORT,
+ GTK_ARG_INT,
+ GTK_ARG_LONG,
+ GTK_ARG_POINTER,
+ GTK_ARG_OBJECT,
+ GTK_ARG_FUNCTION,
+ GTK_ARG_SIGNAL
+} GtkArgType;
+</verb></tscreen>
+
+<p>
+<tt/gtk_signal_new()/ returns a unique integer identifier for the
+signal, that we store in the <tt/tictactoe_signals/ array, which we
+index using an enumeration. (Conventionally, the enumeration elements
+are the signal name, uppercased, but here there would be a conflict
+with the <tt/TICTACTOE()/ macro, so we called it <tt/TICTACTOE_SIGNAL/
+instead.
+
+After creating our signals, we need to tell GTK to associate our
+signals with the Tictactoe class. We do that by calling
+<tt/gtk_object_class_add_signals()/. We then set the pointer which
+points to the default handler for the ``tictactoe'' signal to NULL,
+indicating that there is no default action.
+
+<!-- ----------------------------------------------------------------- -->
+<sect2> The <tt/_init()/ function.
+
+<p>
+
+Each widget class also needs a function to initialize the object
+structure. Usually, this function has the fairly limited role of
+setting the fields of the structure to default values. For composite
+widgets, however, this function also creates the component widgets.
+
+<tscreen><verb>
+
+static void
+tictactoe_init (Tictactoe *ttt)
+{
+ GtkWidget *table;
+ gint i,j;
+
+ table = gtk_table_new (3, 3, TRUE);
+ gtk_container_add (GTK_CONTAINER(ttt), table);
+ gtk_widget_show (table);
+
+ for (i=0;i<3; i++)
+ for (j=0;j<3; j++)
+ {
+ ttt->buttons[i][j] = gtk_toggle_button_new ();
+ gtk_table_attach_defaults (GTK_TABLE(table), ttt->buttons[i][j],
+ i, i+1, j, j+1);
+ gtk_signal_connect (GTK_OBJECT (ttt->buttons[i][j]), "toggled",
+ GTK_SIGNAL_FUNC (tictactoe_toggle), ttt);
+ gtk_widget_set_usize (ttt->buttons[i][j], 20, 20);
+ gtk_widget_show (ttt->buttons[i][j]);
+ }
+}
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect2> And the rest...
+
+<p>
+
+There is one more function that every widget (except for base widget
+types like GtkBin that cannot be instantiated) needs to have - the
+function that the user calls to create an object of that type. This is
+conventionally called <tt/WIDGETNAME_new()/In some
+widgets, thought not for the Tictactoe widgets, this function takes
+arguments, and does some setup based on the arguments. The other two
+functions are specific to the Tictactoe widget.
+
+<p>
+<tt/tictactoe_clear()/ is a public function that resets all the
+buttons in the widget to the up position. Note the use of
+<tt/gtk_signal_handler_block_by_data()/ to keep our signal handler for
+button toggles from being triggered unnecessarily.
+
+<p>
+<tt/tictactoe_toggle()/ is the signal handler that is invoked when the
+user clicks on a button. It checks to see if there are any winning
+combinations that involve the toggled button, and if so, emits
+the "tictactoe" signal.
+
+<tscreen><verb>
+GtkWidget*
+tictactoe_new ()
+{
+ return GTK_WIDGET ( gtk_type_new (tictactoe_get_type ()));
+}
+
+void
+tictactoe_clear (Tictactoe *ttt)
+{
+ int i,j;
+
+ for (i=0;i<3;i++)
+ for (j=0;j<3;j++)
+ {
+ gtk_signal_handler_block_by_data (GTK_OBJECT(ttt->buttons[i][j]), ttt);
+ gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (ttt->buttons[i][j]),
+ FALSE);
+ gtk_signal_handler_unblock_by_data (GTK_OBJECT(ttt->buttons[i][j]), ttt);
+ }
+}
+
+static void
+tictactoe_toggle (GtkWidget *widget, Tictactoe *ttt)
+{
+ int i,k;
+
+ static int rwins[8][3] = { { 0, 0, 0 }, { 1, 1, 1 }, { 2, 2, 2 },
+ { 0, 1, 2 }, { 0, 1, 2 }, { 0, 1, 2 },
+ { 0, 1, 2 }, { 0, 1, 2 } };
+ static int cwins[8][3] = { { 0, 1, 2 }, { 0, 1, 2 }, { 0, 1, 2 },
+ { 0, 0, 0 }, { 1, 1, 1 }, { 2, 2, 2 },
+ { 0, 1, 2 }, { 2, 1, 0 } };
+
+ int success, found;
+
+ for (k=0; k<8; k++)
+ {
+ success = TRUE;
+ found = FALSE;
+
+ for (i=0;i<3;i++)
+ {
+ success = success &amp;&amp;
+ 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 &amp;&amp; found)
+ {
+ gtk_signal_emit (GTK_OBJECT (ttt),
+ tictactoe_signals[TICTACTOE_SIGNAL]);
+ break;
+ }
+ }
+}
+</verb></tscreen>
+
+<p>
+
+And finally, an example program using our Tictactoe widget:
+
+<tscreen><verb>
+#include <gtk/gtk.h>
+#include "tictactoe.h"
+
+/* Invoked when a row, column or diagonal is completed */
+void
+win (GtkWidget *widget, gpointer data)
+{
+ g_print ("Yay!\n");
+ tictactoe_clear (TICTACTOE (widget));
+}
+
+int
+main (int argc, char *argv[])
+{
+ GtkWidget *window;
+ GtkWidget *ttt;
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+ gtk_window_set_title (GTK_WINDOW (window), "Aspect Frame");
+
+ gtk_signal_connect (GTK_OBJECT (window), "destroy",
+ GTK_SIGNAL_FUNC (gtk_exit), NULL);
+
+ gtk_container_border_width (GTK_CONTAINER (window), 10);
+
+ /* Create a new Tictactoe widget */
+ ttt = tictactoe_new ();
+ gtk_container_add (GTK_CONTAINER (window), ttt);
+ gtk_widget_show (ttt);
+
+ /* And attach to its "tictactoe" signal */
+ gtk_signal_connect (GTK_OBJECT (ttt), "tictactoe",
+ GTK_SIGNAL_FUNC (win), NULL);
+
+ gtk_widget_show (window);
+
+ gtk_main ();
+
+ return 0;
+}
+
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Creating a widget from scratch.
+
+<!-- ----------------------------------------------------------------- -->
+<sect2> Introduction
+
+<p>
+
+In this section, we'll learn more about how widgets display themselves
+on the screen and interact with events. As an example of this, we'll
+create a analog dial widget with a pointer that the user can drag to
+set the value.
+
+<!-- ----------------------------------------------------------------- -->
+<sect2> Displaying a widget on the screen
+
+<p>
+There are several steps that are involved in displaying on the screen.
+After the widget is created with a call to <tt/WIDGETNAME_new()/,
+several more functions are needed:
+
+<itemize>
+<item> <tt/WIDGETNAME_realize()/ is responsible for creating an X
+window for the widget if it has one.
+<item> <tt/WIDGETNAME_map()/ is invoked after the user calls
+<tt/gtk_widget_show()/. It is responsible for making sure the widget
+is actually drawn on the screen (<em/mapped/). For a container class,
+it must also make calls to <tt/map()/> functions of any child widgets.
+<item> <tt/WIDGETNAME_draw()/ is invoked when <tt/gtk_widget_draw()/
+is called for the widget or one of its ancestors. It makes the actual
+calls to the drawing functions to draw the widget on the screen. For
+container widgets, this function must make calls to
+<tt/gtk_widget_draw()/ for its child widgets.
+<item> <tt/WIDGETNAME_expose()/ is a handler for expose events for the
+widget. It makes the necessary calls to the drawing functions to draw
+the exposed portion on the screen. For container widgets, this
+function must generate expose events for its child widgets which don't
+have their own windows. (If they have their own windows, then X will
+generate the necessary expose events)
+</itemize>
+
+<p>
+You might notice that the last two functions are quite similar - each
+is responsible for drawing the widget on the screen. In fact many
+types of widgets don't really care about the difference between the
+two. The default <tt/draw()/ function in the widget class simply
+generates a synthetic expose event for the redrawn area. However, some
+types of widgets can save work by distinguishing between the two
+functions. For instance, if a widget has multiple X windows, then
+since expose events identify the exposed window, it can redraw only
+the affected window, which is not possible for calls to <tt/draw()/.
+
+<p>
+Container widgets, even if they don't care about the difference for
+themselves, can't simply use the default <tt/draw()/ function because
+their child widgets might care about the difference. However,
+it would be wasteful to duplicate the drawing code between the two
+functions. The convention is that such widgets have a function called
+<tt/WIDGETNAME_paint()/ that does the actual work of drawing the
+widget, that is then called by the <tt/draw()/ and <tt/expose()/
+functions.
+
+<p>
+In our example approach, since the dial widget is not a container
+widget, and only has a single window, we can take the simplest
+approach and use the default <tt/draw()/ function and only implement
+an <tt/expose()/ function.
+
+<!-- ----------------------------------------------------------------- -->
+<sect2> The origins of the Dial Widget
+
+<p>
+Just as all land animals are just variants on the first amphibian that
+crawled up out of the mud, Gtk widgets tend to start off as variants
+of some other, previously written widget. Thus, although this section
+is entilted ``Creating a Widget from Scratch'', the Dial widget really
+began with the source code for the Range widget. This was picked as a
+starting point because it would be nice if our Dial had the same
+interface as the Scale widgets which are just specialized descendents
+of the Range widget. So, though the source code is presented below in
+finished form, it should not be implied that it was written, <em>deus
+ex machina</em> in this fashion. Also, if you aren't yet familiar with
+how scale widgets work from the application writer's point of view, it
+would be a good idea to look them over before continuing.
+
+<!-- ----------------------------------------------------------------- -->
+<sect2> The Basics
+
+<p>
+Quite a bit of our widget should look pretty familiar from the
+Tictactoe widget. First, we have a header file:
+
+<tscreen><verb>
+/* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __GTK_DIAL_H__
+#define __GTK_DIAL_H__
+
+#include <gdk/gdk.h>
+#include <gtk/gtkadjustment.h>
+#include <gtk/gtkwidget.h>
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+#define GTK_DIAL(obj) GTK_CHECK_CAST (obj, gtk_dial_get_type (), GtkDial)
+#define GTK_DIAL_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, gtk_dial_get_type (), GtkDialClass)
+#define GTK_IS_DIAL(obj) GTK_CHECK_TYPE (obj, gtk_dial_get_type ())
+
+
+typedef struct _GtkDial GtkDial;
+typedef struct _GtkDialClass GtkDialClass;
+
+struct _GtkDial
+{
+ GtkWidget widget;
+
+ /* update policy (GTK_UPDATE_[CONTINUOUS/DELAYED/DISCONTINUOUS]) */
+ guint policy : 2;
+
+ /* Button currently pressed or 0 if none */
+ guint8 button;
+
+ /* Dimensions of dial components */
+ gint radius;
+ gint pointer_width;
+
+ /* ID of update timer, or 0 if none */
+ guint32 timer;
+
+ /* Current angle */
+ gfloat angle;
+
+ /* Old values from adjustment stored so we know when something changes */
+ gfloat old_value;
+ gfloat old_lower;
+ gfloat old_upper;
+
+ /* The adjustment object that stores the data for this dial */
+ GtkAdjustment *adjustment;
+};
+
+struct _GtkDialClass
+{
+ GtkWidgetClass parent_class;
+};
+
+
+GtkWidget* gtk_dial_new (GtkAdjustment *adjustment);
+guint gtk_dial_get_type (void);
+GtkAdjustment* gtk_dial_get_adjustment (GtkDial *dial);
+void gtk_dial_set_update_policy (GtkDial *dial,
+ GtkUpdateType policy);
+
+void gtk_dial_set_adjustment (GtkDial *dial,
+ GtkAdjustment *adjustment);
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif /* __GTK_DIAL_H__ */
+
+</verb></tscreen>
+
+Since there is quite a bit more going on in this widget, than the last
+one, we have more fields in the data structure, but otherwise things
+are pretty similar.
+
+<p>
+
+Next, after including header files, and declaring a few constants,
+we have some functions to provide information about the widget
+and initialize it:
+
+<tscreen><verb>
+#include <math.h>
+#include <stdio.h>
+#include <gtk/gtkmain.h>
+#include <gtk/gtksignal.h>
+
+#include "gtkdial.h"
+
+#define SCROLL_DELAY_LENGTH 300
+#define DIAL_DEFAULT_SIZE 100
+
+/* Forward declararations */
+
+[ omitted to save space ]
+
+/* Local data */
+
+static GtkWidgetClass *parent_class = NULL;
+
+guint
+gtk_dial_get_type ()
+{
+ static guint dial_type = 0;
+
+ if (!dial_type)
+ {
+ GtkTypeInfo dial_info =
+ {
+ "GtkDial",
+ sizeof (GtkDial),
+ sizeof (GtkDialClass),
+ (GtkClassInitFunc) gtk_dial_class_init,
+ (GtkObjectInitFunc) gtk_dial_init,
+ (GtkArgFunc) NULL,
+ };
+
+ dial_type = gtk_type_unique (gtk_widget_get_type (), &amp;dial_info);
+ }
+
+ return dial_type;
+}
+
+static void
+gtk_dial_class_init (GtkDialClass *class)
+{
+ GtkObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+
+ object_class = (GtkObjectClass*) class;
+ widget_class = (GtkWidgetClass*) class;
+
+ parent_class = gtk_type_class (gtk_widget_get_type ());
+
+ object_class->destroy = gtk_dial_destroy;
+
+ widget_class->realize = gtk_dial_realize;
+ widget_class->expose_event = gtk_dial_expose;
+ widget_class->size_request = gtk_dial_size_request;
+ widget_class->size_allocate = gtk_dial_size_allocate;
+ widget_class->button_press_event = gtk_dial_button_press;
+ widget_class->button_release_event = gtk_dial_button_release;
+ widget_class->motion_notify_event = gtk_dial_motion_notify;
+}
+
+static void
+gtk_dial_init (GtkDial *dial)
+{
+ dial->button = 0;
+ dial->policy = GTK_UPDATE_CONTINUOUS;
+ dial->timer = 0;
+ dial->radius = 0;
+ dial->pointer_width = 0;
+ dial->angle = 0.0;
+ dial->old_value = 0.0;
+ dial->old_lower = 0.0;
+ dial->old_upper = 0.0;
+ dial->adjustment = NULL;
+}
+
+GtkWidget*
+gtk_dial_new (GtkAdjustment *adjustment)
+{
+ GtkDial *dial;
+
+ dial = gtk_type_new (gtk_dial_get_type ());
+
+ if (!adjustment)
+ adjustment = (GtkAdjustment*) gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
+
+ gtk_dial_set_adjustment (dial, adjustment);
+
+ return GTK_WIDGET (dial);
+}
+
+static void
+gtk_dial_destroy (GtkObject *object)
+{
+ GtkDial *dial;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GTK_IS_DIAL (object));
+
+ dial = GTK_DIAL (object);
+
+ if (dial->adjustment)
+ gtk_object_unref (GTK_OBJECT (dial->adjustment));
+
+ if (GTK_OBJECT_CLASS (parent_class)->destroy)
+ (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+</verb></tscreen>
+
+Note that this <tt/init()/ function does less than for the Tictactoe
+widget, since this is not a composite widget, and the <tt/new()/
+function does more, since it now has an argument. Also, note that when
+we store a pointer to the Adjustment object, we increment its
+reference count, (and correspondingly decrement when we no longer use
+it) so that GTK can keep track of when it can be safely destroyed.
+
+<p>
+Also, there are a few function to manipulate the widget's options:
+
+<tscreen><verb>
+GtkAdjustment*
+gtk_dial_get_adjustment (GtkDial *dial)
+{
+ g_return_val_if_fail (dial != NULL, NULL);
+ g_return_val_if_fail (GTK_IS_DIAL (dial), NULL);
+
+ return dial->adjustment;
+}
+
+void
+gtk_dial_set_update_policy (GtkDial *dial,
+ GtkUpdateType policy)
+{
+ g_return_if_fail (dial != NULL);
+ g_return_if_fail (GTK_IS_DIAL (dial));
+
+ dial->policy = policy;
+}
+
+void
+gtk_dial_set_adjustment (GtkDial *dial,
+ GtkAdjustment *adjustment)
+{
+ g_return_if_fail (dial != NULL);
+ g_return_if_fail (GTK_IS_DIAL (dial));
+
+ if (dial->adjustment)
+ {
+ gtk_signal_disconnect_by_data (GTK_OBJECT (dial->adjustment), (gpointer) dial);
+ gtk_object_unref (GTK_OBJECT (dial->adjustment));
+ }
+
+ dial->adjustment = adjustment;
+ gtk_object_ref (GTK_OBJECT (dial->adjustment));
+
+ gtk_signal_connect (GTK_OBJECT (adjustment), "changed",
+ (GtkSignalFunc) gtk_dial_adjustment_changed,
+ (gpointer) dial);
+ gtk_signal_connect (GTK_OBJECT (adjustment), "value_changed",
+ (GtkSignalFunc) gtk_dial_adjustment_value_changed,
+ (gpointer) dial);
+
+ dial->old_value = adjustment->value;
+ dial->old_lower = adjustment->lower;
+ dial->old_upper = adjustment->upper;
+
+ gtk_dial_update (dial);
+}
+</verb></tscreen>
+
+<sect2> <tt/gtk_dial_realize()/
+
+<p>
+Now we come to some new types of functions. First, we have a function
+that does the work of creating the X window. Notice that a mask is
+passed to the function <tt/gdk_window_new()/ which specifies which fields of
+the GdkWindowAttr structure actually have data in them (the remaining
+fields wll be given default values). Also worth noting is the way the
+event mask of the widget is created. We call
+<tt/gtk_widget_get_events()/ to retrieve the event mask that the user
+has specified for this widget (with <tt/gtk_widget_set_events()/, and
+add the events that we are interested in ourselves.
+
+<p>
+After creating the window, we set its style and background, and put a
+pointer to the widget in the user data field of the GdkWindow. This
+last step allows GTK to dispatch events for this window to the correct
+widget.
+
+<tscreen><verb>
+static void
+gtk_dial_realize (GtkWidget *widget)
+{
+ GtkDial *dial;
+ GdkWindowAttr attributes;
+ gint attributes_mask;
+
+ g_return_if_fail (widget != NULL);
+ g_return_if_fail (GTK_IS_DIAL (widget));
+
+ GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
+ dial = GTK_DIAL (widget);
+
+ attributes.x = widget->allocation.x;
+ attributes.y = widget->allocation.y;
+ attributes.width = widget->allocation.width;
+ attributes.height = widget->allocation.height;
+ attributes.wclass = GDK_INPUT_OUTPUT;
+ attributes.window_type = GDK_WINDOW_CHILD;
+ attributes.event_mask = gtk_widget_get_events (widget) |
+ GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK |
+ GDK_POINTER_MOTION_HINT_MASK;
+ attributes.visual = gtk_widget_get_visual (widget);
+ attributes.colormap = gtk_widget_get_colormap (widget);
+
+ attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
+ widget->window = gdk_window_new (widget->parent->window, &amp;attributes, attributes_mask);
+
+ widget->style = gtk_style_attach (widget->style, widget->window);
+
+ gdk_window_set_user_data (widget->window, widget);
+
+ gtk_style_set_background (widget->style, widget->window, GTK_STATE_ACTIVE);
+}
+</verb></tscreen>
+
+<sect2> Size negotiation
+
+<p>
+Before the first time that the window containing a widget is
+displayed, and whenever the layout of the window changes, GTK asks
+each child widget for its desired size. This request is handled by the
+function, <tt/gtk_dial_size_request()/. Since our widget isn't a
+container widget, and has no real constraints on its size, we just
+return a reasonable default value.
+
+<tscreen><verb>
+static void
+gtk_dial_size_request (GtkWidget *widget,
+ GtkRequisition *requisition)
+{
+ requisition->width = DIAL_DEFAULT_SIZE;
+ requisition->height = DIAL_DEFAULT_SIZE;
+}
+</verb></tscreen>
+
+<p>
+After all the widgets have requested an ideal size, the layout of the
+window is computed and each child widget is notified of its actual
+size. Usually, this will at least as large as the requested size, but
+if for instance, the user has resized the window, it may occasionally
+be smaller than the requested size. The size notification is handled
+by the function <tt/gtk_dial_size_allocate()/. Notice that as well as
+computing the sizes of some component pieces for future use, this
+routine also does the grunt work of moving the widgets X window into
+the new position and size.
+
+<tscreen><verb>
+static void
+gtk_dial_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation)
+{
+ GtkDial *dial;
+
+ g_return_if_fail (widget != NULL);
+ g_return_if_fail (GTK_IS_DIAL (widget));
+ g_return_if_fail (allocation != NULL);
+
+ widget->allocation = *allocation;
+ if (GTK_WIDGET_REALIZED (widget))
+ {
+ dial = GTK_DIAL (widget);
+
+ gdk_window_move_resize (widget->window,
+ allocation->x, allocation->y,
+ allocation->width, allocation->height);
+
+ dial->radius = MAX(allocation->width,allocation->height) * 0.45;
+ dial->pointer_width = dial->radius / 5;
+ }
+}
+</verb></tscreen>.
+
+<!-- ----------------------------------------------------------------- -->
+<sect2> <tt/gtk_dial_expose()/
+
+<p>
+As mentioned above, all the drawing of this widget is done in the
+handler for expose events. There's not much to remark on here except
+the use of the function <tt/gtk_draw_polygon/ to draw the pointer with
+three dimensional shading according to the colors stored in the
+widget's style.
+
+<tscreen><verb>
+static gint
+gtk_dial_expose (GtkWidget *widget,
+ GdkEventExpose *event)
+{
+ GtkDial *dial;
+ GdkPoint points[3];
+ gdouble s,c;
+ gdouble theta;
+ gint xc, yc;
+ gint tick_length;
+ gint i;
+
+ g_return_val_if_fail (widget != NULL, FALSE);
+ g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ if (event->count > 0)
+ return FALSE;
+
+ dial = GTK_DIAL (widget);
+
+ gdk_window_clear_area (widget->window,
+ 0, 0,
+ widget->allocation.width,
+ widget->allocation.height);
+
+ xc = widget->allocation.width/2;
+ yc = widget->allocation.height/2;
+
+ /* Draw ticks */
+
+ for (i=0; i<25; i++)
+ {
+ theta = (i*M_PI/18. - M_PI/6.);
+ s = sin(theta);
+ c = cos(theta);
+
+ tick_length = (i%6 == 0) ? dial->pointer_width : dial->pointer_width/2;
+
+ gdk_draw_line (widget->window,
+ widget->style->fg_gc[widget->state],
+ xc + c*(dial->radius - tick_length),
+ yc - s*(dial->radius - tick_length),
+ xc + c*dial->radius,
+ yc - s*dial->radius);
+ }
+
+ /* Draw pointer */
+
+ s = sin(dial->angle);
+ c = cos(dial->angle);
+
+
+ points[0].x = xc + s*dial->pointer_width/2;
+ points[0].y = yc + c*dial->pointer_width/2;
+ points[1].x = xc + c*dial->radius;
+ points[1].y = yc - s*dial->radius;
+ points[2].x = xc - s*dial->pointer_width/2;
+ points[2].y = yc - c*dial->pointer_width/2;
+
+ gtk_draw_polygon (widget->style,
+ widget->window,
+ GTK_STATE_NORMAL,
+ GTK_SHADOW_OUT,
+ points, 3,
+ TRUE);
+
+ return FALSE;
+}
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect2> Event handling
+
+<p>
+
+The rest of the widget's code handles various types of events, and
+isn't too different from what would be found in many GTK
+applications. Two types of events can occur - either the user can
+click on the widget with the mouse and drag to move the pointer, or
+the value of the Adjustment object can change due to some external
+circumstance.
+
+<p>
+When the user clicks on the widget, we check to see if the click was
+appropriately near the pointer, and if so, store then button that the
+user clicked with in the <tt/button/ field of the widget
+structure, and grab all mouse events with a call to
+<tt/gtk_grab_add()/. Subsequent motion of the mouse causes the
+value of the control to be recomputed (by the function
+<tt/gtk_dial_update_mouse/). Depending on the policy that has been
+set, "value_changed" events are either generated instantly
+(<tt/GTK_UPDATE_CONTINUOUS/), after a delay in a timer added with
+<tt/gtk_timeout_add()/ (<tt/GTK_UPDATE_DELAYED/), or only when the
+button is released (<tt/GTK_UPDATE_DISCONTINUOUS/).
+
+<tscreen><verb>
+static gint
+gtk_dial_button_press (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ GtkDial *dial;
+ gint dx, dy;
+ double s, c;
+ double d_parallel;
+ double d_perpendicular;
+
+ g_return_val_if_fail (widget != NULL, FALSE);
+ g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ dial = GTK_DIAL (widget);
+
+ /* Determine if button press was within pointer region - we
+ do this by computing the parallel and perpendicular distance of
+ the point where the mouse was pressed from the line passing through
+ the pointer */
+
+ dx = event->x - widget->allocation.width / 2;
+ dy = widget->allocation.height / 2 - event->y;
+
+ s = sin(dial->angle);
+ c = cos(dial->angle);
+
+ d_parallel = s*dy + c*dx;
+ d_perpendicular = fabs(s*dx - c*dy);
+
+ if (!dial->button &&
+ (d_perpendicular < dial->pointer_width/2) &&
+ (d_parallel > - dial->pointer_width))
+ {
+ gtk_grab_add (widget);
+
+ dial->button = event->button;
+
+ gtk_dial_update_mouse (dial, event->x, event->y);
+ }
+
+ return FALSE;
+}
+
+static gint
+gtk_dial_button_release (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ GtkDial *dial;
+
+ g_return_val_if_fail (widget != NULL, FALSE);
+ g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ dial = GTK_DIAL (widget);
+
+ if (dial->button == event->button)
+ {
+ gtk_grab_remove (widget);
+
+ dial->button = 0;
+
+ if (dial->policy == GTK_UPDATE_DELAYED)
+ gtk_timeout_remove (dial->timer);
+
+ if ((dial->policy != GTK_UPDATE_CONTINUOUS) &&
+ (dial->old_value != dial->adjustment->value))
+ gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
+ }
+
+ return FALSE;
+}
+
+static gint
+gtk_dial_motion_notify (GtkWidget *widget,
+ GdkEventMotion *event)
+{
+ GtkDial *dial;
+ GdkModifierType mods;
+ gint x, y, mask;
+
+ g_return_val_if_fail (widget != NULL, FALSE);
+ g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ dial = GTK_DIAL (widget);
+
+ if (dial->button != 0)
+ {
+ x = event->x;
+ y = event->y;
+
+ if (event->is_hint || (event->window != widget->window))
+ gdk_window_get_pointer (widget->window, &amp;x, &amp;y, &amp;mods);
+
+ switch (dial->button)
+ {
+ case 1:
+ mask = GDK_BUTTON1_MASK;
+ break;
+ case 2:
+ mask = GDK_BUTTON2_MASK;
+ break;
+ case 3:
+ mask = GDK_BUTTON3_MASK;
+ break;
+ default:
+ mask = 0;
+ break;
+ }
+
+ if (mods & mask)
+ gtk_dial_update_mouse (dial, x,y);
+ }
+
+ return FALSE;
+}
+
+static gint
+gtk_dial_timer (GtkDial *dial)
+{
+ g_return_val_if_fail (dial != NULL, FALSE);
+ g_return_val_if_fail (GTK_IS_DIAL (dial), FALSE);
+
+ if (dial->policy == GTK_UPDATE_DELAYED)
+ gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
+
+ return FALSE;
+}
+
+static void
+gtk_dial_update_mouse (GtkDial *dial, gint x, gint y)
+{
+ gint xc, yc;
+ gfloat old_value;
+
+ g_return_if_fail (dial != NULL);
+ g_return_if_fail (GTK_IS_DIAL (dial));
+
+ xc = GTK_WIDGET(dial)->allocation.width / 2;
+ yc = GTK_WIDGET(dial)->allocation.height / 2;
+
+ old_value = dial->adjustment->value;
+ dial->angle = atan2(yc-y, x-xc);
+
+ if (dial->angle < -M_PI/2.)
+ dial->angle += 2*M_PI;
+
+ if (dial->angle < -M_PI/6)
+ dial->angle = -M_PI/6;
+
+ if (dial->angle > 7.*M_PI/6.)
+ dial->angle = 7.*M_PI/6.;
+
+ dial->adjustment->value = dial->adjustment->lower + (7.*M_PI/6 - dial->angle) *
+ (dial->adjustment->upper - dial->adjustment->lower) / (4.*M_PI/3.);
+
+ if (dial->adjustment->value != old_value)
+ {
+ if (dial->policy == GTK_UPDATE_CONTINUOUS)
+ {
+ gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
+ }
+ else
+ {
+ gtk_widget_draw (GTK_WIDGET(dial), NULL);
+
+ if (dial->policy == GTK_UPDATE_DELAYED)
+ {
+ if (dial->timer)
+ gtk_timeout_remove (dial->timer);
+
+ dial->timer = gtk_timeout_add (SCROLL_DELAY_LENGTH,
+ (GtkFunction) gtk_dial_timer,
+ (gpointer) dial);
+ }
+ }
+ }
+}
+</verb></tscreen>
+
+<p>
+Changes to the Adjustment by external means are communicated to our
+widget by the ``changed'' and ``value_changed'' signals. The handlers
+for these functions call <tt/gtk_dial_update()/ to validate the
+arguments, compute the new pointer angle, and redraw the widget (by
+calling <tt/gtk_widget_draw()/).
+
+<tscreen><verb>
+static void
+gtk_dial_update (GtkDial *dial)
+{
+ gfloat new_value;
+
+ g_return_if_fail (dial != NULL);
+ g_return_if_fail (GTK_IS_DIAL (dial));
+
+ new_value = dial->adjustment->value;
+
+ if (new_value < dial->adjustment->lower)
+ new_value = dial->adjustment->lower;
+
+ if (new_value > dial->adjustment->upper)
+ new_value = dial->adjustment->upper;
+
+ if (new_value != dial->adjustment->value)
+ {
+ dial->adjustment->value = new_value;
+ gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
+ }
+
+ dial->angle = 7.*M_PI/6. - (new_value - dial->adjustment->lower) * 4.*M_PI/3. /
+ (dial->adjustment->upper - dial->adjustment->lower);
+
+ gtk_widget_draw (GTK_WIDGET(dial), NULL);
+}
+
+static void
+gtk_dial_adjustment_changed (GtkAdjustment *adjustment,
+ gpointer data)
+{
+ GtkDial *dial;
+
+ g_return_if_fail (adjustment != NULL);
+ g_return_if_fail (data != NULL);
+
+ dial = GTK_DIAL (data);
+
+ if ((dial->old_value != adjustment->value) ||
+ (dial->old_lower != adjustment->lower) ||
+ (dial->old_upper != adjustment->upper))
+ {
+ gtk_dial_update (dial);
+
+ dial->old_value = adjustment->value;
+ dial->old_lower = adjustment->lower;
+ dial->old_upper = adjustment->upper;
+ }
+}
+
+static void
+gtk_dial_adjustment_value_changed (GtkAdjustment *adjustment,
+ gpointer data)
+{
+ GtkDial *dial;
+
+ g_return_if_fail (adjustment != NULL);
+ g_return_if_fail (data != NULL);
+
+ dial = GTK_DIAL (data);
+
+ if (dial->old_value != adjustment->value)
+ {
+ gtk_dial_update (dial);
+
+ dial->old_value = adjustment->value;
+ }
+}
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect2> Possible Enhancements
+
+<p>
+
+The Dial widget as we've described it so far runs about 670 lines of
+code. Although that might sound like a fair bit, we've really
+accomplished quite a bit with that much code, especially since much of
+that length is headers and boilerplate. However, there are quite a few
+more enhancements that could be made to this widget:
+
+<itemize>
+<item> If you try this widget out, you'll find that there is some
+flashing as the pointer is dragged around. This is because the entire
+widget is erased every time the pointer is moved before being
+redrawn. Often, the best way to handle this problem is to draw to an
+offscreen pixmap, then copy the final results onto the screen in one
+step. (The ProgressBar widget draws itself in this fashion.)
+
+<item> The user should be able to use the up and down arrow keys to
+increase and decrease the value.
+
+<item> It would be nice if the widget had buttons to increase and
+decrease the value in small or large steps. Although it would be
+possible to use embedded Button widgets for this, we would also like
+the buttons to auto-repeat when held down, as the arrows on a
+scrollbar do. Most of the code to implement this type of behavior can
+be found in the GtkRange widget.
+
+<item> The Dial widget could be made into a container widget with a
+single child widget positioned at the bottom between the buttons
+mentioned above. The user could then add their choice of a label or
+entry widget to display the current value of the dial.
+
+</itemize>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Learning More
+
+<p>
+Only a small part of the many details involved in creating widgets
+could be described above. If you want to write your own widgets, the
+best source of examples is the GTK source itself. Ask yourself some
+questions about the widget you want to write: is it a Container
+widget? does it have its own window? is it a modification of an
+existing widget? Then find a similar widget, and start making changes.
+Good luck!
+
+<!-- ***************************************************************** -->
+<sect>Scribble, A Simple Example Drawing Program
+<!-- ***************************************************************** -->
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Overview
+
+<p>
+In this section, we will build a simple drawing program. In the
+process, we will examine how to handle mouse events, how to draw in a
+window, and how to do drawing better by using a backing pixmap. After
+creating the simple drawing program, we will extend it by adding
+support for XInput devices, such as drawing tablets. GTK provides
+support routines which makes getting extended information, such as
+pressure and tilt, from such devices quite easy.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Event Handling
+
+<p>
+The GTK signals we have already discussed are for high-level actions,
+such as a menu item being selected. However, sometimes it is useful to
+learn about lower-level occurrences, such as the mouse being moved, or
+a key being pressed. There are also GTK signals corresponding to these
+low-level <em>events</em>. The handlers for these signals have an
+extra parameter which is a pointer to a structure containing
+information about the event. For instance, motion events handlers are
+passed a pointer to a GdkEventMotion structure which looks (in part)
+like:
+
+<tscreen><verb>
+struct _GdkEventMotion
+{
+ GdkEventType type;
+ GdkWindow *window;
+ guint32 time;
+ gdouble x;
+ gdouble y;
+ ...
+ guint state;
+ ...
+};
+</verb></tscreen>
+
+<tt/type/ will be set to the event type, in this case
+<tt/GDK_MOTION_NOTIFY/, window is the window in which the event
+occured. <tt/x/ and <tt/y/ give the coordinates of the event,
+and <tt/state/ specifies the modifier state when the event
+occurred (that is, it specifies which modifier keys and mouse buttons
+were pressed.) It is the bitwise OR of some of the following:
+
+<tscreen><verb>
+GDK_SHIFT_MASK
+GDK_LOCK_MASK
+GDK_CONTROL_MASK
+GDK_MOD1_MASK
+GDK_MOD2_MASK
+GDK_MOD3_MASK
+GDK_MOD4_MASK
+GDK_MOD5_MASK
+GDK_BUTTON1_MASK
+GDK_BUTTON2_MASK
+GDK_BUTTON3_MASK
+GDK_BUTTON4_MASK
+GDK_BUTTON5_MASK
+</verb></tscreen>
+
+<p>
+As for other signals, to determine what happens when an event occurs
+we call <tt>gtk_signal_connect()</tt>. But we also need let GTK
+know which events we want to be notified about. To do this, we call
+the function:
+
+<tscreen><verb>
+void gtk_widget_set_events (GtkWidget *widget,
+ gint events);
+</verb></tscreen>
+
+The second field specifies the events we are interested in. It
+is the bitwise OR of constants that specify different types
+of events. For future reference the event types are:
+
+<tscreen><verb>
+GDK_EXPOSURE_MASK
+GDK_POINTER_MOTION_MASK
+GDK_POINTER_MOTION_HINT_MASK
+GDK_BUTTON_MOTION_MASK
+GDK_BUTTON1_MOTION_MASK
+GDK_BUTTON2_MOTION_MASK
+GDK_BUTTON3_MOTION_MASK
+GDK_BUTTON_PRESS_MASK
+GDK_BUTTON_RELEASE_MASK
+GDK_KEY_PRESS_MASK
+GDK_KEY_RELEASE_MASK
+GDK_ENTER_NOTIFY_MASK
+GDK_LEAVE_NOTIFY_MASK
+GDK_FOCUS_CHANGE_MASK
+GDK_STRUCTURE_MASK
+GDK_PROPERTY_CHANGE_MASK
+GDK_PROXIMITY_IN_MASK
+GDK_PROXIMITY_OUT_MASK
+</verb></tscreen>
+
+There are a few subtle points that have to be observed when calling
+<tt/gtk_widget_set_events()/. First, it must be called before the X window
+for a GTK widget is created. In practical terms, this means you
+should call it immediately after creating the widget. Second, the
+widget must have an associated X window. For efficiency, many widget
+types do not have their own window, but draw in their parent's window.
+These widgets are:
+
+<tscreen><verb>
+GtkAlignment
+GtkArrow
+GtkBin
+GtkBox
+GtkImage
+GtkItem
+GtkLabel
+GtkPaned
+GtkPixmap
+GtkScrolledWindow
+GtkSeparator
+GtkTable
+GtkViewport
+GtkAspectFrame
+GtkFrame
+GtkVPaned
+GtkHPaned
+GtkVBox
+GtkHBox
+GtkVSeparator
+GtkHSeparator
+</verb></tscreen>
+
+To capture events for these widgets, you need to use an EventBox
+widget. See the section on
+<ref id="sec_The_EventBox_Widget" name="The EventBox Widget"> for
+details.
+
+<p>
+For our drawing program, we want to know when the mouse button is
+pressed and when the mouse is moved, so we specify
+<tt/GDK_POINTER_MOTION_MASK/ and <tt/GDK_BUTTON_PRESS_MASK/. We also
+want to know when we need to redraw our window, so we specify
+<tt/GDK_EXPOSURE_MASK/. Although we want to be notified via a
+Configure event when our window size changes, we don't have to specify
+the corresponding <tt/GDK_STRUCTURE_MASK/ flag, because it is
+automatically specified for all windows.
+
+<p>
+It turns out, however, that there is a problem with just specifying
+<tt/GDK_POINTER_MOTION_MASK/. This will cause the server to add a new
+motion event to the event queue every time the user moves the mouse.
+Imagine that it takes us 0.1 seconds to handle a motion event, but the
+X server queues a new motion event every 0.05 seconds. We will soon
+get way behind the users drawing. If the user draws for 5 seconds,
+it will take us another 5 seconds to catch up after they release
+the mouse button! What we would like is to only get one motion
+event for each event we process. The way to do this is to
+specify <tt/GDK_POINTER_MOTION_HINT_MASK/.
+
+<p>
+When we specify <tt/GDK_POINTER_MOTION_HINT_MASK/, the server sends
+us a motion event the first time the pointer moves after entering
+our window, or after a button press or release event. Subsequent
+motion events will be suppressed until we explicitely ask for
+the position of the pointer using the function:
+
+<tscreen><verb>
+GdkWindow* gdk_window_get_pointer (GdkWindow *window,
+ gint *x,
+ gint *y,
+ GdkModifierType *mask);
+</verb></tscreen>
+
+(There is another function, <tt>gtk_widget_get_pointer()</tt> which
+has a simpler interface, but turns out not to be very useful, since
+it only retrieves the position of the mouse, not whether the buttons
+are pressed.)
+
+<p>
+The code to set the events for our window then looks like:
+
+<tscreen><verb>
+ 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);
+ 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);
+</verb></tscreen>
+
+We'll save the "expose_event" and "configure_event" handlers for
+later. The "motion_notify_event" and "button_press_event" handlers
+pretty simple:
+
+<tscreen><verb>
+static gint
+button_press_event (GtkWidget *widget, GdkEventButton *event)
+{
+ if (event->button == 1 &amp;&amp; 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, &amp;x, &amp;y, &amp;state);
+ else
+ {
+ x = event->x;
+ y = event->y;
+ state = event->state;
+ }
+
+ if (state &amp; GDK_BUTTON1_MASK &amp;&amp; pixmap != NULL)
+ draw_brush (widget, x, y);
+
+ return TRUE;
+}
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> The DrawingArea Widget, And Drawing
+
+<p>
+We know turn to the process of drawing on the screen. The
+widget we use for this is the DrawingArea widget. A drawing area
+widget is essentially an X window and nothing more. It is a blank
+canvas in which we can draw whatever we like. A drawing area
+is created using the call:
+
+<tscreen><verb>
+GtkWidget* gtk_drawing_area_new (void);
+</verb></tscreen>
+
+A default size for the widget can be specified by calling:
+
+<tscreen><verb>
+void gtk_drawing_area_size (GtkDrawingArea *darea,
+ gint width,
+ gint height);
+</verb></tscreen>
+
+This default size can be overriden, as is true for all widgets,
+by calling <tt>gtk_widget_set_usize()</tt>, and that, in turn, can
+be overridden if the user manually resizes the the window containing
+the drawing area.
+
+<p>
+It should be noted that when we create a DrawingArea widget, we are,
+<em>completely</em> responsible for drawing the contents. If our
+window is obscured then uncovered, we get an exposure event and must
+redraw what was previously hidden.
+
+<p>
+Having to remember everything that was drawn on the screen so we
+can properly redraw it can, to say the least, be a nuisance. In
+addition, it can be visually distracting if portions of the
+window are cleared, then redrawn step by step. The solution to
+this problem is to use an offscreen <em>backing pixmap</em>.
+Instead of drawing directly to the screen, we draw to an image
+stored in server memory but not displayed, then when the image
+changes or new portions of the image are displayed, we copy the
+relevant portions onto the screen.
+
+<p>
+To create an offscreen pixmap, we call the function:
+
+<tscreen><verb>
+GdkPixmap* gdk_pixmap_new (GdkWindow *window,
+ gint width,
+ gint height,
+ gint depth);
+</verb></tscreen>
+
+The <tt>window</tt> parameter specifies a GDK window that this pixmap
+takes some of its properties from. <tt>width</tt> and <tt>height</tt>
+specify the size of the pixmap. <tt>depth</tt> specifies the <em>color
+depth</em>, that is the number of bits per pixel, for the new window.
+If the depth is specified as <tt>-1</tt>, it will match the depth
+of <tt>window</tt>.
+
+<p>
+We create the pixmap in our "configure_event" handler. This event
+is generated whenever the window changes size, including when it
+is originally created.
+
+<tscreen><verb>
+/* 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_destroy(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;
+}
+</verb></tscreen>
+
+The call to <tt>gdk_draw_rectangle()</tt> clears the pixmap
+initially to white. We'll say more about that in a moment.
+
+<p>
+Our exposure event handler then simply copies the relevant portion
+of the pixmap onto the screen (we determine the area we need
+to redraw by using the event->area field of the exposure event):
+
+<tscreen><verb>
+/* Refill 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;
+}
+</verb></tscreen>
+
+We've now seen how to keep the screen up to date with our pixmap, but
+how do we actually draw interesting stuff on our pixmap? There are a
+large number of calls in GTK's GDK library for drawing on
+<em>drawables</em>. A drawable is simply something that can be drawn
+upon. It can be a window, a pixmap, or a bitmap (a black and white
+image). We've already seen two such calls above,
+<tt>gdk_draw_rectangle()</tt> and <tt>gdk_draw_pixmap()</tt>. The
+complete list is:
+
+<tscreen><verb>
+gdk_draw_line ()
+gdk_draw_rectangle ()
+gdk_draw_arc ()
+gdk_draw_polygon ()
+gdk_draw_string ()
+gdk_draw_text ()
+gdk_draw_pixmap ()
+gdk_draw_bitmap ()
+gdk_draw_image ()
+gdk_draw_points ()
+gdk_draw_segments ()
+</verb></tscreen>
+
+See the reference documentation or the header file
+<tt>&lt;gdk/gdk.h&gt;</tt> for further details on these functions.
+These functions all share the same first two arguments. The first
+argument is the drawable to draw upon, the second argument is a
+<em>graphics context</em> (GC).
+
+<p>
+A graphics context encapsulates information about things such as
+foreground and background color and line width. GDK has a full set of
+functions for creating and modifying graphics contexts, but to keep
+things simple we'll just use predefined graphics contexts. Each widget
+has an associated style. (Which can be modified in a gtkrc file, see
+the section GTK's rc file.) This, among other things, stores a number
+of graphics contexts. Some examples of accessing these graphics
+contexts are:
+
+<tscreen><verb>
+widget->style->white_gc
+widget->style->black_gc
+widget->style->fg_gc[GTK_STATE_NORMAL]
+widget->style->bg_gc[GTK_WIDGET_STATE(widget)]
+</verb></tscreen>
+
+The fields <tt>fg_gc</tt>, <tt>bg_gc</tt>, <tt>dark_gc</tt>, and
+<tt>light_gc</tt> are indexed by a parameter of type
+<tt>GtkStateType</tt> which can take on the values:
+
+<tscreen><verb>
+GTK_STATE_NORMAL,
+GTK_STATE_ACTIVE,
+GTK_STATE_PRELIGHT,
+GTK_STATE_SELECTED,
+GTK_STATE_INSENSITIVE
+</verb></tscreen>
+
+For instance, the for <tt/GTK_STATE_SELECTED/ the default foreground
+color is white and the default background color, dark blue.
+
+<p>
+Our function <tt>draw_brush()</tt>, which does the actual drawing
+on the screen, is then:
+
+<tscreen><verb>
+/* 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, &amp;update_rect);
+}
+</verb></tscreen>
+
+After we draw the rectangle representing the brush onto the pixmap,
+we call the function:
+
+<tscreen><verb>
+void gtk_widget_draw (GtkWidget *widget,
+ GdkRectangle *area);
+</verb></tscreen>
+
+which notifies X that the area given by the <tt>area</tt> parameter
+needs to be updated. X will eventually generate an expose event
+(possibly combining the areas passed in several calls to
+<tt>gtk_widget_draw()</tt>) which will cause our expose event handler
+to copy the relevant portions to the screen.
+
+<p>
+We have now covered the entire drawing program except for a few
+mundane details like creating the main window. The complete
+source code is available from the location from which you got
+this tutorial, or from:
+
+<htmlurl url="http://www.msc.cornell.edu/~otaylor/gtk-gimp/tutorial"
+name="http://www.msc.cornell.edu/~otaylor/gtk-gimp/tutorial">
+
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Adding XInput support
+
+<p>
+
+It is now possible to buy quite inexpensive input devices such
+as drawing tablets, which allow drawing with a much greater
+ease of artistic expression than does a mouse. The simplest way
+to use such devices is simply as a replacement for the mouse,
+but that misses out many of the advantages of these devices,
+such as:
+
+<itemize>
+<item> Pressure sensitivity
+<item> Tilt reporting
+<item> Sub-pixel positioning
+<item> Multiple inputs (for example, a stylus with a point and eraser)
+</itemize>
+
+For information about the XInput extension, see the <htmlurl
+url="http://www.msc.cornell.edu/~otaylor/xinput/XInput-HOWTO.html"
+name="XInput-HOWTO">.
+
+<p>
+If we examine the full definition of, for example, the GdkEventMotion
+structure, we see that it has fields to support extended device
+information.
+
+<tscreen><verb>
+struct _GdkEventMotion
+{
+ GdkEventType type;
+ GdkWindow *window;
+ guint32 time;
+ gdouble x;
+ gdouble y;
+ gdouble pressure;
+ gdouble xtilt;
+ gdouble ytilt;
+ guint state;
+ gint16 is_hint;
+ GdkInputSource source;
+ guint32 deviceid;
+};
+</verb></tscreen>
+
+<tt/pressure/ gives the pressure as a floating point number between
+0 and 1. <tt/xtilt/ and <tt/ytilt/ can take on values between
+-1 and 1, corresponding to the degree of tilt in each direction.
+<tt/source/ and <tt/deviceid/ specify the device for which the
+event occurred in two different ways. <tt/source/ gives some simple
+information about the type of device. It can take the enumeration
+values.
+
+<tscreen><verb>
+GDK_SOURCE_MOUSE
+GDK_SOURCE_PEN
+GDK_SOURCE_ERASER
+GDK_SOURCE_CURSOR
+</verb></tscreen>
+
+<tt/deviceid/ specifies a unique numeric ID for the device. This can
+be used to find out further information about the device using the
+<tt/gdk_input_list_devices()/ call (see below). The special value
+<tt/GDK_CORE_POINTER/ is used for the core pointer device. (Usually
+the mouse.)
+
+<sect2> Enabling extended device information
+
+<p>
+To let GTK know about our interest in the extended device information,
+we merely have to add a single line to our program:
+
+<tscreen><verb>
+gtk_widget_set_extension_events (drawing_area, GDK_EXTENSION_EVENTS_CURSOR);
+</verb></tscreen>
+
+By giving the value <tt/GDK_EXTENSION_EVENTS_CURSOR/ we say that
+we are interested in extension events, but only if we don't have
+to draw our own cursor. See the section <ref
+id="sec_Further_Sophistications" name="Further Sophistications"> below
+for more information about drawing the cursor. We could also
+give the values <tt/GDK_EXTENSION_EVENTS_ALL/ if we were willing
+to draw our own cursor, or <tt/GDK_EXTENSION_EVENTS_NONE/ to revert
+back to the default condition.
+
+<p>
+This is not completely the end of the story however. By default,
+no extension devices are enabled. We need a mechanism to allow
+users to enable and configure their extension devices. GTK provides
+the InputDialog widget to automate this process. The following
+procedure manages an InputDialog widget. It creates the dialog if
+it isn't present, and raises it to the top otherwise.
+
+<tscreen><verb>
+void
+input_dialog_destroy (GtkWidget *w, gpointer data)
+{
+ *((GtkWidget **)data) = NULL;
+}
+
+void
+create_input_dialog ()
+{
+ static GtkWidget *inputd = NULL;
+
+ if (!inputd)
+ {
+ inputd = gtk_input_dialog_new();
+
+ gtk_signal_connect (GTK_OBJECT(inputd), "destroy",
+ (GtkSignalFunc)input_dialog_destroy, &amp;inputd);
+ gtk_signal_connect_object (GTK_OBJECT(GTK_INPUT_DIALOG(inputd)->close_button),
+ "clicked",
+ (GtkSignalFunc)gtk_widget_hide,
+ GTK_OBJECT(inputd));
+ gtk_widget_hide ( GTK_INPUT_DIALOG(inputd)->save_button);
+
+ gtk_widget_show (inputd);
+ }
+ else
+ {
+ if (!GTK_WIDGET_MAPPED(inputd))
+ gtk_widget_show(inputd);
+ else
+ gdk_window_raise(inputd->window);
+ }
+}
+</verb></tscreen>
+
+(You might want to take note of the way we handle this dialog. By
+connecting to the "destroy" signal, we make sure that we don't keep a
+pointer to dialog around after it is destroyed - that could lead to a
+segfault.)
+
+<p>
+The InputDialog has two buttons "Close" and "Save", which by default
+have no actions assigned to them. In the above function we make
+"Close" hide the dialog, hide the "Save" button, since we don't
+implement saving of XInput options in this program.
+
+<sect2> Using extended device information
+
+<p>
+Once we've enabled the device, we can just use the extended
+device information in the extra fields of the event structures.
+In fact, it is always safe to use this information since these
+fields will have reasonable default values even when extended
+events are not enabled.
+
+<p>
+Once change we do have to make is to call
+<tt/gdk_input_window_get_pointer()/ instead of
+<tt/gdk_window_get_pointer/. This is necessary because
+<tt/gdk_window_get_pointer/ doesn't return the extended device
+information.
+
+<tscreen><verb>
+void gdk_input_window_get_pointer (GdkWindow *window,
+ guint32 deviceid,
+ gdouble *x,
+ gdouble *y,
+ gdouble *pressure,
+ gdouble *xtilt,
+ gdouble *ytilt,
+ GdkModifierType *mask);
+</verb></tscreen>
+
+When calling this function, we need to specify the device ID as
+well as the window. Usually, we'll get the device ID from the
+<tt/deviceid/ field of an event structure. Again, this function
+will return reasonable values when extension events are not
+enabled. (In this case, <tt/event->deviceid/ will have the value
+<tt/GDK_CORE_POINTER/).
+
+So the basic structure of our button-press and motion event handlers,
+doesn't change much - we just need to add code to deal with the
+extended information.
+
+<tscreen><verb>
+static gint
+button_press_event (GtkWidget *widget, GdkEventButton *event)
+{
+ print_button_press (event->deviceid);
+
+ if (event->button == 1 &amp;&amp; pixmap != NULL)
+ draw_brush (widget, event->source, event->x, event->y, event->pressure);
+
+ return TRUE;
+}
+
+static gint
+motion_notify_event (GtkWidget *widget, GdkEventMotion *event)
+{
+ gdouble x, y;
+ gdouble pressure;
+ GdkModifierType state;
+
+ if (event->is_hint)
+ gdk_input_window_get_pointer (event->window, event->deviceid,
+ &amp;x, &amp;y, &amp;pressure, NULL, NULL, &amp;state);
+ else
+ {
+ x = event->x;
+ y = event->y;
+ pressure = event->pressure;
+ state = event->state;
+ }
+
+ if (state &amp; GDK_BUTTON1_MASK &amp;&amp; pixmap != NULL)
+ draw_brush (widget, event->source, x, y, pressure);
+
+ return TRUE;
+}
+</verb></tscreen>
+
+We also need to do something with the new information. Our new
+<tt/draw_brush()/ function draws with a different color for
+each <tt/event->source/ and changes the brush size depending
+on the pressure.
+
+<tscreen><verb>
+/* Draw a rectangle on the screen, size depending on pressure,
+ and color on the type of device */
+static void
+draw_brush (GtkWidget *widget, GdkInputSource source,
+ gdouble x, gdouble y, gdouble pressure)
+{
+ GdkGC *gc;
+ GdkRectangle update_rect;
+
+ switch (source)
+ {
+ case GDK_SOURCE_MOUSE:
+ gc = widget->style->dark_gc[GTK_WIDGET_STATE (widget)];
+ break;
+ case GDK_SOURCE_PEN:
+ gc = widget->style->black_gc;
+ break;
+ case GDK_SOURCE_ERASER:
+ gc = widget->style->white_gc;
+ break;
+ default:
+ gc = widget->style->light_gc[GTK_WIDGET_STATE (widget)];
+ }
+
+ update_rect.x = x - 10 * pressure;
+ update_rect.y = y - 10 * pressure;
+ update_rect.width = 20 * pressure;
+ update_rect.height = 20 * pressure;
+ gdk_draw_rectangle (pixmap, gc, TRUE,
+ update_rect.x, update_rect.y,
+ update_rect.width, update_rect.height);
+ gtk_widget_draw (widget, &amp;update_rect);
+}
+</verb></tscreen>
+
+<sect2> Finding out more about a device
+
+<p>
+As an example of how to find out more about a device, our program
+will print the name of the device that generates each button
+press. To find out the name of a device, we call the function:
+
+<tscreen><verb>
+GList *gdk_input_list_devices (void);
+</verb></tscreen>
+
+which returns a GList (a linked list type from the glib library)
+of GdkDeviceInfo structures. The GdkDeviceInfo strucure is defined
+as:
+
+<tscreen><verb>
+struct _GdkDeviceInfo
+{
+ guint32 deviceid;
+ gchar *name;
+ GdkInputSource source;
+ GdkInputMode mode;
+ gint has_cursor;
+ gint num_axes;
+ GdkAxisUse *axes;
+ gint num_keys;
+ GdkDeviceKey *keys;
+};
+</verb></tscreen>
+
+Most of these fields are configuration information that you
+can ignore unless you are implemented XInput configuration
+saving. The we are interested in here is <tt/name/ which is
+simply the name that X assigns to the device. The other field
+that isn't configuration information is <tt/has_cursor/. If
+<tt/has_cursor/ is false, then we we need to draw our own
+cursor. But since we've specified <tt/GDK_EXTENSION_EVENTS_CURSOR/,
+we don't have to worry about this.
+
+<p>
+Our <tt/print_button_press()/ function simply iterates through
+the returned list until it finds a match, then prints out
+the name of the device.
+
+<tscreen><verb>
+static void
+print_button_press (guint32 deviceid)
+{
+ GList *tmp_list;
+
+ /* gdk_input_list_devices returns an internal list, so we shouldn't
+ free it afterwards */
+ tmp_list = gdk_input_list_devices();
+
+ while (tmp_list)
+ {
+ GdkDeviceInfo *info = (GdkDeviceInfo *)tmp_list->data;
+
+ if (info->deviceid == deviceid)
+ {
+ printf("Button press on device '%s'\n", info->name);
+ return;
+ }
+
+ tmp_list = tmp_list->next;
+ }
+}
+</verb></tscreen>
+
+That completes the changes to ``XInputize'' our program. As with
+the first version, the complete source is available at the location
+from which you got this tutorial, or from:
+
+<htmlurl url="http://www.msc.cornell.edu/~otaylor/gtk-gimp/tutorial"
+name="http://www.msc.cornell.edu/~otaylor/gtk-gimp/tutorial">
+
+
+<sect2> Further sophistications <label id="sec_Further_Sophistications">
+
+<p>
+Although our program now supports XInput quite well, it lacks some
+features we would want in a full-featured application. First, the user
+probably doesn't want to have to configure their device each time they
+run the program, so we should allow them to save the device
+configuration. This is done by iterating through the return of
+<tt/gdk_input_list_devices()/ and writing out the configuration to a
+file.
+
+<p>
+To restore the state next time the program is run, GDK provides
+functions to change device configuration:
+
+<tscreen><verb>
+gdk_input_set_extension_events()
+gdk_input_set_source()
+gdk_input_set_mode()
+gdk_input_set_axes()
+gdk_input_set_key()
+</verb></tscreen>
+
+(The list returned from <tt/gdk_input_list_devices()/ should not be
+modified directly.) An example of doing this can be found in the
+drawing program gsumi. (Available from <htmlurl
+url="http://www.msc.cornell.edu/~otaylor/gsumi/"
+name="http://www.msc.cornell.edu/~otaylor/gsumi/">) Eventually, it
+would be nice to have a standard way of doing this for all
+applications. This probably belongs at a slightly higher level than
+GTK, perhaps in the GNOME library.
+
+<p>
+Another major ommission that we have mentioned above is the lack of
+cursor drawing. Platforms other than XFree86 currently do not allow
+simultaneously using a device as both the core pointer and directly by
+an application. See the <url
+url="http://www.msc.cornell.edu/~otaylor/xinput/XInput-HOWTO.html"
+name="XInput-HOWTO"> for more information about this. This means that
+applications that want to support the widest audience need to draw
+their own cursor.
+
+<p>
+An application that draws it's own cursor needs to do two things:
+determine if the current device needs a cursor drawn or not, and
+determine if the current device is in proximity. (If the current
+device is a drawing tablet, it's a nice touch to make the cursor
+disappear when the stylus is lifted from the tablet. When the
+device is touching the stylus, that is called "in proximity.")
+The first is done by searching the device list, as we did
+to find out the device name. The second is achieved by selecting
+"proximity_out" events. An example of drawing one's own cursor is
+found in the 'testinput' program found in the GTK distribution.
+
+<!-- ***************************************************************** -->
+<sect>Tips For Writing GTK Applications
+<!-- ***************************************************************** -->
+
+<p>
+This section is simply a gathering of wisdom, general style guidelines and hints to
+creating good GTK applications. It is totally useless right now cause it's
+only a topic sentence :)
+
+Use GNU autoconf and automake! They are your friends :) I am planning to
+make a quick intro on them here.
+
+<!-- ***************************************************************** -->
+<sect>Contributing
+<!-- ***************************************************************** -->
+
+<p>
+This document, like so much other great software out there, was created for
+free by volunteers. If you are at all knowledgeable about any aspect of GTK
+that does not already have documentation, please consider contributing to
+this document.
+<p>
+If you do decide to contribute, please mail your text to Tony Gale,
+<tt><htmlurl url="mailto:gale@gimp.org"
+name="gale@gimp.org"></tt>. Also, be aware that the entirety of this
+document is free, and any addition by yourself must also be free. That is,
+people may use any portion of your examples in their programs, and copies
+of this document may be distributed at will etc.
+<p>
+Thank you.
+
+<!-- ***************************************************************** -->
+<sect>Credits
+<!-- ***************************************************************** -->
+<p>
+I would like to thank the following for their contributions to this text.
+
+<itemize>
+<item>Bawer Dagdeviren, <tt><htmlurl url="mailto:chamele0n@geocities.com"
+name="chamele0n@geocities.com"></tt> for the menus tutorial.
+
+<item>Raph Levien, <tt><htmlurl url="mailto:raph@acm.org"
+name="raph@acm.org"></tt>
+for hello world ala GTK, widget packing, and general all around wisdom.
+He's also generously donated a home for this tutorial.
+
+<item>Peter Mattis, <tt><htmlurl url="mailto:petm@xcf.berkeley.edu"
+name="petm@xcf.berkeley.edu"></tt> for the simplest GTK program..
+and the ability to make it :)
+
+<item>Werner Koch <tt><htmlurl url="mailto:werner.koch@guug.de"
+name="werner.koch@guug.de"></tt> for converting the original plain text to
+SGML, and the widget class hierarchy.
+
+<item>Mark Crichton <tt><htmlurl url="mailto:crichton@expert.cc.purdue.edu"
+name="crichton@expert.cc.purdue.edu"></tt> for the menu factory code, and
+the table packing tutorial.
+
+<item>Owen Taylor <tt><htmlurl url="mailto:owt1@cornell.edu"
+name="owt1@cornell.edu"></tt> for the EventBox widget section (and
+the patch to the distro). He's also responsible for the selections code and
+tutorial, as well as the sections on writing your own GTK widgets, and the
+example application. Thanks a lot Owen for all you help!
+
+<item>Mark VanderBoom <tt><htmlurl url="mailto:mvboom42@calvin.edu"
+name="mvboom42@calvin.edu"></tt> for his wonderful work on the Notebook,
+Progress Bar, Dialogs, and File selection widgets. Thanks a lot Mark!
+You've been a great help.
+
+<item>Tim Janik <tt><htmlurl url="mailto:timj@psynet.net"
+name="timj@psynet.net"></tt> for his great job on the Lists Widget.
+Thanks Tim :)
+
+<item>Rajat Datta <tt><htmlurl url="mailto:rajat@ix.netcom.com"
+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.
+
+</itemize>
+<p>
+And to all of you who commented and helped refine this document.
+<p>
+Thanks.
+
+<!-- ***************************************************************** -->
+<sect> Tutorial Copyright and Permissions Notice
+<!-- ***************************************************************** -->
+
+<p>
+The GTK Tutorial is Copyright (C) 1997 Ian Main.
+
+Copyright (C) 1998 Tony Gale.
+<p>
+Permission is granted to make and distribute verbatim copies of this
+manual provided the copyright notice and this permission notice are
+preserved on all copies.
+<P>Permission is granted to copy and distribute modified versions of
+this document under the conditions for verbatim copying, provided that
+this copyright notice is included exactly as in the original,
+and that the entire resulting derived work is distributed under
+the terms of a permission notice identical to this one.
+<P>Permission is granted to copy and distribute translations of this
+document into another language, under the above conditions for modified
+versions.
+<P>If you are intending to incorporate this document into a published
+work, please contact the maintainer, and we will make an effort
+to ensure that you have the most up to date information available.
+<P>There is no guarentee that this document lives up to its intended
+purpose. This is simply provided as a free resource. As such,
+the authors and maintainers of the information provided within can
+not make any guarentee that the information is even accurate.
+</article>
diff --git a/docs/tutorial/gtk_tut_it.sgml b/docs/tutorial/gtk_tut_it.sgml
new file mode 100644
index 000000000..9ed24eb86
--- /dev/null
+++ b/docs/tutorial/gtk_tut_it.sgml
@@ -0,0 +1,8340 @@
+
+<!doctype linuxdoc system>
+<article>
+<title>GTK Tutorial
+<author>Ian Main, <tt><htmlurl url="mailto:slow@intergate.bc.ca"
+ name="slow@intergate.bc.ca"></tt>
+
+<date>December 1, 1997 - Traduzione Aggiornata al 19 Gennaio 1998
+
+<abstract>Tradotto da Michel Morelli, <tt><htmlurl url="mailto:ziobudda@chiara.dei.unipd.it" name="ziobudda@chiara.dei.unipd.it"></tt>, Daniele Canazza, <tt><htmlurl url="mailto:dcanazz@tin.it" name="dcanazz@tin.it"></tt> e Antonio Schifano, <tt><htmlurl url="mailto:schifano@cli.di.unipi.it" name="schifano@cli.di.unipi.it"></tt>
+</abstract>
+
+<sect>Introduzione
+<p>
+GTK (GIMP Toolkit) era orginariamente sviluppato come toolkit per il programma
+GIMP (General Image Manipulation Program). GTK &egrave; costruito sulla base del
+kit di disegno di GIMP, il GDK (GIMP Drawing Kit) il quale &egrave; costruito a sua
+volta attorno alle funzioni della Xlib. E' chiamato ``toolkit di GIMP'' perch&eacute;
+era inizialmente scritto per sviluppare GIMP, ma ora viene utilizzato nello
+sviluppo di molti progetti software liberi. Gli autori sono
+<itemize>
+<item> Peter Mattis <tt><htmlurl url="mailto:petm@xcf.berkeley.edu"
+ name="petm@xcf.berkeley.edu"></tt>
+<item> Spencer Kimball <tt><htmlurl url="mailto:spencer@xcf.berkeley.edu"
+ name="spencer@xcf.berkeley.edu"></tt>
+<item> Josh MacDonald <tt><htmlurl url="mailto:jmacd@xcf.berkeley.edu"
+ name="jmacd@xcf.berkeley.edu"></tt>
+</itemize>
+
+<p>
+GTK &egrave; essenzialmente una API (application programmers interface)
+orientata agli oggetti.
+Anche se scritto completamente in C, &egrave; implementato usando l'idea delle
+classi e delle funzioni di callback (puntatori a funzioni).
+
+<p>
+C'&egrave; anche una terza componente chiamata glib che contiene una serie di
+implementazioni differenti di alcune chiamate di funzioni standard e anche
+alcune funzioni aggiuntive, per esempio per la manipolazione delle liste
+collegate, eccetera. Le funzioni sostitutive sono usate per migliorare la
+portabilit&agrave; di GTK. Alcune delle funzioni implementate qui non sono
+disponibili o non sono standard, altre sono uniche come g_strerror().
+Altre contengono miglioramenti alle stesse della libc come g_malloc che ha
+delle utility di debugging migliorate.
+
+<p>
+Questo tutorial &egrave; un tentativo di documentare il meglio possibile la libreria gtk
+e non pretende di essere completo. Questo tutorial suppone una buona conoscenza del
+linugaggio C e di come creare programmi in C. Saranno facilitati i lettori che hanno una
+precedente esperienza nella programmazione in X. Se il GTK &egrave; il primo insieme di widget
+che studiate, siete pregati di dirmi come avete trovato questo tutorial e che tipo di problemi
+avete avuto.
+Notate che c'&egrave; anche una versione per il C++ della libreria GTK (chiamata GTK--), quindi
+se preferite utilizzare questo linguaggio al posto del C potreste cercare questa versione
+e non la GTK normale.
+Ci sono poi un ``wrapper'' Objective C e un collegamento a Guile, ma non ne seguo
+l'evoluzione.
+
+<p>
+Mi farebbe molto piacere conoscere qualsiasi problema che abbiate avuto nell'imparare il GTK
+da questo documento e apprezzerei anche critiche sul come migliorarlo.
+
+<sect>Iniziamo
+<p>
+La prima cosa da fare &egrave; certamente quella di scaricare il GTK e installarlo. Potete prendere
+l'ultima versione dal sito ftp.gimp.org nella directory /pub/gimp. Un'altra possibile sorgente
+di informazioni &egrave; il sito http://www.gimp.org/gtk. GTK usa il comando GNU autoconf per
+autoconfigurarsi.
+Una volta estratti i file dall'archivio tar, eseguite configure --help per vedere una lista delle
+opzioni del comando configure.
+
+<p>
+Per iniziare la nostra introduzione a GTK, cominceremo con il pi&ugrave; semplice programma
+possibile . Questo programma crea una finestra con dimensioni (in pixel) di 200x200 e
+l'unica possibilit&agrave; di uscita &egrave; di ucciderlo ucciso usando la shell o il Window Manager.
+
+<tscreen><verb>
+#include <gtk/gtk.h>
+
+int main (int argc, char *argv[])
+{
+ GtkWidget *window;
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_widget_show (window);
+
+ gtk_main ();
+
+ return 0;
+}
+</verb></tscreen>
+
+Tutti i programmi certamente includeranno &lt;gtk/gtk.h&gt; che dichiara le variabili, le funzioni,
+le strutture, etc. che saranno usate nella tua applicazione GTK.
+
+<p>
+La linea seguente:
+
+<tscreen><verb>
+gtk_init (&amp;argc, &amp;argv);
+</verb></tscreen>
+
+invoca la funzione gtk_init(gint *argc, gchar ***argv) che sar&agrave; usata in tutte le
+applicazioni GTK. Questa funzione sistema alcune cose al posto nostro, come la visuale
+predefinita e la mappa dei colori, e procede poi chiamando gdk_init(gint *argc, gchar ***argv).
+Questa funzione inizializza la libreria per l'uso, setta il gestore predefinito dei segnali
+e guarda negli argomenti, passati via linea di comando alla tua applicazione, alla ricerca
+di uno di questi argomenti:
+<itemize>
+<item> <tt/--display/
+<item> <tt/--debug-level/
+<item> <tt/--no-xshm/
+<item> <tt/--sync/
+<item> <tt/--show-events/
+<item> <tt/--no-show-events/
+</itemize>
+<p>
+Rimuove questi argomenti dalla lista degli argomenti passati, lasciando quelli non
+riconosciuti a disposizione della tua applicazione che potr&agrave; tenerne conto o ignorarli.
+In questo modo si crea un set di argomenti standard accettato da tutte le applicazione GTK.
+
+<p>
+Le seguenti 2 linee di codice creano e mostrano la finestra.
+
+<tscreen><verb>
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_widget_show (window);
+</verb></tscreen>
+
+L'argomento GTK_WINDOW_TOPLEVEL specifica che noi vogliamo che la nostra finestra si
+sottometta alle decorazioni del windows manager e alla posizione che quest'ultimo indicher&agrave;.
+Invece di creare una finestra avente dimensioni 0x0, la dimensione di una finestra senza
+figli (altri widget, come i bottoni, etc) &egrave; predefinita a 200x200 cos&igrave; che si possa manipolarla.
+La funzione gtk_widget_show() fa s&igrave; che GTK sappia che abbiamo finito di settare gli
+attributi di questo widget e che quindi quest'ultimo pu&ograve; essere visualizzato.
+
+<p>
+L'ultima linea ci fa entrare nel ciclo principale del GTK.
+
+<tscreen><verb>
+gtk_main ();
+</verb></tscreen>
+
+gtk_main() &egrave; un'altra chiamata che tu vedrete in tutte le applicazioni GTK. Quando il controllo
+raggiunge questo punto, l'applicazione si metter&agrave; a dormire aspettando che si verifichino eventi
+di X (come la pressione di un bottone o di un tasto), timeout o notifiche di Input/Output dei file
+Nel nostro esempio, comunque, tutti gli eventi sono ignorati.
+
+<sect1>Hello World in GTK
+<p>
+Ok, ora un programma con un widget (un bottone). E' il classico ``Hello World'' alla GTK.
+
+<tscreen><verb>
+
+#include <gtk/gtk.h>
+
+
+/* E' una funzione di ritorno (callback). Gli argomenti passati sono ignorati in questo
+* esempio.
+* Piu' informazioni sulle callback in seguito. */
+
+void hello (GtkWidget *widget, gpointer data)
+{
+ g_print ("Hello World\n");
+}
+
+gint delete_event(GtkWidget *widget, gpointer data)
+ {
+ g_print ("delete event occured\n");
+ /* Se si d&agrave; TRUE al manipolatore del segnale ``delete_event'', GTK emettera' il segnale
+ ``destroy''. Fornire FALSE significa non volere che la finestra sia distrutta.
+ Cambia FALSE con TRUE e la finestra principale sara' distrutta con un "delete_event"
+ */
+
+/* Un'altra callback */
+void destroy (GtkWidget *widget, gpointer data)
+{
+ gtk_main_quit ();
+}
+
+int main (int argc, char *argv[])
+{
+ /* GtkWidget e' il tipo di dato per i Widget */
+ GtkWidget *window;
+ GtkWidget *button;
+
+ /* Questa e' una chiamata presente in tutte le applicazioni GTK. Gli argomenti della
+ linea di comando vengono scorsi e restituiti alla applicazione */
+ gtk_init (&amp;argc, &amp;argv);
+
+ /* Crea una nuova finestra */
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+ /* Quando alla finestra viene passato il segnale ``delete_event'' (questo
+ * segnale viene passato Windows Manager di solito con l'opzione 'close'
+ * o con la barra del titolo (title bar)) noi chiediamo che la funzione
+ * delete_event() (definita sopra) venga invocata.
+ * Il dato passato come argomento alla funzione di ritorno &eacute; NULL
+ * ed &eacute; ignorato dalla funzione stessa. */
+ gtk_signal_connect (GTK_OBJECT (window), "delete_event",
+ GTK_SIGNAL_FUNC (destroy), NULL);
+
+ /* Qui connettiamo l'evento ``destroy'' al gestore del segnale.
+ * Questo evento accade quando noi chiamimo la funzione gtk_widget_destroy()
+ * sulla finestra o se ritorniamo TRUE dalla callback ``delete_event''. */
+ gtk_signal_connect (GTK_OBJECT (window), "destroy",
+ GTK_SIGNAL_FUNC (destroy), NULL);
+
+ /* Setta il bordo interno della finestra */
+ gtk_container_border_width (GTK_CONTAINER (window), 10);
+
+ /* Crea un nuovo bottone avente etichetta (label) uguale a ``Hello World'' */
+ button = gtk_button_new_with_label ("Hello World");
+
+ /* Quando il bottone riceve il segnale ``clicked'', invochera' la funzione
+ * hello() passando NULL come argomento della funzione. La funzione
+ * hello() &eacute; definita sopra. */
+ gtk_signal_connect (GTK_OBJECT (button), "clicked",
+ GTK_SIGNAL_FUNC (hello), NULL);
+
+ /* Questo far&agrave; s&igrave; che la finestra venga distrutta dalla chiamata
+ * gtk_widget_destroy(window) quando il bottone verr&agrave; premuto. Ancora,
+ * questo segnale (``destroy'') puo' arrivare da qui o dal windows
+ * manager */
+ gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
+ GTK_SIGNAL_FUNC (gtk_widget_destroy),
+ GTK_OBJECT (window));
+
+ /* Questo inserisce il bottone nella finestra
+ * (un contenitore GTK) */
+ gtk_container_add (GTK_CONTAINER (window), button);
+
+ /* Il passo finale &eacute; il mostrare questo nuovo widget appena creato */
+ gtk_widget_show (button);
+
+ /* e la finestra */
+ gtk_widget_show (window);
+
+ /* Tutte le applicazioni GTK devono avere la funzione gtk_main().
+ * Il controllo finisce qui e attende un evento (come la pressione
+ * di un tasto o l'evento di un mouse).
+ gtk_main ();
+
+ return 0;
+}
+</verb></tscreen>
+
+<sect1>Compilare hello World
+<p>
+Per compilare si utilizza :
+
+<tscreen><verb>
+gcc -Wall -g helloworld.c -o hello_world -L/usr/X11R6/lib \
+ -lglib -lgdk -lgtk -lX11 -lXext -lm
+</verb></tscreen>
+<p>
+Le librerie sopra (glib, gtk,...) devono essere tutte nel percorso predefinito
+delle librerie. Se cosi' non fosse aggiungi ``-L&lt;directory&gt;'' e il gcc
+guarder&agrave; in questa directory per cercare le librerie di cui necessita.
+Per esempio sul mio sistema debian-linux io ho dovuto aggiungere
+<tt>-L/usr/X11R6/lib</> per riuscire a far trovare le librerie di X11.
+
+<p>
+L'odine della dichiarazione delle librerie &eacute; significativo. Il linker
+sa quali funzioni di una libreria ha bisogno prima di processarla.
+
+<p>
+le librerie che noi linkiamo sono:
+<itemize>
+<item> la libreria glib (-lglib), contiene varie funzioni, ma solo
+g_print() &eacute; usato in questo esempio. GTK si appoggia a questa
+libreria cosi' devi sempre, comunque, linkarla. Vedi comunque la <ref
+id="sec_glib" name="glib"> sezione sulla glib per altri dettagli.
+<item>La libreria GDK (-lgdk), la copertura della X11.
+<item>La libreria GTK (-lgtk), la libreria dei widget, basata sulla GDK.
+<item>La libreria xlib(-lX11) la quale &egrave; usata dalla GDK.
+<item>La libreria Xext(-lXext). Questa contiene il codice per le pixmap a
+memoria condivisa e altre estensioni di X.
+<item>La libreria matematica (-lm). Questa &eacute; usata dalla GTK per vari scopi.
+</itemize>
+
+<sect1>Teoria dei segnali e delle funzioni di ritorno (callback)
+<p>
+Prima di guardare in dettaglio ``Hello World'', discuteremo gli eventi e le
+funzioni di ritorno. GTK &egrave; un toolkit guidato dagli eventi, il che significa
+che se ne star&agrave; a dorimire in gtk_main finch&eacute; non succeder&agrave; un evento ed il
+controllo passer&agrave; alla funzione appropriata.
+
+<p>
+Questo passaggio di controllo &egrave; fatto usando l'idea dei segnali. Quando succede un
+evento, come la pressione di un bottone del mouse, verr&agrave; emesso il segnale appropriato
+dal widget che &eacute; stato premuto.
+Questo &egrave; il modo in cui GTK fa molto del suo utile lavoro. Per fare s&igrave; che un
+bottone esegua una azione, noi prepareremo un gestore del segnale che catturi
+questi segnali e chiami la funzione corretta. Questo &egrave; fatto usando una
+funzione del tipo:
+
+<tscreen><verb>
+gint gtk_signal_connect (GtkObject *object,
+ gchar *name,
+ GtkSignalFunc func,
+ gpointer func_data);
+</verb></tscreen>
+
+<p>
+Dove, il primo argomento &egrave; il widget che emetter&agrave; il segnale, il secondo &egrave; il nome
+del segnale che si vuole catturare,il terzo &egrave; la funzione che verr&agrave; invocata
+quando il segnale sar&agrave; catturato e il quarto &egrave; il dato che potr essere passato a
+questa funzione.
+
+<p>
+La funzione specificata come terzo argomento &egrave; chiamata ``funzione di ritorno (callback)'',
+e dovrebbe essere della forma:
+
+<tscreen><verb>
+void callback_func(GtkWidget *widget, gpointer *callback_data);
+</verb></tscreen>
+<p>
+Dove il primo argomento sar&agrave; un puntatore al widget che emette il segnale e il
+secondo un puntatore al dato passato come ultimo argomento della funzione
+gtk_signal_connect() come descritto sopra.
+
+<p>
+Un'altra chiamata usata nell'esempio Hello World &egrave;:
+
+<tscreen><verb>
+gint gtk_signal_connect_object (GtkObject *object,
+ gchar *name,
+ GtkSignalFunc func,
+ GtkObject *slot_object);
+</verb></tscreen>
+<p>
+gtk_signal_connect_object() &egrave; uguale a gtk_signal_connect() eccetto che la
+funzione di callback usa solo un argomento, un puntatore ad un'oggetto GTK.
+Cosi' quando usa questa funzione per connettere i segnali, la callback
+potrebbe essere della forma :
+
+<tscreen><verb>
+ void callback_func (GtkObject *object);
+</verb></tscreen>
+<p>
+Dove object &egrave; di solito un widget. Noi, generalmente, non assegnamo una callback per
+gtk_signal_connect_object. Queste sono invocate ,usualmente, per chiamare
+una funzione GTK che accetta un widget singolo o un oggetto come argomento,
+come nel caso dell'esempio Hello World.
+
+Lo scopo di avere due funzioni per connettere i segnali &egrave; semplicemente quello di
+permettere alla funzione di callback di avere un numero di argomenti diverso.
+Molte funzioni della libreria GTK accettano solo un singolo puntatore ad un widget
+GTK come argomento, cos&igrave; per queste si pu&ograve; usare la funzione gtk_signal_connect_object(),
+mentre per le vostre funzioni potreste aver bisogno di passare dati supplementari alle
+funzioni di ritorno.
+
+<sect1>Attraverso Hello World passo per passo
+<p>
+Ora che conosciamo la teoria che vi &egrave; dietro, iniziamo ad essere pi&ugrave; chiari
+camminando attraverso il programma di Hello World.
+
+<p>
+Questa &egrave; la funzione di callback che sar&agrave; invocata quando il bottone &egrave; clickato.
+Noi, in questo esempio, ignoriamo sia il widget che i dati passati, ma non &egrave;
+difficile farci invece qualcosa. Il prossimo esempio user&agrave; l'argomento passato
+per dire quale bottone &egrave; stato premuto.
+
+<tscreen><verb>
+void hello (GtkWidget *widget, gpointer *data)
+{
+ g_print ("Hello World\n");
+}
+</verb></tscreen>
+
+<p>
+Questa callback &egrave; un po' speciale. L'evento ``delete'' avviene quanto il Window Manager
+manda questo evento all'applicazione. Qui abbiamo una scelta da fare: cosa fare di questo evento.
+Possiamo ignorarlo, creare qualche tipo di risposta, o semplicemente terminare
+l'applicazione.
+
+Il valore che si restituisce in questa callback fa s&igrave; che la GTK sappia cosa fare.
+Restituire FALSE significa che noi non vogliamo che il segnale ``destroy'' sia emesso,
+quindi far s&igrave; che la nostra applicazione continui a procedere. Ritornare TRUE vuole dire
+far emettere il segnale ``destroy'' il quale chiamer&agrave; il gestore del segnale ``destroy''
+(o meglio : la nostra funzione di callback).
+
+<tscreen><verb>
+ gint delete_event(GtkWidget *widget, gpointer data)
+ {
+ g_print ("delete event occured\n");
+
+ return (FALSE);
+ }
+</verb></tscreen>
+
+<p>
+Questa &egrave; un'altra funzione di callback la quale fa uscire dal programma chiamando
+gtk_main_quit(). Non c'&egrave; molto da dire al riguardo, &egrave; abbastanza auto-esplicativa.
+
+<tscreen><verb>
+void destroy (GtkWidget *widget, gpointer *data)
+{
+ gtk_main_quit ();
+}
+</verb></tscreen>
+<p>
+Ritengo che conosciate la funzione main()... si, come tutte le altre applicazioni
+anche le applicazioni GTK hanno questa funzione.
+
+<tscreen><verb>
+int main (int argc, char *argv[])
+{
+</verb></tscreen>
+
+<p>
+Questa parte dichiara un puntatore ad una struttura di tipo GtkWidget. Queste sono
+usate sotto per creare una finestra ed un bottone.
+
+<tscreen><verb>
+ GtkWidget *window;
+ GtkWidget *button;
+</verb></tscreen>
+<p>
+Qui vi &egrave; ancora la nostra gtk_init. Come prima questa inizializza il toolkit e
+analizza gli argomenti trovati nella linea di comando_ Tutti gli argomenti riconosciuti
+nella linea di comando sono rimossi dalla lista degli argomenti e vengono cos&igrave; modificati
+argc e argv per far s&igrave; che sembri che questi non siano mai esisitie permettere alla
+tua applicazione di analizzare gli argomenti rimasti.
+
+<tscreen><verb>
+ gtk_init (&amp;argc, &amp;argv);
+</verb></tscreen>
+<p>
+Crea una nuova finestra. Questo viene spiegato abbastanza approfonditamente pi&ugrave; avanti.
+Viene allocata la memoria per la struttura GtkWidget *window cos&igrave; che si punti ad una struttura
+valida. In questo modo si predispone la nuova finestra, ma non la si visualizza fino a sotto dove, quasi
+alla fine del nostro programma, invochiamo gtk_widget_show(window).
+<tscreen><verb>
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+</verb></tscreen>
+<p>
+Questo &egrave; un esempio di come connettere un gestore dei segnali con un oggetto, in questo
+caso la finestra. Qui viene catturato il segnale ``destroy''. Questo &egrave; emesso quando usiamo
+il Window Manager per uccidere la finestra (e noi restituiamo TRUE dal gestore di ``delete_event'')
+o quando emettiamo la chiamata gtk_widget_destroy() passando l'oggetto finestra
+come oggetto da distruggere. Sistemando le cose cos&igrave;, trattiamo entrambi i casi con una singola
+chiamata. Qui &egrave; giusto invocare la funzione destroy() definita sopra con NULL come argomento,
+la quale termina l'applicazione GTK per noi.
+Questo ci permetter&agrave; di utilizzare il Window Manager per uccidere il programma.
+<!-- fino a qui -->
+<p>
+GTK_OBJECT e GTK_SIGNAL_FUNC sono macro che interpretano il casting e il controllo di tipo per noi,
+cos&igrave; da rendere piu' leggibile il codice.
+
+<tscreen><verb>
+ gtk_signal_connect (GTK_OBJECT (window), "destroy",
+ GTK_SIGNAL_FUNC (destroy), NULL);
+</verb></tscreen>
+<p>
+La prossima funzione &egrave; usata per settare un attributo di un oggetto contenitore. Questo
+sistema la finestra cos&igrave; da avere un'area vuota all'interno della finestrra larga 10 pixel dove
+non potr&agrave; andare nessun widget. Ci sono altre funzioni simili che vedremo nella
+sezione <ref id="sec_setting_widget_attributes" name="Settare gli attributi del Widget.">
+
+<p>
+E ancora, GTK_CONTAINER &egrave; una macro per interpretare il casting di tipo.
+
+<tscreen><verb>
+ gtk_container_border_width (GTK_CONTAINER (window), 10);
+</verb></tscreen>
+<p>
+Questa chiamata crea un nuovo bottone. Alloca spazio in memoria per un nuovo GtkWidget,
+inizializzandolo e facendo s&igrave; che il puntatore a bottone punti ad esso.
+Quando sar&agrave; visualizzato, avr&agrave; etichetta ``Hello World''.
+
+<tscreen><verb>
+ button = gtk_button_new_with_label ("Hello World");
+</verb></tscreen>
+<p>
+Qui prendiamo il bottone e gli facciamo fare qualcosa di utile.
+Gli colleghiamo un un gestore di segnale in modo che quando emetter&agrave; il
+segnale ``clicked'', verr&agrave; invocata la nostra funzione hello(). Il dato passato
+alla funzione &egrave; ignorato, cosicch&eacute; alla funzione di callback hello() passiamo
+semplicemente NULL. Evidentemente il segnale ``clicked'' viene emesso quando
+premiamo il bottone con il mouse.
+
+<tscreen><verb>
+ gtk_signal_connect (GTK_OBJECT (button), "clicked",
+ GTK_SIGNAL_FUNC (hello), NULL);
+</verb></tscreen>
+<p>
+Usiamo questo bottone anche per uscire dal programma. Questo illustrera'
+come il segnale ``destroy'' pu&ograve; arrivare sia dal Window Manager che dal nostro programma.
+Quando il bottone &egrave; ``clicked'', come sopra, chiamera' la funzione di callback
+hello() e poi questa nell'ordine in cui sono definite. Si possono avere
+tante funzioni di callback, quante sono necessarie, e saranno eseguite nell'ordine in cui
+sono connesse. Visto che la funzione gtk_widget_destroy() accetta come argomento solo un
+GtkWidget *widget, usiamo la funzione gtk_signal_connect_object()
+al posto della semplice gtk_signal_connect().
+
+<tscreen><verb>
+ gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
+ GTK_SIGNAL_FUNC (gtk_widget_destroy),
+ GTK_OBJECT (window));
+</verb></tscreen>
+<p>
+Questa &eacute; una chiamata di ``impacchettamento'' che sar&agrave; spiegata pi&ugrave; avanti.
+Ma &egrave; molto facile da capire. Semplicemente dice alla libreria GTK che il
+bottone &egrave; da mettere nella finestra dove sar&agrave; visualizzato.
+
+<tscreen><verb>
+ gtk_container_add (GTK_CONTAINER (window), button);
+</verb></tscreen>
+<p>
+A questo punto abbiamo predisposto tutto quello che ci eravamo prefissati.
+Con tutti i gestori di segnale a posto e il bottone messo nella finestra in cui
+dovrebbe essere, possiamo dire a GTK di mostrare gli oggetti sullo schermo.
+L'oggetto finestra viene mostrato per ultimo cos&igrave; che la finestra completa di tutti
+i suoi oggetti sar&agrave; mostrata in una volta sola, invece di vedere
+prima la finestra spoglia e poi la comparsa del bottone all'interno di essa.
+Per quanto, con questi semplici esempi, questo l'avrai gi&agrave; notato.
+<tscreen><verb>
+ gtk_widget_show (button);
+
+ gtk_widget_show (window);
+</verb></tscreen>
+<p>
+E naturalmente chiamiamo gtk_main(), la quale aspetta l'arrivo degli eventi
+dal server X e chiamer&agrave; l'oggetto interessato per fargli emettere il segnale
+adeguato.
+<tscreen><verb>
+ gtk_main ();
+</verb></tscreen>
+E il return finale. Il controllo ritorna qui dopo che viene invocata gtk_quit().
+
+<tscreen><verb>
+ return 0;
+</verb></tscreen>
+<p>
+Ora, quando premiamo il bottone del mouse su un bottone GTK, questo oggetto
+emette il segnale ``clicked''. Per poter utilizzare queste informazioni, il nostro
+programma predispone un gestore di segnale per catturare quel segnale, il quale
+avvia la funzione da noi scelta. Nel nostro esempio, quando il bottone creato viene
+clickato , la funzione hello() &egrave; invocata con un argomento NULL, dopoodich&eacute;
+viene invocato il successivo gestore di questo segnale. Questo chiama la funziona
+gtk_widget_destroy(), passandole l'oggetto-finestra (window) come argomento, che
+distrugger&agrave; la finestra. Questo fa s&igrave; che la finestra emetta il segnale
+``destroy'' che viene catturato e che fa invocare la funzione di ritorno
+destroy(), che semplicemente esce dal programma GTK.
+
+<p>
+Un'altro modo in cui possono andare le cose &egrave; l'uso del window manager per uccidere
+la finestra. Questo causera' l'emissione del segnale ``delete_event'' che
+automaticamente chiamer&agrave; il gestore del segnale ``delete_event''. Se qui noi
+restituiamo il valore FALSE, la finestra non verr&agrave; toccata e tutto proceder&agrave; come
+se nulla fosse successo. Dare invece il valore TRUE causer&agrave; l'emissione da parte
+di GTK del segnale ``destroy'' il quale, a sua volta, invocher&agrave; la callback ``destroy'',
+uscendo dall'applicazione.
+
+<p>
+Nota che questi segnali non sono gli stessi del sistema Unix e che non sono
+implementati usando quei segnali, anche se la terminologia &egrave; praticamente identica.
+
+<sect>Proseguiamo
+<p>
+<sect1>Tipi di Dato
+<p>
+Ci sono alcune cose che avrete probabilmente notato nei precedenti esempi che
+hanno bisogno di una spiegazione. I gint, gchar ecc. che vedete sono tipi di dato
+riferiti rispettivamente a int e char. Questo viene fatto per rimediare alla brutta
+dipendenza dalle dimensioni di semplici tipi di dato quando si fanno dei calcoli.
+Un buon esempio &egrave; ``gint32'' il quale sar&agrave; un tipo di dato riferito ad un intero a
+32 bit per tutte le piattaforme x86 e ad un 64 bit per gli alpha.
+I tipi di dato sono ben spiegati pi&ugrave; avanti ed intuitivi. Sono definiti in
+glib/glib.h (il quale viene incluso da gtk.h).
+
+<p>
+Noterete anche la possibilit&agrave; di utilizzare un GtkWidget quando la funzione richiede
+un GtkObject. GTK &egrave; una libreria orienta agli oggetti ed un widget &egrave; un oggetto.
+
+<sect1>Altri Dettagli sui Segnali
+<p>
+Diamo un'altra occhiata alla dichiarazione della funzione gtk_signal_connect.
+
+<tscreen><verb>
+gint gtk_signal_connect (GtkObject *object, gchar *name,
+ GtkSignalFunc func, gpointer func_data);
+</verb></tscreen>
+Notate il valore di ritorno definito come gint? questo &egrave; un identificatore per
+la tua funzione di callback. Come detto sopra, si possono avere pi&ugrave; funzioni di
+ritorno per ogni segnale e per ogni ogetto a seconda delle necessit&agrave;. ed ognuna sar&agrave;
+eseguita in sequenza, nell'ordine in cui sono state collegate. Questo identificatore
+ti permette di rimuovere una funzione dalla lista delle funzioni di ritorno tramite
+la seguente chiamata
+<tscreen><verb>
+void gtk_signal_disconnect (GtkObject *object,
+ gint id);
+</verb></tscreen>
+Cos&igrave; passando il widget da cui vuoi rimuovere il gestore di segnale, e
+l'identificativo restituito da una delle funzioni signal_connect, puoi rimuovere
+il gestore di segnale che desideri da quella del widget.
+
+<p>
+Un'altra funzione per rimuovere tutti i segnali di un widget in una volta sola &egrave;:
+
+<tscreen><verb>
+gtk_signal_handlers_destroy (GtkObject *object);
+</verb></tscreen>
+<p>
+Questa chiamata &egrave; abbastanza auto esplicativa. Semplicemente rimuove tutti i segnali
+collegati al widget che passi alla funzione come argomento.
+
+<sect1>Miglioriamo Hello World
+
+<p>
+Diamo un'occhiata ad una migliorata versione di Hello World con altri esempi sulle
+callback. Questo anche ci introdurr&agrave; al nostro prossimo argomento,
+l'impacchettamento dei widget.
+
+<tscreen><verb>
+#include <gtk/gtk.h>
+
+/* La nostra funzione di callback migliorata. I dati passati a questa
+ * vengono stampati su stdout. */
+void callback (GtkWidget *widget, gpointer *data)
+{
+ g_print ("Hello again - %s was pressed\n", (char *) data);
+}
+
+/* Un'altra callback */
+void delete_event (GtkWidget *widget, gpointer *data)
+{
+ gtk_main_quit ();
+}
+
+int main (int argc, char *argv[])
+{
+ /* GtkWidget e' il tipo di dato per i widget */
+ GtkWidget *window;
+ GtkWidget *button;
+ GtkWidget *box1;
+
+ /* Questa funzione e' invocata in tutte le applicazioni GTK, gli
+ argomenti sono analizzati e restituiti all'applicazione. */
+ gtk_init (&amp;argc, &amp;argv);
+
+ /* Crea una nuova finestra */
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+ /* Questa e' una nuova chiamata. Assegna "Hello Buttons" come titolo
+ della nostra finestra */
+ gtk_window_set_title (GTK_WINDOW (window), "Hello Buttons!");
+
+ /* Qui settiamo il gestore per il segnale "delete_event" che
+ immediatamente esce dalla applicazione.
+ gtk_signal_connect (GTK_OBJECT (window), "delete_event",
+ GTK_SIGNAL_FUNC (delete_event), NULL);
+
+
+ /* predispone il bordo della finestra */
+ gtk_container_border_width (GTK_CONTAINER (window), 10);
+
+ /* creiamo una scatola dove mettere tutti i widget. Questa &egrave; descritta
+ dettagliatamente nella sezione "packing". La scatola non &egrave; realmente
+ visibile, &egrave; solamente usata per sistemare i widget. */
+ box1 = gtk_hbox_new(FALSE, 0);
+
+ /* Inseriamo la scatola nella finestra */
+ gtk_container_add (GTK_CONTAINER (window), box1);
+
+ /* Creiamo un nuovo bottone con etichetta "Button 1" */
+ button = gtk_button_new_with_label ("Button 1");
+
+ /* Quando il bottone e' premuto, noi invocheremo la funzione di callback,
+ con un puntatore alla stringa "button 1" come proprio argomento) */
+ gtk_signal_connect (GTK_OBJECT (button), "clicked",
+ GTK_SIGNAL_FUNC (callback), (gpointer) "button 1");
+
+ /* invece di aggiungerlo alla finestra, lo inseriamo nella scatola invisibile,
+ la quale e' stata inserita nella finstra. */
+ gtk_box_pack_start(GTK_BOX(box1), button, TRUE, TRUE, 0);
+
+ /* Ricordati sempre questo passo. Dice a GTK che la preparazione di questo
+ bottone e' finita e che quindi puo' essere mostrato. */
+ gtk_widget_show(button);
+
+ /* Facciamo la stessa cosa per il secondo bottone. */
+ button = gtk_button_new_with_label ("Button 2");
+
+ /* Chiamiamo la stessa funzione ma passandogli un argomento differente,
+ gli passiamo un puntatore alla stringa "button 2" */
+ gtk_signal_connect (GTK_OBJECT (button), "clicked",
+ GTK_SIGNAL_FUNC (callback), (gpointer) "button 2");
+
+ gtk_box_pack_start(GTK_BOX(box1), button, TRUE, TRUE, 0);
+
+ /* L'ordine nel quale i bottoni sono visualizzati non e' realmente importante,
+ ma io ti raccomando di mostrare per ultima la finestra cosi' che tutto
+ sia visualizzato in una volta sola */
+ gtk_widget_show(button);
+
+ gtk_widget_show(box1);
+
+ gtk_widget_show (window);
+
+ /* e ora ci mettiamo in gtk_main e aspettiamo che il diverimento inizi.
+ gtk_main ();
+
+ return 0;
+}
+</verb></tscreen>
+<p>
+Compilate questo programma usando gli stessi argomenti di link del nostro primo
+esempio. Noterete che questa volta non c'&egrave; un modo semplice per uscire dal programma,
+si deve usare il nostro window manager o la linea di comando per uccidere
+l'applicazione.
+Un buon esercizio per il lettore &egrave; quello di inserire un tezo bottone ``quit'' che
+faccia uscire dal programma. Potete anche divertirvi con le opzioni di
+gtk_box_pack_start() mentre leggete il prossimo capitolo. Provate a ridimensionare
+la finestra ed a osservare cosa succede.
+
+<p>
+Solo una piccola nota, c'&egrave; un'altra definizione di gtk_window_new() -
+GTK_WINDOW_DIALOG. Questa interagisce con il window manager in un modo un po'
+diverso, e dovrebbe essere usata per finestre temporanee.
+
+<sect>Come ``Impacchettare'' i Widget
+<p>
+Nel momento in cui si crea un'applicazione, normalmente si avr&agrave; la necessit&agrave; di mettere pi&ugrave;
+di un unico bottone all'interno di una finestra. Il nostro primo esempio ``Hello World''
+usava un solo oggetto, cosicch&eacute; abbiamo potuto usare semplicemente una chiamata
+a gtk_container_add per impacchettare il widget nella finestra. Quando invece si vuole
+inserire pi&ugrave; di un unico widget in una finestra, come si fa a controllare dove vengono
+posizionati i propri oggetti? E' qui che entra in gioco il meccanismo dell'``impacchettamento''.
+<sect1>Teoria delle Scatole per Impacchettamento
+<p>
+La maggior parte dell'impacchettamento viene effettuata creando delle scatole
+come nell'esempio pi&ugrave; sopra. Le scatole sono dei contenitori invisibili di
+widget che possiamo usare per imballarci i nostri oggetti e che esistono in
+due variet&agrave;: in particolare si possono avere scatole orizzontali (hbox) e
+verticali (vbox).
+Quando si impacchentano degli oggetti in una scatola orizzontale, gli oggetti vengono inseriti
+orizzontalmente da sinistra a destra oppure da destra a sinistra a seconda della
+chiamata di funzione che si usa. In una scatola verticale, gli oggetti vengono inseriti
+dall'alto in basso o viceversa. Si pu&ograve; usare qualsiasi combinazione di scatole
+all'interno o a fianco di altre scatole, fino ad ottenere l'effetto desiderato.
+<p>
+Per creare una nuova scatola orizzontale, si usa una chiamata a gtk_hbox_new(), mentre
+per le scatole verticali si usa gtk_vbox_new(). Per inserire i widget
+all'interno di questi contenitori si usano le funzioni gtk_box_pack_start() e
+gtk_box_pack_end(). La funzione gtk_box_pack_start() comincer&agrave; dall'alto verso il
+basso in una vbox e da sinistra a destra in una hbox. gtk_box_pack_end() fa l'opposto,
+impacchettando dal basso verso l'alto in una vbox e da destra a sinistra in una hbox.
+Queste funzioni ci permettono di giustificare a destra o a sinistra i nostri
+widget, e possono essere mescolate in qualsiasi modo per ottenere l'effetto desiderato.
+Useremo gtk_box_pack_start() nella maggior parte dei nostri esempi. Un oggetto pu&ograve;
+essere costituito da un altro contenitore o da un oggetto grafico. Infatti, molti
+oggetti grafici sono a loro volta dei contenitori, compreso il bottone, anche se
+tipicamente all'interno del bottone mettiamo solo una etichetta.
+<p>
+
+Usando queste chiamate, GTK riesce a capire dove si vogliono piazzare i propri
+widget, in modo di essere poi in grado di effettuare il ridimensionamento
+automatico e altre cose interessanti. Esiste poi un insieme di opzioni che riguardano
+il modo in cui i propri oggetti grafici dovrebbero essere impacchettati. Come
+si pu&ograve; immaginare, questo metodo d&agrave; una buona flessibilit&agrave; nella creazione e
+nella disposizione dei propri widget.
+<sect1>Dettagli sulle Scatole
+<p>
+A causa di questa flessibilit&agrave;, le scatole per impacchettamento del GTK
+possono, di primo acchito, creare un po' di disorientamento. Sono infatti disponibili
+molte opzioni, e non &egrave; immediato il modo in cui si combinano l'una con l'altra.
+Alla fine per&ograve;, si possono ottenere essenzialmente cinque diversi stili.
+
+<p>
+<?
+<IMG ALIGN="center" SRC="packbox1.gif"
+VSPACE="15" HSPACE="10" ALT="Box Packing Example Image" WIDTH="528"
+HEIGHT="235">
+>
+
+
+Ogni linea contiene una scatola orizzontale (hbox) con diversi bottoni.
+La chiamata a gtk_box_pack &egrave; una scorciatoia per la chiamata di impacchettamento
+di ognuno dei bottoni nella hbox. Ognuno dei bottoni viene impacchettato nella
+hbox nello stesso modo (cio&egrave;, con gli stessi argomenti per la funzione gtk_box_pack_start ()).
+<p>
+Questa &egrave; la dichiarazione della funzione gtk_box_pack_start.
+
+<tscreen><verb>
+void gtk_box_pack_start (GtkBox *box,
+ GtkWidget *child,
+ gint expand,
+ gint fill,
+ gint padding);
+</verb></tscreen>
+Il primo argomento &egrave; la scatola nella quale si stanno inscatolando i
+widget, il secondo &egrave; il widget stesso. Gli oggetti per ora saranno
+bottoni, quindi quello che faremo sar&agrave; impacchettare bottoni in scatole.
+<p>
+L'argomento ``expand'' in gtk_box_pack_start() o gtk_box_pack_end() controlla
+se gli oggetti devono essere sistemati nella scatola in modo da riempire tutto
+lo spazio in diponibile presente nella scatola, in modo che la scatola si espanda fino
+ad occupare tutta l'area assegnatale (valore TRUE).
+La scatola pu&ograve; anche essere rimpiciolita in modo da contenere esattamente i
+widget (valore FALSE). Assegnare a expand il valore FALSE permette di giustificare
+a destra o sinistra i propri oggetti. In caso contrario, tutti gli ogetti si espandono
+fino ad adattarsi alla scatola, e il medesimo effetto si pu&ograve; ottenere usando solo una
+delle funzioni gtk_box_pack_start o pack_end.
+<p>
+L'argomento ``fill'' delle funzioni gtk_box_pack stabilisce se lo spazio disponibile
+nella scatola deve essere allocato agli oggetti (TRUE) o se deve essere mantenuto
+come riempimento attorno a questi oggetti (FALSE). Questo argomento ha effetto
+solo se a expand &egrave; assegnato il valore TRUE.
+<p>
+Quando si crea una nuova scatola, la funzione ha questo aspetto:
+
+<tscreen><verb>
+GtkWidget * gtk_hbox_new (gint homogeneous,
+ gint spacing);
+</verb></tscreen>
+
+L'argomento homogeneous di gtk_hbox_new (la stesso per gtk_vbox_new)
+determina se ogni oggetto nella scatola deve avere la stessa dimensione (cio&egrave;
+la stessa ampiezza in una hbox o la stessa altezza in una vbox). Se &egrave; settato,
+l'argomento expand delle routine gtk_box_pack &egrave; sempre attivato.
+<p>
+Qual &egrave; la differenza fra la spaziatura (che &egrave; stabilita quando la scatola
+viene creata) e il riempimento (che viene stabilito quando gli elementi vengono
+impacchettati)? La spaziatura viene inserita fra gli oggetti, mentre il
+riempimento viene aggiuno a ciascuno dei lati dell'oggetti. La seguente figura
+dovrebbe chiarire meglio questo punto:
+
+<?
+<IMG ALIGN="center" SRC="packbox2.gif"
+VSPACE="15" HSPACE="10" ALT="Box Packing Example Image" WIDTH="509"
+HEIGHT="213">
+>
+
+
+Di seguito &egrave; riportato il codice usato per creare le immagini precedenti.
+L'ho commentato in modo piuttosto pesante, in modo che non dovreste avere
+problemi nel seguirlo. Compilatelo voi stessi e preovate a giocarci un po'.
+
+<sect1>Programma Dimostrativo di Impacchettamento
+<p>
+
+<tscreen><verb>
+#include "gtk/gtk.h"
+
+void
+delete_event (GtkWidget *widget, gpointer *data)
+{
+ gtk_main_quit ();
+}
+
+/* Costruisco una nuova hbox riempita con bottoni-etichette. Gli
+ * argomenti per le varabili che ci interessano sono passati
+ * in questa funzione. Non mostriamo la scatola, ma mostriamo
+ * tutto quello che c'&egrave; dentro. */
+GtkWidget *make_box (gint homogeneous, gint spacing,
+ gint expand, gint fill, gint padding)
+{
+ GtkWidget *box;
+ GtkWidget *button;
+ char padstr[80];
+
+ /* costruisco una nuova hbox con i valori appropriati di
+ * homogeneous e spacing */
+ box = gtk_hbox_new (homogeneous, spacing);
+
+ /* costruisco una serie di bottoni con i valori appropriati */
+ button = gtk_button_new_with_label ("gtk_box_pack");
+ gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
+ gtk_widget_show (button);
+
+ button = gtk_button_new_with_label ("(box,");
+ gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
+ gtk_widget_show (button);
+
+ button = gtk_button_new_with_label ("button,");
+ gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
+ gtk_widget_show (button);
+
+ /* costruisco un bottone con l'etichetta che dipende dal valore di
+ * expand. */
+ if (expand == TRUE)
+ button = gtk_button_new_with_label ("TRUE,");
+ else
+ button = gtk_button_new_with_label ("FALSE,");
+
+ gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
+ gtk_widget_show (button);
+
+ /* Questo &egrave; la stessa cosa della creazione del bottone per "expand"
+ * pi&ugrave; sopra, ma usa la forma breve. */
+ button = gtk_button_new_with_label (fill ? "TRUE," : "FALSE,");
+ gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
+ gtk_widget_show (button);
+
+ sprintf (padstr, "%d);", padding);
+
+ button = gtk_button_new_with_label (padstr);
+ gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
+ gtk_widget_show (button);
+
+ return box;
+}
+
+int
+main (int argc, char *argv[])
+{
+ GtkWidget *window;
+ GtkWidget *button;
+ GtkWidget *box1;
+ GtkWidget *box2;
+ GtkWidget *separator;
+ GtkWidget *label;
+ GtkWidget *quitbox;
+ int which;
+
+ /* La nostra inizializzazione, non dimenticatela! :) */
+ gtk_init (&amp;argc, &amp;argv);
+
+ if (argc != 2) {
+ fprintf (stderr, "uso: packbox num, dove num &egrave; 1, 2, o 3.\n");
+ /* questo fa solo un po' di pulizia in GTK, ed esce con un valore 1. */
+ gtk_exit (1);
+ }
+
+ which = atoi (argv[1]);
+
+ /* Creiamo la nostra finestra */
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+ /* Ci si dovrebbe sempre ricordare di connettere il segnale di destroy
+ * alla finestra principale. Ci&ograve; &egrave; molto importante per avere un funzionamento
+ * corretto dal punto di vista intuitivo */
+ gtk_signal_connect (GTK_OBJECT (window), "delete_event",
+ GTK_SIGNAL_FUNC (delete_event), NULL);
+ gtk_container_border_width (GTK_CONTAINER (window), 10);
+
+ /* Creiamo una scatola verticale (vbox) in cui impacchettare quelle
+ * orizzontali. Questo ci permette di impilare le scatole orizzontali
+ * piene di bottoni una sull'altra in questa vbox. */
+
+ box1 = gtk_vbox_new (FALSE, 0);
+
+ /* Decide quale esempio si deve mostrare. Corrispondono alle figure precedenti */
+ switch (which) {
+ case 1:
+ /* creare una nuova etichetta. */
+ label = gtk_label_new ("gtk_hbox_new (FALSE, 0);");
+
+ /* allineare l'etichetta al lato sinistro. Discuteremo questa e altre
+ * funzioni nella sezione dedicata agli attributi degli oggetti grafici. */
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
+
+ /* Impacchettare l'etichetta nella scatola verticale (vbox box1).
+ * Ricordare che gli oggetti che vengono aggiunti in una vbox vengono
+ * impacchettati uno sopra all'altro in ordine. */
+ gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 0);
+
+ /* mostrare l'etichetta */
+ gtk_widget_show (label);
+
+ /* chiamare la nostra funzione make_box - homogeneous = FALSE,
+ * spacing = 0, expand = FALSE, fill = FALSE, padding = 0 */
+ box2 = make_box (FALSE, 0, FALSE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
+ gtk_widget_show (box2);
+
+ /* chiamare la nostra funzione make_box - homogeneous = FALSE, spacing = 0,
+ * expand = FALSE, fill = FALSE, padding = 0 */
+ box2 = make_box (FALSE, 0, TRUE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
+ gtk_widget_show (box2);
+
+ /* Gli argomenti sono: homogeneous, spacing, expand, fill, padding */
+ box2 = make_box (FALSE, 0, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
+ gtk_widget_show (box2);
+
+ /* Questo crea un separatore. Li conosceremo meglio in seguito,
+ * comunque sono piuttosto semplici. */
+ separator = gtk_hseparator_new ();
+
+ /* Impacchetta il separatore nella vbox. Ricordare che stiamo impacchettando
+ * ognuno di questi oggetti in una vbox, cosicch&eacute; essi verranno
+ * impacchettati verticalmente. */
+ gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);
+ gtk_widget_show (separator);
+
+ /* crea un'altra nuova etichetta e mostrala. */
+ label = gtk_label_new ("gtk_hbox_new (TRUE, 0);");
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
+ gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ /* Gli argomenti sono: homogeneous, spacing, expand, fill, padding */
+ box2 = make_box (TRUE, 0, TRUE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
+ gtk_widget_show (box2);
+
+ /* Gli argomenti sono: homogeneous, spacing, expand, fill, padding */
+ box2 = make_box (TRUE, 0, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
+ gtk_widget_show (box2);
+
+ /* ancora un nuovo separatore. */
+ separator = gtk_hseparator_new ();
+ /* Gli ultimi 3 argumenti per gtk_box_pack_start sono: expand, fill, padding. */
+ gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);
+ gtk_widget_show (separator);
+
+ break;
+
+ case 2:
+
+ /* creare una nuova etichetta, ricordare che box1 &egrave; la vbox creata
+ * vicino all'inizio di main() */
+ label = gtk_label_new ("gtk_hbox_new (FALSE, 10);");
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
+ gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ /* Gli argomenti sono: homogeneous, spacing, expand, fill, padding */
+ box2 = make_box (FALSE, 10, TRUE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
+ gtk_widget_show (box2);
+
+ /* Gli argomenti sono: homogeneous, spacing, expand, fill, padding */
+ box2 = make_box (FALSE, 10, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
+ gtk_widget_show (box2);
+
+ separator = gtk_hseparator_new ();
+ /* Gli ultimi tre arcomenti di gtk_box_pack_start sono: expand, fill, padding. */
+ gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);
+ gtk_widget_show (separator);
+
+ label = gtk_label_new ("gtk_hbox_new (FALSE, 0);");
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
+ gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ /* Gli argomenti sono: homogeneous, spacing, expand, fill, padding */
+ box2 = make_box (FALSE, 0, TRUE, FALSE, 10);
+ gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
+ gtk_widget_show (box2);
+
+ /* Gli argomenti sono: homogeneous, spacing, expand, fill, padding */
+ box2 = make_box (FALSE, 0, TRUE, TRUE, 10);
+ gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
+ gtk_widget_show (box2);
+
+ separator = gtk_hseparator_new ();
+ /* Gli ultimi tre argomenti di gtk_box_pack_start sono: expand, fill, padding. */
+ gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);
+ gtk_widget_show (separator);
+ break;
+
+ case 3:
+
+ /* Questo dimostra la possibilit&agrave; di usare use gtk_box_pack_end() per
+ * giustificare gli oggetti a destra. Per prima cosa creiamo una
+
+ * nuova scatola come prima. */
+ box2 = make_box (FALSE, 0, FALSE, FALSE, 0);
+ /* creiamo l'etichetta che sar&agrave; aggiunta alla fine. */
+ label = gtk_label_new ("end");
+ /* impacchettiamola usando gtk_box_pack_end(), cos&igrave; che viene inserita
+ * sul lato destro della hbox creata nella chiamata a the make_box(). */
+ gtk_box_pack_end (GTK_BOX (box2), label, FALSE, FALSE, 0);
+ /* mostriamo l'etichetta. */
+ gtk_widget_show (label);
+
+ /* impacchettiamo box2 in box1 (the vbox, ricordate? :) */
+ gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
+ gtk_widget_show (box2);
+
+ /* un separatore per il fondo */
+ separator = gtk_hseparator_new ();
+ /* Questo assegna esplicitamente al separatore l'ampiezza di 400 pixel
+ * e l'altezza di 5 pixel. Ci&ograve; fa s&igrave; che la hbox che abbiamo creato sia
+ * anche essa larga 400 pixel, e che l'etichetta finale sia separata dalle
+ * altre etichette nella hbox. In caso contrario, tutti gli oggetti nella
+ * hbox sarebbero impacchettati il pi&ugrave; vicino possibile. */
+ gtk_widget_set_usize (separator, 400, 5);
+ /* impacchetta il separatore nella vbox (box1) creata vicino all'inizio
+ * di main() */
+ gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);
+ gtk_widget_show (separator);
+ }
+
+ /* Creare un'altra nuova hbox.. ricordate che ne possiamo usare quante ne vogliamo! */
+ quitbox = gtk_hbox_new (FALSE, 0);
+
+ /* Il nostro bottone di uscita. */
+ button = gtk_button_new_with_label ("Quit");
+
+
+ /* Configuriamo il segnale per distruggere la finestra. Ricordate che
+ * ci&ograve; mander&agrave; alla finestra il segnale "destroy", che verr&agrave; catturato
+ * dal nostro gestore di segnali che abbiamo definito in precedenza. */
+ gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
+ GTK_SIGNAL_FUNC (gtk_widget_destroy),
+ GTK_OBJECT (window));
+ /* impacchetta il bottone in quitbox.
+ * Gli ultimi tre argomenti di gtk_box_pack_start sono: expand, fill, padding. */
+ gtk_box_pack_start (GTK_BOX (quitbox), button, TRUE, FALSE, 0);
+ /* impacchetta quitbox nella vbox (box1) */
+ gtk_box_pack_start (GTK_BOX (box1), quitbox, FALSE, FALSE, 0);
+
+ /* impacchetta la vbox (box1), che ora contiene tutti i nostri oggetti,
+ * nella finestra principale. */
+ gtk_container_add (GTK_CONTAINER (window), box1);
+
+ /* e mostra tutto quel che rimane */
+ gtk_widget_show (button);
+ gtk_widget_show (quitbox);
+
+ gtk_widget_show (box1);
+ /* Mostriamo la finestra alla fine in modo che tutto spunti fuori assieme. */
+ gtk_widget_show (window);
+
+ /* E, naturalmente, la nostra funzione main. */
+ gtk_main ();
+
+ /* Il controllo ritorna a questo punto quando viene chiamata gtk_main_quit(),
+ * ma non quando si usa gtk_exit. */
+
+ return 0;
+}
+</verb></tscreen>
+
+<p>
+<sect1>Impacchettamento con uso di Tabelle
+<p>
+Diamo ora un'occhiata ad un altro modo di impacchettare - le Tabelle.
+In certe situazioni, possono risultare estremamente utili.
+
+Usando le tabelle, creiamo una griglia in cui possiamo piazzare gli oggetti.
+Gli oggetti possono occupare tanti spazi quanti ne specifichiamo.
+
+Naturalmente, la prima cosa da vedere &egrave; la funzione gtk_table_new:
+
+<tscreen><verb>
+GtkWidget* gtk_table_new (gint rows,
+ gint columns,
+ gint homogeneous);
+</verb></tscreen>
+<p>
+Il primo argomento rappresenta il numero di righe da mettere nella tabella,
+mentre il secondo &egrave; ovviamente il numero di colonne.
+
+L'argomento homogeneous ha a che fare con il modo in cui le caselle della tabella
+sono dimensionate. Se homogeneous ha il valore TRUE, le caselle sono ridimensionate
+fino alla dimensione del pi&ugrave; grande oggetto contenuto nella tabelle. Se &egrave; FALSE, la
+dimensione delle caselle&egrave; decisa dal pi&ugrave; alto oggetto in una certa riga e dal pi&ugrave;
+largo oggetto in una stessa colonna.
+
+Le righe e le colonne sono disposte a partire da 0 fino a n, dove n &egrave; il numero
+che era stato specificato nella chiamata a gtk_table_new. Cos&igrave;, se specificate
+rows = 2 e columns = 2, lo schema avr&agrave; questo aspetto:
+
+<tscreen><verb>
+ 0 1 2
+0+----------+----------+
+ | | |
+1+----------+----------+
+ | | |
+2+----------+----------+
+</verb></tscreen>
+<p>
+Notate che il sistema di coordinate ha origine nel vertice in alto a sinistra. Per
+mettere un oggetto in una tabella, usate la seguente funzione:
+
+<tscreen><verb>
+void gtk_table_attach (GtkTable *table,
+ GtkWidget *child,
+ gint left_attach,
+ gint right_attach,
+ gint top_attach,
+ gint bottom_attach,
+ gint xoptions,
+ gint yoptions,
+ gint xpadding,
+ gint ypadding);
+</verb></tscreen>
+<p>
+In cui il primo argomento (``table'') &egrave; la tabella che avete creato e il secondo
+(``child'') &egrave; l'oggetto che volete piazzare nella tabella.
+
+Gli argomenti ``attach'' (right, left, top, bottom) specificano dove mettere l'oggetto
+e quante caselle adoperare. Se volete mettere un bottone nella casella in basso a destra
+nella nostra tabella 2x2, e volete che esso riempia SOLO quella casella, dovete porre
+left_attach = 1, right_attach = 2, top_attach = 1, bottom_attach = 2.
+
+Se invece volete che un oggetto si prenda tutta la riga pi&ugrave; in alto nella nostra tabella
+2x2, dovreste usare left_attach = 0, right_attach =2, top_attach = 0,
+bottom_attach = 1.
+
+Gli argomenti ``xoptions'' e ``yoptions'' sono usati per specificare le opzioni di impacchettamento;
+di essi si pu&ograve; fare l'OR in modo di ottenere opzioni multiple.
+
+Le opzioni sono:
+<itemize>
+<item>GTK_FILL - Se la parte di tabella in cui si vuole inserire il widget &egrave; pi&ugrave;
+grande dell'oggetto, e se si specifica GTK_FILL, l'oggetto viene espanso fino ad
+occupare tutto lo spazio disponibile.
+
+<item>GTK_SHRINK - Se si alloca all'oggetto nella tabella meno spazio del necessario
+(di solito succede quando l'utente ridimensiona la finestra), allora normalmente
+l'oggetto verrebbe spinto fuori dal fondo della finestra fino a sparire.
+Se invece si specifica GTK_SHRINK is specified, gli oggetti si rimpiccioliscono
+assieme alla tabella.
+
+<item>GTK_EXPAND - Questo fa s&igrave; che la tabella si espanda fino ad occupare tutto lo
+spazio che rimane nella finestra.
+</itemize>
+
+Il riempimento funziona come nelle scatole, con la creazione di un'area vuota
+attorno all'oggetto la cui dimensione viene specificata in pixel.
+
+La funzione gtk_table_attach() ha UN MUCCHIO di opzioni. Quindi, ecco una scorciatoia:
+
+<tscreen><verb>
+void gtk_table_attach_defaults (GtkTable *table,
+ GtkWidget *widget,
+ gint left_attach,
+ gint right_attach,
+ gint top_attach,
+ gint bottom_attach);
+</verb></tscreen>
+
+Le xoptions e yoptions vengono posti per difetto a GTK_FILL | GTK_EXPAND, e sia xpadding
+che ypadding vengono posti a 0. Il resto degli argomenti sono identici a quelli della funzione
+precedente.
+
+Ci sono poi le funzioni gtk_table_set_row_spacing() and gtk_table_set_col_spacing().
+Queste mettono dello spazio fra le righe (o colonne)in corrispondenza di una specifica
+riga (o colonna).
+
+<tscreen><verb>
+void gtk_table_set_row_spacing (GtkTable *table,
+ gint row,
+ gint spacing);
+</verb></tscreen>
+e
+<tscreen><verb>
+void gtk_table_set_col_spacing (GtkTable *table,
+ gint column,
+ gint spacing);
+</verb></tscreen>
+
+Notate che per le colonne lo spazio viene posto alla destra della colonna, mentre
+per le righe lo spazio viene posto al di sotto della riga.
+
+Si pu&ograve; poi inserire una spaziatura identica fra tutte le righe e/o colonne usando:
+
+<tscreen><verb>
+void gtk_table_set_row_spacings (GtkTable *table,
+ gint spacing);
+</verb></tscreen>
+<p>
+e
+<tscreen><verb>
+void gtk_table_set_col_spacings (GtkTable *table,
+ gint spacing);
+</verb></tscreen>
+<p>
+Notate che con queste chiamate, all'ultima riga e all'ultima colonna
+non viene assegnata alcuna spaziatura.
+
+<sect1>Esempio di Impacchettamento con Tabelle
+<p>
+Per il momento, si prega di fare riferimento all'esempio di tabella in
+testgtk.c distribuito con i sorgenti di gtk.
+
+
+<sect>Panoramica sui Widget
+<p>
+<p>
+La procedura generale di creazione di un widget in GTK prevede i seguenti passi:
+<enum>
+<item> gtk_*_new - una delle varie funzioni che servono per greare un nuovo widget.
+In questa sezione le vedremo tutte in dettaglio.
+
+<item> Connettere tutti i segnali che si vogliono usare alle funzione gestione appropriate.
+
+<item> Assegnare gli attributi all'oggetto.
+
+<item> Impacchettare l'oggetto in un contenitore usando la chiamate appropriata,
+per esempio gtk_container_add() o gtk_box_pack_start().
+
+<item> Mostrare l'oggetto con gtk_widget_show().
+</enum>
+<p>
+gtk_widget_show() fa s&igrave; che GTK sappia che abbiamo terminato di assegnare gli
+attributi dell'oggetto grafico, e che &egrave; pronto per essere visualizzato.
+Si pu&ograve; anche usare la funzione gtk_widget_hide per farlo sparire di nuovo.
+L'ordine in cui mostrate gli oggetti grafici non &egrave; importante, ma io suggerisco
+di mostrare per ultima la finestra, in modo che questa spunti fuori gi&agrave; completa,
+invece di vedere i singoli oggetti che arrivano sullo schermo a mano a mano che si
+formano. I figli di un oggetto grafico (anche una finestra &egrave; un oggetto grafico) non
+vengono infatti mostrati finch&eacute; la finestra stessa non viene mostrata usando la
+funzione gtk_widget_show().
+
+
+<sect1> Casting
+<p>
+Noterete andando avanti che GTK usa un sistema di casting di tipo. Questa operazione
+viene sempre effettuata usando delle macro che allo stesso tempo controllano la
+possibilit&agrave; di effettuare il cast sull'elemento dato e lo effettuano realmente.
+Alcune macro che avrete modo di incontrare sono:
+
+<itemize>
+<item> GTK_WIDGET(widget)
+<item> GTK_OBJECT(object)
+<item> GTK_SIGNAL_FUNC(function)
+<item> GTK_CONTAINER(container)
+<item> GTK_WINDOW(window)
+<item> GTK_BOX(box)
+</itemize>
+
+Tutte queste funzioni sono usate per fare il cast di argomenti di funzione. Le vedrete
+negli esempi, e capirete se &egrave; il caso di usarle semplicemente guardando alle
+dichiarazioni delle funzioni.
+
+Come potrete vedere pi&ugrave; sotto nella gerarchia delle classi, tutti i GtkWidgets
+sono derivati dalla classe base GtkObject. Ci&ograve; significa che potete usare un
+widget in ogni posto in cui una funzione richiede un oggetto - semplicemente
+usate la macro GTK_OBJECT().
+
+Per esempio:
+
+<tscreen><verb>
+gtk_signal_connect(GTK_OBJECT(button), "clicked",
+ GTK_SIGNAL_FUNC(callback_function), callback_data);
+</verb></tscreen>
+
+Questo fa il cast del bottone in un oggetto e fornisce alla chiamata di ritorno
+un cast al puntatore a funzione.
+
+Molti oggetti grafici sono anche contenitori. Se guardate alla gerarchia delle
+classi pi&ugrave; sotto, vedrete che molti oggetti grafici sono derivati dalla classe
+GtkContainer. Ognuna di queste classi pu&ograve; essere usata, con la macro GTK_CONTAINER,
+come argomento per funzioni che richiedono un contenitore.
+
+Sfortunatamente, in questo tutorial non si parler&agrave; in modo estensivo di queste macro,
+ma raccomando di dare un'occhiata ai file header di GTK. Pu&ograve; essere una cosa molto
+educativa. Infatti, non &egrave; difficile imparare come funziona un oggetto solo guardando
+le dichiarazioni delle funzioni.
+
+<p>
+<sect1>Gerarchia degli Oggetti Grafici
+<p>
+Ecco, per vostro riferimento, la gerarchia delle classi usata per implementare gli
+oggetti grafici.
+
+<tscreen><verb>
+ GtkObject
+ +-- GtkData
+ | \-- GtkAdjustment
+ |
+ \-- GtkWidget
+ +-- GtkContainer
+ | +-- GtkBin
+ | | +-- GtkAlignment
+ | | +-- GtkFrame
+ | | | *-- GtkAspectFrame
+ | | |
+ | | +-- GtkItem
+ | | | +-- GtkListItem
+ | | | +-- GtkMenuItem
+ | | | | +-- GtkCheckMenuItem
+ | | | | *-- GtkRadioMenuItem
+ | | | |
+ | | | *-- GtkTreeItem
+ | | |
+ | | +-- GtkViewport
+ | | \-- GtkWindow
+ | | +-- GtkDialog
+ | | \-- GtkFileSelection
+ | |
+ | +-- GtkBox
+ | | +-- GtkHBox
+ | | \-- GtkVBox
+ | | +-- GtkColorSelection
+ | | \-- GtkCurve
+ | |
+ | +-- GtkButton
+ | | +-- GtkOptionMenu
+ | | \-- GtkToggleButton
+ | | \-- GtkCheckButton
+ | | \-- GtkRadioButton
+ | |
+ | +-- GtkList
+ | +-- GtkMenuShell
+ | | +-- GtkMenu
+ | | \-- GtkMenuBar
+ | |
+ | +-- GtkNotebook
+ | +-- GtkScrolledWindow
+ | +-- GtkTable
+ | \-- GtkTree
+ |
+ +-- GtkDrawingArea
+ +-- GtkEntry
+ +-- GtkMisc
+ | +-- GtkArrow
+ | +-- GtkImage
+ | +-- GtkLabel
+ | \-- GtkPixmap
+ |
+ +-- GtkPreview
+ +-- GtkProgressBar
+ +-- GtkRange
+ | +-- GtkScale
+ | | +-- GtkHScale
+ | | \-- GtkVScale
+ | |
+ | \-- GtkScrollbar
+ | +-- GtkHScrollbar
+ | \-- GtkVScrollbar
+ |
+ +-- GtkRuler
+ | +-- GtkHRuler
+ | \-- GtkVRuler
+ |
+ \-- GtkSeparator
+ +-- GtkHSeparator
+ \-- GtkVSeparator
+
+</verb></tscreen>
+<p>
+
+<sect1>Oggetti senza Finestre
+<p>
+Gli oggetti seguenti non hanno una finestra associata. Se volete catturare
+degli eventi, dovrete usare l'oggetto GtkEventBox. Vedete anche la sezione su
+<ref id="sec_The_EventBox_Widget" name="Il Widget EventBox">
+
+<tscreen><verb>
+GtkAlignment
+GtkArrow
+GtkBin
+GtkBox
+GtkImage
+GtkItem
+GtkLabel
+GtkPaned
+GtkPixmap
+GtkScrolledWindow
+GtkSeparator
+GtkTable
+GtkViewport
+GtkAspectFrame
+GtkFrame
+GtkVPaned
+GtkHPaned
+GtkVBox
+GtkHBox
+GtkVSeparator
+GtkHSeparator
+</verb></tscreen>
+<p>
+Proseguiremo la nostra esplorazione di GTK esaminando uno alla volta tutti
+gli oggetti, creando qualche semplice funzione per mostrarli. Un'altra
+buona sorgente &egrave; il programma testgtk.c che viene fornito con GTK. Potete
+trovarlo in gtk/testgtk.c.
+
+<sect>Il Widget Bottone (Button)
+<p>
+<sect1>Bottoni Normali
+<p>
+Ormai abbiamo visto tutto quello che c'&egrave; da vedere riguardo all'oggetto
+``bottone''. E' piuttosto semplice, ma ci sono due modi per crare un bottone.
+Potete usare gtk_button_new_with_label() per creare un bottone con una
+etichetta, o usare gtk_button_new() per creare un bottone vuoto. In tal caso &egrave; poi
+vostro compito impacchettare un'etichetta o una pixmap sul bottone creato.
+Per fare ci&ograve;, create una nuova scatola, e poi impacchettateci i vostri
+oggetti usando la solita gtk_box_pack_start, e infine usate la funzione
+gtk_container_add per impacchettare la scatola nel bottone.
+<p>
+Ecco un esempio di utilizzo di gtk_button_new per creare un bottone con
+un'immagine ed un'etichetta su di s&egrave;. Ho separato il codice usato per
+creare la scatola in modo che lo possiate usare nei vostri programmi.
+
+<tscreen><verb>
+#include <gtk/gtk.h>
+
+
+/* crea una nuova hbox contenente un'immagine ed un'etichetta
+ * e ritorna la scatola creata. */
+
+GtkWidget *xpm_label_box (GtkWidget *parent, gchar *xpm_filename, gchar *label_text)
+{
+ GtkWidget *box1;
+ GtkWidget *label;
+ GtkWidget *pixmapwid;
+ GdkPixmap *pixmap;
+ GdkBitmap *mask;
+ GtkStyle *style;
+
+ /* creare una scatola per una xpm ed una etichetta */
+ box1 = gtk_hbox_new (FALSE, 0);
+ gtk_container_border_width (GTK_CONTAINER (box1), 2);
+
+ /* ottengo lo stile del bottone. Penso che sia per avere il colore
+ * dello sfondo. Se qualcuno sa il vero motivo, &egrave; pregato di dirmelo. */
+ style = gtk_widget_get_style(parent);
+
+ /* e ora via con le faccende dell'xpm stuff. Carichiamo l'xpm*/
+ pixmap = gdk_pixmap_create_from_xpm (parent->window, &amp;mask,
+ &amp;style->bg[GTK_STATE_NORMAL],
+ xpm_filename);
+ pixmapwid = gtk_pixmap_new (pixmap, mask);
+
+ /* creiamo l'etichetta per il bottone */
+ label = gtk_label_new (label_text);
+
+ /* impacchettiamo la pixmap e l'etichetta nella scatola */
+ gtk_box_pack_start (GTK_BOX (box1),
+ pixmapwid, FALSE, FALSE, 3);
+
+ gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 3);
+
+ gtk_widget_show(pixmapwid);
+ gtk_widget_show(label);
+
+ return (box1);
+}
+
+/* la nostra solita funzione di callback */
+void callback (GtkWidget *widget, gpointer *data)
+{
+ g_print ("Hello again - %s was pressed\n", (char *) data);
+}
+
+
+int main (int argc, char *argv[])
+{
+ /* GtkWidget &egrave; il tipo per contenere gli oggetti */
+ GtkWidget *window;
+ GtkWidget *button;
+ GtkWidget *box1;
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ /* creiamo una nuova finestra */
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+ gtk_window_set_title (GTK_WINDOW (window), "Pixmap'd Buttons!");
+
+ /* E' una buona idea fare questo per tutte le finestre. */
+ gtk_signal_connect (GTK_OBJECT (window), "destroy",
+ GTK_SIGNAL_FUNC (gtk_exit), NULL);
+
+
+ /* assegnamo lo spessore del bordo della finestra */
+ gtk_container_border_width (GTK_CONTAINER (window), 10);
+
+ /* creiamo un nuovo bottone */
+ button = gtk_button_new ();
+
+ /* Ormai dovreste esservi abituati a vedere la maggior parte di
+ * queste funzioni */
+ gtk_signal_connect (GTK_OBJECT (button), "clicked",
+ GTK_SIGNAL_FUNC (callback), (gpointer) "cool button");
+
+ /* questa chiama la nostra funzione di creazione di scatole */
+ box1 = xpm_label_box(window, "info.xpm", "cool button");
+
+ /* impacchetta e mostra tutti i nostri oggetti */
+ gtk_widget_show(box1);
+
+ gtk_container_add (GTK_CONTAINER (button), box1);
+
+ gtk_widget_show(button);
+
+ gtk_container_add (GTK_CONTAINER (window), button);
+
+ gtk_widget_show (window);
+
+ /* mettiti in gtk_main e aspetta che cominci il divertimento! */
+ gtk_main ();
+
+ return 0;
+}
+</verb></tscreen>
+La funzione xpm_label_box pu&ograve; essere usata per impacchettare delle xpm
+e delle etichette su qualsiasi oggetto che pu&ograve; essere un contenitore.
+
+<sect1> Bottoni a Commutazione (Toggle Buttons)
+<p>
+I bottoni a commutazione sono molto simili ai bottoni normali, tranne che per il
+fatto che essi si trovano sempre in uno di due stati, che si alternano ad ogni
+click. Possono trovarsi nello stato ``premuto'', e quando li si ripreme, tornano
+ad essere sollevati. Ri-clickandoli, torneranno gi&ugrave;.
+
+I bottoni a commutazione sono la base per i bottoni di controllo (check button) e
+per i radio-bottoni, e quindi molte delle chiamate disponibili per i bottoni
+a commutazione vengono ereditati dai radio-bottoni e dai bottoni di controllo.
+Ma vedremo questi aspetti nel momento in cui li incontreremo.
+
+Creare un nuovo bottone a commutazione:
+
+<tscreen><verb>
+GtkWidget* gtk_toggle_button_new (void);
+
+GtkWidget* gtk_toggle_button_new_with_label (gchar *label);
+</verb></tscreen>
+<p>
+Come potete immaginare, queste funzioni lavorano in modo identico che per
+i bottoni normali. La prima crea un bottone a commutazione vuoto e la seconda un
+bottone con un'etichetta.
+<p>
+Per ottenere lo stato dei widget a commutazione, compresi i radio-bottoni e i
+bottoni di controllo, si pu&ograve; usare una macro come mostrato nell'esempio
+pi&ugrave; sotto. In questo modo lo stato dell'oggetto commutabile viene valutato in
+una funzione di ritorno. Il segnale emesso dai bottoni a commutazione
+(toggle button, il radio button o il check button) che ci interessa &egrave; il segnale
+``toggled''. Per controllare lo stato di questi bottoni, create un gestore di
+segnali che catturi il ``toggled'', e usate la macro per determinare
+il suo stato. La funzione di callback avr&agrave; un aspetto pi&ugrave; o meno cos&igrave;:
+
+<tscreen><verb>
+void toggle_button_callback (GtkWidget *widget, gpointer data)
+ {
+ if (GTK_TOGGLE_BUTTON (widget)->active)
+ {
+ /* Se il programma si &egrave; arrivato a questo punto, il bottone
+ * a commutazione &egrave; sollevato */
+
+ } else {
+
+ /* il bottone &egrave; abbassato */
+ }
+ }
+ </verb></tscreen>
+
+<!--
+
+COMMENTED!
+
+<tscreen><verb>
+guint gtk_toggle_button_get_type (void);
+</verb></tscreen>
+<p>
+No idea... they all have this, but I dunno what it is :)
+
+
+<tscreen><verb>
+void gtk_toggle_button_set_mode (GtkToggleButton *toggle_button,
+ gint draw_indicator);
+</verb></tscreen>
+<p>
+No idea.
+-->
+
+<tscreen><verb>
+void gtk_toggle_button_set_state (GtkToggleButton *toggle_button,
+ gint state);
+</verb></tscreen>
+<p>
+La chiamata qui sopra pu&ograve; essere usata per fare l'assegnazione dello stato
+del bottone a commutazione e dei suoi figli, il radio-bottone e il bottone di
+controllo. Passando come primo argomento a questa funzione il vostro bottone e
+come secondo argomento il valore TRUE o FALSE, si pu&ograve; specificare se il
+bottone deve essere sollevato (rilasciato) o abbassato (premuto). Il valore
+di difetto &egrave; sollevato, cio&egrave; FALSE.
+
+Notate che quando usate la funzione gtk_toggle_button_set_state(), e lo
+stato viene cambiato, si ha il risultato che il bottone emette il segnale
+``clicked''.
+
+<tscreen><verb>
+void gtk_toggle_button_toggled (GtkToggleButton *toggle_button);
+</verb></tscreen>
+<p>
+Questa funzione semplicemente commuta il bottone, ed emette il segnale ``toggled''.
+
+<sect1> Bottoni di Controllo (Check Buttons)
+<p>
+I bottoni di controllo ereditano molte propriet&agrave; e funzioni dal bottone a commutazione,
+ma hanno un aspetto un po' diverso. Invece di essere bottoni contenenti del testo,
+si tratta di quadratini con del testo alla propria destra. Questi bottoni sono
+spesso usati nelle applicazioni per commutare fra lo stato attivato e disattivato delle
+opzioni.
+
+Le due funzioni di creazione sono analoghe a quelle del bottone normale..
+
+<tscreen><verb>
+GtkWidget* gtk_check_button_new (void);
+
+GtkWidget* gtk_check_button_new_with_label (gchar *label);
+</verb></tscreen>
+
+La funzione new_with_label crea un bottone di controllo con una etichetta
+a fianco di esso.
+
+Per controllare lo stato del check button si opera in modo identico al bottone
+a commutazione.
+
+<sect1> Radio-Bottoni (Radio Buttons)
+<p>
+I radio-bottoni sono simili ai bottoni di controllo, tranne che per il
+fatto che sono sempre raggruppati in modo che solo uno alla volta di essi
+pu&ograve; essere selezionato (premuto). Tornano utili quando nella propria applicazione
+si ha bisogno di selezionare una opzione da una breve lista.
+
+La creazione di un nuovo radio-bottone si fa con una di queste chiamate:
+
+<tscreen><verb>
+GtkWidget* gtk_radio_button_new (GSList *group);
+
+GtkWidget* gtk_radio_button_new_with_label (GSList *group,
+ gchar *label);
+</verb></tscreen>
+<p>
+Avrete notato l'argomento in pi&ugrave; che c'&egrave; in queste chiamate. Queste hanno
+infatti bisogno dela specificazione di un ``gruppo'' per svolgere il loro compito.
+Per il primo bottone di un gruppo si deve passare come primo argomento il valore
+NULL. Dopodich&eacute; potete creare un gruppo usando la funzione:
+
+<tscreen><verb>
+GSList* gtk_radio_button_group (GtkRadioButton *radio_button);
+</verb></tscreen>
+
+<p>
+A questo punto potete passare questo gruppo ad ogni chiamata successiva a
+gtk_radio_button_new o new_with_label. E' anche una buona idea specificare
+esplicitamente quale dei bottoni dovr&agrave; essere quello premuto per difetto,
+usando:
+
+<tscreen><verb>
+void gtk_toggle_button_set_state (GtkToggleButton *toggle_button,
+ gint state);
+</verb></tscreen>
+<p>
+Questa funzione &egrave; descritta nella sezione sui bottoni a commutazione, e funziona
+nello stesso identico modo.
+
+<p>
+[Inserir&ograve; un esempio di come usare questi oggetti, penso che sarebbe molto
+utile]
+
+
+<sect> Alcuni Widget
+<p>
+<sect1> L'Etichetta (Label)
+<p>
+Le etichette sono molto usate in GTK, e sono relativamente semplici. Le
+etichette non emettono segnali, dal momento che non hanno una finestra
+X a loro assegnata. Se avete la necessit&agrave; di avere dei segnali o di fare
+delle operazioni di clipping, potete usare il widget EventBox.
+
+Per creare una nuova etichetta, si usa:
+
+<tscreen><verb>
+GtkWidget* gtk_label_new (char *str);
+</verb></tscreen>
+
+In cui l'unico argomento &egrave; la stringa che si vuole sia mostrata.
+
+Per cambiare il testo dell'etichetta dopo che &egrave; stata creata, si usa
+la funzione:
+
+<tscreen><verb>
+void gtk_label_set (GtkLabel *label,
+ char *str);
+</verb></tscreen>
+<p>
+in cui il primo argomento &egrave; l'etichetta creata in precedenza (di cui si
+fa il cast usando la macro GTK_LABEL()), mentre il secondo &egrave; la nuova
+stringa.
+
+Nel caso, lo spazio necessario per la nuova stringa verr&agrave; regolato automaticamente.
+
+Per ottenere la stringa corrente si usa:
+
+<tscreen><verb>
+void gtk_label_get (GtkLabel *label,
+ char **str);
+</verb></tscreen>
+
+in cui il primo argomento &egrave; l'etichetta che avete creato, e il secondo
+&egrave; il valore di ritorno per la stringa.
+
+
+<sect1>Il Widget Suggerimenti (Tooltips)
+<p>
+I suggerimenti sono piccole stringhe di testo che spuntano quando lasciate il
+puntatore su un bottone o un altro widget per qualche secondo. Sono piuttosto
+semplici da usare, per cui ne dar&ograve; la spiegazione senza corredarla di esempi.
+Se volede vedere un po' di codice, date un'occhiata al programma testgtk.c
+distribuito con GTK.
+<p>
+Con alcuni widget (per esempio con l'etichetta) i suggerimenti non funzionano.
+<p>
+La prima chiamata che si usa per creare un nuovo tooltip &egrave; la seguente.
+In una data funzione, &egrave; necessario chiamarla una sola volta: il GtkTooltip
+che viene ritornato da questa funzione pu&ograve; essere usato per creare suggerimenti
+multipli.
+
+<tscreen><verb>
+GtkTooltips *gtk_tooltips_new (void);
+</verb></tscreen>
+
+Una volta creato un nuovo suggerimento e il widget su cui lo volete usare,
+basta usare la seguente chiamata per fare l'assegnazione:
+
+<tscreen><verb>
+void gtk_tooltips_set_tips (GtkTooltips *tooltips,
+ GtkWidget *widget,
+ gchar *tips_text);
+</verb></tscreen>
+
+Il primo argomento &egrave; il suggerimento che era gi&agrave; stato creato, che &egrave; seguito
+dal widget da cui volete che spunti il suggerimento e dal testo che volete
+venga mostrato.
+<p>
+Ecco un piccolo esempio:
+
+<tscreen><verb>
+GtkTooltips *tooltips;
+GtkWidget *button;
+...
+tooltips = gtk_tooltips_new ();
+button = gtk_button_new_with_label ("button 1");
+...
+gtk_tooltips_set_tips (tooltips, button, "This is button 1");
+</verb></tscreen>
+
+Ci sono anche altre funzioni che si usano con i suggerimenti. Eccone una lista
+con una breve descrizione di quello che fanno.
+
+<tscreen><verb>
+void gtk_tooltips_destroy (GtkTooltips *tooltips);
+</verb></tscreen>
+
+Distrugge un suggerimento esistente.
+
+<tscreen><verb>
+void gtk_tooltips_enable (GtkTooltips *tooltips);
+</verb></tscreen>
+
+Abilita un gruppo di suggerimenti disbilitato.
+
+<tscreen><verb>
+void gtk_tooltips_disable (GtkTooltips *tooltips);
+</verb></tscreen>
+
+Disabilita un gruppo di suggerimenti abilitato.
+
+<tscreen><verb>
+void gtk_tooltips_set_delay (GtkTooltips *tooltips,
+ gint delay);
+
+</verb></tscreen>
+Stabilisce quanti millisecondi si deve mantenere il puntatore sopra al
+widget prima che venga mostrato il suggerimento. Il valore di difetto
+&egrave; di 1000 millisecondi.
+
+<tscreen><verb>
+void gtk_tooltips_set_tips (GtkTooltips *tooltips,
+ GtkWidget *widget,
+ gchar *tips_text);
+</verb></tscreen>
+
+Cambia il testo di un suggerimento gi&agrave; esistente.
+
+<tscreen><verb>
+void gtk_tooltips_set_colors (GtkTooltips *tooltips,
+ GdkColor *background,
+ GdkColor *foreground);
+</verb></tscreen>
+
+Assegna i colori di primo piano e di sfondo dei suggerimenti. (Non ho idea
+di come si specifichino i colori).
+<p>
+E questo &egrave; tutto riguardo alle funzioni relative ai suggerimenti. Pi&ugrave;
+di quanto avreste mai voluto sapere :)
+
+<sect1> La Barra di Avanzamento (Progress Bar)
+<p>
+Le barre di avanzamento sono usate per mostrare lo stato di una operazione. Come potete
+vedere nel frammento di codice qui sotto, sono piuttosto semplici da usare.
+Ma prima vediamo come cominciare con la chiamata per creare una nuova progrss
+bar.
+
+<tscreen><verb>
+GtkWidget *gtk_progress_bar_new (void);
+</verb></tscreen>
+
+Ora che la barra di avanzamento &egrave; stata creata, possiamo usarla..
+
+<tscreen><verb>
+void gtk_progress_bar_update (GtkProgressBar *pbar, gfloat percentage);
+</verb></tscreen>
+
+Il primo argomento &egrave; la barra di avanzamento su cui volete lavorare, e il secondo
+&egrave; la quantit&agrave; 'completato', cio&egrave; la quantit&agrave; di riempimento della progress
+bar fra 0 e 100% (un numero reale fra 0 e 1).
+
+Le barre di avanzamento sono usate di solito con funzioni di timeout o altre di
+questo tipo (vedi alla sezione <ref id="sec_timeouts" name="Timeouts,
+I/O and Idle Functions">) per dare l'illusione del multitasking. Tutte
+usano la funzione gtk_progress_bar_update nello stesso modo.
+
+Ecco un esempio di barra di avanzamento, in cui l'aggiornamento avviene usando
+dei timeout. Questo codice vi mostra anche come riinizializzare le
+barre di avanzamento.
+
+<tscreen><verb>
+#include <gtk/gtk.h>
+
+static int ptimer = 0;
+int pstat = TRUE;
+
+/* Questa funzione incrementa e aggiorna la barra di avanzamento, e la rimette
+ a zero se pstat &egrave; FALSE */
+gint progress (gpointer data)
+{
+ gfloat pvalue;
+
+ /* ottiene il valore corrente della status bar */
+ pvalue = GTK_PROGRESS_BAR (data)->percentage;
+
+ if ((pvalue >= 1.0) || (pstat == FALSE)) {
+ pvalue = 0.0;
+ pstat = TRUE;
+ }
+ pvalue += 0.01;
+
+ gtk_progress_bar_update (GTK_PROGRESS_BAR (data), pvalue);
+
+ return TRUE;
+}
+
+/* Questa funzione segnala la riinizializzazione della
+ barra di avanzamento */
+void progress_r (void)
+{
+ pstat = FALSE;
+}
+
+void destroy (GtkWidget *widget, gpointer *data)
+{
+ gtk_main_quit ();
+}
+
+int main (int argc, char *argv[])
+{
+ GtkWidget *window;
+ GtkWidget *button;
+ GtkWidget *label;
+ GtkWidget *table;
+ GtkWidget *pbar;
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+ gtk_signal_connect (GTK_OBJECT (window), "delete_event",
+ GTK_SIGNAL_FUNC (destroy), NULL);
+
+ gtk_container_border_width (GTK_CONTAINER (window), 10);
+
+ table = gtk_table_new(3,2,TRUE);
+ gtk_container_add (GTK_CONTAINER (window), table);
+
+ label = gtk_label_new ("Progress Bar Example");
+ gtk_table_attach_defaults(GTK_TABLE(table), label, 0,2,0,1);
+ gtk_widget_show(label);
+ /* Crea una nuova barra di avanzamento, impacchettala nella tabella
+ e mostrala */
+ pbar = gtk_progress_bar_new ();
+ gtk_table_attach_defaults(GTK_TABLE(table), pbar, 0,2,1,2);
+ gtk_widget_show (pbar);
+
+ /* Attiva un timeout che gestisca l'aggiornamento automatico della barra */
+ ptimer = gtk_timeout_add (100, progress, pbar);
+
+ /* Questo bottone segnala alla barra che deve essere resettata */
+ button = gtk_button_new_with_label ("Reset");
+ gtk_signal_connect (GTK_OBJECT (button), "clicked",
+ GTK_SIGNAL_FUNC (progress_r), NULL);
+ gtk_table_attach_defaults(GTK_TABLE(table), button, 0,1,2,3);
+ gtk_widget_show(button);
+
+ button = gtk_button_new_with_label ("Cancel");
+ gtk_signal_connect (GTK_OBJECT (button), "clicked",
+ GTK_SIGNAL_FUNC (destroy), NULL);
+
+ gtk_table_attach_defaults(GTK_TABLE(table), button, 1,2,2,3);
+ gtk_widget_show (button);
+
+ gtk_widget_show(table);
+ gtk_widget_show(window);
+
+ gtk_main ();
+
+ return 0;
+}
+</verb></tscreen>
+
+In questo programmino ci sono quattro aree che riguardano il modo di
+uso generale delle Barre di Avanzamento; le vediamo ora nell'ordine.
+
+<tscreen><verb>
+pbar = gtk_progress_bar_new ();
+</verb></tscreen>
+
+Questo codice crea una nuova barra ciamata pbar.
+
+<tscreen><verb>
+ptimer = gtk_timeout_add (100, progress, pbar);
+</verb></tscreen>
+
+Questo codice usa dei timeout per abilitare degli intervalli di tempo uguali.
+Per usare le barre di avanzamento non &egrave; per&ograve; necessario servirsi di timeout.
+
+<tscreen><verb>
+pvalue = GTK_PROGRESS_BAR (data)->percentage;
+</verb></tscreen>
+
+Qui si assegna a pvalue il valore corrente della percentuale di avanzamento.
+
+<tscreen><verb>
+gtk_progress_bar_update (GTK_PROGRESS_BAR (data), pvalue);
+</verb></tscreen>
+
+Infine, questo codice aggiorna la barra di avanzamento con il valore di pvalue.
+
+Questo &egrave; tutto quanto c'&egrave; da sapere sulle barre di avanzamento, divertitevi.
+
+<sect1> Dialoghi
+<p>
+
+Il widget ``Dialogo'' &egrave; molto semplice: si tratta in realt&agrave; di una finestra
+con alcuni elementi pre-impacchettati. La struttura di un dialogo &egrave; la
+seguente:
+
+<tscreen><verb>
+struct GtkDialog
+{
+ GtkWindow window;
+
+ GtkWidget *vbox;
+ GtkWidget *action_area;
+};
+</verb></tscreen>
+
+Come potete vedere, crea semplicemente una finestra vi inserisce una vbox
+in cima, poi un separatore e infine una hbox come ``area di azione''.
+
+Un Dialogo pu&ograve; essere utilizzato per messaggi per l'utente e
+altri scopi simili. E' un widget molto essenziale, che ha una sola funzione,
+e precisamente:
+
+<tscreen><verb>
+GtkWidget* gtk_dialog_new (void);
+</verb></tscreen>
+
+Per cui, per creare una nuova finestra di dialogo, uate:
+
+<tscreen><verb>
+GtkWidget window;
+window = gtk_dialog_new ();
+</verb></tscreen>
+
+Questa funzione crea una finestra di dialogo, dopodich&eacute; sta a voi
+utilizzarla. Potete mettere un bottone nella action_area facendo
+qualcosa del tipo:
+
+<tscreen><verb>
+button = ...
+gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->action_area), button,
+ TRUE, TRUE, 0);
+gtk_widget_show (button);
+</verb></tscreen>
+
+Potreste anche aggiungere, ad esempio, un'etichetta all'area della vbox,
+con qualcosa di questo genere:
+
+<tscreen><verb>
+label = gtk_label_new ("Dialogs are groovy");
+gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->vbox), label, TRUE,
+ TRUE, 0);
+gtk_widget_show (label);
+</verb></tscreen>
+
+Per provare a usare una finestra di dialogo, potreste provare a mettere
+due bottoni nella action_area, per esempio un bottone ``Cancella'' ed un
+bottone ``OK'' e un'etichetta nella vbox che chieda qualcosa all'utente o
+segnali un errore. Poi potreste collegare un diverso segnale a ciascun
+bottone ed eseguire l'operazione che l'utente che viene scelta dall'utente.
+
+
+<sect1> Pixmaps
+<p>
+
+Le Pixmap sono strutture dati che contengono immagini. Queste immagini
+possono poi essere utilizzate in varie occasioni, per esempio come
+icone sul desktop X-Window o come cusori. Una bitmap &egrave; una pixmap a due
+colori.
+
+Per usare una pixmap in GTK, dobbiamo in primo luogo creare una struttura
+GdkPixmap utilizzando le routine disponibili nello strato GDK. Una Pixmap
+pu&ograve; essere creata a partire da dati presenti in memoria o letti da un file.
+Vedremo ora una ad una le chiamate utilizzate per creare una pixmap.
+
+<tscreen><verb>
+GdkPixmap *gdk_bitmap_create_from_data( GdkWindow *window,
+ gchar *data,
+ gint width,
+ gint height );
+</verb></tscreen>
+<p>
+Si usa questa routine per creare una pixmap ad un solo piano (2 colori) da
+dati disponibili in memoria. Ogni bit nei dati indica lo stato acceso o
+spento di un pixel. L'altezza (height) e la larghezza (width) sono espresse
+ in pixel. GdkWindow &egrave; un puntatore alla finestra corrente, dal momento che
+le risorse di una pixmap hanno significato solo nel contesto dello schermo
+in cui deve essere mostrata.
+
+<tscreen><verb>
+GdkPixmap* gdk_pixmap_create_from_data( GdkWindow *window,
+ gchar *data,
+ gint width,
+ gint height,
+ gint depth,
+ GdkColor *fg,
+ GdkColor *bg );
+</verb></tscreen>
+
+Questa &egrave; usata per creare una pixmap con la profondit&agrave; data (depth, ossia
+numero di colori) usando i dati specificati. fg e bg indicano i colori da
+usare per il primo piano e per lo sfondo.
+
+<tscreen><verb>
+GdkPixmap* gdk_pixmap_create_from_xpm( GdkWindow *window,
+ GdkBitmap **mask,
+ GdkColor *transparent_color,
+ const gchar *filename );
+</verb></tscreen>
+
+Il formato XPM &egrave; una rappresentazione di pixmap leggibile per X Window. E' una
+rappresentazione molto diffusa, e sono disponibili parecchi programmi per creare
+immagini in questo formato. Il file specificato da ``filename'' deve contenere
+un'immagine in questo formato, che viene caricato nella struttura pixmap.
+La maschera (mask) specifica quali pixel della pixmap devono essere opachi.
+Tutti gli altri pixel sono colorati usando il colore specificato da
+transparent_color. Pi&ugrave; sotto mostreremo un esempio di uso di questa funzione.
+
+<tscreen><verb>
+GdkPixmap* gdk_pixmap_create_from_xpm_d (GdkWindow *window,
+ GdkBitmap **mask,
+ GdkColor *transparent_color,
+ gchar **data);
+</verb></tscreen>
+
+Si possono incorporare piccole immagini all'interno di un programma sotto
+forma di dati in formato XPM. In questo modo, invece di leggerli da un file,
+si possono usare questi dati per creare una pixmap. Un esempio di questo tipo
+di dati &egrave;
+
+<tscreen><verb>
+/* XPM */
+static const char * xpm_data[] = {
+"16 16 3 1",
+" c None",
+". c #000000000000",
+"X c #FFFFFFFFFFFF",
+" ",
+" ...... ",
+" .XXX.X. ",
+" .XXX.XX. ",
+" .XXX.XXX. ",
+" .XXX..... ",
+" .XXXXXXX. ",
+" .XXXXXXX. ",
+" .XXXXXXX. ",
+" .XXXXXXX. ",
+" .XXXXXXX. ",
+" .XXXXXXX. ",
+" .XXXXXXX. ",
+" ......... ",
+" ",
+" "};
+</verb></tscreen>
+
+<tscreen><verb>
+void gdk_pixmap_destroy( GdkPixmap *pixmap );
+</verb></tscreen>
+<p>
+Quando abbiamo finito di usare una pixmap e pensiamo di non doverla riutilizzare
+presto, &egrave; una buona idea liberare queste risorse usando la funzione
+dk_pixmap_destroy. Le pixmap devono essere considerate una risorsa preziosa.
+
+Quando abbiamo creato una pixmap, possiamo mostrarla come un widget GTK.
+E' necessario creare un widget pixmap che contenga una pixmap GDK. Questa
+operazione viene compiuta usando
+
+<tscreen><verb>
+GtkWidget* gtk_pixmap_new( GdkPixmap *pixmap,
+ GdkBitmap *mask );
+</verb></tscreen>
+<p>
+Le altre chiamate per i widget pixmap sono
+
+<tscreen><verb>
+guint gtk_pixmap_get_type( void );
+void gtk_pixmap_set( GtkPixmap *pixmap,
+ GdkPixmap *val,
+ GdkBitmap *mask);
+void gtk_pixmap_get( GtkPixmap *pixmap,
+ GdkPixmap **val,
+ GdkBitmap **mask);
+</verb></tscreen>
+<p>
+La funzione gtk_pixmap_set viene usata per cambiare la pixmap che viene
+gestita correntemente dal widget.
+gtk_pixmap_set is used to change the pixmap that the widget is currently
+managing. ``val'' &egrave; la pixmap che &egrave; stata creata usando il GDK.
+Segue un esempio di uso di una pixmap in un bottone.
+
+<tscreen><verb>
+
+#include <gtk/gtk.h>
+
+
+/* dat XPM dell'icona Apri File */
+static const char * xpm_data[] = {
+"16 16 3 1",
+" c None",
+". c #000000000000",
+"X c #FFFFFFFFFFFF",
+" ",
+" ...... ",
+" .XXX.X. ",
+" .XXX.XX. ",
+" .XXX.XXX. ",
+" .XXX..... ",
+" .XXXXXXX. ",
+" .XXXXXXX. ",
+" .XXXXXXX. ",
+" .XXXXXXX. ",
+" .XXXXXXX. ",
+" .XXXXXXX. ",
+" .XXXXXXX. ",
+" ......... ",
+" ",
+" "};
+
+
+/* quando invocata (con il segnale delete_event), termina l'applicazione. */
+void close_application( GtkWidget *widget, gpointer *data ) {
+ gtk_main_quit();
+}
+
+
+/* invocata se il bottone &egrave; clickato. Stampa semplicemente un messaggio */
+void button_clicked( GtkWidget *widget, gpointer *data ) {
+ printf( "button clicked\n" );
+}
+
+
+
+
+int main( int argc, char *argv[] )
+{
+ /* i widget sono memorizzati nel tipo GtkWidget */
+ GtkWidget *window, *pixmapwid, *button;
+ GdkPixmap *pixmap;
+ GdkBitmap *mask;
+ GtkStyle *style;
+
+ /* crea la finestra principale, e collega il segnale delete_event
+ alla terminazione dell'applicazione */
+ gtk_init( &amp;argc, &amp;argv );
+ window = gtk_window_new( GTK_WINDOW_TOPLEVEL );
+ gtk_signal_connect( GTK_OBJECT (window), "delete_event",
+ GTK_SIGNAL_FUNC (close_application), NULL );
+ gtk_container_border_width( GTK_CONTAINER (window), 10 );
+ gtk_widget_show( window );
+
+ /* la pixmap proviene da gdk */
+ style = gtk_widget_get_style( window );
+ pixmap = gdk_pixmap_create_from_xpm_d( window->window, &amp;mask,
+ &amp;style->bg[GTK_STATE_NORMAL],
+ (gchar **)xpm_data );
+
+ /* un widget pixmap per contenere la pixmap */
+ pixmapwid = gtk_pixmap_new( pixmap, mask );
+ gtk_widget_show( pixmapwid );
+
+ /* un bottone per contenere il widget pixmap */
+ button = gtk_button_new();
+ gtk_container_add( GTK_CONTAINER(button), pixmapwid );
+ gtk_container_add( GTK_CONTAINER(window), button );
+ gtk_widget_show( button );
+
+ gtk_signal_connect( GTK_OBJECT(button), "clicked",
+ GTK_SIGNAL_FUNC(button_clicked), NULL );
+
+ /* mostra la finestra */
+ gtk_main ();
+
+ return 0;
+}
+</verb></tscreen>
+
+
+Per caricare una pixmap da un file XPM chiamato icon0.xpm che si trova
+nella direttorio corrente, avremmo creato la pixmap in questo modo:
+
+<tscreen><verb>
+ /* carica una pixmap da un file */
+ pixmap = gdk_pixmap_create_from_xpm( window->window, &amp;mask,
+ &amp;style->bg[GTK_STATE_NORMAL],
+ "./icon0.xpm" );
+ pixmapwid = gtk_pixmap_new( pixmap, mask );
+ gtk_widget_show( pixmapwid );
+ gtk_container_add( GTK_CONTAINER(window), pixmapwid );
+</verb></tscreen>
+
+
+Usare le Sagome
+<p>
+Uno degli svantaggi di usare le pixmap &egrave; costituito dal fatto che l'oggetto
+mostrato &egrave; sempre rettangolare, a prescindere dall'immagine. Ci piacerebbe
+invece poter crare dei desktop e delle immagini con forme pi&ugrave; naturali. Per
+esempio, per l'interfaccia di un gioco, potremmo volere avere dei pulsanti
+circolari. Il modo per ottenere questo effetto &egrave; di usare delle finestre
+sagomate.
+
+Una finestra sagomata &egrave; semplicemente una pixmap in cui i pixel dello
+sfondo sono trasparenti. In questo modo, se l'immagine di sfondo &egrave;
+multicolore, possiamo evitare di sovrascriverla con un bordo rettangolare
+attorno all'icona. Il prossimo esempio mostra una carriola sul desktop.
+
+<tscreen><verb>
+
+#include <gtk/gtk.h>
+
+
+
+/* XPM */
+static char * WheelbarrowFull_xpm[] = {
+"48 48 64 1",
+" c None",
+". c #DF7DCF3CC71B",
+"X c #965875D669A6",
+"o c #71C671C671C6",
+"O c #A699A289A699",
+"+ c #965892489658",
+"@ c #8E38410330C2",
+"# c #D75C7DF769A6",
+"$ c #F7DECF3CC71B",
+"% c #96588A288E38",
+"&amp; c #A69992489E79",
+"* c #8E3886178E38",
+"= c #104008200820",
+"- c #596510401040",
+"; c #C71B30C230C2",
+": c #C71B9A699658",
+"> c #618561856185",
+", c #20811C712081",
+"< c #104000000000",
+"1 c #861720812081",
+"2 c #DF7D4D344103",
+"3 c #79E769A671C6",
+"4 c #861782078617",
+"5 c #41033CF34103",
+"6 c #000000000000",
+"7 c #49241C711040",
+"8 c #492445144924",
+"9 c #082008200820",
+"0 c #69A618611861",
+"q c #B6DA71C65144",
+"w c #410330C238E3",
+"e c #CF3CBAEAB6DA",
+"r c #71C6451430C2",
+"t c #EFBEDB6CD75C",
+"y c #28A208200820",
+"u c #186110401040",
+"i c #596528A21861",
+"p c #71C661855965",
+"a c #A69996589658",
+"s c #30C228A230C2",
+"d c #BEFBA289AEBA",
+"f c #596545145144",
+"g c #30C230C230C2",
+"h c #8E3882078617",
+"j c #208118612081",
+"k c #38E30C300820",
+"l c #30C2208128A2",
+"z c #38E328A238E3",
+"x c #514438E34924",
+"c c #618555555965",
+"v c #30C2208130C2",
+"b c #38E328A230C2",
+"n c #28A228A228A2",
+"m c #41032CB228A2",
+"M c #104010401040",
+"N c #492438E34103",
+"B c #28A2208128A2",
+"V c #A699596538E3",
+"C c #30C21C711040",
+"Z c #30C218611040",
+"A c #965865955965",
+"S c #618534D32081",
+"D c #38E31C711040",
+"F c #082000000820",
+" ",
+" .XoO ",
+" +@#$%o&amp; ",
+" *=-;#::o+ ",
+" >,<12#:34 ",
+" 45671#:X3 ",
+" +89<02qwo ",
+"e* >,67;ro ",
+"ty> 459@>+&amp;&amp; ",
+"$2u+ ><ipas8* ",
+"%$;=* *3:.Xa.dfg> ",
+"Oh$;ya *3d.a8j,Xe.d3g8+ ",
+" Oh$;ka *3d$a8lz,,xxc:.e3g54 ",
+" Oh$;kO *pd$%svbzz,sxxxxfX..&amp;wn> ",
+" Oh$@mO *3dthwlsslszjzxxxxxxx3:td8M4 ",
+" Oh$@g&amp; *3d$XNlvvvlllm,mNwxxxxxxxfa.:,B* ",
+" Oh$@,Od.czlllllzlmmqV@V#V@fxxxxxxxf:%j5&amp; ",
+" Oh$1hd5lllslllCCZrV#r#:#2AxxxxxxxxxcdwM* ",
+" OXq6c.%8vvvllZZiqqApA:mq:Xxcpcxxxxxfdc9* ",
+" 2r<6gde3bllZZrVi7S@SV77A::qApxxxxxxfdcM ",
+" :,q-6MN.dfmZZrrSS:#riirDSAX@Af5xxxxxfevo",
+" +A26jguXtAZZZC7iDiCCrVVii7Cmmmxxxxxx%3g",
+" *#16jszN..3DZZZZrCVSA2rZrV7Dmmwxxxx&amp;en",
+" p2yFvzssXe:fCZZCiiD7iiZDiDSSZwwxx8e*>",
+" OA1<jzxwwc:$d%NDZZZZCCCZCCZZCmxxfd.B ",
+" 3206Bwxxszx%et.eaAp77m77mmmf3&amp;eeeg* ",
+" @26MvzxNzvlbwfpdettttttttttt.c,n&amp; ",
+" *;16=lsNwwNwgsvslbwwvccc3pcfu<o ",
+" p;<69BvwwsszslllbBlllllllu<5+ ",
+" OS0y6FBlvvvzvzss,u=Blllj=54 ",
+" c1-699Blvlllllu7k96MMMg4 ",
+" *10y8n6FjvllllB<166668 ",
+" S-kg+>666<M<996-y6n<8* ",
+" p71=4 m69996kD8Z-66698&amp;&amp; ",
+" &amp;i0ycm6n4 ogk17,0<6666g ",
+" N-k-<> >=01-kuu666> ",
+" ,6ky&amp; &amp;46-10ul,66, ",
+" Ou0<> o66y<ulw<66&amp; ",
+" *kk5 >66By7=xu664 ",
+" <<M4 466lj<Mxu66o ",
+" *>> +66uv,zN666* ",
+" 566,xxj669 ",
+" 4666FF666> ",
+" >966666M ",
+" oM6668+ ",
+" *4 ",
+" ",
+" "};
+
+
+/* quando invocata (con il segnale delete_event), termina l'applicazione. */
+void close_application( GtkWidget *widget, gpointer *data ) {
+ gtk_main_quit();
+}
+
+
+int main (int argc, char *argv[])
+{
+ /* il tipo di dato per i widget &egrave; GtkWidget */
+ GtkWidget *window, *pixmap, *fixed;
+ GdkPixmap *gdk_pixmap;
+ GdkBitmap *mask;
+ GtkStyle *style;
+ GdkGC *gc;
+
+ /* crea la finestra principale e collega il segnale delete_event per
+ terminare l'applicazione. Notare che non mettiamo un titolo
+ alla finestra. */
+ gtk_init (&amp;argc, &amp;argv);
+ window = gtk_window_new( GTK_WINDOW_POPUP );
+ gtk_signal_connect (GTK_OBJECT (window), "delete_event",
+ GTK_SIGNAL_FUNC (close_application), NULL);
+ gtk_widget_show (window);
+
+ /* ora occupiamoci della pixmap e del widget pixmap */
+ style = gtk_widget_get_default_style();
+ gc = style->black_gc;
+ gdk_pixmap = gdk_pixmap_create_from_xpm_d( window->window, &amp;mask,
+ &amp;style->bg[GTK_STATE_NORMAL],
+ WheelbarrowFull_xpm );
+ pixmap = gtk_pixmap_new( gdk_pixmap, mask );
+ gtk_widget_show( pixmap );
+
+ /* Per mostrare la pixmap, usiamo un widget "fixed" in cui metterla */
+ fixed = gtk_fixed_new();
+ gtk_widget_set_usize( fixed, 200, 200 );
+ gtk_fixed_put( GTK_FIXED(fixed), pixmap, 0, 0 );
+ gtk_container_add( GTK_CONTAINER(window), fixed );
+ gtk_widget_show( fixed );
+
+ /* Questa maschera tutto tranne l'immagine stessa */
+ gtk_widget_shape_combine_mask( window, mask, 0, 0 );
+
+ /* mostra la finestra */
+ gtk_widget_set_uposition( window, 20, 400 );
+ gtk_widget_show( window );
+ gtk_main ();
+
+ return 0;
+}
+</verb></tscreen>
+<p>
+Per rendere sensibile l'immagine della carriola, potremmo collegare
+il segnale di pressione del bottone in modo che venga compiuta una certa
+azione. Le prossime linee renderebbero l'immagine sensibile alla pressione
+di un bottone del mouse che fa s&igrave; che l'applicazione termini.
+
+<tscreen><verb>
+gtk_widget_set_events( window,
+ gtk_widget_get_events( window ) |
+ GDK_BUTTON_PRESS_MASK );
+
+gtk_signal_connect( GTK_OBJECT(window), "button_press_event",
+ GTK_SIGNAL_FUNC(close_application), NULL );
+</verb></tscreen>
+
+
+<sect> Widget Contenitore
+
+<sect1> Il widget Blocco Note (Notebook)
+<p>
+Il widget Blocco note &egrave; un insieme di pagine sovrapposte l'una con l'altra,
+ognuna contente cose diverse. Questo widget &egrave; diventato molto comune nella
+programmazione delle interfacce utente ed &egrave; un buon metodo per mostrare informazioni
+tra loro correlate ma che debbano essere mostrate separatamente.
+
+<p>
+La prima funzione da invocare che si deve conoscere, come si pu&ograve; intuire, &egrave; usata
+per creare un nuovo Blocco Note.
+
+<tscreen><verb>
+GtkWidget* gtk_notebook_new (void);
+</verb></tscreen>
+
+Una volta che il notebook &egrave; sato creato, ci sono 12 funzioni che possono
+operare sul widget notebook. Guardiamole individualmente.
+
+La prima che vediamo riguarda come posizionare l'indicatore di pagina.
+Questi inidicatori di pagina o ``linguette'' (come possono anche essere chiamati)
+possono essere posizionati in quattro posti: alto, basso, sinistra.destra.
+
+<tscreen><verb>
+void gtk_notebook_set_tab_pos (GtkNotebook *notebook, GtkPositionType pos);
+</verb></tscreen>
+
+GtkPositionType sar&agrave; uno dei seguenti valori (molto autoesplicativi)
+<itemize>
+<item> GTK_POS_LEFT
+<item> GTK_POS_RIGHT
+<item> GTK_POS_TOP
+<item> GTK_POS_BOTTOM
+</itemize>
+
+GTK_POS_TOP e' il valore predefinito.
+
+Ora vediamo come aggiugere le pagine al Blocco Note. Ci sono 3 modi per farlo. Diamo
+un'occhiata ai primi due insieme, viste che sono molto simili.
+
+<tscreen><verb>
+void gtk_notebook_append_page (GtkNotebook *notebook, GtkWidget *child, GtkWidget *tab_label);
+
+void gtk_notebook_prepend_page (GtkNotebook *notebook, GtkWidget *child, GtkWidget *tab_label);
+</verb></tscreen>
+
+Queste funzioni aggiungono pagine al notebook inserendole rispettivamente alla fine
+(append) o all'inizio (prepend). *child &egrave; il widget che &egrave; posto nella pagina del
+notebook e *tab_label e la intestazione della pagina stessa.
+
+L'ultima funzione per aggiungere una pagina al notebook contiene tutte le propriet&agrave;
+delle precedenti due, ma permette di specificare dove posizionare la pagina che
+si vuole inserire.
+
+<tscreen><verb>
+void gtk_notebook_insert_page (GtkNotebook *notebook, GtkWidget *child, GtkWidget *tab_label, gint position);
+</verb></tscreen>
+
+I parametri sono gli stessi di _append_ e _prepend_ tranne che per il parametro in
+pi&ugrave;: ``position''.
+Questo parametro viene usato per specificare in che posizione ineserire la pagina.
+
+Ora che conosciamo come aggiungere le pagine, vediamo come poter toglierne una.
+
+<tscreen><verb>
+void gtk_notebook_remove_page (GtkNotebook *notebook, gint page_num);
+</verb></tscreen>
+
+Questa funzione prende il numero della pagina specificata dal campo page_num e
+rimuove la pagina corrispondente dal Blocco Note.
+
+Per trovare qual'&egrave; la pagina corrente nel notebook bisogna usare la funzione:
+
+<tscreen><verb>
+gint gtk_notebook_current_page (GtkNotebook *notebook);
+</verb></tscreen>
+
+Le prossime due funzioni sono semplicemente delle chiamate che muovono la pagina del
+notebook avanti o indietro. Semplicemente forniscono le chiamate alle rispettive
+funzioni del widget notebook su si pu&ograve; operare. NB: quando un notebook &egrave;
+correntemente sull'ultima pagina e viene invocata la funzione gtk_notebook_next_page,
+il notebook ritorner&agrave; automaticamente alla prima pagina. Logicamente succede anche
+il contrario quando invochi gtk_notebook_prev_page e ti trovi sulla prima pagina.
+
+<tscreen><verb>
+void gtk_notebook_next_page (GtkNoteBook *notebook);
+void gtk_notebook_prev_page (GtkNoteBook *notebook);
+</verb></tscreen>
+
+La prossima funzione stabilisce la pagina ``attiva''. Se si vuole che la pagina
+principale del notebook sia per esempio la 5 (ad esempio) si pu&ograve; usare questa
+funzione.
+Se non si usa questa funzione la pagina principale sar&agrave; la 1.
+
+<tscreen><verb>
+void gtk_notebook_set_page (GtkNotebook *notebook, gint page_num);
+</verb></tscreen>
+
+Le prossime due funzioni aggiungono o rimuovono, rispettivamente, le intestazioni e
+i bordi delle pagine.
+
+<tscreen><verb>
+void gtk_notebook_set_show_tabs (GtkNotebook *notebook, gint show_tabs);
+void gtk_notebook_set_show_border (GtkNotebook *notebook, gint show_border);
+</verb></tscreen>
+
+show_tabs e show_border posso avere come valore TRUE o FALSE (0 or 1).
+
+Diamo ora una occhiata ad un esempio. Si tratta di una espansione del codice preso
+dal file testgtk.c che &egrave; compreso in tutte le distribuzioni, e mostra
+tutte le 13 funzioni. Questo piccolo programma crea una finestra con un notebook
+e 6 bottoni. Il notebook contiene 11 pagine, aggiunte nei 3 modi differenti (alla
+fine, all'inizio o in qualsiasi posizione). I bottoni permettono di girare le
+intestazioni, aggiungere/rimuovere le intestazioni e i bordi, rimuovere una
+pagina, cambiare la pagina avanti e indietro e uscire dal programma.
+
+<tscreen><verb>
+
+#include <gtk/gtk.h>
+
+/* Queta funzione ruota le posizione delle linguette delle pagine */
+void rotate_book (GtkButton *button, GtkNotebook *notebook)
+{
+ gtk_notebook_set_tab_pos (notebook, (notebook->tab_pos +1) %4);
+}
+
+/* Aggiunge e rimuove le linguette e i bordi */
+void tabsborder_book (GtkButton *button, GtkNotebook *notebook)
+{
+ gint tval = FALSE;
+ gint bval = FALSE;
+ if (notebook->show_tabs == 0)
+ tval = TRUE;
+ if (notebook->show_border == 0)
+ bval = TRUE;
+
+ gtk_notebook_set_show_tabs (notebook, tval);
+ gtk_notebook_set_show_border (notebook, bval);
+}
+
+/* Rimuove una pagina */
+void remove_book (GtkButton *button, GtkNotebook *notebook)
+{
+ gint page;
+
+ page = gtk_notebook_current_page(notebook);
+ gtk_notebook_remove_page (notebook, page);
+ /* E' necessario fare un refresh del widget --
+ Questo forza il widget a ridisegnarsi. */
+ gtk_widget_draw(GTK_WIDGET(notebook), NULL);
+}
+
+void delete (GtkWidget *widget, gpointer *data)
+{
+ gtk_main_quit ();
+}
+
+int main (int argc, char *argv[])
+{
+ GtkWidget *window;
+ GtkWidget *button;
+ GtkWidget *table;
+ GtkWidget *notebook;
+ GtkWidget *frame;
+ GtkWidget *label;
+ GtkWidget *checkbutton;
+ int i;
+ char bufferf[32];
+ char bufferl[32];
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+ gtk_signal_connect (GTK_OBJECT (window), "destroy",
+ GTK_SIGNAL_FUNC (destroy), NULL);
+
+ gtk_container_border_width (GTK_CONTAINER (window), 10);
+
+ table = gtk_table_new(2,6,TRUE);
+ gtk_container_add (GTK_CONTAINER (window), table);
+
+ /* Crea un nuovo notebook, e tabilisce la posizione delle linguette */
+ notebook = gtk_notebook_new ();
+ gtk_notebook_set_tab_pos (GTK_NOTEBOOK (notebook), GTK_POS_TOP);
+ gtk_table_attach_defaults(GTK_TABLE(table), notebook, 0,6,0,1);
+ gtk_widget_show(notebook);
+
+ /* appende una parte delle pagine */
+ for (i=0; i < 5; i++) {
+ sprintf(bufferf, "Append Frame %d", i+1);
+ sprintf(bufferl, "Page %d", i+1);
+
+ frame = gtk_frame_new (bufferf);
+ gtk_container_border_width (GTK_CONTAINER (frame), 10);
+ gtk_widget_set_usize (frame, 100, 75);
+ gtk_widget_show (frame);
+
+ label = gtk_label_new (bufferf);
+ gtk_container_add (GTK_CONTAINER (frame), label);
+ gtk_widget_show (label);
+
+ label = gtk_label_new (bufferl);
+ gtk_notebook_append_page (GTK_NOTEBOOK (notebook), frame, label);
+ }
+
+
+ /* Ora aggiungiamo una pagina in una certa posizione */
+ checkbutton = gtk_check_button_new_with_label ("Check me please!");
+ gtk_widget_set_usize(checkbutton, 100, 75);
+ gtk_widget_show (checkbutton);
+
+ label = gtk_label_new ("Add spot");
+ gtk_container_add (GTK_CONTAINER (checkbutton), label);
+ gtk_widget_show (label);
+ label = gtk_label_new ("Add page");
+ gtk_notebook_insert_page (GTK_NOTEBOOK (notebook), checkbutton, label, 2);
+
+ /* Ora finalmente aggiungiamo le pagine all'inizio */
+ for (i=0; i < 5; i++) {
+ sprintf(bufferf, "Prepend Frame %d", i+1);
+ sprintf(bufferl, "PPage %d", i+1);
+
+ frame = gtk_frame_new (bufferf);
+ gtk_container_border_width (GTK_CONTAINER (frame), 10);
+ gtk_widget_set_usize (frame, 100, 75);
+ gtk_widget_show (frame);
+
+ label = gtk_label_new (bufferf);
+ gtk_container_add (GTK_CONTAINER (frame), label);
+ gtk_widget_show (label);
+
+ label = gtk_label_new (bufferl);
+ gtk_notebook_prepend_page (GTK_NOTEBOOK(notebook), frame, label);
+ }
+
+ /* Stabilisce quale sar&agrave; la prima pagina che sar&agrave; visualizzata. */
+ gtk_notebook_set_page (GTK_NOTEBOOK(notebook), 3);
+
+
+ /* Crea un set di bottoni */
+ button = gtk_button_new_with_label ("close");
+ gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
+ GTK_SIGNAL_FUNC (destroy), NULL);
+ gtk_table_attach_defaults(GTK_TABLE(table), button, 0,1,1,2);
+ gtk_widget_show(button);
+
+ button = gtk_button_new_with_label ("next page");
+ gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
+ (GtkSignalFunc) gtk_notebook_next_page,
+ GTK_OBJECT (notebook));
+ gtk_table_attach_defaults(GTK_TABLE(table), button, 1,2,1,2);
+ gtk_widget_show(button);
+
+ button = gtk_button_new_with_label ("prev page");
+ gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
+ (GtkSignalFunc) gtk_notebook_prev_page,
+ GTK_OBJECT (notebook));
+ gtk_table_attach_defaults(GTK_TABLE(table), button, 2,3,1,2);
+ gtk_widget_show(button);
+
+ button = gtk_button_new_with_label ("tab position");
+ gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
+ (GtkSignalFunc) rotate_book, GTK_OBJECT(notebook));
+ gtk_table_attach_defaults(GTK_TABLE(table), button, 3,4,1,2);
+ gtk_widget_show(button);
+
+ button = gtk_button_new_with_label ("tabs/border on/off");
+ gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
+ (GtkSignalFunc) tabsborder_book,
+ GTK_OBJECT (notebook));
+ gtk_table_attach_defaults(GTK_TABLE(table), button, 4,5,1,2);
+ gtk_widget_show(button);
+
+ button = gtk_button_new_with_label ("remove page");
+ gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
+ (GtkSignalFunc) remove_book,
+ GTK_OBJECT(notebook));
+ gtk_table_attach_defaults(GTK_TABLE(table), button, 5,6,1,2);
+ gtk_widget_show(button);
+
+ gtk_widget_show(table);
+ gtk_widget_show(window);
+
+ gtk_main ();
+
+ return 0;
+}
+</verb></tscreen>
+<p>
+E speriamo che questo vi aiuti a creare i Blocco Note per le vostre applicazioni GTK!
+
+<sect1> Finestre Scorribili (Scrolled Windows)
+<p>
+Le Finestre Scorribili sono usate per creare areee scorribili in una vera finestra.
+Si pu&ograve; inserire qualsiasi tipo di widget in questo tipo di finestra, e possono poi
+essere accessibili a prescindere dalle dimensioni usando le barre di scorrimento.
+
+La funzione seguente &egrave; usata per creare una nuova scrolled window.
+
+<tscreen><verb>
+GtkWidget* gtk_scrolled_window_new (GtkAdjustment *hadjustment,
+ GtkAdjustment *vadjustment);
+</verb></tscreen>
+<p>
+Il primo argomento &egrave; l'aggiustamento (di quanto scendere ogni
+volta) orizzontale e il secondo &egrave; quello verticale. A questi si assegna
+quasi sempre il valore NULL.
+
+<tscreen><verb>
+void gtk_scrolled_window_set_policy (GtkScrolledWindow *scrolled_window,
+ GtkPolicyType hscrollbar_policy,
+ GtkPolicyType vscrollbar_policy);
+</verb></tscreen>
+
+Questa funzione stabilisce la politica da usare nella barra di scorrimento. Il primo
+argomento &egrave; la finestra scorribile interessata. Il secondo stabilisce la politica
+per la barra di scorrimento orizzontale e il terzo &egrave; quello per la politca verticale.
+
+La politica pu&ograve; essere GTK_POLICY AUTOMATIC o GTK_POLICY_ALWAYS.
+GTK_POLICY_AUTOMATIC decide automaticamente se la barra di scorrimento deve essere
+visualizzata, mentre con GTK_POLICY_ALWAYS la barra verr&agrave; sempre mostrata.
+
+<tscreen><verb>
+#include <gtk/gtk.h>
+
+void destroy(GtkWidget *widget, gpointer *data)
+{
+ gtk_main_quit();
+}
+
+int main (int argc, char *argv[])
+{
+ static GtkWidget *window;
+ GtkWidget *scrolled_window;
+ GtkWidget *table;
+ GtkWidget *button;
+ char buffer[32];
+ int i, j;
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ /* Crea una nuove finestra di dialogo in cui la scrolled window sar&agrave;
+ inserita. Una finestra di dialogo &egrave; semplicemente come una
+ finestra normale, ma ha anche un vbox e un separatore orizzontale
+ gi&agrave; inseriti per difetto. E'un modo semplice per
+ creare finestre di dialogo. */
+ window = gtk_dialog_new ();
+ gtk_signal_connect (GTK_OBJECT (window), "destroy",
+ (GtkSignalFunc) destroy, NULL);
+ gtk_window_set_title (GTK_WINDOW (window), "dialog");
+ gtk_container_border_width (GTK_CONTAINER (window), 0);
+
+ /* crea una nuova finestra scorribile. */
+ scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+
+ gtk_container_border_width (GTK_CONTAINER (scrolled_window), 10);
+
+ /* la politica &egrave; GTK_POLICY AUTOMATIC per lo scorrimento orizzontale e
+ GTK_POLICY_ALWAYS per quello verticale. */
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
+
+ /* La finestra di dialogo &egrave; creata con un vbox gi&agrave; inserito.*/
+ gtk_box_pack_start (GTK_BOX (GTK_DIALOG(window)->vbox), scrolled_window,
+ TRUE, TRUE, 0);
+ gtk_widget_show (scrolled_window);
+
+ /* crea una tablella di10 x 10. */
+ table = gtk_table_new (10, 10, FALSE);
+
+ /* setta lo spazio tra ogni cella di 10 pixel sia verticale sia orizzontale*/
+ gtk_table_set_row_spacings (GTK_TABLE (table), 10);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 10);
+
+ /* inserisce la tabella nella finestra scorribile*/
+ gtk_container_add (GTK_CONTAINER (scrolled_window), table);
+ gtk_widget_show (table);
+
+ /* questo semplicemente crea una griglia di bottoni nella tabelle per
+ dimostrare il comportamento della finestra scorribile */
+ for (i = 0; i < 10; i++)
+ for (j = 0; j < 10; j++) {
+ sprintf (buffer, "button (%d,%d)\n", i, j);
+ button = gtk_toggle_button_new_with_label (buffer);
+ gtk_table_attach_defaults (GTK_TABLE (table), button,
+ i, i+1, j, j+1);
+ gtk_widget_show (button);
+ }
+
+ /* Aggiunge un bottone "close" alla fine della finestra */
+ button = gtk_button_new_with_label ("close");
+ gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
+ (GtkSignalFunc) gtk_widget_destroy,
+ GTK_OBJECT (window));
+
+ /* questo fa s&igrave; che questo bottone sia quello predefinito */
+
+ GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
+ gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->action_area), button, TRUE, TRUE, 0);
+
+ /* Questo ottiene il bottone predefinito. Premendo semplicemente l'"enter" il
+ bottone si avvier&agrave; */
+ gtk_widget_grab_default (button);
+ gtk_widget_show (button);
+
+ gtk_widget_show (window);
+
+ gtk_main();
+
+ return(0);
+}
+</verb></tscreen>
+<p>
+Prova a giocare con il ridemensionamento della finestra. Noterete la reazione della
+barra di scorrimento. Potete anche usare la funzione gtk_widget_set_usize() per
+assegnare la dimensione predefinita della finestra o di un widget.
+<!-- (ndMichel: questa chiamata non funziona per i bottoni!) -->
+
+
+<sect> Il Widgets Lista
+<p>
+Il widget GtkList serve come contenitore verticale per altri widget che
+devono essere di tipo GtkListItem.
+
+Un widget GtkList possiede una sua propria finestra per ricevere eventi
+e un suo proprio colore di sfondo che di solito &egrave; bianco. Dal momento
+che &egrave; direttamente derivato dal widget GtkContainer, pu&ograve; essere trattato
+come tale usando la macro GTK_CONTAINER(List); si veda il widget GtkContainer
+per ulteriori dettagli.
+Per usare il widget GtkList in tutte le sue potenzialit&agrave;, si dovrebbe essere
+gi&agrave; familiari con l'uso della GList e delle relative funzioni g_list_*().
+
+All'interno della definizione della struttura del widget GtkList c'&egrave; un
+campo che sar&agrave; per noi di grande interesse, cio&egrave;:
+
+<tscreen><verb>
+struct _GtkList
+{
+ ...
+ GList *selection;
+ guint selection_mode;
+ ...
+};
+</verb></tscreen>
+
+Il campo ``selection'' in un GtkList punta a una lista collegata di tutti
+gli elementi che sono selezionati correntemente, oppure a NULL se la
+selezione &egrave; vuota. Quindi, per avere informazioni sulla selezione corrente,
+leggiamo il campo GTK_LIST()->selection, senza per&ograve; modificarlo dal momento
+che i campi interni debbono essere gestiti dalle funzioni gtk_list_*().
+
+Le modalit&agrave; di selezione in una GtkList, e quindi il contenuto di
+GTK_LIST()->selection, sono determinate dal campo selection_mode:
+
+selection_mode pu&ograve; assumere uno dei seguenti valori:
+<itemize>
+<item> GTK_SELECTION_SINGLE - La selezione pu&ograve; essere o NULL oppure
+ un puntatore GList* per un singolo elemento
+ selezionato.
+
+<item> GTK_SELECTION_BROWSE - La selezione &egrave; null se la lista non contiene
+ alcun widget o se ha solo widget non sensibili,
+ oppure pu&ograve; contenere un puntatore a una struttura
+ GList, e quindi esattamente un elemento di lista.
+
+<item> GTK_SELECTION_MULTIPLE - La selezione &egrave; ``NULL'' se non &egrave; selezionato
+ alcun elemento di lista, oppure un puntatore GList al
+ primo elemento selezionato. Quello, a sua volta, punta
+ a una struttura GList per il secondo elemento selezionato
+ e cos&igrave; via.
+
+<item> GTK_SELECTION_EXTENDED - La selezione &egrave; sempre NULL.
+</itemize>
+<p>
+Il valore per difetto &egrave; GTK_SELECTION_MULTIPLE.
+
+<sect1> Segnali
+<p>
+<tscreen><verb>
+void GtkList::selection_changed (GtkList *LIST)
+</verb></tscreen>
+
+Questo segnale verr&agrave; invocato ogni volta che il campo di
+selezione di una GtkList &egrave; cambiato. Questo accade quando
+un figlio della GtkList viene selezionato o deselezionato.
+
+<tscreen><verb>
+void GtkList::select_child (GtkList *LIST, GtkWidget *CHILD)
+</verb></tscreen>
+
+Questo segnale viene invocato quando un fuglio di una GtkList
+sta per essere selezionato. Questo accade principalmente in
+occasione di chiamate a gtk_list_select_item() e gtk_list_select_child(),
+di pressioni di bottoni e a volte pu&ograve; venir fatto scattare indirettamente
+in altre occasioni, in cui vengono aggiunti o rimossi dei figli
+dalla GtkList.
+
+<tscreen><verb>
+void GtkList::unselect_child (GtkList *LIST, GtkWidget *CHILD)
+</verb></tscreen>
+
+Questo segnale viene invocato quando un figlio della GtkList sta
+per essere deselezionato. Ci&ograve; accade principalmente in occasione
+di chiamate a gtk_list_unselect_item() e gtk_list_unselect_child(),
+di pressioni di bottoni, e a volte pu&ograve; venir fatto scattare indirettamente
+in altre occasioni, in cui vengono aggiunti o rimossi dei figli
+dalla GtkList.
+
+<sect1> Funzioni
+<p>
+<tscreen><verb>
+guint gtk_list_get_type (void)
+</verb></tscreen>
+
+Restituisce l'identificatore di tipo `GtkList'.
+
+<tscreen><verb>
+GtkWidget* gtk_list_new (void)
+</verb></tscreen>
+
+Crea un nuovo oggetto `GtkList'. Il nuovo widget viene
+restituito sotto forma di un puntoatore ad un oggetto
+`GtkWidget&igrave;'. In caso di fallimento, viene ritornato NULL.
+
+<tscreen><verb>
+void gtk_list_insert_items (GtkList *LIST, GList *ITEMS, gint POSITION)
+</verb></tscreen>
+
+Inserisce degli elementi di lista nella LIST, a partire da
+POSITION. ITEMS ITEMS &egrave; una lista doppiamente collegata, in
+cui ci si aspetta che i puntatori di ogni nodo puntino a
+un GtkListItem appena creato. I nodi GList di ITEMS vengono
+assunti dalla LIST.
+
+<tscreen><verb>
+void gtk_list_append_items (GtkList *LIST, GList *ITEMS)
+</verb></tscreen>
+
+Inserisce elementi di lista proprio come gtk_list_insert_items(),
+ma alla fine della LIST. I nodi GList di ITEMS vengono
+assunti dalla LIST.
+
+<tscreen><verb>
+void gtk_list_prepend_items (GtkList *LIST, GList *ITEMS)
+</verb></tscreen>
+
+Inserisce elementi di lista proprio come gtk_list_insert_items(),
+ma al principio della LIST. I nodi GList di ITEMS vengono
+assunti dalla LIST.
+
+<tscreen><verb>
+void gtk_list_remove_items (GtkList *LIST, GList *ITEMS)
+</verb></tscreen>
+
+Rimuove degli elementi di lista dalla LIST. ITEMS &egrave; una lista
+doppiamente collegata in cui ci si aspetta che i puntatori di
+ogni nodo puntino a un figlio diretto di LIST. E' poi responsabilit&agrave;
+del chiamante di fare una chiamata a g_list_free(ITEMS). E' anche
+necessario che il chiamante distrugga lui stesso gli elementi della
+lista.
+
+<tscreen><verb>
+void gtk_list_clear_items (GtkList *LIST, gint START, gint END)
+</verb></tscreen>
+
+Rimuove e distrugge elementi di lista da LIST. Un widget ne &egrave;
+interessato se la sua posizione corrente all'interno di LIST &egrave; compreso
+fra START ed END.
+
+<tscreen><verb>
+void gtk_list_select_item (GtkList *LIST, gint ITEM)
+</verb></tscreen>
+
+Invoca il segnale GtkList::select_child per un elemento di lista
+specificato dalla sua posizione corrente all'interno di LIST.
+
+<tscreen><verb>
+void gtk_list_unselect_item (GtkList *LIST, gint ITEM)
+</verb></tscreen>
+
+Invoca il segnale GtkList::unselect_child per un elemento di lista
+specificato dalla sua posizione corrente all'interno di LIST.
+
+<tscreen><verb>
+void gtk_list_select_child (GtkList *LIST, GtkWidget *CHILD)
+</verb></tscreen>
+
+Invoca il segnale GtkList::select_child per uno specifico CHILD.
+
+<tscreen><verb>
+void gtk_list_unselect_child (GtkList *LIST, GtkWidget *CHILD)
+</verb></tscreen>
+
+Invoca il segnale GtkList::unselect_child per uno specifico CHILD.
+
+<tscreen><verb>
+gint gtk_list_child_position (GtkList *LIST, GtkWidget *CHILD)
+</verb></tscreen>
+
+Restituisce la posizione di CHILD all'interno di LIST. In caso di fallimento,
+viene restituito `-1'.
+
+<tscreen><verb>
+void gtk_list_set_selection_mode (GtkList *LIST, GtkSelectionMode MODE)
+</verb></tscreen>
+
+Assegna a LIST il modo di selezione MODE, che pu&ograve; essere uno fra
+GTK_SELECTION_SINGLE, GTK_SELECTION_BROWSE, GTK_SELECTION_MULTIPLE o
+GTK_SELECTION_EXTENDED.
+
+<tscreen><verb>
+GtkList* GTK_LIST (gpointer OBJ)
+</verb></tscreen>
+
+Fa il cast di un generico puntatore a `GtkList*'. Per maggiori
+informazioni vedere Standard Macros::.
+
+<tscreen><verb>
+GtkListClass* GTK_LIST_CLASS (gpointer CLASS)
+</verb></tscreen>
+
+Fa il cast di un generico puntatore a `GtkListClass*'. Per maggiori
+informazioni vedere Standard Macros::.
+
+<tscreen><verb>
+gint GTK_IS_LIST (gpointer OBJ)
+</verb></tscreen>
+
+Determina se un generico puntatore si riferisce ad un oggetto `GtkList'.
+Per maggiori informazioni vedere Standard Macros::.
+
+
+<sect1> Esempio
+<p>
+Diamo di seguito un programma di esempio che stamper&agrave; i campbiamenti
+della selezione di una GtkList, e vi lascia ``imprigionare'' gli elementi
+di una lista selezionandoli con il pulsante destro del mouse:
+
+<tscreen><verb>
+/* compilate questo programma con:
+ * $ gcc -I/usr/local/include/ -lgtk -lgdk -lglib -lX11 -lm -Wall main.c
+ */
+
+/* includiamo i file header di gtk+
+ * includiamo stdio.h, ne abbiamo bisogno per printf()
+ */
+#include <gtk/gtk.h>
+#include <stdio.h>
+
+/* Questa e' la nostra stringa di identificazione dei dati per assegnarli
+ * ad elementi di lista
+ */
+const gchar *list_item_data_key="list_item_data";
+
+
+/* prototipi per i gestori di segnale che connetteremo
+ * al widget GtkList
+ */
+static void sigh_print_selection (GtkWidget *gtklist,
+ gpointer func_data);
+static void sigh_button_event (GtkWidget *gtklist,
+ GdkEventButton *event,
+ GtkWidget *frame);
+
+
+/* funzione main per predisporre l'interfaccia utente */
+
+gint main (int argc, gchar *argv[])
+{
+ GtkWidget *separator;
+ GtkWidget *window;
+ GtkWidget *vbox;
+ GtkWidget *scrolled_window;
+ GtkWidget *frame;
+ GtkWidget *gtklist;
+ GtkWidget *button;
+ GtkWidget *list_item;
+ GList *dlist;
+ guint i;
+ gchar buffer[64];
+
+
+ /* inizializza gtk+ (e di conseguenza gdk) */
+
+ gtk_init(&amp;argc, &amp;argv);
+
+
+ /* crea una finestra in cui mettere tutti i widget
+ * connette gtk_main_quit() al segnale "destroy" della finestra
+ * per gestire le richieste di chiusura finestra del window manager
+ */
+ window=gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title(GTK_WINDOW(window), "GtkList Example");
+ gtk_signal_connect(GTK_OBJECT(window),
+ "destroy",
+ GTK_SIGNAL_FUNC(gtk_main_quit),
+ NULL);
+
+
+ /* all'interno della finestra abbiamo bisogno di una scatola
+ * in cui mettere i widget verticalmente */
+ vbox=gtk_vbox_new(FALSE, 5);
+ gtk_container_border_width(GTK_CONTAINER(vbox), 5);
+ gtk_container_add(GTK_CONTAINER(window), vbox);
+ gtk_widget_show(vbox);
+
+ /* questa &egrave; la finestra scorribile in cui mettere il widget GtkList */
+ scrolled_window=gtk_scrolled_window_new(NULL, NULL);
+ gtk_widget_set_usize(scrolled_window, 250, 150);
+ gtk_container_add(GTK_CONTAINER(vbox), scrolled_window);
+ gtk_widget_show(scrolled_window);
+
+ /* crea il widget GtkList
+ * connette il gestore di segnale sigh_print_selection()
+ * al segnale "selection_changed" della GtkList, per stampare
+ * gli elementi selezionati ogni volta che la selezione cambia
+ */
+ gtklist=gtk_list_new();
+ gtk_container_add(GTK_CONTAINER(scrolled_window), gtklist);
+ gtk_widget_show(gtklist);
+ gtk_signal_connect(GTK_OBJECT(gtklist),
+ "selection_changed",
+ GTK_SIGNAL_FUNC(sigh_print_selection),
+ NULL);
+
+ /* creiamo una "Prigione" (Prison) in cui mettere gli elementi di lista ;)
+ */
+ frame=gtk_frame_new("Prison");
+ gtk_widget_set_usize(frame, 200, 50);
+ gtk_container_border_width(GTK_CONTAINER(frame), 5);
+ gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT);
+ gtk_container_add(GTK_CONTAINER(vbox), frame);
+ gtk_widget_show(frame);
+
+ /* connette il gestore di segnale sigh_button_event() alla GtkList
+ * il quale gestira' l'"imprigionamento" degli elementi di lista
+ */
+ gtk_signal_connect(GTK_OBJECT(gtklist),
+ "button_release_event",
+ GTK_SIGNAL_FUNC(sigh_button_event),
+ frame);
+
+ /* crea un separatore
+ */
+ separator=gtk_hseparator_new();
+ gtk_container_add(GTK_CONTAINER(vbox), separator);
+ gtk_widget_show(separator);
+
+ /* infine creiamo un bottone e connettiamone il segnale "clicked"
+ * alla distruzione della finestra
+ */
+ button=gtk_button_new_with_label("Close");
+ gtk_container_add(GTK_CONTAINER(vbox), button);
+ gtk_widget_show(button);
+ gtk_signal_connect_object(GTK_OBJECT(button),
+ "clicked",
+ GTK_SIGNAL_FUNC(gtk_widget_destroy),
+ GTK_OBJECT(window));
+
+
+ /* a questo punto creiamo 5 elementi di lista, ognuno con la
+ * propria etichetta, e li aggiungiamo alla GtkList usando
+ * gtk_container_add(). Inoltre, recuperiamo la stringa di testo
+ * dall'etichetta e la associamo, per ogni elemento, a
+ * list_item_data_key
+ */
+ for (i=0; i<5; i++) {
+ GtkWidget *label;
+ gchar *string;
+
+ sprintf(buffer, "ListItemContainer with Label #%d", i);
+ label=gtk_label_new(buffer);
+ list_item=gtk_list_item_new();
+ gtk_container_add(GTK_CONTAINER(list_item), label);
+ gtk_widget_show(label);
+ gtk_container_add(GTK_CONTAINER(gtklist), list_item);
+ gtk_widget_show(list_item);
+ gtk_label_get(GTK_LABEL(label), &amp;string);
+ gtk_object_set_data(GTK_OBJECT(list_item),
+ list_item_data_key,
+ string);
+ }
+
+ /* qui creiamo altre 5 etichette, questa volta usando
+ * per la creazione gtk_list_item_new_with_label().
+ * Non possiamo recuperare la stringa di testo dall'etichetta
+ * dal momento che non disponiamo di puntatori alle etichette,
+ * quindi associamo semplicemente il list_item_data_key di ogni
+ * elemento di lista con la medesima stringa di testo.
+ * Per aggiungere elementi di lista, li mettiamo tutti in una lista
+ * doppiamente collegata (GList), e quindi li aggiungiamo con una
+ * unica chiamata a gtk_list_append_items().
+ * Dal momento che usiamo g_list_prepend() per mettere gli elementi
+ * nella lista doppiamente collegata, il loro ordine sara' discendente
+ * (invece che ascendente come sarebbe se usassimo g_list_append())
+ */
+ dlist=NULL;
+ for (; i<10; i++) {
+ sprintf(buffer, "List Item with Label %d", i);
+ list_item=gtk_list_item_new_with_label(buffer);
+ dlist=g_list_prepend(dlist, list_item);
+ gtk_widget_show(list_item);
+ gtk_object_set_data(GTK_OBJECT(list_item),
+ list_item_data_key,
+ "ListItem with integrated Label");
+ }
+ gtk_list_append_items(GTK_LIST(gtklist), dlist);
+
+ /* e finalmente vogliamo vedere la finestra, non e' vero? ;)
+ */
+ gtk_widget_show(window);
+
+ /* lancia il ciclo principale di gtk
+ */
+ gtk_main();
+
+ /* si arriva a questo punto dopo la chiamata di gtk_main_quit(),
+ * il che accade quando viene distrutta la finestra principale
+ */
+ return 0;
+}
+
+/* questo e' il gestore di segnale che e' stato connesso all'evento di
+ * pressione/rilascio del bottone della GtkList
+ */
+void
+sigh_button_event (GtkWidget *gtklist,
+ GdkEventButton *event,
+ GtkWidget *frame)
+{
+ /* facciamo qualcosa solo nel caso di rilascio del terzo bottone
+ * (quello piu' a destra)
+ */
+ if (event->type==GDK_BUTTON_RELEASE &amp;&amp;
+ event->button==3) {
+ GList *dlist, *free_list;
+ GtkWidget *new_prisoner;
+
+ /* recuperiamo l'elemento di lista selezionato correntemente,
+ * che sara' il nostro prossimo prigioniero ;)
+ */
+ dlist=GTK_LIST(gtklist)->selection;
+ if (dlist)
+ new_prisoner=GTK_WIDGET(dlist->data);
+ else
+ new_prisoner=NULL;
+
+ /* cerchiamo elementi di lista gia' imprigionati,
+ * li rimetteremo nella lista.
+ * Ricordare di liberare la lista doppiamente collegata
+ * che viene restituita da gtk_container_children()
+ */
+ dlist=gtk_container_children(GTK_CONTAINER(frame));
+ free_list=dlist;
+ while (dlist) {
+ GtkWidget *list_item;
+
+ list_item=dlist->data;
+
+ gtk_widget_reparent(list_item, gtklist);
+
+ dlist=dlist->next;
+ }
+ g_list_free(free_list);
+
+ /* se abbiamo un nuovo prigioniero, lo rimuoviamo
+ * dalla GtkList e lo mettiamo nella cornice della
+ * "Prigione". Dobbiamo prima deselezionare l'elemento
+ */
+ if (new_prisoner) {
+ GList static_dlist;
+
+ static_dlist.data=new_prisoner;
+ static_dlist.next=NULL;
+ static_dlist.prev=NULL;
+
+ gtk_list_unselect_child(GTK_LIST(gtklist),
+ new_prisoner);
+ gtk_widget_reparent(new_prisoner, frame);
+ }
+ }
+}
+
+/* questo e' il gestore di segnaleche viene chiamato de la
+ * GtkList emette il segnale "selection_changed"
+ */
+void
+sigh_print_selection (GtkWidget *gtklist,
+ gpointer func_data)
+{
+ GList *dlist;
+
+ /* recuperiamo la lista doppiamente collegata degli
+ * elementi selezionati della GtkList, ricordate di
+ * trattarla come sola lettura
+ */
+ dlist=GTK_LIST(gtklist)->selection;
+
+ /* se non ci sono elementi selezionati non c'e' altro da
+ * fare che dirlo all'utente
+ */
+ if (!dlist) {
+ g_print("Selection cleared\n");
+ return;
+ }
+ /* ok, abbiamo una selezione e quindi lo scriviamo
+ */
+ g_print("The selection is a ");
+
+ /* ottieniamo l'elemento di lista dalla lista doppiamente
+ * collegata e poi richiediamo i dati associati con
+ * list_item_data_key. Poi semplicemente li stampiamo
+ */
+ while (dlist) {
+ GtkObject *list_item;
+ gchar *item_data_string;
+
+ list_item=GTK_OBJECT(dlist->data);
+ item_data_string=gtk_object_get_data(list_item,
+ list_item_data_key);
+ g_print("%s ", item_data_string);
+
+ dlist=dlist->next;
+ }
+ g_print("\n");
+}
+</verb></tscreen>
+
+<sect1> Il Widget Elemento di Lista (List Item)
+<p>
+Il widget GtkListItem &egrave; progettato allo scopo di essere un contenitore
+collegato ad un figlio, per fornire le funzioni per la selezione e deselezione
+allo stesso modo in cui il widget GtkList ne ha bisogno per i propri figli.
+
+Un GtkListItem ha la sua propria finestra per ricevere eventi, e ha il suo
+proprio colore di sfondo, che di solito &egrave; bianco.
+
+Dal momento che questo widget deriva direttamente da GtkItem, pu&ograve; essere
+trattato come tale usando la macro GTK_ITEM(ListItem), vedere il widget
+GtkItem per ulteriori informazioni.
+Di solito un GtkListItem ha solo un'etichetta per identificare per esempio
+un nome di file all'interno di una GtkList -- per cui viene fornita la
+funzione appropriata gtk_list_item_new_with_label(). Si pu&ograve; ottenere lo
+stesso effetto creando una GtkLabel da sola, assegnando al suo allineamento
+i valori xalign=0 e yalign=0.5, aggiungendo successivamente un contenitore
+alla GtkListItem.
+
+Dal momento che non si &egrave; obbligati a mettere una GtkLabel, si pu&ograve; anche
+aggiungere una GtkVBox una GtkArrow ecc. alla GtkListItem.
+
+<sect1> Segnali
+<p>
+Un GtkListItem non crea alcun nuovo segnale di per se, ma eredita
+i segnali di GtkItem. Per ulteriori informazioni, vedere GtkItem::.
+
+
+<sect1> Funzioni
+<p>
+
+<tscreen><verb>
+guint gtk_list_item_get_type (void)
+</verb></tscreen>
+
+Restituisce l'identificatore di tipo `GtkListItem'.
+
+<tscreen><verb>
+GtkWidget* gtk_list_item_new (void)
+</verb></tscreen>
+
+Crea un nuovo oggetto `GtkListItem'. Il nuovo widget viene restituito
+sottoforma di un puntatore ad un oggetto `GtkWidget'. In caso di
+fallimento, viene restituito `NULL'.
+
+<tscreen><verb>
+GtkWidget* gtk_list_item_new_with_label (gchar *LABEL)
+</verb></tscreen>
+
+Cre un nuovo oggetto `GtkListItem', avente come unico figlio
+un GtkLabel. Il nuovo widget viene restituito
+sottoforma di un puntatore ad un oggetto `GtkWidget'. In caso di
+fallimento, viene restituito `NULL'.
+
+<tscreen><verb>
+void gtk_list_item_select (GtkListItem *LIST_ITEM)
+</verb></tscreen>
+
+Questa funzione &egrave; essenzialmente un wrapper per una chiamata a
+gtk_item_select (GTK_ITEM (list_item)) che emetter&agrave; il segnale
+GtkItem::select.
+Vedere GtkItem:: per maggiori informazioni.
+
+<tscreen><verb>
+void gtk_list_item_deselect (GtkListItem *LIST_ITEM)
+</verb></tscreen>
+
+Questa funzione &egrave; essenzialmente un wrapper per una chiamata a
+gtk_item_deselect (GTK_ITEM (list_item)) che emetter&agrave; il segnale
+GtkItem::deselect.
+Vedere GtkItem:: per maggiori informazioni.
+
+<tscreen><verb>
+GtkListItem* GTK_LIST_ITEM (gpointer OBJ)
+</verb></tscreen>
+
+Effettua il cast di un puntatore generico a `GtkListItem*'. Vedere
+Standard Macros:: per maggiorni informazioni.
+
+<tscreen><verb>
+GtkListItemClass* GTK_LIST_ITEM_CLASS (gpointer CLASS)
+</verb></tscreen>
+
+Effettua il cast di un puntatore generico a `GtkListItemClass*'. Vedere
+Standard Macros:: per maggiorni informazioni.
+
+<tscreen><verb>
+gint GTK_IS_LIST_ITEM (gpointer OBJ)
+</verb></tscreen>
+
+Determina se un puntatore generico si riferisce ad un oggetto
+`GtkListItem'. Vedere Standard Macros:: per maggiorni informazioni.
+
+<sect1> Esempio
+<p>
+Come esempio su questo argomento, si veda quello relativo alla GtkList,
+che riguarda anche l'uso del GtkListItem.
+
+<sect> Selezione di File (File Selections)
+<p>
+
+Il widget Selezione di File &egrave; un modo rapido e semplice per mostrare una
+finestra di dialogo `File'. Questa si presenta completa di bottoni Ok,
+Cancel e Help, un buon modo per tagliare i tempi di programmazione.
+
+Per creare una nuova finestra di selezione file usate:
+
+<tscreen><verb>
+GtkWidget* gtk_file_selection_new (gchar *title);
+</verb></tscreen>
+
+Per assegnare il nome del file, ad esempio per predisporre una certa
+directory o per dare un certo nome di file per difetto, usate la seguente
+funzione:
+
+<tscreen><verb>
+void gtk_file_selection_set_filename (GtkFileSelection *filesel, gchar *filename);
+</verb></tscreen>
+
+Per recuperare il testo che l'utente ha inserito o che ha selezionato con
+il mouse, si usa la funzione:
+
+<tscreen><verb>
+gchar* gtk_file_selection_get_filename (GtkFileSelection *filesel);
+</verb></tscreen>
+
+Ci sono anche dei puntatori ai widget che sono contenuti all'interno
+del widget di selezione file. Si tratta di:
+
+<itemize>
+<item>dir_list
+<item>file_list
+<item>selection_entry
+<item>selection_text
+<item>main_vbox
+<item>ok_button
+<item>cancel_button
+<item>help_button
+</itemize>
+
+Molto probabilmente potreste voler usare i puntatori a ok_button,
+cancel_button e help_button per segnalarne l'uso.
+
+Ecco un esempio rubato da testgtk.c, nodificato per essere eseguito da
+solo. Come potrete vedere, non c'&egrave; molto pi&ugrave; che la creazione di un
+widget di selezione file. In questo esempio, il bottone Help non fa nulla
+mentre &egrave; mostrato allo schermo, dal momento che non c'&egrave; alcun segnale
+collegato con esso.
+
+<tscreen><verb>
+#include <gtk/gtk.h>
+
+/* Recupera il nome di file selezionato e stampalo a console */
+void file_ok_sel (GtkWidget *w, GtkFileSelection *fs)
+{
+ g_print ("%s\n", gtk_file_selection_get_filename (GTK_FILE_SELECTION (fs)));
+}
+
+void destroy (GtkWidget *widget, gpointer *data)
+{
+ gtk_main_quit ();
+}
+
+int main (int argc, char *argv[])
+{
+ GtkWidget *filew;
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ /* Crea un nuovo widget di selezione file */
+ filew = gtk_file_selection_new ("File selection");
+
+ gtk_signal_connect (GTK_OBJECT (filew), "destroy",
+ (GtkSignalFunc) destroy, &amp;filew);
+ /* Connette ok_button alla funzione file_ok_sel */
+ gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (filew)->ok_button),
+ "clicked", (GtkSignalFunc) file_ok_sel, filew );
+
+ /* Connette cancel_button alla funzione di distruzione del widget */
+ gtk_signal_connect_object (GTK_OBJECT (GTK_FILE_SELECTION (filew)->cancel_button),
+ "clicked", (GtkSignalFunc) gtk_widget_destroy,
+ GTK_OBJECT (filew));
+
+ /* Preassegnamo un nome di file, come se stessimo dando un valore per difetto in
+ dialogo di tipo `` salva con nome '' */
+ gtk_file_selection_set_filename (GTK_FILE_SELECTION(filew),
+ "penguin.png");
+
+ gtk_widget_show(filew);
+ gtk_main ();
+ return 0;
+}
+</verb></tscreen>
+
+<sect>Il Widget Men&ugrave; (Menu Widgets)
+<p>
+Ci sono due modi per creare dei men&ugrave;, quello facile e quello difficile.
+Ognuno &egrave; pi&ugrave; adatto per certe circostanze, ma di solito si pu&ograve; usare il
+modo semplice, cio&eacute; menu_factory (la ``fabbrica dei men&ugrave;''). Il modo
+``difficile'' &egrave; di crearsi tutti i men&ugrave; usando direttamente le chiamate.
+Quello semplice &egrave; di usare le chiamate di tipo gtk_menu_factory. Anche se
+&egrave; un modo molto pi&ugrave; semplice, ci sono svantaggi e vantaggi per ciascuno
+dei due approcci.
+
+La menufactory &egrave; molto pi&ugrave; semplice da usare e per aggiungere dei nuovi
+men&ugrave;, anche se scriversi un po' di funzioni per creare dei men&ugrave; con il
+metodo manuale pu&ograve; dare risultati molto migliori dal punto di vista
+dell'usabilit&agrave;. Con la menufactory, non &egrave; possibile mettere immagini o
+segni '/' nei men&ugrave;.
+<p>
+<sect1>Creazione Manuale di Men&ugrave;
+<p>
+Seguendo la tradizionale arte dell'insegnamento, partiamo dal modo
+difficile. <tt>:)</>
+<p>
+Diamo un'occhiata alle funzioni usate per creare dei men&ugrave;.
+Con questa prima funzione si crea un nuovo men&ugrave;:
+
+<tscreen><verb>
+GtkWidget *gtk_menu_bar_new()
+</verb></tscreen>
+
+Questa funzione crea una nuova barra di men&ugrave;. Per impacchettarla in una
+finestra o si usa la funzione gtk_container_add, oppure, per impacchettarla
+in una scatola, le funzioni box_pack - come con i bottoni.
+
+<tscreen><verb>
+GtkWidget *gtk_menu_new();
+</verb></tscreen>
+
+Questa funzione restituisce un puntatore ad un nuovo men&ugrave;, non viene mai
+realmente mostrato (con gtk_widget_show), serve solo per contenere gli
+elementi del men&ugrave;. Spero che il tutto risulti pi&ugrave; chiaro quando dare
+un'occhiata all'esempio pi&ugrave; sotto.
+<p>
+Le prossime due chiamate sono usate per creare degli elementi che poi
+vengono impacchettati nel men&ugrave;.
+
+<tscreen><verb>
+GtkWidget *gtk_menu_item_new()
+</verb></tscreen>
+
+e
+
+<tscreen><verb>
+GtkWidget *gtk_menu_item_new_with_label(const char *label)
+</verb></tscreen>
+
+Queste chiamate sono usate per creare i menu che devono essere mostrati.
+Ricordate la differenza che esiste fra un ``men&ugrave;'' come quelli creati con
+gtk_menu_new e un ``elemento di men&ugrave;'' (menu item) come quelli creati con
+la funzione creata con gtk_menu_item_new. L'elemento di men&ugrave; sar&agrave; un bottone
+vero e proprio con una azione associata, mentre un men&ugrave; &egrave; solo un contenitore
+che li raccoglie.
+
+<tscreen><verb>
+gtk_menu_item_append()
+
+gtk_menu_item_set_submenu()
+</verb></tscreen>
+
+Le funzioni gtk_menu_item_new_with_label e gtk_menu_item_new si comportano esattamente come
+vi aspettereste dopo aver visto le funzioni che riguardano i bottoni. La prima
+crea un elemento di men&ugrave; con un'etichetta gi&agrave; applicata, mentre la seconda crea
+un nuovo elemento di men&ugrave; vuoto.
+<p>
+Ecco i passi necessari per creare una barra di men&ugrave; con i relativi men&ugrave; collegati:
+<itemize>
+<item> Create un nuovo men&ugrave; con gtk_menu_new()
+<item> Create un elementoa di men&ugrave; con using gtk_menu_item_new(). Questo rappresenta
+ la base del men&ugrave;, e il testo che appare qui sar&agrave; sulla barra stessa.
+<item> Usate delle chiamate multiple a gtk_menu_item_new() per ognuno degli
+ elementi che volete mettere nel vostro men&ugrave;. Usate inoltre gtk_menu_item_append()
+ per mettere assieme ognuno di questi nuovo elementi. Si crea cos&igrave; una lista di
+ elementi di men&ugrave;.
+<item> Usate gtk_menu_item_set_submenu() per attaccare gli elementi di men&ugrave;
+ creati all'elemento di men&ugrave; base (quello creato nel secondo passaggio).
+<item> Create una nuova barra di men&ugrave; usando gtk_menu_bar_new. Questo passo
+ necessita di essere effettuato una sola volta quando si crea una serie di
+ men&ugrave; su una serie di men&ugrave; su una sola barra.
+<item> Usate gtk_menu_bar_append per mettere il men&ugrave; base sulla barra dei men&ugrave;.
+</itemize>
+<p>
+Creare un men&ugrave; a comparsa &egrave; pi&ugrave; o meno la stessa cosa. La differenza &egrave; che il
+il men&ugrave; non viene attivato ``automaticamente'' da una barra, bens&igrave; esplicitamente
+con la chiamata alla funzione gtk_menu_popup() da un evento di pressione di
+un pulsante.
+Seguite questi passaggi:
+<itemize>
+<item>Create una funzione di gestione di un evento. Essa deve seguire il prototipo
+<tscreen>
+static gint handler(GtkWidget *widget, GdkEvent *event);
+</tscreen>
+e usare l'evento per scoprire dove il menu deve essere fatto comparire.
+<item>Nel gestore di evento, se questo &egrave; la pressione di un bottone, trattate
+<tt>event</tt> come l'evento relativo ad un bottone (cosa che in effetti &egrave;)
+e usatelo come mostrato nel codice di esempio per passare informazioni a
+gtk_menu_popup().
+<item>Collegate il gestore di evento a un widget con
+<tscreen>
+gtk_signal_connect_object(GTK_OBJECT(widget), "event",
+ GTK_SIGNAL_FUNC (handler), GTK_OBJECT(menu));
+</tscreen>
+in cui <tt>widget</tt> &egrave; il widget a cui state effettuando il collegamento, e
+<tt>handler</tt> &egrave; la funzione di gestione, mentre <tt>menu</tt> &egrave; un men&ugrave;
+creato con gtk_menu_new(). Quest'ultimo pu&ograve; essere un men&ugrave; che viene anche
+attivato da una barra di men&ugrave;, come mostrato nel codice di esempio.
+</itemize>
+<p>
+<sect1>Esempio di Men&ugrave; Manuale
+<p>
+Per la teoria dovrebbe essere abbastanza. Diamo un'occhiata ad un esempio che
+ci aiuti a chiarire le cose.
+
+<tscreen><verb>
+
+#include <gtk/gtk.h>
+
+static gint button_press (GtkWidget *, GdkEvent *);
+static void menuitem_response (gchar *);
+
+
+int main (int argc, char *argv[])
+{
+
+ GtkWidget *window;
+ GtkWidget *menu;
+ GtkWidget *menu_bar;
+ GtkWidget *root_menu;
+ GtkWidget *menu_items;
+ GtkWidget *vbox;
+ GtkWidget *button;
+ char buf[128];
+ int i;
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ /* crea una nuova finestra */
+ window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title(GTK_WINDOW (window), "GTK Menu Test");
+ gtk_signal_connect(GTK_OBJECT (window), "delete_event",
+ (GtkSignalFunc) gtk_exit, NULL);
+
+ /* Inizializziamo il men&ugrave;, e ricordate: mai applicare
+ * gtk_show_widget() al widget men&ugrave;!!
+ * Questo &egrave; il men&ugrave; che contiene gli elementi, quello che
+ * spunta quando si fa click sul "Men&ugrave; radice" nell'applicazione */
+ menu = gtk_menu_new();
+
+ /* Questo &egrave; il men&ugrave; radice, e l'etichetta sar&agrave; il nome del men&ugrave; che
+ * verr&agrave; mostrato sulla barra dei men&ugrave;. Non ci sar&agrave; alcun gestore di
+ * segnale collegato, dal momento che non fa altro che mostrare il resto
+ * del men&ugrave; quando viene premuto. */
+ root_menu = gtk_menu_item_new_with_label("Root Menu");
+
+ gtk_widget_show(root_menu);
+
+ /* Ora creiamo un ciclo che crea tre elementi di menu per "test-menu".
+ * Notete la chiamata a gtk_menu_append. In questo punto aggiungiamo una
+ * lista di elementi al nostro men&ugrave;. Normalmente, dovremmo poi catturare
+ * il segnale di attivazione per ognuno degli elementi del menu, e creare
+ * una funzione di ritorno per ciascuno di essi, ma qui non li mettiamo per
+ * brevit&agrave;. */
+
+ for(i = 0; i < 3; i++)
+ {
+ /* Copia i nomi in buf. */
+ sprintf(buf, "Test-undermenu - %d", i);
+
+ /* Crea un nuovo elemento di men&ugrave; con un nome... */
+ menu_items = gtk_menu_item_new_with_label(buf);
+
+ /* ...e aggiungilo al men&ugrave;. */
+ gtk_menu_append(GTK_MENU (menu), menu_items);
+
+ /* Fa qualcosa di interessante quando si seleziona l'elemento */
+ gtk_signal_connect_object(GTK_OBJECT(menu_items), "activate",
+ GTK_SIGNAL_FUNC(menuitem_response), (gpointer) g_strdup(buf));
+
+ /* Mostra il widget */
+ gtk_widget_show(menu_items);
+ }
+
+ /* Ora specifichiamo che vogliamo che il men&ugrave; che abbiamo appena creato
+ * sia il men&ugrave; radice *//
+ gtk_menu_item_set_submenu(GTK_MENU_ITEM (root_menu), menu);
+
+ /* Una vbox in cui mettere un men&ugrave; ed un bottone: */
+ vbox = gtk_vbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(window), vbox);
+ gtk_widget_show(vbox);
+
+ /* Crea una barra dei men&ugrave; per metterci i men&ugrave; e l'aggiunge alla finestra principale */
+ menu_bar = gtk_menu_bar_new();
+ gtk_box_pack_start(GTK_BOX(vbox), menu_bar, FALSE, FALSE, 2);
+ gtk_widget_show(menu_bar);
+
+ /* Crea un bottone a cui collegare un men&ugrave; */
+ button = gtk_button_new_with_label("press me");
+ gtk_signal_connect_object(GTK_OBJECT(button), "event",
+ GTK_SIGNAL_FUNC (button_press), GTK_OBJECT(menu));
+ gtk_box_pack_end(GTK_BOX(vbox), button, TRUE, TRUE, 2);
+ gtk_widget_show(button);
+
+ /* E finalmente attacchiamo l'elemento di men&ugrave; alla barra dei men&ugrave; -- questo
+ * &egrave; l'elemento di men&ugrave; "radice" di cui parlavo */
+ gtk_menu_bar_append(GTK_MENU_BAR (menu_bar), root_menu);
+
+ /* La finestra va mostrata sempre come ultimo passo in modo che sia gi&agrave;
+ * completa di tutti i suoi elementi. */
+ gtk_widget_show(window);
+
+ gtk_main ();
+
+ return 0;
+}
+
+
+
+/* Risponde alla pressione di un bottone impostando un men&ugrave; che
+ * viene passato come widget.
+ * Notate che l'argomento "widget" si riferisce al men&ugrave; impostato
+ * e NON al bottone premuto.
+ */
+
+static gint button_press (GtkWidget *widget, GdkEvent *event)
+{
+
+ if (event->type == GDK_BUTTON_PRESS) {
+ GdkEventButton *bevent = (GdkEventButton *) event;
+ gtk_menu_popup (GTK_MENU(widget), NULL, NULL, NULL, NULL,
+ bevent->button, bevent->time);
+ /* Riferisce al codice chiamante che abbiamo trattato l'evento;
+ * la faccenda finisce qui. */
+ return TRUE;
+ }
+
+ /* Riferisce al codice chiamante che abbiamo trattato l'evento; passa avanti. */
+ return FALSE;
+}
+
+
+/* Stampa una stringa quando viene selezionato un elemento di men&ugrave; */
+
+static void menuitem_response (gchar *string)
+{
+ printf("%s\n", string);
+}
+</verb></tscreen>
+
+Si pu&ograve; anche fare in modo che un elemento di men&ugrave; sia insensibile e, usando
+una tabella di acelleratori, collegare dei tasti a delle funzioni di men&ugrave;.
+<p>
+<sect1>Usare GtkMenuFactory
+<p>
+Ora che vi abbiamo mostrato il modo difficile, ecco invece come si fa usando
+le chiamate di gtk_menu_factory.
+<p>
+<sect1>Esempio di Menu Factory
+<p>
+Ecco un esempio di utilizzo della ``Fabbrica'' di Men&ugrave; di GTK (Menu Factory).
+Questo &egrave; il primo file, menus.h. Teniemo dei file menus.c e main.c separati
+a causa delle variabili globali usate nel file menus.c.
+
+<tscreen><verb>
+#ifndef __MENUS_H__
+#define __MENUS_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+void get_main_menu (GtkWidget **menubar, GtkAcceleratorTable **table);
+void menus_create(GtkMenuEntry *entries, int nmenu_entries);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __MENUS_H__ */
+</verb></tscreen>
+<p>
+Ed ecco il file menus.c.
+
+<tscreen><verb>
+
+#include <gtk/gtk.h>
+#include <strings.h>
+
+#include "main.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);
+
+/* Questa &egrave; la struttuta GtkMenuEntry, che viene usata per creare dei nuovi
+ * men&ugrave;. Il primo membro &agrave; la stringa di definizione del men&ugrave;. Il secondo
+ * &egrave; il tasto acceleratore predefinito, usato per accedere a questa funzione
+ * con la tastiera. Il terzo &egrave; la funzione di ritorno che viene chiamata
+ * quando si seleziona con la tastiera o il mouse questo elemento di men&ugrave;.
+ * L'ultimo membro costituisce il dato che viene passato alla funzione di
+ * ritorno. */
+
+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}
+};
+
+/* calculail numero di menu_item */
+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)
+{
+ 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);
+ }
+}
+
+void menus_create(GtkMenuEntry * entries, int nmenu_entries)
+{
+ char *accelerator;
+ int i;
+
+ if (initialize)
+ menus_init();
+
+ if (entry_ht)
+ for (i = 0; i < nmenu_entries; i++) {
+ accelerator = g_hash_table_lookup(entry_ht, entries[i].path);
+ if (accelerator) {
+ if (accelerator[0] == '\0')
+ entries[i].accelerator = NULL;
+ else
+ entries[i].accelerator = accelerator;
+ }
+ }
+ gtk_menu_factory_add_entries(factory, entries, nmenu_entries);
+
+ for (i = 0; i < nmenu_entries; i++)
+ if (entries[i].widget) {
+ gtk_signal_connect(GTK_OBJECT(entries[i].widget), "install_accelerator",
+ (GtkSignalFunc) menus_install_accel,
+ entries[i].path);
+ gtk_signal_connect(GTK_OBJECT(entries[i].widget), "remove_accelerator",
+ (GtkSignalFunc) menus_remove_accel,
+ entries[i].path);
+ }
+}
+
+static gint menus_install_accel(GtkWidget * widget, gchar * signal_name, gchar key, gchar modifiers, gchar * path)
+{
+ char accel[64];
+ char *t1, t2[2];
+
+ accel[0] = '\0';
+ if (modifiers & GDK_CONTROL_MASK)
+ strcat(accel, "<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_string_hash, g_string_equal);
+
+ g_hash_table_insert(entry_ht, path, g_strdup(accel));
+
+ return TRUE;
+}
+
+static void menus_remove_accel(GtkWidget * widget, gchar * signal_name, gchar * path)
+{
+ char *t;
+
+ if (entry_ht) {
+ t = g_hash_table_lookup(entry_ht, path);
+ g_free(t);
+
+ g_hash_table_insert(entry_ht, path, g_strdup(""));
+ }
+}
+
+void menus_set_sensitive(char *path, int sensitive)
+{
+ GtkMenuPath *menu_path;
+
+ if (initialize)
+ menus_init();
+
+ menu_path = gtk_menu_factory_find(factory, path);
+ if (menu_path)
+ gtk_widget_set_sensitive(menu_path->widget, sensitive);
+ else
+ g_warning("Impossibile assegnare sensibilit&agrave; a men&ugrave; inesistente: %s", path);
+}
+
+</verb></tscreen>
+<p>
+Ed ecco main.h
+
+<tscreen><verb>
+#ifndef __MAIN_H__
+#define __MAIN_H__
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+void file_quit_cmd_callback(GtkWidget *widget, gpointer data);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __MAIN_H__ */
+
+</verb></tscreen>
+<p>
+E main.c
+
+<tscreen><verb>
+#include <gtk/gtk.h>
+
+#include "main.h"
+#include "menus.h"
+
+
+int main(int argc, char *argv[])
+{
+ GtkWidget *window;
+ GtkWidget *main_vbox;
+ GtkWidget *menubar;
+
+ GtkAcceleratorTable *accel;
+
+ gtk_init(&amp;argc, &amp;argv);
+
+ window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_signal_connect(GTK_OBJECT(window), "destroy",
+ GTK_SIGNAL_FUNC(file_quit_cmd_callback),
+ "WM destroy");
+ gtk_window_set_title(GTK_WINDOW(window), "Menu Factory");
+ gtk_widget_set_usize(GTK_WIDGET(window), 300, 200);
+
+ main_vbox = gtk_vbox_new(FALSE, 1);
+ gtk_container_border_width(GTK_CONTAINER(main_vbox), 1);
+ gtk_container_add(GTK_CONTAINER(window), main_vbox);
+ gtk_widget_show(main_vbox);
+
+ get_main_menu(&amp;menubar, &amp;accel);
+ gtk_window_add_accelerator_table(GTK_WINDOW(window), accel);
+ gtk_box_pack_start(GTK_BOX(main_vbox), menubar, FALSE, TRUE, 0);
+ gtk_widget_show(menubar);
+
+ gtk_widget_show(window);
+ gtk_main();
+
+ return(0);
+}
+
+/* Questo &egrave; per mostrare come si usano le funzioni di ritorno quando
+ * si utilizza la MenuFactory. Spesso, si mettono tutte le funzioni di
+ * callback in un file separato, e le si fanno chiamare le funzioni
+ * appropriate da l&igrave;. Cos&igrave; le cose sono pi&ugrave; organizzate. */
+void file_quit_cmd_callback (GtkWidget *widget, gpointer data)
+{
+ g_print ("%s\n", (char *) data);
+ gtk_exit(0);
+}
+</verb></tscreen>
+<p>
+Ed infine un bel makefile per semplificare la compilazione.
+
+<tscreen><verb>
+CC = gcc
+PROF = -g
+C_FLAGS = -Wall $(PROF) -L/usr/local/include -DDEBUG
+L_FLAGS = $(PROF) -L/usr/X11R6/lib -L/usr/local/lib
+L_POSTFLAGS = -lgtk -lgdk -lglib -lXext -lX11 -lm
+PROGNAME = at
+
+O_FILES = menus.o main.o
+
+$(PROGNAME): $(O_FILES)
+ rm -f $(PROGNAME)
+ $(CC) $(L_FLAGS) -o $(PROGNAME) $(O_FILES) $(L_POSTFLAGS)
+
+.c.o:
+ $(CC) -c $(C_FLAGS) $<
+
+clean:
+ rm -f core *.o $(PROGNAME) nohup.out
+distclean: clean
+ rm -f *~
+</verb></tscreen>
+<p>
+Per il momento, accontentatevi di questo esempio. Pi&ugrave; avanti aggiungeremo
+una spiegazione ed un bel po' di commenti.
+
+
+<sect> Widget non documentati
+<p>
+Per questi sarebbe utile il contributo degli autori! :) Prendete in
+considerazione la possibilit&agrave; di contribuire al nostro tutorial.
+
+Se dovete usare uno di questi widget non documentati, vi suggeriamo
+caldamente di dare un'occhiata ai loro rispettivi file header nella
+distribuzione di GTK. I nomi delle funzioni di GTK sono molto descrittivi.
+Non appena si capisce come funzionano le cose, non &egrave;
+difficile dedurre il modo d'uso di un widget semplicemente guardando la
+dichiarazione di funzione ad esso associata. Aggiungendo a questo qualche
+spunto tratto dal codice di altri non dovrebbero esserci problemi.
+
+Quando avrete raggiunto una comprensione globale di tutte le funzioni
+di un widget non documentato, considerate la possibilit&agrave; di scrivere
+un tutorial su di esso, in modo che altri possano beneficiare del
+vostro lavoro.
+
+<sect1> Ingressi di testo (Text Entries)
+<p>
+
+<sect1> Selezioni di colore (Color Selections)
+<p>
+
+<sect1> Controlli di intervallo (Range Controls)
+<p>
+
+<sect1> Righelli (Rulers)
+<p>
+
+<sect1> Caselle di testo (Text Boxes)
+<p>
+
+<sect1> Anteprime
+<p>
+
+(Potrebbe essere necessario riscrivere questa parte per conformarsi allo stile
+del resto del tutorial)
+
+<p>
+Le anteprime servono a un certo numero di cose in GIMP/GTK. La pi&ugrave;
+importante &egrave; questa: a risoluzioni molto alte le immagini possono
+facilmente occupare diverse decine di megabyte di memoria; ogni operazione
+su immagini cos&igrave; grosse pu&ograve richiedere molto tempo. Se per la
+scelta di una data modifica vi occorrono 5-10 tentativi (cio&egrave; 10-20
+passi, poich&eacute; &egrave; necessario ripristinare l'originale se si
+&egrave; commesso un errore), possono volerci letteralmente delle ore per
+fare quella giusta - se non si rimane a corto di memoria prima! Coloro che
+hanno passato ore in camera oscura conoscono la sensazione. In questi casi
+le anteprime sono utilissime!
+
+Ma la seccatura dell'attesa non &egrave; l'unico caso. Spesso &egrave; utile
+confrontare la versione precedente con la successiva affiancandole, o almeno
+alternandole. Se si sta lavorando con grandi immagini e ritardi di una decina
+di secondi un confronto efficace &egrave; quantomeno difficile da fare.
+Per immagini di 30 mega (4 pollici per 6 pollici, 600 punti per pollice, 24 bit)
+tale confronto risulta impraticabile per la maggior parte degli utenti. In
+questo caso le anteprime sono di grande aiuto!
+
+Ma c'&egrave; di pi&ugrave;. Con le anteprime &egrave; possibile scrivere
+plug-in per ottenere addirittura anteprime di anteprime (per esempio, la
+simulazione del pacchetto di filtri). Questi plug-in possono cos&igrave;
+fornire un certo numero di anticipazioni di quel che si otterrebbe applicando
+certe opzioni. Un simile approccio funziona come una tavolozza di anteprime,
+ed &egrave; molto efficace per piccoli cambiamenti!
+
+Non &egrave; finita. Per alcuni plug-in pu&ograve; essere necessario un
+intervento umano in tempo reale specifico per ogni immagine. Nel plug-in
+SuperNova, ad esempio, vengono chieste le coordinate del centro della
+futura supernova. Il modo pi&ugrave; semplice per fare questo &egrave;
+senza dubbio quello di mostrare un'anteprima all'utente chiedendogli di
+selezionare interattivamente il centro.
+
+Infine, un paio di applicazioni tipiche. Le anteprime possono essere usate
+anche quando non si sta lavorando con grandi immagini. Per esempio, sono
+utili quando si stanno calcolando dei pattern complicati (date un'occhiata
+al venerabile plug in ``Diffraction'' e a molti altri!). Altro esempio:
+date un'occhiata al plug-in di rotazione della mappa dei colori (in allestimento).
+Le anteprime possono anche essere usate per visualizzare in un plug-in
+piccoli logo o, addirittura, l'immagine dell'Autore!
+
+Quando non usare le anteprime
+
+Le anteprime non vanno usate per grafici, disegni ecc., poich&eacute; per
+queste cose GDK &egrave; molto pi&ugrave; veloce. Le anteprime vanno usate
+solo per immagini derivate da un'elaborazione!
+
+Le anteprime possono essere inserite dappertutto. In un vbox, in un hbox,
+in una tabella, in un bottone, ecc. Sicuramente per&ograve; hanno il loro
+look migliore se bordate con delle cornici (frame). Le anteprime non hanno
+bordi propri e appaiono piatte senza (naturalmente, se quel che si vuole
+&egrave; proprio un aspetto piatto...). I bordi possono essere creati con
+delle cornici.
+
+ [Image][Image]
+
+Le anteprime sono per molti aspetti simili agli altri widget in GTK (con
+tutto ci&ograve; che questo implica), con l'eccezione di avere una
+caratteristica in pi&ugrave;: &egrave; necessario che siano riempite con
+qualche tipo di immagine! Inizialmente parleremo solo dell'aspetto GTK
+delle anteprime e successivamente discuteremo di come riempirle.
+
+Semplicemente:
+
+<tscreen><verb>
+ /* Crea un widget di anteprima,
+ inizializzane le dimensioni
+ e visualizzalo */
+GtkWidget *preview;
+preview=gtk_preview_new(GTK_PREVIEW_COLOR)
+ /* Alternativamente:
+ GTK_PREVIEW_GRAYSCALE);*/
+gtk_preview_size (GTK_PREVIEW (preview), WIDTH, HEIGHT);
+gtk_widget_show(preview);
+my_preview_rendering_function(preview);
+</verb></tscreen>
+
+Come gi&agrave; detto, le anteprime hanno un buon aspetto dentro le cornici,
+quindi:
+
+<tscreen><verb>
+GtkWidget *create_a_preview(int Width,
+ int Height,
+ int Colorfulness)
+{
+ GtkWidget *preview;
+ GtkWidget *frame;
+
+ frame = gtk_frame_new(NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
+ gtk_container_border_width (GTK_CONTAINER(frame),0);
+ gtk_widget_show(frame);
+
+ preview=gtk_preview_new (Colorfulness?GTK_PREVIEW_COLOR
+ :GTK_PREVIEW_GRAYSCALE);
+ gtk_preview_size (GTK_PREVIEW (preview), Width, Height);
+ gtk_container_add(GTK_CONTAINER(frame),preview);
+ gtk_widget_show(preview);
+
+ my_preview_rendering_function(preview);
+ return frame;
+}
+
+</verb></tscreen>
+
+Questa &egrave; una semplice anteprima. Questa funzione restituisce la cornice
+``madre'', in modo che sia possibile metterla in qualche altro posto nella vostra
+interfaccia. Naturalmente &egrave; possibile passare alla routine la cornice
+madre come parametro. In molte situazioni, comunque, il contenuto di un'anteprima
+viene aggiornato continuamente dall'applicazione; in questi casi potreste
+preferire passare alla funzione ``create_a_preview()'' un puntatore
+all'anteprima, ottenendone cos&igrave; il controllo dopo.
+
+Un'avvertimento pi&ugrave; importante che potrebbe un giorno risparmiarvi
+tanto tempo perso: a volte &egrave; preferibile etichettare le anteprime;
+ad esempio, &egrave; possibile etichettare l'anteprima contenente l'immagine
+originale come ``Originale'' e quella contenente l'immagine modificata come
+``Modificata''. Potrebbe capitarvi di impacchettare in un vbox l'anteprima
+insieme con l'etichetta associata. L'insidia inattesa sta nel fatto che se
+l'etichetta &egrave; pi&ugrave; ampia dell'anteprima (cosa che pu&ograve;
+accadere per una variet&agrave; di motivi da voi non prevedibili, come il
+fatto che la dimensione dell'anteprima viene decisa dinamicamente, o la
+dimensione del font), la cornice si espande e non risulta pi&ugrave;
+perfettamente aderente all'anteprima. Questo stesso problema probabilmente
+pu&ograve; verificarsi anche in altre situazioni.
+
+ [Image]
+
+La soluzione &egrave; quella di mettere l'anteprima e l'etichetta in una
+tabella 2x1 e di legarle insieme chiamando la funzione gtk_table_attach con
+i seguenti parametri (questa &egrave; una delle varianti possibili,
+naturalmente; l'importante &egrave; che non ci sia GTK_FILL nella seconda
+gtk_table_attach):
+
+<tscreen><verb>
+gtk_table_attach(GTK_TABLE(table),label,0,1,0,1,
+ 0,
+ GTK_EXPAND|GTK_FILL,
+ 0,0);
+gtk_table_attach(GTK_TABLE(table),frame,0,1,1,2,
+ GTK_EXPAND,
+ GTK_EXPAND,
+ 0,0);
+</verb></tscreen>
+
+Ed ecco il risultato:
+
+ [Image]
+
+Altri suggerimenti
+
+La maniera pi&ugrave; semplice per rendere cliccabile un'anteprima &egrave;
+quella di metterla dentro un bottone. Questo ha anche l'effetto di aggiungere
+un bel bordo attorno all'anteprima, il che rende superfluo metterla in una
+cornice.
+
+Questo &egrave; tutto per quel che riguarda GTK.
+
+
+Completare un'anteprima
+
+Per impratichirci con le basi del completamento delle anteprime, creiamo
+il seguente disegno (trovato per tentativi):
+
+ [Image]
+
+<tscreen><verb>
+void
+my_preview_rendering_function(GtkWidget *preview)
+{
+#define SIZE 100
+#define HALF (SIZE/2)
+
+ guchar *row=(guchar *) malloc(3*SIZE); /* 3 bits per dot */
+ gint i, j; /* Coordinates */
+ double r, alpha, x, y;
+
+ if (preview==NULL) return; /* Di solito aggiungo questo per */
+ /* evitare piantamenti stupidi. */
+ /* Probabilmente bisognerebbe */
+ /* assicurarsi che tutto sia stato*/
+ /* inizializzato con successo */
+ for (j=0; j < ABS(cos(2*alpha)) ) { /* Siamo dentro la sagoma? */
+ /* glib.h contiene ABS(x). */
+ row[i*3+0] = sqrt(1-r)*255; /* Definisce il Rosso */
+ row[i*3+1] = 128; /* Definisce il Verde */
+ row[i*3+2] = 224; /* Definisce il Blu */
+ } /* "+0" &egrave; per allineamento */
+ else {
+ row[i*3+0] = r*255;
+ row[i*3+1] = ABS(sin((float)i/SIZE*2*PI))*255;
+ row[i*3+2] = ABS(sin((float)j/SIZE*2*PI))*255;
+ }
+ }
+ gtk_preview_draw_row( GTK_PREVIEW(preview),row,0,j,SIZE);
+ /* Inserisce "row" in "preview" a partire del punto avente */
+ /* coordinate (0,j) prima colonna, j-esima riga, per SIZE */
+ /* pixel verso destra */
+ }
+
+ free(row); /* libera un po' di memoria */
+ gtk_widget_draw(preview,NULL); /* indovina cosa fa questo? */
+ gdk_flush(); /* e questo? */
+}
+</verb></tscreen>
+Coloro che non usano GIMP probabilmente hanno gi&agrave; visto abbastanza
+per fare molte cose. Per gli utenti GIMP c'&egrave; ancora qualcosa da
+aggiungere.
+
+Anteprima dell'immagine
+
+Probabilmente &egrave; opportuno tenere pronta una versione ridotta dell'immagine,
+grande quanto basta per riempire l'anteprima. Questo pu&ograve; essere fatto
+selezionando un pixel ogni n, dove n &egrave; il rapporto tra la dimensione
+dell'immagine e la dimensione dell'anteprima. Tutte le operazioni successive
+(compreso il riempimento dell'anteprima) sono fatte solo sul ridotto numero
+di pixel selezionati. Di seguito &egrave; riportata un'implementazione della
+riduzione dell'immagine (si tenga presente che ho preso solo lezioni basilari
+di C!).
+
+
+(ATTENZIONE: CODICE NON VERIFICATO!!!)
+
+<tscreen><verb>
+
+typedef struct {
+ gint width;
+ gint height;
+ gint bbp;
+ guchar *rgb;
+ guchar *mask;
+} ReducedImage;
+
+enum {
+ SELECTION_ONLY,
+ SELCTION_IN_CONTEXT,
+ ENTIRE_IMAGE
+};
+
+ReducedImage *Reduce_The_Image(GDrawable *drawable,
+ GDrawable *mask,
+ gint LongerSize,
+ gint Selection)
+{
+ /* Questa funzione riduce l'immagine alla dimens. scelta per l'anteprima */
+ /* La dimensione dell'anteprima &egrave; determinata da LongerSize, cio&egrave; la pi&ugrave; */
+ /* grande delle dimensioni. Funziona solo per immagini RGB! */
+ gint RH, RW; /* Altezza ridotta e larghezza ridotta */
+ gint width, height; /* Larghezza e altezza dell'area da ridurre */
+ gint bytes=drawable->bpp;
+ ReducedImage *temp=(ReducedImage *)malloc(sizeof(ReducedImage));
+
+ guchar *tempRGB, *src_row, *tempmask, *src_mask_row,R,G,B;
+ gint i, j, whichcol, whichrow, x1, x2, y1, y2;
+ GPixelRgn srcPR, srcMask;
+ gint NoSelectionMade=TRUE; /* Assumiamo di trattare l'intera immagine */
+
+ gimp_drawable_mask_bounds (drawable->id, &amp;x1, &amp;y1, &amp;x2, &amp;y2);
+ width = x2-x1;
+ height = y2-y1;
+ /* Se c'&egrave; una SELEZIONE, ne abbiamo avuto gli estremi! */
+
+ if (width != drawable->width &amp;&amp; height != drawable->height)
+ NoSelectionMade=FALSE;
+ /* Controlliamo se l'utente ha una selezione attiva. Questo */
+ /* diventer&agrave; importante dopo, alla creazione di una maschera ridotta */
+
+ /* Se si vuole l'anteprima dell'immagine intera, annulla quanto sopra */
+ /* Naturalmente, in assenza di una selezione, questo non cambia nulla */
+ if (Selection==ENTIRE_IMAGE) {
+ x1=0;
+ x2=drawable->width;
+ y1=0;
+ y2=drawable->height;
+ }
+
+ /* Se si vuole l'anteprima di una selezione con parte dell'area */
+ /* circostante bisogna espanderla un po'. */
+ if (Selection==SELECTION_IN_CONTEXT) {
+ x1=MAX(0, x1-width/2.0);
+ x2=MIN(drawable->width, x2+width/2.0);
+ y1=MAX(0, y1-height/2.0);
+ y2=MIN(drawable->height, y2+height/2.0);
+ }
+
+ /* Cos&igrave; si determinano larghezza e altezza dell'area da ridurre. */
+ width = x2-x1;
+ height = y2-y1;
+
+ /* Le linee seguenti determinano quale dimensione deve essere il */
+ /* lato pi&ugrave; lungo. L'idea &egrave; presa dal plug-in supernova. Ritengo */
+ /* che avrei potuto pensarci da solo, ma la verit&agrave; va detta. */
+ /* Brutta cosa il plagio! */
+ if (width>height) {
+ RW=LongerSize;
+ RH=(float) height * (float) LongerSize/ (float) width;
+ }
+ else {
+ RH=LongerSize;
+ RW=(float)width * (float) LongerSize/ (float) height;
+ }
+
+ /* L'intera immagine viene "stirata" in una stringa! */
+ tempRGB = (guchar *) malloc(RW*RH*bytes);
+ tempmask = (guchar *) malloc(RW*RH);
+
+ gimp_pixel_rgn_init (&amp;srcPR, drawable, x1, y1, width, height, FALSE, FALSE);
+ gimp_pixel_rgn_init (&amp;srcMask, mask, x1, y1, width, height, FALSE, FALSE);
+
+ /* Prendine abbastanza da contenere una riga di immagine e una di maschera */
+ src_row = (guchar *) malloc (width*bytes);
+ src_mask_row = (guchar *) malloc (width);
+
+ for (i=0; i < RH; i++) {
+ whichrow=(float)i*(float)height/(float)RH;
+ gimp_pixel_rgn_get_row (&amp;srcPR, src_row, x1, y1+whichrow, width);
+ gimp_pixel_rgn_get_row (&amp;srcMask, src_mask_row, x1, y1+whichrow, width);
+
+ for (j=0; j < RW; j++) {
+ whichcol=(float)j*(float)width/(float)RW;
+
+ /* Nessuna selezione = tutti i punti sono completamente selezionati */
+ if (NoSelectionMade)
+ tempmask[i*RW+j]=255;
+ else
+ tempmask[i*RW+j]=src_mask_row[whichcol];
+
+ /* Aggiungi la riga alla lunga stringa che ora contiene l'immagine */
+ tempRGB[i*RW*bytes+j*bytes+0]=src_row[whichcol*bytes+0];
+ tempRGB[i*RW*bytes+j*bytes+1]=src_row[whichcol*bytes+1];
+ tempRGB[i*RW*bytes+j*bytes+2]=src_row[whichcol*bytes+2];
+
+ /* Mantieni anche la trasparenza (alpha) */
+ if (bytes==4)
+ tempRGB[i*RW*bytes+j*bytes+3]=src_row[whichcol*bytes+3];
+ }
+ }
+ temp->bpp=bytes;
+ temp->width=RW;
+ temp->height=RH;
+ temp->rgb=tempRGB;
+ temp->mask=tempmask;
+ return temp;
+}
+</verb></tscreen>
+
+
+La seguente &egrave; una funzione di anteprima che usa lo stesso tipo
+ReducedImage! Si noti che usa una finta trasparenza - se ne &egrave; presente
+una, tramite fake_transparency che &egrave; definita come segue:
+
+<tscreen><verb>
+gint fake_transparency(gint i, gint j)
+{
+ if ( ((i%20)- 10) * ((j%20)- 10)>0 )
+ return 64;
+ else
+ return 196;
+}
+
+</verb></tscreen>
+E adesso la funzione per l'anteprima:
+<tscreen><verb>
+void
+my_preview_render_function(GtkWidget *preview,
+ gint changewhat,
+ gint changewhich)
+{
+ gint Inten, bytes=drawable->bpp;
+ gint i, j, k;
+ float partial;
+ gint RW=reduced->width;
+ gint RH=reduced->height;
+ guchar *row=malloc(bytes*RW);;
+
+
+ for (i=0; i < RH; i++) {
+ for (j=0; j < RW; j++) {
+
+ row[j*3+0] = reduced->rgb[i*RW*bytes + j*bytes + 0];
+ row[j*3+1] = reduced->rgb[i*RW*bytes + j*bytes + 1];
+ row[j*3+2] = reduced->rgb[i*RW*bytes + j*bytes + 2];
+
+ if (bytes==4)
+ for (k=0; k<3; k++) {
+ float transp=reduced->rgb[i*RW*bytes+j*bytes+3]/255.0;
+ row[3*j+k]=transp*a[3*j+k]+(1-transp)*fake_transparency(i,j);
+ }
+ }
+ gtk_preview_draw_row( GTK_PREVIEW(preview),row,0,i,RW);
+ }
+
+ free(a);
+ gtk_widget_draw(preview,NULL);
+ gdk_flush();
+}
+
+Funzioni Applicabili
+
+guint gtk_preview_get_type (void);
+/* No idea */
+void gtk_preview_uninit (void);
+/* No idea */
+GtkWidget* gtk_preview_new (GtkPreviewType type);
+/* Descritta precedentemente */
+void gtk_preview_size (GtkPreview *preview,
+ gint width,
+ gint height);
+/* Permette di ridimensionare un'anteprima esistente */
+/* Pare che un bug in GTK renda disordinato questo */
+/* processo. Un modo di rimettere le cose a posto */
+/* &egrave; quello di ridimensionare manualmente */
+/* la finestra contenente l'anteprima dopo aver */
+/* ridimensionato l'anteprima. */
+
+void gtk_preview_put (GtkPreview *preview,
+ GdkWindow *window,
+ GdkGC *gc,
+ gint srcx,
+ gint srcy,
+ gint destx,
+ gint desty,
+ gint width,
+ gint height);
+/* No idea */
+
+void gtk_preview_put_row (GtkPreview *preview,
+ guchar *src,
+ guchar *dest,
+ gint x,
+ gint y,
+ gint w);
+/* No idea */
+
+void gtk_preview_draw_row (GtkPreview *preview,
+ guchar *data,
+ gint x,
+ gint y,
+ gint w);
+/* Descritta nel testo */
+
+void gtk_preview_set_expand (GtkPreview *preview,
+ gint expand);
+/* No idea */
+
+/* Nessun indizio per le seguenti, ma dovrebbero */
+/* essere standard per la maggior parte dei widget */
+void gtk_preview_set_gamma (double gamma);
+void gtk_preview_set_color_cube (guint nred_shades,
+ guint ngreen_shades,
+ guint nblue_shades,
+ guint ngray_shades);
+void gtk_preview_set_install_cmap (gint install_cmap);
+void gtk_preview_set_reserved (gint nreserved);
+GdkVisual* gtk_preview_get_visual (void);
+GdkColormap* gtk_preview_get_cmap (void);
+GtkPreviewInfo* gtk_preview_get_info (void);
+
+E' tutto!
+
+</verb></tscreen>
+
+
+<sect1> Curve
+<p>
+
+
+<sect>Il Widget EventBox<label id="sec_The_EventBox_Widget">
+<p>
+E' disponibile solo a partire dalla distribuzione gtk+970916.tar.gz.
+<p>
+Alcuni widget gtk non sono associati a finestre X, sicch&eacute;
+semplicemente disegnano sui loro genitori. Per questo motivo essi non possono
+ricevere eventi e se sono sovradimensionati non vengono troncati, ma rischiano
+di sovrapporsi, generando confusione. Se si vuole di pi&ugrave; da questi
+widget si pu&ograve; ricorrere agli EventBox.
+
+A prima vista il widget EventBox potrebbe sembrare completamente inutile. Non
+disegna nulla sullo schermo e non risponde a nessun evento. Tuttavia ha
+una funzione: fornire una finestra X al suo widget figlio. Ci&ograve;
+&egrave; importante in quanto molti widget GTK non hanno una finestra X
+associata. Se questo da una parte risparmia memoria e migliora le prestazioni,
+dall'altra introduce degli svantaggi: un widget senza una finestra X non
+pu&ograve; ricevere eventi, e non taglia in alcun modo il suo contenuto.
+Sebbene il nome ``EventBox'' (casella di eventi) enfasizzi la funzione di
+gestione degli eventi, il widget pu&ograve; essere usato anche per
+limitare la dimensione dei widget figli (ma anche per altro: si veda
+l'esempio seguente).
+
+<p>
+Per creare un widget di tipo EventBox:
+
+<tscreen><verb>
+GtkWidget* gtk_event_box_new (void);
+</verb></tscreen>
+
+<p>
+All'EventBox si pu&ograve aggiungere un widget figlio:
+
+<tscreen><verb>
+gtk_container_add (GTK_CONTAINER(event_box), widget);
+</verb></tscreen>
+
+<p>
+The following example demonstrates both uses of an EventBox - a label
+is created that clipped to a small box, and set up so that a
+mouse-click on the label causes the program to exit.
+Il seguente esempio mostra entrambi gli usi di un EventBox - si crea
+un'etichetta limitata da un rettangolo piccolo, fatta in modo che
+cliccando con il mouse su di essa il programma termina.
+
+<tscreen><verb>
+#include <gtk/gtk.h>
+
+int
+main (int argc, char *argv[])
+{
+ GtkWidget *window;
+ GtkWidget *event_box;
+ GtkWidget *label;
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+ gtk_window_set_title (GTK_WINDOW (window), "Event Box");
+
+ gtk_signal_connect (GTK_OBJECT (window), "destroy",
+ GTK_SIGNAL_FUNC (gtk_exit), NULL);
+
+ gtk_container_border_width (GTK_CONTAINER (window), 10);
+
+ /* Crea un EventBox e lo aggiunge alla finestra principale */
+
+ event_box = gtk_event_box_new ();
+ gtk_container_add (GTK_CONTAINER(window), event_box);
+ gtk_widget_show (event_box);
+
+ /* Crea una etichetta lunga */
+
+ label = gtk_label_new ("Click here to quit, quit, quit, quit, quit");
+ gtk_container_add (GTK_CONTAINER (event_box), label);
+ gtk_widget_show (label);
+
+ /* Limitane le dimensioni */
+ gtk_widget_set_usize (label, 110, 20);
+
+ /* E collega ad essa una azione */
+ gtk_widget_set_events (event_box, GDK_BUTTON_PRESS_MASK);
+ gtk_signal_connect (GTK_OBJECT(event_box), "button_press_event",
+ GTK_SIGNAL_FUNC (gtk_exit), NULL);
+
+ /* Un'altra cosa per cui si ha bisogno di una finestra X ... */
+
+ gtk_widget_realize (event_box);
+ gdk_window_set_cursor (event_box->window, gdk_cursor_new (GDK_HAND1));
+
+ gtk_widget_show (window);
+
+ gtk_main ();
+
+ return 0;
+}
+</verb></tscreen>
+
+<sect>Selezionare gli Attributi dei Widget<label id="sec_setting_widget_attributes">
+<p>
+Qui si descrivono le funzioni per la gestione dei widget. Esse possono essere
+usate per impostarne lo stile, il padding, le dimensioni, ...
+
+(Forse andrebbe fatta un'intera sezione sugli acceleratori).
+
+<tscreen><verb>
+void gtk_widget_install_accelerator (GtkWidget *widget,
+ GtkAcceleratorTable *table,
+ gchar *signal_name,
+ gchar key,
+ guint8 modifiers);
+
+void gtk_widget_remove_accelerator (GtkWidget *widget,
+ GtkAcceleratorTable *table,
+ gchar *signal_name);
+
+void gtk_widget_activate (GtkWidget *widget);
+
+void gtk_widget_set_name (GtkWidget *widget,
+ gchar *name);
+gchar* gtk_widget_get_name (GtkWidget *widget);
+
+void gtk_widget_set_sensitive (GtkWidget *widget,
+ gint sensitive);
+
+void gtk_widget_set_style (GtkWidget *widget,
+ GtkStyle *style);
+
+GtkStyle* gtk_widget_get_style (GtkWidget *widget);
+
+GtkStyle* gtk_widget_get_default_style (void);
+
+void gtk_widget_set_uposition (GtkWidget *widget,
+ gint x,
+ gint y);
+void gtk_widget_set_usize (GtkWidget *widget,
+ gint width,
+ gint height);
+
+void gtk_widget_grab_focus (GtkWidget *widget);
+
+void gtk_widget_show (GtkWidget *widget);
+
+void gtk_widget_hide (GtkWidget *widget);
+</verb></tscreen>
+
+
+
+<sect>Funzioni periodiche, di I/O e di attesa<label id="sec_timeouts">
+<p>
+<sect1>Funzioni periodiche
+<p>
+Probabilmente vi sarete chiesti come far fare qualcosa di utile a GTK
+durante la chiamata alla gtk_main(). Ci sono diverse possibilit&agrave;.
+Usando le seguenti funzioni si possono creare funzioni che vengono chiamate
+periodicamente.
+
+<tscreen><verb>
+gint gtk_timeout_add (guint32 interval,
+ GtkFunction function,
+ gpointer data);
+</verb></tscreen>
+
+Il primo argomento &egrave; il numero di millisecondi tra le chiamate alla
+funzione. Il secondo &egrave; la funzione periodica, mentre il terzo
+rappresenta i dati che vengono passati alla funzione. Il valore restituito
+&egrave; un'etichetta che pu&ograve; essere utilizzata per fermare la chiamata
+periodica, passandolo alla funzione:
+
+<tscreen><verb>
+void gtk_timeout_remove (gint tag);
+</verb></tscreen>
+
+La chiamata periodica si ferma anche se la funzione periodica ritorna zero
+o FALSE. Naturalmente questo vuol dire che se si vuole che la funzione periodica
+continui ad essere richiamata, essa deve restituire un valore non nullo,
+cio&egrave; TRUE.
+
+La dichiarazione della funzione periodica dovrebbe essere come questa:
+
+<tscreen><verb>
+gint timeout_callback (gpointer data);
+</verb></tscreen>
+
+<sect1>Controllo dell'I/O
+<p>
+Un'altra utile caratteristica di GTK &egrave; la possibilit&agrave; di fargli
+controllare che siano verificate certe condizioni su un descrittore di file
+(come quelli restituiti da open(2) o socket(2)). Questo &egrave; utile in
+particolar modo per le applicazioni di rete. La funzione &egrave; la seguente:
+
+<tscreen><verb>
+gint gdk_input_add (gint source,
+ GdkInputCondition condition,
+ GdkInputFunction function,
+ gpointer data);
+</verb></tscreen>
+
+Il primo argomento &egrave; il descrittore che si desidera venga controllato,
+mentre il secondo specifica quale condizione si vuole che GDK controlli.
+Questa pu&ograve; essere una tra:
+<p>
+GDK_INPUT_READ - Chiama la funzione quando ci sono dati pronti per la lettura
+nel descrittore di file.
+<p>
+GDK_INPUT_WRITE - Chiama la funzione quando il descrittore di file &egrave;
+pronto per la scrittura.
+<p>
+Come sicuramente avrete gi&agrave; intuito, il terzo parametro &egrave; la
+funzione da chiamare quando la condizione specificata &egrave; soddisfatta,
+mentre il quarto rappresenta i dati da passare a questa funzione.
+<p>
+Il valore di ritorno &egrave; un etichetta che pu&ograve; essere usata per
+fermare il controllo di GDK sul descrittore di file, usando la seguente
+funzione:
+<p>
+<tscreen><verb>
+void gdk_input_remove (gint tag);
+</verb></tscreen>
+<p>
+La funzione da richiamare va dichiarata cos&igrave;:
+<p>
+<tscreen><verb>
+void input_callback (gpointer data, gint source,
+ GdkInputCondition condition);
+</verb></tscreen>
+<p>
+
+<sect1>Funzioni di attesa (``Idle'')
+<p>
+Cosa fare se si ha una funzione che si vuole venga chiamata quando non
+sta accadendo nient'altro?
+
+<tscreen><verb>
+gint gtk_idle_add (GtkFunction function,
+ gpointer data);
+</verb></tscreen>
+
+Questa fa si che GDK chiami la funzione specificata quando non c'&egrave;
+nessuna altra operazione in corso.
+
+<tscreen><verb>
+void gtk_idle_remove (gint tag);
+</verb></tscreen>
+<p>
+Non ci soffermeremo sul significato dei parametri in quanto del tutto analoghi
+ai precedenti. La funzione puntata dal primo argomento della gtk_idle_add
+viene chiamata non appena se ne presenta l'opportunit&agrave;; come
+negli altri casi, se essa restituisce FALSE non viene pi&ugrave; chiamata.
+
+
+<sect>La gestione delle selezioni
+
+<sect1> Overview
+
+<p>
+
+Le <em>selezioni</em> sono un tipo di comunicazione tra processi
+supportato da GTK. Una selezione identifica un frammento di dati; per
+esempio, una porzione di testo selezionata dall'utente in qualche modo,
+magari con il mouse. Su un display solo un'applicazione alla volta
+(il <em>proprietario</em>) pu&oacute; essere proprietaria di una
+particolare selezione, sicch&eacute; quando un'applicazione richiede
+una selezione il precedente proprietario deve comunicare all'utente che
+la selezione &egrave; stata ceduta. Altre applicazioni possono richiedere
+il contenuto di una selezione in diverse forme, chiamate <em>obiettivi</em>.
+Ci pu&ograve; essere un numero qualsiasi di selezioni, ma la maggior parte
+delle applicazioni X pu&ograve; gestirne solo una, la <em>selezione
+primaria</em>.
+
+<p>
+Nella maggior parte dei casi per una applicazione GTK non &egrave;
+necessario gestire esplicitamente le selezioni. I widget standard,
+come quello di Ingresso, hanno gi&agrave; la capacit&agrave; di
+chiedere la selezione se necessario (p. e., quando l'utente
+seleziona sul testo), e di recuperare il contenuto di una selezione
+di un altro widget o di un'altra applicazione (p. e., quando l'utente
+clicca il tasto centrale del mouse). Ci possono comunque essere dei
+casi nei quali si vuole dare ad altri widget la capacit&agrave; di
+fornire la selezione, o si vogliono recuperare degli obiettivi non
+supportati direttamente.
+
+<p>
+Un concetto fondamentale necessario per comprendere la gestione delle
+selezioni &egrave; quello di <em>atomo</em>. Un atomo &egrave; un intero
+che identifica univocamente una stringa (su un certo display).
+Certi atomi sono predefiniti dal server X, e in alcuni casi in <tt>gtk.h</tt>
+ci sono costanti corrispondenti a questi atomi. Per esempio, la costante
+<tt>GDK_PRIMARY_SELECTION</tt> corrisponde alla stringa ``PRIMARY''.
+Negli altri casi bisogna usare le funzioni <tt>gdk_atom_intern()</tt>
+per ottenere l'atomo corrispondente ad una stringa, e <tt>gdk_atom_name()</tt>
+per ottenere il nome di un atomo. Sia le selezioni sia gli obiettivi sono
+identificati da atomi.
+
+<sect1> Recuperare le selezioni
+
+<p>
+
+Il recupero di una selezione &egrave; un processo asincrono. Per iniziare
+il processo, si chiama:
+<tscreen><verb>
+gint gtk_selection_convert (GtkWidget *widget,
+ GdkAtom selection,
+ GdkAtom target,
+ guint32 time)
+</verb</tscreen>
+
+Questo <em>converte</em> la selezione nella forma specificata
+dall'obiettivo <tt/target/. Se possibile, il campo <tt/time/
+dovrebbe essere il tempo dell'evento che ha attivato la selezione.
+Questo aiuta a far si che gli eventi avvengano nell'ordine in cui
+l'utente li ha richiesti. Se comunque non fosse disponibile (per
+esempio, se la conversione &egrave; stata attivata da un segnale di
+``cliccato''), allora si pu&ograve; usare la costante
+<tt>GDK_CURRENT_TIME</tt>.
+
+<p>
+Quando il proprietario di una selezione risponde ad una richiesta,
+un segnale ``selection_received'' (selezione ricevuta) viene inviato
+alla vostra applicazione. Il gestore di questo segnale riceve un
+puntatore ad una struttura <tt>GtkSelectionData</tt>, che &egrave;
+definita nel modo seguente:
+<tscreen><verb>
+struct _GtkSelectionData
+{
+ GdkAtom selection;
+ GdkAtom target;
+ GdkAtom type;
+ gint format;
+ guchar *data;
+ gint length;
+};
+</verb></tscreen>
+<tt>selection</tt> e <tt>target</tt> sono i valori da voi specificati
+nella chiamata <tt>gtk_selection_convert()</tt>. <tt>type</tt> &egrave;
+un atomo che identifica il tipo di dati restituiti dal proprietario della
+selezione. Alcuni valori possibili sono ``STRING'', una stringa di
+caratteri latin-1, ``ATOM'', una serie di atomi, ``INTEGER'', un intero, ecc.
+La maggior parte degli obiettivi pu&ograve; restituire solo un tipo.
+<tt/format/ ci d&agrave; la lunghezza delle unit&agrave; (per esempio caratteri)
+in bit. Di solito, quando si ricevono i dati non ci si cura di questo.
+<tt>data</tt> &egrave; un puntatore ai dati restituiti, e <tt>length</tt>
+&egrave; la lunghezza dei dati restituiti, in byte. Se <tt>length</tt>
+&egrave; negativo allora si &egrave; verificato un errore e non &egrave;
+stato possibile recuperare la selezione. Questo pu&ograve; avvenire se
+nessuna applicazione era proprietaria della selezione, o se si &egrave;
+richiesto un obiettivo non supportato dall'applicazione. Viene garantito
+che il buffer sia un byte pi&ugrave; lungo di <tt>length</tt>; il byte
+in pi&ugrave; sar&agrave; sempre zero, in modo che non sia necessario
+ricopiare le stringhe solo per farle terminare con zero.
+
+<p>
+Nell'esempio che segue viene recuperato l'obiettivo speciale ``TARGETS'',
+che &egrave; una lista di tutti gli obiettivi in cui pu&ograve; essere
+convertita la selezione.
+<tscreen><verb>
+#include <gtk/gtk.h>
+
+void selection_received (GtkWidget *widget,
+ GtkSelectionData *selection_data,
+ gpointer data);
+
+/* Gestore di segnale chiamato quando l'utente clicca nel bottone */
+/* "Get Targets" */
+void
+get_targets (GtkWidget *widget, gpointer data)
+{
+ static GdkAtom targets_atom = GDK_NONE;
+
+ /* Prende l'atomo corrispondente alla stringa "TARGETS" */
+ if (targets_atom == GDK_NONE)
+ targets_atom = gdk_atom_intern ("TARGETS", FALSE);
+
+ /* E richiede l'obiettivo "TARGETS" per la selezione primaria */
+ gtk_selection_convert (widget, GDK_SELECTION_PRIMARY, targets_atom,
+ GDK_CURRENT_TIME);
+}
+
+/* Gestore di segnale chiamato quando il proprietario della selezione */
+/* restituisce i dati */
+void
+selection_received (GtkWidget *widget, GtkSelectionData *selection_data,
+ gpointer data)
+{
+ GdkAtom *atoms;
+ GList *item_list;
+ int i;
+
+ /* **** IMPORTANTE **** Controlla che il recupero sia riuscito */
+ if (selection_data->length < 0)
+ {
+ g_print ("Selection retrieval failed\n");
+ return;
+ }
+ /* Make sure we got the data in the expected form */
+ if (selection_data->type != GDK_SELECTION_TYPE_ATOM)
+ {
+ g_print ("Selection \"TARGETS\" was not returned as atoms!\n");
+ return;
+ }
+
+ /* Stampa gli atomi ricevuti */
+ atoms = (GdkAtom *)selection_data->data;
+
+ item_list = NULL;
+ for (i=0; i<selection_data->length/sizeof(GdkAtom); i++)
+ {
+ char *name;
+ name = gdk_atom_name (atoms[i]);
+ if (name != NULL)
+ g_print ("%s\n",name);
+ else
+ g_print ("(bad atom)\n");
+ }
+
+ return;
+}
+
+int
+main (int argc, char *argv[])
+{
+ GtkWidget *window;
+ GtkWidget *button;
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ /* Create the toplevel window */
+
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title (GTK_WINDOW (window), "Event Box");
+ gtk_container_border_width (GTK_CONTAINER (window), 10);
+
+ gtk_signal_connect (GTK_OBJECT (window), "destroy",
+ GTK_SIGNAL_FUNC (gtk_exit), NULL);
+
+ /* Crea un bottone che l'utente pu&ograve; cliccare per ottenere gli obiettivi */
+
+ button = gtk_button_new_with_label ("Get Targets");
+ gtk_container_add (GTK_CONTAINER (window), button);
+
+ gtk_signal_connect (GTK_OBJECT(button), "clicked",
+ GTK_SIGNAL_FUNC (get_targets), NULL);
+ gtk_signal_connect (GTK_OBJECT(button), "selection_received",
+ GTK_SIGNAL_FUNC (selection_received), NULL);
+
+ gtk_widget_show (button);
+ gtk_widget_show (window);
+
+ gtk_main ();
+
+ return 0;
+}
+</verb></tscreen>
+
+<sect1> Fornire una selezione
+
+<p>
+Fornire la selezione &egrave; un po' pi&ugrave; complicato. Bisogna
+registrare i gestori che verranno chiamati quando viene richiesta la
+propria selezione. Per ogni coppia selezione/obiettivo che si gestir&agrave;
+occorre una chiamata a:
+
+<tscreen><verb>
+void gtk_selection_add_handler (GtkWidget *widget,
+ GdkAtom selection,
+ GdkAtom target,
+ GtkSelectionFunction function,
+ GtkRemoveFunction remove_func,
+ gpointer data);
+</verb></tscreen>
+
+<tt/widget/, <tt/selection/, e <tt/target/ identificano le richieste
+che questo gestore soddisfer&agrave;. <tt/remove_func/, se non &egrave;
+NULL, verr&agrave; chiamato quando il gestore di segnale viene rimosso.
+Questo &egrave; utile, per esempio, per linguaggi interpretati ai quali
+serve di tener traccia di un conteggio di riferimento per <tt/data/.
+
+<p>
+La funzione di richiamo ha la forma:
+
+<tscreen><verb>
+typedef void (*GtkSelectionFunction) (GtkWidget *widget,
+ GtkSelectionData *selection_data,
+ gpointer data);
+
+</verb></tscreen>
+
+La GtkSelectionData &egrave; la stessa di prima, ma stavolta siamo
+responsabili di riempire i campi <tt/type/, <tt/format/, <tt/data/,
+e <tt/length/. (Il campo <tt/format/ qui &egrave; effettivamente
+importante - il server X lo usa per capire se occorre che i byte
+dei dati vengano scambiati o no. Di solito sar&agrave; 8 - cio&egrave;
+un carattere - o 32 - cio&egrave; un intero.) Questo viene fatto
+chiamando la funzione:
+
+<tscreen><verb>
+void gtk_selection_data_set (GtkSelectionData *selection_data,
+ GdkAtom type,
+ gint format,
+ guchar *data,
+ gint length);
+</verb></tscreen>
+Questa funzione si prende cura di fare propriamente una copia dei dati
+in modo che non ci si debba preoccupare di conservarli (&egrave; opportuno
+evitare di riempire a mano i campi della struttura GtkSelectionData).
+
+<p>
+Quando richiesto dall'utente, richiederete la propriet&agrave; della selezione
+chiamando:
+
+<tscreen><verb>
+gint gtk_selection_owner_set (GtkWidget *widget,
+ GdkAtom selection,
+ guint32 time);
+</verb></tscreen>
+
+Se un'altra applicazione richiede la propriet&agrave; della selezione,
+riceverete un evento di azzeramento della selezione (``selection_clear_event'').
+
+Come esempio di fornitura della selezione, il programma seguente aggiunge
+la funzionalit&agrave; di selezione a un bottone di attivazione. Quando il
+bottone viene premuto, il programma richiede la selezione primaria.
+L'unico obiettivo supportato (oltre a certi obiettivi come ``TARGETS''
+fornito dalla stessa GTK) &egrave; l'obiettivo ``STRING''. Quando viene
+richiesto questo obiettivo, viene restituita una rappresentazione stringa
+del tempo.
+
+<tscreen><verb>
+#include <gtk/gtk.h>
+#include <time.h>
+
+/* Richiamata quando l'utente attiva la selezione */
+void
+selection_toggled (GtkWidget *widget, gint *have_selection)
+{
+ if (GTK_TOGGLE_BUTTON(widget)->active)
+ {
+ *have_selection = gtk_selection_owner_set (widget,
+ GDK_SELECTION_PRIMARY,
+ GDK_CURRENT_TIME);
+ /* se il richiamo della selezione &egrave; fallito, si riporta il
+ bottone nello stato non premuto */
+ if (!*have_selection)
+ gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON(widget), FALSE);
+ }
+ else
+ {
+ if (*have_selection)
+ {
+ /* Prima di annullare la selezione mettendone a NULL il proprietario,
+ controlliamo se siamo i veri proprietari */
+ if (gdk_selection_owner_get (GDK_SELECTION_PRIMARY) == widget->window)
+ gtk_selection_owner_set (NULL, GDK_SELECTION_PRIMARY,
+ GDK_CURRENT_TIME);
+ *have_selection = FALSE;
+ }
+ }
+}
+
+/* Chiamata quando un'altra applicazione richiede la selezione */
+gint
+selection_clear (GtkWidget *widget, GdkEventSelection *event,
+ gint *have_selection)
+{
+ *have_selection = FALSE;
+ gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON(widget), FALSE);
+
+ return TRUE;
+}
+
+/* Fornisce come selezione il tempo attuale */
+void
+selection_handle (GtkWidget *widget,
+ GtkSelectionData *selection_data,
+ gpointer data)
+{
+ gchar *timestr;
+ time_t current_time;
+
+ current_time = time (NULL);
+ timestr = asctime (localtime(&amp;current_time));
+ /* Quando si restituisce una singola stringa, non occorre che finisca
+ con NULL. Questo verr&agrave; fatto automaticamente */
+
+ gtk_selection_data_set (selection_data, GDK_SELECTION_TYPE_STRING,
+ 8, timestr, strlen(timestr));
+}
+
+int
+main (int argc, char *argv[])
+{
+ GtkWidget *window;
+
+ GtkWidget *selection_button;
+
+ static int have_selection = FALSE;
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ /* Crea la finestra di livello superiore */
+
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title (GTK_WINDOW (window), "Event Box");
+ gtk_container_border_width (GTK_CONTAINER (window), 10);
+
+ gtk_signal_connect (GTK_OBJECT (window), "destroy",
+ GTK_SIGNAL_FUNC (gtk_exit), NULL);
+
+ /* Crea un bottone a commutazione che agisce come la selezione */
+
+ selection_button = gtk_toggle_button_new_with_label ("Claim Selection");
+ gtk_container_add (GTK_CONTAINER (window), selection_button);
+ gtk_widget_show (selection_button);
+
+ gtk_signal_connect (GTK_OBJECT(selection_button), "toggled",
+ GTK_SIGNAL_FUNC (selection_toggled), &amp;have_selection);
+ gtk_signal_connect (GTK_OBJECT(selection_button), "selection_clear_event",
+ GTK_SIGNAL_FUNC (selection_clear), &amp;have_selection);
+
+ gtk_selection_add_handler (selection_button, GDK_SELECTION_PRIMARY,
+ GDK_SELECTION_TYPE_STRING,
+ selection_handle, NULL, NULL);
+
+ gtk_widget_show (selection_button);
+ gtk_widget_show (window);
+
+ gtk_main ();
+
+ return 0;
+}
+</verb></tscreen>
+
+
+
+<sect>La glib<label id="sec_glib">
+<p>
+La glib fornisce molte funzioni e definizioni utili pronte all'uso quando si
+creano applicazioni GDK e GTK. Qui verranno elencate tutte, con una
+breve spiegazione. Molte sono duplicati delle funzioni standard della libc,
+e quindi per queste non si scender&agrave; nei dettagli. Questa vuole essere una
+lista di riferimento, in modo che si sappia cosa &egrave; possibile usare.
+
+<sect1>Definizioni
+<p>
+Le definizioni per gli estremi di molti dei tipi standard sono:
+
+<tscreen><verb>
+G_MINFLOAT
+G_MAXFLOAT
+G_MINDOUBLE
+G_MAXDOUBLE
+G_MINSHORT
+G_MAXSHORT
+G_MININT
+G_MAXINT
+G_MINLONG
+G_MAXLONG
+</verb></tscreen>
+
+Ci sono anche le seguenti definizioni di tipo. Quelle rimaste non specificate
+sono dipendenti dall'architettura. Si ricordi di evitare di fare affidamento
+sulla dimensione di un puntatore se si vuole la portabilit&agrave;! P.e., un puntatore
+su un Alpha &egrave; lungo 8 byte, ma 4 su un Intel.
+
+<tscreen><verb>
+char gchar;
+short gshort;
+long glong;
+int gint;
+char gboolean;
+
+unsigned char guchar;
+unsigned short gushort;
+unsigned long gulong;
+unsigned int guint;
+
+float gfloat;
+double gdouble;
+long double gldouble;
+
+void* gpointer;
+
+gint8
+guint8
+gint16
+guint16
+gint32
+guint32
+</verb></tscreen>
+
+<sect1>Liste a doppio collegamento
+<p>
+le seguenti funzioni sono usate per creare, gestire e distruggere liste a
+doppio collegamento. Si assume che il lettore sappia gi&agrave; cosa sono le liste
+collegate, poich&eacute; descriverle &egrave; fuori dagli scopi di questo documento.
+Naturalmente non &egrave; necessario conoscerle per l'uso generale di GTK, per
+quanto conoscerle sia comunque interessante.
+
+<tscreen><verb>
+GList* g_list_alloc (void);
+
+void g_list_free (GList *list);
+
+void g_list_free_1 (GList *list);
+
+GList* g_list_append (GList *list,
+ gpointer data);
+
+GList* g_list_prepend (GList *list,
+ gpointer data);
+
+GList* g_list_insert (GList *list,
+ gpointer data,
+ gint position);
+
+GList* g_list_remove (GList *list,
+ gpointer data);
+
+GList* g_list_remove_link (GList *list,
+ GList *link);
+
+GList* g_list_reverse (GList *list);
+
+GList* g_list_nth (GList *list,
+ gint n);
+
+GList* g_list_find (GList *list,
+ gpointer data);
+
+GList* g_list_last (GList *list);
+
+GList* g_list_first (GList *list);
+
+gint g_list_length (GList *list);
+
+void g_list_foreach (GList *list,
+ GFunc func,
+ gpointer user_data);
+</verb></tscreen>
+
+
+<sect1>Liste a collegamento singolo
+<p>
+Molte delle funzioni per le liste a collegamento singolo sono identiche alle
+precedenti. Eccone una lista completa:
+<tscreen><verb>
+GSList* g_slist_alloc (void);
+
+void g_slist_free (GSList *list);
+
+void g_slist_free_1 (GSList *list);
+
+GSList* g_slist_append (GSList *list,
+ gpointer data);
+
+GSList* g_slist_prepend (GSList *list,
+ gpointer data);
+
+GSList* g_slist_insert (GSList *list,
+ gpointer data,
+ gint position);
+
+GSList* g_slist_remove (GSList *list,
+ gpointer data);
+
+GSList* g_slist_remove_link (GSList *list,
+ GSList *link);
+
+GSList* g_slist_reverse (GSList *list);
+
+GSList* g_slist_nth (GSList *list,
+ gint n);
+
+GSList* g_slist_find (GSList *list,
+ gpointer data);
+
+GSList* g_slist_last (GSList *list);
+
+gint g_slist_length (GSList *list);
+
+void g_slist_foreach (GSList *list,
+ GFunc func,
+ gpointer user_data);
+
+</verb></tscreen>
+
+<sect1>Gestione della memoria
+<p>
+<tscreen><verb>
+gpointer g_malloc (gulong size);
+</verb></tscreen>
+
+Questa &egrave; una sostituta di malloc(). Non occorre controllare il valore
+restituito, in quanto lo fa gi&agrave; questa funzione.
+
+<tscreen><verb>
+gpointer g_malloc0 (gulong size);
+</verb></tscreen>
+
+Come la precedente, ma la memoria viene azzerata prima di restituire un
+puntatore ad essa.
+
+<tscreen><verb>
+gpointer g_realloc (gpointer mem,
+ gulong size);
+</verb></tscreen>
+
+Riloca ``size'' byte di memoria che inizia a ``mem''. Ovviamente, la memoria
+dovrebbe essere stata allocata precedentemente.
+
+<tscreen><verb>
+void g_free (gpointer mem);
+</verb></tscreen>
+
+Libera la memoria. Facile!
+
+<tscreen><verb>
+void g_mem_profile (void);
+</verb></tscreen>
+
+Emette un profilo della memoria usata, ma occorre ricompilare e reinstallare
+la libreria aggiungendo #define MEM_PROFILE all'inizio del file glib/gmem.c.
+
+<tscreen><verb>
+void g_mem_check (gpointer mem);
+</verb></tscreen>
+
+Controlla che una locazione di memoria sia valida. Occorre ricompilare e
+reinstallare la libreria aggiungendo #define MEM_CHECK all'inizio del file
+gmem.c.
+
+<sect1>Timer
+<p>
+Funzioni legate ai timer...
+
+<tscreen><verb>
+GTimer* g_timer_new (void);
+
+void g_timer_destroy (GTimer *timer);
+
+void g_timer_start (GTimer *timer);
+
+void g_timer_stop (GTimer *timer);
+
+void g_timer_reset (GTimer *timer);
+
+gdouble g_timer_elapsed (GTimer *timer,
+ gulong *microseconds);
+</verb></tscreen>
+
+<sect1>Gestione delle stringhe
+<p>
+Un'accozzaglia di funzioni per la gestione delle stringhe. Sembrano tutte molto
+interessanti, e probabilmente migliori per molte caratteristiche delle funzioni
+standard del C per le stringhe, ma necessitano di documentazione.
+
+<tscreen><verb>
+GString* g_string_new (gchar *init);
+void g_string_free (GString *string,
+ gint free_segment);
+
+GString* g_string_assign (GString *lval,
+ gchar *rval);
+
+GString* g_string_truncate (GString *string,
+ gint len);
+
+GString* g_string_append (GString *string,
+ gchar *val);
+
+GString* g_string_append_c (GString *string,
+ gchar c);
+
+GString* g_string_prepend (GString *string,
+ gchar *val);
+
+GString* g_string_prepend_c (GString *string,
+ gchar c);
+
+void g_string_sprintf (GString *string,
+ gchar *fmt,
+ ...);
+
+void g_string_sprintfa (GString *string,
+ gchar *fmt,
+ ...);
+</verb></tscreen>
+
+<sect1>Funzioni d'utilit&agrave; e di errore
+<p>
+<tscreen><verb>
+gchar* g_strdup (const gchar *str);
+</verb></tscreen>
+
+Funzione sostitutiva della strdup. Copia i contenuti originari delle stringhe
+in memoria appena allocata, restituendo un puntatore ad essa.
+
+<tscreen><verb>
+gchar* g_strerror (gint errnum);
+</verb></tscreen>
+Si raccomanda di usare questa gunzione per tutti i messaggi di errore. E' molto
+pi&ugrave; graziosa, e pi&ugrave; portabile di perror() o di altre. L'output di solito ha la
+forma:
+
+<tscreen><verb>
+nome programma:funzione fallita:file o altre descrizioni:strerror
+</verb></tscreen>
+
+Di seguito un esempio di una chiamata di questo tipo usata nel nostro
+programma Hello World:
+
+<tscreen><verb>
+g_print("hello_world:open:%s:%s\n", filename, g_strerror(errno));
+</verb></tscreen>
+
+<tscreen><verb>
+void g_error (gchar *format, ...);
+</verb></tscreen>
+
+Visualizza un messaggio di errore. Il formato &egrave; come quello di printf,
+ma prepone ``** ERROR **: '' al messaggio e termina il programma. Da usare solo
+per errori gravi.
+
+<tscreen><verb>
+void g_warning (gchar *format, ...);
+</verb></tscreen>
+
+Come la precedente, ma prepone ``** WARNING **: '' e non termina il programma.
+
+<tscreen><verb>
+void g_message (gchar *format, ...);
+</verb></tscreen>
+
+Visualizza ``message: '' e poi il messaggio.
+
+<tscreen><verb>
+void g_print (gchar *format, ...);
+</verb></tscreen>
+
+Sostituta di printf().
+
+L'ultima funzione:
+
+<tscreen><verb>
+gchar* g_strsignal (gint signum);
+</verb></tscreen>
+
+Visualizza il nome del messaggio del sistema Unix associato al numero di
+segnale. Utile nelle funzioni generiche di gestione dei segnali.
+
+Tutte le funzioni elencate sono pi&ugrave; o meno prese da glib.h. Se qualcuno volesse
+documentare qualche funzione, mandi una email all'autore!
+
+<sect>I file rc di GTK
+<p>
+GTK ha un suo modo di trattare le preferenze delle applicazioni, usando
+i file rc. Questi possono essere usati per scegliere i colori di quasi tutti
+i widget, e possono anche essere usati per inserire delle pixmap nello sfondo
+di alcuni widget.
+
+<sect1>Funzioni per i file rc
+<p>
+All'inizio della vostra applicazione dovrebbe esserci una chiamata a
+<tscreen><verb>
+void gtk_rc_parse (char *filename);
+</verb></tscreen>
+<p>
+passando come parametro il nome del vostro file rc. Questo far&agrave; si che GTK
+analizzi tale file e usi le impostazioni di stile per i tipi di widget ivi
+definite.
+<p>
+Se si desidera avere un insieme speciale di widget che abbia uno stile diverso
+dagli altri, o qualsiasi altra divisione logica dei widget, si chiami
+<tscreen><verb>
+void gtk_widget_set_name (GtkWidget *widget,
+ gchar *name);
+</verb></tscreen>
+<p>
+passando un widget appena creato come primo argomento, e il nome che gli si
+vuole dare come secondo. Questo consentir&agrave; di cambiare gli attributi di
+questo widget per nome tramite il file rc.
+<p>
+Effettuando una chiamata come questa:
+
+<tscreen><verb>
+button = gtk_button_new_with_label ("Special Button");
+gtk_widget_set_name (button, "special button");
+</verb></tscreen>
+<p>
+allora a questo bottone viene dato il nome ``special button'' ed esso pu&ograve; essere
+riferito per nome nel file rc come ``special button.GtkButton''. [<--- Verificatemi!]
+<p>
+Il seguente esempio di file rc imposta le propriet&agrave; della finestra principale,
+e fa si che tutti i figli di questa finestra ereditino lo stile descritto
+dallo stile ``main button''. Il codice usato nell'applicazione &egrave;:
+
+<tscreen><verb>
+window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+gtk_widget_set_name (window, "main window");
+</verb></tscreen>
+<p>
+Lo stile viene definito nel file rc usando:
+
+<tscreen><verb>
+widget "main window.*GtkButton*" style "main_button"
+</verb></tscreen>
+<p>
+che assegna a tutti i widget GtkButton nella finestra principale lo stile
+``main_buttons'' secondo la definizione data nel file rc.
+<p>
+Come si pu&ograve; vedere, questo sistema &egrave; molto potente e flessibile. Usate la
+vostra immaginazione per trarre il massimo vantaggio da esso.
+
+<sect1>Il formato dei file rc di GTK
+<p>
+Nell'esempio che segue viene illustrato il formato del file GTK. Si tratta
+del file testgkrc dalla distribuzione del GTK, a cui sono stati aggiunti
+vari commenti e varie cose. Potete includere questa spiegazione nella
+vostra applicazione per consentire all'utente di personalizzarla finemente.
+<p>
+There are several directives to change the attributes of a widget.
+Ci sono diverse direttive per cambiare gli attributi di un widget.
+<itemize>
+<item>fg - Assegna il colore di primo piano di un widget.
+<item>bg - Assegna il colore di sfondo di un widget.
+<item>bg_pixmap - Inserisce nello sfondo di un widget una pixmap.
+<item>font - Sceglie il font da usarsi con il dato widget.
+</itemize>
+<p>
+Inoltre ci sono diversi stati in cui pu&ograve; trovarsi un widget, e si possono
+assegnare diversi colori, pixmap e font per ogni stato. Essi sono:
+<itemize>
+<item>NORMAL - Lo stato normale di un widget, quando il mouse non si trova su
+di esso, quando non &egrave; premuto, ecc.
+<item>PRELIGHT (evidenziato)- Quando il mouse si trova sopra al widget
+verranno usati i colori assegnati per questo stato.
+<item>ACTIVE (attivo) - Quando il widget &egrave; premuto o cliccato esso sar&agrave; attivo,
+e verranno usati gli attributi assegnati da questa etichetta.
+<item>INSENSITIVE (insensibile)- Quando un widget viene reso insensibile,
+e non pu&ograve; essere attivato, prender&agrave; questi attributi.
+<item>SELECTED (selezionato) - Quando un oggetto viene selezionato, prende
+questi attributi.
+</itemize>
+<p>
+Quando si usano le parole chiave ``fg'' e ``bg'' per assegnare i colori dei
+widget il formato &egrave;:
+<tscreen><verb>
+fg[<STATE>] = { Rosso, Verde, Blu }
+</verb></tscreen>
+<p>
+Dove STATE &egrave; uno degli stati visti prima (PRELIGHT, ACTIVE ecc.), e Rosso,
+Verde e Blu sono valori nell'intervallo 0 - 1.0; { 1.0, 1.0, 1.0 } rappresenta
+il bianco.
+Devono essere in formato float, o verranno visti come 0, sicch&eacute; un ``1'' diretto
+non funziona, deve essere ``1.0''. Uno ``0'' diretto va invece bene, poich&eacute; poco
+importa se non viene riconosciuto: valori non riconosciuti vengono considerati
+0.
+<p>
+bg_pixmap &egrave; molto simile al precedente, tranne per i colori che vengono
+sostituiti dal nome di un file.
+
+pixmap_path &egrave; una lista di percorsi separati da ``:''. In questi percorsi vengono
+cercate le pixmap specificate.
+<p>
+La direttiva font &egrave; semplicemente:
+<tscreen><verb>
+font = "<font name>"
+</verb></tscreen>
+<p>
+dove l'unica parte complicata &egrave; immaginare la stringa del font. Allo scopo
+pu&ograve; servire usare xfontsel o una utilit&agrave; analoga.
+<p>
+``widget_class'' assegna lo stile di una classe di widget. Queste classi sono
+elencate nell'introduzione ai widget sulla gerarchia delle classi.
+<p>
+La direttiva ``widget'' assegna un insieme di widget dal nome specificato ad
+un dato stile, annullando qualsiasi stile assegnato per la data classe di widget.
+Questi widget vengono registrati nell'applicazione usando la chiamata
+gtk_widget_set_name(). Questo consente di specificare gli attributi di un
+widget singlarmente, piuttosto che assegnando gli attributi di un'intera classe
+di widget. E' opportuno documentare tutti questi widget speciali in modo che
+gli utenti possano personalizzarli.
+<p>
+Quando la parola chiave ``<tt>parent</>'' viene usata come un attributo, il
+widget erediter&agrave; gli attributi del suo genitore nell'applicazione.
+<p>
+Quando si definisce uno stile si possono assegnare gli attributi di uno
+stile definito precedentemente a quello nuovo.
+<tscreen><verb>
+style "main_button" = "button"
+{
+ font = "-adobe-helvetica-medium-r-normal--*-100-*-*-*-*-*-*"
+ bg[PRELIGHT] = { 0.75, 0, 0 }
+}
+</verb></tscreen>
+<p>
+Questo esempio prende lo stile ``button'' e crea un nuovo stile
+semplicemente cambiando il font e il colore di sfondo dello stato ``prelight''
+nello stile ``button''.
+<p>
+Naturalmente, molti di questi attributi non sono applicabili a tutti i widget.
+E' veramente un semplice problema di buon senso. Tutto quello che potrebbe
+applicarsi, dovrebbe.
+
+<sect1>Esempio di file rc
+<p>
+
+<tscreen><verb>
+# pixmap_path "<dir 1>:<dir 2>:<dir 3>:..."
+#
+pixmap_path "/usr/include/X11R6/pixmaps:/home/imain/pixmaps"
+#
+# style <name> [= <name>]
+# {
+# <option>
+# }
+#
+# widget <widget_set> style <style_name>
+# widget_class <widget_class_set> style <style_name>
+
+
+# Ecco una lista di tutti gli stati possibili. Si noti che alcuni non sono
+# applicabili a certi widget.
+#
+# NORMAL - Lo stato normale di un widget, quando il mouse non si trova su
+# di esso, quando non &egrave; premuto, ecc.
+#
+# PRELIGHT (evidenziato)- Quando il mouse si trova sopra al widget
+# verranno usati i colori assegnati per questo stato.
+#
+# ACTIVE (attivo) - Quando il widget &egrave; premuto o cliccato esso sar&agrave; attivo,
+# e verranno usati gli attributi assegnati da questa etichetta.
+#
+# INSENSITIVE (insensibile)- Quando un widget viene reso insensibile,
+# e non pu&ograve; essere attivato, prender&agrave; questi attributi.
+#
+# SELECTED (selezionato) - Quando un oggetto viene selezionato, prende
+# questi attributi.
+#
+# Dati questi stati, &egrave; possibile assegnare gli attributi dei widget in
+# ognuno di questi stati usando le seguenti direttive.
+#
+# fg - Assegna il colore di primo piano di un widget.
+# bg - Assegna il colore di sfondo di un widget.
+# bg_pixmap - Inserisce nello sfondo di un widget una pixmap.
+# font - Sceglie il font da usarsi con il dato widget.
+#
+
+# Questo &egrave; uno stile chiamato "button". Il nome non &egrave; veramente importante,
+# in quanto viene assegnato ai veri widget alla fine del file.
+
+style "window"
+{
+ # Questo inserisce nella spaziatura attorno alla finestra la pixmap
+ # specificata.
+ #bg_pixmap[<STATE>] = "<pixmap filename>"
+ bg_pixmap[NORMAL] = "warning.xpm"
+}
+
+style "scale"
+{
+ # Mette il colore di primo piano (il colore del font) a rosso nello
+ # stato "NORMAL".
+
+ fg[NORMAL] = { 1.0, 0, 0 }
+
+ # Inserisce nello sfondo del gadget la stessa pixmap usata dal suo genitore.
+ bg_pixmap[NORMAL] = "<parent>"
+}
+
+style "button"
+{
+ # Questo mostra tutti i possibili stati per un bottone. L'unico che
+ # non &egrave; applicabile &egrave; lo stato "SELECTED".
+
+ fg[PRELIGHT] = { 0, 1.0, 1.0 }
+ bg[PRELIGHT] = { 0, 0, 1.0 }
+ bg[ACTIVE] = { 1.0, 0, 0 }
+ fg[ACTIVE] = { 0, 1.0, 0 }
+ bg[NORMAL] = { 1.0, 1.0, 0 }
+ fg[NORMAL] = { .99, 0, .99 }
+ bg[INSENSITIVE] = { 1.0, 1.0, 1.0 }
+ fg[INSENSITIVE] = { 1.0, 0, 1.0 }
+}
+
+# In questi esempio ereditiamo gli attributi dello stile "button" e poi
+# alteriamo il font e il colore di sfondo quando evidenziato per creare
+# un nuovo stile "main_button".
+
+style "main_button" = "button"
+{
+ font = "-adobe-helvetica-medium-r-normal--*-100-*-*-*-*-*-*"
+ bg[PRELIGHT] = { 0.75, 0, 0 }
+}
+
+style "toggle_button" = "button"
+{
+ fg[NORMAL] = { 1.0, 0, 0 }
+ fg[ACTIVE] = { 1.0, 0, 0 }
+
+ # Questo seleziona come pixmap di sfondo per il toggle_button quella del
+ # suo widget genitore (definita nell'applicazione).
+ bg_pixmap[NORMAL] = "<parent>"
+}
+
+style "text"
+{
+ bg_pixmap[NORMAL] = "marble.xpm"
+ fg[NORMAL] = { 1.0, 1.0, 1.0 }
+}
+
+style "ruler"
+{
+ font = "-adobe-helvetica-medium-r-normal--*-80-*-*-*-*-*-*"
+}
+
+# pixmap_path "~/.pixmaps"
+
+# Queste assegnano ai tipi di widget gli stili definiti prima.
+# I tipi di widget sono elencati nella gerarchia delle classi, ma probabilmente
+# dovrebbero essere elencati in questo documento come riferimento per l'utente.
+
+widget_class "GtkWindow" style "window"
+widget_class "GtkDialog" style "window"
+widget_class "GtkFileSelection" style "window"
+widget_class "*Gtk*Scale" style "scale"
+widget_class "*GtkCheckButton*" style "toggle_button"
+widget_class "*GtkRadioButton*" style "toggle_button"
+widget_class "*GtkButton*" style "button"
+widget_class "*Ruler" style "ruler"
+widget_class "*GtkText" style "text"
+
+# Questo assegna lo stile main_button a tutti i bottoni che sono figli della
+# "main window" (finestra principale). Questi devono essere documenati per
+# potersene avvantaggiare.
+widget "main window.*GtkButton*" style "main_button"
+</verb></tscreen>
+
+
+
+<sect>Scrivere un proprio Widget
+
+<p>
+<sect1> Panoramica
+<p>
+Anche se la distribuzione GTK contiene molto tipi di widget che possono
+coprire molte necessit&agrave; basilari, pu&ograve; essere necessario costruirsi
+un proprio widget. GTK usa molto l'ereditariet&agrave; tra i vari
+widget e, di solito, vi &egrave; un widget che si avvicina a quello che ti
+servirebbe, ed &egrave; spesso possibile creare un nuovo widget con poche linee
+di codice. Ma prima di iniziare il lavoro su un nuovo widget, vediamo
+se qualcuno non lo ha gi&agrave; creato. Questo eviter&agrave; un duplicazione
+di lavoro e far&agrave; s&igrave; che i widget non-GTK puri siano minimi, cos&igrave; da
+aiutare sia chi crea il codice che chi l'interfaccia per applicazioni GTK
+molto grosse. D'altra parte, quando hai finito di scrivere un widget,
+annuncialo a tutto il mondo cos&igrave; che le altre persone ne possano
+beneficiare. Il miglioro modo dove farlo &egrave; la <tt>gtk-list</tt>.
+
+<sect1> L'anatomia di un widget
+
+<p>
+Per creare un nuovo widget &egrave; importante aver capito come gli ogetti
+di GTK lavorano. Questa sezione &egrave; solo una breve spiegazione. Guarda la
+documentazione di riferimento per maggiori dettagli.
+
+<p>
+I widget GTK sono implementati in un modo orientato agli oggetti,
+anche se usando il C standard. Questo aumenta notevolmente la portabilit&agrave;
+e la stabilit&agrave;, specialmente per le correnti generazioni di compilatori C++;
+comunque questo significa che chi scrive un widget deve fare attenzione
+ad alcuni dettagli di implementazione. L'informazione comune a tutte le
+istanze di una classe di widget (ad esempio: a tutti i bottoni) &egrave; memorizzata
+<em>class structure</em>. C'e' solamente una copia di questo in cui
+sono memorizzate le informazioni riguardanti i segnali della classe
+(assomiglia ad una funzione virtuale in C). Per supportare l'ereditariet&agrave;
+il primo campo della struttura di una classe deve essere una copia della
+struttura della classe genitore. La dichiarazione della struttura della
+classe GtkButton &egrave;:
+
+<tscreen><verb>
+struct _GtkButtonClass
+{
+ GtkContainerClass parent_class;
+
+ void (* pressed) (GtkButton *button);
+ void (* released) (GtkButton *button);
+ void (* clicked) (GtkButton *button);
+ void (* enter) (GtkButton *button);
+ void (* leave) (GtkButton *button);
+};
+</verb></tscreen>
+
+<p>
+Quando un bottone viene trattato come un contenitore (ad esempio quando viene
+ridimensionato) si pu&ograve; fare il cast della struttura della sua classe con la
+GtkContainerClass, e usare i campi rilevanti per gestire i segnali.
+
+<p>
+C'&egrave; anche una struttura per ogni widget che viene creata
+ad ogni istanza. Questa struttura ha campi per
+memorizzare le informazioni che sono differenti per ogni volta che il widget
+viene istanziato. Chiameremo questa struttura la <em> struttura
+oggetto</em>. Per la classe Bottone, questa ha l'aspetto:
+
+<tscreen><verb>
+struct _GtkButton
+{
+ GtkContainer container;
+
+ GtkWidget *child;
+
+ guint in_button : 1;
+ guint button_down : 1;
+};
+</verb></tscreen>
+
+<p>
+Si noti che, similmente alla struttura della classe, il primo campo
+&egrave; la struttura dell'oggetto della classe madre, cos&igrave; che, se necessario, si pu&ograve; fare il
+cast di questa struttura con quella dell'oggetto della classe madre.
+
+<sect1> Creare un Widget composto
+
+<sect2> Introduzione
+
+<p>
+Un tipo di widget a cui potreste essere interessati &egrave; un widget che
+&egrave; semplicemnte un aggregato di altri widget GTK. Questo tipo di
+widget non fa nulla che non possa essere fatto creando un nuovo
+widget, ma fornisce un modo conveniente per inscatolare elementi
+dell'interfaccia utente per poi riutilizzarli.
+I widget FileSelection e ColorSelection della ditribuzione standard
+sono esempi di questo tipo di widget.
+
+<p>
+Il widget di esempio che creeremo in questo capitolo &egrave; il
+Tictactoe, un vettore 3x3 di bottoni a commutazione il quale emette
+un segnale quando tutti e 3 i bottoni di una riga, colonna o di una
+diagonale sono premuti.
+
+<sect2> Scegliere la classe madre
+
+<p>
+La classe madre per un widget composto e' tipicamente la classe
+contenitrice che racchiude tutti gli elementi del widget composto.
+Per esempio, la classe madre del widget FileSelection &egrave; la classe
+Dialog. Visto che i nostri bottoni sono inseriti in una tabella, &egrave;
+naturale pensare che la nostra classe madre possa essere la GtkTable.
+Sfortunatamente, cos&igrave; non &egrave;. La creazione di un widget &egrave; diviso
+tra 2 funzioni : la funzione <tt/WIDGETNAME_new()/ che viene invocata
+dall'utente, e la funzione <tt/WIDGETNAME_init()/ che ha il compito
+principale di inizializzare il widget che &egrave; indipendente dai valori
+passati alla funzione <tt/_new()/. Widget figli o discendenti possono
+chiamare, solamente, la funzione del loro widget genitore.
+Ma questa divisione del lavoro non funziona bene per la tabella, la
+quale, quando creata, necessita di conoscere il numero di righe e
+colonne che la comporr&agrave;. A meno che non vogliamo duplicare molte delle
+fuinzionalit&agrave; della <tt/gtk_table_new()/ nel nostro widget
+Tictactoe, faremmo meglio a evitare di derivarlo dalla GtkTable. Per questa
+ragione lo deriviamo invece da GtkVBox, e uniamo la nostra tabella
+dentro il VBox.
+
+<sect2> Il File Header
+
+<p>
+Ogni classe di widget ha un file header il quale dichiara l'oggetto e la
+struttura della classe del widget, comprese le funzioni pubbliche.
+Per prevenire duplicati di definizioni, noi includiamo l'intero file header fra:
+
+<tscreen><verb>
+#ifndef __TICTACTOE_H__
+#define __TICTACTOE_H__
+.
+.
+.
+#endif /* __TICTACTOE_H__ */
+</verb></tscreen>
+
+E per far felici i programmi in C++ che includono il nostro file header, in:
+
+<tscreen><verb>
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+.
+.
+.
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+</verb></tscreen>
+
+Insieme alle funzioni e alle strutture, dichiariamo tre macro
+standard nel nostro file header, <tt/TICTACTOE(obj)/,
+<tt/TICTACTOE_CLASS(klass)/, e <tt/IS_TICTACTOE(obj)/, i quali rispettivamente
+fanno il cast di un puntatore ad un puntatore ad un ogetto od ad una struttura
+di classe, e guarda se un oggetto &egrave; un widget Tictactoe.
+
+
+Qui vi &egrave; il file header completo:
+
+<tscreen><verb>
+
+#ifndef __TICTACTOE_H__
+#define __TICTACTOE_H__
+
+#include <gdk/gdk.h>
+#include <gtk/gtkvbox.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#define TICTACTOE(obj) GTK_CHECK_CAST (obj, tictactoe_get_type (), Tictactoe)
+#define TICTACTOE_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, tictactoe_get_type (), TictactoeClass)
+#define IS_TICTACTOE(obj) GTK_CHECK_TYPE (obj, tictactoe_get_type ())
+
+
+typedef struct _Tictactoe Tictactoe;
+typedef struct _TictactoeClass TictactoeClass;
+
+struct _Tictactoe
+{
+ GtkVBox vbox;
+
+ GtkWidget *buttons[3][3];
+};
+
+struct _TictactoeClass
+{
+ GtkVBoxClass parent_class;
+
+ void (* tictactoe) (Tictactoe *ttt);
+};
+
+guint tictactoe_get_type (void);
+GtkWidget* tictactoe_new (void);
+void tictactoe_clear (Tictactoe *ttt);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __TICTACTOE_H__ */
+
+</verb></tscreen>
+
+<sect2> La funzione <tt/_get_type()/
+
+<p>
+Continuiamo ora con l'implementazione del nostro widget. Una funzione
+basilare di ogni widget &egrave; la funzione <tt/WIDGETNAME_get_type()/.
+Questa funzione, quando chiamata la prima volta, comunica a GTK la classe
+del widget, e ottiene un identificativo univoco per la classe del
+widget. Chiamate successive restituiscono semplicemente l'identificativo.
+
+<tscreen><verb>
+guint
+tictactoe_get_type ()
+{
+ static guint ttt_type = 0;
+
+ if (!ttt_type)
+ {
+ GtkTypeInfo ttt_info =
+ {
+ "Tictactoe",
+ sizeof (Tictactoe),
+ sizeof (TictactoeClass),
+ (GtkClassInitFunc) tictactoe_class_init,
+ (GtkObjectInitFunc) tictactoe_init,
+ (GtkArgFunc) NULL,
+ };
+
+ ttt_type = gtk_type_unique (gtk_vbox_get_type (), &amp;ttt_info);
+ }
+
+ return ttt_type;
+}
+</verb></tscreen>
+
+<p>
+La struttura GtkTypeInfo ha la seguente definizione:
+
+<tscreen><verb>
+struct _GtkTypeInfo
+{
+ gchar *type_name;
+ guint object_size;
+ guint class_size;
+ GtkClassInitFunc class_init_func;
+ GtkObjectInitFunc object_init_func;
+ GtkArgFunc arg_func;
+};
+</verb></tscreen>
+
+<p>
+I campi di questa struttura sono abbastanza auto-esplicativi.
+Ignoreremo, per ora, il campo <tt/arg_func/: ha un ruolo importante, ma
+non ancora largamente implementato, nel permettere ai linguaggi interpretati
+di settare convenientemente le opzioni del widget.
+Una volta che il GTK ha completato correttamente una copia di questa
+struttura, sa come creare un oggetto di un particolare widget.
+
+<sect2> La funzione <tt/_class_init()/
+<p>
+La funzione <tt/WIDGETNAME_class_init()/ inizialiazza i campi della
+struttura della classe del widget, e setta ogni segnale della classe.
+Per il nostro widget Tictactoe ha il seguente aspetto:
+
+<tscreen><verb>
+
+enum {
+ TICTACTOE_SIGNAL,
+ LAST_SIGNAL
+};
+
+static gint tictactoe_signals[LAST_SIGNAL] = { 0 };
+
+static void
+tictactoe_class_init (TictactoeClass *class)
+{
+ GtkObjectClass *object_class;
+
+ object_class = (GtkObjectClass*) class;
+
+ tictactoe_signals[TICTACTOE_SIGNAL] = gtk_signal_new ("tictactoe",
+ GTK_RUN_FIRST,
+ object_class->type,
+ GTK_SIGNAL_OFFSET (TictactoeClass, tictactoe),
+ gtk_signal_default_marshaller, GTK_ARG_NONE, 0);
+
+
+ gtk_object_class_add_signals (object_class, tictactoe_signals, LAST_SIGNAL);
+
+ class->tictactoe = NULL;
+}
+</verb></tscreen>
+
+<p>
+Il nostro widget ha semplicemente il segnale ``tictactoe'' che &egrave;
+invocato quando una riga, colonna o diagonale &egrave; completamente premuta.
+Non tutti i widget composti necessitano di segnali, quindi se stai
+leggendo questo per la prima volta, puoi anche saltare alla prossima sezione,
+dal momento che a questo punto le cose diventano un po' complicate.
+
+La funzione:
+<tscreen><verb>
+gint gtk_signal_new (gchar *name,
+ GtkSignalRunType run_type,
+ gint object_type,
+ gint function_offset,
+ GtkSignalMarshaller marshaller,
+ GtkArgType return_val,
+ gint nparams,
+ ...);
+</verb></tscreen>
+
+crea un nuovo segnale. I parametri sono:
+
+<itemize>
+<item> <tt/name/: Il nome del segnale.
+<item> <tt/run_type/: Se il segstore predefinito viene eseguito prima o dopo
+di quello dell'utente. Di norma questo sar&agrave; <tt/GTK_RUN_FIRST/, o <tt/GTK_RUN_LAST/,
+anche se ci sono altre possibilit&agrave;.
+<item> <tt/object_type/: l'identificativo dell'oggetto a cui questo segnale si
+riferisce. Esso sar&agrave; anche applicato agli oggetti discendenti.
+<item> <tt/function_offset/: L'offset nella struttura della classe di un
+puntatore al gestore predefinito.
+<item> <tt/marshaller/: una funzione che &egrave; usata per invocare il gestore
+del segnale. Per gestori di segnali che non hanno argomenti oltre
+all'oggetto che emette il segnale e i dati dell'utente, possiamo usare
+la funzione predefinita <tt/gtk_signal_default_marshaller/
+<item> <tt/return_val/: Il tipo del valore di ritorno.
+<item> <tt/nparams/: Il numero di parametri del gestore di segnali (oltre
+ai due predefiniti menzionati sopra)
+<item> <tt/.../: i tipi dei parametri
+</itemize>
+
+Quando si specificano i tipi, si usa l'enumerazione <tt/GtkArgType/:
+
+<tscreen><verb>
+typedef enum
+{
+ GTK_ARG_INVALID,
+ GTK_ARG_NONE,
+ GTK_ARG_CHAR,
+ GTK_ARG_SHORT,
+ GTK_ARG_INT,
+ GTK_ARG_LONG,
+ GTK_ARG_POINTER,
+ GTK_ARG_OBJECT,
+ GTK_ARG_FUNCTION,
+ GTK_ARG_SIGNAL
+} GtkArgType;
+</verb></tscreen>
+
+<p>
+<tt/gtk_signal_new()/ restituisce un identificatore unico intero per il segnale,
+che memorizziamo nel vettore <tt/tictactoe_signals/, che
+indicizzeremo usando una enumerazione. (Convenzionalmente, gli elementi dell'enumerazione
+sono i nomi dei segnali, in maiuscolo,
+ma qui ci potrebbe essere un conflitto con la macro <tt/TICTACTOE()/,
+quindi l'abbiamo chiamato <tt/TICTACTOE_SIGNAL/
+
+Dopo aver creato un nostro segnale, abbiamo bisogno di dire a GTK
+di associare il nostro segnale alla classe Tictactoe. Lo facciamo
+invocando <tt/gtk_object_class_add_signals()/. Settiamo quindi a NULL
+il puntatore che punta al gestore predefinito per il segnale
+``tictactoe'' a NULL, indicando che non ci sono azioni predefinite.
+
+<sect2> La funzione <tt/_init()/
+
+<p>
+
+Ogni classe di Widget necessita anche di una funzione per inizializzare
+la struttura dell'oggetto. Usualmente questa funzione ha il ruolo abbastanza
+limitato di assegnare ai campi della struttura i valori predefiniti.
+Per widget composti, comunque, questa funzione crea, anche,
+i widget componenti del widget composto.
+
+<tscreen><verb>
+
+static void
+tictactoe_init (Tictactoe *ttt)
+{
+ GtkWidget *table;
+ gint i,j;
+
+ table = gtk_table_new (3, 3, TRUE);
+ gtk_container_add (GTK_CONTAINER(ttt), table);
+ gtk_widget_show (table);
+
+ for (i=0;i<3; i++)
+ for (j=0;j<3; j++)
+ {
+ ttt->buttons[i][j] = gtk_toggle_button_new ();
+ gtk_table_attach_defaults (GTK_TABLE(table), ttt->buttons[i][j],
+ i, i+1, j, j+1);
+ gtk_signal_connect (GTK_OBJECT (ttt->buttons[i][j]), "toggled",
+ GTK_SIGNAL_FUNC (tictactoe_toggle), ttt);
+ gtk_widget_set_usize (ttt->buttons[i][j], 20, 20);
+ gtk_widget_show (ttt->buttons[i][j]);
+ }
+}
+</verb></tscreen>
+
+<sect2> E il resto...
+
+<p>
+
+C'&egrave; un'altra funzione che ogni widget (eccetto i Widget di base come
+GtkBin che non possono essere instanziati) deve avere : la funzione
+che l'utente invoca per creare un oggetto di quel tipo. Questa &egrave;
+convenzionalmente chiamata <tt/WIDGETNAME_new()/. In alcuni widget,
+non nel caso del nostro Tictactoe, questa funzione richiede degli
+argomenti, e fa alcune operazioni basandosi su di essi. Le altre
+due funzioni sono specifiche del widget Tictactoe.
+
+<p>
+<tt/tictactoe_clear()/ &egrave; una funzione pubblica che resetta tutti i
+bottoni, nel widget, allo stato iniziale (non premuto). Notate l'uso
+di <tt/gtk_signal_handler_block_by_data()/ per impedire che il nostro
+gestore dei segnali venga attivato quando non ce n'&egrave; bisogno.
+
+<p>
+<tt/tictactoe_toggle()/ &egrave; il gestore del segnale che viene invocato
+quando l'utente preme il bottone. Esso guarda se vi &egrave;
+qualche combinazione vincente che coinvolge i bottoni premuti, e nel
+caso ci fosse, emette il segnale ``tictactoe''.
+
+<tscreen><verb>
+GtkWidget*
+tictactoe_new ()
+{
+ return GTK_WIDGET ( gtk_type_new (tictactoe_get_type ()));
+}
+
+void
+tictactoe_clear (Tictactoe *ttt)
+{
+ int i,j;
+
+ for (i=0;i<3;i++)
+ for (j=0;j<3;j++)
+ {
+ gtk_signal_handler_block_by_data (GTK_OBJECT(ttt->buttons[i][j]), ttt);
+ gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (ttt->buttons[i][j]),
+ FALSE);
+ gtk_signal_handler_unblock_by_data (GTK_OBJECT(ttt->buttons[i][j]), ttt);
+ }
+}
+
+static void
+tictactoe_toggle (GtkWidget *widget, Tictactoe *ttt)
+{
+ int i,k;
+
+ static int rwins[8][3] = { { 0, 0, 0 }, { 1, 1, 1 }, { 2, 2, 2 },
+ { 0, 1, 2 }, { 0, 1, 2 }, { 0, 1, 2 },
+ { 0, 1, 2 }, { 0, 1, 2 } };
+ static int cwins[8][3] = { { 0, 1, 2 }, { 0, 1, 2 }, { 0, 1, 2 },
+ { 0, 0, 0 }, { 1, 1, 1 }, { 2, 2, 2 },
+ { 0, 1, 2 }, { 2, 1, 0 } };
+
+ int success, found;
+
+ for (k=0; k<8; k++)
+ {
+ success = TRUE;
+ found = FALSE;
+
+ for (i=0;i<3;i++)
+ {
+ success = success &amp;&amp;
+ 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 &amp;&amp; found)
+ {
+ gtk_signal_emit (GTK_OBJECT (ttt),
+ tictactoe_signals[TICTACTOE_SIGNAL]);
+ break;
+ }
+ }
+}
+</verb></tscreen>
+
+<p>
+
+E finalmente un programma di esempio che usa il nostro widget
+Tictactoe:
+
+<tscreen><verb>
+#include <gtk/gtk.h>
+#include "tictactoe.h"
+
+/* Invocato quando una riga, colonna o diagonale e' completata. */
+void
+win (GtkWidget *widget, gpointer data)
+{
+ g_print ("Yay!\n");
+ tictactoe_clear (TICTACTOE (widget));
+}
+
+int
+main (int argc, char *argv[])
+{
+ GtkWidget *window;
+ GtkWidget *ttt;
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+ gtk_window_set_title (GTK_WINDOW (window), "Aspect Frame");
+
+ gtk_signal_connect (GTK_OBJECT (window), "destroy",
+ GTK_SIGNAL_FUNC (gtk_exit), NULL);
+
+ gtk_container_border_width (GTK_CONTAINER (window), 10);
+
+ /* Crea un nuovo widget Tictactoe. */
+ ttt = tictactoe_new ();
+ gtk_container_add (GTK_CONTAINER (window), ttt);
+ gtk_widget_show (ttt);
+
+ /* E gli aggancia il segnale "tictactoe" */
+ gtk_signal_connect (GTK_OBJECT (ttt), "tictactoe",
+ GTK_SIGNAL_FUNC (win), NULL);
+
+ gtk_widget_show (window);
+
+ gtk_main ();
+
+ return 0;
+}
+
+</verb></tscreen>
+
+
+<sect1> Creare un widget a partire da zero
+
+<sect2> Introduzione
+
+<p>
+
+In questa sezione impareremo meglio come i widget si mostrano sullo schermo
+e interagiscono con gli eventi. Come esempio, creeremo
+un widget di quadrante analogico con un puntatore che l'utente
+pu&ograve; trascinare per assegnare il valore.
+
+<sect2> Mostrare un widget sullo schermo
+
+<p>
+Ci sono alcuni passi che sono necessari nella visualizzazione sullo
+schermo. Dopo che il widget &egrave; stato creato con una chiamata a
+<tt/WIDGETNAME_new()/, sono necessarie alcune altre funzioni:
+
+<itemize>
+<item> <tt/WIDGETNAME_realize()/ &egrave; responsabile della creazione di
+una finestra X per il widget se ne ha una.
+<item> <tt/WIDGETNAME_map()/ &egrave; invocata dopo che l'utente ha
+chiamato <tt/gtk_widget_show()/. E' responsabile di vedere se il
+widget &egrave; attualmente disegnato sullo schermo (<em/mappato/). Per
+una classe contenitore, essa deve anche creare chiamate alle
+funzioni <tt/map()/> per ogni widget figlio.
+<item> <tt/WIDGETNAME_draw()/ &egrave; invocata quando
+<tt/gtk_widget_draw()/ viene chiamata per il widget o per uno dei suoi
+predecessori. Esso fa s&igrave; che l'attuale chiamata alla
+funzione di disegno del widget disegni il widget sullo schermo.
+Per la classe contenitore, questa funzione deve eseguire le
+chiamate alla funzioni <tt/gtk_widget_draw()/ di ogni suo widget
+figlio.
+<item> <tt/WIDGETNAME_expose()/ &egrave; un gestore per l'evento di esposizione
+per il widget. Esso crea le chiamate necessarie alle funzioni di disegno
+per disegnare la porzione che si &egrave; resa visibile. Per le classi
+contenitore, questa funzione deve generare gli eventi di ``expose'' per
+tutti i widget figli che non hanno una propria finestra (se essi hanno
+una loro finestra, sar&agrave; X che generer&agrave; i necessari eventi di expose).
+</itemize>
+
+<p>
+Potete notare che le ultime due funzioni sono molto simili, ognuna &egrave;
+responsabile per il disegno del widget sullo schermo. Infatti molti
+tipi di widget non sanno relamente la differenza tra le due.
+La funzione di predefinita <tt/draw()/ nella classe widget, semplicemente
+genera un sintetico evento di ``expose'' per l'area da ridisegnare.
+Comunque, alcuni tipi di widget possono risparmiare tempo distinguendo
+le due funzioni. Per esempio, se un widget ha piu' finestre X, allora
+visto che l'evento ``expose'' identifica solo la finestra esposta,
+esso pu&ograve; ridisegnare solo la finestra interessata, cosa che non &egrave;
+possibile per chiamate a <tt/draw()/.
+
+<p>
+I widget contenitori, anche se essi non farebbero differenze,
+non possono semplicemente usare la funzione <tt/draw()/ perch&egrave; per i
+loro widget figli la differenza potrebbere essere importante. Comunque,
+sarebbe uno spreco duplicare il codice di disegno nelle due
+funzioni. La convenzione &egrave; che questi widget abbiano una funzione
+chiamata <tt/WIDGETNAME_paint()/ che disegna il widget, che &egrave; poi
+chiamata dalle funzioni <tt/draw()/ e <tt/expose()/
+
+<p>
+Nell'approccio del nostro esempio, visto che il widget, ha
+una sola finestra, possiamo utilizzare il modo piu' semplice
+ed usare la funzione predefinita <tt/draw()/ e implementare
+solamente la funzione <tt/expose()/.
+
+<sect2> Le origini del widget Dial
+
+<p>
+Come tutti gli animali terresti sono semplicemente varianti del primo
+amfibio, i widget Gtk tendono ad essere varianti di altri widget, precedentemente
+scritti. Cos&igrave;, anche se questa sezione &egrave; intitolata ``Creare
+un widget a partire da zero", il nostro widget inizia in realt&agrave; con il codice
+sorgente del widget Range. Questo &egrave; stato preso come punto d'inizio
+perche' sarebbe carino se il nostro widget avesse la
+stessa interfaccia del widget Scale il quale &egrave; semplicemente una
+specializzazione del widget Range. Cos&igrave;, sebbene il codice sorgente e'
+presentato sotto in forma definitiva, non si deve pensare che sia stato
+scritto <em>deus ex machina</em> in questo modo. Se poi non avete familiarit&agrave;
+con il funzionamento del widget Scale dal punto di vista di chi scrive
+un'applicazione, potrebbe essere una buona idea guardare indietro prima
+di continuare.
+
+<sect2> Le basi
+
+<p>
+Una parte del nostro widget potrebbe essere simile
+al widget Tictactoe. In primo luogo, abbiamo il file header:
+
+<tscreen><verb>
+/* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __GTK_DIAL_H__
+#define __GTK_DIAL_H__
+
+#include <gdk/gdk.h>
+#include <gtk/gtkadjustment.h>
+#include <gtk/gtkwidget.h>
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+#define GTK_DIAL(obj) GTK_CHECK_CAST (obj, gtk_dial_get_type (), GtkDial)
+#define GTK_DIAL_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, gtk_dial_get_type (), GtkDialClass)
+#define GTK_IS_DIAL(obj) GTK_CHECK_TYPE (obj, gtk_dial_get_type ())
+
+
+typedef struct _GtkDial GtkDial;
+typedef struct _GtkDialClass GtkDialClass;
+
+struct _GtkDial
+{
+ GtkWidget widget;
+
+ /* Politica di update (GTK_UPDATE_[CONTINUOUS/DELAYED/DISCONTINUOUS]) */
+ guint policy : 2;
+
+ /* Bottone correntemente premuto o 0 altrimenti */
+ guint8 button;
+
+ /* Dimensione della componente Dial. */
+ gint radius;
+ gint pointer_width;
+
+ /* ID del timer di update, o 0 altrimenti */
+ guint32 timer;
+
+ /* Angolo corrente. */
+ gfloat angle;
+
+ /* Vecchi valori dell'aggiustamento cos&igrave; sappiamo quando
+ * qualcosa cambia */
+ gfloat old_value;
+ gfloat old_lower;
+ gfloat old_upper;
+
+ /* L'oggetto adjustament che memorizza i dati per questo dial */
+ GtkAdjustment *adjustment;
+};
+
+struct _GtkDialClass
+{
+ GtkWidgetClass parent_class;
+};
+
+
+GtkWidget* gtk_dial_new (GtkAdjustment *adjustment);
+guint gtk_dial_get_type (void);
+GtkAdjustment* gtk_dial_get_adjustment (GtkDial *dial);
+void gtk_dial_set_update_policy (GtkDial *dial,
+ GtkUpdateType policy);
+
+void gtk_dial_set_adjustment (GtkDial *dial,
+ GtkAdjustment *adjustment);
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif /* __GTK_DIAL_H__ */
+
+</verb></tscreen>
+
+Essendoci pi&ugrave; cose da fare con questo widget, rispetto al precedente,
+abbiamo pi&ugrave; cambi nella struttura dati, ma le altre cose sono
+abbastamza simili.
+
+<p>
+
+Dopo aver incluso i file di header e aver dichiarato alcune costanti,
+dobbiamo fornire alcune funzioni circa il widget e la sua
+inizializzazione.
+
+<tscreen><verb>
+#include <math.h>
+#include <stdio.h>
+#include <gtk/gtkmain.h>
+#include <gtk/gtksignal.h>
+
+#include "gtkdial.h"
+
+#define SCROLL_DELAY_LENGTH 300
+#define DIAL_DEFAULT_SIZE 100
+
+/* Dichiarazioni di funzioni successive */
+
+[ omesse per salvare spazio ]
+
+/* variabili locali. */
+
+static GtkWidgetClass *parent_class = NULL;
+
+guint
+gtk_dial_get_type ()
+{
+ static guint dial_type = 0;
+
+ if (!dial_type)
+ {
+ GtkTypeInfo dial_info =
+ {
+ "GtkDial",
+ sizeof (GtkDial),
+ sizeof (GtkDialClass),
+ (GtkClassInitFunc) gtk_dial_class_init,
+ (GtkObjectInitFunc) gtk_dial_init,
+ (GtkArgFunc) NULL,
+ };
+
+ dial_type = gtk_type_unique (gtk_widget_get_type (), &amp;dial_info);
+ }
+
+ return dial_type;
+}
+
+static void
+gtk_dial_class_init (GtkDialClass *class)
+{
+ GtkObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+
+ object_class = (GtkObjectClass*) class;
+ widget_class = (GtkWidgetClass*) class;
+
+ parent_class = gtk_type_class (gtk_widget_get_type ());
+
+ object_class->destroy = gtk_dial_destroy;
+
+ widget_class->realize = gtk_dial_realize;
+ widget_class->expose_event = gtk_dial_expose;
+ widget_class->size_request = gtk_dial_size_request;
+ widget_class->size_allocate = gtk_dial_size_allocate;
+ widget_class->button_press_event = gtk_dial_button_press;
+ widget_class->button_release_event = gtk_dial_button_release;
+ widget_class->motion_notify_event = gtk_dial_motion_notify;
+}
+
+static void
+gtk_dial_init (GtkDial *dial)
+{
+ dial->button = 0;
+ dial->policy = GTK_UPDATE_CONTINUOUS;
+ dial->timer = 0;
+ dial->radius = 0;
+ dial->pointer_width = 0;
+ dial->angle = 0.0;
+ dial->old_value = 0.0;
+ dial->old_lower = 0.0;
+ dial->old_upper = 0.0;
+ dial->adjustment = NULL;
+}
+
+GtkWidget*
+gtk_dial_new (GtkAdjustment *adjustment)
+{
+ GtkDial *dial;
+
+ dial = gtk_type_new (gtk_dial_get_type ());
+
+ if (!adjustment)
+ adjustment = (GtkAdjustment*) gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
+
+ gtk_dial_set_adjustment (dial, adjustment);
+
+ return GTK_WIDGET (dial);
+}
+
+static void
+gtk_dial_destroy (GtkObject *object)
+{
+ GtkDial *dial;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GTK_IS_DIAL (object));
+
+ dial = GTK_DIAL (object);
+
+ if (dial->adjustment)
+ gtk_object_unref (GTK_OBJECT (dial->adjustment));
+
+ if (GTK_OBJECT_CLASS (parent_class)->destroy)
+ (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+</verb></tscreen>
+
+Notate che questa funzione <tt/init()/ fa meno rispetto all'analoga del
+widget Tictactoe, essendo questo un widget non composto, e la
+funzione <tt/new()/ fa di pi&ugrave;, essendoci un argomento. Inoltre,
+notate che quando memorizziamo un puntatore all'oggetto Adjustment,
+incrementiamo il conteggio dei suoi riferimenti(e corrispondentemente
+lo decrementato quando non lo usiamo pi&ugrave;) cos&igrave; che GTK pu&ograve; tener traccia di
+quando &egrave; possibile distruggerlo senza causare guai.
+
+<p>
+Inoltre, ci sono alcune funzioni per manipolare le opzioni del widget:
+
+<tscreen><verb>
+GtkAdjustment*
+gtk_dial_get_adjustment (GtkDial *dial)
+{
+ g_return_val_if_fail (dial != NULL, NULL);
+ g_return_val_if_fail (GTK_IS_DIAL (dial), NULL);
+
+ return dial->adjustment;
+}
+
+void
+gtk_dial_set_update_policy (GtkDial *dial,
+ GtkUpdateType policy)
+{
+ g_return_if_fail (dial != NULL);
+ g_return_if_fail (GTK_IS_DIAL (dial));
+
+ dial->policy = policy;
+}
+
+void
+gtk_dial_set_adjustment (GtkDial *dial,
+ GtkAdjustment *adjustment)
+{
+ g_return_if_fail (dial != NULL);
+ g_return_if_fail (GTK_IS_DIAL (dial));
+
+ if (dial->adjustment)
+ {
+ gtk_signal_disconnect_by_data (GTK_OBJECT (dial->adjustment), (gpointer) dial);
+ gtk_object_unref (GTK_OBJECT (dial->adjustment));
+ }
+
+ dial->adjustment = adjustment;
+ gtk_object_ref (GTK_OBJECT (dial->adjustment));
+
+ gtk_signal_connect (GTK_OBJECT (adjustment), "changed",
+ (GtkSignalFunc) gtk_dial_adjustment_changed,
+ (gpointer) dial);
+ gtk_signal_connect (GTK_OBJECT (adjustment), "value_changed",
+ (GtkSignalFunc) gtk_dial_adjustment_value_changed,
+ (gpointer) dial);
+
+ dial->old_value = adjustment->value;
+ dial->old_lower = adjustment->lower;
+ dial->old_upper = adjustment->upper;
+
+ gtk_dial_update (dial);
+}
+</verb></tscreen>
+
+<sect2> <tt/gtk_dial_realize()/
+
+<p>
+Abbiamo ora raggiunto alcuni nuovi tipi di funzione. In primo luogo,
+abbiamo una funzione che crea la finestra di X. Noterete che viene
+passata alla funzione <tt/gdk_window_new()/ una maschera che
+specifica quali campi della struttura GdkWindowAttr non sono vuoti
+(ai rimanenti campi pu&ograve; essere dato il valore predefinito). Anche
+il modo con cui la maschera degli eventi del widget creata non &egrave;
+complicato. Chiameremo <tt/gtk_widget_get_events()/ per sapere la
+maschera degli eventi che l'utente ha specificato per questo widget
+(con <tt/gtk_widget_set_events()/) e aggiungeremo gli eventi che ci possono
+interessare.
+
+<p>
+Dopo aver creato la finestra, settiamo lo stile e lo sfondo,
+e creiamo un puntatore al widget nel campo dei dati utente (user data)
+del GdkWindow. Quest'ultimo passo permette a GTK di mandare gli
+eventi della finestra al widget corretto.
+
+<tscreen><verb>
+static void
+gtk_dial_realize (GtkWidget *widget)
+{
+ GtkDial *dial;
+ GdkWindowAttr attributes;
+ gint attributes_mask;
+
+ g_return_if_fail (widget != NULL);
+ g_return_if_fail (GTK_IS_DIAL (widget));
+
+ GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
+ dial = GTK_DIAL (widget);
+
+ attributes.x = widget->allocation.x;
+ attributes.y = widget->allocation.y;
+ attributes.width = widget->allocation.width;
+ attributes.height = widget->allocation.height;
+ attributes.wclass = GDK_INPUT_OUTPUT;
+ attributes.window_type = GDK_WINDOW_CHILD;
+ attributes.event_mask = gtk_widget_get_events (widget) |
+ GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK |
+ GDK_POINTER_MOTION_HINT_MASK;
+ attributes.visual = gtk_widget_get_visual (widget);
+ attributes.colormap = gtk_widget_get_colormap (widget);
+
+ attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
+ widget->window = gdk_window_new (widget->parent->window, &amp;attributes, attributes_mask);
+
+ widget->style = gtk_style_attach (widget->style, widget->window);
+
+ gdk_window_set_user_data (widget->window, widget);
+
+ gtk_style_set_background (widget->style, widget->window, GTK_STATE_ACTIVE);
+}
+</verb></tscreen>
+
+<sect2> Negoziazione della dimensione
+
+<p>
+Prima di visualizzare per la prima volta la finestra, e se il
+layout della finestra cambia, GTK chiede ad ogni widget, incluso nella
+finestra, la propria dimensione. Questa richiesta &egrave; fatta dalla
+funzione <tt/gtk_dial_size_request()/. Non essendo il nostro widget
+un contenitore, e non avendo dei veri limiti per la propria
+dimensione, restituiamo semplicemnte un valore ragionevole.
+
+<tscreen><verb>
+static void
+gtk_dial_size_request (GtkWidget *widget,
+ GtkRequisition *requisition)
+{
+ requisition->width = DIAL_DEFAULT_SIZE;
+ requisition->height = DIAL_DEFAULT_SIZE;
+}
+</verb></tscreen>
+
+<p>
+Dopo che tutti i widget hanno restituito una dimensione ideale, viene
+calcolata la disposizione della finestra e ad ogni widget figlio &egrave;
+notificata la propria dimensione attuale <!--ndMichel : che pu&ograve; essere diversa
+da quella restitutita con la funzione sopra -->. Usualmente, questo sar&agrave;
+almeno quanto richiesto, ma occasionalmente pu&ograve; essere pi&ugrave; piccolo.
+La notifica della dimensione viene fatta dalla funzione
+ <tt/gtk_dial_size_allocate()/. Notate che questa funzione &egrave; utilizzata
+anche quando la finestra X del widget &egrave; spostata o modificata come
+dimensione.
+
+<tscreen><verb>
+static void
+gtk_dial_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation)
+{
+ GtkDial *dial;
+
+ g_return_if_fail (widget != NULL);
+ g_return_if_fail (GTK_IS_DIAL (widget));
+ g_return_if_fail (allocation != NULL);
+
+ widget->allocation = *allocation;
+ if (GTK_WIDGET_REALIZED (widget))
+ {
+ dial = GTK_DIAL (widget);
+
+ gdk_window_move_resize (widget->window,
+ allocation->x, allocation->y,
+ allocation->width, allocation->height);
+
+ dial->radius = MAX(allocation->width,allocation->height) * 0.45;
+ dial->pointer_width = dial->radius / 5;
+ }
+}
+</verb></tscreen>.
+
+<sect2> <tt/gtk_dial_expose()/
+
+<p>
+Come menzionato sopra, tutto il lavoro di questo widget viene fatto nella
+gestione dell'evento ``expose''. Non c'&egrave; molto da notare su questo eccetto
+l'uso della funzione <tt/gtk_draw_polygon/ per disegnare il
+puntatore con un'ombreggiatura a tre dimensioni in accordo con il colore
+memorizzato nello stile del wiget.
+
+<tscreen><verb>
+static gint
+gtk_dial_expose (GtkWidget *widget,
+ GdkEventExpose *event)
+{
+ GtkDial *dial;
+ GdkPoint points[3];
+ gdouble s,c;
+ gdouble theta;
+ gint xc, yc;
+ gint tick_length;
+ gint i;
+
+ g_return_val_if_fail (widget != NULL, FALSE);
+ g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ if (event->count > 0)
+ return FALSE;
+
+ dial = GTK_DIAL (widget);
+
+ gdk_window_clear_area (widget->window,
+ 0, 0,
+ widget->allocation.width,
+ widget->allocation.height);
+
+ xc = widget->allocation.width/2;
+ yc = widget->allocation.height/2;
+
+ /* Draw ticks */
+
+ for (i=0; i<25; i++)
+ {
+ theta = (i*M_PI/18. - M_PI/6.);
+ s = sin(theta);
+ c = cos(theta);
+
+ tick_length = (i%6 == 0) ? dial->pointer_width : dial->pointer_width/2;
+
+ gdk_draw_line (widget->window,
+ widget->style->fg_gc[widget->state],
+ xc + c*(dial->radius - tick_length),
+ yc - s*(dial->radius - tick_length),
+ xc + c*dial->radius,
+ yc - s*dial->radius);
+ }
+
+ /* Draw pointer */
+
+ s = sin(dial->angle);
+ c = cos(dial->angle);
+
+
+ points[0].x = xc + s*dial->pointer_width/2;
+ points[0].y = yc + c*dial->pointer_width/2;
+ points[1].x = xc + c*dial->radius;
+ points[1].y = yc - s*dial->radius;
+ points[2].x = xc - s*dial->pointer_width/2;
+ points[2].y = yc - c*dial->pointer_width/2;
+
+ gtk_draw_polygon (widget->style,
+ widget->window,
+ GTK_STATE_NORMAL,
+ GTK_SHADOW_OUT,
+ points, 3,
+ TRUE);
+
+ return FALSE;
+}
+</verb></tscreen>
+
+<sect2> Gestore degli eventi
+
+<p>
+
+Il resto del codice del widget manipola vari tipi di eventi, e non
+&egrave; differente da quello che pu&ograve; essere trovato in molte applicazione
+GTK. Due tipi di eventi possono verificarsi: l'utente pu&ograve;
+clickare sul widget con il mouse e trascinare per muovere il puntatore,
+o il valore dell'oggetto Adjustmente pu&ograve; cambiare a causa di alcune
+circostanze esterne.
+
+<p>
+Quando l'utente clicka sul widget, noi vediamo se la pressione
+era veramente vicina al puntatore, e se cos&igrave;, memorizziamo il bottone
+premuto dall'utente con il campo <tt/button/ della struttura del
+widget, e prendiamo tutti gli eventi del mouse con una chiamata alla
+funzione <tt/gtk_grab_add()/. Successivi movimenti del mouse causano il
+ricalcolo dei valori di controllo (fatto dalla funzione
+<tt/gtk_dial_update_mouse/). Dipendentemente dalla politica che abbiamo
+stabilito, gli eventi ``value_changed'' possono essere generati
+istantaneamente (<tt/GTK_UPDATE_CONTINUOUS/), dopo un certo tempo aggiunto
+con la funzione <tt/gtk_timeout_add()/ (<tt/GTK_UPDATE_DELAYED/), o
+solamente quando il bottone del mouse e' rilasciato
+(<tt/GTK_UPDATE_DISCONTINUOUS/).
+
+<tscreen><verb>
+static gint
+gtk_dial_button_press (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ GtkDial *dial;
+ gint dx, dy;
+ double s, c;
+ double d_parallel;
+ double d_perpendicular;
+
+ g_return_val_if_fail (widget != NULL, FALSE);
+ g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ dial = GTK_DIAL (widget);
+
+ /* Determina se il bottone premuto era dentro la regione del puntatore:
+ lo facciamo calcolando la distanza parallela e
+ perpendicolare dal punto dove il bottone del mouse e' stato premuto
+ alla linea passante per il puntatore. */
+
+ dx = event->x - widget->allocation.width / 2;
+ dy = widget->allocation.height / 2 - event->y;
+
+ s = sin(dial->angle);
+ c = cos(dial->angle);
+
+ d_parallel = s*dy + c*dx;
+ d_perpendicular = fabs(s*dx - c*dy);
+
+ if (!dial->button &&
+ (d_perpendicular < dial->pointer_width/2) &&
+ (d_parallel > - dial->pointer_width))
+ {
+ gtk_grab_add (widget);
+
+ dial->button = event->button;
+
+ gtk_dial_update_mouse (dial, event->x, event->y);
+ }
+
+ return FALSE;
+}
+
+static gint
+gtk_dial_button_release (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ GtkDial *dial;
+
+ g_return_val_if_fail (widget != NULL, FALSE);
+ g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ dial = GTK_DIAL (widget);
+
+ if (dial->button == event->button)
+ {
+ gtk_grab_remove (widget);
+
+ dial->button = 0;
+
+ if (dial->policy == GTK_UPDATE_DELAYED)
+ gtk_timeout_remove (dial->timer);
+
+ if ((dial->policy != GTK_UPDATE_CONTINUOUS) &&
+ (dial->old_value != dial->adjustment->value))
+ gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
+ }
+
+ return FALSE;
+}
+
+static gint
+gtk_dial_motion_notify (GtkWidget *widget,
+ GdkEventMotion *event)
+{
+ GtkDial *dial;
+ GdkModifierType mods;
+ gint x, y, mask;
+
+ g_return_val_if_fail (widget != NULL, FALSE);
+ g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ dial = GTK_DIAL (widget);
+
+ if (dial->button != 0)
+ {
+ x = event->x;
+ y = event->y;
+
+ if (event->is_hint || (event->window != widget->window))
+ gdk_window_get_pointer (widget->window, &amp;x, &amp;y, &amp;mods);
+
+ switch (dial->button)
+ {
+ case 1:
+ mask = GDK_BUTTON1_MASK;
+ break;
+ case 2:
+ mask = GDK_BUTTON2_MASK;
+ break;
+ case 3:
+ mask = GDK_BUTTON3_MASK;
+ break;
+ default:
+ mask = 0;
+ break;
+ }
+
+ if (mods & mask)
+ gtk_dial_update_mouse (dial, x,y);
+ }
+
+ return FALSE;
+}
+
+static gint
+gtk_dial_timer (GtkDial *dial)
+{
+ g_return_val_if_fail (dial != NULL, FALSE);
+ g_return_val_if_fail (GTK_IS_DIAL (dial), FALSE);
+
+ if (dial->policy == GTK_UPDATE_DELAYED)
+ gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
+
+ return FALSE;
+}
+
+static void
+gtk_dial_update_mouse (GtkDial *dial, gint x, gint y)
+{
+ gint xc, yc;
+ gfloat old_value;
+
+ g_return_if_fail (dial != NULL);
+ g_return_if_fail (GTK_IS_DIAL (dial));
+
+ xc = GTK_WIDGET(dial)->allocation.width / 2;
+ yc = GTK_WIDGET(dial)->allocation.height / 2;
+
+ old_value = dial->adjustment->value;
+ dial->angle = atan2(yc-y, x-xc);
+
+ if (dial->angle < -M_PI/2.)
+ dial->angle += 2*M_PI;
+
+ if (dial->angle < -M_PI/6)
+ dial->angle = -M_PI/6;
+
+ if (dial->angle > 7.*M_PI/6.)
+ dial->angle = 7.*M_PI/6.;
+
+ dial->adjustment->value = dial->adjustment->lower + (7.*M_PI/6 - dial->angle) *
+ (dial->adjustment->upper - dial->adjustment->lower) / (4.*M_PI/3.);
+
+ if (dial->adjustment->value != old_value)
+ {
+ if (dial->policy == GTK_UPDATE_CONTINUOUS)
+ {
+ gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
+ }
+ else
+ {
+ gtk_widget_draw (GTK_WIDGET(dial), NULL);
+
+ if (dial->policy == GTK_UPDATE_DELAYED)
+ {
+ if (dial->timer)
+ gtk_timeout_remove (dial->timer);
+
+ dial->timer = gtk_timeout_add (SCROLL_DELAY_LENGTH,
+ (GtkFunction) gtk_dial_timer,
+ (gpointer) dial);
+ }
+ }
+ }
+}
+</verb></tscreen>
+
+<p>
+Cambiamenti esterni all'Adjustment sono comunicati al nostro widget
+dai segnali ``changed'' e ``value_changed''. Il gestore per
+queste funzioni chiama <tt/gtk_dial_update()/ per validare gli
+argomenti, calcolare il nuovo angolo del puntatore e ridisegnare il
+widget (chiamando <tt/gtk_widget_draw()/).
+
+<tscreen><verb>
+static void
+gtk_dial_update (GtkDial *dial)
+{
+ gfloat new_value;
+
+ g_return_if_fail (dial != NULL);
+ g_return_if_fail (GTK_IS_DIAL (dial));
+
+ new_value = dial->adjustment->value;
+
+ if (new_value < dial->adjustment->lower)
+ new_value = dial->adjustment->lower;
+
+ if (new_value > dial->adjustment->upper)
+ new_value = dial->adjustment->upper;
+
+ if (new_value != dial->adjustment->value)
+ {
+ dial->adjustment->value = new_value;
+ gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
+ }
+
+ dial->angle = 7.*M_PI/6. - (new_value - dial->adjustment->lower) * 4.*M_PI/3. /
+ (dial->adjustment->upper - dial->adjustment->lower);
+
+ gtk_widget_draw (GTK_WIDGET(dial), NULL);
+}
+
+static void
+gtk_dial_adjustment_changed (GtkAdjustment *adjustment,
+ gpointer data)
+{
+ GtkDial *dial;
+
+ g_return_if_fail (adjustment != NULL);
+ g_return_if_fail (data != NULL);
+
+ dial = GTK_DIAL (data);
+
+ if ((dial->old_value != adjustment->value) ||
+ (dial->old_lower != adjustment->lower) ||
+ (dial->old_upper != adjustment->upper))
+ {
+ gtk_dial_update (dial);
+
+ dial->old_value = adjustment->value;
+ dial->old_lower = adjustment->lower;
+ dial->old_upper = adjustment->upper;
+ }
+}
+
+static void
+gtk_dial_adjustment_value_changed (GtkAdjustment *adjustment,
+ gpointer data)
+{
+ GtkDial *dial;
+
+ g_return_if_fail (adjustment != NULL);
+ g_return_if_fail (data != NULL);
+
+ dial = GTK_DIAL (data);
+
+ if (dial->old_value != adjustment->value)
+ {
+ gtk_dial_update (dial);
+
+ dial->old_value = adjustment->value;
+ }
+}
+</verb></tscreen>
+
+<sect2> Possibili Miglioramenti
+
+<p>
+
+Il widget Dial, da come l'abbiamo costruito, &egrave; lungo circa 670 linee
+di codice C. Anche se questo potrebbe sembrare un po' troppo, abbiamo
+realmente fatto un bel po' con quel tanto di codice, specialmente
+considerando che molta della lunghezza &egrave; costituita da file header e
+commmenti. Comunque ci sono alcuni miglioramenti che potrebbero essere
+fatti a questo widget:
+
+<itemize>
+<item> Se tu provate questo widget, troverete che ci sono alcuni lampeggiamenti
+quando il puntatore viene trascinato in giro. Questo
+perch&egrave; l'intero widget &egrave; cancellato ogni volta che il
+puntatore viene mosso, prima di essere ridisegnato. Spesso, il modo migliore
+per gestire questo tipo di problema &egrave; il disegnare il tutto su una
+pixmap non visibile, poi copiare il risultato finale sullo schermo
+in una passata sola (il widget ProgressBar viene disegnato in questo
+modo).
+
+<item> L'utente potrebbe essere abilitato ad usare le frecce su e giu per
+incrementare e diminuire il valore.
+
+<item> Potrebbe essere carino se il widget avesse i bottoni per
+incrementare e decrementare il valore di step. Anche se potrebbe essere
+possibile usare dei widget Bottone incorporati per questo, possiamo anche
+far s&igrave; che il bottone sia auto-ripentente quando premuto, come le frecce
+in una barra di scorrimento. Molto del codice per implementare questo tipo di
+comportamento pu&ograve; essere trovato nel widget GtkRange.
+
+<item> il widget Dial potrebbe essere fatto/creato dentro un widget
+contenitore con un singolo widget figlio posizionato all'inizio tra i
+2 bottoni menzionati prima. L'utente potrebbe poi aggiungere o una etichetta
+o un widget ``entry'' per mostrare il valore corrente del dial.
+
+</itemize>
+
+<sect1> Impararne di pi&ugrave;
+
+<p>
+Fin qui abbiamo esposto solo una piccola parte di tutto quello che serve
+per creare un widget. Se volete davvero scrivere un vostro widget, la
+miglior risorsa di esempi &egrave; lo stesso codice sorgente GTK. Chiedete a voi
+stessi alcune cose su come deve essere il widget che volete scrivere: &egrave;
+un widget contenitore? dovr&agrave; avere una propria finestra? &egrave; una modifica di
+un widget precedente? Trovate poi un widget simile e iniziate a fargli
+delle modifiche.
+Buone Fortuna.
+
+
+<sect>Scribble, Un semplice esempio di Programma di Disegno
+
+<sect1> Panoramica
+
+<p>
+In questa sezione, creeremo un semplice programma di disegno. Durante
+questo processo, esamineremo come gestire gli eventi generati dal mouse,
+come disegnare all'interno di una finestra e come disegnare in modo migliore
+usando una pixmap di supporto. Dopo averlo creato, lo amplieremo aggiungendo
+il supporto per i dispositivi XInput, per esempio le tavolette grafiche.
+Il GTK fornisce delle routine di supporto grazie alle quali risulta piuttosto
+semplice ottenere informazioni estese, come la pressione o l'inclinazione.
+
+<sect1> Gestione degli Eventi
+
+<p>
+I segnali di GTK che abbiamo discusso finora si riferivano ad azioni di
+alto livello, ad esempio la selezione di un elemento di un men&ugrave;. Per&ograve;, a volte
+&egrave; utile sapere qualcosa su cose che si svolgono a livello pi&ugrave; basso livello,
+come possono essere il movimento del mouse o la pressione di un tasto.
+Ci sono segnali di GTK anche per questi <em>eventi</em> di basso livello.
+I gestori di questo tipo di segnali hanno un parametro caratteristico in pi&ugrave;,
+che &egrave; il puntatore ad una struttura che contiene informazioni riguardo
+all'evento. Per esempio, ai gestori di eventi che riguardano dei movimenti,
+si passa un puntatore ad una struttura GdkEventMotion, che &egrave; fatta (in parte)
+cos&igrave;:
+
+<tscreen><verb>
+struct _GdkEventMotion
+{
+ GdkEventType type;
+ GdkWindow *window;
+ guint32 time;
+ gdouble x;
+ gdouble y;
+ ...
+ guint state;
+ ...
+};
+</verb></tscreen>
+
+<tt/type/ avr&agrave; il valore del tipo di evento, in questo caso
+<tt/GDK_MOTION_NOTIFY/, <tt/window/ rappresenta la finestra in cui l'evento
+si &egrave; verificato. <tt/x/ e <tt/y/ forniscono le coordinate dell'evento e
+<tt/state/ specifica lo stato dei modificatori nel momento in cui l'evento
+si &egrave; verificato (cio&egrave;, specifica quali tasti modificatori e tasti del mouse
+erano premuti in quel momento). E' un OR bit per bit dei seguenti valori:
+
+<tscreen><verb>
+GDK_SHIFT_MASK
+GDK_LOCK_MASK
+GDK_CONTROL_MASK
+GDK_MOD1_MASK
+GDK_MOD2_MASK
+GDK_MOD3_MASK
+GDK_MOD4_MASK
+GDK_MOD5_MASK
+GDK_BUTTON1_MASK
+GDK_BUTTON2_MASK
+GDK_BUTTON3_MASK
+GDK_BUTTON4_MASK
+GDK_BUTTON5_MASK
+</verb></tscreen>
+
+<p>
+Come succede per gli altri segnali, per determinare cosa deve accadere in
+corrispondenza di un evento, si chiama <tt>gtk_signal_connect()</tt>. Ma
+&egrave; anche necessario far s&igrave; che GTK sappia di quali eventi vogliamo essere
+informati. A questo fine, chiamiamo la funzione:
+
+<tscreen><verb>
+void gtk_widget_set_events (GtkWidget *widget, gint events);
+</verb></tscreen>
+
+Il secondo campo specifica gli eventi che ci interessano. Si tratta dell'OR
+bit per bit delle costanti che identificano i diversi tipi di eventi. La lista
+dei tipi di eventi &egrave; la seguente:
+
+<tscreen><verb>
+GDK_EXPOSURE_MASK
+GDK_POINTER_MOTION_MASK
+GDK_POINTER_MOTION_HINT_MASK
+GDK_BUTTON_MOTION_MASK
+GDK_BUTTON1_MOTION_MASK
+GDK_BUTTON2_MOTION_MASK
+GDK_BUTTON3_MOTION_MASK
+GDK_BUTTON_PRESS_MASK
+GDK_BUTTON_RELEASE_MASK
+GDK_KEY_PRESS_MASK
+GDK_KEY_RELEASE_MASK
+GDK_ENTER_NOTIFY_MASK
+GDK_LEAVE_NOTIFY_MASK
+GDK_FOCUS_CHANGE_MASK
+GDK_STRUCTURE_MASK
+GDK_PROPERTY_CHANGE_MASK
+GDK_PROXIMITY_IN_MASK
+GDK_PROXIMITY_OUT_MASK
+</verb></tscreen>
+
+Per chiamare <tt/gtk_widget_set_events()/, si devono fare alcune osservazioni
+sottili. In primo luogo, la si deve chiamare prima che sia stata creata la
+finestra X per il widget GTK. In pratica, ci&ograve; significa che la si deve
+chiamare subito dopo aver creato il widget. In secondo luogo, il widget
+deve avere una finestra X associata. Molti widget, per ragioni di
+efficienza, non hanno una propria finetra, e vengono mostrati nella
+finestra madre. Questi widget sono:
+
+<tscreen><verb>
+GtkAlignment
+GtkArrow
+GtkBin
+GtkBox
+GtkImage
+GtkItem
+GtkLabel
+GtkPaned
+GtkPixmap
+GtkScrolledWindow
+GtkSeparator
+GtkTable
+GtkViewport
+GtkAspectFrame
+GtkFrame
+GtkVPaned
+GtkHPaned
+GtkVBox
+GtkHBox
+GtkVSeparator
+GtkHSeparator
+</verb></tscreen>
+
+Per catturare degli eventi per questo tipo di widget, si deve fare uso
+del widget EventBox. Si veda a questo proposito la sezione su
+<ref id="sec_The_EventBox_Widget" name="The EventBox Widget">.
+
+<p>
+Per il nostro programma di disegno, vogliamo sapere quando il pulsante del
+mouse &egrave; premuto e quando viene mosso, quindi specificheremo
+<tt/GDK_POINTER_MOTION_MASK/ e <tt/GDK_BUTTON_PRESS_MASK/. Vogliamo anche
+essere informati su quando &egrave; necessario ridisegnare la nostra finestra,
+quindi specifichiamo <tt/GDK_EXPOSURE_MASK/. Anche se vogliamo essere
+avvertiti con un evento ``Configure'' se la dimensione della nostra finestra
+cambia, non &egrave; necessario specificare il flag <tt/GDK_STRUCTURE_MASK/, dal
+momento che questo viene specificato automaticamente per tutte le finestre.
+
+<p>
+Risulta, conunque, che specificando semplicemente <tt/GDK_POINTER_MOTION_MASK/
+si crea un problema. Ci&ograve; infatti fa s&igrave; che il server aggiunga nella coda un
+un nuovo evento di movimento ogni volta che l'utente muovoe il mouse. Immaginate
+che ci vogliano 0.1 secondi per gestire uno di questi eventi, e che il server
+X metta in coda un nuovo evento ogni 0.05 secondi. Rimarremo ben presto indietro
+rispetto al disegno dell'utente. Se l'utente disegna per 5 secondi, ci metteremmo
+altri 5 secondi prima di finire dopo che l'utente ha rilasciato il pulsante del
+mouse! Vorremmo quindi che venga notificato un solo evento di movimento per
+ogni evento che processiamo. Il modo per farlo &egrave; di specificare
+<tt/GDK_POINTER_MOTION_HINT_MASK/.
+
+<p>
+Quando specifichiamo <tt/GDK_POINTER_MOTION_HINT_MASK/, il server ci notifica
+un evento di movimento la prima volta che il puntatore si muove dopo essere
+entrato nella nostra finestra, oppure dopo ogni rilascio di un pulsante del
+mouse. Gli altri eventi di movimento verranno soppressi finch&eacute; non richiediamo
+esplicitamente la posizione del puntatore con la funzione:
+
+<tscreen><verb>
+GdkWindow* gdk_window_get_pointer (GdkWindow *window,
+ gint *x,
+ gint *y,
+ GdkModifierType *mask);
+</verb></tscreen>
+
+(c'&egrave; anche un'altra funzione, <tt>gtk_widget_get_pointer()</tt>, che ha
+un'interfaccia pi&ugrave; semplice, ma che non risulta molto utile dal momento
+che restituisce solo la posizione del puntatore, senza dettagli sullo
+sato dei pulsanti.)
+
+<p>
+Quindi, il codice per assegnare gli eventi per la nostra finestra, avr&agrave; l'aspetto:
+
+<tscreen><verb>
+ 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);
+ 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);
+</verb></tscreen>
+
+Teniamo per dopo i gestori di ``expose_event'' e ``configure_event''. Quelli di
+``motion_notify_event'' e ``button_press_event'' sono piuttosto semplici:
+
+<tscreen><verb>
+static gint
+button_press_event (GtkWidget *widget, GdkEventButton *event)
+{
+ if (event->button == 1 &amp;&amp; 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, &amp;x, &amp;y, &amp;state);
+ else
+ {
+ x = event->x;
+ y = event->y;
+ state = event->state;
+ }
+
+ if (state &amp; GDK_BUTTON1_MASK &amp;&amp; pixmap != NULL)
+ draw_brush (widget, x, y);
+
+ return TRUE;
+}
+</verb></tscreen>
+
+
+<sect1> Il widget Area di Disegno (DrawingArea) e il procedimento per Disegnare
+
+<p>
+Vediamo ora il procedimento per disegnare sullo schermo. Il
+widget da usare &egrave; l'Area di Disegno (DrawingArea). Essenzialmente si
+tratta di una finestra X e nient'altro. E' una tela bianca su cui possimo
+disegnare tutto quello che vogliamo. Per crearne una usiamo la chiamata:
+
+<tscreen><verb>
+GtkWidget* gtk_drawing_area_new (void);
+</verb></tscreen>
+
+Per specificare una dimensione predefinita, si puo fare:
+
+<tscreen><verb>
+void gtk_drawing_area_size (GtkDrawingArea *darea,
+ gint width,
+ gint height);
+</verb></tscreen>
+
+Come &egrave; vero per tutti i widget, si pu&ograve; modificare questa dimensione
+predefinita, tramite la chamata a <tt>gtk_widget_set_usize()</tt>, e
+questa a sua volta pu&ograve; essere modificata dall'utente ridimensionando
+manualmente la finestra che contiene l'area di disegno.
+
+<p>
+Si deve notare che nel momento in cui creiamo un widget DrawingArea, siamo
+<em>completamente</em> responsabili di disegnarne il contenuto. Se ad
+esempio la nostra finestra viene prima nascosta e poi dinuovo portata in
+primo piano, otteniamo un evento di ``esposizione'' e doppiamo ridisegnare
+ci&ograve; che era stato precedente nascosto.
+
+<p>
+Dover ricordare tutto quello che era disegnato sulla finestra in modo da
+poterlo ridisegnare successivamente, pu&ograve; essere, come minimo, noioso.
+In pi&ugrave;, pu&ograve; essere spiacevole dal punto di vista visivo, se delle porzioni
+dello schermo vengono prima cancellate e poi ridisegnate passo per passo.
+La soluzione per questo problema &egrave; di usare una <em>pixmap di supporto</em>.
+Invece di disegnare direttamente sullo schermo, disegnamo su un'iimagine
+conservata nella memoria del server ma che non viene mostrata; quindi, quando
+l'immagine cambia o ne vengono mostrate nuove porzioni, copiamo sullo schermo
+le parti corrispondenti.
+
+<p>
+Per creare una ppixmap fuori dallo schermo, usiamo la funzione:
+
+<tscreen><verb>
+GdkPixmap* gdk_pixmap_new (GdkWindow *window,
+ gint width,
+ gint height,
+ gint depth);
+</verb></tscreen>
+
+Il parametro <tt>window</tt>specifica una finestra GDK dalla quale questa
+pixmap prende alcune delle sue propriet&agrave;. <tt>width</tt> e <tt>height</tt>
+specificano le dimensioni della pixmap. <tt>depth</tt> specifica la
+<em>profondit&agrave; di colore</em>, cio&egrave; il numero di bit per ogni pixel, per
+la nuova pixmap. Se alla profondit&agrave; &egrave; assegnato il valore <tt>-1</tt>, questa
+verr&agrave; posta identica a quella di <tt>window</tt>.
+
+<p>
+Creiamo la pixmap all'interno del gestore di ``configure_event''. Questo evento
+&egrave; generato ogni volta che la finestra cambia di dimensione, compreso il
+momento in cui viene creata per la prima volta.
+
+<tscreen><verb>
+/* Pixmap di supporto per l'area di disegno */
+static GdkPixmap *pixmap = NULL;
+
+/* Creare una pixmap della dimensione appropriata */
+static gint
+configure_event (GtkWidget *widget, GdkEventConfigure *event)
+{
+ if (pixmap)
+ {
+ gdk_pixmap_destroy(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;
+}
+</verb></tscreen>
+
+La chiamata a <tt>gdk_draw_rectangle()</tt> inizialmente rende bianca l'intera
+pixmap. Fra un momento ne riparleremo.
+
+<p>
+Il gestore dell'evento ``esposizione'', copia quindi la porzione appropriata
+della pixmap sullo schermo (determiniamo qual &egrave; l'area da ridisegnare usando
+il campo event->area dell'evento di esposizione):
+
+<tscreen><verb>
+/* Ridisegna sullo schermo a partire dalla pixmap di supporto */
+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;
+}
+</verb></tscreen>
+
+Abbiamo quindi visto come tenete aggiornato lo schermo con la nostra
+pixmap, ma come facciamo per disegnare delle cose interessanti sulla
+pixmap? Ci sono un bel po' di funzioni nella libreria GDK di GTK che
+servono per disegnare su superfici <em>disegnabili</em>. Una superficie
+disegnabile &egrave; semplicemente qualcosa su cui si pu&ograve; disegnare un'immagine.
+Pu&ograve; essere una finestra, una pixmap o una bitmap (un'immagine in bianco e
+nero). Abbiamo gi&agrave; visto sopra due di chiamate,
+<tt>gdk_draw_rectangle()</tt> and <tt>gdk_draw_pixmap()</tt>. La lista
+completa &egrave; la seguente:
+
+<tscreen><verb>
+gdk_draw_line ()
+gdk_draw_rectangle ()
+gdk_draw_arc ()
+gdk_draw_polygon ()
+gdk_draw_string ()
+gdk_draw_text ()
+gdk_draw_pixmap ()
+gdk_draw_bitmap ()
+gdk_draw_image ()
+gdk_draw_points ()
+gdk_draw_segments ()
+</verb></tscreen>
+
+Per ulteriori dettagli su queste funzioni, vedete la documentazione di
+riferimento nei file header <tt>&lt;gdk/gdk.h&gt;</tt>.
+Tutte queste funzioni hanno i medesimi primi due argomenti. Il primo
+&egrave; la superficie disegnabili su cui disegnare, il secondo &egrave; un
+<em>contesto grafico</em> (GC).
+
+<p>
+Un contesto grafico incapsula delle informazioni riguardo a cose come
+il colore di sfondo e di primo piano e lo spessore della linea.
+GDK ha un ampio insieme di funzioni per crare e modificare contesti grafici,
+ma per tenere le cose semplici useremo solo dei contesti grafici predefiniti.
+Ogni widget ha uno stile associato (che pu&ograve; essere modificato agendo su un
+file gtkrc). Questo, fra le altre cose, contiene un certo numero di contesti
+grafici. Alcuni esempi di come accedere a questi contesti grafici sono
+i seguenti:
+
+<tscreen><verb>
+widget->style->white_gc
+widget->style->black_gc
+widget->style->fg_gc[GTK_STATE_NORMAL]
+widget->style->bg_gc[GTK_WIDGET_STATE(widget)]
+</verb></tscreen>
+
+I campi <tt>fg_gc</tt>, <tt>bg_gc</tt>, <tt>dark_gc</tt>, e
+<tt>light_gc</tt> sono indicizzati tramite un parametri di tipo
+<tt>GtkStateType</tt>, che pu&ograve; assumere i valori:
+
+<tscreen><verb>
+GTK_STATE_NORMAL,
+GTK_STATE_ACTIVE,
+GTK_STATE_PRELIGHT,
+GTK_STATE_SELECTED,
+GTK_STATE_INSENSITIVE
+</verb></tscreen>
+
+Per esempio, per <tt/GTK_STATE_SELECTED/ il colore di sfondo predefinito
+&egrave; blu scuro e quello di primo piano bianco.
+
+<p>
+La nostra funzione <tt>draw_brush()</tt>, che efettivamente disegna sullo
+schermo, diventa quindi:
+
+<tscreen><verb>
+/* Disegna un rettangolo sullo schermo */
+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, &amp;update_rect);
+}
+</verb></tscreen>
+
+Dopo aver disegnato il rettangolo sulla pixmap, chiamiamo la funzione:
+
+<tscreen><verb>
+void gtk_widget_draw (GtkWidget *widget,
+ GdkRectangle *area);
+</verb></tscreen>
+
+che notifica a X che l'area data dal parametro <tt>area</tt> deve essere
+aggiornata. X poi generer&agrave; un evento di esposizione (che pu&ograve; essere combinato
+con le aree passate da diverse chiamate a <tt>gtk_widget_draw()</tt>) che
+far&agrave; s&igrave; che il nostro gestore dell'evento di esposizione, copi le porzioni
+rilevanti sullo schermo.
+
+<p>
+Abbiamo a questo punto creato tutto il programma di disegno, tranne che
+per qualche dettaglio irrilevante come la creazione della finestra principale.
+Il codice sorgente completo &egrave; reperibile dove avete ottenuto questo tutorial.
+
+<sect1> Aggiungere il supporto per XInput
+
+<p>
+Al giorno d'oggi &egrave; possibile acquistare dei dispositivi abbastanza a buon
+mercato, come tavolette grafice, che permettono di disegnare con una
+espressivit&agrave; artistica molto semplificata rispetto ad un mouse.
+Il modo pi&ugrave; semplice per usare questi dispositivi &egrave; di sostituirli
+semplicemente al mouse, ma in questo modo si perdono molti dei loro
+vantaggi, come:
+
+<itemize>
+<item> Sensibilit&agrave; alla pressione
+<item> Sensibilit&agrave; all'inclinazione
+<item> Posizionamento infra-pixel
+<item> Ingressi multipli (per esempio, uno stilo che contiene sia una ``matita''
+sia una ``gomma'')
+</itemize>
+
+Per ulteriori informazioni sulle estensioni XInput, vedere l'<url
+url="http://www.msc.cornell.edu/~otaylor/xinput/XInput-HOWTO.html"
+name="XInput-HOWTO">.
+
+<p>
+Se esaminiamo, per esempio, la definizione completa della struttura
+GdkEventMotion, possiamo vedere che contiene dei campi per il supporto
+delle informazioni estese dai dispositivi.
+
+<tscreen><verb>
+struct _GdkEventMotion
+{
+ GdkEventType type;
+ GdkWindow *window;
+ guint32 time;
+ gdouble x;
+ gdouble y;
+ gdouble pressure;
+ gdouble xtilt;
+ gdouble ytilt;
+ guint state;
+ gint16 is_hint;
+ GdkInputSource source;
+ guint32 deviceid;
+};
+</verb></tscreen>
+
+<tt/pressure/ fornisce la pressione sotto forma di un numero decimale
+compreso fra 0 e 1. <tt/xtilt/ e <tt/ytilt/ possono assumere valori
+compresi fra -1 e 1, corrispondenti al grado di inclinazione in ciascuna
+direzione. <tt/source/ e <tt/deviceid/ specificano il dispositivo per il
+quale si &egrave; verificato l'evento in due modi distinti. <tt/source/ da alcune
+semplici informazioni sul tipo di dispositivo, e pu&ograve; assumere i valori:
+
+<tscreen><verb>
+GDK_SOURCE_MOUSE
+GDK_SOURCE_PEN
+GDK_SOURCE_ERASER
+GDK_SOURCE_CURSOR
+</verb></tscreen>
+
+<tt/deviceid/ specifica invece un identificativo numerico univoco per il
+dispositivo. Questo pu&ograve; essere a sua volta utilizzato per avere ulteriori
+informazioni sul dispositivo tramite la chiamata a <tt/gdk_input_list_devices()/
+(vedi sotto). Il valore speciale <tt/GDK_CORE_POINTER/ viene usato per identificare
+il dispositivo di puntamento principale (di solito il mouse).
+
+<sect2> Abilitare le informazioni estese
+
+<p>
+Per far s&igrave; che GTK sappia che ci interessano le informazioni estese dai
+dispositivi, basta aggiungere un'unica linea al nostro programma:
+
+<tscreen><verb>
+gtk_widget_set_extension_events (drawing_area, GDK_EXTENSION_EVENTS_CURSOR);
+</verb></tscreen>
+
+Dando il valore <tt/GDK_EXTENSION_EVENTS_CURSOR/, diciamo che ci interessano
+gli eventi relativi alle estensioni, ma solo se non dobbiamo disegnare da noi
+il nostro cursore. Si veda pi&ugrave; sotto alla sezione <ref
+id="sec_Further_Sophistications" name="Ulteriori Sofisticazioni"> per ulteriori
+informazioni sul modo si disegnare i cursori. Potremmo anche dare i valori
+<tt/GDK_EXTENSION_EVENTS_ALL/ se vogliamo disegnare il nostro cursore o
+<tt/GDK_EXTENSION_EVENTS_NONE/ se vogliamo tornare alle condizioni predefinite.
+
+<p>
+Comunque, non finisce tutto qui. Non ci sono estensioni abilitate per difetto.
+Abbiamo bisogno di un meccanismo per permettere agli utenti l'abilitazione e
+la configurazione delle estensioni dei loro dispositivi, GTK fornisce il
+widget InputDialog per automatizzare questo processo. La seguente procedura
+mostra come gestire un widget InputDialog. Crea la finestra di dialogo nel
+caso non sia presente, mentre la porta in primo piano in caso contrario.
+
+<tscreen><verb>
+void
+input_dialog_destroy (GtkWidget *w, gpointer data)
+{
+ *((GtkWidget **)data) = NULL;
+}
+
+void
+create_input_dialog ()
+{
+ static GtkWidget *inputd = NULL;
+
+ if (!inputd)
+ {
+ inputd = gtk_input_dialog_new();
+
+ gtk_signal_connect (GTK_OBJECT(inputd), "destroy",
+ (GtkSignalFunc)input_dialog_destroy, &amp;inputd);
+ gtk_signal_connect_object (GTK_OBJECT(GTK_INPUT_DIALOG(inputd)->close_button),
+ "clicked",
+ (GtkSignalFunc)gtk_widget_hide,
+ GTK_OBJECT(inputd));
+ gtk_widget_hide ( GTK_INPUT_DIALOG(inputd)->save_button);
+
+ gtk_widget_show (inputd);
+ }
+ else
+ {
+ if (!GTK_WIDGET_MAPPED(inputd))
+ gtk_widget_show(inputd);
+ else
+ gdk_window_raise(inputd->window);
+ }
+}
+</verb></tscreen>
+
+(Notate come gestiamo questo dialogo. Con la connessione del segnale
+``destroy'' ci assicuriamo di non tenerci in giro il puntatore al dialogo
+dopo che lo abbiamo distrutto, cosa che potrebbe portare ad un errore di
+segmentazione.)
+
+<p>
+L'InputDialog ha due pulsanti, ``Close'' e ``Save'', i quali non hanno alcuna
+azione predefinita assegnata ad essi. Nella funzione precedente, abbiamo
+fatto in modo che ``Close'' nasconda la finestra di dialogo, e abbiamo nascosto
+il pulsante ``Save'' dal momento che in questo programma non implementiamo il
+salvataggio delle opzioni di XInput.
+
+<sect2> Usare le informazioni estese
+
+<p>
+Una volta abilitato il dipositivo, possiamo usare le informazioni estese
+che si trovano nei corrispondenti campi delle strutture che descrivono gli
+eventi. A dire il vero, l'utilizzo di questi campi &egrave; sempre sicuro, perch&eacute;
+sono tutti posti per difetto a valori ragionevoli ancje quando la gestione
+degli eventi estesi non &egrave; abilitata.
+
+<p>
+Un cambiamento che dobbiamo fare &egrave; di chiamare <tt/gdk_input_window_get_pointer()/
+invece di <tt/gdk_window_get_pointer/. Ci&ograve; si rende necessario perch&eacute;
+<tt/gdk_window_get_pointer/ non restituisce le informazioni esetese.
+
+<tscreen><verb>
+void gdk_input_window_get_pointer (GdkWindow *window,
+ guint32 deviceid,
+ gdouble *x,
+ gdouble *y,
+ gdouble *pressure,
+ gdouble *xtilt,
+ gdouble *ytilt,
+ GdkModifierType *mask);
+</verb></tscreen>
+
+Quando chiamiamo questa funzione, dobbiamo specificare l'identificativo
+del dispositivo e la finestra. Normalmente questo identificativo lo si
+ottiene dal campo <tt/deviceid/ della struttura dell'evento.
+Questa funzione restituir&agrave; valori ragionevoli nel caso che la gestione
+degli eventi estesi non sia attivata (in questo caso, <tt/event->deviceid/
+avr&agrave; il valore <tt/GDK_CORE_POINTER/).
+
+Quindi, la struttura di base dei gestori degli eventi relativi alla
+pressione di bottoni e ai movomenti non cambia molto - abbiamo solo
+bisogno di aggiungere il codice necessario per tenere conto delle
+informazioni estese.
+
+<tscreen><verb>
+static gint
+button_press_event (GtkWidget *widget, GdkEventButton *event)
+{
+ print_button_press (event->deviceid);
+
+ if (event->button == 1 &amp;&amp; pixmap != NULL)
+ draw_brush (widget, event->source, event->x, event->y, event->pressure);
+
+ return TRUE;
+}
+
+static gint
+motion_notify_event (GtkWidget *widget, GdkEventMotion *event)
+{
+ gdouble x, y;
+ gdouble pressure;
+ GdkModifierType state;
+
+ if (event->is_hint)
+ gdk_input_window_get_pointer (event->window, event->deviceid,
+ &amp;x, &amp;y, &amp;pressure, NULL, NULL, &amp;state);
+ else
+ {
+ x = event->x;
+ y = event->y;
+ pressure = event->pressure;
+ state = event->state;
+ }
+
+ if (state &amp; GDK_BUTTON1_MASK &amp;&amp; pixmap != NULL)
+ draw_brush (widget, event->source, x, y, pressure);
+
+ return TRUE;
+}
+</verb></tscreen>
+
+Avremo anche bisogno di fare qualcosa con queste nuove informazioni. La
+nostra nuova funzione <tt/draw_brush/ disegna con un colore diverso per
+ogni <tt/event->source/ e cambia la dimensione della linea in funzione
+della pressione.
+
+<tscreen><verb>
+/* Disegna un rettangolo sullo schermo, con la dimensione dipendente
+ dalla pressione e il colore dipendente dal tipo di dispositivo */
+static void
+draw_brush (GtkWidget *widget, GdkInputSource source,
+ gdouble x, gdouble y, gdouble pressure)
+{
+ GdkGC *gc;
+ GdkRectangle update_rect;
+
+ switch (source)
+ {
+ case GDK_SOURCE_MOUSE:
+ gc = widget->style->dark_gc[GTK_WIDGET_STATE (widget)];
+ break;
+ case GDK_SOURCE_PEN:
+ gc = widget->style->black_gc;
+ break;
+ case GDK_SOURCE_ERASER:
+ gc = widget->style->white_gc;
+ break;
+ default:
+ gc = widget->style->light_gc[GTK_WIDGET_STATE (widget)];
+ }
+
+ update_rect.x = x - 10 * pressure;
+ update_rect.y = y - 10 * pressure;
+ update_rect.width = 20 * pressure;
+ update_rect.height = 20 * pressure;
+ gdk_draw_rectangle (pixmap, gc, TRUE,
+ update_rect.x, update_rect.y,
+ update_rect.width, update_rect.height);
+ gtk_widget_draw (widget, &amp;update_rect);
+}
+</verb></tscreen>
+
+<sect2> Trovare ulteriori informazioni su di un dispositivo
+
+<p>
+Come esempio del modo di trovare altre informazioni su di un dispositivo,
+il nostro programma stamper&agrave; il nome di ogni dispositivo che genera un
+evento di pressione di un pulsante. Per avere il nome di un dispositivo,
+chiamiamo la funzione
+
+<tscreen><verb>
+GList *gdk_input_list_devices (void);
+</verb></tscreen>
+
+che restituisce una GList (un tipo di lista collegata che si trova nella
+libreria glib) di strutture di tipo GdkDeviceInfo. La definizione di
+GdkDeviceInfo &egrave; la seguente:
+
+<tscreen><verb>
+struct _GdkDeviceInfo
+{
+ guint32 deviceid;
+ gchar *name;
+ GdkInputSource source;
+ GdkInputMode mode;
+ gint has_cursor;
+ gint num_axes;
+ GdkAxisUse *axes;
+ gint num_keys;
+ GdkDeviceKey *keys;
+};
+</verb></tscreen>
+
+La maggior parte di questi campi rappresentano informazioni di configurazione
+che potete ignorare a meno che non implementiate il salvataggio della
+configurazione di un XInput. Quelle che ci interessano sono <tt/name/, che
+&egrave; semplicemente il nome che X assegna al dispositivo, e <tt/has_cursor/. Anche
+<tt/has_cursor/ non &egrave; informazione di configurazione, e indica, nel caso
+abbia valore ``falso'', che dobbiamo disegnare da soli il nostro cursore. Ma
+dal momento che abbiamo specificato <tt/GDK_EXTENSION_EVENTS_CURSOR/,
+possiamo anche non preoccuparcene.
+
+<p>
+
+La nostra funzione <tt/print_button_press()/ scorre semplicemente la lista
+che &egrave; stata restituita finch&eacute; non trova il valore corretto, e poi stampa
+il nome del dispositivo.
+
+<tscreen><verb>
+static void
+print_button_press (guint32 deviceid)
+{
+ GList *tmp_list;
+
+ /* gdk_input_list_devices restituisce una lista interna, cos&igrave; poi
+ non dobbiamo liberarla */
+ tmp_list = gdk_input_list_devices();
+
+ while (tmp_list)
+ {
+ GdkDeviceInfo *info = (GdkDeviceInfo *)tmp_list->data;
+
+ if (info->deviceid == deviceid)
+ {
+ printf("Button press on device '%s'\n", info->name);
+ return;
+ }
+
+ tmp_list = tmp_list->next;
+ }
+}
+</verb></tscreen>
+Questo completa i cambiamenti necessari per usare gli XInput nel nostro
+programma. Come per la prima versione, i sorgenti completi sono prelevabili
+da dove avete prelevato questo tutorial.
+
+<sect2> Ulteriori sofisticazioni <label id="sec_Further_Sophistications">
+
+<p>
+Anche se ora il nostro programma supporta XInput pittosto bene, gli mancano
+alcune caratteristiche che probabilmente vorremmo mettere in una applicazione
+completa. In primo luogo, probabilmente all'utente non far&agrave; piacere dover
+configurare i propri dispositivi ogni volta che lanciano il programma, per
+cui dovremmo dare la possibilit&agrave; di salvare la configurazione dei dispositivi.
+Ci&ograve; pu&ograve; essere fatto scorrendo la lista restituita da <tt/gdk_input_list_devices()/
+e scrivendo la configurazione su di un file.
+
+<p>
+Per tornare allo stato salvato la prossima volta che il programma viene
+eseguito, GDK mette a disposizione delle funzioni per cambiare la configurazione
+dei dispositivi:
+
+<tscreen><verb>
+gdk_input_set_extension_events()
+gdk_input_set_source()
+gdk_input_set_mode()
+gdk_input_set_axes()
+gdk_input_set_key()
+</verb></tscreen>
+
+(La lista restituita da <tt/gdk_input_list_devices()/ non dovrebbe
+essere modificata direttamente.) Un esempio di come fare pu&ograve; essere
+trovato nel programma di disegno gsumi (disponibile da <htmlurl
+url="http://www.msc.cornell.edu/~otaylor/gsumi/"
+name="http://www.msc.cornell.edu/~otaylor/gsumi/">). Sarebbe bello
+avere alla fine un modo standard di recuperare le informazioni per tutte
+le applicazioni. Questo probabilmente appartiene ad un livello un po'
+pi&ugrave; elevato ripetto a GTK, forse alla libreria GNOME.
+
+<p>
+Un'altra notevole omissione a cui abbiamo accennato precedentemente &egrave; il
+fatto di non disegnare il cursore direttamente. Piattaforme diverse da
+XFree86 non permettono in questo momento di usare contemporaneamente un
+dispositivo sia come puntatore principale sia direttamente da una
+applicazione. Vedere <url url="http://www.msc.cornell.edu/~otaylor/xinput/XInput-HOWTO.html"
+name="XInput-HOWTO"> per ulteriori informazioni. Ci&ograve; significa che le
+applicazioni che vogliono rivolgersi al pubblico pi&ugrave; ampio dovranno prevedere
+di disegnare esse stesse il proprio cursore.
+
+<p>
+Un'applicazione che voglia disegnare il proprio cursore dovr&agrave; fare due cose:
+determinare se il dispositivo corrente necessita che venga disegnato un
+cursore, e determinare se il dispositivo corrente &egrave; in prossimit&agrave;. (Se il
+dispositivo &egrave; una tavoletta grafica, un tocco di finezza &egrave; fare sparire
+il puntatore quando lo stilo viene sollevato dalla tavoletta. Quando c'&egrave;
+contatto fra lo stilo e la tavoletta, si dice che il dispositivo &egrave; ``in
+prossimit&agrave;".) La prima cosa viene fatta scorrendo la lista dei dispositivi,
+come abbiamo fatto per trovare il nome del dispositivo. La seconda cosa
+viene ottenuta selezionando gli eventi ``proximity_out''. Un esempio di
+disegno del proprio cursore si trova nel programma 'testinput' incluso nella
+distribuzione di GTK.
+
+<sect>Consigli per scrivere Applicazioni GTK
+
+<p>
+
+Questa sezione &egrave; semplicemente una raccolta di saggezza, una
+guida di stile e un aiuto per creare buone applicazioni GTK. E' totalmente
+inutile per ora perch&eacute; &egrave; solamente un appunto.
+
+Usa autoconf e automake! Sono tuoi amici :) Ho intenzione di fare una
+piccola introduzione su di loro qui.
+
+<sect>Contributi
+<p>
+
+Questo documento, come molti altri grandi software fuori di qui, &egrave; stato
+creato da volontari. Se sai tutto quello che c'&egrave; da sapere su GTK e non
+lo hai trovato qui allora considera la possibilit&agrave; di contribuire a questo
+documento.
+
+<p>
+Se decidi di contribuire, per favore trasmettimi il tuo testo a
+<tt><htmlurl url="mailto:slow@intergate.bc.ca"
+name="slow@intergate.bc.ca"></tt>. Inoltre, Si consapevole che l'intero
+documento &egrave; ``free'', e ogni tua aggiunta sar&agrave; considerata allo stesso modo.
+Per questo motivo le persone possono usare porzioni dei tuoi esempi nei loro
+programmi, copie di questo documento possono essere distribuite all'infinito,
+ecc...
+
+<p>
+
+Grazie.
+
+
+<sect>Credits
+<p>
+Voglio qui ringraziare le persone che seguono, per il loro contributo
+alla stesura di questo testo.
+
+<itemize>
+<item>Bawer Dagdeviren, <tt><htmlurl url="mailto:chamele0n@geocities.com"
+name="chamele0n@geocities.com"></tt> per il tutorial sui men&ugrave;.
+
+<item>Raph Levien, <tt><htmlurl url="mailto:raph@acm.org"
+ name="raph@acm.org"></tt>
+per il "hello world" alla GTK, l'immpacchettamento del widget, e in generale
+per tutta la sua saggezza.
+Lui ha anche donato una casa per questo tutorial.
+
+<item>Peter Mattis, <tt><htmlurl url="mailto:petm@xcf.berkeley.edu"
+name="petm@xcf.berkeley.edu"></tt> Per il pi&ugrave; semplice programma GTK e l'abilit&agrave;
+di farlo. :)
+
+<item>Werner Koch <tt><htmlurl url="mailto:werner.koch@guug.de"
+name="werner.koch@guug.de"></tt> per la conversione da testo semplice a SGML
+e la gerarchia delle classi di widget.
+
+<item>Mark Crichton <tt><htmlurl url="mailto:crichton@expert.cc.purdue.edu"
+name="crichton@expert.cc.purdue.edu"></tt> per il codice della "MenuFactory"
+e per la parte sull'impacchettamento nelle tabelle del tutorial.
+
+<item>Owen Taylor <tt><htmlurl url="mailto:owt1@cornell.edu"
+name="mailto:owt1@cornell.edu"></tt> per la sezione del widget EventBox
+(e il patch alla distribuzione). Lui &egrave; anche responsabile per il codice
+e il tutorial delle selezioni, come per la sezione sulla scrittura di un
+proprio widget, e l'applicazione d'esempio. Grazie di tutto Owen.
+
+<item>Mark VanderBoom <tt><htmlurl url="mailto:mvboom42@calvin.edu"
+name="mailto:mailto:mvboom42@calvin.edu"></tt> per il suo meraviglioso lavoro
+sul Notebook, Progres Bar, Dialogs e File selection. Grazie molto Mark. Sei
+stato di grande aiuto.
+
+<item>Tim Janik <tt><htmlurl url="mailto:timj@psynet.net"
+name="mailto:timj@psynet.net"></tt> per il suo grande lavoro sul widget List.
+Grazie Tim :)
+
+<item> Michael K. Johnson <tt><htmlurl url="mailto:johnsonm@redhat.com"
+name="johnsonm@redhat.com"> </tt> per le informazioni e il codice dei menu
+a comparsa.
+
+</itemize>
+<p>
+E a tutti voi che avete fatto commenti e avete aiutato a raffinare questo documento.
+<p>
+
+Thanks.
+
+<sect> Copying
+<p>
+This tutorial is Copyright (c) 1997 Ian Main
+
+La traduzione italiana &egrave; sotto Copyright (c) 1997-1998 di Michel Morelli,
+Daniele Canazza e Antonio Schifano.
+
+<p>
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+<p>
+This program 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 General Public License for more details.
+<p>
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+</article>
+