/* * mooutils/mooprefsdialog.c * * Copyright (C) 2004-2006 by Yevgen Muntyan * * 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 of the License, or * (at your option) any later version. * * See COPYING file that comes with this distribution. */ #include "mooutils/mooprefsdialog.h" #include "mooutils/moomarshals.h" #include "mooutils/moocompat.h" #include "mooutils/moodialogs.h" enum { ICON_COLUMN, ICON_ID_COLUMN, LABEL_COLUMN, PAGE_COLUMN, N_COLUMNS }; static void moo_prefs_dialog_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); static void moo_prefs_dialog_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); static void moo_prefs_dialog_destroy (GtkObject *object); static void moo_prefs_dialog_response (GtkDialog *dialog, int response); static void moo_prefs_dialog_init_sig (MooPrefsDialog *dialog); static void moo_prefs_dialog_apply (MooPrefsDialog *dialog); static void setup_pages_list (MooPrefsDialog *dialog); static void pages_list_selection_changed (MooPrefsDialog *dialog, GtkTreeSelection *selection); enum { PROP_0, PROP_HIDE_ON_DELETE }; enum { INIT, APPLY, LAST_SIGNAL }; static guint prefs_dialog_signals[LAST_SIGNAL] = {0}; /* MOO_TYPE_PREFS_DIALOG */ G_DEFINE_TYPE (MooPrefsDialog, moo_prefs_dialog, GTK_TYPE_DIALOG) static void moo_prefs_dialog_class_init (MooPrefsDialogClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GtkObjectClass *gtkobject_class = GTK_OBJECT_CLASS (klass); GtkDialogClass *dialog_class = GTK_DIALOG_CLASS (klass); gobject_class->set_property = moo_prefs_dialog_set_property; gobject_class->get_property = moo_prefs_dialog_get_property; gtkobject_class->destroy = moo_prefs_dialog_destroy; dialog_class->response = moo_prefs_dialog_response; klass->init = moo_prefs_dialog_init_sig; klass->apply = moo_prefs_dialog_apply; g_object_class_install_property (gobject_class, PROP_HIDE_ON_DELETE, g_param_spec_boolean ("hide_on_delete", "hide_on_delete", "Hide on delete", FALSE, G_PARAM_READWRITE)); prefs_dialog_signals[APPLY] = g_signal_new ("apply", G_OBJECT_CLASS_TYPE (klass), (GSignalFlags) (G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION), G_STRUCT_OFFSET (MooPrefsDialogClass, apply), NULL, NULL, _moo_marshal_VOID__VOID, G_TYPE_NONE, 0); prefs_dialog_signals[INIT] = g_signal_new ("init", G_OBJECT_CLASS_TYPE (klass), (GSignalFlags) (G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION), G_STRUCT_OFFSET (MooPrefsDialogClass, init), NULL, NULL, _moo_marshal_VOID__VOID, G_TYPE_NONE, 0); } static void moo_prefs_dialog_init (MooPrefsDialog *dialog) { GtkWidget *hbox, *scrolledwindow, *notebook; gtk_window_set_destroy_with_parent (GTK_WINDOW (dialog), TRUE); gtk_dialog_add_buttons (GTK_DIALOG (dialog), GTK_STOCK_APPLY, GTK_RESPONSE_APPLY, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL); #if GTK_MINOR_VERSION >= 6 gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog), GTK_RESPONSE_OK, GTK_RESPONSE_CANCEL, GTK_RESPONSE_APPLY, -1); #endif /* GTK_MINOR_VERSION >= 6 */ gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK); hbox = gtk_hbox_new (FALSE, 0); gtk_widget_show (GTK_WIDGET (hbox)); gtk_box_pack_start (GTK_BOX (GTK_DIALOG(dialog)->vbox), hbox, TRUE, TRUE, 0); scrolledwindow = gtk_scrolled_window_new (NULL, NULL); gtk_widget_show (scrolledwindow); gtk_box_pack_start (GTK_BOX (hbox), scrolledwindow, FALSE, FALSE, 0); gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolledwindow), GTK_SHADOW_IN); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); setup_pages_list (dialog); gtk_widget_show (GTK_WIDGET (dialog->pages_list)); gtk_container_add (GTK_CONTAINER (scrolledwindow), GTK_WIDGET (dialog->pages_list)); notebook = gtk_notebook_new (); gtk_widget_show (notebook); gtk_box_pack_start (GTK_BOX (hbox), notebook, TRUE, TRUE, 0); GTK_WIDGET_UNSET_FLAGS (notebook, GTK_CAN_FOCUS); gtk_notebook_set_show_tabs (GTK_NOTEBOOK (notebook), FALSE); gtk_notebook_set_show_border (GTK_NOTEBOOK (notebook), FALSE); dialog->notebook = GTK_NOTEBOOK (notebook); } static gboolean destroy_page (GtkTreeModel *model, G_GNUC_UNUSED GtkTreePath *path, GtkTreeIter *iter) { GtkWidget *page = NULL; gtk_tree_model_get (model, iter, PAGE_COLUMN, &page, -1); g_return_val_if_fail (page != NULL, FALSE); g_object_set_data (G_OBJECT (page), "moo-prefs-dialog-row", NULL); gtk_widget_destroy (page); g_object_unref (page); return FALSE; } static void moo_prefs_dialog_destroy (GtkObject *object) { MooPrefsDialog *dialog = MOO_PREFS_DIALOG (object); if (dialog->store) { GtkTreeSelection *selection = gtk_tree_view_get_selection (dialog->pages_list); g_signal_handlers_disconnect_by_func (selection, (gpointer) pages_list_selection_changed, dialog); gtk_tree_view_set_model (dialog->pages_list, NULL); gtk_tree_model_foreach (GTK_TREE_MODEL (dialog->store), (GtkTreeModelForeachFunc) destroy_page, NULL); g_object_unref (dialog->store); dialog->store = NULL; } GTK_OBJECT_CLASS(moo_prefs_dialog_parent_class)->destroy (object); } static void setup_pages_list (MooPrefsDialog *dialog) { GtkWidget *tree; GtkCellRenderer *icon_renderer, *label_renderer; GtkTreeViewColumn *icon_column, *label_column; GtkTreeSelection *selection; dialog->store = gtk_list_store_new (N_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING, MOO_TYPE_PREFS_DIALOG_PAGE); tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (dialog->store)); gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (tree), FALSE); gtk_tree_view_set_enable_search (GTK_TREE_VIEW (tree), FALSE); icon_renderer = gtk_cell_renderer_pixbuf_new (); icon_column = gtk_tree_view_column_new_with_attributes ("Icon", icon_renderer, "stock-id", ICON_ID_COLUMN, "pixbuf", ICON_COLUMN, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW (tree), icon_column); label_renderer = gtk_cell_renderer_text_new (); label_column = gtk_tree_view_column_new_with_attributes ("Label", label_renderer, "markup", LABEL_COLUMN, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW (tree), label_column); selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree)); gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE); g_signal_connect_swapped (G_OBJECT (selection), "changed", G_CALLBACK (pages_list_selection_changed), dialog); dialog->pages_list = GTK_TREE_VIEW (tree); } static void pages_list_selection_changed (MooPrefsDialog *dialog, GtkTreeSelection *selection) { GtkTreeIter iter; GtkTreeModel *model; if (gtk_tree_selection_get_selected (selection, &model, &iter)) { MooPrefsDialogPage *page = NULL; gtk_tree_model_get (model, &iter, PAGE_COLUMN, &page, -1); g_return_if_fail (page != NULL); gtk_notebook_set_current_page (dialog->notebook, gtk_notebook_page_num (dialog->notebook, GTK_WIDGET (page))); g_object_unref (page); } else { g_critical ("%s: nothing selected", G_STRLOC); if (gtk_tree_model_get_iter_first (model, &iter)) gtk_tree_selection_select_iter (selection, &iter); else g_critical ("%s: list is empty", G_STRLOC); } } static void moo_prefs_dialog_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MooPrefsDialog *dialog = MOO_PREFS_DIALOG (object); g_return_if_fail (dialog != NULL); switch (prop_id) { case PROP_HIDE_ON_DELETE: dialog->hide_on_delete = g_value_get_boolean (value); g_object_notify (object, "hide-on-delete"); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void moo_prefs_dialog_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MooPrefsDialog *dialog = MOO_PREFS_DIALOG (object); g_return_if_fail (dialog != NULL); switch (prop_id) { case PROP_HIDE_ON_DELETE: g_value_set_boolean (value, dialog->hide_on_delete); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } /**************************************************************************/ /* MooPrefsDialog methods */ GtkWidget* moo_prefs_dialog_new (const char *title) { return GTK_WIDGET (g_object_new (MOO_TYPE_PREFS_DIALOG, "title", title, NULL)); } void moo_prefs_dialog_run (MooPrefsDialog *dialog, GtkWidget *parent) { g_return_if_fail (MOO_IS_PREFS_DIALOG (dialog)); moo_position_window (GTK_WIDGET (dialog), parent, FALSE, FALSE, 0, 0); g_signal_emit_by_name (dialog, "init"); gtk_window_present (GTK_WINDOW (dialog)); } static void moo_prefs_dialog_response (GtkDialog *dialog, int response) { G_GNUC_UNUSED GtkWindow *parent; switch (response) { case GTK_RESPONSE_APPLY: g_signal_emit_by_name (dialog, "apply"); break; case GTK_RESPONSE_OK: g_signal_emit_by_name (dialog, "apply"); /* fallthrough */ default: if (MOO_PREFS_DIALOG(dialog)->hide_on_delete) gtk_widget_hide (GTK_WIDGET (dialog)); else gtk_widget_destroy (GTK_WIDGET (dialog)); #ifdef __WIN32__ parent = gtk_window_get_transient_for (GTK_WINDOW (dialog)); if (parent) gtk_window_present (parent); #endif } } static void moo_prefs_dialog_init_sig (MooPrefsDialog *dialog) { int n, i; g_return_if_fail (MOO_IS_PREFS_DIALOG (dialog)); n = gtk_notebook_get_n_pages (dialog->notebook); for (i = 0; i < n; ++i) g_signal_emit_by_name (gtk_notebook_get_nth_page (dialog->notebook, i), "init", NULL); } static void moo_prefs_dialog_apply (MooPrefsDialog *dialog) { int n, i; g_return_if_fail (MOO_IS_PREFS_DIALOG (dialog)); n = gtk_notebook_get_n_pages (dialog->notebook); for (i = 0; i < n; ++i) g_signal_emit_by_name (gtk_notebook_get_nth_page (dialog->notebook, i), "apply", NULL); } void moo_prefs_dialog_append_page (MooPrefsDialog *dialog, GtkWidget *page) { moo_prefs_dialog_insert_page (dialog, page, -1); } void moo_prefs_dialog_insert_page (MooPrefsDialog *dialog, GtkWidget *page, int position) { char *label = NULL, *icon_id = NULL; GdkPixbuf *icon = NULL; GtkTreeIter iter; GtkTreeRowReference *ref; GtkTreePath *path = NULL; g_return_if_fail (MOO_IS_PREFS_DIALOG (dialog)); g_return_if_fail (MOO_IS_PREFS_DIALOG_PAGE (page)); g_return_if_fail (page->parent == NULL); if (position < 0) position = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (dialog->store), NULL); gtk_list_store_insert (dialog->store, &iter, position); gtk_widget_show (page); gtk_notebook_insert_page (dialog->notebook, page, NULL, -1); g_object_get (page, "label", &label, "icon", &icon, "icon-stock-id", &icon_id, NULL); gtk_list_store_set (dialog->store, &iter, ICON_ID_COLUMN, icon_id, ICON_COLUMN, icon, LABEL_COLUMN, label, PAGE_COLUMN, page, -1); path = gtk_tree_model_get_path (GTK_TREE_MODEL (dialog->store), &iter); ref = gtk_tree_row_reference_new (GTK_TREE_MODEL (dialog->store), path); g_object_set_data_full (G_OBJECT (page), "moo-prefs-dialog-row", ref, (GDestroyNotify) gtk_tree_row_reference_free); gtk_tree_path_free (path); g_free (label); g_free (icon_id); if (icon) g_object_unref (icon); } void moo_prefs_dialog_remove_page (MooPrefsDialog *dialog, GtkWidget *page) { GtkTreeRowReference *ref; GtkTreePath *path; GtkTreeIter iter; g_return_if_fail (MOO_IS_PREFS_DIALOG (dialog)); g_return_if_fail (MOO_IS_PREFS_DIALOG_PAGE (page)); ref = g_object_get_data (G_OBJECT (page), "moo-prefs-dialog-row"); g_return_if_fail (ref && gtk_tree_row_reference_valid (ref)); path = gtk_tree_row_reference_get_path (ref); gtk_tree_model_get_iter (GTK_TREE_MODEL (dialog->store), &iter, path); gtk_list_store_remove (dialog->store, &iter); g_object_set_data (G_OBJECT (page), "moo-prefs-dialog-row", NULL); gtk_tree_path_free (path); gtk_notebook_remove_page (dialog->notebook, gtk_notebook_page_num (dialog->notebook, page)); }