From d09d8de870b6697c8a8b262e7e077b871a69b315 Mon Sep 17 00:00:00 2001 From: Matthew Barnes Date: Mon, 10 Dec 2012 08:09:59 -0500 Subject: Consolidate base utility libraries into libeutil. Evolution consists of entirely too many small utility libraries, which increases linking and loading time, places a burden on higher layers of the application (e.g. modules) which has to remember to link to all the small in-tree utility libraries, and makes it difficult to generate API documentation for these utility libraries in one Gtk-Doc module. Merge the following utility libraries under the umbrella of libeutil, and enforce a single-include policy on libeutil so we can reorganize the files as desired without disrupting its pseudo-public API. libemail-utils/libemail-utils.la libevolution-utils/libevolution-utils.la filter/libfilter.la widgets/e-timezone-dialog/libetimezonedialog.la widgets/menus/libmenus.la widgets/misc/libemiscwidgets.la widgets/table/libetable.la widgets/text/libetext.la This also merges libedataserverui from the Evolution-Data-Server module, since Evolution is its only consumer nowadays, and I'd like to make some improvements to those APIs without concern for backward-compatibility. And finally, start a Gtk-Doc module for libeutil. It's going to be a project just getting all the symbols _listed_ much less _documented_. But the skeletal structure is in place and I'm off to a good start. --- e-util/e-preferences-window.c | 643 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 643 insertions(+) create mode 100644 e-util/e-preferences-window.c (limited to 'e-util/e-preferences-window.c') diff --git a/e-util/e-preferences-window.c b/e-util/e-preferences-window.c new file mode 100644 index 0000000000..fbe6c01d21 --- /dev/null +++ b/e-util/e-preferences-window.c @@ -0,0 +1,643 @@ +/* + * e-preferences-window.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "e-preferences-window.h" + +#include +#include + +#include "e-misc-utils.h" + +#define SWITCH_PAGE_INTERVAL 250 + +#define E_PREFERENCES_WINDOW_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_PREFERENCES_WINDOW, EPreferencesWindowPrivate)) + +struct _EPreferencesWindowPrivate { + gboolean setup; + gpointer shell; + + GtkWidget *icon_view; + GtkWidget *scroll; + GtkWidget *notebook; + GHashTable *index; + + GtkListStore *store; + GtkTreeModelFilter *filter; + const gchar *filter_view; +}; + +enum { + COLUMN_ID, /* G_TYPE_STRING */ + COLUMN_TEXT, /* G_TYPE_STRING */ + COLUMN_HELP, /* G_TYPE_STRING */ + COLUMN_PIXBUF, /* GDK_TYPE_PIXBUF */ + COLUMN_PAGE, /* G_TYPE_INT */ + COLUMN_SORT /* G_TYPE_INT */ +}; + +G_DEFINE_TYPE ( + EPreferencesWindow, + e_preferences_window, + GTK_TYPE_WINDOW) + +static gboolean +preferences_window_filter_view (GtkTreeModel *model, + GtkTreeIter *iter, + EPreferencesWindow *window) +{ + gchar *str; + gboolean visible = FALSE; + + if (!window->priv->filter_view) + return TRUE; + + gtk_tree_model_get (model, iter, COLUMN_ID, &str, -1); + if (strncmp (window->priv->filter_view, "mail", 4) == 0) { + /* Show everything except calendar */ + if (str && (strncmp (str, "cal", 3) == 0)) + visible = FALSE; + else + visible = TRUE; + } else if (strncmp (window->priv->filter_view, "cal", 3) == 0) { + /* Show only calendar and nothing else */ + if (str && (strncmp (str, "cal", 3) != 0)) + visible = FALSE; + else + visible = TRUE; + + } else /* In any other case, show everything */ + visible = TRUE; + + g_free (str); + + return visible; +} + +static GdkPixbuf * +preferences_window_load_pixbuf (const gchar *icon_name) +{ + GtkIconTheme *icon_theme; + GtkIconInfo *icon_info; + GdkPixbuf *pixbuf; + const gchar *filename; + gint size; + GError *error = NULL; + + icon_theme = gtk_icon_theme_get_default (); + + if (!gtk_icon_size_lookup (GTK_ICON_SIZE_DIALOG, &size, 0)) + return NULL; + + icon_info = gtk_icon_theme_lookup_icon ( + icon_theme, icon_name, size, 0); + + if (icon_info == NULL) + return NULL; + + filename = gtk_icon_info_get_filename (icon_info); + + pixbuf = gdk_pixbuf_new_from_file (filename, &error); + + gtk_icon_info_free (icon_info); + + if (error != NULL) { + g_warning ("%s", error->message); + g_error_free (error); + } + + return pixbuf; +} + +static void +preferences_window_help_clicked_cb (EPreferencesWindow *window) +{ + GtkTreeModel *model; + GtkTreeIter iter; + GList *list; + gchar *help = NULL; + + g_return_if_fail (window != NULL); + + model = GTK_TREE_MODEL (window->priv->filter); + list = gtk_icon_view_get_selected_items ( + GTK_ICON_VIEW (window->priv->icon_view)); + + if (list != NULL) { + gtk_tree_model_get_iter (model, &iter, list->data); + gtk_tree_model_get (model, &iter, COLUMN_HELP, &help, -1); + + } else if (gtk_tree_model_get_iter_first (model, &iter)) { + gint page_index, current_index; + + current_index = gtk_notebook_get_current_page ( + GTK_NOTEBOOK (window->priv->notebook)); + do { + gtk_tree_model_get ( + model, &iter, COLUMN_PAGE, &page_index, -1); + + if (page_index == current_index) { + gtk_tree_model_get ( + model, &iter, COLUMN_HELP, &help, -1); + break; + } + } while (gtk_tree_model_iter_next (model, &iter)); + } + + e_display_help (GTK_WINDOW (window), help ? help : "index"); + + g_free (help); +} + +static void +preferences_window_selection_changed_cb (EPreferencesWindow *window) +{ + GtkIconView *icon_view; + GtkNotebook *notebook; + GtkTreeModel *model; + GtkTreeIter iter; + GList *list; + gint page; + + icon_view = GTK_ICON_VIEW (window->priv->icon_view); + list = gtk_icon_view_get_selected_items (icon_view); + if (list == NULL) + return; + + model = GTK_TREE_MODEL (window->priv->filter); + gtk_tree_model_get_iter (model, &iter, list->data); + gtk_tree_model_get (model, &iter, COLUMN_PAGE, &page, -1); + + notebook = GTK_NOTEBOOK (window->priv->notebook); + gtk_notebook_set_current_page (notebook, page); + + g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL); + g_list_free (list); + + gtk_widget_grab_focus (GTK_WIDGET (icon_view)); +} + +static void +preferences_window_dispose (GObject *object) +{ + EPreferencesWindowPrivate *priv; + + priv = E_PREFERENCES_WINDOW_GET_PRIVATE (object); + + if (priv->icon_view != NULL) { + g_object_unref (priv->icon_view); + priv->icon_view = NULL; + } + + if (priv->notebook != NULL) { + g_object_unref (priv->notebook); + priv->notebook = NULL; + } + + if (priv->shell) { + g_object_remove_weak_pointer (priv->shell, &priv->shell); + priv->shell = NULL; + } + + g_hash_table_remove_all (priv->index); + + /* Chain up to parent's dispose() method. */ + G_OBJECT_CLASS (e_preferences_window_parent_class)->dispose (object); +} + +static void +preferences_window_finalize (GObject *object) +{ + EPreferencesWindowPrivate *priv; + + priv = E_PREFERENCES_WINDOW_GET_PRIVATE (object); + + g_hash_table_destroy (priv->index); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (e_preferences_window_parent_class)->finalize (object); +} + +static void +preferences_window_show (GtkWidget *widget) +{ + EPreferencesWindowPrivate *priv; + GtkIconView *icon_view; + GtkTreePath *path; + + priv = E_PREFERENCES_WINDOW_GET_PRIVATE (widget); + if (!priv->setup) + g_warning ("Preferences window has not been setup correctly"); + + icon_view = GTK_ICON_VIEW (priv->icon_view); + + path = gtk_tree_path_new_first (); + gtk_icon_view_select_path (icon_view, path); + gtk_icon_view_scroll_to_path (icon_view, path, FALSE, 0.0, 0.0); + gtk_tree_path_free (path); + + gtk_widget_grab_focus (priv->icon_view); + + /* Chain up to parent's show() method. */ + GTK_WIDGET_CLASS (e_preferences_window_parent_class)->show (widget); +} + +static void +e_preferences_window_class_init (EPreferencesWindowClass *class) +{ + GObjectClass *object_class; + GtkWidgetClass *widget_class; + + g_type_class_add_private (class, sizeof (EPreferencesWindowPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->dispose = preferences_window_dispose; + object_class->finalize = preferences_window_finalize; + + widget_class = GTK_WIDGET_CLASS (class); + widget_class->show = preferences_window_show; +} + +static void +e_preferences_window_init (EPreferencesWindow *window) +{ + GtkListStore *store; + GtkWidget *container; + GtkWidget *hbox; + GtkWidget *vbox; + GtkWidget *widget; + GHashTable *index; + const gchar *title; + GtkAccelGroup *accel_group; + + index = g_hash_table_new_full ( + g_str_hash, g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) gtk_tree_row_reference_free); + + window->priv = E_PREFERENCES_WINDOW_GET_PRIVATE (window); + window->priv->index = index; + window->priv->filter_view = NULL; + + store = gtk_list_store_new ( + 6, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, + GDK_TYPE_PIXBUF, G_TYPE_INT, G_TYPE_INT); + gtk_tree_sortable_set_sort_column_id ( + GTK_TREE_SORTABLE (store), COLUMN_SORT, GTK_SORT_ASCENDING); + window->priv->store = store; + + window->priv->filter = (GtkTreeModelFilter *) + gtk_tree_model_filter_new (GTK_TREE_MODEL (store), NULL); + gtk_tree_model_filter_set_visible_func ( + window->priv->filter, (GtkTreeModelFilterVisibleFunc) + preferences_window_filter_view, window, NULL); + + title = _("Evolution Preferences"); + gtk_window_set_title (GTK_WINDOW (window), title); + gtk_window_set_resizable (GTK_WINDOW (window), TRUE); + gtk_container_set_border_width (GTK_CONTAINER (window), 12); + + g_signal_connect ( + window, "delete-event", + G_CALLBACK (gtk_widget_hide_on_delete), NULL); + + widget = gtk_vbox_new (FALSE, 12); + gtk_container_add (GTK_CONTAINER (window), widget); + gtk_widget_show (widget); + + vbox = widget; + + widget = gtk_hbox_new (FALSE, 12); + gtk_box_pack_start (GTK_BOX (vbox), widget, TRUE, TRUE, 0); + gtk_widget_show (widget); + + hbox = widget; + + widget = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy ( + GTK_SCROLLED_WINDOW (widget), + GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type ( + GTK_SCROLLED_WINDOW (widget), GTK_SHADOW_IN); + gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, TRUE, 0); + window->priv->scroll = widget; + gtk_widget_show (widget); + + container = widget; + + widget = gtk_icon_view_new_with_model ( + GTK_TREE_MODEL (window->priv->filter)); + gtk_icon_view_set_columns (GTK_ICON_VIEW (widget), 1); + gtk_icon_view_set_text_column (GTK_ICON_VIEW (widget), COLUMN_TEXT); + gtk_icon_view_set_pixbuf_column (GTK_ICON_VIEW (widget), COLUMN_PIXBUF); + g_signal_connect_swapped ( + widget, "selection-changed", + G_CALLBACK (preferences_window_selection_changed_cb), window); + gtk_container_add (GTK_CONTAINER (container), widget); + window->priv->icon_view = g_object_ref (widget); + gtk_widget_show (widget); + g_object_unref (store); + + widget = gtk_notebook_new (); + gtk_notebook_set_show_tabs (GTK_NOTEBOOK (widget), FALSE); + gtk_notebook_set_show_border (GTK_NOTEBOOK (widget), FALSE); + gtk_box_pack_start (GTK_BOX (hbox), widget, TRUE, TRUE, 0); + window->priv->notebook = g_object_ref (widget); + gtk_widget_show (widget); + + widget = gtk_hbutton_box_new (); + gtk_button_box_set_layout ( + GTK_BUTTON_BOX (widget), GTK_BUTTONBOX_END); + gtk_box_pack_start (GTK_BOX (vbox), widget, FALSE, FALSE, 0); + gtk_widget_show (widget); + + container = widget; + + widget = gtk_button_new_from_stock (GTK_STOCK_HELP); + g_signal_connect_swapped ( + widget, "clicked", + G_CALLBACK (preferences_window_help_clicked_cb), window); + gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0); + gtk_button_box_set_child_secondary ( + GTK_BUTTON_BOX (container), widget, TRUE); + gtk_widget_show (widget); + + widget = gtk_button_new_from_stock (GTK_STOCK_CLOSE); + g_signal_connect_swapped ( + widget, "clicked", + G_CALLBACK (gtk_widget_hide), window); + gtk_widget_set_can_default (widget, TRUE); + gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0); + accel_group = gtk_accel_group_new (); + gtk_widget_add_accelerator ( + widget, "activate", accel_group, + GDK_KEY_Escape, (GdkModifierType) 0, + GTK_ACCEL_VISIBLE); + gtk_window_add_accel_group (GTK_WINDOW (window), accel_group); + gtk_widget_grab_default (widget); + gtk_widget_show (widget); +} + +GtkWidget * +e_preferences_window_new (gpointer shell) +{ + EPreferencesWindow *window; + + window = g_object_new (E_TYPE_PREFERENCES_WINDOW, NULL); + + /* ideally should be an object property */ + window->priv->shell = shell; + if (shell) + g_object_add_weak_pointer (shell, &window->priv->shell); + + return GTK_WIDGET (window); +} + +gpointer +e_preferences_window_get_shell (EPreferencesWindow *window) +{ + g_return_val_if_fail (E_IS_PREFERENCES_WINDOW (window), NULL); + + return window->priv->shell; +} + +void +e_preferences_window_add_page (EPreferencesWindow *window, + const gchar *page_name, + const gchar *icon_name, + const gchar *caption, + const gchar *help_target, + EPreferencesWindowCreatePageFn create_fn, + gint sort_order) +{ + GtkTreeRowReference *reference; + GtkIconView *icon_view; + GtkNotebook *notebook; + GtkTreeModel *model; + GtkTreePath *path; + GHashTable *index; + GdkPixbuf *pixbuf; + GtkTreeIter iter; + GtkWidget *align; + gint page; + + g_return_if_fail (E_IS_PREFERENCES_WINDOW (window)); + g_return_if_fail (create_fn != NULL); + g_return_if_fail (page_name != NULL); + g_return_if_fail (icon_name != NULL); + g_return_if_fail (caption != NULL); + + icon_view = GTK_ICON_VIEW (window->priv->icon_view); + notebook = GTK_NOTEBOOK (window->priv->notebook); + + page = gtk_notebook_get_n_pages (notebook); + model = GTK_TREE_MODEL (window->priv->store); + pixbuf = preferences_window_load_pixbuf (icon_name); + + gtk_list_store_append (GTK_LIST_STORE (model), &iter); + + gtk_list_store_set ( + GTK_LIST_STORE (model), &iter, + COLUMN_ID, page_name, + COLUMN_TEXT, caption, + COLUMN_HELP, help_target, + COLUMN_PIXBUF, pixbuf, + COLUMN_PAGE, page, + COLUMN_SORT, sort_order, + -1); + + index = window->priv->index; + path = gtk_tree_model_get_path (model, &iter); + reference = gtk_tree_row_reference_new (model, path); + g_hash_table_insert (index, g_strdup (page_name), reference); + gtk_tree_path_free (path); + + align = g_object_new (GTK_TYPE_ALIGNMENT, NULL); + gtk_widget_show (GTK_WIDGET (align)); + g_object_set_data (G_OBJECT (align), "create_fn", create_fn); + gtk_notebook_append_page (notebook, align, NULL); + gtk_container_child_set ( + GTK_CONTAINER (notebook), align, + "tab-fill", FALSE, "tab-expand", FALSE, NULL); + + /* Force GtkIconView to recalculate the text wrap width, + * otherwise we get a really narrow icon list on the left + * side of the preferences window. */ + gtk_icon_view_set_item_width (icon_view, -1); + gtk_widget_queue_resize (GTK_WIDGET (window)); +} + +void +e_preferences_window_show_page (EPreferencesWindow *window, + const gchar *page_name) +{ + GtkTreeRowReference *reference; + GtkIconView *icon_view; + GtkTreePath *path; + + g_return_if_fail (E_IS_PREFERENCES_WINDOW (window)); + g_return_if_fail (page_name != NULL); + g_return_if_fail (window->priv->setup); + + icon_view = GTK_ICON_VIEW (window->priv->icon_view); + reference = g_hash_table_lookup (window->priv->index, page_name); + g_return_if_fail (reference != NULL); + + path = gtk_tree_row_reference_get_path (reference); + gtk_icon_view_select_path (icon_view, path); + gtk_icon_view_scroll_to_path (icon_view, path, FALSE, 0.0, 0.0); + gtk_tree_path_free (path); +} + +void +e_preferences_window_filter_page (EPreferencesWindow *window, + const gchar *page_name) +{ + GtkTreeRowReference *reference; + GtkIconView *icon_view; + GtkTreePath *path; + + g_return_if_fail (E_IS_PREFERENCES_WINDOW (window)); + g_return_if_fail (page_name != NULL); + g_return_if_fail (window->priv->setup); + + icon_view = GTK_ICON_VIEW (window->priv->icon_view); + reference = g_hash_table_lookup (window->priv->index, page_name); + g_return_if_fail (reference != NULL); + + path = gtk_tree_row_reference_get_path (reference); + gtk_icon_view_select_path (icon_view, path); + gtk_icon_view_scroll_to_path (icon_view, path, FALSE, 0.0, 0.0); + gtk_tree_path_free (path); + + window->priv->filter_view = page_name; + gtk_tree_model_filter_refilter (window->priv->filter); + + /* XXX: We need a better solution to hide the icon view when + * there is just one entry */ + if (strncmp (page_name, "cal", 3) == 0) { + gtk_widget_hide (window->priv->scroll); + } else + gtk_widget_show (window->priv->scroll); +} + +/* + * Create all the deferred configuration pages. + */ +void +e_preferences_window_setup (EPreferencesWindow *window) +{ + gint i, num; + GtkNotebook *notebook; + GtkRequisition requisition; + gint width = -1, height = -1, content_width = -1, content_height = -1; + EPreferencesWindowPrivate *priv; + + g_return_if_fail (E_IS_PREFERENCES_WINDOW (window)); + + priv = E_PREFERENCES_WINDOW_GET_PRIVATE (window); + + if (priv->setup) + return; + + gtk_window_get_default_size (GTK_WINDOW (window), &width, &height); + if (width < 0 || height < 0) { + gtk_widget_get_preferred_size (GTK_WIDGET (window), &requisition, NULL); + + width = requisition.width; + height = requisition.height; + } + + notebook = GTK_NOTEBOOK (priv->notebook); + num = gtk_notebook_get_n_pages (notebook); + + for (i = 0; i < num; i++) { + GtkBin *align; + GtkWidget *content; + EPreferencesWindowCreatePageFn create_fn; + + align = GTK_BIN (gtk_notebook_get_nth_page (notebook, i)); + create_fn = g_object_get_data (G_OBJECT (align), "create_fn"); + + if (!create_fn || gtk_bin_get_child (align)) + continue; + + content = create_fn (window); + if (content) { + GtkScrolledWindow *scrolled; + + scrolled = GTK_SCROLLED_WINDOW (gtk_scrolled_window_new (NULL, NULL)); + gtk_scrolled_window_add_with_viewport (scrolled, content); + gtk_scrolled_window_set_min_content_width (scrolled, 320); + gtk_scrolled_window_set_min_content_height (scrolled, 240); + gtk_scrolled_window_set_policy (scrolled, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type (scrolled, GTK_SHADOW_NONE); + + gtk_viewport_set_shadow_type ( + GTK_VIEWPORT (gtk_bin_get_child (GTK_BIN (scrolled))), + GTK_SHADOW_NONE); + + gtk_widget_show (content); + + gtk_widget_get_preferred_size (GTK_WIDGET (content), &requisition, NULL); + + if (requisition.width > content_width) + content_width = requisition.width; + if (requisition.height > content_height) + content_height = requisition.height; + + gtk_widget_show (GTK_WIDGET (scrolled)); + + gtk_container_add (GTK_CONTAINER (align), GTK_WIDGET (scrolled)); + } + } + + if (content_width > 0 && content_height > 0 && width > 0 && height > 0) { + GdkScreen *screen; + GdkRectangle monitor_area; + gint x = 0, y = 0, monitor; + + screen = gtk_window_get_screen (GTK_WINDOW (window)); + gtk_window_get_position (GTK_WINDOW (window), &x, &y); + + monitor = gdk_screen_get_monitor_at_point (screen, x, y); + if (monitor < 0 || monitor >= gdk_screen_get_n_monitors (screen)) + monitor = 0; + + gdk_screen_get_monitor_workarea (screen, monitor, &monitor_area); + + if (content_width > monitor_area.width - width) + content_width = monitor_area.width - width; + + if (content_height > monitor_area.height - height) + content_height = monitor_area.height - height; + + if (content_width > 0 && content_height > 0) + gtk_window_set_default_size (GTK_WINDOW (window), width + content_width, height + content_height); + } + + priv->setup = TRUE; +} -- cgit v1.2.3