2 /* Notification plugin for Claws-Mail
3 * Copyright (C) 2005-2007 Holger Berndt
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 /* This code is based on foldersel.c in Claws Mail.
20 * Some functions are only slightly modified, almost 1:1 copies from there. */
24 # include "claws-features.h"
28 #include <glib/gi18n.h>
30 /* Basic definitions first */
31 #include "common/defs.h"
35 #include <gdk/gdkkeysyms.h>
38 /* Claws Mail includes */
39 #include "manage_window.h"
41 #include "stock_pixmap.h"
42 #include "gtk/gtkutils.h"
43 #include "common/utils.h"
44 #include "common/prefs.h"
45 #include "common/xml.h"
46 #include "common/hooks.h"
47 #include "prefs_common.h"
50 #include "notification_foldercheck.h"
52 /* enums and structures */
54 FOLDERCHECK_FOLDERNAME,
55 FOLDERCHECK_FOLDERITEM,
57 FOLDERCHECK_PIXBUF_OPEN,
66 GtkTreeStore *tree_store;
73 } SpecificFolderArrayEntry;
75 /* variables with file scope */
76 static GdkPixbuf *folder_pixbuf;
77 static GdkPixbuf *folderopen_pixbuf;
78 static GdkPixbuf *foldernoselect_pixbuf;
79 static GdkPixbuf *foldernoselectopen_pixbuf;
81 static GArray *specific_folder_array;
82 static guint specific_folder_array_size;
84 static guint hook_folder_update;
88 #define FOLDERCHECK_ARRAY "notification_foldercheck.xml"
89 #define foldercheck_get_entry_from_id(id) \
90 ((id) < specific_folder_array_size) ? \
91 g_array_index(specific_folder_array,SpecificFolderArrayEntry*,(id)) : NULL
93 /* function prototypes */
94 static void folder_checked(guint);
95 static void foldercheck_create_window(SpecificFolderArrayEntry*);
96 static void foldercheck_destroy_window(SpecificFolderArrayEntry*);
97 static gint foldercheck_folder_name_compare(GtkTreeModel*, GtkTreeIter*,
98 GtkTreeIter*, gpointer);
99 static gboolean foldercheck_selected(GtkTreeSelection*,
100 GtkTreeModel*, GtkTreePath*,
103 static gint delete_event(GtkWidget*, GdkEventAny*, gpointer);
104 static void foldercheck_ok(GtkButton*, gpointer);
105 static void foldercheck_cancel(GtkButton*, gpointer);
106 static void foldercheck_set_tree(SpecificFolderArrayEntry*);
107 static void foldercheck_insert_gnode_in_store(GtkTreeStore*, GNode*,
109 static void foldercheck_append_item(GtkTreeStore*, FolderItem*,
110 GtkTreeIter*, GtkTreeIter*);
111 static void foldercheck_recursive_cb(GtkToggleButton*, gpointer);
112 static void folder_toggle_cb(GtkCellRendererToggle*, gchar*, gpointer);
113 static void folder_toggle_recurse_tree(GtkTreeStore*, GtkTreeIter*, gint,
115 static gboolean foldercheck_foreach_check(GtkTreeModel*, GtkTreePath*,
116 GtkTreeIter*, gpointer);
117 static gboolean foldercheck_foreach_update_to_list(GtkTreeModel*, GtkTreePath*,
118 GtkTreeIter*, gpointer);
119 static gchar *foldercheck_get_array_path(void);
120 static gboolean my_folder_update_hook(gpointer, gpointer);
121 static gboolean key_pressed(GtkWidget*, GdkEventKey*,gpointer);
124 /* Creates an entry in the specific_folder_array, and fills it with a new
125 * SpecificFolderArrayEntry*. If specific_folder_array already has an entry
126 * with the same name, return its ID. (The ID is the index in the array.) */
127 guint notification_register_folder_specific_list(gchar *node_name)
129 SpecificFolderArrayEntry *entry;
132 /* If array does not yet exist, create it. */
133 if(!specific_folder_array) {
134 specific_folder_array = g_array_new(FALSE, FALSE,
135 sizeof(SpecificFolderArrayEntry*));
136 specific_folder_array_size = 0;
138 /* Register hook for folder update */
139 /* "The hook is registered" is bound to "the array is allocated" */
140 hook_folder_update = hooks_register_hook(FOLDER_UPDATE_HOOKLIST,
141 my_folder_update_hook, NULL);
142 if(hook_folder_update == (guint) -1) {
143 debug_print("Warning: Failed to register hook to folder update "
145 "Strange things can occur when deleting folders.\n");
149 /* Check if we already have such a name. If so, return its id. */
150 while(ii < specific_folder_array_size) {
151 entry = g_array_index(specific_folder_array,SpecificFolderArrayEntry*,ii);
153 if(!strcmp2(entry->name,node_name))
159 /* Create an entry with the corresponding node name. */
160 entry = g_new(SpecificFolderArrayEntry, 1);
161 entry->name = g_strdup(node_name);
163 entry->window = NULL;
164 entry->treeview = NULL;
165 entry->cancelled = FALSE;
166 entry->finished = FALSE;
167 entry->recursive = FALSE;
168 entry->tree_store = gtk_tree_store_new(N_FOLDERCHECK_COLUMNS,
174 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(entry->tree_store),
175 FOLDERCHECK_FOLDERNAME,
176 foldercheck_folder_name_compare,
178 specific_folder_array = g_array_append_val(specific_folder_array, entry);
179 return specific_folder_array_size++;
182 /* This function is called in plugin_done. It frees the whole
183 * folder_specific_array with all its entries. */
184 void notification_free_folder_specific_array(void)
187 SpecificFolderArrayEntry *entry;
189 for(ii = 0; ii < specific_folder_array_size; ii++) {
190 entry = g_array_index(specific_folder_array,SpecificFolderArrayEntry*,ii);
194 g_slist_free(entry->list);
195 if(entry->tree_store)
196 g_object_unref(G_OBJECT(entry->tree_store));
200 if(specific_folder_array) {
202 g_array_free(specific_folder_array, TRUE);
204 /* Unregister hook */
205 hooks_unregister_hook(FOLDER_UPDATE_HOOKLIST, hook_folder_update);
207 specific_folder_array = NULL;
208 specific_folder_array_size = 0;
211 /* Returns the list of the entry with the corresponding ID, or NULL if
212 * no such element exists. */
213 GSList* notification_foldercheck_get_list(guint id)
215 SpecificFolderArrayEntry *entry;
217 entry = foldercheck_get_entry_from_id(id);
225 /* Save selections in a common xml-file. Called when unloading the plugin.
226 * This is analog to folder.h::folder_write_list. */
227 void notification_foldercheck_write_array(void)
236 /* Do nothing if foldercheck is not in use */
237 if(specific_folder_array_size == 0)
240 path = foldercheck_get_array_path();
241 if((pfile = prefs_write_open(path)) == NULL) {
242 debug_print("Notification Plugin Error: Cannot open "
243 "file " FOLDERCHECK_ARRAY " for writing\n");
247 /* XML declarations */
248 xml_file_put_xml_decl(pfile->fp);
250 /* Build up XML tree */
253 tag = xml_tag_new("foldercheckarray");
254 xmlnode = xml_node_new(tag, NULL);
255 rootnode = g_node_new(xmlnode);
258 for(ii = 0; ii < specific_folder_array_size; ii++) {
261 SpecificFolderArrayEntry *entry;
263 entry = foldercheck_get_entry_from_id(ii);
265 tag = xml_tag_new("branch");
266 xml_tag_add_attr(tag, xml_attr_new("name",entry->name));
267 xmlnode = xml_node_new(tag, NULL);
268 branchnode = g_node_new(xmlnode);
269 g_node_append(rootnode, branchnode);
271 /* Write out the list as leaf nodes */
272 for(walk = entry->list; walk != NULL; walk = g_slist_next(walk)) {
275 FolderItem *item = (FolderItem*) walk->data;
277 identifier = folder_item_get_identifier(item);
279 tag = xml_tag_new("folderitem");
280 xml_tag_add_attr(tag, xml_attr_new("identifier", identifier));
282 xmlnode = xml_node_new(tag, NULL);
283 node = g_node_new(xmlnode);
284 g_node_append(branchnode, node);
285 } /* for all list elements in branch node */
287 } /* for all branch nodes */
289 /* Actual writing and cleanup */
290 xml_write_tree(rootnode, pfile->fp);
292 if(prefs_file_close(pfile) < 0) {
293 debug_print("Notification Plugin Error: Failed to write "
294 "file " FOLDERCHECK_ARRAY "\n");
298 xml_free_tree(rootnode);
301 /* Read selections from a common xml-file. Called when loading the plugin.
302 * Returns TRUE if data has been read, FALSE if no data is available
303 * or an error occurred.
304 * This is analog to folder.h::folder_read_list. */
305 gboolean notification_foldercheck_read_array(void)
308 GNode *rootnode, *node, *branchnode;
310 gboolean success = FALSE;
312 path = foldercheck_get_array_path();
313 if(!is_file_exist(path)) {
318 /* We don't do merging, so if the file existed, clear what we
319 have stored in memory right now.. */
320 notification_free_folder_specific_array();
322 /* .. and evaluate the file */
323 rootnode = xml_parse_file(path);
328 xmlnode = rootnode->data;
330 /* Check that root entry is "foldercheckarray" */
331 if(strcmp2(xmlnode->tag->tag, "foldercheckarray") != 0) {
332 g_warning("wrong foldercheck array file");
333 xml_free_tree(rootnode);
337 /* Process branch entries */
338 for(branchnode = rootnode->children; branchnode != NULL;
339 branchnode = branchnode->next) {
342 SpecificFolderArrayEntry *entry = NULL;
344 xmlnode = branchnode->data;
345 if(strcmp2(xmlnode->tag->tag, "branch") != 0) {
346 g_warning("tag name != \"branch\"");
350 /* Attributes of the branch nodes */
351 list = xmlnode->tag->attr;
352 for(; list != NULL; list = list->next) {
353 XMLAttr *attr = list->data;
355 if(attr && attr->name && attr->value && !strcmp2(attr->name, "name")) {
356 id = notification_register_folder_specific_list(attr->value);
357 entry = foldercheck_get_entry_from_id(id);
358 /* We have found something */
363 if((list == NULL) || (entry == NULL)) {
364 g_warning("Did not find attribute \"name\" in tag \"branch\"");
365 continue; /* with next branch */
368 /* Now descent into the children of the brach, which are the folderitems */
369 for(node = branchnode->children; node != NULL; node = node->next) {
370 FolderItem *item = NULL;
372 /* These should all be leaves. */
373 if(!G_NODE_IS_LEAF(node))
374 g_warning("Subnodes in \"branch\" nodes should all be leaves. "
375 "Ignoring deeper subnodes.");
377 /* Check if tag is "folderitem" */
378 xmlnode = node->data;
379 if(strcmp2(xmlnode->tag->tag, "folderitem") != 0) {
380 g_warning("tag name != \"folderitem\"");
381 continue; /* to next node in branch */
384 /* Attributes of the leaf nodes */
385 list = xmlnode->tag->attr;
386 for(; list != NULL; list = list->next) {
387 XMLAttr *attr = list->data;
389 if(attr && attr->name && attr->value &&
390 !strcmp2(attr->name, "identifier")) {
391 item = folder_find_item_from_identifier(attr->value);
395 if((list == NULL) || (item == NULL)) {
396 g_warning("Did not find attribute \"identifier\" in tag "
398 continue; /* with next leaf node */
401 /* Store all FolderItems in the list */
402 /* We started with a cleared array, so we don't need to check if
403 it's already in there. */
404 entry->list = g_slist_prepend(entry->list, item);
406 } /* for all subnodes in branch */
408 } /* for all branches */
412 /* Stolen from folder.c. Return value should NOT be freed. */
413 static gchar *foldercheck_get_array_path(void)
415 static gchar *filename = NULL;
418 filename = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
419 FOLDERCHECK_ARRAY, NULL);
423 /* Callback for selecting folders. If no selection dialog exists yet, create
424 * one and initialize the selection. The recurse through the whole model, and
425 * add all selected items to the list. */
426 static void folder_checked(guint id)
428 SpecificFolderArrayEntry *entry;
429 GSList *checked_list = NULL;
431 entry = foldercheck_get_entry_from_id(id);
434 foldercheck_create_window(entry);
435 gtk_widget_show(entry->window);
436 manage_window_set_transient(GTK_WINDOW(entry->window));
438 entry->cancelled = entry->finished = FALSE;
439 while(entry->finished == FALSE)
440 gtk_main_iteration();
442 foldercheck_destroy_window(entry);
444 if(!entry->cancelled) {
445 /* recurse through the whole model, add all selected items to the list */
446 gtk_tree_model_foreach(GTK_TREE_MODEL(entry->tree_store),
447 foldercheck_foreach_check, &checked_list);
450 g_slist_free(entry->list);
453 entry->list = g_slist_copy(checked_list);
454 g_slist_free(checked_list);
457 gtk_tree_store_clear(entry->tree_store);
459 entry->cancelled = FALSE;
460 entry->finished = FALSE;
463 /* Create the window for selecting folders with checkboxes */
464 static void foldercheck_create_window(SpecificFolderArrayEntry *entry)
467 GtkWidget *scrolledwin;
468 GtkWidget *confirm_area;
470 GtkWidget *cancel_button;
471 GtkWidget *ok_button;
472 GtkTreeSelection *selection;
473 GtkTreeViewColumn *column;
474 GtkCellRenderer *renderer;
475 static GdkGeometry geometry;
478 entry->window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "notification_foldercheck");
479 gtk_window_set_title(GTK_WINDOW(entry->window), _("Select folder(s)"));
480 gtk_container_set_border_width(GTK_CONTAINER(entry->window), 4);
481 gtk_window_set_position(GTK_WINDOW(entry->window), GTK_WIN_POS_CENTER);
482 gtk_window_set_modal(GTK_WINDOW(entry->window), TRUE);
483 gtk_window_set_resizable(GTK_WINDOW(entry->window), TRUE);
484 gtk_window_set_wmclass
485 (GTK_WINDOW(entry->window), "folder_selection", "Claws Mail");
486 g_signal_connect(G_OBJECT(entry->window), "delete_event",
487 G_CALLBACK(delete_event), entry);
488 g_signal_connect(G_OBJECT(entry->window), "key_press_event",
489 G_CALLBACK(key_pressed), entry);
490 MANAGE_WINDOW_SIGNALS_CONNECT(entry->window);
493 vbox = gtk_vbox_new(FALSE, 4);
494 gtk_container_add(GTK_CONTAINER(entry->window), vbox);
496 /* scrolled window */
497 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
498 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
499 GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
500 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
502 gtk_box_pack_start(GTK_BOX(vbox), scrolledwin, TRUE, TRUE, 0);
506 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_CLOSE,
508 if(!folderopen_pixbuf)
509 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_OPEN,
511 if(!foldernoselect_pixbuf)
512 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_NOSELECT_CLOSE,
513 &foldernoselect_pixbuf);
514 if(!foldernoselectopen_pixbuf)
515 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_NOSELECT_OPEN,
516 &foldernoselectopen_pixbuf);
519 foldercheck_set_tree(entry);
520 gtk_tree_model_foreach(GTK_TREE_MODEL(entry->tree_store),
521 foldercheck_foreach_update_to_list, entry);
526 gtk_tree_view_new_with_model(GTK_TREE_MODEL(entry->tree_store));
527 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(entry->treeview), FALSE);
528 gtk_tree_view_set_search_column(GTK_TREE_VIEW(entry->treeview),
529 FOLDERCHECK_FOLDERNAME);
530 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(entry->treeview),
531 prefs_common_get_prefs()->use_stripes_everywhere);
532 gtk_tree_view_set_enable_tree_lines(GTK_TREE_VIEW(entry->treeview), FALSE);
534 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(entry->treeview));
535 gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE);
536 gtk_tree_selection_set_select_function(selection, foldercheck_selected,
539 gtk_container_add(GTK_CONTAINER(scrolledwin), entry->treeview);
541 /* --- column 1 --- */
542 column = gtk_tree_view_column_new();
543 gtk_tree_view_column_set_title(column, "sel");
544 gtk_tree_view_column_set_spacing(column, 2);
547 renderer = gtk_cell_renderer_toggle_new();
548 g_object_set(renderer, "xalign", 0.0, NULL);
549 gtk_tree_view_column_pack_start(column, renderer, TRUE);
550 g_signal_connect(renderer, "toggled", G_CALLBACK(folder_toggle_cb),entry);
551 gtk_tree_view_column_set_attributes(column, renderer,
552 "active", FOLDERCHECK_CHECK,NULL);
554 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
555 gtk_tree_view_append_column(GTK_TREE_VIEW(entry->treeview), column);
557 /* --- column 2 --- */
558 column = gtk_tree_view_column_new();
559 gtk_tree_view_column_set_title(column, "Folder");
560 gtk_tree_view_column_set_spacing(column, 2);
563 renderer = gtk_cell_renderer_pixbuf_new();
564 gtk_tree_view_column_pack_start(column, renderer, FALSE);
565 gtk_tree_view_column_set_attributes
567 "pixbuf", FOLDERCHECK_PIXBUF,
568 "pixbuf-expander-open", FOLDERCHECK_PIXBUF_OPEN,
569 "pixbuf-expander-closed", FOLDERCHECK_PIXBUF,
573 renderer = gtk_cell_renderer_text_new();
574 gtk_tree_view_column_pack_start(column, renderer, TRUE);
575 gtk_tree_view_column_set_attributes(column, renderer,
576 "text", FOLDERCHECK_FOLDERNAME,
579 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
580 gtk_tree_view_append_column(GTK_TREE_VIEW(entry->treeview), column);
583 checkbox = gtk_check_button_new_with_label( _("select recursively"));
584 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox), FALSE);
585 g_signal_connect(G_OBJECT(checkbox), "toggled",
586 G_CALLBACK(foldercheck_recursive_cb), entry);
587 gtk_box_pack_start(GTK_BOX(vbox), checkbox, FALSE, FALSE, 10);
589 gtkut_stock_button_set_create(&confirm_area,
590 &cancel_button, GTK_STOCK_CANCEL,
591 &ok_button, GTK_STOCK_OK,
593 gtk_box_pack_end(GTK_BOX(vbox), confirm_area, FALSE, FALSE, 0);
594 gtk_widget_grab_default(ok_button);
596 g_signal_connect(G_OBJECT(ok_button), "clicked",
597 G_CALLBACK(foldercheck_ok), entry);
598 g_signal_connect(G_OBJECT(cancel_button), "clicked",
599 G_CALLBACK(foldercheck_cancel), entry);
601 if(!geometry.min_height) {
602 geometry.min_width = 360;
603 geometry.min_height = 360;
606 gtk_window_set_geometry_hints(GTK_WINDOW(entry->window), NULL, &geometry,
609 gtk_tree_view_expand_all(GTK_TREE_VIEW(entry->treeview));
611 gtk_widget_show_all(vbox);
614 static void foldercheck_destroy_window(SpecificFolderArrayEntry *entry)
616 gtk_widget_destroy(entry->window);
617 entry->window = NULL;
618 entry->treeview = NULL;
619 entry->recursive = FALSE;
622 /* Handler for the delete event of the windows for selecting folders */
623 static gint delete_event(GtkWidget *widget, GdkEventAny *event, gpointer data)
625 foldercheck_cancel(NULL, data);
629 /* sortable_set_sort_func */
630 static gint foldercheck_folder_name_compare(GtkTreeModel *model,
631 GtkTreeIter *a, GtkTreeIter *b,
634 gchar *str_a = NULL, *str_b = NULL;
636 FolderItem *item_a = NULL, *item_b = NULL;
639 gtk_tree_model_get(model, a, FOLDERCHECK_FOLDERITEM, &item_a, -1);
640 gtk_tree_model_get(model, b, FOLDERCHECK_FOLDERITEM, &item_b, -1);
642 /* no sort for root folder */
643 if (!gtk_tree_model_iter_parent(GTK_TREE_MODEL(model), &parent, a))
646 /* if both a and b are special folders, sort them according to
647 * their types (which is in-order). Note that this assumes that
648 * there are no multiple folders of a special type. */
649 if (item_a->stype != F_NORMAL && item_b->stype != F_NORMAL)
650 return item_a->stype - item_b->stype;
652 /* if b is normal folder, and a is not, b is smaller (ends up
653 * lower in the list) */
654 if (item_a->stype != F_NORMAL && item_b->stype == F_NORMAL)
655 return item_b->stype - item_a->stype;
657 /* if b is special folder, and a is not, b is larger (ends up
658 * higher in the list) */
659 if (item_a->stype == F_NORMAL && item_b->stype != F_NORMAL)
660 return item_b->stype - item_a->stype;
662 /* XXX g_utf8_collate_key() comparisons may speed things
663 * up when having large lists of folders */
664 gtk_tree_model_get(model, a, FOLDERCHECK_FOLDERNAME, &str_a, -1);
665 gtk_tree_model_get(model, b, FOLDERCHECK_FOLDERNAME, &str_b, -1);
667 /* otherwise just compare the folder names */
668 val = g_utf8_collate(str_a, str_b);
676 /* select_function of the gtk tree selection */
677 static gboolean foldercheck_selected(GtkTreeSelection *selection,
678 GtkTreeModel *model, GtkTreePath *path,
679 gboolean currently_selected,gpointer data)
682 FolderItem *item = NULL;
684 if (currently_selected)
687 if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, path))
690 gtk_tree_model_get(model, &iter, FOLDERCHECK_FOLDERITEM, &item, -1);
695 /* Callback for the OK-button of the folderselection dialog */
696 static void foldercheck_ok(GtkButton *button, gpointer data)
698 SpecificFolderArrayEntry *entry = (SpecificFolderArrayEntry*) data;
700 entry->finished = TRUE;
703 /* Callback for the Cancel-button of the folderselection dialog. Gets also
704 * called on a delete-event of the folderselection window. */
705 static void foldercheck_cancel(GtkButton *button, gpointer data)
707 SpecificFolderArrayEntry *entry = (SpecificFolderArrayEntry*) data;
709 entry->cancelled = TRUE;
710 entry->finished = TRUE;
713 /* Set tree of the tree-store. This includes getting the folder tree and
715 static void foldercheck_set_tree(SpecificFolderArrayEntry *entry)
720 for(list = folder_get_list(); list != NULL; list = list->next) {
721 folder = FOLDER(list->data);
724 debug_print("Notification plugin::foldercheck_set_tree(): Found a NULL folder.\n");
728 /* Only regard built-in folders, because folders from plugins (such as RSS, calendar,
729 * or plugin-provided mailbox storage systems like Maildir or MBox) may vanish
730 * without letting us know. */
731 switch(folder->klass->type) {
735 foldercheck_insert_gnode_in_store(entry->tree_store, folder->node, NULL);
742 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(entry->tree_store),
743 FOLDERCHECK_FOLDERNAME,
746 if(GTK_IS_TREE_VIEW(entry->treeview))
747 gtk_tree_view_expand_all(GTK_TREE_VIEW(entry->treeview));
750 /* Helper function for foldercheck_set_tree */
751 static void foldercheck_insert_gnode_in_store(GtkTreeStore *store, GNode *node,
758 g_return_if_fail(node != NULL);
759 g_return_if_fail(node->data != NULL);
760 g_return_if_fail(store != NULL);
762 item = FOLDER_ITEM(node->data);
763 foldercheck_append_item(store, item, &child, parent);
765 /* insert its children (this node as parent) */
766 for(iter = node->children; iter != NULL; iter = iter->next)
767 foldercheck_insert_gnode_in_store(store, iter, &child);
770 /* Helper function for foldercheck_insert_gnode_in_store */
771 static void foldercheck_append_item(GtkTreeStore *store, FolderItem *item,
772 GtkTreeIter *iter, GtkTreeIter *parent)
774 gchar *name, *tmpname;
775 GdkPixbuf *pixbuf, *pixbuf_open;
777 name = tmpname = folder_item_get_name(item);
779 if (item->stype != F_NORMAL && FOLDER_IS_LOCAL(item->folder)) {
780 switch (item->stype) {
782 if (!strcmp2(item->name, INBOX_DIR))
786 if (!strcmp2(item->name, OUTBOX_DIR))
790 if (!strcmp2(item->name, QUEUE_DIR))
794 if (!strcmp2(item->name, TRASH_DIR))
798 if (!strcmp2(item->name, DRAFT_DIR))
806 if (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0) {
807 name = g_strdup_printf("%s (%d)", name, item->total_msgs);
808 } else if (item->unread_msgs > 0) {
809 name = g_strdup_printf("%s (%d)", name, item->unread_msgs);
811 name = g_strdup(name);
813 pixbuf = item->no_select ? foldernoselect_pixbuf : folder_pixbuf;
815 item->no_select ? foldernoselectopen_pixbuf : folderopen_pixbuf;
817 /* insert this node */
818 gtk_tree_store_append(store, iter, parent);
819 gtk_tree_store_set(store, iter,
820 FOLDERCHECK_FOLDERNAME, name,
821 FOLDERCHECK_FOLDERITEM, item,
822 FOLDERCHECK_PIXBUF, pixbuf,
823 FOLDERCHECK_PIXBUF_OPEN, pixbuf_open,
829 /* Callback of the recursive-checkbox */
830 static void foldercheck_recursive_cb(GtkToggleButton *button, gpointer data)
832 SpecificFolderArrayEntry *entry = (SpecificFolderArrayEntry*) data;
834 entry->recursive = gtk_toggle_button_get_active(button);
837 /* Callback of the checkboxes corresponding to the folders. Obeys
838 * the "recursive" selection. */
839 static void folder_toggle_cb(GtkCellRendererToggle *cell_renderer,
840 gchar *path_str, gpointer data)
842 gboolean toggle_item;
844 SpecificFolderArrayEntry *entry = (SpecificFolderArrayEntry*) data;
845 GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
847 gtk_tree_model_get_iter(GTK_TREE_MODEL(entry->tree_store), &iter, path);
848 gtk_tree_path_free(path);
849 gtk_tree_model_get(GTK_TREE_MODEL(entry->tree_store), &iter,
850 FOLDERCHECK_CHECK, &toggle_item, -1);
851 toggle_item = !toggle_item;
853 if(!entry->recursive)
854 gtk_tree_store_set(entry->tree_store, &iter,
855 FOLDERCHECK_CHECK, toggle_item, -1);
858 gtk_tree_store_set(entry->tree_store, &iter,
859 FOLDERCHECK_CHECK, toggle_item, -1);
860 if(gtk_tree_model_iter_children(GTK_TREE_MODEL(entry->tree_store),
862 folder_toggle_recurse_tree(entry->tree_store,&child,
863 FOLDERCHECK_CHECK,toggle_item);
866 while(gtk_events_pending())
867 gtk_main_iteration();
870 /* Helper function for folder_toggle_cb */
871 /* This function calls itself recurively */
872 static void folder_toggle_recurse_tree(GtkTreeStore *tree_store,
873 GtkTreeIter *iterp, gint column,
874 gboolean toggle_item)
876 GtkTreeIter iter = *iterp;
879 /* set the value of this iter */
880 gtk_tree_store_set(tree_store, &iter, column, toggle_item, -1);
882 /* do the same for the first child */
883 if(gtk_tree_model_iter_children(GTK_TREE_MODEL(tree_store),&next, &iter))
884 folder_toggle_recurse_tree(tree_store,&next,
885 FOLDERCHECK_CHECK, toggle_item);
887 /* do the same for the next sibling */
888 if(gtk_tree_model_iter_next(GTK_TREE_MODEL(tree_store), &iter))
889 folder_toggle_recurse_tree(tree_store, &iter,
890 FOLDERCHECK_CHECK, toggle_item);
893 /* Helper function to be used with a foreach statement of the model. Checks
894 * if a node is checked, and adds it to a list if it is. data us a (GSList**)
895 * where the result it to be stored */
896 static gboolean foldercheck_foreach_check(GtkTreeModel *model,
898 GtkTreeIter *iter, gpointer data)
900 gboolean toggled_item;
901 GSList **list = (GSList**) data;
903 gtk_tree_model_get(model, iter, FOLDERCHECK_CHECK, &toggled_item, -1);
907 gtk_tree_model_get(model, iter, FOLDERCHECK_FOLDERITEM, &item, -1);
908 *list = g_slist_prepend(*list, item);
914 /* Helper function to be used with a foreach statement of the model. Checks
915 * if a node is checked, and adds it to a list if it is. data us a (GSList**)
916 * where the result it to be stored */
917 static gboolean foldercheck_foreach_update_to_list(GtkTreeModel *model,
922 gchar *ident_tree, *ident_list;
925 gboolean toggle_item = FALSE;
926 SpecificFolderArrayEntry *entry = (SpecificFolderArrayEntry*) data;
928 gtk_tree_model_get(model, iter, FOLDERCHECK_FOLDERITEM, &item, -1);
930 if(item->path != NULL)
931 ident_tree = folder_item_get_identifier(item);
935 for(walk = entry->list; walk != NULL; walk = g_slist_next(walk)) {
936 FolderItem *list_item = (FolderItem*) walk->data;
937 ident_list = folder_item_get_identifier(list_item);
938 if(!strcmp2(ident_list,ident_tree)) {
947 gtk_tree_store_set(entry->tree_store, iter, FOLDERCHECK_CHECK,
954 /* Callback for the folder selection dialog. Basically a wrapper around
955 * folder_checked that first resolves the name to an ID first. */
956 void notification_foldercheck_sel_folders_cb(GtkButton *button, gpointer data)
959 gchar *name = (gchar*) data;
961 id = notification_register_folder_specific_list(name);
966 static gboolean my_folder_update_hook(gpointer source, gpointer data)
968 FolderUpdateData *hookdata = (FolderUpdateData*) source;
970 if(hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM) {
972 SpecificFolderArrayEntry *entry;
973 FolderItem *item = hookdata->item;
975 /* If that folder is in anywhere in the array, cut it out. */
976 for(ii = 0; ii < specific_folder_array_size; ii++) {
977 entry = foldercheck_get_entry_from_id(ii);
978 entry->list = g_slist_remove(entry->list, item);
979 } /* for all entries in the array */
980 } /* A FolderItem was deleted */
985 static gboolean key_pressed(GtkWidget *widget, GdkEventKey *event, gpointer data)
987 if(event && (event->keyval == GDK_Escape)) {
988 foldercheck_cancel(NULL, data);