GTK+ is a C based toolkit for programming graphical applications in X
windows.
A D V E R T I S E M E N T
It is highly object oriented and has bindings to many popular
languages, such as C++, Objective C, Perl, TOM, Guile, Python, etc ... GTK+
also uses GLib, which is a very useful C library, and includes things to
help porting to different architectures, and containers such as a linked
list or a hash. If you are already familiar with GTK+, you are now free to
get bored.
GLib
Naming Conventions
GLib is a utility library which is heavily used in GTK+ and most of
GNOME. GLib's functions are named starting with g_
(such as g_strdup), GLib's typedefs for common types
are just prefixed with a g (such as
gint32), and GLib's structures are capitalized and
start with G (such as GHashTable).
Typedefs
GLib provides some typedefs for portability, simplification of code,
clarity of code and yet others just to keep consistent. The following table
lists these typedefs. If the Equivalent, field is
blank, there is no platform independent equivalent.
Table 2-1. GLib Typedefs
Name
Equivalent
Description
gint8
8bit wide signed integer
guint8
8bit wide unsigned integer
gint16
16bit wide signed integer
guint16
16bit wide unsigned integer
gint32
32bit wide signed integer
guint32
32bit wide unsigned integer
gint64
64bit wide signed integer (see
note below)
guint64
64bit wide unsigned integer (see
note below)
gchar
char
Standard character value
guchar
unsigned char
Standard unsigned character value
gshort
short
Standard short integer
gushort
unsigned short
Standard unsigned short integer
glong
long
Standard long integer
gulong
unsigned long
Standard unsigned long integer
gint
int
Standard integer
guint
unsigned int
Standard unsigned integer
gfloat
float
Standard float number type
gdouble
double
Standard float number type
gboolean
int
Type for storing TRUE/FALSE values
gpointer
void *
Type for storing pointers to
arbitrary objects
gconstpointer
const void *
Type for storing pointers to
arbitrary immutable objects
It should be noted that gint64 and
guint64 might not be available on all platforms. You
can check for this in your code by checking to see if the macro
G_HAVE_GINT64 is defined.
As you can see, some of the typedefs such as gint
seem to have no other meaning in life then that of having a 'g' prefix and
looking the same as the other typedefs. The logic behind this is to make the
code look more consistent and clear. While it is no crime not to use these
typedefs, you should really be consistent in your code. Some of the typedefs
such as gboolean are only for improving code clarity
and you could just as well use int to do exactly the
same thing, but the former method clearly indicates that you are talking
about a value that can only take TRUE or FALSE.
Portability and Utility Functions
There are some functions that have different implementations across
different systems or are not extremely safe, or don't exist at all on some
systems, so GLib provides it's own implementations or wrappers that have a
constant behavior and usually check their arguments.
Here are some of the more useful functions that fit this category. Note
that the prototype is more of an informative one, as some of these might be
macros in reality.
Table 2-2. Few GLib Portability Functions
Prototype
Description
gchar * g_strdup (const gchar *)
Returns a newly allocated string
which is a copy of the argument, if the argument is NULL, NULL
is returned
gpointer g_malloc (int size)
Returns a newly region of memory
with 'size' bytes
void g_free (gpointer p)
Frees memory pointed to by 'p',
and only returns if 'p' is NULL
gint g_snprintf (gchar *string,
gulong n, gchar const *format, ...)
Works just like sprintf by
printing the arguments according to the 'format' into string,
however it will only use 'n' bytes of the string and will thus
truncate the result if it needed more. It returns the number of
bytes actually printed into 'string'
void g_usleep (gulong count)
Suspend execution for at least
'count' microseconds
Compare the first n characters of
string s1 and s2 in a case insensitive manner
And there are also some utility functions and macros that are not really
found in the normal c library. Here is a very short list of some of the more
important and useful ones.
Table 2-3. Few GLib Utility Functions
Prototype
Description
g_new (type,count)
A macro which will allocate new
memory for 'count' items of type 'type' and cast the result to
'type'. It is equivalent to '(type) g_malloc(count *
sizeof(type))'
g_new0 (type,count)
Same semantics as g_new, except
that the returned memory will be set to all zeros. Note that you
should not assume that setting the memory to zeros will zero out
floating point types
gchar * g_strconcat (const gchar
*str, ...)
When passed any number of
arguments of the type (const char *) and a NULL after the last
argument, it will return a newly allocated string that results
by concatenation of all the arguments.
A printf like function that will
return a newly allocated string with the result of the printf
operation
gchar * g_strstrip (gchar *string)
Will strip leading and trailing
whitespace from the string. It will not allocate new memory, but
will modify the original string and return a pointer to it. If
you wish to allocate new memory use a construction such as:
'string2 = g_strstrip(g_strdup(string1));'
There are many other useful methods in GLib, and I urge you to study GLib
documentation and the GLib header file (glib.h), and
you may be able to save a lot of time by not re-implementing some basic
functionality.
Containers
Probably the best part of GLib are its containers. Here's a list of
GLib's containers.
Table 2-4. Common GLib Containers
Name
Description
GList
Doubly linked list
GSList
Singly linked list
GHashTable
Hash table
GCache
Cache
GTree
Balanced binary tree
GNode
n-ary tree
GString
Dynamically sized string
GArray
Dynamically sized array
GPtrArray
Dynamically sized array of
pointers
GByteArray
Dynamically sized array of bytes
(guint8)
GList, Doubly Linked List
The easiest to use are GList's. The basic
GList structure is just a single node of the
linked list and you can put your data into the data pointer in the
GList structure. To store a linked list you just
store a pointer to the first node of the list. Here is the list of
functions that operate on a GList. The functions usually take in a
pointer and return the new pointer of the list, since the first node
could now be a different one.
Table 2-5. Most Important GList Functions
Prototype
Description
GList* g_list_append (GList
*list, gpointer data)
Append 'data' to a list.
'list' can be NULL to make a new list.
Prepend 'data' to a list.
'list' can be NULL to make a new list.
GList* g_list_remove (GList
*list, gpointer data)
Remove the node containing
'data' from the list.
GList* g_list_find (GList
*list, gpointer data)
Find the GList node that
contains the 'data'.
GList* g_list_next (GList
*list)
A macro that returns a pointer
to the next node.
GList* g_list_previous (GList
*list)
A macro that returns a pointer
to the previous node.
void g_list_free(GList *list)
Free the entire list.
To access the data from a particular GList
node You look at the data member in the
GList structure. So code that would create a
linked list of two elements which are strdup'ed strings, and later free
that list and the strings would look like:
GList *list = NULL; /*the actual list pointer*/
GList *li; /*just a temporary pointer to a node used for iterating
over the list*/
...
/*here we add two strings to the list*/
list = g_list_append(list,g_strdup("String 1"));
list = g_list_append(list,g_strdup("String 2"));
...
/*here we loop though the list, freeing all the strings and then
we free the list itself*/
for(li = list; li!= NULL; li = g_list_next(li)) {
char *string = li->data;
g_free(string);
}
g_list_free(list);
GString, Dynamically Sized String
Type
Another simple to use and useful container is the
GString container. It's a dynamically sized string container for the
times when you don't know how large the string you will need will be.
Here's a list of the most important functions.
Table 2-6. Most Important GString Functions
Prototype
Description
GString* g_string_new (const
gchar *init)
Create a new GString with
initial value of 'init'
void g_string_free (GString
*string, int free_segment)
Free the GString structure and
optionally also the string data segment
A sprintf like function for
GString, but appends the string instead of overwriting it
To access the string data for use as a char *,
just access the str element of the
GString structure. You can actually free the
GString structure without freeing this data
segment. This is useful if you want to create a normal C string. The
following example is a function that takes an array of integers and
sprintfs them into a string and returns a char *.
char *
create_number_list(int array[], int array_len)
{
int i; /* the array iterator */
GString *string; /* the GString */
char *ret; /* the return value */
/* make a new GString that is empty */
string = g_string_new("");
/* iterate over the integer array */
for(i=0; i<array_len; i++) {
/* append the number to the string in parenthesis */
g_string_sprintfa(string, "(%d)", array[i]);
}
/* setup the return value */
ret = string->str;
/* free the GString structure, but not the data */
g_string_free(string,FALSE);
/* return the string */
return ret;
}
GHashTable
Though less often used then GList's and GString's. The hash table
container is a very useful one. Usually by a hash table one would mean
an object (in GLib's terms a gpointer) would
have a string key, by which we could recall the object at a later time.
GLib takes this a step further, making the key a
gpointer as well, and letting you provide a hashing and a comparison
function yourself. While this makes GHashTable
much more flexible, it can lead to some confusion with respect to memory
allocation for the keys. Let's give some important functions and deal
with the details later:
Lookup the data with the key
of 'value_key', store the original key pointer in 'orig_key'
and the value in 'value'. Returns TRUE if the lookup was
successful else it returns FALSE. You should use this
function when removing an item to get rid of the original
key in memory.
Run a function for each data
stored in the hash table. The 'user_data' will be passed to
the function as the last argument. The GHFunc prototype
follows.
This is the function prototype
that you will use for the function that is passed to
g_hash_table_foreach. It gets passed the key, the value and
the user_data specified in the g_hash_table_foreach call.
guint g_str_hash (gconstpointer
v)
A standard string hash
function for string hash tables
A standard string compare
function for string hash tables
To create a hash table, you pass the hash and key compare functions
to g_hash_table_new. There are standard
functions defined for strings (g_str_hash and
g_str_equal) and others. However if you pass
NULL as the hash and compare functions, you will get a direct pointer
hash, where pointers will be actually themselves used as keys.
The problem of memory allocation becomes apparent when we start using
string hashes. GHashTable doesn't store the
string, all it stores is a pointer. Therefore, when inserting a value
into the hash, you have to create a new copy of the key for that value.
This is an important thing to remember as otherwise things are not going
to behave really nice for you. The other problem is how to then get rid
of the key. If you do a g_hash_table_remove, you
give as a key a string with the same contents as the original key, but
not the same memory location. After then a pointer to the original key
would be lost and unless you stored a pointer to it somewhere, you just
created a memory leak. What you need to do instead is to do a
g_hash_table_lookup_extended first to get both
the value and the original key pointer and then do the
g_hash_table_remove.
The following example will make a new string hash, insert a couple of
strings into it, retrieve them, and then destroy the hash and the values
stored in it:
/* function we use for freeing the keys and data in the hash before
we destroy the hash */
static void
free_key_value(gpointer key, gpointer value, gpointer user_data)
{
g_free(key);
g_free(value);
}
...
/* somewhere else in the code */
GHashTable *ht;
/* create a new hash table with strings as keys */
ht = g_hash_table_new(g_str_hash, g_str_equal);
/* insert a couple of strings (say colors keyed by shape) */
g_hash_table_insert(ht, g_strdup("triangle"), g_strdup("green"));
g_hash_table_insert(ht, g_strdup("square"), g_strdup("red"));
g_hash_table_insert(ht, g_strdup("circle"), g_strdup("blue"));
/* again, somewhere else in the code */
...
/* now here we wish to print out the color of a square */
char *color;
/* get the color of a square */
color = g_hash_table_lookup(ht, "square");
printf("The color of a square is: %s\n",color);
/* yet again somewhere else */
...
/* Now here we just want to destroy the hash table and free all the
* memory associated with it. We use the free_key_value function and
* have it run over all the values in the hash table. */
g_hash_foreach(ht, free_key_value, NULL);
/* now we can destroy the actual hash table */
g_hash_table_destroy(ht);