GConf

by Rachel Hestilow (hestilow@ximian.com)
updated by Pablo Baena (pbaena@uol.com.ar)

Introduction

GConf is a system for storing configuration information and maintaining it across application sessions, much like the Windows registry but more powerful. The complete documentation for the underlying technology is here, but as we Mono users are very high-level people (at the programming level, at least), I'll show you a quick and simple example of how to use it.

The GConf# binding

In this example, we just create a Mono program that stores and retrieves a value from GConf.

using System;
class GConfExample
{
   static GConf.Client gconfClient = new GConf.Client ();

   public static void Main ()
   {
      string key = "/apps/monoapps/myAppName/mySetting";
      try
      {
         string val = (string) gconfClient.Get (key);
         Console.WriteLine ("The value is: '{0}'", val);
      }
      catch (GConf.NoSuchKeyException)
      {
         Console.WriteLine ("The value is not set. Setting it for the next time.");
         gconfClient.Set (key, "Value");        
      }
   }
}

Remember to add a reference to gconf-sharp.dll in the Mono C# compiler command line. If you don't have it installed, remember that GConf# is part of the Gtk# bindings, although it can be in a separated package (GConf doesn't depend on Gtk).

The 'key' used in the example, is a slash-separated path similar to the ones used in the UNIX filesystem. Each slash denotes a level in the GConf namespace. We use the apps/monoapps/ sub-level only to organize the tree in an orderly fashion. If you have the gconf-editor program installed, you can run it and look for the key yourself in the tree structure. You'll see it stored with the value we put it, so the next time you run the program, it will retrieve the stored value.

There is a nifty feature of GConf that lets you notify your program whenever a key's value is changed, either in your program or externally (i.e. via gconf-editor). You only have to attach a GConf.NotifyEventHandler delegate to a key, this way:

gconfClient.AddNotify (key, new GConf.NotifyEventHandler(function));

This is very important for graphic interfaces, as it is a good practice to make the application react instantly to preference changes.

It can't be that simple, can it?

The above example is perfect for a single setting, but what if our application needs to store more settings, and we want to provide default values for some of our preferences for when they aren't set. Or even more, we want to store data types other than only strings. You have three options:

Next we'll cover the schema option in this tutorial, as my library isn't part of the official GConf# bindings. If you're interested in it anyway, you can get more information about it here.

Using schemas turned easy

From the documentation: "A schema describes some configuration key. Its primary purpose is to provide documentation about the key to system administrators manipulating the GConf database. Secondarily, schemas contain a good default value for the key; GConf will automatically return this value when a key is unset. Schemas also include the name of the application that created a key (useful when trying to clean old junk out of the database, for example)".

Using GConf directly from an application is often inconvenient. Because the configuration key system effectively represents a string-based API, accessing GConf values leaves your application open to runtime errors from a single typo. Even if you store your keys in constants (as is recommended), you still wind up maintaining two lists of keys: one in the schema, and one in your app.

Another problem is that the GConf value types are very limited: string, float, int, bool, and list. When storing common value types such as colors or enumerations, one has to serialize these values to and from strings.

Finally, providing graphical dialogs for your settings can be a very tedious process. For each config element exposed in your UI, you have to manually hook up the "changed" signal on the UI to a custom handler setting the GConf value. Inversely, one must initially populate the dialog based on previously-set GConf values. Complicating all of this is the GNOME HIG, which specifies that all preference dialogs be instant-apply if possible.

GConf# addresses these issues with two new utilities: SchemaGen, and the GConf.PropertyEditors library.

SchemaGen

SchemaGen is a new tool which generates a custom API for the settings in a gconf schema file. Given a schema, it generates two classes, Settings and SettingKeys. Settings provides static properties for each specified gconf setting. These properties map directly to the values in the gconf database. Settings also specifies static events for each setting indicating when the setting has changed, and a generic Changed event which notifies when any of your settings have changed. Finally, for every property in Settings, SchemaGen creates a read-only property of the same name in SettingKeys specifying the GConf key used to access this property. SettingKeys is not needed if you are only using the Settings class, but it is very useful when attaching PropertyEditors, or when accessing GConf directly.

SchemaGen also provides for limited marshalling to and from the GConf value types. It currently defines marshallers for System.Drawing.Color and for enumerations. This is done by specifying a cstype directive in the GConf schema.

Example

First, we need to write a GConf schema specifying our settings.

<gconfschemafile>
 <schemalist>
  <schema>
   <key>/schemas/apps/gconfsharp/sample_app/toolbar_alignment</key>
   <applyto>/apps/gconfsharp/sample_app/toolbar_alignment</applyto>
   <owner>sample_app</owner>
  <type>string</type>
  <cstype class="Sample.Alignment">enum</cstype>
   <locale name="C">
    <short>Toolbar alignment</short>
    <long>Where the toolbar should be. Can be one of Top, Bottom, Left, Right.</long>
   </locale>
   <default>Top</default>
  </schema>
  <schema>
   <key>/schemas/apps/gconfsharp/sample_app/text_color</key>
   <applyto>/apps/gconfsharp/sample_app/text_color</applyto>
   <owner>sample_app</owner>
  <type>string</type>
  <cstype>color</cstype>
   <locale name="C">
    <short>Text color</short>
    <long>Text color</long>
   </locale>
   <default>#000000</default>
  </schema>
  <schema>
   <key>/schemas/apps/gconfsharp/sample_app/enable_text_color</key>
   <applyto>/apps/gconfsharp/sample_app/enable_text_color</applyto>
   <owner>sample_app</owner>
   <type>bool</type>
   <locale name="C">
    <short>Show colored text</short>
    <long>Show colored text</long>
   </locale>
   <default>false</default>
  </schema>
 </schemalist>
</gconfschemafile>  

Note how we specify both the cstype and type for the enum and color. This is because GConf doesn't know anything about cstype.

We save this to sample.schema, and install it with the command:

env GCONF_CONFIG_SOURCE="" gconftool-2 --makefile-install-rule sample.schema

GConf generates a warning because it doesn't recognize cstype, but it is nothing to worry about.

We're now ready to run SchemaGen. Installing the schema before running SchemaGen is not required, but you should make sure to install it before running your application, because GConf# will throw an exception if GConf can't find your settings.

SchemaGen's full executable name is gconfsharp-schemagen. It takes a namespace and the schema filename as parameters, and outputs to the console. Here is the command used to generate Settings.cs for sample.schema:

gconfsharp-schemagen Sample sample.schema > Settings.cs

Now we're ready to write a small program using our new settings.

namespace Sample
{
   using System;
   using System.Drawing;
   
   public enum Alignment
   {
     Top,
     Bottom,
     Left,
     Right
   }
   
   class X
   {
      public static void Main (string args[])
      {
         Alignment align = Settings.ToolbarAlignment;
         Console.WriteLine ("Alignment is: {0}", align);
         Settings.ToolbarAlignment = Left;
         Console.WriteLine ("Color is: {0}", Settings.TextColor);
         Console.WriteLine ("Color is enabled: {0}", Settings.TextEnableColor);
      }
   }
}

As you can see, our GConf settings are now nicely encapsulated, and are as easy to access as any other .NET properties.

Property Editors

Property Editors are objects which act as intermediaries between GConf and Gtk+ widgets. They are analoguous to the Controller of the MVC design pattern. Property editors do two things:

This means that the widget and GConf are always in sync, no matter if the value is set through Gtk+ or through GConf. GConf# defines a number of Property Editors which correspond to common widget/setting combinations -- for example, a CheckButton representing a boolean setting.

GConf# also provides a utility class called EditorShell. EditorShell exposes a simple API to automatically construct PropertyEditors for a Glade UI.

As a historical note, and in the interests of giving credit where credit is due, it should be noted that GConf Property Editors are not unique to GConf#. They were originally conceived and implemented by Bradford Hovinen for use in the GNOME Control Center. Although the APIs are somewhat similar, the original version was written in C and was limited to internal use in the Control Center. Similarly, Hovinen's work on GConf Property Editors were based on Dietmar Maurer's Bonobo-Conf Property Editors.

Example

For this example we'll assume you have written a Glade UI for the settings in the previous example. It employs three elements: an option menu named "toolbar_menu", a checkbox named "enable_check", and a color picker named "colorpicker". Given our Glade.XML object, all we have to do is set up the EditorShell.

Glade.XML gxml = ...
GConf.PropertyEditors.EditorShell shell = new GConf.PropertyEditors.EditorShell (gxml);

shell.Add (SettingKeys.ToolbarAlignment, "toolbar_menu", typeof (Sample.Alignment));
shell.Add (SettingKeys.EnableTextColor, "enable_check");
shell.Add (SettingKeys.TextColor, "colorpicker");

That's all that is needed to implement the preferences dialog. Note how we didn't have to specify what type of property editor to use. The EditorShell uses introspection to create the correct Property Editor for all the types of widgets it knows about. Also note that because ToolbarAlignment is an enum, we had to pass its type in so that the Property Editor can store its value correctly. By default, Property Editors assume that the order of elements in an OptionMenu (or RadioButton) correspond to the order of values in an enum. If this is not the case, you can pass in an additional parameter to Add: an array of integers, each array element being the integer representation of an enum value. This array represents the order of values in your GUI element.

Now, a really good preference dialog would "ghost out" the color picker if the "Enable text color" option is disabled. This requires a small addition to our previous code:

shell.AddGuard (SettingKeys.EnableTextColor, "color_box");

Again, for the purposes of the example, "color_box" is the name of the HBox containing the label and color picker corresponding to TextColor.