1 /* Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
2 * Copyright (C) 2007 Holger Berndt <hb@claws-mail.org>
3 * and the Claws Mail team
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/>.
24 #include <glib/gi18n.h>
29 #include "addrduplicates.h"
30 #include "addrindex.h"
31 #include "alertpanel.h"
47 static gboolean create_dialog(void);
48 static void create_addr_hash(void);
49 static void present_finder_results(void);
50 static void cb_finder_results_dialog_destroy(GtkWindow*, gpointer);
51 static void destroy_addr_hash_val(gpointer);
52 static GSList* deep_copy_hash_val(GSList*);
53 static void fill_hash_table();
54 static gint collect_emails(ItemPerson*, const gchar*);
55 static gboolean is_not_duplicate(gpointer, gpointer, gpointer);
56 static gint books_compare(gconstpointer, gconstpointer);
57 static GtkWidget* create_email_view(GtkListStore*);
58 static GtkWidget* create_detail_view(GtkListStore*);
59 static void append_to_email_store(gpointer,gpointer,gpointer);
60 static void email_selection_changed(GtkTreeSelection*,gpointer);
62 static GHashTable *addr_hash;
63 static gboolean include_same_book = TRUE;
64 static gboolean include_other_books = TRUE;
65 static GtkListStore *detail_store;
67 void addrduplicates_find(void)
71 present_finder_results();
75 static gboolean create_dialog(void)
79 GtkWidget *check_same_book;
80 GtkWidget *check_other_book;
85 vbox = gtk_vbox_new(FALSE, 0);
86 check_same_book = gtk_check_button_new_with_label(_("Show duplicates in "
88 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_same_book),
90 gtk_box_pack_start(GTK_BOX(vbox), check_same_book, FALSE, FALSE, 0);
91 gtk_widget_show(check_same_book);
92 check_other_book = gtk_check_button_new_with_label(_("Show duplicates in "
94 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_other_book),
96 gtk_box_pack_start(GTK_BOX(vbox), check_other_book, FALSE, FALSE, 0);
97 gtk_widget_show(check_other_book);
99 /* prevent checkboxes from being destroyed on dialog close */
100 g_object_ref(check_same_book);
101 g_object_ref(check_other_book);
103 val = alertpanel_full(_("Find address book email duplicates"),
104 _("Claws-Mail will now search for duplicate email "
105 "addresses in the addressbook."),
106 _("Cancel"),_("Search"),NULL, FALSE, vbox, ALERT_NOTICE,
108 if(val == G_ALERTALTERNATE) {
113 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(check_same_book));
114 include_other_books =
115 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(check_other_book));
119 g_object_unref(check_same_book);
120 g_object_unref(check_other_book);
124 static void create_addr_hash(void)
127 g_hash_table_destroy(addr_hash);
128 addr_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
129 g_free, destroy_addr_hash_val);
133 static void destroy_addr_hash_val(gpointer value)
135 GSList *list = (GSList*) value;
138 for(walk = list; walk; walk = walk->next) {
139 AddrDupListEntry *entry = (AddrDupListEntry*) walk->data;
149 static GSList* deep_copy_hash_val(GSList *in)
154 out = g_slist_copy(in);
155 for(walk = out; walk; walk = walk->next) {
156 AddrDupListEntry *out_entry;
157 AddrDupListEntry *in_entry = walk->data;
159 out_entry = g_new0(AddrDupListEntry,1);
160 out_entry->person = in_entry->person;
161 out_entry->book = g_strdup(in_entry->book);
162 walk->data = out_entry;
168 static void fill_hash_table()
170 addrindex_load_person_attribute(NULL, collect_emails);
171 g_hash_table_foreach_remove(addr_hash,is_not_duplicate, NULL);
174 static gboolean is_not_duplicate(gpointer key, gpointer value,
177 gboolean is_in_same_book;
178 gboolean is_in_other_books;
182 GSList *list = value;
184 /* remove everything that is just in one book */
185 if(g_slist_length(list) <= 1)
188 /* work on a shallow copy */
189 books = g_slist_copy(list);
191 /* sorting the list makes it easier to check for books */
192 books = g_slist_sort(books, books_compare);
194 /* check if a book appears twice */
195 is_in_same_book = FALSE;
196 for(walk = books; walk && walk->next; walk = walk->next) {
197 if(books_compare(walk->data, walk->next->data) == 0) {
198 is_in_same_book = TRUE;
203 /* check is at least two different books appear in the list */
204 is_in_other_books = FALSE;
205 if(books && books->next) {
206 for(walk = books->next; walk; walk = walk->next) {
207 if(books_compare(walk->data, books->data) != 0) {
208 is_in_other_books = TRUE;
214 /* delete the shallow copy */
218 if(is_in_same_book && include_same_book)
220 if(is_in_other_books && include_other_books)
227 static gint collect_emails(ItemPerson *itemperson, const gchar *book)
233 AddrDupListEntry *entry;
235 /* Process each E-Mail address */
236 nodeM = itemperson->listEMail;
238 ItemEMail *email = nodeM->data;
240 addr = g_strdup(email->address);
242 old_val = g_hash_table_lookup(addr_hash, addr);
244 new_val = deep_copy_hash_val(old_val);
248 entry = g_new0(AddrDupListEntry,1);
249 entry->person = itemperson;
250 entry->book = g_strdup(book);
252 new_val = g_slist_prepend(new_val, entry);
253 g_hash_table_insert(addr_hash, addr, new_val);
255 nodeM = g_list_next(nodeM);
260 static gint books_compare(gconstpointer a, gconstpointer b)
262 const AddrDupListEntry *entry1;
263 const AddrDupListEntry *entry2;
266 return strcmp(entry1->book,entry2->book);
269 static void present_finder_results(void)
271 GtkListStore *email_store;
272 GtkWidget *email_view;
273 GtkWidget *detail_view;
274 GtkWidget *dialog = NULL;
279 GtkTreeSelection *email_select;
280 GtkTreeSelection *detail_select;
281 static GdkGeometry geometry;
283 if(g_hash_table_size(addr_hash) == 0) {
284 alertpanel_notice(_("No duplicate email addresses in the addressbook found"));
288 email_store = gtk_list_store_new(1, G_TYPE_STRING);
289 g_hash_table_foreach(addr_hash,append_to_email_store,email_store);
290 email_view = create_email_view(email_store);
291 email_select = gtk_tree_view_get_selection(GTK_TREE_VIEW(email_view));
292 gtk_tree_selection_set_mode(email_select,GTK_SELECTION_SINGLE);
294 g_signal_connect(email_select, "changed",
295 (GCallback)email_selection_changed, NULL);
297 detail_store = gtk_list_store_new(NUM_COLS, G_TYPE_STRING, G_TYPE_STRING);
298 detail_view = create_detail_view(detail_store);
299 detail_select = gtk_tree_view_get_selection(GTK_TREE_VIEW(detail_view));
300 gtk_tree_selection_set_mode(email_select,GTK_SELECTION_SINGLE);
302 dialog = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "address_dupes_finder");
303 if(!geometry.min_height) {
304 geometry.min_width = 600;
305 geometry.min_height = 400;
307 gtk_window_set_geometry_hints(GTK_WINDOW(dialog), NULL, &geometry,
309 gtk_window_set_title(GTK_WINDOW(dialog), _("Duplicate email addresses"));
312 vbox = gtk_vbox_new(FALSE, 0);
313 gtk_container_add(GTK_CONTAINER(dialog), vbox);
315 hpaned = gtk_hpaned_new();
316 gtk_box_pack_start(GTK_BOX(vbox), hpaned, TRUE, TRUE, 0);
318 gtk_paned_add1(GTK_PANED(hpaned), email_view);
319 gtk_paned_add2(GTK_PANED(hpaned), detail_view);
321 hbox = gtk_hbox_new (FALSE, 0);
322 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
324 close = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
325 gtk_box_pack_end(GTK_BOX(hbox), close, FALSE, FALSE, 0);
327 g_signal_connect(dialog, "destroy",
328 G_CALLBACK(cb_finder_results_dialog_destroy), NULL);
329 g_signal_connect_swapped(close, "clicked",
330 G_CALLBACK(gtk_widget_destroy), dialog);
332 gtk_widget_show_all(dialog);
335 static void cb_finder_results_dialog_destroy(GtkWindow *win, gpointer data)
337 g_hash_table_destroy(addr_hash);
342 static GtkWidget* create_email_view(GtkListStore *store)
345 GtkCellRenderer *renderer;
347 view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
348 renderer = gtk_cell_renderer_text_new();
349 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
355 g_object_unref(store);
359 static GtkWidget* create_detail_view(GtkListStore *store)
362 GtkCellRenderer *renderer;
364 view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
365 renderer = gtk_cell_renderer_text_new();
368 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
375 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
385 static void append_to_email_store(gpointer key,gpointer value,gpointer data)
388 GtkListStore *store = (GtkListStore*) data;
390 gtk_list_store_append(store, &iter);
391 gtk_list_store_set(store, &iter, 0, (gchar*) key, -1);
394 static void email_selection_changed(GtkTreeSelection *selection, gpointer data)
400 if(gtk_tree_selection_get_selected(selection, &model, &iter)) {
404 gtk_tree_model_get(model, &iter, 0, &email, -1);
406 hashval = g_hash_table_lookup(addr_hash, email);
407 gtk_list_store_clear(detail_store);
408 for(walk = hashval; walk; walk = walk->next) {
409 AddrDupListEntry *entry = walk->data;
412 gtk_list_store_append(detail_store, &iter);
413 gtk_list_store_set(detail_store, &iter,
414 COL_BOOK, entry->book,
415 COL_NAME, ADDRITEM_NAME(entry->person),