Documentation/Maemo 5 Developer Guide/Using Generic Platform Components/Application Preferences-Gconf

m (Application Preferences - GConf)
(Implementing Notifications on Changes in GConf: fix link)
 
(9 intermediate revisions not shown)
Line 1: Line 1:
-
= Application Preferences - GConf=
+
[http://projects.gnome.org/gconf/ GConf] is used by the GNOME desktop environment for storing shared configuration settings for the desktop and applications. The daemon process GConfd follows the changes in the database. When a change occurs in the database, the daemon applies the new settings to the applications using them. For example, the control panel application uses GConf.
-
GConf is used by the GNOME desktop environment for storing shared configuration settings for the desktop and applications. The daemon process GConfd follows the changes in the database. When a change occurs in the database, the daemon applies the new settings to the applications using them. For example, the control panel application uses GConf.
+
If settings are used only by a single application, GLib utility for .ini style files should be used instead. Applications should naturally have working default settings. Settings should be saved only when the user changes them.  
-
If settings are used only by a single application, Glib utility for .ini style files should be used instead. Applications should naturally have working default settings. Settings should be saved only when the user changes them.
+
This chapter uses the following examples:
-
 
+
* [https://vcs.maemo.org/svn/maemoexamples/tags/maemo_5.0/support-libraries/hildon_helloworld-9.c hildon_helloworld-9.c]
-
This chapter uses the following example:
+
* [https://vcs.maemo.org/svn/maemoexamples/tags/maemo_5.0/gconf-listener/ gconf-listener]
-
*[https://vcs.maemo.org/svn/maemoexamples/tags/maemo_5.0/support-libraries/hildon_helloworld-9.c hildon_helloworld-9.c]
+
-
*[https://vcs.maemo.org/svn/maemoexamples/tags/maemo_5.0/gconf-listener/ gconf-listener]
+
==GConf Basics==
==GConf Basics==
Line 23: Line 21:
The GConf model consists of two parts: the GConf client library (which will be used here) and the GConf server daemon that is the guardian and reader/writer of the back-end databases. In a regular GNOME environment, the client communicates with the server either by using the Bonobo library (lightweight object IPC mechanism) or D-Bus.
The GConf model consists of two parts: the GConf client library (which will be used here) and the GConf server daemon that is the guardian and reader/writer of the back-end databases. In a regular GNOME environment, the client communicates with the server either by using the Bonobo library (lightweight object IPC mechanism) or D-Bus.
-
As Bonobo is not used in maemo (it is quite heavy, even if lightweight), the client will communicate with the server using D-Bus. This also allows the daemon to be started on demand, when there is at least one client wanting to use that service (this is a feature of D-Bus). The communication mechanism is encapsulated by the GConf client library, and as such, will be transparent.
+
As Bonobo is not used in Maemo (it is quite heavy, even if lightweight), the client will communicate with the server using D-Bus. This also allows the daemon to be started on demand, when there is at least one client wanting to use that service (this is a feature of D-Bus). The communication mechanism is encapsulated by the GConf client library, and as such, will be transparent.
In order to read or write the preference database, it is necessary to decide on the key to use to access the application values. The database namespace is hierarchical, and uses the '/'-character to implement this hierarchy, starting from a root location similar to UNIX file system namespace.
In order to read or write the preference database, it is necessary to decide on the key to use to access the application values. The database namespace is hierarchical, and uses the '/'-character to implement this hierarchy, starting from a root location similar to UNIX file system namespace.
-
Each application will use its own "directory" under '''/apps/Maemo/appname/'''. N.B. Even when the word "directory" is seen in connection to GConf, one has to be careful to distinguish '''real directories''' from '''preference namespaces''' inside the GConf namespace. The /apps/Maemo/appname/ above is in the GConf namespace, so there will not actually be a physical directory called '''/apps/''' on a system.
+
Each application will use its own "directory" under <code>/apps/Maemo/appname/</code>. N.B. Even when the word "directory" is seen in connection to GConf, one has to be careful to distinguish '''real directories''' from '''preference namespaces''' inside the GConf namespace. The <code>/apps/Maemo/appname/</code> above is in the GConf namespace, so there will not actually be a physical directory called <code>/apps/</code> on a system.
-
The keys should be named according to the platform guidelines. The current guideline is that each application should store its configuration keys under '''/apps/Maemo/appname/''', where appname is the name of the application. There is no central registry on the names in use currently, so names should be selected carefully. Key names should all be lowercase, with underscore used to separate multiple words. Also, ASCII should be used, since GConf does not support localization for key names (it does for key values, but that is not covered in this material).
+
The keys should be named according to the platform guidelines. The current guideline is that each application should store its configuration keys under <code>/apps/Maemo/appname/</code>, where <code>appname</code> is the name of the application. There is no central registry on the names in use currently, so names should be selected carefully. Key names should all be lowercase, with underscore used to separate multiple words. Also, ASCII should be used, since GConf does not support localization for key names (it does for key values, but that is not covered in this material).
GConf values are typed, which means that it is necessary to select the type for the data that the key is supposed to hold.
GConf values are typed, which means that it is necessary to select the type for the data that the key is supposed to hold.
Line 35: Line 33:
The following types are supported for values in GConf:
The following types are supported for values in GConf:
-
* gint (32-bit signed)
+
* <code>gint</code> (32-bit signed)
-
* gboolean
+
* <code>gboolean</code>
-
* gchar (ASCII/ISO 8859-1/UTF-8 C string)
+
* <code>gchar</code> (ASCII/ISO 8859-1/UTF-8 C string)
-
* gfloat (with the limitation that the resolution is not guaranteed nor specified by GConf because of portability issues)
+
* <code>gfloat</code> (with the limitation that the resolution is not guaranteed nor specified by GConf because of portability issues)
* a list of values of one type
* a list of values of one type
* a pair of values, each having their own type (useful for storing "mapping" data)
* a pair of values, each having their own type (useful for storing "mapping" data)
Line 44: Line 42:
What is missing from the above list is storing binary data (for a good reason). The type system is also fairly limited. This is on purpose, so that complex configurations (like the Apache HTTP daemon uses, or Samba) are not attempted using GConf.
What is missing from the above list is storing binary data (for a good reason). The type system is also fairly limited. This is on purpose, so that complex configurations (like the Apache HTTP daemon uses, or Samba) are not attempted using GConf.
-
There is a diagnostic and administration tool called gconftool-2 that is also available in the SDK. It can be used to set and unset keys, as well as display their current contents.
+
There is a diagnostic and administration tool called <code>gconftool-2</code> that is also available in the SDK. It can be used to set and unset keys, as well as display their current contents.
Some examples of using gconftool-2 (on the SDK):
Some examples of using gconftool-2 (on the SDK):
-
* Displaying the contents of all keys stored under '''/apps/''' (listing cut for brevity) <br /><br />
+
* Displaying the contents of all keys stored under <code>/apps/</code> (listing cut for brevity)
-
+
 
-
[sbox-DIABLO_X86: ~] &gt; run-standalone.sh gconftool-2 -R /apps
+
<pre>
-
  /apps/osso:
+
[sbox-DIABLO_X86: ~] > run-standalone.sh gconftool-2 -R /apps
-
  /apps/osso/inputmethod:
+
/apps/osso:
-
    launch_finger_kb_on_select = true
+
  /apps/osso/inputmethod:
-
    input_method_plugin = himExample_vkb
+
  launch_finger_kb_on_select = true
-
    available_languages = [en_GB]
+
  input_method_plugin = himExample_vkb
-
    use_finger_kb = true
+
  available_languages = [en_GB]
-
    /apps/osso/inputmethod/hildon-im-languages:
+
  use_finger_kb = true
-
    language-0 = en_GB
+
  /apps/osso/inputmethod/hildon-im-languages:
-
    current = 0
+
    language-0 = en_GB
-
    language-1 =
+
    current = 0
-
    list = []
+
    language-1 =
-
  /apps/osso/fontconfig:
+
    list = []
-
    font_scaling_factor = Schema (type: `float' list_type:
+
  /apps/osso/fontconfig:
-
    '*invalid*' car_type: '*invalid*' cdr_type: '*invalid*'
+
  font_scaling_factor = Schema (type: `float' list_type:
-
    locale: `C')
+
    '*invalid*' car_type: '*invalid*' cdr_type: '*invalid*'
-
  /apps/osso/apps:
+
    locale: `C')
-
    /apps/osso/apps/controlpanel:
+
  /apps/osso/apps:
-
    groups = [copa_ia_general,copa_ia_connectivity,
+
  /apps/osso/apps/controlpanel:
-
              copa_ia_personalisation]
+
    groups = [copa_ia_general,copa_ia_connectivity,
-
    icon_size = false
+
              copa_ia_personalisation]
-
    group_ids = [general,connectivity,personalisation]
+
    icon_size = false
-
  /apps/osso/osso:
+
    group_ids = [general,connectivity,personalisation]
-
    /apps/osso/osso/thumbnailers:
+
  /apps/osso/osso:
-
    /apps/osso/osso/thumbnailers/audio@x-mp3:
+
  /apps/osso/osso/thumbnailers:
-
      command = /usr/bin/hildon-thumb-libid3
+
    /apps/osso/osso/thumbnailers/audio@x-mp3:
-
    /apps/osso/osso/thumbnailers/audio@x-m4a:
+
    command = /usr/bin/hildon-thumb-libid3
-
      command = /usr/bin/hildon-thumb-libid3
+
    /apps/osso/osso/thumbnailers/audio@x-m4a:
-
    /apps/osso/osso/thumbnailers/audio@mp3:
+
    command = /usr/bin/hildon-thumb-libid3
-
      command = /usr/bin/hildon-thumb-libid3
+
    /apps/osso/osso/thumbnailers/audio@mp3:
-
    /apps/osso/osso/thumbnailers/audio@x-mp2:
+
    command = /usr/bin/hildon-thumb-libid3
-
      command = /usr/bin/hildon-thumb-libid3
+
    /apps/osso/osso/thumbnailers/audio@x-mp2:
 +
    command = /usr/bin/hildon-thumb-libid3
 +
</pre>
-
* Creating and setting the value to a new key. <br /><br />
+
* Creating and setting the value to a new key.
   
   
  [sbox-DIABLO_X86: ~] &gt; run-standalone.sh gconftool-2  \
  [sbox-DIABLO_X86: ~] &gt; run-standalone.sh gconftool-2  \
   --set /apps/Maemo/testing/testkey --type=int 5
   --set /apps/Maemo/testing/testkey --type=int 5
-
* Listing all keys under the namespace '''/apps/Maemo/testing'''. <br /><br />
+
* Listing all keys under the namespace <code>/apps/Maemo/testing</code>.
   
   
  [sbox-DIABLO_X86: ~] &gt; run-standalone.sh gconftool-2  \
  [sbox-DIABLO_X86: ~] &gt; run-standalone.sh gconftool-2  \
Line 94: Line 94:
   testkey = 5
   testkey = 5
-
* Removing the last key will also remove the key directory. <br /><br />
+
* Removing the last key will also remove the key directory.
   
   
  [sbox-DIABLO_X86: ~] &gt; run-standalone.sh gconftool-2  \
  [sbox-DIABLO_X86: ~] &gt; run-standalone.sh gconftool-2  \
Line 101: Line 101:
   -R /apps/Maemo/testing
   -R /apps/Maemo/testing
-
* Removing whole key hierarchies is also possible. <br /><br />
+
* Removing whole key hierarchies is also possible.
   
   
  [sbox-DIABLO_X86: ~] &gt; run-standalone.sh gconftool-2  \
  [sbox-DIABLO_X86: ~] &gt; run-standalone.sh gconftool-2  \
   --recursive-unset /apps/Maemo/testing
   --recursive-unset /apps/Maemo/testing
-
 
+
For more detailed information, please see [http://maemo.org/api_refs/5.0/5.0-final/gconf2/ Gconf API documentation].
-
For more detailed information, please see [http://maemo.org/api_refs/5.0/beta/gconf2/ Gconf API documentation].
+
==  Using GConf to read and write preferences ==
==  Using GConf to read and write preferences ==
-
 
-
Section ''Application Settings'' [/node9.html#sec:application_settings [[Image:crossref.png|[*]]]] of chapter ''Application Development'' in Maemo Reference Manual presents a short introductory example of gconf usage. The following example is a bit more complicated.
 
The example is required to:
The example is required to:
Line 118: Line 115:
* Load the color preference on application startup.
* Load the color preference on application startup.
-
Even if GConf concepts seem to be logical, it can be seen that using GConf will require one to learn some new things (e.g. the GError-object). Since the GConf client code is in its own library, the relevant compiler flags and library options need to be added again. The pkg-config package name is gconf-2.0 hildon_helloworld-9.c
+
Even if GConf concepts seem to be logical, it can be seen that using GConf will require one to learn some new things (e.g. the <code>GError</code>-object). Since the GConf client code is in its own library, the relevant compiler flags and library options need to be added again. The pkg-config package name is <code>gconf-2.0</code> hildon_helloworld-9.c
-
<tt><span>''<span><font color="#9A1900">/**</font></span>''</span>
+
<source lang="c">
-
  <span>''<span><font color="#9A1900"> * hildon_helloworld-9.c</font></span>''</span>
+
/**
-
  <span>''<span><font color="#9A1900"> *</font></span>''</span>
+
  * hildon_helloworld-9.c
-
  <span>''<span><font color="#9A1900"> * This maemo code example is licensed under a MIT-style license,</font></span>''</span>
+
  *
-
  <span>''<span><font color="#9A1900"> * that can be found in the file called "License" in the same</font></span>''</span>
+
  * This maemo code example is licensed under a MIT-style license,
-
  <span>''<span><font color="#9A1900"> * directory as this file.</font></span>''</span>
+
  * that can be found in the file called "License" in the same
-
  <span>''<span><font color="#9A1900"> * Copyright (c) 2007-2008 Nokia Corporation. All rights reserved.</font></span>''</span>
+
  * directory as this file.
-
  <span>''<span><font color="#9A1900"> *</font></span>''</span>
+
  * Copyright (c) 2007-2008 Nokia Corporation. All rights reserved.
-
  <span>''<span><font color="#9A1900"> * We'll store the color that the user selects into a GConf</font></span>''</span>
+
  *
-
  <span>''<span><font color="#9A1900"> * preference. In fact, we'll have three settings, one for each</font></span>''</span>
+
  * We'll store the color that the user selects into a GConf
-
  <span>''<span><font color="#9A1900"> * channel of the color (red, green and blue).</font></span>''</span>
+
  * preference. In fact, we'll have three settings, one for each
-
  <span>''<span><font color="#9A1900"> *</font></span>''</span>
+
  * channel of the color (red, green and blue).
-
  <span>''<span><font color="#9A1900"> * Look for lines with "NEW" or "MODIFIED" in them.</font></span>''</span>
+
  *
-
  <span>''<span><font color="#9A1900"> */</font></span>''</span>
+
  * Look for lines with "NEW" or "MODIFIED" in them.
-
+
  */
-
<span>'''<span><font color="#000080"><nowiki>#include</nowiki></font></span>'''</span> <span><font color="#FF0000">&lt;stdlib.h&gt;</font></span>
+
 
-
<span>'''<span><font color="#000080"><nowiki>#include</nowiki></font></span>'''</span> <span><font color="#FF0000">&lt;hildon/hildon-program.h&gt;</font></span>
+
#include <stdlib.h>
-
<span>'''<span><font color="#000080"><nowiki>#include</nowiki></font></span>'''</span> <span><font color="#FF0000">&lt;hildon/hildon-color-button.h&gt;</font></span>
+
#include <hildon/hildon-program.h>
-
<span>'''<span><font color="#000080"><nowiki>#include</nowiki></font></span>'''</span> <span><font color="#FF0000">&lt;hildon/hildon-find-toolbar.h&gt;</font></span>
+
#include <hildon/hildon-color-button.h>
-
<span>'''<span><font color="#000080"><nowiki>#include</nowiki></font></span>'''</span> <span><font color="#FF0000">&lt;hildon/hildon-file-chooser-dialog.h&gt;</font></span>
+
#include <hildon/hildon-find-toolbar.h>
-
<span>'''<span><font color="#000080"><nowiki>#include</nowiki></font></span>'''</span> <span><font color="#FF0000">&lt;hildon/hildon-banner.h&gt;</font></span>
+
#include <hildon/hildon-file-chooser-dialog.h>
-
<span>'''<span><font color="#000080"><nowiki>#include</nowiki></font></span>'''</span> <span><font color="#FF0000">&lt;libgnomevfs/gnome-vfs.h&gt;</font></span>
+
#include <hildon/hildon-banner.h>
-
<span>''<span><font color="#9A1900">/* Include the prototypes for GConf client functions (NEW). */</font></span>''</span>
+
#include <libgnomevfs/gnome-vfs.h>
-
<span>'''<span><font color="#000080"><nowiki>#include</nowiki></font></span>'''</span> <span><font color="#FF0000">&lt;gconf/gconf-client.h&gt;</font></span>
+
/* Include the prototypes for GConf client functions (NEW). */
-
+
#include <gconf/gconf-client.h>
-
<span>''<span><font color="#9A1900">/* The application name -part of the GConf namespace (NEW). */</font></span>''</span>
+
 
-
<span>'''<span><font color="#000080"><nowiki>#define</nowiki></font></span>'''</span> APP_NAME <span><font color="#FF0000">"hildon_hello"</font></span>
+
/* The application name -part of the GConf namespace (NEW). */
-
<span>''<span><font color="#9A1900">/* This will be the root "directory" for our preferences (NEW). */</font></span>''</span>
+
#define APP_NAME "hildon_hello"
-
<span>'''<span><font color="#000080"><nowiki>#define</nowiki></font></span>'''</span> GC_ROOT  <span><font color="#FF0000">"/apps/Maemo/"</font></span> APP_NAME <span><font color="#FF0000">"/"</font></span>
+
/* This will be the root "directory" for our preferences (NEW). */
-
+
#define GC_ROOT  "/apps/Maemo/" APP_NAME "/"
-
  <span>''<span><font color="#9A1900">/*... Listing cut for brevity ...*/</font></span>''</span>
+
 
-
+
  /*... Listing cut for brevity ...*/
-
<span>''<span><font color="#9A1900">/**</font></span>''</span>
+
 
-
  <span>''<span><font color="#9A1900"> * NEW</font></span>''</span>
+
/**
-
  <span>''<span><font color="#9A1900"> *</font></span>''</span>
+
  * NEW
-
  <span>''<span><font color="#9A1900"> * Utility function to store the given color into our application</font></span>''</span>
+
  *
-
  <span>''<span><font color="#9A1900"> * preferences. We could use a list of integers as well, but we'll</font></span>''</span>
+
  * Utility function to store the given color into our application
-
  <span>''<span><font color="#9A1900"> * settle for three separate properties; one for each of RGB</font></span>''</span>
+
  * preferences. We could use a list of integers as well, but we'll
-
  <span>''<span><font color="#9A1900"> * channels.</font></span>''</span>
+
  * settle for three separate properties; one for each of RGB
-
  <span>''<span><font color="#9A1900"> *</font></span>''</span>
+
  * channels.
-
  <span>''<span><font color="#9A1900"> * The config keys that will be used are 'red', 'green' and 'blue'.</font></span>''</span>
+
  *
-
  <span>''<span><font color="#9A1900"> *</font></span>''</span>
+
  * The config keys that will be used are 'red', 'green' and 'blue'.
-
  <span>''<span><font color="#9A1900"> * NOTE:</font></span>''</span>
+
  *
-
  <span>''<span><font color="#9A1900"> *  We're doing things very non-optimally. If our application would</font></span>''</span>
+
  * NOTE:
-
  <span>''<span><font color="#9A1900"> *  have multiple preference settings, and we would like to know</font></span>''</span>
+
  *  We're doing things very non-optimally. If our application would
-
  <span>''<span><font color="#9A1900"> *  when someone will change them (external program, another</font></span>''</span>
+
  *  have multiple preference settings, and we would like to know
-
  <span>''<span><font color="#9A1900"> *  instance of our program, etc), we'd have to keep a reference to</font></span>''</span>
+
  *  when someone will change them (external program, another
-
  <span>''<span><font color="#9A1900"> *  the GConf client connection. Listening for changes in</font></span>''</span>
+
  *  instance of our program, etc), we'd have to keep a reference to
-
  <span>''<span><font color="#9A1900"> *  preferences would also require a callback registration, but this</font></span>''</span>
+
  *  the GConf client connection. Listening for changes in
-
  <span>''<span><font color="#9A1900"> *  is covered in the "maemo Platform Development" material.</font></span>''</span>
+
  *  preferences would also require a callback registration, but this
-
  <span>''<span><font color="#9A1900"> */</font></span>''</span>
+
  *  is covered in the "maemo Platform Development" material.
-
<span>'''<span><font color="#0000FF">static</font></span>'''</span> <span><font color="#009900">void</font></span> <span>'''<span><font color="#000000">confStoreColor</font></span>'''</span><span><font color="#990000">(</font></span><span>'''<span><font color="#0000FF">const</font></span>'''</span> GdkColor<span><font color="#990000"><nowiki>*</nowiki></font></span> color<span><font color="#990000">)</font></span> <span><font color="#FF0000">{</font></span>
+
  */
-
+
static void confStoreColor(const GdkColor* color) {
-
  <span>''<span><font color="#9A1900">/* We'll store the pointer to the GConf connection here. */</font></span>''</span>
+
 
-
  GConfClient<span><font color="#990000"><nowiki>*</nowiki></font></span> gcClient <span><font color="#990000"><nowiki>=</nowiki></font></span> NULL<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
  /* We'll store the pointer to the GConf connection here. */
-
+
  GConfClient* gcClient = NULL;
-
  <span>''<span><font color="#9A1900">/* Make sure that no NULLs are passed for the color. GdkColor is</font></span>''</span>
+
 
-
<span>''<span><font color="#9A1900">    not a proper GObject, so there is no GDK_IS_COLOR macro. */</font></span>''</span>
+
  /* Make sure that no NULLs are passed for the color. GdkColor is
-
  <span>'''<span><font color="#000000">g_assert</font></span>'''</span><span><font color="#990000">(</font></span>color<span><font color="#990000">);</font></span>
+
    not a proper GObject, so there is no GDK_IS_COLOR macro. */
-
+
   g_assert(color);
-
  <span>'''<span><font color="#000000">g_print</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#FF0000">"confStoreColor: invoked</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">);</font></span>
+
 
-
+
   g_print("confStoreColor: invoked\n");
-
  <span>''<span><font color="#9A1900">/* Open a connection to gconfd-2 (via D-Bus in maemo). The GConf</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">    API doesn't say whether this function can ever return NULL or</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">    how it will behave in error conditions. */</font></span>''</span>
+
-
  gcClient <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">gconf_client_get_default</font></span>'''</span><span><font color="#990000">();</font></span>
+
-
  <span>''<span><font color="#9A1900">/* We make sure that it's a valid GConf-client object. */</font></span>''</span>
+
-
  <span>'''<span><font color="#000000">g_assert</font></span>'''</span><span><font color="#990000">(</font></span><span>'''<span><font color="#000000">GCONF_IS_CLIENT</font></span>'''</span><span><font color="#990000">(</font></span>gcClient<span><font color="#990000">));</font></span>
+
-
+
-
  <span>''<span><font color="#9A1900">/* Store the values. */</font></span>''</span>
+
-
  <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">(!</font></span><span>'''<span><font color="#000000">gconf_client_set_int</font></span>'''</span><span><font color="#990000">(</font></span>gcClient<span><font color="#990000">,</font></span> GC_ROOT <span><font color="#FF0000">"red"</font></span><span><font color="#990000">,</font></span> color<span><font color="#990000">-&gt;</font></span>red<span><font color="#990000">,</font></span>
+
-
                            NULL<span><font color="#990000">))</font></span> <span><font color="#FF0000">{</font></span>
+
-
    <span>'''<span><font color="#000000">g_warning</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#FF0000">" failed to set %s/red to %d</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">,</font></span> GC_ROOT<span><font color="#990000">,</font></span> color<span><font color="#990000">-&gt;</font></span>red<span><font color="#990000">);</font></span>
+
-
  <span><font color="#FF0000">}</font></span>
+
-
  <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">(!</font></span><span>'''<span><font color="#000000">gconf_client_set_int</font></span>'''</span><span><font color="#990000">(</font></span>gcClient<span><font color="#990000">,</font></span> GC_ROOT <span><font color="#FF0000">"green"</font></span><span><font color="#990000">,</font></span> color<span><font color="#990000">-&gt;</font></span>green<span><font color="#990000">,</font></span>
+
-
                            NULL<span><font color="#990000">))</font></span> <span><font color="#FF0000">{</font></span>
+
-
    <span>'''<span><font color="#000000">g_warning</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#FF0000">" failed to set %s/green to %d</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">,</font></span> GC_ROOT<span><font color="#990000">,</font></span>
+
-
              color<span><font color="#990000">-&gt;</font></span>green<span><font color="#990000">);</font></span>
+
-
  <span><font color="#FF0000">}</font></span>
+
-
  <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">(!</font></span><span>'''<span><font color="#000000">gconf_client_set_int</font></span>'''</span><span><font color="#990000">(</font></span>gcClient<span><font color="#990000">,</font></span> GC_ROOT <span><font color="#FF0000">"blue"</font></span><span><font color="#990000">,</font></span> color<span><font color="#990000">-&gt;</font></span>blue<span><font color="#990000">,</font></span>
+
-
                            NULL<span><font color="#990000">))</font></span> <span><font color="#FF0000">{</font></span>
+
-
    <span>'''<span><font color="#000000">g_warning</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#FF0000">" failed to set %s/blue to %d</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">,</font></span> GC_ROOT<span><font color="#990000">,</font></span>
+
-
              color<span><font color="#990000">-&gt;</font></span>blue<span><font color="#990000">);</font></span>
+
-
  <span><font color="#FF0000">}</font></span>
+
-
+
-
  <span>''<span><font color="#9A1900">/* Release the GConf client object (with GObject-unref). */</font></span>''</span>
+
-
  <span>'''<span><font color="#000000">g_object_unref</font></span>'''</span><span><font color="#990000">(</font></span>gcClient<span><font color="#990000">);</font></span>
+
-
  gcClient <span><font color="#990000"><nowiki>=</nowiki></font></span> NULL<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
<span><font color="#FF0000">}</font></span>
+
-
+
-
<span>''<span><font color="#9A1900">/**</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> * NEW</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> *</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> * A utility function to get an integer but also return the status</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> * whether the requested key existed or not.</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> *</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> * NOTE:</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> *   It's also possible to use gconf_client_get_int(), but it's not</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> *  possible to then know whether they key existed or not, because</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> *  the function will return 0 if the key doesn't exist (and if the</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> *  value is 0, how could you tell these two conditions apart?).</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> *</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> * Parameters:</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> * - GConfClient: the client object to use</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> * - const gchar*: the key</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> * - gint*: the address to store the integer to if the key exists</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> *</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> * Returns:</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> * - TRUE: if integer has been updated with a value from GConf.</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> *  FALSE: there was no such key or it wasn't an integer.</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> */</font></span>''</span>
+
-
<span>'''<span><font color="#0000FF">static</font></span>'''</span> gboolean <span>'''<span><font color="#000000">confGetInt</font></span>'''</span><span><font color="#990000">(</font></span>GConfClient<span><font color="#990000"><nowiki>*</nowiki></font></span> gcClient<span><font color="#990000">,</font></span> <span>'''<span><font color="#0000FF">const</font></span>'''</span> gchar<span><font color="#990000"><nowiki>*</nowiki></font></span> key<span><font color="#990000">,</font></span>
+
-
                            gint<span><font color="#990000"><nowiki>*</nowiki></font></span> number<span><font color="#990000">)</font></span> <span><font color="#FF0000">{</font></span>
+
-
+
-
  <span>''<span><font color="#9A1900">/* This will hold the type/value pair at some point. */</font></span>''</span>
+
-
  GConfValue<span><font color="#990000"><nowiki>*</nowiki></font></span> val <span><font color="#990000"><nowiki>=</nowiki></font></span> NULL<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
  <span>''<span><font color="#9A1900">/* Return flag (tells the caller whether this function wrote behind</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">    the 'number' pointer or not). */</font></span>''</span>
+
-
  gboolean hasChanged <span><font color="#990000"><nowiki>=</nowiki></font></span> FALSE<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
+
-
  <span>''<span><font color="#9A1900">/* Try to get the type/value from the GConf DB.</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">    NOTE:</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">      We're using a version of the getter that will not return any</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">      defaults (if a schema would specify one). Instead, it will</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">      return the value if one has been set (or NULL).</font></span>''</span>
+
-
+
-
<span>''<span><font color="#9A1900">    We're not really interested in errors as this will return a NULL</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">    in case of missing keys or errors and that is quite enough for</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">    us. */</font></span>''</span>
+
-
  val <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">gconf_client_get_without_default</font></span>'''</span><span><font color="#990000">(</font></span>gcClient<span><font color="#990000">,</font></span> key<span><font color="#990000">,</font></span> NULL<span><font color="#990000">);</font></span>
+
-
  <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">(</font></span>val <span><font color="#990000"><nowiki>==</nowiki></font></span> NULL<span><font color="#990000">)</font></span> <span><font color="#FF0000">{</font></span>
+
-
    <span>''<span><font color="#9A1900">/* Key wasn't found, no need to touch anything. */</font></span>''</span>
+
-
    <span>'''<span><font color="#000000">g_warning</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#FF0000">"confGetInt: key %s not found</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">,</font></span> key<span><font color="#990000">);</font></span>
+
-
    <span>'''<span><font color="#0000FF">return</font></span>'''</span> FALSE<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
  <span><font color="#FF0000">}</font></span>
+
-
+
-
  <span>''<span><font color="#9A1900">/* Check whether the value stored behind the key is an integer. If</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">    it is not, we issue a warning, but return normally. */</font></span>''</span>
+
-
  <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">(</font></span>val<span><font color="#990000">-&gt;</font></span>type <span><font color="#990000"><nowiki>==</nowiki></font></span> GCONF_VALUE_INT<span><font color="#990000">)</font></span> <span><font color="#FF0000">{</font></span>
+
-
    <span>''<span><font color="#9A1900">/* It's an integer, get it and store. */</font></span>''</span>
+
-
    <span><font color="#990000"><nowiki>*</nowiki></font></span>number <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">gconf_value_get_int</font></span>'''</span><span><font color="#990000">(</font></span>val<span><font color="#990000">);</font></span>
+
-
    <span>''<span><font color="#9A1900">/* Mark that we've changed the integer behind 'number'. */</font></span>''</span>
+
-
    hasChanged <span><font color="#990000"><nowiki>=</nowiki></font></span> TRUE<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
  <span><font color="#FF0000">}</font></span> <span>'''<span><font color="#0000FF">else</font></span>'''</span> <span><font color="#FF0000">{</font></span>
+
-
    <span>'''<span><font color="#000000">g_warning</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#FF0000">"confGetInt: key %s is not an integer</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">,</font></span> key<span><font color="#990000">);</font></span>
+
-
  <span><font color="#FF0000">}</font></span>
+
-
+
-
  <span>''<span><font color="#9A1900">/* Free the type/value-pair. */</font></span>''</span>
+
-
  <span>'''<span><font color="#000000">gconf_value_free</font></span>'''</span><span><font color="#990000">(</font></span>val<span><font color="#990000">);</font></span>
+
-
  val <span><font color="#990000"><nowiki>=</nowiki></font></span> NULL<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
+
-
  <span>'''<span><font color="#0000FF">return</font></span>'''</span> hasChanged<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
<span><font color="#FF0000">}</font></span>
+
-
+
-
<span>''<span><font color="#9A1900">/**</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> * NEW</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> *</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> * Utility function to change the given color into the one that is</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> * specified in application preferences.</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> *</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> * If some key is missing, that channel is left untouched. The</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> * function also checks for proper values for the channels so that</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> * invalid values are not accepted (guint16 range of GdkColor).</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> *</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> * Parameters:</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> * - GdkColor*: the color structure to modify if changed from prefs.</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> *</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> * Returns:</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> * - TRUE if the color was been changed by this routine.</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> *  FALSE if the color wasn't changed (there was an error or the</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> *  color was already exactly the same as in the preferences).</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> */</font></span>''</span>
+
-
<span>'''<span><font color="#0000FF">static</font></span>'''</span> gboolean <span>'''<span><font color="#000000">confLoadCurrentColor</font></span>'''</span><span><font color="#990000">(</font></span>GdkColor<span><font color="#990000"><nowiki>*</nowiki></font></span> color<span><font color="#990000">)</font></span> <span><font color="#FF0000">{</font></span>
+
-
+
-
  GConfClient<span><font color="#990000"><nowiki>*</nowiki></font></span> gcClient <span><font color="#990000"><nowiki>=</nowiki></font></span> NULL<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
  <span>''<span><font color="#9A1900">/* Temporary holders for the pref values. */</font></span>''</span>
+
-
  gint red <span><font color="#990000"><nowiki>=</nowiki></font></span> <span><font color="#990000">-</font></span><span><font color="#993399">1</font></span><span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
  gint green <span><font color="#990000"><nowiki>=</nowiki></font></span> <span><font color="#990000">-</font></span><span><font color="#993399">1</font></span><span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
  gint blue <span><font color="#990000"><nowiki>=</nowiki></font></span> <span><font color="#990000">-</font></span><span><font color="#993399">1</font></span><span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
  <span>''<span><font color="#9A1900">/* Temp variable to hold whether the color has changed. */</font></span>''</span>
+
-
  gboolean hasChanged <span><font color="#990000"><nowiki>=</nowiki></font></span> FALSE<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
+
-
  <span>'''<span><font color="#000000">g_assert</font></span>'''</span><span><font color="#990000">(</font></span>color<span><font color="#990000">);</font></span>
+
-
+
-
  <span>'''<span><font color="#000000">g_print</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#FF0000">"confLoadCurrentColor: invoked</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">);</font></span>
+
-
+
-
  <span>''<span><font color="#9A1900">/* Open a connection to gconfd-2 (via d-bus). */</font></span>''</span>
+
-
  gcClient <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">gconf_client_get_default</font></span>'''</span><span><font color="#990000">();</font></span>
+
-
  <span>''<span><font color="#9A1900">/* Make sure that it's a valid GConf-client object. */</font></span>''</span>
+
-
  <span>'''<span><font color="#000000">g_assert</font></span>'''</span><span><font color="#990000">(</font></span><span>'''<span><font color="#000000">GCONF_IS_CLIENT</font></span>'''</span><span><font color="#990000">(</font></span>gcClient<span><font color="#990000">));</font></span>
+
-
+
-
  <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">(</font></span><span>'''<span><font color="#000000">confGetInt</font></span>'''</span><span><font color="#990000">(</font></span>gcClient<span><font color="#990000">,</font></span> GC_ROOT <span><font color="#FF0000">"red"</font></span><span><font color="#990000">,</font></span> <span><font color="#990000">&amp;</font></span>red<span><font color="#990000">))</font></span> <span><font color="#FF0000">{</font></span>
+
-
    <span>''<span><font color="#9A1900">/* We got the value successfully, now clamp it. */</font></span>''</span>
+
-
    <span>'''<span><font color="#000000">g_print</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#FF0000">" got red = %d, "</font></span><span><font color="#990000">,</font></span> red<span><font color="#990000">);</font></span>
+
-
    <span>''<span><font color="#9A1900">/* We got a value, so let's limit it between 0 and 65535 (the</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">      legal range for guint16). We use the CLAMP macro from GLib for</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">      this. */</font></span>''</span>
+
-
    red <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">CLAMP</font></span>'''</span><span><font color="#990000">(</font></span>red<span><font color="#990000">,</font></span> <span><font color="#993399">0</font></span><span><font color="#990000">,</font></span> G_MAXUINT16<span><font color="#990000">);</font></span>
+
-
    <span>'''<span><font color="#000000">g_print</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#FF0000">"after clamping = %d</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">,</font></span> red<span><font color="#990000">);</font></span>
+
-
    <span>''<span><font color="#9A1900">/* Update &amp; mark that at least this component changed. */</font></span>''</span>
+
-
    color<span><font color="#990000">-&gt;</font></span>red <span><font color="#990000"><nowiki>=</nowiki></font></span> <span><font color="#990000">(</font></span>guint16<span><font color="#990000">)</font></span>red<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
    hasChanged <span><font color="#990000"><nowiki>=</nowiki></font></span> TRUE<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
  <span><font color="#FF0000">}</font></span>
+
-
  <span>''<span><font color="#9A1900">/* Repeat the same logic for the green component. */</font></span>''</span>
+
-
  <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">(</font></span><span>'''<span><font color="#000000">confGetInt</font></span>'''</span><span><font color="#990000">(</font></span>gcClient<span><font color="#990000">,</font></span> GC_ROOT <span><font color="#FF0000">"green"</font></span><span><font color="#990000">,</font></span> <span><font color="#990000">&amp;</font></span>green<span><font color="#990000">))</font></span> <span><font color="#FF0000">{</font></span>
+
-
    <span>'''<span><font color="#000000">g_print</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#FF0000">" got green = %d, "</font></span><span><font color="#990000">,</font></span> green<span><font color="#990000">);</font></span>
+
-
    green <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">CLAMP</font></span>'''</span><span><font color="#990000">(</font></span>green<span><font color="#990000">,</font></span> <span><font color="#993399">0</font></span><span><font color="#990000">,</font></span> G_MAXUINT16<span><font color="#990000">);</font></span>
+
-
    <span>'''<span><font color="#000000">g_print</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#FF0000">"after clamping = %d</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">,</font></span> green<span><font color="#990000">);</font></span>
+
-
    color<span><font color="#990000">-&gt;</font></span>green <span><font color="#990000"><nowiki>=</nowiki></font></span> <span><font color="#990000">(</font></span>guint16<span><font color="#990000">)</font></span>green<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
    hasChanged <span><font color="#990000"><nowiki>=</nowiki></font></span> TRUE<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
  <span><font color="#FF0000">}</font></span>
+
-
  <span>''<span><font color="#9A1900">/* Repeat the same logic for the last component (blue). */</font></span>''</span>
+
-
  <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">(</font></span><span>'''<span><font color="#000000">confGetInt</font></span>'''</span><span><font color="#990000">(</font></span>gcClient<span><font color="#990000">,</font></span> GC_ROOT <span><font color="#FF0000">"blue"</font></span><span><font color="#990000">,</font></span> <span><font color="#990000">&amp;</font></span>blue<span><font color="#990000">))</font></span> <span><font color="#FF0000">{</font></span>
+
-
    <span>'''<span><font color="#000000">g_print</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#FF0000">" got blue = %d, "</font></span><span><font color="#990000">,</font></span> blue<span><font color="#990000">);</font></span>
+
-
    blue <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">CLAMP</font></span>'''</span><span><font color="#990000">(</font></span>blue<span><font color="#990000">,</font></span> <span><font color="#993399">0</font></span><span><font color="#990000">,</font></span> G_MAXUINT16<span><font color="#990000">);</font></span>
+
-
    <span>'''<span><font color="#000000">g_print</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#FF0000">"after clamping = %d</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">,</font></span> blue<span><font color="#990000">);</font></span>
+
-
    color<span><font color="#990000">-&gt;</font></span>blue <span><font color="#990000"><nowiki>=</nowiki></font></span> <span><font color="#990000">(</font></span>guint16<span><font color="#990000">)</font></span>blue<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
    hasChanged <span><font color="#990000"><nowiki>=</nowiki></font></span> TRUE<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
  <span><font color="#FF0000">}</font></span>
+
-
+
-
  <span>''<span><font color="#9A1900">/* Release the client object (with GObject-unref). */</font></span>''</span>
+
-
  <span>'''<span><font color="#000000">g_object_unref</font></span>'''</span><span><font color="#990000">(</font></span>gcClient<span><font color="#990000">);</font></span>
+
-
  gcClient <span><font color="#990000"><nowiki>=</nowiki></font></span> NULL<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
+
-
  <span>''<span><font color="#9A1900">/* Return status if the color was been changed by this routine. */</font></span>''</span>
+
-
  <span>'''<span><font color="#0000FF">return</font></span>'''</span> hasChanged<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
<span><font color="#FF0000">}</font></span>
+
-
+
-
<span>''<span><font color="#9A1900">/**</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> * MODIFIED</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> *</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> * Invoked when the user selects a color (or will cancel the dialog).</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> *</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> * Will also write the color to preferences (GConf) each time the</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> * color changes. We'll compare whether it has really changed (to</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> * avoid writing to GConf is nothing really changed).</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> */</font></span>''</span>
+
-
<span>'''<span><font color="#0000FF">static</font></span>'''</span> <span><font color="#009900">void</font></span> <span>'''<span><font color="#000000">cbActionColorChanged</font></span>'''</span><span><font color="#990000">(</font></span>HildonColorButton<span><font color="#990000"><nowiki>*</nowiki></font></span> colorButton<span><font color="#990000">,</font></span>
+
-
                                  ApplicationState<span><font color="#990000"><nowiki>*</nowiki></font></span> app<span><font color="#990000">)</font></span> <span><font color="#FF0000">{</font></span>
+
-
+
-
  <span>''<span><font color="#9A1900">/* Local variables that we'll need to handle the change (NEW). */</font></span>''</span>
+
-
  gboolean hasChanged <span><font color="#990000"><nowiki>=</nowiki></font></span> FALSE<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
  GdkColor newColor <span><font color="#990000"><nowiki>=</nowiki></font></span> <span><font color="#FF0000">{}</font></span><span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
  GdkColor<span><font color="#990000"><nowiki>*</nowiki></font></span> curColor <span><font color="#990000"><nowiki>=</nowiki></font></span> NULL<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
+
-
  <span>'''<span><font color="#000000">g_assert</font></span>'''</span><span><font color="#990000">(</font></span>app <span><font color="#990000"><nowiki>!=</nowiki></font></span> NULL<span><font color="#990000">);</font></span>
+
-
+
-
  <span>'''<span><font color="#000000">g_print</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#FF0000">"cbActionColorChanged invoked</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">);</font></span>
+
-
  <span>''<span><font color="#9A1900">/* Retrieve the new color from the color button (NEW). */</font></span>''</span>
+
-
  <span>'''<span><font color="#000000">hildon_color_button_get_color</font></span>'''</span><span><font color="#990000">(</font></span>colorButton<span><font color="#990000">,</font></span> <span><font color="#990000">&amp;</font></span>newColor<span><font color="#990000">);</font></span>
+
-
  <span>''<span><font color="#9A1900">/* Just an alias to save some typing (could also use</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">    app-&gt;currentColor) (NEW). */</font></span>''</span>
+
-
  curColor <span><font color="#990000"><nowiki>=</nowiki></font></span> <span><font color="#990000">&amp;</font></span>app<span><font color="#990000">-&gt;</font></span>currentColor<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
+
-
  <span>''<span><font color="#9A1900">/* Check whether the color really changed (NEW). */</font></span>''</span>
+
-
  <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">((</font></span>newColor<span><font color="#990000">.</font></span>red   <span><font color="#990000"><nowiki>!=</nowiki></font></span> curColor<span><font color="#990000">-&gt;</font></span>red<span><font color="#990000">)</font></span> <span><font color="#990000"><nowiki>||</nowiki></font></span>
+
-
      <span><font color="#990000">(</font></span>newColor<span><font color="#990000">.</font></span>green <span><font color="#990000"><nowiki>!=</nowiki></font></span> curColor<span><font color="#990000">-&gt;</font></span>green<span><font color="#990000">)</font></span> <span><font color="#990000"><nowiki>||</nowiki></font></span>
+
-
      <span><font color="#990000">(</font></span>newColor<span><font color="#990000">.</font></span>blue  <span><font color="#990000"><nowiki>!=</nowiki></font></span> curColor<span><font color="#990000">-&gt;</font></span>blue<span><font color="#990000">))</font></span> <span><font color="#FF0000">{</font></span>
+
-
    hasChanged <span><font color="#990000"><nowiki>=</nowiki></font></span> TRUE<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
  <span><font color="#FF0000">}</font></span>
+
-
  <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">(!</font></span>hasChanged<span><font color="#990000">)</font></span> <span><font color="#FF0000">{</font></span>
+
-
    <span>'''<span><font color="#000000">g_print</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#FF0000">" color not really changed</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">);</font></span>
+
-
    <span>'''<span><font color="#0000FF">return</font></span>'''</span><span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
  <span><font color="#FF0000">}</font></span>
+
-
  <span>''<span><font color="#9A1900">/* Color really changed, store to preferences (NEW). */</font></span>''</span>
+
-
  <span>'''<span><font color="#000000">g_print</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#FF0000">" color changed, storing into preferences.. </font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">);</font></span>
+
-
  <span>'''<span><font color="#000000">confStoreColor</font></span>'''</span><span><font color="#990000">(&amp;</font></span>newColor<span><font color="#990000">);</font></span>
+
-
  <span>'''<span><font color="#000000">g_print</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#FF0000">" done.</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">);</font></span>
+
-
+
-
  <span>''<span><font color="#9A1900">/* Update the changed color into the application state. */</font></span>''</span>
+
-
  app<span><font color="#990000">-&gt;</font></span>currentColor <span><font color="#990000"><nowiki>=</nowiki></font></span> newColor<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
<span><font color="#FF0000">}</font></span>
+
-
+
-
  <span>''<span><font color="#9A1900">/*... Listing cut for brevity ...*/</font></span>''</span>
+
-
+
-
<span>''<span><font color="#9A1900">/**</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> * MODIFIED</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> *</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> * The color of the color button will be loaded from the application</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> * preferences (or keep the default if preferences have no setting).</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> */</font></span>''</span>
+
-
<span>'''<span><font color="#0000FF">static</font></span>'''</span> GtkWidget<span><font color="#990000"><nowiki>*</nowiki></font></span> <span>'''<span><font color="#000000">buildToolbar</font></span>'''</span><span><font color="#990000">(</font></span>ApplicationState<span><font color="#990000"><nowiki>*</nowiki></font></span> app<span><font color="#990000">)</font></span> <span><font color="#FF0000">{</font></span>
+
-
+
-
  GtkToolbar<span><font color="#990000"><nowiki>*</nowiki></font></span>  toolbar <span><font color="#990000"><nowiki>=</nowiki></font></span> NULL<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
  GtkToolItem<span><font color="#990000"><nowiki>*</nowiki></font></span> tbOpen <span><font color="#990000"><nowiki>=</nowiki></font></span> NULL<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
  GtkToolItem<span><font color="#990000"><nowiki>*</nowiki></font></span> tbSave <span><font color="#990000"><nowiki>=</nowiki></font></span> NULL<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
  GtkToolItem<span><font color="#990000"><nowiki>*</nowiki></font></span> tbSep <span><font color="#990000"><nowiki>=</nowiki></font></span> NULL<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
  GtkToolItem<span><font color="#990000"><nowiki>*</nowiki></font></span> tbFind <span><font color="#990000"><nowiki>=</nowiki></font></span> NULL<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
  GtkToolItem<span><font color="#990000"><nowiki>*</nowiki></font></span> tbColorButton <span><font color="#990000"><nowiki>=</nowiki></font></span> NULL<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
  GtkWidget<span><font color="#990000"><nowiki>*</nowiki></font></span>  colorButton <span><font color="#990000"><nowiki>=</nowiki></font></span> NULL<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
+
-
  <span>'''<span><font color="#000000">g_assert</font></span>'''</span><span><font color="#990000">(</font></span>app <span><font color="#990000"><nowiki>!=</nowiki></font></span> NULL<span><font color="#990000">);</font></span>
+
-
+
-
  tbOpen <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">gtk_tool_button_new_from_stock</font></span>'''</span><span><font color="#990000">(</font></span>GTK_STOCK_OPEN<span><font color="#990000">);</font></span>
+
-
  tbSave <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">gtk_tool_button_new_from_stock</font></span>'''</span><span><font color="#990000">(</font></span>GTK_STOCK_SAVE<span><font color="#990000">);</font></span>
+
-
  tbSep  <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">gtk_separator_tool_item_new</font></span>'''</span><span><font color="#990000">();</font></span>
+
-
  tbFind <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">gtk_tool_button_new_from_stock</font></span>'''</span><span><font color="#990000">(</font></span>GTK_STOCK_FIND<span><font color="#990000">);</font></span>
+
-
+
-
  tbColorButton <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">gtk_tool_item_new</font></span>'''</span><span><font color="#990000">();</font></span>
+
-
  colorButton <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">hildon_color_button_new</font></span>'''</span><span><font color="#990000">();</font></span>
+
-
  <span>''<span><font color="#9A1900">/* Copy the color from the color button into the application state.</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">    This is done to detect whether the color in preferences matches</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">    the default color or not (NEW). */</font></span>''</span>
+
-
  <span>'''<span><font color="#000000">hildon_color_button_get_color</font></span>'''</span><span><font color="#990000">(</font></span><span>'''<span><font color="#000000">HILDON_COLOR_BUTTON</font></span>'''</span><span><font color="#990000">(</font></span>colorButton<span><font color="#990000">),</font></span>
+
-
                                <span><font color="#990000">&amp;</font></span>app<span><font color="#990000">-&gt;</font></span>currentColor<span><font color="#990000">);</font></span>
+
-
  <span>''<span><font color="#9A1900">/* Load preferences and change the color if necessary. */</font></span>''</span>
+
-
  <span>'''<span><font color="#000000">g_print</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#FF0000">"buildToolbar: loading color pref.</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">);</font></span>
+
-
  <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">(</font></span><span>'''<span><font color="#000000">confLoadCurrentColor</font></span>'''</span><span><font color="#990000">(&amp;</font></span>app<span><font color="#990000">-&gt;</font></span>currentColor<span><font color="#990000">))</font></span> <span><font color="#FF0000">{</font></span>
+
-
    <span>'''<span><font color="#000000">g_print</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#FF0000">" color not same as default one</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">);</font></span>
+
-
    <span>'''<span><font color="#000000">hildon_color_button_set_color</font></span>'''</span><span><font color="#990000">(</font></span><span>'''<span><font color="#000000">HILDON_COLOR_BUTTON</font></span>'''</span><span><font color="#990000">(</font></span>colorButton<span><font color="#990000">),</font></span>
+
-
                                  <span><font color="#990000">&amp;</font></span>app<span><font color="#990000">-&gt;</font></span>currentColor<span><font color="#990000">);</font></span>
+
-
  <span><font color="#FF0000">}</font></span> <span>'''<span><font color="#0000FF">else</font></span>'''</span> <span><font color="#FF0000">{</font></span>
+
-
    <span>'''<span><font color="#000000">g_print</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#FF0000">" loaded color same as default</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">);</font></span>
+
-
  <span><font color="#FF0000">}</font></span>
+
-
  <span>'''<span><font color="#000000">gtk_container_add</font></span>'''</span><span><font color="#990000">(</font></span><span>'''<span><font color="#000000">GTK_CONTAINER</font></span>'''</span><span><font color="#990000">(</font></span>tbColorButton<span><font color="#990000">),</font></span> colorButton<span><font color="#990000">);</font></span>
+
-
+
-
  <span>''<span><font color="#9A1900">/*... Listing cut for brevity ...*/</font></span>''</span>
+
-
+
-
<span><font color="#FF0000">}</font></span></tt>
+
-
Since the graphical appearance of the program does not change (except that the ColorButton will display the correct initial color), a look will be taken at the stdout display of the program.
+
  /* Open a connection to gconfd-2 (via D-Bus in maemo). The GConf
 +
    API doesn't say whether this function can ever return NULL or
 +
    how it will behave in error conditions. */
 +
  gcClient = gconf_client_get_default();
 +
  /* We make sure that it's a valid GConf-client object. */
 +
  g_assert(GCONF_IS_CLIENT(gcClient));
-
[sbox-DIABLO_X86: ~/appdev] &gt; run-standalone.sh ./hildon_helloworld-9
+
  /* Store the values. */
-
buildToolbar: loading color pref.
+
  if (!gconf_client_set_int(gcClient, GC_ROOT "red", color->red,
-
confLoadCurrentColor: invoked
+
                            NULL)) {
-
hildon_helloworld-9[19840]: GLIB WARNING ** default -  
+
    g_warning(" failed to set %s/red to %d\n", GC_ROOT, color->red);
-
   confGetInt: key /apps/Maemo/hildon_hello/red not found
+
   }
-
hildon_helloworld-9[19840]: GLIB WARNING ** default -  
+
  if (!gconf_client_set_int(gcClient, GC_ROOT "green", color->green,
-
  confGetInt: key /apps/Maemo/hildon_hello/green not found
+
                            NULL)) {
-
hildon_helloworld-9[19840]: GLIB WARNING ** default -  
+
    g_warning(" failed to set %s/green to %d\n", GC_ROOT,
-
   confGetInt: key /apps/Maemo/hildon_hello/blue not found
+
              color->green);
-
   loaded color same as default
+
   }
-
main: calling gtk_main
+
   if (!gconf_client_set_int(gcClient, GC_ROOT "blue", color->blue,
-
cbActionMainToolbarToggle invoked
+
                            NULL)) {
-
cbActionColorChanged invoked
+
    g_warning(" failed to set %s/blue to %d\n", GC_ROOT,
-
  color changed, storing into preferences..
+
              color->blue);
-
confStoreColor: invoked
+
   }
-
   done.
+
-
main: returned from gtk_main and exiting with success
+
 +
  /* Release the GConf client object (with GObject-unref). */
 +
  g_object_unref(gcClient);
 +
  gcClient = NULL;
 +
}
 +
 +
/**
 +
* NEW
 +
*
 +
* A utility function to get an integer but also return the status
 +
* whether the requested key existed or not.
 +
*
 +
* NOTE:
 +
*  It's also possible to use gconf_client_get_int(), but it's not
 +
*  possible to then know whether they key existed or not, because
 +
*  the function will return 0 if the key doesn't exist (and if the
 +
*  value is 0, how could you tell these two conditions apart?).
 +
*
 +
* Parameters:
 +
* - GConfClient: the client object to use
 +
* - const gchar*: the key
 +
* - gint*: the address to store the integer to if the key exists
 +
*
 +
* Returns:
 +
* - TRUE: if integer has been updated with a value from GConf.
 +
*  FALSE: there was no such key or it wasn't an integer.
 +
*/
 +
static gboolean confGetInt(GConfClient* gcClient, const gchar* key,
 +
                          gint* number) {
 +
 +
  /* This will hold the type/value pair at some point. */
 +
  GConfValue* val = NULL;
 +
  /* Return flag (tells the caller whether this function wrote behind
 +
    the 'number' pointer or not). */
 +
  gboolean hasChanged = FALSE;
 +
 +
  /* Try to get the type/value from the GConf DB.
 +
    NOTE:
 +
      We're using a version of the getter that will not return any
 +
      defaults (if a schema would specify one). Instead, it will
 +
      return the value if one has been set (or NULL).
 +
 +
    We're not really interested in errors as this will return a NULL
 +
    in case of missing keys or errors and that is quite enough for
 +
    us. */
 +
  val = gconf_client_get_without_default(gcClient, key, NULL);
 +
  if (val == NULL) {
 +
    /* Key wasn't found, no need to touch anything. */
 +
    g_warning("confGetInt: key %s not found\n", key);
 +
    return FALSE;
 +
  }
 +
 +
  /* Check whether the value stored behind the key is an integer. If
 +
    it is not, we issue a warning, but return normally. */
 +
  if (val->type == GCONF_VALUE_INT) {
 +
    /* It's an integer, get it and store. */
 +
    *number = gconf_value_get_int(val);
 +
    /* Mark that we've changed the integer behind 'number'. */
 +
    hasChanged = TRUE;
 +
  } else {
 +
    g_warning("confGetInt: key %s is not an integer\n", key);
 +
  }
 +
 +
  /* Free the type/value-pair. */
 +
  gconf_value_free(val);
 +
  val = NULL;
 +
 +
  return hasChanged;
 +
}
 +
 +
/**
 +
* NEW
 +
*
 +
* Utility function to change the given color into the one that is
 +
* specified in application preferences.
 +
*
 +
* If some key is missing, that channel is left untouched. The
 +
* function also checks for proper values for the channels so that
 +
* invalid values are not accepted (guint16 range of GdkColor).
 +
*
 +
* Parameters:
 +
* - GdkColor*: the color structure to modify if changed from prefs.
 +
*
 +
* Returns:
 +
* - TRUE if the color was been changed by this routine.
 +
*  FALSE if the color wasn't changed (there was an error or the
 +
*  color was already exactly the same as in the preferences).
 +
*/
 +
static gboolean confLoadCurrentColor(GdkColor* color) {
 +
 +
  GConfClient* gcClient = NULL;
 +
  /* Temporary holders for the pref values. */
 +
  gint red = -1;
 +
  gint green = -1;
 +
  gint blue = -1;
 +
  /* Temp variable to hold whether the color has changed. */
 +
  gboolean hasChanged = FALSE;
 +
 +
  g_assert(color);
 +
 +
  g_print("confLoadCurrentColor: invoked\n");
 +
 +
  /* Open a connection to gconfd-2 (via d-bus). */
 +
  gcClient = gconf_client_get_default();
 +
  /* Make sure that it's a valid GConf-client object. */
 +
  g_assert(GCONF_IS_CLIENT(gcClient));
 +
 +
  if (confGetInt(gcClient, GC_ROOT "red", &red)) {
 +
    /* We got the value successfully, now clamp it. */
 +
    g_print(" got red = %d, ", red);
 +
    /* We got a value, so let's limit it between 0 and 65535 (the
 +
      legal range for guint16). We use the CLAMP macro from GLib for
 +
      this. */
 +
    red = CLAMP(red, 0, G_MAXUINT16);
 +
    g_print("after clamping = %d\n", red);
 +
    /* Update & mark that at least this component changed. */
 +
    color->red = (guint16)red;
 +
    hasChanged = TRUE;
 +
  }
 +
  /* Repeat the same logic for the green component. */
 +
  if (confGetInt(gcClient, GC_ROOT "green", &green)) {
 +
    g_print(" got green = %d, ", green);
 +
    green = CLAMP(green, 0, G_MAXUINT16);
 +
    g_print("after clamping = %d\n", green);
 +
    color->green = (guint16)green;
 +
    hasChanged = TRUE;
 +
  }
 +
  /* Repeat the same logic for the last component (blue). */
 +
  if (confGetInt(gcClient, GC_ROOT "blue", &blue)) {
 +
    g_print(" got blue = %d, ", blue);
 +
    blue = CLAMP(blue, 0, G_MAXUINT16);
 +
    g_print("after clamping = %d\n", blue);
 +
    color->blue = (guint16)blue;
 +
    hasChanged = TRUE;
 +
  }
 +
 +
  /* Release the client object (with GObject-unref). */
 +
  g_object_unref(gcClient);
 +
  gcClient = NULL;
 +
 +
  /* Return status if the color was been changed by this routine. */
 +
  return hasChanged;
 +
}
 +
 +
/**
 +
* MODIFIED
 +
*
 +
* Invoked when the user selects a color (or will cancel the dialog).
 +
*
 +
* Will also write the color to preferences (GConf) each time the
 +
* color changes. We'll compare whether it has really changed (to
 +
* avoid writing to GConf is nothing really changed).
 +
*/
 +
static void cbActionColorChanged(HildonColorButton* colorButton,
 +
                                ApplicationState* app) {
 +
 +
  /* Local variables that we'll need to handle the change (NEW). */
 +
  gboolean hasChanged = FALSE;
 +
  GdkColor newColor = {};
 +
  GdkColor* curColor = NULL;
 +
 +
  g_assert(app != NULL);
 +
 +
  g_print("cbActionColorChanged invoked\n");
 +
  /* Retrieve the new color from the color button (NEW). */
 +
  hildon_color_button_get_color(colorButton, &newColor);
 +
  /* Just an alias to save some typing (could also use
 +
    app->currentColor) (NEW). */
 +
  curColor = &app->currentColor;
 +
 +
  /* Check whether the color really changed (NEW). */
 +
  if ((newColor.red  != curColor->red) ||
 +
      (newColor.green != curColor->green) ||
 +
      (newColor.blue  != curColor->blue)) {
 +
    hasChanged = TRUE;
 +
  }
 +
  if (!hasChanged) {
 +
    g_print(" color not really changed\n");
 +
    return;
 +
  }
 +
  /* Color really changed, store to preferences (NEW). */
 +
  g_print(" color changed, storing into preferences.. \n");
 +
  confStoreColor(&newColor);
 +
  g_print(" done.\n");
 +
 +
  /* Update the changed color into the application state. */
 +
  app->currentColor = newColor;
 +
}
 +
 +
  /*... Listing cut for brevity ...*/
 +
 +
/**
 +
* MODIFIED
 +
*
 +
* The color of the color button will be loaded from the application
 +
* preferences (or keep the default if preferences have no setting).
 +
*/
 +
static GtkWidget* buildToolbar(ApplicationState* app) {
 +
 +
  GtkToolbar*  toolbar = NULL;
 +
  GtkToolItem* tbOpen = NULL;
 +
  GtkToolItem* tbSave = NULL;
 +
  GtkToolItem* tbSep = NULL;
 +
  GtkToolItem* tbFind = NULL;
 +
  GtkToolItem* tbColorButton = NULL;
 +
  GtkWidget*  colorButton = NULL;
 +
 +
  g_assert(app != NULL);
 +
 +
  tbOpen = gtk_tool_button_new_from_stock(GTK_STOCK_OPEN);
 +
  tbSave = gtk_tool_button_new_from_stock(GTK_STOCK_SAVE);
 +
  tbSep  = gtk_separator_tool_item_new();
 +
  tbFind = gtk_tool_button_new_from_stock(GTK_STOCK_FIND);
 +
 +
  tbColorButton = gtk_tool_item_new();
 +
  colorButton = hildon_color_button_new();
 +
  /* Copy the color from the color button into the application state.
 +
    This is done to detect whether the color in preferences matches
 +
    the default color or not (NEW). */
 +
  hildon_color_button_get_color(HILDON_COLOR_BUTTON(colorButton),
 +
                                &app->currentColor);
 +
  /* Load preferences and change the color if necessary. */
 +
  g_print("buildToolbar: loading color pref.\n");
 +
  if (confLoadCurrentColor(&app->currentColor)) {
 +
    g_print(" color not same as default one\n");
 +
    hildon_color_button_set_color(HILDON_COLOR_BUTTON(colorButton),
 +
                                  &app->currentColor);
 +
  } else {
 +
    g_print(" loaded color same as default\n");
 +
  }
 +
  gtk_container_add(GTK_CONTAINER(tbColorButton), colorButton);
 +
 +
  /*... Listing cut for brevity ...*/
 +
 +
}
 +
</source>
 +
 +
Since the graphical appearance of the program does not change (except that the <code>ColorButton</code> will display the correct initial color), a look will be taken at the stdout display of the program.
 +
 +
<pre>
 +
[sbox-DIABLO_X86: ~/appdev] &gt; run-standalone.sh ./hildon_helloworld-9
 +
buildToolbar: loading color pref.
 +
confLoadCurrentColor: invoked
 +
hildon_helloworld-9[19840]: GLIB WARNING ** default -
 +
confGetInt: key /apps/Maemo/hildon_hello/red not found
 +
hildon_helloworld-9[19840]: GLIB WARNING ** default -
 +
confGetInt: key /apps/Maemo/hildon_hello/green not found
 +
hildon_helloworld-9[19840]: GLIB WARNING ** default -
 +
confGetInt: key /apps/Maemo/hildon_hello/blue not found
 +
loaded color same as default
 +
main: calling gtk_main
 +
cbActionMainToolbarToggle invoked
 +
cbActionColorChanged invoked
 +
color changed, storing into preferences..
 +
confStoreColor: invoked
 +
done.
 +
main: returned from gtk_main and exiting with success
 +
</pre>
When running the program for the first time, warnings about the missing keys can be expected (since the values were not present in GConf).
When running the program for the first time, warnings about the missing keys can be expected (since the values were not present in GConf).
Line 465: Line 465:
Run the program again and exit:
Run the program again and exit:
-
[sbox-DIABLO_X86: ~/appdev] &gt; run-standalone.sh ./hildon_helloworld-9
+
<pre>
-
buildToolbar: loading color pref.
+
[sbox-DIABLO_X86: ~/appdev] &gt; run-standalone.sh ./hildon_helloworld-9
-
confLoadCurrentColor: invoked
+
buildToolbar: loading color pref.
-
  got red = 65535, after clamping = 65535
+
confLoadCurrentColor: invoked
-
  got green = 65535, after clamping = 65535
+
got red = 65535, after clamping = 65535
-
  got blue = 0, after clamping = 0
+
got green = 65535, after clamping = 65535
-
  color not same as default one
+
got blue = 0, after clamping = 0
-
main: calling gtk_main
+
color not same as default one
-
main: returned from gtk_main and exiting with success
+
main: calling gtk_main
-
 
+
main: returned from gtk_main and exiting with success
 +
</pre>
The next step is to remove one key (red), and run the program again (this is to test and verify that the logic works):
The next step is to remove one key (red), and run the program again (this is to test and verify that the logic works):
-
+
 
-
[sbox-DIABLO_X86: ~/appdev] &gt; run-standalone.sh gconftool-2  \
+
<pre>
-
  --unset /apps/Maemo/hildon_hello/red
+
[sbox-DIABLO_X86: ~/appdev] &gt; run-standalone.sh gconftool-2  \
-
[sbox-DIABLO_X86: ~/appdev] &gt; run-standalone.sh gconftool-2  \
+
--unset /apps/Maemo/hildon_hello/red
-
  -R /apps/Maemo/hildon_hello
+
[sbox-DIABLO_X86: ~/appdev] &gt; run-standalone.sh gconftool-2  \
-
  green = 65535
+
-R /apps/Maemo/hildon_hello
-
  blue = 0
+
green = 65535
-
[sbox-DIABLO_X86: ~/appdev] &gt; run-standalone.sh ./hildon_helloworld-9
+
blue = 0
-
buildToolbar: loading color pref.
+
[sbox-DIABLO_X86: ~/appdev] &gt; run-standalone.sh ./hildon_helloworld-9
-
confLoadCurrentColor: invoked
+
buildToolbar: loading color pref.
-
hildon_helloworld-9[19924]: GLIB WARNING ** default -
+
confLoadCurrentColor: invoked
-
  confGetInt: key /apps/Maemo/hildon_hello/red not found
+
hildon_helloworld-9[19924]: GLIB WARNING ** default -
-
  got green = 65535, after clamping = 65535
+
confGetInt: key /apps/Maemo/hildon_hello/red not found
-
  got blue = 0, after clamping = 0
+
got green = 65535, after clamping = 65535
-
  color not same as default one
+
got blue = 0, after clamping = 0
-
main: calling gtk_main
+
color not same as default one
-
main: returned from gtk_main and exiting with success
+
main: calling gtk_main
 +
main: returned from gtk_main and exiting with success
 +
</pre>
==  Asynchronous GConf ==
==  Asynchronous GConf ==
Line 503: Line 506:
When the configuration needs for the service are simple, and reacting to configuration changes in "realtime" is desired, it is advisable to use GConf. Also, people tend to use GConf when they are too lazy to write their own configuration file parsers (although there is a simple one in GLib), or too lazy to write the GUI part to change the settings. This example program will simulate the first case, and react to changes in a subset of GConf configuration name space when the changes happen.
When the configuration needs for the service are simple, and reacting to configuration changes in "realtime" is desired, it is advisable to use GConf. Also, people tend to use GConf when they are too lazy to write their own configuration file parsers (although there is a simple one in GLib), or too lazy to write the GUI part to change the settings. This example program will simulate the first case, and react to changes in a subset of GConf configuration name space when the changes happen.
-
The application will be interested in two string values; one to set the device to use for communication (connection), and the other to set the communication parameters for the device (connectionparams). Since this example will be concentrating on just the change notifications, the program logic is simplified by omitting the proper set-up code in the program. This means that it is necessary to set up some values to the GConf keys prior to running the program. For this, gconftool-2 will be used, and a target has been prepared in the '''Makefile''' just for this (see section [http://wiki.maemo.org/Documentation/Maemo_5_Developer_Guide/GNU_Build_System#GNU_Make_and_Makefiles GNU Make and Makefiles] if necessary): gconf-listener/Makefile
+
The application will be interested in two string values; one to set the device to use for communication (connection), and the other to set the communication parameters for the device (connectionparams). Since this example will be concentrating on just the change notifications, the program logic is simplified by omitting the proper set-up code in the program. This means that it is necessary to set up some values to the GConf keys prior to running the program. For this, <code>gconftool-2</code> will be used, and a target has been prepared in the '''Makefile''' just for this (see section [[Documentation/Maemo 5 Developer Guide/GNU Build System#GNU Make and Makefiles|GNU Make and Makefiles]] if necessary): gconf-listener/Makefile
-
<tt><span>''<span><font color="#9A1900"><nowiki># Define a variable for this so that the GConf root may be changed</nowiki></font></span>''</span>
+
<source lang="make">
-
gconf_root <span><font color="#990000"><nowiki>:=</nowiki></font></span> /apps/Maemo/platdev_ex
+
# Define a variable for this so that the GConf root may be changed
-
+
gconf_root := /apps/Maemo/platdev_ex
-
<span>''<span><font color="#9A1900"><nowiki># ... Listing cut for brevity ...</nowiki></font></span>''</span>
+
 
-
+
# ... Listing cut for brevity ...
-
<span>''<span><font color="#9A1900"><nowiki># This will setup the keys into default values.</nowiki></font></span>''</span>
+
-
<span>''<span><font color="#9A1900"><nowiki># It will first do a clear to remove any existing keys.</nowiki></font></span>''</span>
+
-
primekeys<span><font color="#990000"><nowiki>:</nowiki></font></span> clearkeys
+
-
        gconftool-<span><font color="#993399">2</font></span> -set -type string <span><font color="#990000">\</font></span>
+
-
                    <span><font color="#009900">$(gconf_root)</font></span>/connection btcomm0
+
-
        gconftool-<span><font color="#993399">2</font></span> -set -type string <span><font color="#990000">\</font></span>
+
-
                    <span><font color="#009900">$(gconf_root)</font></span>/connectionparams <span><font color="#993399">9600</font></span><span><font color="#990000">,</font></span><span><font color="#993399">8</font></span><span><font color="#990000">,</font></span>N<span><font color="#990000">,</font></span><span><font color="#993399">1</font></span>
+
-
+
-
<span>''<span><font color="#9A1900"><nowiki># Remove all application keys</nowiki></font></span>''</span>
+
-
clearkeys<span><font color="#990000"><nowiki>:</nowiki></font></span>
+
-
        @gconftool-<span><font color="#993399">2</font></span> -recursive-unset <span><font color="#009900">$(gconf_root)</font></span>
+
-
+
-
<span>''<span><font color="#9A1900"><nowiki># Dump all application keys</nowiki></font></span>''</span>
+
-
dumpkeys<span><font color="#990000"><nowiki>:</nowiki></font></span>
+
-
        @echo Keys under <span><font color="#009900">$(gconf_root)</font></span><span><font color="#990000"><nowiki>:</nowiki></font></span>
+
-
        @gconftool-<span><font color="#993399">2</font></span> -recursive-list <span><font color="#009900">$(gconf_root)</font></span>
+
-
</tt>
+
-
The next step is to prepare the keyspace by running the primekeys target, and to verify that it succeeds by running the dumpkeys target:
+
# This will setup the keys into default values.
 +
# It will first do a clear to remove any existing keys.
 +
primekeys: clearkeys
 +
        gconftool-2 -set -type string \
 +
                    $(gconf_root)/connection btcomm0
 +
        gconftool-2 -set -type string \
 +
                    $(gconf_root)/connectionparams 9600,8,N,1
-
  [sbox-DIABLO_X86: ~/gconf-listener] &gt; make primekeys
+
# Remove all application keys
 +
clearkeys:
 +
        @gconftool-2 -recursive-unset $(gconf_root)
 +
 
 +
# Dump all application keys
 +
dumpkeys:
 +
        @echo Keys under $(gconf_root):
 +
        @gconftool-2 -recursive-list $(gconf_root)
 +
</source>
 +
 
 +
The next step is to prepare the keyspace by running the <code>primekeys</code> target, and to verify that it succeeds by running the <code>dumpkeys</code> target:
 +
 
 +
<pre>
 +
  [sbox-DIABLO_X86: ~/gconf-listener] > make primekeys
  gconftool-2 --set --type string \
  gconftool-2 --set --type string \
             /apps/Maemo/platdev_ex/connection btcomm0
             /apps/Maemo/platdev_ex/connection btcomm0
  gconftool-2 --set --type string \
  gconftool-2 --set --type string \
             /apps/Maemo/platdev_ex/connectionparams 9600,8,N,1
             /apps/Maemo/platdev_ex/connectionparams 9600,8,N,1
-
  [sbox-DIABLO_X86: ~/gconf-listener] &gt; make dumpkeys
+
  [sbox-DIABLO_X86: ~/gconf-listener] > make dumpkeys
  Keys under /apps/Maemo/platdev_ex:
  Keys under /apps/Maemo/platdev_ex:
   connectionparams = 9600,8,N,1
   connectionparams = 9600,8,N,1
   connection = btcomm0
   connection = btcomm0
 +
</pre>
===  Implementing Notifications on Changes in GConf ===
===  Implementing Notifications on Changes in GConf ===
Line 544: Line 550:
The first step here is to take care of the necessary header information. The GConf namespace settings have been all implemented using cpp macros, so that one can easily change the prefix of the name space if required later on. gconf-listener/gconf-key-watch.c
The first step here is to take care of the necessary header information. The GConf namespace settings have been all implemented using cpp macros, so that one can easily change the prefix of the name space if required later on. gconf-listener/gconf-key-watch.c
-
<tt><span>'''<span><font color="#000080"><nowiki>#include</nowiki></font></span>'''</span> <span><font color="#FF0000">&lt;glib.h&gt;</font></span>
+
<source lang="c">
-
<span>'''<span><font color="#000080"><nowiki>#include</nowiki></font></span>'''</span> <span><font color="#FF0000">&lt;gconf/gconf-client.h&gt;</font></span>
+
#include <glib.h>
-
<span>'''<span><font color="#000080"><nowiki>#include</nowiki></font></span>'''</span> <span><font color="#FF0000">&lt;string.h&gt;</font></span> <span>''<span><font color="#9A1900">/* strcmp */</font></span>''</span>
+
#include <gconf/gconf-client.h>
-
+
#include <string.h> /* strcmp */
-
<span>''<span><font color="#9A1900">/* As per maemo Coding Style and Guidelines document, we use the</font></span>''</span>
+
 
-
<span>''<span><font color="#9A1900">  /apps/Maemo/ -prefix.</font></span>''</span>
+
/* As per maemo Coding Style and Guidelines document, we use the
-
<span>''<span><font color="#9A1900">  NOTE: There is no central registry (as of this moment) that you</font></span>''</span>
+
  /apps/Maemo/ -prefix.
-
<span>''<span><font color="#9A1900">        could check that your application name doesn't collide with</font></span>''</span>
+
  NOTE: There is no central registry (as of this moment) that you
-
<span>''<span><font color="#9A1900">        other application names, so caution is advised! */</font></span>''</span>
+
        could check that your application name doesn't collide with
-
<span>'''<span><font color="#000080"><nowiki>#define</nowiki></font></span>'''</span> SERVICE_GCONF_ROOT <span><font color="#FF0000">"/apps/Maemo/platdev_ex"</font></span>
+
        other application names, so caution is advised! */
-
+
#define SERVICE_GCONF_ROOT "/apps/Maemo/platdev_ex"
-
<span>''<span><font color="#9A1900">/* We define the names of the keys symbolically so that we may change</font></span>''</span>
+
 
-
<span>''<span><font color="#9A1900">  them later if necessary, and so that the GConf "root directory" for</font></span>''</span>
+
/* We define the names of the keys symbolically so that we may change
-
<span>''<span><font color="#9A1900">  our application will be automatically prefixed to the paths. */</font></span>''</span>
+
  them later if necessary, and so that the GConf "root directory" for
-
<span>'''<span><font color="#000080"><nowiki>#define</nowiki></font></span>'''</span> SERVICE_KEY_CONNECTION <span><font color="#990000">\</font></span>
+
  our application will be automatically prefixed to the paths. */
-
        SERVICE_GCONF_ROOT <span><font color="#FF0000">"/connection"</font></span>
+
#define SERVICE_KEY_CONNECTION \
-
<span>'''<span><font color="#000080"><nowiki>#define</nowiki></font></span>'''</span> SERVICE_KEY_CONNECTIONPARAMS <span><font color="#990000">\</font></span>
+
        SERVICE_GCONF_ROOT "/connection"
-
        SERVICE_GCONF_ROOT <span><font color="#FF0000">"/connectionparams"</font></span></tt>
+
#define SERVICE_KEY_CONNECTIONPARAMS \
 +
        SERVICE_GCONF_ROOT "/connectionparams"
 +
</source>
The main starts innocently enough, by creating a GConf client object (that encapsulates the connection to the GConf daemon), and then displays the two values on output: gconf-listener/gconf-key-watch.c
The main starts innocently enough, by creating a GConf client object (that encapsulates the connection to the GConf daemon), and then displays the two values on output: gconf-listener/gconf-key-watch.c
-
<tt><span><font color="#009900">int</font></span> <span>'''<span><font color="#000000">main</font></span>'''</span> <span><font color="#990000">(</font></span><span><font color="#009900">int</font></span> argc<span><font color="#990000">,</font></span> <span><font color="#009900">char</font></span><span><font color="#990000"><nowiki>**</nowiki></font></span> argv<span><font color="#990000">)</font></span> <span><font color="#FF0000">{</font></span>
+
<source lang="c">
-
  <span>''<span><font color="#9A1900">/* Will hold reference to the GConfClient object. */</font></span>''</span>
+
int main (int argc, char** argv) {
-
  GConfClient<span><font color="#990000"><nowiki>*</nowiki></font></span> client <span><font color="#990000"><nowiki>=</nowiki></font></span> NULL<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
  /* Will hold reference to the GConfClient object. */
-
  <span>''<span><font color="#9A1900">/* Initialize this to NULL so that we'll know whether an error</font></span>''</span>
+
  GConfClient* client = NULL;
-
<span>''<span><font color="#9A1900">    occurred or not (and we don't have an existing GError object</font></span>''</span>
+
  /* Initialize this to NULL so that we'll know whether an error
-
<span>''<span><font color="#9A1900">    anyway at this point). */</font></span>''</span>
+
    occurred or not (and we don't have an existing GError object
-
  GError<span><font color="#990000"><nowiki>*</nowiki></font></span> error <span><font color="#990000"><nowiki>=</nowiki></font></span> NULL<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
    anyway at this point). */
-
  <span>''<span><font color="#9A1900">/* This will hold a reference to the mainloop object. */</font></span>''</span>
+
  GError* error = NULL;
-
  GMainLoop<span><font color="#990000"><nowiki>*</nowiki></font></span> mainloop <span><font color="#990000"><nowiki>=</nowiki></font></span> NULL<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
  /* This will hold a reference to the mainloop object. */
-
+
  GMainLoop* mainloop = NULL;
-
  <span>'''<span><font color="#000000">g_print</font></span>'''</span><span><font color="#990000">(</font></span>PROGNAME <span><font color="#FF0000">":main Starting.</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">);</font></span>
+
-
+
-
  <span>''<span><font color="#9A1900">/* Must be called to initialize GType system. The API reference for</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">    gconf_client_get_default() insists.</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">    NOTE: Using gconf_init() is deprecated! */</font></span>''</span>
+
-
  <span>'''<span><font color="#000000">g_type_init</font></span>'''</span><span><font color="#990000">();</font></span>
+
-
+
-
  <span>''<span><font color="#9A1900">/* Create a new mainloop structure that we'll use. Use default</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">    context (NULL) and set the 'running' flag to FALSE. */</font></span>''</span>
+
-
  mainloop <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">g_main_loop_new</font></span>'''</span><span><font color="#990000">(</font></span>NULL<span><font color="#990000">,</font></span> FALSE<span><font color="#990000">);</font></span>
+
-
  <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">(</font></span>mainloop <span><font color="#990000"><nowiki>==</nowiki></font></span> NULL<span><font color="#990000">)</font></span> <span><font color="#FF0000">{</font></span>
+
-
    <span>'''<span><font color="#000000">g_error</font></span>'''</span><span><font color="#990000">(</font></span>PROGNAME <span><font color="#FF0000">": Failed to create mainloop!</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">);</font></span>
+
-
  <span><font color="#FF0000">}</font></span>
+
-
+
-
  <span>''<span><font color="#9A1900">/* Create a new GConfClient object using the default settings. */</font></span>''</span>
+
-
  client <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">gconf_client_get_default</font></span>'''</span><span><font color="#990000">();</font></span>
+
-
  <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">(</font></span>client <span><font color="#990000"><nowiki>==</nowiki></font></span> NULL<span><font color="#990000">)</font></span> <span><font color="#FF0000">{</font></span>
+
-
    <span>'''<span><font color="#000000">g_error</font></span>'''</span><span><font color="#990000">(</font></span>PROGNAME <span><font color="#FF0000">": Failed to create GConfClient!</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">);</font></span>
+
-
  <span><font color="#FF0000">}</font></span>
+
-
+
-
  <span>'''<span><font color="#000000">g_print</font></span>'''</span><span><font color="#990000">(</font></span>PROGNAME <span><font color="#FF0000">":main GType and GConfClient initialized.</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">);</font></span>
+
-
+
-
  <span>''<span><font color="#9A1900">/* Display the starting values for the two keys. */</font></span>''</span>
+
-
  <span>'''<span><font color="#000000">dispStringKey</font></span>'''</span><span><font color="#990000">(</font></span>client<span><font color="#990000">,</font></span> SERVICE_KEY_CONNECTION<span><font color="#990000">);</font></span>
+
-
  <span>'''<span><font color="#000000">dispStringKey</font></span>'''</span><span><font color="#990000">(</font></span>client<span><font color="#990000">,</font></span> SERVICE_KEY_CONNECTIONPARAMS<span><font color="#990000">);</font></span></tt>
+
-
The dispStringKey utility is rather simple as well, building on the GConf material that was covered in the previous section [localhost#sec:using_gconf_to_read_write_pref 9.3.2]<nowiki>: </nowiki>gconf-listener/gconf-key-watch.c
+
  g_print(PROGNAME ":main Starting.\n");
-
<tt><span>''<span><font color="#9A1900">/**</font></span>''</span>
+
  /* Must be called to initialize GType system. The API reference for
-
<span>''<span><font color="#9A1900"> * Utility to retrieve a string key and display it.</font></span>''</span>
+
    gconf_client_get_default() insists.
-
  <span>''<span><font color="#9A1900"> * (Just as a small refresher on the API.)</font></span>''</span>
+
    NOTE: Using gconf_init() is deprecated! */
-
  <span>''<span><font color="#9A1900"> */</font></span>''</span>
+
  g_type_init();
-
<span>'''<span><font color="#0000FF">static</font></span>'''</span> <span><font color="#009900">void</font></span> <span>'''<span><font color="#000000">dispStringKey</font></span>'''</span><span><font color="#990000">(</font></span>GConfClient<span><font color="#990000"><nowiki>*</nowiki></font></span> client<span><font color="#990000">,</font></span>
+
 
-
                          <span>'''<span><font color="#0000FF">const</font></span>'''</span> gchar<span><font color="#990000"><nowiki>*</nowiki></font></span> keyname<span><font color="#990000">)</font></span> <span><font color="#FF0000">{</font></span>
+
  /* Create a new mainloop structure that we'll use. Use default
-
+
    context (NULL) and set the 'running' flag to FALSE. */
-
  <span>''<span><font color="#9A1900">/* This will hold the string value of the key. It will be</font></span>''</span>
+
  mainloop = g_main_loop_new(NULL, FALSE);
-
<span>''<span><font color="#9A1900">    dynamically allocated for us, so we need to release it ourselves</font></span>''</span>
+
  if (mainloop == NULL) {
-
<span>''<span><font color="#9A1900">    when done (before returning). */</font></span>''</span>
+
    g_error(PROGNAME ": Failed to create mainloop!\n");
-
  gchar<span><font color="#990000"><nowiki>*</nowiki></font></span> valueStr <span><font color="#990000"><nowiki>=</nowiki></font></span> NULL<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
  }
-
+
 
-
  <span>''<span><font color="#9A1900">/* We're not interested in the errors themselves (the last</font></span>''</span>
+
  /* Create a new GConfClient object using the default settings. */
-
<span>''<span><font color="#9A1900">    parameter), but the function will return NULL if there is one,</font></span>''</span>
+
  client = gconf_client_get_default();
-
<span>''<span><font color="#9A1900">    so we just end in that case. */</font></span>''</span>
+
  if (client == NULL) {
-
  valueStr <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">gconf_client_get_string</font></span>'''</span><span><font color="#990000">(</font></span>client<span><font color="#990000">,</font></span> keyname<span><font color="#990000">,</font></span> NULL<span><font color="#990000">);</font></span>
+
    g_error(PROGNAME ": Failed to create GConfClient!\n");
-
+
  }
-
  <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">(</font></span>valueStr <span><font color="#990000"><nowiki>==</nowiki></font></span> NULL<span><font color="#990000">)</font></span> <span><font color="#FF0000">{</font></span>
+
 
-
    <span>'''<span><font color="#000000">g_error</font></span>'''</span><span><font color="#990000">(</font></span>PROGNAME <span><font color="#FF0000">": No string value for %s. Quitting</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">,</font></span> keyname<span><font color="#990000">);</font></span>
+
  g_print(PROGNAME ":main GType and GConfClient initialized.\n");
-
    <span>''<span><font color="#9A1900">/* Application terminates. */</font></span>''</span>
+
 
-
  <span><font color="#FF0000">}</font></span>
+
  /* Display the starting values for the two keys. */
-
+
  dispStringKey(client, SERVICE_KEY_CONNECTION);
-
  <span>'''<span><font color="#000000">g_print</font></span>'''</span><span><font color="#990000">(</font></span>PROGNAME <span><font color="#FF0000">": Value for key '%s' is set to '%s'</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">,</font></span>
+
  dispStringKey(client, SERVICE_KEY_CONNECTIONPARAMS);
-
          keyname<span><font color="#990000">,</font></span> valueStr<span><font color="#990000">);</font></span>
+
</source>
-
+
 
-
  <span>''<span><font color="#9A1900">/* Normally one would want to use the value for something beyond</font></span>''</span>
+
The <code>dispStringKey</code> utility is rather simple as well, building on the GConf material that was covered in the [[#Using GConf to read and write preferences|previous section]]: gconf-listener/gconf-key-watch.c
-
<span>''<span><font color="#9A1900">    just displaying it, but since this code doesn't, we release the</font></span>''</span>
+
 
-
<span>''<span><font color="#9A1900">    allocated value string. */</font></span>''</span>
+
<source lang="c">
-
  <span>'''<span><font color="#000000">g_free</font></span>'''</span><span><font color="#990000">(</font></span>valueStr<span><font color="#990000">);</font></span>
+
/**
-
<span><font color="#FF0000">}</font></span></tt>
+
* Utility to retrieve a string key and display it.
 +
  * (Just as a small refresher on the API.)
 +
  */
 +
static void dispStringKey(GConfClient* client,
 +
                          const gchar* keyname) {
 +
 
 +
  /* This will hold the string value of the key. It will be
 +
    dynamically allocated for us, so we need to release it ourselves
 +
    when done (before returning). */
 +
  gchar* valueStr = NULL;
 +
 
 +
  /* We're not interested in the errors themselves (the last
 +
    parameter), but the function will return NULL if there is one,
 +
    so we just end in that case. */
 +
  valueStr = gconf_client_get_string(client, keyname, NULL);
 +
 
 +
  if (valueStr == NULL) {
 +
    g_error(PROGNAME ": No string value for %s. Quitting\n", keyname);
 +
    /* Application terminates. */
 +
  }
 +
 
 +
  g_print(PROGNAME ": Value for key '%s' is set to '%s'\n",
 +
          keyname, valueStr);
 +
 
 +
  /* Normally one would want to use the value for something beyond
 +
    just displaying it, but since this code doesn't, we release the
 +
    allocated value string. */
 +
  g_free(valueStr);
 +
}
 +
</source>
Next, the GConf client is told to attach itself to a specific name space part that this example is going to operate with: gconf-listener/gconf-key-watch.c
Next, the GConf client is told to attach itself to a specific name space part that this example is going to operate with: gconf-listener/gconf-key-watch.c
-
<tt>  <span>''<span><font color="#9A1900">/**</font></span>''</span>
+
<source lang="c">
-
  <span>''<span><font color="#9A1900">  * Register directory to watch for changes. This will then tell</font></span>''</span>
+
/**
-
  <span>''<span><font color="#9A1900">  * GConf to watch for changes in this namespace, and cause the</font></span>''</span>
+
  * Register directory to watch for changes. This will then tell
-
  <span>''<span><font color="#9A1900">  * "value-changed"-signal to be emitted. We won't be using that</font></span>''</span>
+
  * GConf to watch for changes in this namespace, and cause the
-
  <span>''<span><font color="#9A1900">  * mechanism, but will opt to a more modern (and potentially more</font></span>''</span>
+
  * "value-changed"-signal to be emitted. We won't be using that
-
  <span>''<span><font color="#9A1900">  * scalable solution). The directory needs to be added to the</font></span>''</span>
+
  * mechanism, but will opt to a more modern (and potentially more
-
  <span>''<span><font color="#9A1900">  * watch list in either case.</font></span>''</span>
+
  * scalable solution). The directory needs to be added to the
-
  <span>''<span><font color="#9A1900">  *</font></span>''</span>
+
  * watch list in either case.
-
  <span>''<span><font color="#9A1900">  * When adding directories, you can sometimes optimize your program</font></span>''</span>
+
  *
-
  <span>''<span><font color="#9A1900">  * performance by asking GConfClient to preload some (or all) keys</font></span>''</span>
+
  * When adding directories, you can sometimes optimize your program
-
  <span>''<span><font color="#9A1900">  * under a specific directory. This is done via the preload_type</font></span>''</span>
+
  * performance by asking GConfClient to preload some (or all) keys
-
  <span>''<span><font color="#9A1900">  * parameter (we use GCONF_CLIENT_PRELOAD_NONE below). Since our</font></span>''</span>
+
  * under a specific directory. This is done via the preload_type
-
  <span>''<span><font color="#9A1900">  * program will only listen for changes, we don't want to use extra</font></span>''</span>
+
  * parameter (we use GCONF_CLIENT_PRELOAD_NONE below). Since our
-
  <span>''<span><font color="#9A1900">  * memory to keep the keys cached.</font></span>''</span>
+
  * program will only listen for changes, we don't want to use extra
-
  <span>''<span><font color="#9A1900">  *</font></span>''</span>
+
  * memory to keep the keys cached.
-
  <span>''<span><font color="#9A1900">  * Parameters:</font></span>''</span>
+
  *
-
  <span>''<span><font color="#9A1900">  * - client: GConf-client object</font></span>''</span>
+
  * Parameters:
-
  <span>''<span><font color="#9A1900">  * - SERVICEPATH: the name of the GConf namespace to follow</font></span>''</span>
+
  * - client: GConf-client object
-
  <span>''<span><font color="#9A1900">  * - GCONF_CLIENT_PRELOAD_NONE: do not preload any of contents</font></span>''</span>
+
  * - SERVICEPATH: the name of the GConf namespace to follow
-
  <span>''<span><font color="#9A1900">  * - error: where to store the pointer to allocated GError on</font></span>''</span>
+
  * - GCONF_CLIENT_PRELOAD_NONE: do not preload any of contents
-
  <span>''<span><font color="#9A1900">  *          errors.</font></span>''</span>
+
  * - error: where to store the pointer to allocated GError on
-
  <span>''<span><font color="#9A1900">  */</font></span>''</span>
+
  *          errors.
-
  <span>'''<span><font color="#000000">gconf_client_add_dir</font></span>'''</span><span><font color="#990000">(</font></span>client<span><font color="#990000">,</font></span>
+
  */
-
                        SERVICE_GCONF_ROOT<span><font color="#990000">,</font></span>
+
gconf_client_add_dir(client,
-
                        GCONF_CLIENT_PRELOAD_NONE<span><font color="#990000">,</font></span>
+
                    SERVICE_GCONF_ROOT,
-
                        <span><font color="#990000">&amp;</font></span>error<span><font color="#990000">);</font></span>
+
                    GCONF_CLIENT_PRELOAD_NONE,
-
+
                    &error);
-
  <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">(</font></span>error <span><font color="#990000"><nowiki>!=</nowiki></font></span> NULL<span><font color="#990000">)</font></span> <span><font color="#FF0000">{</font></span>
+
 
-
    <span>'''<span><font color="#000000">g_error</font></span>'''</span><span><font color="#990000">(</font></span>PROGNAME <span><font color="#FF0000">": Failed to add a watch to GCClient: %s</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">,</font></span>
+
if (error != NULL) {
-
            error<span><font color="#990000">-&gt;</font></span>message<span><font color="#990000">);</font></span>
+
  g_error(PROGNAME ": Failed to add a watch to GCClient: %s\n",
-
    <span>''<span><font color="#9A1900">/* Normally we'd also release the allocated GError, but since</font></span>''</span>
+
          error->message);
-
<span>''<span><font color="#9A1900">      this program will terminate on g_error, we won't do that.</font></span>''</span>
+
  /* Normally we'd also release the allocated GError, but since
-
<span>''<span><font color="#9A1900">      Hence the next line is commented. */</font></span>''</span>
+
    this program will terminate on g_error, we won't do that.
-
    <span>''<span><font color="#9A1900">/* g_error_free(error); */</font></span>''</span>
+
    Hence the next line is commented. */
-
+
  /* g_error_free(error); */
-
    <span>''<span><font color="#9A1900">/* When you want to release the error if it has been allocated,</font></span>''</span>
+
 
-
<span>''<span><font color="#9A1900">      or just continue if not, use g_clear_error(&amp;error); */</font></span>''</span>
+
  /* When you want to release the error if it has been allocated,
-
  <span><font color="#FF0000">}</font></span>
+
    or just continue if not, use g_clear_error(&error); */
-
+
}
-
  <span>'''<span><font color="#000000">g_print</font></span>'''</span><span><font color="#990000">(</font></span>PROGNAME <span><font color="#FF0000">":main Added "</font></span> SERVICE_GCONF_ROOT <span><font color="#FF0000">".</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">);</font></span></tt>
+
 
 +
g_print(PROGNAME ":main Added " SERVICE_GCONF_ROOT ".\n");
 +
</source>
Proceeding with the callback function registration, we have: gconf-listener/gconf-key-watch.c
Proceeding with the callback function registration, we have: gconf-listener/gconf-key-watch.c
-
<tt>  <span>''<span><font color="#9A1900">/* Register our interest (in the form of a callback function) for</font></span>''</span>
+
<source lang="c">
-
<span>''<span><font color="#9A1900">    any changes under the namespace that we just added.</font></span>''</span>
+
/* Register our interest (in the form of a callback function) for
-
+
  any changes under the namespace that we just added.
-
<span>''<span><font color="#9A1900">    Parameters:</font></span>''</span>
+
 
-
<span>''<span><font color="#9A1900">    - client: GConfClient object.</font></span>''</span>
+
  Parameters:
-
<span>''<span><font color="#9A1900">    - SERVICEPATH: namespace under which we can get notified for</font></span>''</span>
+
  - client: GConfClient object.
-
<span>''<span><font color="#9A1900">                    changes.</font></span>''</span>
+
  - SERVICEPATH: namespace under which we can get notified for
-
<span>''<span><font color="#9A1900">    - gconf_notify_func: callback that will be called on changes.</font></span>''</span>
+
                  changes.
-
<span>''<span><font color="#9A1900">    - NULL: user-data pointer (not used here).</font></span>''</span>
+
  - gconf_notify_func: callback that will be called on changes.
-
<span>''<span><font color="#9A1900">    - NULL: function to call on user-data when notify is removed or</font></span>''</span>
+
  - NULL: user-data pointer (not used here).
-
<span>''<span><font color="#9A1900">            GConfClient destroyed. NULL for none (since we don't</font></span>''</span>
+
  - NULL: function to call on user-data when notify is removed or
-
<span>''<span><font color="#9A1900">            have user-data anyway).</font></span>''</span>
+
          GConfClient destroyed. NULL for none (since we don't
-
<span>''<span><font color="#9A1900">    - error: return location for an allocated GError.</font></span>''</span>
+
          have user-data anyway).
-
+
  - error: return location for an allocated GError.
-
<span>''<span><font color="#9A1900">    Returns:</font></span>''</span>
+
 
-
<span>''<span><font color="#9A1900">    guint: an ID for this notification so that we could remove it</font></span>''</span>
+
  Returns:
-
<span>''<span><font color="#9A1900">            later with gconf_client_notify_remove(). We're not going</font></span>''</span>
+
  guint: an ID for this notification so that we could remove it
-
<span>''<span><font color="#9A1900">            to use it so we don't store it anywhere. */</font></span>''</span>
+
          later with gconf_client_notify_remove(). We're not going
-
  <span>'''<span><font color="#000000">gconf_client_notify_add</font></span>'''</span><span><font color="#990000">(</font></span>client<span><font color="#990000">,</font></span> SERVICE_GCONF_ROOT<span><font color="#990000">,</font></span>
+
          to use it so we don't store it anywhere. */
-
                          keyChangeCallback<span><font color="#990000">,</font></span> NULL<span><font color="#990000">,</font></span> NULL<span><font color="#990000">,</font></span> <span><font color="#990000">&amp;</font></span>error<span><font color="#990000">);</font></span>
+
gconf_client_notify_add(client, SERVICE_GCONF_ROOT,
-
  <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">(</font></span>error <span><font color="#990000"><nowiki>!=</nowiki></font></span> NULL<span><font color="#990000">)</font></span> <span><font color="#FF0000">{</font></span>
+
                        keyChangeCallback, NULL, NULL, &error);
-
    <span>'''<span><font color="#000000">g_error</font></span>'''</span><span><font color="#990000">(</font></span>PROGNAME <span><font color="#FF0000">": Failed to add register the callback: %s</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">,</font></span>
+
if (error != NULL) {
-
            error<span><font color="#990000">-&gt;</font></span>message<span><font color="#990000">);</font></span>
+
  g_error(PROGNAME ": Failed to add register the callback: %s\n",
-
    <span>''<span><font color="#9A1900">/* Program terminates. */</font></span>''</span>
+
          error->message);
-
  <span><font color="#FF0000">}</font></span>
+
  /* Program terminates. */
-
+
}
-
  <span>'''<span><font color="#000000">g_print</font></span>'''</span><span><font color="#990000">(</font></span>PROGNAME <span><font color="#FF0000">":main CB registered &amp; starting main loop</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">);</font></span></tt>
+
 
 +
g_print(PROGNAME ":main CB registered & starting main loop\n");
 +
</source>
When dealing with regular desktop software, one could use multiple callback functions; one for each key to track. However, this would require implementing multiple callback functions, and this runs a risk of enlarging the size of the code. For this reason, the example code uses one callback function, which will internally multiplex between the two keys (by using strcmp): gconf-listener/gconf-key-watch.c
When dealing with regular desktop software, one could use multiple callback functions; one for each key to track. However, this would require implementing multiple callback functions, and this runs a risk of enlarging the size of the code. For this reason, the example code uses one callback function, which will internally multiplex between the two keys (by using strcmp): gconf-listener/gconf-key-watch.c
-
<tt><span>''<span><font color="#9A1900">/**</font></span>''</span>
+
<source lang="c">
-
  <span>''<span><font color="#9A1900"> * Callback called when a key in watched directory changes.</font></span>''</span>
+
/**
-
  <span>''<span><font color="#9A1900"> * Prototype for the callback must be compatible with</font></span>''</span>
+
  * Callback called when a key in watched directory changes.
-
  <span>''<span><font color="#9A1900"> * GConfClientNotifyFunc (for ref).</font></span>''</span>
+
  * Prototype for the callback must be compatible with
-
  <span>''<span><font color="#9A1900"> *</font></span>''</span>
+
  * GConfClientNotifyFunc (for ref).
-
  <span>''<span><font color="#9A1900"> * It will find out which key changed (using strcmp, since the same</font></span>''</span>
+
  *
-
  <span>''<span><font color="#9A1900"> * callback is used to track both keys) and the display the new value</font></span>''</span>
+
  * It will find out which key changed (using strcmp, since the same
-
  <span>''<span><font color="#9A1900"> * of the key.</font></span>''</span>
+
  * callback is used to track both keys) and the display the new value
-
  <span>''<span><font color="#9A1900"> *</font></span>''</span>
+
  * of the key.
-
  <span>''<span><font color="#9A1900"> * The changed key/value pair will be communicated in the entry</font></span>''</span>
+
  *
-
  <span>''<span><font color="#9A1900"> * parameter. userData will be NULL (can be set on notify_add [in</font></span>''</span>
+
  * The changed key/value pair will be communicated in the entry
-
  <span>''<span><font color="#9A1900"> * main]). Normally the application state would be carried within the</font></span>''</span>
+
  * parameter. userData will be NULL (can be set on notify_add [in
-
  <span>''<span><font color="#9A1900"> * userData parameter, so that this callback could then modify the</font></span>''</span>
+
  * main]). Normally the application state would be carried within the
-
  <span>''<span><font color="#9A1900"> * view based on the change. Since this program does not have a state,</font></span>''</span>
+
  * userData parameter, so that this callback could then modify the
-
  <span>''<span><font color="#9A1900"> * there is little that we can do within the function (it will abort</font></span>''</span>
+
  * view based on the change. Since this program does not have a state,
-
  <span>''<span><font color="#9A1900"> * the program on errors though).</font></span>''</span>
+
  * there is little that we can do within the function (it will abort
-
  <span>''<span><font color="#9A1900"> */</font></span>''</span>
+
  * the program on errors though).
-
<span>'''<span><font color="#0000FF">static</font></span>'''</span> <span><font color="#009900">void</font></span> <span>'''<span><font color="#000000">keyChangeCallback</font></span>'''</span><span><font color="#990000">(</font></span>GConfClient<span><font color="#990000"><nowiki>*</nowiki></font></span> client<span><font color="#990000">,</font></span>
+
  */
-
                              guint        cnxn_id<span><font color="#990000">,</font></span>
+
static void keyChangeCallback(GConfClient* client,
-
                              GConfEntry<span><font color="#990000"><nowiki>*</nowiki></font></span> entry<span><font color="#990000">,</font></span>
+
                              guint        cnxn_id,
-
                              gpointer    userData<span><font color="#990000">)</font></span> <span><font color="#FF0000">{</font></span>
+
                              GConfEntry*  entry,
-
+
                              gpointer    userData) {
-
  <span>''<span><font color="#9A1900">/* This will hold the pointer to the value. */</font></span>''</span>
+
-
  <span>'''<span><font color="#0000FF">const</font></span>'''</span> GConfValue<span><font color="#990000"><nowiki>*</nowiki></font></span> value <span><font color="#990000"><nowiki>=</nowiki></font></span> NULL<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
  <span>''<span><font color="#9A1900">/* This will hold a pointer to the name of the key that changed. */</font></span>''</span>
+
-
  <span>'''<span><font color="#0000FF">const</font></span>'''</span> gchar<span><font color="#990000"><nowiki>*</nowiki></font></span> keyname <span><font color="#990000"><nowiki>=</nowiki></font></span> NULL<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
  <span>''<span><font color="#9A1900">/* This will hold a dynamically allocated human-readable</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">    representation of the changed value. */</font></span>''</span>
+
-
  gchar<span><font color="#990000"><nowiki>*</nowiki></font></span> strValue <span><font color="#990000"><nowiki>=</nowiki></font></span> NULL<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
+
-
  <span>'''<span><font color="#000000">g_print</font></span>'''</span><span><font color="#990000">(</font></span>PROGNAME <span><font color="#FF0000">": keyChangeCallback invoked.</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">);</font></span>
+
-
+
-
  <span>''<span><font color="#9A1900">/* Get a pointer to the key (this is not a copy). */</font></span>''</span>
+
-
  keyname <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">gconf_entry_get_key</font></span>'''</span><span><font color="#990000">(</font></span>entry<span><font color="#990000">);</font></span>
+
-
+
-
  <span>''<span><font color="#9A1900">/* It will be quite fatal if after change we cannot retrieve even</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">    the name for the gconf entry, so we error out here. */</font></span>''</span>
+
-
  <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">(</font></span>keyname <span><font color="#990000"><nowiki>==</nowiki></font></span> NULL<span><font color="#990000">)</font></span> <span><font color="#FF0000">{</font></span>
+
-
    <span>'''<span><font color="#000000">g_error</font></span>'''</span><span><font color="#990000">(</font></span>PROGNAME <span><font color="#FF0000">": Couldn't get the key name!</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">);</font></span>
+
-
    <span>''<span><font color="#9A1900">/* Application terminates. */</font></span>''</span>
+
-
  <span><font color="#FF0000">}</font></span>
+
-
+
-
  <span>''<span><font color="#9A1900">/* Get a pointer to the value from changed entry. */</font></span>''</span>
+
-
  value <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">gconf_entry_get_value</font></span>'''</span><span><font color="#990000">(</font></span>entry<span><font color="#990000">);</font></span>
+
-
+
-
  <span>''<span><font color="#9A1900">/* If we get a NULL as the value, it means that the value either has</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">    not been set, or is at default. As a precaution we assume that</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">    this cannot ever happen, and will abort if it does.</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">    NOTE: A real program should be more resilient in this case, but</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">          the problem is: what is the correct action in this case?</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">          This is not always simple to decide.</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">    NOTE: You can trip this assert with 'make primekeys', since that</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">          will first remove all the keys (which causes the CB to</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">          be invoked, and abort here). */</font></span>''</span>
+
-
  <span>'''<span><font color="#000000">g_assert</font></span>'''</span><span><font color="#990000">(</font></span>value <span><font color="#990000"><nowiki>!=</nowiki></font></span> NULL<span><font color="#990000">);</font></span>
+
-
+
-
  <span>''<span><font color="#9A1900">/* Check that it looks like a valid type for the value. */</font></span>''</span>
+
-
  <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">(!</font></span><span>'''<span><font color="#000000">GCONF_VALUE_TYPE_VALID</font></span>'''</span><span><font color="#990000">(</font></span>value<span><font color="#990000">-&gt;</font></span>type<span><font color="#990000">))</font></span> <span><font color="#FF0000">{</font></span>
+
-
    <span>'''<span><font color="#000000">g_error</font></span>'''</span><span><font color="#990000">(</font></span>PROGNAME <span><font color="#FF0000">": Invalid type for gconfvalue!</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">);</font></span>
+
-
  <span><font color="#FF0000">}</font></span>
+
-
+
-
  <span>''<span><font color="#9A1900">/* Create a human readable representation of the value. Since this</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">    will be a new string created just for us, we'll need to be</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">    careful and free it later. */</font></span>''</span>
+
-
  strValue <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">gconf_value_to_string</font></span>'''</span><span><font color="#990000">(</font></span>value<span><font color="#990000">);</font></span>
+
-
+
-
  <span>''<span><font color="#9A1900">/* Print out a message (depending on which of the tracked keys</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">    change. */</font></span>''</span>
+
-
  <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">(</font></span><span>'''<span><font color="#000000">strcmp</font></span>'''</span><span><font color="#990000">(</font></span>keyname<span><font color="#990000">,</font></span> SERVICE_KEY_CONNECTION<span><font color="#990000">)</font></span> <span><font color="#990000"><nowiki>==</nowiki></font></span> <span><font color="#993399">0</font></span><span><font color="#990000">)</font></span> <span><font color="#FF0000">{</font></span>
+
-
    <span>'''<span><font color="#000000">g_print</font></span>'''</span><span><font color="#990000">(</font></span>PROGNAME <span><font color="#FF0000">": Connection type setting changed: [%s]</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">,</font></span>
+
-
            strValue<span><font color="#990000">);</font></span>
+
-
  <span><font color="#FF0000">}</font></span> <span>'''<span><font color="#0000FF">else</font></span>'''</span> <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">(</font></span><span>'''<span><font color="#000000">strcmp</font></span>'''</span><span><font color="#990000">(</font></span>keyname<span><font color="#990000">,</font></span> SERVICE_KEY_CONNECTIONPARAMS<span><font color="#990000">)</font></span> <span><font color="#990000"><nowiki>==</nowiki></font></span> <span><font color="#993399">0</font></span><span><font color="#990000">)</font></span> <span><font color="#FF0000">{</font></span>
+
-
    <span>'''<span><font color="#000000">g_print</font></span>'''</span><span><font color="#990000">(</font></span>PROGNAME <span><font color="#FF0000">": Connection params setting changed: [%s]</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">,</font></span>
+
-
            strValue<span><font color="#990000">);</font></span>
+
-
  <span><font color="#FF0000">}</font></span> <span>'''<span><font color="#0000FF">else</font></span>'''</span> <span><font color="#FF0000">{</font></span>
+
-
    <span>'''<span><font color="#000000">g_print</font></span>'''</span><span><font color="#990000">(</font></span>PROGNAME <span><font color="#FF0000">":Unknown key: %s (value: [%s])</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">,</font></span> keyname<span><font color="#990000">,</font></span>
+
-
            strValue<span><font color="#990000">);</font></span>
+
-
  <span><font color="#FF0000">}</font></span>
+
-
+
-
  <span>''<span><font color="#9A1900">/* Free the string representation of the value. */</font></span>''</span>
+
-
  <span>'''<span><font color="#000000">g_free</font></span>'''</span><span><font color="#990000">(</font></span>strValue<span><font color="#990000">);</font></span>
+
-
+
-
  <span>'''<span><font color="#000000">g_print</font></span>'''</span><span><font color="#990000">(</font></span>PROGNAME <span><font color="#FF0000">": keyChangeCallback done.</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">);</font></span>
+
-
<span><font color="#FF0000">}</font></span></tt>
+
-
The complications in the above code rise from the fact that GConf communicates values using a GValue structure, which can carry values of any simple type. Since GConf (or the user for that matter) cannot be completely trusted to return the correct type for the value, it is necessary to be extra careful, and not assume that it will always be a string. GConf also supports "default" values, which are communicated to the application using NULLs, so it is also necessary to guard against that. Especially since the application does not provide a schema for the configuration space that would contain the default values.
+
  /* This will hold the pointer to the value. */
 +
  const GConfValue* value = NULL;
 +
  /* This will hold a pointer to the name of the key that changed. */
 +
  const gchar* keyname = NULL;
 +
  /* This will hold a dynamically allocated human-readable
 +
    representation of the changed value. */
 +
  gchar* strValue = NULL;
-
The next step is to build and test the program. The program will be started on the background, so that gconftool-2 can be used to see how the program reacts to changing parameters:
+
  g_print(PROGNAME ": keyChangeCallback invoked.\n");
-
<nowiki>
+
  /* Get a pointer to the key (this is not a copy). */
-
[sbox-DIABLO_X86: ~/gconf-listener] &gt; make
+
  keyname = gconf_entry_get_key(entry);
-
cc -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include -I/usr/include/gconf/2 \
+
-
    -I/usr/include/dbus-1.0 -I/usr/lib/dbus-1.0/include -Wall -g \
+
-
    -DPROGNAME=\"gconf-key-watch\" gconf-key-watch.c -o gconf-key-watch \
+
-
    -lgconf-2 -ldbus-glib-1 -ldbus-1 -lgobject-2.0 -lglib-2.0
+
-
[sbox-DIABLO_X86: ~/gconf-listener] &gt; run-standalone.sh ./gconf-key-watch &amp;
+
-
[2] 21385
+
-
gconf-key-watch:main Starting.
+
-
gconf-key-watch:main GType and GConfClient initialized.
+
-
gconf-key-watch: Value for key '/apps/Maemo/platdev_ex/connection'
+
-
  is set to 'btcomm0'
+
-
gconf-key-watch: Value for key '/apps/Maemo/platdev_ex/connectionparams'
+
-
  is set to '9600,8,N,1'
+
-
gconf-key-watch:main Added /apps/Maemo/platdev_ex.
+
-
gconf-key-watch:main CB registered &amp; starting main loop
+
-
[sbox-DIABLO_X86: ~/gconf-listener] &gt; gconftool-2 --set --type string \
+
-
  /apps/Maemo/platdev_ex/connection ttyS0
+
-
gconf-key-watch: keyChangeCallback invoked.
+
-
gconf-key-watch: Connection type setting changed: [ttyS0]
+
-
gconf-key-watch: keyChangeCallback done.
+
-
[sbox-DIABLO_X86: ~/gconf-listener] &gt; gconftool-2 --set --type string \
+
-
  /apps/Maemo/platdev_ex/connectionparams ''
+
-
gconf-key-watch: keyChangeCallback invoked.
+
-
gconf-key-watch: Connection params setting changed: []
+
-
gconf-key-watch: keyChangeCallback done.
+
-
</nowiki>
+
 +
  /* It will be quite fatal if after change we cannot retrieve even
 +
    the name for the gconf entry, so we error out here. */
 +
  if (keyname == NULL) {
 +
    g_error(PROGNAME ": Couldn't get the key name!\n");
 +
    /* Application terminates. */
 +
  }
 +
 +
  /* Get a pointer to the value from changed entry. */
 +
  value = gconf_entry_get_value(entry);
 +
 +
  /* If we get a NULL as the value, it means that the value either has
 +
    not been set, or is at default. As a precaution we assume that
 +
    this cannot ever happen, and will abort if it does.
 +
    NOTE: A real program should be more resilient in this case, but
 +
          the problem is: what is the correct action in this case?
 +
          This is not always simple to decide.
 +
    NOTE: You can trip this assert with 'make primekeys', since that
 +
          will first remove all the keys (which causes the CB to
 +
          be invoked, and abort here). */
 +
  g_assert(value != NULL);
 +
 +
  /* Check that it looks like a valid type for the value. */
 +
  if (!GCONF_VALUE_TYPE_VALID(value->type)) {
 +
    g_error(PROGNAME ": Invalid type for gconfvalue!\n");
 +
  }
 +
 +
  /* Create a human readable representation of the value. Since this
 +
    will be a new string created just for us, we'll need to be
 +
    careful and free it later. */
 +
  strValue = gconf_value_to_string(value);
 +
 +
  /* Print out a message (depending on which of the tracked keys
 +
    change. */
 +
  if (strcmp(keyname, SERVICE_KEY_CONNECTION) == 0) {
 +
    g_print(PROGNAME ": Connection type setting changed: [%s]\n",
 +
            strValue);
 +
  } else if (strcmp(keyname, SERVICE_KEY_CONNECTIONPARAMS) == 0) {
 +
    g_print(PROGNAME ": Connection params setting changed: [%s]\n",
 +
            strValue);
 +
  } else {
 +
    g_print(PROGNAME ":Unknown key: %s (value: [%s])\n", keyname,
 +
            strValue);
 +
  }
 +
 +
  /* Free the string representation of the value. */
 +
  g_free(strValue);
 +
 +
  g_print(PROGNAME ": keyChangeCallback done.\n");
 +
}
 +
</source>
 +
 +
The complications in the above code rise from the fact that GConf communicates values using a <code>GValue</code> structure, which can carry values of any simple type. Since GConf (or the user for that matter) cannot be completely trusted to return the correct type for the value, it is necessary to be extra careful, and not assume that it will always be a string. GConf also supports "default" values, which are communicated to the application using <code>NULL</code>s, so it is also necessary to guard against that. Especially since the application does not provide a schema for the configuration space that would contain the default values.
 +
 +
The next step is to build and test the program. The program will be started on the background, so that <code>gconftool-2</code> can be used to see how the program reacts to changing parameters:
 +
 +
<pre>
 +
[sbox-DIABLO_X86: ~/gconf-listener] > make
 +
cc -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include -I/usr/include/gconf/2 \
 +
  -I/usr/include/dbus-1.0 -I/usr/lib/dbus-1.0/include -Wall -g \
 +
  -DPROGNAME=\"gconf-key-watch\" gconf-key-watch.c -o gconf-key-watch \
 +
  -lgconf-2 -ldbus-glib-1 -ldbus-1 -lgobject-2.0 -lglib-2.0
 +
[sbox-DIABLO_X86: ~/gconf-listener] &gt; run-standalone.sh ./gconf-key-watch &amp;
 +
[2] 21385
 +
gconf-key-watch:main Starting.
 +
gconf-key-watch:main GType and GConfClient initialized.
 +
gconf-key-watch: Value for key '/apps/Maemo/platdev_ex/connection'
 +
is set to 'btcomm0'
 +
gconf-key-watch: Value for key '/apps/Maemo/platdev_ex/connectionparams'
 +
is set to '9600,8,N,1'
 +
gconf-key-watch:main Added /apps/Maemo/platdev_ex.
 +
gconf-key-watch:main CB registered & starting main loop
 +
[sbox-DIABLO_X86: ~/gconf-listener] > gconftool-2 --set --type string \
 +
  /apps/Maemo/platdev_ex/connection ttyS0
 +
gconf-key-watch: keyChangeCallback invoked.
 +
gconf-key-watch: Connection type setting changed: [ttyS0]
 +
gconf-key-watch: keyChangeCallback done.
 +
[sbox-DIABLO_X86: ~/gconf-listener] > gconftool-2 --set --type string \
 +
  /apps/Maemo/platdev_ex/connectionparams ''
 +
gconf-key-watch: keyChangeCallback invoked.
 +
gconf-key-watch: Connection params setting changed: []
 +
gconf-key-watch: keyChangeCallback done.
 +
</pre>
The latter change is somewhat problematic (which the code needs to deal with as well). It is necessary to decide how to react to values that are of the correct type, but with senseless values. GConf in itself does not provide syntax checking for the values, or any semantic checking support. It is recommended that configuration changes will only be reacted to when they pass some internal (to the application) logic that will check their validity, both at syntax level and also at semantic level.
The latter change is somewhat problematic (which the code needs to deal with as well). It is necessary to decide how to react to values that are of the correct type, but with senseless values. GConf in itself does not provide syntax checking for the values, or any semantic checking support. It is recommended that configuration changes will only be reacted to when they pass some internal (to the application) logic that will check their validity, both at syntax level and also at semantic level.
Line 831: Line 848:
One option would also be resetting the value back to a valid value, whenever the program detects an invalid value set attempt. This will lead to a lot of problems, if the value is set programmatically from another program that will obey the same rule, so it is not advisable. Quitting the program on invalid values is also an option that should not be used, since the restricted environment does not provide many ways to inform the user that the program has quit.
One option would also be resetting the value back to a valid value, whenever the program detects an invalid value set attempt. This will lead to a lot of problems, if the value is set programmatically from another program that will obey the same rule, so it is not advisable. Quitting the program on invalid values is also an option that should not be used, since the restricted environment does not provide many ways to inform the user that the program has quit.
-
An additional possible problem is having multiple keys that are all "related" to a single setting or action. It is necessary to decide, how to deal with changes across multiple GConf keys that are related, yet changed separately. The two key example code demonstrates the inherent problem: should the server re-initialize the (theoretic) connection, when the connection key is changed, or when the connectionparams key is changed? If the connection is re-initialized when either of the keys is changed, then the connection will be re-initialized twice when both are changed "simultaneously" (user presses "Apply" on a settings dialog, or gconftool-2 is run and sets both keys). It is easy to see how this might be an even larger problem, if instead of two keys, there were five per connection. GConf and the GConfClient GObject wrapper that has been used here do not support "configuration set transactions", which would allow setting and processing multiple related keys using an atomic model. The example program ignores this issue completely.
+
An additional possible problem is having multiple keys that are all "related" to a single setting or action. It is necessary to decide, how to deal with changes across multiple GConf keys that are related, yet changed separately. The two key example code demonstrates the inherent problem: should the server re-initialize the (theoretic) connection, when the connection key is changed, or when the connectionparams key is changed? If the connection is re-initialized when either of the keys is changed, then the connection will be re-initialized twice when both are changed "simultaneously" (user presses "Apply" on a settings dialog, or gconftool-2 is run and sets both keys). It is easy to see how this might be an even larger problem, if instead of two keys, there were five per connection. GConf and the <code>GConfClient</code> <code>GObject</code> wrapper that has been used here do not support "configuration set transactions", which would allow setting and processing multiple related keys using an atomic model. The example program ignores this issue completely.
The next step is to test how the program (which is still running) reacts to other problematic situations:
The next step is to test how the program (which is still running) reacts to other problematic situations:
-
[sbox-DIABLO_X86: ~/gconf-listener] &gt; gconftool-2 --set --type int \
+
<pre>
-
  /apps/Maemo/platdev_ex/connectionparams 5
+
[sbox-DIABLO_X86: ~/gconf-listener] > gconftool-2 --set --type int \
-
gconf-key-watch: keyChangeCallback invoked.
+
  /apps/Maemo/platdev_ex/connectionparams 5
-
gconf-key-watch: Connection params setting changed: [5]
+
gconf-key-watch: keyChangeCallback invoked.
-
gconf-key-watch: keyChangeCallback done.
+
gconf-key-watch: Connection params setting changed: [5]
-
[sbox-DIABLO_X86: ~/gconf-listener] &gt; gconftool-2 --set --type boolean \
+
gconf-key-watch: keyChangeCallback done.
-
  /apps/Maemo/platdev_ex/connectionparams true
+
[sbox-DIABLO_X86: ~/gconf-listener] > gconftool-2 --set --type boolean \
-
gconf-key-watch: keyChangeCallback invoked.
+
  /apps/Maemo/platdev_ex/connectionparams true
-
gconf-key-watch: Connection params setting changed: [true]
+
gconf-key-watch: keyChangeCallback invoked.
-
gconf-key-watch: keyChangeCallback done.
+
gconf-key-watch: Connection params setting changed: [true]
-
 
+
gconf-key-watch: keyChangeCallback done.
 +
</pre>
The next example removes the configuration keys, while the program is still running:
The next example removes the configuration keys, while the program is still running:
-
[sbox-DIABLO_X86: ~/gconf-listener] &gt; make clearkeys
+
<pre>
-
gconf-key-watch: keyChangeCallback invoked.
+
[sbox-DIABLO_X86: ~/gconf-listener] > make clearkeys
-
gconf-key-watch[21403]: GLIB ERROR ** default -  
+
gconf-key-watch: keyChangeCallback invoked.
-
  file gconf-key-watch.c: line 129 (keyChangeCallback):
+
gconf-key-watch[21403]: GLIB ERROR ** default -  
-
  assertion failed: (value != NULL)
+
  file gconf-key-watch.c: line 129 (keyChangeCallback):
-
aborting...
+
  assertion failed: (value != NULL)
-
/usr/bin/run-standalone.sh: line 11: 21403 Aborted (core dumped) "$@"
+
aborting...
-
[1]+  Exit 134 run-standalone.sh ./gconf-key-watch
+
/usr/bin/run-standalone.sh: line 11: 21403 Aborted (core dumped) "$@"
 +
[1]+  Exit 134 run-standalone.sh ./gconf-key-watch
 +
</pre>
 +
Since the code (in the callback function) contains an assert that checks for non-<code>NULL</code> values, it will abort when the key is removed, and that causes the value to go to <code>NULL</code>. So the abortion in the above case is expected.
-
Since the code (in the callback function) contains an assert that checks for non-NULL values, it will abort when the key is removed, and that causes the value to go to NULL. So the abortion in the above case is expected.
+
[[Category:Development]]
 +
[[Category:Documentation]]
 +
[[Category:Fremantle]]

Latest revision as of 13:38, 17 August 2010

GConf is used by the GNOME desktop environment for storing shared configuration settings for the desktop and applications. The daemon process GConfd follows the changes in the database. When a change occurs in the database, the daemon applies the new settings to the applications using them. For example, the control panel application uses GConf.

If settings are used only by a single application, GLib utility for .ini style files should be used instead. Applications should naturally have working default settings. Settings should be saved only when the user changes them.

This chapter uses the following examples:

Contents

[edit] GConf Basics

GConf is a system for GNOME applications to store settings into a database system in a centralized manner. The aim of the GConf library is to provide applications a consistent view of the database access functions, as well as to provide tools for system administrators to enable them to distribute software settings in a centralized manner (across multiple computers).

The GConf "database" may consist of multiple databases (configured by the system administrator), but normally there will be at least one database engine that uses XML to store settings. This keeps the database still in a human readable form (as opposed to binary), and allows some consistency checks via schema verifications.

The interface for the client (program that uses GConf to store its settings) is always the same, irrespective of the database back-end (the client does not see this).

What makes GConf interesting, is its capability of notifying running clients that their settings have been changed by some other process than themselves. This allows for the clients to react soon (not quite real-time), and this leads to a situation where a user will change, for example, the GNOME HTTP proxy settings, and clients that are interested in that setting will get a notification (via a callback function) that the setting has changed. The clients will then read the new setting and modify their data structures to take the new setting into account.

[edit] Using GConf

The GConf model consists of two parts: the GConf client library (which will be used here) and the GConf server daemon that is the guardian and reader/writer of the back-end databases. In a regular GNOME environment, the client communicates with the server either by using the Bonobo library (lightweight object IPC mechanism) or D-Bus.

As Bonobo is not used in Maemo (it is quite heavy, even if lightweight), the client will communicate with the server using D-Bus. This also allows the daemon to be started on demand, when there is at least one client wanting to use that service (this is a feature of D-Bus). The communication mechanism is encapsulated by the GConf client library, and as such, will be transparent.

In order to read or write the preference database, it is necessary to decide on the key to use to access the application values. The database namespace is hierarchical, and uses the '/'-character to implement this hierarchy, starting from a root location similar to UNIX file system namespace.

Each application will use its own "directory" under /apps/Maemo/appname/. N.B. Even when the word "directory" is seen in connection to GConf, one has to be careful to distinguish real directories from preference namespaces inside the GConf namespace. The /apps/Maemo/appname/ above is in the GConf namespace, so there will not actually be a physical directory called /apps/ on a system.

The keys should be named according to the platform guidelines. The current guideline is that each application should store its configuration keys under /apps/Maemo/appname/, where appname is the name of the application. There is no central registry on the names in use currently, so names should be selected carefully. Key names should all be lowercase, with underscore used to separate multiple words. Also, ASCII should be used, since GConf does not support localization for key names (it does for key values, but that is not covered in this material).

GConf values are typed, which means that it is necessary to select the type for the data that the key is supposed to hold.

The following types are supported for values in GConf:

  • gint (32-bit signed)
  • gboolean
  • gchar (ASCII/ISO 8859-1/UTF-8 C string)
  • gfloat (with the limitation that the resolution is not guaranteed nor specified by GConf because of portability issues)
  • a list of values of one type
  • a pair of values, each having their own type (useful for storing "mapping" data)

What is missing from the above list is storing binary data (for a good reason). The type system is also fairly limited. This is on purpose, so that complex configurations (like the Apache HTTP daemon uses, or Samba) are not attempted using GConf.

There is a diagnostic and administration tool called gconftool-2 that is also available in the SDK. It can be used to set and unset keys, as well as display their current contents.

Some examples of using gconftool-2 (on the SDK):

  • Displaying the contents of all keys stored under /apps/ (listing cut for brevity)
[sbox-DIABLO_X86: ~] > run-standalone.sh gconftool-2 -R /apps
 /apps/osso:
  /apps/osso/inputmethod:
   launch_finger_kb_on_select = true
   input_method_plugin = himExample_vkb
   available_languages = [en_GB]
   use_finger_kb = true
   /apps/osso/inputmethod/hildon-im-languages:
    language-0 = en_GB
    current = 0
    language-1 =
    list = []
  /apps/osso/fontconfig:
   font_scaling_factor = Schema (type: `float' list_type:
    '*invalid*' car_type: '*invalid*' cdr_type: '*invalid*'
    locale: `C')
  /apps/osso/apps:
   /apps/osso/apps/controlpanel:
    groups = [copa_ia_general,copa_ia_connectivity,
              copa_ia_personalisation]
    icon_size = false
    group_ids = [general,connectivity,personalisation]
  /apps/osso/osso:
   /apps/osso/osso/thumbnailers:
    /apps/osso/osso/thumbnailers/audio@x-mp3:
     command = /usr/bin/hildon-thumb-libid3
    /apps/osso/osso/thumbnailers/audio@x-m4a:
     command = /usr/bin/hildon-thumb-libid3
    /apps/osso/osso/thumbnailers/audio@mp3:
     command = /usr/bin/hildon-thumb-libid3
    /apps/osso/osso/thumbnailers/audio@x-mp2:
     command = /usr/bin/hildon-thumb-libid3
  • Creating and setting the value to a new key.
[sbox-DIABLO_X86: ~] > run-standalone.sh gconftool-2  \
 --set /apps/Maemo/testing/testkey --type=int 5
  • Listing all keys under the namespace /apps/Maemo/testing.
[sbox-DIABLO_X86: ~] > run-standalone.sh gconftool-2  \
 -R /apps/Maemo/testing
 testkey = 5
  • Removing the last key will also remove the key directory.
[sbox-DIABLO_X86: ~] > run-standalone.sh gconftool-2  \
 --unset /apps/Maemo/testing/testkey
[sbox-DIABLO_X86: ~] > run-standalone.sh gconftool-2  \
 -R /apps/Maemo/testing
  • Removing whole key hierarchies is also possible.
[sbox-DIABLO_X86: ~] > run-standalone.sh gconftool-2  \
 --recursive-unset /apps/Maemo/testing

For more detailed information, please see Gconf API documentation.

[edit] Using GConf to read and write preferences

The example is required to:

  • Store the color that the user selects when the color button (in the toolbar) is used.
  • Load the color preference on application startup.

Even if GConf concepts seem to be logical, it can be seen that using GConf will require one to learn some new things (e.g. the GError-object). Since the GConf client code is in its own library, the relevant compiler flags and library options need to be added again. The pkg-config package name is gconf-2.0 hildon_helloworld-9.c

/**
 * hildon_helloworld-9.c
 *
 * This maemo code example is licensed under a MIT-style license,
 * that can be found in the file called "License" in the same
 * directory as this file.
 * Copyright (c) 2007-2008 Nokia Corporation. All rights reserved.
 *
 * We'll store the color that the user selects into a GConf
 * preference. In fact, we'll have three settings, one for each
 * channel of the color (red, green and blue).
 *
 * Look for lines with "NEW" or "MODIFIED" in them.
 */
 
#include <stdlib.h>
#include <hildon/hildon-program.h>
#include <hildon/hildon-color-button.h>
#include <hildon/hildon-find-toolbar.h>
#include <hildon/hildon-file-chooser-dialog.h>
#include <hildon/hildon-banner.h>
#include <libgnomevfs/gnome-vfs.h>
/* Include the prototypes for GConf client functions (NEW). */
#include <gconf/gconf-client.h>
 
/* The application name -part of the GConf namespace (NEW). */
#define APP_NAME "hildon_hello"
/* This will be the root "directory" for our preferences (NEW). */
#define GC_ROOT  "/apps/Maemo/" APP_NAME "/"
 
  /*... Listing cut for brevity ...*/
 
/**
 * NEW
 *
 * Utility function to store the given color into our application
 * preferences. We could use a list of integers as well, but we'll
 * settle for three separate properties; one for each of RGB
 * channels.
 *
 * The config keys that will be used are 'red', 'green' and 'blue'.
 *
 * NOTE:
 *   We're doing things very non-optimally. If our application would
 *   have multiple preference settings, and we would like to know
 *   when someone will change them (external program, another
 *   instance of our program, etc), we'd have to keep a reference to
 *   the GConf client connection. Listening for changes in
 *   preferences would also require a callback registration, but this
 *   is covered in the "maemo Platform Development" material.
 */
static void confStoreColor(const GdkColor* color) {
 
  /* We'll store the pointer to the GConf connection here. */
  GConfClient* gcClient = NULL;
 
  /* Make sure that no NULLs are passed for the color. GdkColor is
     not a proper GObject, so there is no GDK_IS_COLOR macro. */
  g_assert(color);
 
  g_print("confStoreColor: invoked\n");
 
  /* Open a connection to gconfd-2 (via D-Bus in maemo). The GConf
     API doesn't say whether this function can ever return NULL or
     how it will behave in error conditions. */
  gcClient = gconf_client_get_default();
  /* We make sure that it's a valid GConf-client object. */
  g_assert(GCONF_IS_CLIENT(gcClient));
 
  /* Store the values. */
  if (!gconf_client_set_int(gcClient, GC_ROOT "red", color->red,
                            NULL)) {
    g_warning(" failed to set %s/red to %d\n", GC_ROOT, color->red);
  }
  if (!gconf_client_set_int(gcClient, GC_ROOT "green", color->green,
                            NULL)) {
    g_warning(" failed to set %s/green to %d\n", GC_ROOT,
              color->green);
  }
  if (!gconf_client_set_int(gcClient, GC_ROOT "blue", color->blue,
                            NULL)) {
    g_warning(" failed to set %s/blue to %d\n", GC_ROOT,
              color->blue);
  }
 
  /* Release the GConf client object (with GObject-unref). */
  g_object_unref(gcClient);
  gcClient = NULL;
}
 
/**
 * NEW
 *
 * A utility function to get an integer but also return the status
 * whether the requested key existed or not.
 *
 * NOTE:
 *   It's also possible to use gconf_client_get_int(), but it's not
 *   possible to then know whether they key existed or not, because
 *   the function will return 0 if the key doesn't exist (and if the
 *   value is 0, how could you tell these two conditions apart?).
 *
 * Parameters:
 * - GConfClient: the client object to use
 * - const gchar*: the key
 * - gint*: the address to store the integer to if the key exists
 *
 * Returns:
 * - TRUE: if integer has been updated with a value from GConf.
 *   FALSE: there was no such key or it wasn't an integer.
 */
static gboolean confGetInt(GConfClient* gcClient, const gchar* key,
                           gint* number) {
 
  /* This will hold the type/value pair at some point. */
  GConfValue* val = NULL;
  /* Return flag (tells the caller whether this function wrote behind
     the 'number' pointer or not). */
  gboolean hasChanged = FALSE;
 
  /* Try to get the type/value from the GConf DB.
     NOTE:
       We're using a version of the getter that will not return any
       defaults (if a schema would specify one). Instead, it will
       return the value if one has been set (or NULL).
 
     We're not really interested in errors as this will return a NULL
     in case of missing keys or errors and that is quite enough for
     us. */
  val = gconf_client_get_without_default(gcClient, key, NULL);
  if (val == NULL) {
    /* Key wasn't found, no need to touch anything. */
    g_warning("confGetInt: key %s not found\n", key);
    return FALSE;
  }
 
  /* Check whether the value stored behind the key is an integer. If
     it is not, we issue a warning, but return normally. */
  if (val->type == GCONF_VALUE_INT) {
    /* It's an integer, get it and store. */
    *number = gconf_value_get_int(val);
    /* Mark that we've changed the integer behind 'number'. */
    hasChanged = TRUE;
  } else {
    g_warning("confGetInt: key %s is not an integer\n", key);
  }
 
  /* Free the type/value-pair. */
  gconf_value_free(val);
  val = NULL;
 
  return hasChanged;
}
 
/**
 * NEW
 *
 * Utility function to change the given color into the one that is
 * specified in application preferences.
 *
 * If some key is missing, that channel is left untouched. The
 * function also checks for proper values for the channels so that
 * invalid values are not accepted (guint16 range of GdkColor).
 *
 * Parameters:
 * - GdkColor*: the color structure to modify if changed from prefs.
 *
 * Returns:
 * - TRUE if the color was been changed by this routine.
 *   FALSE if the color wasn't changed (there was an error or the
 *   color was already exactly the same as in the preferences).
 */
static gboolean confLoadCurrentColor(GdkColor* color) {
 
  GConfClient* gcClient = NULL;
  /* Temporary holders for the pref values. */
  gint red = -1;
  gint green = -1;
  gint blue = -1;
  /* Temp variable to hold whether the color has changed. */
  gboolean hasChanged = FALSE;
 
  g_assert(color);
 
  g_print("confLoadCurrentColor: invoked\n");
 
  /* Open a connection to gconfd-2 (via d-bus). */
  gcClient = gconf_client_get_default();
  /* Make sure that it's a valid GConf-client object. */
  g_assert(GCONF_IS_CLIENT(gcClient));
 
  if (confGetInt(gcClient, GC_ROOT "red", &red)) {
    /* We got the value successfully, now clamp it. */
    g_print(" got red = %d, ", red);
    /* We got a value, so let's limit it between 0 and 65535 (the
       legal range for guint16). We use the CLAMP macro from GLib for
       this. */
    red = CLAMP(red, 0, G_MAXUINT16);
    g_print("after clamping = %d\n", red);
    /* Update & mark that at least this component changed. */
    color->red = (guint16)red;
    hasChanged = TRUE;
  }
  /* Repeat the same logic for the green component. */
  if (confGetInt(gcClient, GC_ROOT "green", &green)) {
    g_print(" got green = %d, ", green);
    green = CLAMP(green, 0, G_MAXUINT16);
    g_print("after clamping = %d\n", green);
    color->green = (guint16)green;
    hasChanged = TRUE;
  }
  /* Repeat the same logic for the last component (blue). */
  if (confGetInt(gcClient, GC_ROOT "blue", &blue)) {
    g_print(" got blue = %d, ", blue);
    blue = CLAMP(blue, 0, G_MAXUINT16);
    g_print("after clamping = %d\n", blue);
    color->blue = (guint16)blue;
    hasChanged = TRUE;
  }
 
  /* Release the client object (with GObject-unref). */
  g_object_unref(gcClient);
  gcClient = NULL;
 
  /* Return status if the color was been changed by this routine. */
  return hasChanged;
}
 
/**
 * MODIFIED
 *
 * Invoked when the user selects a color (or will cancel the dialog).
 *
 * Will also write the color to preferences (GConf) each time the
 * color changes. We'll compare whether it has really changed (to
 * avoid writing to GConf is nothing really changed).
 */
static void cbActionColorChanged(HildonColorButton* colorButton,
                                 ApplicationState* app) {
 
  /* Local variables that we'll need to handle the change (NEW). */
  gboolean hasChanged = FALSE;
  GdkColor newColor = {};
  GdkColor* curColor = NULL;
 
  g_assert(app != NULL);
 
  g_print("cbActionColorChanged invoked\n");
  /* Retrieve the new color from the color button (NEW). */
  hildon_color_button_get_color(colorButton, &newColor);
  /* Just an alias to save some typing (could also use
     app->currentColor) (NEW). */
  curColor = &app->currentColor;
 
  /* Check whether the color really changed (NEW). */
  if ((newColor.red   != curColor->red) ||
      (newColor.green != curColor->green) ||
      (newColor.blue  != curColor->blue)) {
    hasChanged = TRUE;
  }
  if (!hasChanged) {
    g_print(" color not really changed\n");
    return;
  }
  /* Color really changed, store to preferences (NEW). */
  g_print(" color changed, storing into preferences.. \n");
  confStoreColor(&newColor);
  g_print(" done.\n");
 
  /* Update the changed color into the application state. */
  app->currentColor = newColor;
}
 
  /*... Listing cut for brevity ...*/
 
/**
 * MODIFIED
 *
 * The color of the color button will be loaded from the application
 * preferences (or keep the default if preferences have no setting).
 */
static GtkWidget* buildToolbar(ApplicationState* app) {
 
  GtkToolbar*  toolbar = NULL;
  GtkToolItem* tbOpen = NULL;
  GtkToolItem* tbSave = NULL;
  GtkToolItem* tbSep = NULL;
  GtkToolItem* tbFind = NULL;
  GtkToolItem* tbColorButton = NULL;
  GtkWidget*   colorButton = NULL;
 
  g_assert(app != NULL);
 
  tbOpen = gtk_tool_button_new_from_stock(GTK_STOCK_OPEN);
  tbSave = gtk_tool_button_new_from_stock(GTK_STOCK_SAVE);
  tbSep  = gtk_separator_tool_item_new();
  tbFind = gtk_tool_button_new_from_stock(GTK_STOCK_FIND);
 
  tbColorButton = gtk_tool_item_new();
  colorButton = hildon_color_button_new();
  /* Copy the color from the color button into the application state.
     This is done to detect whether the color in preferences matches
     the default color or not (NEW). */
  hildon_color_button_get_color(HILDON_COLOR_BUTTON(colorButton),
                                &app->currentColor);
  /* Load preferences and change the color if necessary. */
  g_print("buildToolbar: loading color pref.\n");
  if (confLoadCurrentColor(&app->currentColor)) {
    g_print(" color not same as default one\n");
    hildon_color_button_set_color(HILDON_COLOR_BUTTON(colorButton),
                                  &app->currentColor);
  } else {
    g_print(" loaded color same as default\n");
  }
  gtk_container_add(GTK_CONTAINER(tbColorButton), colorButton);
 
  /*... Listing cut for brevity ...*/
 
}

Since the graphical appearance of the program does not change (except that the ColorButton will display the correct initial color), a look will be taken at the stdout display of the program.

[sbox-DIABLO_X86: ~/appdev] > run-standalone.sh ./hildon_helloworld-9
buildToolbar: loading color pref.
confLoadCurrentColor: invoked
hildon_helloworld-9[19840]: GLIB WARNING ** default - 
 confGetInt: key /apps/Maemo/hildon_hello/red not found
hildon_helloworld-9[19840]: GLIB WARNING ** default - 
 confGetInt: key /apps/Maemo/hildon_hello/green not found
hildon_helloworld-9[19840]: GLIB WARNING ** default - 
 confGetInt: key /apps/Maemo/hildon_hello/blue not found
 loaded color same as default
main: calling gtk_main
cbActionMainToolbarToggle invoked
cbActionColorChanged invoked
 color changed, storing into preferences..
confStoreColor: invoked
 done.
main: returned from gtk_main and exiting with success

When running the program for the first time, warnings about the missing keys can be expected (since the values were not present in GConf).

Run the program again and exit:

[sbox-DIABLO_X86: ~/appdev] > run-standalone.sh ./hildon_helloworld-9
buildToolbar: loading color pref.
confLoadCurrentColor: invoked
 got red = 65535, after clamping = 65535
 got green = 65535, after clamping = 65535
 got blue = 0, after clamping = 0
 color not same as default one
main: calling gtk_main
main: returned from gtk_main and exiting with success

The next step is to remove one key (red), and run the program again (this is to test and verify that the logic works):

 
[sbox-DIABLO_X86: ~/appdev] > run-standalone.sh gconftool-2  \
 --unset /apps/Maemo/hildon_hello/red
[sbox-DIABLO_X86: ~/appdev] > run-standalone.sh gconftool-2  \
 -R /apps/Maemo/hildon_hello
 green = 65535
 blue = 0
[sbox-DIABLO_X86: ~/appdev] > run-standalone.sh ./hildon_helloworld-9
buildToolbar: loading color pref.
confLoadCurrentColor: invoked
hildon_helloworld-9[19924]: GLIB WARNING ** default -
 confGetInt: key /apps/Maemo/hildon_hello/red not found
 got green = 65535, after clamping = 65535
 got blue = 0, after clamping = 0
 color not same as default one
main: calling gtk_main
main: returned from gtk_main and exiting with success

[edit] Asynchronous GConf

[edit] Listening to changes in GConf

Now it is time to see how to extend GConf to be more suitable in asynchronous work, and especially when implementing services.

When the configuration needs for the service are simple, and reacting to configuration changes in "realtime" is desired, it is advisable to use GConf. Also, people tend to use GConf when they are too lazy to write their own configuration file parsers (although there is a simple one in GLib), or too lazy to write the GUI part to change the settings. This example program will simulate the first case, and react to changes in a subset of GConf configuration name space when the changes happen.

The application will be interested in two string values; one to set the device to use for communication (connection), and the other to set the communication parameters for the device (connectionparams). Since this example will be concentrating on just the change notifications, the program logic is simplified by omitting the proper set-up code in the program. This means that it is necessary to set up some values to the GConf keys prior to running the program. For this, gconftool-2 will be used, and a target has been prepared in the Makefile just for this (see section GNU Make and Makefiles if necessary): gconf-listener/Makefile

# Define a variable for this so that the GConf root may be changed
gconf_root := /apps/Maemo/platdev_ex
 
# ... Listing cut for brevity ...
 
# This will setup the keys into default values.
# It will first do a clear to remove any existing keys.
primekeys: clearkeys
        gconftool-2 -set -type string \
                    $(gconf_root)/connection btcomm0
        gconftool-2 -set -type string \
                    $(gconf_root)/connectionparams 9600,8,N,1
 
# Remove all application keys
clearkeys:
        @gconftool-2 -recursive-unset $(gconf_root)
 
# Dump all application keys
dumpkeys:
        @echo Keys under $(gconf_root):
        @gconftool-2 -recursive-list $(gconf_root)

The next step is to prepare the keyspace by running the primekeys target, and to verify that it succeeds by running the dumpkeys target:

 [sbox-DIABLO_X86: ~/gconf-listener] > make primekeys
 gconftool-2 --set --type string \
             /apps/Maemo/platdev_ex/connection btcomm0
 gconftool-2 --set --type string \
             /apps/Maemo/platdev_ex/connectionparams 9600,8,N,1
 [sbox-DIABLO_X86: ~/gconf-listener] > make dumpkeys
 Keys under /apps/Maemo/platdev_ex:
  connectionparams = 9600,8,N,1
  connection = btcomm0

[edit] Implementing Notifications on Changes in GConf

The first step here is to take care of the necessary header information. The GConf namespace settings have been all implemented using cpp macros, so that one can easily change the prefix of the name space if required later on. gconf-listener/gconf-key-watch.c

#include <glib.h>
#include <gconf/gconf-client.h>
#include <string.h> /* strcmp */
 
/* As per maemo Coding Style and Guidelines document, we use the
   /apps/Maemo/ -prefix.
   NOTE: There is no central registry (as of this moment) that you
         could check that your application name doesn't collide with
         other application names, so caution is advised! */
#define SERVICE_GCONF_ROOT "/apps/Maemo/platdev_ex"
 
/* We define the names of the keys symbolically so that we may change
   them later if necessary, and so that the GConf "root directory" for
   our application will be automatically prefixed to the paths. */
#define SERVICE_KEY_CONNECTION \
        SERVICE_GCONF_ROOT "/connection"
#define SERVICE_KEY_CONNECTIONPARAMS \
        SERVICE_GCONF_ROOT "/connectionparams"

The main starts innocently enough, by creating a GConf client object (that encapsulates the connection to the GConf daemon), and then displays the two values on output: gconf-listener/gconf-key-watch.c

int main (int argc, char** argv) {
  /* Will hold reference to the GConfClient object. */
  GConfClient* client = NULL;
  /* Initialize this to NULL so that we'll know whether an error
     occurred or not (and we don't have an existing GError object
     anyway at this point). */
  GError* error = NULL;
  /* This will hold a reference to the mainloop object. */
  GMainLoop* mainloop = NULL;
 
  g_print(PROGNAME ":main Starting.\n");
 
  /* Must be called to initialize GType system. The API reference for
     gconf_client_get_default() insists.
     NOTE: Using gconf_init() is deprecated! */
  g_type_init();
 
  /* Create a new mainloop structure that we'll use. Use default
     context (NULL) and set the 'running' flag to FALSE. */
  mainloop = g_main_loop_new(NULL, FALSE);
  if (mainloop == NULL) {
    g_error(PROGNAME ": Failed to create mainloop!\n");
  }
 
  /* Create a new GConfClient object using the default settings. */
  client = gconf_client_get_default();
  if (client == NULL) {
    g_error(PROGNAME ": Failed to create GConfClient!\n");
  }
 
  g_print(PROGNAME ":main GType and GConfClient initialized.\n");
 
  /* Display the starting values for the two keys. */
  dispStringKey(client, SERVICE_KEY_CONNECTION);
  dispStringKey(client, SERVICE_KEY_CONNECTIONPARAMS);

The dispStringKey utility is rather simple as well, building on the GConf material that was covered in the previous section: gconf-listener/gconf-key-watch.c

/**
 * Utility to retrieve a string key and display it.
 * (Just as a small refresher on the API.)
 */
static void dispStringKey(GConfClient* client,
                          const gchar* keyname) {
 
  /* This will hold the string value of the key. It will be
     dynamically allocated for us, so we need to release it ourselves
     when done (before returning). */
  gchar* valueStr = NULL;
 
  /* We're not interested in the errors themselves (the last
     parameter), but the function will return NULL if there is one,
     so we just end in that case. */
  valueStr = gconf_client_get_string(client, keyname, NULL);
 
  if (valueStr == NULL) {
    g_error(PROGNAME ": No string value for %s. Quitting\n", keyname);
    /* Application terminates. */
  }
 
  g_print(PROGNAME ": Value for key '%s' is set to '%s'\n",
          keyname, valueStr);
 
  /* Normally one would want to use the value for something beyond
     just displaying it, but since this code doesn't, we release the
     allocated value string. */
  g_free(valueStr);
}

Next, the GConf client is told to attach itself to a specific name space part that this example is going to operate with: gconf-listener/gconf-key-watch.c

/**
 * Register directory to watch for changes. This will then tell
 * GConf to watch for changes in this namespace, and cause the
 * "value-changed"-signal to be emitted. We won't be using that
 * mechanism, but will opt to a more modern (and potentially more
 * scalable solution). The directory needs to be added to the
 * watch list in either case.
 *
 * When adding directories, you can sometimes optimize your program
 * performance by asking GConfClient to preload some (or all) keys
 * under a specific directory. This is done via the preload_type
 * parameter (we use GCONF_CLIENT_PRELOAD_NONE below). Since our
 * program will only listen for changes, we don't want to use extra
 * memory to keep the keys cached.
 *
 * Parameters:
 * - client: GConf-client object
 * - SERVICEPATH: the name of the GConf namespace to follow
 * - GCONF_CLIENT_PRELOAD_NONE: do not preload any of contents
 * - error: where to store the pointer to allocated GError on
 *          errors.
 */
gconf_client_add_dir(client,
                     SERVICE_GCONF_ROOT,
                     GCONF_CLIENT_PRELOAD_NONE,
                     &error);
 
if (error != NULL) {
  g_error(PROGNAME ": Failed to add a watch to GCClient: %s\n",
          error->message);
  /* Normally we'd also release the allocated GError, but since
     this program will terminate on g_error, we won't do that.
     Hence the next line is commented. */
  /* g_error_free(error); */
 
  /* When you want to release the error if it has been allocated,
     or just continue if not, use g_clear_error(&error); */
}
 
g_print(PROGNAME ":main Added " SERVICE_GCONF_ROOT ".\n");

Proceeding with the callback function registration, we have: gconf-listener/gconf-key-watch.c

/* Register our interest (in the form of a callback function) for
   any changes under the namespace that we just added.
 
   Parameters:
   - client: GConfClient object.
   - SERVICEPATH: namespace under which we can get notified for
                  changes.
   - gconf_notify_func: callback that will be called on changes.
   - NULL: user-data pointer (not used here).
   - NULL: function to call on user-data when notify is removed or
           GConfClient destroyed. NULL for none (since we don't
           have user-data anyway).
   - error: return location for an allocated GError.
 
   Returns:
   guint: an ID for this notification so that we could remove it
          later with gconf_client_notify_remove(). We're not going
          to use it so we don't store it anywhere. */
gconf_client_notify_add(client, SERVICE_GCONF_ROOT,
                        keyChangeCallback, NULL, NULL, &error);
if (error != NULL) {
  g_error(PROGNAME ": Failed to add register the callback: %s\n",
          error->message);
  /* Program terminates. */
 }
 
g_print(PROGNAME ":main CB registered & starting main loop\n");

When dealing with regular desktop software, one could use multiple callback functions; one for each key to track. However, this would require implementing multiple callback functions, and this runs a risk of enlarging the size of the code. For this reason, the example code uses one callback function, which will internally multiplex between the two keys (by using strcmp): gconf-listener/gconf-key-watch.c

/**
 * Callback called when a key in watched directory changes.
 * Prototype for the callback must be compatible with
 * GConfClientNotifyFunc (for ref).
 *
 * It will find out which key changed (using strcmp, since the same
 * callback is used to track both keys) and the display the new value
 * of the key.
 *
 * The changed key/value pair will be communicated in the entry
 * parameter. userData will be NULL (can be set on notify_add [in
 * main]). Normally the application state would be carried within the
 * userData parameter, so that this callback could then modify the
 * view based on the change. Since this program does not have a state,
 * there is little that we can do within the function (it will abort
 * the program on errors though).
 */
static void keyChangeCallback(GConfClient* client,
                              guint        cnxn_id,
                              GConfEntry*  entry,
                              gpointer     userData) {
 
  /* This will hold the pointer to the value. */
  const GConfValue* value = NULL;
  /* This will hold a pointer to the name of the key that changed. */
  const gchar* keyname = NULL;
  /* This will hold a dynamically allocated human-readable
     representation of the changed value. */
  gchar* strValue = NULL;
 
  g_print(PROGNAME ": keyChangeCallback invoked.\n");
 
  /* Get a pointer to the key (this is not a copy). */
  keyname = gconf_entry_get_key(entry);
 
  /* It will be quite fatal if after change we cannot retrieve even
     the name for the gconf entry, so we error out here. */
  if (keyname == NULL) {
    g_error(PROGNAME ": Couldn't get the key name!\n");
    /* Application terminates. */
  }
 
  /* Get a pointer to the value from changed entry. */
  value = gconf_entry_get_value(entry);
 
  /* If we get a NULL as the value, it means that the value either has
     not been set, or is at default. As a precaution we assume that
     this cannot ever happen, and will abort if it does.
     NOTE: A real program should be more resilient in this case, but
           the problem is: what is the correct action in this case?
           This is not always simple to decide.
     NOTE: You can trip this assert with 'make primekeys', since that
           will first remove all the keys (which causes the CB to
           be invoked, and abort here). */
  g_assert(value != NULL);
 
  /* Check that it looks like a valid type for the value. */
  if (!GCONF_VALUE_TYPE_VALID(value->type)) {
    g_error(PROGNAME ": Invalid type for gconfvalue!\n");
  }
 
  /* Create a human readable representation of the value. Since this
     will be a new string created just for us, we'll need to be
     careful and free it later. */
  strValue = gconf_value_to_string(value);
 
  /* Print out a message (depending on which of the tracked keys
     change. */
  if (strcmp(keyname, SERVICE_KEY_CONNECTION) == 0) {
    g_print(PROGNAME ": Connection type setting changed: [%s]\n",
            strValue);
  } else if (strcmp(keyname, SERVICE_KEY_CONNECTIONPARAMS) == 0) {
    g_print(PROGNAME ": Connection params setting changed: [%s]\n",
            strValue);
  } else {
    g_print(PROGNAME ":Unknown key: %s (value: [%s])\n", keyname,
            strValue);
  }
 
  /* Free the string representation of the value. */
  g_free(strValue);
 
  g_print(PROGNAME ": keyChangeCallback done.\n");
}

The complications in the above code rise from the fact that GConf communicates values using a GValue structure, which can carry values of any simple type. Since GConf (or the user for that matter) cannot be completely trusted to return the correct type for the value, it is necessary to be extra careful, and not assume that it will always be a string. GConf also supports "default" values, which are communicated to the application using NULLs, so it is also necessary to guard against that. Especially since the application does not provide a schema for the configuration space that would contain the default values.

The next step is to build and test the program. The program will be started on the background, so that gconftool-2 can be used to see how the program reacts to changing parameters:

[sbox-DIABLO_X86: ~/gconf-listener] > make
cc -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include -I/usr/include/gconf/2 \
   -I/usr/include/dbus-1.0 -I/usr/lib/dbus-1.0/include -Wall -g \
   -DPROGNAME=\"gconf-key-watch\" gconf-key-watch.c -o gconf-key-watch \
   -lgconf-2 -ldbus-glib-1 -ldbus-1 -lgobject-2.0 -lglib-2.0
[sbox-DIABLO_X86: ~/gconf-listener] > run-standalone.sh ./gconf-key-watch &
[2] 21385
gconf-key-watch:main Starting.
gconf-key-watch:main GType and GConfClient initialized.
gconf-key-watch: Value for key '/apps/Maemo/platdev_ex/connection'
 is set to 'btcomm0'
gconf-key-watch: Value for key '/apps/Maemo/platdev_ex/connectionparams'
 is set to '9600,8,N,1'
gconf-key-watch:main Added /apps/Maemo/platdev_ex.
gconf-key-watch:main CB registered & starting main loop
[sbox-DIABLO_X86: ~/gconf-listener] > gconftool-2 --set --type string \
  /apps/Maemo/platdev_ex/connection ttyS0
gconf-key-watch: keyChangeCallback invoked.
gconf-key-watch: Connection type setting changed: [ttyS0]
gconf-key-watch: keyChangeCallback done.
[sbox-DIABLO_X86: ~/gconf-listener] > gconftool-2 --set --type string \
  /apps/Maemo/platdev_ex/connectionparams ''
gconf-key-watch: keyChangeCallback invoked.
gconf-key-watch: Connection params setting changed: []
gconf-key-watch: keyChangeCallback done.

The latter change is somewhat problematic (which the code needs to deal with as well). It is necessary to decide how to react to values that are of the correct type, but with senseless values. GConf in itself does not provide syntax checking for the values, or any semantic checking support. It is recommended that configuration changes will only be reacted to when they pass some internal (to the application) logic that will check their validity, both at syntax level and also at semantic level.

One option would also be resetting the value back to a valid value, whenever the program detects an invalid value set attempt. This will lead to a lot of problems, if the value is set programmatically from another program that will obey the same rule, so it is not advisable. Quitting the program on invalid values is also an option that should not be used, since the restricted environment does not provide many ways to inform the user that the program has quit.

An additional possible problem is having multiple keys that are all "related" to a single setting or action. It is necessary to decide, how to deal with changes across multiple GConf keys that are related, yet changed separately. The two key example code demonstrates the inherent problem: should the server re-initialize the (theoretic) connection, when the connection key is changed, or when the connectionparams key is changed? If the connection is re-initialized when either of the keys is changed, then the connection will be re-initialized twice when both are changed "simultaneously" (user presses "Apply" on a settings dialog, or gconftool-2 is run and sets both keys). It is easy to see how this might be an even larger problem, if instead of two keys, there were five per connection. GConf and the GConfClient GObject wrapper that has been used here do not support "configuration set transactions", which would allow setting and processing multiple related keys using an atomic model. The example program ignores this issue completely.

The next step is to test how the program (which is still running) reacts to other problematic situations:

[sbox-DIABLO_X86: ~/gconf-listener] > gconftool-2 --set --type int \
  /apps/Maemo/platdev_ex/connectionparams 5
gconf-key-watch: keyChangeCallback invoked.
gconf-key-watch: Connection params setting changed: [5]
gconf-key-watch: keyChangeCallback done.
[sbox-DIABLO_X86: ~/gconf-listener] > gconftool-2 --set --type boolean \
  /apps/Maemo/platdev_ex/connectionparams true
gconf-key-watch: keyChangeCallback invoked.
gconf-key-watch: Connection params setting changed: [true]
gconf-key-watch: keyChangeCallback done.

The next example removes the configuration keys, while the program is still running:

[sbox-DIABLO_X86: ~/gconf-listener] > make clearkeys
gconf-key-watch: keyChangeCallback invoked.
gconf-key-watch[21403]: GLIB ERROR ** default - 
  file gconf-key-watch.c: line 129 (keyChangeCallback):
  assertion failed: (value != NULL)
aborting...
/usr/bin/run-standalone.sh: line 11: 21403 Aborted (core dumped) "$@"
[1]+  Exit 134 run-standalone.sh ./gconf-key-watch

Since the code (in the callback function) contains an assert that checks for non-NULL values, it will abort when the key is removed, and that causes the value to go to NULL. So the abortion in the above case is expected.