No issues found
1 /*
2 * e-preferences-window.c
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) version 3.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with the program; if not, see <http://www.gnu.org/licenses/>
16 *
17 *
18 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
19 *
20 */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include "e-preferences-window.h"
27
28 #include <glib/gi18n.h>
29 #include <gdk/gdkkeysyms.h>
30 #include <e-util/e-util.h>
31
32 #define SWITCH_PAGE_INTERVAL 250
33
34 #define E_PREFERENCES_WINDOW_GET_PRIVATE(obj) \
35 (G_TYPE_INSTANCE_GET_PRIVATE \
36 ((obj), E_TYPE_PREFERENCES_WINDOW, EPreferencesWindowPrivate))
37
38 struct _EPreferencesWindowPrivate {
39 gboolean setup;
40 gpointer shell;
41
42 GtkWidget *icon_view;
43 GtkWidget *scroll;
44 GtkWidget *notebook;
45 GHashTable *index;
46
47 GtkListStore *store;
48 GtkTreeModelFilter *filter;
49 const gchar *filter_view;
50 };
51
52 enum {
53 COLUMN_ID, /* G_TYPE_STRING */
54 COLUMN_TEXT, /* G_TYPE_STRING */
55 COLUMN_HELP, /* G_TYPE_STRING */
56 COLUMN_PIXBUF, /* GDK_TYPE_PIXBUF */
57 COLUMN_PAGE, /* G_TYPE_INT */
58 COLUMN_SORT /* G_TYPE_INT */
59 };
60
61 G_DEFINE_TYPE (
62 EPreferencesWindow,
63 e_preferences_window,
64 GTK_TYPE_WINDOW)
65
66 static gboolean
67 preferences_window_filter_view (GtkTreeModel *model,
68 GtkTreeIter *iter,
69 EPreferencesWindow *window)
70 {
71 gchar *str;
72 gboolean visible = FALSE;
73
74 if (!window->priv->filter_view)
75 return TRUE;
76
77 gtk_tree_model_get (model, iter, COLUMN_ID, &str, -1);
78 if (strncmp (window->priv->filter_view, "mail", 4) == 0) {
79 /* Show everything except calendar */
80 if (str && (strncmp (str, "cal", 3) == 0))
81 visible = FALSE;
82 else
83 visible = TRUE;
84 } else if (strncmp (window->priv->filter_view, "cal", 3) == 0) {
85 /* Show only calendar and nothing else */
86 if (str && (strncmp (str, "cal", 3) != 0))
87 visible = FALSE;
88 else
89 visible = TRUE;
90
91 } else /* In any other case, show everything */
92 visible = TRUE;
93
94 g_free (str);
95
96 return visible;
97 }
98
99 static GdkPixbuf *
100 preferences_window_load_pixbuf (const gchar *icon_name)
101 {
102 GtkIconTheme *icon_theme;
103 GtkIconInfo *icon_info;
104 GdkPixbuf *pixbuf;
105 const gchar *filename;
106 gint size;
107 GError *error = NULL;
108
109 icon_theme = gtk_icon_theme_get_default ();
110
111 if (!gtk_icon_size_lookup (GTK_ICON_SIZE_DIALOG, &size, 0))
112 return NULL;
113
114 icon_info = gtk_icon_theme_lookup_icon (
115 icon_theme, icon_name, size, 0);
116
117 if (icon_info == NULL)
118 return NULL;
119
120 filename = gtk_icon_info_get_filename (icon_info);
121
122 pixbuf = gdk_pixbuf_new_from_file (filename, &error);
123
124 gtk_icon_info_free (icon_info);
125
126 if (error != NULL) {
127 g_warning ("%s", error->message);
128 g_error_free (error);
129 }
130
131 return pixbuf;
132 }
133
134 static void
135 preferences_window_help_clicked_cb (EPreferencesWindow *window)
136 {
137 GtkTreeModel *model;
138 GtkTreeIter iter;
139 GList *list;
140 gchar *help = NULL;
141
142 g_return_if_fail (window != NULL);
143
144 model = GTK_TREE_MODEL (window->priv->filter);
145 list = gtk_icon_view_get_selected_items (
146 GTK_ICON_VIEW (window->priv->icon_view));
147
148 if (list != NULL) {
149 gtk_tree_model_get_iter (model, &iter, list->data);
150 gtk_tree_model_get (model, &iter, COLUMN_HELP, &help, -1);
151
152 } else if (gtk_tree_model_get_iter_first (model, &iter)) {
153 gint page_index, current_index;
154
155 current_index = gtk_notebook_get_current_page (
156 GTK_NOTEBOOK (window->priv->notebook));
157 do {
158 gtk_tree_model_get (
159 model, &iter, COLUMN_PAGE, &page_index, -1);
160
161 if (page_index == current_index) {
162 gtk_tree_model_get (
163 model, &iter, COLUMN_HELP, &help, -1);
164 break;
165 }
166 } while (gtk_tree_model_iter_next (model, &iter));
167 }
168
169 e_display_help (GTK_WINDOW (window), help ? help : "index");
170
171 g_free (help);
172 }
173
174 static void
175 preferences_window_selection_changed_cb (EPreferencesWindow *window)
176 {
177 GtkIconView *icon_view;
178 GtkNotebook *notebook;
179 GtkTreeModel *model;
180 GtkTreeIter iter;
181 GList *list;
182 gint page;
183
184 icon_view = GTK_ICON_VIEW (window->priv->icon_view);
185 list = gtk_icon_view_get_selected_items (icon_view);
186 if (list == NULL)
187 return;
188
189 model = GTK_TREE_MODEL (window->priv->filter);
190 gtk_tree_model_get_iter (model, &iter, list->data);
191 gtk_tree_model_get (model, &iter, COLUMN_PAGE, &page, -1);
192
193 notebook = GTK_NOTEBOOK (window->priv->notebook);
194 gtk_notebook_set_current_page (notebook, page);
195
196 g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
197 g_list_free (list);
198
199 gtk_widget_grab_focus (GTK_WIDGET (icon_view));
200 }
201
202 static void
203 preferences_window_dispose (GObject *object)
204 {
205 EPreferencesWindowPrivate *priv;
206
207 priv = E_PREFERENCES_WINDOW_GET_PRIVATE (object);
208
209 if (priv->icon_view != NULL) {
210 g_object_unref (priv->icon_view);
211 priv->icon_view = NULL;
212 }
213
214 if (priv->notebook != NULL) {
215 g_object_unref (priv->notebook);
216 priv->notebook = NULL;
217 }
218
219 if (priv->shell) {
220 g_object_remove_weak_pointer (priv->shell, &priv->shell);
221 priv->shell = NULL;
222 }
223
224 g_hash_table_remove_all (priv->index);
225
226 /* Chain up to parent's dispose() method. */
227 G_OBJECT_CLASS (e_preferences_window_parent_class)->dispose (object);
228 }
229
230 static void
231 preferences_window_finalize (GObject *object)
232 {
233 EPreferencesWindowPrivate *priv;
234
235 priv = E_PREFERENCES_WINDOW_GET_PRIVATE (object);
236
237 g_hash_table_destroy (priv->index);
238
239 /* Chain up to parent's finalize() method. */
240 G_OBJECT_CLASS (e_preferences_window_parent_class)->finalize (object);
241 }
242
243 static void
244 preferences_window_show (GtkWidget *widget)
245 {
246 EPreferencesWindowPrivate *priv;
247 GtkIconView *icon_view;
248 GtkTreePath *path;
249
250 priv = E_PREFERENCES_WINDOW_GET_PRIVATE (widget);
251 if (!priv->setup)
252 g_warning ("Preferences window has not been setup correctly");
253
254 icon_view = GTK_ICON_VIEW (priv->icon_view);
255
256 path = gtk_tree_path_new_first ();
257 gtk_icon_view_select_path (icon_view, path);
258 gtk_icon_view_scroll_to_path (icon_view, path, FALSE, 0.0, 0.0);
259 gtk_tree_path_free (path);
260
261 gtk_widget_grab_focus (priv->icon_view);
262
263 /* Chain up to parent's show() method. */
264 GTK_WIDGET_CLASS (e_preferences_window_parent_class)->show (widget);
265 }
266
267 static void
268 e_preferences_window_class_init (EPreferencesWindowClass *class)
269 {
270 GObjectClass *object_class;
271 GtkWidgetClass *widget_class;
272
273 g_type_class_add_private (class, sizeof (EPreferencesWindowPrivate));
274
275 object_class = G_OBJECT_CLASS (class);
276 object_class->dispose = preferences_window_dispose;
277 object_class->finalize = preferences_window_finalize;
278
279 widget_class = GTK_WIDGET_CLASS (class);
280 widget_class->show = preferences_window_show;
281 }
282
283 static void
284 e_preferences_window_init (EPreferencesWindow *window)
285 {
286 GtkListStore *store;
287 GtkWidget *container;
288 GtkWidget *hbox;
289 GtkWidget *vbox;
290 GtkWidget *widget;
291 GHashTable *index;
292 const gchar *title;
293 GtkAccelGroup *accel_group;
294
295 index = g_hash_table_new_full (
296 g_str_hash, g_str_equal,
297 (GDestroyNotify) g_free,
298 (GDestroyNotify) gtk_tree_row_reference_free);
299
300 window->priv = E_PREFERENCES_WINDOW_GET_PRIVATE (window);
301 window->priv->index = index;
302 window->priv->filter_view = NULL;
303
304 store = gtk_list_store_new (
305 6, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
306 GDK_TYPE_PIXBUF, G_TYPE_INT, G_TYPE_INT);
307 gtk_tree_sortable_set_sort_column_id (
308 GTK_TREE_SORTABLE (store), COLUMN_SORT, GTK_SORT_ASCENDING);
309 window->priv->store = store;
310
311 window->priv->filter = (GtkTreeModelFilter *)
312 gtk_tree_model_filter_new (GTK_TREE_MODEL (store), NULL);
313 gtk_tree_model_filter_set_visible_func (
314 window->priv->filter, (GtkTreeModelFilterVisibleFunc)
315 preferences_window_filter_view, window, NULL);
316
317 title = _("Evolution Preferences");
318 gtk_window_set_title (GTK_WINDOW (window), title);
319 gtk_window_set_resizable (GTK_WINDOW (window), TRUE);
320 gtk_container_set_border_width (GTK_CONTAINER (window), 12);
321
322 g_signal_connect (
323 window, "delete-event",
324 G_CALLBACK (gtk_widget_hide_on_delete), NULL);
325
326 widget = gtk_vbox_new (FALSE, 12);
327 gtk_container_add (GTK_CONTAINER (window), widget);
328 gtk_widget_show (widget);
329
330 vbox = widget;
331
332 widget = gtk_hbox_new (FALSE, 12);
333 gtk_box_pack_start (GTK_BOX (vbox), widget, TRUE, TRUE, 0);
334 gtk_widget_show (widget);
335
336 hbox = widget;
337
338 widget = gtk_scrolled_window_new (NULL, NULL);
339 gtk_scrolled_window_set_policy (
340 GTK_SCROLLED_WINDOW (widget),
341 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
342 gtk_scrolled_window_set_shadow_type (
343 GTK_SCROLLED_WINDOW (widget), GTK_SHADOW_IN);
344 gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, TRUE, 0);
345 window->priv->scroll = widget;
346 gtk_widget_show (widget);
347
348 container = widget;
349
350 widget = gtk_icon_view_new_with_model (
351 GTK_TREE_MODEL (window->priv->filter));
352 gtk_icon_view_set_columns (GTK_ICON_VIEW (widget), 1);
353 gtk_icon_view_set_text_column (GTK_ICON_VIEW (widget), COLUMN_TEXT);
354 gtk_icon_view_set_pixbuf_column (GTK_ICON_VIEW (widget), COLUMN_PIXBUF);
355 g_signal_connect_swapped (
356 widget, "selection-changed",
357 G_CALLBACK (preferences_window_selection_changed_cb), window);
358 gtk_container_add (GTK_CONTAINER (container), widget);
359 window->priv->icon_view = g_object_ref (widget);
360 gtk_widget_show (widget);
361 g_object_unref (store);
362
363 widget = gtk_notebook_new ();
364 gtk_notebook_set_show_tabs (GTK_NOTEBOOK (widget), FALSE);
365 gtk_notebook_set_show_border (GTK_NOTEBOOK (widget), FALSE);
366 gtk_box_pack_start (GTK_BOX (hbox), widget, TRUE, TRUE, 0);
367 window->priv->notebook = g_object_ref (widget);
368 gtk_widget_show (widget);
369
370 widget = gtk_hbutton_box_new ();
371 gtk_button_box_set_layout (
372 GTK_BUTTON_BOX (widget), GTK_BUTTONBOX_END);
373 gtk_box_pack_start (GTK_BOX (vbox), widget, FALSE, FALSE, 0);
374 gtk_widget_show (widget);
375
376 container = widget;
377
378 widget = gtk_button_new_from_stock (GTK_STOCK_HELP);
379 g_signal_connect_swapped (
380 widget, "clicked",
381 G_CALLBACK (preferences_window_help_clicked_cb), window);
382 gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
383 gtk_button_box_set_child_secondary (
384 GTK_BUTTON_BOX (container), widget, TRUE);
385 gtk_widget_show (widget);
386
387 widget = gtk_button_new_from_stock (GTK_STOCK_CLOSE);
388 g_signal_connect_swapped (
389 widget, "clicked",
390 G_CALLBACK (gtk_widget_hide), window);
391 gtk_widget_set_can_default (widget, TRUE);
392 gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
393 accel_group = gtk_accel_group_new ();
394 gtk_widget_add_accelerator (
395 widget, "activate", accel_group,
396 GDK_KEY_Escape, (GdkModifierType) 0,
397 GTK_ACCEL_VISIBLE);
398 gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);
399 gtk_widget_grab_default (widget);
400 gtk_widget_show (widget);
401 }
402
403 GtkWidget *
404 e_preferences_window_new (gpointer shell)
405 {
406 EPreferencesWindow *window;
407
408 window = g_object_new (E_TYPE_PREFERENCES_WINDOW, NULL);
409
410 /* ideally should be an object property */
411 window->priv->shell = shell;
412 if (shell)
413 g_object_add_weak_pointer (shell, &window->priv->shell);
414
415 return GTK_WIDGET (window);
416 }
417
418 gpointer
419 e_preferences_window_get_shell (EPreferencesWindow *window)
420 {
421 g_return_val_if_fail (E_IS_PREFERENCES_WINDOW (window), NULL);
422
423 return window->priv->shell;
424 }
425
426 void
427 e_preferences_window_add_page (EPreferencesWindow *window,
428 const gchar *page_name,
429 const gchar *icon_name,
430 const gchar *caption,
431 const gchar *help_target,
432 EPreferencesWindowCreatePageFn create_fn,
433 gint sort_order)
434 {
435 GtkTreeRowReference *reference;
436 GtkIconView *icon_view;
437 GtkNotebook *notebook;
438 GtkTreeModel *model;
439 GtkTreePath *path;
440 GHashTable *index;
441 GdkPixbuf *pixbuf;
442 GtkTreeIter iter;
443 GtkWidget *align;
444 gint page;
445
446 g_return_if_fail (E_IS_PREFERENCES_WINDOW (window));
447 g_return_if_fail (create_fn != NULL);
448 g_return_if_fail (page_name != NULL);
449 g_return_if_fail (icon_name != NULL);
450 g_return_if_fail (caption != NULL);
451
452 icon_view = GTK_ICON_VIEW (window->priv->icon_view);
453 notebook = GTK_NOTEBOOK (window->priv->notebook);
454
455 page = gtk_notebook_get_n_pages (notebook);
456 model = GTK_TREE_MODEL (window->priv->store);
457 pixbuf = preferences_window_load_pixbuf (icon_name);
458
459 gtk_list_store_append (GTK_LIST_STORE (model), &iter);
460
461 gtk_list_store_set (
462 GTK_LIST_STORE (model), &iter,
463 COLUMN_ID, page_name,
464 COLUMN_TEXT, caption,
465 COLUMN_HELP, help_target,
466 COLUMN_PIXBUF, pixbuf,
467 COLUMN_PAGE, page,
468 COLUMN_SORT, sort_order,
469 -1);
470
471 index = window->priv->index;
472 path = gtk_tree_model_get_path (model, &iter);
473 reference = gtk_tree_row_reference_new (model, path);
474 g_hash_table_insert (index, g_strdup (page_name), reference);
475 gtk_tree_path_free (path);
476
477 align = g_object_new (GTK_TYPE_ALIGNMENT, NULL);
478 gtk_widget_show (GTK_WIDGET (align));
479 g_object_set_data (G_OBJECT (align), "create_fn", create_fn);
480 gtk_notebook_append_page (notebook, align, NULL);
481 gtk_container_child_set (
482 GTK_CONTAINER (notebook), align,
483 "tab-fill", FALSE, "tab-expand", FALSE, NULL);
484
485 /* Force GtkIconView to recalculate the text wrap width,
486 * otherwise we get a really narrow icon list on the left
487 * side of the preferences window. */
488 gtk_icon_view_set_item_width (icon_view, -1);
489 gtk_widget_queue_resize (GTK_WIDGET (window));
490 }
491
492 void
493 e_preferences_window_show_page (EPreferencesWindow *window,
494 const gchar *page_name)
495 {
496 GtkTreeRowReference *reference;
497 GtkIconView *icon_view;
498 GtkTreePath *path;
499
500 g_return_if_fail (E_IS_PREFERENCES_WINDOW (window));
501 g_return_if_fail (page_name != NULL);
502 g_return_if_fail (window->priv->setup);
503
504 icon_view = GTK_ICON_VIEW (window->priv->icon_view);
505 reference = g_hash_table_lookup (window->priv->index, page_name);
506 g_return_if_fail (reference != NULL);
507
508 path = gtk_tree_row_reference_get_path (reference);
509 gtk_icon_view_select_path (icon_view, path);
510 gtk_icon_view_scroll_to_path (icon_view, path, FALSE, 0.0, 0.0);
511 gtk_tree_path_free (path);
512 }
513
514 void
515 e_preferences_window_filter_page (EPreferencesWindow *window,
516 const gchar *page_name)
517 {
518 GtkTreeRowReference *reference;
519 GtkIconView *icon_view;
520 GtkTreePath *path;
521
522 g_return_if_fail (E_IS_PREFERENCES_WINDOW (window));
523 g_return_if_fail (page_name != NULL);
524 g_return_if_fail (window->priv->setup);
525
526 icon_view = GTK_ICON_VIEW (window->priv->icon_view);
527 reference = g_hash_table_lookup (window->priv->index, page_name);
528 g_return_if_fail (reference != NULL);
529
530 path = gtk_tree_row_reference_get_path (reference);
531 gtk_icon_view_select_path (icon_view, path);
532 gtk_icon_view_scroll_to_path (icon_view, path, FALSE, 0.0, 0.0);
533 gtk_tree_path_free (path);
534
535 window->priv->filter_view = page_name;
536 gtk_tree_model_filter_refilter (window->priv->filter);
537
538 /* XXX: We need a better solution to hide the icon view when
539 * there is just one entry */
540 if (strncmp (page_name, "cal", 3) == 0) {
541 gtk_widget_hide (window->priv->scroll);
542 } else
543 gtk_widget_show (window->priv->scroll);
544 }
545
546 /*
547 * Create all the deferred configuration pages.
548 */
549 void
550 e_preferences_window_setup (EPreferencesWindow *window)
551 {
552 gint i, num;
553 GtkNotebook *notebook;
554 GtkRequisition requisition;
555 gint width = -1, height = -1, content_width = -1, content_height = -1;
556 EPreferencesWindowPrivate *priv;
557
558 g_return_if_fail (E_IS_PREFERENCES_WINDOW (window));
559
560 priv = E_PREFERENCES_WINDOW_GET_PRIVATE (window);
561
562 if (priv->setup)
563 return;
564
565 gtk_window_get_default_size (GTK_WINDOW (window), &width, &height);
566 if (width < 0 || height < 0) {
567 gtk_widget_get_preferred_size (GTK_WIDGET (window), &requisition, NULL);
568
569 width = requisition.width;
570 height = requisition.height;
571 }
572
573 notebook = GTK_NOTEBOOK (priv->notebook);
574 num = gtk_notebook_get_n_pages (notebook);
575
576 for (i = 0; i < num; i++) {
577 GtkBin *align;
578 GtkWidget *content;
579 EPreferencesWindowCreatePageFn create_fn;
580
581 align = GTK_BIN (gtk_notebook_get_nth_page (notebook, i));
582 create_fn = g_object_get_data (G_OBJECT (align), "create_fn");
583
584 if (!create_fn || gtk_bin_get_child (align))
585 continue;
586
587 content = create_fn (window);
588 if (content) {
589 GtkScrolledWindow *scrolled;
590
591 scrolled = GTK_SCROLLED_WINDOW (gtk_scrolled_window_new (NULL, NULL));
592 gtk_scrolled_window_add_with_viewport (scrolled, content);
593 gtk_scrolled_window_set_min_content_width (scrolled, 320);
594 gtk_scrolled_window_set_min_content_height (scrolled, 240);
595 gtk_scrolled_window_set_policy (scrolled, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
596 gtk_scrolled_window_set_shadow_type (scrolled, GTK_SHADOW_NONE);
597
598 gtk_viewport_set_shadow_type (
599 GTK_VIEWPORT (gtk_bin_get_child (GTK_BIN (scrolled))),
600 GTK_SHADOW_NONE);
601
602 gtk_widget_show (content);
603
604 gtk_widget_get_preferred_size (GTK_WIDGET (content), &requisition, NULL);
605
606 if (requisition.width > content_width)
607 content_width = requisition.width;
608 if (requisition.height > content_height)
609 content_height = requisition.height;
610
611 gtk_widget_show (GTK_WIDGET (scrolled));
612
613 gtk_container_add (GTK_CONTAINER (align), GTK_WIDGET (scrolled));
614 }
615 }
616
617 if (content_width > 0 && content_height > 0 && width > 0 && height > 0) {
618 GdkScreen *screen;
619 GdkRectangle monitor_area;
620 gint x = 0, y = 0, monitor;
621
622 screen = gtk_window_get_screen (GTK_WINDOW (window));
623 gtk_window_get_position (GTK_WINDOW (window), &x, &y);
624
625 monitor = gdk_screen_get_monitor_at_point (screen, x, y);
626 if (monitor < 0 || monitor >= gdk_screen_get_n_monitors (screen))
627 monitor = 0;
628
629 gdk_screen_get_monitor_workarea (screen, monitor, &monitor_area);
630
631 if (content_width > monitor_area.width - width)
632 content_width = monitor_area.width - width;
633
634 if (content_height > monitor_area.height - height)
635 content_height = monitor_area.height - height;
636
637 if (content_width > 0 && content_height > 0)
638 gtk_window_set_default_size (GTK_WINDOW (window), width + content_width, height + content_height);
639 }
640
641 priv->setup = TRUE;
642 }