/* $Id: mailer.c,v 1.82 2011/06/05 14:01:01 khorben Exp $ */ /* Copyright (c) 2011 Pierre Pronchery */ /* This file is part of DeforaOS Desktop Mailer */ /* This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include "folder.h" #include "message.h" #include "compose.h" #include "callbacks.h" #include "mailer.h" #include "../config.h" #include "common.c" #define _(string) gettext(string) #define N_(string) (string) /* constants */ #ifndef PREFIX # define PREFIX "/usr/local" #endif #ifndef LIBDIR # define LIBDIR PREFIX "/lib" #endif #ifndef PLUGINDIR # define PLUGINDIR LIBDIR "/Mailer" #endif /* Mailer */ /* private */ /* types */ struct _Mailer { Account ** available; /* XXX consider using another data type */ unsigned int available_cnt; Account ** account; unsigned int account_cnt; Account * account_cur; Folder * folder_cur; guint source; /* configuration */ Config * config; /* widgets */ /* folders */ GtkWidget * fo_window; GtkTreeStore * fo_store; GtkWidget * fo_view; /* headers */ GtkWidget * he_window; GtkWidget * he_view; GtkTreeViewColumn * he_view_from; GtkTreeViewColumn * he_view_to; /* body */ GtkWidget * bo_window; GtkWidget * hdr_vbox; GtkWidget * hdr_subject; GtkWidget * hdr_from; GtkWidget * hdr_to; GtkWidget * hdr_date; GtkTextBuffer * bo_buffer; GtkWidget * bo_view; GtkWidget * statusbar; gint statusbar_id; /* about */ GtkWidget * ab_window; /* preferences */ GtkWidget * pr_window; GtkWidget * pr_accounts; GtkWidget * pr_messages_font; }; /* FIXME use a more elegant model with an AccountMessage directly */ typedef void (*MailerForeachMessageCallback)(Mailer * mailer, GtkTreeModel * model, GtkTreeIter * iter); /* constants */ static const char * _title[3] = { N_("New account"), N_("Account settings"), N_("Account confirmation") }; /* variables */ #ifdef EMBEDDED static DesktopAccel _mailer_accel[] = { { G_CALLBACK(on_quit), GDK_CONTROL_MASK, GDK_KEY_Q }, { G_CALLBACK(on_view_source), GDK_CONTROL_MASK, GDK_KEY_U }, { NULL, 0, 0 } }; #endif #ifndef EMBEDDED static DesktopMenu _menu_file[] = { { N_("_New mail"), G_CALLBACK(on_file_new_mail), "stock_mail-compose", GDK_CONTROL_MASK, GDK_KEY_N }, { "", NULL, NULL, 0, 0 }, { N_("Send / Receive"), NULL, "stock_mail-send-receive", GDK_CONTROL_MASK, GDK_KEY_R }, { "", NULL, NULL, 0, 0 }, { N_("_Print"), NULL, GTK_STOCK_PRINT, GDK_CONTROL_MASK, GDK_KEY_P }, { N_("Print pre_view"), NULL, GTK_STOCK_PRINT_PREVIEW, GDK_CONTROL_MASK, 0 }, { "", NULL, NULL, 0, 0 }, { N_("_Quit"), G_CALLBACK(on_file_quit), GTK_STOCK_QUIT, GDK_CONTROL_MASK, GDK_KEY_Q }, { NULL, NULL, NULL, 0, 0 } }; static DesktopMenu _menu_edit[] = { { N_("_Cut"), G_CALLBACK(on_edit_cut), GTK_STOCK_CUT, GDK_CONTROL_MASK, GDK_KEY_X }, { N_("Cop_y"), G_CALLBACK(on_edit_copy), GTK_STOCK_COPY, GDK_CONTROL_MASK, GDK_KEY_C }, { N_("_Paste"), G_CALLBACK(on_edit_paste), GTK_STOCK_PASTE, GDK_CONTROL_MASK, GDK_KEY_V }, { "", NULL, NULL, 0, 0 }, { N_("Select _all"), G_CALLBACK(on_edit_select_all), #if GTK_CHECK_VERSION(2, 10, 0) GTK_STOCK_SELECT_ALL, #else "edit-select-all", #endif GDK_CONTROL_MASK, GDK_KEY_A }, { N_("_Unselect all"), G_CALLBACK(on_edit_unselect_all), NULL, 0, 0 }, { "", NULL, NULL, 0, 0 }, { N_("_Preferences"), G_CALLBACK(on_edit_preferences), GTK_STOCK_PREFERENCES, 0, 0 }, { NULL, NULL, NULL, 0, 0 } }; static DesktopMenu _menu_message[] = { { N_("_Reply"), G_CALLBACK(on_message_reply), "stock_mail-reply", 0, 0 }, { N_("Reply to _all"), G_CALLBACK(on_message_reply_to_all), "stock_mail-reply-to-all", 0, 0 }, { N_("_Forward"), G_CALLBACK(on_message_forward), "stock_mail-forward", 0, 0 }, { "", NULL, NULL, 0, 0 }, { N_("_Delete"), G_CALLBACK(on_message_delete), GTK_STOCK_DELETE, 0, GDK_KEY_Delete }, { "", NULL, NULL, 0, 0 }, { N_("_View source"), G_CALLBACK(on_message_view_source), NULL, GDK_CONTROL_MASK, GDK_KEY_U }, { NULL, NULL, NULL, 0, 0 } }; static DesktopMenu _menu_help[] = { #if GTK_CHECK_VERSION(2, 6, 0) { N_("_About"), G_CALLBACK(on_help_about), GTK_STOCK_ABOUT, 0, 0 }, #else { N_("_About"), G_CALLBACK(on_help_about), NULL, 0, 0 }, #endif { NULL, NULL, NULL, 0, 0 } }; static DesktopMenubar _mailer_menubar[] = { { N_("_File"), _menu_file }, { N_("_Edit"), _menu_edit }, { N_("_Message"), _menu_message }, { N_("_Help"), _menu_help }, { NULL, NULL } }; #endif static DesktopToolbar _mailer_fo_toolbar[] = { { N_("New mail"), G_CALLBACK(on_file_new_mail), "stock_mail-compose", 0, 0, NULL }, { "", NULL, NULL, 0, 0, NULL }, { N_("Send / Receive"), NULL, "stock_mail-send-receive", 0, 0, NULL }, { N_("Stop"), NULL, GTK_STOCK_STOP, 0, GDK_KEY_Escape, NULL }, { "", NULL, NULL, 0, 0, NULL }, #ifndef EMBEDDED { N_("Reply"), G_CALLBACK(on_reply), "stock_mail-reply", 0, 0, NULL }, { N_("Reply to all"), G_CALLBACK(on_reply_to_all), "stock_mail-reply-to-all", 0, 0, NULL }, { N_("Forward"), G_CALLBACK(on_forward), "stock_mail-forward", 0, 0, NULL}, #endif { N_("Delete"), G_CALLBACK(on_delete), GTK_STOCK_DELETE, 0, 0, NULL }, { N_("Print"), NULL, GTK_STOCK_PRINT, 0, 0, NULL }, #ifdef EMBEDDED { "", NULL, NULL, 0, 0, NULL }, { N_("Preferences"), G_CALLBACK(on_preferences), GTK_STOCK_PREFERENCES, 0, 0, NULL }, #endif { NULL, NULL, NULL, 0, 0, NULL } }; #ifdef EMBEDDED static DesktopToolbar _mailer_he_toolbar[] = { { N_("New mail"), G_CALLBACK(on_file_new_mail), "stock_mail-compose", 0, 0, NULL }, { "", NULL, NULL, 0, 0, NULL }, { N_("Reply"), G_CALLBACK(on_reply), "stock_mail-reply", 0, 0, NULL }, { N_("Reply to all"), G_CALLBACK(on_reply_to_all), "stock_mail-reply-to-all", 0, 0, NULL }, { N_("Forward"), G_CALLBACK(on_forward), "stock_mail-forward", 0, 0, NULL}, { N_("Delete"), G_CALLBACK(on_delete), GTK_STOCK_DELETE, 0, 0, NULL }, { NULL, NULL, NULL, 0, 0, NULL } }; static DesktopToolbar _mailer_bo_toolbar[] = { { N_("New mail"), G_CALLBACK(on_file_new_mail), "stock_mail-compose", 0, 0, NULL }, { "", NULL, NULL, 0, 0, NULL }, { N_("Reply"), G_CALLBACK(on_reply), "stock_mail-reply", 0, 0, NULL }, { N_("Reply to all"), G_CALLBACK(on_reply_to_all), "stock_mail-reply-to-all", 0, 0, NULL }, { N_("Forward"), G_CALLBACK(on_forward), "stock_mail-forward", 0, 0, NULL}, { N_("Delete"), G_CALLBACK(on_delete), GTK_STOCK_DELETE, 0, 0, NULL }, { N_("Print"), NULL, GTK_STOCK_PRINT, 0, 0, NULL }, { NULL, NULL, NULL, 0, 0, NULL } }; #endif /* prototypes */ static int _mailer_config_load_account(Mailer * mailer, char const * name); static gboolean _mailer_confirm(Mailer * mailer, char const * message); static void _mailer_foreach_message_selected(Mailer * mailer, MailerForeachMessageCallback callback); static char * _mailer_get_config_filename(void); static void _mailer_update_status(Mailer * mailer); /* functions */ static int _mailer_config_load_account(Mailer * mailer, char const * name) { Account * account; char const * type; #ifdef DEBUG fprintf(stderr, "DEBUG: %s(\"%s\")\n", __func__, name); #endif if((type = config_get(mailer->config, name, "type")) == NULL) return -1; if((account = account_new(mailer, type, name, mailer->fo_store)) == NULL) return -mailer_error(mailer, error_get(), 1); account_config_load(account, mailer->config); mailer_account_add(mailer, account); return 0; } /* public */ /* functions */ /* mailer_new */ static int _new_plugins(Mailer * mailer); static GtkWidget * _new_folders_view(Mailer * mailer); static void _on_folders_changed(GtkTreeSelection * selection, gpointer data); static GtkWidget * _new_headers_view(Mailer * mailer); static GtkWidget * _new_headers(Mailer * mailer); static GtkTreeViewColumn * _headers_view_column_pixbuf(GtkTreeView * view, char const * title, int id, int sortid); static GtkTreeViewColumn * _headers_view_column_text(GtkTreeView * view, char const * title, int id, int sortid, int boldid); static void _on_headers_changed(GtkTreeSelection * selection, gpointer data); static gboolean _new_config_load(gpointer data); Mailer * mailer_new(void) { Mailer * mailer; GtkAccelGroup * group; GtkWidget * vbox; GtkWidget * vbox2; #ifndef EMBEDDED GtkWidget * hpaned; GtkWidget * vpaned; #endif GtkWidget * widget; if((mailer = object_new(sizeof(*mailer))) == NULL) { error_print("mailer"); return NULL; } _new_plugins(mailer); mailer->account = NULL; mailer->account_cnt = 0; mailer->account_cur = NULL; mailer->folder_cur = NULL; /* widgets */ group = gtk_accel_group_new(); mailer->fo_window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_add_accel_group(GTK_WINDOW(mailer->fo_window), group); #ifndef EMBEDDED gtk_window_set_default_size(GTK_WINDOW(mailer->fo_window), 800, 600); gtk_window_set_title(GTK_WINDOW(mailer->fo_window), _("Mailer")); #else gtk_window_set_default_size(GTK_WINDOW(mailer->fo_window), 200, 300); gtk_window_set_title(GTK_WINDOW(mailer->fo_window), _("Folders")); #endif #if GTK_CHECK_VERSION(2, 6, 0) gtk_window_set_icon_name(GTK_WINDOW(mailer->fo_window), "mailer"); #endif g_signal_connect_swapped(G_OBJECT(mailer->fo_window), "delete-event", G_CALLBACK(on_closex), mailer); vbox = gtk_vbox_new(FALSE, 0); /* menubar */ #ifndef EMBEDDED widget = desktop_menubar_create(_mailer_menubar, mailer, group); gtk_box_pack_start(GTK_BOX(vbox), widget, FALSE, FALSE, 0); #else desktop_accel_create(_mailer_accel, mailer, group); #endif /* toolbar */ widget = desktop_toolbar_create(_mailer_fo_toolbar, mailer, group); gtk_box_pack_start(GTK_BOX(vbox), widget, FALSE, TRUE, 0); /* folders */ widget = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(widget), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); mailer->fo_view = _new_folders_view(mailer); gtk_container_add(GTK_CONTAINER(widget), mailer->fo_view); #ifndef EMBEDDED hpaned = gtk_hpaned_new(); gtk_paned_set_position(GTK_PANED(hpaned), 160); gtk_paned_add1(GTK_PANED(hpaned), widget); vpaned = gtk_vpaned_new(); gtk_paned_set_position(GTK_PANED(vpaned), 160); gtk_box_pack_start(GTK_BOX(vbox), hpaned, TRUE, TRUE, 0); #else gtk_box_pack_start(GTK_BOX(vbox), widget, TRUE, TRUE, 0); #endif /* statusbar */ mailer->statusbar = gtk_statusbar_new(); mailer->statusbar_id = 0; _mailer_update_status(mailer); gtk_box_pack_start(GTK_BOX(vbox), mailer->statusbar, FALSE, TRUE, 0); gtk_container_add(GTK_CONTAINER(mailer->fo_window), vbox); gtk_widget_show_all(vbox); /* messages list */ #ifndef EMBEDDED mailer->he_window = mailer->fo_window; #else group = gtk_accel_group_new(); mailer->he_window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_add_accel_group(GTK_WINDOW(mailer->he_window), group); gtk_window_set_default_size(GTK_WINDOW(mailer->he_window), 200, 300); # if GTK_CHECK_VERSION(2, 6, 0) gtk_window_set_icon_name(GTK_WINDOW(mailer->he_window), "mailer"); # endif gtk_window_set_title(GTK_WINDOW(mailer->he_window), _("Message list")); g_signal_connect_swapped(G_OBJECT(mailer->he_window), "delete-event", G_CALLBACK(on_closex), mailer); vbox = gtk_vbox_new(FALSE, 0); gtk_container_add(GTK_CONTAINER(mailer->he_window), vbox); widget = desktop_toolbar_create(_mailer_he_toolbar, mailer, group); gtk_box_pack_start(GTK_BOX(vbox), widget, FALSE, TRUE, 0); #endif widget = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(widget), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); mailer->he_view = _new_headers_view(mailer); gtk_container_add(GTK_CONTAINER(widget), mailer->he_view); #ifndef EMBEDDED gtk_paned_add1(GTK_PANED(vpaned), widget); #else gtk_box_pack_start(GTK_BOX(vbox), widget, TRUE, TRUE, 0); #endif gtk_widget_show_all(vbox); /* messages body */ #ifndef EMBEDDED mailer->bo_window = mailer->fo_window; #else group = gtk_accel_group_new(); mailer->bo_window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_add_accel_group(GTK_WINDOW(mailer->bo_window), group); gtk_window_set_default_size(GTK_WINDOW(mailer->bo_window), 200, 300); #if GTK_CHECK_VERSION(2, 6, 0) gtk_window_set_icon_name(GTK_WINDOW(mailer->bo_window), "mailer"); #endif gtk_window_set_title(GTK_WINDOW(mailer->bo_window), _("Message")); g_signal_connect_swapped(G_OBJECT(mailer->bo_window), "delete-event", G_CALLBACK(on_closex), mailer); vbox = gtk_vbox_new(FALSE, 0); gtk_container_add(GTK_CONTAINER(mailer->bo_window), vbox); widget = desktop_toolbar_create(_mailer_bo_toolbar, mailer, group); gtk_box_pack_start(GTK_BOX(vbox), widget, FALSE, TRUE, 0); #endif vbox2 = _new_headers(mailer); mailer->bo_buffer = gtk_text_buffer_new(NULL); mailer->bo_view = gtk_text_view_new_with_buffer(mailer->bo_buffer); gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(mailer->bo_view), FALSE); gtk_text_view_set_editable(GTK_TEXT_VIEW(mailer->bo_view), FALSE); gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(mailer->bo_view), GTK_WRAP_WORD_CHAR); widget = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(widget), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_container_add(GTK_CONTAINER(widget), mailer->bo_view); gtk_box_pack_start(GTK_BOX(vbox2), widget, TRUE, TRUE, 0); #ifndef EMBEDDED gtk_paned_add2(GTK_PANED(vpaned), vbox2); gtk_paned_add2(GTK_PANED(hpaned), vpaned); #else gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0); #endif gtk_widget_show_all(vbox); /* widgets */ mailer->ab_window = NULL; mailer->pr_window = NULL; /* show window */ gtk_widget_hide(mailer->hdr_vbox); gtk_widget_show(mailer->fo_window); gtk_widget_show(mailer->he_window); gtk_widget_show(mailer->bo_window); /* load configuration */ mailer->source = g_idle_add(_new_config_load, mailer); return mailer; } static int _new_plugins(Mailer * mailer) { int ret = 0; char * dirname; DIR * dir; struct dirent * de; size_t len; Account ** p; mailer->available = NULL; mailer->available_cnt = 0; if((dirname = string_new_append(PLUGINDIR, "/account", NULL)) == NULL) return -1; if((dir = opendir(dirname)) == NULL) { error_set_code(1, "%s: %s", dirname, strerror(errno)); string_delete(dirname); return error_print("mailer"); } for(de = readdir(dir); de != NULL; de = readdir(dir)) { if((len = strlen(de->d_name)) < 4 || strcmp(".so", &de->d_name[len - 3]) != 0) continue; de->d_name[len - 3] = '\0'; if((p = realloc(mailer->available, sizeof(*p) * (mailer->available_cnt + 1))) == NULL) { error_set_print("mailer", 1, "%s", strerror(errno)); continue; } mailer->available = p; if((p[mailer->available_cnt] = account_new(mailer, de->d_name, NULL, NULL)) == NULL) { error_print("mailer"); continue; } mailer->available_cnt++; } if(closedir(dir) != 0) ret = error_set_print("mailer", 1, "%s: %s", dirname, strerror( errno)); string_delete(dirname); return ret; } static GtkWidget * _new_folders_view(Mailer * mailer) { GtkWidget * widget; GtkCellRenderer * renderer; GtkTreeSelection * treesel; mailer->fo_store = gtk_tree_store_new(MFC_COUNT, G_TYPE_POINTER, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_POINTER, GDK_TYPE_PIXBUF, G_TYPE_STRING); widget = gtk_tree_view_new_with_model(GTK_TREE_MODEL(mailer->fo_store)); renderer = gtk_cell_renderer_pixbuf_new(); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(widget), -1, NULL, renderer, "pixbuf", MFC_ICON, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(widget), -1, _("Folders"), renderer, "text", MFC_NAME, NULL); treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget)); g_signal_connect(G_OBJECT(treesel), "changed", G_CALLBACK( _on_folders_changed), mailer); return widget; } static void _on_folders_changed(GtkTreeSelection * selection, gpointer data) { Mailer * mailer = data; GtkTreeModel * model; GtkTreeIter iter; GtkTreePath * path; gboolean sent; #ifdef DEBUG fprintf(stderr, "DEBUG: %s()\n", __func__); #endif if(!gtk_tree_selection_get_selected(selection, &model, &iter)) { mailer->account_cur = NULL; mailer->folder_cur = NULL; gtk_tree_view_set_model(GTK_TREE_VIEW(mailer->he_view), NULL); _mailer_update_status(mailer); return; } /* get current folder */ gtk_tree_model_get(model, &iter, MFC_FOLDER, &mailer->folder_cur, -1); /* get current account */ path = gtk_tree_model_get_path(model, &iter); while(gtk_tree_path_get_depth(path) > 1 && gtk_tree_path_up(path)); gtk_tree_model_get_iter(model, &iter, path); gtk_tree_model_get(model, &iter, MFC_ACCOUNT, &mailer->account_cur, -1); gtk_tree_path_free(path); /* display relevant columns */ sent = (mailer->folder_cur != NULL && folder_get_type( mailer->folder_cur) == FT_SENT) ? TRUE : FALSE; gtk_tree_view_column_set_visible(mailer->he_view_from, !sent); gtk_tree_view_column_set_visible(mailer->he_view_to, sent); /* display headers */ model = (mailer->folder_cur != NULL) ? GTK_TREE_MODEL( folder_get_messages(mailer->folder_cur)) : NULL; gtk_tree_view_set_model(GTK_TREE_VIEW(mailer->he_view), model); _mailer_update_status(mailer); } static GtkWidget * _new_headers_view(Mailer * mailer) { GtkWidget * widget; GtkTreeView * treeview; GtkTreeSelection * treesel; widget = gtk_tree_view_new(); treeview = GTK_TREE_VIEW(widget); gtk_tree_view_set_rules_hint(treeview, TRUE); _headers_view_column_pixbuf(treeview, "", MHC_ICON, MHC_READ); _headers_view_column_text(treeview, _("Subject"), MHC_SUBJECT, MHC_SUBJECT, MHC_WEIGHT); mailer->he_view_from = _headers_view_column_text(treeview, _("From"), MHC_FROM, MHC_FROM, MHC_WEIGHT); mailer->he_view_to = _headers_view_column_text(treeview, _("To"), MHC_TO, MHC_TO, MHC_WEIGHT); _headers_view_column_text(treeview, _("Date"), MHC_DATE_DISPLAY, MHC_DATE, MHC_WEIGHT); treesel = gtk_tree_view_get_selection(treeview); gtk_tree_selection_set_mode(treesel, GTK_SELECTION_MULTIPLE); g_signal_connect(G_OBJECT(treesel), "changed", G_CALLBACK( _on_headers_changed), mailer); return widget; } static void _on_headers_changed(GtkTreeSelection * selection, gpointer data) { Mailer * mailer = data; GtkTreeModel * model; GList * sel; GtkTreeIter iter; gchar * p; gchar * q; gchar * r; Message * message; GtkTextBuffer * tbuf; #ifdef DEBUG fprintf(stderr, "DEBUG: %s()\n", __func__); #endif sel = gtk_tree_selection_get_selected_rows(selection, &model); if(sel == NULL || sel->next != NULL) /* empty or multiple */ { gtk_widget_hide(mailer->hdr_vbox); gtk_text_view_set_buffer(GTK_TEXT_VIEW(mailer->bo_view), mailer->bo_buffer); g_list_foreach(sel, (GFunc)gtk_tree_path_free, NULL); g_list_free(sel); return; } gtk_tree_model_get_iter(model, &iter, sel->data); gtk_tree_model_get(model, &iter, MHC_MESSAGE, &message, -1); gtk_tree_model_get(model, &iter, MHC_SUBJECT, &p, -1); gtk_label_set_text(GTK_LABEL(mailer->hdr_subject), p); g_free(p); gtk_tree_model_get(model, &iter, MHC_FROM, &p, MHC_FROM_EMAIL, &q, -1); if(q == NULL || strlen(q) == 0 || strcmp(p, q) == 0) r = g_strdup(p); else r = g_strdup_printf("%s <%s>", p, q); gtk_label_set_text(GTK_LABEL(mailer->hdr_from), r); g_free(p); g_free(q); g_free(r); gtk_tree_model_get(model, &iter, MHC_TO, &p, MHC_TO_EMAIL, &q, -1); if(q == NULL || strlen(q) == 0 || strcmp(p, q) == 0) r = g_strdup(p); else r = g_strdup_printf("%s <%s>", p, q); gtk_label_set_text(GTK_LABEL(mailer->hdr_to), r); g_free(p); g_free(q); g_free(r); gtk_tree_model_get(model, &iter, MHC_DATE_DISPLAY, &p, -1); gtk_label_set_text(GTK_LABEL(mailer->hdr_date), p); g_free(p); message_set_read(message, TRUE); gtk_widget_show(mailer->hdr_vbox); if((tbuf = account_select(mailer->account_cur, mailer->folder_cur, message)) != NULL) gtk_text_view_set_buffer(GTK_TEXT_VIEW(mailer->bo_view), tbuf); g_list_foreach(sel, (GFunc)gtk_tree_path_free, NULL); g_list_free(sel); } static GtkTreeViewColumn * _headers_view_column_pixbuf(GtkTreeView * view, char const * title, int id, int sortid) { GtkCellRenderer * renderer; GtkTreeViewColumn * column; renderer = gtk_cell_renderer_pixbuf_new(); column = gtk_tree_view_column_new_with_attributes(title, renderer, "pixbuf", id, NULL); gtk_tree_view_column_set_sort_column_id(column, sortid); gtk_tree_view_append_column(view, column); return column; } static GtkTreeViewColumn * _headers_view_column_text(GtkTreeView * view, char const * title, int id, int sortid, int weightid) { GtkCellRenderer * renderer; GtkTreeViewColumn * column; renderer = gtk_cell_renderer_text_new(); g_object_set(G_OBJECT(renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL); column = gtk_tree_view_column_new_with_attributes(title, renderer, "text", id, (weightid >= 0) ? "weight" : NULL, weightid, NULL); #if GTK_CHECK_VERSION(2, 4, 0) gtk_tree_view_column_set_expand(column, TRUE); #endif gtk_tree_view_column_set_resizable(column, TRUE); gtk_tree_view_column_set_sort_column_id(column, sortid); gtk_tree_view_append_column(view, column); return column; } static GtkWidget * _new_headers(Mailer * mailer) { struct { char * hdr; GtkWidget ** widget; } widgets[] = { { N_(" Subject: "), NULL }, { N_(" From: "), NULL }, { N_(" To: "), NULL }, { N_(" Date: "), NULL }, { NULL, NULL } }; int i; GtkWidget * vbox; GtkWidget * hbox; GtkWidget * widget; GtkSizeGroup * group; PangoFontDescription * bold; widgets[0].widget = &mailer->hdr_subject; widgets[1].widget = &mailer->hdr_from; widgets[2].widget = &mailer->hdr_to; widgets[3].widget = &mailer->hdr_date; vbox = gtk_vbox_new(FALSE, 0); mailer->hdr_vbox = gtk_vbox_new(FALSE, 0); group = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); bold = pango_font_description_new(); pango_font_description_set_weight(bold, PANGO_WEIGHT_BOLD); for(i = 0; widgets[i].hdr != NULL; i++) { hbox = gtk_hbox_new(FALSE, 0); widget = gtk_label_new(_(widgets[i].hdr)); gtk_widget_modify_font(widget, bold); gtk_misc_set_alignment(GTK_MISC(widget), 1.0, 0.0); gtk_size_group_add_widget(GTK_SIZE_GROUP(group), widget); gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, TRUE, 0); widget = gtk_label_new(""); *(widgets[i].widget) = widget; gtk_label_set_ellipsize(GTK_LABEL(widget), PANGO_ELLIPSIZE_MIDDLE); gtk_misc_set_alignment(GTK_MISC(widget), 0.0, 0.0); gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(mailer->hdr_vbox), hbox, FALSE, FALSE, 0); } gtk_box_pack_start(GTK_BOX(vbox), mailer->hdr_vbox, FALSE, FALSE, 0); pango_font_description_free(bold); return vbox; } static gboolean _new_config_load(gpointer data) { Mailer * mailer = data; char * filename; char const * value; PangoFontDescription * font; char * p; char * q; mailer->source = 0; if((mailer->config = config_new()) == NULL) return FALSE; if((filename = _mailer_get_config_filename()) == NULL) return FALSE; config_load(mailer->config, filename); free(filename); if((value = config_get(mailer->config, NULL, "messages_font")) != NULL) { font = pango_font_description_from_string(value); gtk_widget_modify_font(mailer->bo_view, font); pango_font_description_free(font); } if((value = config_get(mailer->config, NULL, "accounts")) == NULL || value[0] == '\0') return FALSE; if((p = strdup(value)) == NULL) return FALSE; value = p; for(q = p; *q != '\0'; q++) { if(*q != ',') continue; *q = '\0'; _mailer_config_load_account(mailer, value); value = q + 1; } if(value[0] != '\0') _mailer_config_load_account(mailer, value); free(p); return FALSE; } /* mailer_delete */ void mailer_delete(Mailer * mailer) { unsigned int i; if(mailer->source != 0) g_source_remove(mailer->source); for(i = 0; i < mailer->available_cnt; i++) account_delete(mailer->available[i]); free(mailer->available); for(i = 0; i < mailer->account_cnt; i++) account_delete(mailer->account[i]); free(mailer->account); object_delete(mailer); } /* accessors */ /* mailer_get_config */ char const * mailer_get_config(Mailer * mailer, char const * variable) { return config_get(mailer->config, NULL, variable); } /* mailer_set_status */ void mailer_set_status(Mailer * mailer, char const * status) { GtkStatusbar * sb; sb = GTK_STATUSBAR(mailer->statusbar); if(mailer->statusbar_id != 0) gtk_statusbar_remove(sb, gtk_statusbar_get_context_id(sb, ""), mailer->statusbar_id); mailer->statusbar_id = gtk_statusbar_push(sb, gtk_statusbar_get_context_id(sb, ""), status); } /* useful */ /* mailer_error */ int mailer_error(Mailer * mailer, char const * message, int ret) { GtkWidget * dialog; if(mailer == NULL) return error_set_print("mailer", ret, "%s", message); dialog = gtk_message_dialog_new(GTK_WINDOW(mailer->fo_window), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, #if GTK_CHECK_VERSION(2, 6, 0) "%s", _("Error")); gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog), #endif "%s", message); gtk_window_set_title(GTK_WINDOW(dialog), _("Error")); gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW( mailer->fo_window)); g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK( gtk_widget_destroy), NULL); gtk_widget_show(dialog); return ret; } /* mailer_account_add */ int mailer_account_add(Mailer * mailer, Account * account) { Account ** p; #ifdef DEBUG fprintf(stderr, "DEBUG: %s(%p)\n", __func__, (void*)account); #endif if((p = realloc(mailer->account, sizeof(*p) * (mailer->account_cnt + 1))) == NULL) return -mailer_error(mailer, "realloc", 1); mailer->account = p; mailer->account[mailer->account_cnt] = account; if(account_init(account) != 0) return -mailer_error(mailer, account_get_title(account), 1); mailer->account_cnt++; return 0; } #if 0 /* FIXME deprecate? */ /* mailer_account_disable */ int mailer_account_disable(Mailer * mailer, Account * account) { unsigned int i; for(i = 0; i < mailer->account_cnt; i++) if(mailer->account[i] == account) break; if(i == mailer->account_cnt) return 1; account_set_enabled(account, 0); return 0; } /* mailer_account_enable */ int mailer_account_enable(Mailer * mailer, Account * account) { unsigned int i; for(i = 0; i < mailer->account_cnt; i++) if(mailer->account[i] == account) break; if(i == mailer->account_cnt) return 1; account_set_enabled(account, 1); return 0; } #endif /* mailer_delete_selected */ static void _mailer_delete_selected_foreach(GtkTreeRowReference * reference, Mailer * mailer); void mailer_delete_selected(Mailer * mailer) { /* FIXME figure which area is focused first (deleting folders) */ GtkTreeModel * model; GtkTreeSelection * treesel; GList * selected; GList * s; GtkTreePath * path; GtkTreeRowReference * reference; if((model = gtk_tree_view_get_model(GTK_TREE_VIEW(mailer->he_view))) == NULL) return; if((treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW( mailer->he_view))) == NULL) return; if((selected = gtk_tree_selection_get_selected_rows(treesel, NULL)) == NULL) return; /* FIXME move messages to trash first */ if(_mailer_confirm(mailer, _("The messages selected will be deleted.\n" "Continue?")) != TRUE) { g_list_free(selected); return; } for(s = g_list_first(selected); s != NULL; s = g_list_next(s)) { if((path = s->data) == NULL) continue; reference = gtk_tree_row_reference_new(model, path); s->data = reference; gtk_tree_path_free(path); } g_list_foreach(selected, (GFunc)_mailer_delete_selected_foreach, mailer); g_list_free(selected); } static void _mailer_delete_selected_foreach(GtkTreeRowReference * reference, Mailer * mailer) { GtkTreeModel * model; GtkTreePath * path; GtkTreeIter iter; Message * message; if((model = gtk_tree_view_get_model(GTK_TREE_VIEW(mailer->he_view))) == NULL) return; if(reference == NULL) return; if((path = gtk_tree_row_reference_get_path(reference)) == NULL) return; if(gtk_tree_model_get_iter(model, &iter, path) == TRUE) gtk_tree_model_get(model, &iter, MHC_MESSAGE, &message, -1); gtk_list_store_remove(GTK_LIST_STORE(model), &iter); gtk_tree_path_free(path); } /* mailer_copy */ void mailer_copy(Mailer * mailer) { GtkTextBuffer * buffer; GtkClipboard * clipboard; if(gtk_window_get_focus(GTK_WINDOW(mailer->fo_window)) == mailer->bo_view) { buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW( mailer->bo_view)); clipboard = gtk_widget_get_clipboard(mailer->bo_view, GDK_SELECTION_CLIPBOARD); gtk_text_buffer_copy_clipboard(buffer, clipboard); } /* FIXME implement the other widgets */ } /* mailer_cut */ void mailer_cut(Mailer * mailer) { GtkTextBuffer * buffer; GtkClipboard * clipboard; if(gtk_window_get_focus(GTK_WINDOW(mailer->fo_window)) == mailer->bo_view) /* XXX copies, really */ { buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW( mailer->bo_view)); clipboard = gtk_widget_get_clipboard(mailer->bo_view, GDK_SELECTION_CLIPBOARD); gtk_text_buffer_cut_clipboard(buffer, clipboard, FALSE); } /* FIXME implement the other widgets */ } /* mailer_open_selected_source */ static void _open_selected_source(Mailer * mailer, GtkTreeModel * model, GtkTreeIter * iter); void mailer_open_selected_source(Mailer * mailer) { _mailer_foreach_message_selected(mailer, _open_selected_source); } static void _open_selected_source(Mailer * mailer, GtkTreeModel * model, GtkTreeIter * iter) { Message * message; GtkWidget * window; GtkWidget * scrolled; PangoFontDescription * font; char const * p; GtkWidget * widget; GtkTextBuffer * tbuf; gtk_tree_model_get(model, iter, MHC_MESSAGE, &message, -1); if(message == NULL) return; if((tbuf = account_select_source(mailer->account_cur, mailer->folder_cur, message)) == NULL) return; window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_default_size(GTK_WINDOW(window), 640, 480); /* XXX choose a better title */ gtk_window_set_title(GTK_WINDOW(window), _("Mailer - View source")); scrolled = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); widget = gtk_text_view_new_with_buffer(tbuf); if((p = config_get(mailer->config, NULL, "messages_font")) != NULL) { font = pango_font_description_from_string(p); gtk_widget_modify_font(widget, font); pango_font_description_free(font); } gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(widget), FALSE); gtk_text_view_set_editable(GTK_TEXT_VIEW(widget), FALSE); gtk_container_add(GTK_CONTAINER(scrolled), widget); gtk_container_add(GTK_CONTAINER(window), scrolled); gtk_widget_show_all(window); /* FIXME count the window */ } /* mailer_paste */ void mailer_paste(Mailer * mailer) { GtkTextBuffer * buffer; GtkClipboard * clipboard; if(gtk_window_get_focus(GTK_WINDOW(mailer->fo_window)) == mailer->bo_view) /* XXX does nothing, really */ { buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW( mailer->bo_view)); clipboard = gtk_widget_get_clipboard(mailer->bo_view, GDK_SELECTION_CLIPBOARD); gtk_text_buffer_paste_clipboard(buffer, clipboard, NULL, FALSE); } /* FIXME implement the other widgets */ } /* mailer_reply_selected */ static void _reply_selected(Mailer * mailer, GtkTreeModel * model, GtkTreeIter * iter); void mailer_reply_selected(Mailer * mailer) { _mailer_foreach_message_selected(mailer, _reply_selected); } static void _reply_selected(Mailer * mailer, GtkTreeModel * model, GtkTreeIter * iter) { Compose * compose; char * from; char * subject; char * p; char const * q; if((compose = compose_new(mailer)) == NULL) return; /* XXX error message? */ if((q = mailer_get_config(mailer, "messages_font")) != NULL) compose_set_font(compose, q); gtk_tree_model_get(model, iter, MHC_FROM, &from, MHC_SUBJECT, &subject, -1); #if 0 /* FIXME adapt */ if(from != NULL) compose_set_to(compose, from); #endif q = N_("Re: "); if(subject != NULL && strncasecmp(subject, q, strlen(q)) != 0 && strncasecmp(subject, _(q), strlen(_(q))) != 0 && (p = malloc(strlen(q) + strlen(subject) + 1)) != NULL) { sprintf(p, "%s%s", q, subject); free(subject); subject = p; } compose_set_subject(compose, subject); free(subject); free(from); } /* mailer_reply_selected_to_all */ static void _reply_selected_to_all(Mailer * mailer, GtkTreeModel * model, GtkTreeIter * iter); void mailer_reply_selected_to_all(Mailer * mailer) { _mailer_foreach_message_selected(mailer, _reply_selected_to_all); } static void _reply_selected_to_all(Mailer * mailer, GtkTreeModel * model, GtkTreeIter * iter) { /* FIXME really implement */ _reply_selected(mailer, model, iter); } /* mailer_select_all */ void mailer_select_all(Mailer * mailer) { GtkTreeSelection * treesel; #if GTK_CHECK_VERSION(2, 4, 0) GtkTextBuffer * tbuf; GtkTextIter start; GtkTextIter end; #endif #if GTK_CHECK_VERSION(2, 4, 0) if(gtk_window_get_focus(GTK_WINDOW(mailer->fo_window)) == mailer->bo_view) { tbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(mailer->bo_view)); gtk_text_buffer_get_start_iter(tbuf, &start); gtk_text_buffer_get_end_iter(tbuf, &end); gtk_text_buffer_select_range(tbuf, &start, &end); return; } #endif treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW(mailer->he_view)); gtk_tree_selection_select_all(treesel); } /* mailer_show_about */ static gboolean _about_on_closex(gpointer data); void mailer_show_about(Mailer * mailer, gboolean show) { GtkWidget * dialog; if(mailer->ab_window != NULL) { if(show) gtk_widget_show(mailer->ab_window); else gtk_widget_hide(mailer->ab_window); return; } dialog = desktop_about_dialog_new(); mailer->ab_window = dialog; g_signal_connect_swapped(G_OBJECT(dialog), "delete-event", G_CALLBACK( _about_on_closex), mailer); gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW( mailer->fo_window)); desktop_about_dialog_set_name(dialog, PACKAGE); desktop_about_dialog_set_version(dialog, VERSION); desktop_about_dialog_set_authors(dialog, _authors); desktop_about_dialog_set_copyright(dialog, _copyright); desktop_about_dialog_set_logo_icon_name(dialog, "mailer"); desktop_about_dialog_set_license(dialog, _license); gtk_widget_show(dialog); } static gboolean _about_on_closex(gpointer data) { Mailer * mailer = data; gtk_widget_hide(mailer->ab_window); return TRUE; } /* mailer_show_preferences */ typedef enum _AccountColumn { AC_DATA = 0, AC_ACTIVE, AC_ENABLED, AC_TITLE, AC_TYPE } AccountColumn; #define AC_LAST AC_TYPE #define AC_COUNT (AC_LAST + 1) static void _preferences_set(Mailer * mailer); static gboolean _on_preferences_closex(gpointer data); static void _on_preferences_account_delete(gpointer data); static void _on_preferences_account_edit(gpointer data); static void _on_preferences_account_new(gpointer data); static void _on_preferences_account_toggle(GtkCellRendererToggle * renderer, char * path, gpointer data); static void _on_preferences_cancel(gpointer data); static void _on_preferences_ok(gpointer data); static int _preferences_ok_accounts(Mailer * mailer); static int _preferences_ok_display(Mailer * mailer); static int _preferences_ok_save(Mailer * mailer); void mailer_show_preferences(Mailer * mailer, gboolean show) { GtkWidget * notebook; GtkWidget * vbox; GtkWidget * hbox; GtkWidget * vbox2; GtkWidget * vbox3; GtkWidget * widget; GtkSizeGroup * group; GtkListStore * store; size_t i; Account * ac; GtkTreeIter iter; GtkCellRenderer * renderer; if(mailer->pr_window != NULL) { if(show) gtk_widget_show(mailer->pr_window); else gtk_widget_hide(mailer->pr_window); return; } mailer->pr_window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_default_size(GTK_WINDOW(mailer->pr_window), 300, 200); gtk_container_set_border_width(GTK_CONTAINER(mailer->pr_window), 4); gtk_window_set_title(GTK_WINDOW(mailer->pr_window), _("Mailer preferences")); gtk_window_set_transient_for(GTK_WINDOW(mailer->pr_window), GTK_WINDOW( mailer->fo_window)); g_signal_connect_swapped(G_OBJECT(mailer->pr_window), "delete-event", G_CALLBACK(_on_preferences_closex), mailer); vbox = gtk_vbox_new(FALSE, 4); notebook = gtk_notebook_new(); /* accounts */ vbox2 = gtk_vbox_new(FALSE, 4); gtk_container_set_border_width(GTK_CONTAINER(vbox2), 4); hbox = gtk_hbox_new(FALSE, 4); widget = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(widget), GTK_SHADOW_IN); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(widget), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); store = gtk_list_store_new(AC_COUNT, G_TYPE_POINTER, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_STRING); for(i = 0; i < mailer->account_cnt; i++) { ac = mailer->account[i]; gtk_list_store_insert_with_values(store, &iter, -1, AC_DATA, ac, AC_ACTIVE, TRUE, AC_ENABLED, account_get_enabled(ac), AC_TITLE, account_get_title(ac), AC_TYPE, account_get_type(ac), -1); } mailer->pr_accounts = gtk_tree_view_new_with_model(GTK_TREE_MODEL( store)); gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(mailer->pr_accounts), TRUE); gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(mailer->pr_accounts), TRUE); renderer = gtk_cell_renderer_toggle_new(); g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK( _on_preferences_account_toggle), store); gtk_tree_view_append_column(GTK_TREE_VIEW(mailer->pr_accounts), gtk_tree_view_column_new_with_attributes(_("Enabled"), renderer, "active", AC_ENABLED, NULL)); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_append_column(GTK_TREE_VIEW(mailer->pr_accounts), gtk_tree_view_column_new_with_attributes(_("Name"), renderer, "text", AC_TITLE, NULL)); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_append_column(GTK_TREE_VIEW(mailer->pr_accounts), gtk_tree_view_column_new_with_attributes(_("Type"), renderer, "text", AC_TYPE, NULL)); gtk_container_add(GTK_CONTAINER(widget), mailer->pr_accounts); gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0); vbox3 = gtk_vbox_new(FALSE, 4); widget = gtk_button_new_from_stock(GTK_STOCK_NEW); g_signal_connect_swapped(G_OBJECT(widget), "clicked", G_CALLBACK( _on_preferences_account_new), mailer); gtk_box_pack_start(GTK_BOX(vbox3), widget, FALSE, TRUE, 0); #if GTK_CHECK_VERSION(2, 6, 0) widget = gtk_button_new_from_stock(GTK_STOCK_EDIT); #else widget = gtk_button_new_with_mnemonic("_Edit"); #endif g_signal_connect_swapped(G_OBJECT(widget), "clicked", G_CALLBACK( _on_preferences_account_edit), mailer); gtk_box_pack_start(GTK_BOX(vbox3), widget, FALSE, TRUE, 0); widget = gtk_button_new_from_stock(GTK_STOCK_DELETE); g_signal_connect_swapped(G_OBJECT(widget), "clicked", G_CALLBACK( _on_preferences_account_delete), mailer); gtk_box_pack_start(GTK_BOX(vbox3), widget, FALSE, TRUE, 0); gtk_box_pack_start(GTK_BOX(hbox), vbox3, FALSE, TRUE, 0); gtk_box_pack_start(GTK_BOX(vbox2), hbox, TRUE, TRUE, 0); gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox2, gtk_label_new( _("Accounts"))); /* display */ vbox2 = gtk_vbox_new(FALSE, 4); gtk_container_set_border_width(GTK_CONTAINER(vbox2), 4); group = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); /* default font */ hbox = gtk_hbox_new(FALSE, 4); widget = gtk_label_new(_("Messages font:")); gtk_misc_set_alignment(GTK_MISC(widget), 0.0, 0.5); gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, TRUE, 0); mailer->pr_messages_font = gtk_font_button_new(); widget = mailer->pr_messages_font; gtk_size_group_add_widget(group, widget); gtk_font_button_set_use_font(GTK_FONT_BUTTON(widget), TRUE); gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(vbox2), hbox, FALSE, FALSE, 0); gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox2, gtk_label_new( _("Display"))); gtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 0); /* dialog */ hbox = gtk_hbox_new(FALSE, 4); group = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); widget = gtk_button_new_from_stock(GTK_STOCK_OK); gtk_size_group_add_widget(group, widget); g_signal_connect_swapped(G_OBJECT(widget), "clicked", G_CALLBACK( _on_preferences_ok), mailer); gtk_box_pack_end(GTK_BOX(hbox), widget, FALSE, TRUE, 0); widget = gtk_button_new_from_stock(GTK_STOCK_CANCEL); gtk_size_group_add_widget(group, widget); g_signal_connect_swapped(G_OBJECT(widget), "clicked", G_CALLBACK( _on_preferences_cancel), mailer); gtk_box_pack_end(GTK_BOX(hbox), widget, FALSE, TRUE, 0); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); gtk_container_add(GTK_CONTAINER(mailer->pr_window), vbox); _preferences_set(mailer); gtk_widget_show_all(vbox); if(show) gtk_widget_show(mailer->pr_window); else gtk_widget_hide(mailer->pr_window); } static void _preferences_set(Mailer * mailer) { char const * p; if((p = mailer_get_config(mailer, "messages_font")) == NULL) p = MAILER_MESSAGES_FONT; gtk_font_button_set_font_name(GTK_FONT_BUTTON(mailer->pr_messages_font), p); } static gboolean _on_preferences_closex(gpointer data) { Mailer * mailer = data; gtk_widget_hide(mailer->pr_window); return TRUE; } /* _on_preferences_account_new */ /* types */ typedef struct _AccountData { Mailer * mailer; char * type; AccountIdentity identity; unsigned int available; Account * account; GtkWidget * assistant; GtkWidget * settings; GtkWidget * confirm; } AccountData; /* functions */ static GtkWidget * _assistant_account_select(AccountData * ad); static GtkWidget * _assistant_account_config(AccountConfig * config); #if !GTK_CHECK_VERSION(2, 10, 0) # include "gtkassistant.c" #endif static void _on_assistant_cancel(GtkWidget * widget, gpointer data); static void _on_assistant_close(GtkWidget * widget, gpointer data); static void _on_assistant_apply(GtkWidget * widget, gpointer data); static void _on_assistant_prepare(GtkWidget * widget, GtkWidget * page, gpointer data); static void _on_entry_changed(GtkWidget * widget, gpointer data); static void _on_account_type_changed(GtkWidget * widget, gpointer data); static void _on_preferences_account_new(gpointer data) { Mailer * mailer = data; AccountData * ad; GtkAssistant * assistant; GtkWidget * page; if(mailer->available_cnt == 0) { mailer_error(mailer, _("No account plug-in available"), 0); return; } if((ad = malloc(sizeof(*ad))) == NULL) { mailer_error(mailer, strerror(errno), 0); return; } ad->mailer = mailer; ad->type = strdup(""); memset(&(ad->identity), 0, sizeof(ad->identity)); ad->available = 0; ad->account = NULL; ad->assistant = gtk_assistant_new(); assistant = GTK_ASSISTANT(ad->assistant); g_signal_connect(G_OBJECT(ad->assistant), "cancel", G_CALLBACK( _on_assistant_cancel), ad); g_signal_connect(G_OBJECT(ad->assistant), "close", G_CALLBACK( _on_assistant_close), ad); g_signal_connect(G_OBJECT(ad->assistant), "apply", G_CALLBACK( _on_assistant_apply), ad); g_signal_connect(G_OBJECT(ad->assistant), "prepare", G_CALLBACK( _on_assistant_prepare), ad); /* plug-in selection */ page = _assistant_account_select(ad); gtk_assistant_append_page(assistant, page); gtk_assistant_set_page_title(assistant, page, _(_title[0])); gtk_assistant_set_page_type(assistant, page, GTK_ASSISTANT_PAGE_INTRO); gtk_assistant_set_page_complete(assistant, page, FALSE); /* plug-in preferences */ page = gtk_vbox_new(FALSE, 0); ad->settings = page; gtk_widget_show(page); gtk_assistant_append_page(assistant, page); gtk_assistant_set_page_title(assistant, page, _(_title[1])); gtk_assistant_set_page_type(assistant, page, GTK_ASSISTANT_PAGE_CONTENT); gtk_assistant_set_page_complete(assistant, page, TRUE); /* confirmation page */ page = gtk_vbox_new(FALSE, 0); ad->confirm = page; gtk_widget_show(page); gtk_assistant_append_page(assistant, page); gtk_assistant_set_page_title(assistant, page, _(_title[2])); gtk_assistant_set_page_type(assistant, page, GTK_ASSISTANT_PAGE_CONFIRM); gtk_assistant_set_page_complete(assistant, page, TRUE); gtk_widget_show(ad->assistant); } static void _on_assistant_cancel(GtkWidget * widget, gpointer data) { _on_assistant_close(widget, data); } static void _on_assistant_close(GtkWidget * widget, gpointer data) { AccountData * ad = data; if(ad->account != NULL) account_delete(ad->account); free(ad); gtk_widget_destroy(widget); } static void _on_assistant_apply(GtkWidget * widget, gpointer data) { AccountData * ad = data; GtkTreeModel * model; GtkTreeIter iter; model = gtk_tree_view_get_model(GTK_TREE_VIEW(ad->mailer->pr_accounts)); gtk_list_store_append(GTK_LIST_STORE(model), &iter); #ifdef DEBUG fprintf(stderr, "%s%p%s%s%s%s\n", "AC_DATA ", (void*)ad->account, ", AC_ACTIVE FALSE, AC_ENABLED TRUE, AC_TITLE ", account_get_title(ad->account), ", AC_TYPE ", account_get_type(ad->account)); #endif gtk_list_store_set(GTK_LIST_STORE(model), &iter, AC_DATA, ad->account, AC_ACTIVE, FALSE, AC_ENABLED, TRUE, AC_TITLE, account_get_title(ad->account), AC_TYPE, account_get_type(ad->account), -1); ad->account = NULL; /* _on_assistant_close is then automatically called */ } /* on_assistant_prepare */ static GtkWidget * _account_display(Account * account); static void _on_assistant_prepare(GtkWidget * widget, GtkWidget * page, gpointer data) { static int old = 0; AccountData * ad = data; unsigned int i; Account * ac; i = gtk_assistant_get_current_page(GTK_ASSISTANT(widget)); gtk_window_set_title(GTK_WINDOW(widget), _(_title[i])); if(i == 1) { /* XXX something is wrong with gtk_container_remove */ gtk_container_remove(GTK_CONTAINER(page), ad->settings); if(old == 0) { if(ad->account != NULL) account_delete(ad->account); ac = ad->mailer->available[ad->available]; ad->account = account_new(ad->mailer, account_get_type(ac), ad->type, NULL); } if(ad->account == NULL) { mailer_error(ad->mailer, error_get(), 0); gtk_assistant_set_current_page(GTK_ASSISTANT(widget), 0); ad->settings = _assistant_account_select(ad); } else ad->settings = _assistant_account_config( account_get_config(ad->account)); gtk_container_add(GTK_CONTAINER(page), ad->settings); gtk_widget_show_all(ad->settings); } else if(i == 2) { gtk_container_remove(GTK_CONTAINER(page), ad->confirm); ad->confirm = _account_display(ad->account); gtk_container_add(GTK_CONTAINER(page), ad->confirm); } old = i; } /* _assistant_account_select */ static void _on_account_name_changed(GtkWidget * widget, gpointer data); static void _account_add_label(GtkWidget * box, PangoFontDescription * desc, GtkSizeGroup * group, char const * text); static GtkWidget * _assistant_account_select(AccountData * ad) { GtkWidget * vbox; GtkWidget * hbox; GtkSizeGroup * group; PangoFontDescription * desc; GtkWidget * widget; unsigned int i; vbox = gtk_vbox_new(FALSE, 4); gtk_container_set_border_width(GTK_CONTAINER(vbox), 4); group = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); hbox = gtk_hbox_new(FALSE, 4); desc = pango_font_description_new(); pango_font_description_set_weight(desc, PANGO_WEIGHT_BOLD); _account_add_label(hbox, desc, group, _("Account name")); widget = gtk_entry_new(); g_signal_connect(G_OBJECT(widget), "changed", G_CALLBACK( _on_account_name_changed), ad); gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); hbox = gtk_hbox_new(FALSE, 4); _account_add_label(hbox, NULL, group, _("Your name")); widget = gtk_entry_new(); g_signal_connect(G_OBJECT(widget), "changed", G_CALLBACK( _on_entry_changed), &(ad->identity.from)); gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); hbox = gtk_hbox_new(FALSE, 4); _account_add_label(hbox, NULL, group, _("e-mail address")); widget = gtk_entry_new(); g_signal_connect(G_OBJECT(widget), "changed", G_CALLBACK( _on_entry_changed), &(ad->identity.email)); gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); hbox = gtk_hbox_new(FALSE, 4); _account_add_label(hbox, NULL, group, _("Type of account")); #if GTK_CHECK_VERSION(3, 0, 0) widget = gtk_combo_box_text_new(); #else widget = gtk_combo_box_new_text(); #endif /* XXX this works because there is no plug-in list reload * would it be implemented this will need validation later */ for(i = 0; i < ad->mailer->available_cnt; i++) #if GTK_CHECK_VERSION(3, 0, 0) gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(widget), NULL, account_get_name(ad->mailer->available[i])); #else gtk_combo_box_append_text(GTK_COMBO_BOX(widget), account_get_name(ad->mailer->available[i])); #endif gtk_combo_box_set_active(GTK_COMBO_BOX(widget), 0); g_signal_connect(G_OBJECT(widget), "changed", G_CALLBACK( _on_account_type_changed), ad); gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); pango_font_description_free(desc); gtk_widget_show_all(vbox); return vbox; } static void _on_account_name_changed(GtkWidget * widget, gpointer data) { AccountData * ad = data; int current; GtkWidget * page; _on_entry_changed(widget, &ad->type); current = gtk_assistant_get_current_page(GTK_ASSISTANT(ad->assistant)); page = gtk_assistant_get_nth_page(GTK_ASSISTANT(ad->assistant), current); gtk_assistant_set_page_complete(GTK_ASSISTANT(ad->assistant), page, strlen(ad->type) ? TRUE : FALSE); } static void _account_add_label(GtkWidget * box, PangoFontDescription * desc, GtkSizeGroup * group, char const * text) { static char buf[80]; /* XXX hard-coded size */ GtkWidget * label; snprintf(buf, sizeof(buf), "%s:", text); label = gtk_label_new(buf); if(desc != NULL) gtk_widget_modify_font(label, desc); if(group != NULL) gtk_size_group_add_widget(group, label); gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5); gtk_box_pack_start(GTK_BOX(box), label, FALSE, TRUE, 0); } /* _assistant_account_config */ static GtkWidget * _update_string(AccountConfig * config, PangoFontDescription * desc, GtkSizeGroup * group); static GtkWidget * _update_password(AccountConfig * config, PangoFontDescription * desc, GtkSizeGroup * group); static GtkWidget * _update_file(AccountConfig * config, PangoFontDescription * desc, GtkSizeGroup * group); static GtkWidget * _update_uint16(AccountConfig * config, PangoFontDescription * desc, GtkSizeGroup * group); static GtkWidget * _update_boolean(AccountConfig * config); static GtkWidget * _assistant_account_config(AccountConfig * config) { GtkWidget * vbox; GtkSizeGroup * group; GtkWidget * widget; size_t i; vbox = gtk_vbox_new(FALSE, 4); gtk_container_set_border_width(GTK_CONTAINER(vbox), 4); group = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); for(i = 0; config != NULL && config[i].name != NULL; i++) { switch(config[i].type) { case ACT_NONE: continue; case ACT_STRING: widget = _update_string(&config[i], NULL, group); break; case ACT_PASSWORD: widget = _update_password(&config[i], NULL, group); break; case ACT_FILE: widget = _update_file(&config[i], NULL, group); break; case ACT_UINT16: widget = _update_uint16(&config[i], NULL, group); break; case ACT_BOOLEAN: widget = _update_boolean(&config[i]); break; default: /* should not happen */ continue; } gtk_box_pack_start(GTK_BOX(vbox), widget, FALSE, TRUE, 0); } return vbox; } static GtkWidget * _update_string(AccountConfig * config, PangoFontDescription * desc, GtkSizeGroup * group) { GtkWidget * hbox; GtkWidget * widget; hbox = gtk_hbox_new(FALSE, 0); _account_add_label(hbox, desc, group, config->title); widget = gtk_entry_new(); if(config->value != NULL) gtk_entry_set_text(GTK_ENTRY(widget), config->value); g_signal_connect(G_OBJECT(widget), "changed", G_CALLBACK( _on_entry_changed), &config->value); gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0); return hbox; } static GtkWidget * _update_password(AccountConfig * config, PangoFontDescription * desc, GtkSizeGroup * group) { GtkWidget * hbox; GtkWidget * widget; hbox = gtk_hbox_new(FALSE, 0); _account_add_label(hbox, desc, group, config->title); widget = gtk_entry_new(); gtk_entry_set_visibility(GTK_ENTRY(widget), FALSE); if(config->value != NULL) gtk_entry_set_text(GTK_ENTRY(widget), config->value); g_signal_connect(G_OBJECT(widget), "changed", G_CALLBACK( _on_entry_changed), &config->value); gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0); return hbox; } static void _on_file_activated(GtkWidget * widget, gpointer data); static GtkWidget * _update_file(AccountConfig * config, PangoFontDescription * desc, GtkSizeGroup * group) { GtkWidget * hbox; GtkWidget * widget; hbox = gtk_hbox_new(FALSE, 0); _account_add_label(hbox, desc, group, config->title); widget = gtk_file_chooser_button_new(_("Choose file"), GTK_FILE_CHOOSER_ACTION_OPEN); if(config->value != NULL) gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(widget), config->value); gtk_file_chooser_button_set_title(GTK_FILE_CHOOSER_BUTTON(widget), config->title); g_signal_connect(G_OBJECT(widget), "file-set", G_CALLBACK( _on_file_activated), &config->value); gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0); return hbox; } static void _on_file_activated(GtkWidget * widget, gpointer data) { char * filename; char ** value = data; char * p = NULL; filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(widget)); #ifdef DEBUG fprintf(stderr, "DEBUG: %s(%s)\n", __func__, filename); #endif if(filename == NULL) free(*value); else if((p = realloc(*value, strlen(filename) + 1)) == NULL) { mailer_error(NULL, strerror(errno), 0); return; } *value = p; if(filename != NULL) strcpy(p, filename); } static void _on_uint16_changed(GtkWidget * widget, gpointer data); static GtkWidget * _update_uint16(AccountConfig * config, PangoFontDescription * desc, GtkSizeGroup * group) { GtkWidget * hbox; GtkWidget * widget; uint16_t u16 = (intptr_t)(config->value); gdouble value = u16; hbox = gtk_hbox_new(FALSE, 0); _account_add_label(hbox, desc, group, config->title); widget = gtk_spin_button_new_with_range(0, 65535, 1); gtk_spin_button_set_digits(GTK_SPIN_BUTTON(widget), 0); gtk_spin_button_set_value(GTK_SPIN_BUTTON(widget), value); g_signal_connect(G_OBJECT(widget), "value-changed", G_CALLBACK( _on_uint16_changed), &config->value); gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0); return hbox; } static void _on_uint16_changed(GtkWidget * widget, gpointer data) { int * value = data; *value = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget)); #ifdef DEBUG fprintf(stderr, "DEBUG: new value is %d\n", *value); #endif } static void _on_boolean_toggled(GtkWidget * widget, gpointer data); static GtkWidget * _update_boolean(AccountConfig * config) { GtkWidget * widget; widget = gtk_check_button_new_with_label(config->title); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), config->value != NULL); g_signal_connect(G_OBJECT(widget), "toggled", G_CALLBACK( _on_boolean_toggled), &config->value); return widget; } static void _on_boolean_toggled(GtkWidget * widget, gpointer data) { int * value = data; *value = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)); } static GtkWidget * _display_string(AccountConfig * config, PangoFontDescription * desc, GtkSizeGroup * group); static GtkWidget * _display_password(AccountConfig * config, PangoFontDescription * desc, GtkSizeGroup * group); static GtkWidget * _display_file(AccountConfig * config, PangoFontDescription * desc, GtkSizeGroup * group); static GtkWidget * _display_uint16(AccountConfig * config, PangoFontDescription * desc, GtkSizeGroup * group); static GtkWidget * _display_boolean(AccountConfig * config, PangoFontDescription * desc, GtkSizeGroup * group); static GtkWidget * _account_display(Account * account) { AccountConfig * config; AccountConfig p; GtkWidget * vbox; GtkSizeGroup * group; PangoFontDescription * desc; GtkWidget * widget; unsigned int i; config = account_get_config(account); vbox = gtk_vbox_new(FALSE, 4); gtk_container_set_border_width(GTK_CONTAINER(vbox), 4); group = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); p.name = NULL; p.title = _("Account name"); p.value = account_get_title(account); desc = pango_font_description_new(); pango_font_description_set_weight(desc, PANGO_WEIGHT_BOLD); widget = _display_string(&p, desc, group); gtk_box_pack_start(GTK_BOX(vbox), widget, FALSE, TRUE, 0); for(i = 0; config[i].name != NULL; i++) { switch(config[i].type) { case ACT_NONE: continue; case ACT_STRING: widget = _display_string(&config[i], desc, group); break; case ACT_PASSWORD: widget = _display_password(&config[i], desc, group); break; case ACT_FILE: widget = _display_file(&config[i], desc, group); break; case ACT_UINT16: widget = _display_uint16(&config[i], desc, group); break; case ACT_BOOLEAN: widget = _display_boolean(&config[i], desc, group); break; default: /* should not happen */ assert(0); continue; } gtk_box_pack_start(GTK_BOX(vbox), widget, FALSE, TRUE, 0); } pango_font_description_free(desc); gtk_widget_show_all(vbox); return vbox; } static GtkWidget * _display_string(AccountConfig * config, PangoFontDescription * desc, GtkSizeGroup * group) { GtkWidget * hbox; GtkWidget * widget; hbox = gtk_hbox_new(FALSE, 0); _account_add_label(hbox, desc, group, config->title); widget = gtk_label_new(config->value); gtk_misc_set_alignment(GTK_MISC(widget), 0.0, 0.5); gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0); return hbox; } static GtkWidget * _display_file(AccountConfig * config, PangoFontDescription * desc, GtkSizeGroup * group) { return _display_string(config, desc, group); } static GtkWidget * _display_password(AccountConfig * config, PangoFontDescription * desc, GtkSizeGroup * group) { GtkWidget * hbox; GtkWidget * widget; hbox = gtk_hbox_new(FALSE, 0); _account_add_label(hbox, desc, group, config->title); widget = gtk_label_new(_("hidden")); desc = pango_font_description_new(); pango_font_description_set_style(desc, PANGO_STYLE_ITALIC); gtk_widget_modify_font(widget, desc); gtk_misc_set_alignment(GTK_MISC(widget), 0.0, 0.5); gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0); return hbox; } static GtkWidget * _display_uint16(AccountConfig * config, PangoFontDescription * desc, GtkSizeGroup * group) { GtkWidget * hbox; GtkWidget * widget; char buf[6]; uint16_t u16 = (intptr_t)config->value; hbox = gtk_hbox_new(FALSE, 0); _account_add_label(hbox, desc, group, config->title); snprintf(buf, sizeof(buf), "%hu", u16); widget = gtk_label_new(buf); gtk_misc_set_alignment(GTK_MISC(widget), 0.0, 0.5); gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0); return hbox; } static GtkWidget * _display_boolean(AccountConfig * config, PangoFontDescription * desc, GtkSizeGroup * group) { GtkWidget * hbox; GtkWidget * widget; hbox = gtk_hbox_new(FALSE, 0); _account_add_label(hbox, desc, group, config->title); widget = gtk_label_new(config->value != 0 ? _("Yes") : _("No")); gtk_misc_set_alignment(GTK_MISC(widget), 0.0, 0.5); gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0); return hbox; } static void _on_entry_changed(GtkWidget * widget, gpointer data) { const char * text; char ** value = data; char * p; text = gtk_entry_get_text(GTK_ENTRY(widget)); if((p = realloc(*value, strlen(text) + 1)) == NULL) { mailer_error(NULL, strerror(errno), 0); return; } *value = p; strcpy(p, text); } static void _on_account_type_changed(GtkWidget * widget, gpointer data) { AccountData * ad = data; ad->available = gtk_combo_box_get_active(GTK_COMBO_BOX(widget)); #ifdef DEBUG fprintf(stderr, "%s%u%s", _("Account type "), ad->available, _(" active\n")); #endif } static void _on_preferences_account_toggle(GtkCellRendererToggle * renderer, char * path, gpointer data) { GtkListStore * store = data; GtkTreeIter iter; gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(store), &iter, path); gtk_list_store_set(store, &iter, AC_ENABLED, !gtk_cell_renderer_toggle_get_active(renderer), -1); } /* _on_preferences_account_edit */ static void _account_edit(Mailer * mailer, Account * account); static void _on_preferences_account_edit(gpointer data) { Mailer * mailer = data; GtkTreeSelection * selection; GtkTreeModel * model; GtkTreeIter iter; Account * account; selection = gtk_tree_view_get_selection(GTK_TREE_VIEW( mailer->pr_accounts)); if(gtk_tree_selection_get_selected(selection, &model, &iter) != TRUE) return; gtk_tree_model_get(model, &iter, AC_DATA, &account, -1); _account_edit(mailer, account); } static void _account_edit(Mailer * mailer, Account * account) { GtkWidget * window; char buf[80]; GtkWidget * vbox; GtkWidget * widget; GtkWidget * hbox; GtkSizeGroup * group; window = gtk_window_new(GTK_WINDOW_TOPLEVEL); snprintf(buf, sizeof(buf), "%s%s", _("Edit account: "), account_get_title(account)); gtk_window_set_title(GTK_WINDOW(window), buf); gtk_window_set_transient_for(GTK_WINDOW(window), GTK_WINDOW( mailer->fo_window)); gtk_window_set_modal(GTK_WINDOW(window), TRUE); vbox = gtk_vbox_new(FALSE, 4); /* FIXME also allow to modify the identity, plug-in values... */ /* FIXME this affects the account directly (eg cancel does not) */ widget = _assistant_account_config(account_get_config(account)); gtk_box_pack_start(GTK_BOX(vbox), widget, TRUE, TRUE, 0); hbox = gtk_hbox_new(FALSE, 0); widget = gtk_hseparator_new(); gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 4); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0); hbox = gtk_hbox_new(FALSE, 0); group = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); widget = gtk_button_new_from_stock(GTK_STOCK_OK); gtk_size_group_add_widget(group, widget); /* FIXME implement properly */ g_signal_connect_swapped(G_OBJECT(widget), "clicked", G_CALLBACK( gtk_widget_destroy), window); gtk_box_pack_end(GTK_BOX(hbox), widget, FALSE, TRUE, 4); widget = gtk_button_new_from_stock(GTK_STOCK_CANCEL); gtk_size_group_add_widget(group, widget); g_signal_connect_swapped(G_OBJECT(widget), "clicked", G_CALLBACK( gtk_widget_destroy), window); gtk_box_pack_end(GTK_BOX(hbox), widget, FALSE, TRUE, 0); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 4); gtk_container_add(GTK_CONTAINER(window), vbox); gtk_widget_show_all(window); } static void _on_preferences_account_delete(gpointer data) { Mailer * mailer = data; GtkTreePath * path; GtkTreeModel * model; GtkTreeIter iter; gtk_tree_view_get_cursor(GTK_TREE_VIEW(mailer->pr_accounts), &path, NULL); if(path == NULL) return; model = gtk_tree_view_get_model(GTK_TREE_VIEW(mailer->pr_accounts)); gtk_tree_model_get_iter(model, &iter, path); gtk_tree_path_free(path); gtk_list_store_remove(GTK_LIST_STORE(model), &iter); /* FIXME non-interface code (flag account as deleted and on ok apply) */ } static void _on_preferences_cancel(gpointer data) { Mailer * mailer = data; gtk_widget_hide(mailer->pr_window); _preferences_set(mailer); } static void _on_preferences_ok(gpointer data) { Mailer * mailer = data; mailer_show_preferences(mailer, FALSE); if(_preferences_ok_accounts(mailer) != 0 || _preferences_ok_display(mailer) != 0 || _preferences_ok_save(mailer) != 0) mailer_error(mailer, _("An error occured while saving" " preferences"), 0); } static int _preferences_ok_accounts(Mailer * mailer) { GtkTreeModel * model; GtkTreeIter iter; GtkTreeModel * view_model; gboolean loop; Account * account; gboolean active; gboolean enabled; char * title; size_t title_len; char * accounts = NULL; size_t accounts_len = 0; char * p; model = gtk_tree_view_get_model(GTK_TREE_VIEW(mailer->pr_accounts)); view_model = gtk_tree_view_get_model(GTK_TREE_VIEW( mailer->fo_view)); for(loop = gtk_tree_model_get_iter_first(model, &iter); loop == TRUE; loop = gtk_tree_model_iter_next(model, &iter)) { gtk_tree_model_get(model, &iter, AC_DATA, &account, AC_ACTIVE, &active, AC_ENABLED, &enabled, AC_TITLE, &title, -1); title_len = strlen(title); if(account_config_save(account, mailer->config) != 0) return 1; if((p = realloc(accounts, accounts_len + title_len + 2)) == NULL) { free(accounts); return 1; } accounts = p; sprintf(&accounts[accounts_len], "%s%s", accounts_len ? "," : "", title); accounts_len += title_len + (accounts_len ? 1 : 0); if(active) { if(enabled) continue; #if 0 /* FIXME API+behaviour change here */ if(mailer_account_disable(mailer, account) == 0) gtk_list_store_set(GTK_LIST_STORE(model), &iter, AC_ACTIVE, FALSE, -1); #endif } else if(enabled && mailer_account_add(mailer, account) == 0) gtk_list_store_set(GTK_LIST_STORE(model), &iter, AC_ACTIVE, TRUE, -1); } /* FIXME delete accounts that need to be */ /* FIXME force a refresh of the ones remaining and not just added */ #ifdef DEBUG fprintf(stderr, "DEBUG: saved accounts \"%s\"\n", accounts); #endif config_set(mailer->config, "", "accounts", accounts); free(accounts); return 0; } static int _preferences_ok_display(Mailer * mailer) { PangoFontDescription * font = NULL; char const * p; p = gtk_font_button_get_font_name(GTK_FONT_BUTTON( mailer->pr_messages_font)); config_set(mailer->config, "", "messages_font", p); if(p != NULL) font = pango_font_description_from_string(p); gtk_widget_modify_font(mailer->bo_view, font); if(font != NULL) pango_font_description_free(font); return 0; } static int _preferences_ok_save(Mailer * mailer) { int ret; char * p; if((p = _mailer_get_config_filename()) == NULL) return 1; ret = config_save(mailer->config, p); free(p); return ret; } /* mailer_unselect_all */ void mailer_unselect_all(Mailer * mailer) { GtkTreeSelection * treesel; #if GTK_CHECK_VERSION(2, 4, 0) GtkTextBuffer * tbuf; GtkTextMark * mark; GtkTextIter iter; #endif if(gtk_window_get_focus(GTK_WINDOW(mailer->fo_window)) == mailer->bo_view) { #if GTK_CHECK_VERSION(2, 4, 0) tbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(mailer->bo_view)); mark = gtk_text_buffer_get_mark(tbuf, "insert"); gtk_text_buffer_get_iter_at_mark(tbuf, &iter, mark); gtk_text_buffer_select_range(tbuf, &iter, &iter); #endif return; } treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW(mailer->he_view)); gtk_tree_selection_unselect_all(treesel); } /* private */ /* functions */ /* mailer_confirm */ static gboolean _mailer_confirm(Mailer * mailer, char const * message) { GtkWidget * dialog; int res; dialog = gtk_message_dialog_new(GTK_WINDOW(mailer->fo_window), GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, "%s", #if GTK_CHECK_VERSION(2, 8, 0) _("Question")); gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog), "%s", #endif message); gtk_window_set_title(GTK_WINDOW(dialog), _("Question")); res = gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy(dialog); if(res == GTK_RESPONSE_YES) return TRUE; return FALSE; } /* mailer_foreach_message_selected */ static void _mailer_foreach_message_selected(Mailer * mailer, MailerForeachMessageCallback callback) { GtkTreeModel * model; GtkTreeSelection * treesel; GList * selected; GList * s; GtkTreePath * path; GtkTreeIter iter; if((model = gtk_tree_view_get_model(GTK_TREE_VIEW(mailer->he_view))) == NULL) return; if((treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW( mailer->he_view))) == NULL) return; if((selected = gtk_tree_selection_get_selected_rows(treesel, NULL)) == NULL) return; for(s = g_list_first(selected); s != NULL; s = g_list_next(s)) { if((path = s->data) == NULL) continue; gtk_tree_model_get_iter(model, &iter, path); callback(mailer, model, &iter); } g_list_free(selected); } /* mailer_get_config_filename */ static char * _mailer_get_config_filename(void) /* FIXME consider replacing with mailer_save_config() */ { char const * homedir; char * filename; if((homedir = getenv("HOME")) == NULL) return NULL; if((filename = malloc(strlen(homedir) + sizeof(MAILER_CONFIG_FILE) + 1)) == NULL) return NULL; sprintf(filename, "%s/%s", homedir, MAILER_CONFIG_FILE); return filename; } /* mailer_update_status */ static void _mailer_update_status(Mailer * mailer) { GtkTreeModel * store; int cnt; char buf[256]; if((store = gtk_tree_view_get_model(GTK_TREE_VIEW(mailer->he_view))) != NULL) { cnt = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(store), NULL); snprintf(buf, sizeof(buf), _("%s/%s: %d %s"), account_get_name(mailer->account_cur), folder_get_name(mailer->folder_cur), cnt, (cnt > 1) ? _("messages") : _("message")); } else snprintf(buf, sizeof(buf), "%s", _("Ready")); mailer_set_status(mailer, buf); }