2007-08-27 [colin] 2.10.0cvs179
[claws.git] / src / addressbook_foldersel.c
blobce27f531ba087ddc5af679965bf402a5ebc21cda
1 /*
2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 2001-2007 Match Grun 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/>.
21 * Add address to address book dialog.
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
28 #include "defs.h"
30 #include <glib.h>
31 #include <glib/gi18n.h>
32 #include <gdk/gdkkeysyms.h>
33 #include <gtk/gtkwindow.h>
34 #include <gtk/gtksignal.h>
35 #include <gtk/gtkhbox.h>
36 #include <gtk/gtklabel.h>
37 #include <gtk/gtkentry.h>
38 #include <gtk/gtkhbbox.h>
39 #include <gtk/gtkbutton.h>
41 #include "gtkutils.h"
42 #include "stock_pixmap.h"
43 #include "prefs_common.h"
44 #include "addressadd.h"
45 #include "addritem.h"
46 #include "addrbook.h"
47 #include "addrindex.h"
48 #include "manage_window.h"
50 typedef struct {
51 AddressBookFile *book;
52 ItemFolder *folder;
53 } FolderInfo;
55 typedef struct {
56 gchar **folder_path;
57 gboolean matched;
58 gint index;
59 GtkCTreeNode *node;
60 } FolderPathMatch;
62 static struct _AddressBookFolderSel_dlg {
63 GtkWidget *window;
64 GtkWidget *tree_folder;
65 GtkWidget *ok_btn;
66 GtkWidget *cancel_btn;
67 gint status_cid;
68 FolderInfo *fiSelected;
69 } addressbook_foldersel_dlg;
71 static GdkPixmap *folderXpm;
72 static GdkBitmap *folderXpmMask;
73 static GdkPixmap *bookXpm;
74 static GdkBitmap *bookXpmMask;
76 static gboolean addressbook_foldersel_cancelled;
78 static FolderInfo *addressbook_foldersel_create_folderinfo( AddressBookFile *abf, ItemFolder *folder )
80 FolderInfo *fi = g_new0( FolderInfo, 1 );
81 fi->book = abf;
82 fi->folder = folder;
83 return fi;
86 static void addressbook_foldersel_free_folderinfo( FolderInfo *fi ) {
87 fi->book = NULL;
88 fi->folder = NULL;
89 g_free( fi );
92 static gint addressbook_foldersel_delete_event( GtkWidget *widget, GdkEventAny *event, gboolean *cancelled )
94 addressbook_foldersel_cancelled = TRUE;
95 gtk_main_quit();
96 return TRUE;
99 static gboolean addressbook_foldersel_key_pressed( GtkWidget *widget, GdkEventKey *event, gboolean *cancelled )
101 if ( event && event->keyval == GDK_Escape ) {
102 addressbook_foldersel_cancelled = TRUE;
103 gtk_main_quit();
105 return FALSE;
108 static void addressbook_foldersel_ok( GtkWidget *widget, gboolean *cancelled )
110 addressbook_foldersel_cancelled = FALSE;
111 gtk_main_quit();
114 static void addressbook_foldersel_cancel( GtkWidget *widget, gboolean *cancelled )
116 addressbook_foldersel_cancelled = TRUE;
117 gtk_main_quit();
120 static void addressbook_foldersel_folder_select( GtkCTree *ctree, gint row, gint column,
121 GdkEvent *event, gpointer data )
123 addressbook_foldersel_dlg.fiSelected = gtk_clist_get_row_data( GTK_CLIST(ctree), row );
126 static gboolean addressbook_foldersel_tree_button( GtkCTree *ctree, GdkEventButton *event, gpointer data )
128 if ( ! event )
129 return FALSE;
130 if ( event->button == 1 ) {
131 /* Handle double click */
132 if ( event->type == GDK_2BUTTON_PRESS ) {
133 addressbook_foldersel_cancelled = FALSE;
134 gtk_main_quit();
138 return FALSE;
141 static void addressbook_foldersel_size_allocate_cb(GtkWidget *widget,
142 GtkAllocation *allocation)
144 g_return_if_fail(allocation != NULL);
146 prefs_common.addressbook_folderselwin_width = allocation->width;
147 prefs_common.addressbook_folderselwin_height = allocation->height;
150 static void addressbook_foldersel_create( void )
152 GtkWidget *window;
153 GtkWidget *vbox;
154 GtkWidget *tree_folder;
155 GtkWidget *vlbox;
156 GtkWidget *tree_win;
157 GtkWidget *hbbox;
158 GtkWidget *ok_btn;
159 GtkWidget *cancel_btn;
160 static GdkGeometry geometry;
162 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "addressbook_foldersel" );
163 gtk_container_set_border_width( GTK_CONTAINER(window), 0 );
164 gtk_window_set_title( GTK_WINDOW(window), _("Select Address Book Folder") );
165 gtk_window_set_position( GTK_WINDOW(window), GTK_WIN_POS_MOUSE );
166 gtk_window_set_modal( GTK_WINDOW(window), TRUE );
167 g_signal_connect( G_OBJECT(window), "delete_event",
168 G_CALLBACK(addressbook_foldersel_delete_event), NULL );
169 g_signal_connect( G_OBJECT(window), "key_press_event",
170 G_CALLBACK(addressbook_foldersel_key_pressed), NULL );
171 g_signal_connect(G_OBJECT(window), "size_allocate",
172 G_CALLBACK(addressbook_foldersel_size_allocate_cb), NULL);
174 vbox = gtk_vbox_new(FALSE, 8);
175 gtk_container_add(GTK_CONTAINER(window), vbox);
176 gtk_container_set_border_width( GTK_CONTAINER(vbox), 8 );
178 /* Address book/folder tree */
179 vlbox = gtk_vbox_new(FALSE, 8);
180 gtk_box_pack_start(GTK_BOX(vbox), vlbox, TRUE, TRUE, 0);
181 gtk_container_set_border_width( GTK_CONTAINER(vlbox), 8 );
183 tree_win = gtk_scrolled_window_new( NULL, NULL );
184 gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(tree_win),
185 GTK_POLICY_AUTOMATIC,
186 GTK_POLICY_AUTOMATIC );
187 gtk_box_pack_start( GTK_BOX(vlbox), tree_win, TRUE, TRUE, 0 );
189 tree_folder = gtk_ctree_new( 1, 0 );
190 gtk_container_add( GTK_CONTAINER(tree_win), tree_folder );
191 gtk_clist_column_titles_show( GTK_CLIST(tree_folder) );
192 gtk_clist_set_column_title( GTK_CLIST(tree_folder), 0, _( "Address Book" ) );
193 gtk_ctree_set_line_style( GTK_CTREE(tree_folder), GTK_CTREE_LINES_DOTTED );
194 gtk_clist_set_selection_mode( GTK_CLIST(tree_folder), GTK_SELECTION_BROWSE );
195 gtk_ctree_set_expander_style( GTK_CTREE(tree_folder), GTK_CTREE_EXPANDER_SQUARE );
196 gtk_ctree_set_indent( GTK_CTREE(tree_folder), CTREE_INDENT );
197 gtk_clist_set_auto_sort( GTK_CLIST(tree_folder), TRUE );
199 /* Button panel */
200 gtkut_stock_button_set_create( &hbbox, &cancel_btn, GTK_STOCK_CANCEL,
201 &ok_btn, GTK_STOCK_OK,
202 NULL, NULL );
203 gtk_box_pack_end( GTK_BOX(vbox), hbbox, FALSE, FALSE, 0 );
204 gtk_container_set_border_width( GTK_CONTAINER(hbbox), 0 );
205 gtk_widget_grab_default( ok_btn );
207 g_signal_connect( G_OBJECT(ok_btn), "clicked",
208 G_CALLBACK(addressbook_foldersel_ok), NULL );
209 g_signal_connect( G_OBJECT(cancel_btn), "clicked",
210 G_CALLBACK(addressbook_foldersel_cancel), NULL );
211 g_signal_connect( G_OBJECT(tree_folder), "select_row",
212 G_CALLBACK(addressbook_foldersel_folder_select), NULL );
213 g_signal_connect( G_OBJECT(tree_folder), "button_press_event",
214 G_CALLBACK(addressbook_foldersel_tree_button), NULL );
216 if ( !geometry.min_height ) {
217 geometry.min_width = 300;
218 geometry.min_height = 350;
221 gtk_window_set_geometry_hints( GTK_WINDOW(window), NULL, &geometry,
222 GDK_HINT_MIN_SIZE );
223 gtk_widget_set_size_request( window, prefs_common.addressbook_folderselwin_width,
224 prefs_common.addressbook_folderselwin_height );
226 gtk_widget_show_all( vbox );
228 addressbook_foldersel_dlg.window = window;
229 addressbook_foldersel_dlg.tree_folder = tree_folder;
230 addressbook_foldersel_dlg.ok_btn = ok_btn;
231 addressbook_foldersel_dlg.cancel_btn = cancel_btn;
233 gtk_widget_show_all( window );
235 stock_pixmap_gdk( window, STOCK_PIXMAP_BOOK, &bookXpm, &bookXpmMask );
236 stock_pixmap_gdk( window, STOCK_PIXMAP_DIR_OPEN,
237 &folderXpm, &folderXpmMask );
240 static void addressbook_foldersel_load_folder( GtkCTreeNode *parentNode, ItemFolder *parentFolder,
241 FolderInfo *fiParent, FolderPathMatch *match )
243 GtkCTree *tree = GTK_CTREE( addressbook_foldersel_dlg.tree_folder );
244 GList *list;
245 ItemFolder *folder;
246 gchar *fName;
247 gchar **name;
248 GtkCTreeNode *node;
249 FolderInfo *fi;
250 FolderPathMatch *nextmatch = NULL;
252 list = parentFolder->listFolder;
253 while ( list ) {
254 folder = list->data;
255 fName = g_strdup( ADDRITEM_NAME(folder) );
257 name = &fName;
258 node = gtk_sctree_insert_node( tree, parentNode, NULL, name, FOLDER_SPACING,
259 folderXpm, folderXpmMask, folderXpm, folderXpmMask,
260 FALSE, TRUE );
262 /* match folder name, match pointer will be set to NULL if next recursive call
263 doesn't need to match subfolder name */
264 if ( match != NULL &&
265 match->matched == FALSE ) {
266 if ( strcmp(match->folder_path[match->index], folder->obj.uid) == 0 ) {
267 /* folder name matches, prepare next subfolder match */
269 debug_print("matched folder name '%s'\n", fName);
271 match->index++;
273 if ( match->folder_path[match->index] == NULL ) {
274 /* we've matched all elements */
275 match->matched = TRUE;
276 match->node = node;
277 debug_print("book/folder path matched!\n");
278 } else {
279 /* keep on matching */
280 nextmatch = match;
285 g_free( fName );
287 fi = addressbook_foldersel_create_folderinfo( fiParent->book, folder );
288 gtk_ctree_node_set_row_data_full( tree, node, fi,
289 ( GtkDestroyNotify ) addressbook_foldersel_free_folderinfo );
290 addressbook_foldersel_load_folder( node, folder, fi, nextmatch );
291 list = g_list_next( list );
295 static void addressbook_foldersel_load_data( AddressIndex *addrIndex, gchar *path, FolderPathMatch* match )
297 AddressDataSource *ds;
298 GList *list, *nodeDS;
299 gchar **name;
300 gchar *dsName;
301 ItemFolder *rootFolder;
302 AddressBookFile *abf;
303 FolderInfo *fi;
304 GtkCTree *tree = GTK_CTREE( addressbook_foldersel_dlg.tree_folder );
305 GtkCTreeNode *node;
306 FolderPathMatch *nextmatch;
308 gtk_clist_clear( GTK_CLIST( tree ) );
309 list = addrindex_get_interface_list( addrIndex );
310 while ( list ) {
311 AddressInterface *interface = list->data;
312 if ( interface->type == ADDR_IF_BOOK ) {
313 nodeDS = interface->listSource;
314 while ( nodeDS ) {
315 ds = nodeDS->data;
316 dsName = g_strdup( addrindex_ds_get_name( ds ) );
318 /* Read address book */
319 if( ! addrindex_ds_get_read_flag( ds ) ) {
320 addrindex_ds_read_data( ds );
323 /* Add node for address book */
324 abf = ds->rawDataSource;
325 name = &dsName;
326 node = gtk_sctree_insert_node( tree, NULL, NULL,
327 name, FOLDER_SPACING, bookXpm,
328 bookXpmMask, bookXpm, bookXpmMask,
329 FALSE, TRUE );
330 g_free( dsName );
332 /* try to match subfolders if this book is the right book
333 (and if there's smth to match, and not yet matched) */
334 nextmatch = NULL;
335 if ( match->folder_path != NULL &&
336 match->matched == FALSE &&
337 match->folder_path[0] != NULL &&
338 strcmp(match->folder_path[0], abf->fileName) == 0 ) {
340 debug_print("matched book name '%s'\n", abf->fileName);
342 match->index = 1;
344 if ( match->folder_path[match->index] == NULL ) {
345 /* we've matched all elements */
346 match->matched = TRUE;
347 match->node = node;
348 debug_print("book path matched!\n");
349 } else {
350 /* keep on matching */
351 nextmatch = match;
355 fi = addressbook_foldersel_create_folderinfo( abf, NULL );
356 gtk_ctree_node_set_row_data_full( tree, node, fi,
357 ( GtkDestroyNotify ) addressbook_foldersel_free_folderinfo );
359 rootFolder = addrindex_ds_get_root_folder( ds );
360 addressbook_foldersel_load_folder( node, rootFolder, fi, nextmatch );
362 nodeDS = g_list_next( nodeDS );
365 list = g_list_next( list );
369 gboolean addressbook_foldersel_selection( AddressIndex *addrIndex,
370 AddressBookFile **book, ItemFolder **folder, gchar* path)
372 FolderPathMatch folder_path_match = { NULL, FALSE, 0, NULL };
373 gboolean retVal = FALSE;
374 addressbook_foldersel_cancelled = FALSE;
376 if ( ! addressbook_foldersel_dlg.window )
377 addressbook_foldersel_create();
378 gtk_widget_grab_focus(addressbook_foldersel_dlg.ok_btn);
379 gtk_widget_show(addressbook_foldersel_dlg.window);
380 manage_window_set_transient(GTK_WINDOW(addressbook_foldersel_dlg.window));
382 addressbook_foldersel_dlg.fiSelected = NULL;
384 /* split the folder path we've received, we'll try to match this path, subpath by
385 subpath against the book/folder structure in order to select the folder that
386 corresponds to what we received */
388 if ( path != NULL ) {
389 if ( strcasecmp(path, _("Any")) == 0 || *path == '\0' )
390 /* consider "Any" and "" as valid addressbook root */
391 folder_path_match.matched = TRUE;
392 else
393 folder_path_match.folder_path = g_strsplit( path, "/", 256 );
396 addressbook_foldersel_load_data( addrIndex, path, &folder_path_match );
398 if ( folder_path_match.folder_path != NULL && folder_path_match.matched == FALSE)
399 g_warning("addressbook_foldersel_load_data: couldn't match book/folder path '%s'\n", path);
401 g_strfreev( folder_path_match.folder_path );
403 if ( folder_path_match.node != NULL)
404 gtk_ctree_select( GTK_CTREE( addressbook_foldersel_dlg.tree_folder ),
405 GTK_CTREE_NODE( folder_path_match.node ) );
406 else
407 gtk_clist_select_row( GTK_CLIST( addressbook_foldersel_dlg.tree_folder ), 0, 0 );
408 gtk_widget_show(addressbook_foldersel_dlg.window);
410 gtk_main();
411 gtk_widget_hide( addressbook_foldersel_dlg.window );
413 if ( ! addressbook_foldersel_cancelled ) {
415 *book = NULL;
416 *folder = NULL;
418 if ( addressbook_foldersel_dlg.fiSelected ) {
419 *book = addressbook_foldersel_dlg.fiSelected->book;
420 *folder = addressbook_foldersel_dlg.fiSelected->folder;
421 retVal = TRUE;
425 gtk_clist_clear( GTK_CLIST( addressbook_foldersel_dlg.tree_folder ) );
427 return retVal;
431 * End of Source.