2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 2001-2012 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.
26 #include "claws-features.h"
32 #include <glib/gi18n.h>
33 #include <gdk/gdkkeysyms.h>
37 #include "stock_pixmap.h"
38 #include "prefs_common.h"
39 #include "addressadd.h"
42 #include "addrindex.h"
43 #include "manage_window.h"
46 AddressBookFile
*book
;
57 static struct _AddressBookFolderSel_dlg
{
59 GtkWidget
*tree_folder
;
61 GtkWidget
*cancel_btn
;
63 FolderInfo
*fiSelected
;
64 } addressbook_foldersel_dlg
;
66 static GdkPixbuf
*folderXpm
;
67 static GdkPixbuf
*bookXpm
;
69 static gboolean addressbook_foldersel_cancelled
;
71 static FolderInfo
*addressbook_foldersel_create_folderinfo( AddressBookFile
*abf
, ItemFolder
*folder
)
73 FolderInfo
*fi
= g_new0( FolderInfo
, 1 );
79 static void addressbook_foldersel_free_folderinfo( FolderInfo
*fi
) {
85 static gint
addressbook_foldersel_delete_event( GtkWidget
*widget
, GdkEventAny
*event
, gboolean
*cancelled
)
87 addressbook_foldersel_cancelled
= TRUE
;
92 static gboolean
addressbook_foldersel_key_pressed( GtkWidget
*widget
, GdkEventKey
*event
, gboolean
*cancelled
)
94 if ( event
&& event
->keyval
== GDK_KEY_Escape
) {
95 addressbook_foldersel_cancelled
= TRUE
;
101 static void addressbook_foldersel_ok( GtkWidget
*widget
, gboolean
*cancelled
)
103 addressbook_foldersel_cancelled
= FALSE
;
107 static void addressbook_foldersel_cancel( GtkWidget
*widget
, gboolean
*cancelled
)
109 addressbook_foldersel_cancelled
= TRUE
;
113 static void addressbook_foldersel_folder_select( GtkCMCTree
*ctree
, GtkCMCTreeNode
*node
,
114 gint column
, gpointer data
)
116 addressbook_foldersel_dlg
.fiSelected
= gtk_cmctree_node_get_row_data( ctree
, node
);
119 static gboolean
addressbook_foldersel_tree_button( GtkCMCTree
*ctree
, GdkEventButton
*event
, gpointer data
)
123 if ( event
->button
== 1 ) {
124 /* Handle double click */
125 if ( event
->type
== GDK_2BUTTON_PRESS
) {
126 addressbook_foldersel_cancelled
= FALSE
;
134 static void addressbook_foldersel_size_allocate_cb(GtkWidget
*widget
,
135 GtkAllocation
*allocation
)
137 cm_return_if_fail(allocation
!= NULL
);
139 prefs_common
.addressbook_folderselwin_width
= allocation
->width
;
140 prefs_common
.addressbook_folderselwin_height
= allocation
->height
;
143 static void addressbook_foldersel_create( void )
147 GtkWidget
*tree_folder
;
152 GtkWidget
*cancel_btn
;
153 static GdkGeometry geometry
;
156 window
= gtkut_window_new(GTK_WINDOW_TOPLEVEL
, "addressbook_foldersel" );
157 gtk_container_set_border_width( GTK_CONTAINER(window
), 0 );
158 gtk_window_set_title( GTK_WINDOW(window
), _("Select Address Book Folder") );
159 gtk_window_set_position( GTK_WINDOW(window
), GTK_WIN_POS_MOUSE
);
160 g_signal_connect( G_OBJECT(window
), "delete_event",
161 G_CALLBACK(addressbook_foldersel_delete_event
), NULL
);
162 g_signal_connect( G_OBJECT(window
), "key_press_event",
163 G_CALLBACK(addressbook_foldersel_key_pressed
), NULL
);
164 g_signal_connect(G_OBJECT(window
), "size_allocate",
165 G_CALLBACK(addressbook_foldersel_size_allocate_cb
), NULL
);
167 vbox
= gtk_vbox_new(FALSE
, 8);
168 gtk_container_add(GTK_CONTAINER(window
), vbox
);
169 gtk_container_set_border_width( GTK_CONTAINER(vbox
), 8 );
171 /* Address book/folder tree */
172 vlbox
= gtk_vbox_new(FALSE
, 8);
173 gtk_box_pack_start(GTK_BOX(vbox
), vlbox
, TRUE
, TRUE
, 0);
174 gtk_container_set_border_width( GTK_CONTAINER(vlbox
), 8 );
176 tree_win
= gtk_scrolled_window_new( NULL
, NULL
);
177 gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(tree_win
),
178 GTK_POLICY_AUTOMATIC
,
179 GTK_POLICY_AUTOMATIC
);
180 gtk_box_pack_start( GTK_BOX(vlbox
), tree_win
, TRUE
, TRUE
, 0 );
182 titles
[0] = _( "Address Book") ;
184 tree_folder
= gtk_sctree_new_with_titles( 1, 0, titles
);
185 gtk_container_add( GTK_CONTAINER(tree_win
), tree_folder
);
186 gtk_cmclist_column_titles_show( GTK_CMCLIST(tree_folder
) );
187 gtk_cmctree_set_line_style(GTK_CMCTREE(tree_folder
), GTK_CMCTREE_LINES_NONE
);
188 gtk_cmctree_set_expander_style(GTK_CMCTREE(tree_folder
),
189 GTK_CMCTREE_EXPANDER_TRIANGLE
);
190 gtk_sctree_set_stripes(GTK_SCTREE(tree_folder
), prefs_common
.use_stripes_everywhere
);
191 gtk_cmclist_set_selection_mode( GTK_CMCLIST(tree_folder
), GTK_SELECTION_BROWSE
);
192 gtk_cmctree_set_indent( GTK_CMCTREE(tree_folder
), CTREE_INDENT
);
193 gtk_cmclist_set_auto_sort( GTK_CMCLIST(tree_folder
), TRUE
);
196 gtkut_stock_button_set_create( &hbbox
, &cancel_btn
, GTK_STOCK_CANCEL
,
197 &ok_btn
, GTK_STOCK_OK
,
199 gtk_box_pack_end( GTK_BOX(vbox
), hbbox
, FALSE
, FALSE
, 0 );
200 gtk_container_set_border_width( GTK_CONTAINER(hbbox
), 0 );
201 gtk_widget_grab_default( ok_btn
);
203 g_signal_connect( G_OBJECT(ok_btn
), "clicked",
204 G_CALLBACK(addressbook_foldersel_ok
), NULL
);
205 g_signal_connect( G_OBJECT(cancel_btn
), "clicked",
206 G_CALLBACK(addressbook_foldersel_cancel
), NULL
);
207 g_signal_connect( G_OBJECT(tree_folder
), "tree_select_row",
208 G_CALLBACK(addressbook_foldersel_folder_select
), NULL
);
209 g_signal_connect( G_OBJECT(tree_folder
), "button_press_event",
210 G_CALLBACK(addressbook_foldersel_tree_button
), NULL
);
212 if ( !geometry
.min_height
) {
213 geometry
.min_width
= 300;
214 geometry
.min_height
= 350;
217 gtk_window_set_geometry_hints( GTK_WINDOW(window
), NULL
, &geometry
,
219 gtk_widget_set_size_request( window
, prefs_common
.addressbook_folderselwin_width
,
220 prefs_common
.addressbook_folderselwin_height
);
222 gtk_widget_show_all( vbox
);
224 addressbook_foldersel_dlg
.window
= window
;
225 addressbook_foldersel_dlg
.tree_folder
= tree_folder
;
226 addressbook_foldersel_dlg
.ok_btn
= ok_btn
;
227 addressbook_foldersel_dlg
.cancel_btn
= cancel_btn
;
229 gtk_widget_show_all( window
);
231 stock_pixbuf_gdk(STOCK_PIXMAP_BOOK
, &bookXpm
);
232 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_OPEN
, &folderXpm
);
235 static void addressbook_foldersel_load_folder( GtkCMCTreeNode
*parentNode
, ItemFolder
*parentFolder
,
236 FolderInfo
*fiParent
, FolderPathMatch
*match
)
238 GtkCMCTree
*tree
= GTK_CMCTREE( addressbook_foldersel_dlg
.tree_folder
);
243 GtkCMCTreeNode
*node
;
245 FolderPathMatch
*nextmatch
= NULL
;
247 list
= parentFolder
->listFolder
;
250 fName
= g_strdup( ADDRITEM_NAME(folder
) );
253 node
= gtk_cmctree_insert_node( tree
, parentNode
, NULL
, name
, FOLDER_SPACING
,
254 folderXpm
, folderXpm
,
257 /* match folder name, match pointer will be set to NULL if next recursive call
258 doesn't need to match subfolder name */
259 if ( match
!= NULL
&&
260 match
->matched
== FALSE
) {
261 if ( strcmp(match
->folder_path
[match
->index
], folder
->obj
.uid
) == 0 ) {
262 /* folder name matches, prepare next subfolder match */
264 debug_print("matched folder name '%s'\n", fName
);
268 if ( match
->folder_path
[match
->index
] == NULL
) {
269 /* we've matched all elements */
270 match
->matched
= TRUE
;
272 debug_print("book/folder path matched!\n");
274 /* keep on matching */
282 fi
= addressbook_foldersel_create_folderinfo( fiParent
->book
, folder
);
283 gtk_cmctree_node_set_row_data_full( tree
, node
, fi
,
284 ( GDestroyNotify
) addressbook_foldersel_free_folderinfo
);
285 addressbook_foldersel_load_folder( node
, folder
, fi
, nextmatch
);
286 list
= g_list_next( list
);
290 static void addressbook_foldersel_load_data( AddressIndex
*addrIndex
,
291 FolderPathMatch
* match
)
293 AddressDataSource
*ds
;
294 GList
*list
, *nodeDS
;
297 ItemFolder
*rootFolder
;
298 AddressBookFile
*abf
;
300 GtkCMCTree
*tree
= GTK_CMCTREE( addressbook_foldersel_dlg
.tree_folder
);
301 GtkCMCTreeNode
*node
;
302 FolderPathMatch
*nextmatch
;
304 gtk_cmclist_clear( GTK_CMCLIST( tree
) );
305 list
= addrindex_get_interface_list( addrIndex
);
307 AddressInterface
*interface
= list
->data
;
308 if ( interface
->type
== ADDR_IF_BOOK
) {
309 nodeDS
= interface
->listSource
;
312 dsName
= g_strdup( addrindex_ds_get_name( ds
) );
314 /* Read address book */
315 if( ! addrindex_ds_get_read_flag( ds
) ) {
316 addrindex_ds_read_data( ds
);
319 /* Add node for address book */
320 abf
= ds
->rawDataSource
;
322 node
= gtk_cmctree_insert_node( tree
, NULL
, NULL
,
323 name
, FOLDER_SPACING
, bookXpm
,
328 /* try to match subfolders if this book is the right book
329 (and if there's smth to match, and not yet matched) */
331 if ( match
->folder_path
!= NULL
&&
332 match
->matched
== FALSE
&&
333 match
->folder_path
[0] != NULL
&&
334 strcmp(match
->folder_path
[0], abf
->fileName
) == 0 ) {
336 debug_print("matched book name '%s'\n", abf
->fileName
);
340 if ( match
->folder_path
[match
->index
] == NULL
) {
341 /* we've matched all elements */
342 match
->matched
= TRUE
;
344 debug_print("book path matched!\n");
346 /* keep on matching */
351 fi
= addressbook_foldersel_create_folderinfo( abf
, NULL
);
352 gtk_cmctree_node_set_row_data_full( tree
, node
, fi
,
353 ( GDestroyNotify
) addressbook_foldersel_free_folderinfo
);
355 rootFolder
= addrindex_ds_get_root_folder( ds
);
356 addressbook_foldersel_load_folder( node
, rootFolder
, fi
, nextmatch
);
358 nodeDS
= g_list_next( nodeDS
);
361 list
= g_list_next( list
);
365 gboolean
addressbook_foldersel_selection( AddressIndex
*addrIndex
,
366 AddressBookFile
**book
, ItemFolder
**folder
,
369 FolderPathMatch folder_path_match
= { NULL
, FALSE
, 0, NULL
};
370 gboolean retVal
= FALSE
;
371 addressbook_foldersel_cancelled
= FALSE
;
373 if ( ! addressbook_foldersel_dlg
.window
)
374 addressbook_foldersel_create();
375 gtk_widget_grab_focus(addressbook_foldersel_dlg
.ok_btn
);
376 gtk_widget_show(addressbook_foldersel_dlg
.window
);
377 manage_window_set_transient(GTK_WINDOW(addressbook_foldersel_dlg
.window
));
378 gtk_window_set_modal(GTK_WINDOW(addressbook_foldersel_dlg
.window
), TRUE
);
380 addressbook_foldersel_dlg
.fiSelected
= NULL
;
382 /* split the folder path we've received, we'll try to match this path, subpath by
383 subpath against the book/folder structure in order to select the folder that
384 corresponds to what we received */
386 if ( path
!= NULL
) {
387 if ( g_utf8_collate(path
, _("Any")) == 0 || strcasecmp(path
, "Any") ==0 || *path
== '\0' )
388 /* consider "Any" (both translated or untranslated forms) and ""
389 as valid addressbook roots */
390 folder_path_match
.matched
= TRUE
;
392 folder_path_match
.folder_path
= g_strsplit( path
, "/", 256 );
395 addressbook_foldersel_load_data( addrIndex
, &folder_path_match
);
397 if ( folder_path_match
.folder_path
!= NULL
&& folder_path_match
.matched
== FALSE
)
398 g_warning("addressbook_foldersel_load_data: couldn't match book/folder path '%s'", path
);
400 g_strfreev( folder_path_match
.folder_path
);
402 if ( folder_path_match
.node
!= NULL
)
403 gtk_cmctree_select( GTK_CMCTREE( addressbook_foldersel_dlg
.tree_folder
),
404 GTK_CMCTREE_NODE( folder_path_match
.node
) );
406 gtk_cmclist_select_row( GTK_CMCLIST( addressbook_foldersel_dlg
.tree_folder
), 0, 0 );
407 gtk_widget_show(addressbook_foldersel_dlg
.window
);
410 gtk_widget_hide( addressbook_foldersel_dlg
.window
);
411 gtk_window_set_modal(GTK_WINDOW(addressbook_foldersel_dlg
.window
), FALSE
);
412 if ( ! addressbook_foldersel_cancelled
) {
417 if ( addressbook_foldersel_dlg
.fiSelected
) {
418 *book
= addressbook_foldersel_dlg
.fiSelected
->book
;
419 *folder
= addressbook_foldersel_dlg
.fiSelected
->folder
;
424 gtk_cmclist_clear( GTK_CMCLIST( addressbook_foldersel_dlg
.tree_folder
) );