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.
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>
42 #include "stock_pixmap.h"
43 #include "prefs_common.h"
44 #include "addressadd.h"
47 #include "addrindex.h"
48 #include "manage_window.h"
51 AddressBookFile *book;
62 static struct _AddressBookFolderSel_dlg {
64 GtkWidget *tree_folder;
66 GtkWidget *cancel_btn;
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 );
86 static void addressbook_foldersel_free_folderinfo( FolderInfo *fi ) {
92 static gint addressbook_foldersel_delete_event( GtkWidget *widget, GdkEventAny *event, gboolean *cancelled )
94 addressbook_foldersel_cancelled = 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;
108 static void addressbook_foldersel_ok( GtkWidget *widget, gboolean *cancelled )
110 addressbook_foldersel_cancelled = FALSE;
114 static void addressbook_foldersel_cancel( GtkWidget *widget, gboolean *cancelled )
116 addressbook_foldersel_cancelled = TRUE;
120 static void addressbook_foldersel_folder_select( GtkCTree *ctree, GtkCTreeNode *node,
121 gint column, gpointer data )
123 addressbook_foldersel_dlg.fiSelected = gtk_ctree_node_get_row_data( ctree, node );
126 static gboolean addressbook_foldersel_tree_button( GtkCTree *ctree, GdkEventButton *event, gpointer data )
130 if ( event->button == 1 ) {
131 /* Handle double click */
132 if ( event->type == GDK_2BUTTON_PRESS ) {
133 addressbook_foldersel_cancelled = 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 )
154 GtkWidget *tree_folder;
159 GtkWidget *cancel_btn;
160 static GdkGeometry geometry;
163 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "addressbook_foldersel" );
164 gtk_container_set_border_width( GTK_CONTAINER(window), 0 );
165 gtk_window_set_title( GTK_WINDOW(window), _("Select Address Book Folder") );
166 gtk_window_set_position( GTK_WINDOW(window), GTK_WIN_POS_MOUSE );
167 gtk_window_set_modal( GTK_WINDOW(window), TRUE );
168 g_signal_connect( G_OBJECT(window), "delete_event",
169 G_CALLBACK(addressbook_foldersel_delete_event), NULL );
170 g_signal_connect( G_OBJECT(window), "key_press_event",
171 G_CALLBACK(addressbook_foldersel_key_pressed), NULL );
172 g_signal_connect(G_OBJECT(window), "size_allocate",
173 G_CALLBACK(addressbook_foldersel_size_allocate_cb), NULL);
175 vbox = gtk_vbox_new(FALSE, 8);
176 gtk_container_add(GTK_CONTAINER(window), vbox);
177 gtk_container_set_border_width( GTK_CONTAINER(vbox), 8 );
179 /* Address book/folder tree */
180 vlbox = gtk_vbox_new(FALSE, 8);
181 gtk_box_pack_start(GTK_BOX(vbox), vlbox, TRUE, TRUE, 0);
182 gtk_container_set_border_width( GTK_CONTAINER(vlbox), 8 );
184 tree_win = gtk_scrolled_window_new( NULL, NULL );
185 gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(tree_win),
186 GTK_POLICY_AUTOMATIC,
187 GTK_POLICY_AUTOMATIC );
188 gtk_box_pack_start( GTK_BOX(vlbox), tree_win, TRUE, TRUE, 0 );
190 titles[0] = _( "Address Book") ;
192 tree_folder = gtk_sctree_new_with_titles( 1, 0, titles );
193 gtk_container_add( GTK_CONTAINER(tree_win), tree_folder );
194 gtk_clist_column_titles_show( GTK_CLIST(tree_folder) );
195 if (prefs_common.enable_dotted_lines) {
196 gtk_ctree_set_line_style(GTK_CTREE(tree_folder), GTK_CTREE_LINES_DOTTED);
197 gtk_ctree_set_expander_style(GTK_CTREE(tree_folder),
198 GTK_CTREE_EXPANDER_SQUARE);
200 gtk_ctree_set_line_style(GTK_CTREE(tree_folder), GTK_CTREE_LINES_NONE);
201 gtk_ctree_set_expander_style(GTK_CTREE(tree_folder),
202 GTK_CTREE_EXPANDER_TRIANGLE);
204 gtk_sctree_set_stripes(GTK_SCTREE(tree_folder), prefs_common.use_stripes_everywhere);
205 gtk_clist_set_selection_mode( GTK_CLIST(tree_folder), GTK_SELECTION_BROWSE );
206 gtk_ctree_set_indent( GTK_CTREE(tree_folder), CTREE_INDENT );
207 gtk_clist_set_auto_sort( GTK_CLIST(tree_folder), TRUE );
210 gtkut_stock_button_set_create( &hbbox, &cancel_btn, GTK_STOCK_CANCEL,
211 &ok_btn, GTK_STOCK_OK,
213 gtk_box_pack_end( GTK_BOX(vbox), hbbox, FALSE, FALSE, 0 );
214 gtk_container_set_border_width( GTK_CONTAINER(hbbox), 0 );
215 gtk_widget_grab_default( ok_btn );
217 g_signal_connect( G_OBJECT(ok_btn), "clicked",
218 G_CALLBACK(addressbook_foldersel_ok), NULL );
219 g_signal_connect( G_OBJECT(cancel_btn), "clicked",
220 G_CALLBACK(addressbook_foldersel_cancel), NULL );
221 g_signal_connect( G_OBJECT(tree_folder), "tree_select_row",
222 G_CALLBACK(addressbook_foldersel_folder_select), NULL );
223 g_signal_connect( G_OBJECT(tree_folder), "button_press_event",
224 G_CALLBACK(addressbook_foldersel_tree_button), NULL );
226 if ( !geometry.min_height ) {
227 geometry.min_width = 300;
228 geometry.min_height = 350;
231 gtk_window_set_geometry_hints( GTK_WINDOW(window), NULL, &geometry,
233 gtk_widget_set_size_request( window, prefs_common.addressbook_folderselwin_width,
234 prefs_common.addressbook_folderselwin_height );
236 gtk_widget_show_all( vbox );
238 addressbook_foldersel_dlg.window = window;
239 addressbook_foldersel_dlg.tree_folder = tree_folder;
240 addressbook_foldersel_dlg.ok_btn = ok_btn;
241 addressbook_foldersel_dlg.cancel_btn = cancel_btn;
243 gtk_widget_show_all( window );
245 stock_pixmap_gdk( window, STOCK_PIXMAP_BOOK, &bookXpm, &bookXpmMask );
246 stock_pixmap_gdk( window, STOCK_PIXMAP_DIR_OPEN,
247 &folderXpm, &folderXpmMask );
250 static void addressbook_foldersel_load_folder( GtkCTreeNode *parentNode, ItemFolder *parentFolder,
251 FolderInfo *fiParent, FolderPathMatch *match )
253 GtkCTree *tree = GTK_CTREE( addressbook_foldersel_dlg.tree_folder );
260 FolderPathMatch *nextmatch = NULL;
262 list = parentFolder->listFolder;
265 fName = g_strdup( ADDRITEM_NAME(folder) );
268 node = gtk_ctree_insert_node( tree, parentNode, NULL, name, FOLDER_SPACING,
269 folderXpm, folderXpmMask, folderXpm, folderXpmMask,
272 /* match folder name, match pointer will be set to NULL if next recursive call
273 doesn't need to match subfolder name */
274 if ( match != NULL &&
275 match->matched == FALSE ) {
276 if ( strcmp(match->folder_path[match->index], folder->obj.uid) == 0 ) {
277 /* folder name matches, prepare next subfolder match */
279 debug_print("matched folder name '%s'\n", fName);
283 if ( match->folder_path[match->index] == NULL ) {
284 /* we've matched all elements */
285 match->matched = TRUE;
287 debug_print("book/folder path matched!\n");
289 /* keep on matching */
297 fi = addressbook_foldersel_create_folderinfo( fiParent->book, folder );
298 gtk_ctree_node_set_row_data_full( tree, node, fi,
299 ( GtkDestroyNotify ) addressbook_foldersel_free_folderinfo );
300 addressbook_foldersel_load_folder( node, folder, fi, nextmatch );
301 list = g_list_next( list );
305 static void addressbook_foldersel_load_data( AddressIndex *addrIndex,
306 FolderPathMatch* match )
308 AddressDataSource *ds;
309 GList *list, *nodeDS;
312 ItemFolder *rootFolder;
313 AddressBookFile *abf;
315 GtkCTree *tree = GTK_CTREE( addressbook_foldersel_dlg.tree_folder );
317 FolderPathMatch *nextmatch;
319 gtk_clist_clear( GTK_CLIST( tree ) );
320 list = addrindex_get_interface_list( addrIndex );
322 AddressInterface *interface = list->data;
323 if ( interface->type == ADDR_IF_BOOK ) {
324 nodeDS = interface->listSource;
327 dsName = g_strdup( addrindex_ds_get_name( ds ) );
329 /* Read address book */
330 if( ! addrindex_ds_get_read_flag( ds ) ) {
331 addrindex_ds_read_data( ds );
334 /* Add node for address book */
335 abf = ds->rawDataSource;
337 node = gtk_ctree_insert_node( tree, NULL, NULL,
338 name, FOLDER_SPACING, bookXpm,
339 bookXpmMask, bookXpm, bookXpmMask,
343 /* try to match subfolders if this book is the right book
344 (and if there's smth to match, and not yet matched) */
346 if ( match->folder_path != NULL &&
347 match->matched == FALSE &&
348 match->folder_path[0] != NULL &&
349 strcmp(match->folder_path[0], abf->fileName) == 0 ) {
351 debug_print("matched book name '%s'\n", abf->fileName);
355 if ( match->folder_path[match->index] == NULL ) {
356 /* we've matched all elements */
357 match->matched = TRUE;
359 debug_print("book path matched!\n");
361 /* keep on matching */
366 fi = addressbook_foldersel_create_folderinfo( abf, NULL );
367 gtk_ctree_node_set_row_data_full( tree, node, fi,
368 ( GtkDestroyNotify ) addressbook_foldersel_free_folderinfo );
370 rootFolder = addrindex_ds_get_root_folder( ds );
371 addressbook_foldersel_load_folder( node, rootFolder, fi, nextmatch );
373 nodeDS = g_list_next( nodeDS );
376 list = g_list_next( list );
380 gboolean addressbook_foldersel_selection( AddressIndex *addrIndex,
381 AddressBookFile **book, ItemFolder **folder,
384 FolderPathMatch folder_path_match = { NULL, FALSE, 0, NULL };
385 gboolean retVal = FALSE;
386 addressbook_foldersel_cancelled = FALSE;
388 if ( ! addressbook_foldersel_dlg.window )
389 addressbook_foldersel_create();
390 gtk_widget_grab_focus(addressbook_foldersel_dlg.ok_btn);
391 gtk_widget_show(addressbook_foldersel_dlg.window);
392 manage_window_set_transient(GTK_WINDOW(addressbook_foldersel_dlg.window));
394 addressbook_foldersel_dlg.fiSelected = NULL;
396 /* split the folder path we've received, we'll try to match this path, subpath by
397 subpath against the book/folder structure in order to select the folder that
398 corresponds to what we received */
400 if ( path != NULL ) {
401 if ( g_utf8_collate(path, _("Any")) == 0 || strcasecmp(path, "Any") ==0 || *path == '\0' )
402 /* consider "Any" (both translated or untranslated forms) and ""
403 as valid addressbook roots */
404 folder_path_match.matched = TRUE;
406 folder_path_match.folder_path = g_strsplit( path, "/", 256 );
409 addressbook_foldersel_load_data( addrIndex, &folder_path_match );
411 if ( folder_path_match.folder_path != NULL && folder_path_match.matched == FALSE)
412 g_warning("addressbook_foldersel_load_data: couldn't match book/folder path '%s'\n", path);
414 g_strfreev( folder_path_match.folder_path );
416 if ( folder_path_match.node != NULL)
417 gtk_ctree_select( GTK_CTREE( addressbook_foldersel_dlg.tree_folder ),
418 GTK_CTREE_NODE( folder_path_match.node ) );
420 gtk_clist_select_row( GTK_CLIST( addressbook_foldersel_dlg.tree_folder ), 0, 0 );
421 gtk_widget_show(addressbook_foldersel_dlg.window);
424 gtk_widget_hide( addressbook_foldersel_dlg.window );
426 if ( ! addressbook_foldersel_cancelled ) {
431 if ( addressbook_foldersel_dlg.fiSelected ) {
432 *book = addressbook_foldersel_dlg.fiSelected->book;
433 *folder = addressbook_foldersel_dlg.fiSelected->folder;
438 gtk_clist_clear( GTK_CLIST( addressbook_foldersel_dlg.tree_folder ) );