Initial import of ephy (rev# 7126) from svn
[ephy-soc.git] / lib / .svn / text-base / ephy-dialog.c.svn-base
bloba94a312fc28650f6dd24cd3462f4007dcbe9b041
1 /*
2  *  Copyright © 2000-2003 Marco Pesenti Gritti
3  *  Copyright © 2003, 2004 Christian Persch
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  *
19  *  $Id$
20  */
22 #include "config.h"
24 #include "ephy-dialog.h"
25 #include "ephy-state.h"
26 #include "ephy-gui.h"
27 #include "eel-gconf-extensions.h"
28 #include "ephy-debug.h"
30 #include <stdlib.h>
31 #include <string.h>
32 #include <gtk/gtktogglebutton.h>
33 #include <gtk/gtkradiobutton.h>
34 #include <gtk/gtkcombobox.h>
35 #include <gtk/gtkspinbutton.h>
36 #include <gtk/gtkeditable.h>
37 #include <gtk/gtkentry.h>
38 #include <gtk/gtksizegroup.h>
39 #include <gtk/gtkdialog.h>
40 #include <glade/glade-xml.h>
42 enum
44         PROP_0,
45         PROP_PARENT_WINDOW,
46         PROP_MODAL,
47         PROP_PERSIST_POSITION,
48         PROP_DEFAULT_WIDTH,
49         PROP_DEFAULT_HEIGHT
52 typedef enum
54         PT_TOGGLEBUTTON,
55         PT_RADIOBUTTON,
56         PT_SPINBUTTON,
57         PT_COMBOBOX,
58         PT_EDITABLE,
59         PT_UNKNOWN
60 } WidgetType;
62 typedef struct
64         const char *id;
65         EphyDialog *dialog;
66         char *pref;
67         EphyDialogApplyType apply_type;
68         GtkWidget *widget;
69         WidgetType widget_type;
70         GType data_type;
71         GList *string_enum;
72         int data_col;
73         gboolean loaded;
74         gboolean sane_state;
75 } PropertyInfo;
77 #define EPHY_DIALOG_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), EPHY_TYPE_DIALOG, EphyDialogPrivate))
79 struct _EphyDialogPrivate
81         char *name;
83         GHashTable *props;
84         GtkWidget *parent;
85         GtkWidget *dialog;
87         guint modal : 1;
88         guint has_default_size : 1;
89         guint disposing : 1;
90         guint initialized : 1;
91         guint persist_position : 1;
92         int default_width;
93         int default_height;
96 #define SPIN_DELAY 0.20
98 enum
100         CHANGED,
101         LAST_SIGNAL
104 static guint signals [LAST_SIGNAL] = { 0, };
106 static void ephy_dialog_class_init (EphyDialogClass *klass);
107 static void ephy_dialog_init       (EphyDialog *window);
109 static GObjectClass *parent_class = NULL;
111 GType
112 ephy_dialog_get_type (void)
114         static GType type = 0;
116         if (G_UNLIKELY (type == 0))
117         {
118                 const GTypeInfo our_info =
119                 {
120                         sizeof (EphyDialogClass),
121                         NULL, /* base_init */
122                         NULL, /* base_finalize */
123                         (GClassInitFunc) ephy_dialog_class_init,
124                         NULL,
125                         NULL, /* class_data */
126                         sizeof (EphyDialog),
127                         0, /* n_preallocs */
128                         (GInstanceInitFunc) ephy_dialog_init
129                 };
131                 type = g_type_register_static (G_TYPE_OBJECT,
132                                                "EphyDialog",
133                                                &our_info, 0);
134         }
136         return type;
139 static PropertyInfo *
140 lookup_info (EphyDialog *dialog, const char *id)
142         return g_hash_table_lookup (dialog->priv->props, id);
145 static void
146 set_sensitivity (PropertyInfo *info, gboolean sensitive)
148         g_return_if_fail (info->widget != NULL);
150         if (info->widget_type == PT_RADIOBUTTON)
151         {
152                 GSList *list, *l;
154                 list = gtk_radio_button_get_group (GTK_RADIO_BUTTON (info->widget));
156                 for (l = list; l != NULL; l = l->next)
157                 {
158                         gtk_widget_set_sensitive (GTK_WIDGET (l->data), sensitive);
159                 }
160         }
161         else if (info->widget_type == PT_EDITABLE)
162         {
163                 gtk_editable_set_editable (GTK_EDITABLE (info->widget), sensitive);
164         }
165         else
166         {
167                 gtk_widget_set_sensitive (info->widget, sensitive);
168         }
171 static void
172 set_value_from_pref (PropertyInfo *info, GValue *value)
174         char *text;
176         switch (info->data_type)
177         {
178                 case G_TYPE_STRING:
179                         g_value_init (value, G_TYPE_STRING);
180                         text = eel_gconf_get_string (info->pref);
181                         g_value_take_string (value, text);
182                         break;
183                 case G_TYPE_INT:
184                         g_value_init (value, G_TYPE_INT);
185                         g_value_set_int (value, eel_gconf_get_integer (info->pref));
186                         break;
187                 case G_TYPE_FLOAT:
188                         g_value_init (value, G_TYPE_FLOAT);
189                         g_value_set_float (value, eel_gconf_get_float (info->pref));
190                         break;
191                 case G_TYPE_BOOLEAN:
192                         g_value_init (value, G_TYPE_BOOLEAN);
193                         g_value_set_boolean (value, eel_gconf_get_boolean (info->pref));
194                         break;
195                 default:
196                         g_warning ("Unsupported value read from pref %s\n", info->pref);
197                         break;
198         }
201 static void
202 set_pref_from_value (PropertyInfo *info, GValue *value)
204         const char *pref = info->pref;
206         if (!G_VALUE_HOLDS (value, info->data_type))
207         {
208                 g_warning ("Value type mismatch for id[%s], pref[%s]", info->id, info->pref);
209                 return;
210         }
212         switch (info->data_type)
213         {
214                 case G_TYPE_STRING:
215                 {
216                         const char *string = g_value_get_string (value);
217                         if (string != NULL)
218                         {
219                                 eel_gconf_set_string (pref, string);
220                         }
221                         else
222                         {
223                                 eel_gconf_unset_key (pref);
224                         }
225                         break;
226                 }
227                 case G_TYPE_INT:
228                         eel_gconf_set_integer (pref, g_value_get_int (value));
229                         break;
230                 case G_TYPE_FLOAT:
231                         eel_gconf_set_float (pref, g_value_get_float (value));
232                         break;
233                 case G_TYPE_BOOLEAN:
234                         eel_gconf_set_boolean (pref, g_value_get_boolean (value));
235                         break;
236                 default:
237                         break;
238         }
241 static gboolean
242 set_value_from_editable (PropertyInfo *info, GValue *value)
244         char *text;
245         gboolean retval = TRUE;
246         gboolean free_text = TRUE;
248         g_return_val_if_fail (GTK_IS_EDITABLE (info->widget), FALSE);
250         text = gtk_editable_get_chars (GTK_EDITABLE (info->widget), 0, -1);
252         g_value_init (value, info->data_type);
253         switch (info->data_type)
254         {
255                 case G_TYPE_STRING:
256                         g_value_take_string (value, text);
257                         free_text = FALSE;
258                         break;
259                 /* FIXME : handle possible errors in the input for int and float */
260                 case G_TYPE_INT:
261                         g_value_set_int (value, atoi (text));
262                         break;
263                 case G_TYPE_FLOAT:
264                         g_value_set_float (value, strtod (text, NULL));
265                         break;
266                 default:
267                         retval = FALSE;
268                         g_value_unset (value);
269                         g_warning ("Unsupported value type for editable %s", info->id);
270                         break;
271         }
273         if (free_text)
274         {
275                 g_free (text);
276         }
278         return retval;
281 static gboolean
282 set_value_from_combobox (PropertyInfo *info, GValue *value)
284         g_return_val_if_fail (GTK_IS_COMBO_BOX (info->widget), FALSE);
286         if (info->data_col != -1)
287         {
288                 GtkTreeModel *model;
289                 GtkTreeIter iter;
291                 if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (info->widget), &iter))
292                 {
293                         model = gtk_combo_box_get_model (GTK_COMBO_BOX (info->widget));
294                         gtk_tree_model_get_value (model, &iter, info->data_col, value);
296                         return TRUE;
297                 }
298         }
299         else if (info->data_type == G_TYPE_INT)
300         {
301                 int index;
303                 index = gtk_combo_box_get_active (GTK_COMBO_BOX (info->widget));
305                 if (index >= 0)
306                 {
307                         g_value_init (value, G_TYPE_INT);
308                         g_value_set_int (value, index);
310                         return TRUE;
311                 }
312         }
313         else
314         {
315                 g_warning ("Unsupported data type for combo %s\n", info->id);
316         }
318         return FALSE;
321 static int
322 get_radio_button_active_index (GtkWidget *radiobutton)
324         GtkToggleButton *toggle_button;
325         GSList *list;
326         int index, i, length;
328         /* get group list */
329         list = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radiobutton));
330         length = g_slist_length (list);
332         /* iterate over list to find active button */
333         for (i = 0; list != NULL; i++, list = list->next)
334         {
335                 /* get button and text */
336                 toggle_button = GTK_TOGGLE_BUTTON (list->data);
337                 if (gtk_toggle_button_get_active (toggle_button))
338                 {
339                         break;
340                 }
341         }
343         /* check we didn't run off end */
344         g_assert (list != NULL);
346         /* return index (reverse order!) */
347         return index = (length - 1) - i;
350 static gboolean
351 set_value_from_radiobuttongroup (PropertyInfo *info, GValue *value)
353         gboolean retval = TRUE;
354         int index;
356         g_return_val_if_fail (GTK_IS_RADIO_BUTTON (info->widget), FALSE);
358         index = get_radio_button_active_index (info->widget);
359         g_return_val_if_fail (index >= 0, FALSE);
361         if (info->data_type == G_TYPE_STRING)
362         {
363                 g_return_val_if_fail (info->string_enum != NULL, FALSE);
364                 g_return_val_if_fail (g_list_nth_data (info->string_enum, index) != NULL, FALSE);
366                 g_value_init (value, G_TYPE_STRING);
367                 g_value_set_string (value, (char*) g_list_nth_data (info->string_enum, index));
368         }
369         else if (info->data_type == G_TYPE_INT)
370         {
371                 g_value_init (value, G_TYPE_INT);
372                 g_value_set_int (value, index);
373         }
374         else
375         {
376                 retval = FALSE;
377                 g_warning ("unsupported data type for radio button %s\n", info->id);
378         }
380         return retval;
383 static gboolean
384 set_value_from_spin_button (PropertyInfo *info, GValue *value)
386         gboolean retval = TRUE;
387         gdouble f;
388         gboolean is_int;
390         g_return_val_if_fail (GTK_IS_SPIN_BUTTON (info->widget), FALSE);
392         f = gtk_spin_button_get_value (GTK_SPIN_BUTTON (info->widget));
394         is_int = (gtk_spin_button_get_digits (GTK_SPIN_BUTTON(info->widget)) == 0);
396         if (info->data_type == G_TYPE_INT && is_int)
397         {
398                 g_value_init (value, G_TYPE_INT);
399                 g_value_set_int (value, (int) f);
400         }
401         else if (info->data_type == G_TYPE_FLOAT)
402         {
403                 g_value_init (value, G_TYPE_FLOAT);
404                 g_value_set_float (value, f);
405         }
406         else
407         {
408                 retval = FALSE;
409                 g_warning ("Unsupported data type for spin button %s\n", info->id);
410         }
412         return retval;
415 static gboolean
416 set_value_from_togglebutton (PropertyInfo *info, GValue *value)
418         gboolean retval = TRUE;
419         gboolean active;
421         g_return_val_if_fail (GTK_IS_TOGGLE_BUTTON (info->widget), FALSE);
423         active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (info->widget));
425         if (info->apply_type & PT_INVERTED)
426         {
427                 active = !active;
428         }
430         if (info->data_type == G_TYPE_BOOLEAN)
431         {
432                 g_value_init (value, info->data_type);
433                 g_value_set_boolean (value, active);
434         }
435         else
436         {
437                 retval = FALSE;
438                 g_warning ("Unsupported data type for toggle button %s\n", info->id);
439         }
441         return retval;
444 static gboolean
445 set_value_from_info (PropertyInfo *info, GValue *value)
447         gboolean retval;
449         if (info->sane_state == FALSE)
450         {
451                 return FALSE;
452         }
454         switch (info->widget_type)
455         {
456                 case PT_SPINBUTTON:
457                         retval = set_value_from_spin_button (info, value);
458                         break;
459                 case PT_RADIOBUTTON:
460                         retval = set_value_from_radiobuttongroup (info, value);
461                         break;
462                 case PT_TOGGLEBUTTON:
463                         retval = set_value_from_togglebutton (info, value);
464                         break;
465                 case PT_EDITABLE:
466                         retval = set_value_from_editable (info, value);
467                         break;
468                 case PT_COMBOBOX:
469                         retval = set_value_from_combobox (info, value);
470                         break;
471                 default:
472                         retval = FALSE;
473                         g_warning ("Unsupported widget type\n");
474                         break;
475         }
477         return retval;
480 static void
481 set_editable_from_value (PropertyInfo *info, const GValue *value)
483         char *text = NULL;
484         int pos = 0; /* insertion position */
486         g_return_if_fail (GTK_IS_EDITABLE (info->widget));
488         switch (info->data_type)
489         {
490                 case G_TYPE_STRING:
491                         text = g_value_dup_string (value);
492                         break;
493                 case G_TYPE_INT:
494                         text = g_strdup_printf ("%d", g_value_get_int (value));
495                         break;
496                 case G_TYPE_FLOAT:
497                         text = g_strdup_printf ("%.2f", g_value_get_float (value));
498                         break;
499                 default:
500                         break;
501         }
503         if (text == NULL)
504         {
505                 text = g_strdup ("");
506         }
508         info->sane_state = TRUE;
510         gtk_editable_delete_text (GTK_EDITABLE (info->widget), 0, -1);
511         gtk_editable_insert_text (GTK_EDITABLE (info->widget), text, strlen (text), &pos);
513         g_free (text);
516 static int
517 strcmp_with_null (const char *key1,
518                   const char *key2)
520         if (key1 == NULL && key2 == NULL)
521         {
522                 return 0;
523         }
524         if (key1 == NULL)
525         {
526                 return -1;
527         }
528         if (key2 == NULL)
529         {
530                 return 1;
531         }
533         return strcmp (key1, key2);
536 static int
537 get_index_from_value (const GValue *value, GList *string_enum)
539         int index = -1;
540         const char *val;
541         GList *s = NULL;
543         if (string_enum)
544         {
545                 val = g_value_get_string (value);
547                 s = g_list_find_custom (string_enum, val, (GCompareFunc) strcmp_with_null);
549                 if (s)
550                 {
551                         index = g_list_position (string_enum, s);
552                 }
554         }
555         else
556         {
557                 index = g_value_get_int (value);
558         }
560         return index;
563 static gboolean
564 compare_values (const GValue *a, const GValue *b)
566         if (G_VALUE_HOLDS (a, G_TYPE_STRING))
567         {
568                 const char *ta, *tb;
570                 ta = g_value_get_string (a);
571                 tb = g_value_get_string (b);
573                 return (strcmp_with_null (ta, tb) == 0);
574         }
575         else if (G_VALUE_HOLDS (a, G_TYPE_INT))
576         {
577                 return g_value_get_int (a) == g_value_get_int (b);
578         }
579         else if (G_VALUE_HOLDS (a, G_TYPE_FLOAT))
580         {
581                 return g_value_get_float (a) == g_value_get_float (b);
582         }
583         else if (G_VALUE_HOLDS (a, G_TYPE_BOOLEAN))
584         {
585                 return g_value_get_boolean (a) == g_value_get_boolean (b);
586         }
588         return FALSE;
591 static void
592 set_combo_box_from_value (PropertyInfo *info, const  GValue *value)
594         g_return_if_fail (GTK_IS_COMBO_BOX (info->widget));
596         if (info->data_col != -1)
597         {
598                 GValue data = { 0, };
599                 GtkTreeModel *model;
600                 GtkTreeIter iter;
601                 gboolean valid, found = FALSE;
603                 model = gtk_combo_box_get_model (GTK_COMBO_BOX (info->widget));
605                 valid = gtk_tree_model_get_iter_first (model, &iter);
606                 while (valid)
607                 {
608                         gtk_tree_model_get_value (model, &iter, info->data_col, &data);
609                         found = compare_values (&data, value);
610                         g_value_unset (&data);
612                         if (found) break;
614                         valid = gtk_tree_model_iter_next (model, &iter);
615                 }
617                 if (found)
618                 {
619                         gtk_combo_box_set_active_iter (GTK_COMBO_BOX (info->widget), &iter);
620                 }
621                 else
622                 {
623                         gtk_combo_box_set_active (GTK_COMBO_BOX (info->widget), 0);
624                 }
626                 info->sane_state = found;
627         }
628         else if (info->data_type == G_TYPE_INT)
629         {
630                 int index;
632                 index = g_value_get_int (value);
634                 info->sane_state = index >= 0;
636                 g_return_if_fail (index >= -1);
638                 gtk_combo_box_set_active (GTK_COMBO_BOX (info->widget), index);
639         }
640         else
641         {
642                 g_warning ("Unsupported data type for combo box %s\n", info->id);
643         }
646 static void
647 set_radiobuttongroup_from_value (PropertyInfo *info, const GValue *value)
649         GtkToggleButton *button;
650         GSList *list;
651         gint length;
652         int index;
654         g_return_if_fail (GTK_IS_RADIO_BUTTON (info->widget));
656         list = gtk_radio_button_get_group (GTK_RADIO_BUTTON (info->widget));
658         length = g_slist_length (list);
660         index = get_index_from_value (value, info->string_enum);
662         /* new buttons are *prepended* to the list, so button added as first
663          * has last position in the list */
664         index = (length - 1) - index;
666         if (index < 0 || index >= length)
667         {
668                 info->sane_state = FALSE;
669                 g_return_if_fail (index >= 0 && index < length);
670                 return;
671         }
673         button = GTK_TOGGLE_BUTTON (g_slist_nth_data (list, index));
674         g_return_if_fail (button != NULL);
676         info->sane_state = TRUE;
678         if (gtk_toggle_button_get_active (button) == FALSE)
679         {
680                 gtk_toggle_button_set_active (button, TRUE);
681         }
684 static void
685 set_spin_button_from_value (PropertyInfo *info, const GValue *value)
687         gdouble f = 0.0;
688         gboolean is_int;
690         g_return_if_fail (GTK_IS_SPIN_BUTTON (info->widget));
692         is_int = (gtk_spin_button_get_digits (GTK_SPIN_BUTTON (info->widget)) == 0);
694         if (info->data_type == G_TYPE_INT && is_int)
695         {
696                 f = (float) g_value_get_int (value);
697         }
698         else if (info->data_type == G_TYPE_FLOAT)
699         {
700                 f = g_value_get_float (value);
701         }
702         else
703         {
704                 info->sane_state = FALSE;
705                 g_warning ("Unsupported data type for spin button %s\n", info->id);
706                 return;
707         }
709         info->sane_state = TRUE;
711         gtk_spin_button_set_value (GTK_SPIN_BUTTON (info->widget), f);
714 static void
715 set_togglebutton_from_value (PropertyInfo *info, const GValue *value)
717         gboolean active;
719         g_return_if_fail (GTK_IS_TOGGLE_BUTTON (info->widget));
720         g_return_if_fail (info->data_type == G_TYPE_BOOLEAN);
722         active = g_value_get_boolean (value);
724         if (info->apply_type & PT_INVERTED)
725         {
726                 active = !active;
727         }
729         info->sane_state = TRUE;
731         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (info->widget), active);
734 static void
735 set_info_from_value (PropertyInfo *info, const GValue *value)
737         if (!G_VALUE_HOLDS (value, info->data_type))
738         {
739                 g_warning ("Incompatible value types for id %s\n", info->id);
740                 return;
741         }
743         switch (info->widget_type)
744         {
745                 case PT_SPINBUTTON:
746                         set_spin_button_from_value (info, value);
747                         break;
748                 case PT_RADIOBUTTON:
749                         set_radiobuttongroup_from_value (info, value);
750                         break;
751                 case PT_TOGGLEBUTTON:
752                         set_togglebutton_from_value (info, value);
753                         break;
754                 case PT_EDITABLE:
755                         set_editable_from_value (info, value);
756                         break;
757                 case PT_COMBOBOX:
758                         set_combo_box_from_value (info, value);
759                         break;
760                 default:
761                         g_warning ("Unknown widget type\n");
762                         break;
763         }
766 /* widget changed callbacks */
768 static void
769 set_pref_from_info_and_emit (PropertyInfo *info)
771         GValue value = { 0, };
773         if (!set_value_from_info (info, &value))
774         {
775                 return;
776         }
778         g_signal_emit (info->dialog, signals[CHANGED], g_quark_from_string (info->id), &value);
780         if ((info->apply_type & PT_AUTOAPPLY) && info->pref != NULL)
781         {
782                 set_pref_from_value (info, &value);
783         }
785         g_value_unset (&value);
788 static void
789 togglebutton_clicked_cb (GtkWidget *widget, PropertyInfo *info)
791         info->sane_state = TRUE;
793         set_pref_from_info_and_emit (info);
796 static void
797 radiobutton_clicked_cb (GtkWidget *widget, PropertyInfo *info)
799         info->sane_state = TRUE;
801         if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widget)))
802         {
803                 return;
804         }
806         set_pref_from_info_and_emit (info);
809 static gboolean
810 spinbutton_timeout_cb (PropertyInfo *info)
812         GTimer *spin_timer;
814         spin_timer = (GTimer *) g_object_get_data (G_OBJECT (info->widget), "timer");
816         /* timer still valid? */
817         if (spin_timer == NULL)
818         {
819                 /* don't call me again */
820                 return FALSE;
821         }
823         /* okay, we're ready to set */
824         if (g_timer_elapsed (spin_timer, NULL) >= SPIN_DELAY)
825         {
826                 /* kill off the timer */
827                 g_timer_destroy (spin_timer);
828                 g_object_set_data (G_OBJECT (info->widget), "timer", NULL);
830                 /* HACK update the spinbutton here so that the
831                  * changes made directly in the entry are accepted
832                  * and set in the pref. Otherwise the old value is used */
833                 gtk_spin_button_update (GTK_SPIN_BUTTON (info->widget));
835                 info->sane_state = TRUE;
837                 set_pref_from_info_and_emit (info);
839                 /* done, don't run again */
840                 return FALSE;
841         }
843         /* not elapsed yet, call me again */
844         return TRUE;
847 static void
848 spinbutton_changed_cb (GtkWidget *widget, PropertyInfo *info)
850         GTimer *spin_timer;
852         if ((info->apply_type & PT_AUTOAPPLY) == 0) return;
854         spin_timer = g_object_get_data (G_OBJECT (info->widget), "timer");
856         /* destroy an existing timer */
857         if (spin_timer != NULL)
858         {
859                 g_timer_destroy (spin_timer);
860         }
862         /* start tnew timer */
863         spin_timer = g_timer_new();
864         g_timer_start (spin_timer);
865         g_object_set_data (G_OBJECT (info->widget), "timer", spin_timer);
867         g_timeout_add (50, (GSourceFunc) spinbutton_timeout_cb, info);
870 static void
871 changed_cb (GtkWidget *widget, PropertyInfo *info)
873         info->sane_state = TRUE;
875         set_pref_from_info_and_emit (info);
878 static void
879 connect_signals (gpointer key, PropertyInfo *info, EphyDialog *dialog)
881         GSList *list;
883         g_return_if_fail (info->widget != NULL);
885         switch (info->widget_type)
886         {
887                 case PT_TOGGLEBUTTON:
888                         g_signal_connect (G_OBJECT (info->widget), "clicked",
889                                           G_CALLBACK (togglebutton_clicked_cb),
890                                           (gpointer)info);
891                         break;
892                 case PT_RADIOBUTTON:
893                         list = gtk_radio_button_get_group
894                                 (GTK_RADIO_BUTTON (info->widget));
895                         for (; list != NULL; list = list->next)
896                         {
897                                 g_signal_connect
898                                         (G_OBJECT (list->data), "clicked",
899                                          G_CALLBACK (radiobutton_clicked_cb),
900                                          info);
901                         }
902                         break;
903                 case PT_SPINBUTTON:
904                         g_signal_connect (G_OBJECT (info->widget), "changed",
905                                           G_CALLBACK (spinbutton_changed_cb),
906                                           info);
907                         break;
908                 case PT_COMBOBOX:
909                         g_signal_connect (G_OBJECT (info->widget), "changed",
910                                           G_CALLBACK (changed_cb), info);
911                         break;
912                 case PT_EDITABLE:
913                         g_signal_connect (G_OBJECT (info->widget), "changed",
914                                           G_CALLBACK (changed_cb), info);
915                         break;
916                 case PT_UNKNOWN:
917                         break;
918         }
921 static void
922 disconnect_signals (gpointer key, PropertyInfo *info, EphyDialog *dialog)
924         g_return_if_fail (info->widget != NULL);
926         g_signal_handlers_disconnect_matched (info->widget, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, info);
929 static void
930 init_props (EphyDialog *dialog, const EphyDialogProperty *properties, GladeXML *gxml)
932         int i;
934         for (i = 0 ; properties[i].id != NULL; i++)
935         {
936                 PropertyInfo *info = g_new0 (PropertyInfo, 1);
938                 info->id = properties[i].id;
939                 info->dialog = dialog;
940                 info->pref = g_strdup (properties[i].pref);
941                 info->apply_type = properties[i].apply_type;
942                 info->string_enum = NULL;
943                 info->data_col = -1;
945                 info->widget = glade_xml_get_widget (gxml, info->id);
946                 
947                 if (GTK_IS_COMBO_BOX (info->widget))
948                 {
949                         info->widget_type = PT_COMBOBOX;
950                         info->data_type = G_TYPE_INT;
951                 }
952                 else if (GTK_IS_SPIN_BUTTON (info->widget))
953                 {
954                         info->widget_type = PT_SPINBUTTON;
955                         info->data_type = G_TYPE_INT;
956                 }
957                 else if (GTK_IS_RADIO_BUTTON (info->widget))
958                 {
959                         info->widget_type = PT_RADIOBUTTON;
960                         info->data_type = G_TYPE_INT;
961                 }
962                 else if (GTK_IS_TOGGLE_BUTTON (info->widget))
963                 {
964                         info->widget_type = PT_TOGGLEBUTTON;
965                         info->data_type = G_TYPE_BOOLEAN;
966                 }
967                 else if (GTK_IS_EDITABLE (info->widget))
968                 {
969                         info->widget_type = PT_EDITABLE;
970                         info->data_type = G_TYPE_STRING;
971                 }
972                 else
973                 {
974                         info->widget_type = PT_UNKNOWN;
975                         info->data_type = G_TYPE_INVALID;
976                 }
978                 if (properties[i].data_type != 0)
979                 {
980                         info->data_type = properties[i].data_type;
981                 }
983                 info->loaded = FALSE;
984                 info->sane_state = FALSE;
986                 g_hash_table_insert (dialog->priv->props, (char *) info->id, info);             
987         }
990 static void
991 load_info (gpointer key, PropertyInfo *info, EphyDialog *dialog)
993         GValue value = { 0, };
995         g_return_if_fail (info->widget != NULL);
997         if (info->pref != NULL)
998         {
999                 set_value_from_pref (info, &value);
1000                 set_info_from_value (info, &value);
1002                 g_signal_emit (info->dialog, signals[CHANGED], g_quark_from_string (info->id), &value);
1004                 g_value_unset (&value);
1005         
1006                 set_sensitivity (info, eel_gconf_key_is_writable (info->pref));
1007         }
1009         info->loaded = TRUE;
1012 static void
1013 save_info (gpointer key, PropertyInfo *info, EphyDialog *dialog)
1015         GValue value = { 0, };
1017         if (info->pref == NULL || (info->apply_type & PT_NORMAL) == 0)
1018         {
1019                 return;
1020         }
1022         if (!info->sane_state)
1023         {
1024                 g_warning ("Not persisting insane state of id[%s]", info->id);
1025                 return;
1026         }
1028         if (set_value_from_info (info, &value))
1029         {
1030                 set_pref_from_value (info, &value);
1031                 g_value_unset (&value);
1032         }
1035 static void
1036 setup_default_size (EphyDialog *dialog)
1038         if (dialog->priv->has_default_size == FALSE)
1039         {
1040                 EphyStateWindowFlags flags;
1042                 flags = EPHY_STATE_WINDOW_SAVE_SIZE;
1044                 if (dialog->priv->persist_position)
1045                 {
1046                         flags |= EPHY_STATE_WINDOW_SAVE_POSITION;
1047                 }
1049                 ephy_state_add_window (dialog->priv->dialog,
1050                                        dialog->priv->name,
1051                                        dialog->priv->default_width,
1052                                        dialog->priv->default_height,
1053                                        FALSE, flags);
1055                 dialog->priv->has_default_size = TRUE;
1056         }
1059 static void
1060 dialog_destroy_cb (GtkWidget *widget, EphyDialog *dialog)
1062         g_hash_table_foreach (dialog->priv->props, (GHFunc) save_info, dialog);
1064         if (dialog->priv->disposing == FALSE)
1065         {
1066                 g_object_unref (dialog);
1067         }
1070 static void
1071 impl_construct (EphyDialog *dialog,
1072                 const EphyDialogProperty *properties,
1073                 const char *file,
1074                 const char *name,
1075                 const char *domain)
1077         EphyDialogPrivate *priv = dialog->priv;
1078         GladeXML *gxml;
1080         gxml = glade_xml_new (file, name, domain);
1081         g_return_if_fail (gxml != NULL);
1083         priv->dialog = glade_xml_get_widget (gxml, name);
1084         g_return_if_fail (priv->dialog != NULL);
1086         if (priv->name == NULL)
1087         {
1088                 priv->name = g_strdup (name);
1089         }
1091         if (properties)
1092         {
1093                 init_props (dialog, properties, gxml);
1094         }
1096         g_signal_connect_object (dialog->priv->dialog, "destroy",
1097                                  G_CALLBACK(dialog_destroy_cb), dialog, 0);
1099         g_object_unref (gxml);
1102 static void
1103 impl_show (EphyDialog *dialog)
1105         if (dialog->priv->initialized == FALSE)
1106         {
1107                 dialog->priv->initialized = TRUE;
1109                 g_hash_table_foreach (dialog->priv->props, (GHFunc) load_info, dialog);
1110                 g_hash_table_foreach (dialog->priv->props, (GHFunc) connect_signals, dialog);
1111         }
1113         setup_default_size (dialog);
1115         if (dialog->priv->parent != NULL)
1116         {
1117                 /* make the dialog transient again, because it seems to get
1118                  * forgotten after gtk_widget_hide
1119                  */
1120                 gtk_window_set_transient_for (GTK_WINDOW (dialog->priv->dialog),
1121                                               GTK_WINDOW (dialog->priv->parent));
1122         }
1124         gtk_window_present (GTK_WINDOW (dialog->priv->dialog));
1127 void
1128 ephy_dialog_set_modal (EphyDialog *dialog,
1129                        gboolean is_modal)
1131         dialog->priv->modal = is_modal != FALSE;
1133         gtk_window_set_modal (GTK_WINDOW(dialog->priv->dialog), is_modal);
1136 void
1137 ephy_dialog_add_enum (EphyDialog *dialog,
1138                       const char *id,
1139                       guint n_items,
1140                       const char *const *items)
1142         PropertyInfo *info;
1143         int i = 0;
1144         GList *l = NULL;
1146         info = lookup_info (dialog, id);
1147         g_return_if_fail (info != NULL);
1149         for (i = 0; i < n_items; i++)
1150         {
1151                 l = g_list_prepend (l, g_strdup (items[i]));
1152         }
1154         info->string_enum = g_list_reverse (l);
1157 void
1158 ephy_dialog_set_data_column (EphyDialog *dialog,
1159                              const char *id,
1160                              int column)
1162         PropertyInfo *info;
1164         info = lookup_info (dialog, id);
1165         g_return_if_fail (info != NULL);
1167         info->data_col = column;
1170 void
1171 ephy_dialog_set_pref (EphyDialog *dialog,
1172                       const char *property_id,
1173                       const char *pref)
1175         PropertyInfo *info;
1177         info = lookup_info (dialog, property_id);
1178         g_return_if_fail (info != NULL);
1180         disconnect_signals (NULL, info, dialog);
1182         info->loaded = FALSE;
1183         info->sane_state = FALSE;
1184         g_free (info->pref);
1185         info->pref = g_strdup (pref);
1187         if (dialog->priv->initialized)
1188         {
1189                 /* dialog is already initialised, so initialise this here */
1190                 load_info (NULL, info, dialog);
1191                 connect_signals (NULL, info, dialog);
1192         }
1195 void
1196 ephy_dialog_set_size_group (EphyDialog *dialog,
1197                             const char *first_id,
1198                             ...)
1200         GtkSizeGroup *size_group;
1201         va_list vl;
1203         size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
1205         va_start (vl, first_id);
1207         while (first_id != NULL)
1208         {
1209                 PropertyInfo *info;
1211                 info = lookup_info (dialog, first_id);
1212                 g_return_if_fail (info != NULL);
1214                 g_return_if_fail (info->widget != NULL);
1216                 gtk_size_group_add_widget (size_group, info->widget);
1218                 first_id = va_arg (vl, const char*);
1219         }
1221         va_end (vl);
1223         g_object_unref (size_group);
1226 void
1227 ephy_dialog_construct (EphyDialog *dialog,
1228                        const EphyDialogProperty *properties,
1229                        const char *file,
1230                        const char *name,
1231                        const char *domain)
1233         EphyDialogClass *klass = EPHY_DIALOG_GET_CLASS (dialog);
1234         klass->construct (dialog, properties, file, name, domain);
1237 void
1238 ephy_dialog_show (EphyDialog *dialog)
1240         EphyDialogClass *klass = EPHY_DIALOG_GET_CLASS (dialog);
1241         klass->show (dialog);
1244 void
1245 ephy_dialog_hide (EphyDialog *dialog)
1247         g_return_if_fail (EPHY_IS_DIALOG (dialog));
1248         g_return_if_fail (dialog->priv->dialog != NULL);
1250         gtk_widget_hide (dialog->priv->dialog);
1254 ephy_dialog_run (EphyDialog *dialog)
1256         ephy_dialog_show (dialog);
1258         gtk_window_group_add_window (ephy_gui_ensure_window_group (GTK_WINDOW (dialog->priv->parent)),
1259                                      GTK_WINDOW (dialog->priv->dialog));
1261         return gtk_dialog_run (GTK_DIALOG (dialog->priv->dialog));
1264 GtkWidget *
1265 ephy_dialog_get_control (EphyDialog *dialog,
1266                          const char *property_id)
1268         PropertyInfo *info;
1270         info = lookup_info (dialog, property_id);
1271         g_return_val_if_fail (info != NULL, NULL);
1273         return info->widget;
1276 void
1277 ephy_dialog_get_controls (EphyDialog *dialog,
1278                           const char *property_id,
1279                           ...)
1281         PropertyInfo *info;
1282         GtkWidget **wptr;
1283         va_list varargs;
1285         va_start (varargs, property_id);
1287         while (property_id != NULL)
1288         {
1289                 info = lookup_info (dialog, property_id);
1290                 g_return_if_fail (info != NULL);
1292                 wptr = va_arg (varargs, GtkWidget **);
1293                 *wptr = info->widget;
1295                 property_id = va_arg (varargs, const char *);
1296         }
1298         va_end (varargs);
1301 gboolean
1302 ephy_dialog_get_value (EphyDialog *dialog,
1303                        const char *property_id,
1304                        GValue *value)
1306         PropertyInfo *info;
1308         info = lookup_info (dialog, property_id);
1309         g_return_val_if_fail (info != NULL, FALSE);
1311         return set_value_from_info (info, value);
1314 void
1315 ephy_dialog_set_value (EphyDialog *dialog,
1316                        const char *property_id,
1317                        const GValue *value)
1319         PropertyInfo *info;
1321         info = lookup_info (dialog, property_id);
1322         g_return_if_fail (info != NULL);
1324         set_info_from_value (info, value);
1327 static void
1328 free_prop_info (PropertyInfo *info)
1330         if (info->string_enum)
1331         {
1332                 g_list_foreach (info->string_enum, (GFunc)g_free, NULL);
1333                 g_list_free (info->string_enum);
1334         }
1336         g_free (info->pref);
1338         g_free (info);
1341 static void
1342 ephy_dialog_init (EphyDialog *dialog)
1344         dialog->priv = EPHY_DIALOG_GET_PRIVATE (dialog);
1346         dialog->priv->default_width = -1;
1347         dialog->priv->default_height = -1;
1349         dialog->priv->props = g_hash_table_new_full 
1350                 (g_str_hash, g_str_equal, NULL, (GDestroyNotify) free_prop_info);
1353 static void
1354 ephy_dialog_dispose (GObject *object)
1356         EphyDialog *dialog = EPHY_DIALOG (object);
1358         if (dialog->priv->dialog)
1359         {
1360                 dialog->priv->disposing = TRUE;
1361                 gtk_widget_destroy (dialog->priv->dialog);
1362                 dialog->priv->dialog = NULL;
1363         }
1365         parent_class->dispose (object);
1368 static void
1369 ephy_dialog_finalize (GObject *object)
1371         EphyDialog *dialog = EPHY_DIALOG (object);
1373         g_hash_table_destroy (dialog->priv->props);
1375         g_free (dialog->priv->name);
1377         G_OBJECT_CLASS (parent_class)->finalize (object);
1380 GtkWidget *
1381 ephy_dialog_get_parent (EphyDialog *dialog)
1383         g_return_val_if_fail (EPHY_IS_DIALOG (dialog), NULL);
1385         return dialog->priv->parent;
1388 void
1389 ephy_dialog_set_parent (EphyDialog *dialog,
1390                         GtkWidget *parent)
1392         dialog->priv->parent = parent;
1394         g_object_notify (G_OBJECT (dialog), "parent-window");
1397 static void
1398 ephy_dialog_set_property (GObject *object,
1399                           guint prop_id,
1400                           const GValue *value,
1401                           GParamSpec *pspec)
1403         EphyDialog *dialog = EPHY_DIALOG (object);
1405         switch (prop_id)
1406         {
1407                 case PROP_PARENT_WINDOW:
1408                         ephy_dialog_set_parent (dialog, g_value_get_object (value));
1409                         break;
1410                 case PROP_MODAL:
1411                         ephy_dialog_set_modal (dialog, g_value_get_boolean (value));
1412                         break;
1413                 case PROP_PERSIST_POSITION:
1414                         dialog->priv->persist_position = g_value_get_boolean (value);
1415                         break;
1416                 case PROP_DEFAULT_WIDTH:
1417                         dialog->priv->default_width = g_value_get_int (value);
1418                         break;
1419                 case PROP_DEFAULT_HEIGHT:
1420                         dialog->priv->default_height = g_value_get_int (value);
1421                         break;
1422         }
1425 static void
1426 ephy_dialog_get_property (GObject *object,
1427                           guint prop_id,
1428                           GValue *value,
1429                           GParamSpec *pspec)
1431         EphyDialog *dialog = EPHY_DIALOG (object);
1433         switch (prop_id)
1434         {
1435                 case PROP_PARENT_WINDOW:
1436                         g_value_set_object (value, dialog->priv->parent);
1437                         break;
1438                 case PROP_MODAL:
1439                         g_value_set_boolean (value, dialog->priv->modal);
1440                         break;
1441                 case PROP_PERSIST_POSITION:
1442                         g_value_set_boolean (value, dialog->priv->persist_position);
1443                         break;
1444                 case PROP_DEFAULT_WIDTH:
1445                         g_value_set_int (value, dialog->priv->default_width);
1446                         break;
1447                 case PROP_DEFAULT_HEIGHT:
1448                         g_value_set_int (value, dialog->priv->default_height);
1449                         break;
1450         }
1453 static void
1454 ephy_dialog_class_init (EphyDialogClass *klass)
1456         GObjectClass *object_class = G_OBJECT_CLASS (klass);
1458         parent_class = g_type_class_peek_parent (klass);
1460         object_class->finalize = ephy_dialog_finalize;
1461         object_class->dispose = ephy_dialog_dispose;
1462         object_class->set_property = ephy_dialog_set_property;
1463         object_class->get_property = ephy_dialog_get_property;
1465         klass->construct = impl_construct;
1466         klass->show = impl_show;
1468         signals[CHANGED] =
1469                 g_signal_new ("changed",
1470                               EPHY_TYPE_DIALOG,
1471                               G_SIGNAL_RUN_FIRST | G_SIGNAL_DETAILED,
1472                               G_STRUCT_OFFSET (EphyDialogClass, changed),
1473                               NULL, NULL,
1474                               g_cclosure_marshal_VOID__POINTER,
1475                               G_TYPE_NONE,
1476                               1,
1477                               G_TYPE_POINTER);
1479         g_object_class_install_property (object_class,
1480                                          PROP_PARENT_WINDOW,
1481                                          g_param_spec_object ("parent-window",
1482                                                               "Parent window",
1483                                                               "Parent window",
1484                                                               GTK_TYPE_WINDOW,
1485                                                               G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
1487         g_object_class_install_property (object_class,
1488                                          PROP_MODAL,
1489                                          g_param_spec_boolean ("Modal",
1490                                                                "Modal",
1491                                                                "Modal dialog",
1492                                                                FALSE,
1493                                                                G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
1495         g_object_class_install_property (object_class,
1496                                          PROP_PERSIST_POSITION,
1497                                          g_param_spec_boolean ("persist-position",
1498                                                                "Persist position",
1499                                                                "Persist dialog position",
1500                                                                FALSE,
1501                                                                G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB |
1502                                                                G_PARAM_CONSTRUCT_ONLY));
1504         g_object_class_install_property (object_class,
1505                                          PROP_DEFAULT_WIDTH,
1506                                          g_param_spec_int ("default-width",
1507                                                            "Default width",
1508                                                            "Default dialog width",
1509                                                            -1,
1510                                                            G_MAXINT,
1511                                                            -1,
1512                                                            G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB |
1513                                                            G_PARAM_CONSTRUCT_ONLY));
1515         g_object_class_install_property (object_class,
1516                                          PROP_DEFAULT_HEIGHT,
1517                                          g_param_spec_int ("default-height",
1518                                                            "Default height",
1519                                                            "Default dialog height",
1520                                                            -1,
1521                                                            G_MAXINT,
1522                                                            -1,
1523                                                            G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB |
1524                                                            G_PARAM_CONSTRUCT_ONLY));
1526         g_type_class_add_private (object_class, sizeof (EphyDialogPrivate));
1529 EphyDialog *
1530 ephy_dialog_new (void)
1532         return EPHY_DIALOG (g_object_new (EPHY_TYPE_DIALOG, NULL));
1535 EphyDialog *
1536 ephy_dialog_new_with_parent (GtkWidget *parent_window)
1538         return EPHY_DIALOG (g_object_new (EPHY_TYPE_DIALOG,
1539                                           "parent-window", parent_window,
1540                                           NULL));