Re: [gtk-list] boring GtkTutorial stuff..






On Sat, 2 Aug 1997, Ian Main wrote:

> 
> Hi again,
> 
> Yes, I stayed up half the night writing more on the tutorial, and yes, my
> wife hates me :)  (just kidding.. she's actually very nice about it :)

I didn't know that married folks worked on GIMP/GTK...
> 
> I've included the SGML source here again.. hopefuly no one has problems
> doing ye old sgml2html or what have you on it.
> 
> How does one go about getting more people to help out ?
> 
The more tutorials the better... What I would personally like is a
POSTSCRIPT version of all tutorials since I like printing stuff out,
stapling it, and reading it in a relaxed manner over a cup of tea (and
making notes to myself on the margins). HTML is acceptable, too, provided
it's a one-pager. I don't know how difficult it is to convert everything
to .ps but what does everyone else think?
(Perhaps sgml can be printed on the fly, too... How?)

Pavel

PS: I erased by mistake the post with that new gtk-tutorial page. What was
that page, again?


 > Maybe we should have a competition to see who can write the
most > documentation over the course of the weekend ?  Whatya think ?
> 
> If you can write more than me, I'll umm.. umm... umm.. well.. you name it
>:)
> 
> As usual, any comments are appreciated.  Even better would be additions!
>:) hint hint, nudge, nudge...
> 
> 
> Ian
> 
> 
> ------------------------------------------------------------------------
> 
> 
> 
> <!-- This is the tutorial marked up in SGML
>      (just to show how to write a comment)
> -->
> 
> 
> <!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>July 1997
> 
> 
> <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 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 printf, malloc etc.  These are used to increase gtk's
> portability, as some of the functions implemented here are not available or
> nonstandard on other Unicies such as g_strerror().   Some also contain 
> enhancements to the libc versions such as
> g_malloc will test for errors for you.
> <p>
> This tutorial is an attempt to document as much as possible of gtk, t is by 
> no means complete.  This
> tutorial assumes a good understanding of C, and how to create C programs.
> It would be a great benifit 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.
> <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 either get the distro with GIMP, or d/l it from Peter
> Mattis's home dir ftp.xcf.berkely.edu/pub/pmattis (Hope this is ok :) ..
> may change ?).
> <p>
> We'll start with the simplest GTK program possible.  This program will
> create a 200x200 pixel window and has no way of exiting except to be
> killed using the shell or 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>
> 
> All programs will of course include the gtk/gtk.h which declares the
> variables and functions 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 colormap 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
> arguements 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>
> It removes these from the arguement 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 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 keypresses) to occur.
> In our simple example however, no events will be registered.
> 
> 
> <sect1>Hello World in GTK
> <p>
> OK, now for a program with a widget (a button).  It's the classic hello
> world ala gtk.	This would make a good base for a new gtk application.
> 
> <tscreen><verb>
> 
> #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");
> }
> 
> /* another callback */
> void destroy (void)
> {
>     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.  Arguements 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 "destroy" signal (this can be given
> 						      * by the application, or the window manager, the function destroy
> 						      * will be called as defined above.  The NULL passed to this function
> 						      * is used to pass optional data into this "callback" function. */
>     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 arguement.  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>
> 
> 
> To compile use:
> 
> <tscreen><verb>
> gcc -Wall -g -lgtk -lgdk -lglib -lX11 -lXext -lm hello_world.c -o hello_world
> </verb></tscreen>
> 
> 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 box, I have to add
> <tt>-L/usr/X11R6/lib</> for it to find the X11 libraries.
> <p>
> Now the explanation:
> <p>
> As you can see from this example, it is quite easy to build a simple gtk
> application.  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.  By passing in a pointer to the function to be called when the
> appropriate event occurs (by connecting a "signal") we tell the
> application what we want to have done with when a given event occurs.
> <p>
> (You may also add timeout functions and IO checks to take control from
> gtk_main, this is described in the approprate section.)
> <p>
> The general steps to creating a widget in gtk are:
> <enum>
> <item> gtk_*_new - one of various functions to create a new widget.
> 
> <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 gtk_container_add() or
> gtk_box_pack_start() (are there others ?).
> 
> <item> gtk_widget_show()
> </enum>
> 
> 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 that it all pops up at once rather than seeing the individual
> widgets come up on the screen as they're formed.
> 
> 
> <sect>Events
> <p>
> When setting up a signal handler (better term ?) we use a call to
> gtk_signal_connect().  The deceleration of this function is:
> 
> <tscreen><verb>
> gint gtk_signal_connect (GtkObject *object, gchar *name,
> 			 GtkSignalFunc func, gpointer func_data);
> </verb></tscreen>
> 
> There are a few things you'll notice right away from this example.  The
> gint, gchar etc. are typedefs to int and char respectively.  This is done
> for portabilities sake.  A good example is "gint32" which will be
> typedef'd to a 32 bit integer for any platform, whether it be the 64 bit
> alpha (int (or short int?)), or the 32 bit Intel (long int or int).  The
> typedefs are very straight forward and intuitive.  They are all defined in
> glib/glib.h (which gets included from gtk.h).
> <p>
> The fist arguement to gtk_signal_connect is the object which will generate
> the signal, usually a widget (always?).  The second of course is the
> signal name, followed by the function you wish to be called.  This function
> is called a "callback" function.  Then last arguement is a
> pointer to the data you wish to pass to the callback function.
> <p>
> The callback function you specify should be of the form:
> 
> <tscreen><verb>
> void callback_func(GtkWidget *widget, gpointer *callback_data);
> </verb></tscreen>
> 
> Generally, the GtkWidget arguement in the callback is unused.
> <p>
> So, if we wanted to pass the above function some data when a button is
> pressed, we use:
> 
> <tscreen><verb>
> gtk_signal_connect (GTK_OBJECT (button), "clicked",
> 		    GTK_SIGNAL_FUNC (callback_func), (gpointer) callback_data);
> </verb></tscreen>
> 
> This assumes of course that you have already created a button.	The
> GTK_OBJECT, GTK_SIGNAL_FUNC are just macros that perform type casting while
> adding readability.
> <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>
> #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 destroy (void)
> {
>     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.	Arguements 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!");
> 
>     /* It's a good idea to do this for all windows. */
>     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);
> 
>     /* 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 arguement */
>     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 arguement,
>      * 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);
> 
>     /* we show the widgets in the reverse order we created them,
>      * someone want to explain to me why ?? :) or is it even
>      * necessary ? */
>     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 arguements as above.  You'll notice in
> this example 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.  Try playing with resizing the window.
> <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.
> <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.
> <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.
> 
> <tscreen><verb>    <!-- Don't know the tag for images, but there is one -->
> &lt;p align=center> &lt;img src="packbox1.gif" width=528 height=235> &lt;/p>
> </verb></tscreen>
> 
> 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 arguement 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>
> [does the above sound right to you ?]
> <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
> adding (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:
> 
> <tscreen><verb>    <!-- Don't know the tag for images, but there is one -->
> &lt;p align=center> &lt;img src="packbox2.gif" width=509 height=213> &lt;/p>
> </verb></tscreen>
> 
> <p>
> Someone write about table widgets! :)
> 
> <sect>Widget Overview
> <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.
> <p>
> For your reference, here is the class hierarchy tree used to implement widgets.
> 
> <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>
> 
> <sect1>The Button Widget
> <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>
> #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);
> 
> 
>     /* sets the border width of the window. */
>     gtk_container_border_width (GTK_CONTAINER (window), 10);
> 
>     /* 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 can be used to put xpm's and labels in many
> different widgets (I think :-).
> 
> 
> <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 inhereted 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, one with a
> label widget already packed into it.
> <p>
> To retreive 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, use a callback to
> catch this signal that looks something like this:
> 
> <tscreen><verb>
> void toggle_button_callback (GtkWidget *widget, gpointer   data)
> {
>     if (GTK_TOGGLE_BUTTON (widget)->active) 
>     {
>         /* If control reaches here, the toggle button is depressed. */
>     }
> }
> </verb></tscreen>
> 
> The above is in need of testing.. It's just a guess..
> 
> <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 arguement, and a TRUE or FALSE
> for the second state arguement to specify whether it should be up (released) or
> down (depressed).  Default is up, or FALSE.
> 
> <tscreen><verb>
> void       gtk_toggle_button_toggled        (GtkToggleButton *toggle_button);
> </verb></tscreen>
> <p>
> Hmm.. judging from the gtk code, it 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 arguement to these calls.  They require a group to
> perform they're duty properly.  The first call should pass NULL as the first
> arguement.  You then create a group using:
> 
> <tscreen><verb>
> GSList* gtk_radio_button_group (GtkRadioButton *radio_button);
> </verb></tscreen>
> 
> <p>
> You then pass this group as the first arguement to each subsequent call to
> gtk_radio_button_new or new_with_label.  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>
> [Insert an example here of how to use these guys cause I think it could do a
> lot of good at this point.]
> 
> <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 gtk.
> <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 arguement 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);
> 			      void	  gtk_tooltips_set_tips
> 			      (GtkTooltips *tooltips,
> 			      GtkWidget   *widget,
> 			      gchar	  *tips_text);
> </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_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> Scrolled Windows
> <p>
> Please write about me :)
> 
> <sect1> Text Entries
> <p>
> Please write about me :)
> 
> <sect1> Lists
> <p>
> Please write about me :)
> 
> <sect1> Color Selections
> <p>
> Please write about me :)
> 
> <sect1> File Selections
> <p>
> Please write about me :)
> 
> <sect1> Dialogs
> <p>
> Please write about me :)
> 
> <sect1> Range Controls
> <p>
> Please write about me :)
> 
> <sect1> Rulers
> <p>
> Please write about me :)
> 
> <sect1> Text Boxes
> <p>
> Please write about me :)
> 
> <sect1> Notebooks
> <p>
> Please write about me :)
> 
> <sect1> Progress Bars
> <p>
> Please write about me :)
> 
> <sect1> Color Previews
> <p>
> Please write about me :)
> 
> <sect1> Grey Previews
> <p>
> Please write about me :)
> 
> <sect1> Curves
> <p>
> Please write about me :)
> 
> <sect1> Pixmaps
> <p>
> Please write about me :)
> 
> 
> <sect1>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 menu_factory
> (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 I've had problems because of the '/' that is used to
> seperated menus.  Other than this, there is no real reason to use the
> manual method (is this true ??)
> <p>
> In the true tradition of teaching, we'll show you the hard
> way first. <tt>:)</>
> <p>
> Let's look at the functions that are used to create menus.
> This first function is used to create a new menu.
> 
> <tscreen><verb>
> GtkWidget *gtk_menu_bar_new()
> </verb></tscreen>
> 
> This rather self explanatory function creates a new menu bar.  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 just holds 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.
> 
> <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 menus 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 these
> 
> <tscreen><verb>
> gtk_menu_item_append()
> 
> gtk_menu_item_set_submenu()
> </verb></tscreen>
> 
> The gtk_menu_new_with_label and plain 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.
> <p>
> The steps to create a menu are outlined below:
> <itemize>
> <item>	Create a new menu using gtk_menu_new()
> <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 menu bar itself.
> <item>	Use multiple calls to gtk_menu_item_new for each item you wish to have on
>   your menu.  And use gtk_menu_item_append() to put each of these new items on
>   together.  This creates a list of menu items.
> <item>	Use gtk_menu_item_set_submenu() to attach the newly created menu_items to
>   the root menu item (The one created in the second step).
> <item>	Create a new menu bar 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>
> And that should about do it.  Let's take a look at an example to help clarify.
> 
> 
> <tscreen><verb>
> #include <gtk/gtk.h>
> 
> int main (int argc, char *argv[])
> {
> 
>     GtkWidget *window;
>     GtkWidget *menu;
>     GtkWidget *menu_bar;
>     GtkWidget *root_menu;
>     GtkWidget *menu_items;
>     char buf[128];
>     int i;
> 
>     gtk_init (&amp;argc, &amp;argv);
> 
>     /* create a new window */
>     window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
>     gtk_window_set_title(GTK_WINDOW (window), "GTK Menu Test");
>     gtk_signal_connect(GTK_OBJECT (window), "destroy",
> 		       (GtkSignalFunc) gtk_exit, NULL);
> 
>     /* Init the menu-widget, and remember -- never
>      * gtk_show_widget() the menu widget!! */
>     menu = gtk_menu_new();
> 
>     /* This is the root menu, and will be the label will be the menu name 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);
> 
>     /* 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);
> 
> 	    /* Show the widget */
> 	    gtk_widget_show(menu_items);
> 	}
> 
>     /* 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);
> 
>     /* Create a menu-bar to hold the menus and add it to our main window*/
>     menu_bar = gtk_menu_bar_new();
>     gtk_container_add(GTK_CONTAINER(window), menu_bar);
>     gtk_widget_show(menu_bar);
> 
>     /* 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;
> }
> </verb></tscreen>
> 
> You may also set a menu item to be insensitive or, using an accelerator
> table, bind keys to menu functions.
> <p>
> [ maybe insert a few calls or something ]
> <p>
> Now that we've shown you the hard way, here's how you do it using the
> gtk_menu_factory calls.
> <p>
> [ gtk_menu_factory example and explanation ]
> 
> <sect>Timeouts, IO and Idle Functions
> <p>
> <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.
> 
> <tscreen><verb>
> gint gtk_timeout_add (guint32 interval,
> 		      GtkFunction function,
> 		      gpointer data);
> </verb></tscreen>
> 
> The first arguement is the number of milliseconds
> between calls to your function.  The second arguement 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-negative value, ie TRUE.
> 
> The declaration of you 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 arguement 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 arguement 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.
> 
> <tscreen><verb>
> void gdk_input_remove (gint tag);
> </verb></tscreen>
> <p>
> As with the gtk_timeout_add function above, a FALSE return from your
> callback function will also cause gtk to stop monitoring the file
> descriptor. (will have to verify this.)
> 
> 
> <sect1>Idle Functions
> <p>
> What about if you have a function you want called when nothing else is
> happening ?
> 
> <tscreen><verb>
> gint gtk_idle_add (GtkFunction function,
> 		   gpointer data);
> 
> void gtk_idle_remove (gint tag);
> </verb></tscreen>
> <p>
> I won't explain the meaning of the arguements as they follow very much like
> the ones above.  The function pointed to by the first arguement 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>glib Functions
> <p>
> There are several useful function that we recomend you use when creating gdk
> and gtk applications.  I will list them all here with a brief explanation.
> They are all duplicates of standard libc functions so I won't go into
> detail.  This is mostly to be used as a reference, so you know what is
> available for use.
> 
> 
> <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 zero's 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>
> 
> Free's memory.  Easy one.
> 
> <tscreen><verb>
> gchar* g_strdup    (const gchar *str);
> </verb></tscreen>
> 
> Replacement strdup function.  Allocates memory for a new string and returns
> a pointer to it.
> 
> <tscreen><verb>
> gchar* g_strerror  (gint errnum);
> </verb></tscreen>
> 
> I recomend 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>
> 
> And our last function:
> 
> <tscreen><verb>
> gchar* g_strsignal (gint signum);
> </verb></tscreen>
> 
> Prints out the name of the signal given the signal number.
> 
> There are many more such functions in glib/glib.h, but I will not go into
> depth on them.  If anyone wants to add some short descriptions of other
> functions in here, like for the linked list stuff, please do.
> 
> 
> 
> <sect>gtkrc Files
> <p>
> GTK has it's own way of dealing with application defaults, by using rc
> files.  The following functions are used:
> 
> <tscreen><verb>
> 
> void gtk_rc_init (void);
> 
> void gtk_rc_parse (char *filename);
> 
> GtkStyle* gtk_rc_get_style (GtkWidget *widget);
> 
> void gtk_rc_add_widget_name_style (GtkStyle *style,
>                                    char *pattern);
> 				   
> void gtk_rc_add_widget_class_style (GtkStyle *style,
>                                     char *pattern);
> 					 
> </verb></tscreen>
> <p>
> The most common one is gtk_rc_init() which will cause 
> GTK to parse the file given as its arguementand and use the defaults found in it.
> This can be used to set colors, pixmaps for backgrounds etc.
> <p>
> If someone could expand further on this... I'd like to see examples of gtkrc
> files too.  This is something that still mystifies me.
> 
> <sect>Writing Your Own Widgets
> <p>
> It's in the works.
> 
> 
> <sect>Contributing
> 
> <p>
> This document, like so much other great software out there, was created for
> free by volenteers.  If you are at all knowledgable 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 me, Ian Main, 
> <tt><htmlurl url="mailto:slow@intergate.bc.ca"
> name="slow@intergate.bc.ca"></tt>. Also, be aware that the entirity 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, and the widget packing.
> 
> <item>Peter Mattis, <tt><htmlurl url="mailto:petm@xcf.berkeley.edu"
>                           name="petm@xcf.berkeley.edu"></tt>
> for the simplest gtk program.
> 
> <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.
> 
> </itemize>
> <p>
> And to all of you who commented and helped refine this document.
> <p>
> Thanks.
> 
> </article>
> 
> --
> To unsubscribe: mail -s unsubscribe gtk-list-request@redhat.com < /dev/null
> 
> 

------------------------------------------------------ 
/  Pavel Grinfeld, a.k.a.
/  Pavel Greenfield
/
/  http://www.geocities.com/SoHo/5277
/
/  pavel@ml.com
/
/  (212) 449 1318 (w)  
/  (212) 449 5865 (w if you really need to find me) 
/  (212) 797 1688
______________________________________________________



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]