1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Copyright (C) 2005 Novell, Inc.
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of version 2 of the GNU Lesser General Public
7 * License as published by the Free Software Foundation.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
25 #include <glib/gi18n-lib.h>
26 #include <gtk/gtkbox.h>
27 #include <gtk/gtkbutton.h>
28 #include <gtk/gtkcellrenderertext.h>
29 #include <gtk/gtkcellrenderertoggle.h>
30 #include <gtk/gtkcellrendererpixbuf.h>
31 #include <gtk/gtkentry.h>
32 #include <gtk/gtkfilechooserbutton.h>
33 #include <gtk/gtkliststore.h>
34 #include <gtk/gtkmain.h>
35 #include <gtk/gtkmessagedialog.h>
36 #include <gtk/gtktable.h>
37 #include <gtk/gtkstock.h>
38 #include <gtk/gtktreeselection.h>
39 #include <gtk/gtktreeview.h>
40 #include <glade/glade-xml.h>
41 #include "libedataserver/e-categories.h"
42 #include "libedataserver/libedataserver-private.h"
43 #include "e-categories-dialog.h"
52 struct _ECategoriesDialogPrivate {
54 GtkWidget *categories_entry;
55 GtkWidget *categories_list;
56 GtkWidget *new_button;
57 GtkWidget *edit_button;
58 GtkWidget *delete_button;
60 GHashTable *selected_categories;
63 static GObjectClass *parent_class = NULL;
65 /* Category properties dialog */
68 ECategoriesDialog *parent;
70 GtkWidget *the_dialog;
71 GtkWidget *category_name;
72 GtkWidget *category_color;
73 GtkWidget *category_icon;
74 } CategoryPropertiesDialog;
76 static CategoryPropertiesDialog *
77 load_properties_dialog (ECategoriesDialog *parent)
79 CategoryPropertiesDialog *prop_dialog;
82 prop_dialog = g_new0 (CategoryPropertiesDialog, 1);
84 gladefile = g_build_filename (E_DATA_SERVER_UI_GLADEDIR,
85 "e-categories-dialog.glade",
87 prop_dialog->gui = glade_xml_new (gladefile, "properties-dialog", GETTEXT_PACKAGE);
90 if (!prop_dialog->gui) {
95 prop_dialog->parent = parent;
97 prop_dialog->the_dialog = glade_xml_get_widget (prop_dialog->gui, "properties-dialog");
98 gtk_window_set_transient_for (GTK_WINDOW (prop_dialog->the_dialog), GTK_WINDOW (parent));
100 prop_dialog->category_name = glade_xml_get_widget (prop_dialog->gui, "category-name");
101 prop_dialog->category_color = glade_xml_get_widget (prop_dialog->gui, "category-color");
102 prop_dialog->category_icon = glade_xml_get_widget (prop_dialog->gui, "category-icon");
108 free_properties_dialog (CategoryPropertiesDialog *prop_dialog)
110 if (prop_dialog->the_dialog) {
111 gtk_widget_destroy (prop_dialog->the_dialog);
112 prop_dialog->the_dialog = NULL;
115 if (prop_dialog->gui) {
116 g_object_unref (prop_dialog->gui);
117 prop_dialog->gui = NULL;
120 g_free (prop_dialog);
123 /* GObject methods */
125 G_DEFINE_TYPE (ECategoriesDialog, e_categories_dialog, GTK_TYPE_DIALOG)
128 e_categories_dialog_dispose (GObject *object)
130 ECategoriesDialogPrivate *priv = E_CATEGORIES_DIALOG (object)->priv;
133 g_object_unref (priv->gui);
137 if (priv->selected_categories) {
138 g_hash_table_destroy (priv->selected_categories);
139 priv->selected_categories = NULL;
142 (* G_OBJECT_CLASS (parent_class)->dispose) (object);
146 e_categories_dialog_finalize (GObject *object)
148 ECategoriesDialogPrivate *priv = E_CATEGORIES_DIALOG (object)->priv;
151 E_CATEGORIES_DIALOG (object)->priv = NULL;
153 (* G_OBJECT_CLASS (parent_class)->finalize) (object);
157 e_categories_dialog_class_init (ECategoriesDialogClass *klass)
159 GObjectClass *object_class = G_OBJECT_CLASS (klass);
161 object_class->dispose = e_categories_dialog_dispose;
162 object_class->finalize = e_categories_dialog_finalize;
164 parent_class = g_type_class_peek_parent (klass);
168 add_comma_sep_categories (gpointer key, gpointer value, gpointer user_data)
170 GString **str = user_data;
172 if (strlen ((*str)->str) > 0)
173 *str = g_string_append (*str, ",");
175 *str = g_string_append (*str, (const char *) key);
179 category_toggled_cb (GtkCellRenderer *renderer, const gchar *path, gpointer user_data)
181 ECategoriesDialogPrivate *priv;
184 ECategoriesDialog *dialog = user_data;
187 model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->categories_list));
189 if (gtk_tree_model_get_iter_from_string (model, &iter, path)) {
194 gtk_tree_model_get (model, &iter,
195 COLUMN_ACTIVE, &place_bool,
196 COLUMN_CATEGORY, &place_string,
199 g_hash_table_remove (priv->selected_categories, place_string);
200 gtk_list_store_set (GTK_LIST_STORE (model), &iter, COLUMN_ACTIVE, FALSE, -1);
202 g_hash_table_insert (priv->selected_categories, g_strdup (place_string), g_strdup (place_string));
203 gtk_list_store_set (GTK_LIST_STORE (model), &iter, COLUMN_ACTIVE, TRUE, -1);
206 str = g_string_new ("");
207 g_hash_table_foreach (priv->selected_categories, (GHFunc) add_comma_sep_categories, &str);
208 gtk_entry_set_text (GTK_ENTRY (priv->categories_entry), str->str);
211 g_string_free (str, TRUE);
212 g_free (place_string);
214 gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), GTK_RESPONSE_OK, TRUE);
219 entry_changed_cb (GtkEditable *editable, gpointer user_data)
221 ECategoriesDialog *dialog = user_data;
223 gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), GTK_RESPONSE_OK, TRUE);
227 check_category_name (const char *name)
230 char *p = (char *) name;
232 str = g_string_new ("");
238 str = g_string_append_c (str, *p);
244 g_string_free (str, FALSE);
250 new_button_clicked_cb (GtkButton *button, gpointer user_data)
252 ECategoriesDialog *dialog;
253 CategoryPropertiesDialog *prop_dialog;
257 prop_dialog = load_properties_dialog (dialog);
262 if (gtk_dialog_run (GTK_DIALOG (prop_dialog->the_dialog)) == GTK_RESPONSE_OK) {
263 const char *category_name;
264 char *correct_category_name;
267 category_name = gtk_entry_get_text (GTK_ENTRY (prop_dialog->category_name));
268 correct_category_name = check_category_name (category_name);
270 if (e_categories_exist (correct_category_name)) {
271 GtkWidget *error_dialog;
273 error_dialog = gtk_message_dialog_new (
274 GTK_WINDOW (prop_dialog->the_dialog),
275 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
276 _("There is already a category '%s' in the configuration. Please use another name"),
279 gtk_dialog_run (GTK_DIALOG (error_dialog));
280 gtk_widget_destroy (error_dialog);
281 g_free (correct_category_name);
283 gchar *category_icon;
284 GdkPixbuf *icon = NULL;
285 GtkListStore *list_store = GTK_LIST_STORE (
286 gtk_tree_view_get_model (GTK_TREE_VIEW (prop_dialog->parent->priv->categories_list)));
287 /* FIXME: get color */
288 category_icon = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (prop_dialog->category_icon));
290 icon = gdk_pixbuf_new_from_file (category_icon, NULL);
292 e_categories_add (correct_category_name, NULL, category_icon ? category_icon : NULL, TRUE);
294 gtk_list_store_append (list_store, &iter);
295 gtk_list_store_set (list_store, &iter,
296 COLUMN_ACTIVE, FALSE,
298 COLUMN_CATEGORY,correct_category_name,
302 g_object_unref (icon);
304 g_free (category_icon);
305 g_free (correct_category_name);
313 free_properties_dialog (prop_dialog);
317 edit_button_clicked_cb (GtkButton *button, gpointer user_data)
319 ECategoriesDialog *dialog;
320 ECategoriesDialogPrivate *priv;
321 CategoryPropertiesDialog *prop_dialog;
329 /* get the currently selected item */
330 model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->categories_list));
332 if (!gtk_tree_selection_get_selected (gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->categories_list)),
336 /* load the properties dialog */
337 prop_dialog = load_properties_dialog (dialog);
341 gtk_tree_model_get (model, &iter, COLUMN_CATEGORY, &category_name, -1);
342 gtk_entry_set_text (GTK_ENTRY (prop_dialog->category_name), category_name);
343 gtk_widget_set_sensitive (prop_dialog->category_name, FALSE);
344 gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (prop_dialog->category_icon),
345 e_categories_get_icon_file_for (category_name));
347 if (gtk_dialog_run (GTK_DIALOG (prop_dialog->the_dialog)) == GTK_RESPONSE_OK) {
348 gchar *category_icon;
350 /* FIXME: get color */
351 category_icon = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (prop_dialog->category_icon));
354 GdkPixbuf *icon = NULL;
355 e_categories_set_icon_file_for (category_name, category_icon);
356 icon = gdk_pixbuf_new_from_file (category_icon, NULL);
358 gtk_list_store_set (GTK_LIST_STORE (model), &iter, COLUMN_ICON, icon, -1);
359 g_object_unref (icon);
362 g_free (category_icon);
364 gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), GTK_RESPONSE_OK, TRUE);
367 g_free (category_name);
368 free_properties_dialog (prop_dialog);
372 delete_button_clicked_cb (GtkButton *button, gpointer user_data)
374 ECategoriesDialog *dialog;
375 ECategoriesDialogPrivate *priv;
383 /* get the currently selected item */
384 model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->categories_list));
386 if (!gtk_tree_selection_get_selected (gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->categories_list)),
390 gtk_tree_model_get (model, &iter, COLUMN_CATEGORY, &category_name, -1);
391 e_categories_remove (category_name);
392 gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
396 e_categories_dialog_init (ECategoriesDialog *dialog)
398 ECategoriesDialogPrivate *priv;
400 GtkCellRenderer *renderer;
401 GtkTreeViewColumn *column;
403 GtkWidget *main_widget;
406 priv = g_new0 (ECategoriesDialogPrivate, 1);
407 priv->selected_categories = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
410 /* load the UI from our Glade file */
411 gladefile = g_build_filename (E_DATA_SERVER_UI_GLADEDIR,
412 "e-categories-dialog.glade",
414 priv->gui = glade_xml_new (gladefile, "table-categories", GETTEXT_PACKAGE);
418 g_warning (G_STRLOC ": can't load e-categories-dialog.glade file");
422 main_widget = glade_xml_get_widget (priv->gui, "table-categories");
423 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), main_widget, TRUE, TRUE, 0);
425 priv->categories_entry = glade_xml_get_widget (priv->gui, "entry-categories");
426 priv->categories_list = glade_xml_get_widget (priv->gui, "categories-list");
428 priv->new_button = glade_xml_get_widget (priv->gui, "button-new");
429 g_signal_connect (G_OBJECT (priv->new_button), "clicked", G_CALLBACK (new_button_clicked_cb), dialog);
430 priv->edit_button = glade_xml_get_widget (priv->gui, "button-edit");
431 g_signal_connect (G_OBJECT (priv->edit_button), "clicked", G_CALLBACK (edit_button_clicked_cb), dialog);
432 priv->delete_button = glade_xml_get_widget (priv->gui, "button-delete");
433 g_signal_connect (G_OBJECT (priv->delete_button), "clicked", G_CALLBACK (delete_button_clicked_cb), dialog);
435 gtk_dialog_add_buttons (GTK_DIALOG (dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
436 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
437 gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
438 gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), GTK_RESPONSE_OK, FALSE);
439 gtk_window_set_title (GTK_WINDOW (dialog), _("Categories"));
441 /* set up the categories list */
442 model = gtk_list_store_new (N_COLUMNS, G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF, G_TYPE_STRING);
443 cat_list = e_categories_get_list ();
444 while (cat_list != NULL) {
447 /* only add categories that are user-visible */
448 if (e_categories_is_searchable ((const char *) cat_list->data)) {
449 const gchar *icon_file;
450 GdkPixbuf *icon = NULL;
451 icon_file = e_categories_get_icon_file_for ((const char *) cat_list->data);
453 icon = gdk_pixbuf_new_from_file (icon_file, NULL);
454 gtk_list_store_append (model, &iter);
455 gtk_list_store_set (model, &iter,
456 COLUMN_ACTIVE, FALSE,
458 COLUMN_CATEGORY, cat_list->data,
461 g_object_unref (icon);
464 cat_list = g_list_remove (cat_list, cat_list->data);
466 gtk_tree_view_set_model (GTK_TREE_VIEW (priv->categories_list), GTK_TREE_MODEL (model));
468 renderer = gtk_cell_renderer_toggle_new ();
469 g_signal_connect (G_OBJECT (renderer), "toggled", G_CALLBACK (category_toggled_cb), dialog);
470 column = gtk_tree_view_column_new_with_attributes ("?", renderer,
471 "active", COLUMN_ACTIVE, NULL);
472 gtk_tree_view_append_column (GTK_TREE_VIEW (priv->categories_list), column);
474 renderer = gtk_cell_renderer_pixbuf_new ();
475 column = gtk_tree_view_column_new_with_attributes (_("Icon"), renderer,
476 "pixbuf", COLUMN_ICON, NULL);
477 gtk_tree_view_append_column (GTK_TREE_VIEW (priv->categories_list), column);
479 renderer = gtk_cell_renderer_text_new ();
480 column = gtk_tree_view_column_new_with_attributes (_("Category"), renderer,
481 "text", COLUMN_CATEGORY, NULL);
482 gtk_tree_view_append_column (GTK_TREE_VIEW (priv->categories_list), column);
485 g_object_unref (model);
489 * e_categories_dialog_new:
490 * @initial_category_list: Comma-separated list of initial categories.
492 * Creates a new #ECategoriesDialog widget.
494 * Return value: A pointer to the newly created #ECategoriesDialog widget.
497 e_categories_dialog_new (const char *initial_category_list)
499 ECategoriesDialog *dialog;
501 dialog = E_CATEGORIES_DIALOG (g_object_new (E_TYPE_CATEGORIES_DIALOG, NULL));
502 if (initial_category_list)
503 e_categories_dialog_set_categories (dialog, initial_category_list);
505 g_signal_connect (G_OBJECT (dialog->priv->categories_entry), "changed",
506 G_CALLBACK (entry_changed_cb), dialog);
508 return GTK_WIDGET (dialog);
512 * e_categories_dialog_get_categories:
513 * @dialog: An #ECategoriesDialog widget.
515 * Gets a comma-separated list of the categories currently selected on the dialog.
517 * Return value: comma-separated list of categories.
520 e_categories_dialog_get_categories (ECategoriesDialog *dialog)
522 ECategoriesDialogPrivate *priv;
524 g_return_val_if_fail (E_IS_CATEGORIES_DIALOG (dialog), NULL);
528 return gtk_entry_get_text (GTK_ENTRY (priv->categories_entry));
532 * e_categories_dialog_set_categories:
533 * @dialog: An #ECategoriesDialog widget.
534 * @categories: Comma-separated list of categories.
536 * Sets the list of categories selected on the dialog.
539 e_categories_dialog_set_categories (ECategoriesDialog *dialog, const char *categories)
541 ECategoriesDialogPrivate *priv;
546 g_return_if_fail (E_IS_CATEGORIES_DIALOG (dialog));
550 /* clean up the table of selected categories */
551 g_hash_table_foreach_remove (priv->selected_categories, (GHRFunc) gtk_true, NULL);
553 arr = g_strsplit (categories, ",", 0);
556 while (arr[i] != NULL) {
557 g_hash_table_insert (priv->selected_categories, g_strdup (arr[i]), g_strdup (arr[i]));
564 /* set the widgets */
565 gtk_entry_set_text (GTK_ENTRY (priv->categories_entry), categories);
567 model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->categories_list));
568 if (gtk_tree_model_get_iter_first (model, &iter)) {
572 gtk_tree_model_get (model, &iter, COLUMN_CATEGORY, &place_string, -1);
573 if (g_hash_table_lookup (priv->selected_categories, place_string))
574 gtk_list_store_set (GTK_LIST_STORE (model), &iter, COLUMN_ACTIVE, TRUE, -1);
576 gtk_list_store_set (GTK_LIST_STORE (model), &iter, COLUMN_ACTIVE, FALSE, -1);
578 g_free (place_string);
579 } while (gtk_tree_model_iter_next (model, &iter));