2 * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 2007-2013 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/>.
22 #include "claws-features.h"
28 #include <glib/gi18n.h>
30 #include <gdk/gdkkeysyms.h>
35 #include "addrcustomattr.h"
36 #include "manage_window.h"
37 #include "prefs_common.h"
38 #include "alertpanel.h"
40 #include "editaddress.h"
42 static GtkActionGroup
*custom_attr_popup_action
= NULL
;
43 static GtkWidget
*custom_attr_popup_menu
= NULL
;
45 static struct CustomAttrWindow
53 GtkWidget
*cancel_btn
;
64 static gchar
*default_addressbook_attributes_table
[] = {
77 static gboolean dirty
= FALSE
;
79 static void custom_attr_window_create(void);
80 static void custom_attr_selected_attr_edited(GtkCellRendererText
*widget
,
81 gchar
*arg1
, gchar
*arg2
,
82 GtkWidget
*list_view
);
83 static void custom_attr_window_load_list(GList
*list
);
84 static void custom_attr_window_save_list (void);
85 static GList
*custom_attr_default_list(void);
87 void addressbook_custom_attr_edit()
89 if (!custom_attr_window
.window
)
90 custom_attr_window_create();
92 manage_window_set_transient(GTK_WINDOW(custom_attr_window
.window
));
93 gtk_widget_grab_focus(custom_attr_window
.ok_btn
);
95 custom_attr_window_load_list(prefs_common
.addressbook_custom_attributes
);
97 gtk_widget_show(custom_attr_window
.window
);
98 gtk_widget_grab_focus(custom_attr_window
.attr_list
);
99 gtk_window_set_modal(GTK_WINDOW(custom_attr_window
.window
), TRUE
);
102 static gint
custom_attr_cmp_func (GtkTreeModel
*model
, GtkTreeIter
*a
,
103 GtkTreeIter
*b
, gpointer userdata
)
105 gchar
*name1
, *name2
;
107 gtk_tree_model_get(model
, a
, CUSTOM_ATTR_NAME
, &name1
, -1);
108 gtk_tree_model_get(model
, b
, CUSTOM_ATTR_NAME
, &name2
, -1);
111 return name2
== NULL
? 0:1;
116 return g_utf8_collate(name1
, name2
);
119 static GtkListStore
* custom_attr_window_create_data_store(void)
121 GtkListStore
*store
= gtk_list_store_new(N_CUSTOM_ATTR
,
124 GtkTreeSortable
*sortable
= GTK_TREE_SORTABLE(store
);
126 gtk_tree_sortable_set_sort_func(sortable
, 0, custom_attr_cmp_func
,
132 static void custom_attr_window_create_list_view_columns(GtkWidget
*list_view
)
134 GtkTreeViewColumn
*column
;
135 GtkCellRenderer
*renderer
;
137 renderer
= gtk_cell_renderer_text_new();
138 g_object_set(G_OBJECT(renderer
), "editable", TRUE
, NULL
);
140 column
= gtk_tree_view_column_new_with_attributes
141 (_("Attribute name"),
143 "text", CUSTOM_ATTR_NAME
,
145 gtk_tree_view_append_column(GTK_TREE_VIEW(list_view
), column
);
146 gtk_tree_view_column_set_resizable(column
, TRUE
);
147 gtk_tree_view_set_search_column(GTK_TREE_VIEW(list_view
),
149 g_signal_connect(G_OBJECT(renderer
), "edited",
150 G_CALLBACK(custom_attr_selected_attr_edited
),
154 static void custom_attr_window_list_view_clear_list(GtkWidget
*list_view
, gboolean warn
)
156 if (!warn
|| alertpanel(_("Delete all attribute names"),
157 _("Do you really want to delete all attribute names?"),
158 GTK_STOCK_CANCEL
, GTK_STOCK_DELETE
, NULL
) == G_ALERTALTERNATE
) {
159 GtkListStore
*list_store
= GTK_LIST_STORE(gtk_tree_view_get_model
160 (GTK_TREE_VIEW(list_view
)));
161 gtk_list_store_clear(list_store
);
166 static void custom_attr_popup_clear_list (void *obj
, void *data
)
168 custom_attr_window_list_view_clear_list(custom_attr_window
.attr_list
, TRUE
);
171 static void custom_attr_popup_delete (void *obj
, void *data
)
176 if (!gtk_tree_selection_get_selected(gtk_tree_view_get_selection
177 (GTK_TREE_VIEW(custom_attr_window
.attr_list
)),
181 if (alertpanel(_("Delete attribute name"),
182 _("Do you really want to delete this attribute name?"),
183 GTK_STOCK_CANCEL
, GTK_STOCK_DELETE
, NULL
) == G_ALERTALTERNATE
) {
184 gtk_list_store_remove(GTK_LIST_STORE(model
), &sel
);
189 static void custom_attr_popup_factory_defaults (void *obj
, void *data
)
191 if (alertpanel(_("Reset to default"),
192 _("Do you really want to replace all attribute names\nwith the default set?"),
193 GTK_STOCK_NO
, GTK_STOCK_YES
, NULL
) == G_ALERTALTERNATE
) {
194 GList
*tmp
= custom_attr_default_list();
195 custom_attr_window_load_list(tmp
);
209 static GtkActionEntry custom_attr_popup_entries
[] =
211 {"CustomAttrPopup", NULL
, "CustomAttrPopup" },
212 {"CustomAttrPopup/Delete", NULL
, N_("_Delete"), NULL
, NULL
, G_CALLBACK(custom_attr_popup_delete
) },
213 {"CustomAttrPopup/DeleteAll", NULL
, N_("Delete _all"), NULL
, NULL
, G_CALLBACK(custom_attr_popup_clear_list
) },
214 {"CustomAttrPopup/Reset", NULL
, N_("_Reset to default"), NULL
, NULL
, G_CALLBACK(custom_attr_popup_factory_defaults
) },
217 static gint
custom_attr_list_btn_pressed(GtkWidget
*widget
, GdkEventButton
*event
,
218 GtkTreeView
*list_view
)
220 if (event
&& event
->button
== 3) {
221 GtkTreeModel
*model
= gtk_tree_view_get_model(list_view
);
225 if (!custom_attr_popup_menu
) {
226 custom_attr_popup_action
= cm_menu_create_action_group("CustomAttrPopup", custom_attr_popup_entries
,
227 G_N_ELEMENTS(custom_attr_popup_entries
), (gpointer
)list_view
);
228 MENUITEM_ADDUI("/Menus", "CustomAttrPopup", "CustomAttrPopup", GTK_UI_MANAGER_MENU
)
229 MENUITEM_ADDUI("/Menus/CustomAttrPopup", "Delete", "CustomAttrPopup/Delete", GTK_UI_MANAGER_MENUITEM
)
230 MENUITEM_ADDUI("/Menus/CustomAttrPopup", "DeleteAll", "CustomAttrPopup/DeleteAll", GTK_UI_MANAGER_MENUITEM
)
231 MENUITEM_ADDUI("/Menus/CustomAttrPopup", "Reset", "CustomAttrPopup/Reset", GTK_UI_MANAGER_MENUITEM
)
232 custom_attr_popup_menu
= gtk_menu_item_get_submenu(GTK_MENU_ITEM(
233 gtk_ui_manager_get_widget(gtkut_ui_manager(), "/Menus/CustomAttrPopup")) );
236 /* grey out popup menu items if list is empty */
237 non_empty
= gtk_tree_model_get_iter_first(model
, &iter
);
238 cm_menu_set_sensitive("CustomAttrPopup/Delete", non_empty
);
239 cm_menu_set_sensitive("CustomAttrPopup/DeleteAll", non_empty
);
241 gtk_menu_popup(GTK_MENU(custom_attr_popup_menu
),
242 NULL
, NULL
, NULL
, NULL
,
243 event
->button
, event
->time
);
250 static gboolean
custom_attr_list_popup_menu(GtkWidget
*widget
, gpointer data
)
252 GtkTreeView
*list_view
= (GtkTreeView
*)data
;
253 GdkEventButton event
;
256 event
.time
= gtk_get_current_event_time();
258 custom_attr_list_btn_pressed(NULL
, &event
, list_view
);
263 static GtkWidget
*custom_attr_window_list_view_create (void)
265 GtkTreeView
*list_view
;
266 GtkTreeSelection
*selector
;
269 model
= GTK_TREE_MODEL(custom_attr_window_create_data_store());
270 list_view
= GTK_TREE_VIEW(gtk_tree_view_new_with_model(model
));
271 g_object_unref(model
);
272 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(model
),
273 CUSTOM_ATTR_NAME
, GTK_SORT_ASCENDING
);
275 gtk_tree_view_set_rules_hint(list_view
, prefs_common
.use_stripes_everywhere
);
277 selector
= gtk_tree_view_get_selection(list_view
);
278 gtk_tree_selection_set_mode(selector
, GTK_SELECTION_BROWSE
);
280 /* create the columns */
281 custom_attr_window_create_list_view_columns(GTK_WIDGET(list_view
));
283 g_signal_connect(G_OBJECT(list_view
), "popup-menu",
284 G_CALLBACK(custom_attr_list_popup_menu
), list_view
);
285 g_signal_connect(G_OBJECT(list_view
), "button-press-event",
286 G_CALLBACK(custom_attr_list_btn_pressed
), list_view
);
287 return GTK_WIDGET(list_view
);
290 static void custom_attr_window_close(void)
293 custom_attr_window_save_list();
294 custom_attr_window_list_view_clear_list(custom_attr_window
.attr_list
, FALSE
);
295 gtk_widget_hide(custom_attr_window
.window
);
296 gtk_window_set_modal(GTK_WINDOW(custom_attr_window
.window
), FALSE
);
297 if (dirty
&& !prefs_common
.addressbook_use_editaddress_dialog
)
298 addressbook_edit_reload_attr_list();
301 static void custom_attr_window_cancel_cb(GtkWidget
*widget
,
305 custom_attr_window_close();
308 static void custom_attr_window_ok_cb(GtkWidget
*widget
,
311 custom_attr_window_close();
314 static void custom_attr_selected_attr_edited(GtkCellRendererText
*widget
,
315 gchar
*path
, gchar
*new_text
,
316 GtkWidget
*list_view
)
319 GtkTreeModel
*model
= gtk_tree_view_get_model(GTK_TREE_VIEW(list_view
));
321 if (!gtk_tree_model_get_iter_from_string(model
, &iter
, path
))
324 if (!new_text
|| !*new_text
)
327 gtk_list_store_set(GTK_LIST_STORE(model
), &iter
,
328 CUSTOM_ATTR_NAME
, new_text
,
333 typedef struct FindAttrInStore
{
339 static gboolean
find_attr_in_store(GtkTreeModel
*model
,
342 FindAttrInStore
*data
)
345 gtk_tree_model_get(model
, iter
, CUSTOM_ATTR_NAME
, &attr
, -1);
347 if (g_utf8_collate(data
->attr
, attr
)==0) {
348 data
->path
= path
; /* signal we found it */
355 static void custom_attr_window_add_attr(void)
357 gchar
*new_attr
= gtk_editable_get_chars(GTK_EDITABLE(custom_attr_window
.add_entry
),
359 g_strstrip(new_attr
);
360 if (new_attr
&& *new_attr
) {
361 GtkListStore
*list_store
= GTK_LIST_STORE(gtk_tree_view_get_model
362 (GTK_TREE_VIEW(custom_attr_window
.attr_list
)));
367 gtk_tree_model_foreach(gtk_tree_view_get_model
368 (GTK_TREE_VIEW(custom_attr_window
.attr_list
)),
369 (GtkTreeModelForeachFunc
) find_attr_in_store
,
373 /* activate existing one */
374 GtkTreeSelection
*selection
;
376 GtkTreeModel
*model
= gtk_tree_view_get_model(
377 GTK_TREE_VIEW(custom_attr_window
.attr_list
));
379 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(custom_attr_window
.attr_list
));
380 gtk_tree_selection_select_iter(selection
, &fis
.iter
);
382 path
= gtk_tree_model_get_path(model
, &fis
.iter
);
383 /* XXX returned path may not be valid??? create new one to be sure */
384 gtk_tree_view_set_cursor(GTK_TREE_VIEW(custom_attr_window
.attr_list
),
387 gtk_list_store_set(list_store
, &fis
.iter
,
388 CUSTOM_ATTR_NAME
, new_attr
,
391 gtk_tree_path_free(path
);
396 gtk_list_store_append(list_store
, &iter
);
397 gtk_list_store_set(list_store
, &iter
,
398 CUSTOM_ATTR_NAME
, new_attr
,
403 alertpanel_error(_("Attribute name is not set."));
408 static void custom_attr_window_add_attr_cb(GtkWidget
*widget
,
411 custom_attr_window_add_attr();
412 gtk_entry_set_text(GTK_ENTRY(custom_attr_window
.add_entry
), "");
413 gtk_widget_grab_focus(custom_attr_window
.attr_list
);
416 static void custom_attr_window_del_attr_cb(GtkWidget
*widget
,
419 custom_attr_popup_delete(NULL
, NULL
);
420 gtk_widget_grab_focus(custom_attr_window
.attr_list
);
423 static gboolean
custom_attr_window_key_pressed(GtkWidget
*widget
,
424 GdkEventKey
*event
, gpointer data
)
426 if (event
&& event
->keyval
== GDK_KEY_Escape
)
427 custom_attr_window_close();
428 else if (event
&& event
->keyval
== GDK_KEY_Delete
)
429 custom_attr_popup_delete(NULL
, NULL
);
433 static gboolean
custom_attr_window_add_key_pressed(GtkWidget
*widget
,
434 GdkEventKey
*event
, gpointer data
)
436 if (event
&& (event
->keyval
== GDK_KEY_KP_Enter
|| event
->keyval
== GDK_KEY_Return
)) {
437 custom_attr_window_add_attr();
438 gtk_entry_set_text(GTK_ENTRY(custom_attr_window
.add_entry
), "");
439 gtk_widget_grab_focus(custom_attr_window
.attr_list
);
444 static void custom_attr_window_create(void)
451 GtkWidget
*attr_list
;
452 GtkWidget
*cancel_btn
;
454 GtkWidget
*scrolledwin
;
455 GtkWidget
*new_attr_label
;
456 GtkWidget
*new_attr_entry
;
460 window
= gtkut_window_new(GTK_WINDOW_TOPLEVEL
, "custom_attr_edit_window");
461 gtk_window_set_title (GTK_WINDOW(window
),
462 C_("Dialog title", "Edit attribute names"));
464 gtk_container_set_border_width (GTK_CONTAINER (window
), 8);
465 gtk_window_set_position (GTK_WINDOW (window
), GTK_WIN_POS_CENTER
);
466 gtk_window_set_resizable(GTK_WINDOW (window
), TRUE
);
467 g_signal_connect(G_OBJECT(window
), "delete_event",
468 G_CALLBACK(custom_attr_window_cancel_cb
), NULL
);
469 g_signal_connect(G_OBJECT(window
), "key_press_event",
470 G_CALLBACK(custom_attr_window_key_pressed
), NULL
);
471 MANAGE_WINDOW_SIGNALS_CONNECT (window
);
473 vbox1
= gtk_vbox_new(FALSE
, 6);
474 hbox1
= gtk_hbox_new(FALSE
, 6);
476 new_attr_label
= gtk_label_new(_("New attribute name:"));
477 gtk_misc_set_alignment(GTK_MISC(new_attr_label
), 0, 0.5);
478 gtk_box_pack_start(GTK_BOX(hbox1
), new_attr_label
, FALSE
, FALSE
, 0);
480 new_attr_entry
= gtk_entry_new();
481 gtk_box_pack_start(GTK_BOX(hbox1
), new_attr_entry
, FALSE
, FALSE
, 0);
482 g_signal_connect(G_OBJECT(new_attr_entry
), "key_press_event",
483 G_CALLBACK(custom_attr_window_add_key_pressed
), NULL
);
485 add_btn
= gtk_button_new_from_stock(GTK_STOCK_ADD
);
486 gtk_box_pack_start(GTK_BOX(hbox1
), add_btn
, FALSE
, FALSE
, 0);
488 del_btn
= gtk_button_new_from_stock(GTK_STOCK_DELETE
);
489 gtk_box_pack_start(GTK_BOX(hbox1
), del_btn
, FALSE
, FALSE
, 0);
491 gtkut_stock_button_set_create(&hbox2
, &cancel_btn
, GTK_STOCK_CANCEL
,
492 &ok_btn
, GTK_STOCK_OK
,
495 gtk_widget_show(new_attr_label
);
496 gtk_widget_show(new_attr_entry
);
497 gtk_widget_show(add_btn
);
498 gtk_widget_show(del_btn
);
499 gtk_widget_show(cancel_btn
);
500 gtk_widget_show(ok_btn
);
502 g_signal_connect(G_OBJECT(cancel_btn
), "clicked",
503 G_CALLBACK(custom_attr_window_cancel_cb
), NULL
);
504 g_signal_connect(G_OBJECT(ok_btn
), "clicked",
505 G_CALLBACK(custom_attr_window_ok_cb
), NULL
);
506 g_signal_connect(G_OBJECT(add_btn
), "clicked",
507 G_CALLBACK(custom_attr_window_add_attr_cb
), NULL
);
508 g_signal_connect(G_OBJECT(del_btn
), "clicked",
509 G_CALLBACK(custom_attr_window_del_attr_cb
), NULL
);
511 attr_list
= custom_attr_window_list_view_create();
513 label
= gtk_label_new(_("Adding or removing attribute names won't "
514 "affect attributes already set for contacts."));
515 gtk_widget_set_size_request(GTK_WIDGET(label
), 380, -1);
516 gtk_label_set_line_wrap(GTK_LABEL(label
), TRUE
);
517 gtk_misc_set_alignment(GTK_MISC(label
), 0, 0.5);
518 gtk_box_pack_start(GTK_BOX(vbox1
), label
, FALSE
, TRUE
, 0);
520 scrolledwin
= gtk_scrolled_window_new(NULL
, NULL
);
521 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin
),
522 GTK_POLICY_AUTOMATIC
, GTK_POLICY_ALWAYS
);
524 gtk_widget_set_size_request(scrolledwin
, 400, 250);
526 gtk_container_add(GTK_CONTAINER(scrolledwin
), attr_list
);
527 gtk_box_pack_start(GTK_BOX(vbox1
), scrolledwin
, TRUE
, TRUE
, 0);
528 gtk_box_pack_start(GTK_BOX(vbox1
), hbox1
, FALSE
, FALSE
, 0);
529 gtk_box_pack_start(GTK_BOX(vbox1
), hbox2
, FALSE
, FALSE
, 0);
531 gtk_widget_show(label
);
532 gtk_widget_show(scrolledwin
);
533 gtk_widget_show(attr_list
);
534 gtk_widget_show(hbox2
);
535 gtk_widget_show(hbox1
);
536 gtk_widget_show(vbox1
);
537 gtk_container_add(GTK_CONTAINER (window
), vbox1
);
539 custom_attr_window
.window
= window
;
540 custom_attr_window
.hbox1
= hbox1
;
541 custom_attr_window
.hbox2
= hbox2
;
542 custom_attr_window
.vbox1
= vbox1
;
543 custom_attr_window
.label
= label
;
544 custom_attr_window
.attr_list
= attr_list
;
545 custom_attr_window
.cancel_btn
= cancel_btn
;
546 custom_attr_window
.ok_btn
= ok_btn
;
547 custom_attr_window
.add_btn
= add_btn
;
548 custom_attr_window
.add_entry
= new_attr_entry
;
551 static void custom_attr_window_load_list (GList
*list
)
553 /* copy attribute names list from prefs to store */
556 GtkListStore
*list_store
= GTK_LIST_STORE(gtk_tree_view_get_model
557 (GTK_TREE_VIEW(custom_attr_window
.attr_list
)));
559 custom_attr_window_list_view_clear_list(custom_attr_window
.attr_list
, FALSE
);
563 gtk_list_store_append(list_store
, &iter
);
564 gtk_list_store_set(list_store
, &iter
,
565 CUSTOM_ATTR_NAME
, cur
->data
,
571 static GList
*store_to_glist
= NULL
;
573 static gboolean
custom_attr_store_to_glist (GtkTreeModel
*model
,
580 gtk_tree_model_get(model
, iter
, CUSTOM_ATTR_NAME
, &attr
, -1);
582 store_to_glist
= g_list_prepend(store_to_glist
, g_strdup(attr
));
587 static void custom_attr_window_save_list (void)
591 /* clear existing attribute names list in prefs */
592 cur
= prefs_common
.addressbook_custom_attributes
;
597 g_list_free(prefs_common
.addressbook_custom_attributes
);
599 /* copy attribute names list from store to prefs */
600 gtk_tree_model_foreach(gtk_tree_view_get_model
601 (GTK_TREE_VIEW(custom_attr_window
.attr_list
)),
602 (GtkTreeModelForeachFunc
) custom_attr_store_to_glist
,
604 prefs_common
.addressbook_custom_attributes
= g_list_reverse(store_to_glist
);
605 store_to_glist
= NULL
;
608 static GList
*custom_attr_default_list(void)
610 /* returned GList must be deallocated by caller */
616 while (default_addressbook_attributes_table
[i
]) {
617 list
= g_list_prepend(
618 list
, g_strdup(gettext(default_addressbook_attributes_table
[i
])));
621 list
= g_list_reverse(list
);
625 GList
*addressbook_update_custom_attr_from_prefs(void)
627 /* load addressbook custom attribute names list from file */
628 /* use a list of default attribute names if storage file doesn't exist */
630 GList
*default_attr_list
;
633 /* load table into glist */
634 default_attr_list
= custom_attr_default_list();
636 list
= prefs_common_read_history_from_dir_with_defaults(ADDRBOOK_DIR
,
637 ADDRESSBOOK_CUSTOM_ATTRIBUTES
,
640 /* free glist if it's the one we return (the default one) */
641 if (list
!= default_attr_list
) {
642 cur
= default_attr_list
;
647 g_list_free(default_attr_list
);