Using GConf#

Introduction

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.

<gconfschema>
 <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, Rigt.</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>#000000</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.

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.

Last updated: October 19th, 2002
Rachel Hestilow (hestilow@ximian.com)