From 11044cd0ebb4fff492a207750050111d895d3bce Mon Sep 17 00:00:00 2001 From: =?utf8?q?Dan=20Vr=C3=A1til?= Date: Thu, 5 May 2011 10:01:43 -0400 Subject: [PATCH] Bug 332497 - Add Edit -> Available Categories Split ECategoriesDialog into smaller widgets: ECategoriesEditor ECategoriesSelector ECategoryEditor --- .../libedataserverui/libedataserverui-docs.sgml | 3 + .../libedataserverui/libedataserverui-sections.txt | 66 ++ .../libedataserverui/libedataserverui.types | 6 + .../libedataserverui/tmpl/e-categories-editor.sgml | 87 +++ .../tmpl/e-categories-selector.sgml | 114 +++ .../libedataserverui/tmpl/e-category-editor.sgml | 58 ++ libedataserverui/Makefile.am | 6 + libedataserverui/e-categories-dialog.c | 823 +-------------------- libedataserverui/e-categories-editor.c | 427 +++++++++++ libedataserverui/e-categories-editor.h | 76 ++ libedataserverui/e-categories-selector.c | 570 ++++++++++++++ libedataserverui/e-categories-selector.h | 85 +++ libedataserverui/e-category-editor.c | 348 +++++++++ libedataserverui/e-category-editor.h | 69 ++ libedataserverui/e-data-server-ui-marshal.list | 1 + 15 files changed, 1953 insertions(+), 786 deletions(-) create mode 100644 docs/reference/libedataserverui/tmpl/e-categories-editor.sgml create mode 100644 docs/reference/libedataserverui/tmpl/e-categories-selector.sgml create mode 100644 docs/reference/libedataserverui/tmpl/e-category-editor.sgml create mode 100644 libedataserverui/e-categories-editor.c create mode 100644 libedataserverui/e-categories-editor.h create mode 100644 libedataserverui/e-categories-selector.c create mode 100644 libedataserverui/e-categories-selector.h create mode 100644 libedataserverui/e-category-editor.c create mode 100644 libedataserverui/e-category-editor.h diff --git a/docs/reference/libedataserverui/libedataserverui-docs.sgml b/docs/reference/libedataserverui/libedataserverui-docs.sgml index 130697f..3242839 100644 --- a/docs/reference/libedataserverui/libedataserverui-docs.sgml +++ b/docs/reference/libedataserverui/libedataserverui-docs.sgml @@ -12,6 +12,9 @@ Evolution-Data-Server Manual: Graphical Utilities (libedataserverui) + + + diff --git a/docs/reference/libedataserverui/libedataserverui-sections.txt b/docs/reference/libedataserverui/libedataserverui-sections.txt index 6165102..e703f1c 100644 --- a/docs/reference/libedataserverui/libedataserverui-sections.txt +++ b/docs/reference/libedataserverui/libedataserverui-sections.txt @@ -26,6 +26,52 @@ e_categories_dialog_get_type
+e-categories-editor +ECategoriesEditor +ECategoriesEditor +e_categories_editor_new +e_categories_editor_get_categories +e_categories_editor_set_categories +e_categories_editor_get_entry_visible +e_categories_editor_set_entry_visible + +E_CATEGORIES_EDITOR +E_IS_CATEGORIES_EDITOR +E_TYPE_CATEGORIES_EDITOR +E_CATEGORIES_EDITOR_CLASS +E_IS_CATEGORIES_EDITOR_CLASS +E_CATEGORIES_EDITOR_GET_CLASS +ECategoriesEditorClass + +ECategoriesEditorPrivate +e_categories_editor_get_type +
+ +
+e-categories-selector +ECategoriesSelector +ECategoriesSelector +e_categories_selector_new +e_categories_selector_get_checked +e_categories_selector_set_checked +e_categories_selector_get_items_checkable +e_categories_selector_set_items_checkable +e_categories_selector_delete_selection +e_categories_selector_get_selected + +E_CATEGORIES_SELECTOR +E_IS_CATEGORIES_SELECTOR +E_TYPE_CATEGORIES_SELECTOR +E_CATEGORIES_SELECTOR_CLASS +E_IS_CATEGORIES_SELECTOR_CLASS +E_CATEGORIES_SELECTOR_GET_CLASS +ECategoriesSelectorClass + +ECategoriesSelectorPrivate +e_categories_selector_get_type +
+ +
e-category-completion ECategoryCompletion ECategoryCompletion @@ -44,6 +90,26 @@ e_category_completion_get_type
+e-category-editor +ECategoryEditor +ECategoryEditor +e_category_editor_new +e_category_editor_create_category +e_category_editor_edit_category + +E_CATEGORY_EDITOR +E_IS_CATEGORY_EDITOR +E_TYPE_CATEGORY_EDITOR +E_CATEGORY_EDITOR_CLASS +E_IS_CATEGORY_EDITOR_CLASS +E_CATEGORY_EDITOR_GET_CLASS +ECategoryEditorClass + +ECategoryEditorPrivate +e_category_editor_get_type +
+ +
e-cell-renderer-color ECellRendererColor ECellRendererColor diff --git a/docs/reference/libedataserverui/libedataserverui.types b/docs/reference/libedataserverui/libedataserverui.types index 04eae99..0041710 100644 --- a/docs/reference/libedataserverui/libedataserverui.types +++ b/docs/reference/libedataserverui/libedataserverui.types @@ -1,4 +1,7 @@ #include +#include +#include +#include #include #include #include @@ -12,6 +15,9 @@ #include e_categories_dialog_get_type +e_categories_editor_get_type +e_categories_selector_get_type +e_category_editor_get_type e_contact_store_get_type e_destination_store_get_type e_name_selector_get_type diff --git a/docs/reference/libedataserverui/tmpl/e-categories-editor.sgml b/docs/reference/libedataserverui/tmpl/e-categories-editor.sgml new file mode 100644 index 0000000..1671364 --- /dev/null +++ b/docs/reference/libedataserverui/tmpl/e-categories-editor.sgml @@ -0,0 +1,87 @@ + +ECategoriesEditor + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +@ecategorieseditor: the object which received the signal. + + + + + + + + + + + +@void: +@Returns: + + + + + + + +@editor: +@Returns: + + + + + + + +@editor: +@categories: + + + + + + + +@editor: +@Returns: + + + + + + + +@editor: +@entry_visible: + + diff --git a/docs/reference/libedataserverui/tmpl/e-categories-selector.sgml b/docs/reference/libedataserverui/tmpl/e-categories-selector.sgml new file mode 100644 index 0000000..f0c1dc2 --- /dev/null +++ b/docs/reference/libedataserverui/tmpl/e-categories-selector.sgml @@ -0,0 +1,114 @@ + +ECategoriesSelector + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +@ecategoriesselector: the object which received the signal. +@arg1: +@arg2: + + + + + + +@ecategoriesselector: the object which received the signal. +@arg1: + + + + + + + + + + + +@void: +@Returns: + + + + + + + +@selector: +@Returns: + + + + + + + +@selector: +@categories: + + + + + + + +@selector: +@Returns: + + + + + + + +@selectr: +@checkable: + + + + + + + +@selector: + + + + + + + +@selector: +@Returns: + + diff --git a/docs/reference/libedataserverui/tmpl/e-category-editor.sgml b/docs/reference/libedataserverui/tmpl/e-category-editor.sgml new file mode 100644 index 0000000..8ef7cf3 --- /dev/null +++ b/docs/reference/libedataserverui/tmpl/e-category-editor.sgml @@ -0,0 +1,58 @@ + +ECategoryEditor + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +@void: +@Returns: + + + + + + + +@editor: +@Returns: + + + + + + + +@editor: +@category: +@Returns: + + diff --git a/libedataserverui/Makefile.am b/libedataserverui/Makefile.am index e20028d..af00b8a 100644 --- a/libedataserverui/Makefile.am +++ b/libedataserverui/Makefile.am @@ -17,7 +17,10 @@ lib_LTLIBRARIES = libedataserverui-3.0.la libedataserveruiinclude_HEADERS = \ e-categories-dialog.h \ + e-categories-editor.h \ + e-categories-selector.h \ e-category-completion.h \ + e-category-editor.h \ e-destination-store.h \ e-book-auth-util.h \ e-contact-store.h \ @@ -44,7 +47,10 @@ endif libedataserverui_3_0_la_SOURCES = \ $(MARSHAL_GENERATED) \ e-categories-dialog.c \ + e-categories-editor.c \ + e-categories-selector.c \ e-category-completion.c \ + e-category-editor.c \ e-destination-store.c \ e-book-auth-util.c \ e-contact-store.c \ diff --git a/libedataserverui/e-categories-dialog.c b/libedataserverui/e-categories-dialog.c index 6e3b360..0be5698 100644 --- a/libedataserverui/e-categories-dialog.c +++ b/libedataserverui/e-categories-dialog.c @@ -28,756 +28,63 @@ #include "libedataserver/e-categories.h" #include "libedataserver/libedataserver-private.h" #include "e-categories-dialog.h" +#include "e-categories-editor.h" +#include "e-categories-selector.h" #include "e-category-completion.h" +#include "e-category-editor.h" G_DEFINE_TYPE (ECategoriesDialog, e_categories_dialog, GTK_TYPE_DIALOG) struct _ECategoriesDialogPrivate { - GtkWidget *categories_entry; - GtkWidget *categories_list; - GtkWidget *new_button; - GtkWidget *edit_button; - GtkWidget *delete_button; - - GHashTable *selected_categories; - - guint ignore_category_changes : 1; -}; - -enum { - COLUMN_ACTIVE, - COLUMN_ICON, - COLUMN_CATEGORY, - N_COLUMNS + GtkWidget *categories_editor; }; -static gpointer parent_class; - -/* Category properties dialog */ - -typedef struct { - ECategoriesDialog *parent; - GtkWidget *the_dialog; - GtkWidget *category_name; - GtkWidget *category_icon; -} CategoryPropertiesDialog; - -static void -update_preview (GtkFileChooser *chooser, gpointer user_data) -{ - GtkImage *image; - gchar *filename; - - g_return_if_fail (chooser != NULL); - - image = GTK_IMAGE (gtk_file_chooser_get_preview_widget (chooser)); - g_return_if_fail (image != NULL); - - filename = gtk_file_chooser_get_preview_filename (chooser); - - gtk_image_set_from_file (image, filename); - gtk_file_chooser_set_preview_widget_active (chooser, filename != NULL); - - g_free (filename); -} - -static void -file_chooser_response (GtkDialog *dialog, gint response_id, GtkFileChooser *button) -{ - g_return_if_fail (button != NULL); - - if (response_id == GTK_RESPONSE_NO) { - gtk_file_chooser_unselect_all (button); - } -} - -static void -category_name_changed_cb (GtkEntry *category_name_entry, CategoryPropertiesDialog *prop_dialog) -{ - gchar *name; - - g_return_if_fail (prop_dialog != NULL); - g_return_if_fail (prop_dialog->the_dialog != NULL); - g_return_if_fail (category_name_entry != NULL); - - name = g_strdup (gtk_entry_get_text (category_name_entry)); - if (name) - name = g_strstrip (name); - - gtk_dialog_set_response_sensitive (GTK_DIALOG (prop_dialog->the_dialog), GTK_RESPONSE_OK, name && *name); - - g_free (name); -} - -static CategoryPropertiesDialog * -create_properties_dialog (ECategoriesDialog *parent) -{ - CategoryPropertiesDialog *prop_dialog; - GtkWidget *properties_dialog; - GtkWidget *dialog_content; - GtkWidget *dialog_action_area; - GtkWidget *table_category_properties; - GtkWidget *label4; - GtkWidget *label6; - GtkWidget *category_name; - GtkWidget *cancelbutton1; - GtkWidget *okbutton1; - - properties_dialog = gtk_dialog_new (); - gtk_window_set_title (GTK_WINDOW (properties_dialog), _("Category Properties")); - gtk_window_set_type_hint (GTK_WINDOW (properties_dialog), GDK_WINDOW_TYPE_HINT_DIALOG); - - dialog_content = gtk_dialog_get_content_area (GTK_DIALOG (properties_dialog)); - - table_category_properties = gtk_table_new (3, 2, FALSE); - gtk_box_pack_start (GTK_BOX (dialog_content), table_category_properties, TRUE, TRUE, 0); - gtk_container_set_border_width (GTK_CONTAINER (table_category_properties), 12); - gtk_table_set_row_spacings (GTK_TABLE (table_category_properties), 6); - gtk_table_set_col_spacings (GTK_TABLE (table_category_properties), 6); - - label4 = gtk_label_new_with_mnemonic (_("Category _Name")); - gtk_table_attach (GTK_TABLE (table_category_properties), label4, 0, 1, 0, 1, - (GtkAttachOptions) (GTK_FILL), - (GtkAttachOptions) (0), 0, 0); - gtk_misc_set_alignment (GTK_MISC (label4), 0, 0.5); - - label6 = gtk_label_new_with_mnemonic (_("Category _Icon")); - gtk_table_attach (GTK_TABLE (table_category_properties), label6, 0, 1, 2, 3, - (GtkAttachOptions) (GTK_FILL), - (GtkAttachOptions) (0), 0, 0); - gtk_misc_set_alignment (GTK_MISC (label6), 0, 0.5); - - category_name = gtk_entry_new (); - gtk_table_attach (GTK_TABLE (table_category_properties), category_name, 1, 2, 0, 1, - (GtkAttachOptions) (GTK_EXPAND | GTK_SHRINK | GTK_FILL), - (GtkAttachOptions) (0), 0, 0); - - dialog_action_area = gtk_dialog_get_action_area (GTK_DIALOG (properties_dialog)); - gtk_button_box_set_layout (GTK_BUTTON_BOX (dialog_action_area), GTK_BUTTONBOX_END); - - cancelbutton1 = gtk_button_new_from_stock ("gtk-cancel"); - gtk_dialog_add_action_widget (GTK_DIALOG (properties_dialog), cancelbutton1, GTK_RESPONSE_CANCEL); - gtk_widget_set_can_default (cancelbutton1, TRUE); - - okbutton1 = gtk_button_new_from_stock ("gtk-ok"); - gtk_dialog_add_action_widget (GTK_DIALOG (properties_dialog), okbutton1, GTK_RESPONSE_OK); - gtk_widget_set_can_default (okbutton1, TRUE); - - gtk_label_set_mnemonic_widget (GTK_LABEL (label4), category_name); - - gtk_widget_show_all (dialog_content); - - prop_dialog = g_new0 (CategoryPropertiesDialog, 1); - prop_dialog->parent = parent; - - prop_dialog->the_dialog = properties_dialog; - gtk_window_set_transient_for (GTK_WINDOW (prop_dialog->the_dialog), GTK_WINDOW (parent)); - - prop_dialog->category_name = category_name; - g_signal_connect (prop_dialog->category_name, "changed", G_CALLBACK (category_name_changed_cb), prop_dialog); - category_name_changed_cb (GTK_ENTRY (prop_dialog->category_name), prop_dialog); - - if (table_category_properties) { - GtkFileChooser *chooser; - GtkWidget *dialog, *button; - GtkWidget *image = gtk_image_new (); - - dialog = gtk_file_chooser_dialog_new ( _("Category Icon"), - NULL, - GTK_FILE_CHOOSER_ACTION_OPEN, - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, - NULL); - - button = gtk_button_new_with_mnemonic (_("_No Image")); - gtk_button_set_image (GTK_BUTTON (button), gtk_image_new_from_stock (GTK_STOCK_CLOSE, GTK_ICON_SIZE_BUTTON)); - gtk_widget_show (button); - gtk_dialog_add_action_widget (GTK_DIALOG (dialog), button, GTK_RESPONSE_NO); - gtk_dialog_add_button (GTK_DIALOG (dialog), GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT); - button = NULL; - - chooser = GTK_FILE_CHOOSER (gtk_file_chooser_button_new_with_dialog (dialog)); - - gtk_file_chooser_set_local_only (chooser, TRUE); - - g_signal_connect (dialog, "response", (GCallback) file_chooser_response, chooser); - - prop_dialog->category_icon = GTK_WIDGET (chooser); - gtk_widget_show (prop_dialog->category_icon); - gtk_table_attach (GTK_TABLE (table_category_properties), prop_dialog->category_icon, 1, 2, 2, 3, GTK_FILL, GTK_FILL, 0, 0); - - gtk_widget_show (image); - - gtk_file_chooser_set_preview_widget (chooser, image); - gtk_file_chooser_set_preview_widget_active (chooser, TRUE); - - g_signal_connect (G_OBJECT (chooser), "update-preview", (GCallback) update_preview, NULL); - } - - return prop_dialog; -} - static void -free_properties_dialog (CategoryPropertiesDialog *prop_dialog) +entry_changed_cb (GtkEntry *entry, + ECategoriesDialog *dialog) { - if (prop_dialog->the_dialog) { - gtk_widget_destroy (prop_dialog->the_dialog); - prop_dialog->the_dialog = NULL; - } - - g_free (prop_dialog); -} - -static void -categories_dialog_build_model (ECategoriesDialog *dialog) -{ - GtkTreeView *tree_view; - GtkListStore *store; - GList *list, *iter; - - store = gtk_list_store_new ( - N_COLUMNS, G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF, G_TYPE_STRING); - - gtk_tree_sortable_set_sort_column_id ( - GTK_TREE_SORTABLE (store), - COLUMN_CATEGORY, GTK_SORT_ASCENDING); - - list = e_categories_get_list (); - for (iter = list; iter != NULL; iter = iter->next) { - const gchar *category_name = iter->data; - const gchar *filename; - GdkPixbuf *pixbuf = NULL; - GtkTreeIter iter; - gboolean active; - - /* Only add user-visible categories. */ - if (!e_categories_is_searchable (category_name)) - continue; - - active = (g_hash_table_lookup ( - dialog->priv->selected_categories, - category_name) != NULL); - - filename = e_categories_get_icon_file_for (category_name); - if (filename != NULL) - pixbuf = gdk_pixbuf_new_from_file (filename, NULL); - - gtk_list_store_append (store, &iter); - - gtk_list_store_set ( - store, &iter, - COLUMN_ACTIVE, active, - COLUMN_ICON, pixbuf, - COLUMN_CATEGORY, category_name, - -1); - - if (pixbuf != NULL) - g_object_unref (pixbuf); - } - - tree_view = GTK_TREE_VIEW (dialog->priv->categories_list); - gtk_tree_view_set_model (tree_view, GTK_TREE_MODEL (store)); - - /* This has to be reset everytime we install a new model. */ - gtk_tree_view_set_search_column (tree_view, COLUMN_CATEGORY); - - g_list_free (list); - g_object_unref (store); -} - -static void -categories_dialog_listener_cb (gpointer useless_pointer, - ECategoriesDialog *dialog) -{ - /* Don't rebuild the model if we're in - * the middle of changing it ourselves. */ - if (dialog->priv->ignore_category_changes) - return; - - categories_dialog_build_model (dialog); -} - -static void -add_comma_sep_categories (gpointer key, gpointer value, gpointer user_data) -{ - GString **str = user_data; - - if (strlen ((*str)->str) > 0) - *str = g_string_append (*str, ","); - - *str = g_string_append (*str, (const gchar *) key); -} - -static void -category_toggled_cb (GtkCellRenderer *renderer, const gchar *path, gpointer user_data) -{ - ECategoriesDialogPrivate *priv; - GtkTreeIter iter; - GtkTreeModel *model; - ECategoriesDialog *dialog = user_data; - - priv = dialog->priv; - model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->categories_list)); - - if (gtk_tree_model_get_iter_from_string (model, &iter, path)) { - gboolean place_bool; - gchar *place_string; - GString *str; - - gtk_tree_model_get (model, &iter, - COLUMN_ACTIVE, &place_bool, - COLUMN_CATEGORY, &place_string, - -1); - if (place_bool) { - g_hash_table_remove (priv->selected_categories, place_string); - gtk_list_store_set (GTK_LIST_STORE (model), &iter, COLUMN_ACTIVE, FALSE, -1); - } else { - g_hash_table_insert (priv->selected_categories, g_strdup (place_string), g_strdup (place_string)); - gtk_list_store_set (GTK_LIST_STORE (model), &iter, COLUMN_ACTIVE, TRUE, -1); - } - - str = g_string_new (""); - g_hash_table_foreach (priv->selected_categories, (GHFunc) add_comma_sep_categories, &str); - gtk_entry_set_text (GTK_ENTRY (priv->categories_entry), str->str); - - /* free memory */ - g_string_free (str, TRUE); - g_free (place_string); - - gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), GTK_RESPONSE_OK, TRUE); - } -} - -static void -entry_changed_cb (GtkEditable *editable, gpointer user_data) -{ - ECategoriesDialog *dialog = user_data; - - gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), GTK_RESPONSE_OK, TRUE); -} - -static gchar * -check_category_name (const gchar *name) -{ - GString *str = NULL; - gchar *p = (gchar *) name; - - str = g_string_new (""); - while (*p) { - switch (*p) { - case ',' : - break; - default : - str = g_string_append_c (str, *p); - } - p++; - } - - p = g_strstrip (g_string_free (str, FALSE)); - - return p; -} - -static void -new_button_clicked_cb (GtkButton *button, gpointer user_data) -{ - ECategoriesDialog *dialog; - CategoryPropertiesDialog *prop_dialog; - - dialog = user_data; - - prop_dialog = create_properties_dialog (dialog); - if (!prop_dialog) - return; - - do { - if (gtk_dialog_run (GTK_DIALOG (prop_dialog->the_dialog)) == GTK_RESPONSE_OK) { - const gchar *category_name; - gchar *correct_category_name; - - category_name = gtk_entry_get_text (GTK_ENTRY (prop_dialog->category_name)); - correct_category_name = check_category_name (category_name); - - if (e_categories_exist (correct_category_name)) { - GtkWidget *error_dialog; - - error_dialog = gtk_message_dialog_new ( - GTK_WINDOW (prop_dialog->the_dialog), - 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, - _("There is already a category '%s' in the configuration. Please use another name"), - category_name); - - gtk_dialog_run (GTK_DIALOG (error_dialog)); - gtk_widget_destroy (error_dialog); - g_free (correct_category_name); - } else { - gchar *category_icon; - - category_icon = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (prop_dialog->category_icon)); - - e_categories_add (correct_category_name, NULL, category_icon, TRUE); - - g_free (category_icon); - g_free (correct_category_name); - - break; - } - } else - break; - } while (TRUE); - - free_properties_dialog (prop_dialog); -} - -static void -edit_button_clicked_cb (GtkButton *button, gpointer user_data) -{ - ECategoriesDialog *dialog; - CategoryPropertiesDialog *prop_dialog; - GtkTreeSelection *selection; - GtkTreeIter iter; - GtkTreeModel *model; - GtkTreeView *tree_view; - GList *selected; - gchar *category_name; - const gchar *icon_file; - - dialog = user_data; - - tree_view = GTK_TREE_VIEW (dialog->priv->categories_list); - selection = gtk_tree_view_get_selection (tree_view); - selected = gtk_tree_selection_get_selected_rows (selection, &model); - g_return_if_fail (g_list_length (selected) == 1); - - /* load the properties dialog */ - prop_dialog = create_properties_dialog (dialog); - if (!prop_dialog) - return; - - gtk_tree_model_get_iter (model, &iter, selected->data); - gtk_tree_model_get (model, &iter, COLUMN_CATEGORY, &category_name, -1); - gtk_entry_set_text (GTK_ENTRY (prop_dialog->category_name), category_name); - gtk_widget_set_sensitive (prop_dialog->category_name, FALSE); - - icon_file = e_categories_get_icon_file_for (category_name); - if (icon_file) - gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (prop_dialog->category_icon), icon_file); - - if (gtk_dialog_run (GTK_DIALOG (prop_dialog->the_dialog)) == GTK_RESPONSE_OK) { - gchar *category_icon; - - category_icon = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (prop_dialog->category_icon)); - - e_categories_set_icon_file_for (category_name, category_icon); - - if (category_icon) { - GdkPixbuf *icon = NULL; - - icon = gdk_pixbuf_new_from_file (category_icon, NULL); - if (icon) { - gtk_list_store_set (GTK_LIST_STORE (model), &iter, COLUMN_ICON, icon, -1); - g_object_unref (icon); - } - - g_free (category_icon); - } else { - gtk_list_store_set (GTK_LIST_STORE (model), &iter, COLUMN_ICON, NULL, -1); - } - - gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), GTK_RESPONSE_OK, TRUE); - } - - g_free (category_name); - free_properties_dialog (prop_dialog); - - g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL); - g_list_free (selected); -} - -static void -categories_dialog_delete_cb (ECategoriesDialog *dialog) -{ - GtkTreeSelection *selection; - GtkTreeView *tree_view; - GtkTreeModel *model; - GList *selected, *item; - - tree_view = GTK_TREE_VIEW (dialog->priv->categories_list); - selection = gtk_tree_view_get_selection (tree_view); - selected = gtk_tree_selection_get_selected_rows (selection, &model); - - /* Remove categories in reverse order to avoid invalidating - * tree paths as we iterate over the list. Note, the list is - * probably already sorted but we sort again just to be safe. */ - selected = g_list_reverse (g_list_sort ( - selected, (GCompareFunc) gtk_tree_path_compare)); - - /* Prevent the model from being rebuilt every time we - * remove a category, since we're already modifying it. */ - dialog->priv->ignore_category_changes = TRUE; - - for (item = selected; item != NULL; item = item->next) { - GtkTreePath *path = item->data; - GtkTreeIter iter; - gchar *category; - gint column_id; - - column_id = COLUMN_CATEGORY; - gtk_tree_model_get_iter (model, &iter, path); - gtk_tree_model_get (model, &iter, column_id, &category, -1); - gtk_list_store_remove (GTK_LIST_STORE (model), &iter); - e_categories_remove (category); - g_free (category); - } - - dialog->priv->ignore_category_changes = FALSE; - - /* If we only removed one category, try to select another. */ - if (g_list_length (selected) == 1) { - GtkTreePath *path = selected->data; - - gtk_tree_selection_select_path (selection, path); - if (!gtk_tree_selection_path_is_selected (selection, path)) - if (gtk_tree_path_prev (path)) - gtk_tree_selection_select_path (selection, path); - } - - g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL); - g_list_free (selected); -} - -static void -categories_dialog_selection_changed_cb (ECategoriesDialog *dialog, - GtkTreeSelection *selection) -{ - GtkWidget *widget; - gint n_rows; - - n_rows = gtk_tree_selection_count_selected_rows (selection); - - widget = dialog->priv->edit_button; - gtk_widget_set_sensitive (widget, n_rows == 1); - - widget = dialog->priv->delete_button; - gtk_widget_set_sensitive (widget, n_rows >= 1); -} - -static gboolean -categories_dialog_key_press_event (ECategoriesDialog *dialog, - GdkEventKey *event) -{ - GtkButton *button; - - button = GTK_BUTTON (dialog->priv->delete_button); - - if (event->keyval == GDK_KEY_Delete) { - gtk_button_clicked (button); - return TRUE; - } - - return FALSE; -} - -static void -categories_dialog_dispose (GObject *object) -{ - ECategoriesDialogPrivate *priv; - - priv = E_CATEGORIES_DIALOG (object)->priv; - - g_hash_table_remove_all (priv->selected_categories); - - /* Chain up to parent's dispose() method. */ - G_OBJECT_CLASS (parent_class)->dispose (object); -} - -static void -categories_dialog_finalize (GObject *object) -{ - ECategoriesDialogPrivate *priv; - - priv = E_CATEGORIES_DIALOG (object)->priv; - - e_categories_unregister_change_listener ( - G_CALLBACK (categories_dialog_listener_cb), object); - - g_hash_table_destroy (priv->selected_categories); - - /* Chain up to parent's finalize() method. */ - G_OBJECT_CLASS (parent_class)->finalize (object); + gtk_dialog_set_response_sensitive ( + GTK_DIALOG (dialog), GTK_RESPONSE_OK, TRUE); } static void e_categories_dialog_class_init (ECategoriesDialogClass *class) { - GObjectClass *object_class; - - parent_class = g_type_class_peek_parent (class); g_type_class_add_private (class, sizeof (ECategoriesDialogPrivate)); - - object_class = G_OBJECT_CLASS (class); - object_class->dispose = categories_dialog_dispose; - object_class->finalize = categories_dialog_finalize; } static void e_categories_dialog_init (ECategoriesDialog *dialog) { - GtkCellRenderer *renderer; - GtkEntryCompletion *completion; - GtkTreeViewColumn *column; - GtkTreeSelection *selection; - GtkTreeView *tree_view; GtkWidget *dialog_content; - GtkWidget *table_categories; - GtkWidget *entry_categories; - GtkWidget *label_header; - GtkWidget *label2; - GtkWidget *scrolledwindow1; - GtkWidget *categories_list; - GtkWidget *hbuttonbox1; - GtkWidget *button_new; - GtkWidget *button_edit; - GtkWidget *alignment1; - GtkWidget *hbox1; - GtkWidget *image1; - GtkWidget *label3; - GtkWidget *button_delete; - - gtk_window_set_default_size (GTK_WINDOW (dialog), -1, 400); - - dialog_content = gtk_dialog_get_content_area (GTK_DIALOG (dialog)); - - table_categories = gtk_table_new (5, 1, FALSE); - gtk_box_pack_start (GTK_BOX (dialog_content), table_categories, TRUE, TRUE, 0); - gtk_container_set_border_width (GTK_CONTAINER (table_categories), 12); - gtk_table_set_row_spacings (GTK_TABLE (table_categories), 6); - gtk_table_set_col_spacings (GTK_TABLE (table_categories), 6); - - entry_categories = gtk_entry_new (); - gtk_table_attach (GTK_TABLE (table_categories), entry_categories, 0, 1, 1, 2, - (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), - (GtkAttachOptions) (0), 0, 0); - - label_header = gtk_label_new_with_mnemonic (_("Currently _used categories:")); - gtk_table_attach (GTK_TABLE (table_categories), label_header, 0, 1, 0, 1, - (GtkAttachOptions) (GTK_FILL), - (GtkAttachOptions) (0), 0, 0); - gtk_label_set_justify (GTK_LABEL (label_header), GTK_JUSTIFY_CENTER); - gtk_misc_set_alignment (GTK_MISC (label_header), 0, 0.5); - - label2 = gtk_label_new_with_mnemonic (_("_Available Categories:")); - gtk_table_attach (GTK_TABLE (table_categories), label2, 0, 1, 2, 3, - (GtkAttachOptions) (GTK_FILL), - (GtkAttachOptions) (0), 0, 0); - gtk_label_set_justify (GTK_LABEL (label2), GTK_JUSTIFY_CENTER); - gtk_misc_set_alignment (GTK_MISC (label2), 0, 0.5); - - scrolledwindow1 = gtk_scrolled_window_new (NULL, NULL); - gtk_table_attach (GTK_TABLE (table_categories), scrolledwindow1, 0, 1, 3, 4, - (GtkAttachOptions) (GTK_EXPAND | GTK_SHRINK | GTK_FILL), - (GtkAttachOptions) (GTK_EXPAND | GTK_SHRINK | GTK_FILL), 0, 0); - gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow1), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); - gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolledwindow1), GTK_SHADOW_IN); - - categories_list = gtk_tree_view_new (); - gtk_container_add (GTK_CONTAINER (scrolledwindow1), categories_list); - gtk_widget_set_size_request (categories_list, -1, 350); - gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (categories_list), FALSE); - gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (categories_list), TRUE); - - hbuttonbox1 = gtk_hbutton_box_new (); - gtk_table_attach (GTK_TABLE (table_categories), hbuttonbox1, 0, 1, 4, 5, - (GtkAttachOptions) (GTK_EXPAND | GTK_SHRINK | GTK_FILL), - (GtkAttachOptions) (GTK_SHRINK), 0, 0); - gtk_box_set_spacing (GTK_BOX (hbuttonbox1), 6); - - button_new = gtk_button_new_from_stock ("gtk-new"); - gtk_container_add (GTK_CONTAINER (hbuttonbox1), button_new); - gtk_widget_set_can_default (button_new, TRUE); - - button_edit = gtk_button_new (); - gtk_container_add (GTK_CONTAINER (hbuttonbox1), button_edit); - gtk_widget_set_can_default (button_edit, TRUE); - - alignment1 = gtk_alignment_new (0.5, 0.5, 0, 0); - gtk_container_add (GTK_CONTAINER (button_edit), alignment1); - - hbox1 = gtk_hbox_new (FALSE, 2); - gtk_container_add (GTK_CONTAINER (alignment1), hbox1); - - image1 = gtk_image_new_from_stock ("gtk-properties", GTK_ICON_SIZE_BUTTON); - gtk_box_pack_start (GTK_BOX (hbox1), image1, FALSE, FALSE, 0); - - label3 = gtk_label_new_with_mnemonic (_("_Edit")); - gtk_box_pack_start (GTK_BOX (hbox1), label3, FALSE, FALSE, 0); - - button_delete = gtk_button_new_from_stock ("gtk-delete"); - gtk_container_add (GTK_CONTAINER (hbuttonbox1), button_delete); - gtk_widget_set_can_default (button_delete, TRUE); - - gtk_label_set_mnemonic_widget (GTK_LABEL (label_header), entry_categories); - gtk_label_set_mnemonic_widget (GTK_LABEL (label2), categories_list); + GtkWidget *categories_editor; dialog->priv = G_TYPE_INSTANCE_GET_PRIVATE ( dialog, E_TYPE_CATEGORIES_DIALOG, ECategoriesDialogPrivate); - dialog->priv->selected_categories = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); - - dialog->priv->categories_entry = entry_categories; - dialog->priv->categories_list = categories_list; - - tree_view = GTK_TREE_VIEW (dialog->priv->categories_list); - selection = gtk_tree_view_get_selection (tree_view); - gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE); - - g_signal_connect_swapped ( - selection, "changed", - G_CALLBACK (categories_dialog_selection_changed_cb), dialog); - g_signal_connect_swapped ( - dialog->priv->categories_list, "key-press-event", - G_CALLBACK (categories_dialog_key_press_event), dialog); + categories_editor = e_categories_editor_new (); + dialog->priv->categories_editor = categories_editor; - completion = e_category_completion_new (); - gtk_entry_set_completion (GTK_ENTRY (dialog->priv->categories_entry), completion); - g_object_unref (completion); - - dialog->priv->new_button = button_new; - g_signal_connect (G_OBJECT (dialog->priv->new_button), "clicked", G_CALLBACK (new_button_clicked_cb), dialog); - dialog->priv->edit_button = button_edit; - g_signal_connect (G_OBJECT (dialog->priv->edit_button), "clicked", G_CALLBACK (edit_button_clicked_cb), dialog); - dialog->priv->delete_button = button_delete; - g_signal_connect_swapped ( - G_OBJECT (dialog->priv->delete_button), "clicked", - G_CALLBACK (categories_dialog_delete_cb), dialog); - - gtk_dialog_add_buttons (GTK_DIALOG (dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, - GTK_STOCK_OK, GTK_RESPONSE_OK, NULL); + dialog_content = gtk_dialog_get_content_area (GTK_DIALOG (dialog)); + gtk_container_set_border_width (GTK_CONTAINER (dialog), 12); + gtk_box_pack_start ( + GTK_BOX (dialog_content), categories_editor, TRUE, TRUE, 0); + gtk_box_set_spacing (GTK_BOX (dialog_content), 12); + + g_signal_connect ( + categories_editor, "entry-changed", + G_CALLBACK (entry_changed_cb), dialog); + + gtk_dialog_add_buttons ( + GTK_DIALOG (dialog), + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OK, GTK_RESPONSE_OK, NULL); gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK); - gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), GTK_RESPONSE_OK, FALSE); + gtk_dialog_set_response_sensitive ( + GTK_DIALOG (dialog), GTK_RESPONSE_OK, FALSE); gtk_window_set_title (GTK_WINDOW (dialog), _("Categories")); - gtk_widget_show_all (dialog_content); - - renderer = gtk_cell_renderer_toggle_new (); - g_signal_connect (G_OBJECT (renderer), "toggled", G_CALLBACK (category_toggled_cb), dialog); - column = gtk_tree_view_column_new_with_attributes ("?", renderer, - "active", COLUMN_ACTIVE, NULL); - gtk_tree_view_append_column (GTK_TREE_VIEW (dialog->priv->categories_list), column); - - renderer = gtk_cell_renderer_pixbuf_new (); - column = gtk_tree_view_column_new_with_attributes (_("Icon"), renderer, - "pixbuf", COLUMN_ICON, NULL); - gtk_tree_view_append_column (GTK_TREE_VIEW (dialog->priv->categories_list), column); - - renderer = gtk_cell_renderer_text_new (); - column = gtk_tree_view_column_new_with_attributes (_("Category"), renderer, - "text", COLUMN_CATEGORY, NULL); - gtk_tree_view_append_column (GTK_TREE_VIEW (dialog->priv->categories_list), column); - - categories_dialog_build_model (dialog); - - e_categories_register_change_listener ( - G_CALLBACK (categories_dialog_listener_cb), dialog); + gtk_widget_show_all (categories_editor); } /** @@ -794,13 +101,11 @@ e_categories_dialog_new (const gchar *categories) { ECategoriesDialog *dialog; - dialog = E_CATEGORIES_DIALOG (g_object_new (E_TYPE_CATEGORIES_DIALOG, NULL)); + dialog = g_object_new (E_TYPE_CATEGORIES_DIALOG, NULL); + if (categories) e_categories_dialog_set_categories (dialog, categories); - g_signal_connect (G_OBJECT (dialog->priv->categories_entry), "changed", - G_CALLBACK (entry_changed_cb), dialog); - return GTK_WIDGET (dialog); } @@ -816,30 +121,14 @@ e_categories_dialog_new (const gchar *categories) const gchar * e_categories_dialog_get_categories (ECategoriesDialog *dialog) { - GtkEntry *entry; - const gchar *text; + const gchar *categories; g_return_val_if_fail (E_IS_CATEGORIES_DIALOG (dialog), NULL); - entry = GTK_ENTRY (dialog->priv->categories_entry); + categories = e_categories_editor_get_categories ( + E_CATEGORIES_EDITOR (dialog->priv->categories_editor)); - text = gtk_entry_get_text (entry); - if (text) { - gint len = strlen (text), old_len = len; - - while (len > 0 && (text[len -1] == ' ' || text[len - 1] == ',')) - len--; - - if (old_len != len) { - gchar *tmp = g_strndup (text, len); - - gtk_entry_set_text (entry, tmp); - - g_free (tmp); - } - } - - return gtk_entry_get_text (entry); + return categories; } /** @@ -853,47 +142,9 @@ void e_categories_dialog_set_categories (ECategoriesDialog *dialog, const gchar *categories) { - ECategoriesDialogPrivate *priv; - gchar **arr; - GtkTreeIter iter; - GtkTreeModel *model; - g_return_if_fail (E_IS_CATEGORIES_DIALOG (dialog)); - priv = dialog->priv; - - /* clean up the table of selected categories */ - g_hash_table_foreach_remove (priv->selected_categories, (GHRFunc) gtk_true, NULL); - - arr = g_strsplit (categories, ",", 0); - if (arr) { - gint i = 0; - while (arr[i] != NULL) { - arr[i] = g_strstrip (arr[i]); - - if (arr[i][0]) - g_hash_table_insert (priv->selected_categories, g_strdup (arr[i]), g_strdup (arr[i])); - i++; - } - - g_strfreev (arr); - } - - /* set the widgets */ - gtk_entry_set_text (GTK_ENTRY (priv->categories_entry), categories); - - model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->categories_list)); - if (gtk_tree_model_get_iter_first (model, &iter)) { - do { - gchar *place_string; - - gtk_tree_model_get (model, &iter, COLUMN_CATEGORY, &place_string, -1); - if (g_hash_table_lookup (priv->selected_categories, place_string)) - gtk_list_store_set (GTK_LIST_STORE (model), &iter, COLUMN_ACTIVE, TRUE, -1); - else - gtk_list_store_set (GTK_LIST_STORE (model), &iter, COLUMN_ACTIVE, FALSE, -1); - - g_free (place_string); - } while (gtk_tree_model_iter_next (model, &iter)); - } + e_categories_editor_set_categories ( + E_CATEGORIES_EDITOR (dialog->priv->categories_editor), + categories); } diff --git a/libedataserverui/e-categories-editor.c b/libedataserverui/e-categories-editor.c new file mode 100644 index 0000000..7a4e208 --- /dev/null +++ b/libedataserverui/e-categories-editor.c @@ -0,0 +1,427 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU Lesser General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include "libedataserver/e-categories.h" +#include "libedataserver/libedataserver-private.h" +#include "e-categories-editor.h" +#include "e-categories-selector.h" +#include "e-category-completion.h" +#include "e-category-editor.h" + +G_DEFINE_TYPE (ECategoriesEditor, e_categories_editor, GTK_TYPE_TABLE) + +struct _ECategoriesEditorPrivate { + ECategoriesSelector *categories_list; + GtkWidget *categories_entry; + GtkWidget *categories_entry_label; + GtkWidget *new_button; + GtkWidget *edit_button; + GtkWidget *delete_button; + + guint ignore_category_changes : 1; +}; + +enum { + COLUMN_ACTIVE, + COLUMN_ICON, + COLUMN_CATEGORY, + N_COLUMNS +}; + +enum { + PROP_0, + PROP_ENTRY_VISIBLE +}; + +enum { + ENTRY_CHANGED, + LAST_SIGNAL +}; + +static gint signals[LAST_SIGNAL] = {0}; + +static void +entry_changed_cb (GtkEntry *entry, + ECategoriesEditor *editor) +{ + g_signal_emit (editor, signals[ENTRY_CHANGED], 0); +} + + +static void +categories_editor_selection_changed_cb (ECategoriesEditor *editor, + GtkTreeSelection *selection) +{ + GtkWidget *widget; + gint n_rows; + + n_rows = gtk_tree_selection_count_selected_rows (selection); + + widget = editor->priv->edit_button; + gtk_widget_set_sensitive (widget, n_rows == 1); + + widget = editor->priv->delete_button; + gtk_widget_set_sensitive (widget, n_rows >= 1); +} + +static void +category_checked_cb (ECategoriesSelector *selector, + const gchar *category, + const gboolean checked, + ECategoriesEditor *editor) +{ + GtkEntry *entry; + const gchar *categories; + + entry = GTK_ENTRY (editor->priv->categories_entry); + categories = e_categories_selector_get_checked (selector); + + gtk_entry_set_text (entry, categories); +} + +static void +new_button_clicked_cb (GtkButton *button, + ECategoriesEditor *editor) +{ + ECategoryEditor *cat_editor = e_category_editor_new(); + + e_category_editor_create_category (cat_editor); + + gtk_widget_destroy (GTK_WIDGET (cat_editor)); +} + +static void +edit_button_clicked_cb (GtkButton *button, + ECategoriesEditor *editor) +{ + ECategoryEditor *cat_editor = e_category_editor_new(); + const gchar *category; + + category = e_categories_selector_get_selected ( + editor->priv->categories_list); + + e_category_editor_edit_category (cat_editor, category); + + gtk_widget_destroy (GTK_WIDGET (cat_editor)); +} + +static void +categories_editor_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_ENTRY_VISIBLE: + e_categories_editor_set_entry_visible ( + E_CATEGORIES_EDITOR (object), + g_value_get_boolean (value)); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +categories_editor_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_ENTRY_VISIBLE: + g_value_set_boolean ( + value, e_categories_editor_get_entry_visible ( + E_CATEGORIES_EDITOR (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +e_categories_editor_class_init (ECategoriesEditorClass *class) +{ + GObjectClass *object_class; + + g_type_class_add_private (class, sizeof (ECategoriesEditorPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->set_property = categories_editor_set_property; + object_class->get_property = categories_editor_get_property; + + g_object_class_install_property ( + object_class, + PROP_ENTRY_VISIBLE, + g_param_spec_boolean ( + "entry-visible", + NULL, + NULL, + TRUE, + G_PARAM_READWRITE)); + + signals[ENTRY_CHANGED] = g_signal_new ( + "entry-changed", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (ECategoriesEditorClass, entry_changed), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); +} + +static void +e_categories_editor_init (ECategoriesEditor *editor) +{ + GtkEntryCompletion *completion; + GtkWidget *entry_categories; + GtkWidget *label_header; + GtkWidget *label2; + GtkWidget *scrolledwindow1; + GtkWidget *categories_list; + GtkWidget *hbuttonbox1; + GtkWidget *button_new; + GtkWidget *button_edit; + GtkWidget *button_delete; + + gtk_widget_set_size_request (GTK_WIDGET (editor), -1, 400); + + gtk_table_resize (GTK_TABLE (editor), 3, 2); + gtk_table_set_row_spacings (GTK_TABLE (editor), 6); + gtk_table_set_col_spacings (GTK_TABLE (editor), 6); + + entry_categories = gtk_entry_new (); + gtk_table_attach ( + GTK_TABLE (editor), + entry_categories, 0, 1, 1, 2, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + + label_header = gtk_label_new_with_mnemonic ( + _("Currently _used categories:")); + gtk_table_attach ( + GTK_TABLE (editor), + label_header, 0, 1, 0, 1, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_label_set_justify (GTK_LABEL (label_header), GTK_JUSTIFY_CENTER); + gtk_misc_set_alignment (GTK_MISC (label_header), 0, 0.5); + + label2 = gtk_label_new_with_mnemonic (_("_Available Categories:")); + gtk_table_attach ( + GTK_TABLE (editor), + label2, 0, 1, 2, 3, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_label_set_justify (GTK_LABEL (label2), GTK_JUSTIFY_CENTER); + gtk_misc_set_alignment (GTK_MISC (label2), 0, 0.5); + + scrolledwindow1 = gtk_scrolled_window_new (NULL, NULL); + gtk_table_attach ( + GTK_TABLE (editor), + scrolledwindow1, 0, 1, 3, 4, + (GtkAttachOptions) (GTK_EXPAND | GTK_SHRINK | GTK_FILL), + (GtkAttachOptions) (GTK_EXPAND | GTK_SHRINK | GTK_FILL), 0, 0); + gtk_scrolled_window_set_policy ( + GTK_SCROLLED_WINDOW (scrolledwindow1), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type ( + GTK_SCROLLED_WINDOW (scrolledwindow1), GTK_SHADOW_IN); + + categories_list = GTK_WIDGET (e_categories_selector_new ()); + gtk_container_add (GTK_CONTAINER (scrolledwindow1), categories_list); + gtk_widget_set_size_request (categories_list, -1, 350); + gtk_tree_view_set_headers_visible ( + GTK_TREE_VIEW (categories_list), FALSE); + gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (categories_list), TRUE); + g_signal_connect (G_OBJECT (categories_list), "category-checked", + G_CALLBACK (category_checked_cb), editor); + + hbuttonbox1 = gtk_hbutton_box_new (); + gtk_table_attach ( + GTK_TABLE (editor), + hbuttonbox1, 0, 1, 4, 5, + (GtkAttachOptions) (GTK_EXPAND | GTK_SHRINK | GTK_FILL), + (GtkAttachOptions) (GTK_SHRINK), 0, 0); + gtk_box_set_spacing (GTK_BOX (hbuttonbox1), 6); + + button_new = gtk_button_new_from_stock (GTK_STOCK_NEW); + gtk_container_add (GTK_CONTAINER (hbuttonbox1), button_new); + gtk_widget_set_can_default (button_new, TRUE); + + button_edit = gtk_button_new_from_stock (GTK_STOCK_EDIT); + gtk_container_add (GTK_CONTAINER (hbuttonbox1), button_edit); + gtk_widget_set_can_default (button_edit, TRUE); + + button_delete = gtk_button_new_from_stock (GTK_STOCK_DELETE); + gtk_container_add (GTK_CONTAINER (hbuttonbox1), button_delete); + gtk_widget_set_can_default (button_delete, TRUE); + + gtk_label_set_mnemonic_widget ( + GTK_LABEL (label_header), entry_categories); + gtk_label_set_mnemonic_widget ( + GTK_LABEL (label2), categories_list); + + editor->priv = G_TYPE_INSTANCE_GET_PRIVATE ( + editor, E_TYPE_CATEGORIES_EDITOR, ECategoriesEditorPrivate); + + editor->priv->categories_list = E_CATEGORIES_SELECTOR (categories_list); + editor->priv->categories_entry = entry_categories; + editor->priv->categories_entry_label = label_header; + + g_signal_connect_swapped ( + editor->priv->categories_list, "selection-changed", + G_CALLBACK (categories_editor_selection_changed_cb), editor); + + completion = e_category_completion_new (); + gtk_entry_set_completion ( + GTK_ENTRY (editor->priv->categories_entry), completion); + g_object_unref (completion); + + editor->priv->new_button = button_new; + g_signal_connect ( + editor->priv->new_button, "clicked", + G_CALLBACK (new_button_clicked_cb), editor); + + editor->priv->edit_button = button_edit; + g_signal_connect ( + editor->priv->edit_button, "clicked", + G_CALLBACK (edit_button_clicked_cb), editor); + + editor->priv->delete_button = button_delete; + g_signal_connect_swapped ( + editor->priv->delete_button, "clicked", + G_CALLBACK (e_categories_selector_delete_selection), + editor->priv->categories_list); + + g_signal_connect ( + editor->priv->categories_entry, "changed", + G_CALLBACK (entry_changed_cb), editor); + + gtk_widget_show_all (GTK_WIDGET (editor)); +} + +/** + * e_categories_editor_new: + * + * Creates a new #ECategoriesEditor widget. + * + * Returns: a new #ECategoriesEditor + * + * Since: 3.2 + **/ +GtkWidget * +e_categories_editor_new (void) +{ + return g_object_new (E_TYPE_CATEGORIES_EDITOR, NULL); +} + +/** + * e_categories_editor_get_categories: + * @editor: an #ECategoriesEditor + * + * Gets a comma-separated list of the categories currently selected + * in the editor. + * + * Returns: a comma-separated list of categories + * + * Since: 3.2 + **/ +const gchar * +e_categories_editor_get_categories (ECategoriesEditor *editor) +{ + ECategoriesSelector *categories_list; + + g_return_val_if_fail (E_IS_CATEGORIES_EDITOR (editor), NULL); + + categories_list = editor->priv->categories_list; + + return e_categories_selector_get_checked (categories_list); +} + +/** + * e_categories_editor_set_categories: + * @dialog: an #ECategoriesEditor + * @categories: comma-separated list of categories + * + * Sets the list of categories selected on the editor. + * + * Since: 3.2 + **/ +void +e_categories_editor_set_categories (ECategoriesEditor *editor, + const gchar *categories) +{ + ECategoriesSelector *categories_list; + + g_return_if_fail (E_IS_CATEGORIES_EDITOR (editor)); + + categories_list = editor->priv->categories_list; + + e_categories_selector_set_checked (categories_list, categories); + category_checked_cb (categories_list, NULL, FALSE, editor); +} + +/** + * e_categories_editor_get_entry_visible: + * @editor: an #ECategoriesEditor + * + * Return the visibility of the category input entry. + * + * Returns: whether the entry is visible + * + * Since: 3.2 + **/ +gboolean +e_categories_editor_get_entry_visible (ECategoriesEditor *editor) +{ + g_return_val_if_fail (E_IS_CATEGORIES_EDITOR (editor), TRUE); + + return gtk_widget_get_visible (editor->priv->categories_entry); +} + +/** + * e_categories_editor_set_entry_visible: + * @editor: an #ECategoriesEditor + * @entry_visible: whether to make the entry visible + * + * Sets the visibility of the category input entry. + * + * Since: 3.2 + **/ +void +e_categories_editor_set_entry_visible (ECategoriesEditor *editor, + gboolean entry_visible) +{ + g_return_if_fail (E_IS_CATEGORIES_EDITOR (editor)); + + gtk_widget_set_visible ( + editor->priv->categories_entry, entry_visible); + gtk_widget_set_visible ( + editor->priv->categories_entry_label, entry_visible); + e_categories_selector_set_items_checkable ( + editor->priv->categories_list, entry_visible); + + g_object_notify (G_OBJECT (editor), "entry-visible"); +} diff --git a/libedataserverui/e-categories-editor.h b/libedataserverui/e-categories-editor.h new file mode 100644 index 0000000..2789c0d --- /dev/null +++ b/libedataserverui/e-categories-editor.h @@ -0,0 +1,76 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU Lesser General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef E_CATEGORIES_EDITOR_H +#define E_CATEGORIES_EDITOR_H + +#include + +/* Standard GObject macros */ +#define E_TYPE_CATEGORIES_EDITOR \ + (e_categories_editor_get_type ()) +#define E_CATEGORIES_EDITOR(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_CATEGORIES_EDITOR, ECategoriesEditor)) +#define E_CATEGORIES_EDITOR_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_CATEGORIES_EDITOR, ECategoriesEditorClass)) +#define E_IS_CATEGORIES_EDITOR(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_CATEGORIES_EDITOR)) +#define E_IS_CATEGORIES_EDITOR_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_CATEGORIES_EDITOR)) +#define E_CATEGORIES_EDITOR_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_CATEGORIES_EDITOR, ECategoriesEditorClass)) + +G_BEGIN_DECLS + +typedef struct _ECategoriesEditor ECategoriesEditor; +typedef struct _ECategoriesEditorClass ECategoriesEditorClass; +typedef struct _ECategoriesEditorPrivate ECategoriesEditorPrivate; + +struct _ECategoriesEditor { + GtkTable parent; + ECategoriesEditorPrivate *priv; +}; + +struct _ECategoriesEditorClass { + GtkTableClass parent_class; + + void (*entry_changed) (GtkEntry *entry); +}; + +GType e_categories_editor_get_type (void); +GtkWidget * e_categories_editor_new (void); +const gchar * e_categories_editor_get_categories + (ECategoriesEditor *editor); +void e_categories_editor_set_categories + (ECategoriesEditor *editor, + const gchar *categories); +gboolean e_categories_editor_get_entry_visible + (ECategoriesEditor *editor); +void e_categories_editor_set_entry_visible + (ECategoriesEditor *editor, + gboolean entry_visible); + +G_END_DECLS + +#endif /* E_CATEGORIES_EDITOR_H */ diff --git a/libedataserverui/e-categories-selector.c b/libedataserverui/e-categories-selector.c new file mode 100644 index 0000000..addd266 --- /dev/null +++ b/libedataserverui/e-categories-selector.c @@ -0,0 +1,570 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU Lesser General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include +#include "libedataserver/e-categories.h" +#include "e-categories-selector.h" +#include "e-data-server-ui-marshal.h" + +G_DEFINE_TYPE ( + ECategoriesSelector, + e_categories_selector, + GTK_TYPE_TREE_VIEW) + +struct _ECategoriesSelectorPrivate { + gboolean checkable; + GHashTable *selected_categories; + + gboolean ignore_category_changes; +}; + +enum { + PROP_0, + PROP_ITEMS_CHECKABLE +}; + +enum { + CATEGORY_CHECKED, + SELECTION_CHANGED, + LAST_SIGNAL +}; + +enum { + COLUMN_ACTIVE, + COLUMN_ICON, + COLUMN_CATEGORY, + N_COLUMNS +}; + +static gint signals[LAST_SIGNAL] = {0}; + +static void +categories_selector_build_model (ECategoriesSelector *selector) +{ + GtkListStore *store; + GList *list, *iter; + + store = gtk_list_store_new ( + N_COLUMNS, G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF, G_TYPE_STRING); + + gtk_tree_sortable_set_sort_column_id ( + GTK_TREE_SORTABLE (store), + COLUMN_CATEGORY, GTK_SORT_ASCENDING); + + list = e_categories_get_list (); + for (iter = list; iter != NULL; iter = iter->next) { + const gchar *category_name = iter->data; + const gchar *filename; + GdkPixbuf *pixbuf = NULL; + GtkTreeIter iter; + gboolean active; + + /* Only add user-visible categories. */ + if (!e_categories_is_searchable (category_name)) + continue; + + active = (g_hash_table_lookup ( + selector->priv->selected_categories, + category_name) != NULL); + + filename = e_categories_get_icon_file_for (category_name); + if (filename != NULL) + pixbuf = gdk_pixbuf_new_from_file (filename, NULL); + + gtk_list_store_append (store, &iter); + + gtk_list_store_set ( + store, &iter, + COLUMN_ACTIVE, active, + COLUMN_ICON, pixbuf, + COLUMN_CATEGORY, category_name, + -1); + + if (pixbuf != NULL) + g_object_unref (pixbuf); + } + + gtk_tree_view_set_model ( + GTK_TREE_VIEW (selector), GTK_TREE_MODEL (store)); + + /* This has to be reset everytime we install a new model */ + gtk_tree_view_set_search_column ( + GTK_TREE_VIEW (selector), COLUMN_CATEGORY); + + g_list_free (list); + g_object_unref (store); +} + +static void +category_toggled_cb (GtkCellRenderer *renderer, + const gchar *path, + ECategoriesSelector *selector) +{ + GtkTreeModel *model; + GtkTreePath *tree_path; + GtkTreeIter iter; + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (selector)); + g_return_if_fail (model); + + tree_path = gtk_tree_path_new_from_string (path); + g_return_if_fail (tree_path); + + if (gtk_tree_model_get_iter (model, &iter, tree_path)) { + gchar *category; + gboolean active; + + gtk_tree_model_get (model, &iter, + COLUMN_ACTIVE, &active, + COLUMN_CATEGORY, &category, -1); + + gtk_list_store_set ( + GTK_LIST_STORE (model), &iter, + COLUMN_ACTIVE, !active, -1); + + if (active) + g_hash_table_remove ( + selector->priv->selected_categories, category); + else + g_hash_table_insert ( + selector->priv->selected_categories, + g_strdup (category), g_strdup (category)); + + g_signal_emit ( + selector, signals[CATEGORY_CHECKED], 0, + category, !active); + + g_free (category); + } + + gtk_tree_path_free (tree_path); +} + +static void +categories_selector_listener_cb (gpointer useless_pointer, + ECategoriesSelector *selector) +{ + if (!selector->priv->ignore_category_changes) + categories_selector_build_model (selector); +} + +static gboolean +categories_selector_key_press_event (ECategoriesSelector *selector, + GdkEventKey *event) +{ + if (event->keyval == GDK_KEY_Delete) { + e_categories_selector_delete_selection (selector); + return TRUE; + } + + return FALSE; +} + +static void +categories_selector_selection_changed (GtkTreeSelection *selection, + ECategoriesSelector *selector) +{ + g_signal_emit (selector, signals[SELECTION_CHANGED], 0, selection); +} + +static void +categories_selector_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_ITEMS_CHECKABLE: + g_value_set_boolean ( + value, + e_categories_selector_get_items_checkable ( + E_CATEGORIES_SELECTOR (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +categories_selector_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_ITEMS_CHECKABLE: + e_categories_selector_set_items_checkable ( + E_CATEGORIES_SELECTOR (object), + g_value_get_boolean (value)); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +categories_selector_dispose (GObject *object) +{ + ECategoriesSelectorPrivate *priv; + + priv = E_CATEGORIES_SELECTOR (object)->priv; + + if (priv->selected_categories != NULL) { + g_hash_table_destroy (priv->selected_categories); + priv->selected_categories = NULL; + } + + /* Chain up to parent's dispose() method.*/ + G_OBJECT_CLASS (e_categories_selector_parent_class)->dispose (object); +} + +static void +categories_selector_finalize (GObject *object) +{ + e_categories_unregister_change_listener ( + G_CALLBACK (categories_selector_listener_cb), object); +} + +static void +e_categories_selector_class_init (ECategoriesSelectorClass *class) +{ + GObjectClass *object_class; + + g_type_class_add_private (class, sizeof (ECategoriesSelectorPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->set_property = categories_selector_set_property; + object_class->get_property = categories_selector_get_property; + object_class->dispose = categories_selector_dispose; + object_class->finalize = categories_selector_finalize; + + g_object_class_install_property ( + object_class, + PROP_ITEMS_CHECKABLE, + g_param_spec_boolean ( + "items-checkable", + NULL, + NULL, + TRUE, + G_PARAM_READWRITE)); + + signals[CATEGORY_CHECKED] = g_signal_new ( + "category-checked", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (ECategoriesSelectorClass, category_checked), + NULL, NULL, + e_data_server_ui_marshal_VOID__STRING_BOOLEAN, + G_TYPE_NONE, 2, + G_TYPE_STRING, + G_TYPE_BOOLEAN); + + signals[SELECTION_CHANGED] = g_signal_new ( + "selection-changed", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (ECategoriesSelectorClass, selection_changed), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + GTK_TYPE_TREE_SELECTION); +} + +static void +e_categories_selector_init (ECategoriesSelector *selector) +{ + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + GtkTreeSelection *selection; + + selector->priv = G_TYPE_INSTANCE_GET_PRIVATE ( + selector, E_TYPE_CATEGORIES_SELECTOR, + ECategoriesSelectorPrivate); + + selector->priv->checkable = TRUE; + selector->priv->selected_categories = g_hash_table_new_full ( + (GHashFunc) g_str_hash, + (GEqualFunc) g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) g_free); + selector->priv->ignore_category_changes = FALSE; + + renderer = gtk_cell_renderer_toggle_new (); + column = gtk_tree_view_column_new_with_attributes ( + "?", renderer, "active", COLUMN_ACTIVE, NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW (selector), column); + + g_signal_connect ( + renderer, "toggled", + G_CALLBACK (category_toggled_cb), selector); + + renderer = gtk_cell_renderer_pixbuf_new (); + column = gtk_tree_view_column_new_with_attributes ( + _("Icon"), renderer, "pixbuf", COLUMN_ICON, NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW (selector), column); + + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes ( + _("Category"), renderer, "text", COLUMN_CATEGORY, NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW (selector), column); + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (selector)); + g_signal_connect ( + selection, "changed", + G_CALLBACK (categories_selector_selection_changed), selector); + + g_signal_connect ( + selector, "key-press-event", + G_CALLBACK(categories_selector_key_press_event), NULL); + + e_categories_register_change_listener ( + G_CALLBACK (categories_selector_listener_cb), selector); + + categories_selector_build_model (selector); +} + +/** + * e_categories_selector_new: + * + * Since: 3.2 + **/ +GtkWidget * +e_categories_selector_new (void) +{ + return g_object_new ( + E_TYPE_CATEGORIES_SELECTOR, + "items-checkable", TRUE, NULL); +} + +/** + * e_categories_selector_get_items_checkable: + * + * Since: 3.2 + **/ +gboolean +e_categories_selector_get_items_checkable (ECategoriesSelector *selector) +{ + g_return_val_if_fail (E_IS_CATEGORIES_SELECTOR (selector), TRUE); + + return selector->priv->checkable; +} + +/** + * e_categories_selector_set_items_checkable: + * + * Since: 3.2 + **/ +void +e_categories_selector_set_items_checkable (ECategoriesSelector *selector, + gboolean checkable) +{ + GtkTreeViewColumn *column; + + g_return_if_fail (E_IS_CATEGORIES_SELECTOR (selector)); + + selector->priv->checkable = checkable; + + column = gtk_tree_view_get_column ( + GTK_TREE_VIEW (selector), COLUMN_ACTIVE); + gtk_tree_view_column_set_visible (column, checkable); + + g_object_notify (G_OBJECT (selector), "items-checkable"); +} + +/** + * e_categories_selector_get_checked: + * + * Since: 3.2 + **/ +const gchar * +e_categories_selector_get_checked (ECategoriesSelector *selector) +{ + GString *str = g_string_new (""); + GList *list, *category; + + g_return_val_if_fail (E_IS_CATEGORIES_SELECTOR (selector), NULL); + + list = g_hash_table_get_values (selector->priv->selected_categories); + + for (category = list; category != NULL; category = category->next) { + if (str->len > 0) + g_string_append_printf ( + str, ",%s", (gchar *) category->data); + else + g_string_append (str, (gchar *) category->data); + } + + g_list_free (list); + + return g_string_free (str, FALSE); +} + +/** + * e_categories_selector_set_checked: + * + * Since: 3.2 + **/ +void +e_categories_selector_set_checked (ECategoriesSelector *selector, + const gchar *categories) +{ + GtkTreeModel *model; + GtkTreeIter iter; + gchar **arr; + gint i; + + g_return_if_fail (E_IS_CATEGORIES_SELECTOR (selector)); + + /* Clean up table of selected categories. */ + g_hash_table_remove_all (selector->priv->selected_categories); + + arr = g_strsplit (categories, ",", 0); + if (arr) { + for (i = 0; arr[i] != NULL; i++) { + g_strstrip (arr[i]); + g_hash_table_insert ( + selector->priv->selected_categories, + g_strdup (arr[i]), g_strdup (arr[i])); + } + g_strfreev (arr); + } + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (selector)); + if (gtk_tree_model_get_iter_first (model, &iter)) { + do { + gchar *category_name; + gboolean found; + + gtk_tree_model_get (model, &iter, + COLUMN_CATEGORY, &category_name, + -1); + found = (g_hash_table_lookup ( + selector->priv->selected_categories, + category_name) != NULL); + gtk_list_store_set ( + GTK_LIST_STORE (model), &iter, + COLUMN_ACTIVE, found, -1); + + g_free (category_name); + } while (gtk_tree_model_iter_next (model, &iter)); + } +} + +/** + * e_categories_selector_delete_selection: + * + * Since: 3.2 + **/ +void +e_categories_selector_delete_selection (ECategoriesSelector *selector) +{ + GtkTreeModel *model; + GtkTreeSelection *selection; + GList *selected, *item; + + g_return_if_fail (E_IS_CATEGORIES_SELECTOR (selector)); + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (selector)); + g_return_if_fail (model != NULL); + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (selector)); + selected = gtk_tree_selection_get_selected_rows (selection, &model); + + /* Remove categories in reverse order to avoid invalidating + * tree paths as we iterate over the list. Note, the list is + * probably already sorted but we sort again just to be safe. */ + selected = g_list_reverse (g_list_sort ( + selected, (GCompareFunc) gtk_tree_path_compare)); + + /* Prevent the model from being rebuilt every time we + remove a category, since we're already modifying it. */ + selector->priv->ignore_category_changes = TRUE; + + for (item = selected; item != NULL; item = item->next) { + GtkTreePath *path = item->data; + GtkTreeIter iter; + gchar *category; + + gtk_tree_model_get_iter (model, &iter, path); + gtk_tree_model_get (model, &iter, + COLUMN_CATEGORY, &category, -1); + gtk_list_store_remove (GTK_LIST_STORE (model), &iter); + e_categories_remove (category); + g_free (category); + } + + selector->priv->ignore_category_changes = FALSE; + + /* If we only remove one category, try to select another */ + if (g_list_length (selected) == 1) { + GtkTreePath *path = selected->data; + + gtk_tree_selection_select_path (selection, path); + if (!gtk_tree_selection_path_is_selected (selection, path)) + if (gtk_tree_path_prev (path)) + gtk_tree_selection_select_path (selection, path); + } + + g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL); + g_list_free (selected); +} + +/** + * e_categories_selector_get_selected: + * + * Since: 3.2 + **/ +const gchar * +e_categories_selector_get_selected (ECategoriesSelector *selector) +{ + GtkTreeModel *model; + GtkTreeSelection *selection; + GList *selected, *item; + GString *str = g_string_new (""); + + g_return_val_if_fail (E_IS_CATEGORIES_SELECTOR (selector), NULL); + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (selector)); + g_return_val_if_fail (model != NULL, NULL); + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (selector)); + selected = gtk_tree_selection_get_selected_rows (selection, &model); + + for (item = selected; item != NULL; item = item->next) { + GtkTreePath *path = item->data; + GtkTreeIter iter; + gchar *category; + + gtk_tree_model_get_iter (model, &iter, path); + gtk_tree_model_get (model, &iter, + COLUMN_CATEGORY, &category, -1); + if (str->len == 0) + g_string_assign (str, category); + else + g_string_append_printf (str, ",%s", category); + + g_free (category); + } + + g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL); + g_list_free (selected); + + return g_string_free (str, FALSE); +} diff --git a/libedataserverui/e-categories-selector.h b/libedataserverui/e-categories-selector.h new file mode 100644 index 0000000..75dbfa5 --- /dev/null +++ b/libedataserverui/e-categories-selector.h @@ -0,0 +1,85 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU Lesser General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef E_CATEGORIES_SELECTOR_H +#define E_CATEGORIES_SELECTOR_H + +#include + +/* Standard GObject macros */ +#define E_TYPE_CATEGORIES_SELECTOR \ + (e_categories_selector_get_type ()) +#define E_CATEGORIES_SELECTOR(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_CATEGORIES_SELECTOR, ECategoriesSelector)) +#define E_CATEGORIES_SELECTOR_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_CATEGORIES_SELECTOR, ECategoriesSelectorClass)) +#define E_IS_CATEGORIES_SELECTOR(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_CATEGORIES_SELECTOR)) +#define E_IS_CATEGORIES_SELECTOR_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_CATEGORIES_SELECTOR)) +#define E_CATEGORIES_SELECTOR_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_CATEGORIES_SELECTOR, ECategoriesSelectorClass)) + +G_BEGIN_DECLS + +typedef struct _ECategoriesSelector ECategoriesSelector; +typedef struct _ECategoriesSelectorClass ECategoriesSelectorClass; +typedef struct _ECategoriesSelectorPrivate ECategoriesSelectorPrivate; + +struct _ECategoriesSelector { + GtkTreeView parent; + ECategoriesSelectorPrivate *priv; +}; + +struct _ECategoriesSelectorClass { + GtkTreeViewClass parent_class; + + void (*category_checked) (ECategoriesSelector *selector, + const gchar *category, + gboolean checked); + + void (*selection_changed) (ECategoriesSelector *selector, + GtkTreeSelection *selection); +}; + +GType e_categories_selector_get_type (void); +GtkWidget * e_categories_selector_new (void); +const gchar * e_categories_selector_get_checked + (ECategoriesSelector *selector); +void e_categories_selector_set_checked + (ECategoriesSelector *selector, + const gchar *categories); +gboolean e_categories_selector_get_items_checkable + (ECategoriesSelector *selector); +void e_categories_selector_set_items_checkable + (ECategoriesSelector *selectr, + gboolean checkable); +void e_categories_selector_delete_selection + (ECategoriesSelector *selector); +const gchar * e_categories_selector_get_selected + (ECategoriesSelector *selector); + +G_END_DECLS + +#endif /* E_CATEGORIES_SELECTOR_H */ diff --git a/libedataserverui/e-category-editor.c b/libedataserverui/e-category-editor.c new file mode 100644 index 0000000..958a578 --- /dev/null +++ b/libedataserverui/e-category-editor.c @@ -0,0 +1,348 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU Lesser General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include "libedataserver/e-categories.h" +#include "e-category-editor.h" + +G_DEFINE_TYPE (ECategoryEditor, e_category_editor, GTK_TYPE_DIALOG) + +struct _ECategoryEditorPrivate { + GtkWidget *category_name; + GtkWidget *category_icon; +}; + +static void +update_preview (GtkFileChooser *chooser, + gpointer user_data) +{ + GtkImage *image; + gchar *filename; + + g_return_if_fail (chooser != NULL); + + image = GTK_IMAGE (gtk_file_chooser_get_preview_widget (chooser)); + g_return_if_fail (image != NULL); + + filename = gtk_file_chooser_get_preview_filename (chooser); + + gtk_image_set_from_file (image, filename); + gtk_file_chooser_set_preview_widget_active (chooser, filename != NULL); + + g_free (filename); +} + +static void +file_chooser_response (GtkDialog *dialog, + gint response_id, + GtkFileChooser *button) +{ + g_return_if_fail (button != NULL); + + if (response_id == GTK_RESPONSE_NO) + gtk_file_chooser_unselect_all (button); +} + +static void +category_editor_category_name_changed (GtkEntry *category_name_entry, + ECategoryEditor *editor) +{ + gchar *name; + + g_return_if_fail (editor != NULL); + g_return_if_fail (category_name_entry != NULL); + + name = g_strdup (gtk_entry_get_text (category_name_entry)); + if (name != NULL) + name = g_strstrip (name); + + gtk_dialog_set_response_sensitive ( + GTK_DIALOG (editor), GTK_RESPONSE_OK, name && *name); + + g_free (name); +} + +static gchar * +check_category_name (const gchar *name) +{ + GString *str = NULL; + gchar *p = (gchar *) name; + + str = g_string_new (""); + while (*p) { + switch (*p) { + case ',': + break; + default: + str = g_string_append_c (str, *p); + } + p++; + } + + p = g_strstrip (g_string_free (str, FALSE)); + + return p; +} + +static void +e_category_editor_class_init (ECategoryEditorClass *class) +{ + g_type_class_add_private (class, sizeof (ECategoryEditorPrivate)); +} + +static void +e_category_editor_init (ECategoryEditor *editor) +{ + GtkWidget *dialog_content; + GtkWidget *dialog_action_area; + GtkWidget *table_category_properties; + GtkWidget *label_name; + GtkWidget *label_icon; + GtkWidget *category_name; + GtkWidget *chooser_button; + GtkWidget *no_image_button; + GtkWidget *chooser_dialog; + GtkWidget *preview; + + editor->priv = G_TYPE_INSTANCE_GET_PRIVATE ( + editor, E_TYPE_CATEGORY_EDITOR, ECategoryEditorPrivate); + + chooser_dialog = gtk_file_chooser_dialog_new ( + _("Category Icon"), + NULL, GTK_FILE_CHOOSER_ACTION_OPEN, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL); + + no_image_button = gtk_button_new_with_mnemonic (_("_No Image")); + gtk_button_set_image ( + GTK_BUTTON (no_image_button), + gtk_image_new_from_stock ( + GTK_STOCK_CLOSE, GTK_ICON_SIZE_BUTTON)); + gtk_dialog_add_action_widget ( + GTK_DIALOG (chooser_dialog), + no_image_button, GTK_RESPONSE_NO); + gtk_dialog_add_button ( + GTK_DIALOG (chooser_dialog), + GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT); + gtk_file_chooser_set_local_only ( + GTK_FILE_CHOOSER (chooser_dialog), TRUE); + gtk_widget_show (no_image_button); + + g_signal_connect ( + chooser_dialog, "update-preview", + G_CALLBACK (update_preview), NULL); + + preview = gtk_image_new(); + gtk_file_chooser_set_preview_widget ( + GTK_FILE_CHOOSER (chooser_dialog), preview); + gtk_file_chooser_set_preview_widget_active ( + GTK_FILE_CHOOSER (chooser_dialog), TRUE); + gtk_widget_show_all (preview); + + dialog_content = gtk_dialog_get_content_area (GTK_DIALOG (editor)); + + table_category_properties = gtk_table_new (3, 2, FALSE); + gtk_box_pack_start ( + GTK_BOX (dialog_content), + table_category_properties, TRUE, TRUE, 0); + gtk_container_set_border_width ( + GTK_CONTAINER (table_category_properties), 12); + gtk_table_set_row_spacings (GTK_TABLE (table_category_properties), 6); + gtk_table_set_col_spacings (GTK_TABLE (table_category_properties), 6); + + label_name = gtk_label_new_with_mnemonic (_("Category _Name")); + gtk_misc_set_alignment (GTK_MISC (label_name), 0, 0.5); + gtk_table_attach ( + GTK_TABLE (table_category_properties), + label_name, 0, 1, 0, 1, + (GtkAttachOptions) GTK_FILL, + (GtkAttachOptions) 0, 0, 0); + + label_icon = gtk_label_new_with_mnemonic (_("Category _Icon")); + gtk_misc_set_alignment (GTK_MISC (label_icon), 0, 0.5); + gtk_table_attach ( + GTK_TABLE (table_category_properties), + label_icon, 0, 1, 2, 3, + (GtkAttachOptions) GTK_FILL, + (GtkAttachOptions) 0, 0, 0); + + category_name = gtk_entry_new (); + gtk_label_set_mnemonic_widget (GTK_LABEL (label_name), category_name); + gtk_table_attach ( + GTK_TABLE (table_category_properties), + category_name, 1, 2, 0, 1, + (GtkAttachOptions) GTK_EXPAND | GTK_SHRINK | GTK_FILL, + (GtkAttachOptions) 0, 0, 0); + editor->priv->category_name = category_name; + + chooser_button = GTK_WIDGET ( + gtk_file_chooser_button_new_with_dialog (chooser_dialog)); + gtk_label_set_mnemonic_widget (GTK_LABEL (label_icon), chooser_button); + gtk_table_attach ( + GTK_TABLE (table_category_properties), + chooser_button, 1, 2, 2, 3, + (GtkAttachOptions) GTK_EXPAND | GTK_SHRINK | GTK_FILL, + (GtkAttachOptions) 0, 0, 0); + editor->priv->category_icon = chooser_button; + + g_signal_connect ( + chooser_dialog, "response", + G_CALLBACK (file_chooser_response), chooser_button); + + dialog_action_area = gtk_dialog_get_action_area (GTK_DIALOG (editor)); + gtk_button_box_set_layout ( + GTK_BUTTON_BOX (dialog_action_area), GTK_BUTTONBOX_END); + + gtk_dialog_add_buttons ( + GTK_DIALOG (editor), + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OK, GTK_RESPONSE_OK, NULL); + gtk_dialog_set_default_response (GTK_DIALOG (editor), GTK_RESPONSE_OK); + gtk_window_set_title (GTK_WINDOW (editor), _("Category Properties")); + gtk_window_set_type_hint ( + GTK_WINDOW (editor), GDK_WINDOW_TYPE_HINT_DIALOG); + + gtk_widget_show_all (dialog_content); + + g_signal_connect ( + category_name, "changed", + G_CALLBACK (category_editor_category_name_changed), editor); + + category_editor_category_name_changed ( + GTK_ENTRY (category_name), editor); +} + +/** + * e_categort_editor_new: + * + * Creates a new #ECategoryEditor widget. + * + * Returns: a new #ECategoryEditor + * + * Since: 3.2 + **/ +ECategoryEditor * +e_category_editor_new () +{ + return g_object_new (E_TYPE_CATEGORY_EDITOR, NULL); +} + +/** + * e_category_editor_create_category: + * + * Since: 3.2 + **/ +const gchar * +e_category_editor_create_category (ECategoryEditor *editor) +{ + GtkEntry *entry; + GtkFileChooser *file_chooser; + + g_return_val_if_fail (E_IS_CATEGORY_EDITOR (editor), NULL); + + entry = GTK_ENTRY (editor->priv->category_name); + file_chooser = GTK_FILE_CHOOSER (editor->priv->category_icon); + + do { + const gchar *category_name; + const gchar *correct_category_name; + + if (gtk_dialog_run (GTK_DIALOG (editor)) != GTK_RESPONSE_OK) + return NULL; + + category_name = gtk_entry_get_text (entry); + correct_category_name = check_category_name (category_name); + + if (e_categories_exist (correct_category_name)) { + GtkWidget *error_dialog; + + error_dialog = gtk_message_dialog_new ( + GTK_WINDOW (editor), + 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, + _("There is already a category '%s' in the " + "configuration. Please use another name"), + category_name); + + gtk_dialog_run (GTK_DIALOG (error_dialog)); + gtk_widget_destroy (error_dialog); + + /* Now we loop and run the dialog again. */ + + } else { + gchar *category_icon; + + category_icon = + gtk_file_chooser_get_filename (file_chooser); + e_categories_add ( + correct_category_name, NULL, + category_icon, TRUE); + g_free (category_icon); + + return correct_category_name; + } + + } while (TRUE); +} + +/** + * e_category_editor_edit_category: + * + * Since: 3.2 + **/ +gboolean +e_category_editor_edit_category (ECategoryEditor *editor, + const gchar *category) +{ + GtkFileChooser *file_chooser; + const gchar *icon_file; + + g_return_val_if_fail (E_IS_CATEGORY_EDITOR (editor), FALSE); + g_return_val_if_fail (category != NULL, FALSE); + + file_chooser = GTK_FILE_CHOOSER (editor->priv->category_icon); + + gtk_entry_set_text (GTK_ENTRY (editor->priv->category_name), category); + gtk_widget_set_sensitive (editor->priv->category_name, FALSE); + + icon_file = e_categories_get_icon_file_for (category); + if (icon_file) { + gtk_file_chooser_set_filename (file_chooser, icon_file); + update_preview (file_chooser, NULL); + } + + if (gtk_dialog_run (GTK_DIALOG (editor)) == GTK_RESPONSE_OK) { + gchar *category_icon; + + category_icon = gtk_file_chooser_get_filename (file_chooser); + e_categories_set_icon_file_for (category, category_icon); + + gtk_dialog_set_response_sensitive ( + GTK_DIALOG (editor), GTK_RESPONSE_OK, TRUE); + + g_free (category_icon); + + return TRUE; + } + + return FALSE; +} diff --git a/libedataserverui/e-category-editor.h b/libedataserverui/e-category-editor.h new file mode 100644 index 0000000..19c706b --- /dev/null +++ b/libedataserverui/e-category-editor.h @@ -0,0 +1,69 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU Lesser General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef E_CATEGORY_EDITOR_H +#define E_CATEGORY_EDITOR_H + +#include + +/* Standard GObject macros */ +#define E_TYPE_CATEGORY_EDITOR \ + (e_category_editor_get_type ()) +#define E_CATEGORY_EDITOR(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_CATEGORY_EDITOR, ECategoryEditor)) +#define E_CATEGORY_EDITOR_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_CATEGORY_EDITOR, ECategoryEditorClass)) +#define E_IS_CATEGORY_EDITOR(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_CATEGORY_EDITOR)) +#define E_IS_CATEGORY_EDITOR_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_CATEGORY_EDITOR)) +#define E_CATEGORY_EDITOR_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_CATEGORY_EDITOR, ECategoryEditorClass)) + +G_BEGIN_DECLS + +typedef struct _ECategoryEditor ECategoryEditor; +typedef struct _ECategoryEditorClass ECategoryEditorClass; +typedef struct _ECategoryEditorPrivate ECategoryEditorPrivate; + +struct _ECategoryEditor { + GtkDialog parent; + ECategoryEditorPrivate *priv; +}; + +struct _ECategoryEditorClass { + GtkDialogClass parent_class; +}; + +GType e_category_editor_get_type (void); +ECategoryEditor * + e_category_editor_new (void); +const gchar * e_category_editor_create_category + (ECategoryEditor *editor); +gboolean e_category_editor_edit_category (ECategoryEditor *editor, + const gchar *category); + +G_END_DECLS + +#endif /* E_CATEGORY_EDITOR_H */ diff --git a/libedataserverui/e-data-server-ui-marshal.list b/libedataserverui/e-data-server-ui-marshal.list index 333b042..5b7fbe9 100644 --- a/libedataserverui/e-data-server-ui-marshal.list +++ b/libedataserverui/e-data-server-ui-marshal.list @@ -1,2 +1,3 @@ BOOLEAN:OBJECT,BOXED BOOLEAN:BOXED,OBJECT,FLAGS,UINT +NONE:STRING,BOOLEAN -- 2.7.4