7.3 Programming with GConf


7.3 Programming with GConf

To use GConf in your application, add the client include file to your application header:

 #include <gconf/gconf-client.h> 

You can get the header and library paths with pkg-config (package name : gconf-2.0 ), but this is not necessary if your program is a GNOME application, because the GNOME libraries depend on GConf.

7.3.1 Initializing and Finalizing GConf

To use GConf, you must create a GConfClient object that represents the connection between your application and gconfd-2 and holds a data cache. Use this generator function to get a connection:

 GConfClient *  client  ;  client  = gconf_client_get_default(); 

To initialize a GConfClient cache, you must inform the object of the directories that you plan to use:

 GError *  error  ; gchar *  path  ; gconf_client_add_dir(  client  ,  path  ,  preload  ,  error  ); 

Here, path is a GConf path, error follows the usual error-reporting rules through the GCONF_ERROR domain (see Section 7.3.5 for the error codes), and preload determines the keys and values that client adds to its cache. Possible preload values are as follows:

  • GCONF_CLIENT_PRELOAD_NONE : Do not cache any keys and values.

  • GCONF_CLIENT_PRELOAD_ONELEVEL : Cache the keys and values in the path.

  • GCONF_CLIENT_PRELOAD_RECURSIVE : Cache all keys and values in this directory and all subdirectories. This can take some time for extensive directory hierachies, but it does substantially speed up subsequent access.

You can find path for your application with

 GnomeProgram *  program  ; gchar *  relative_path  ;  path  = gnome_gconf_get_app_settings_relative(  program  ,  relative_path  ); 

This function returns /apps/app_id/relative_path , where app_id is the application identifier in program , and relative_path is the string behind relative_path . Most applications aren't so extensive that they need configuration subdivisions; you will probably stick with "" as the relative path.

You can remove a directory from a client with

 gconf_client_remove_dir(  client  ,  path  ,  error  ) 
Note  

You may add a directory to a client as many times as you like, as long as you remove it that many times.

Here is how a GConf-aware application skeleton might look:

 #include <gnome.h> #include <gconf/gconf-client.h>   << ... >> int main(int argc, char **argv) {   GnomeProgram *program;   GConfClient *client;   gchar *path;   << initialize program >>   /* create GConf client object */   client = gconf_client_get_default();   gconf_client_set_error_handling(client, GCONF_CLIENT_HANDLE_UNRETURNED);   /* add this application's recommended path */   path = gnome_gconf_get_app_settings_relative(program, "");   gconf_client_add_dir(client, path, GCONF_CLIENT_PRELOAD_RECURSIVE, NULL);   << ... >>   gtk_main();   << ... >>   /* remove path */   gconf_client_remove_dir(client, path, NULL);   g_free(path);   /* destroy client object */   g_object_unref(client);   << ... >> } 
Note  

You cannot add a subdirectory to a GConf client after you add a parent. For example, adding /apps/my_app/this/that after /apps/my_app/this will not work.

7.3.2 Reading and Writing GConf Values

After you create your client and add a directory, you're ready to get down to the real business of reading and writing the values behind keys. These functions retrieve values:

  • gint gconf_client_get_int(GConfClient *client , const gchar * key , GError **error )

    Returns the integer at key in the GConfClient object client . If no such key exists or an error occurs, this function returns 0 as a default. As before, errors show up in error .

  • gdouble gconf_client_get_float(GConfClient *client , const gchar * key , GError **error )

    Like the preceding function, but for a floating-point value. The default is 0.0.

  • gboolean gconf_client_get_bool(GConfClient * client , const gchar * key , GError ** error )

    Like the preceding function, but for a Boolean value. The default is FALSE .

  • gchar *gconf_client_get_string(GConfClient * client , const gchar * key , GError ** error )

    Like the preceding function, but for a string; the return value is a newly allocated string, or NULL if no such key exists or some other error occurs.

  • GSList *gconf_client_get_list(GConfClient * client , const gchar * key , GConfValueType type , GError ** error )

    Returns a list at key , but only if the list elements match type . Types include:

     GCONF_VALUE_INT      GCONF_VALUE_FLOAT      GCONF_VALUE_BOOL      GCONF_VALUE_STRING 

    The elements of the newly allocated list are untyped pointers ( gpointer ). To get at integers and Boolean values, you need only cast an element with GPOINTER_TO_INT() . However, floating-point and string elements use new memory. You must dereference a node's data pointer to get at a floating-point number and free the memory for each node when you're through.

  • gboolean *gconf_client_get_pair(GConfClient * client , const gchar * key , GConfValueType type1 , GConfValueType type2 , gpointer val1_target , gpointer val2_target , GError ** error )

    Like the preceding function, but for pairs: type1 and type2 indicated the types in the pair; val1_target and val2_target point to some memory for the values in the pair (for strings, you must supply the address of a pointer). Upon success, this function returns TRUE .

Similar functions set a key's value. All of the following return TRUE upon success. When a problem arises, the return value is FALSE, and an error condition shows up at * error .

  • gboolean gconf_client_set_int(GConfClient * client , const gchar * key , gint value , GError ** error )

    Sets the value for key to the integer value .

  • gboolean gconf_client_set_float(GConfClient * client , const gchar * key , gdouble value , GError ** error )

    Sets the value for key to the floating-point number value .

  • gboolean gconf_client_set_bool(GConfClient * client , const gchar * key , gboolean value , GError ** error )

    Sets the value for key to the Boolean value .

  • gboolean gconf_client_set_string(GConfClient * client , const gchar * key , gchar * value , GError ** error )

    Sets the value for key to the string value .

  • gboolean gconf_client_set_list(GConfClient * client , const gchar * key , GConfValueType type , GSList * list , GError ** error )

    Sets the value for key to a list; the elements in list have type type .

  • gboolean gconf_client_set_pair(GConfClient * client , const gchar * key , GConfValueType type1 , GConfValueType type2 , gconstpointer * value1 , gconstpointer * value2 , GError ** error )

    Sets the value for key to a pair; the first element is behind value1 and has type type1 ; the second element is behind value2 and has type type2 .

Warning  

All GConf database transactions are asynchronous. After you call one of the gconf_client_set_*() functions, the new value may not be immediately available to other processes; gconfd-2 waits until it has the time to set the value. Therefore, you should not use GConf values for critical interprocess communication.

Before you try to write a value, you can see if its key is read-only with

 gconf_client_key_is_writable(  client  ,  path  ,  error  ) 

This function returns FALSE if the key is read-only and TRUE if you can write to its value.

Here is some example code that reads and writes values:

 GConfClient *client; GSList *list, *l_ptr; gchar *name; gint n;   << ... >> /* read some values */ g_print("Your desktop has %d workspace(s).\n",         gconf_client_get_int(client,           "/desktop/gnome/applications/window_manager/number_of_workspaces",           NULL)); g_print("The desktop names are:\n"); list = gconf_client_get_list(client,          "/desktop/gnome/applications/window_manager/workspace_names",          GCONF_VALUE_STRING,          NULL); for (l_ptr = list; l_ptr != NULL; l_ptr = g_slist_next(l_ptr)) {    g_print(" %s", (gchar *)l_ptr->data);    g_free(l_ptr->data); } g_slist_free(list); g_print("\n"); if (gconf_client_get_bool       (client, "/desktop/gnome/applications/cursor_blink", NULL)) {    g_print("Your cursor blinks.\n"); } else {    g_print("Your cursor does not blink.\n"); } name = gconf_client_get_string(client,                                "/desktop/gnome/interface/gtk_theme",                                NULL); g_print("Your GTK+ theme is: %s\n", name); g_free(name); /* write some values */ gconf_client_set_int(client, "/example/number", 42, NULL); gconf_client_set_float(client, "/example/pi", 3.14159, NULL); gconf_client_set_bool(client, "/example/state", FALSE, NULL); gconf_client_set_string(client, "/example/message", "Hello, World.", NULL); /* put a list of integers into conf value */ list = NULL; list = g_slist_append(list, GINT_TO_POINTER(42)); list = g_slist_append(list, GINT_TO_POINTER(37)); list = g_slist_append(list, GINT_TO_POINTER(11217)); gconf_client_set_list(client, "/example/nums", GCONF_VALUE_INT, list, NULL); g_slist_free(list); /* set a pair; a number and a string */ n = 52; name = g_strdup("fifty-two"); gconf_client_set_pair(client,                       "/example/number_name",                       GCONF_VALUE_INT,                       GCONF_VALUE_STRING,                       &n,                       &name,                       NULL); g_free(name); 

GConfValue

The preceding functions assume that you know the key type; if you try to read or write a value with the wrong type, you get an error. However, you can read a value if you do not know its type by creating a GConfValue data structure with the GConfValueType identifier field ( type ). You saw this earlier when reading and writing lists, but did not encounter the full list of identifiers:

  • GCONF_VALUE_INVALID

  • GCONF_VALUE_STRING

  • GCONF_VALUE_INT

  • GCONF_VALUE_FLOAT

  • GCONF_VALUE_BOOL

  • GCONF_VALUE_LIST

  • GCONF_VALUE_PAIR

  • GCONF_VALUE_SCHEMA

You can create a new GConfValue structure as follows:

 GConfValue *  conf_value  ;  conf_value  = gconf_value_new(  type_identifier  ); 

Here, type_identifier is one of the identifiers in the preceding list. To deallocate the structure memory, use

 gconf_value_free(  conf_value  ) 
Warning  

Do not try to free a GConfValue structure by any other means.

You can also get a newly allocated GConfValue from a key at the GConf database client :

  conf_value  = gconf_client_get(  client, key, error  ); 

To write an entry in the database, use

 gconf_client_set(client,  key, conf_value, error  ); 

Once you have a GConfValue structure, you can look at its type field (for example, conf_value ->type ). Then you can set a basic type with one of these functions:

 void gconf_value_set_int(GConfValue *conf_value, gint number) void gconf_value_set_float(GConfValue *conf_value, gdouble number) void gconf_value_set_bool(GConfValue *conf_value, gboolean state) void gconf_value_set_string(GConfValue *conf_value, gchar *string) 

To return a value from a GConfValue, call one of the following:

 int gconf_value_get_int(const GConfValue *  conf_value  ) double gconf_value_get_float(const GConfValue *  conf_value  ) gboolean gconf_value_get_bool(const GConfValue *  conf_value  ) const char *gconf_value_get_string(const GConfValue *  conf_value  ) 
Warning  

A string return value is a constant; you may not alter or deallocate it.

Setting lists is a little trickier. First set the list type; then pass a GSList of GConfValue structures:

 void gconf_value_set_list_type(GConfValue *  conf_value  , GConfValueType  type  ) void gconf_value_set_list(GConfValue *  conf_value  , GSList *  list  ) void gconf_value_set_list_nocopy(GConfValue *  conf_value  , GSList *  list  ) 

The _nocopy variant does not make copies of the GConfValue structures in list when writing to conf_value . If you create your own structures just for the list, you can save a little time with this.

To get a GSList of GConfValue structures in a GConf list conf_value , use

 GSList *gconf_value_get_list(const GConfValue *  conf_value  ) 
Warning  

The strings in lists of strings are not copies. Do not try to alter or deallocate any string data inside the GConfValue structures.

Setting and retrieving GConfValue pair structures is similar, except that there are separate functions for the first and second elements in the pair:

 void gconf_value_set_car(GConfValue *  conf_value  , const GConfValue *  first  ) void gconf_value_set_cdr(GConfValue *  conf_value  , const GConfValue *  second  ) void gconf_value_set_car_nocopy(GConfValue *  conf_value  , const GConfValue *  first  ) void gconf_value_set_cdr_nocopy(GConfValue *  conf_value  , const GConfValue *  second  ) GConfValue *gconf_value_get_car(const GConfValue *  conf_value  ) GConfValue *gconf_value_get_cdr(const GConfValue *  conf_value  ) 

There are two interesting GConfValue utility functions:

  • GConfValue *gconf_value_copy(const GConfValue * conf_value )

    Returns a complete copy of conf_value , including all list and pair elements.

  • gchar *gconf_value_to_string(const GConfValue * conf_value )

    Returns a newly allocated string representation of conf_value . This function is good for diagnostic purposes, but it does not have a specific format.

Here are some GConfValue structures in action:

 GConfValue *conf_value; GSList *list; gchar *string; /* create a value */ conf_value = gconf_value_new(GCONF_VALUE_INT); /* set a value */ gconf_value_set_int(conf_value, 42); /* check the value's type */ switch (conf_value->type) {   case GCONF_VALUE_INT:      g_print("conf_value is an integer.\n");      break;   case GCONF_VALUE_STRING:      g_print("conf_value is a string.\n");      break;   default:      g_print("conf_value is not an integer or a string.\n");      break; } /* print the value (by means of access function) */ g_print("conf_value = %d\n", gconf_value_get_int(conf_value)); /* print with diagnostic function */ string = gconf_value_to_string(conf_value); g_print("conf_value = %s\n", string); g_free(string); /* deallocate */ gconf_value_free(conf_value); /* create a list */ list = NULL; conf_value = gconf_value_new(GCONF_VALUE_STRING); gconf_value_set_string(conf_value, "yesterday"); list = g_slist_append(list, conf_value); conf_value = gconf_value_new(GCONF_VALUE_STRING); gconf_value_set_string(conf_value, "today"); list = g_slist_append(list, conf_value); conf_value = gconf_value_new(GCONF_VALUE_STRING); gconf_value_set_string(conf_value, "tomorrow"); list = g_slist_append(list, conf_value); /* put the list into a GConfValue (conf_list_value) */ conf_value = gconf_value_new(GCONF_VALUE_LIST); gconf_value_set_list_type(conf_value, GCONF_VALUE_STRING); gconf_value_set_list_nocopy(conf_value, list); /* print string with diagnostic function */ string = gconf_value_to_string(conf_value); g_print("conf_value = %s\n", string); g_free(string); gconf_value_free(conf_value); 

GConfEntry

You can go one step beyond GConfValue with GConfEntry , a data structure that contains the key along with its value. To create a GConfEntry data structure, call this function:

 GConfEntry *  entry  ; entry = gconf_client_get_entry(  client  ,  key  , NULL,  use_default  ,  error  ); 

Here, client , key , and error are the GConfClient object, key string, and error structure pointer, as in the access functions that you saw earlier. Set the Boolean use_default parameter to TRUE if you want to get the schema default values. The third parameter is supposed to be the locale, but is not supported at the moment. Use NULL for the default locale.

You can get the key or GConfValue inside a GConfEntry structure with these two functions:

 const char *gconf_entry_get_key(const GConfEntry *  entry  ) GConfValue *gconf_entry_get_value(const GConfEntry *  entry  ) 
Warning  

The gconf_entry_get_value() function can return NULL if its key has no value. You should always check this by hand.

To deallocate a GConfEntry structure, call

 gconf_entry_free(  entry  ) 

If you want to retain the GConfValue structure inside an entry before you free an entry, call this function first:

  conf_value  = gconf_entry_steal_value(  entry  ); 

Remember that you are responsible for deallocating conf_value .

Here are a few other GConf API functions that can help you replicate some gconftool-2 navigation functionality:

  • gboolean gconf_client_dir_exists(GConfClient * client , const gchar * dir , GError **error )

    Returns TRUE if dir exists in the GConf database.

  • GSList *gconf_client_all_entries(GConfClient * client , const gchar * dir , GError **error )

    Returns a list of GConfEntry structures for all keys and values in dir .

  • GSList *gconf_client_all_dirs(GConfClient * client , const gchar * dir , GError ** error )

    Returns a list of all subdirectories in dir . The list elements are newly allocated strings containing the absolute paths of each subdirectory. Make sure that you free the strings before the list when you are finished with the list.

  • gboolean gconf_client_unset(GConfClient * client , const gchar * path , GError ** error )

    Removes the directory or key at path and returns TRUE . If no such item exists or the path is read-only, this function returns FALSE and sets * error .

Now it's time for an example: a program that searches and prints all entries in an entire directory and its subdirectories, similar to gconftool-2 -R . Notice that you do not need the entire GNOME include paths:

 /* -*-coding: utf-8;-*- */ /* gconflister.c -- lists contents in a GConf database */ #include <gnome.h> #include <gconf/gconf-client.h> /* the path to list */ #define PATH "/" 

The list_contents procedure that follows does all of the work. The parameters are a GConf client, a path, and a current depth. This function calls itself with depth+1 to update the depth.

 /* recursively list the entries in the GConf database under path */ void list_contents(GConfClient *client, gchar *path, gint depth) {   GSList *subdirs = NULL, *entries = NULL, *p;   gint i;   gchar *value_str; 

The preparatory work includes error checking, extraction of the directory contents, and printing of the directory name:

 /* make sure that this is a directory */   if (!gconf_client_dir_exists(client, path, NULL))   {      g_printerr("%s: No such GConf directory in database\n", path);      return;   }   /* extract all subdirectories in path */   subdirs = gconf_client_all_dirs(client, path, NULL);   /* extract all entries in path */   entries = gconf_client_all_entries(client, path, NULL);   /* depth is the current directory depth; print spaces before the output */   if (depth)   {      for (i=0; i < depth; i++)      {         g_print(" ");      }   }   /* print directory name */   g_print("%s:\n", path); 

To print the contents of each subdirectory, this function traverses the subdirectory list and calls itself on each subdirectory.

 /* print the subdirectory contents */   if (subdirs)   {      for (p = subdirs; p != NULL; p = g_slist_next(p))      {         list_contents(client, p->data, depth+1);         g_free(p->data);      }      g_slist_free(subdirs);   } 

The last order of business is to print the values of any keys that might be in the current directory:

 /* print all entries in the directory */   if (entries)   {      for (p = entries; p != NULL; p = g_slist_next(p))      {         /* if there is no value behind a key, print the value as NULL,            otherwise, format the value a diagnostic function */         if (!gconf_entry_get_value(p->data))         {            value_str = g_strdup("NULL");         } else {            value_str =              gconf_value_to_string(gconf_entry_get_value((GConfEntry*)(p->data)));         }         /* indent the output */         for (i=0; i < (depth+1); i++)         {            g_print(" ");         }         /* print the key and formatted value */          g_print("%s = %s\n",                gconf_entry_get_key((GConfEntry*)(p->data)), value_str);        g_free(value_str);        gconf_entry_free(p->data);     }     g_print("\n");     g_slist_free(entries);   } } 

The main program creates a client and starts printing at PATH .

 int main(int argc, char **argv) {   GnomeProgram *program;   GConfClient *client; program = gnome_program_init("gconflister", "0.1",                          LIBGNOMEUI_MODULE,                          argc, argv,                          GNOME_PROGRAM_STANDARD_PROPERTIES,                          GNOME_PARAM_HUMAN_READABLE_NAME, "GConfLister",                          GNOME_PARAM_ENABLE_SOUND, TRUE,                          NULL);   /* create client object */   client = gconf_client_get_default();   gconf_client_set_error_handling(client, GCONF_CLIENT_HANDLE_UNRETURNED);   /* list everything in PATH */   list_contents(client, PATH, 0);   /* finalize client object */   g_object_unref(client);   return 0; } 

Now it's time to see how your application should recognize changes in GConf keys.

7.3.3 GConf Value Change Notification

To detect a change in a GConf value, bind a callback function to the value's key. If any application changes the value, GConf calls the callback function so that your application can react appropriately.

The callback function prototype follows the GConfClientNotifyFunc type definition:

 typedef void (*GConfClientNotifyFunc)(GConfClient *client,                                       guint cnxn_id,                                       GConfEntry *entry,                                       gpointer user_data); 

Here, client and entry are the GConf client and applicable GConf entry; user_data is the auxiliary data pointer that you have already seen many times in this book in connection with signals.

The cnxn_id identifier is a connection identifier; it is the return value when you bind the handler callback_func to the path in client with this function:

 guint gconf_client_notify_add(GConfClient  client  ,                                   const gchar *  path  ,                                   GConfClientNotifyFunc  callback_func  ,                                   gpointer  user_data  ,                                   GFreeFunc  destroy_notify  ,                                   GError **  error  ) 

Here, path can be a key or a directory (for directories, GConf calls callback_func when anything in or below the directory changes), and destroy_notify is a destructor function for user_data .

To remove a callback function from GConf's watchlist, use

 gconf_client_notify_remove(  client  ,  connection_id  ) 

where connection_id is the connection identifier described earlier.

Here is a callback function that prints only a changed value:

 void print_changes(GConfClient *client, guint id,                    GConfEntry *entry, gpointer data) {   gchar *key, *value_str;   key = (gchar *)gconf_entry_get_key(entry);   value_str = gconf_value_to_string(gconf_entry_get_value(entry));   g_print("%s: value changed to: %s\n", key, value_str);   g_free(value_str); } 

You can attach and remove the callback as follows:

 gchar *path; GnomeProgram *program; guint print_cxnid;   << ... >> path = gnome_gconf_get_app_settings_relative(program, "some/path") gconf_client_notify_remove(client, print_cxnid); g_free(path);   << ... >> gconf_client_notify_remove(client, print_cxnid); 

An application's GConf callback function normally alters its widget properties, and that, in turn , alters the appearance of the application. If you keep all of your important settings in widget and object properties, your GConf callback functions will not be hard to implement.

7.3.4 The GConf Cache

You can manipulate the cache in a GConfClient object, for example, if you need GConf to take action immediately (remember that the database transactions are asynchronous).

  • void gconf_client_clear_cache(GConfClient *client )

    Clear the cache for client so that the next read comes directly from the database. This can save a little bit of memory.

  • void gconf_client_preload(GConfClient * client , const gchar * dir , GConfClientPreloadType type, GError **error )

    If you think that you're going to use several components in dir at once, you can call this function to load some entries into the cache. The domain for type is the same as for gconf_client_add_dir() ; the useful values are GCONF_CLIENT_PRELOAD_ONELEVEL and GCONF_CLIENT_PRELOAD_RECURSIVE .

  • void gconf_client_suggest_sync(GConfClient *client, GError **error)

    Tells client and gconfd-2 that they should bring all of their cache data up-to-date. Normally, gconfd-2 decides when to write its configuration cache.

7.3.5 Error Handling

You have probably noticed that a large portion of the GConfClient functions has a GError argument. Normally, you can put NULL into this place because GConfClient does extensive error checking on its own. If you need some extra information, here are the error codes for the GCONF_ERROR domain:

  • GCONF_ERROR_SUCCESS : No error.

  • GCONF_ERROR_FAILED : Unknown error. You may be able to determine the problem from an auxiliary message.

  • GCONF_ERROR_NO_SERVER : The client cannot make contact with gconfd-2 ; this could be a configuration or installation error.

  • GCONF_ERROR_NO_PERMISSION : Database access denied . This could be as minor as a permissions problem.

  • GCONF_ERROR_BAD_ADDRESS : There is a problem with a configuration source in GConf path (see Section 7.1).

  • GCONF_ERROR_BAD_KEY : Invalid key name.

  • GCONF_ERROR_PARSE_ERROR : GConf could not parse the string representation of one of its data structures; see the message log. GConfClient objects should not get this error because they do not use the string representation.

  • GCONF_ERROR_CORRUPT : The GConf database has a defect (probably a problem with an XML file).

  • GCONF_ERROR_TYPE_MISMATCH : You asked for a value with a certain type, but the value in the database does not match that type.

  • GCONF_ERROR_IS_DIR : You tried to access a directory as a key.

  • GCONF_ERROR_IS_KEY : You tried to access a key as a directory.

  • GCONF_ERROR_OVERRIDDEN : You tried to change a value for a key that has a readonly value GConf path.

As mentioned earlier, GConfClient performs its own error handling. If you link against the GNOME libraries, a dialog such as Figure 7.2 appears when a problem crops up.

click to expand
Figure 7.2: Built-in GConf error dialog.

You can control these error dialogs with

 gconf_client_set_error_handling(  client  ,  mode  ) 

where mode is one of the following:

  • GCONF_CLIENT_HANDLE_NONE : No dialogs.

  • GCONF_CLIENT_HANDLE_UNRETURNED : Dialogs appear only when the error does not go into a GError structure.

  • GCONF_CLIENT_HANDLE_ALL : Shows dialogs for every error.

You can set your own error handler with the GConfClient error signal. The handler prototype is

 void handler(GConfClient *client, gpointer *error, gpointer data); 

Here, error is a pointer to a GError structure. You can also set the unreturned-error signal handler to catch any errors where you passed NULL as the GError parameter in a gconf_client_*() function.

With all of this infrastructure, you can define complex layers of error handling, but for the most part, the warning dialogs suffice.

7.3.6 Schemas

You have not seen much about the GConf schema key type so far, because schemas are not normal configuration values that you can use in your application ” they serve only to document other keys. For example, the GConf configuration editor displays the schema data for an entry that you click.

Schemas go in /schemas ; if you have a key named /apps/example/setting1 , the corresponding schema is /schemas/apps/example/setting1 .

Note  

A schema can describe several keys with identical properties.

GConf has an entire API to read and write schemas, but this really isn't necessary. All you need to do is supply your schemas in a .schemas XML file and then install the file with gconftool-2 .

Here is an example of a .schemas file:

 <gconfschemafile>  <schemalist>   <schema>    <key>/schemas/apps/miracletext/check_spelling</key>    <applyto>/apps/miracletext/check_spelling</applyto>    <owner>miracletext</owner>    <type>bool</type>    <locale name="C">       <default>true</default>       <short>Check spelling</short>       <long>Automatically check spelling in documents</long>    </locale>    <locale name="de">       <default>true</default>       <short>Rechtschreibung pruefen</short>       <long>Rechtschreibung in Dokumenten automatisch prfen</long>    </locale>   </schema>   <schema>    <key>/schemas/apps/miracletext/signature</key>    <applyto>/apps/miracletext/signature</applyto>    <owner>miracletext</owner>    <type>string</type>    <locale name="C">       <short>Signature</short>       <long>Signature for the bottom of letters</long>    </locale>    <locale name="de">       <short>Signatur</short>       <long>Signatur under Briefen</long>    </locale>   </schema>  </schemalist> </gconfschemafile> 

Enclose the entire file with <gconfschemafile> tags, with <schemalist> just inside. Then you can add as many schemas as you like with <schema> tags.

The internal schema tags are as follows:

  • <key> : The schema key path: for example, /schemas/apps/example/setting1 .

  • <applyto> : The key that the schema describes: for example, /apps/example/setting1 . You may have more than one applyto element in a schema.

  • <owner> : The application that owns the schema; this should be the application identifier.

  • <type> : The key type; one of int , float , bool , string , pair , list .

  • <default> : The key's default value. This should permit the application to function normally if the user has not set the value. The format is identical to the output of gconftool-2 .

  • <list_type> : If the key is a list, this is the type of the list elements.

  • <car_type> : If the key is a pair, this is the first element's type.

  • <cdr_type> : If the key is a pair, this is the second element's type.

  • <locale> : Key descriptions for a locale; specify the locale with the name= locale attribute. You should always set a C locale. Each locale section should have these two elements:

    • <short> A label for the key; this label should not be more than 40 characters long.

    • <long> A more drawn-out description that says what the key's value actually does.

You can place a default values inside or outside of the locale section; if it is outside, the default applies to all locales. If you opt to put the default value inside the locale, make sure that you define a default for all locales. The default locale is C.

Installing Schemas

If you decide to use intltool in conjunction with the GNU autotools, do the following to install your schemas:

  1. Create a schemas file with a single locale, C; store this as a .schemas.in file.

  2. Add something like this to your top-level Makefile.am :

     schemasdir = $(sysconfdir)/gconf/schemas schemas_in_files = prog.schemas.in schemas_DATA = $(schemas_in_files:.schemas.in=.schemas) @INTLTOOL_SCHEMAS_RULE@ 
  3. Add an install-data-local target to your Makefile.am that looks something like this:

     install-data-local:         GCONF_CONFIG_SOURCE=$(GCONF_CONFIG_SOURCE) \         $(GCONFTOOL) --makefile-install-rule $(top_src_dir)/miracletext.schemas 
  4. Enter translations for the schema in your po/*.po files. When you build the package, intltool creates a .schemas file with appropriate <locale> sections.

GCONF_CONFIG_SOURCE tells gconftool-2 where to install the schemas. This is a source (for example, xml:readwrite:/ some / path ).

If you just want to install a .schemas file by hand, use

 $ gconftool-2 --makefile-install-rule  name  .schemas 

7.3.7 A Complete Example

You're ready to see a complete example that includes a schema. The application has a single Boolean GConf value ( /apps/gconfdemo/show_nums_as_words ) and three labels that display numbers . When the configuration value is TRUE , the labels spell their numbers in words. Figure 7.3 shows the final application.

click to expand
Figure 7.3: GConf demonstration application.

The global declarations include the label widgets and the top-level window. In addition, the WORDS_KEY macro defines the relative GConf key pathname. The application always combines WORDS_KEY with the application GConf path ( /apps/gconfdemo ).

 /* -*-coding: utf-8;-*- */ /* gconfdemo.c -- GConf demo application */ #include <gnome.h> #include <gconf/gconf-client.h> GnomeProgram *program; GConfClient *client; GtkLabel *label[3]; GnomeApp *window; /* the relative path of the key */ #define WORDS_KEY "show_nums_as_words" /* standard window event handlers */ gint delete_event(GtkWidget *widget, GdkEvent event, gpointer data) {   return FALSE; } void end_program(GtkWidget *widget, gpointer data) {   gtk_main_quit(); } 

The application includes a Preferences dialog with a check box for turning WORDS_KEY on and off. The button_changed handler is a signal handler for the check box and does the work of changing the GConf value in the database. Notice that WORDS_KEY does not appear in the function. The key name goes through the key_ptr data pointer. Therefore, you can use this handler for any number of check boxes attached to GConf keys.

 void button_changed(GtkCheckButton *button, gpointer key_ptr) {   gboolean value;   gchar *path;   gchar *key = (gchar *)key_ptr;   g_object_get(button, "active", &value, NULL);   path = gnome_gconf_get_app_settings_relative(program, key),   gconf_client_set_bool(client, path, value, NULL);   g_free(path); } 

You have seen the setup for dialogs like Preferences before:

 void preferences(void) {   static GtkDialog *dialog;   static GtkCheckButton *word_button;   gboolean show_words_init;   gboolean words_key_writable;   GError *error = NULL;   gchar *path; dialog = GTK_DIALOG(gtk_dialog_new_with_buttons("GConf Demo Preferences",                     GTK_WINDOW(window),                     GTK_DIALOG_DESTROY_WITH_PARENT,                     GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,                     NULL)); g_object_set(dialog, "border-width", 12, NULL); gtk_dialog_set_default_response(dialog, GTK_RESPONSE_ACCEPT); g_signal_connect_swapped(dialog,                          "response", G_CALLBACK(gtk_widget_destroy), dialog); word_button = g_object_new(GTK_TYPE_CHECK_BUTTON,                           "label", "Display numbers as _words",                           "use-underline", TRUE,                           NULL); 

To set up the word_button check button correctly, you need to know the current WORDS_KEY configuration value. If there is no such value, use a default of TRUE .

 path = gnome_gconf_get_app_settings_relative(program, WORDS_KEY);   show_words_init = gconf_client_get_bool(client, path, &error);   if (error)   {      show_words_init = TRUE;      g_error_free(error);   }   words_key_writable = gconf_client_key_is_writable(client, path, NULL);   g_free(path);   g_object_set(word_button,                "active", show_words_init,                "sensitive", words_key_writable,                NULL); 

Now you attach the check button's toggled signal to the function in the preceding code. Note that the data pointer is WORDS_KEY .

 g_signal_connect(word_button,                  "toggled", G_CALLBACK(button_changed), WORDS_KEY);   gtk_box_pack_start_defaults(GTK_BOX(dialog->vbox), GTK_WIDGET(word_button));   gtk_widget_show_all(GTK_WIDGET(dialog)); } 

This program uses gnome-app-helper to create a menu bar. The application runs preferences (in the preceding code) when the user selects Edit > Preferences .

 GnomeUIInfo file_items[] = {   GNOMEUIINFO_MENU_QUIT_ITEM(end_program, NULL),   GNOMEUIINFO_END }; GnomeUIInfo edit_items[] = {   GNOMEUIINFO_MENU_PREFERENCES_ITEM(preferences, NULL),   GNOMEUIINFO_END }; GnomeUIInfo menu_bar_items[] = {   GNOMEUIINFO_MENU_FILE_TREE(file_items),   GNOMEUIINFO_MENU_EDIT_TREE(edit_items),   GNOMEUIINFO_END }; 

Although you do not yet know the origin of label , it's easy to see that the following function changes label text based on the value of WORDS_KEY .

 void set_labels(void) {   gboolean show_as_words;   GError *error = NULL;   gchar *path;   path = gnome_gconf_get_app_settings_relative(program, WORDS_KEY);   show_as_words = gconf_client_get_bool(client, path, &error);   g_free(path);   if (error)   {      show_as_words = TRUE;      g_error_free(error);   }   if (show_as_words)   {     g_object_set(label[0], "label", "One", NULL);     g_object_set(label[1], "label", "Two", NULL);     g_object_set(label[2], "label", "Forty-two", NULL);   } else {     g_object_set(label[0], "label", "1", NULL);     g_object_set(label[1], "label", "2", NULL);     g_object_set(label[2], "label", "42", NULL);   } } 

Next is a callback function for changing a GConf configuration value. The main program attaches this callback to changes to WORDS_KEY .

Note  

The pedants among us might note that this does not change the check button in Preferences (if this dialog exists). Therefore, if some external application changed WORDS_KEY , that check button would hold invalid data. To make the button widget update, you should block its signal handlers when you set its active property, because you risk infinite signal recursion otherwise.

 void display_type_changed(GConfClient *client, guint cxnid,                           GConfEntry *entry, gpointer data) {   set_labels(); } 

The bulk of the main program defines the main window and label widgets, as well as the GConf initialization.

 int main(int argc, char **argv) {   gchar *gconf_path;   GtkStatusbar *status;   GtkVBox *vbox;   gint i;   guint words_cxnid;   gchar *path;   program = gnome_program_init("gconfdemo", "0.1",                            LIBGNOMEUI_MODULE,                            argc, argv,                            GNOME_PROGRAM_STANDARD_PROPERTIES,                            GNOME_PARAM_HUMAN_READABLE_NAME, "GConf Demo",                            GNOME_PARAM_ENABLE_SOUND, TRUE,                            NULL);   /* create GConf client object */   client = gconf_client_get_default();   gconf_client_set_error_handling(client, GCONF_CLIENT_HANDLE_UNRETURNED);   /* add this application's recommended path */   gconf_path = gnome_gconf_get_app_settings_relative(program, "");   gconf_client_add_dir(client, gconf_path, GCONF_CLIENT_PRELOAD_ONELEVEL, NULL);   /* create window */   window = g_object_new(GNOME_TYPE_APP,                          "title", "GConf Demo",                          "app-id", "gconfdemo",                          "default-width", 300,                          "default-height", 300,                          NULL);   /* attach standard signal handlers */   g_signal_connect(window, "delete_event", G_CALLBACK(delete_event), NULL);   g_signal_connect(window, "destroy", G_CALLBACK(end_program), NULL);   /* add menu and status bar */   gnome_app_create_menus(window, menu_bar_items);   status = g_object_new(GTK_TYPE_STATUSBAR, NULL);   gnome_app_set_statusbar(window, GTK_WIDGET(status));   /* create some labels for the main window */   vbox = g_object_new(GTK_TYPE_VBOX, "border-width", 12, NULL);   for (i=0; i<3; i++)   {      label[i] = g_object_new(GTK_TYPE_LABEL, "xalign", 0.0, NULL);      gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(label[i]), FALSE, FALSE, 0);   }   /* set the label text */   set_labels();   /* add label vbox to main window */   gnome_app_set_contents(window, GTK_WIDGET(vbox)); Now you're ready to attach the GConf callback functions.   /* attach callback functions for when value behind WORDS_KEY changes */   path = gnome_gconf_get_app_settings_relative(program, WORDS_KEY),   words_cxnid = gconf_client_notify_add(client, path, display_type_changed,                                         NULL, NULL, NULL);   g_free(path);   /* show window, start main event loop */   gtk_widget_show_all(GTK_WIDGET(window));   gtk_main(); 

After the user leaves the main event loop, you can clean and finalize GConf.

 /* remove callback functions */   gconf_client_notify_remove(client, words_cxnid);   /* remove path */   gconf_client_remove_dir(client, gconf_path, NULL);   g_free(gconf_path);   /* let go of reference to GConf client object */   g_object_unref(client);   return 0; } 

The GConf error reporting mode is GCONF_CLIENT_HANDLE_UNRETURNED (see the preceding code). Therefore, you can safely run a stress test:

 gconftool-2 --break-key /apps/gconfdemo/show_nums_as_words 

The gconfdemo program handles errors silently because it checks error codes by hand when trying to retrieve a value. Otherwise, there is no reason to bother with errors.

Schemas

Here is the .schemas file for gconfdemo :

 <gconfschemafile>  <schemalist>   <schema>    <key>/schemas/apps/gconfdemo/show_nums_as_words</key>    <applyto>/apps/gconfdemo/show_nums_as_words</applyto>    <owner>gconfdemo</owner>    <type>bool</type>    <locale name="C">       <default>true</default>       <short>Numbers as words</short>       <long>Display numbers as words?</long>    </locale>   </schema>  </schemalist> </gconfschemafile> 

7.3.8 Preferences Guidelines

When you build a Preferences dialog, try to follow these rules:

  • Don't use Cancel , Apply , or OK buttons in GNOME 2 applications. It is difficult to build such dialogs with GConf, and applications are easier to use when you see the changes immediately.

  • Use a window title such as Application Preferences , where Application is your application's name.

  • If you have a lot of preferences, use GtkNotebook pages to display all of the widgets. However, if there is a tremendous number, you may want to consider icon hierarchies (like Nautilus) or a tree representation (for infrequently used settings).

  • Don't stuff a lot of unrelated preferences into one dialog. Use GtkNotebook or a frame container to group related settings.




The Official GNOME 2 Developers Guide
The Official GNOME 2 Developers Guide
ISBN: 1593270305
EAN: 2147483647
Year: 2004
Pages: 108

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net