/* * Copyright (C) 2000-2003 Marco Pesenti Gritti * Copyright (C) 2003, 2004 Christian Persch * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * $Id$ */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "ephy-dialog.h" #include "ephy-glade.h" #include "ephy-state.h" #include "ephy-gui.h" #include "eel-gconf-extensions.h" #include "ephy-debug.h" #include #include #include #include #include #include #include #include enum { PROP_0, PROP_PARENT_WINDOW, PROP_MODAL, PROP_PERSIST_POSITION, PROP_DEFAULT_WIDTH, PROP_DEFAULT_HEIGHT }; typedef enum { PT_TOGGLEBUTTON, PT_RADIOBUTTON, PT_SPINBUTTON, PT_COMBOBOX, PT_EDITABLE, PT_UNKNOWN } WidgetType; typedef struct { const char *id; EphyDialog *dialog; char *pref; EphyDialogApplyType apply_type; GtkWidget *widget; WidgetType widget_type; GType data_type; GList *string_enum; int data_col; gboolean loaded; gboolean sane_state; } PropertyInfo; #define EPHY_DIALOG_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), EPHY_TYPE_DIALOG, EphyDialogPrivate)) struct EphyDialogPrivate { char *name; GHashTable *props; GtkWidget *parent; GtkWidget *dialog; gboolean modal; gboolean has_default_size; gboolean disposing; gboolean initialized; gboolean persist_position; int default_width; int default_height; }; #define SPIN_DELAY 0.20 enum { CHANGED, LAST_SIGNAL }; static guint signals [LAST_SIGNAL] = { 0, }; static void ephy_dialog_class_init (EphyDialogClass *klass); static void ephy_dialog_init (EphyDialog *window); static GObjectClass *parent_class = NULL; GType ephy_dialog_get_type (void) { static GType type = 0; if (type == 0) { static const GTypeInfo our_info = { sizeof (EphyDialogClass), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) ephy_dialog_class_init, NULL, NULL, /* class_data */ sizeof (EphyDialog), 0, /* n_preallocs */ (GInstanceInitFunc) ephy_dialog_init }; type = g_type_register_static (G_TYPE_OBJECT, "EphyDialog", &our_info, 0); } return type; } static PropertyInfo * lookup_info (EphyDialog *dialog, const char *id) { return g_hash_table_lookup (dialog->priv->props, id); } static void set_sensitivity (PropertyInfo *info, gboolean sensitive) { g_return_if_fail (info->widget != NULL); if (info->widget_type == PT_RADIOBUTTON) { GSList *list, *l; list = gtk_radio_button_get_group (GTK_RADIO_BUTTON (info->widget)); for (l = list; l != NULL; l = l->next) { gtk_widget_set_sensitive (GTK_WIDGET (l->data), sensitive); } } else if (info->widget_type == PT_EDITABLE) { gtk_editable_set_editable (GTK_EDITABLE (info->widget), sensitive); } else { gtk_widget_set_sensitive (info->widget, sensitive); } } static void set_value_from_pref (PropertyInfo *info, GValue *value) { char *text; switch (info->data_type) { case G_TYPE_STRING: g_value_init (value, G_TYPE_STRING); text = eel_gconf_get_string (info->pref); g_value_take_string (value, text ? text : g_strdup ("")); break; case G_TYPE_INT: g_value_init (value, G_TYPE_INT); g_value_set_int (value, eel_gconf_get_integer (info->pref)); break; case G_TYPE_FLOAT: g_value_init (value, G_TYPE_FLOAT); g_value_set_float (value, eel_gconf_get_float (info->pref)); break; case G_TYPE_BOOLEAN: g_value_init (value, G_TYPE_BOOLEAN); g_value_set_boolean (value, eel_gconf_get_boolean (info->pref)); break; default: g_warning ("Unsupported value read from pref %s\n", info->pref); break; } } static void set_pref_from_value (PropertyInfo *info, GValue *value) { const char *pref = info->pref; if (!G_VALUE_HOLDS (value, info->data_type)) { g_warning ("Value type mismatch for id[%s], pref[%s]", info->id, info->pref); return; } switch (info->data_type) { case G_TYPE_STRING: eel_gconf_set_string (pref, g_value_get_string (value)); break; case G_TYPE_INT: eel_gconf_set_integer (pref, g_value_get_int (value)); break; case G_TYPE_FLOAT: eel_gconf_set_float (pref, g_value_get_float (value)); break; case G_TYPE_BOOLEAN: eel_gconf_set_boolean (pref, g_value_get_boolean (value)); break; default: break; } } static gboolean set_value_from_editable (PropertyInfo *info, GValue *value) { char *text; gboolean retval = TRUE; gboolean free_text = TRUE; g_return_val_if_fail (GTK_IS_EDITABLE (info->widget), FALSE); text = gtk_editable_get_chars (GTK_EDITABLE (info->widget), 0, -1); g_value_init (value, info->data_type); switch (info->data_type) { case G_TYPE_STRING: g_value_take_string (value, text); free_text = FALSE; break; /* FIXME : handle possible errors in the input for int and float */ case G_TYPE_INT: g_value_set_int (value, atoi (text)); break; case G_TYPE_FLOAT: g_value_set_float (value, strtod (text, NULL)); break; default: retval = FALSE; g_value_unset (value); g_warning ("Unsupported value type for editable %s", info->id); break; } if (free_text) { g_free (text); } return retval; } static gboolean set_value_from_combobox (PropertyInfo *info, GValue *value) { g_return_val_if_fail (GTK_IS_COMBO_BOX (info->widget), FALSE); if (info->data_col != -1) { GtkTreeModel *model; GtkTreeIter iter; if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (info->widget), &iter)) { model = gtk_combo_box_get_model (GTK_COMBO_BOX (info->widget)); gtk_tree_model_get_value (model, &iter, info->data_col, value); return TRUE; } } else if (info->data_type == G_TYPE_INT) { int index; index = gtk_combo_box_get_active (GTK_COMBO_BOX (info->widget)); if (index >= 0) { g_value_init (value, G_TYPE_INT); g_value_set_int (value, index); return TRUE; } } else { g_warning ("Unsupported data type for combo %s\n", info->id); } return FALSE; } static int get_radio_button_active_index (GtkWidget *radiobutton) { GtkToggleButton *toggle_button; GSList *list; int index, i, length; /* get group list */ list = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radiobutton)); length = g_slist_length (list); /* iterate over list to find active button */ for (i = 0; list != NULL; i++, list = list->next) { /* get button and text */ toggle_button = GTK_TOGGLE_BUTTON (list->data); if (gtk_toggle_button_get_active (toggle_button)) { break; } } /* check we didn't run off end */ g_assert (list != NULL); /* return index (reverse order!) */ return index = (length - 1) - i; } static gboolean set_value_from_radiobuttongroup (PropertyInfo *info, GValue *value) { gboolean retval = TRUE; int index; g_return_val_if_fail (GTK_IS_RADIO_BUTTON (info->widget), FALSE); index = get_radio_button_active_index (info->widget); g_return_val_if_fail (index >= 0, FALSE); if (info->data_type == G_TYPE_STRING) { g_return_val_if_fail (info->string_enum != NULL, FALSE); g_return_val_if_fail (g_list_nth_data (info->string_enum, index) != NULL, FALSE); g_value_init (value, G_TYPE_STRING); g_value_set_string (value, (char*) g_list_nth_data (info->string_enum, index)); } else if (info->data_type == G_TYPE_INT) { g_value_init (value, G_TYPE_INT); g_value_set_int (value, index); } else { retval = FALSE; g_warning ("unsupported data type for radio button %s\n", info->id); } return retval; } static gboolean set_value_from_spin_button (PropertyInfo *info, GValue *value) { gboolean retval = TRUE; gdouble f; gboolean is_int; g_return_val_if_fail (GTK_IS_SPIN_BUTTON (info->widget), FALSE); f = gtk_spin_button_get_value (GTK_SPIN_BUTTON (info->widget)); is_int = (gtk_spin_button_get_digits (GTK_SPIN_BUTTON(info->widget)) == 0); if (info->data_type == G_TYPE_INT && is_int) { g_value_init (value, G_TYPE_INT); g_value_set_int (value, (int) f); } else if (info->data_type == G_TYPE_FLOAT) { g_value_init (value, G_TYPE_FLOAT); g_value_set_float (value, f); } else { retval = FALSE; g_warning ("Unsupported data type for spin button %s\n", info->id); } return retval; } static gboolean set_value_from_togglebutton (PropertyInfo *info, GValue *value) { gboolean retval = TRUE; gboolean active; g_return_val_if_fail (GTK_IS_TOGGLE_BUTTON (info->widget), FALSE); active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (info->widget)); if (info->data_type == G_TYPE_BOOLEAN) { g_value_init (value, info->data_type); g_value_set_boolean (value, active); } else { retval = FALSE; g_warning ("Unsupported data type for toggle button %s\n", info->id); } return retval; } static gboolean set_value_from_info (PropertyInfo *info, GValue *value) { gboolean retval; if (info->sane_state == FALSE) { return FALSE; } switch (info->widget_type) { case PT_SPINBUTTON: retval = set_value_from_spin_button (info, value); break; case PT_RADIOBUTTON: retval = set_value_from_radiobuttongroup (info, value); break; case PT_TOGGLEBUTTON: retval = set_value_from_togglebutton (info, value); break; case PT_EDITABLE: retval = set_value_from_editable (info, value); break; case PT_COMBOBOX: retval = set_value_from_combobox (info, value); break; default: retval = FALSE; g_warning ("Unsupported widget type\n"); break; } return retval; } static void set_editable_from_value (PropertyInfo *info, const GValue *value) { char *text = NULL; int pos; g_return_if_fail (GTK_IS_EDITABLE (info->widget)); switch (info->data_type) { case G_TYPE_STRING: text = g_value_dup_string (value); break; case G_TYPE_INT: text = g_strdup_printf ("%d", g_value_get_int (value)); break; case G_TYPE_FLOAT: text = g_strdup_printf ("%.2f", g_value_get_float (value)); break; default: break; } if (text == NULL) { text = g_strdup (""); } info->sane_state = TRUE; gtk_editable_delete_text (GTK_EDITABLE (info->widget), 0, -1); gtk_editable_insert_text (GTK_EDITABLE (info->widget), text, strlen (text), &pos); g_free (text); } static int get_index_from_value (const GValue *value, GList *string_enum) { int index = -1; const char *val; GList *s = NULL; if (string_enum) { val = g_value_get_string (value); if (val) { s = g_list_find_custom (string_enum, val, (GCompareFunc) strcmp); } if (s) { index = g_list_position (string_enum, s); } } else { index = g_value_get_int (value); } return index; } static gboolean compare_values (const GValue *a, const GValue *b) { if (G_VALUE_HOLDS (a, G_TYPE_STRING)) { const char *ta, *tb; ta = g_value_get_string (a); tb = g_value_get_string (b); return (ta && tb && strcmp (ta, tb) == 0); } else if (G_VALUE_HOLDS (a, G_TYPE_INT)) { return g_value_get_int (a) == g_value_get_int (b); } else if (G_VALUE_HOLDS (a, G_TYPE_FLOAT)) { return g_value_get_float (a) == g_value_get_float (b); } else if (G_VALUE_HOLDS (a, G_TYPE_BOOLEAN)) { return g_value_get_boolean (a) == g_value_get_boolean (b); } return FALSE; } static void set_combo_box_from_value (PropertyInfo *info, const GValue *value) { g_return_if_fail (GTK_IS_COMBO_BOX (info->widget)); if (info->data_col != -1) { GValue data = { 0, }; GtkTreeModel *model; GtkTreeIter iter; gboolean valid, found = FALSE; model = gtk_combo_box_get_model (GTK_COMBO_BOX (info->widget)); valid = gtk_tree_model_get_iter_first (model, &iter); while (valid) { gtk_tree_model_get_value (model, &iter, info->data_col, &data); found = compare_values (&data, value); g_value_unset (&data); if (found) break; valid = gtk_tree_model_iter_next (model, &iter); } if (found) { gtk_combo_box_set_active_iter (GTK_COMBO_BOX (info->widget), &iter); } else { char *v; gtk_combo_box_set_active (GTK_COMBO_BOX (info->widget), -1); v = g_strdup_value_contents (value); g_warning ("Value '%s' not found in model for combo %s\n", v, info->id); g_free (v); } info->sane_state = found; } else if (info->data_type == G_TYPE_INT) { int index; index = g_value_get_int (value); info->sane_state = index >= 0; g_return_if_fail (index >= -1); gtk_combo_box_set_active (GTK_COMBO_BOX (info->widget), index); } else { g_warning ("Unsupported data type for combo box %s\n", info->id); } } static void set_radiobuttongroup_from_value (PropertyInfo *info, const GValue *value) { GtkToggleButton *button; GSList *list; gint length; int index; g_return_if_fail (GTK_IS_RADIO_BUTTON (info->widget)); list = gtk_radio_button_get_group (GTK_RADIO_BUTTON (info->widget)); length = g_slist_length (list); index = get_index_from_value (value, info->string_enum); /* new buttons are *prepended* to the list, so button added as first * has last position in the list */ index = (length - 1) - index; if (index < 0 || index >= length) { info->sane_state = FALSE; g_return_if_fail (index >= 0 && index < length); return; } button = GTK_TOGGLE_BUTTON (g_slist_nth_data (list, index)); g_return_if_fail (button != NULL); info->sane_state = TRUE; if (gtk_toggle_button_get_active (button) == FALSE) { gtk_toggle_button_set_active (button, TRUE); } } static void set_spin_button_from_value (PropertyInfo *info, const GValue *value) { gdouble f = 0.0; gboolean is_int; g_return_if_fail (GTK_IS_SPIN_BUTTON (info->widget)); is_int = (gtk_spin_button_get_digits (GTK_SPIN_BUTTON (info->widget)) == 0); if (info->data_type == G_TYPE_INT && is_int) { f = (float) g_value_get_int (value); } else if (info->data_type == G_TYPE_FLOAT) { f = g_value_get_float (value); } else { info->sane_state = FALSE; g_warning ("Unsupported data type for spin button %s\n", info->id); return; } info->sane_state = TRUE; gtk_spin_button_set_value (GTK_SPIN_BUTTON (info->widget), f); } static void set_togglebutton_from_value (PropertyInfo *info, const GValue *value) { gboolean active; g_return_if_fail (GTK_IS_TOGGLE_BUTTON (info->widget)); g_return_if_fail (info->data_type == G_TYPE_BOOLEAN); active = g_value_get_boolean (value); info->sane_state = TRUE; gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (info->widget), active); } static void set_info_from_value (PropertyInfo *info, const GValue *value) { if (!G_VALUE_HOLDS (value, info->data_type)) { g_warning ("Incompatible value types for id %s\n", info->id); return; } switch (info->widget_type) { case PT_SPINBUTTON: set_spin_button_from_value (info, value); break; case PT_RADIOBUTTON: set_radiobuttongroup_from_value (info, value); break; case PT_TOGGLEBUTTON: set_togglebutton_from_value (info, value); break; case PT_EDITABLE: set_editable_from_value (info, value); break; case PT_COMBOBOX: set_combo_box_from_value (info, value); break; default: g_warning ("Unknown widget type\n"); break; } } /* widget changed callbacks */ static void set_pref_from_info_and_emit (PropertyInfo *info) { GValue value = { 0, }; if (!set_value_from_info (info, &value)) { return; } g_signal_emit (info->dialog, signals[CHANGED], g_quark_from_string (info->id), &value); if (info->apply_type == PT_AUTOAPPLY && info->pref != NULL) { set_pref_from_value (info, &value); } g_value_unset (&value); } static void togglebutton_clicked_cb (GtkWidget *widget, PropertyInfo *info) { info->sane_state = TRUE; set_pref_from_info_and_emit (info); } static void radiobutton_clicked_cb (GtkWidget *widget, PropertyInfo *info) { info->sane_state = TRUE; if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widget))) { return; } set_pref_from_info_and_emit (info); } static gboolean spinbutton_timeout_cb (PropertyInfo *info) { GTimer *spin_timer; spin_timer = (GTimer *) g_object_get_data (G_OBJECT (info->widget), "timer"); /* timer still valid? */ if (spin_timer == NULL) { /* don't call me again */ return FALSE; } /* okay, we're ready to set */ if (g_timer_elapsed (spin_timer, NULL) >= SPIN_DELAY) { /* kill off the timer */ g_timer_destroy (spin_timer); g_object_set_data (G_OBJECT (info->widget), "timer", NULL); /* HACK update the spinbutton here so that the * changes made directly in the entry are accepted * and set in the pref. Otherwise the old value is used */ gtk_spin_button_update (GTK_SPIN_BUTTON (info->widget)); info->sane_state = TRUE; set_pref_from_info_and_emit (info); /* done, don't run again */ return FALSE; } /* not elapsed yet, call me again */ return TRUE; } static void spinbutton_changed_cb (GtkWidget *widget, PropertyInfo *info) { GTimer *spin_timer; if (info->apply_type != PT_AUTOAPPLY) return; spin_timer = g_object_get_data (G_OBJECT (info->widget), "timer"); /* destroy an existing timer */ if (spin_timer != NULL) { g_timer_destroy (spin_timer); } /* start tnew timer */ spin_timer = g_timer_new(); g_timer_start (spin_timer); g_object_set_data (G_OBJECT (info->widget), "timer", spin_timer); g_timeout_add (50, (GSourceFunc) spinbutton_timeout_cb, info); } static void changed_cb (GtkWidget *widget, PropertyInfo *info) { info->sane_state = TRUE; set_pref_from_info_and_emit (info); } static void connect_signals (gpointer key, PropertyInfo *info, EphyDialog *dialog) { GSList *list; g_return_if_fail (info->widget != NULL); switch (info->widget_type) { case PT_TOGGLEBUTTON: g_signal_connect (G_OBJECT (info->widget), "clicked", G_CALLBACK (togglebutton_clicked_cb), (gpointer)info); break; case PT_RADIOBUTTON: list = gtk_radio_button_get_group (GTK_RADIO_BUTTON (info->widget)); for (; list != NULL; list = list->next) { g_signal_connect (G_OBJECT (list->data), "clicked", G_CALLBACK (radiobutton_clicked_cb), info); } break; case PT_SPINBUTTON: g_signal_connect (G_OBJECT (info->widget), "changed", G_CALLBACK (spinbutton_changed_cb), info); break; case PT_COMBOBOX: g_signal_connect (G_OBJECT (info->widget), "changed", G_CALLBACK (changed_cb), info); break; case PT_EDITABLE: g_signal_connect (G_OBJECT (info->widget), "changed", G_CALLBACK (changed_cb), info); break; case PT_UNKNOWN: break; } } static void disconnect_signals (gpointer key, PropertyInfo *info, EphyDialog *dialog) { g_return_if_fail (info->widget != NULL); g_signal_handlers_disconnect_matched (info->widget, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, info); } static void init_props (EphyDialog *dialog, const EphyDialogProperty *properties, GladeXML *gxml) { int i; for (i = 0 ; properties[i].id != NULL; i++) { PropertyInfo *info = g_new0 (PropertyInfo, 1); info->id = properties[i].id; info->dialog = dialog; info->pref = g_strdup (properties[i].pref); info->apply_type = properties[i].apply_type; info->string_enum = NULL; info->data_col = -1; info->widget = glade_xml_get_widget (gxml, info->id); if (GTK_IS_COMBO_BOX (info->widget)) { info->widget_type = PT_COMBOBOX; info->data_type = G_TYPE_INT; } else if (GTK_IS_SPIN_BUTTON (info->widget)) { info->widget_type = PT_SPINBUTTON; info->data_type = G_TYPE_INT; } else if (GTK_IS_RADIO_BUTTON (info->widget)) { info->widget_type = PT_RADIOBUTTON; info->data_type = G_TYPE_INT; } else if (GTK_IS_TOGGLE_BUTTON (info->widget)) { info->widget_type = PT_TOGGLEBUTTON; info->data_type = G_TYPE_BOOLEAN; } else if (GTK_IS_EDITABLE (info->widget)) { info->widget_type = PT_EDITABLE; info->data_type = G_TYPE_STRING; } else { info->widget_type = PT_UNKNOWN; info->data_type = G_TYPE_INVALID; } if (properties[i].data_type != 0) { info->data_type = properties[i].data_type; } info->loaded = FALSE; info->sane_state = FALSE; g_hash_table_insert (dialog->priv->props, (char *) info->id, info); } } static void load_info (gpointer key, PropertyInfo *info, EphyDialog *dialog) { GValue value = { 0, }; g_return_if_fail (info->widget != NULL); if (info->pref != NULL) { set_value_from_pref (info, &value); set_info_from_value (info, &value); g_signal_emit (info->dialog, signals[CHANGED], g_quark_from_string (info->id), &value); g_value_unset (&value); set_sensitivity (info, eel_gconf_key_is_writable (info->pref)); } info->loaded = TRUE; } static void save_info (gpointer key, PropertyInfo *info, EphyDialog *dialog) { GValue value = { 0, }; if (info->pref == NULL || info->apply_type != PT_NORMAL) { return; } if (!info->sane_state) { g_warning ("Not persisting insane state of id[%s]", info->id); return; } if (set_value_from_info (info, &value)) { set_pref_from_value (info, &value); g_value_unset (&value); } } static void setup_default_size (EphyDialog *dialog) { if (dialog->priv->has_default_size == FALSE) { EphyStateWindowFlags flags; flags = EPHY_STATE_WINDOW_SAVE_SIZE; if (dialog->priv->persist_position) { flags |= EPHY_STATE_WINDOW_SAVE_POSITION; } ephy_state_add_window (dialog->priv->dialog, dialog->priv->name, dialog->priv->default_width, dialog->priv->default_height, flags); dialog->priv->has_default_size = TRUE; } } static void dialog_destroy_cb (GtkWidget *widget, EphyDialog *dialog) { g_hash_table_foreach (dialog->priv->props, (GHFunc) save_info, dialog); if (dialog->priv->disposing == FALSE) { g_object_unref (dialog); } } static void impl_construct (EphyDialog *dialog, const EphyDialogProperty *properties, const char *file, const char *name, const char *domain) { GladeXML *gxml; gxml = ephy_glade_widget_new (file, name, &(dialog->priv->dialog), dialog, domain); if (dialog->priv->name == NULL) { dialog->priv->name = g_strdup (name); } if (properties) { init_props (dialog, properties, gxml); } g_signal_connect_object (dialog->priv->dialog, "destroy", G_CALLBACK(dialog_destroy_cb), dialog, 0); g_object_unref (gxml); } static void impl_show (EphyDialog *dialog) { if (dialog->priv->initialized == FALSE) { dialog->priv->initialized = TRUE; g_hash_table_foreach (dialog->priv->props, (GHFunc) load_info, dialog); g_hash_table_foreach (dialog->priv->props, (GHFunc) connect_signals, dialog); } setup_default_size (dialog); if (dialog->priv->parent != NULL) { /* make the dialog transient again, because it seems to get * forgotten after gtk_widget_hide */ gtk_window_set_transient_for (GTK_WINDOW (dialog->priv->dialog), GTK_WINDOW (dialog->priv->parent)); } gtk_window_present (GTK_WINDOW (dialog->priv->dialog)); } void ephy_dialog_set_modal (EphyDialog *dialog, gboolean is_modal) { dialog->priv->modal = is_modal; gtk_window_set_modal (GTK_WINDOW(dialog->priv->dialog), is_modal); } void ephy_dialog_add_enum (EphyDialog *dialog, const char *id, guint n_items, const char **items) { PropertyInfo *info; int i = 0; GList *l = NULL; info = lookup_info (dialog, id); g_return_if_fail (info != NULL); for (i = 0; i < n_items; i++) { l = g_list_prepend (l, g_strdup (items[i])); } info->string_enum = g_list_reverse (l); } void ephy_dialog_set_data_column (EphyDialog *dialog, const char *id, int column) { PropertyInfo *info; info = lookup_info (dialog, id); g_return_if_fail (info != NULL); info->data_col = column; } void ephy_dialog_set_pref (EphyDialog *dialog, const char *property_id, const char *pref) { PropertyInfo *info; info = lookup_info (dialog, property_id); g_return_if_fail (info != NULL); disconnect_signals (NULL, info, dialog); info->loaded = FALSE; info->sane_state = FALSE; g_free (info->pref); info->pref = g_strdup (pref); if (dialog->priv->initialized) { /* dialog is already initialised, so initialise this here */ load_info (NULL, info, dialog); connect_signals (NULL, info, dialog); } } void ephy_dialog_set_size_group (EphyDialog *dialog, const char *first_id, ...) { GtkSizeGroup *size_group; va_list vl; size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); va_start (vl, first_id); while (first_id != NULL) { PropertyInfo *info; info = lookup_info (dialog, first_id); g_return_if_fail (info != NULL); g_return_if_fail (info->widget != NULL); gtk_size_group_add_widget (size_group, info->widget); first_id = va_arg (vl, const char*); } va_end (vl); } void ephy_dialog_construct (EphyDialog *dialog, const EphyDialogProperty *properties, const char *file, const char *name, const char *domain) { EphyDialogClass *klass = EPHY_DIALOG_GET_CLASS (dialog); klass->construct (dialog, properties, file, name, domain); } void ephy_dialog_show (EphyDialog *dialog) { EphyDialogClass *klass = EPHY_DIALOG_GET_CLASS (dialog); klass->show (dialog); } void ephy_dialog_hide (EphyDialog *dialog) { g_return_if_fail (EPHY_IS_DIALOG (dialog)); g_return_if_fail (dialog->priv->dialog != NULL); gtk_widget_hide (dialog->priv->dialog); } int ephy_dialog_run (EphyDialog *dialog) { ephy_dialog_show (dialog); return gtk_dialog_run (GTK_DIALOG (dialog->priv->dialog)); } GtkWidget * ephy_dialog_get_control (EphyDialog *dialog, const char *property_id) { PropertyInfo *info; info = lookup_info (dialog, property_id); g_return_val_if_fail (info != NULL, NULL); return info->widget; } gboolean ephy_dialog_get_value (EphyDialog *dialog, const char *property_id, GValue *value) { PropertyInfo *info; info = lookup_info (dialog, property_id); g_return_val_if_fail (info != NULL, FALSE); return set_value_from_info (info, value); } void ephy_dialog_set_value (EphyDialog *dialog, const char *property_id, const GValue *value) { PropertyInfo *info; info = lookup_info (dialog, property_id); g_return_if_fail (info != NULL); set_info_from_value (info, value); } static void free_prop_info (PropertyInfo *info) { if (info->string_enum) { g_list_foreach (info->string_enum, (GFunc)g_free, NULL); g_list_free (info->string_enum); } g_free (info->pref); g_free (info); } static void ephy_dialog_init (EphyDialog *dialog) { dialog->priv = EPHY_DIALOG_GET_PRIVATE (dialog); dialog->priv->parent = NULL; dialog->priv->dialog = NULL; dialog->priv->name = NULL; dialog->priv->initialized = FALSE; dialog->priv->has_default_size = FALSE; dialog->priv->disposing = FALSE; dialog->priv->persist_position = FALSE; dialog->priv->default_width = -1; dialog->priv->default_height = -1; dialog->priv->props = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify) free_prop_info); } static void ephy_dialog_dispose (GObject *object) { EphyDialog *dialog = EPHY_DIALOG (object); if (dialog->priv->dialog) { dialog->priv->disposing = TRUE; gtk_widget_destroy (dialog->priv->dialog); dialog->priv->dialog = NULL; } } static void ephy_dialog_finalize (GObject *object) { EphyDialog *dialog = EPHY_DIALOG (object); g_hash_table_destroy (dialog->priv->props); g_free (dialog->priv->name); G_OBJECT_CLASS (parent_class)->finalize (object); } GtkWidget * ephy_dialog_get_parent (EphyDialog *dialog) { g_return_val_if_fail (EPHY_IS_DIALOG (dialog), NULL); return dialog->priv->parent; } void ephy_dialog_set_parent (EphyDialog *dialog, GtkWidget *parent) { dialog->priv->parent = parent; g_object_notify (G_OBJECT (dialog), "parent-window"); } static void ephy_dialog_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { EphyDialog *dialog = EPHY_DIALOG (object); switch (prop_id) { case PROP_PARENT_WINDOW: ephy_dialog_set_parent (dialog, g_value_get_object (value)); break; case PROP_MODAL: ephy_dialog_set_modal (dialog, g_value_get_boolean (value)); break; case PROP_PERSIST_POSITION: dialog->priv->persist_position = g_value_get_boolean (value); break; case PROP_DEFAULT_WIDTH: dialog->priv->default_width = g_value_get_int (value); break; case PROP_DEFAULT_HEIGHT: dialog->priv->default_height = g_value_get_int (value); break; } } static void ephy_dialog_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { EphyDialog *dialog = EPHY_DIALOG (object); switch (prop_id) { case PROP_PARENT_WINDOW: g_value_set_object (value, dialog->priv->parent); break; case PROP_MODAL: g_value_set_boolean (value, dialog->priv->modal); break; case PROP_PERSIST_POSITION: g_value_set_boolean (value, dialog->priv->persist_position); break; case PROP_DEFAULT_WIDTH: g_value_set_int (value, dialog->priv->default_width); break; case PROP_DEFAULT_HEIGHT: g_value_set_int (value, dialog->priv->default_height); break; } } static void ephy_dialog_class_init (EphyDialogClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); parent_class = g_type_class_peek_parent (klass); object_class->finalize = ephy_dialog_finalize; object_class->dispose = ephy_dialog_dispose; object_class->set_property = ephy_dialog_set_property; object_class->get_property = ephy_dialog_get_property; klass->construct = impl_construct; klass->show = impl_show; signals[CHANGED] = g_signal_new ("changed", EPHY_TYPE_DIALOG, G_SIGNAL_RUN_FIRST | G_SIGNAL_DETAILED, G_STRUCT_OFFSET (EphyDialogClass, changed), NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER); g_object_class_install_property (object_class, PROP_PARENT_WINDOW, g_param_spec_object ("parent-window", "Parent window", "Parent window", GTK_TYPE_WINDOW, G_PARAM_READWRITE)); g_object_class_install_property (object_class, PROP_MODAL, g_param_spec_boolean ("Modal", "Modal", "Modal dialog", FALSE, G_PARAM_READWRITE)); g_object_class_install_property (object_class, PROP_PERSIST_POSITION, g_param_spec_boolean ("persist-position", "Persist position", "Persist dialog position", FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_DEFAULT_WIDTH, g_param_spec_int ("default-width", "Default width", "Default dialog width", -1, G_MAXINT, -1, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_DEFAULT_HEIGHT, g_param_spec_int ("default-height", "Default height", "Default dialog height", -1, G_MAXINT, -1, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_type_class_add_private (object_class, sizeof (EphyDialogPrivate)); } EphyDialog * ephy_dialog_new (void) { return EPHY_DIALOG (g_object_new (EPHY_TYPE_DIALOG, NULL)); } EphyDialog * ephy_dialog_new_with_parent (GtkWidget *parent_window) { g_return_val_if_fail (parent_window != NULL, NULL); return EPHY_DIALOG (g_object_new (EPHY_TYPE_DIALOG, "parent-window", parent_window, NULL)); }