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
, 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
)
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
;
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
);
200 gtkut_stock_button_set_create( &hbbox
, &cancel_btn
, GTK_STOCK_CANCEL
,
201 &ok_btn
, GTK_STOCK_OK
,
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
,
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
);
250 FolderPathMatch
*nextmatch
= NULL
;
252 list
= parentFolder
->listFolder
;
255 fName
= g_strdup( ADDRITEM_NAME(folder
) );
258 node
= gtk_sctree_insert_node( tree
, parentNode
, NULL
, name
, FOLDER_SPACING
,
259 folderXpm
, folderXpmMask
, folderXpm
, folderXpmMask
,
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
);
273 if ( match
->folder_path
[match
->index
] == NULL
) {
274 /* we've matched all elements */
275 match
->matched
= TRUE
;
277 debug_print("book/folder path matched!\n");
279 /* keep on matching */
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
;
301 ItemFolder
*rootFolder
;
302 AddressBookFile
*abf
;
304 GtkCTree
*tree
= GTK_CTREE( addressbook_foldersel_dlg
.tree_folder
);
306 FolderPathMatch
*nextmatch
;
308 gtk_clist_clear( GTK_CLIST( tree
) );
309 list
= addrindex_get_interface_list( addrIndex
);
311 AddressInterface
*interface
= list
->data
;
312 if ( interface
->type
== ADDR_IF_BOOK
) {
313 nodeDS
= interface
->listSource
;
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
;
326 node
= gtk_sctree_insert_node( tree
, NULL
, NULL
,
327 name
, FOLDER_SPACING
, bookXpm
,
328 bookXpmMask
, bookXpm
, bookXpmMask
,
332 /* try to match subfolders if this book is the right book
333 (and if there's smth to match, and not yet matched) */
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
);
344 if ( match
->folder_path
[match
->index
] == NULL
) {
345 /* we've matched all elements */
346 match
->matched
= TRUE
;
348 debug_print("book path matched!\n");
350 /* keep on matching */
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
;
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
) );
407 gtk_clist_select_row( GTK_CLIST( addressbook_foldersel_dlg
.tree_folder
), 0, 0 );
408 gtk_widget_show(addressbook_foldersel_dlg
.window
);
411 gtk_widget_hide( addressbook_foldersel_dlg
.window
);
413 if ( ! addressbook_foldersel_cancelled
) {
418 if ( addressbook_foldersel_dlg
.fiSelected
) {
419 *book
= addressbook_foldersel_dlg
.fiSelected
->book
;
420 *folder
= addressbook_foldersel_dlg
.fiSelected
->folder
;
425 gtk_clist_clear( GTK_CLIST( addressbook_foldersel_dlg
.tree_folder
) );