Updated Korean translation
[anjuta.git] / libanjuta / anjuta-preferences.c
blobeafabef586fb24e8c9d5d9855cbebd1b092d08f1
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3 * preferences.c
4 * Copyright (C) 2000 - 2003 Naba Kumar <naba@gnome.org>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 /**
22 * SECTION:anjuta-preferences
23 * @short_description: Anjuta Preferences system.
24 * @see_also: #AnjutaPreferencesDialog
25 * @stability: Unstable
26 * @include: libanjuta/anjuta-preferences.h
28 * #AnjutaPreferences is a way to let plugins register their preferences. There
29 * are mainly two ways a plugin could register its preferences in Anjuta.
31 * First is to not use #AnjutaPreferences at all. Simply register a
32 * preferences page in #AnjutaPreferencesDialog using the function
33 * anjuta_preferences_dialog_add_page(). The plugin should take
34 * care of loading, saving and widgets synchronization of the
35 * preferences values. This can be done using #GSettings bindings
36 * for example.
38 * Second is to use anjuta_preferences_add_page(), which will
39 * automatically register the preferences keys and values from
40 * a glade xml file. The glade xml file contains a preferences
41 * page of the plugin. The widget names in the page are
42 * given in a particular way (see anjuta_preferences_add_page()) to
43 * let it know property key details. The preference dialog will automatically
44 * setup the bindings between GSettings and the widgets.
48 #ifdef HAVE_CONFIG_H
49 # include <config.h>
50 #endif
52 #include <sys/stat.h>
53 #include <unistd.h>
54 #include <string.h>
55 #include <stdlib.h>
57 #include <libanjuta/anjuta-preferences.h>
58 #include <libanjuta/anjuta-utils.h>
59 #include <libanjuta/resources.h>
60 #include <libanjuta/anjuta-debug.h>
61 #include <libanjuta/interfaces/ianjuta-preferences.h>
63 /* AnjutaPreferences is a singleton */
64 static AnjutaPreferences* default_preferences = NULL;
66 struct _AnjutaPreferencesPriv
68 GtkWidget *prefs_dialog;
69 AnjutaPluginManager *plugin_manager;
71 gchar *common_schema_id;
72 GHashTable *common_gsettings;
75 #define PREFERENCE_PROPERTY_PREFIX "preferences"
77 G_DEFINE_TYPE (AnjutaPreferences, anjuta_preferences, G_TYPE_OBJECT);
79 static GVariant*
80 string_to_gdkcolor (const GValue* value, const GVariantType* type, gpointer user_data)
82 GdkColor* color = g_value_get_boxed (value);
83 gchar* name = gdk_color_to_string (color);
85 GVariant* variant = g_variant_new_string (name);
86 g_free (name);
88 return variant;
91 static gboolean
92 gdkcolor_to_string (GValue* value, GVariant* variant, gpointer user_data)
94 GdkColor color;
95 gdk_color_parse (g_variant_get_string (variant, NULL), &color);
96 g_value_set_boxed (value, &color);
97 return TRUE;
100 static GVariant*
101 active_to_string (const GValue* value, const GVariantType* type, gpointer user_data)
103 GtkComboBox* combo = GTK_COMBO_BOX(user_data);
105 return g_variant_new_string (gtk_combo_box_get_active_id (combo));
108 static gboolean
109 string_to_active (GValue* value, GVariant* variant, gpointer user_data)
111 GtkComboBox* combo = GTK_COMBO_BOX(user_data);
113 gtk_combo_box_set_active_id (combo, g_variant_get_string (variant, NULL));
114 g_value_set_int (value, gtk_combo_box_get_active (combo));
116 return g_value_get_int (value) >= 0;
119 static void
120 update_file_property (GtkWidget* widget, gpointer user_data)
122 GtkFileChooser *chooser = GTK_FILE_CHOOSER (user_data);
123 GSettings *gsettings = g_object_get_data (G_OBJECT (chooser), "anjuta-bind-gsettings");
124 const gchar *key = g_object_get_data (G_OBJECT (chooser), "anjuta-bind-key");
125 gchar* text_value = gtk_file_chooser_get_filename (chooser);
127 g_settings_set_string (gsettings, key, text_value);
129 g_free (text_value);
133 * anjuta_preferences_register_property:
134 * @pr: a #AnjutaPreferences object
135 * @settings: the #GSettings object associated with that property
136 * @object: Widget to register
137 * @key: Property key
139 * This registers only one widget. The widget could be shown elsewhere.
140 * The widget needs to fulfill the properties described in
141 * #anjuta_preferences_add_page documentation.
143 * Return value: TRUE if sucessful.
145 gboolean
146 anjuta_preferences_register_property (AnjutaPreferences *pr,
147 GSettings *settings,
148 GtkWidget *object,
149 const gchar *key)
151 gboolean ok = TRUE;
153 g_return_val_if_fail (ANJUTA_IS_PREFERENCES (pr), FALSE);
154 g_return_val_if_fail (GTK_IS_WIDGET (object), FALSE);
155 g_return_val_if_fail (strlen(key) > 0, FALSE);
157 /* Start with the most specialized widget as a GtkSpinButton
158 * is a GtkEntry too */
159 if (GTK_IS_COLOR_BUTTON (object))
161 g_settings_bind_with_mapping (settings, key,
162 object, "color",
163 G_SETTINGS_BIND_DEFAULT,
164 gdkcolor_to_string,
165 string_to_gdkcolor,
166 object,
167 NULL);
169 else if (GTK_IS_FONT_BUTTON (object))
171 g_settings_bind (settings, key, object, "font-name",
172 G_SETTINGS_BIND_DEFAULT);
174 else if (GTK_IS_SPIN_BUTTON (object))
176 g_settings_bind (settings, key, object, "value",
177 G_SETTINGS_BIND_DEFAULT);
179 else if (GTK_IS_FILE_CHOOSER_BUTTON (object))
181 gchar *filename;
183 switch (gtk_file_chooser_get_action (GTK_FILE_CHOOSER (object)))
185 case GTK_FILE_CHOOSER_ACTION_OPEN:
186 case GTK_FILE_CHOOSER_ACTION_SAVE:
187 filename = g_settings_get_string (settings, key);
188 gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (object),
189 filename);
190 g_free (filename);
191 g_object_set_data_full (G_OBJECT (object), "anjuta-bind-gsettings", g_object_ref (settings), (GDestroyNotify)g_object_unref);
192 g_object_set_data_full (G_OBJECT (object), "anjuta-bind-key", g_strdup (key), (GDestroyNotify)g_free);
193 g_signal_connect (G_OBJECT (object), "file-set",
194 G_CALLBACK (update_file_property), object);
195 break;
196 case GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER:
197 case GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER:
198 filename = g_settings_get_string (settings, key);
199 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (object),
200 filename);
201 g_free (filename);
202 g_object_set_data_full (G_OBJECT (object), "anjuta-bind-gsettings", g_object_ref (settings), (GDestroyNotify)g_object_unref);
203 g_object_set_data_full (G_OBJECT (object), "anjuta-bind-key", g_strdup (key), (GDestroyNotify)g_free);
204 g_signal_connect (G_OBJECT(object), "current-folder-changed",
205 G_CALLBACK (update_file_property), object);
206 break;
207 default:
208 ok = FALSE;
211 else if (GTK_IS_COMBO_BOX (object))
213 g_settings_bind_with_mapping (settings, key,
214 object, "active",
215 G_SETTINGS_BIND_DEFAULT,
216 string_to_active,
217 active_to_string,
218 object,
219 NULL);
221 else if (GTK_IS_CHECK_BUTTON (object))
223 g_settings_bind (settings, key, object, "active",
224 G_SETTINGS_BIND_DEFAULT);
226 else if (GTK_IS_ENTRY (object))
228 g_settings_bind (settings, key, object, "text",
229 G_SETTINGS_BIND_DEFAULT);
231 else
233 ok = FALSE;
236 return ok;
240 * anjuta_preferences_register_all_properties_from_builder_xml:
241 * @pr: a #AnjutaPreferences Object
242 * @builder: GtkBuilder object containing the properties widgets.
243 * @parent: Parent widget in the builder object
245 * This will register all the properties names of the format described above
246 * without considering the UI. Useful if you have the widgets shown elsewhere
247 * but you want them to be part of preferences system.
249 void
250 anjuta_preferences_register_all_properties_from_builder_xml (AnjutaPreferences *pr,
251 GtkBuilder *builder,
252 GSettings *settings,
253 GtkWidget *parent)
255 GSList *widgets;
256 GSList *node;
258 g_return_if_fail (ANJUTA_IS_PREFERENCES (pr));
259 g_return_if_fail (builder != NULL);
261 widgets = gtk_builder_get_objects (builder);
262 for (node = widgets; node != NULL; node = g_slist_next (node))
264 const gchar *name;
265 const gchar *key;
266 const gchar *ptr;
267 GtkWidget *widget, *p;
268 gboolean cont_flag = FALSE;
269 GSettings *key_settings = settings;
271 if (!GTK_IS_WIDGET (node->data) || !GTK_IS_BUILDABLE (node->data))
272 continue;
274 widget = node->data;
275 name = gtk_buildable_get_name (GTK_BUILDABLE (widget));
277 if (!g_str_has_prefix (name, PREFERENCE_PROPERTY_PREFIX))
278 continue;
280 /* Only ':' is needed between "preferences" and the key name but accept
281 * '_' and additional fields separated by ':' to work with the old
282 * widget naming scheme. */
283 key = &name[strlen (PREFERENCE_PROPERTY_PREFIX)];
284 if ((*key != '_') && (*key != ':'))
285 continue;
287 for (ptr = ++key; *ptr != '\0'; ptr++)
289 if (*ptr == ':') key = ptr + 1;
291 if (*key == '\0') continue;
293 /* Check if we need to use common settings */
294 if (*key == '.')
296 const gchar *id = strrchr (key, '.');
297 GString *schema_id;
299 schema_id = g_string_new (pr->priv->common_schema_id);
300 if (key != id)
302 g_string_append_len (schema_id, key, id - key);
304 key = id + 1;
306 key_settings = (GSettings *)g_hash_table_lookup (pr->priv->common_gsettings, schema_id->str);
307 if (key_settings == NULL)
309 key_settings = g_settings_new (schema_id->str);
310 g_hash_table_insert (pr->priv->common_gsettings, schema_id->str, key_settings);
311 g_string_free (schema_id, FALSE);
313 else
315 g_string_free (schema_id, TRUE);
319 /* Added only if it's a descendant child of the parent */
320 p = gtk_widget_get_parent (widget);
321 while (p != parent)
323 if (p == NULL)
325 cont_flag = TRUE;
326 break;
328 p = gtk_widget_get_parent (p);
330 if (cont_flag)
331 continue;
333 if (!anjuta_preferences_register_property (pr, key_settings, widget, key))
335 g_critical ("Invalid preference widget named %s, check anjuta_preferences_add_page function documentation.", name);
341 * anjuta_preferences_add_page:
342 * @pr: a #AnjutaPreferences object
343 * @builder: #GtkBuilder object containing the preferences page
344 * @settings: the #GSettings object associated with that page
345 * @gwidget_name: Page widget name (as give with glade interface editor).
346 * The widget will be searched with the given name and detached
347 * (that is, removed from the container, if present) from it's parent.
348 * @icon_filename: File name (of the form filename.png) of the icon representing
349 * the preference page.
351 * Add a page to the preferences sytem.
352 * builder is the GtkBuilder object of the dialog containing the page widget.
353 * The dialog will contain the layout of the preferences widgets.
354 * The widgets which are preference widgets (e.g. toggle button) should have
355 * widget names of the form:
357 * <programlisting>
358 * preferences(_.*)?:(.SCHEMAID)?PROPERTYKEY
359 * where,
360 * SCHEMAID if present the key will be added not in the page settings but
361 * in common settings using SCHEMAID as a suffix.
362 * PROPERTYKEY is the property key. e.g - 'tab-size'.
363 * </programlisting>
365 * The widget must derivated from one of the following Gtk widget:
366 * * GtkColorButton
367 * * GtkFontButton
368 * * GtkSpinButton
369 * * GtkFileChooserButton
370 * * GtkComboBox
371 * * GtkCheckButton
372 * * GtkEntry
374 * In addition, the model of a GtkComboBox must have an id column.
375 * It is the value of this column which is saved as preference.
377 * All widgets having the above names in the gxml tree will be registered
378 * and will become part of auto saving/loading. For example, refer to
379 * anjuta preferences dialogs and study the widget names.
381 * Older versions of Anjuta use more fields in the widget name. They do not
382 * need an id column in GtkComboBox model and could work with a custom widget.
384 void
385 anjuta_preferences_add_from_builder (AnjutaPreferences *pr,
386 GtkBuilder *builder,
387 GSettings *settings,
388 const gchar *widget_name,
389 const gchar *title,
390 const gchar *icon_filename)
392 GtkWidget *parent;
393 GtkWidget *page;
394 GdkPixbuf *pixbuf;
395 gchar *image_path;
397 g_return_if_fail (ANJUTA_IS_PREFERENCES (pr));
398 g_return_if_fail (widget_name != NULL);
399 g_return_if_fail (icon_filename != NULL);
401 page = GTK_WIDGET(gtk_builder_get_object (builder, widget_name));
402 g_object_ref (page);
403 g_return_if_fail (GTK_IS_WIDGET (page));
404 parent = gtk_widget_get_parent (page);
405 if (parent && GTK_IS_CONTAINER (parent))
407 if (GTK_IS_NOTEBOOK (parent))
409 gint page_num;
411 page_num = gtk_notebook_page_num (GTK_NOTEBOOK (parent), page);
412 gtk_notebook_remove_page (GTK_NOTEBOOK (parent), page_num);
414 else
416 gtk_container_remove (GTK_CONTAINER (parent), page);
419 image_path = anjuta_res_get_pixmap_file (icon_filename);
420 pixbuf = gdk_pixbuf_new_from_file (image_path, NULL);
421 anjuta_preferences_dialog_add_page (ANJUTA_PREFERENCES_DIALOG (pr->priv->prefs_dialog),
422 widget_name, title, pixbuf, page);
423 anjuta_preferences_register_all_properties_from_builder_xml (pr, builder, settings, page);
424 g_object_unref (page);
425 g_free (image_path);
426 g_object_unref (pixbuf);
429 void
430 anjuta_preferences_remove_page (AnjutaPreferences *pr,
431 const gchar *page_name)
433 if (pr->priv->prefs_dialog)
435 anjuta_preferences_dialog_remove_page (ANJUTA_PREFERENCES_DIALOG (pr->priv->prefs_dialog),
436 page_name);
440 static void
441 on_preferences_dialog_destroyed (GtkWidget *preferencess_dialog,
442 AnjutaPreferences *pr)
444 GList *plugins;
445 GList *current_plugin;
447 plugins = anjuta_plugin_manager_get_active_plugin_objects (pr->priv->plugin_manager);
448 current_plugin = plugins;
450 while (current_plugin)
452 if (IANJUTA_IS_PREFERENCES (current_plugin->data))
454 ianjuta_preferences_unmerge (IANJUTA_PREFERENCES (current_plugin->data),
455 pr, NULL);
458 current_plugin = g_list_next (current_plugin);
462 g_object_unref (pr->priv->prefs_dialog);
464 g_list_free (plugins);
465 pr->priv->prefs_dialog = NULL;
470 * anjuta_preferences_get_dialog:
471 * @pr: AnjutaPreferences object
473 * Returns: The preference dialog. Creates the dialog if it doesn't exist
475 GtkWidget *
476 anjuta_preferences_get_dialog (AnjutaPreferences *pr)
478 GList *plugins;
479 GList *current_plugin;
481 if (pr->priv->prefs_dialog)
482 return pr->priv->prefs_dialog;
483 else
485 pr->priv->prefs_dialog = anjuta_preferences_dialog_new ();
487 g_signal_connect (G_OBJECT (pr->priv->prefs_dialog), "destroy",
488 G_CALLBACK (on_preferences_dialog_destroyed),
489 pr);
491 plugins = anjuta_plugin_manager_get_active_plugin_objects (pr->priv->plugin_manager);
492 current_plugin = plugins;
494 while (current_plugin)
496 if (IANJUTA_IS_PREFERENCES (current_plugin->data))
498 ianjuta_preferences_merge (IANJUTA_PREFERENCES (current_plugin->data),
499 pr, NULL);
502 current_plugin = g_list_next (current_plugin);
505 g_list_free (plugins);
507 return g_object_ref_sink (pr->priv->prefs_dialog);
512 * anjuta_preferences_is_dialog_created:
513 * @pr: AnjutaPreferences
515 * Returns: Whether the preference dialog was already created
517 gboolean
518 anjuta_preferences_is_dialog_created (AnjutaPreferences *pr)
520 return (pr->priv->prefs_dialog != NULL);
523 static void
524 anjuta_preferences_dispose (GObject *obj)
526 AnjutaPreferences *pr = ANJUTA_PREFERENCES (obj);
528 if (pr->priv->common_gsettings)
530 g_hash_table_destroy (pr->priv->common_gsettings);
531 pr->priv->common_gsettings = NULL;
533 g_free (pr->priv->common_schema_id);
534 pr->priv->common_schema_id = NULL;
537 static void
538 anjuta_preferences_init (AnjutaPreferences *pr)
540 pr->priv = g_new0 (AnjutaPreferencesPriv, 1);
543 static void
544 anjuta_preferences_finalize (GObject *obj)
546 AnjutaPreferences *pr = ANJUTA_PREFERENCES (obj);
548 if (pr->priv->prefs_dialog)
549 gtk_widget_destroy (pr->priv->prefs_dialog);
551 g_object_unref (pr->priv->plugin_manager);
552 g_free (pr->priv);
555 static void
556 anjuta_preferences_class_init (AnjutaPreferencesClass *class)
558 GObjectClass *object_class = G_OBJECT_CLASS (class);
560 object_class->dispose = anjuta_preferences_dispose;
561 object_class->finalize = anjuta_preferences_finalize;
565 * anjuta_preferences_new:
566 * @plugin_manager: #AnjutaPluginManager to be used
567 * @common_schema_id: Common schema id used for key starting with .
569 * Creates a new #AnjutaPreferences object
571 * Return value: A #AnjutaPreferences object.
573 AnjutaPreferences *
574 anjuta_preferences_new (AnjutaPluginManager *plugin_manager, const gchar *common_schema_id)
576 AnjutaPreferences *pr;
578 if (!default_preferences)
580 pr = g_object_new (ANJUTA_TYPE_PREFERENCES, NULL);
581 pr->priv->plugin_manager = g_object_ref (plugin_manager);
582 pr->priv->common_schema_id = g_strdup (common_schema_id);
583 pr->priv->common_gsettings = g_hash_table_new_full (g_str_hash, g_str_equal,
584 g_free,
585 (GDestroyNotify) g_object_unref);
586 default_preferences = pr;
587 return pr;
589 else
590 return default_preferences;
595 * anjuta_preferences_default:
597 * Get the default instace of anjuta preferences
599 * Return value: A #AnjutaPreferences object.
601 AnjutaPreferences *anjuta_preferences_default ()
603 return default_preferences;