<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-6666239544818473000</id><updated>2012-01-29T18:32:20.648+01:00</updated><category term='GtkComboBox'/><category term='GtkTreeModel'/><category term='Tips'/><category term='threads'/><category term='GtkLabel'/><category term='GtkDialog'/><category term='Glade'/><category term='GtkTreeView'/><category term='GTK+'/><category term='Maintenance'/><category term='glib'/><category term='tutorial'/><title type='text'>Bits and pieces</title><subtitle type='html'>Tips and tutorials related to GTK+ toolkit</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://blog.borovsak.si/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6666239544818473000/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://blog.borovsak.si/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Tadej Borovšak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-V5S0cD-5b_I/AAAAAAAAAAI/AAAAAAAAArU/nAyUYzn6Yx8/s512-c/photo.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>28</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-6666239544818473000.post-3686027556957586438</id><published>2010-10-01T16:01:00.000+02:00</published><updated>2010-10-01T16:01:28.211+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Maintenance'/><title type='text'>Slowly fixing things</title><content type='html'>Hello all.&lt;br /&gt;
&lt;br /&gt;
I've been neglecting my blog for quite a while now. But hopefully I'll be able to write some more again now that things have settled down a bit. For starters, I'll clean my existing posts (fix code, update documentation references, etc.).&lt;br /&gt;
&lt;br /&gt;
And that's basically it. Hope to see you around.&lt;br /&gt;
&lt;br /&gt;
Stay healthy.&lt;br /&gt;
&lt;br /&gt;
Tadej&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6666239544818473000-3686027556957586438?l=blog.borovsak.si' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.borovsak.si/feeds/3686027556957586438/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.borovsak.si/2010/10/slowly-fixing-things.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6666239544818473000/posts/default/3686027556957586438'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6666239544818473000/posts/default/3686027556957586438'/><link rel='alternate' type='text/html' href='http://blog.borovsak.si/2010/10/slowly-fixing-things.html' title='Slowly fixing things'/><author><name>Tadej Borovšak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-V5S0cD-5b_I/AAAAAAAAAAI/AAAAAAAAArU/nAyUYzn6Yx8/s512-c/photo.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6666239544818473000.post-1670429101993157491</id><published>2010-02-10T19:11:00.009+01:00</published><updated>2010-10-01T15:48:43.467+02:00</updated><title type='text'>My favorite waste of time ...</title><content type='html'>Hello.
&lt;br /&gt;&lt;br /&gt;
You've all probably noticed that my posting frequency decreased dramatically lately. Today I'm happy to present main culprit for this: small bundle of happiness named Lovro.
&lt;br /&gt;
&lt;a href="http://img801.imageshack.us/img801/9840/img0001kq.jpg" rel="shadowbox[lovro]"&gt;&lt;img src="http://img137.imageshack.us/img137/4979/img0001sh.jpg" /&gt;&lt;/a&gt;
&lt;a href="http://img812.imageshack.us/img812/2927/img0002zk.jpg" rel="shadowbox[lovro]"&gt;&lt;img src="http://img440.imageshack.us/img440/6574/img0002sy.jpg" /&gt;&lt;/a&gt;
&lt;a href="http://img413.imageshack.us/img413/4743/img0003ih.jpg" rel="shadowbox[lovro]"&gt;&lt;img src="http://img839.imageshack.us/img839/2407/img0003sfa.jpg" /&gt;&lt;/a&gt;
&lt;br /&gt;
Stay healthy.
&lt;br /&gt;&lt;br /&gt;
Tadej&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6666239544818473000-1670429101993157491?l=blog.borovsak.si' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.borovsak.si/feeds/1670429101993157491/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.borovsak.si/2010/02/my-favorite-waste-of-time_2166.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6666239544818473000/posts/default/1670429101993157491'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6666239544818473000/posts/default/1670429101993157491'/><link rel='alternate' type='text/html' href='http://blog.borovsak.si/2010/02/my-favorite-waste-of-time_2166.html' title='My favorite waste of time ...'/><author><name>Tadej Borovšak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-V5S0cD-5b_I/AAAAAAAAAAI/AAAAAAAAArU/nAyUYzn6Yx8/s512-c/photo.jpg'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6666239544818473000.post-463322875643923840</id><published>2009-09-27T19:30:00.007+02:00</published><updated>2010-10-01T19:43:32.842+02:00</updated><title type='text'>Imagination 2.0 released</title><content type='html'>Hello all.
&lt;br /&gt;&lt;br /&gt;
This is just a small announcement about new release of Imagination Slideshow maker.
&lt;br /&gt;&lt;br /&gt;
Highlights of this release are:
&lt;ul&gt;
&lt;li&gt;Added Ken Burns animation&lt;/li&gt;
&lt;li&gt;Slides can now have label&lt;/li&gt;
&lt;li&gt;Audio is finally handled correctly&lt;/li&gt;
&lt;/ul&gt;
Never heard of Imagination before? &lt;a href="http://imagination.sf.net"&gt;imagination.sf.net&lt;/a&gt; is a page that you should definitely visit today;)
&lt;br /&gt;&lt;br /&gt;
Here are some screenshots from the latest version:&lt;br /&gt;
&lt;a href="http://i626.photobucket.com/albums/tt344/tadeboro/imagination-2-0/2009-09-27-193605.png" rel="shadowbox[imagination]" title="Main window in normal mode"&gt;&lt;img src="http://i626.photobucket.com/albums/tt344/tadeboro/imagination-2-0/th_2009-09-27-193605.png" /&gt;&lt;/a&gt;
&lt;a href="http://i626.photobucket.com/albums/tt344/tadeboro/imagination-2-0/2009-09-27-194739.png" rel="shadowbox[imagination]" title="Overview mode"&gt;&lt;img src="http://i626.photobucket.com/albums/tt344/tadeboro/imagination-2-0/th_2009-09-27-194739.png" /&gt;&lt;/a&gt;
&lt;br /&gt;&lt;br /&gt;
Stay healthy&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6666239544818473000-463322875643923840?l=blog.borovsak.si' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.borovsak.si/feeds/463322875643923840/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.borovsak.si/2009/09/imagination-20-released_1283.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6666239544818473000/posts/default/463322875643923840'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6666239544818473000/posts/default/463322875643923840'/><link rel='alternate' type='text/html' href='http://blog.borovsak.si/2009/09/imagination-20-released_1283.html' title='Imagination 2.0 released'/><author><name>Tadej Borovšak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-V5S0cD-5b_I/AAAAAAAAAAI/AAAAAAAAArU/nAyUYzn6Yx8/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://i626.photobucket.com/albums/tt344/tadeboro/imagination-2-0/th_2009-09-27-193605.png' height='72' width='72'/><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6666239544818473000.post-6608201521873672293</id><published>2009-09-27T19:06:00.004+02:00</published><updated>2009-09-27T19:29:56.533+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='tutorial'/><category scheme='http://www.blogger.com/atom/ns#' term='GTK+'/><category scheme='http://www.blogger.com/atom/ns#' term='Glade'/><title type='text'>Glade3 tutorial (6) - Signals</title><content type='html'>Hello.
&lt;br /&gt;&lt;br /&gt;
As promised, we'll finally connect some signals to handlers and write some C
code that will do something useful.
&lt;br /&gt;&lt;br /&gt;
&lt;span class="s_title"&gt;Contents&lt;/span&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/09/glade3-tutorial-1-introduction.html"&gt;Glade3 tutorial (1) - Introduction&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/09/glade3-tutorial-2-constructing.html"&gt;Glade3 tutorial (2) - Constructing interface&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/09/glade3-tutorial-3-size-negotiation.html"&gt;Glade3 tutorial (3) - Size negotiation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/09/glade3-tutorial-4-gtktreeview-data.html"&gt;Glade3 tutorial (4) - GtkTreeView data backend&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/09/glade3-tutorial-5-modifying-widget-tree.html"&gt;Glade3 tutorial (5) - Modifying widget tree&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/09/glade3-tutorial-6-signals.html"&gt;Glade3 tutorial (6) - Signals&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;span class="s_title"&gt;Signals tab in Glade&lt;/span&gt;
&lt;br /&gt;&lt;br /&gt;
If you select one of the buttons, signals tab will look like this (I color-coded
columns):
&lt;br /&gt;&lt;br /&gt;
&lt;img alt="Signals tab" src="http://i626.photobucket.com/albums/tt344/tadeboro/2009-09-24-211801.png" /&gt;
&lt;br /&gt;&lt;br /&gt;
Inside &amp;quot;Signal&amp;quot; column are listed signals, grouped by widget type.
&amp;quot;Handler&amp;quot; column is where you insert name of the function that should
be hooked to this signal. &amp;quot;User data&amp;quot; column can hold object name from
this glade file that should be sent as data parameter to callback function
(note: if field is not empty, signal will be connected the same way as if you
were to call &lt;span class="cf"&gt;g_signal_connect_swapped&lt;/span&gt; macro). Last,
&amp;quot;After&amp;quot; column holds check button that controls how you callback is
connected: if checked, your signal will be connected like you were to call &lt;span
class="cf"&gt;g_signal_connect_after&lt;/span&gt;, which means that your function will be
called after the default signal handler.
&lt;br /&gt;&lt;br /&gt;
For 99% of time, you can safely ignore last two columns, since they are rarely
used, but it's still nice to know why exactly they stand there for.
&lt;br /&gt;&lt;br /&gt;
&lt;span class="s_title"&gt;Signal connection theory&lt;/span&gt;
&lt;br /&gt;&lt;br /&gt;
Signal connection is done in two stages:
&lt;ol&gt;
  &lt;li&gt;Names of the functions that should be evoked when signal is emitted are
   specified in &amp;quot;Signals&amp;quot; tab in Glade.&lt;/li&gt;
  &lt;li&gt;Mapping of &lt;em&gt;function name&lt;/em&gt; -&gt; &lt;em&gt;function address&lt;/em&gt; is done at
   runtime by either using GModule or manually by user.&lt;/li&gt;
&lt;/ol&gt;
First stage is simple: just fill appropriate fields inside &amp;quot;Signals&amp;quot;
tab and save the project.
&lt;br /&gt;&lt;br /&gt;
Second stage is more complex to understand, because there are two different ways
of mapping function names to function addresses. First one (the simple one) is
to use &lt;span class="cf"&gt;gtk_builder_connect_signals&lt;/span&gt; function, which will
automatically search for function with proper names in your main executable. I
won't go into details of how this is done, but keep in mind that GModule is
required for this operation. Second method of mapping names to addresses is to
use &lt;span class="cf"&gt;gtk_builder_connect_signals_full&lt;/span&gt;, where you must
provide function that will do the mapping.
&lt;br /&gt;&lt;br /&gt;
In this tutorial, we'll use automatic method, since this is what most people
will end up using in their applications.
&lt;br /&gt;&lt;br /&gt;
&lt;span class="s_title"&gt;Passing custom data to callbacks&lt;/span&gt;
&lt;br /&gt;&lt;br /&gt;
Any modestly complex application will require some data exchange to and from
callback functions. So how can we achieve this?
&lt;br /&gt;&lt;br /&gt;
As already mentioned, one way of passing data to callback function is to specify
object name in Glade. This is simple, but fairly limited method, since only
object from your UI file can be passed like this. More flexible way of passing
data to callbacks is by using data parameter of &lt;span
class="cf"&gt;gtk_builder_connect_signals&lt;/span&gt;. Whatever you specify as a last
parameter will be passed to all of the connected callbacks as a last parameter.
&lt;br /&gt;&lt;br /&gt;
&amp;quot;But there is only one argument available, and I would need more!&amp;quot;
Well, this problem is usually solved by defining a structure that holds all of
the data that may be needed in any of the callbacks. Pointer to an instance of
that structure is then passed as a last parameter to &lt;span
class="cf"&gt;gtk_builder_connect_signals&lt;/span&gt;.
&lt;br /&gt;&lt;br /&gt;
&lt;span class="s_title"&gt;Callback handler definition&lt;/span&gt;
&lt;br /&gt;&lt;br /&gt;
When using &lt;span class="cf"&gt;gtk_builder_connect_signals&lt;/span&gt; to connect
signals, you most take some additional steps to ensure that functions are found
by GModule. Exact steps needed are platform specific, but Glib developers
kindly provided means to write portable code.
&lt;br /&gt;&lt;br /&gt;
Before function declaration/definition, you should place &lt;span
class="cf"&gt;G_MODULE_EXPORT&lt;/span&gt; macro. So if we have callback handler for
button's &amp;quot;clicked&amp;quot; signal defined like this in non-glade application:
&lt;pre class="brush: c"&gt;
static void
cb_clicked( GtkButton *button,
            gpointer   data )
{
    /* CODE HERE */
}
&lt;/pre&gt;
we need to modify it to this in order for GModule to be able to find it:
&lt;pre class="brush: c"&gt;
G_MODULE_EXPORT void
cb_clicked( GtkButton *button,
            gpointer   data )
{
    /* CODE HERE */
}
&lt;/pre&gt;
Other step needed to make code function properly on all platforms is to link
executable with proper linker flags. Thanks to Glib developers, all that we need
to do is add &amp;quot;gmodule-2.0&amp;quot; to pkg-config parameters. If you compiled
your normal applications using compile lines similar to those:
&lt;pre class="brush: text; gutter: false;"&gt;
gcc -c object1.c $(pkg-config --clfags gtk+-2.0)
gcc -c object2.c $(pkg-config --cflags gtk+-2.0)
gcc -o app object1.o object2.o $(pkg-config --libs gtk+-2.0)
&lt;/pre&gt;
then adopted new compile lines will look like this:
&lt;pre class="brush: text; gutter: false;"&gt;
gcc -c object1.c $(pkg-config --clfags gtk+-2.0 gmodule-2.0)
gcc -c object2.c $(pkg-config --cflags gtk+-2.0 gmodule-2.0)
gcc -o app object1.o object2.o $(pkg-config --libs gtk+-2.0 gmodule-2.0)
&lt;/pre&gt;
&lt;span class="s_title"&gt;Preparing glade file&lt;/span&gt;
&lt;br /&gt;&lt;br /&gt;
Before we can start writing code, we need to do some minor modifications to
glade file: rename &amp;quot;window1&amp;quot; to &amp;quot;main_window&amp;quot;, rename
&amp;quot;drawingarea1&amp;quot; to &amp;quot;chart_area&amp;quot; and connect
&amp;quot;expose-event&amp;quot; to cb_expose_chart function.
&lt;br /&gt;&lt;br /&gt;
&lt;img alt="Modifications" src="http://i626.photobucket.com/albums/tt344/tadeboro/2009-09-25-113545.png" /&gt;
&lt;br /&gt;&lt;br /&gt;
Note though, renaming is not needed, but it's more convenient if we rename
widgets that we need. With these changes in place, we're ready to start creating
code.
&lt;br /&gt;&lt;br /&gt;
&lt;span class="s_title"&gt;Code&lt;/span&gt;
&lt;br /&gt;&lt;br /&gt;
This is where Glade cannot help us anymore. Let's start by creating empty folder
that will hold all of our files. Now copy latest glade file you saved here and
rename it to &amp;quot;charter.glade&amp;quot;.
&lt;br /&gt;&lt;br /&gt;
Our code will be initially divided into 3 source files:
&lt;ol&gt;
  &lt;li&gt;charter.c will hold main function&lt;/li&gt;
  &lt;li&gt;support.h will hold some convenience macros and main data structure
   definition&lt;/li&gt;
  &lt;li&gt;callbacks.c will hold functions that we connected in Glade&lt;/li&gt;
&lt;/ol&gt;
We'll start with support.h file.
&lt;pre class="brush: c"&gt;
#ifndef __SUPPORT_H__
#define __SUPPORT_H__

#include &amp;lt;gtk/gtk.h&amp;gt;

/* Convenience macros for obtaining objects from UI file */
#define CH_GET_OBJECT( builder, name, type, data ) \
    data-&amp;gt;name = type( gtk_builder_get_object( builder, #name ) )
#define CH_GET_WIDGET( builder, name, data ) \
    CH_GET_OBJECT( builder, name, GTK_WIDGET, data )

/* Main data structure definition */
typedef struct _ChData ChData;
struct _ChData
{
    /* Widgets */
    GtkWidget *main_window;  /* Main application window */
    GtkWidget *chart_area;   /* Chart drawing area */
};

#endif /* __SUPPORT_H__ */
&lt;/pre&gt;
First thing we do is include GTK+ header file, which will provide type and
function declarations. Next we define two macros that will make obtaining object
pointers less verbose. Consult your favourite C preprocessor manual for more
information about how this two macros work. Last thing in this file is structure
definition. This structure will serve as our main data storage. We'll add more
fields to it gradually. For starters, main window and chart area pointers will
suffice.
&lt;br /&gt;&lt;br /&gt;
Next file that we'll create is charter.c.
&lt;pre class="brush: c"&gt;
#include &amp;quot;support.h&amp;quot;

#define UI_FILE &amp;quot;charter.glade&amp;quot;

int
main( int    argc,
      char **argv )
{
    ChData     *data;
    GtkBuilder *builder;
    GError     *error = NULL;

    /* Init GTK+ */
    gtk_init( &amp;amp;argc, &amp;amp;argv );

    /* Create new GtkBuilder object */
    builder = gtk_builder_new();
    if( ! gtk_builder_add_from_file( builder, UI_FILE, &amp;amp;error ) )
    {
        g_warning( &amp;quot;%s&amp;quot;, error-&amp;gt;message );
        g_free( error );
        return( 1 );
    }

    /* Allocate data structure */
    data = g_slice_new( ChData );

    /* Get objects from UI */
#define GW( name ) CH_GET_WIDGET( builder, name, data )
    GW( main_window );
    GW( chart_area );
#undef GW

    /* Connect signals */
    gtk_builder_connect_signals( builder, data );

    /* Destroy builder, since we don't need it anymore */
    g_object_unref( G_OBJECT( builder ) );

    /* Show window. All other widgets are automatically shown by GtkBuilder */
    gtk_widget_show( data-&amp;gt;main_window );

    /* Start main loop */
    gtk_main();

    /* Free any allocated data */
    g_slice_free( ChData, data );

    return( 0 );
}
&lt;/pre&gt;
This file is almost exactly the same as tut.c file from second part of this
tutorial. One important difference is creation of data structure that is passed
to all of the callbacks.
&lt;br /&gt;&lt;br /&gt;
Last file is callbacks.c. We'll be adding callback handlers into this file as we
connect them inside Glade. There is only one function that we need to write:
cb_expose_chart. But before we start writing anything, we need to check what
prototype expose handler should have. Navigate to GtkWidget API reference and
search for &amp;quot;expose-event&amp;quot; signal. (Or, if you're too lazy to search
for yourself, click &lt;a
href="http://library.gnome.org/devel/gtk/stable/GtkWidget.html#GtkWidget-expose-event"&gt;this
link&lt;/a&gt;.)
&lt;br /&gt;&lt;br /&gt;
In reference, you'll see that expose event handler should be defined as:
&lt;pre class="brush: c"&gt;
gboolean
function( GtkWidget      *widget,
          GdkEventExpose *event,
          gpointer        user_data )
&lt;/pre&gt;
Having this knowledge, we can now create callback file.
&lt;pre class="brush: c"&gt;
#include &amp;quot;support.h&amp;quot;

G_MODULE_EXPORT gboolean
cb_expose_chart( GtkWidget      *widget,
                 GdkEventExpose *event,
                 ChData         *data )
{
    cairo_t *cr;

    /* Create cairo context from GdkWindow */
    cr = gdk_cairo_create( event-&amp;gt;window );

    /* Paint whole area in green color */
    cairo_set_source_rgb( cr, 0, 1, 0 );
    cairo_paint( cr );

    /* Destroy cairo context */
    cairo_destroy( cr );

    /* Return TRUE, since we handled this event */
    return( TRUE );
}
&lt;/pre&gt;
You can see that we added G_MODULE_EXPORT in front of function definition. We
also modified last parameter to avoid some casting. You can ignore function body
for now, since cairo drawing will be explained more when we'll start drawing our
charts.
&lt;br /&gt;&lt;br /&gt;
&lt;span class="s_title"&gt;Compiling application&lt;/span&gt;
&lt;br /&gt;&lt;br /&gt;
Last thing we need to do is compile application. This will in our case be done
in three steps:
&lt;ol&gt;
  &lt;li&gt;compile charter.c into charter.o&lt;/li&gt;
  &lt;li&gt;compile callbacks.c into callbacks.o&lt;/li&gt;
  &lt;li&gt;link both object files into charter&lt;/li&gt;
&lt;/ol&gt;
This translates into terminal command lines:
&lt;pre class="brush: text; gutter: false;"&gt;
gcc -c charter.c -o charter.o $(pkg-config --cflags gtk+-2.0 gmodule-2.0)
gcc -c callbacks.c -o callbacks.o $(pkg-config --cflags gtk+-2.0 gmodule-2.0)
gcc -o charter charter.o callbacks.o $(pkg-config --libs gtk+-2.0 gmodule-2.0)
&lt;/pre&gt;
Since this can be quite demanding to write for each recompilation, I created
Makefile that should make things a bit easier.
&lt;pre class="brush: text; gutter: false;"&gt;
CC     = gcc
CFLAGS = `pkg-config --cflags gtk+-2.0 gmodule-2.0`
LIBS   = `pkg-config --libs   gtk+-2.0 gmodule-2.0`
DEBUG  = -Wall -g

OBJECTS = charter.o callbacks.o

.PHONY: clean

all: charter

charter: $(OBJECTS)
    $(CC) $(DEBUG) $(LIBS) $(OBJECTS) -o $@

charter.o: charter.c support.h
    $(CC) $(DEBUG) $(CFLAGS) -c $&amp;lt; -o $@

callbacks.o: callbacks.c support.h
    $(CC) $(DEBUG) $(CFLAGS) -c $&amp;lt; -o $@

clean:
    rm -f *.o charter
&lt;/pre&gt;
Consult you make manual for detailed information about this file. And we're done
for today.
&lt;br /&gt;&lt;br /&gt;
&lt;span class="s_title"&gt;Getting sources&lt;/span&gt;
&lt;br /&gt;&lt;br /&gt;
This is something new. I created a git repository at Gtihub.com to host files
from this tutorial. I'll pack a code from each part of the tutorial and upload
it on Github.com, but if you prefer to use git for obtaining code, I'll also tag
commits. &lt;a href="http://github.com/tadeboro/Glade3-tutorial"&gt;Here is my Github repository&lt;/a&gt; and &lt;a href="http://cloud.github.com/downloads/tadeboro/Glade3-tutorial/glade_tut_06.tar.bz2"&gt;here are
files for this tutorial part&lt;/a&gt;.
&lt;br /&gt;&lt;br /&gt;
I also started converting posts from this tutorial into a DocBook format, which
should hopefully make this tutorial more flexible and available in more formats.
&lt;br /&gt;&lt;br /&gt;
Stay healthy.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6666239544818473000-6608201521873672293?l=blog.borovsak.si' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.borovsak.si/feeds/6608201521873672293/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.borovsak.si/2009/09/glade3-tutorial-6-signals.html#comment-form' title='17 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6666239544818473000/posts/default/6608201521873672293'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6666239544818473000/posts/default/6608201521873672293'/><link rel='alternate' type='text/html' href='http://blog.borovsak.si/2009/09/glade3-tutorial-6-signals.html' title='Glade3 tutorial (6) - Signals'/><author><name>Tadej Borovšak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-V5S0cD-5b_I/AAAAAAAAAAI/AAAAAAAAArU/nAyUYzn6Yx8/s512-c/photo.jpg'/></author><thr:total>17</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6666239544818473000.post-2837188439286362177</id><published>2009-09-24T13:01:00.004+02:00</published><updated>2009-09-27T19:29:38.053+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='tutorial'/><category scheme='http://www.blogger.com/atom/ns#' term='GTK+'/><category scheme='http://www.blogger.com/atom/ns#' term='Glade'/><title type='text'>Glade3 tutorial (5) - Modifying widget tree</title><content type='html'>Hi.
&lt;br /&gt;&lt;br /&gt;
Last time we added data backend to our application, but we discovered at the end
that our GUI isn't flexible enough. We'll fix that today by adding paned widget
into widget tree and replace image and label buttons with image only buttons.
&lt;br /&gt;&lt;br /&gt;
&lt;span class="s_title"&gt;Contents&lt;/span&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/09/glade3-tutorial-1-introduction.html"&gt;Glade3 tutorial (1) - Introduction&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/09/glade3-tutorial-2-constructing.html"&gt;Glade3 tutorial (2) - Constructing interface&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/09/glade3-tutorial-3-size-negotiation.html"&gt;Glade3 tutorial (3) - Size negotiation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/09/glade3-tutorial-4-gtktreeview-data.html"&gt;Glade3 tutorial (4) - GtkTreeView data backend&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/09/glade3-tutorial-5-modifying-widget-tree.html"&gt;Glade3 tutorial (5) - Modifying widget tree&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/09/glade3-tutorial-6-signals.html"&gt;Glade3 tutorial (6) - Signals&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;span class="s_title"&gt;New widget tree&lt;/span&gt;
&lt;br /&gt;&lt;br /&gt;
We're going to do some changes to the widget tree. We'll do those changes on
paper first and from that we'll create our modification plan. And here are old
and new widget trees:
&lt;br /&gt;&lt;br /&gt;
&lt;img alt="Widget trees" src="http://i626.photobucket.com/albums/tt344/tadeboro/2009-09-24-110521.png" /&gt;
&lt;br /&gt;&lt;br /&gt;
What steps do we need to make to transform old tree (on the left side of the
image) into new tree (on the right side)?
&lt;ol&gt;
  &lt;li&gt;Add GtkHPaned as parent of GtkHBox&lt;/li&gt;
  &lt;li&gt;Reparent display area GtkHBox from GtkHBox to GtkHPaned&lt;/li&gt;
  &lt;li&gt;Add GtkAlignment as parent of GtkVButtonBox&lt;/li&gt;
  &lt;li&gt;Replace GtkVButtonBox with GtkVBox&lt;/li&gt;
  &lt;li&gt;Replace stock items on buttons with stock images only&lt;/li&gt;
&lt;/ol&gt;
We'll also need to adjust some widget and packing parameters, but to keep
initial plan as simple as possible, I'll just describe those changes on-the-go.
&lt;br /&gt;&lt;br /&gt;
&lt;span class="s_title"&gt;Inserting widget into widget tree&lt;/span&gt;
&lt;br /&gt;&lt;br /&gt;
So far, we were building our GUI in top-to-bottom linear manner, where we first
constructed parent of the widget and than widget itself. Now I'll show you how
to insert widget in the middle of the widget tree. 
&lt;br /&gt;&lt;br /&gt;
Right click GtkHBox from widget tree and select &amp;quot;Add Parent -&gt; Horizontal
Panes&amp;quot;. You should end up wih something similar to this:
&lt;br /&gt;&lt;br /&gt;
&lt;img alt="Inserted horizontal paned" src="http://i626.photobucket.com/albums/tt344/tadeboro/2009-09-24-111521.png" /&gt;
&lt;br /&gt;&lt;br /&gt;
Now we need to reparent display area vertical box to right pane. Right click in
vertical box in widget tree and select &amp;quot;Cut&amp;quot;. Now right click inside
right pane and select &amp;quot;Paste&amp;quot;. Simple.
&lt;br /&gt;&lt;br /&gt;
&lt;img alt="Reparent display area vertical box" src="http://i626.photobucket.com/albums/tt344/tadeboro/2009-09-24-112831.png" /&gt;
&lt;br /&gt;&lt;br /&gt;
You can see that we have some empty space on the right side of our buttons. To
remove it, select horizontal box and decrese number of elements to 2.
&lt;br /&gt;&lt;br /&gt;
&lt;img alt="Remove empty space" src="http://i626.photobucket.com/albums/tt344/tadeboro/2009-09-24-114133.png" /&gt;
&lt;br /&gt;&lt;br /&gt;
Next on the list is adding GtkAlignment as parent of vertical button box. This
process is analogous to adding panes. Right click vertical button box and select
&amp;quot;Add Parent -&gt; Alignment&amp;quot;. That's all there is to it.
&lt;br /&gt;&lt;br /&gt;
&lt;img alt="Reparent vertical button box" src="http://i626.photobucket.com/albums/tt344/tadeboro/2009-09-24-115257.png" /&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;span class="s_title"&gt;Creating image-only buttons&lt;/span&gt;
&lt;br /&gt;&lt;br /&gt;
Last thing we need to do is replace vertical button box with normal vertical box
and add only stock icons to the buttons. The simplest way of doing this is to
delete the button box and recreate buttons from scratch. We could cut-paste each
button, but creating them will be faster in this case.
&lt;br /&gt;&lt;br /&gt;
After deleting button box, insert vertical box with 6 elements into empty space
and set element spacing to 2 px.
&lt;br /&gt;&lt;br /&gt;
&lt;img alt="Create new vertical box" src="http://i626.photobucket.com/albums/tt344/tadeboro/2009-09-24-120114.png" /&gt;
&lt;br /&gt;&lt;br /&gt;
Now create new button, insert it into one of the fields in vertical box and set
it's content to custom.
&lt;br /&gt;&lt;br /&gt;
&lt;img alt="Add new custom content button" src="http://i626.photobucket.com/albums/tt344/tadeboro/2009-09-24-120216.png" /&gt;
&lt;br /&gt;&lt;br /&gt;
Inside empty space insert GtkImage and set it's stock ID to &amp;quote;Top&amp;quote;.
&lt;br /&gt;&lt;br /&gt;
&lt;img alt="Add image to button" src="http://i626.photobucket.com/albums/tt344/tadeboro/2009-09-24-120545.png" /&gt;
&lt;br /&gt;&lt;br /&gt;
Now repeate this five more times, using stock ID's &amp;quot;Up&amp;quot;,
&amp;quot;Add&amp;quot;, &amp;quot;Remove&amp;quot;, &amp;quot;Down&amp;quot; and &amp;quot;Bottom&amp;quot;.
You should see something like this when you finish:
&lt;br /&gt;&lt;br /&gt;
&lt;img alt="All buttons populated" src="http://i626.photobucket.com/albums/tt344/tadeboro/2009-09-24-121108.png" /&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;span class="s_title"&gt;Final tweaks&lt;/span&gt;
&lt;br /&gt;&lt;br /&gt;
All that is left now is to tweak our GUI properties a bit. First thing we need
to do is make data scrolled window expandable and scrollable. Select
scrolledwindow1 and under &amp;quot;General&amp;quot; tab set &amp;quot;Horizontal Scrollbar
Policy&amp;quot; to &amp;quot;Automatic&amp;quot;. Now open &amp;quot;Packing&amp;quot; tab and set
&amp;quot;Expand&amp;quot; to &amp;quot;Yes&amp;quot;.
&lt;br /&gt;&lt;br /&gt;
Now select alignment2 and under &amp;quot;General&amp;quot; tab set &amp;quot;Horizontal
scale&amp;quot; and &amp;quot;Vertical Scale&amp;quot; to 0, which will make our buttons
centered.
&lt;br /&gt;&lt;br /&gt;
And we're done. Save the interface and have fun. Get complete &lt;a href="http://tadeboro.googlepages.com/tut03.glade"&gt;glade
file&lt;/a&gt; or &lt;a href=""&gt;watch screencast&lt;/a&gt; as usual.
&lt;br /&gt;&lt;br /&gt;
Next time we'll connect some callbacs (and I mean it;). Stay healthy until then.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6666239544818473000-2837188439286362177?l=blog.borovsak.si' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.borovsak.si/feeds/2837188439286362177/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.borovsak.si/2009/09/glade3-tutorial-5-modifying-widget-tree.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6666239544818473000/posts/default/2837188439286362177'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6666239544818473000/posts/default/2837188439286362177'/><link rel='alternate' type='text/html' href='http://blog.borovsak.si/2009/09/glade3-tutorial-5-modifying-widget-tree.html' title='Glade3 tutorial (5) - Modifying widget tree'/><author><name>Tadej Borovšak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-V5S0cD-5b_I/AAAAAAAAAAI/AAAAAAAAArU/nAyUYzn6Yx8/s512-c/photo.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6666239544818473000.post-4497623295216662458</id><published>2009-09-19T21:37:00.005+02:00</published><updated>2009-09-27T19:29:19.914+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='GtkTreeView'/><category scheme='http://www.blogger.com/atom/ns#' term='tutorial'/><category scheme='http://www.blogger.com/atom/ns#' term='GtkTreeModel'/><category scheme='http://www.blogger.com/atom/ns#' term='GTK+'/><category scheme='http://www.blogger.com/atom/ns#' term='Glade'/><title type='text'>Glade3 tutorial (4) - GtkTreeView data backend</title><content type='html'>Welcome.
&lt;br /&gt;&lt;br /&gt;
First, I need to tell you that we won't be connecting any callbacks today. I
knew I promised this last time, but we simply don't have any data to manipulate
inside callbacks. So what are we going to do today? Well, adding data seems like
a good idea;)
&lt;br /&gt;&lt;br /&gt;
&lt;span class="s_title"&gt;Contents&lt;/span&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/09/glade3-tutorial-1-introduction.html"&gt;Glade3 tutorial (1) - Introduction&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/09/glade3-tutorial-2-constructing.html"&gt;Glade3 tutorial (2) - Constructing interface&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/09/glade3-tutorial-3-size-negotiation.html"&gt;Glade3 tutorial (3) - Size negotiation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/09/glade3-tutorial-4-gtktreeview-data.html"&gt;Glade3 tutorial (4) - GtkTreeView data backend&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/09/glade3-tutorial-5-modifying-widget-tree.html"&gt;Glade3 tutorial (5) - Modifying widget tree&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/09/glade3-tutorial-6-signals.html"&gt;Glade3 tutorial (6) - Signals&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;span class="s_title"&gt;Model-view-controller (MVC) design&lt;/span&gt;
&lt;br /&gt;&lt;br /&gt;
Before we start doing anything, we need to know some things about how
GtkTreeView operates. Information about this can be found in &lt;a href="http://scentric.net/tutorial/treeview-tutorial.html"&gt;API
documentation&lt;/a&gt; and in &lt;a href="http://scentric.net/tutorial/treeview-tutorial.html"&gt;GtkTreeView tutorial&lt;/a&gt;. I would advice you
to read those two references if you're just starting to code with GTK+.
&lt;br /&gt;&lt;br /&gt;
If you don't have time to read those two in entirety, I'll just quickly sum the
contents:
&lt;ol&gt;
  &lt;li&gt;GtkTreeView is basically just container that holds columns and provides
   surface on which cell renderers draw on.&lt;/li&gt;
  &lt;li&gt;GtkCellRenderer is object that draws inside tree view based on the
   data inside backend storage.&lt;/li&gt;
  &lt;li&gt;GtkTreeModel is interface that any data store needs to implement if it
   wants to be used as a data backend to tree view.&lt;/li&gt;
  &lt;li&gt;GtkTreeStore and GtkListStore are two data stores that implement
   GtkTreeModel interface and are part of GTK+ itself.&lt;/li&gt;
&lt;/ol&gt;
Keep this in mind since it's crucial for understanding what exactly will we be
doing with glade.
&lt;br /&gt;&lt;br /&gt;
&lt;span class="s_title"&gt;Creating data backend&lt;/span&gt;
&lt;br /&gt;&lt;br /&gt;
Load project form last post into Glade. Now scroll down widget catalog until you
see &amp;quot;Tree Model&amp;quot; part. Expand it and click on &amp;quot;List Store&amp;quot;
icon. New entry will appear under &amp;quot;Objects&amp;quot; inside object tree.
&lt;br /&gt;&lt;br /&gt;
&lt;img alt="Creating new list store" src="http://i626.photobucket.com/albums/tt344/tadeboro/2009-09-14-110610.png" /&gt;
&lt;br /&gt;&lt;br /&gt;
You'll probably want to resize right panel to get more space for editing list
store properties. We'll do two things now:
&lt;ol&gt;
  &lt;li&gt;Define number of columns inside list store and their types&lt;/li&gt;
  &lt;li&gt;Add some sample data to list store for demonstration purposes&lt;/li&gt;
&lt;/ol&gt;
Number of columns and their types are defined in upper part of the
&amp;quot;General&amp;quot; tab. Simply start typing into proper field to add new
column. What columns do we need? Since we're developing charting application,
we'll need one column that will hold X coordinates of the points, one column to
hold Y coordinates and one column for optional label that can be added to point.
Types of the columns will be gdouble for X and Y coordinates and gchararray for
label column (gchararray is just another name for &amp;quot;gchar *&amp;quot; that is
registered using GType system).
&lt;br /&gt;&lt;br /&gt;
&lt;img alt="Defined columns" src="http://i626.photobucket.com/albums/tt344/tadeboro/2009-09-14-111034.png" /&gt;
&lt;br /&gt;&lt;br /&gt;
Now we'll add some sample data to list store. Scroll down to expose data
insertion part, add six rows using plus button and fill them with with data like
this:
&lt;pre&gt;+-------+-------+---------------------+
| - 4.5 | - 2   | Start               |
| - 3   | - 1.5 |                     |
| - 1.5 | - 1   |                     |
|   0   | - 0.5 | Y axis intersection |
|   1.5 |   0   | X axis intersection |
|   3   |   0.5 | End                 |
+-------+-------+---------------------+&lt;/pre&gt;
&lt;img alt="Sample data" src="http://i626.photobucket.com/albums/tt344/tadeboro/2009-09-14-111309.png" /&gt;
&lt;br /&gt;&lt;br /&gt;
Our list store is finished now.
&lt;br /&gt;&lt;br /&gt;
&lt;span class="s_title"&gt;Connecting tree view with list store&lt;/span&gt;
&lt;br /&gt;&lt;br /&gt;
Next, we need to connect tree view and list store. Select tree view and in
&amp;quot;General&amp;quot; tab click button with &amp;quot;...&amp;quot; next to the
&amp;quot;TreeView model&amp;quot; field. Inside pop-up dialog select
&amp;quot;liststore1&amp;quot; and click &amp;quot;OK&amp;quot;.
&lt;br /&gt;&lt;br /&gt;
&lt;img alt="Connecting tree view and list store" src="http://i626.photobucket.com/albums/tt344/tadeboro/2009-09-14-111454.png" /&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;span class="s_title"&gt;Adding display components to tree view&lt;/span&gt;
&lt;br /&gt;&lt;br /&gt;
Now we need to add columns to out tree view that will hold cell renderers.
When we select tree view, new icon labeled &amp;quot;Edit...&amp;quot;will appear at the
end of the toolbar. Clicking it will open new window with tree view editor.
&lt;br /&gt;&lt;br /&gt;
&lt;img alt="Open tree view editor" src="http://i626.photobucket.com/albums/tt344/tadeboro/2009-09-14-112746.png" /&gt;
&lt;br /&gt;&lt;br /&gt;
Inside &amp;quot;General&amp;quot; tab of Tree View Editor we can see some information
we already entered before. To create display parts of tree view, we need to
switch to &amp;quot;Hierarchy&amp;quot; tab, where we're presented with a lot of empty
space;). To add new column, click &amp;quot;Add&amp;quot; button and new column will be
added to left field, with it's properties displayed in rigth field. We'll change
column's title to &amp;quot;X&amp;quot; and this is all that we'll do with this column.
&lt;br /&gt;&lt;br /&gt;
&lt;img alt="Create X column" src="http://i626.photobucket.com/albums/tt344/tadeboro/2009-09-14-113731.png" /&gt;
&lt;br /&gt;&lt;br /&gt;
Now right-click newly created column and select &amp;quot;Add child Spin item&amp;quot;,
which will add new GtkCellRendererSpin to hierarchy. Now we need to again change
some properties. But since changing cell renderer's properties is a bit
different that changing properties of normal objects, I'll explain this a bit
more in detail.
&lt;br /&gt;&lt;br /&gt;
If you look at the properties editor, you'll notice that it is composed from 4
columns: first column holds check button, second column holds property name,
third column holds spin button and last column holds combo box. Why are things
so complex? Because cell renderers can have their properties set on two
different ways: on a global basis, which means that this property will be the
same for all lines that cell renderer draws; or on a per-line basis, which means
that property is stored inside data backend and is read from there for each
line.
&lt;br /&gt;&lt;br /&gt;
How do these two methods map onto property editor? Check button controls whether
property is set per-line or global (active check button means per-line, inactive
means global). When we set some property on a per-line basis, we must inform
cell renderer in which column of data store are values for this property stored,
and we can use spin button to directly input column number or combo box to
select column based on label that we assigned to it when constructing list
store. When we set some property on a global scale, spin button and combo box
are hidden and we get regular property editing widget instead.
&lt;br /&gt;&lt;br /&gt;
Now let's set up our spin cell renderer. First property that we're goint to set
is &amp;quot;Text&amp;quot;. We want to display values from data backend, so we'll leave
the check button activated and set the column, from which this property will
take it's values to X-coords column.
&lt;br /&gt;&lt;br /&gt;
&lt;img alt="Text property being set" src="http://i626.photobucket.com/albums/tt344/tadeboro/2009-09-14-130626.png" /&gt;
&lt;br /&gt;&lt;br /&gt;
Second property that we'll set is &amp;quot;Adjustment&amp;quot; property. We'll set it
on a global scale, since we want all lines to have the same range of available
values. So we need to remove tick from check button and then click the button
with &amp;quot;...&amp;quot; and create new adjustment (we don't have any yet, so we
created it).
&lt;br /&gt;&lt;br /&gt;
&lt;img alt="setting adjustment property" src="http://i626.photobucket.com/albums/tt344/tadeboro/2009-09-14-130908.png" /&gt;
&lt;br /&gt;&lt;br /&gt;
Last property that we'll set is &amp;quot;Editable&amp;quot;. We'll again set it globaly
to &amp;quot;Yes&amp;quot;, since we want all our X coordinate cells to be editable.
&lt;br /&gt;&lt;br /&gt;
&lt;img alt="Setting editable to yes" src="http://i626.photobucket.com/albums/tt344/tadeboro/2009-09-14-131155.png" /&gt;
&lt;br /&gt;&lt;br /&gt;
Now we need to create another column for Y coordinates. Process is exactly the
same as before:
&lt;ul&gt;
  &lt;li&gt;right-click X column and select &amp;quot;Add Column item&amp;quot;&lt;/li&gt;
  &lt;li&gt;change title to &amp;quot;Y&amp;quot;&lt;/li&gt;
  &lt;li&gt;right click Y column and select &amp;quot;Add child Spin item&amp;quot;&lt;/li&gt;
  &lt;li&gt;Adjust properties exactly as before (when setting &amp;quot;Text&amp;quot;
   property, slect Y-coords clumns as source of values and when you set
   &amp;quot;Adjustment&amp;quot; property, you don't create new one, just reuse
   adjustment1 that we created for X spin renderer)&lt;/li&gt;
&lt;/ul&gt;
Last column that we need to add will hold point labels. We again right-click on
column Y and select &amp;quot;Add Column item&amp;quot;. We set it's title property to
&amp;quot;Label&amp;quot;. Now right-click on Label columns and select &amp;quot;Add child
Text item&amp;qout; On the text renderer, set &amp;quot;Text&amp;quot; property to Label
column adn &amp;quot;Editable&amp;quot; property globaly to &amp;quot;Yes&amp;quot;.
&lt;br /&gt;&lt;br /&gt;
&lt;img alt="Complete hierarchy" src="http://i626.photobucket.com/albums/tt344/tadeboro/2009-09-14-132700.png" /&gt;
&lt;br /&gt;&lt;br /&gt;
There is one last thing that we need to do: adjusting properties of adjustment1
that we created when setting properties of spin button. Close tree view editor
and select adjustment1 from object tree. Now set minimum value to -5000, maximum
value to 5000, step increment to 0.1, page increment to 10 and page size to 0
(only adjustments that are connected to scroll bar should have non-zero page
size).
&lt;br /&gt;&lt;br /&gt;
&lt;img alt="Adjustment properties set" src="http://i626.photobucket.com/albums/tt344/tadeboro/2009-09-14-210108.png" /&gt;
&lt;br /&gt;&lt;br /&gt;
You can see that values that we entered into list store are now displayed inside
tree view. But we have a problem: labels are not visible because we set size
request of the scrolled window too low. But how can we determine how much space
will we need on different themes with different font sizes? The answer is:
&amp;quot;We cannot.&amp;quot;. And this is why we'll modify our GUI in next part of
this tutorial and make it more flexible.
&lt;br /&gt;&lt;br /&gt;
As usual, you can get the &lt;a href="http://tadeboro.googlepages.com/tut02.glade"&gt;latest glade file&lt;/a&gt; or &lt;a
href="http://www.screentoaster.com/watch/stU0tWQk1IR1xcQ1RbU15ZV1Fd/glade3_tutorial_creating_data_backend"&gt;watch screencast&lt;/a&gt;.
&lt;br /&gt;&lt;br /&gt;
&lt;span class="s_title"&gt;Little Glade3-3.6.7 bug&lt;/span&gt;
&lt;br /&gt;&lt;br /&gt;
Glade3-3.6.7 has some problems with loading projects that use somewhat complex
cell renderer settings. In our case, the problematic part is adjustment for spin
buttons. You'll notice that when you load this project next time, adjustment
property will be marked like it was set on per line basis (see image for
details). All you have to do is remove those checks and you should be OK. This
bug is already fixed in git version, so all that we have to do is wait for next
release.
&lt;br /&gt;&lt;br /&gt;
&lt;img alt="Loading bug" src="http://i626.photobucket.com/albums/tt344/tadeboro/2009-09-19-212945.png" /&gt;
&lt;br /&gt;&lt;br /&gt;
So long and stay healthy.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6666239544818473000-4497623295216662458?l=blog.borovsak.si' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.borovsak.si/feeds/4497623295216662458/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.borovsak.si/2009/09/glade3-tutorial-4-gtktreeview-data.html#comment-form' title='17 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6666239544818473000/posts/default/4497623295216662458'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6666239544818473000/posts/default/4497623295216662458'/><link rel='alternate' type='text/html' href='http://blog.borovsak.si/2009/09/glade3-tutorial-4-gtktreeview-data.html' title='Glade3 tutorial (4) - GtkTreeView data backend'/><author><name>Tadej Borovšak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-V5S0cD-5b_I/AAAAAAAAAAI/AAAAAAAAArU/nAyUYzn6Yx8/s512-c/photo.jpg'/></author><thr:total>17</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6666239544818473000.post-6855793270515277277</id><published>2009-09-09T03:08:00.009+02:00</published><updated>2009-09-27T19:28:57.611+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='tutorial'/><category scheme='http://www.blogger.com/atom/ns#' term='GTK+'/><category scheme='http://www.blogger.com/atom/ns#' term='Glade'/><title type='text'>Glade3 tutorial (3) - Size negotiation</title><content type='html'>Hello.
&lt;br /&gt;&lt;br /&gt;
Last time we managed to create initial GUI for our &amp;quot;Charter&amp;quot;
application. Today we'll try to make it more flexible. I'll explain some
fundamental things about size negotiation process first, and after this
introduction, we'll play a bit with Glade.
&lt;br /&gt;&lt;br /&gt;
&lt;span class="s_title"&gt;Contents&lt;/span&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/09/glade3-tutorial-1-introduction.html"&gt;Glade3 tutorial (1) - Introduction&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/09/glade3-tutorial-2-constructing.html"&gt;Glade3 tutorial (2) - Constructing interface&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/09/glade3-tutorial-3-size-negotiation.html"&gt;Glade3 tutorial (3) - Size negotiation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/09/glade3-tutorial-4-gtktreeview-data.html"&gt;Glade3 tutorial (4) - GtkTreeView data backend&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/09/glade3-tutorial-5-modifying-widget-tree.html"&gt;Glade3 tutorial (5) - Modifying widget tree&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/09/glade3-tutorial-6-signals.html"&gt;Glade3 tutorial (6) - Signals&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;span class="s_title"&gt;Size negotiation in GTK+&lt;/span&gt;
&lt;br /&gt;&lt;br /&gt;
Knowing how GTK+ handles widget sizing is one of the most important things if
you want to create clean and efficient GUIs. I called this process negotiation,
since there are two processes that contribute to final result: size requisition
and size allocation.
&lt;br /&gt;&lt;br /&gt;
Size requistion stage can be seen as recursive &amp;quot;How much space do you
need?&amp;quot; question. Toplevel window ask this question it's child, child
widgets asks their children, ... This process ends when all of the widgets in
tree responded. In our case this process looks like this:
&lt;br /&gt;&lt;br /&gt;
&lt;img alt="Requisition cascade" src="http://i626.photobucket.com/albums/tt344/tadeboro/text3037.png" /&gt;
&lt;br /&gt;&lt;br /&gt;
There are two important findings in this cascade:
&lt;ol&gt;
  &lt;li&gt;Child widgets do not know anything about parent's size preferences&lt;/li&gt;
  &lt;li&gt;Parent bases it's size on sizes of it's children&lt;/li&gt;
&lt;/ol&gt;
Now that toplevel window knows how much space is needed in ideal conditions, it
decides how much space will actually be available. If requisition stage returned
some sensible value, this is what is ususally used. But if we manually changed
size request of toplevel window (using &lt;span
class="cf"&gt;gtk_window_set_default_size&lt;/span&gt; for example) or for any other
reason requested size cannot be used, window will discard it and use different
one.
&lt;br /&gt;&lt;br /&gt;
And here is where the second stage begins: allocation. This can be also seen as
a command: &amp;quot;Here you have some space, do whatever you need to do to fit
into it.&amp;quot; that is passed from parent to it's children. And if the widget
has more than one child, it's also responsible to properly divide available
space among it's children. Remember this, since this is very important for
things that come next.
&lt;br /&gt;&lt;br /&gt;
&lt;span class="s_title"&gt;Widget packing&lt;/span&gt;
&lt;br /&gt;&lt;br /&gt;
I won't talk about packing much here, since this topic is nicely represented in
official GTK+ tutorial. And now it's time for you to go there and read packing
section: &lt;a
href="http://library.gnome.org/devel/gtk-tutorial/stable/c355.html"&gt;GTK+
tutorial - Packing Widgets&lt;/a&gt;.
&lt;br /&gt;&lt;br /&gt;
Done reading? Good. Now how are all these different options placed inside Glade?
They can be found on two separate places:
&lt;ol&gt;
  &lt;li&gt;&amp;quot;General&amp;quot; tab of container widget contains options that are set
   on container widget itself (examples would be &amp;quot;Homogeneous&amp;quot;
   property of GtkVBox or &amp;quot;Column spacing&amp;quot; of GtkTable.&lt;/li&gt;
  &lt;li&gt;&amp;quot;Packing&amp;quot; tab of widget that is being added into container
   contains options that are set at insertion time (in code, those parameters
   are set using &lt;span class="cf"&gt;gtk_box_pack_*&lt;/span&gt; and &lt;span
   class="cf"&gt;gtk_table_attach&lt;/span&gt; family of functions.&lt;/li&gt;
&lt;/ol&gt;
Are things relativelly clear now? If not, try recreating packing examples from
tutorial using Glade3. This is a great practise to get code&lt;-&gt;Glade3 connections
properly set up.
&lt;br /&gt;&lt;br /&gt;
&lt;span class="s_title"&gt;Widget packing and application resizing&lt;/span&gt;
&lt;br /&gt;&lt;br /&gt;
And we finally came to the point where we'll talk about the problem with our
application - resizing is just wrong;). But before we get too exited and start
editing our interface, let's put some of the newly acquired knowledge to work.
&lt;br /&gt;&lt;br /&gt;
We'll describe how central horizontal box determines sizes of it's children. I
took a screenshot of two instances of our application, running at the same time.
I resized them and measured parts of the horizontal box. You can see that the
central part with buttons retained it's width, while other two parts gained
equal amount of space. Why is this so? This is caused by second part of the size
negotiation process. Parent widget allocated some extra space to the horizontal
box and box then divided that extra space among it's chilren with expand
property on.
&lt;br /&gt;&lt;br /&gt;
&lt;img alt="Effects of resizing" src="http://i626.photobucket.com/albums/tt344/tadeboro/2009-09-09-020037.png" /&gt;
&lt;br /&gt;&lt;br /&gt;
Now we're ready to start fixing our application by making left part of the
application fixed in size. Start up glade, load project and click on left
scrolled window.
&lt;br /&gt;&lt;br /&gt;
&lt;img alt="Loaded project" src="http://i626.photobucket.com/albums/tt344/tadeboro/2009-09-09-023138.png" /&gt;
&lt;br /&gt;&lt;br /&gt;
Now go to &amp;quot;Common&amp;quot; tab and set width request to 150 px. This will
ensure that scrolled widget always requests 150 px wide space from parent.
&lt;br /&gt;&lt;br /&gt;
&lt;img alt="Modify width request" src="http://i626.photobucket.com/albums/tt344/tadeboro/2009-09-09-023527.png" /&gt;
&lt;br /&gt;&lt;br /&gt;
But this is not enough to make our left side fixed, since size requests only
specify minimal amount of space that this widget needs. It's completely legal to
allocate more space to it. To make sure that scrolled window gets exactly 150
px, we need to modify it's packing parameters too. Open &amp;quot;Packing&amp;quot; tab
and set &amp;quot;Expand&amp;quot; property to &amp;quot;No&amp;quot;.
&lt;br /&gt;&lt;br /&gt;
&lt;img alt="Modify packing parameters" src="http://i626.photobucket.com/albums/tt344/tadeboro/2009-09-09-024618.png" /&gt;
&lt;br /&gt;&lt;br /&gt;
With things set like this, any extra space that will be allocated to horizontal
box will be added to display area. Save your project and run sample application
again. BTW, you don't need to recompile it to see the changes, since we only
changed glade file, which is loaded at runtime. See how our GUI reacts to
resizing now?
&lt;br /&gt;&lt;br /&gt;
&lt;img alt="Resizing new application" src="http://i626.photobucket.com/albums/tt344/tadeboro/2009-09-09-024857.png" /&gt;
&lt;br /&gt;&lt;br /&gt;
And this concludes today's part of tutorial. Feel free to experiment with
different packing options. Final glade file of today's tutorial can be
downloaded from &lt;a href="http://tadeboro.googlepages.com/tut01.glade"&gt;here&lt;/a&gt; (make sure you rename it to tut.glade
before trying to run our sample application).
&lt;br /&gt;&lt;br /&gt;
You're all invited to join me next time, when we'll add data store to out tree
view and connect some buttons to their signals.
&lt;br /&gt;&lt;br /&gt;
Stay healthy.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6666239544818473000-6855793270515277277?l=blog.borovsak.si' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.borovsak.si/feeds/6855793270515277277/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.borovsak.si/2009/09/glade3-tutorial-3-size-negotiation.html#comment-form' title='12 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6666239544818473000/posts/default/6855793270515277277'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6666239544818473000/posts/default/6855793270515277277'/><link rel='alternate' type='text/html' href='http://blog.borovsak.si/2009/09/glade3-tutorial-3-size-negotiation.html' title='Glade3 tutorial (3) - Size negotiation'/><author><name>Tadej Borovšak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-V5S0cD-5b_I/AAAAAAAAAAI/AAAAAAAAArU/nAyUYzn6Yx8/s512-c/photo.jpg'/></author><thr:total>12</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6666239544818473000.post-3599535235205703057</id><published>2009-09-07T11:37:00.010+02:00</published><updated>2009-09-27T19:28:33.326+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='tutorial'/><category scheme='http://www.blogger.com/atom/ns#' term='GTK+'/><category scheme='http://www.blogger.com/atom/ns#' term='Glade'/><title type='text'>Glade3 tutorial (2) - Constructing interface</title><content type='html'>Welcome back.
&lt;br /&gt;&lt;br /&gt;
In this part of tutorial, we'll create application's GUI using Glade3 according
to our blueprint and widget tree. To check how our GUI looks like, we'll also
write a minimalistic application.
&lt;br /&gt;&lt;br /&gt;
I'll be using Glade3-3.6.7 in this tutorial, but any version from 3.6 series
will do. Our project will be saved as a GtkBuilder project and we'll use
GtkBuilder to create our interface at runtime. Glade3-3.4.5 cannot be used when
following this tutorial, since we'll use some new features of Glade3 that were
introduced in 3.6 series.
&lt;br /&gt;&lt;br /&gt;
&lt;span class="s_title"&gt;Contents&lt;/span&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/09/glade3-tutorial-1-introduction.html"&gt;Glade3 tutorial (1) - Introduction&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/09/glade3-tutorial-2-constructing.html"&gt;Glade3 tutorial (2) - Constructing interface&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/09/glade3-tutorial-3-size-negotiation.html"&gt;Glade3 tutorial (3) - Size negotiation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/09/glade3-tutorial-4-gtktreeview-data.html"&gt;Glade3 tutorial (4) - GtkTreeView data backend&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/09/glade3-tutorial-5-modifying-widget-tree.html"&gt;Glade3 tutorial (5) - Modifying widget tree&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/09/glade3-tutorial-6-signals.html"&gt;Glade3 tutorial (6) - Signals&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;span class="s_title"&gt;Constructing interface&lt;/span&gt;
&lt;br /&gt;&lt;br /&gt;
I tried to document each step in GUI creation with screenshot and instructions.
Video of the actual process can be found on &lt;a
href="http://www.screentoaster.com/watch/stU0tWQk1IR1xdQ1xYUlJZVVFT/glade3_tutorial_creating_gui"&gt;ScreenToaster
site&lt;/a&gt;.
&lt;br /&gt;&lt;br /&gt;
When you start Glade3 without opening project, you're greeted by two windows:
main application window that we'll be using to create interface and preferences
dialog that lets you set some project settings.
&lt;br /&gt;&lt;br /&gt;
&lt;img alt="Initial screen" src="http://i626.photobucket.com/albums/tt344/tadeboro/2009-09-05-105210.png" /&gt;
&lt;br /&gt;&lt;br /&gt;
You can see from this screen that we'll be using GtkBuilder project format with
object names being unique across the whole project. We won't be using any images
in this project, so resource location is not important to us. Lastly, this
project will be compatible with GTK+ &gt;= 2.14, which should make it usable on
most distributions out there. You can check your project for any
incompatibilities from this dialog too, but since we're starting new poject,
this is not needed. Last thing is to click Close and we're done with initial
setup.
&lt;br /&gt;&lt;br /&gt;
Now it's time to place initial toplevel window into project. Just click
&amp;quot;Window&amp;quot; icon in &amp;quot;Toplevels&amp;quot; category and you should see something like this:
&lt;br /&gt;&lt;br /&gt;
&lt;img alt="Initial toplevel window" src="http://i626.photobucket.com/albums/tt344/tadeboro/2009-09-05-105255.png" /&gt;
&lt;br /&gt;&lt;br /&gt;
Now we'll set window title to &amp;quot;Charter&amp;quot; and default size to 600 x 400
px.
&lt;br /&gt;&lt;br /&gt;
&lt;img alt="Modified window settings" src="http://i626.photobucket.com/albums/tt344/tadeboro/2009-09-05-105423.png" /&gt;
&lt;br /&gt;&lt;br /&gt;
Now we need to switch to &amp;quot;Common&amp;quot; tab in properties section and set
&amp;quot;Border width&amp;quot; to 6 px.
&lt;br /&gt;&lt;br /&gt;
&lt;img alt="Set border width" src="http://i626.photobucket.com/albums/tt344/tadeboro/2009-09-05-105502.png" /&gt;
&lt;br /&gt;&lt;br /&gt;
Last thing we need to do with main window is to connect &lt;span
class="cf"&gt;gtk_main_quit&lt;/span&gt; function to &amp;quot;destroy&amp;quot; signal. This
will close our application when we'll click on close button. I'll talk more
about signals in one of the following posts, so no more details will be given
here.
&lt;br /&gt;&lt;br /&gt;
&lt;img alt="Connect gtk_main_quit() to destroy signal" src="http://i626.photobucket.com/albums/tt344/tadeboro/2009-09-05-105523.png" /&gt;
&lt;br /&gt;&lt;br /&gt;
With our main window finished, we need to add GtkVBox to it. In previous post,
we planed to add four widgets inside this box, but since menu bar and tool bar
will be created using GtkUIManager and manually inserted from code, we only need
two. So when we're asked about number of elements, we enter 2.
&lt;br /&gt;&lt;br /&gt;
&lt;img alt="Creating main vbox" src="http://i626.photobucket.com/albums/tt344/tadeboro/2009-09-05-105626.png" /&gt;
&lt;br /&gt;&lt;br /&gt;
Now we'll add status bar to application. Click status bar icon and insert it
into bottom of the box. Your GUI should look something like this:
&lt;br /&gt;&lt;br /&gt;
&lt;img alt="Inserted status bar" src="http://i626.photobucket.com/albums/tt344/tadeboro/2009-09-05-105751.png" /&gt;
&lt;br /&gt;&lt;br /&gt;
Now we need to make sure that status bar is packed from bottom to top. Why is
this important? If we would to pack status bar from top to bottom, it would be
impossible to add menu and tool bar at the top of the application. How to ensure
that status bar is packed from bottom to top? Select &amp;quot;Packing tab&amp;quot; and
select &amp;quot;End&amp;quot; as pack type and set &amp;quot;Position&amp;quot; to 0. What does
this do? It instructs Glade to pack status bar as first element from bottom to
top.
&lt;br /&gt;&lt;br /&gt;
&lt;img alt="Selecting pack type" src="http://i626.photobucket.com/albums/tt344/tadeboro/2009-09-06-102929.png" /&gt;
&lt;br /&gt;&lt;br /&gt;
Next is horizontal box that will hold three widgets. Creating one is exactly the
same as creating vertical box. Don't forget to adjust pack type of this box to
&amp;quot;End&amp;quot; too!!!
&lt;br /&gt;&lt;br /&gt;
&lt;img alt="Creating horizontal box" src="http://i626.photobucket.com/albums/tt344/tadeboro/2009-09-06-103242.png" /&gt;
&lt;br /&gt;&lt;br /&gt;
According to our widget tree, we need to insert scrolled window into first
compartment. And since we only want to have vertical scrollbar present when
needed, we need to adjust horizontal scrollbar policy too.
&lt;br /&gt;&lt;br /&gt;
&lt;img alt="Inserting scrolled window" src="http://i626.photobucket.com/albums/tt344/tadeboro/2009-09-06-103516.png" /&gt;
&lt;br /&gt;&lt;br /&gt;
Inserting tree view into scrolled window is easy. Click tree view icon and
insert it into scrolled window. When asked about TreeView model, just leave
field empty and click OK (we'll be dealing with this when we'll add data storage
to our application).
&lt;br /&gt;&lt;br /&gt;
&lt;img alt="Inserting tree view" src="http://i626.photobucket.com/albums/tt344/tadeboro/2009-09-06-103814.png" /&gt;
&lt;br /&gt;&lt;br /&gt;
Inside second field of horizontal box goes vertical button box that will hold
buttons for modifying data inside tree view. We'll need six buttons inside this
box: &amp;quot;move to top&amp;quot;, &amp;quot;move up&amp;quot;, &amp;quot;add&amp;quot;,
&amp;quot;remove&amp;quot;, &amp;quot;mode down&amp;quot; and &amp;quot;move to bottom&amp;quot;. So
when we're asked about number of fields when creating vertical button box, we
enter 6 and click OK. Next, we need to modify some of the properties of the
button box. We'll set &amp;quot;Layout style&amp;quot; to &amp;quot;Center&amp;quot;,
&amp;quot;Spacing&amp;quot; to 2 and &amp;quot;Homogeneous&amp;quot; to &amp;quot;Yes&amp;quot;.
&lt;br /&gt;&lt;br /&gt;
&lt;img alt="Creating vertical button box" src="http://i626.photobucket.com/albums/tt344/tadeboro/2009-09-06-104311.png" /&gt;
&lt;br /&gt;&lt;br /&gt;
Under &amp;quot;Packing&amp;quot; tab, we set the &amp;quot;Expand&amp;quot; property to
&amp;quot;No&amp;quot;, since we want buttons to take as little space as possible.
&lt;br /&gt;&lt;br /&gt;
&lt;img alt="Modifying vertical button box properties" src="http://i626.photobucket.com/albums/tt344/tadeboro/2009-09-06-104801.png" /&gt;
&lt;br /&gt;&lt;br /&gt;
Creating buttons is simple. Just click button icon and place it onto proper
space. Now under &amp;quot;General&amp;quot; tab search for &amp;quot;Stock button&amp;quot; and
set it to &amp;quot;Top&amp;quot; (Glade will convert icon you select to stock item name
automatically, so don't worry about what is written in Stock Button field).
&lt;br /&gt;&lt;br /&gt;
&lt;img alt="Creating top button" src="http://i626.photobucket.com/albums/tt344/tadeboro/2009-09-06-105439.png" /&gt;
&lt;br /&gt;&lt;br /&gt;
Using exactly the same steps create another five buttons. Icons for them should
be &amp;quot;Up&amp;quot;, &amp;quot;Add&amp;quot;, &amp;quot;Remove&amp;quot;, &amp;quot;Down&amp;quot; and
&amp;quot;Bottom&amp;quot;.
&lt;br /&gt;&lt;br /&gt;
&lt;img alt="All buttons set up" src="http://i626.photobucket.com/albums/tt344/tadeboro/2009-09-06-110127.png" /&gt;
&lt;br /&gt;&lt;br /&gt;
Last empty field in horizontal box should be, according to our widget tree,
filled with vertical box that will house two children widgets. End result should
look something like this:
&lt;br /&gt;&lt;br /&gt;
&lt;img alt="Add vertical box" src="http://i626.photobucket.com/albums/tt344/tadeboro/2009-09-06-110421.png" /&gt;
&lt;br /&gt;&lt;br /&gt;
Inside bottom compartment, we need to add horizontal button box for two toggle
buttons. We'll also set &amp;quot;Layout style&amp;quot; to &amp;quot;Center&amp;quot;,
&amp;quot;Spacing&amp;quot; to 2 and &amp;quot;Homogeneous&amp;quot; to &amp;quot;Yes&amp;quot;.
&lt;br /&gt;&lt;br /&gt;
&lt;img alt="Creating horizontal button box" src="http://i626.photobucket.com/albums/tt344/tadeboro/2009-09-06-110831.png" /&gt;
&lt;br /&gt;&lt;br /&gt;
Under packing, set &amp;quot;Expand&amp;quot; property to &amp;quot;No&amp;quot;, since we want
this section to take as little space as possible.
&lt;br /&gt;&lt;br /&gt;
&lt;img alt="Set expand to No" src="http://i626.photobucket.com/albums/tt344/tadeboro/2009-09-06-110847.png" /&gt;
&lt;br /&gt;&lt;br /&gt;
Adding buttons to this box is very similar to adding them to vertical box. But
we'll add check buttons here instead of normal ones. Simply click Check button
icon and insert it into empty space inside button box. After that, change the
button's label to &amp;quot;Show points&amp;quot;. Repeat those steps to create second
check button and change label to &amp;quot;Show lines&amp;quot;.
&lt;br /&gt;&lt;br /&gt;
&lt;img alt="Insert check buttons" src="http://i626.photobucket.com/albums/tt344/tadeboro/2009-09-06-110945.png" /&gt;
&lt;br /&gt;&lt;br /&gt;
Upper part of the vertical box will house scrolled window. Add it exactly the
same as we added the one for data part. Inside this scrolled window we need to
insert viewport.
&lt;br /&gt;&lt;br /&gt;
&lt;img alt="Inserting viewport" src="http://i626.photobucket.com/albums/tt344/tadeboro/2009-09-06-111436.png" /&gt;
&lt;br /&gt;&lt;br /&gt;
Next in widget tree is alignment which will take care of centering the drawing
area. Creating one is simple: just click Alignment icon and insert it into
viewport. We also need to modify horizontal and vertical scale values. Those two
control the expansion of the child widget. Value 1 means "Expand child as much
as possible", 0 means "Do not expand child at all" while 0.4 means "Expand child
to take 40% of allocated size". We'll set both values to 0, since we want our
child to be constant in size.
&lt;br /&gt;&lt;br /&gt;
&lt;img alt="Inserting alignment" src="http://i626.photobucket.com/albums/tt344/tadeboro/2009-09-06-112329.png" /&gt;
&lt;br /&gt;&lt;br /&gt;
Inserting frame takes some work in our case, since Glade3 does some things by
default that we don't need. Click frame icon and insert it into alignment.
Change &amp;quot;Frame shadow&amp;quot; to &amp;quot;In&amp;quot; and you should end up with
something like this:
&lt;br /&gt;&lt;br /&gt;
&lt;img alt="Inserted frame" src="http://i626.photobucket.com/albums/tt344/tadeboro/2009-09-06-121201.png" /&gt;
&lt;br /&gt;&lt;br /&gt;
You can see that Glade automatically added label and alignment into frame. We
don't need any, so we'll delete them both. This is how things look after
removal:
&lt;br /&gt;&lt;br /&gt;
&lt;img alt="Frame stripped" src="http://i626.photobucket.com/albums/tt344/tadeboro/2009-09-06-123352.png" /&gt;
&lt;br /&gt;&lt;br /&gt;
And we are left with only one widget to pack: drawing area. Click icon and
insert it into bottom compartment of frame (upper one will be left empty, since
we don't want any label). Under &amp;quot;Common&amp;quot;, set widget's size request to 300 x 200
px and we're done.
&lt;br /&gt;&lt;br /&gt;
&lt;img alt="Inserting drawing area" src="http://i626.photobucket.com/albums/tt344/tadeboro/2009-09-06-123804.png" /&gt;
&lt;br /&gt;&lt;br /&gt;
There is only one thing left to do for today: save and test this GUI. We'll
write really simple C application for this task. I won't explain much about code
today, since we'll be dealing with coding in later posts, but most of the code
should be easy to understand.
&lt;pre class="brush: c"&gt;
/*
 * Compile me with:
 *   gcc -o tut tut.c $(pkg-config --cflags --libs gtk+-2.0 gmodule-2.0)
 */

#include &amp;lt;gtk/gtk.h&amp;gt;

int
main( int    argc,
      char **argv )
{
    GtkBuilder *builder;
    GtkWidget  *window;
    GError     *error = NULL;

    /* Init GTK+ */
    gtk_init( &amp;amp;argc, &amp;amp;argv );

    /* Create new GtkBuilder object */
    builder = gtk_builder_new();
    /* Load UI from file. If error occurs, report it and quit application.
     * Replace &amp;quot;tut.glade&amp;quot; with your saved project. */
    if( ! gtk_builder_add_from_file( builder, &amp;quot;tut.glade&amp;quot;, &amp;amp;error ) )
    {
        g_warning( &amp;quot;%s&amp;quot;, error-&amp;gt;message );
        g_free( error );
        return( 1 );
    }

    /* Get main window pointer from UI */
    window = GTK_WIDGET( gtk_builder_get_object( builder, &amp;quot;window1&amp;quot; ) );

    /* Connect signals */
    gtk_builder_connect_signals( builder, NULL );

    /* Destroy builder, since we don't need it anymore */
    g_object_unref( G_OBJECT( builder ) );

    /* Show window. All other widgets are automatically shown by GtkBuilder */
    gtk_widget_show( window );

    /* Start main loop */
    gtk_main();

    return( 0 );
}
&lt;/pre&gt;
Now run this code and be amazed;). Not too bad, but nothing special either. Try
resizing window to see how compartments behave when resized. Do you like it? I
don't either, so join me next time when we'll be dealing with space
alocation/requisition in detail.
&lt;br /&gt;&lt;br /&gt;
And we're done. If you need more detailed process of clicking/changing
properties, head to &lt;a href="http://www.screentoaster.com/watch/stU0tWQk1IR1xdQ1xYUlJZVVFT/glade3_tutorial_creating_gui"&gt;ScreenToaster site&lt;/a&gt;, where you can watch
video of the whole thing. Hope you'll find it useful.
&lt;br /&gt;&lt;br /&gt;
Finished glade file can be obtained from here: &lt;a
href="http://tadeboro.googlepages.com/tut.glade"&gt;tut.glade&lt;/a&gt;.
&lt;br /&gt;&lt;br /&gt;
Stay healthy.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6666239544818473000-3599535235205703057?l=blog.borovsak.si' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.borovsak.si/feeds/3599535235205703057/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.borovsak.si/2009/09/glade3-tutorial-2-constructing.html#comment-form' title='65 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6666239544818473000/posts/default/3599535235205703057'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6666239544818473000/posts/default/3599535235205703057'/><link rel='alternate' type='text/html' href='http://blog.borovsak.si/2009/09/glade3-tutorial-2-constructing.html' title='Glade3 tutorial (2) - Constructing interface'/><author><name>Tadej Borovšak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-V5S0cD-5b_I/AAAAAAAAAAI/AAAAAAAAArU/nAyUYzn6Yx8/s512-c/photo.jpg'/></author><thr:total>65</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6666239544818473000.post-120367676744621831</id><published>2009-09-04T21:54:00.012+02:00</published><updated>2009-09-27T19:28:16.751+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='tutorial'/><category scheme='http://www.blogger.com/atom/ns#' term='GTK+'/><category scheme='http://www.blogger.com/atom/ns#' term='Glade'/><title type='text'>Glade3 tutorial (1) - Introduction</title><content type='html'>Hello again.
&lt;br /&gt;&lt;br /&gt;
In next series of posts I'll try to show you some of the techniques I personally
use when developing applications using Glade3.
&lt;br /&gt;&lt;br /&gt;
&lt;span class="s_title"&gt;What are we going to code&lt;/span&gt;
&lt;br /&gt;&lt;br /&gt;
Our sample application will be simple, but fully functional graph viewer and
editor. Things that I'll be demonstrating through this sample application are:
&lt;ul&gt;
  &lt;li&gt;How to approach to new project that will use Glade3 as GUI designer.&lt;/li&gt;
  &lt;li&gt;How to use Glade3 to create visual elements of application.&lt;/li&gt;
  &lt;li&gt;How to use Glade3 to create data storage objects for application.&lt;/li&gt;
  &lt;li&gt;How to connect callback functions to widgets' signals.&lt;/li&gt;
  &lt;li&gt;How to draw onto widgets using Cairo graphics library.&lt;/li&gt;
  &lt;li&gt;How to use Pango from Cairo using pangocairo.&lt;/li&gt;
  &lt;li&gt;How to create action-based menus and toolbar.&lt;/li&gt;
&lt;/ul&gt;
I may add other stuff to this list if interest/need arises.
&lt;br /&gt;&lt;br /&gt;
&lt;span class="s_title"&gt;Who may be interested in this series of posts?&lt;/span&gt;
&lt;br /&gt;&lt;br /&gt;
I'll do my best to write this tutorial "newcomer to GTK+" friendly and try to
explain almost everything I'll do. Experienced users may die from boredom when
reading a section explaining some fundamental thing, so please, if youre GTK+
guru, read this series with caution;).
&lt;br /&gt;&lt;br /&gt;
&lt;span class="s_title"&gt;Contents&lt;/span&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/09/glade3-tutorial-1-introduction.html"&gt;Glade3 tutorial (1) - Introduction&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/09/glade3-tutorial-2-constructing.html"&gt;Glade3 tutorial (2) - Constructing interface&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/09/glade3-tutorial-3-size-negotiation.html"&gt;Glade3 tutorial (3) - Size negotiation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/09/glade3-tutorial-4-gtktreeview-data.html"&gt;Glade3 tutorial (4) - GtkTreeView data backend&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/09/glade3-tutorial-5-modifying-widget-tree.html"&gt;Glade3 tutorial (5) - Modifying widget tree&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/09/glade3-tutorial-6-signals.html"&gt;Glade3 tutorial (6) - Signals&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;span class="s_title"&gt;First steps&lt;/span&gt;
&lt;br /&gt;&lt;br /&gt;
OK, since it looks like you gave me a chance to show how I usually work, let's
start.
&lt;br /&gt;&lt;br /&gt;
Today's work will be separated into two sections:
&lt;ol&gt;
  &lt;li&gt;Creating "blueprint" of our application's GUI based on requirements.&lt;/li&gt;
  &lt;li&gt;Writing down widget tree, based on blueprint from previous section.&lt;/li&gt;
&lt;/ol&gt;
Now, what are requirements for our application. Obviously, it needs to be able
to display data in numeric and graphic form. We also want to be able to add new,
delete, rearrange and modify points. We also want to be able to display point
markers, connecting lines or both on the chart. And this is about it for initial
requirements. We'll leave some space for future expansion in our plans, just in
case if we decide to add anything to it later.
&lt;br /&gt;&lt;br /&gt;
Now comes the fun part - drawing interface. I prefer to do this on paper using
pencil, but feel free to experiment. For sample project, I came up with this
design:
&lt;br /&gt;&lt;br /&gt;
&lt;img src="http://img525.imageshack.us/img525/1571/blueprint.png"
     alt="Mock-up of interface" /&gt;
&lt;br /&gt;&lt;br /&gt;
Dark gray rectangles represent scroll bars. What do you think? Do I suck as
interface designer or do I really suck as interface designer?;)
&lt;br /&gt;&lt;br /&gt;
Now for the last thing we need to do today: writing down widget tree. In GTK+,
everything starts with top-level &lt;a
href="http://library.gnome.org/devel/gtk/stable/GtkWindow.html"&gt;GtkWindow&lt;/a&gt;,
which will serve as a root of our widget tree.
&lt;br /&gt;&lt;br /&gt;
Our main window will be split into four vertical sections: one for menu bar, one
for tool bar, one for central part where all the action will happen and last one
for status bar. Since our main window (&lt;a
href="http://library.gnome.org/devel/gtk/stable/GtkWindow.html"&gt;GtkWindow&lt;/a&gt;)
can hold only one child widget, we need &lt;a
href="http://library.gnome.org/devel/gtk/stable/GtkVBox.html"&gt;GtkVBox&lt;/a&gt; in
which we will pack, from bottom to top: &lt;a
href="http://library.gnome.org/devel/gtk/stable/GtkStatusbar.html"&gt;GtkStatusbar&lt;/a&gt;,
widget for central part, &lt;a
href="http://library.gnome.org/devel/gtk/stable/GtkToolbar.html"&gt;GtkToolbar&lt;/a&gt;
and &lt;a
href="http://library.gnome.org/devel/gtk/stable/GtkMenuBar.html"&gt;GtkMenuBar&lt;/a&gt;.
Why did I say that we'll be packing from bottom? I'll explain this in next post,
when we'll be playing with Glade3.
&lt;br /&gt;&lt;br /&gt;
Central part will need to be further divided into horizontal sections: one for
data table, one for point controls and one for display area. So this will
require &lt;a
href="http://library.gnome.org/devel/gtk/stable/GtkHBox.html"&gt;GtkHBox&lt;/a&gt;. What
widgets will we need inside it? For data table we'll use &lt;a
href="http://library.gnome.org/devel/gtk/stable/GtkTreeView.html"&gt;GtkTreeView&lt;/a&gt;,
which is packed inside &lt;a
href="http://library.gnome.org/devel/gtk/stable/GtkScrolledWindow.html"&gt;GtkScrolledWindow&lt;/a&gt;
to enable scrolling. For point controls we'll need &lt;a
href="http://library.gnome.org/devel/gtk/stable/GtkVButtonBox.html"&gt;GtkVButtonBox&lt;/a&gt;
that will house our buttons.
&lt;br /&gt;&lt;br /&gt;
Now for the display area. We again have two parts: upper part that will actually
display chart; and bottom part that will hold check buttons. So we'll need
another &lt;a
href="http://library.gnome.org/devel/gtk/stable/GtkVBox.html"&gt;GtkVBox&lt;/a&gt; to
hold the whole section. Bottom part will be represented by &lt;a
href="http://library.gnome.org/devel/gtk/stable/GtkHButtonBox.html"&gt;GtkHButtonBox&lt;/a&gt;
with GtkToggleButtons, while upper part deserves another paragraph;).
&lt;br /&gt;&lt;br /&gt;
Why another paragraph? Because we'll need to add quite a few widgets in order to
get desired layout. As you can see on my drawing, I want to have graph area
centered in display part. In order to be able to add zoom to chart area, we also
need some kind of scrolling widget. And how to assemble all this together? First
we'll add &lt;a
href="http://library.gnome.org/devel/gtk/stable/GtkScrolledWindow.html"&gt;GtkScrolledWindow&lt;/a&gt;
to &lt;a href="http://library.gnome.org/devel/gtk/stable/GtkVBox.html"&gt;GtkVBox&lt;/a&gt;
from previous paragraph. To make contents scrollable, we need to pack &lt;a
href="http://library.gnome.org/devel/gtk/stable/GtkViewport.html"&gt;GtkViewport&lt;/a&gt;
inside &lt;a
href="http://library.gnome.org/devel/gtk/stable/GtkScrolledWindow.html"&gt;GtkScrolledWindow&lt;/a&gt;.
Inside &lt;a
href="http://library.gnome.org/devel/gtk/stable/GtkViewport.html"&gt;GtkViewport&lt;/a&gt;
we'll add &lt;a
href="http://library.gnome.org/devel/gtk/stable/GtkAlignment.html"&gt;GtkAlignment&lt;/a&gt;,
which will take care of centering the chart area. Inside &lt;a
href="http://library.gnome.org/devel/gtk/stable/GtkAlignment.html"&gt;GtkAlignment&lt;/a&gt;
we'll add &lt;a
href="http://library.gnome.org/devel/gtk/stable/GtkFrame.html"&gt;GtkFrame&lt;/a&gt;,
which will add a shadow to chart area. Finally, we add GtkDrawing area inside &lt;a
href="http://library.gnome.org/devel/gtk/stable/GtkFrame.html"&gt;GtkFrame&lt;/a&gt;. And
we're done.
&lt;br /&gt;&lt;br /&gt;
If we convert this verbose description into tree-like representation, we get
this:
&lt;pre&gt;GtkWindow
 `- GtkVBox
     +- GtkMenuBar
     |   `- /* Menus here */
     +- GtkToolbar
     |   `- /* Tool buttons here */
     +- GtkHBox
     |   +- GtkScrolledWindow
     |   |   `- GtkTreeView
     |   +- GtkVButtonBox
     |   |   `- /* Buttons here */
     |   +- GtkVBox
     |       +- GtkScrolledWindow
     |       |   `- GtkViewport
     |       |       `- GtkAlignment
     |       |           `- GtkFrame
     |       |               `- GtkDrawingArea
     |       `- GtkHButtonBox
     |           `- /* Check buttons here */
     `- GtkStatusBar
&lt;/pre&gt;
There is just one thing I would like to explain today. When adding &lt;a
href="http://library.gnome.org/devel/gtk/stable/GtkTreeView.html"&gt;GtkTreeView&lt;/a&gt;
to &lt;a
href="http://library.gnome.org/devel/gtk/stable/GtkScrolledWindow.html"&gt;GtkScrolledWindow&lt;/a&gt;
I haven't used &lt;a
href="http://library.gnome.org/devel/gtk/stable/GtkViewport.html"&gt;GtkViewport&lt;/a&gt;
as an adapter widget, while adding &lt;a
href="http://library.gnome.org/devel/gtk/stable/GtkAlignment.html"&gt;GtkAlignment&lt;/a&gt;
did require one. Why? When it comes to &lt;a
href="http://library.gnome.org/devel/gtk/stable/GtkScrolledWindow.html"&gt;GtkScrolledWindow&lt;/a&gt;,
there are two kinds of widgets: the ones that support scrolling natively and the
ones that don't. &lt;a
href="http://library.gnome.org/devel/gtk/stable/GtkTreeView.html"&gt;GtkTreeView&lt;/a&gt;,
&lt;a
href="http://library.gnome.org/devel/gtk/stable/GtkTextView.html"&gt;GtkTextView&lt;/a&gt;,
&lt;a
href="http://library.gnome.org/devel/gtk/stable/GtkIconView.html"&gt;GtkIconView&lt;/a&gt;
and &lt;a
href="http://library.gnome.org/devel/gtk/stable/GtkViewport.html"&gt;GtkViewport&lt;/a&gt;
do support scrolling and can be added into &lt;a
href="http://library.gnome.org/devel/gtk/stable/GtkScrolledWindow.html"&gt;GtkScrolledWindow&lt;/a&gt;
directly. All other widgets need &lt;a
href="http://library.gnome.org/devel/gtk/stable/GtkViewport.html"&gt;GtkViewport&lt;/a&gt;
as an adapter.
&lt;br /&gt;&lt;br /&gt;
I hope this starting lesson wasn't too tough and you'll join me next time when
we'll fire up Glade3 and do some property mangling.
&lt;br /&gt;&lt;br /&gt;
Stay healthy.
&lt;br /&gt;&lt;br /&gt;
Bye.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6666239544818473000-120367676744621831?l=blog.borovsak.si' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.borovsak.si/feeds/120367676744621831/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.borovsak.si/2009/09/glade3-tutorial-1-introduction.html#comment-form' title='13 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6666239544818473000/posts/default/120367676744621831'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6666239544818473000/posts/default/120367676744621831'/><link rel='alternate' type='text/html' href='http://blog.borovsak.si/2009/09/glade3-tutorial-1-introduction.html' title='Glade3 tutorial (1) - Introduction'/><author><name>Tadej Borovšak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-V5S0cD-5b_I/AAAAAAAAAAI/AAAAAAAAArU/nAyUYzn6Yx8/s512-c/photo.jpg'/></author><thr:total>13</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6666239544818473000.post-3774070665808033998</id><published>2009-07-19T20:40:00.003+02:00</published><updated>2009-07-19T20:44:09.771+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='GTK+'/><category scheme='http://www.blogger.com/atom/ns#' term='Glade'/><title type='text'>Glade, Gazpacho, Stetic, libglade, GtkBuilder ...</title><content type='html'>... and a generous amount of tequilla;) No, this is not a recipe for some crazy
cocktail, these are the ingredients for building GTK+ GUIs. Don't belive me? Read
on.
&lt;br /&gt;&lt;br /&gt;
&lt;span class="s_title"&gt;Glade2&lt;/span&gt;
&lt;br /&gt;&lt;br /&gt;
This is the first GTK+ GUI builder that I know of. When user creates GUI for
his application, glade2 &amp;quot;translates&amp;quot; this interface into source code.
All user has to now is write callback functions for connected signals and
compile application.
&lt;br /&gt;&lt;br /&gt;
This method of creating GUIs is simple and efficient, since no run-time effort is
needed to display application. But the price one has to pay for this efficiency
is quite high: modifying application is a logistical nightmare. To avoid this
drawback, another generation of GUI builders has been developed.
&lt;br /&gt;&lt;br /&gt;
&lt;span class="s_title"&gt;Glade3, Gazpacho, Stetic&lt;/span&gt;
&lt;br /&gt;&lt;br /&gt;
Those applications take a different approach to creating GUIs. Instead of
translating GUI definition into source code, they write interface file.
&lt;br /&gt;&lt;br /&gt;
Interface file holds instructions on how to build GUI. This means that
application needs to parse this file at startup and build GUI according to the
instructions. Bad side of this approach is somewhat slower start of the
application, but it's easily outweighed by the fact that applications are now
much easier to maintain and update as far as the GUI part goes (small
adjustments of GUI that don't involve functionality change can be even done
without the need to recompile application).
&lt;br /&gt;&lt;br /&gt;
&lt;span class="s_title"&gt;libglade and GtkBuilder&lt;/span&gt;
&lt;br /&gt;&lt;br /&gt;
Say hello to interface builders. These are the real workers that read
instructions and build your GUI. libglade is the older of the two and is
distributed as separate library. It is being deprecated in favour of GtkBuilder
lately. GtkBuilder is part of the GTK+ since version 2.12.
&lt;br /&gt;&lt;br /&gt;
&lt;span class="s_title"&gt;Putting it all together&lt;/span&gt;
&lt;br /&gt;&lt;br /&gt;
Now that we're familiar with all of the elements, we can describe four ways of
creating application's GUI.
&lt;ol&gt;
  &lt;li&gt;Create GUI using Glade2 and produce source code. (deprecated)&lt;/li&gt;
  &lt;li&gt;Create GUI using Glade3, Gazpacho or Stetic and save it into glade
   XML file. Use libglade to build GUI. (slowly being deprecated)&lt;/li&gt;
  &lt;li&gt;Create GUI using Glade3, Gazpacho or Stetic and save it into glade
   XML file. Convert glade XML to builder XML using gtk-builder-convert
   script. Build your GUI by using GtkBuilder.&lt;/li&gt;
  &lt;li&gt;Create GUI using Glade &gt;= 3.6 and save it into builder XML file. Use
   GtkBuilder to build your GUI.&lt;/li&gt;
&lt;/ol&gt;
All four ways are graphically represented on this image:
&lt;a href="http://img198.imageshack.us/img198/8213/schemec.png"&gt;&lt;img src="http://img41.imageshack.us/img41/2971/scheme.png" /&gt;&lt;/a&gt;
&lt;br /&gt;&lt;br /&gt;
Stay healthy.
&lt;br /&gt;&lt;br /&gt;
Bye&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6666239544818473000-3774070665808033998?l=blog.borovsak.si' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.borovsak.si/feeds/3774070665808033998/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.borovsak.si/2009/07/glade-gazpacho-stetic-libglade.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6666239544818473000/posts/default/3774070665808033998'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6666239544818473000/posts/default/3774070665808033998'/><link rel='alternate' type='text/html' href='http://blog.borovsak.si/2009/07/glade-gazpacho-stetic-libglade.html' title='Glade, Gazpacho, Stetic, libglade, GtkBuilder ...'/><author><name>Tadej Borovšak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-V5S0cD-5b_I/AAAAAAAAAAI/AAAAAAAAArU/nAyUYzn6Yx8/s512-c/photo.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6666239544818473000.post-7479159502053378678</id><published>2009-07-15T22:45:00.001+02:00</published><updated>2009-07-15T22:52:37.716+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='glib'/><category scheme='http://www.blogger.com/atom/ns#' term='GTK+'/><title type='text'>Spawning processes using glib</title><content type='html'>Hello.
&lt;br /&gt;&lt;br /&gt;
It's been almost a month since my last post, but here I am with a new post. And
I'll be talking about spawning child processes from callback functions and how
to display their output in our main GUI.
&lt;br /&gt;&lt;br /&gt;
&lt;span class="s_title"&gt;Spawning process&lt;/span&gt;
&lt;br /&gt;&lt;br /&gt;
If we want to spawn child process and avoid freezing our GUI, we need to do this
asynchronously, which means that we don't wait for child process to return
before we resume with our work.
&lt;br /&gt;&lt;br /&gt;
Glib offers quite a few functions that help us with this task, but we'll focus
on most powerful one: &lt;a
href="http://library.gnome.org/devel/glib/stable/glib-Spawning-Processes.html#g-spawn-async-with-pipes"&gt;g_spawn_async_with_pipes&lt;/a&gt;.
If you look at the API documentation, you can see that this function takes quite
a few arguments. Meaning of each argument is nicely described in official docs,
so I won't duplicate the effort here. Inspect this invocation from our sample
code and try to determine what exactly each parameter does.
&lt;pre class="brush: c"&gt;
    ret = g_spawn_async_with_pipes( NULL, argv, NULL,
                                    G_SPAWN_DO_NOT_REAP_CHILD, NULL,
                                    NULL, &amp;amp;pid, NULL, &amp;amp;out, &amp;amp;err, NULL );
    if( ! ret )
    {
        g_error( &amp;quot;SPAWN FAILED&amp;quot; );
        return;
    }

    /* Add watch function to catch termination of the process. This function
     * will clean any remnants of process. */
    g_child_watch_add( pid, (GChildWatchFunc)cb_child_watch, data );
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;span class="s_title"&gt;Reading output&lt;/span&gt;
&lt;br /&gt;&lt;br /&gt;
Now we would like to read whatever our spawned process outputs. First step
towards this was made when we spawned process and specified locations for
standard output and error file descriptors. We can now read data directly from
those file descriptors using standard functions like read, but those functions
would block the application flow until something is sent down the pipe.
&lt;br /&gt;&lt;br /&gt;
To avoid that, we'll use &lt;a
href="http://library.gnome.org/devel/glib/stable/glib-IO-Channels.html"&gt;GIOChannels&lt;/a&gt;
and only read data from file descriptor on demand. The following code snippet
shows how channels should be created in platform-independent way.
&lt;pre class="brush: c"&gt;
    /* Create channels that will be used to read data from pipes. */
#ifdef G_OS_WIN32
    out_ch = g_io_channel_win32_new_fd( out );
    err_ch = g_io_channel_win32_new_fd( err );
#else
    out_ch = g_io_channel_unix_new( out );
    err_ch = g_io_channel_unix_new( err );
#endif

    /* Add watches to channels */
    g_io_add_watch( out_ch, G_IO_IN | G_IO_HUP, (GIOFunc)cb_out_watch, data );
    g_io_add_watch( err_ch, G_IO_IN | G_IO_HUP, (GIOFunc)cb_err_watch, data );
&lt;/pre&gt;
What exactly those watches do? We instruct glib to monitor our channel and
inform us when either some data has been sent down the pipe or pipe has been
broken.
&lt;br /&gt;&lt;br /&gt;
&lt;span class="s_title"&gt;Putting it all together&lt;/span&gt;
&lt;br /&gt;&lt;br /&gt;
To show you how things are set-up in real world application, I created a sample
application that reads output from child process and displays it inside
GtkTextView. Feel free to experiment.
&lt;br /&gt;&lt;br /&gt;
And this is it for today. Stay healthy and keep of the sun. Bye.
&lt;br /&gt;&lt;br /&gt;
&lt;pre class="brush: c"&gt;
/*
 * Compile me with:
 *   gcc -o spawn spawn.c $(pkg-config --cflags --libs gtk+-2.0)
 */
#include &amp;lt;gtk/gtk.h&amp;gt;

typedef struct _Data Data;
struct _Data
{
    /* Buffers that will display output */
    GtkTextBuffer *out;
    GtkTextBuffer *err;

    /* Progress bar that will be updated */
    GtkProgressBar *progress;

    /* Timeout source id */
    gint timeout_id;
};

static void
cb_child_watch( GPid  pid,
                gint  status,
                Data *data )
{
    /* Remove timeout callback */
    g_source_remove( data-&amp;gt;timeout_id );

    /* Close pid */
    g_spawn_close_pid( pid );
}

static gboolean
cb_out_watch( GIOChannel   *channel,
              GIOCondition  cond,
              Data         *data )
{
    gchar *string;
    gsize  size;

    if( cond == G_IO_HUP )
    {
        g_io_channel_unref( channel );
        return( FALSE );
    }

    g_io_channel_read_line( channel, &amp;amp;string, &amp;amp;size, NULL, NULL );
    gtk_text_buffer_insert_at_cursor( data-&amp;gt;out, string, -1 );
    g_free( string );

    return( TRUE );
}

static gboolean
cb_err_watch( GIOChannel   *channel,
              GIOCondition  cond,
              Data         *data )
{
    gchar *string;
    gsize  size;

    if( cond == G_IO_HUP )
    {
        g_io_channel_unref( channel );
        return( FALSE );
    }

    g_io_channel_read_line( channel, &amp;amp;string, &amp;amp;size, NULL, NULL );
    gtk_text_buffer_insert_at_cursor( data-&amp;gt;err, string, -1 );
    g_free( string );

    return( TRUE );
}

static gboolean
cb_timeout( Data *data )
{
    /* Bounce progress bar */
    gtk_progress_bar_pulse( data-&amp;gt;progress );

    return( TRUE );
}

static void
cb_execute( GtkButton *button,
            Data      *data )
{
    GPid        pid;
    gchar      *argv[] = { &amp;quot;./helper&amp;quot;, NULL };
    gint        out,
                err;
    GIOChannel *out_ch,
               *err_ch;
    gboolean    ret;

    /* Spawn child process */
    ret = g_spawn_async_with_pipes( NULL, argv, NULL,
                                    G_SPAWN_DO_NOT_REAP_CHILD, NULL,
                                    NULL, &amp;amp;pid, NULL, &amp;amp;out, &amp;amp;err, NULL );
    if( ! ret )
    {
        g_error( &amp;quot;SPAWN FAILED&amp;quot; );
        return;
    }

    /* Add watch function to catch termination of the process. This function
     * will clean any remnants of process. */
    g_child_watch_add( pid, (GChildWatchFunc)cb_child_watch, data );

    /* Create channels that will be used to read data from pipes. */
#ifdef G_OS_WIN32
    out_ch = g_io_channel_win32_new_fd( out );
    err_ch = g_io_channel_win32_new_fd( err );
#else
    out_ch = g_io_channel_unix_new( out );
    err_ch = g_io_channel_unix_new( err );
#endif

    /* Add watches to channels */
    g_io_add_watch( out_ch, G_IO_IN | G_IO_HUP, (GIOFunc)cb_out_watch, data );
    g_io_add_watch( err_ch, G_IO_IN | G_IO_HUP, (GIOFunc)cb_err_watch, data );

    /* Install timeout fnction that will move the progress bar */
    data-&amp;gt;timeout_id = g_timeout_add( 100, (GSourceFunc)cb_timeout, data );
}

int
main( int    argc,
      char **argv )
{
    GtkWidget *window,
              *table,
              *button,
              *progress,
              *text;
    Data      *data;

    data = g_slice_new( Data );

    gtk_init( &amp;amp;argc, &amp;amp;argv );

    window = gtk_window_new( GTK_WINDOW_TOPLEVEL );
    gtk_window_set_default_size( GTK_WINDOW( window ), 400, 300 );
    g_signal_connect( G_OBJECT( window ), &amp;quot;destroy&amp;quot;,
                      G_CALLBACK( gtk_main_quit ), NULL );

    table = gtk_table_new( 2, 2, FALSE );
    gtk_table_set_row_spacings( GTK_TABLE( table ), 6 );
    gtk_table_set_col_spacings( GTK_TABLE( table ), 6 );
    gtk_container_add( GTK_CONTAINER( window ), table );

    button = gtk_button_new_from_stock( GTK_STOCK_EXECUTE );
    g_signal_connect( G_OBJECT( button ), &amp;quot;clicked&amp;quot;,
                      G_CALLBACK( cb_execute ), data );
    gtk_table_attach( GTK_TABLE( table ), button, 0, 1, 0, 1,
                      GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0 );

    progress = gtk_progress_bar_new();
    data-&amp;gt;progress = GTK_PROGRESS_BAR( progress );
    gtk_table_attach( GTK_TABLE( table ), progress, 1, 2, 0, 1,
                      GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0 );

    text = gtk_text_view_new();
    data-&amp;gt;out = gtk_text_view_get_buffer( GTK_TEXT_VIEW( text ) );
    gtk_table_attach( GTK_TABLE( table ), text, 0, 1, 1, 2,
                      GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0 );

    text = gtk_text_view_new();
    data-&amp;gt;err = gtk_text_view_get_buffer( GTK_TEXT_VIEW( text ) );
    gtk_table_attach( GTK_TABLE( table ), text, 1, 2, 1, 2,
                      GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0 );

    gtk_widget_show_all( window );

    gtk_main();

    g_slice_free( Data, data );

    return( 0 );
}
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class="brush: c"&gt;/*
 * Compile me with:
 *   gcc -o helper helper.c
 */
#include &lt;stdio.h&gt;

int
main( int    argc,
      char **argv )
{
    int i;

    for( i = 0; i &amp;lt; 10; i++ )
    {
        char stdout_string[] = &amp;quot;Normal message no:  .&amp;quot;;
        char stderr_string[] = &amp;quot;Error message no:  .&amp;quot;;

        stdout_string[19] = '0' + i;
        stderr_string[18] = '0' + i;

        sleep( 1 );
        fprintf( stdout, &amp;quot;%s\n&amp;quot;, stdout_string );
        sleep( 1 );
        fprintf( stderr, &amp;quot;%s\n&amp;quot;, stderr_string );
    }

    return( 0 );
}
&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6666239544818473000-7479159502053378678?l=blog.borovsak.si' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.borovsak.si/feeds/7479159502053378678/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.borovsak.si/2009/07/spawning-processes-using-glib.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6666239544818473000/posts/default/7479159502053378678'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6666239544818473000/posts/default/7479159502053378678'/><link rel='alternate' type='text/html' href='http://blog.borovsak.si/2009/07/spawning-processes-using-glib.html' title='Spawning processes using glib'/><author><name>Tadej Borovšak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-V5S0cD-5b_I/AAAAAAAAAAI/AAAAAAAAArU/nAyUYzn6Yx8/s512-c/photo.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6666239544818473000.post-6602366842564851258</id><published>2009-06-22T12:23:00.001+02:00</published><updated>2009-06-22T12:24:19.752+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='threads'/><category scheme='http://www.blogger.com/atom/ns#' term='tutorial'/><category scheme='http://www.blogger.com/atom/ns#' term='GTK+'/><title type='text'>Multi threaded GTK+ applications</title><content type='html'>It's been a while since I wrote something. And I'm happy to be back (although I
didn't go anywhere actually;).
&lt;br /&gt;&lt;br /&gt;
In this post I'll talk about how threads should be used together with GTK+ and
try to emphasize things that have a potential to go wrong. I won't talk about
mutexes and other thread related stuff that isn't specific to GTK+, so you
should find another source of information for those things.
&lt;br /&gt;&lt;br /&gt;
One of the most frequently asked questions about GTK+ is: "Is GTK+ thread
safe?". And the most likely answer you'll get to this question is: "No, but it
can be made thread aware.". What exactly is thread aware? And how does this
awareness helps us when developing application? Let's start from the beginning.
&lt;br /&gt;&lt;br /&gt;
&lt;span class="s_title"&gt;Relaxed introduction&lt;/span&gt;
&lt;br /&gt;&lt;br /&gt;
In GTK+ world, everything starts with glib. Glib can be made thread safe simply
by calling &lt;span class="cf"&gt;g_thread_init&lt;/span&gt; function at the beginning of
our application. And here we have the first possible show stopper: &lt;span
class="cf"&gt;g_thread_init&lt;/span&gt; should be called only once. Any subsequent calls
will abort with an error. Fortunately for us, there is simple solution for this
potential pitfall: call &lt;span class="cf"&gt;g_thread_init&lt;/span&gt; function in
construct like this:
&lt;pre class="brush: c"&gt;
if( ! g_thread_supported() )
    g_thread_init( NULL );
&lt;/pre&gt;
This will prevent calling &lt;span class="cf"&gt;g_thread_init&lt;/span&gt; again it the
glib's thread system has been initialized already. And this is all that is
needed to be safe when using glib from multiple threads.
&lt;br /&gt;&lt;br /&gt;
GTK+ on the other hand cannot be made completely thread safe. We'll need to take
some preventive measures ourselves in order to be able to use GTK+ from multiple
threads safely (this is why GTK+ is called thread aware). First thing we should
do in our application is call &lt;span class="cf"&gt;gdk_threads_init&lt;/span&gt; function.
This will set up gtk's global mutex which can be controlled (locked and
unlocked) by calling &lt;span class="cf"&gt;gdk_threads_enter&lt;/span&gt; and &lt;span
class="cf"&gt;gdk_threads_leave&lt;/span&gt; functions. From now on, any gtk function
call should be enclosed by &lt;span class="cf"&gt;gdk_threads_enter&lt;/span&gt;/&lt;span
class="cf"&gt;gdk_threads_leave&lt;/span&gt;, which will ensure that gtk's functions are
called from only one thread at a time.
&lt;br /&gt;&lt;br /&gt;
If we put everything that we learned so far into minimalistic example, this is
what we get:
&lt;pre class="brush: c"&gt;
/* Compile me with:
 *  gcc -o sample1 sample1.c $(pkg-config --cflags --libs gtk+-2.0 gthread-2.0)
 */
#include &amp;lt;gtk/gtk.h&amp;gt;

int
main( int    argc,
      char **argv )
{
    GtkWidget *window;
    GtkWidget *button;

    /* Secure glib */
    if( ! g_thread_supported() )
        g_thread_init( NULL );

    /* Secure gtk */
    gdk_threads_init();

    /* Obtain gtk's global lock */
    gdk_threads_enter();

    /* Do stuff as usual */
    gtk_init( &amp;amp;argc, &amp;amp;argv );

    window = gtk_window_new( GTK_WINDOW_TOPLEVEL );
    g_signal_connect( G_OBJECT( window ), &amp;quot;destroy&amp;quot;,
                      G_CALLBACK( gtk_main_quit ), NULL );

    button = gtk_button_new_with_label( &amp;quot;Initial value&amp;quot; );
    gtk_container_add( GTK_CONTAINER( window ), button );

    gtk_widget_show_all( window );

    gtk_main();

    /* Release gtk's global lock */
    gdk_threads_leave();

    return( 0 );
}
&lt;/pre&gt;
Simple enough.
&lt;br /&gt;&lt;br /&gt;
&lt;span class="s_title"&gt;More in-depth hows and whys&lt;/span&gt;
&lt;br /&gt;&lt;br /&gt;
Observant readers may have noticed that gtk's global lock is obtained at the
start of the application and not released until the end. How is it possible to
call gtk functions from other threads then? The secret lies in gtk's main loop,
which releases the global lock on each iteration. This gives other threads
opportunity to obtain the global lock and execute gtk functions safely.
&lt;br /&gt;&lt;br /&gt;
What about callbacks? Here things become a little complex. Callbacks for gtk
signals are made with global mutex locked, so you don't need to surround them
with &lt;span class="cf"&gt;gdk_threads_enter&lt;/span&gt;/&lt;span
class="cf"&gt;gdk_threads_leave&lt;/span&gt;. Idle and timeout callbacks on the other
hand are executed without obtaining global lock, so you're obliged to enclose
any gtk calls in those callbacks by &lt;span
class="cf"&gt;gdk_threads_enter&lt;/span&gt;/&lt;span class="cf"&gt;gdk_threads_leave&lt;/span&gt;.
And since this may become annoying, gtk offers variants of &lt;span
class="cf"&gt;g_idle_add&lt;/span&gt; and &lt;span class="cf"&gt;g_timeout_add&lt;/span&gt; that
obtain the lock automatically: &lt;span class="cf"&gt;gdk_treads_add_idle&lt;/span&gt; and
&lt;span class="cf"&gt;gdk_threads_add_timeout&lt;/span&gt;.
&lt;br /&gt;&lt;br /&gt;
Next code snippet is simple upgrade from previous one and displays how callbacks
should be treated.
&lt;pre class="brush: c"&gt;
/* Compile me with:
 *  gcc -o sample2 sample2.c $(pkg-config --cflags --libs gtk+-2.0 gthread-2.0)
 */
#include &amp;lt;gtk/gtk.h&amp;gt;

static void
cb_clicked( GtkButton *button,
            gpointer   data )
{
    /* No need to call gdk_threads_enter/gdk_threads_leave,
       since gtk callbacks are executed withing main lock. */
    gtk_button_set_label( button, &amp;quot;Clicked&amp;quot; );
}

static gboolean
cb_idle( gpointer data )
{
    /* Idle callback don't automatically obtain main lock,
       so we need to do it manually. */
    gdk_threads_enter();
    gtk_button_set_label( GTK_BUTTON( data ), &amp;quot;Idle&amp;quot; );
    gdk_threads_leave();

    return( FALSE );
}

static gboolean
cb_timeout( gpointer data )
{
    /* Timeouts also don't automatically obtain main lock, but
       since we added this one using gdk_threads_add_timeout,
       lock has been obtained for us. */
    gtk_button_set_label( GTK_BUTTON( data ), &amp;quot;Timeout&amp;quot; );

    return( TRUE );
}

int
main( int    argc,
      char **argv )
{
    GtkWidget *window;
    GtkWidget *button;

    /* Secure glib */
    if( ! g_thread_supported() )
        g_thread_init( NULL );

    /* Secure gtk */
    gdk_threads_init();

    /* Obtain gtk's global lock */
    gdk_threads_enter();

    /* Do stuff as usual */
    gtk_init( &amp;amp;argc, &amp;amp;argv );

    window = gtk_window_new( GTK_WINDOW_TOPLEVEL );
    g_signal_connect( G_OBJECT( window ), &amp;quot;destroy&amp;quot;,
                      G_CALLBACK( gtk_main_quit ), NULL );

    button = gtk_button_new_with_label( &amp;quot;Initial value&amp;quot; );
    g_signal_connect( G_OBJECT( button ), &amp;quot;clicked&amp;quot;,
                      G_CALLBACK( cb_clicked ), NULL );
    gtk_container_add( GTK_CONTAINER( window ), button );

    g_idle_add( cb_idle, (gpointer)button );
    gdk_threads_add_timeout( 2300, cb_timeout, (gpointer)button );

    gtk_widget_show_all( window );

    gtk_main();

    /* Release gtk's global lock */
    gdk_threads_leave();

    return( 0 );
}
&lt;/pre&gt;
Still relatively simple.
&lt;br /&gt;&lt;br /&gt;
&lt;span class="s_title"&gt;Adding thread&lt;/span&gt;
&lt;br /&gt;&lt;br /&gt;
Have you noticed that we're talking about multi threaded applications and all of
my samples have been single threaded? It's time to become double threaded.
&lt;br /&gt;&lt;br /&gt;
I again modified my sample code, which now sports second thread, which changes
button's label every 3 seconds. Additionally, we have a timeout function that
changes button label every 2300 milliseconds and a possibility of manually
changing button label by clicking on it.
&lt;pre class="brush: c"&gt;
/* Compile me with:
 *  gcc -o sample3 sample3.c $(pkg-config --cflags --libs gtk+-2.0 gthread-2.0)
 */
#include &amp;lt;gtk/gtk.h&amp;gt;

static gpointer
thread_func( gpointer data )
{
    while( TRUE )
    {
        sleep( 3 );

        gdk_threads_enter();
        gtk_button_set_label( GTK_BUTTON( data ), &amp;quot;Thread&amp;quot; );
        gdk_threads_leave();
    }

    return( NULL );
}

static void
cb_clicked( GtkButton *button,
            gpointer   data )
{
    /* No need to call gdk_threads_enter/gdk_threads_leave,
       since gtk callbacks are executed withing main lock. */
    gtk_button_set_label( button, &amp;quot;Clicked&amp;quot; );
}

static gboolean
cb_timeout( gpointer data )
{
    /* Timeouts also don't automatically obtain main lock, but
       since we added this one using gdk_threads_add_timeout,
       lock has been obtained for us. */
    gtk_button_set_label( GTK_BUTTON( data ), &amp;quot;Timeout&amp;quot; );

    return( TRUE );
}

int
main( int    argc,
      char **argv )
{
    GtkWidget *window;
    GtkWidget *button;
    GThread   *thread;
    GError    *error = NULL;

    /* Secure glib */
    if( ! g_thread_supported() )
        g_thread_init( NULL );

    /* Secure gtk */
    gdk_threads_init();

    /* Obtain gtk's global lock */
    gdk_threads_enter();

    /* Do stuff as usual */
    gtk_init( &amp;amp;argc, &amp;amp;argv );

    window = gtk_window_new( GTK_WINDOW_TOPLEVEL );
    g_signal_connect( G_OBJECT( window ), &amp;quot;destroy&amp;quot;,
                      G_CALLBACK( gtk_main_quit ), NULL );

    button = gtk_button_new_with_label( &amp;quot;Initial value&amp;quot; );
    g_signal_connect( G_OBJECT( button ), &amp;quot;clicked&amp;quot;,
                      G_CALLBACK( cb_clicked ), NULL );
    gtk_container_add( GTK_CONTAINER( window ), button );

    gdk_threads_add_timeout( 2300, cb_timeout, (gpointer)button );

    /* Create new thread */
    thread = g_thread_create( thread_func, (gpointer)button,
                              FALSE, &amp;amp;error );
    if( ! thread )
    {
        g_print( &amp;quot;Error: %s\n&amp;quot;, error-&amp;gt;message );
        return( -1 );
    }

    gtk_widget_show_all( window );

    gtk_main();

    /* Release gtk's global lock */
    gdk_threads_leave();

    return( 0 );
}
&lt;/pre&gt;
Do I hear someone complaining that button should be protected by a mutex? You're
right, it should be. And it actually is protected - by gtk's global mutex. How?
We access button only through gtk function calls, never directly. And since gtk
functions are allowed to be called only from one thread at a time, our button is
automatically being modified only from single thread at a time. (BTW, if you
access some parts of the widgets directly, you should really try to replace
those direct accesses with accessor function calls. This will make you code more
robust across version changes.)
&lt;br /&gt;&lt;br /&gt;
&lt;span class="s_title"&gt;Real world deployment&lt;/span&gt;
&lt;br /&gt;&lt;br /&gt;
There are two quite distinct ways of writing multi threaded gtk applications.
One way is to protect all gtk calls with main lock as I've been doing in my
previous examples, second way of doing it is to call gtk only from single
thread. Which method to use? Probably the right answer is "The one that suits
you best", but I would recommend the second method. Facts that attribute to this
decision can be roughly summoned as:
&lt;ul&gt;
  &lt;li&gt;No need to call gdk_threads_enter/leave.&lt;/li&gt;
  &lt;li&gt;Works both on Linux and Windows.&lt;/li&gt;
  &lt;li&gt;Data is separated from it's displayed form (this is a simple form of Model-View-Controller design)&lt;/li&gt;
&lt;/ul&gt;
So why did I bother to show you the first way and then marked it as inferior?
Because this knowledge will enable you to make an educated choice (or at least
an educated guess;) next time you decide to start coding. Still angry?;)
&lt;br /&gt;&lt;br /&gt;
And for the finish, here is the last variation of my sample code that
demonstrates the call-gtk-from-single-thread-only approach in action.
&lt;pre class="brush: c"&gt;
/* Compile me with:
 *  gcc -o sample4 sample4.c $(pkg-config --cflags --libs gtk+-2.0 gthread-2.0)
 */
#include &amp;lt;gtk/gtk.h&amp;gt;

/* Progress variable and it's associated mutex */
gint progress = 0;
G_LOCK_DEFINE_STATIC( progress );


static gpointer
thread_func( gpointer data )
{
    gint i;

    for( i = 0; i &amp;lt; 1000; i++ )
    {
        g_usleep( 10000 );

        G_LOCK( progress );
        progress = i;
        G_UNLOCK( progress );
    }

    return( NULL );
}

static gboolean
cb_timeout( gpointer data )
{
    gchar *label;

    G_LOCK( progress );
    label = g_strdup_printf( &amp;quot;Finished %d of 999&amp;quot;, progress );
    G_UNLOCK( progress );

    gtk_button_set_label( GTK_BUTTON( data ), label );
    g_free( label );

    return( TRUE );
}

int
main( int    argc,
      char **argv )
{
    GtkWidget *window;
    GtkWidget *button;
    GThread   *thread;
    GError    *error = NULL;

    /* Secure glib */
    if( ! g_thread_supported() )
        g_thread_init( NULL );

    /* Do stuff as usual */
    gtk_init( &amp;amp;argc, &amp;amp;argv );

    window = gtk_window_new( GTK_WINDOW_TOPLEVEL );
    g_signal_connect( G_OBJECT( window ), &amp;quot;destroy&amp;quot;,
                      G_CALLBACK( gtk_main_quit ), NULL );

    button = gtk_button_new_with_label( &amp;quot;Initial value&amp;quot; );
    gtk_container_add( GTK_CONTAINER( window ), button );

    gdk_threads_add_timeout( 100, cb_timeout, (gpointer)button );

    /* Create new thread */
    thread = g_thread_create( thread_func, (gpointer)button,
                              FALSE, &amp;amp;error );
    if( ! thread )
    {
        g_print( &amp;quot;Error: %s\n&amp;quot;, error-&amp;gt;message );
        return( -1 );
    }

    gtk_widget_show_all( window );
    gtk_main();

    return( 0 );
}
&lt;/pre&gt;
That's it. I hope you enjoyed and learned something new. Until next time, stay
healthy.
&lt;br /&gt;&lt;br /&gt;
Bye.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6666239544818473000-6602366842564851258?l=blog.borovsak.si' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.borovsak.si/feeds/6602366842564851258/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.borovsak.si/2009/06/multi-threaded-gtk-applications.html#comment-form' title='12 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6666239544818473000/posts/default/6602366842564851258'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6666239544818473000/posts/default/6602366842564851258'/><link rel='alternate' type='text/html' href='http://blog.borovsak.si/2009/06/multi-threaded-gtk-applications.html' title='Multi threaded GTK+ applications'/><author><name>Tadej Borovšak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-V5S0cD-5b_I/AAAAAAAAAAI/AAAAAAAAArU/nAyUYzn6Yx8/s512-c/photo.jpg'/></author><thr:total>12</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6666239544818473000.post-9213844831792559353</id><published>2009-05-28T15:58:00.001+02:00</published><updated>2009-05-28T16:01:15.630+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='tutorial'/><category scheme='http://www.blogger.com/atom/ns#' term='GtkTreeModel'/><category scheme='http://www.blogger.com/atom/ns#' term='GTK+'/><title type='text'>GtkTreeModel and filtering (4)</title><content type='html'>As promised, I'll talk about efficiency: what and why degrades applications
performance when using filters and how to make your application faster.
&lt;br /&gt;&lt;br /&gt;
Content:
&lt;ol&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/05/gtktreemodel-and-filtering.html"&gt;Introduction to filtering&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/05/gtktreemodel-and-filtering-2.html"&gt;Limiting the amount of data being displayed&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/05/gtktreemodel-and-filtering-3.html"&gt;Modifying data's representation (data transformations)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/05/gtktreemodel-and-filtering-4.html"&gt;Performance issues related to filtering&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;span class="s_title"&gt;Performance issues&lt;/span&gt;
&lt;br /&gt;&lt;br /&gt;
One important aspect of filters is how they handle additions to child model. For
example, if you add a row of data into child model, filter needs to check the
row in question and determine the rows visibility. Where is the performance
issue here? Let me explain. When we're adding a row of data in model, we usually
do it in two separate steps: first we insert empty row in model; next we fill
this row with data. And this is where the performance issue stems from:
inserting new row in model causes filter to be evoked twice and filling the row
with data evokes filter once. So for each addition, filter gets evoked three
times.
&lt;br /&gt;&lt;br /&gt;
How do we handle this situation? Rule of thumb that I use when dealing with
filters and large lists: "If list has more that 100 rows and I need to add more
that 50% of the contents, I remove filter and recreate it at the end.". For
example, if my list holds 1000 rows, I would recreate filter if I would need to
add more that 500 rows.
&lt;br /&gt;&lt;br /&gt;
To demonstrate this issue, I wrote a simple application that shows you the
difference in CPU time used for repopulating list with 1000 entries. I use
visible column for this sample, speed gains would be even bigger if I were using
filter function or transformation.
&lt;pre class="brush: c"&gt;
/*
 * Compile this application without optimization:
 *   gcc -o filter4a filter4a.c $(pkg-config --cflags --libs gtk+-2.0)
 *
 * Compile this application with optimization:
 *   gcc -DREMOVE -o filter4a filter4a.c $(pkg-config --cflags --libs gtk+-2.0)
 */
#include &amp;lt;gtk/gtk.h&amp;gt;
#include &amp;lt;time.h&amp;gt;

/* Callback function that repopulates store.
 *
 * If REMOVE macro has been defined, this function destroys filter before
 * changing the contents of the store, which makes quite a difference.
 *
 * NOTE: Speed gain you notice is not only obtained by filter not filtering
 * contents while being added but also from the fact that tree view doesn't
 * update display while the data is added. */
static void
cb_clicked( GtkButton   *button,
            GtkTreeView *tree )
{
    GtkTreeModel *filter;
    GtkListStore *store;
    gint          i;
    clock_t       start, end;

    start = clock();

    filter = gtk_tree_view_get_model( tree );
    store = GTK_LIST_STORE( gtk_tree_model_filter_get_model(
                                GTK_TREE_MODEL_FILTER( filter ) ) );

#ifdef REMOVE
    g_object_ref( G_OBJECT( store ) );
    gtk_tree_view_set_model( tree, NULL );
#endif

    gtk_list_store_clear( store );
    for( i = 0; i &amp;lt; 1000; i++ )
    {
        GtkTreeIter iter;

        gtk_list_store_append( store, &amp;amp;iter );
        gtk_list_store_set( store, &amp;amp;iter, 0, i, 1, (gboolean)( i % 2 ), -1 );
    }

#ifdef REMOVE
    filter = gtk_tree_model_filter_new( GTK_TREE_MODEL( store ), NULL );
    g_object_unref( G_OBJECT( store ) );

    gtk_tree_view_set_model( tree, filter );
    g_object_unref( G_OBJECT( filter ) );
#endif

    end = clock();

    g_print( &amp;quot;CPU time taken for this task: %f\n&amp;quot;,
                ( (gdouble)( end - start ) ) / CLOCKS_PER_SEC );
}

int
main( int    argc,
      char **argv )
{
    GtkWidget *window;
    GtkWidget *vbox;
    GtkWidget *button;
    GtkWidget *swindow;
    GtkWidget *tree;

    /* Data part */
    GtkListStore *store;
    GtkTreeModel *filter;

    /* Display */
    GtkCellRenderer *cell;

    /* Misc */
    gint i;


    gtk_init( &amp;amp;argc, &amp;amp;argv );

    window = gtk_window_new( GTK_WINDOW_TOPLEVEL );
    g_signal_connect( G_OBJECT( window ), &amp;quot;destroy&amp;quot;,
                      G_CALLBACK( gtk_main_quit ), NULL );

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

    button = gtk_button_new_with_label( &amp;quot;Repopulate&amp;quot; );
    gtk_box_pack_start( GTK_BOX( vbox ), button, FALSE, FALSE, 0 );

    swindow = gtk_scrolled_window_new( NULL, NULL );
    gtk_box_pack_start( GTK_BOX( vbox ), swindow, TRUE, TRUE, 0 );

    store = gtk_list_store_new( 2, G_TYPE_INT, G_TYPE_BOOLEAN );
    for( i = 0; i &amp;lt; 1000; i++ )
    {
        GtkTreeIter iter;

        gtk_list_store_append( store, &amp;amp;iter );
        gtk_list_store_set( store, &amp;amp;iter, 0, i, 1, (gboolean)( i % 2 ), -1 );
    }

    filter = gtk_tree_model_filter_new( GTK_TREE_MODEL( store ), NULL );
    g_object_unref( G_OBJECT( store ) );
    gtk_tree_model_filter_set_visible_column(
            GTK_TREE_MODEL_FILTER( filter ), 1 );

    tree = gtk_tree_view_new_with_model( filter );
    g_object_unref( G_OBJECT( filter ) );
    gtk_container_add( GTK_CONTAINER( swindow ), tree );

    cell = gtk_cell_renderer_text_new();
    gtk_tree_view_insert_column_with_attributes( GTK_TREE_VIEW( tree ),
                                                 -1, &amp;quot;Numbers&amp;quot;, cell,
                                                 &amp;quot;text&amp;quot;, 0, NULL );

    g_signal_connect( G_OBJECT( button ), &amp;quot;clicked&amp;quot;,
                      G_CALLBACK( cb_clicked ), GTK_TREE_VIEW( tree ) );

    gtk_widget_show_all( window );
    gtk_main();

    return( 0 );
}
&lt;/pre&gt;
Another problem are lists that are being dynamically filtered. Below is a sample
application that searches through the list and displays only those lines that
contain string, entered in entry field.
&lt;br /&gt;&lt;br /&gt;
Because we want our application to be as user-friendly as possible, we filter
the contents while the user types. We have a handler connected to "changed"
signal of GtkEntry, in which we search for string. Without optimization, model
is refiltered for every key press. When optimizations are enabled, we queue
refilter instead of directly executing it. What do we gain this way? If the user
types three letters in quickly, we only refilter once instead of three times.
Test this behavior with next sample application. (names.txt file is derived from
list of names from &lt;a
href="http://projecteuler.net/index.php?section=problems&amp;id=22"&gt;this Project
Euler's problem&lt;/a&gt; and can be dowloaded from &lt;a
href="http://tadeboro.googlepages.com/names.txt"&gt;here&lt;/a&gt;.)
&lt;pre class="brush: c"&gt;
/* Compile non-optimized version with:
 *   gcc -Wall -o filter4b filter4b.c $(pkg-config --cflags --libs gtk+-2.0)
 *
 * Compile optimized version with:
 *   gcc -DOPTIMIZE -o filter4b filter4b.c $(pkg-config --cflags --libs gtk+-2.0)
 */

#include &amp;lt;gtk/gtk.h&amp;gt;
#include &amp;lt;string.h&amp;gt;

#ifdef OPTIMIZE
static gint timeout_id = 0;
#endif

static gboolean
do_refilter( GtkTreeModelFilter *filter )
{
    g_print( &amp;quot;Refiltering ...&amp;quot; );
    gtk_tree_model_filter_refilter( filter );
    g_print( &amp;quot; Done\n&amp;quot; );

#ifdef OPTIMIZE
    timeout_id = 0;
#endif

    return( FALSE );
}

#ifdef OPTIMIZE
static void
queue_refilter( GtkTreeModelFilter *filter )
{
    if( timeout_id )
        g_source_remove( timeout_id );

    timeout_id = g_timeout_add( 300, (GSourceFunc)do_refilter, filter );
}
#endif

static gboolean
visible_func( GtkTreeModel *model,
              GtkTreeIter  *iter,
              GtkEntry     *entry )
{
    const gchar *needle;
    gchar       *haystack;
    gboolean     result;

    needle = gtk_entry_get_text( entry );
    if( *needle == '\0' )
        return( TRUE );
    gtk_tree_model_get( model, iter, 0, &amp;amp;haystack, -1 );

    result = ( strstr( haystack, needle ) ? TRUE : FALSE );
    g_free( haystack );

    return( result );
}

static void
cb_changed( GtkEditable        *entry,
            GtkTreeModelFilter *filter )
{
#ifdef OPTIMIZE
    queue_refilter( filter );
#else
    do_refilter( filter );
#endif
}


int
main( int    argc,
      char **argv )
{
    GtkWidget *window;
    GtkWidget *vbox;
    GtkWidget *entry;
    GtkWidget *swindow;
    GtkWidget *tree;

    /* Data store */
    GtkListStore *store;
    GtkTreeModel *filter;

    /* Display components */
    GtkCellRenderer *cell;

    /* Misc variables */
    gchar *name, *file;

    gtk_init( &amp;amp;argc, &amp;amp;argv );

    window = gtk_window_new( GTK_WINDOW_TOPLEVEL );
    g_signal_connect( G_OBJECT( window ), &amp;quot;destroy&amp;quot;,
                      G_CALLBACK( gtk_main_quit ), NULL );

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

    entry = gtk_entry_new();
    gtk_box_pack_start( GTK_BOX( vbox ), entry, FALSE, FALSE, 0 );

    swindow = gtk_scrolled_window_new( NULL, NULL );
    gtk_box_pack_start( GTK_BOX( vbox ), swindow, TRUE, TRUE, 0 );

    store = gtk_list_store_new( 1, G_TYPE_STRING );
    g_file_get_contents( &amp;quot;names.txt&amp;quot;, &amp;amp;file, NULL, NULL );
    name = strtok( file, &amp;quot;,&amp;quot; );
    while( name )
    {
        GtkTreeIter iter;

        gtk_list_store_append( store, &amp;amp;iter );
        gtk_list_store_set( store, &amp;amp;iter, 0, name, -1 );

        name = strtok( NULL, &amp;quot;,&amp;quot; );
    }
    g_free( file );

    filter = gtk_tree_model_filter_new( GTK_TREE_MODEL( store ), NULL );
    g_object_unref( G_OBJECT( store ) );
    gtk_tree_model_filter_set_visible_func(
            GTK_TREE_MODEL_FILTER( filter ),
            (GtkTreeModelFilterVisibleFunc)visible_func,
            GTK_ENTRY( entry ), NULL );

    tree = gtk_tree_view_new_with_model( filter );
    g_object_unref( G_OBJECT( filter ) );
    gtk_container_add( GTK_CONTAINER( swindow ), tree );

    cell = gtk_cell_renderer_text_new();
    gtk_tree_view_insert_column_with_attributes( GTK_TREE_VIEW( tree ),
                                                 -1, &amp;quot;Name&amp;quot;, cell,
                                                 &amp;quot;text&amp;quot;, 0, NULL );

    g_signal_connect( G_OBJECT( entry ), &amp;quot;changed&amp;quot;,
                      G_CALLBACK( cb_changed ),
                      GTK_TREE_MODEL_FILTER( filter ) );

    gtk_widget_show_all( window );
    gtk_main();

    return( 0 );
}
&lt;/pre&gt;
And this is the end of the last post in this series. I hope you enjoyed it.
&lt;br /&gt;&lt;br /&gt;
Bye.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6666239544818473000-9213844831792559353?l=blog.borovsak.si' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.borovsak.si/feeds/9213844831792559353/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.borovsak.si/2009/05/gtktreemodel-and-filtering-4.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6666239544818473000/posts/default/9213844831792559353'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6666239544818473000/posts/default/9213844831792559353'/><link rel='alternate' type='text/html' href='http://blog.borovsak.si/2009/05/gtktreemodel-and-filtering-4.html' title='GtkTreeModel and filtering (4)'/><author><name>Tadej Borovšak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-V5S0cD-5b_I/AAAAAAAAAAI/AAAAAAAAArU/nAyUYzn6Yx8/s512-c/photo.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6666239544818473000.post-6582604826477401987</id><published>2009-05-21T13:52:00.005+02:00</published><updated>2009-05-28T16:00:54.458+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='tutorial'/><category scheme='http://www.blogger.com/atom/ns#' term='GtkTreeModel'/><category scheme='http://www.blogger.com/atom/ns#' term='GTK+'/><title type='text'>GtkTreeModel and filtering (3)</title><content type='html'>In this post, I'll show you how to transform data inside model.
&lt;br /&gt;&lt;br /&gt;
Content:
&lt;ol&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/05/gtktreemodel-and-filtering.html"&gt;Introduction to filtering&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/05/gtktreemodel-and-filtering-2.html"&gt;Limiting the amount of data being displayed&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/05/gtktreemodel-and-filtering-3.html"&gt;Modifying data's representation (data transformations)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/05/gtktreemodel-and-filtering-4.html"&gt;Performance issues related to filtering&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;span class="s_title"&gt;Transforming data&lt;/span&gt;
&lt;br /&gt;&lt;br /&gt;
This is probably the most powerful capability of filter. You can transform
original data to meet your specific needs. All you need to do is write a
function that will do the transformation and instruct the filter to use it. But
because this function is called for each data access, you can expect your
application to slow down a bit (or a lot, depending on the complexity of
transformation).
&lt;br /&gt;&lt;br /&gt;
Using GtkTreeModelFilter to transform data can be quite complex at first, but if
you answer next three questions, you should be able to write a transformation
without driving yourself mad.
&lt;ol&gt;
  &lt;li&gt;How is source data structured?&lt;/li&gt;
  &lt;li&gt;How should transformed data should be structured?&lt;/li&gt;
  &lt;li&gt;What kind of transformation do I need to get from source to transformed
  data?&lt;/li&gt;
&lt;/ol&gt;
Before I expose you to my sample code, I'll describe the process of writing this
sample application, so you can see how my three step development cycle should be
done in practice.
&lt;br /&gt;&lt;br /&gt;
I'll use GtkListStore with one G_TYPE_STRING column that contains strings of
length 3 as a source of data. In final product, I want to display Unicode code
point for each character in string in separate column, with original string at
the end. So I will need four G_TYPE_STRING columns to hold three code points and
original string. The transformation function should do the following:
&lt;ul&gt;
  &lt;li&gt;split string into characters&lt;/li&gt;
  &lt;li&gt;obtain Unicode code point for each character&lt;/li&gt;
  &lt;li&gt;create string from Unicode code point&lt;/li&gt;
&lt;/ul&gt;
Now we're ready to start coding.
&lt;pre class="brush: c"&gt;
#include &amp;lt;gtk/gtk.h&amp;gt;

/* Enums for source model and filter. This way it's easier to track which column
 * goes where when we need to set cell renderer's attributes. */
enum
{
    S_COL_TEXT,
    S_NO_COLS
};

enum
{
    F_COL_1ST,
    F_COL_2ND,
    F_COL_3RD,
    F_COL_STR,
    F_NO_COLS
};

/* Transformation function */
static void
cb_transform( GtkTreeModel *filter, /* Filter */
              GtkTreeIter  *iter_f, /* Iter pointing to row in filter */
              GValue       *value,  /* We must fill this one with data */
              gint          column, /* Column in filter that will hold value */
              gpointer      data )
{
    gchar    *string;  /* This is source string from child model */
    gchar    *new_string;
    GtkTreeModel *child;
    GtkTreeIter   iter_c;

    /* Get child model and iter */
    child = gtk_tree_model_filter_get_model( GTK_TREE_MODEL_FILTER( filter ) );
    gtk_tree_model_filter_convert_iter_to_child_iter( GTK_TREE_MODEL_FILTER( filter ),
                                                      &amp;amp;iter_c, iter_f );
    /* Get source string */
    gtk_tree_model_get( child, &amp;amp;iter_c, S_COL_TEXT, &amp;amp;string, -1 );

    /* If the column is 0, 1 or 2, we must obtain the code point. If column is
     * 4, we just pass original string through. */
    switch( column )
    {
        case F_COL_1ST:
        case F_COL_2ND:
        case F_COL_3RD:
            {
                gunichar  c;
                gint      i;
                gchar    *start = string;

                /* Go to first, second or third character. And since we want to
                 * be internationally friendly, we'll be using UTF-8 mangling
                 * routines. */
                for(  i = 0; i &amp;lt; column; i++ )
                    start = g_utf8_next_char( start );
                c = g_utf8_get_char( start );
                g_free( string );

                new_string = g_strdup_printf( &amp;quot;%04x&amp;quot;, c );
            }
            break;
        case F_COL_STR:
            new_string = string;
            break;
    }

    /* Fill value with new string */
    g_value_set_string( value, new_string );

    /* Free string */
    g_free( new );
}

int
main( int    argc,
      char **argv )
{
    /* Widgets */
    GtkWidget *window;
    GtkWidget *tree;

    /* Data storage */
    GtkListStore *store;
    GtkTreeModel *filter;

    /* Data display */
    GtkCellRenderer *cell;

    /* Misc variables */
    gchar **string;

    /* Strings to be added into data store */
    gchar *data[] = { &amp;quot;abc&amp;quot;, &amp;quot;hHu&amp;quot;, &amp;quot;klr&amp;quot;, &amp;quot;DfU&amp;quot;, &amp;quot;pWB&amp;quot;, NULL };

    /* Column types for filter */
    GType types[] = { G_TYPE_STRING, G_TYPE_STRING,
                      G_TYPE_STRING, G_TYPE_STRING };


    gtk_init( &amp;amp;argc, &amp;amp;argv );

    window = gtk_window_new( GTK_WINDOW_TOPLEVEL );
    g_signal_connect( G_OBJECT( window ), &amp;quot;destroy&amp;quot;,
                      G_CALLBACK( gtk_main_quit ), NULL );

    /* Create data store */
    store = gtk_list_store_new( S_NO_COLS, G_TYPE_STRING );
    for( string = data; *string; string++ )
    {
        GtkTreeIter iter;

        gtk_list_store_append( store, &amp;amp;iter );
        gtk_list_store_set( store, &amp;amp;iter, 0, *string, -1 );
    }

    /* Create filter */
    filter = gtk_tree_model_filter_new( GTK_TREE_MODEL( store ), NULL );
    g_object_unref( G_OBJECT( store ) );
    gtk_tree_model_filter_set_modify_func( GTK_TREE_MODEL_FILTER( filter ),
                                           F_NO_COLS, types, cb_transform, NULL, NULL );

    /* Cretate tree view */
    tree = gtk_tree_view_new_with_model( filter );
    g_object_unref( G_OBJECT( filter ) );
    gtk_container_add( GTK_CONTAINER( window ), tree );

    /* Create display part of tree view. We need to be careful when setting cell
     * renderer's attributes!!! */
    cell = gtk_cell_renderer_text_new();
    gtk_tree_view_insert_column_with_attributes( GTK_TREE_VIEW( tree ), -1, &amp;quot;1st&amp;quot;,
                                                 cell, &amp;quot;text&amp;quot;, F_COL_1ST, NULL );

    cell = gtk_cell_renderer_text_new();
    gtk_tree_view_insert_column_with_attributes( GTK_TREE_VIEW( tree ), -1, &amp;quot;2nd&amp;quot;,
                                                 cell, &amp;quot;text&amp;quot;, F_COL_2ND, NULL );

    cell = gtk_cell_renderer_text_new();
    gtk_tree_view_insert_column_with_attributes( GTK_TREE_VIEW( tree ), -1, &amp;quot;3rd&amp;quot;,
                                                 cell, &amp;quot;text&amp;quot;, F_COL_3RD, NULL );

    cell = gtk_cell_renderer_text_new();
    gtk_tree_view_insert_column_with_attributes( GTK_TREE_VIEW( tree ), -1, &amp;quot;String&amp;quot;,
                                                 cell, &amp;quot;text&amp;quot;, F_COL_STR, NULL );

    /* Show */
    gtk_widget_show_all( window );
    gtk_main();

    return( 0 );
}
&lt;/pre&gt;
I think this is enough for today. Join me next time for some all important
performance guides and tricks that will make your application faster when using
filters (and yes, you should really read the last part if you intend to
implement filtering;).
&lt;br /&gt;&lt;br /&gt;
Stay save and healthy until next time.
&lt;br /&gt;&lt;br /&gt;
Bye.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6666239544818473000-6582604826477401987?l=blog.borovsak.si' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.borovsak.si/feeds/6582604826477401987/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.borovsak.si/2009/05/gtktreemodel-and-filtering-3.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6666239544818473000/posts/default/6582604826477401987'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6666239544818473000/posts/default/6582604826477401987'/><link rel='alternate' type='text/html' href='http://blog.borovsak.si/2009/05/gtktreemodel-and-filtering-3.html' title='GtkTreeModel and filtering (3)'/><author><name>Tadej Borovšak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-V5S0cD-5b_I/AAAAAAAAAAI/AAAAAAAAArU/nAyUYzn6Yx8/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6666239544818473000.post-2582569538150714134</id><published>2009-05-19T16:41:00.008+02:00</published><updated>2009-05-28T16:01:28.620+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='tutorial'/><category scheme='http://www.blogger.com/atom/ns#' term='GtkTreeModel'/><category scheme='http://www.blogger.com/atom/ns#' term='GTK+'/><title type='text'>GtkTreeModel and filtering (2)</title><content type='html'>As I promised in my previous post, we'll learn how to limit the amount of data
being displayed in our widgets.
&lt;br /&gt;&lt;br /&gt;
Content:
&lt;ol&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/05/gtktreemodel-and-filtering.html"&gt;Introduction to filtering&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/05/gtktreemodel-and-filtering-2.html"&gt;Limiting the amount of data being displayed&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/05/gtktreemodel-and-filtering-3.html"&gt;Modifying data's representation (data transformations)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/05/gtktreemodel-and-filtering-4.html"&gt;Performance issues related to filtering&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;span class="s_title"&gt;Limiting the amount of data being displayed&lt;/span&gt;
&lt;br /&gt;&lt;br /&gt;
First way of limiting the amount of data is to specify the virtual root when
creating new filter. This way we can show only one tree branch. Let me
demonstrate this with a really simple application (parts of the code that are
specific to filtering are highlighted):
&lt;pre class="brush: c; highlight: [13, 14, 48, 49, 50, 51, 52]"&gt;
#include &amp;lt;gtk/gtk.h&amp;gt;

int
main( int    argc,
      char **argv )
{
    /* Widgets */
    GtkWidget *window;
    GtkWidget *tree;

    /* Data parts */
    GtkTreeStore *store;
    GtkTreeModel *filter;
    GtkTreePath  *virtual_root;

    /* Display part */
    GtkCellRenderer *cell;

    /* Misc variables */
    gint i, j;


    gtk_init( &amp;amp;argc, &amp;amp;argv );

    window = gtk_window_new( GTK_WINDOW_TOPLEVEL );
    g_signal_connect( G_OBJECT( window ), &amp;quot;destroy&amp;quot;,
                      G_CALLBACK( gtk_main_quit ), NULL );

    /* Create data store and populate it with data. */
    store = gtk_tree_store_new( 1, G_TYPE_INT );
    for( i = 1; i &amp;lt; 4; i++ )
    {
        GtkTreeIter parent_iter;

        gtk_tree_store_append( store, &amp;amp;parent_iter, NULL );
        gtk_tree_store_set( store, &amp;amp;parent_iter, 0, i, -1 );

        /* Create leaves */
        for( j = 1; j &amp;lt; 10; j++ )
        {
            GtkTreeIter child_iter;

            gtk_tree_store_append( store, &amp;amp;child_iter, &amp;amp;parent_iter );
            gtk_tree_store_set( store, &amp;amp;child_iter, 0, i * 10 + j, -1 );
        }
    }

    /* Create filter with virtual root set to second branch*/
    virtual_root = gtk_tree_path_new_from_indices( 1, -1 );
    filter = gtk_tree_model_filter_new( GTK_TREE_MODEL( store ), virtual_root );
    gtk_tree_path_free( virtual_root );
    g_object_unref( G_OBJECT( store ) );

    /* Create tree view */
    tree = gtk_tree_view_new_with_model( filter );
    gtk_container_add( GTK_CONTAINER( window ), tree );

    /* Add display components to tree */
    cell = gtk_cell_renderer_text_new();
    gtk_tree_view_insert_column_with_attributes( GTK_TREE_VIEW( tree ), -1,
                                                 &amp;quot;Sample tree&amp;quot;, cell,
                                                 &amp;quot;text&amp;quot;, 0,
                                                 NULL );

    gtk_widget_show_all( window );
    gtk_main();

    return( 0 );
}
&lt;/pre&gt;
As you can see, adding a simple filter is really easy. And now we're ready to
implement row based filtering.
&lt;br /&gt;&lt;br /&gt;
If you read my previous post carefully, you already know that we can do this
kind of filtering either by using "visible column" or "visible function" method.
How exactly do those two methods work?
&lt;br /&gt;&lt;br /&gt;
As the name of the method suggests, "visible column" method checks values inside
one of the child's column and filters according to the value found. Column which
holds visibility information should be of type G_TYPE_BOOLEAN, where TRUE value
means "this row should be shown" and FALSE means "this row should be hidden".
The column that filter will monitor is set with &lt;a
href="http://library.gnome.org/devel/gtk/stable/GtkTreeModelFilter.html#gtk-tree-model-filter-set-visible-column"&gt;gtk_tree_model_filter_set_visible_column&lt;/a&gt;
function. And without any further ado, let's have a look at another simple application
that will demonstrate "visible column" method. (Again, filter related lines are
highlighted.)
&lt;pre class="brush: c; highlight: [13, 52, 53, 54, 55, 56]"&gt;
#include &amp;lt;gtk/gtk.h&amp;gt;

int
main( int    argc,
      char **argv )
{
    /* Widgets */
    GtkWidget *window;
    GtkWidget *iconview;

    /* Data parts */
    GtkListStore *store;
    GtkTreeModel *filter;

    /* Actual data for icon view */
    gchar *images[] = { GTK_STOCK_APPLY,
                        GTK_STOCK_CANCEL,
                        GTK_STOCK_MEDIA_PLAY,
                        GTK_STOCK_CLEAR,
                        GTK_STOCK_CONVERT,
                        NULL };

    /* Misc variables */
    gint i;


    gtk_init( &amp;amp;argc, &amp;amp;argv );

    window = gtk_window_new( GTK_WINDOW_TOPLEVEL );
    g_signal_connect( G_OBJECT( window ), &amp;quot;destroy&amp;quot;,
                      G_CALLBACK( gtk_main_quit ), NULL );

    /* Create data store and populate it with data.
     * Values in second column are FALSE, TRUE, FALSE, TRUE, ... */
    store = gtk_list_store_new( 2, GDK_TYPE_PIXBUF, G_TYPE_BOOLEAN );
    for( i = 0; images[i]; i++ )
    {
        GtkTreeIter  iter;
        GdkPixbuf   *pixbuf;

        /* Render stock pixbuf */
        pixbuf = gtk_widget_render_icon( window, images[i],
                                         GTK_ICON_SIZE_LARGE_TOOLBAR, NULL );

        gtk_list_store_append( store, &amp;amp;iter );
        gtk_list_store_set( store, &amp;amp;iter, 0, pixbuf,
                                          1, (gboolean)( i % 2 ),
                                          -1 );
        g_object_unref( G_OBJECT( pixbuf ) );
    }

    /* Create filter and set visible column to second column */
    filter = gtk_tree_model_filter_new( GTK_TREE_MODEL( store ), NULL );
    gtk_tree_model_filter_set_visible_column(
            GTK_TREE_MODEL_FILTER( filter ), 1 );
    g_object_unref( G_OBJECT( store ) );

    /* Create icon view */
    iconview = gtk_icon_view_new_with_model( filter );
    g_object_unref( G_OBJECT( filter ) );
    gtk_icon_view_set_pixbuf_column( GTK_ICON_VIEW( iconview ), 0 );
    gtk_container_add( GTK_CONTAINER( window ), iconview );

    gtk_widget_show_all( window );
    gtk_main();

    return( 0 );
}
&lt;/pre&gt;
You can see that icon view only displays two images (GTK_STOCK_CANCEL and
GTK_STOCK_CLEAR), all other images are hidden.
&lt;br /&gt;&lt;br /&gt;
More powerful (and "expensive") way of filtering content is by using "visible
function" method. When using this method, you don't need separate column for
visibility information. Instead, function is called for each row in model, and
visibility of this row is determined by the return value of the function (if
your filter function returns TRUE, row will be visible, otherwise not).
Parameters of visible function uniquely describe one row of the child model. You
can then obtain data from this row and make your decision based on it.
&lt;pre class="brush: c"&gt;
#include &amp;lt;gtk/gtk.h&amp;gt;

/* This function returns TRUE if the value is odd. */
static gboolean
odd_numbers( GtkTreeModel *model,
             GtkTreeIter  *iter,
             gpointer      data )
{
    gint value;

    gtk_tree_model_get( model, iter, 0, &amp;amp;value, -1 );

    return( (gboolean)( value % 2 ) );
}

/* This function returns TRUE if the value is even. */
static gboolean
even_numbers( GtkTreeModel *model,
              GtkTreeIter  *iter,
              gpointer      data )
{
    gint value;

    gtk_tree_model_get( model, iter, 0, &amp;amp;value, -1 );

    return( (gboolean)( ( value + 1 ) % 2 ) );
}

int
main( int    argc,
      char **argv )
{
    /* Widgets */
    GtkWidget *window;
    GtkWidget *vbox;
    GtkWidget *frame;
    GtkWidget *combo;

    /* Cell renderer */
    GtkCellRenderer *cell;

    /* Data store */
    GtkListStore *store;
    GtkTreeModel *filter;

    /* Misc */
    gint i;

    gtk_init( &amp;amp;argc, &amp;amp;argv );

    window = gtk_window_new( GTK_WINDOW_TOPLEVEL );
    g_signal_connect( G_OBJECT( window ), &amp;quot;destroy&amp;quot;,
                      G_CALLBACK( gtk_main_quit ), NULL );

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

    /* Create data store */
    store = gtk_list_store_new( 1, G_TYPE_INT );
    for( i = 0; i &amp;lt; 20; i++ )
    {
        GtkTreeIter iter;

        gtk_list_store_append( store, &amp;amp;iter );
        gtk_list_store_set( store, &amp;amp;iter, 0, i, -1 );
    }

    /* Create combo box for odd numbers */
    frame = gtk_frame_new( &amp;quot;Odd numbers&amp;quot; );
    gtk_box_pack_start( GTK_BOX( vbox ), frame, FALSE, FALSE, 0 );

    filter = gtk_tree_model_filter_new( GTK_TREE_MODEL( store ), NULL );
    gtk_tree_model_filter_set_visible_func(
            GTK_TREE_MODEL_FILTER( filter ), odd_numbers, NULL, NULL );

    combo = gtk_combo_box_new_with_model( filter );
    gtk_container_add( GTK_CONTAINER( frame ), combo );
    g_object_unref( G_OBJECT( filter ) );

    cell = gtk_cell_renderer_text_new();
    gtk_cell_layout_pack_start( GTK_CELL_LAYOUT( combo ), cell, TRUE );
    gtk_cell_layout_set_attributes( GTK_CELL_LAYOUT( combo ), cell,
                                    &amp;quot;text&amp;quot;, 0, NULL );

    /* Create combo box for even numbers */
    frame = gtk_frame_new( &amp;quot;Even numbers&amp;quot; );
    gtk_box_pack_start( GTK_BOX( vbox ), frame, FALSE, FALSE, 0 );

    filter = gtk_tree_model_filter_new( GTK_TREE_MODEL( store ), NULL );
    gtk_tree_model_filter_set_visible_func(
            GTK_TREE_MODEL_FILTER( filter ), even_numbers, NULL, NULL );

    combo = gtk_combo_box_new_with_model( filter );
    gtk_container_add( GTK_CONTAINER( frame ), combo );
    g_object_unref( G_OBJECT( filter ) );

    cell = gtk_cell_renderer_text_new();
    gtk_cell_layout_pack_start( GTK_CELL_LAYOUT( combo ), cell, TRUE );
    gtk_cell_layout_set_attributes( GTK_CELL_LAYOUT( combo ), cell,
                                    &amp;quot;text&amp;quot;, 0, NULL );


    g_object_unref( G_OBJECT( store ) );

    /* Show everything */
    gtk_widget_show_all( window );
    gtk_main();

    return( 0 );
}
&lt;/pre&gt;
This application also shows how to reuse data using filters.
&lt;br /&gt;&lt;br /&gt;
This application concludes today's post. I hope you'll find it useful and will
be back for part three, where we'll be looking at data transformations.
&lt;br /&gt;&lt;br /&gt;
Bye.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6666239544818473000-2582569538150714134?l=blog.borovsak.si' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.borovsak.si/feeds/2582569538150714134/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.borovsak.si/2009/05/gtktreemodel-and-filtering-2.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6666239544818473000/posts/default/2582569538150714134'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6666239544818473000/posts/default/2582569538150714134'/><link rel='alternate' type='text/html' href='http://blog.borovsak.si/2009/05/gtktreemodel-and-filtering-2.html' title='GtkTreeModel and filtering (2)'/><author><name>Tadej Borovšak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-V5S0cD-5b_I/AAAAAAAAAAI/AAAAAAAAArU/nAyUYzn6Yx8/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6666239544818473000.post-1703362887812505059</id><published>2009-05-17T20:46:00.005+02:00</published><updated>2009-05-21T13:55:39.236+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='tutorial'/><category scheme='http://www.blogger.com/atom/ns#' term='GtkTreeModel'/><category scheme='http://www.blogger.com/atom/ns#' term='GTK+'/><title type='text'>GtkTreeModel and filtering (1)</title><content type='html'>Welcome to my humble blog.
&lt;br /&gt;&lt;br /&gt;
In this series of post I'll show you how to filter the contents of GtkTreeModel. Because this is quite large topic, I'll split it into four parts:
&lt;ol&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/05/gtktreemodel-and-filtering.html"&gt;Introduction to filtering&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/05/gtktreemodel-and-filtering-2.html"&gt;Limiting the amount of data being displayed&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/05/gtktreemodel-and-filtering-3.html"&gt;Modifying data's representation (data transformations)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Performance issues related to filtering&lt;/li&gt;
&lt;/ol&gt;
Now let's start with a short introduction.
&lt;br /&gt;&lt;br /&gt;
When we have data stored inside &lt;a href="http://library.gnome.org/devel/gtk/stable/GtkTreeModel.html"&gt;GtkTreeModel&lt;/a&gt;, we usually attach this model to some &lt;a href="http://library.gnome.org/devel/gtk/stable/GtkCellLayout.html"&gt;GtkCellLayout&lt;/a&gt; enabled widget (&lt;a href="http://library.gnome.org/devel/gtk/stable/GtkTreeView.html"&gt;GtkTreeView&lt;/a&gt;, &lt;a href="http://library.gnome.org/devel/gtk/stable/GtkIconView.html"&gt;GtkIconView&lt;/a&gt; or &lt;a href="http://library.gnome.org/devel/gtk/stable/GtkComboBox.html"&gt;GtkComboBox&lt;/a&gt;) and that widget then displays the entire contents of the model. But if we wish to filter our data, we need to add another element in between model and presenting widget: &lt;a href="http://library.gnome.org/devel/gtk/stable/GtkTreeModelFilter.html"&gt;GtkTreeModelFilter&lt;/a&gt;.
&lt;br /&gt;&lt;br /&gt;
GtkTreeModelFilter is a powerful tool which can help us reuse our data and display it in number of different ways. GtkTreeModelFilter wraps around it's child GtkTreeModel and provides two groups of functionalities:
&lt;ol&gt;
  &lt;li&gt;limiting the amount of data that is being displayed&lt;/li&gt;
  &lt;li&gt;modifying data's representation&lt;/li&gt;
&lt;/ol&gt;
We can limit the amount of data being displayed on two levels. First level would be specifying a "virtual root" when creating new filter. This way we can display only one branch of our whole tree. The second level is filtering data on a row-by-row basis. When we filter data this way, GtkTreeModelFilter inspects each line and decides if this line should be visible or not. This decision is either based on value inside child's GtkTreeModel (I'll name this method "visible column method", for more info check out next post) or on filter function's return value ("visible function method").
&lt;br /&gt;&lt;br /&gt;
Modifying data's representation is probably the most powerful capability of the GtkTreeModelFilter. You can transform original data to meet almost any need. All you need to do is write a function that does the transformation. But this flexibility comes at a price: since transformation function is called whenever the data needs to be displayed, it may slow your application down a bit (or a lot;).
&lt;br /&gt;&lt;br /&gt;
And that's almost it for today. There is only one thing left to do: you must read first five chapters of &lt;a href="http://scentric.net/tutorial/"&gt;GtkTreeView tutorial&lt;/a&gt; before reading my next post. (I'm not kidding, go reading now!!!)
&lt;br /&gt;&lt;br /&gt;
Bye.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6666239544818473000-1703362887812505059?l=blog.borovsak.si' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.borovsak.si/feeds/1703362887812505059/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.borovsak.si/2009/05/gtktreemodel-and-filtering.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6666239544818473000/posts/default/1703362887812505059'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6666239544818473000/posts/default/1703362887812505059'/><link rel='alternate' type='text/html' href='http://blog.borovsak.si/2009/05/gtktreemodel-and-filtering.html' title='GtkTreeModel and filtering (1)'/><author><name>Tadej Borovšak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-V5S0cD-5b_I/AAAAAAAAAAI/AAAAAAAAArU/nAyUYzn6Yx8/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6666239544818473000.post-6500151825255567206</id><published>2009-05-07T01:22:00.005+02:00</published><updated>2009-05-21T13:57:49.543+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='GtkLabel'/><category scheme='http://www.blogger.com/atom/ns#' term='Tips'/><category scheme='http://www.blogger.com/atom/ns#' term='GTK+'/><title type='text'>Wrapping and resizing GtkLabel dynamically</title><content type='html'>This is just a quick post that describes a small hack that enables us to resize
and wrap GtkLabel dynamically. But before I show you the code, I want to explain
some things about gtk's dynamic size negotiation and why this is causing us
troubles with GtkLabel and wrapping.
&lt;br /&gt;&lt;br /&gt;
There are two separate phases when negotiating a size: size requisition and
size allocation. In the requisition phase, parent widgets ask their children
how much space they need and then report sum of those values to the parent.
Allocation phase is accomplished in opposite direction: parent widgets inform
their children how much space they actually got.
&lt;br /&gt;&lt;br /&gt;
Let's take an imaginary application, composed from single main GtkWindow, that
has a GtkVBox packed in. GtkVBox contains two labels. When the requisition
phase begins, main window asks vbox how much space it needs, and vbox asks the
same question both labels. Labels return some value to vbox, which sums them up
and returns that to main window. This finishes the requisition phase and now
main window knows how much space is needed. After some calculations it decides
how much space will actually be available and passes that value to vbox. This
is when the allocation phase begins. Vbox now splits the available space between
the children, which concludes size negotiation.
&lt;br /&gt;&lt;br /&gt;
This process may seem "over engineered" at first glance, but it enables us to
create very flexible interfaces, which are necessary if the application is going
to be translated.
&lt;br /&gt;&lt;br /&gt;
Now we can understand why labels cannot adjust their width according to parent's
size: because they know nothing about it. Parent will calculate it's preferred
size based on children sizes and not the other way around.
&lt;br /&gt;&lt;br /&gt;
So how can we dynamically adjust the label's width then? We can do it by
monitoring "size-allocate" signal and request the size that will just fit into
parent widget. But there is one last catch here: if we set the child's width to
the allocation width, parent widget won't be able to shrink, which makes our
application "grow-only". To avoid that, we make the label slightly smaller than
allocation width. Voila, and here we have our dynamic label.
&lt;br /&gt;&lt;br /&gt;
Keep in mind though that this method is a hack and should be used only if there
is no other solution available. Why? Applications that use this hack will behave
somewhat strange, since parent can be shrunk only for the difference between
allocation width and actual label width during one allocate event. You've been
warned.
&lt;br /&gt;&lt;br /&gt;
And finally, the code. As usual, feel free to abuse it anyway you wish.
&lt;br /&gt;&lt;br /&gt;
Bye.
&lt;br /&gt;&lt;br /&gt;
&lt;pre class="brush: c"&gt;
#include &amp;lt;gtk/gtk.h&amp;gt;

static void
cb_allocate( GtkWidget     *label,
             GtkAllocation *allocation,
             gpointer       data )
{
    gtk_widget_set_size_request( label, allocation-&amp;gt;width - 2, -1 );
}


int
main( int    argc,
      char **argv )
{
    GtkWidget     *window;
    GtkWidget     *label;

    gtk_init( &amp;amp;argc, &amp;amp;argv );

    window = gtk_window_new( GTK_WINDOW_TOPLEVEL );
    gtk_container_set_border_width( GTK_CONTAINER( window ), 10 );
    g_signal_connect( G_OBJECT( window ), &amp;quot;destroy&amp;quot;,
                      G_CALLBACK( gtk_main_quit ), NULL );

    label = gtk_label_new( &amp;quot;One long line of text that will almost definitely &amp;quot;
                           &amp;quot;wrap once, if not twice.&amp;quot; );
    gtk_label_set_line_wrap( GTK_LABEL( label ), TRUE );
    g_signal_connect( G_OBJECT( label ), &amp;quot;size-allocate&amp;quot;,
                      G_CALLBACK( cb_allocate ), NULL );
    gtk_container_add( GTK_CONTAINER( window ), label );

    gtk_widget_show_all( window );
    gtk_main();

    return( 0 );
}
&lt;/pre&gt;
&lt;pre class="brush: python"&gt;
#!/usr/bin/env python
# vim: set fileencoding=utf-8

import pygtk
pygtk.require( &amp;quot;2.0&amp;quot; )
import gtk

class Sample( gtk.Window ):
    def __init__( self ):
        gtk.Window.__init__( self )
        self.connect( &amp;quot;destroy&amp;quot;, lambda *w: gtk.main_quit() )

        label = gtk.Label( &amp;quot;One long line of text that will almost definitely &amp;quot;
                           &amp;quot;wrap once, if not twice.&amp;quot; )
        label.set_line_wrap( True )
        label.connect( &amp;quot;size-allocate&amp;quot;, self.cb_allocate )
        self.add( label )

    def cb_allocate( self, label, allocation ):
        label.set_size_request( allocation.width - 2, -1 )

if __name__ == &amp;quot;__main__&amp;quot;:
    win = Sample()
    win.show_all()
    gtk.main()
&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6666239544818473000-6500151825255567206?l=blog.borovsak.si' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.borovsak.si/feeds/6500151825255567206/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.borovsak.si/2009/05/wrapping-adn-resizing-gtklabel.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6666239544818473000/posts/default/6500151825255567206'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6666239544818473000/posts/default/6500151825255567206'/><link rel='alternate' type='text/html' href='http://blog.borovsak.si/2009/05/wrapping-adn-resizing-gtklabel.html' title='Wrapping and resizing GtkLabel dynamically'/><author><name>Tadej Borovšak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-V5S0cD-5b_I/AAAAAAAAAAI/AAAAAAAAArU/nAyUYzn6Yx8/s512-c/photo.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6666239544818473000.post-7816986215153021121</id><published>2009-05-06T09:06:00.006+02:00</published><updated>2009-05-20T06:59:29.456+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Tips'/><category scheme='http://www.blogger.com/atom/ns#' term='GTK+'/><title type='text'>Using pop-up windows</title><content type='html'>I was quite bussy last week and haven't posted anything, but here I am back with
new topic.
&lt;br /&gt;&lt;br /&gt;
We'll be looking at pop-up windows today (windows that are not visible
throughout the applications life cycle and are usually shown in response to some
user's action - preference/settings dialog being most usually implemented like
this). I'll describe different methods of creating pop-up windows, what being
"modal" and "transient" means and how creating user interfaces with glade fits
in. At the end, you'll also find sample applications that demonstrate some of
the principles found in this post.
&lt;br /&gt;&lt;br /&gt;
&lt;span class="s_title"&gt;Important information&lt;/span&gt;
&lt;br /&gt;&lt;br /&gt;
When creating new pop-up window, you may be tempted to create window like this:
&lt;pre class="brush: c"&gt;window = gtk_window_new( GTK_WINDOW_POPUP );&lt;/pre&gt;
or, if you're using Python:
&lt;pre class="brush: python"&gt;window = gtk.Window( gtk.WINDOW_POPUP )&lt;/pre&gt;
Just don't. Creating this kind of windows is reserved for tooltips, menus and other specialized occasions. What you'll want to do is to set window type hint with &lt;span class="cf"&gt;gtk_window_set_type_hint&lt;/span&gt; function (&lt;span class="pf"&gt;set_type_hint&lt;/span&gt; method).
&lt;br /&gt;&lt;br /&gt;
You've been warned.
&lt;br /&gt;&lt;br /&gt;
&lt;span class="s_title"&gt;Creating pop-up window&lt;/span&gt;
&lt;br /&gt;&lt;br /&gt;
As I already wrote a couple of times before, there are two quite distinct ways
of handling pop-up windows (I made up those names, so don't be shocked if
someone asks you: "Where the heck did you learn that!?";):
&lt;ol&gt;
  &lt;li&gt;&lt;em&gt;create-destroy&lt;/em&gt; approach, where pop-up window is created just
      before it's shown and destroyed after not being needed anymore&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;show-hide&lt;/em&gt; approach, where pop-up window is created only once,
      usually at application startup or just before the first show&lt;/li&gt;
&lt;/ol&gt;
There are pros and cons to both of those methods and when combined, we could say
that:
&lt;ul&gt;
  &lt;li&gt;create-destroy method is more memory efficient, since window is only held in
      memory when needed, but might be a little slower compared to show-hide
   method, because window needs to be recreated every time we want to show
   it&lt;/li&gt;
  &lt;li&gt;show-hide method uses more memory, since window is loaded throughout the
      application's life cycle, but window is generally faster to show up&lt;/li&gt;
&lt;/ul&gt;
Which method is better? As usual, there is no single truth. On embedded systems,
where memory available is scarce, you'll probably opt for the first method,
while on desktop systems, second method might be preferred since it's a bit
faster. Second method also makes it easier to make sure only one copy of pop-up
is shown (see cb_show_prop callback function in second example code for more
info).
&lt;br /&gt;&lt;br /&gt;
What about glade? You can use both methods with glade, although the second
method might feel a bit more natural, since interfaces tend to be created at
application startup from a single glade UI file.
&lt;br /&gt;&lt;br /&gt;
&lt;span class="s_title"&gt;Making sure user is pleased AND plays by the rules&lt;/span&gt;
&lt;br /&gt;&lt;br /&gt;
This is where modality and making window transient comes into play.
&lt;br /&gt;&lt;br /&gt;
Making window modal means restricting user's interaction with other open
windows. Windows can be made application modal (when window is opened,
interacting with other windows in the same application is not possible) or
system modal (interaction with other windows is not possible, regardless of
application window belongs to), although GTK+ only supports making windows
application modal (which is a "Good Thing&amp;trade;" IMHO).
&lt;br /&gt;&lt;br /&gt;
Since any kind of restriction usually makes people unhappy, try to make windows
modal only if absolutely necessary. One good reason for making your window modal
is possibility of data loss if user interacts with other windows.
&lt;br /&gt;&lt;br /&gt;
Making pop-up window transient for the main window means allowing window
managers to do some magic with our window like keeping it on top of and
centering it over the main window. But this is only a hint, hence the "allow" in
the first sentence; window manager might do nothing or eat your pet (but in
general, window managers are quite well mannered and do what you expect;).
&lt;br /&gt;&lt;br /&gt;
&lt;span class="s_title"&gt;Sample code&lt;/span&gt;
&lt;br /&gt;&lt;br /&gt;
In order to demonstrate some of the principles described above, I wrote two
sample applications (one in C and another one in Python). Feel free to dissect
them and use any newly acquired knowledge in your applications. Happy coding.
&lt;br /&gt;&lt;br /&gt;
Bye.
&lt;br /&gt;&lt;br /&gt;
&lt;pre class="brush: python"&gt;
#!/usr/bin/env python
# vim: set fileencoding=utf-8

# Sample PyGTK application that demonstrates create-destroy method of
# creating pop-up windows.

import pygtk
pygtk.require( &amp;quot;2.0&amp;quot; )
import gtk

class Sample( gtk.Window ):
    def __init__( self ):
        gtk.Window.__init__( self )
        self.connect( &amp;quot;destroy&amp;quot;, lambda *w: gtk.main_quit() )

        button = gtk.Button( &amp;quot;Properties&amp;quot; )
        button.connect( &amp;quot;clicked&amp;quot;, self.cb_show_prop )
        self.add( button )

    def cb_show_prop( self, button ):
        popup = gtk.Window()
        popup.set_title( &amp;quot;Properties&amp;quot; )

        popup.add( gtk.Label( &amp;quot;No properties here, but this popup still\n&amp;quot;
                              &amp;quot;represents a threat to data integrity.;)&amp;quot; ) )

        popup.set_modal( True )
        popup.set_transient_for( self )
        popup.set_type_hint( gtk.gdk.WINDOW_TYPE_HINT_DIALOG )

        popup.connect( &amp;quot;destroy&amp;quot;, lambda *w: gtk.main_quit() )

        popup.show_all()

if __name__ == &amp;quot;__main__&amp;quot;:
    win = Sample()
    win.show_all()
    gtk.main()
&lt;/pre&gt;
&lt;pre class="brush: c"&gt;
#include &amp;lt;gtk/gtk.h&amp;gt;

G_MODULE_EXPORT void
cb_show_prop( GtkButton *button,
              GtkWidget *popup )
{
    gtk_window_present( GTK_WINDOW( popup ) );
}

int
main( int    argc,
      char **argv )
{
    GtkBuilder *builder;
    GtkWidget  *window;
    GtkWidget  *popup;

    gtk_init( &amp;amp;argc, &amp;amp;argv );

    builder = gtk_builder_new();
    gtk_builder_add_from_file( builder, &amp;quot;popup.builder&amp;quot;, NULL );

    window = GTK_WIDGET( gtk_builder_get_object( builder, &amp;quot;main_w&amp;quot; ) );
    popup  = GTK_WIDGET( gtk_builder_get_object( builder, &amp;quot;popup_w&amp;quot; ) );

    gtk_builder_connect_signals( builder, popup );
    g_object_unref( G_OBJECT( builder ) );

    gtk_widget_show_all( window );
    gtk_main();

    return( 0 );
}
&lt;/pre&gt;
&lt;pre class="brush: xml"&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot;?&amp;gt;
&amp;lt;interface&amp;gt;
  &amp;lt;!-- interface-requires gtk+ 2.12 --&amp;gt;
  &amp;lt;!-- interface-naming-policy project-wide --&amp;gt;
  &amp;lt;object class=&amp;quot;GtkWindow&amp;quot; id=&amp;quot;main_w&amp;quot;&amp;gt;
    &amp;lt;signal name=&amp;quot;destroy&amp;quot; handler=&amp;quot;gtk_main_quit&amp;quot;/&amp;gt;
    &amp;lt;child&amp;gt;
      &amp;lt;object class=&amp;quot;GtkButton&amp;quot; id=&amp;quot;button1&amp;quot;&amp;gt;
        &amp;lt;property name=&amp;quot;label&amp;quot; translatable=&amp;quot;yes&amp;quot;&amp;gt;gtk-preferences&amp;lt;/property&amp;gt;
        &amp;lt;property name=&amp;quot;visible&amp;quot;&amp;gt;True&amp;lt;/property&amp;gt;
        &amp;lt;property name=&amp;quot;can_focus&amp;quot;&amp;gt;True&amp;lt;/property&amp;gt;
        &amp;lt;property name=&amp;quot;receives_default&amp;quot;&amp;gt;True&amp;lt;/property&amp;gt;
        &amp;lt;property name=&amp;quot;use_stock&amp;quot;&amp;gt;True&amp;lt;/property&amp;gt;
        &amp;lt;signal name=&amp;quot;clicked&amp;quot; handler=&amp;quot;cb_show_prop&amp;quot;/&amp;gt;
      &amp;lt;/object&amp;gt;
    &amp;lt;/child&amp;gt;
  &amp;lt;/object&amp;gt;
  &amp;lt;object class=&amp;quot;GtkWindow&amp;quot; id=&amp;quot;popup_w&amp;quot;&amp;gt;
    &amp;lt;property name=&amp;quot;title&amp;quot; translatable=&amp;quot;yes&amp;quot;&amp;gt;Properties&amp;lt;/property&amp;gt;
    &amp;lt;signal name=&amp;quot;delete_event&amp;quot; handler=&amp;quot;gtk_widget_hide_on_delete&amp;quot;/&amp;gt;
    &amp;lt;child&amp;gt;
      &amp;lt;object class=&amp;quot;GtkLabel&amp;quot; id=&amp;quot;label1&amp;quot;&amp;gt;
        &amp;lt;property name=&amp;quot;visible&amp;quot;&amp;gt;True&amp;lt;/property&amp;gt;
        &amp;lt;property name=&amp;quot;label&amp;quot; translatable=&amp;quot;yes&amp;quot;&amp;gt;This pop-up is safe.&amp;lt;/property&amp;gt;
      &amp;lt;/object&amp;gt;
    &amp;lt;/child&amp;gt;
  &amp;lt;/object&amp;gt;
&amp;lt;/interface&amp;gt;
&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6666239544818473000-7816986215153021121?l=blog.borovsak.si' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.borovsak.si/feeds/7816986215153021121/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.borovsak.si/2009/05/using-pop-up-windows.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6666239544818473000/posts/default/7816986215153021121'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6666239544818473000/posts/default/7816986215153021121'/><link rel='alternate' type='text/html' href='http://blog.borovsak.si/2009/05/using-pop-up-windows.html' title='Using pop-up windows'/><author><name>Tadej Borovšak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-V5S0cD-5b_I/AAAAAAAAAAI/AAAAAAAAArU/nAyUYzn6Yx8/s512-c/photo.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6666239544818473000.post-2631905803234960204</id><published>2009-04-29T15:25:00.008+02:00</published><updated>2009-05-17T21:14:47.694+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='GtkDialog'/><category scheme='http://www.blogger.com/atom/ns#' term='tutorial'/><category scheme='http://www.blogger.com/atom/ns#' term='GTK+'/><title type='text'>GtkDialog tutorial - part 2</title><content type='html'>Hello everybody!
&lt;br /&gt;&lt;br /&gt;
Creating second part of this tutorial took a little longer than I anticipated to
create, main reason being nasty little bug in Glade3-3.6.x, which produced some
"interesting" results. You can find more details &lt;a
href="http://bugzilla.gnome.org/show_bug.cgi?id=580745"&gt;here&lt;/a&gt;.
&lt;br /&gt;&lt;br /&gt;
Contents:
&lt;ol&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/04/gtkdialog-tutorial-part-1.html"&gt;GtkDialog tutorial - part 1 (manual dialog creation)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/04/gtkdialog-tutorial-part-2.html"&gt;GtkDialog tutorial - part 2 (build dialog using Glade)&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;span class="s_title"&gt;Build using Glade&lt;/span&gt;
&lt;br /&gt;&lt;br /&gt;
Now let's create this application already. As usual, try to create glade GUI,
based on the code from last post, yourself first (remember, "Repetitio Est
...";), and only then check this &lt;a href="http://www.screentoaster.com/watch/stU0tWQk1IR11eQFxUWFNRUVdV/gtkdialog_and_glade"&gt;video out&lt;/a&gt;. Yes, you read it right, video;)
&lt;br /&gt;&lt;br /&gt;
Here is the outline of the video (for those who cannot watch it):
&lt;ul&gt;
 &lt;li&gt;Create main window
  &lt;ul&gt;
   &lt;li&gt;set border property to 10&lt;/li&gt;
   &lt;li&gt;connect delete-event signal to cb_delete_event&lt;/li&gt;
   &lt;li&gt;connect destroy signal to gtk_main_quit&lt;/li&gt;
   &lt;li&gt;add button
    &lt;ul&gt;
     &lt;li&gt;set it's stock id to gtk-about&lt;/li&gt;
     &lt;li&gt;connect clicked signal to cb_show_about callback&lt;/li&gt;
    &lt;/ul&gt;
   &lt;/li&gt;
  &lt;/ul&gt;
 &lt;/li&gt;
 &lt;li&gt;Create dialog
  &lt;ul&gt;
   &lt;li&gt;Add label
    &lt;ul&gt;
     &lt;li&gt;Set it's label property to "Are you sure you want to
 quit?&lt;/li&gt;
     &lt;li&gt;Make it modal.&lt;/li&gt;
     &lt;li&gt;Make it transient for main widow.&lt;/li&gt;
    &lt;/ul&gt;
   &lt;/li&gt;
   &lt;li&gt;Add button
    &lt;ul&gt;
     &lt;li&gt;Set it's stock id to gtk-yes&lt;/li&gt;
     &lt;li&gt;Set it's response id to 1&lt;/li&gt;
    &lt;/ul&gt;
   &lt;/li&gt;
   &lt;li&gt;Add button
    &lt;ul&gt;
     &lt;li&gt;Set it's stock id to gtk-no&lt;/li&gt;
     &lt;li&gt;Set it's response id to 2&lt;/li&gt;
    &lt;/ul&gt;
   &lt;/li&gt;
  &lt;/ul&gt;
 &lt;/li&gt;
 &lt;li&gt;Create about dialog
  &lt;ul&gt;
   &lt;li&gt;Set programe name property to Sample Dialog App&lt;/li&gt;
   &lt;li&gt;Set version property to 0.2&lt;/li&gt;
   &lt;li&gt;Set copyright string to something that suits you&lt;/li&gt;
   &lt;li&gt;Set website URL to tadeboro.blogspot.com&lt;/li&gt;
  &lt;/ul&gt;
 &lt;/li&gt;
&lt;/ul&gt;
You can download builder file from &lt;a href="http://tadeboro.googlepages.com/dialog.builder"&gt;here&lt;/a&gt;. WARNING: if you're
using glade3-3.6.x, you should manually rename about button to something like
about. See my bug report I mentioned at the beginning of the post for more
details. (My uploaded glade project is already fixed.)
&lt;br /&gt;&lt;br /&gt;
What do you think for the first time? What is left for us to do is to write some
code to support the GUI. First, we need to load GUI. We did that a few times
before so you should be familiar with them by now.
&lt;pre class="brush: c"&gt;    /* Create builder and load interface */
    builder = gtk_builder_new();
    gtk_builder_add_from_file( builder, &amp;quot;dialog.builder&amp;quot;, NULL );

    /* Obtain widgets that we need */
    window = GTK_WIDGET( gtk_builder_get_object( builder, &amp;quot;window1&amp;quot; ) );
    data.quit = GTK_WIDGET( gtk_builder_get_object( builder, &amp;quot;dialog1&amp;quot; ) );
    data.about = GTK_WIDGET( gtk_builder_get_object( builder, &amp;quot;aboutdialog1&amp;quot; ) );

    /* Connect callbacks */
    gtk_builder_connect_signals( builder, &amp;amp;data );

    /* Destroy builder */
    g_object_unref( G_OBJECT( builder ) );
&lt;/pre&gt;
&lt;pre class="brush: python"&gt;
    def __init__( self ):
        builder = gtk.Builder()
        builder.add_from_file( &amp;quot;dialog.builder&amp;quot; )
        
        self.window       = builder.get_object( &amp;quot;window1&amp;quot; )
        self.about_dialog = builder.get_object( &amp;quot;aboutdialog1&amp;quot; )
        self.quit_dialog  = builder.get_object( &amp;quot;dialog1&amp;quot; )

        builder.connect_signals( self )
&lt;/pre&gt;
Writting callback is piece of cake this time, since all that we need to do is to
run the right dialog and hide it after use.
&lt;pre class="brush: c"&gt;
G_MODULE_EXPORT gboolean
cb_delete_event( GtkWidget *window,
                 GdkEvent  *event,
                 Data      *data )
{
    gint response = 1;

    /* Run dialog */
    response = gtk_dialog_run( GTK_DIALOG( data-&amp;gt;quit ) );
    gtk_widget_hide( data-&amp;gt;quit );

    return( 1 != response );
}

G_MODULE_EXPORT void
cb_show_about( GtkButton *button,
               Data      *data )
{
    /* Run dialog */
    gtk_dialog_run( GTK_DIALOG( data-&amp;gt;about ) );
    gtk_widget_hide( data-&amp;gt;about );
}
&lt;/pre&gt;
&lt;pre class="brush: python"&gt;
    def cb_delete_event( self, window, event ):
        # Run dialog
        response = self.quit_dialog.run()
        self.quit_dialog.hide()

        return response != 1

    def cb_show_about( self, button ):
        # Run dialog
        self.about_dialog.run()
        self.about_dialog.hide()
&lt;/pre&gt;
And that's it! The whole application listings are posted at the end.
&lt;br /&gt;&lt;br /&gt;
I hope you'll join me next time when we'll be tackling ... hmm, I don't know
what yet. Suggestions are welcome.
&lt;br /&gt;&lt;br /&gt;
Bye
&lt;br /&gt;&lt;br /&gt;
&lt;pre class="brush: c"&gt;
/*
 * Compile me with:
 *  gcc -o dialog1 dialog1.c $(pkg-config --cflags --libs gtk+-2.0 \
 *        gmodule-export-2.0)
 */

#include &amp;lt;gtk/gtk.h&amp;gt;

typedef struct _Data Data;
struct _Data
{
    GtkWidget *quit;
    GtkWidget *about;
};

G_MODULE_EXPORT gboolean
cb_delete_event( GtkWidget *window,
                 GdkEvent  *event,
                 Data      *data )
{
    gint response = 1;

    /* Run dialog */
    response = gtk_dialog_run( GTK_DIALOG( data-&amp;gt;quit ) );
    gtk_widget_hide( data-&amp;gt;quit );

    return( 1 != response );
}

G_MODULE_EXPORT void
cb_show_about( GtkButton *button,
               Data      *data )
{
    /* Run dialog */
    gtk_dialog_run( GTK_DIALOG( data-&amp;gt;about ) );
    gtk_widget_hide( data-&amp;gt;about );
}

int
main( int    argc,
      char **argv )
{
    GtkBuilder *builder;
    GtkWidget  *window;
    Data        data;

    gtk_init( &amp;amp;argc, &amp;amp;argv );

    /* Create builder and load interface */
    builder = gtk_builder_new();
    gtk_builder_add_from_file( builder, &amp;quot;dialog.builder&amp;quot;, NULL );

    /* Obtain widgets that we need */
    window = GTK_WIDGET( gtk_builder_get_object( builder, &amp;quot;window1&amp;quot; ) );
    data.quit = GTK_WIDGET( gtk_builder_get_object( builder, &amp;quot;dialog1&amp;quot; ) );
    data.about = GTK_WIDGET( gtk_builder_get_object( builder, &amp;quot;aboutdialog1&amp;quot; ) );

    /* Connect callbacks */
    gtk_builder_connect_signals( builder, &amp;amp;data );

    /* Destroy builder */
    g_object_unref( G_OBJECT( builder ) );

    /* Show main window and start main loop */
    gtk_widget_show( window );
    gtk_main();

    return( 0 );
}
&lt;/pre&gt;
&lt;pre class="brush: python"&gt;
#!/usr/bin/env python
# vim: set fileencoding=utf-8

import pygtk
pygtk.require( &amp;quot;2.0&amp;quot; )
import gtk

class Sample:
    def gtk_main_quit( self, window ):
        gtk.main_quit()

    def cb_delete_event( self, window, event ):
        # Run dialog
        response = self.quit_dialog.run()
        self.quit_dialog.hide()

        return response != 1

    def cb_show_about( self, button ):
        # Run dialog
        self.about_dialog.run()
        self.about_dialog.hide()

    def __init__( self ):
        builder = gtk.Builder()
        builder.add_from_file( &amp;quot;dialog.builder&amp;quot; )
        
        self.window       = builder.get_object( &amp;quot;window1&amp;quot; )
        self.about_dialog = builder.get_object( &amp;quot;aboutdialog1&amp;quot; )
        self.quit_dialog  = builder.get_object( &amp;quot;dialog1&amp;quot; )

        builder.connect_signals( self )

if __name__ == &amp;quot;__main__&amp;quot;:
    win = Sample()
    win.window.show_all()
    gtk.main()
&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6666239544818473000-2631905803234960204?l=blog.borovsak.si' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.borovsak.si/feeds/2631905803234960204/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.borovsak.si/2009/04/gtkdialog-tutorial-part-2.html#comment-form' title='13 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6666239544818473000/posts/default/2631905803234960204'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6666239544818473000/posts/default/2631905803234960204'/><link rel='alternate' type='text/html' href='http://blog.borovsak.si/2009/04/gtkdialog-tutorial-part-2.html' title='GtkDialog tutorial - part 2'/><author><name>Tadej Borovšak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-V5S0cD-5b_I/AAAAAAAAAAI/AAAAAAAAArU/nAyUYzn6Yx8/s512-c/photo.jpg'/></author><thr:total>13</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6666239544818473000.post-6659789726771674246</id><published>2009-04-25T16:31:00.005+02:00</published><updated>2009-05-17T21:15:00.866+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='GtkDialog'/><category scheme='http://www.blogger.com/atom/ns#' term='tutorial'/><category scheme='http://www.blogger.com/atom/ns#' term='GTK+'/><title type='text'>GtkDialog tutorial - part 1</title><content type='html'>Hello everybody and welcome back!&lt;br /&gt;
&lt;br /&gt;
In next two posts we'll be looking at GtkDialog widget. Today's part will be dealing with manual dialog creation, which will allow us to get a good grip at GtkDialog's internal workings. In second part, we'll try to use our newly acquired knowledge to recreate sample application using glade interface builder.&lt;br /&gt;
&lt;br /&gt;
Contents:
&lt;ol&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/04/gtkdialog-tutorial-part-1.html"&gt;GtkDialog tutorial - part 1 (manual dialog creation)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/04/gtkdialog-tutorial-part-2.html"&gt;GtkDialog tutorial - part 2 (build dialog using Glade)&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;span class="s_title"&gt;Manual creation&lt;/span&gt;
&lt;br /&gt;&lt;br /&gt;
A good place to start learning about GtkDialog is &lt;a href="http://library.gnome.org/devel/gtk/stable/GtkDialog.html#GtkDialog.description"&gt;description section of the API reference&lt;/a&gt;. It'll give you a nice and concise overview of the dialog and make it easier to follow this post.&lt;br /&gt;
&lt;br /&gt;
Done reading API docs? OK, let's move on. Sample application that we'll develop for this tutorial will consist of main window with one "About" button, about dialog that will be shown when we click "About" button  and quit conformation dialog, which will ask us if we really want to quit. Now let's start coding.&lt;br /&gt;
&lt;br /&gt;
First thing that we need to create is our main window and connect "delete-event" and "destroy" signals; add "About" stock button to window and connect "clicked" signal. Try writing it yourself before looking at my sample code, since we're trying to learn here (remember, "Repetitio Est Mater Studiorum").&lt;pre class="brush: c"&gt;
int
main( int    argc,
      char **argv )
{
    GtkWidget *window;
    GtkWidget *button;

    gtk_init( &amp;amp;argc, &amp;amp;argv );

    /* Create main window */
    window = gtk_window_new( GTK_WINDOW_TOPLEVEL );
    gtk_container_set_border_width( GTK_CONTAINER( window ), 10 );

    /* Connect delete event signal - this is where we'll show our
     * quit conformation dialog. */
    g_signal_connect( G_OBJECT( window ), &amp;quot;delete-event&amp;quot;,
                      G_CALLBACK( cb_delete_event ), NULL );

    /* Connect destroy signal, which will be emitted if we return
     * FALSE from our cb_delete_event function. */
    g_signal_connect( G_OBJECT( window ), &amp;quot;destroy&amp;quot;,
                      G_CALLBACK( gtk_main_quit ), NULL );

    /* Create button ... */
    button = gtk_button_new_from_stock( GTK_STOCK_ABOUT );

    /* ... and connect clicked signal to handler that will create
     * and show about dialog. */
    g_signal_connect( G_OBJECT( button ), &amp;quot;clicked&amp;quot;,
                      G_CALLBACK( cb_show_about ), window );
    gtk_container_add( GTK_CONTAINER( window ), button );

    /* Show our main window and start main loop. */
    gtk_widget_show_all( window );
    gtk_main();

    return( 0 );
}
&lt;/pre&gt;
&lt;pre class="brush: python"&gt;
class Sample( gtk.Window ):

    # More code here

    def __init__( self ):
        # Create main window
        gtk.Window.__init__( self )
        self.set_border_width( 10 )

        # Connect delete event signal - this is where we'll show our
        # quit conformation dialog.
        self.connect( 'delete-event', self.cb_delete_event )

        # Connect destroy signal, which will be emitted if we return
        # False from our self.cb_delete_event method. */
        self.connect( 'destroy', lambda *w: gtk.main_quit() )

        # Create button ...
        button = gtk.Button( None, gtk.STOCK_ABOUT )

        # ... and connect clicked signal to handler that will create
        # and show about dialog.
        button.connect( 'clicked', self.cb_show_about )
        self.add( button )

        # Add &amp;quot;placeholders&amp;quot; for dialogs
        self.about_dialog = None
        self.quit_dialog  = None

if __name__ == &amp;quot;__main__&amp;quot;:
    win = Sample()
    win.show_all()
    gtk.main()
&lt;/pre&gt;
Now, we'll write delete event handler and create a conformation dialog. This dialog will be very simple, with label saying "Are you sure you want to quit?" and two buttons, "Yes" and "No". I'll show you how to create dialog step-by-step and by using convenience function.&lt;br /&gt;
&lt;br /&gt;
First, we need to create new dialog with &lt;span class="cf"&gt;gtk_dialog_new&lt;/span&gt; (&lt;span class="pf"&gt;gtk.Dialog&lt;/span&gt;), make it modal and transient for main window and set it's title to "Conformation". Now we need to add two buttons to the dialog with &lt;span class="cf"&gt;gtk_dialog_add_button&lt;/span&gt; function (&lt;span class="pf"&gt;add_button&lt;/span&gt; method). If you're looking at the API docs for this function now (and if you don't, you should from now on;), you see that the last parameter is response id. This value will be returned from &lt;span class="cf"&gt;gtk_dialog_run&lt;/span&gt; function (&lt;span class="pf"&gt;run&lt;/span&gt; method) and will enable us to determine which button has been pressed. We'll assign 1 to "Yes" button and 2 to "No" button. Last thing we need to do is to add label to content area of the widget. And this is where things get a little tricky, since some incompatible changes have been made between 2.12 and 2.14 version of gtk. If we're using gtk+-2.12 or less, we need to access content area directly like a struct member in C and like a attribute in Python, and if our gtk+ version is 2.14 or more, we need to use accessor function &lt;span class="cf"&gt;gtk_dialog_get_content_area&lt;/span&gt; (&lt;span class="pf"&gt;get_content_area&lt;/span&gt; method). See code for more details.&lt;br /&gt;
&lt;br /&gt;
To finish our callback function, we'll write an if statement which will return false if user clicked "Yes" and true otherwise.&lt;pre class="brush: c"&gt;
static gboolean
cb_delete_event( GtkWidget *window,
                 GdkEvent  *event,
                 gpointer   data )
{
    /* We'll create dialog only the first time we enter this callback.
     * After first time, we'll just show it. */
    static GtkWidget *dialog = NULL;
    gint              response;

    if( ! dialog )
    {
        GtkWidget *label;
        GtkWidget *box;
#if 1
        /* Create dialog */
        dialog = gtk_dialog_new();

        /* Set it modal and transient for main window. */
        gtk_window_set_modal( GTK_WINDOW( dialog ), TRUE );
        gtk_window_set_transient_for( GTK_WINDOW( dialog ),
                                      GTK_WINDOW( window ) );

        /* Set title */
        gtk_window_set_title( GTK_WINDOW( dialog ), &amp;quot;Conformation&amp;quot; );

        /* Add buttons. */
        gtk_dialog_add_button( GTK_DIALOG( dialog ), GTK_STOCK_YES, 1 );
        gtk_dialog_add_button( GTK_DIALOG( dialog ), GTK_STOCK_NO,  2 );

#else

        /* If we use convenience API function gtk_dialog_new_with_buttons,
         * last six function calls can be written as: */
        dialog = gtk_dialog_new_with_buttons( &amp;quot;Conformation&amp;quot;,
                                              GTK_WINDOW( window ),
                                              GTK_DIALOG_MODAL,
                                              GTK_STOCK_YES, 1,
                                              GTK_STOCK_NO,  2,
                                              NULL );
#endif

        /* Create label */
        label = gtk_label_new( &amp;quot;Are you sure you want to quit?&amp;quot; );

        /* Pack label, taking API change in account. */
#if GTK_MINOR_VERSION &amp;lt; 14
        box = GTK_DIALOG( dialog )-&amp;gt;vbox;
#else
        box = gtk_dialog_get_content_area( GTK_DIALOG( dialog ) );
#endif
        gtk_box_pack_start( GTK_BOX( box ), label, TRUE, TRUE, 0 );

        /* Show dialog */
        gtk_widget_show_all( dialog );
    }

    /* Run dialog */
    response = gtk_dialog_run( GTK_DIALOG( dialog ) );
    gtk_widget_hide( dialog );

    return( 1 != response );
}
&lt;/pre&gt;
&lt;pre class="brush: python"&gt;
    def cb_delete_event( self, window, event ):
        # If dialog does not exists, create it
        if self.quit_dialog == None:
            # Create dialog
            self.quit_dialog = gtk.Dialog()

            # Set it modal and transient for main window.
            self.quit_dialog.set_modal( True )
            self.quit_dialog.set_transient_for( self )

            # Set title
            self.quit_dialog.set_title( 'Conformation' )

            # Add buttons.
            self.quit_dialog.add_button( gtk.STOCK_YES, 1 )
            self.quit_dialog.add_button( gtk.STOCK_NO,  2 )

            # Using non-null parameter list when creating dialog,
            # the last six calls can be written as:
            # self.quit_dialog = gtk.Dialog( 'Conformation', self,
            #                                gtk.DIALOG_MODAL,
            #                                ( gtk.STOCK_YES, 1,
            #                                  gtk.STOCK_NO,  2 ) )

            # Create label
            label = gtk.Label( 'Are you sure you want to quit?' )

            # Pack label, taking API change in account
            if gtk.pygtk_version[1] &amp;lt; 14:
                self.quit_dialog.vbox.pack_start( label )
            else:
                self.quit_dialog.get_content_area().pack_start( label )

            # Show dialog
            self.quit_dialog.show_all()

        # Run dialog
        response = self.quit_dialog.run()
        self.quit_dialog.hide()

        return response != 1
&lt;/pre&gt;
Now we'll create about dialog and callback function for about button. This function will be quite similar to the delete event handler, but instead of plain GtkDialog, we'll use GtkAboutDialog, which will save us some time we would use packing different widgets inside content area. Again, I wrote code that shows you how to manually set all the options and how to use convenience function. And this is the code:&lt;pre class="brush: c"&gt;
static void
cb_show_about( GtkButton *button,
               GtkWidget *window )
{
#if 1
    /* This is our about dialog, declared static to avoid recreating it
     * each time we click about button. */
    static GtkWidget *dialog = NULL;

    if( ! dialog )
    {
        /* This is just a convenience to avoid castings. */
        GtkAboutDialog *about;
        const gchar    *auth[] = { &amp;quot;Tadej Borovšak &amp;lt;tadeboro@gmail.com&amp;gt;&amp;quot;,
                                   NULL };

        /* Create dialog */
        dialog = gtk_about_dialog_new();
        gtk_window_set_transient_for( GTK_WINDOW( dialog ),
                                      GTK_WINDOW( window ) );
        about = GTK_ABOUT_DIALOG( dialog );

        /* Set it's properties */
        gtk_about_dialog_set_program_name( about, &amp;quot;Sample Dialog App&amp;quot; );
        gtk_about_dialog_set_version( about, &amp;quot;0.1&amp;quot; );
        gtk_about_dialog_set_copyright( about,
                                        &amp;quot;Copyright 2009 © Tadej Borovšak&amp;quot; );
        gtk_about_dialog_set_website( about, &amp;quot;http://tadeboro.blogspot.com&amp;quot; );
        gtk_about_dialog_set_authors( about, auth );

        /* Show dialog */
        gtk_widget_show_all( dialog );
    }

    /* Run dialog and hide it after it returns. */
    gtk_dialog_run( GTK_DIALOG( dialog ) );
    gtk_widget_hide( dialog );

#else

    /* This is how creating about dialog using gtk_show_about_dialog
     * convenience function looks like. */
    const gchar *auth[] = { &amp;quot;Tadej Borovšak &amp;lt;tadeboro@gmail.com&amp;gt;&amp;quot;, NULL };

    gtk_show_about_dialog( GTK_WINDOW( window ),
                          &amp;quot;program-name&amp;quot;, &amp;quot;Sample Dialog App&amp;quot;,
                          &amp;quot;version&amp;quot;, &amp;quot;0.1&amp;quot;,
                          &amp;quot;copyright&amp;quot;, &amp;quot;Copyright 2009 © Tadej Borovšak&amp;quot;,
                          &amp;quot;website&amp;quot;, &amp;quot;http://tadeboeo.blogspot.com&amp;quot;,
                          &amp;quot;authors&amp;quot;, auth,
                          NULL );
#endif
}
&lt;/pre&gt;
&lt;pre class="brush: python"&gt;
    def cb_show_about( self, button ):
        if self.about_dialog == None:
            # Create about dialog
            self.about_dialog = gtk.AboutDialog()
            self.about_dialog.set_transient_for( self )
            
            # Set dialog's properties
            self.about_dialog.set_program_name( &amp;quot;Sample Dialog App&amp;quot; )
            self.about_dialog.set_version( &amp;quot;0.1&amp;quot; )
            self.about_dialog.set_copyright( &amp;quot;Copyright 2009 © Tadej Borovšak&amp;quot; )
            self.about_dialog.set_website( &amp;quot;http://tadeboro.blogspot.com&amp;quot; )
            self.about_dialog.set_authors(
                                [ &amp;quot;Tadej Borovšak &amp;lt;tadeboro@gmail.com&amp;gt;&amp;quot; ] )

            # Show dialog
            self.about_dialog.show_all()

        # Run dialog
        self.about_dialog.run()
        self.about_dialog.hide()

        # Convenience method for creating about dialog is wrapped,
        # but unfortunatelly I don't know how to use it.
        # API reference for PyGTK says nothing about gtk.show_about_dialog
        # Sorry for the inconveniance, Python coders.
&lt;/pre&gt;
And that would be everything for today. Join me next time when we'll recreate this application using glade. All I need to do now is to say "Bye" and paste the complete code. See ya;)&lt;br /&gt;
&lt;pre class="brush: c"&gt;
#include &amp;lt;gtk/gtk.h&amp;gt;

static void
cb_show_about( GtkButton *button,
               GtkWidget *window )
{
#if 1
    /* This is our about dialog, declared static to avoid recreating it
     * each time we click about button. */
    static GtkWidget *dialog = NULL;

    if( ! dialog )
    {
        /* This is just a convenience to avoid castings. */
        GtkAboutDialog *about;
        const gchar    *auth[] = { &amp;quot;Tadej Borovšak &amp;lt;tadeboro@gmail.com&amp;gt;&amp;quot;,
                                   NULL };

        /* Create dialog */
        dialog = gtk_about_dialog_new();
        gtk_window_set_transient_for( GTK_WINDOW( dialog ),
                                      GTK_WINDOW( window ) );
        about = GTK_ABOUT_DIALOG( dialog );

        /* Set it's properties */
        gtk_about_dialog_set_program_name( about, &amp;quot;Sample Dialog App&amp;quot; );
        gtk_about_dialog_set_version( about, &amp;quot;0.1&amp;quot; );
        gtk_about_dialog_set_copyright( about,
                                        &amp;quot;Copyright 2009 © Tadej Borovšak&amp;quot; );
        gtk_about_dialog_set_website( about, &amp;quot;http://tadeboro.blogspot.com&amp;quot; );
        gtk_about_dialog_set_authors( about, auth );

        /* Show dialog */
        gtk_widget_show_all( dialog );
    }

    /* Run dialog and hide it after it returns. */
    gtk_dialog_run( GTK_DIALOG( dialog ) );
    gtk_widget_hide( dialog );

#else

    /* This is how creating about dialog using gtk_show_about_dialog
     * convenience function looks like. */
    const gchar *auth[] = { &amp;quot;Tadej Borovšak &amp;lt;tadeboro@gmail.com&amp;gt;&amp;quot;, NULL };

    gtk_show_about_dialog( GTK_WINDOW( window ),
                          &amp;quot;program-name&amp;quot;, &amp;quot;Sample Dialog App&amp;quot;,
                          &amp;quot;version&amp;quot;, &amp;quot;0.1&amp;quot;,
                          &amp;quot;copyright&amp;quot;, &amp;quot;Copyright 2009 © Tadej Borovšak&amp;quot;,
                          &amp;quot;website&amp;quot;, &amp;quot;http://tadeboeo.blogspot.com&amp;quot;,
                          &amp;quot;authors&amp;quot;, auth,
                          NULL );
#endif
}

static gboolean
cb_delete_event( GtkWidget *window,
                 GdkEvent  *event,
                 gpointer   data )
{
    /* We'll create dialog only the first time we enter this callback.
     * After first time, we'll just show it. */
    static GtkWidget *dialog = NULL;
    gint              response;

    if( ! dialog )
    {
        GtkWidget *label;
        GtkWidget *box;
#if 1
        /* Create dialog */
        dialog = gtk_dialog_new();

        /* Set it modal and transient for main window. */
        gtk_window_set_modal( GTK_WINDOW( dialog ), TRUE );
        gtk_window_set_transient_for( GTK_WINDOW( dialog ),
                                      GTK_WINDOW( window ) );

        /* Set title */
        gtk_window_set_title( GTK_WINDOW( dialog ), &amp;quot;Conformation&amp;quot; );

        /* Add buttons. */
        gtk_dialog_add_button( GTK_DIALOG( dialog ), GTK_STOCK_YES, 1 );
        gtk_dialog_add_button( GTK_DIALOG( dialog ), GTK_STOCK_NO,  2 );
#endif
        /* If we use convenience API function gtk_dialog_new_with_buttons,
         * last six function calls can be written as: */
#if 0
        dialog = gtk_dialog_new_with_buttons( &amp;quot;Conformation&amp;quot;,
                                              GTK_WINDOW( window ),
                                              GTK_DIALOG_MODAL,
                                              GTK_STOCK_YES, 1,
                                              GTK_STOCK_NO,  2,
                                              NULL );
#endif

        /* Create label */
        label = gtk_label_new( &amp;quot;Are you sure you want to quit?&amp;quot; );

        /* Pack label, taking API change in account. */
#if GTK_MINOR_VERSION &amp;lt; 14
        box = GTK_DIALOG( dialog )-&amp;gt;vbox;
#else
        box = gtk_dialog_get_content_area( GTK_DIALOG( dialog ) );
#endif
        gtk_box_pack_start( GTK_BOX( box ), label, TRUE, TRUE, 0 );

        /* Show dialog */
        gtk_widget_show_all( dialog );
    }

    /* Run dialog */
    response = gtk_dialog_run( GTK_DIALOG( dialog ) );
    gtk_widget_hide( dialog );

    return( 1 != response );
}

int
main( int    argc,
      char **argv )
{
    GtkWidget *window;
    GtkWidget *button;

    gtk_init( &amp;amp;argc, &amp;amp;argv );

    /* Create main window */
    window = gtk_window_new( GTK_WINDOW_TOPLEVEL );
    gtk_container_set_border_width( GTK_CONTAINER( window ), 10 );

    /* Connect delete event signal - this is where we'll show our
     * quit conformation dialog. */
    g_signal_connect( G_OBJECT( window ), &amp;quot;delete-event&amp;quot;,
                      G_CALLBACK( cb_delete_event ), NULL );

    /* Connect destroy signal, which will be emitted if we return
     * FALSE from our cb_delete_event function. */
    g_signal_connect( G_OBJECT( window ), &amp;quot;destroy&amp;quot;,
                      G_CALLBACK( gtk_main_quit ), NULL );

    /* Create button ... */
    button = gtk_button_new_from_stock( GTK_STOCK_ABOUT );

    /* ... and connect clicked signal to handler that will create
     * and show about dialog. */
    g_signal_connect( G_OBJECT( button ), &amp;quot;clicked&amp;quot;,
                      G_CALLBACK( cb_show_about ), window );
    gtk_container_add( GTK_CONTAINER( window ), button );

    /* Show our main window and start main loop. */
    gtk_widget_show_all( window );
    gtk_main();

    return( 0 );
}

&lt;/pre&gt;
&lt;pre class="brush: python"&gt;
#!/usr/bin/env python
# vim: set fileencoding=utf-8

import pygtk
pygtk.require( &amp;quot;2.0&amp;quot; )
import gtk

class Sample( gtk.Window ):
    def cb_delete_event( self, window, event ):
        # If dialog does not exists, create it
        if self.quit_dialog == None:
            # Create dialog
            self.quit_dialog = gtk.Dialog()

            # Set it modal and transient for main window.
            self.quit_dialog.set_modal( True )
            self.quit_dialog.set_transient_for( self )

            # Set title
            self.quit_dialog.set_title( 'Conformation' )

            # Add buttons.
            self.quit_dialog.add_button( gtk.STOCK_YES, 1 )
            self.quit_dialog.add_button( gtk.STOCK_NO,  2 )

            '''
            # Using non-null parameter list when creating dialog,
            # the last six calls can be written as:
            self.quit_dialog = gtk.Dialog( 'Conformation', self,
                                           gtk.DIALOG_MODAL,
                                           ( gtk.STOCK_YES, 1,
                                             gtk.STOCK_NO,  2 ) )
            '''

            # Create label
            label = gtk.Label( 'Are you sure you want to quit?' )

            # Pack label, taking API change in account
            if gtk.pygtk_version[1] &amp;lt; 14:
                self.quit_dialog.vbox.pack_start( label )
            else:
                self.quit_dialog.get_content_area().pack_start( label )

            # Show dialog
            self.quit_dialog.show_all()

        # Run dialog
        response = self.quit_dialog.run()
        self.quit_dialog.hide()

        return response != 1

    def cb_show_about( self, button ):
        if self.about_dialog == None:
            # Create about dialog
            self.about_dialog = gtk.AboutDialog()
            self.about_dialog.set_transient_for( self )
            
            # Set dialog's properties
            self.about_dialog.set_program_name( &amp;quot;Sample Dialog App&amp;quot; )
            self.about_dialog.set_version( &amp;quot;0.1&amp;quot; )
            self.about_dialog.set_copyright( &amp;quot;Copyright 2009 © Tadej Borovšak&amp;quot; )
            self.about_dialog.set_website( &amp;quot;http://tadeboro.blogspot.com&amp;quot; )
            self.about_dialog.set_authors(
                                [ &amp;quot;Tadej Borovšak &amp;lt;tadeboro@gmail.com&amp;gt;&amp;quot; ] )

            # Show dialog
            self.about_dialog.show_all()

        # Run dialog
        self.about_dialog.run()
        self.about_dialog.hide()

        # Convenience method for creating about dialog is wrapped,
        # but unfortunatelly I don't know how to use it.
        # API reference for PyGTK says nothing about gtk.show_about_dialog
        # Sorry for the inconveniance, Python coders.

    def __init__( self ):
        # Create main window
        gtk.Window.__init__( self )
        self.set_border_width( 10 )

        # Connect delete event signal - this is where we'll show our
        # quit conformation dialog.
        self.connect( 'delete-event', self.cb_delete_event )

        # Connect destroy signal, which will be emitted if we return
        # False from our self.cb_delete_event method. */
        self.connect( 'destroy', lambda *w: gtk.main_quit() )

        # Create button ...
        button = gtk.Button( None, gtk.STOCK_ABOUT )

        # ... and connect clicked signal to handler that will create
        # and show about dialog.
        button.connect( 'clicked', self.cb_show_about )
        self.add( button )

        # Add &amp;quot;placeholders&amp;quot; for dialogs
        self.about_dialog = None
        self.quit_dialog  = None

if __name__ == &amp;quot;__main__&amp;quot;:
    win = Sample()
    win.show_all()
    gtk.main()

&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6666239544818473000-6659789726771674246?l=blog.borovsak.si' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.borovsak.si/feeds/6659789726771674246/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.borovsak.si/2009/04/gtkdialog-tutorial-part-1.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6666239544818473000/posts/default/6659789726771674246'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6666239544818473000/posts/default/6659789726771674246'/><link rel='alternate' type='text/html' href='http://blog.borovsak.si/2009/04/gtkdialog-tutorial-part-1.html' title='GtkDialog tutorial - part 1'/><author><name>Tadej Borovšak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-V5S0cD-5b_I/AAAAAAAAAAI/AAAAAAAAArU/nAyUYzn6Yx8/s512-c/photo.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6666239544818473000.post-1520126228826750644</id><published>2009-04-24T18:08:00.004+02:00</published><updated>2009-05-17T21:15:40.492+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Maintenance'/><title type='text'>Blog updates (again;)</title><content type='html'>Since none of the blogger templates suited me (none of them seemed minimalistic and light enough), I modified the Minima template and colorized it with colors from Tango palette.&lt;br /&gt;&lt;br /&gt;

I also updated my syntax highlighting templates and brushes a bit to better fit into the new page.&lt;br /&gt;&lt;br /&gt;

And while I was at work, I also modified my perl "publish" script which now converts tabs into spaces, which should make code look good in any text editor you paste it to.&lt;br /&gt;&lt;br /&gt;

Bye&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6666239544818473000-1520126228826750644?l=blog.borovsak.si' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.borovsak.si/feeds/1520126228826750644/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.borovsak.si/2009/04/blog-updates-again.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6666239544818473000/posts/default/1520126228826750644'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6666239544818473000/posts/default/1520126228826750644'/><link rel='alternate' type='text/html' href='http://blog.borovsak.si/2009/04/blog-updates-again.html' title='Blog updates (again;)'/><author><name>Tadej Borovšak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-V5S0cD-5b_I/AAAAAAAAAAI/AAAAAAAAArU/nAyUYzn6Yx8/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6666239544818473000.post-7915632881924596721</id><published>2009-04-24T12:36:00.010+02:00</published><updated>2009-05-21T13:57:27.254+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='GtkTreeView'/><category scheme='http://www.blogger.com/atom/ns#' term='tutorial'/><category scheme='http://www.blogger.com/atom/ns#' term='GTK+'/><title type='text'>Creating GtkTreeView with Glade-3, part 2</title><content type='html'>In first part of this Glade tutorial, we created tree view and added some data to it. Today, wi'll expand our sample application a bit and add some controls that will enable us to modify tree view content - moving items up and down.
&lt;br /&gt;&lt;br /&gt;
Contents:
&lt;ol&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/04/creatin-gtktreeview-with-glade-3.html"&gt;Creating GtkTreeView with Glade-3, part 1&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/04/creating-gtktreeview-with-glade-3-part.html"&gt;Creating GtkTreeView with Glade-3, part 2&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;span class="s_title"&gt;Manipulating data&lt;/span&gt;
&lt;br /&gt;&lt;br /&gt;
First thing we need to do it to create widgets that trigger the modifications of tree view data. Let's load builder project from first part of this tutorial and fill the bottom compartment of vbox.&lt;br /&gt;&lt;br /&gt;

I'll just quickly describe what needs to be done in order to create GUI like the one on the image below:
&lt;ol&gt;&lt;li&gt;Add GtkFrame in bottom compartment of vbox (the one that we left empty in previous post), change it's label to "Controls" and it's expand property to NO.&lt;/li&gt;&lt;li&gt;Add GtkTable with 2 columns and 1 row into the frame and set column spacing to 6 px.&lt;/li&gt;&lt;li&gt;Add two buttons to the table: left one should be "gtk-go-up" stock button and right one "gtk-go-down". Connect their "clicked" signal to "cb_move" function.&lt;/li&gt;&lt;/ol&gt;The final result should look something like this:

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://img105.imageshack.us/img105/5417/20090424094457z.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 640px; height: 400px;" src="http://img105.imageshack.us/img105/7469/20090424094457.png" alt="" border="0" /&gt;&lt;/a&gt;
You can download builder file from &lt;a href="http://tadeboro.googlepages.com/treeview.builder"&gt;here&lt;/a&gt;, just to be sure that we're working with the same GUI;)

Now let's start coding. And for the first time, I'll provide sample application for C and Python coders.&lt;br /&gt;&lt;br /&gt;

First, we need to load our interface. If you listened to my advice and read Micah Carrick's tutorial first, there is nothing new in my code snippets.&lt;br /&gt;&lt;br /&gt;

All that we need to do now is to write cb_move function that will take care of moving our tree view items. The way we'll be doing it is to check which button has been pressed and based on this decide if we're moving item up or down. Actual move will be done using gtk_list_store_swap function. And that's it! Now dissect this code and have fun.
&lt;pre class="brush: c"&gt;
/* treeview.c */

#include &amp;lt;gtk/gtk.h&amp;gt;

typedef struct _Data Data;
struct _Data
{
    GtkWidget *up;   /* Up button */
    GtkWidget *down; /* Down button */
    GtkWidget *tree; /* Tree view */
};


G_MODULE_EXPORT void
cb_move( GtkWidget *button,
         Data      *data )
{
    gboolean          direction = TRUE; /* TRUE means &amp;quot;move up&amp;quot;, FALSE &amp;quot;move down&amp;quot; */
    GList            *rows, *tmp;       /* List of selected row */
    GtkTreeModel     *model;            /* Model with data, which we'll be moving. */
    GtkTreeSelection *sel = gtk_tree_view_get_selection( GTK_TREE_VIEW( data-&amp;gt;tree ) );

    /* If nothing is selected, return. */
    if( ! gtk_tree_selection_count_selected_rows( sel ) )
        return;

    /* In which direction we'll be moving? */
    if( button == data-&amp;gt;down )
        direction = FALSE;

    /* Get selected rows */
    rows = gtk_tree_selection_get_selected_rows( sel, &amp;amp;model );

    /* Get new path for each selected row and swap items. */
    for( tmp = rows; tmp; tmp = g_list_next( tmp ) )
    {
        GtkTreePath *path1, *path2; /* Paths. */
        GtkTreeIter  iter1, iter2;  /* Iters for swapping items. */

        /* Copy path */
        path1 = (GtkTreePath *)tmp-&amp;gt;data;
        path2 = gtk_tree_path_copy( path1 );

        /* Move path2 in right direction */
        if( direction )
            gtk_tree_path_prev( path2 );
        else
            gtk_tree_path_next( path2 );

        /* Compare paths and skip one iteration if the paths are equal, which means we're
         * trying to move first path up. */
        if( ! gtk_tree_path_compare( path1, path2 ) )
        {
            gtk_tree_path_free( path2 );
            continue;
        }

        /* Now finally obtain iters and swap items. If the second iter is invalid, we're
         * trying to move the last item down. */
        gtk_tree_model_get_iter( model, &amp;amp;iter1, path1 );
        if( ! gtk_tree_model_get_iter( model, &amp;amp;iter2, path2 ) )
        {
            gtk_tree_path_free( path2 );
            continue;
        }
        gtk_list_store_swap( GTK_LIST_STORE( model ), &amp;amp;iter1, &amp;amp;iter2 );
        gtk_tree_path_free( path2 );
    }

    /* Free our paths */
    g_list_foreach( rows, (GFunc)gtk_tree_path_free, NULL );
    g_list_free( rows );
}

int
main( int    argc,
      char **argv )
{
    GtkBuilder *builder;
    GtkWidget  *window;
    Data        data;

    gtk_init( &amp;amp;argc, &amp;amp;argv );

    /* Create builder */
    builder = gtk_builder_new();
    gtk_builder_add_from_file( builder, &amp;quot;treeview.builder&amp;quot;, NULL );

    window    = GTK_WIDGET( gtk_builder_get_object( builder, &amp;quot;window1&amp;quot; ) );
    data.up   = GTK_WIDGET( gtk_builder_get_object( builder, &amp;quot;move_up&amp;quot; ) );
    data.down = GTK_WIDGET( gtk_builder_get_object( builder, &amp;quot;move_down&amp;quot; ) );
    data.tree = GTK_WIDGET( gtk_builder_get_object( builder, &amp;quot;treeview&amp;quot; ) );

    gtk_builder_connect_signals( builder, &amp;amp;data );
    g_object_unref( G_OBJECT( builder ) );

    gtk_widget_show( window );
    gtk_main();

    return( 0 );
}
&lt;/pre&gt;
&lt;pre class="brush: python"&gt;
#!/usr/bin/env python
# treeview.py

import pygtk
pygtk.require( &amp;quot;2.0&amp;quot; )
import gtk

class SampleApp( object ):
    def __init__( self ):
        builder = gtk.Builder()
        builder.add_from_file( &amp;quot;treeview.builder&amp;quot; )

        self.win   = builder.get_object( &amp;quot;window1&amp;quot; )
        self.up    = builder.get_object( &amp;quot;move_up&amp;quot; )
        self.down  = builder.get_object( &amp;quot;move_down&amp;quot; )
        self.tree  = builder.get_object( &amp;quot;treeview&amp;quot; )

        builder.connect_signals( self )

    # Callbacks
    def cb_move( self, button ):
        # Obtain selection
        sel = self.tree.get_selection()

        # If nothing is selected, return.
        if sel.count_selected_rows == 0:
            return

        # In which diection we'll be moving?
        if button == self.up:
            direction = True
        else:
            direction = False
    
        # Get selected path
        ( model, rows ) = sel.get_selected_rows()

        # Get new path for each selected row and swap items. */
        for path1 in rows:
            # Move path2 in right direction
             if direction:
                path2 = ( path1[0] - 1, )
            else:
                path2 = ( path1[0] + 1, )

            # If path2 is negative, we're trying to move first path up. Skip
            # one loop iteration.
            if path2[0] &amp;lt; 0:
                continue

            # Obtain iters and swap items. If the second iter is invalid, we're
            # trying to move the last item down. */
            iter1 = model.get_iter( path1 )
            try:
                iter2 = model.get_iter( path2 )
            except ValueError:
                continue
            model.swap( iter1, iter2 )

    def gtk_main_quit( self, object ):
        gtk.main_quit()

if __name__ == &amp;quot;__main__&amp;quot;:
    app = SampleApp()
    app.win.show()
    gtk.main()
&lt;/pre&gt;
I hope you enjoyed this tutorial. Come back soon when I'll show you how to deal with GtkDialog and it's derivates in conjunction with glade.

Bye&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6666239544818473000-7915632881924596721?l=blog.borovsak.si' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.borovsak.si/feeds/7915632881924596721/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.borovsak.si/2009/04/creating-gtktreeview-with-glade-3-part.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6666239544818473000/posts/default/7915632881924596721'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6666239544818473000/posts/default/7915632881924596721'/><link rel='alternate' type='text/html' href='http://blog.borovsak.si/2009/04/creating-gtktreeview-with-glade-3-part.html' title='Creating GtkTreeView with Glade-3, part 2'/><author><name>Tadej Borovšak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-V5S0cD-5b_I/AAAAAAAAAAI/AAAAAAAAArU/nAyUYzn6Yx8/s512-c/photo.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6666239544818473000.post-5447841165335820985</id><published>2009-04-19T21:06:00.029+02:00</published><updated>2009-07-20T11:21:56.199+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='GtkTreeView'/><category scheme='http://www.blogger.com/atom/ns#' term='tutorial'/><category scheme='http://www.blogger.com/atom/ns#' term='GTK+'/><title type='text'>Creating GtkTreeView with Glade-3, part 1</title><content type='html'>In today's bit, I'll show you how to create tree view and underlying model completely from glade GUI builder.
&lt;br /&gt;&lt;br /&gt;
Contents:
&lt;ol&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/04/creatin-gtktreeview-with-glade-3.html"&gt;Creating GtkTreeView with Glade-3, part 1&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/04/creating-gtktreeview-with-glade-3-part.html"&gt;Creating GtkTreeView with Glade-3, part 2&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;span class="s_title"&gt;Creating tree view&lt;/span&gt;
&lt;br /&gt;&lt;br /&gt;
For this tutorial, you'll need glade &gt;= 3.6, since creating non-widget objects is not possible in previous versions.&lt;br /&gt;&lt;br /&gt;

I would also recommend you to read &lt;a href="http://scentric.net/tutorial/treeview-tutorial.html"&gt;GtkTreeView tutorial&lt;/a&gt; and &lt;a href="http://www.micahcarrick.com/12-24-2007/gtk-glade-tutorial-part-1.html"&gt;Micah Carrick's glade tutorial&lt;/a&gt; before trying to follow this one, since I won't be discussing underlying principles much.&lt;br /&gt;&lt;br /&gt;

Do we start glade now? No, not yet. We need to create an outline of our application. We'll create simple application with GtkTreeView. Tree view will have 3 columns:
&lt;ol&gt;&lt;li&gt; "Image" column that will display image and it's name&lt;/li&gt;&lt;li&gt; "Penetration" column that will display progress bar&lt;/li&gt;&lt;li&gt; "Description" column with some text&lt;/li&gt;&lt;/ol&gt;If you read the tree view tutorial, you know that when creating tree view, we need three components: model (store for our data), view (tree view columns and cell renderers) and controller (GtkTreeView). And according to our plan, we'll need three tree view columns and four cell renderers:
&lt;ol&gt;&lt;li&gt;pixbuf cell renderer for displaying image (in first column)
&lt;/li&gt;&lt;li&gt;text cell renderer for displaying image name (in first column)
&lt;/li&gt;&lt;li&gt;progress cell renderer for progress bar and (in second column)
&lt;/li&gt;&lt;li&gt;another text cell renderer for description (in third column)
&lt;/li&gt;&lt;/ol&gt;We'll place data for those renderers inside GtkListStore with three columns: two text columns for stock ids and descriptions and one numeric column for progress values.&lt;br /&gt;&lt;br /&gt;

Now we can (finally;) start glade.&lt;br /&gt;&lt;br /&gt;

After startup, a small dialog ask us about some project properties. Most of the options are fine default, but just make sure you select proper gtk version or GtkBuilder will moan badly when trying to create GUI if your version is less that the one specified in builder file. You've been warned;)

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://img209.imageshack.us/img209/1888/20090419212310.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 640px; height: 400px;" src="http://img504.imageshack.us/img504/1888/20090419212310.png" alt="" border="0" /&gt;&lt;/a&gt;
We'll first create out model and populate it with data. Open the widget gallery and click Tree Model -&gt; List Store. Under "Objects", we see our newly created GtkListStore. Let's modify it a bit by changing it's name to "datastore" and adding three columns:

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://img259.imageshack.us/img259/7431/20090419212530.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 640px; height: 400px;" src="http://img517.imageshack.us/img517/7431/20090419212530.png" alt="" border="0" /&gt;&lt;/a&gt;
If you ever manually created GtkListStore, you may wonder where all of the G_TYPE_* macros go in column definitions. In glade, you can simply specify type without the macros. So GDK_TYPE_PIXBUF is in glade simply specified as GdkPixbuf, G_TYPE_STRING as gchararray, G_TYPE_INT as gint ... And you don't event need to know their exact name by heart, since glade offers helpful popup.&lt;br /&gt;&lt;br /&gt;

Now we'll scroll down a bit add some data into our model. Stock item ids are taken from &lt;a href="http://library.gnome.org/devel/gtk/stable/gtk-Stock-Items.html"&gt;here&lt;/a&gt;. And since glade hides some of the descriptions, I wrote down my original table here too.
&lt;pre class="brush: c"&gt;.-----------+----------+---------------------------------.
| Image     | Progress | Desc                            |
+-----------+----------+---------------------------------+
| gtk-add   |    12    | Add icon is bad at penetrating. |
| gtk-cdrom |    78    | CD-ROM image fares pretty well. |
| gtk-cut   |    98    | Scissors rock!!                 |
`-----------+----------+---------------------------------'&lt;/pre&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://img10.imageshack.us/img10/6893/20090419212810.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 640px; height: 400px;" src="http://img194.imageshack.us/img194/8784/editm.png" alt="" border="0" /&gt;&lt;/a&gt;
And that's it. We now have our model part of Model/View/Controller (MVC) complex.&lt;br /&gt;&lt;br /&gt;

Now we'll create a window and connect it's "destroy" signal to gtk_main_quit function, add vbox with two rows to it and pack GtkTreeView into upper section of vbox (lower section is only a placeholder for the next part of the tutorial, where we'll be adding some controls to our application). When we place our tree view into vbox, glade ask us about model. We'll click on "..." and select "datastore" model, click OK and OK again. And controller part of MVC is done too. Now for the last part - creating display components.

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://img9.imageshack.us/img9/1024/20090419212847.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 640px; height: 400px;" src="http://img407.imageshack.us/img407/1024/20090419212847.png" alt="" border="0" /&gt;&lt;/a&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://img524.imageshack.us/img524/7192/20090419212909.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 640px; height: 400px;" src="http://img208.imageshack.us/img208/7192/20090419212909.png" alt="" border="0" /&gt;&lt;/a&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://img259.imageshack.us/img259/1/20090419212955.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 640px; height: 400px;" src="http://img172.imageshack.us/img172/1/20090419212955.png" alt="" border="0" /&gt;&lt;/a&gt;
To add columns and renderers to out tree view, we need to select it and the click "Edit ..." button. Another window will open, looking something like this:

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://img11.imageshack.us/img11/4673/20090419213009.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 640px; height: 400px;" src="http://img258.imageshack.us/img258/4673/20090419213009.png" alt="" border="0" /&gt;&lt;/a&gt;
In general tab, we'll only rename "treeview1" to "treeview". We could change entries in our data store in this window too, but we already did that when creating model.&lt;br /&gt;&lt;br /&gt;

Now we'll open "Hierarchy" tab and start creating columns and renderers. First we need to add column for images and their names. We click on "Add" button and edit newly created GtkTreeViewColumn by renaming it to "imgcol" and setting title to "Image".

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://img209.imageshack.us/img209/5345/20090419213047.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 640px; height: 400px;" src="http://img300.imageshack.us/img300/5345/20090419213047.png" alt="" border="0" /&gt;&lt;/a&gt;
After we're done with editing, we need to right-click on our column and select "Add child Pixbuf item". This will add GtkCellRendererPixbuf renderer to the column. We'll rename this renderer to "imgcell" and set it's stock id property to Image model column.

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://img410.imageshack.us/img410/764/20090419213158.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 640px; height: 400px;" src="http://img2.imageshack.us/img2/764/20090419213158.png" alt="" border="0" /&gt;&lt;/a&gt;
Now we need to add text renderer which will display image's name, but this time we'll be adding it a little different. First, we select previous cell renderer and then click "Add" button. This created new cell renderer, without the defined type. We'll rename this cell renderer to "namecell", set it's type to "Text" and it's text property to "Image" model column.

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://img2.imageshack.us/img2/1674/20090419213206.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 640px; height: 400px;" src="http://img150.imageshack.us/img150/3270/20090419213622.png" alt="" border="0" /&gt;&lt;/a&gt;
Now we need to create second column for progress bar. Select the last tree view column inside the left part of the window where columns and renderers reside and click "Add" button. Then rename newly created column to "progresscol" like we renamed first column and set title to "Penetration".

Now create new progress cell renderer. Did you manage to do it by yourself? If you did, congratulations. If not, you should right-click your column and select "Add child Progress item". Now rename this cell renderer to "progresscell" and set it's "Value" property to "Progress" model column.

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://img25.imageshack.us/img25/2788/20090419213714.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 640px; height: 400px;" src="http://img407.imageshack.us/img407/1015/20090419213857.png" alt="" border="0" /&gt;&lt;/a&gt;
And now it's time for you to create last column, rename it to "desccol", set it's title to "Description"; add text cell renderer to it, rename it to "desccell" and set it's "text" property to "Desc" model column. After you're finished, your Hierarchy tab should look something like this:

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://img396.imageshack.us/img396/7581/20090419213906.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 640px; height: 400px;" src="http://img300.imageshack.us/img300/9169/20090419213936.png" alt="" border="0" /&gt;&lt;/a&gt;
Close the Edit window and you should see your tree view already populated with some data. And that is actually all that we needed to do today. All that is left for us to do is to store glade project and test our product. I wrote a simple C application that will load our builder file and create an application from it.
&lt;pre class="brush: c"&gt;
/*
 * treeview.c - Application for testing builder file.
 *
 * Compile with:
 *  gcc -o treeview treeview.c $(pkg-config --cflags --libs gtk+-2.0 gmodule-export-2.0)
 */

#include &amp;lt;gtk/gtk.h&amp;gt;

int
main( int    argc,
      char **argv )
{
    /* Vars */
    GtkWidget  *window;
    GtkBuilder *builder;
    GError     *error;

    /* Initialization */
    gtk_init( &amp;amp;argc, &amp;amp;argv );

    /* Create builder and load UI file */
    builder = gtk_builder_new();
    if( ! gtk_builder_add_from_file( builder, &amp;quot;treeview.builder&amp;quot;, &amp;amp;error ) )
    {
        g_print( &amp;quot;Error occured while loading UI file!\n&amp;quot; );
        g_print( &amp;quot;Message: %s\n&amp;quot;, error-&amp;gt;message );
        g_free( error );

        return( 1 );
    }

    /* Get main window and connect signals */
    window = GTK_WIDGET( gtk_builder_get_object( builder, &amp;quot;window1&amp;quot; ) );
    gtk_builder_connect_signals( builder, NULL );

    /* Destroy builder */
    g_object_unref( G_OBJECT( builder ) );

    /* Show main window and start main loop */
    gtk_widget_show( window );
    gtk_main();

    return( 0 );
}

&lt;/pre&gt;
I hope you enjoyed this tutorial and be back for the second part, where we'll also learn how to manipulate data inside our store.

Bye.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6666239544818473000-5447841165335820985?l=blog.borovsak.si' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.borovsak.si/feeds/5447841165335820985/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.borovsak.si/2009/04/creatin-gtktreeview-with-glade-3.html#comment-form' title='11 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6666239544818473000/posts/default/5447841165335820985'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6666239544818473000/posts/default/5447841165335820985'/><link rel='alternate' type='text/html' href='http://blog.borovsak.si/2009/04/creatin-gtktreeview-with-glade-3.html' title='Creating GtkTreeView with Glade-3, part 1'/><author><name>Tadej Borovšak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-V5S0cD-5b_I/AAAAAAAAAAI/AAAAAAAAArU/nAyUYzn6Yx8/s512-c/photo.jpg'/></author><thr:total>11</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6666239544818473000.post-115112519350256652</id><published>2009-04-16T14:26:00.006+02:00</published><updated>2009-05-17T21:15:55.213+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='GtkComboBox'/><category scheme='http://www.blogger.com/atom/ns#' term='tutorial'/><category scheme='http://www.blogger.com/atom/ns#' term='GTK+'/><title type='text'>GtkComboBox widget - part 3</title><content type='html'>Welcome to the last part of our journey through wonders of GtkComboBox widgets. Today's lesson will be a bit different that previous two. Consider it a test at the end of the semester. When you understand every piece of code I wrote today, you'll be able to create almost any combo box you'll ever need.&lt;br /&gt;&lt;br /&gt;

Contents of this tutorial:
&lt;ol&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/04/gtkcombobox-widget-part-1.html"&gt;GtkComboBox widget - part 1 (simple API)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/04/as-promised-im-back-with-second-part-of.html"&gt;GtkComboBox widget - part 2 (complex API)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/04/gtkcombobox-widget-part-3.html"&gt;GtkComboBox widget - part 3 (test your knowledge)&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;span class="s_title"&gt;Test&lt;/span&gt;
&lt;br /&gt;&lt;br /&gt;
Again, if you're not familiar with Model/View/Controller-design, read second part of my tutorial.&lt;br /&gt;&lt;br /&gt;

That being said, here comes the big momma of all combo boxes;) Enjoy dissecting this application.&lt;br /&gt;&lt;br /&gt;

Bye
&lt;pre class="brush: c"&gt;
/*
 * Test me with:
 *  gcc -o combo2 combo2.c $(pkg-config --cflags --libs gtk+-2.0) &amp;amp;&amp;amp; ./combo2
 */

#include &amp;lt;gtk/gtk.h&amp;gt;

/* This enumeration simplifies using data store, since column numbers can be
 * accessed via more meaningful names. */
enum
{
    /* Text related columns */
    TEXT_C = 0,    /* Column with text strings */
    TEXT_VIS_C,    /* Visibility column for text strings */
    TEXT_COL_C,    /* Text color column */

    /* Image related columns */
    PIXBUF_C,     /* Column with GdkPixbufs */
    PIXBUF_VIS_C, /* Visibility column for pixbufs */

    /* Progress renderer related columns */
    PROGRESS_C,     /* Column with progress information [0, 100] */
    PROGRESS_VIS_C, /* Column with progress visibility */

    /* Last element of enumeration holds the number of columns */
    N_COLS
};

/* Structure that holds widgets we need in our callback functions. */
typedef struct _Data Data;
struct _Data
{
    GtkWidget    *combo;  /* Our combo box */
    GtkTreeModel *store;  /* Just a conveniance to avoid calling
                             gtk_combo_box_get_model every time we need to
                             access to data. */

    /* Check buttons that control visibility of renderers. */
    GtkWidget *vis_pixbuf;
    GtkWidget *vis_text;
    GtkWidget *vis_progress;

    /* Entries for modifying values */
    GtkWidget *e_pixbuf;
    GtkWidget *e_text;
    GtkWidget *e_progress;
};

/* Callback function for updating current item. */
static void
cb_clicked( GtkButton *button,
            Data      *data )
{
    const gchar *stock_id, *string;
    gint         value;
    gboolean     pix, text, prog;
    GtkTreeIter  iter;

    /* If nothing is selected, do nothing. */
    if( ! gtk_combo_box_get_active_iter( GTK_COMBO_BOX( data-&amp;gt;combo ), &amp;amp;iter ) )
        return;

    /* Fill variables with proper data */
    stock_id = gtk_entry_get_text( GTK_ENTRY( data-&amp;gt;e_pixbuf ) );
    string   = gtk_entry_get_text( GTK_ENTRY( data-&amp;gt;e_text ) );
    value    = gtk_spin_button_get_value_as_int( GTK_SPIN_BUTTON( data-&amp;gt;e_progress ) );
    pix  = gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( data-&amp;gt;vis_pixbuf ) );
    text = gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( data-&amp;gt;vis_text ) );
    prog = gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( data-&amp;gt;vis_progress ) );

    /* Update data store for current iter */
    gtk_tree_store_set( GTK_TREE_STORE( data-&amp;gt;store ), &amp;amp;iter,
                        TEXT_C, string,
                        TEXT_VIS_C, text,
                        PIXBUF_C, stock_id,
                        PIXBUF_VIS_C, pix,
                        PROGRESS_C, value,
                        PROGRESS_VIS_C, prog,
                        -1 );
}

/* Callback function for changed signal.
 * In this function, we'll set the widgets that control current line. */
static void
cb_changed( GtkComboBox *combo,
            Data        *data )
{
    /* sensitive flag */
    static gboolean sensitive = TRUE;

    /* Vars */
    GtkTreeIter  iter;
    gchar       *stock_id = NULL, *string = NULL;
    gint         value;
    gboolean     pix, text, prog;
    gboolean     active;

    /* Get active iter from combo box. If nothing is selected,
     * disable controls. */
    active = gtk_combo_box_get_active_iter( combo, &amp;amp;iter );
    if( active )
    {
        gtk_tree_model_get( data-&amp;gt;store, &amp;amp;iter, TEXT_C, &amp;amp;string,
                                                TEXT_VIS_C, &amp;amp;text,
                                                PIXBUF_C, &amp;amp;stock_id,
                                                PIXBUF_VIS_C, &amp;amp;pix,
                                                PROGRESS_C, &amp;amp;value,
                                                PROGRESS_VIS_C, &amp;amp;prog,
                                                -1 );

        gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( data-&amp;gt;vis_pixbuf ), pix );
        gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( data-&amp;gt;vis_text ), text );
        gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( data-&amp;gt;vis_progress ), prog );
        gtk_entry_set_text( GTK_ENTRY( data-&amp;gt;e_pixbuf ), stock_id );
        gtk_entry_set_text( GTK_ENTRY( data-&amp;gt;e_text ), string );
        gtk_spin_button_set_value( GTK_SPIN_BUTTON( data-&amp;gt;e_progress ), value );

        /* Free strings */
        g_free( stock_id );
        g_free( string );
    }

    if( sensitive != active )
    {
        gtk_widget_set_sensitive( data-&amp;gt;vis_pixbuf, active );
        gtk_widget_set_sensitive( data-&amp;gt;vis_text, active );
        gtk_widget_set_sensitive( data-&amp;gt;vis_progress, active );
        gtk_widget_set_sensitive( data-&amp;gt;e_pixbuf, active );
        gtk_widget_set_sensitive( data-&amp;gt;e_text, active );
        gtk_widget_set_sensitive( data-&amp;gt;e_progress, active );

        sensitive = active;
    }
}

/* This function creates tree data structure and fills it with data. */
static GtkTreeModel *
create_model( Data *data )
{
    GtkTreeStore *store;
    GtkTreeIter   parent, child;

    /* Create data store. We'll be using GtkTreeStore today, to show you how
     * combo box handles tree structures. */
    store = gtk_tree_store_new( N_COLS, G_TYPE_STRING,  /* text */
                                        G_TYPE_BOOLEAN, /* text visibility */
                                        G_TYPE_STRING,  /* text color */
                                        G_TYPE_STRING,  /* pixbufs */
                                        G_TYPE_BOOLEAN, /* pixbuf visibility */
                                        G_TYPE_INT,     /* progress bar % */
                                        G_TYPE_BOOLEAN  /* progress vis */
                                        );

    /* Fill our store with some data. */
    gtk_tree_store_append( store, &amp;amp;parent, NULL );
    gtk_tree_store_set( store, &amp;amp;parent, TEXT_C, &amp;quot;Root 1&amp;quot;,
                                        TEXT_VIS_C, TRUE,
                                        TEXT_COL_C, &amp;quot;black&amp;quot;,
                                        PIXBUF_C, GTK_STOCK_OK,
                                        PIXBUF_VIS_C, TRUE,
                                        PROGRESS_C, 100,
                                        PROGRESS_VIS_C, TRUE,
                                        -1 );

    gtk_tree_store_append( store, &amp;amp;child, &amp;amp;parent );
    gtk_tree_store_set( store, &amp;amp;child, TEXT_C, &amp;quot;Leaf 1.1&amp;quot;,
                                       TEXT_VIS_C, TRUE,
                                       TEXT_COL_C, &amp;quot;red&amp;quot;,
                                       PIXBUF_C, GTK_STOCK_ADD,
                                       PIXBUF_VIS_C, TRUE,
                                       PROGRESS_C, 100,
                                       PROGRESS_VIS_C, TRUE,
                                       -1 );

    gtk_tree_store_append( store, &amp;amp;child, &amp;amp;parent );
    gtk_tree_store_set( store, &amp;amp;child, TEXT_C, &amp;quot;Leaf 1.2&amp;quot;,
                                       TEXT_VIS_C, TRUE,
                                       TEXT_COL_C, &amp;quot;green&amp;quot;,
                                       PIXBUF_C, GTK_STOCK_APPLY,
                                       PIXBUF_VIS_C, TRUE,
                                       PROGRESS_C, 100,
                                       PROGRESS_VIS_C, TRUE,
                                       -1 );

    gtk_tree_store_append( store, &amp;amp;child, &amp;amp;parent );
    gtk_tree_store_set( store, &amp;amp;child, TEXT_C, &amp;quot;Leaf 1.3&amp;quot;,
                                       TEXT_VIS_C, TRUE,
                                       TEXT_COL_C, &amp;quot;blue&amp;quot;,
                                       PIXBUF_C, GTK_STOCK_CDROM,
                                       PIXBUF_VIS_C, TRUE,
                                       PROGRESS_C, 100,
                                       PROGRESS_VIS_C, TRUE,
                                       -1 );

    gtk_tree_store_append( store, &amp;amp;child, &amp;amp;parent );
    gtk_tree_store_set( store, &amp;amp;child, TEXT_C, &amp;quot;Leaf 1.4&amp;quot;,
                                       TEXT_VIS_C, TRUE,
                                       TEXT_COL_C, &amp;quot;orange&amp;quot;,
                                       PIXBUF_C, GTK_STOCK_QUIT,
                                       PIXBUF_VIS_C, TRUE,
                                       PROGRESS_C, 100,
                                       PROGRESS_VIS_C, TRUE,
                                       -1 );

    gtk_tree_store_append( store, &amp;amp;parent, NULL );
    gtk_tree_store_set( store, &amp;amp;parent, TEXT_C, &amp;quot;Root 2&amp;quot;,
                                        TEXT_VIS_C, TRUE,
                                        TEXT_COL_C, &amp;quot;black&amp;quot;,
                                        PIXBUF_C, GTK_STOCK_FILE,
                                        PIXBUF_VIS_C, TRUE,
                                        PROGRESS_C, 100,
                                        PROGRESS_VIS_C, TRUE,
                                        -1 );

    gtk_tree_store_append( store, &amp;amp;child, &amp;amp;parent );
    gtk_tree_store_set( store, &amp;amp;child, TEXT_C, &amp;quot;Leaf 2.1&amp;quot;,
                                       TEXT_VIS_C, TRUE,
                                       TEXT_COL_C, &amp;quot;blue&amp;quot;,
                                       PIXBUF_C, GTK_STOCK_EXECUTE,
                                       PIXBUF_VIS_C, TRUE,
                                       PROGRESS_C, 100,
                                       PROGRESS_VIS_C, TRUE,
                                       -1 );

    gtk_tree_store_append( store, &amp;amp;child, &amp;amp;parent );
    gtk_tree_store_set( store, &amp;amp;child, TEXT_C, &amp;quot;Leaf 2.2&amp;quot;,
                                       TEXT_VIS_C, TRUE,
                                       TEXT_COL_C, &amp;quot;red&amp;quot;,
                                       PIXBUF_C, GTK_STOCK_HOME,
                                       PIXBUF_VIS_C, TRUE,
                                       PROGRESS_C, 100,
                                       PROGRESS_VIS_C, TRUE,
                                       -1 );

    gtk_tree_store_append( store, &amp;amp;child, &amp;amp;parent );
    gtk_tree_store_set( store, &amp;amp;child, TEXT_C, &amp;quot;Leaf 2.3&amp;quot;,
                                       TEXT_VIS_C, TRUE,
                                       TEXT_COL_C, &amp;quot;gray&amp;quot;,
                                       PIXBUF_C, GTK_STOCK_INFO,
                                       PIXBUF_VIS_C, TRUE,
                                       PROGRESS_C, 100,
                                       PROGRESS_VIS_C, TRUE,
                                       -1 );

    gtk_tree_store_append( store, &amp;amp;child, &amp;amp;parent );
    gtk_tree_store_set( store, &amp;amp;child, TEXT_C, &amp;quot;Leaf 2.4&amp;quot;,
                                       TEXT_VIS_C, TRUE,
                                       TEXT_COL_C, &amp;quot;green&amp;quot;,
                                       PIXBUF_C, GTK_STOCK_PRINT,
                                       PIXBUF_VIS_C, TRUE,
                                       PROGRESS_C, 100,
                                       PROGRESS_VIS_C, TRUE,
                                       -1 );

    return( GTK_TREE_MODEL( store ) );
}

/* Main */
int
main( int    argc,
      char **argv )
{
    GtkWidget       *window;
    GtkWidget       *table;
    GtkWidget       *button;
    GtkCellRenderer *cell;
    Data             data;

    /* Initialization */
    gtk_init( &amp;amp;argc, &amp;amp;argv );

    /* Main window */
    window = gtk_window_new( GTK_WINDOW_TOPLEVEL );
    g_signal_connect( G_OBJECT( window ), &amp;quot;destroy&amp;quot;,
                      G_CALLBACK( gtk_main_quit ), NULL );
    gtk_container_set_border_width( GTK_CONTAINER( window ), 10 );

    /* Table */
    table = gtk_table_new( 4, 3, FALSE );
    gtk_container_add( GTK_CONTAINER( window ), table );

    /* Create combo box */
    data.combo = gtk_combo_box_new();
    g_signal_connect( G_OBJECT( data.combo ), &amp;quot;changed&amp;quot;,
                      G_CALLBACK( cb_changed ), &amp;amp;data );
    gtk_table_attach( GTK_TABLE( table ), data.combo, 0, 3, 0, 1,
                      GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0 );

    /* Create data store */
    data.store = create_model( &amp;amp;data );

    /* Add data store to combo box */
    gtk_combo_box_set_model( GTK_COMBO_BOX( data.combo ), data.store );
    g_object_unref( G_OBJECT( data.store ) );

    /* Create pixbuf cell renderer */
    cell = gtk_cell_renderer_pixbuf_new();

    /* Add cell renderer to combo box */
    gtk_cell_layout_pack_start( GTK_CELL_LAYOUT( data.combo ), cell, FALSE );

    /* Connect cell renderer with data from store */
    gtk_cell_layout_set_attributes( GTK_CELL_LAYOUT( data.combo ), cell,
                                    &amp;quot;stock-id&amp;quot;, PIXBUF_C,
                                    &amp;quot;visible&amp;quot;, PIXBUF_VIS_C,
                                    NULL );

    /* Create text cell renderer */
    cell = gtk_cell_renderer_text_new();
    gtk_cell_layout_pack_start( GTK_CELL_LAYOUT( data.combo ), cell, FALSE );
    gtk_cell_layout_set_attributes( GTK_CELL_LAYOUT( data.combo ), cell,
                                    &amp;quot;text&amp;quot;, TEXT_C,
                                    &amp;quot;visible&amp;quot;, TEXT_VIS_C,
                                    &amp;quot;foreground&amp;quot;, TEXT_COL_C,
                                    NULL );

    /* Create progress renderer */
    cell = gtk_cell_renderer_progress_new();
    gtk_cell_layout_pack_start( GTK_CELL_LAYOUT( data.combo ), cell, TRUE );
    gtk_cell_layout_set_attributes( GTK_CELL_LAYOUT( data.combo ), cell,
                                    &amp;quot;value&amp;quot;, PROGRESS_C,
                                    &amp;quot;visible&amp;quot;, PROGRESS_VIS_C,
                                    NULL );

    /* Create check buttons for controling visibility */
    data.vis_pixbuf = gtk_check_button_new_with_label( &amp;quot;Image visible&amp;quot; );
    gtk_table_attach( GTK_TABLE( table ), data.vis_pixbuf, 0, 1, 1, 2,
                      GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0 );

    data.vis_text = gtk_check_button_new_with_label( &amp;quot;Text visible&amp;quot; );
    gtk_table_attach( GTK_TABLE( table ), data.vis_text, 1, 2, 1, 2,
                      GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0 );

    data.vis_progress = gtk_check_button_new_with_label( &amp;quot;Progress visible&amp;quot; );
    gtk_table_attach( GTK_TABLE( table ), data.vis_progress, 2, 3, 1, 2,
                      GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0 );

    /* Create entries for modifying values */
    data.e_pixbuf = gtk_entry_new();
    gtk_table_attach( GTK_TABLE( table ), data.e_pixbuf, 0, 1, 2, 3,
                      GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0 );

    data.e_text = gtk_entry_new();
    gtk_table_attach( GTK_TABLE( table ), data.e_text, 1, 2, 2, 3,
                      GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0 );

    data.e_progress = gtk_spin_button_new_with_range( 0, 100, 1 );
    gtk_spin_button_set_numeric( GTK_SPIN_BUTTON( data.e_progress ), TRUE );
    gtk_table_attach( GTK_TABLE( table ), data.e_progress, 2, 3, 2, 3,
                      GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0 );

    /* Create button for applying changes */
    button = gtk_button_new_from_stock( GTK_STOCK_APPLY );
    g_signal_connect( G_OBJECT( button ), &amp;quot;clicked&amp;quot;,
                      G_CALLBACK( cb_clicked ), &amp;amp;data );
    gtk_table_attach( GTK_TABLE( table ), button, 0, 3, 3, 4,
                      GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0 );

    /* Manually call cb_changed function to set controllers to right state. */
    cb_changed( GTK_COMBO_BOX( data.combo ), &amp;amp;data );

    /* Show everything and start main loop */
    gtk_widget_show_all( window );
    gtk_main();

    return( 0 );
}
&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6666239544818473000-115112519350256652?l=blog.borovsak.si' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.borovsak.si/feeds/115112519350256652/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.borovsak.si/2009/04/gtkcombobox-widget-part-3.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6666239544818473000/posts/default/115112519350256652'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6666239544818473000/posts/default/115112519350256652'/><link rel='alternate' type='text/html' href='http://blog.borovsak.si/2009/04/gtkcombobox-widget-part-3.html' title='GtkComboBox widget - part 3'/><author><name>Tadej Borovšak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-V5S0cD-5b_I/AAAAAAAAAAI/AAAAAAAAArU/nAyUYzn6Yx8/s512-c/photo.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6666239544818473000.post-758659401615134373</id><published>2009-04-15T17:43:00.008+02:00</published><updated>2009-04-24T18:10:26.143+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Maintenance'/><title type='text'>Blog updates</title><content type='html'>I'm making some changes with syntax highlighting and layout, so please be patient if the page looks ugly at times.
If everything goes according to plan, tomorrow things should be fixed.

Bye&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-weight: bold;"&gt;Update:&lt;/span&gt;&lt;br /&gt;
I just finished maintenance tasks. I still have some problems with blogger template, but I can live with them a bit longer.
During the update, I moved syntax highlighting scripts and other files to my google pages account, so code snippets should load faster than before.

That's it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6666239544818473000-758659401615134373?l=blog.borovsak.si' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.borovsak.si/feeds/758659401615134373/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.borovsak.si/2009/04/blog-updates.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6666239544818473000/posts/default/758659401615134373'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6666239544818473000/posts/default/758659401615134373'/><link rel='alternate' type='text/html' href='http://blog.borovsak.si/2009/04/blog-updates.html' title='Blog updates'/><author><name>Tadej Borovšak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-V5S0cD-5b_I/AAAAAAAAAAI/AAAAAAAAArU/nAyUYzn6Yx8/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6666239544818473000.post-5371692627684818802</id><published>2009-04-15T16:29:00.022+02:00</published><updated>2009-05-17T21:16:09.120+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='GtkComboBox'/><category scheme='http://www.blogger.com/atom/ns#' term='tutorial'/><category scheme='http://www.blogger.com/atom/ns#' term='GTK+'/><title type='text'>GtkComboBox widget - part 2</title><content type='html'>As promised, I'm back with second part of my GtkComboBox widget tutorial.&lt;br /&gt;&lt;br /&gt;

Today, we'll be learning about "complex" API. The main thing to keep in mind when dealing with GtkComboBox using complex API is the fact that data is separated from it's representation, meaning that one object holds data (usually GtkTreeStore or GtkListStore), the other object renders it to the screen (GtkCellRenderer) and third object manipulates it (GtkComboBox in our case). This concept (also known as Model/View/Controller-design or MVC) is also used in GtkTreeView and GtkIconView widgets. GtkCellLayout interface provides the means to achieve all this.&lt;br /&gt;&lt;br /&gt;

Contents of this tutorial:
&lt;ol&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/04/gtkcombobox-widget-part-1.html"&gt;GtkComboBox widget - part 1 (simple API)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/04/as-promised-im-back-with-second-part-of.html"&gt;GtkComboBox widget - part 2 (complex API)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/04/gtkcombobox-widget-part-3.html"&gt;GtkComboBox widget - part 3 (test your knowledge)&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;span class="s_title"&gt;Complex API&lt;/span&gt;
&lt;br /&gt;&lt;br /&gt;
Let's start creating our combo box. We'll rewrite sample application from part 1 using the complex API.&lt;br /&gt;&lt;br /&gt;

First we need to create data object and fill it with some information. Since we're creating simple text list, our data object will be GtkListStore with only one text column. I won't go into details about creating and updating data store, since excellent description already exists in &lt;a href="http://scentric.net/tutorial/sec-treemodels.html"&gt;GtkTreeView tutorial (Chapter 3)&lt;/a&gt;. Read it first if you're not familiar with GtkTreeStore, GtkListStore; GtkTreeModel; GtkTreeIter and GtkTreePath.
&lt;pre class="brush: c; tab-size: 4"&gt;
/* Create data store ... */
store = gtk_list_store_new( 1, G_TYPE_STRING );

/* ... and fill it with some information. */
gtk_list_store_append( store, &amp;amp;iter );
gtk_list_store_set( store, &amp;amp;iter, 0, &amp;quot;Hello World once&amp;quot;, -1 );
gtk_list_store_prepend( store, &amp;amp;iter );
gtk_list_store_set( store, &amp;amp;iter, 0, &amp;quot;Hello World twice&amp;quot;, -1 );
gtk_list_store_insert( store, &amp;amp;iter, 1 );
gtk_list_store_set( store, &amp;amp;iter, 0, &amp;quot;Hello World last time&amp;quot;, -1 );
&lt;/pre&gt;
Now that we have our data safely stored inside GtkListStore object (model part of MVC), we'll create combo box and connect it to the store.
&lt;pre class="brush: c; tab-size: 4"&gt;
/* Create combo box with store as data source. */
combo = gtk_combo_box_new_with_model( GTK_TREE_MODEL( store ) );

/* Remove our reference from store to avoid memory leak. */
g_object_unref( G_OBJECT( store ) );
&lt;/pre&gt;
We now have model and controller parts of MVC, so all there is left to do is to create view. We want to display strings in our combo box, so we'll use GtkCellRendererText as view. After we create our renderer, we need to pack it inside our combo box and connect it to data source.
&lt;pre class="brush: c; tab-size: 4"&gt;
/* Create cell renderer. */
cell = gtk_cell_renderer_text_new();

/* Pack it to the combo box. */
gtk_cell_layout_pack_start( GTK_CELL_LAYOUT( combo ), cell, TRUE );

/* Connect renderer to data source */
gtk_cell_layout_set_attributes( GTK_CELL_LAYOUT( combo ), cell, &amp;quot;text&amp;quot;, 0, NULL );
&lt;/pre&gt;
In this code snippet we finally came across the mysterious GtkCellLayout interface. We used functions from it to add cell renderer to combo box and connect data with renderer. What exactly the last line of code does? We tell cell renderer that values for it's "text" property can be found in first column of the underlying data store. And that's it! We now have working combo box. But how to retrieve data from it like we did in simple example?&lt;br /&gt;&lt;br /&gt;

The process of retrieving data is composed from three steps:
&lt;ul&gt;&lt;li&gt;Getting active iter from combo box.&lt;/li&gt;&lt;li&gt;Getting model from combo box.&lt;/li&gt;&lt;li&gt;Getting data from model.&lt;/li&gt;&lt;/ul&gt;This might seem a bit complicated at first, but if you ever used GtkTreeView, you should feel right at home. This is how these steps look in code:
&lt;pre class="brush: c; tab-size: 4"&gt;
/* Obtain currently selected item from combo box.
 * If nothing is selected, do nothing. */
if( gtk_combo_box_get_active_iter( combo, &amp;amp;iter ) )
{
    /* Obtain data model from combo box. */
    model = gtk_combo_box_get_model( combo );
    
    /* Obtain string from model. */
    gtk_tree_model_get( model, &amp;amp;iter, 0, &amp;amp;string, -1 );
}
&lt;/pre&gt;
And the last thing that we have to implement to imitate previous sample application is removing currently selected item. This operation is very similar to retrieving data from combo box and is done in three steps (again;):
&lt;ul&gt;&lt;li&gt;Getting active iter from combo box.&lt;/li&gt;&lt;li&gt;Getting model from combo box.&lt;/li&gt;&lt;li&gt;Removing item from model.&lt;/li&gt;&lt;/ul&gt;And the code for these steps:
&lt;pre class="brush: c; tab-size: 4"&gt;
/* Obtain currently selected item form combo box.
 * If nothing is selected, do nothing. */
if( gtk_combo_box_get_active_iter( combo, &amp;amp;iter ) )
{
    /* Obtain model from combo box. */
    store = GTK_LIST_STORE( gtk_combo_box_get_model( combo ) );

    /* Remove item. */
    gtk_list_store_remove( store, &amp;amp;iter );
}
&lt;/pre&gt;
And the last thing we need to do is to test our application. Here is complete source code (highlighted parts are the ones that have been changed from simple to complex version):
&lt;pre class="brush: c; tab-size: 4; highlight: [13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113]"&gt;
/*
 * Test me with:
 *  gcc -o combo1 combo1.c $(pkg-config --cflags --libs gtk+-2.0) &amp;amp;&amp;amp; ./combo1
 */

#include &amp;lt;gtk/gtk.h&amp;gt;

/* This function gets called when currently selected item changes. */
static void
cb_changed( GtkComboBox *combo,
            gpointer     data )
{
    GtkTreeIter   iter;
    gchar        *string = NULL;
    GtkTreeModel *model;

    /* Obtain currently selected item from combo box.
     * If nothing is selected, do nothing. */
    if( gtk_combo_box_get_active_iter( combo, &amp;amp;iter ) )
    {
        /* Obtain data model from combo box. */
        model = gtk_combo_box_get_model( combo );

        /* Obtain string from model. */
        gtk_tree_model_get( model, &amp;amp;iter, 0, &amp;amp;string, -1 );
    }

    /* Print string to the console - if string is NULL, print NULL. */
    g_print( &amp;quot;Selected (complex): &amp;gt;&amp;gt; %s &amp;lt;&amp;lt;\n&amp;quot;, ( string ? string : &amp;quot;NULL&amp;quot; ) );

    /* Free string (if not NULL). */
    if( string )
        g_free( string );
}

/* This function deletes currently selected item from combo box. */
static void
cb_delete( GtkButton   *button,
           GtkComboBox *combo )
{
    GtkTreeIter   iter;
    GtkListStore *store;

    /* Obtain currently selected item form combo box.
     * If nothing is selected, do nothing. */
    if( gtk_combo_box_get_active_iter( combo, &amp;amp;iter ) )
    {
        /* Obtain model from combo box. */
        store = GTK_LIST_STORE( gtk_combo_box_get_model( combo ) );

        /* Remove item. */
        gtk_list_store_remove( store, &amp;amp;iter );
    }
}


int
main( int    argc,
      char **argv )
{
    GtkWidget       *window;
    GtkWidget       *vbox;
    GtkWidget       *frame;
    GtkWidget       *combo;
    GtkWidget       *button;

    GtkListStore    *store;
    GtkTreeIter      iter;
    GtkCellRenderer *cell;

    /* Initialization */
    gtk_init( &amp;amp;argc, &amp;amp;argv );

    /* Create main window */
    window = gtk_window_new( GTK_WINDOW_TOPLEVEL );
    g_signal_connect( G_OBJECT( window ), &amp;quot;destroy&amp;quot;,
                      G_CALLBACK( gtk_main_quit ), NULL );
    gtk_container_set_border_width( GTK_CONTAINER( window ), 10 );

    /* Create vbox */
    vbox = gtk_vbox_new( FALSE, 6 );
    gtk_container_add( GTK_CONTAINER( window ), vbox );

    /* Create frame */
    frame = gtk_frame_new( &amp;quot;Complex API&amp;quot; );
    gtk_box_pack_start( GTK_BOX( vbox ), frame, FALSE, FALSE, 0 );

    /* Create data store ... */
    store = gtk_list_store_new( 1, G_TYPE_STRING );

    /* ... and fill it with some information. */
    gtk_list_store_append( store, &amp;amp;iter );
    gtk_list_store_set( store, &amp;amp;iter, 0, &amp;quot;Hello World once&amp;quot;, -1 );
    gtk_list_store_prepend( store, &amp;amp;iter );
    gtk_list_store_set( store, &amp;amp;iter, 0, &amp;quot;Hello World twice&amp;quot;, -1 );
    gtk_list_store_insert( store, &amp;amp;iter, 1 );
    gtk_list_store_set( store, &amp;amp;iter, 0, &amp;quot;Hello World last time&amp;quot;, -1 );

    /* Create combo box with store as data source. */
    combo = gtk_combo_box_new_with_model( GTK_TREE_MODEL( store ) );
    gtk_container_add( GTK_CONTAINER( frame ), combo );

    /* Remove our reference from store to avoid memory leak. */
    g_object_unref( G_OBJECT( store ) );

    /* Create cell renderer. */
    cell = gtk_cell_renderer_text_new();

    /* Pack it into the combo box. */
    gtk_cell_layout_pack_start( GTK_CELL_LAYOUT( combo ), cell, TRUE );

    /* Connect renderer to data source. */
    gtk_cell_layout_set_attributes( GTK_CELL_LAYOUT( combo ), cell, &amp;quot;text&amp;quot;, 0, NULL );

    /* Connect signal to our handler function. */
    g_signal_connect( G_OBJECT( combo ), &amp;quot;changed&amp;quot;,
                      G_CALLBACK( cb_changed ), NULL );

    /* Add button that, when clicked, deletes currently selected entry. */
    button = gtk_button_new_with_mnemonic( &amp;quot;_Delete selected item&amp;quot; );
    gtk_box_pack_start( GTK_BOX( vbox ), button, FALSE, FALSE, 0 );
    g_signal_connect( G_OBJECT( button ), &amp;quot;clicked&amp;quot;,
                      G_CALLBACK( cb_delete ), GTK_COMBO_BOX( combo ) );

    /* Show our application and start main loop. */
    gtk_widget_show_all( window );
    gtk_main();

    return( 0 );
}
&lt;/pre&gt;
And that will conclude our lesson for today. Join me next time in part three, when we'll be making salad from images, labels and progress bars, all served in GtkComboBox bowl;)&lt;br /&gt;&lt;br /&gt;

Bye&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6666239544818473000-5371692627684818802?l=blog.borovsak.si' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.borovsak.si/feeds/5371692627684818802/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.borovsak.si/2009/04/as-promised-im-back-with-second-part-of.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6666239544818473000/posts/default/5371692627684818802'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6666239544818473000/posts/default/5371692627684818802'/><link rel='alternate' type='text/html' href='http://blog.borovsak.si/2009/04/as-promised-im-back-with-second-part-of.html' title='GtkComboBox widget - part 2'/><author><name>Tadej Borovšak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-V5S0cD-5b_I/AAAAAAAAAAI/AAAAAAAAArU/nAyUYzn6Yx8/s512-c/photo.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6666239544818473000.post-9166403513873730532</id><published>2009-04-13T20:25:00.014+02:00</published><updated>2009-05-17T21:16:25.516+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='GtkComboBox'/><category scheme='http://www.blogger.com/atom/ns#' term='tutorial'/><category scheme='http://www.blogger.com/atom/ns#' term='GTK+'/><title type='text'>GtkComboBox widget - part 1</title><content type='html'>I'm back with first meaningful post.

We'll be looking at GtkComboBox widget today. Why? Because &lt;a href="http://library.gnome.org/devel/gtk-tutorial/stable/x1065.html"&gt;GTK+'s official tutorial&lt;/a&gt; only talks about deprecated GtkCombo. Let's start with short introduction.
&lt;a href="http://library.gnome.org/devel/gtk/stable/GtkComboBox.html"&gt;GtkComboBox&lt;/a&gt; is a widget whose main task is to let users select from a list or a tree of valid choices. It is quite powerful widget and is able to display everything from simple text strings to very complex data trees with images, progress bars, etc. All this is reflected in it's API, which can be separated in "simple" (text) API and "complex" API, which is closely related to GTK+'s GtkCellLayout interface. We'll be looking at the simple API functions today.&lt;br /&gt;&lt;br /&gt;

Contents of this tutorial:
&lt;ol&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/04/gtkcombobox-widget-part-1.html"&gt;GtkComboBox widget - part 1 (simple API)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/04/as-promised-im-back-with-second-part-of.html"&gt;GtkComboBox widget - part 2 (complex API)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://tadeboro.blogspot.com/2009/04/gtkcombobox-widget-part-3.html"&gt;GtkComboBox widget - part 3 (test your knowledge)&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;span class="s_title"&gt;Simple API&lt;/span&gt;
&lt;br /&gt;&lt;br /&gt;
Simple API consists of 7 functions:
&lt;ul&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;gtk_combo_box_new_text&lt;/span&gt; for creating combo box&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;gtk_combo_box_append_text&lt;/span&gt;, &lt;span style="font-weight: bold;"&gt;gtk_combo_box_prepend_text&lt;/span&gt; and &lt;span style="font-weight: bold;"&gt;gtk_combo_box_insert_text&lt;/span&gt; for adding items to combo box&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;gtk_combo_box_get_active_text&lt;/span&gt; for obtaining currently selected item&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;gtk_combo_box_get_active&lt;/span&gt; for obtainig active item's index number (this function is part of both, simple and complex, APIs)&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;gtk_combo_box_remove_text&lt;/span&gt; from removing items from combo box&lt;/li&gt;&lt;/ul&gt;
But enough talking, let's see some code now!;)&lt;br /&gt;&lt;br /&gt;

This is a "Hello World! on steroids" application that shows how to create simple text-only combo box using simple API. It demonstrates using all of the functions from simple API. Yes, that is all there is to it.
&lt;pre class="brush: c"&gt;
/* Test me with:
*   gcc -o combo combo.c $(pkg-config --cflags --libs gtk+-2.0) &amp;amp;&amp;amp; ./combo
*/

#include &amp;lt;gtk/gtk.h&amp;gt;

/* This function gets called when currently selected item changes */
static void
cb_changed( GtkComboBox *combo,
            gpointer     data )
{
    /* Obtain currently selected string from combo box */
    gchar *string = gtk_combo_box_get_active_text( combo );

    /* Print it to the console - if nothing is selected, print NULL */
    g_print( "Selected (simple): &amp;gt;&amp;gt; %s &amp;lt;&amp;lt;\n", ( string ? string : "NULL" ) );

    /* Free string */
    g_free( string );
}

/* This function deletes currently selected item from combo box */
static void
cb_delete( GtkButton   *button,
           GtkComboBox *combo )
{
    gint index;

    /* Get currently selected item's index */
    index = gtk_combo_box_get_active( combo );

    /* Remove this item from combo */
    gtk_combo_box_remove_text( combo, index );
}

int
main( int    argc,
      char **argv )
{
    /*Common variables */
    GtkWidget *window;
    GtkWidget *vbox;
    GtkWidget *frame;
    GtkWidget *combo;
    GtkWidget *button;

    /* Initialization */
    gtk_init( &amp;amp;argc, &amp;amp;argv );

    /* Create main window */
    window = gtk_window_new( GTK_WINDOW_TOPLEVEL );
    g_signal_connect( G_OBJECT( window ), "destroy",
                      G_CALLBACK( gtk_main_quit ), NULL );
    gtk_container_set_border_width( GTK_CONTAINER( window ), 10 );

    /* Create vbox */
    vbox = gtk_vbox_new( FALSE, 6 );
    gtk_container_add( GTK_CONTAINER( window ), vbox );

    /* Create frame */
    frame = gtk_frame_new( "Text API" );
    gtk_box_pack_start( GTK_BOX( vbox ), frame, FALSE, FALSE, 0 );

    /* Create combo box using text API and add some data to it */
    combo = gtk_combo_box_new_text();
    gtk_container_add( GTK_CONTAINER( frame ), combo );
    gtk_combo_box_append_text( GTK_COMBO_BOX( combo ), "Hello World once" );
    gtk_combo_box_prepend_text( GTK_COMBO_BOX( combo ), "Hello World twice" );
    gtk_combo_box_insert_text( GTK_COMBO_BOX( combo ), 1, "Hello World last time" );

    /* Connect signal to tour handler function */
    g_signal_connect( G_OBJECT( combo ), "changed",
                      G_CALLBACK( cb_changed ), NULL );


    /* Add button that, when clicked, deletes currently selected entry */
    button = gtk_button_new_with_mnemonic( "_Delete selected item" );
    gtk_box_pack_start( GTK_BOX( vbox ), button, FALSE, FALSE, 0 );
    g_signal_connect( G_OBJECT( button ), "clicked",
                      G_CALLBACK( cb_delete ), GTK_COMBO_BOX( combo ) );

    /* Show our application and start main loop */
    gtk_widget_show_all( window );
    gtk_main();

    return( 0 );
}
&lt;/pre&gt;
See you in part 2 of this tutorial, where we'll tackle advanced API.&lt;br /&gt;&lt;br /&gt;

Bye.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6666239544818473000-9166403513873730532?l=blog.borovsak.si' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.borovsak.si/feeds/9166403513873730532/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.borovsak.si/2009/04/gtkcombobox-widget-part-1.html#comment-form' title='12 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6666239544818473000/posts/default/9166403513873730532'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6666239544818473000/posts/default/9166403513873730532'/><link rel='alternate' type='text/html' href='http://blog.borovsak.si/2009/04/gtkcombobox-widget-part-1.html' title='GtkComboBox widget - part 1'/><author><name>Tadej Borovšak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-V5S0cD-5b_I/AAAAAAAAAAI/AAAAAAAAArU/nAyUYzn6Yx8/s512-c/photo.jpg'/></author><thr:total>12</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6666239544818473000.post-4869546164056016347</id><published>2009-04-13T15:16:00.003+02:00</published><updated>2009-04-24T21:49:59.886+02:00</updated><title type='text'>First post</title><content type='html'>Hello everyone.

Looking for some GTK+/glib sample applications, tips, documentation? You're in the right place. But unfortunately, you came across this site too soon, since nothing is posted here yet. Check out this place again in couple of days and see if you can find something interesting.&lt;br /&gt;&lt;br /&gt;

Bye.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6666239544818473000-4869546164056016347?l=blog.borovsak.si' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.borovsak.si/feeds/4869546164056016347/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.borovsak.si/2009/04/first-post.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6666239544818473000/posts/default/4869546164056016347'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6666239544818473000/posts/default/4869546164056016347'/><link rel='alternate' type='text/html' href='http://blog.borovsak.si/2009/04/first-post.html' title='First post'/><author><name>Tadej Borovšak</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-V5S0cD-5b_I/AAAAAAAAAAI/AAAAAAAAArU/nAyUYzn6Yx8/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry></feed>
