1 /*
2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2018 Hiroyuki Yamamoto
4 *
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 2 of the License, or
8 * (at your option) any later version.
9 *
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.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 */
19
20 #include "defs.h"
21
22 #include <glib.h>
23 #include <glib/gi18n.h>
24 #include <gdk/gdkkeysyms.h>
25 #include <gtk/gtkversion.h>
26 #include <gtk/gtkwidget.h>
27 #include <gtk/gtklabel.h>
28 #include <gtk/gtkoptionmenu.h>
29 #include <gtk/gtkentry.h>
30 #include <gtk/gtktooltips.h>
31 #include <gtk/gtkscrolledwindow.h>
32 #include <gtk/gtktreestore.h>
33 #include <gtk/gtktreeview.h>
34 #include <gtk/gtktreeselection.h>
35 #include <gtk/gtkcellrendererpixbuf.h>
36 #include <gtk/gtkcellrenderertext.h>
37 #include <gtk/gtksignal.h>
38 #include <gtk/gtkmenu.h>
39 #include <gtk/gtkmenuitem.h>
40 #include <gtk/gtkcheckmenuitem.h>
41 #include <gtk/gtkitemfactory.h>
42 #include <gtk/gtkvbox.h>
43 #include <gtk/gtkhbox.h>
44 #include <gtk/gtkwindow.h>
45 #include <gtk/gtkhseparator.h>
46 #include <gtk/gtkarrow.h>
47 #include <gtk/gtkeventbox.h>
48 #include <gtk/gtkstatusbar.h>
49 #include <gtk/gtkstock.h>
50 #include <gtk/gtknotebook.h>
51
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <ctype.h>
56 #include <unistd.h>
57
58 #include "main.h"
59 #include "menu.h"
60 #include "mainwindow.h"
61 #include "folderview.h"
62 #include "summaryview.h"
63 #include "messageview.h"
64 #include "foldersel.h"
65 #include "procmsg.h"
66 #include "procheader.h"
67 #include "sourcewindow.h"
68 #include "prefs_common.h"
69 #include "prefs_summary_column.h"
70 #include "prefs_filter.h"
71 #include "account.h"
72 #include "compose.h"
73 #include "utils.h"
74 #include "gtkutils.h"
75 #include "stock_pixmap.h"
76 #include "filesel.h"
77 #include "alertpanel.h"
78 #include "inputdialog.h"
79 #include "statusbar.h"
80 #include "trayicon.h"
81 #include "printing.h"
82 #include "filter.h"
83 #include "folder.h"
84 #include "colorlabel.h"
85 #include "inc.h"
86 #include "imap.h"
87 #include "plugin.h"
88
89 #define STATUSBAR_PUSH(mainwin, str) \
90 { \
91 gtk_statusbar_push(GTK_STATUSBAR(mainwin->statusbar), \
92 mainwin->summaryview_cid, str); \
93 gtkut_widget_draw_now(mainwin->statusbar); \
94 }
95
96 #define STATUSBAR_POP(mainwin) \
97 { \
98 gtk_statusbar_pop(GTK_STATUSBAR(mainwin->statusbar), \
99 mainwin->summaryview_cid); \
100 }
101
102 #define GET_MSG_INFO(msginfo, iter__) \
103 { \
104 gtk_tree_model_get(GTK_TREE_MODEL(summaryview->store), iter__, \
105 S_COL_MSG_INFO, &msginfo, -1); \
106 }
107
108 #define SORT_BLOCK(key) \
109 if (summaryview->folder_item->sort_key == key) { \
110 sort_key = key; \
111 sort_type = summaryview->folder_item->sort_type; \
112 summary_sort(summaryview, SORT_BY_NONE, SORT_ASCENDING); \
113 }
114
115 #define SORT_UNBLOCK(key) \
116 if (sort_key == key) \
117 summary_sort(summaryview, sort_key, sort_type);
118
119 #define SUMMARY_DISPLAY_TOTAL_NUM(item) \
120 (summaryview->on_filter ? summaryview->flt_msg_total : item->total)
121
122 #ifdef G_OS_WIN32
123 # define SUMMARY_COL_MARK_WIDTH 23
124 # define SUMMARY_COL_UNREAD_WIDTH 26
125 # define SUMMARY_COL_MIME_WIDTH 21
126 #else
127 # define SUMMARY_COL_MARK_WIDTH 21
128 # define SUMMARY_COL_UNREAD_WIDTH 24
129 # define SUMMARY_COL_MIME_WIDTH 19
130 #endif
131
132 static GdkPixbuf *mark_pixbuf;
133 static GdkPixbuf *deleted_pixbuf;
134
135 static GdkPixbuf *mail_pixbuf;
136 static GdkPixbuf *new_pixbuf;
137 static GdkPixbuf *unread_pixbuf;
138 static GdkPixbuf *replied_pixbuf;
139 static GdkPixbuf *forwarded_pixbuf;
140
141 static GdkPixbuf *clip_pixbuf;
142 static GdkPixbuf *html_pixbuf;
143
144 static void summary_clear_list_full (SummaryView *summaryview,
145 gboolean is_refresh);
146
147 static GList *summary_get_selected_rows (SummaryView *summaryview);
148 static void summary_selection_list_free (SummaryView *summaryview);
149
150 static GSList *summary_get_tmp_marked_msg_list
151 (SummaryView *summaryview);
152 static void summary_restore_tmp_marks (SummaryView *summaryview,
153 GSList *save_mark_mlist);
154
155 static void summary_update_msg_list (SummaryView *summaryview);
156
157 static void summary_msgid_table_create (SummaryView *summaryview);
158 static void summary_msgid_table_destroy (SummaryView *summaryview);
159
160 static void summary_set_menu_sensitive (SummaryView *summaryview);
161
162 static void summary_scroll_to_selected (SummaryView *summaryview,
163 gboolean align_center);
164
165 static MsgInfo *summary_get_msginfo (SummaryView *summaryview,
166 GtkTreeRowReference *row);
167 static guint summary_get_msgnum (SummaryView *summaryview,
168 GtkTreeRowReference *row);
169
170 static gboolean summary_find_prev_msg (SummaryView *summaryview,
171 GtkTreeIter *prev,
172 GtkTreeIter *iter);
173 static gboolean summary_find_next_msg (SummaryView *summaryview,
174 GtkTreeIter *next,
175 GtkTreeIter *iter);
176
177 static gboolean summary_find_nearest_msg(SummaryView *summaryview,
178 GtkTreeIter *target,
179 GtkTreeIter *iter);
180
181 static gboolean summary_find_prev_flagged_msg
182 (SummaryView *summaryview,
183 GtkTreeIter *prev,
184 GtkTreeIter *iter,
185 MsgPermFlags flags,
186 gboolean start_from_prev);
187 static gboolean summary_find_next_flagged_msg
188 (SummaryView *summaryview,
189 GtkTreeIter *next,
190 GtkTreeIter *iter,
191 MsgPermFlags flags,
192 gboolean start_from_next);
193
194 static gboolean summary_find_msg_by_msgnum
195 (SummaryView *summaryview,
196 guint msgnum,
197 GtkTreeIter *found);
198
199 static void summary_update_display_state(SummaryView *summaryview,
200 guint disp_msgnum,
201 guint sel_msgnum);
202
203 static void summary_update_status (SummaryView *summaryview);
204
205 /* display functions */
206 static void summary_status_show (SummaryView *summaryview);
207 static void summary_set_row (SummaryView *summaryview,
208 GtkTreeIter *iter,
209 MsgInfo *msginfo);
210 static void summary_set_tree_model_from_list
211 (SummaryView *summaryview,
212 GSList *mlist);
213 static gboolean summary_row_is_displayed(SummaryView *summaryview,
214 GtkTreeIter *iter);
215 static void summary_display_msg (SummaryView *summaryview,
216 GtkTreeIter *iter);
217 static void summary_display_msg_full (SummaryView *summaryview,
218 GtkTreeIter *iter,
219 gboolean new_window,
220 gboolean all_headers,
221 gboolean redisplay);
222
223 static void summary_activate_selected (SummaryView *summaryview);
224
225 /* message handling */
226 static void summary_mark_row (SummaryView *summaryview,
227 GtkTreeIter *iter);
228 static void summary_mark_row_as_read (SummaryView *summaryview,
229 GtkTreeIter *iter);
230 static void summary_mark_row_as_unread (SummaryView *summaryview,
231 GtkTreeIter *iter);
232 static void summary_delete_row (SummaryView *summaryview,
233 GtkTreeIter *iter);
234 static void summary_unmark_row (SummaryView *summaryview,
235 GtkTreeIter *iter);
236 static void summary_move_row_to (SummaryView *summaryview,
237 GtkTreeIter *iter,
238 FolderItem *to_folder);
239 static void summary_copy_row_to (SummaryView *summaryview,
240 GtkTreeIter *iter,
241 FolderItem *to_folder);
242
243 static void summary_remove_invalid_messages
244 (SummaryView *summaryview);
245
246 static gint summary_execute_move (SummaryView *summaryview);
247 static gint summary_execute_copy (SummaryView *summaryview);
248 static gint summary_execute_delete (SummaryView *summaryview);
249
250 static void summary_modify_threads (SummaryView *summaryview);
251
252 static void summary_colorlabel_menu_item_activate_cb
253 (GtkWidget *widget,
254 gpointer data);
255 static void summary_colorlabel_menu_item_activate_item_cb
256 (GtkMenuItem *label_menu_item,
257 gpointer data);
258 static void summary_colorlabel_menu_create
259 (SummaryView *summaryview);
260
261 static GtkWidget *summary_tree_view_create
262 (SummaryView *summaryview);
263
264 /* callback functions */
265 static gboolean summary_toggle_pressed (GtkWidget *eventbox,
266 GdkEventButton *event,
267 SummaryView *summaryview);
268 static gboolean summary_button_pressed (GtkWidget *treeview,
269 GdkEventButton *event,
270 SummaryView *summaryview);
271 static gboolean summary_button_released (GtkWidget *treeview,
272 GdkEventButton *event,
273 SummaryView *summaryview);
274 static gboolean summary_key_pressed (GtkWidget *treeview,
275 GdkEventKey *event,
276 SummaryView *summaryview);
277
278 static void summary_row_expanded (GtkTreeView *treeview,
279 GtkTreeIter *iter,
280 GtkTreePath *path,
281 SummaryView *summaryview);
282 static void summary_row_collapsed (GtkTreeView *treeview,
283 GtkTreeIter *iter,
284 GtkTreePath *path,
285 SummaryView *summaryview);
286
287 static void summary_columns_changed (GtkTreeView *treeview,
288 SummaryView *summaryview);
289
290 static gboolean summary_select_func (GtkTreeSelection *selection,
291 GtkTreeModel *model,
292 GtkTreePath *path,
293 gboolean cur_selected,
294 gpointer data);
295
296 static void summary_selection_changed (GtkTreeSelection *selection,
297 SummaryView *summaryview);
298
299 static void summary_col_resized (GtkWidget *widget,
300 GtkAllocation *allocation,
301 SummaryView *summaryview);
302
303 static void summary_reply_cb (SummaryView *summaryview,
304 guint action,
305 GtkWidget *widget);
306 static void summary_show_all_header_cb (SummaryView *summaryview,
307 guint action,
308 GtkWidget *widget);
309
310 static void summary_add_address_cb (SummaryView *summaryview,
311 guint action,
312 GtkWidget *widget);
313 static void summary_create_filter_cb (SummaryView *summaryview,
314 guint action,
315 GtkWidget *widget);
316
317 static void summary_column_clicked (GtkWidget *button,
318 SummaryView *summaryview);
319
320 static gboolean summary_column_drop_func(GtkTreeView *treeview,
321 GtkTreeViewColumn *column,
322 GtkTreeViewColumn *prev_column,
323 GtkTreeViewColumn *next_column,
324 gpointer data);
325
326 static void summary_drag_begin (GtkWidget *widget,
327 GdkDragContext *drag_context,
328 SummaryView *summaryview);
329 static void summary_drag_end (GtkWidget *widget,
330 GdkDragContext *drag_context,
331 SummaryView *summaryview);
332 static void summary_drag_data_get (GtkWidget *widget,
333 GdkDragContext *drag_context,
334 GtkSelectionData *selection_data,
335 guint info,
336 guint time,
337 SummaryView *summaryview);
338
339 static void summary_text_adj_value_changed
340 (GtkAdjustment *adj,
341 SummaryView *summaryview);
342
343 /* custom compare functions for sorting */
344 static gint summary_cmp_by_mark (GtkTreeModel *model,
345 GtkTreeIter *a,
346 GtkTreeIter *b,
347 gpointer data);
348 static gint summary_cmp_by_unread (GtkTreeModel *model,
349 GtkTreeIter *a,
350 GtkTreeIter *b,
351 gpointer data);
352 static gint summary_cmp_by_mime (GtkTreeModel *model,
353 GtkTreeIter *a,
354 GtkTreeIter *b,
355 gpointer data);
356 static gint summary_cmp_by_num (GtkTreeModel *model,
357 GtkTreeIter *a,
358 GtkTreeIter *b,
359 gpointer data);
360 static gint summary_cmp_by_size (GtkTreeModel *model,
361 GtkTreeIter *a,
362 GtkTreeIter *b,
363 gpointer data);
364 static gint summary_cmp_by_date (GtkTreeModel *model,
365 GtkTreeIter *a,
366 GtkTreeIter *b,
367 gpointer data);
368 static gint summary_cmp_by_thread_date (GtkTreeModel *model,
369 GtkTreeIter *a,
370 GtkTreeIter *b,
371 gpointer data);
372 static gint summary_cmp_by_from (GtkTreeModel *model,
373 GtkTreeIter *a,
374 GtkTreeIter *b,
375 gpointer data);
376 static gint summary_cmp_by_label (GtkTreeModel *model,
377 GtkTreeIter *a,
378 GtkTreeIter *b,
379 gpointer data);
380 static gint summary_cmp_by_to (GtkTreeModel *model,
381 GtkTreeIter *a,
382 GtkTreeIter *b,
383 gpointer data);
384 static gint summary_cmp_by_subject (GtkTreeModel *model,
385 GtkTreeIter *a,
386 GtkTreeIter *b,
387 gpointer data);
388
389
390 /* must be synched with FolderSortKey */
391 static SummaryColumnType sort_key_to_col[] = {
392 -1,
393 S_COL_NUMBER,
394 S_COL_SIZE,
395 S_COL_DATE,
396 S_COL_TDATE,
397 S_COL_FROM,
398 S_COL_SUBJECT,
399 -1,
400 S_COL_LABEL,
401 S_COL_MARK,
402 S_COL_UNREAD,
403 S_COL_MIME,
404 S_COL_TO
405 };
406
407 /* must be synched with SummaryColumnType */
408 static FolderSortKey col_to_sort_key[] = {
409 SORT_BY_MARK,
410 SORT_BY_UNREAD,
411 SORT_BY_MIME,
412 SORT_BY_SUBJECT,
413 SORT_BY_FROM,
414 SORT_BY_DATE,
415 SORT_BY_SIZE,
416 SORT_BY_NUMBER,
417 SORT_BY_TO
418 };
419
420 enum
421 {
422 DRAG_TYPE_TEXT,
423 DRAG_TYPE_RFC822,
424 DRAG_TYPE_URI_LIST,
425
426 N_DRAG_TYPES
427 };
428
429 static GtkTargetEntry summary_drag_types[] =
430 {
431 {"text/plain", GTK_TARGET_SAME_APP, DRAG_TYPE_TEXT},
432 {"message/rfc822", GTK_TARGET_SAME_APP, DRAG_TYPE_RFC822},
433 {"text/uri-list", 0, DRAG_TYPE_URI_LIST}
434 };
435
436 static GtkItemFactoryEntry summary_popup_entries[] =
437 {
438 {N_("/_Reply"), NULL, summary_reply_cb, COMPOSE_REPLY, NULL},
439 {N_("/Repl_y to"), NULL, NULL, 0, "<Branch>"},
440 {N_("/Repl_y to/_all"), NULL, summary_reply_cb, COMPOSE_REPLY_TO_ALL, NULL},
441 {N_("/Repl_y to/_sender"), NULL, summary_reply_cb, COMPOSE_REPLY_TO_SENDER, NULL},
442 {N_("/Repl_y to/mailing _list"),
443 NULL, summary_reply_cb, COMPOSE_REPLY_TO_LIST, NULL},
444 {N_("/---"), NULL, NULL, 0, "<Separator>"},
445 {N_("/_Forward"), NULL, summary_reply_cb, COMPOSE_FORWARD, NULL},
446 {N_("/For_ward as attachment"), NULL, summary_reply_cb, COMPOSE_FORWARD_AS_ATTACH, NULL},
447 {N_("/Redirec_t"), NULL, summary_reply_cb, COMPOSE_REDIRECT, NULL},
448 {N_("/---"), NULL, NULL, 0, "<Separator>"},
449 {N_("/M_ove..."), NULL, summary_move_to, 0, NULL},
450 {N_("/_Copy..."), NULL, summary_copy_to, 0, NULL},
451 {N_("/---"), NULL, NULL, 0, "<Separator>"},
452 {N_("/_Mark"), NULL, NULL, 0, "<Branch>"},
453 {N_("/_Mark/Set _flag"), NULL, summary_mark, 0, NULL},
454 {N_("/_Mark/_Unset flag"), NULL, summary_unmark, 0, NULL},
455 {N_("/_Mark/---"), NULL, NULL, 0, "<Separator>"},
456 {N_("/_Mark/Mark as unr_ead"), NULL, summary_mark_as_unread, 0, NULL},
457 {N_("/_Mark/Mark as rea_d"),
458 NULL, summary_mark_as_read, 0, NULL},
459 {N_("/_Mark/Mark _thread as read"),
460 NULL, summary_mark_thread_as_read, 0, NULL},
461 {N_("/_Mark/Mark all _read"), NULL, summary_mark_all_read, 0, NULL},
462 {N_("/Color la_bel"), NULL, NULL, 0, NULL},
463 {N_("/---"), NULL, NULL, 0, "<Separator>"},
464 {N_("/_Delete"), NULL, summary_delete, 0, NULL},
465 {N_("/---"), NULL, NULL, 0, "<Separator>"},
466 {N_("/Set as _junk mail"), NULL, summary_junk, 0, NULL},
467 {N_("/Set as not j_unk mail"), NULL, summary_not_junk, 0, NULL},
468 {N_("/---"), NULL, NULL, 0, "<Separator>"},
469 {N_("/Re-_edit"), NULL, summary_reedit, 0, NULL},
470 {N_("/---"), NULL, NULL, 0, "<Separator>"},
471 {N_("/Add sender to address boo_k..."),
472 NULL, summary_add_address_cb, 0, NULL},
473 {N_("/Create f_ilter rule"), NULL, NULL, 0, "<Branch>"},
474 {N_("/Create f_ilter rule/_Automatically"),
475 NULL, summary_create_filter_cb, FLT_BY_AUTO, NULL},
476 {N_("/Create f_ilter rule/by _From"),
477 NULL, summary_create_filter_cb, FLT_BY_FROM, NULL},
478 {N_("/Create f_ilter rule/by _To"),
479 NULL, summary_create_filter_cb, FLT_BY_TO, NULL},
480 {N_("/Create f_ilter rule/by _Subject"),
481 NULL, summary_create_filter_cb, FLT_BY_SUBJECT, NULL},
482 {N_("/---"), NULL, NULL, 0, "<Separator>"},
483 {N_("/_View"), NULL, NULL, 0, "<Branch>"},
484 {N_("/_View/Open in new _window"),
485 NULL, summary_open_msg, 0, NULL},
486 {N_("/_View/Mess_age source"), NULL, summary_view_source, 0, NULL},
487 {N_("/_View/All _headers"), NULL, summary_show_all_header_cb, 0, "<ToggleItem>"},
488 {N_("/---"), NULL, NULL, 0, "<Separator>"},
489 {N_("/_Print..."), NULL, summary_print, 0, NULL}
490 };
491
summary_create(void)492 SummaryView *summary_create(void)
493 {
494 SummaryView *summaryview;
495 GtkWidget *vbox;
496 GtkWidget *scrolledwin;
497 GtkWidget *treeview;
498 GtkTreeStore *store;
499 GtkTreeSelection *selection;
500 GtkWidget *hseparator;
501 GtkWidget *hbox;
502 GtkWidget *statlabel_folder;
503 GtkWidget *statlabel_select;
504 GtkWidget *statlabel_msgs;
505 GtkWidget *toggle_eventbox;
506 GtkWidget *toggle_arrow;
507 GtkTooltips *tip;
508 GtkWidget *popupmenu;
509 GtkItemFactory *popupfactory;
510 gint n_entries;
511 GList *child;
512
513 debug_print(_("Creating summary view...\n"));
514 summaryview = g_new0(SummaryView, 1);
515
516 vbox = gtk_vbox_new(FALSE, 1);
517
518 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
519 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
520 GTK_POLICY_AUTOMATIC,
521 GTK_POLICY_ALWAYS);
522 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
523 GTK_SHADOW_IN);
524 gtk_box_pack_start(GTK_BOX(vbox), scrolledwin, TRUE, TRUE, 0);
525 gtk_widget_set_size_request(vbox,
526 prefs_common.summaryview_width,
527 prefs_common.summaryview_height);
528
529 treeview = summary_tree_view_create(summaryview);
530 gtk_container_add(GTK_CONTAINER(scrolledwin), treeview);
531
532 store = GTK_TREE_STORE
533 (gtk_tree_view_get_model(GTK_TREE_VIEW(treeview)));
534 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
535
536 /* create status label */
537 hseparator = gtk_hseparator_new();
538 gtk_box_pack_end(GTK_BOX(vbox), hseparator, FALSE, FALSE, 0);
539
540 hbox = gtk_hbox_new(FALSE, 0);
541 gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
542
543 statlabel_folder = gtk_label_new("");
544 gtk_box_pack_start(GTK_BOX(hbox), statlabel_folder, FALSE, FALSE, 2);
545 statlabel_select = gtk_label_new("");
546 gtk_box_pack_start(GTK_BOX(hbox), statlabel_select, FALSE, FALSE, 12);
547
548 /* toggle view button */
549 toggle_eventbox = gtk_event_box_new();
550 gtk_box_pack_end(GTK_BOX(hbox), toggle_eventbox, FALSE, FALSE, 4);
551 toggle_arrow = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_OUT);
552 gtk_container_add(GTK_CONTAINER(toggle_eventbox), toggle_arrow);
553 g_signal_connect(G_OBJECT(toggle_eventbox), "button_press_event",
554 G_CALLBACK(summary_toggle_pressed), summaryview);
555 tip = gtk_tooltips_new();
556 gtk_tooltips_set_tip(tip, toggle_eventbox, _("Toggle message view"),
557 NULL);
558
559 statlabel_msgs = gtk_label_new("");
560 gtk_misc_set_alignment(GTK_MISC(statlabel_msgs), 1, 0.5);
561 #if GTK_CHECK_VERSION(2, 6, 0)
562 gtk_label_set_ellipsize(GTK_LABEL(statlabel_msgs),
563 PANGO_ELLIPSIZE_START);
564 #endif
565 gtk_box_pack_start(GTK_BOX(hbox), statlabel_msgs, TRUE, TRUE, 2);
566
567 /* create popup menu */
568 n_entries = sizeof(summary_popup_entries) /
569 sizeof(summary_popup_entries[0]);
570 popupmenu = menu_create_items(summary_popup_entries, n_entries,
571 "<SummaryView>", &popupfactory,
572 summaryview);
573
574 summaryview->vbox = vbox;
575 summaryview->scrolledwin = scrolledwin;
576 summaryview->treeview = treeview;
577 summaryview->store = store;
578 summaryview->selection = selection;
579 summaryview->hseparator = hseparator;
580 summaryview->hbox = hbox;
581 summaryview->statlabel_folder = statlabel_folder;
582 summaryview->statlabel_select = statlabel_select;
583 summaryview->statlabel_msgs = statlabel_msgs;
584 summaryview->toggle_eventbox = toggle_eventbox;
585 summaryview->toggle_arrow = toggle_arrow;
586 summaryview->popupmenu = popupmenu;
587 summaryview->popupfactory = popupfactory;
588 summaryview->lock_count = 0;
589
590 summaryview->reedit_menuitem =
591 gtk_item_factory_get_widget(popupfactory, "/Re-edit");
592 child = g_list_find(GTK_MENU_SHELL(popupmenu)->children,
593 summaryview->reedit_menuitem);
594 summaryview->reedit_separator = GTK_WIDGET(child->next->data);
595
596 summaryview->junk_menuitem =
597 gtk_item_factory_get_widget(popupfactory, "/Set as junk mail");
598 summaryview->nojunk_menuitem =
599 gtk_item_factory_get_widget(popupfactory,
600 "/Set as not junk mail");
601 child = g_list_find(GTK_MENU_SHELL(popupmenu)->children,
602 summaryview->nojunk_menuitem);
603 summaryview->junk_separator = GTK_WIDGET(child->next->data);
604
605 gtk_widget_show_all(vbox);
606
607 return summaryview;
608 }
609
summary_init(SummaryView * summaryview)610 void summary_init(SummaryView *summaryview)
611 {
612 GtkWidget *pixmap;
613 PangoFontDescription *font_desc;
614 gint size;
615 TextView *textview;
616 GtkAdjustment *adj;
617
618 stock_pixbuf_gdk(summaryview->treeview, STOCK_PIXMAP_MARK,
619 &mark_pixbuf);
620 stock_pixbuf_gdk(summaryview->treeview, STOCK_PIXMAP_DELETED,
621 &deleted_pixbuf);
622 stock_pixbuf_gdk(summaryview->treeview, STOCK_PIXMAP_MAIL_SMALL,
623 &mail_pixbuf);
624 stock_pixbuf_gdk(summaryview->treeview, STOCK_PIXMAP_NEW,
625 &new_pixbuf);
626 stock_pixbuf_gdk(summaryview->treeview, STOCK_PIXMAP_UNREAD,
627 &unread_pixbuf);
628 stock_pixbuf_gdk(summaryview->treeview, STOCK_PIXMAP_REPLIED,
629 &replied_pixbuf);
630 stock_pixbuf_gdk(summaryview->treeview, STOCK_PIXMAP_FORWARDED,
631 &forwarded_pixbuf);
632 stock_pixbuf_gdk(summaryview->treeview, STOCK_PIXMAP_CLIP,
633 &clip_pixbuf);
634 stock_pixbuf_gdk(summaryview->treeview, STOCK_PIXMAP_HTML,
635 &html_pixbuf);
636
637 font_desc = pango_font_description_new();
638 size = pango_font_description_get_size
639 (summaryview->statlabel_folder->style->font_desc);
640 pango_font_description_set_size(font_desc, size * PANGO_SCALE_SMALL);
641 gtk_widget_modify_font(summaryview->statlabel_folder, font_desc);
642 gtk_widget_modify_font(summaryview->statlabel_select, font_desc);
643 gtk_widget_modify_font(summaryview->statlabel_msgs, font_desc);
644 pango_font_description_free(font_desc);
645
646 pixmap = stock_pixbuf_widget(summaryview->hbox,
647 STOCK_PIXMAP_FOLDER_OPEN);
648 gtk_box_pack_start(GTK_BOX(summaryview->hbox), pixmap, FALSE, FALSE, 4);
649 gtk_box_reorder_child(GTK_BOX(summaryview->hbox), pixmap, 0);
650 gtk_widget_show(pixmap);
651
652 summary_clear_list(summaryview);
653 summary_set_column_order(summaryview);
654 summary_colorlabel_menu_create(summaryview);
655 summary_set_menu_sensitive(summaryview);
656
657 textview = summaryview->messageview->textview;
658 adj = GTK_TEXT_VIEW(textview->text)->vadjustment;
659 g_signal_connect(adj, "value-changed",
660 G_CALLBACK(summary_text_adj_value_changed),
661 summaryview);
662 textview = summaryview->messageview->mimeview->textview;
663 adj = GTK_TEXT_VIEW(textview->text)->vadjustment;
664 g_signal_connect(adj, "value-changed",
665 G_CALLBACK(summary_text_adj_value_changed),
666 summaryview);
667 }
668
get_msg_list_func(Folder * folder,FolderItem * item,gpointer data)669 static void get_msg_list_func(Folder *folder, FolderItem *item, gpointer data)
670 {
671 SummaryView *summary = (SummaryView *)folder->data;
672 gint count = GPOINTER_TO_INT(data);
673 static GTimeVal tv_prev = {0, 0};
674 GTimeVal tv_cur;
675
676 g_get_current_time(&tv_cur);
677
678 if (tv_prev.tv_sec == 0 ||
679 (tv_cur.tv_sec - tv_prev.tv_sec) * G_USEC_PER_SEC +
680 tv_cur.tv_usec - tv_prev.tv_usec > 100 * 1000) {
681 gchar buf[256];
682
683 g_snprintf(buf, sizeof(buf), _("Scanning folder (%s) (%d)..."),
684 item->path, count);
685 STATUSBAR_POP(summary->mainwin);
686 STATUSBAR_PUSH(summary->mainwin, buf);
687 tv_prev = tv_cur;
688 }
689 }
690
summary_show(SummaryView * summaryview,FolderItem * item,gboolean update_cache)691 gboolean summary_show(SummaryView *summaryview, FolderItem *item,
692 gboolean update_cache)
693 {
694 GtkTreeView *treeview = GTK_TREE_VIEW(summaryview->treeview);
695 GtkTreeIter iter;
696 GSList *mlist;
697 gchar *buf;
698 gboolean is_refresh;
699 guint selected_msgnum = 0;
700 guint displayed_msgnum = 0;
701 gboolean moved;
702 gboolean selection_done = FALSE;
703 gboolean do_qsearch = FALSE;
704 gboolean set_column_order_required = FALSE;
705 const gchar *key = NULL;
706 gpointer save_data;
707 GSList *save_mark_mlist = NULL;
708
709 if (summary_is_locked(summaryview)) return FALSE;
710
711 inc_lock();
712 summary_lock(summaryview);
713
714 STATUSBAR_POP(summaryview->mainwin);
715
716 is_refresh = (item == summaryview->folder_item &&
717 update_cache == FALSE) ? TRUE : FALSE;
718 selected_msgnum = summary_get_msgnum(summaryview,
719 summaryview->selected);
720 displayed_msgnum = summary_get_msgnum(summaryview,
721 summaryview->displayed);
722 if (summaryview->folder_item)
723 summaryview->folder_item->last_selected = selected_msgnum;
724 if (!is_refresh) {
725 if (item)
726 selected_msgnum = item->last_selected;
727 else
728 selected_msgnum = 0;
729 displayed_msgnum = 0;
730 }
731
732 /* process the marks if any */
733 if (summaryview->mainwin->lock_count == 0 && !is_refresh &&
734 (summaryview->moved > 0 || summaryview->copied > 0)) {
735 AlertValue val;
736
737 val = alertpanel(_("Process mark"),
738 _("Some marks are left. Process it?"),
739 GTK_STOCK_YES, GTK_STOCK_NO, GTK_STOCK_CANCEL);
740 if (G_ALERTDEFAULT == val) {
741 summary_unlock(summaryview);
742 summary_execute(summaryview);
743 summary_lock(summaryview);
744 GTK_EVENTS_FLUSH();
745 } else if (G_ALERTALTERNATE == val) {
746 summary_write_cache(summaryview);
747 GTK_EVENTS_FLUSH();
748 } else {
749 summary_unlock(summaryview);
750 inc_unlock();
751 return FALSE;
752 }
753 } else {
754 /* save temporary move/copy marks */
755 if (is_refresh &&
756 (summaryview->moved > 0 || summaryview->copied > 0))
757 save_mark_mlist = summary_get_tmp_marked_msg_list(summaryview);
758
759 summary_write_cache(summaryview);
760 }
761
762 if (FOLDER_ITEM_IS_SENT_FOLDER(summaryview->folder_item) !=
763 FOLDER_ITEM_IS_SENT_FOLDER(item))
764 set_column_order_required = TRUE;
765
766 folderview_set_opened_item(summaryview->folderview, item);
767
768 summary_clear_list_full(summaryview, is_refresh);
769
770 buf = NULL;
771 if (!item || !item->path || !item->parent || item->no_select ||
772 (FOLDER_TYPE(item->folder) == F_MH && item->stype != F_VIRTUAL &&
773 ((buf = folder_item_get_path(item)) == NULL ||
774 change_dir(buf) < 0))) {
775 g_free(buf);
776 debug_print("empty folder\n\n");
777 summary_clear_all(summaryview);
778 summaryview->folder_item = item;
779 if (item)
780 item->qsearch_cond_type = QS_ALL;
781 if (set_column_order_required)
782 summary_set_column_order(summaryview);
783 if (save_mark_mlist)
784 procmsg_msg_list_free(save_mark_mlist);
785 summary_unlock(summaryview);
786 inc_unlock();
787 return TRUE;
788 }
789 g_free(buf);
790
791 if (!is_refresh)
792 messageview_clear(summaryview->messageview);
793
794 summaryview->folder_item = item;
795 if (set_column_order_required)
796 summary_set_column_order(summaryview);
797
798 g_signal_handlers_block_matched(G_OBJECT(treeview),
799 (GSignalMatchType)G_SIGNAL_MATCH_DATA,
800 0, 0, NULL, NULL, summaryview);
801
802 buf = g_strdup_printf(_("Scanning folder (%s)..."), item->path);
803 debug_print("%s\n", buf);
804 STATUSBAR_PUSH(summaryview->mainwin, buf);
805 g_free(buf);
806
807 main_window_cursor_wait(summaryview->mainwin);
808
809 save_data = item->folder->data;
810 item->folder->data = summaryview;
811 folder_set_ui_func(item->folder, get_msg_list_func, NULL);
812
813 mlist = folder_item_get_msg_list(item, !update_cache);
814
815 folder_set_ui_func(item->folder, NULL, NULL);
816 item->folder->data = save_data;
817
818 statusbar_pop_all();
819 STATUSBAR_POP(summaryview->mainwin);
820
821 summaryview->all_mlist = mlist;
822
823 /* restore temporary move/copy marks */
824 if (save_mark_mlist) {
825 summary_restore_tmp_marks(summaryview, save_mark_mlist);
826 save_mark_mlist = NULL;
827 }
828
829 /* set tree store and hash table from the msginfo list, and
830 create the thread */
831 if (prefs_common.show_searchbar &&
832 (prefs_common.persist_qsearch_filter || is_refresh)) {
833 if (item->qsearch_cond_type > QS_ALL)
834 do_qsearch = TRUE;
835 if (is_refresh && summaryview->qsearch->entry_entered) {
836 key = gtk_entry_get_text
837 (GTK_ENTRY(summaryview->qsearch->entry));
838 if (key && *key != '\0')
839 do_qsearch = TRUE;
840 else
841 key = NULL;
842 }
843 }
844
845 if (do_qsearch) {
846 gint index;
847 QSearchCondType type = item->qsearch_cond_type;
848
849 index = menu_find_option_menu_index
850 (GTK_OPTION_MENU(summaryview->qsearch->optmenu),
851 GINT_TO_POINTER(type), NULL);
852 if (index > 0) {
853 gtk_option_menu_set_history
854 (GTK_OPTION_MENU(summaryview->qsearch->optmenu),
855 index);
856 } else {
857 gtk_option_menu_set_history
858 (GTK_OPTION_MENU(summaryview->qsearch->optmenu),
859 0);
860 type = QS_ALL;
861 }
862
863 if (type > QS_ALL || key) {
864 summaryview->flt_mlist =
865 quick_search_filter(summaryview->qsearch,
866 type, key);
867 summaryview->on_filter = TRUE;
868 summary_set_tree_model_from_list
869 (summaryview, summaryview->flt_mlist);
870 summary_update_status(summaryview);
871 } else {
872 item->qsearch_cond_type = QS_ALL;
873 summary_set_tree_model_from_list(summaryview, mlist);
874 }
875 } else {
876 item->qsearch_cond_type = QS_ALL;
877 summary_set_tree_model_from_list(summaryview, mlist);
878 }
879
880 if (mlist)
881 gtk_widget_grab_focus(GTK_WIDGET(treeview));
882
883 summary_write_cache(summaryview);
884
885 item->opened = TRUE;
886
887 g_signal_handlers_unblock_matched(G_OBJECT(treeview),
888 (GSignalMatchType)G_SIGNAL_MATCH_DATA,
889 0, 0, NULL, NULL, summaryview);
890
891 if (is_refresh) {
892 summary_update_display_state(summaryview, displayed_msgnum,
893 selected_msgnum);
894
895 if (!summaryview->selected) {
896 /* no selected message - select first unread
897 message, but do not display it */
898 if (summary_find_next_flagged_msg
899 (summaryview, &iter, NULL, MSG_UNREAD, FALSE)) {
900 summary_select_row(summaryview, &iter,
901 FALSE, TRUE);
902 } else if (item->sort_type == SORT_ASCENDING &&
903 SUMMARY_DISPLAY_TOTAL_NUM(item) > 1) {
904 g_signal_emit_by_name
905 (treeview, "move-cursor",
906 GTK_MOVEMENT_BUFFER_ENDS, 1, &moved);
907 GTK_EVENTS_FLUSH();
908 summary_scroll_to_selected(summaryview, TRUE);
909 } else if (gtk_tree_model_get_iter_first
910 (GTK_TREE_MODEL(summaryview->store),
911 &iter)) {
912 summary_select_row(summaryview, &iter,
913 FALSE, TRUE);
914 }
915 }
916 selection_done = TRUE;
917 } else if (prefs_common.remember_last_selected) {
918 summary_unlock(summaryview);
919 summary_select_by_msgnum(summaryview, selected_msgnum);
920 summary_lock(summaryview);
921
922 if (summaryview->selected)
923 selection_done = TRUE;
924 }
925
926 if (!selection_done) {
927 /* select first unread message */
928 if (summary_find_next_flagged_msg(summaryview, &iter, NULL,
929 MSG_UNREAD, FALSE)) {
930 if (prefs_common.open_unread_on_enter ||
931 prefs_common.always_show_msg) {
932 summary_unlock(summaryview);
933 summary_select_row(summaryview, &iter,
934 TRUE, TRUE);
935 summary_lock(summaryview);
936 } else {
937 summary_select_row(summaryview, &iter,
938 FALSE, TRUE);
939 }
940 } else {
941 summary_unlock(summaryview);
942 if (item->sort_type == SORT_ASCENDING &&
943 SUMMARY_DISPLAY_TOTAL_NUM(item) > 1) {
944 g_signal_emit_by_name
945 (treeview, "move-cursor",
946 GTK_MOVEMENT_BUFFER_ENDS, 1, &moved);
947 } else if (gtk_tree_model_get_iter_first
948 (GTK_TREE_MODEL(summaryview->store),
949 &iter)) {
950 summary_select_row(summaryview, &iter,
951 FALSE, TRUE);
952 }
953 summary_lock(summaryview);
954 GTK_EVENTS_FLUSH();
955 summary_scroll_to_selected(summaryview, TRUE);
956 }
957 }
958
959 summary_status_show(summaryview);
960 summary_set_menu_sensitive(summaryview);
961 main_window_set_toolbar_sensitive(summaryview->mainwin);
962
963 debug_print("\n");
964 STATUSBAR_PUSH(summaryview->mainwin, _("Done."));
965
966 main_window_cursor_normal(summaryview->mainwin);
967
968 if (prefs_common.online_mode) {
969 if (FOLDER_IS_REMOTE(item->folder) &&
970 REMOTE_FOLDER(item->folder)->session == NULL) {
971 alertpanel_error(_("Could not establish a connection to the server."));
972 }
973 }
974
975 summary_unlock(summaryview);
976 inc_unlock();
977
978 return TRUE;
979 }
980
summary_unset_sort_column_id(SummaryView * summaryview)981 static void summary_unset_sort_column_id(SummaryView *summaryview)
982 {
983 gint id;
984 GtkSortType order;
985
986 if (gtk_tree_sortable_get_sort_column_id
987 (GTK_TREE_SORTABLE(summaryview->store), &id, &order) &&
988 id >= 0 && id < N_SUMMARY_VISIBLE_COLS) {
989 GtkTreeViewColumn *column = summaryview->columns[id];
990 column->sort_column_id = -1;
991 gtk_tree_view_column_set_sort_indicator(column, FALSE);
992 }
993
994 gtkut_tree_sortable_unset_sort_column_id
995 (GTK_TREE_SORTABLE(summaryview->store));
996 }
997
summary_clear_list(SummaryView * summaryview)998 void summary_clear_list(SummaryView *summaryview)
999 {
1000 summary_clear_list_full(summaryview, FALSE);
1001 }
1002
summary_clear_list_full(SummaryView * summaryview,gboolean is_refresh)1003 static void summary_clear_list_full(SummaryView *summaryview,
1004 gboolean is_refresh)
1005 {
1006 GtkTreeView *treeview = GTK_TREE_VIEW(summaryview->treeview);
1007 GtkAdjustment *adj;
1008
1009 if (summaryview->folder_item) {
1010 folder_item_close(summaryview->folder_item);
1011 summaryview->folder_item = NULL;
1012 }
1013
1014 summaryview->display_msg = FALSE;
1015
1016 summaryview->selected = NULL;
1017 summaryview->displayed = NULL;
1018
1019 summary_selection_list_free(summaryview);
1020
1021 summaryview->total_size = 0;
1022 summaryview->deleted = summaryview->moved = 0;
1023 summaryview->copied = 0;
1024
1025 summary_msgid_table_destroy(summaryview);
1026
1027 summaryview->tmp_mlist = NULL;
1028 summaryview->to_folder = NULL;
1029 if (summaryview->folder_table) {
1030 g_hash_table_destroy(summaryview->folder_table);
1031 summaryview->folder_table = NULL;
1032 }
1033 summaryview->filtered = 0;
1034 summaryview->flt_count = 0;
1035 summaryview->flt_total = 0;
1036
1037 summaryview->on_button_press = FALSE;
1038 summaryview->can_toggle_selection = TRUE;
1039 summaryview->on_drag = FALSE;
1040 if (summaryview->pressed_path) {
1041 gtk_tree_path_free(summaryview->pressed_path);
1042 summaryview->pressed_path = NULL;
1043 }
1044 if (summaryview->drag_list) {
1045 g_free(summaryview->drag_list);
1046 summaryview->drag_list = NULL;
1047 }
1048
1049 if (summaryview->flt_mlist) {
1050 g_slist_free(summaryview->flt_mlist);
1051 summaryview->flt_mlist = NULL;
1052 }
1053 summaryview->total_flt_msg_size = 0;
1054 summaryview->flt_msg_total = 0;
1055 summaryview->flt_deleted = summaryview->flt_moved = 0;
1056 summaryview->flt_copied = 0;
1057 summaryview->flt_new = summaryview->flt_unread = 0;
1058 if (!is_refresh) {
1059 quick_search_clear_entry(summaryview->qsearch);
1060 gtk_option_menu_set_history
1061 (GTK_OPTION_MENU(summaryview->qsearch->optmenu), 0);
1062 }
1063 summaryview->on_filter = FALSE;
1064
1065 procmsg_msg_list_free(summaryview->all_mlist);
1066 summaryview->all_mlist = NULL;
1067
1068 gtkut_tree_view_fast_clear(treeview, summaryview->store);
1069
1070 /* ensure that the "value-changed" signal is always emitted */
1071 adj = gtk_tree_view_get_vadjustment(treeview);
1072 adj->value = 0.0;
1073
1074 summary_unset_sort_column_id(summaryview);
1075 }
1076
summary_clear_all(SummaryView * summaryview)1077 void summary_clear_all(SummaryView *summaryview)
1078 {
1079 messageview_clear(summaryview->messageview);
1080 summary_clear_list(summaryview);
1081 summary_set_menu_sensitive(summaryview);
1082 main_window_set_toolbar_sensitive(summaryview->mainwin);
1083 summary_status_show(summaryview);
1084 }
1085
summary_show_queued_msgs(SummaryView * summaryview)1086 void summary_show_queued_msgs(SummaryView *summaryview)
1087 {
1088 FolderItem *item;
1089 GSList *qlist, *cur;
1090 MsgInfo *msginfo;
1091 GtkTreeStore *store = GTK_TREE_STORE(summaryview->store);
1092 GtkTreeIter iter;
1093
1094 if (summary_is_locked(summaryview))
1095 return;
1096
1097 item = summaryview->folder_item;
1098 if (!item || !item->path || !item->cache_queue ||
1099 item->stype == F_VIRTUAL)
1100 return;
1101
1102 debug_print("summary_show_queued_msgs: appending queued messages to summary (%s)\n", item->path);
1103
1104 qlist = g_slist_reverse(item->cache_queue);
1105 item->cache_queue = NULL;
1106 if (item->mark_queue) {
1107 procmsg_flaginfo_list_free(item->mark_queue);
1108 item->mark_queue = NULL;
1109 }
1110
1111 for (cur = qlist; cur != NULL; cur = cur->next) {
1112 msginfo = (MsgInfo *)cur->data;
1113
1114 debug_print("summary_show_queued_msgs: appending msg %u\n",
1115 msginfo->msgnum);
1116 msginfo->folder = item;
1117 gtk_tree_store_append(store, &iter, NULL);
1118 summary_set_row(summaryview, &iter, msginfo);
1119
1120 if (cur == qlist) {
1121 GtkTreePath *path;
1122
1123 path = gtk_tree_model_get_path(GTK_TREE_MODEL(store),
1124 &iter);
1125 gtk_tree_view_scroll_to_cell
1126 (GTK_TREE_VIEW(summaryview->treeview), path,
1127 NULL, FALSE, 0.0, 0.0);
1128 gtk_tree_path_free(path);
1129 }
1130
1131 summaryview->total_size += msginfo->size;
1132 }
1133
1134 summaryview->all_mlist = g_slist_concat(summaryview->all_mlist, qlist);
1135
1136 item->cache_dirty = TRUE;
1137 summary_selection_list_free(summaryview);
1138
1139 summary_status_show(summaryview);
1140
1141 debug_print("summary_show_queued_msgs: done.\n");
1142 }
1143
summary_lock(SummaryView * summaryview)1144 void summary_lock(SummaryView *summaryview)
1145 {
1146 summaryview->lock_count++;
1147 summaryview->write_lock_count++;
1148 /* g_print("summary_lock: %d\n", summaryview->lock_count); */
1149 }
1150
summary_unlock(SummaryView * summaryview)1151 void summary_unlock(SummaryView *summaryview)
1152 {
1153 /* g_print("summary_unlock: %d\n", summaryview->lock_count); */
1154 if (summaryview->lock_count)
1155 summaryview->lock_count--;
1156 if (summaryview->write_lock_count)
1157 summaryview->write_lock_count--;
1158 }
1159
summary_is_locked(SummaryView * summaryview)1160 gboolean summary_is_locked(SummaryView *summaryview)
1161 {
1162 return summaryview->lock_count > 0 || summaryview->write_lock_count > 0;
1163 }
1164
summary_is_read_locked(SummaryView * summaryview)1165 gboolean summary_is_read_locked(SummaryView *summaryview)
1166 {
1167 return summaryview->lock_count > 0;
1168 }
1169
summary_write_lock(SummaryView * summaryview)1170 void summary_write_lock(SummaryView *summaryview)
1171 {
1172 summaryview->write_lock_count++;
1173 }
1174
summary_write_unlock(SummaryView * summaryview)1175 void summary_write_unlock(SummaryView *summaryview)
1176 {
1177 if (summaryview->write_lock_count)
1178 summaryview->write_lock_count--;
1179 }
1180
summary_is_write_locked(SummaryView * summaryview)1181 gboolean summary_is_write_locked(SummaryView *summaryview)
1182 {
1183 return summaryview->write_lock_count > 0;
1184 }
1185
summary_get_current_folder(SummaryView * summaryview)1186 FolderItem *summary_get_current_folder(SummaryView *summaryview)
1187 {
1188 return summaryview->folder_item;
1189 }
1190
summary_get_selection_type(SummaryView * summaryview)1191 SummarySelection summary_get_selection_type(SummaryView *summaryview)
1192 {
1193 SummarySelection selection;
1194 GList *rows;
1195
1196 rows = summary_get_selected_rows(summaryview);
1197
1198 if (!summaryview->folder_item || summaryview->folder_item->total == 0)
1199 selection = SUMMARY_NONE;
1200 else if (!rows)
1201 selection = SUMMARY_SELECTED_NONE;
1202 else if (rows && !rows->next)
1203 selection = SUMMARY_SELECTED_SINGLE;
1204 else
1205 selection = SUMMARY_SELECTED_MULTIPLE;
1206
1207 return selection;
1208 }
1209
summary_get_selected_rows(SummaryView * summaryview)1210 static GList *summary_get_selected_rows(SummaryView *summaryview)
1211 {
1212 if (!summaryview->selection_list)
1213 summaryview->selection_list =
1214 gtk_tree_selection_get_selected_rows
1215 (summaryview->selection, NULL);
1216
1217 return summaryview->selection_list;
1218 }
1219
summary_selection_list_free(SummaryView * summaryview)1220 static void summary_selection_list_free(SummaryView *summaryview)
1221 {
1222 if (summaryview->selection_list) {
1223 g_list_foreach(summaryview->selection_list,
1224 (GFunc)gtk_tree_path_free, NULL);
1225 g_list_free(summaryview->selection_list);
1226 summaryview->selection_list = NULL;
1227 }
1228 }
1229
summary_get_selected_msg_list(SummaryView * summaryview)1230 GSList *summary_get_selected_msg_list(SummaryView *summaryview)
1231 {
1232 GSList *mlist = NULL;
1233 GList *rows;
1234 GList *cur;
1235 GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
1236 GtkTreeIter iter;
1237 MsgInfo *msginfo;
1238
1239 rows = summary_get_selected_rows(summaryview);
1240 for (cur = rows; cur != NULL; cur = cur->next) {
1241 gtk_tree_model_get_iter(model, &iter, (GtkTreePath *)cur->data);
1242 gtk_tree_model_get(model, &iter, S_COL_MSG_INFO, &msginfo, -1);
1243 mlist = g_slist_prepend(mlist, msginfo);
1244 }
1245
1246 mlist = g_slist_reverse(mlist);
1247
1248 return mlist;
1249 }
1250
summary_get_changed_msg_list(SummaryView * summaryview)1251 GSList *summary_get_changed_msg_list(SummaryView *summaryview)
1252 {
1253 MsgInfo *msginfo;
1254 GSList *mlist = NULL;
1255 GSList *cur;
1256
1257 for (cur = summaryview->all_mlist; cur != NULL; cur = cur->next) {
1258 msginfo = (MsgInfo *)cur->data;
1259 if (MSG_IS_FLAG_CHANGED(msginfo->flags))
1260 mlist = g_slist_prepend(mlist, msginfo);
1261 }
1262
1263 return g_slist_reverse(mlist);
1264 }
1265
summary_get_msg_list(SummaryView * summaryview)1266 GSList *summary_get_msg_list(SummaryView *summaryview)
1267 {
1268 if (summaryview->on_filter)
1269 return g_slist_copy(summaryview->flt_mlist);
1270 else
1271 return g_slist_copy(summaryview->all_mlist);
1272 }
1273
summary_get_flagged_msg_list(SummaryView * summaryview,MsgPermFlags flags)1274 GSList *summary_get_flagged_msg_list(SummaryView *summaryview,
1275 MsgPermFlags flags)
1276 {
1277 MsgInfo *msginfo;
1278 GSList *list, *cur;
1279 GSList *mlist = NULL;
1280
1281 if (summaryview->on_filter)
1282 list = summaryview->flt_mlist;
1283 else
1284 list = summaryview->all_mlist;
1285
1286 for (cur = list; cur != NULL; cur = cur->next) {
1287 msginfo = (MsgInfo *)cur->data;
1288 if ((msginfo->flags.perm_flags & flags) != 0)
1289 mlist = g_slist_prepend(mlist, msginfo);
1290 }
1291
1292 return g_slist_reverse(mlist);
1293 }
1294
1295 /* return list of copied MsgInfo */
summary_get_tmp_marked_msg_list(SummaryView * summaryview)1296 static GSList *summary_get_tmp_marked_msg_list(SummaryView *summaryview)
1297 {
1298 MsgInfo *msginfo, *markinfo;
1299 GSList *mlist = NULL;
1300 GSList *cur;
1301
1302 for (cur = summaryview->all_mlist; cur != NULL; cur = cur->next) {
1303 msginfo = (MsgInfo *)cur->data;
1304 if (MSG_IS_MOVE(msginfo->flags) ||
1305 MSG_IS_COPY(msginfo->flags)) {
1306 markinfo = g_new0(MsgInfo, 1);
1307 markinfo->msgnum = msginfo->msgnum;
1308 markinfo->flags = msginfo->flags;
1309 markinfo->folder = msginfo->folder;
1310 markinfo->to_folder = msginfo->to_folder;
1311 mlist = g_slist_prepend(mlist, markinfo);
1312 }
1313 }
1314
1315 return g_slist_reverse(mlist);
1316 }
1317
summary_restore_tmp_marks(SummaryView * summaryview,GSList * save_mark_mlist)1318 static void summary_restore_tmp_marks(SummaryView *summaryview,
1319 GSList *save_mark_mlist)
1320 {
1321 GSList *cur, *scur;
1322 MsgInfo *msginfo, *markinfo;
1323
1324 debug_print("summary_restore_tmp_marks: restoring temporary marks\n");
1325
1326 for (cur = summaryview->all_mlist; cur != NULL; cur = cur->next) {
1327 msginfo = (MsgInfo *)cur->data;
1328 for (scur = save_mark_mlist; scur != NULL; scur = scur->next) {
1329 markinfo = (MsgInfo *)scur->data;
1330 if (msginfo->msgnum == markinfo->msgnum &&
1331 msginfo->folder == markinfo->folder) {
1332 msginfo->flags.tmp_flags |= (markinfo->flags.tmp_flags & (MSG_MOVE|MSG_COPY));
1333 msginfo->to_folder = markinfo->to_folder;
1334 save_mark_mlist = g_slist_remove
1335 (save_mark_mlist, markinfo);
1336 g_free(markinfo);
1337 if (!save_mark_mlist)
1338 return;
1339 break;
1340 }
1341 }
1342 }
1343
1344 if (save_mark_mlist)
1345 procmsg_msg_list_free(save_mark_mlist);
1346 }
1347
summary_update_msg_list(SummaryView * summaryview)1348 static void summary_update_msg_list(SummaryView *summaryview)
1349 {
1350 GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
1351 GtkTreeIter iter;
1352 GSList *mlist = NULL;
1353 MsgInfo *msginfo;
1354 gboolean valid;
1355
1356 if (summaryview->on_filter)
1357 return;
1358
1359 g_slist_free(summaryview->all_mlist);
1360 summaryview->all_mlist = NULL;
1361
1362 valid = gtk_tree_model_get_iter_first(model, &iter);
1363
1364 while (valid) {
1365 gtk_tree_model_get(model, &iter, S_COL_MSG_INFO, &msginfo, -1);
1366 mlist = g_slist_prepend(mlist, msginfo);
1367 valid = gtkut_tree_model_next(model, &iter);
1368 }
1369
1370 summaryview->all_mlist = g_slist_reverse(mlist);
1371
1372 }
1373
summary_msgid_table_create_func(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer data)1374 static gboolean summary_msgid_table_create_func(GtkTreeModel *model,
1375 GtkTreePath *path,
1376 GtkTreeIter *iter,
1377 gpointer data)
1378 {
1379 GHashTable *msgid_table = (GHashTable *)data;
1380 MsgInfo *msginfo;
1381 GtkTreeIter *iter_;
1382
1383 gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
1384
1385 if (msginfo && !MSG_IS_INVALID(msginfo->flags) &&
1386 !MSG_IS_DELETED(msginfo->flags) &&
1387 msginfo->msgid && msginfo->msgid[0] != '\0') {
1388 iter_ = gtk_tree_iter_copy(iter);
1389 g_hash_table_replace(msgid_table, msginfo->msgid, iter_);
1390 }
1391
1392 return FALSE;
1393 }
1394
summary_msgid_table_create(SummaryView * summaryview)1395 static void summary_msgid_table_create(SummaryView *summaryview)
1396 {
1397 GHashTable *msgid_table;
1398
1399 g_return_if_fail(summaryview->msgid_table == NULL);
1400
1401 msgid_table = g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
1402 (GDestroyNotify)gtk_tree_iter_free);
1403
1404 gtk_tree_model_foreach(GTK_TREE_MODEL(summaryview->store),
1405 summary_msgid_table_create_func, msgid_table);
1406
1407 summaryview->msgid_table = msgid_table;
1408 }
1409
summary_msgid_table_destroy(SummaryView * summaryview)1410 static void summary_msgid_table_destroy(SummaryView *summaryview)
1411 {
1412 if (!summaryview->msgid_table)
1413 return;
1414
1415 g_hash_table_destroy(summaryview->msgid_table);
1416 summaryview->msgid_table = NULL;
1417 }
1418
summary_set_menu_sensitive(SummaryView * summaryview)1419 static void summary_set_menu_sensitive(SummaryView *summaryview)
1420 {
1421 GtkItemFactory *ifactory = summaryview->popupfactory;
1422 SummarySelection selection;
1423 GtkWidget *menuitem;
1424 gboolean sens;
1425
1426 selection = summary_get_selection_type(summaryview);
1427 sens = (selection == SUMMARY_SELECTED_MULTIPLE) ? FALSE : TRUE;
1428
1429 main_window_set_menu_sensitive(summaryview->mainwin);
1430
1431 if (FOLDER_ITEM_IS_SENT_FOLDER(summaryview->folder_item)) {
1432 gtk_widget_show(summaryview->reedit_menuitem);
1433 gtk_widget_show(summaryview->reedit_separator);
1434 menu_set_sensitive(ifactory, "/Re-edit", sens);
1435 } else {
1436 gtk_widget_hide(summaryview->reedit_menuitem);
1437 gtk_widget_hide(summaryview->reedit_separator);
1438 menu_set_sensitive(ifactory, "/Re-edit", FALSE);
1439 }
1440
1441 if (selection == SUMMARY_NONE) {
1442 menu_set_insensitive_all
1443 (GTK_MENU_SHELL(summaryview->popupmenu));
1444 return;
1445 }
1446
1447 if (summaryview->folder_item &&
1448 FOLDER_TYPE(summaryview->folder_item->folder) != F_NEWS) {
1449 menu_set_sensitive(ifactory, "/Move...", TRUE);
1450 menu_set_sensitive(ifactory, "/Delete", TRUE);
1451 } else {
1452 menu_set_sensitive(ifactory, "/Move...", FALSE);
1453 menu_set_sensitive(ifactory, "/Delete", FALSE);
1454 }
1455
1456 menu_set_sensitive(ifactory, "/Copy...", TRUE);
1457
1458 menu_set_sensitive(ifactory, "/Mark", TRUE);
1459 menu_set_sensitive(ifactory, "/Mark/Set flag", TRUE);
1460 menu_set_sensitive(ifactory, "/Mark/Unset flag", TRUE);
1461
1462 menu_set_sensitive(ifactory, "/Mark/Mark as unread", TRUE);
1463 menu_set_sensitive(ifactory, "/Mark/Mark as read", TRUE);
1464 menu_set_sensitive(ifactory, "/Mark/Mark all read", TRUE);
1465
1466 if (prefs_common.enable_junk) {
1467 gtk_widget_show(summaryview->junk_menuitem);
1468 gtk_widget_show(summaryview->nojunk_menuitem);
1469 gtk_widget_show(summaryview->junk_separator);
1470 menu_set_sensitive(ifactory, "/Set as junk mail", TRUE);
1471 menu_set_sensitive(ifactory, "/Set as not junk mail", TRUE);
1472 } else {
1473 gtk_widget_hide(summaryview->junk_menuitem);
1474 gtk_widget_hide(summaryview->nojunk_menuitem);
1475 gtk_widget_hide(summaryview->junk_separator);
1476 menu_set_sensitive(ifactory, "/Set as junk mail", FALSE);
1477 menu_set_sensitive(ifactory, "/Set as not junk mail", FALSE);
1478 }
1479
1480 menu_set_sensitive(ifactory, "/Color label", TRUE);
1481
1482 menu_set_sensitive(ifactory, "/Reply", sens);
1483 menu_set_sensitive(ifactory, "/Reply to", sens);
1484 menu_set_sensitive(ifactory, "/Reply to/all", sens);
1485 menu_set_sensitive(ifactory, "/Reply to/sender", sens);
1486 menu_set_sensitive(ifactory, "/Reply to/mailing list", sens);
1487 menu_set_sensitive(ifactory, "/Forward", TRUE);
1488 menu_set_sensitive(ifactory, "/Forward as attachment", TRUE);
1489 menu_set_sensitive(ifactory, "/Redirect", sens);
1490
1491 menu_set_sensitive(ifactory, "/Add sender to address book...", sens);
1492 menu_set_sensitive(ifactory, "/Create filter rule", sens);
1493
1494 menu_set_sensitive(ifactory, "/View", sens);
1495 menu_set_sensitive(ifactory, "/View/Open in new window", sens);
1496 menu_set_sensitive(ifactory, "/View/Message source", sens);
1497 menu_set_sensitive(ifactory, "/View/All headers", sens);
1498
1499 menu_set_sensitive(ifactory, "/Print...", TRUE);
1500
1501 summary_lock(summaryview);
1502 menuitem = gtk_item_factory_get_widget(ifactory, "/View/All headers");
1503 gtk_check_menu_item_set_active
1504 (GTK_CHECK_MENU_ITEM(menuitem),
1505 summaryview->messageview->textview->show_all_headers);
1506 summary_unlock(summaryview);
1507 }
1508
summary_select_prev_flagged(SummaryView * summaryview,MsgPermFlags flags,const gchar * title,const gchar * ask_msg,const gchar * notice)1509 static void summary_select_prev_flagged(SummaryView *summaryview,
1510 MsgPermFlags flags,
1511 const gchar *title,
1512 const gchar *ask_msg,
1513 const gchar *notice)
1514 {
1515 GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
1516 GtkTreeIter prev, iter;
1517 gboolean start_from_prev = FALSE;
1518 gboolean found;
1519
1520 if (!gtkut_tree_row_reference_get_iter(model, summaryview->selected,
1521 &iter))
1522 return;
1523
1524 if (!messageview_is_visible(summaryview->messageview) ||
1525 summary_row_is_displayed(summaryview, &iter))
1526 start_from_prev = TRUE;
1527
1528 found = summary_find_prev_flagged_msg
1529 (summaryview, &prev, &iter, flags, start_from_prev);
1530
1531 if (!found) {
1532 AlertValue val;
1533
1534 val = alertpanel(title, ask_msg, GTK_STOCK_YES, GTK_STOCK_NO,
1535 NULL);
1536 if (val != G_ALERTDEFAULT) return;
1537 found = summary_find_prev_flagged_msg(summaryview, &prev, NULL,
1538 flags, start_from_prev);
1539 }
1540
1541 if (!found) {
1542 if (notice)
1543 alertpanel_notice("%s", notice);
1544 } else {
1545 gboolean visible;
1546 visible = messageview_is_visible(summaryview->messageview);
1547 summary_select_row(summaryview, &prev, visible, FALSE);
1548 if (visible)
1549 summary_mark_displayed_read(summaryview, &prev);
1550 }
1551 }
1552
summary_select_next_flagged(SummaryView * summaryview,MsgPermFlags flags,const gchar * title,const gchar * ask_msg,const gchar * notice)1553 static void summary_select_next_flagged(SummaryView *summaryview,
1554 MsgPermFlags flags,
1555 const gchar *title,
1556 const gchar *ask_msg,
1557 const gchar *notice)
1558 {
1559 GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
1560 GtkTreeIter next, iter;
1561 gboolean start_from_next = FALSE;
1562 gboolean found;
1563
1564 if (!gtkut_tree_row_reference_get_iter(model, summaryview->selected,
1565 &iter)) {
1566 if (!gtk_tree_model_get_iter_first(model, &iter))
1567 return;
1568 }
1569
1570 if (!messageview_is_visible(summaryview->messageview) ||
1571 summary_row_is_displayed(summaryview, &iter))
1572 start_from_next = TRUE;
1573
1574 found = summary_find_next_flagged_msg
1575 (summaryview, &next, &iter, flags, start_from_next);
1576
1577 if (!found) {
1578 AlertValue val;
1579
1580 val = alertpanel(title, ask_msg, GTK_STOCK_YES, GTK_STOCK_NO,
1581 NULL);
1582 if (val != G_ALERTDEFAULT) return;
1583 found = summary_find_next_flagged_msg(summaryview, &next, NULL,
1584 flags, start_from_next);
1585 }
1586
1587 if (!found) {
1588 if (notice)
1589 alertpanel_notice("%s", notice);
1590 } else {
1591 gboolean visible;
1592 visible = messageview_is_visible(summaryview->messageview);
1593 summary_select_row(summaryview, &next, visible, FALSE);
1594 if (visible)
1595 summary_mark_displayed_read(summaryview, &next);
1596 }
1597 }
1598
summary_select_next_flagged_or_folder(SummaryView * summaryview,MsgPermFlags flags,const gchar * title,const gchar * ask_msg,const gchar * notice)1599 static void summary_select_next_flagged_or_folder(SummaryView *summaryview,
1600 MsgPermFlags flags,
1601 const gchar *title,
1602 const gchar *ask_msg,
1603 const gchar *notice)
1604 {
1605 GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
1606 GtkTreeIter iter, next;
1607 gboolean start_from_next = FALSE;
1608 gboolean visible;
1609
1610 if (!gtkut_tree_row_reference_get_iter(model, summaryview->selected,
1611 &iter)) {
1612 if (!gtk_tree_model_get_iter_first(model, &iter))
1613 return;
1614 }
1615
1616 if (!messageview_is_visible(summaryview->messageview) ||
1617 summary_row_is_displayed(summaryview, &iter))
1618 start_from_next = TRUE;
1619
1620 while (summary_find_next_flagged_msg
1621 (summaryview, &next, &iter, flags, start_from_next) == FALSE) {
1622 AlertValue val;
1623
1624 val = alertpanel(title, ask_msg,
1625 GTK_STOCK_YES, GTK_STOCK_NO,
1626 _("_Search again"));
1627
1628 if (val == G_ALERTDEFAULT) {
1629 folderview_select_next_unread(summaryview->folderview);
1630 return;
1631 } else if (val == G_ALERTOTHER) {
1632 start_from_next = FALSE;
1633 if (!gtk_tree_model_get_iter_first(model, &iter))
1634 return;
1635 } else
1636 return;
1637 }
1638
1639 visible = messageview_is_visible(summaryview->messageview);
1640 summary_select_row(summaryview, &next, visible, FALSE);
1641 if (visible)
1642 summary_mark_displayed_read(summaryview, &next);
1643 }
1644
summary_select_prev_unread(SummaryView * summaryview)1645 void summary_select_prev_unread(SummaryView *summaryview)
1646 {
1647 summary_select_prev_flagged(summaryview, MSG_UNREAD,
1648 _("No more unread messages"),
1649 _("No unread message found. "
1650 "Search from the end?"),
1651 _("No unread messages."));
1652 }
1653
summary_select_next_unread(SummaryView * summaryview)1654 void summary_select_next_unread(SummaryView *summaryview)
1655 {
1656 summary_select_next_flagged_or_folder(summaryview, MSG_UNREAD,
1657 _("No more unread messages"),
1658 _("No unread message found. "
1659 "Go to next unread folder?"),
1660 NULL);
1661 }
1662
summary_select_prev_new(SummaryView * summaryview)1663 void summary_select_prev_new(SummaryView *summaryview)
1664 {
1665 summary_select_prev_flagged(summaryview, MSG_NEW,
1666 _("No more new messages"),
1667 _("No new message found. "
1668 "Search from the end?"),
1669 _("No new messages."));
1670 }
1671
summary_select_next_new(SummaryView * summaryview)1672 void summary_select_next_new(SummaryView *summaryview)
1673 {
1674 summary_select_next_flagged_or_folder(summaryview, MSG_NEW,
1675 _("No more new messages"),
1676 _("No new message found. "
1677 "Go to next folder which has new messages?"),
1678 NULL);
1679 }
1680
summary_select_prev_marked(SummaryView * summaryview)1681 void summary_select_prev_marked(SummaryView *summaryview)
1682 {
1683 summary_select_prev_flagged(summaryview, MSG_MARKED,
1684 _("No more marked messages"),
1685 _("No marked message found. "
1686 "Search from the end?"),
1687 _("No marked messages."));
1688 }
1689
summary_select_next_marked(SummaryView * summaryview)1690 void summary_select_next_marked(SummaryView *summaryview)
1691 {
1692 summary_select_next_flagged(summaryview, MSG_MARKED,
1693 _("No more marked messages"),
1694 _("No marked message found. "
1695 "Search from the beginning?"),
1696 _("No marked messages."));
1697 }
1698
summary_select_prev_labeled(SummaryView * summaryview)1699 void summary_select_prev_labeled(SummaryView *summaryview)
1700 {
1701 summary_select_prev_flagged(summaryview, MSG_CLABEL_FLAG_MASK,
1702 _("No more labeled messages"),
1703 _("No labeled message found. "
1704 "Search from the end?"),
1705 _("No labeled messages."));
1706 }
1707
summary_select_next_labeled(SummaryView * summaryview)1708 void summary_select_next_labeled(SummaryView *summaryview)
1709 {
1710 summary_select_next_flagged(summaryview, MSG_CLABEL_FLAG_MASK,
1711 _("No more labeled messages"),
1712 _("No labeled message found. "
1713 "Search from the beginning?"),
1714 _("No labeled messages."));
1715 }
1716
summary_select_by_msgnum(SummaryView * summaryview,guint msgnum)1717 void summary_select_by_msgnum(SummaryView *summaryview, guint msgnum)
1718 {
1719 GtkTreeIter iter;
1720
1721 if (summary_find_msg_by_msgnum(summaryview, msgnum, &iter))
1722 summary_select_row(summaryview, &iter, FALSE, TRUE);
1723 }
1724
summary_select_by_msginfo(SummaryView * summaryview,MsgInfo * msginfo)1725 gboolean summary_select_by_msginfo(SummaryView *summaryview, MsgInfo *msginfo)
1726 {
1727 GtkTreeIter iter;
1728
1729 if (summaryview->folder_item != msginfo->folder)
1730 return FALSE;
1731
1732 if (summary_find_msg_by_msgnum(summaryview, msginfo->msgnum, &iter)) {
1733 summary_select_row(summaryview, &iter,
1734 messageview_is_visible(summaryview->messageview),
1735 TRUE);
1736 return TRUE;
1737 }
1738
1739 return FALSE;
1740 }
1741
summary_get_msginfo_by_msgnum(SummaryView * summaryview,guint msgnum)1742 MsgInfo *summary_get_msginfo_by_msgnum(SummaryView *summaryview, guint msgnum)
1743 {
1744 GtkTreeIter iter;
1745 MsgInfo *msginfo = NULL;
1746
1747 if (summary_find_msg_by_msgnum(summaryview, msgnum, &iter))
1748 GET_MSG_INFO(msginfo, &iter);
1749
1750 return msginfo;
1751 }
1752
summary_select_true_func(GtkTreeSelection * selection,GtkTreeModel * model,GtkTreePath * path,gboolean cur_selected,gpointer data)1753 static gboolean summary_select_true_func(GtkTreeSelection *selection, GtkTreeModel *model, GtkTreePath *path, gboolean cur_selected, gpointer data)
1754 {
1755 return TRUE;
1756 }
1757
1758 /**
1759 * summary_select_row:
1760 * @summaryview: Summary view.
1761 * @node: Summary tree node.
1762 * @display_msg: TRUE to display the selected message.
1763 * @do_refresh: TRUE to refresh the widget.
1764 *
1765 * Select @node (bringing it into view by scrolling and expanding its
1766 * thread, if necessary) and unselect all others. If @display_msg is
1767 * TRUE, display the corresponding message in the message view.
1768 * If @do_refresh is TRUE, the widget is refreshed.
1769 **/
summary_select_row(SummaryView * summaryview,GtkTreeIter * iter,gboolean display_msg,gboolean do_refresh)1770 void summary_select_row(SummaryView *summaryview, GtkTreeIter *iter,
1771 gboolean display_msg, gboolean do_refresh)
1772 {
1773 GtkTreePath *path;
1774
1775 if (!iter)
1776 return;
1777
1778 gtkut_tree_view_expand_parent_all
1779 (GTK_TREE_VIEW(summaryview->treeview), iter);
1780
1781 summaryview->display_msg = display_msg;
1782 path = gtk_tree_model_get_path(GTK_TREE_MODEL(summaryview->store),
1783 iter);
1784 if (!display_msg)
1785 gtk_tree_selection_set_select_function(summaryview->selection,
1786 summary_select_true_func,
1787 summaryview, NULL);
1788 gtk_tree_view_set_cursor(GTK_TREE_VIEW(summaryview->treeview), path,
1789 NULL, FALSE);
1790 if (!display_msg)
1791 gtk_tree_selection_set_select_function(summaryview->selection,
1792 summary_select_func,
1793 summaryview, NULL);
1794 if (do_refresh) {
1795 GTK_EVENTS_FLUSH();
1796 gtk_tree_view_scroll_to_cell
1797 (GTK_TREE_VIEW(summaryview->treeview),
1798 path, NULL, TRUE, 0.5, 0.0);
1799 } else {
1800 gtkut_tree_view_scroll_to_cell
1801 (GTK_TREE_VIEW(summaryview->treeview), path,
1802 !summaryview->on_button_press);
1803 }
1804
1805 gtk_tree_path_free(path);
1806 }
1807
summary_scroll_to_selected(SummaryView * summaryview,gboolean align_center)1808 static void summary_scroll_to_selected(SummaryView *summaryview,
1809 gboolean align_center)
1810 {
1811 GtkTreePath *path;
1812
1813 if (!summaryview->selected)
1814 return;
1815
1816 path = gtk_tree_row_reference_get_path(summaryview->selected);
1817 if (path) {
1818 if (align_center)
1819 gtk_tree_view_scroll_to_cell
1820 (GTK_TREE_VIEW(summaryview->treeview),
1821 path, NULL, TRUE, 0.5, 0.0);
1822 else
1823 gtkut_tree_view_scroll_to_cell
1824 (GTK_TREE_VIEW(summaryview->treeview), path,
1825 FALSE);
1826 gtk_tree_path_free(path);
1827 }
1828 }
1829
summary_get_msginfo(SummaryView * summaryview,GtkTreeRowReference * row)1830 static MsgInfo *summary_get_msginfo(SummaryView *summaryview,
1831 GtkTreeRowReference *row)
1832 {
1833 GtkTreeIter iter;
1834 MsgInfo *msginfo = NULL;
1835
1836 if (!row)
1837 return 0;
1838 if (!gtkut_tree_row_reference_get_iter
1839 (GTK_TREE_MODEL(summaryview->store), row, &iter))
1840 return 0;
1841
1842 gtk_tree_model_get(GTK_TREE_MODEL(summaryview->store), &iter,
1843 S_COL_MSG_INFO, &msginfo, -1);
1844
1845 return msginfo;
1846 }
1847
summary_get_msgnum(SummaryView * summaryview,GtkTreeRowReference * row)1848 static guint summary_get_msgnum(SummaryView *summaryview,
1849 GtkTreeRowReference *row)
1850 {
1851 MsgInfo *msginfo;
1852
1853 msginfo = summary_get_msginfo(summaryview, row);
1854 if (!msginfo)
1855 return 0;
1856
1857 return msginfo->msgnum;
1858 }
1859
summary_find_prev_msg(SummaryView * summaryview,GtkTreeIter * prev,GtkTreeIter * iter)1860 static gboolean summary_find_prev_msg(SummaryView *summaryview,
1861 GtkTreeIter *prev, GtkTreeIter *iter)
1862 {
1863 GtkTreeIter iter_;
1864 MsgInfo *msginfo;
1865 gboolean valid = TRUE;
1866
1867 if (!iter)
1868 return FALSE;
1869
1870 iter_ = *iter;
1871
1872 while (valid) {
1873 GET_MSG_INFO(msginfo, &iter_);
1874 if (msginfo && !MSG_IS_INVALID(msginfo->flags) &&
1875 !MSG_IS_DELETED(msginfo->flags)) {
1876 *prev = iter_;
1877 return TRUE;
1878 }
1879 valid = gtkut_tree_model_prev
1880 (GTK_TREE_MODEL(summaryview->store), &iter_);
1881 }
1882
1883 return FALSE;
1884 }
1885
summary_find_next_msg(SummaryView * summaryview,GtkTreeIter * next,GtkTreeIter * iter)1886 static gboolean summary_find_next_msg(SummaryView *summaryview,
1887 GtkTreeIter *next, GtkTreeIter *iter)
1888 {
1889 GtkTreeIter iter_;
1890 MsgInfo *msginfo;
1891 gboolean valid = TRUE;
1892
1893 if (!iter)
1894 return FALSE;
1895
1896 iter_ = *iter;
1897
1898 while (valid) {
1899 GET_MSG_INFO(msginfo, &iter_);
1900 if (msginfo && !MSG_IS_INVALID(msginfo->flags) &&
1901 !MSG_IS_DELETED(msginfo->flags)) {
1902 *next = iter_;
1903 return TRUE;
1904 }
1905 valid = gtkut_tree_model_next
1906 (GTK_TREE_MODEL(summaryview->store), &iter_);
1907 }
1908
1909 return FALSE;
1910 }
1911
summary_find_nearest_msg(SummaryView * summaryview,GtkTreeIter * target,GtkTreeIter * iter)1912 static gboolean summary_find_nearest_msg(SummaryView *summaryview,
1913 GtkTreeIter *target, GtkTreeIter *iter)
1914 {
1915 gboolean valid;
1916
1917 valid = summary_find_next_msg(summaryview, target, iter);
1918 if (!valid)
1919 valid = summary_find_prev_msg(summaryview, target, iter);
1920
1921 return valid;
1922 }
1923
summary_find_prev_flagged_msg(SummaryView * summaryview,GtkTreeIter * prev,GtkTreeIter * iter,MsgPermFlags flags,gboolean start_from_prev)1924 static gboolean summary_find_prev_flagged_msg(SummaryView *summaryview,
1925 GtkTreeIter *prev,
1926 GtkTreeIter *iter,
1927 MsgPermFlags flags,
1928 gboolean start_from_prev)
1929 {
1930 GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
1931 GtkTreeIter iter_;
1932 MsgInfo *msginfo;
1933 gboolean valid = TRUE;
1934
1935 if (iter) {
1936 iter_ = *iter;
1937 if (start_from_prev)
1938 valid = gtkut_tree_model_prev(model, &iter_);
1939 } else
1940 valid = gtkut_tree_model_get_iter_last(model, &iter_);
1941
1942 for (; valid == TRUE; valid = gtkut_tree_model_prev(model, &iter_)) {
1943 GET_MSG_INFO(msginfo, &iter_);
1944 if (msginfo && (msginfo->flags.perm_flags & flags) != 0) {
1945 *prev = iter_;
1946 return TRUE;
1947 }
1948 }
1949
1950 return FALSE;
1951 }
1952
summary_find_next_flagged_msg(SummaryView * summaryview,GtkTreeIter * next,GtkTreeIter * iter,MsgPermFlags flags,gboolean start_from_next)1953 static gboolean summary_find_next_flagged_msg(SummaryView *summaryview,
1954 GtkTreeIter *next,
1955 GtkTreeIter *iter,
1956 MsgPermFlags flags,
1957 gboolean start_from_next)
1958 {
1959 GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
1960 GtkTreeIter iter_;
1961 MsgInfo *msginfo;
1962 gboolean valid = TRUE;
1963
1964 if (iter) {
1965 iter_ = *iter;
1966 if (start_from_next)
1967 valid = gtkut_tree_model_next(model, &iter_);
1968 } else
1969 valid = gtk_tree_model_get_iter_first(model, &iter_);
1970
1971 for (; valid == TRUE; valid = gtkut_tree_model_next(model, &iter_)) {
1972 GET_MSG_INFO(msginfo, &iter_);
1973 if (msginfo && (msginfo->flags.perm_flags & flags) != 0) {
1974 *next = iter_;
1975 return TRUE;
1976 }
1977 }
1978
1979 return FALSE;
1980 }
1981
summary_find_msg_by_msgnum(SummaryView * summaryview,guint msgnum,GtkTreeIter * found)1982 static gboolean summary_find_msg_by_msgnum(SummaryView *summaryview,
1983 guint msgnum, GtkTreeIter *found)
1984 {
1985 GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
1986 GtkTreeIter iter;
1987 MsgInfo *msginfo;
1988 gboolean valid;
1989
1990 for (valid = gtk_tree_model_get_iter_first(model, &iter);
1991 valid == TRUE; valid = gtkut_tree_model_next(model, &iter)) {
1992 gtk_tree_model_get(model, &iter, S_COL_MSG_INFO, &msginfo, -1);
1993 if (msginfo && msginfo->msgnum == msgnum) {
1994 *found = iter;
1995 return TRUE;
1996 }
1997 }
1998
1999 return FALSE;
2000 }
2001
summary_update_display_state(SummaryView * summaryview,guint disp_msgnum,guint sel_msgnum)2002 static void summary_update_display_state(SummaryView *summaryview,
2003 guint disp_msgnum, guint sel_msgnum)
2004 {
2005 GtkTreeIter iter;
2006
2007 if (summary_find_msg_by_msgnum(summaryview, disp_msgnum, &iter)) {
2008 GtkTreePath *path;
2009 GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
2010
2011 path = gtk_tree_model_get_path(model, &iter);
2012 gtk_tree_row_reference_free(summaryview->displayed);
2013 summaryview->displayed =
2014 gtk_tree_row_reference_new(model, path);
2015 gtk_tree_path_free(path);
2016 } else
2017 messageview_clear(summaryview->messageview);
2018
2019 summary_select_by_msgnum(summaryview, sel_msgnum);
2020 }
2021
attract_hash_func(gconstpointer key)2022 static guint attract_hash_func(gconstpointer key)
2023 {
2024 gchar str[BUFFSIZE];
2025 gchar *p;
2026 guint h;
2027
2028 strncpy2(str, (const gchar *)key, sizeof(str));
2029 trim_subject_for_compare(str);
2030
2031 p = str;
2032 h = *p;
2033
2034 if (h) {
2035 for (p += 1; *p != '\0'; p++)
2036 h = (h << 5) - h + *p;
2037 }
2038
2039 return h;
2040 }
2041
attract_compare_func(gconstpointer a,gconstpointer b)2042 static gint attract_compare_func(gconstpointer a, gconstpointer b)
2043 {
2044 return subject_compare((const gchar *)a, (const gchar *)b) == 0;
2045 }
2046
summary_attract_by_subject(SummaryView * summaryview)2047 void summary_attract_by_subject(SummaryView *summaryview)
2048 {
2049 GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
2050 GtkTreeIter iter;
2051 MsgInfo *msginfo, *dest_msginfo;
2052 GHashTable *subject_table, *order_table;
2053 GSList *mlist = NULL, *list, *dest, *last = NULL, *next = NULL;
2054 gboolean valid;
2055 gint count, i;
2056 gint *new_order;
2057
2058 if (!summaryview->folder_item)
2059 return;
2060 if (summaryview->folder_item->sort_key != SORT_BY_NONE)
2061 return;
2062
2063 valid = gtk_tree_model_get_iter_first(model, &iter);
2064 if (!valid)
2065 return;
2066
2067 debug_print("Attracting messages by subject...");
2068 STATUSBAR_PUSH(summaryview->mainwin,
2069 _("Attracting messages by subject..."));
2070
2071 main_window_cursor_wait(summaryview->mainwin);
2072
2073 order_table = g_hash_table_new(NULL, NULL);
2074
2075 for (count = 1; valid == TRUE; ++count) {
2076 gtk_tree_model_get(model, &iter, S_COL_MSG_INFO, &msginfo, -1);
2077 g_hash_table_insert(order_table, msginfo,
2078 GINT_TO_POINTER(count));
2079 mlist = g_slist_prepend(mlist, msginfo);
2080 valid = gtk_tree_model_iter_next(model, &iter);
2081 }
2082 --count;
2083
2084 mlist = g_slist_reverse(mlist);
2085
2086 subject_table = g_hash_table_new(attract_hash_func,
2087 attract_compare_func);
2088
2089 for (list = mlist; list != NULL; list = next) {
2090 msginfo = (MsgInfo *)list->data;
2091
2092 next = list->next;
2093
2094 if (!msginfo->subject) {
2095 last = list;
2096 continue;
2097 }
2098
2099 /* find attracting node */
2100 dest = g_hash_table_lookup(subject_table, msginfo->subject);
2101
2102 if (dest) {
2103 dest_msginfo = (MsgInfo *)dest->data;
2104
2105 /* if the time difference is more than 30 days,
2106 don't attract */
2107 if (ABS(msginfo->date_t - dest_msginfo->date_t)
2108 > 60 * 60 * 24 * 30) {
2109 last = list;
2110 continue;
2111 }
2112
2113 if (dest->next != list) {
2114 last->next = list->next;
2115 list->next = dest->next;
2116 dest->next = list;
2117 } else
2118 last = list;
2119 } else
2120 last = list;
2121
2122 g_hash_table_replace(subject_table, msginfo->subject, list);
2123 }
2124
2125 g_hash_table_destroy(subject_table);
2126
2127 new_order = g_new(gint, count);
2128 for (list = mlist, i = 0; list != NULL; list = list->next, ++i) {
2129 gint old_pos;
2130
2131 msginfo = (MsgInfo *)list->data;
2132
2133 old_pos = GPOINTER_TO_INT
2134 (g_hash_table_lookup(order_table, msginfo));
2135 new_order[i] = old_pos - 1;
2136 }
2137 gtk_tree_store_reorder(GTK_TREE_STORE(model), NULL, new_order);
2138 g_free(new_order);
2139
2140 g_slist_free(mlist);
2141 g_hash_table_destroy(order_table);
2142
2143 summaryview->folder_item->cache_dirty = TRUE;
2144 summary_selection_list_free(summaryview);
2145 summary_update_msg_list(summaryview);
2146
2147 summary_scroll_to_selected(summaryview, TRUE);
2148
2149 debug_print("done.\n");
2150 STATUSBAR_POP(summaryview->mainwin);
2151
2152 main_window_cursor_normal(summaryview->mainwin);
2153 }
2154
summary_update_status(SummaryView * summaryview)2155 static void summary_update_status(SummaryView *summaryview)
2156 {
2157 GSList *cur;
2158 MsgInfo *msginfo;
2159 gint64 total_size = 0;
2160 gint deleted = 0, moved = 0, copied = 0;
2161 gint64 flt_total_size = 0;
2162 gint flt_deleted = 0, flt_moved = 0, flt_copied = 0;
2163 gint flt_new = 0, flt_unread = 0, flt_total = 0;
2164
2165 for (cur = summaryview->all_mlist; cur != NULL; cur = cur->next) {
2166 msginfo = (MsgInfo *)cur->data;
2167
2168 if (MSG_IS_DELETED(msginfo->flags))
2169 deleted++;
2170 if (MSG_IS_MOVE(msginfo->flags))
2171 moved++;
2172 if (MSG_IS_COPY(msginfo->flags))
2173 copied++;
2174 total_size += msginfo->size;
2175 }
2176
2177 for (cur = summaryview->flt_mlist; cur != NULL; cur = cur->next) {
2178 msginfo = (MsgInfo *)cur->data;
2179
2180 if (MSG_IS_DELETED(msginfo->flags))
2181 flt_deleted++;
2182 if (MSG_IS_MOVE(msginfo->flags))
2183 flt_moved++;
2184 if (MSG_IS_COPY(msginfo->flags))
2185 flt_copied++;
2186 if (MSG_IS_NEW(msginfo->flags))
2187 flt_new++;
2188 if (MSG_IS_UNREAD(msginfo->flags))
2189 flt_unread++;
2190 flt_total++;
2191 flt_total_size += msginfo->size;
2192 }
2193
2194 summaryview->total_size = total_size;
2195 summaryview->deleted = deleted;
2196 summaryview->moved = moved;
2197 summaryview->copied = copied;
2198 summaryview->total_flt_msg_size = flt_total_size;
2199 summaryview->flt_msg_total = flt_total;
2200 summaryview->flt_deleted = flt_deleted;
2201 summaryview->flt_moved = flt_moved;
2202 summaryview->flt_copied = flt_copied;
2203 summaryview->flt_new = flt_new;
2204 summaryview->flt_unread = flt_unread;
2205 }
2206
summary_status_show(SummaryView * summaryview)2207 static void summary_status_show(SummaryView *summaryview)
2208 {
2209 GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
2210 GString *str;
2211 gchar *name;
2212 GList *rowlist, *cur;
2213 guint n_selected = 0;
2214 gint64 sel_size = 0;
2215 MsgInfo *msginfo;
2216 gint deleted, moved, copied;
2217 gint new, unread, total;
2218 gint64 total_size;
2219
2220 if (!summaryview->folder_item) {
2221 gtk_label_set(GTK_LABEL(summaryview->statlabel_folder), "");
2222 gtk_label_set(GTK_LABEL(summaryview->statlabel_select), "");
2223 gtk_label_set(GTK_LABEL(summaryview->statlabel_msgs), "");
2224 return;
2225 }
2226
2227 rowlist = summary_get_selected_rows(summaryview);
2228 for (cur = rowlist; cur != NULL; cur = cur->next) {
2229 GtkTreeIter iter;
2230 GtkTreePath *path = (GtkTreePath *)cur->data;
2231
2232 if (gtk_tree_model_get_iter(model, &iter, path)) {
2233 gtk_tree_model_get(model, &iter,
2234 S_COL_MSG_INFO, &msginfo, -1);
2235 sel_size += msginfo->size;
2236 n_selected++;
2237 }
2238 }
2239
2240 if (FOLDER_TYPE(summaryview->folder_item->folder) == F_NEWS) {
2241 gchar *group;
2242 group = get_abbrev_newsgroup_name
2243 (g_basename(summaryview->folder_item->path),
2244 prefs_common.ng_abbrev_len);
2245 name = trim_string_before(group, 32);
2246 g_free(group);
2247 } else
2248 name = trim_string_before(summaryview->folder_item->path, 32);
2249 gtk_label_set(GTK_LABEL(summaryview->statlabel_folder), name);
2250 g_free(name);
2251
2252 if (summaryview->on_filter) {
2253 deleted = summaryview->flt_deleted;
2254 moved = summaryview->flt_moved;
2255 copied = summaryview->flt_copied;
2256 } else {
2257 deleted = summaryview->deleted;
2258 moved = summaryview->moved;
2259 copied = summaryview->copied;
2260 }
2261
2262 str = g_string_sized_new(128);
2263
2264 if (n_selected)
2265 g_string_append_printf(str, "%d%s (%s)", n_selected,
2266 _(" item(s) selected"),
2267 to_human_readable(sel_size));
2268 if (str->len > 0 && (deleted || moved || copied))
2269 g_string_append(str, " ");
2270 if (deleted)
2271 g_string_append_printf(str, _("%d deleted"), deleted);
2272 if (moved)
2273 g_string_append_printf(str, _("%s%d moved"),
2274 deleted ? _(", ") : "", moved);
2275 if (copied)
2276 g_string_append_printf(str, _("%s%d copied"),
2277 deleted || moved ? _(", ") : "", copied);
2278
2279 gtk_label_set(GTK_LABEL(summaryview->statlabel_select), str->str);
2280 g_string_truncate(str, 0);
2281
2282 new = summaryview->folder_item->new;
2283 unread = summaryview->folder_item->unread;
2284 total = summaryview->folder_item->total;
2285 total_size = summaryview->total_size;
2286
2287 if (summaryview->on_filter) {
2288 gint f_new, f_unread, f_total;
2289 gint64 f_total_size;
2290 gchar f_ts[16], ts[16];
2291
2292 f_new = summaryview->flt_new;
2293 f_unread = summaryview->flt_unread;
2294 f_total = summaryview->flt_msg_total;
2295 f_total_size = summaryview->total_flt_msg_size;
2296
2297 g_string_printf(str, _("%d/%d new, %d/%d unread, %d/%d total"),
2298 f_new, new, f_unread, unread, f_total, total);
2299 if (FOLDER_IS_LOCAL(summaryview->folder_item->folder)) {
2300 g_string_append_printf(str, " (%s/%s)",
2301 to_human_readable_buf(f_ts, sizeof(f_ts), f_total_size), to_human_readable_buf(ts, sizeof(ts), total_size));
2302 }
2303 } else {
2304 if (FOLDER_IS_LOCAL(summaryview->folder_item->folder)) {
2305 g_string_printf(str,
2306 _("%d new, %d unread, %d total (%s)"),
2307 new, unread, total,
2308 to_human_readable(total_size));
2309 } else {
2310 g_string_printf(str, _("%d new, %d unread, %d total"),
2311 new, unread, total);
2312 }
2313 }
2314
2315 gtk_label_set(GTK_LABEL(summaryview->statlabel_msgs), str->str);
2316 g_string_free(str, TRUE);
2317
2318 folderview_update_opened_msg_num(summaryview->folderview);
2319 }
2320
summary_sort(SummaryView * summaryview,FolderSortKey sort_key,FolderSortType sort_type)2321 void summary_sort(SummaryView *summaryview,
2322 FolderSortKey sort_key, FolderSortType sort_type)
2323 {
2324 FolderItem *item = summaryview->folder_item;
2325 GtkTreeSortable *sortable = GTK_TREE_SORTABLE(summaryview->store);
2326 SummaryColumnType col_type, prev_col_type;
2327 GtkTreeViewColumn *column;
2328
2329 g_return_if_fail(sort_key >= SORT_BY_NONE && sort_key <= SORT_BY_TO);
2330
2331 if (!item || !item->path || !item->parent || item->no_select) return;
2332
2333 if (item->sort_key != sort_key || item->sort_type != sort_type)
2334 item->cache_dirty = TRUE;
2335
2336 col_type = sort_key_to_col[sort_key];
2337 prev_col_type = sort_key_to_col[item->sort_key];
2338
2339 if (col_type == -1) {
2340 item->sort_key = SORT_BY_NONE;
2341 item->sort_type = SORT_ASCENDING;
2342 summary_unset_sort_column_id(summaryview);
2343 summary_set_menu_sensitive(summaryview);
2344 return;
2345 }
2346
2347 debug_print("Sorting summary by key: %d...\n", sort_key);
2348 STATUSBAR_PUSH(summaryview->mainwin, _("Sorting summary..."));
2349
2350 main_window_cursor_wait(summaryview->mainwin);
2351
2352 item->sort_key = sort_key;
2353 item->sort_type = sort_type;
2354
2355 gtk_tree_sortable_set_sort_column_id(sortable, col_type,
2356 (GtkSortType)sort_type);
2357
2358 if (prev_col_type != -1 && col_type != prev_col_type &&
2359 prev_col_type < N_SUMMARY_VISIBLE_COLS) {
2360 column = summaryview->columns[prev_col_type];
2361 column->sort_column_id = -1;
2362 gtk_tree_view_column_set_sort_indicator(column, FALSE);
2363 }
2364 if (col_type != S_COL_MARK && col_type != S_COL_UNREAD &&
2365 col_type != S_COL_MIME && col_type < N_SUMMARY_VISIBLE_COLS) {
2366 column = summaryview->columns[col_type];
2367 column->sort_column_id = col_type;
2368 gtk_tree_view_column_set_sort_indicator(column, TRUE);
2369 gtk_tree_view_column_set_sort_order(column,
2370 (GtkSortType)sort_type);
2371 }
2372
2373 summary_selection_list_free(summaryview);
2374 if (summaryview->all_mlist)
2375 summary_update_msg_list(summaryview);
2376
2377 summary_set_menu_sensitive(summaryview);
2378
2379 summary_scroll_to_selected(summaryview, TRUE);
2380
2381 debug_print("done.\n");
2382 STATUSBAR_POP(summaryview->mainwin);
2383
2384 main_window_cursor_normal(summaryview->mainwin);
2385 }
2386
summary_have_unread_children(SummaryView * summaryview,GtkTreeIter * iter)2387 static gboolean summary_have_unread_children(SummaryView *summaryview,
2388 GtkTreeIter *iter)
2389 {
2390 GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
2391 GtkTreeIter iter_;
2392 MsgInfo *msginfo;
2393 gboolean valid;
2394
2395 gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
2396 if (MSG_IS_UNREAD(msginfo->flags))
2397 return TRUE;
2398
2399 valid = gtk_tree_model_iter_children(model, &iter_, iter);
2400
2401 while (valid) {
2402 if (summary_have_unread_children(summaryview, &iter_))
2403 return TRUE;
2404
2405 valid = gtk_tree_model_iter_next(model, &iter_);
2406 }
2407
2408 return FALSE;
2409 }
2410
summary_set_row(SummaryView * summaryview,GtkTreeIter * iter,MsgInfo * msginfo)2411 static void summary_set_row(SummaryView *summaryview, GtkTreeIter *iter,
2412 MsgInfo *msginfo)
2413 {
2414 GtkTreeStore *store = GTK_TREE_STORE(summaryview->store);
2415 gchar date_modified[80];
2416 const gchar *date_s;
2417 gchar *sw_from_s = NULL;
2418 gchar *subject_s = NULL;
2419 gchar *to_s = NULL;
2420 const gchar *disp_from = NULL;
2421 GdkPixbuf *mark_pix = NULL;
2422 GdkPixbuf *unread_pix = NULL;
2423 GdkPixbuf *mime_pix = NULL;
2424 GdkColor *foreground = NULL;
2425 PangoWeight weight = PANGO_WEIGHT_NORMAL;
2426 MsgFlags flags;
2427 GdkColor color;
2428 gint color_val;
2429
2430 if (!msginfo) {
2431 GET_MSG_INFO(msginfo, iter);
2432 }
2433
2434 if (msginfo->date_t) {
2435 procheader_date_get_localtime(date_modified,
2436 sizeof(date_modified),
2437 msginfo->date_t);
2438 date_s = date_modified;
2439 } else if (msginfo->date)
2440 date_s = msginfo->date;
2441 else
2442 date_s = _("(No Date)");
2443 if (prefs_common.swap_from && msginfo->from && msginfo->to) {
2444 gchar from[BUFFSIZE];
2445
2446 strncpy2(from, msginfo->from, sizeof(from));
2447 extract_address(from);
2448 if (account_address_exist(from)) {
2449 sw_from_s = g_strconcat("-->", msginfo->to, NULL);
2450 disp_from = sw_from_s;
2451 }
2452 }
2453
2454 if (!disp_from) {
2455 /* prevent address-like display-name */
2456 if (!msginfo->fromname ||
2457 strchr(msginfo->fromname, '@') != NULL)
2458 disp_from = msginfo->from;
2459 else
2460 disp_from = msginfo->fromname;
2461 }
2462
2463 if (msginfo->subject && *msginfo->subject) {
2464 if (msginfo->folder && msginfo->folder->trim_summary_subject) {
2465 subject_s = g_strdup(msginfo->subject);
2466 trim_subject(subject_s);
2467 }
2468 }
2469
2470 if (msginfo->to)
2471 to_s = procheader_get_toname(msginfo->to);
2472
2473 flags = msginfo->flags;
2474
2475 /* set flag pixbufs */
2476 if (MSG_IS_DELETED(flags)) {
2477 mark_pix = deleted_pixbuf;
2478 foreground = &summaryview->color_dim;
2479 } else if (MSG_IS_MOVE(flags)) {
2480 /* mark_pix = move_pixbuf; */
2481 foreground = &summaryview->color_marked;
2482 } else if (MSG_IS_COPY(flags)) {
2483 /* mark_pix = copy_pixbuf; */
2484 foreground = &summaryview->color_marked;
2485 } else if (MSG_IS_MARKED(flags))
2486 mark_pix = mark_pixbuf;
2487
2488 if (MSG_IS_NEW(flags))
2489 unread_pix = new_pixbuf;
2490 else if (MSG_IS_UNREAD(flags))
2491 unread_pix = unread_pixbuf;
2492 else if (MSG_IS_REPLIED(flags))
2493 unread_pix = replied_pixbuf;
2494 else if (MSG_IS_FORWARDED(flags))
2495 unread_pix = forwarded_pixbuf;
2496
2497 if (MSG_IS_MIME(flags)) {
2498 mime_pix = clip_pixbuf;
2499 }
2500 if (MSG_IS_MIME_HTML(flags)) {
2501 mime_pix = html_pixbuf;
2502 }
2503
2504 if (prefs_common.bold_unread) {
2505 if (MSG_IS_UNREAD(flags))
2506 weight = PANGO_WEIGHT_BOLD;
2507 else if (gtk_tree_model_iter_has_child(GTK_TREE_MODEL(store),
2508 iter)) {
2509 GtkTreePath *path;
2510
2511 path = gtk_tree_model_get_path
2512 (GTK_TREE_MODEL(store), iter);
2513 if (!gtk_tree_view_row_expanded
2514 (GTK_TREE_VIEW(summaryview->treeview), path) &&
2515 summary_have_unread_children(summaryview, iter))
2516 weight = PANGO_WEIGHT_BOLD;
2517 gtk_tree_path_free(path);
2518 }
2519 }
2520
2521 color_val = MSG_GET_COLORLABEL_VALUE(flags);
2522 if (color_val != 0) {
2523 color = colorlabel_get_color(color_val - 1);
2524 foreground = &color;
2525 }
2526
2527 gtk_tree_store_set(store, iter,
2528 S_COL_MARK, mark_pix,
2529 S_COL_UNREAD, unread_pix,
2530 S_COL_MIME, mime_pix,
2531 S_COL_SUBJECT, subject_s ? subject_s :
2532 msginfo->subject && *msginfo->subject ? msginfo->subject :
2533 _("(No Subject)"),
2534 S_COL_FROM, disp_from ? disp_from : _("(No From)"),
2535 S_COL_DATE, date_s,
2536 S_COL_SIZE, to_human_readable(msginfo->size),
2537 S_COL_NUMBER, msginfo->msgnum,
2538 S_COL_TO, to_s ? to_s : "",
2539
2540 S_COL_MSG_INFO, msginfo,
2541
2542 S_COL_LABEL, color_val,
2543
2544 S_COL_FOREGROUND, foreground,
2545 S_COL_BOLD, weight,
2546 -1);
2547
2548 if (to_s)
2549 g_free(to_s);
2550 if (subject_s)
2551 g_free(subject_s);
2552 if (sw_from_s)
2553 g_free(sw_from_s);
2554 }
2555
summary_insert_gnode(SummaryView * summaryview,GtkTreeStore * store,GtkTreeIter * iter,GtkTreeIter * parent,GtkTreeIter * sibling,GNode * gnode)2556 static void summary_insert_gnode(SummaryView *summaryview, GtkTreeStore *store,
2557 GtkTreeIter *iter, GtkTreeIter *parent,
2558 GtkTreeIter *sibling, GNode *gnode)
2559 {
2560 MsgInfo *msginfo = (MsgInfo *)gnode->data;
2561
2562 if (parent && !sibling)
2563 gtk_tree_store_append(store, iter, parent);
2564 else
2565 gtk_tree_store_insert_after(store, iter, parent, sibling);
2566
2567 summary_set_row(summaryview, iter, msginfo);
2568
2569 if (!parent) {
2570 guint tdate;
2571
2572 tdate = procmsg_get_thread_date(gnode);
2573 gtk_tree_store_set(store, iter, S_COL_TDATE, tdate, -1);
2574 }
2575
2576 for (gnode = gnode->children; gnode != NULL; gnode = gnode->next) {
2577 GtkTreeIter child;
2578
2579 summary_insert_gnode(summaryview, store, &child, iter, NULL,
2580 gnode);
2581 }
2582 }
2583
summary_insert_gnode_before(SummaryView * summaryview,GtkTreeStore * store,GtkTreeIter * iter,GtkTreeIter * parent,GtkTreeIter * sibling,GNode * gnode)2584 static void summary_insert_gnode_before(SummaryView *summaryview,
2585 GtkTreeStore *store,
2586 GtkTreeIter *iter, GtkTreeIter *parent,
2587 GtkTreeIter *sibling, GNode *gnode)
2588 {
2589 MsgInfo *msginfo = (MsgInfo *)gnode->data;
2590
2591 gtk_tree_store_insert_before(store, iter, parent, sibling);
2592
2593 summary_set_row(summaryview, iter, msginfo);
2594
2595 if (!parent) {
2596 guint tdate;
2597
2598 tdate = procmsg_get_thread_date(gnode);
2599 gtk_tree_store_set(store, iter, S_COL_TDATE, tdate, -1);
2600 }
2601
2602 for (gnode = gnode->children; gnode != NULL; gnode = gnode->next) {
2603 GtkTreeIter child;
2604
2605 summary_insert_gnode_before(summaryview, store, &child, iter,
2606 NULL, gnode);
2607 }
2608 }
2609
summary_set_tree_model_from_list(SummaryView * summaryview,GSList * mlist)2610 static void summary_set_tree_model_from_list(SummaryView *summaryview,
2611 GSList *mlist)
2612 {
2613 GtkTreeStore *store = GTK_TREE_STORE(summaryview->store);
2614 GtkTreeIter iter;
2615 MsgInfo *msginfo;
2616 GSList *cur;
2617
2618 debug_print(_("\tSetting summary from message data..."));
2619 STATUSBAR_PUSH(summaryview->mainwin,
2620 _("Setting summary from message data..."));
2621 gdk_flush();
2622
2623 /* temporarily remove the model for speed up */
2624 gtk_tree_view_set_model(GTK_TREE_VIEW(summaryview->treeview), NULL);
2625
2626 if (summaryview->folder_item->threaded) {
2627 GNode *root, *gnode;
2628
2629 root = procmsg_get_thread_tree(mlist);
2630
2631 for (gnode = root->children; gnode != NULL;
2632 gnode = gnode->next) {
2633 summary_insert_gnode
2634 (summaryview, store, &iter, NULL, NULL, gnode);
2635 if (gnode->children && !prefs_common.expand_thread &&
2636 prefs_common.bold_unread &&
2637 summary_have_unread_children(summaryview, &iter)) {
2638 gtk_tree_store_set(store, &iter,
2639 S_COL_BOLD,
2640 PANGO_WEIGHT_BOLD, -1);
2641 }
2642 }
2643
2644 g_node_destroy(root);
2645
2646 for (cur = mlist; cur != NULL; cur = cur->next) {
2647 msginfo = (MsgInfo *)cur->data;
2648
2649 if (MSG_IS_DELETED(msginfo->flags))
2650 summaryview->deleted++;
2651 if (MSG_IS_MOVE(msginfo->flags))
2652 summaryview->moved++;
2653 if (MSG_IS_COPY(msginfo->flags))
2654 summaryview->copied++;
2655 summaryview->total_size += msginfo->size;
2656 }
2657 } else {
2658 GSList *rev_mlist;
2659 GtkTreeIter iter;
2660
2661 rev_mlist = g_slist_reverse(g_slist_copy(mlist));
2662 for (cur = rev_mlist; cur != NULL; cur = cur->next) {
2663 msginfo = (MsgInfo *)cur->data;
2664
2665 gtk_tree_store_prepend(store, &iter, NULL);
2666 summary_set_row(summaryview, &iter, msginfo);
2667
2668 if (MSG_IS_DELETED(msginfo->flags))
2669 summaryview->deleted++;
2670 if (MSG_IS_MOVE(msginfo->flags))
2671 summaryview->moved++;
2672 if (MSG_IS_COPY(msginfo->flags))
2673 summaryview->copied++;
2674 summaryview->total_size += msginfo->size;
2675 }
2676 g_slist_free(rev_mlist);
2677 }
2678
2679 gtk_tree_view_set_model(GTK_TREE_VIEW(summaryview->treeview),
2680 GTK_TREE_MODEL(store));
2681
2682 if (summaryview->folder_item->threaded && prefs_common.expand_thread)
2683 gtk_tree_view_expand_all
2684 (GTK_TREE_VIEW(summaryview->treeview));
2685
2686 if (summaryview->folder_item->sort_key != SORT_BY_NONE) {
2687 summary_sort(summaryview, summaryview->folder_item->sort_key,
2688 summaryview->folder_item->sort_type);
2689 }
2690
2691 debug_print(_("done.\n"));
2692 STATUSBAR_POP(summaryview->mainwin);
2693 }
2694
2695 struct wcachefp
2696 {
2697 FILE *cache_fp;
2698 FILE *mark_fp;
2699 };
2700
summary_write_cache(SummaryView * summaryview)2701 gint summary_write_cache(SummaryView *summaryview)
2702 {
2703 struct wcachefp fps;
2704 FolderItem *item;
2705 gchar *buf;
2706 GSList *cur;
2707
2708 item = summaryview->folder_item;
2709 if (!item || !item->path)
2710 return -1;
2711 if (item->mark_queue)
2712 item->mark_dirty = TRUE;
2713 if (!item->cache_dirty && !item->mark_dirty)
2714 return 0;
2715
2716 if (item->cache_dirty) {
2717 fps.cache_fp = procmsg_open_cache_file(item, DATA_WRITE);
2718 if (fps.cache_fp == NULL)
2719 return -1;
2720 item->mark_dirty = TRUE;
2721 } else
2722 fps.cache_fp = NULL;
2723
2724 if (item->mark_dirty && item->stype != F_VIRTUAL) {
2725 fps.mark_fp = procmsg_open_mark_file(item, DATA_WRITE);
2726 if (fps.mark_fp == NULL) {
2727 if (fps.cache_fp)
2728 fclose(fps.cache_fp);
2729 return -1;
2730 }
2731 } else
2732 fps.mark_fp = NULL;
2733
2734 if (item->cache_dirty) {
2735 buf = g_strdup_printf(_("Writing summary cache (%s)..."),
2736 item->path);
2737 debug_print("%s", buf);
2738 STATUSBAR_PUSH(summaryview->mainwin, buf);
2739 gdk_flush();
2740 g_free(buf);
2741 }
2742
2743 for (cur = summaryview->all_mlist; cur != NULL; cur = cur->next) {
2744 MsgInfo *msginfo = (MsgInfo *)cur->data;
2745
2746 if (msginfo->folder && msginfo->folder->mark_queue != NULL) {
2747 MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_NEW);
2748 }
2749 if (fps.cache_fp)
2750 procmsg_write_cache(msginfo, fps.cache_fp);
2751 if (fps.mark_fp)
2752 procmsg_write_flags(msginfo, fps.mark_fp);
2753 }
2754
2755 if (item->cache_queue)
2756 procmsg_flush_cache_queue(item, fps.cache_fp);
2757 if (item->mark_queue)
2758 procmsg_flush_mark_queue(item, fps.mark_fp);
2759
2760 item->unmarked_num = 0;
2761
2762 if (fps.cache_fp)
2763 fclose(fps.cache_fp);
2764 if (fps.mark_fp)
2765 fclose(fps.mark_fp);
2766
2767 if (item->stype == F_VIRTUAL) {
2768 GSList *mlist;
2769
2770 mlist = summary_get_changed_msg_list(summaryview);
2771 if (mlist) {
2772 procmsg_write_flags_for_multiple_folders(mlist);
2773 g_slist_free(mlist);
2774 folderview_update_all_updated(FALSE);
2775 }
2776 }
2777
2778 debug_print(_("done.\n"));
2779
2780 if (item->cache_dirty) {
2781 STATUSBAR_POP(summaryview->mainwin);
2782 }
2783
2784 item->cache_dirty = item->mark_dirty = FALSE;
2785
2786 return 0;
2787 }
2788
summary_row_is_displayed(SummaryView * summaryview,GtkTreeIter * iter)2789 static gboolean summary_row_is_displayed(SummaryView *summaryview,
2790 GtkTreeIter *iter)
2791 {
2792 GtkTreePath *disp_path, *path;
2793 gint ret;
2794
2795 if (!summaryview->displayed || !iter)
2796 return FALSE;
2797
2798 disp_path = gtk_tree_row_reference_get_path(summaryview->displayed);
2799 if (!disp_path)
2800 return FALSE;
2801
2802 path = gtk_tree_model_get_path(GTK_TREE_MODEL(summaryview->store),
2803 iter);
2804 if (!path) {
2805 gtk_tree_path_free(disp_path);
2806 return FALSE;
2807 }
2808
2809 ret = gtk_tree_path_compare(disp_path, path);
2810 gtk_tree_path_free(path);
2811 gtk_tree_path_free(disp_path);
2812
2813 return (ret == 0);
2814 }
2815
summary_display_msg(SummaryView * summaryview,GtkTreeIter * iter)2816 static void summary_display_msg(SummaryView *summaryview, GtkTreeIter *iter)
2817 {
2818 summary_display_msg_full(summaryview, iter, FALSE, FALSE, FALSE);
2819 }
2820
summary_display_msg_full(SummaryView * summaryview,GtkTreeIter * iter,gboolean new_window,gboolean all_headers,gboolean redisplay)2821 static void summary_display_msg_full(SummaryView *summaryview,
2822 GtkTreeIter *iter,
2823 gboolean new_window, gboolean all_headers,
2824 gboolean redisplay)
2825 {
2826 GtkTreePath *path;
2827 MsgInfo *msginfo = NULL;
2828 gint val;
2829 gboolean do_mark_read = FALSE;
2830
2831 g_return_if_fail(iter != NULL);
2832
2833 if (!new_window && !redisplay &&
2834 summary_row_is_displayed(summaryview, iter))
2835 return;
2836
2837 if (summary_is_read_locked(summaryview)) return;
2838 summary_lock(summaryview);
2839
2840 STATUSBAR_POP(summaryview->mainwin);
2841
2842 gtk_tree_model_get(GTK_TREE_MODEL(summaryview->store), iter,
2843 S_COL_MSG_INFO, &msginfo, -1);
2844
2845 do_mark_read = prefs_common.always_mark_read_on_show_msg;
2846
2847 if (new_window) {
2848 MessageView *msgview;
2849
2850 msgview = messageview_create_with_new_window();
2851 val = messageview_show(msgview, msginfo, all_headers);
2852 do_mark_read = TRUE;
2853 } else {
2854 MessageView *msgview = summaryview->messageview;
2855 gboolean prev_mimeview;
2856
2857 if (!messageview_is_visible(msgview)) {
2858 main_window_toggle_message_view(summaryview->mainwin);
2859 GTK_EVENTS_FLUSH();
2860 }
2861 prev_mimeview =
2862 messageview_get_selected_mime_part(msgview) != NULL;
2863
2864 val = messageview_show(msgview, msginfo, all_headers);
2865 if (prev_mimeview &&
2866 !messageview_get_selected_mime_part(msgview))
2867 gtk_widget_grab_focus(summaryview->treeview);
2868 }
2869
2870 if (val == 0 && do_mark_read) {
2871 if (MSG_IS_NEW(msginfo->flags) ||
2872 MSG_IS_UNREAD(msginfo->flags)) {
2873 summary_mark_row_as_read(summaryview, iter);
2874 if (MSG_IS_IMAP(msginfo->flags))
2875 imap_msg_unset_perm_flags
2876 (msginfo, MSG_NEW | MSG_UNREAD);
2877 summary_set_row(summaryview, iter, msginfo);
2878 summary_status_show(summaryview);
2879 }
2880 }
2881
2882 path = gtk_tree_model_get_path
2883 (GTK_TREE_MODEL(summaryview->store), iter);
2884 if (!new_window) {
2885 gtk_tree_row_reference_free(summaryview->displayed);
2886 summaryview->displayed =
2887 gtk_tree_row_reference_new
2888 (GTK_TREE_MODEL(summaryview->store), path);
2889 }
2890 gtkut_tree_view_scroll_to_cell
2891 (GTK_TREE_VIEW(summaryview->treeview), path,
2892 !summaryview->on_button_press);
2893 gtk_tree_path_free(path);
2894
2895 if (summaryview->folder_item->sort_key == SORT_BY_UNREAD)
2896 summary_selection_list_free(summaryview);
2897
2898 summary_set_menu_sensitive(summaryview);
2899 main_window_set_toolbar_sensitive(summaryview->mainwin);
2900
2901 trayicon_set_tooltip(NULL);
2902 trayicon_set_notify(FALSE);
2903
2904 statusbar_pop_all();
2905
2906 summary_unlock(summaryview);
2907 }
2908
summary_display_msg_selected(SummaryView * summaryview,gboolean new_window,gboolean all_headers)2909 void summary_display_msg_selected(SummaryView *summaryview,
2910 gboolean new_window, gboolean all_headers)
2911 {
2912 GtkTreeIter iter;
2913
2914 if (summary_is_read_locked(summaryview)) return;
2915
2916 if (summaryview->selected) {
2917 if (gtkut_tree_row_reference_get_iter
2918 (GTK_TREE_MODEL(summaryview->store),
2919 summaryview->selected, &iter)) {
2920 summary_display_msg_full(summaryview, &iter,
2921 new_window, all_headers, TRUE);
2922 }
2923 }
2924 }
2925
summary_redisplay_msg(SummaryView * summaryview)2926 void summary_redisplay_msg(SummaryView *summaryview)
2927 {
2928 GtkTreeIter iter;
2929
2930 if (summaryview->displayed) {
2931 if (gtkut_tree_row_reference_get_iter
2932 (GTK_TREE_MODEL(summaryview->store),
2933 summaryview->displayed, &iter)) {
2934 summary_display_msg_full(summaryview, &iter,
2935 FALSE, FALSE, TRUE);
2936 }
2937 }
2938 }
2939
summary_open_msg(SummaryView * summaryview)2940 void summary_open_msg(SummaryView *summaryview)
2941 {
2942 summary_display_msg_selected(summaryview, TRUE, FALSE);
2943 }
2944
summary_activate_selected(SummaryView * summaryview)2945 static void summary_activate_selected(SummaryView *summaryview)
2946 {
2947 if (!summaryview->folder_item)
2948 return;
2949
2950 if (FOLDER_ITEM_IS_SENT_FOLDER(summaryview->folder_item))
2951 summary_reedit(summaryview);
2952 else
2953 summary_open_msg(summaryview);
2954
2955 summaryview->display_msg = FALSE;
2956 }
2957
summary_view_source(SummaryView * summaryview)2958 void summary_view_source(SummaryView *summaryview)
2959 {
2960 GtkTreeIter iter;
2961 MsgInfo *msginfo;
2962 SourceWindow *srcwin;
2963
2964 if (summaryview->selected) {
2965 if (gtkut_tree_row_reference_get_iter
2966 (GTK_TREE_MODEL(summaryview->store),
2967 summaryview->selected, &iter)) {
2968 GET_MSG_INFO(msginfo, &iter);
2969
2970 srcwin = source_window_create();
2971 source_window_show_msg(srcwin, msginfo);
2972 source_window_show(srcwin);
2973 }
2974 }
2975 }
2976
summary_reedit(SummaryView * summaryview)2977 void summary_reedit(SummaryView *summaryview)
2978 {
2979 GtkTreeIter iter;
2980 MsgInfo *msginfo;
2981
2982 if (!summaryview->selected) return;
2983 if (!FOLDER_ITEM_IS_SENT_FOLDER(summaryview->folder_item)) return;
2984
2985 if (gtkut_tree_row_reference_get_iter
2986 (GTK_TREE_MODEL(summaryview->store),
2987 summaryview->selected, &iter)) {
2988 GET_MSG_INFO(msginfo, &iter);
2989 compose_reedit(msginfo);
2990 }
2991 }
2992
summary_step(SummaryView * summaryview,GtkScrollType type)2993 gboolean summary_step(SummaryView *summaryview, GtkScrollType type)
2994 {
2995 GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
2996 GtkTreeIter iter;
2997
2998 if (summary_is_read_locked(summaryview)) return FALSE;
2999
3000 if (!gtkut_tree_row_reference_get_iter
3001 (model, summaryview->selected, &iter))
3002 return FALSE;
3003
3004 if (type == GTK_SCROLL_STEP_FORWARD) {
3005 if (!gtkut_tree_model_next(model, &iter))
3006 return FALSE;
3007 } else {
3008 if (!gtkut_tree_model_prev(model, &iter))
3009 return FALSE;
3010 }
3011
3012 summary_select_row(summaryview, &iter,
3013 messageview_is_visible(summaryview->messageview),
3014 FALSE);
3015
3016 return TRUE;
3017 }
3018
summary_toggle_view(SummaryView * summaryview)3019 void summary_toggle_view(SummaryView *summaryview)
3020 {
3021 if (!messageview_is_visible(summaryview->messageview) &&
3022 summaryview->selected) {
3023 summary_display_msg_selected(summaryview, FALSE, FALSE);
3024 summary_mark_displayed_read(summaryview, NULL);
3025 } else
3026 main_window_toggle_message_view(summaryview->mainwin);
3027 }
3028
summary_update_selected_rows(SummaryView * summaryview)3029 void summary_update_selected_rows(SummaryView *summaryview)
3030 {
3031 GList *rows, *cur;
3032 GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
3033 GtkTreeIter iter;
3034 GtkTreePath *path;
3035
3036 rows = summary_get_selected_rows(summaryview);
3037 for (cur = rows; cur != NULL; cur = cur->next) {
3038 path = (GtkTreePath *)cur->data;
3039 gtk_tree_model_get_iter(model, &iter, path);
3040 summary_set_row(summaryview, &iter, NULL);
3041 }
3042 }
3043
summary_update_by_msgnum(SummaryView * summaryview,guint msgnum)3044 void summary_update_by_msgnum(SummaryView *summaryview, guint msgnum)
3045 {
3046 GtkTreeIter iter;
3047
3048 if (summary_find_msg_by_msgnum(summaryview, msgnum, &iter))
3049 summary_set_row(summaryview, &iter, NULL);
3050 }
3051
summary_mark_row(SummaryView * summaryview,GtkTreeIter * iter)3052 static void summary_mark_row(SummaryView *summaryview, GtkTreeIter *iter)
3053 {
3054 MsgInfo *msginfo = NULL;
3055
3056 GET_MSG_INFO(msginfo, iter);
3057
3058 msginfo->to_folder = NULL;
3059 if (MSG_IS_DELETED(msginfo->flags)) {
3060 summaryview->deleted--;
3061 MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_DELETED);
3062 }
3063 if (MSG_IS_MOVE(msginfo->flags))
3064 summaryview->moved--;
3065 if (MSG_IS_COPY(msginfo->flags))
3066 summaryview->copied--;
3067 MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_MOVE | MSG_COPY);
3068 MSG_SET_PERM_FLAGS(msginfo->flags, MSG_MARKED);
3069 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_FLAG_CHANGED);
3070 summaryview->folder_item->mark_dirty = TRUE;
3071 summary_set_row(summaryview, iter, msginfo);
3072
3073 debug_print(_("Message %d is marked\n"), msginfo->msgnum);
3074 }
3075
summary_mark(SummaryView * summaryview)3076 void summary_mark(SummaryView *summaryview)
3077 {
3078 GList *rows, *cur;
3079 GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
3080 GtkTreeIter iter;
3081 FolderSortKey sort_key = SORT_BY_NONE;
3082 FolderSortType sort_type = SORT_ASCENDING;
3083
3084 if (FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP &&
3085 summary_is_read_locked(summaryview))
3086 return;
3087
3088 summary_lock(summaryview);
3089 SORT_BLOCK(SORT_BY_MARK);
3090
3091 rows = summary_get_selected_rows(summaryview);
3092 for (cur = rows; cur != NULL; cur = cur->next) {
3093 GtkTreePath *path = (GtkTreePath *)cur->data;
3094 gtk_tree_model_get_iter(model, &iter, path);
3095 summary_mark_row(summaryview, &iter);
3096 }
3097
3098 if (FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP) {
3099 GSList *msglist;
3100
3101 msglist = summary_get_selected_msg_list(summaryview);
3102 imap_msg_list_set_perm_flags(msglist, MSG_MARKED);
3103 g_slist_free(msglist);
3104 }
3105
3106 SORT_UNBLOCK(SORT_BY_MARK);
3107 summary_unlock(summaryview);
3108
3109 summary_status_show(summaryview);
3110 }
3111
summary_mark_row_as_read(SummaryView * summaryview,GtkTreeIter * iter)3112 static void summary_mark_row_as_read(SummaryView *summaryview,
3113 GtkTreeIter *iter)
3114 {
3115 MsgInfo *msginfo = NULL;
3116
3117 GET_MSG_INFO(msginfo, iter);
3118
3119 if (MSG_IS_NEW(msginfo->flags)) {
3120 if (summaryview->folder_item->new > 0)
3121 summaryview->folder_item->new--;
3122 if (summaryview->on_filter && summaryview->flt_new > 0)
3123 summaryview->flt_new--;
3124 inc_block_notify(TRUE);
3125 }
3126 if (MSG_IS_UNREAD(msginfo->flags)) {
3127 if (summaryview->folder_item->unread > 0)
3128 summaryview->folder_item->unread--;
3129 if (summaryview->on_filter && summaryview->flt_unread > 0)
3130 summaryview->flt_unread--;
3131 }
3132
3133 if (summaryview->folder_item->stype == F_VIRTUAL) {
3134 if (MSG_IS_NEW(msginfo->flags) && msginfo->folder->new > 0)
3135 msginfo->folder->new--;
3136 if (MSG_IS_UNREAD(msginfo->flags) &&
3137 msginfo->folder->unread > 0)
3138 msginfo->folder->unread--;
3139 folderview_update_item(msginfo->folder, FALSE);
3140 }
3141
3142 if (MSG_IS_NEW(msginfo->flags) || MSG_IS_UNREAD(msginfo->flags)) {
3143 MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_NEW | MSG_UNREAD);
3144 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_FLAG_CHANGED);
3145 summaryview->folder_item->mark_dirty = TRUE;
3146 summary_set_row(summaryview, iter, msginfo);
3147 debug_print(_("Message %d is marked as being read\n"),
3148 msginfo->msgnum);
3149 }
3150 }
3151
summary_mark_as_read(SummaryView * summaryview)3152 void summary_mark_as_read(SummaryView *summaryview)
3153 {
3154 GList *rows, *cur;
3155 GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
3156 GtkTreeIter iter;
3157 FolderSortKey sort_key = SORT_BY_NONE;
3158 FolderSortType sort_type = SORT_ASCENDING;
3159
3160 if (FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP &&
3161 summary_is_read_locked(summaryview))
3162 return;
3163
3164 summary_lock(summaryview);
3165 SORT_BLOCK(SORT_BY_UNREAD);
3166
3167 rows = summary_get_selected_rows(summaryview);
3168
3169 for (cur = rows; cur != NULL; cur = cur->next) {
3170 GtkTreePath *path = (GtkTreePath *)cur->data;
3171
3172 gtk_tree_model_get_iter(model, &iter, path);
3173 summary_mark_row_as_read(summaryview, &iter);
3174 }
3175
3176 if (FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP) {
3177 GSList *msglist;
3178
3179 msglist = summary_get_selected_msg_list(summaryview);
3180 imap_msg_list_unset_perm_flags(msglist, MSG_NEW | MSG_UNREAD);
3181 g_slist_free(msglist);
3182 }
3183
3184 SORT_UNBLOCK(SORT_BY_UNREAD);
3185 summary_unlock(summaryview);
3186
3187 trayicon_set_tooltip(NULL);
3188 trayicon_set_notify(FALSE);
3189 summary_status_show(summaryview);
3190 }
3191
prepend_thread_rows_func(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer data)3192 static gboolean prepend_thread_rows_func(GtkTreeModel *model, GtkTreePath *path,
3193 GtkTreeIter *iter, gpointer data)
3194 {
3195 GSList **thr_rows = (GSList **)data;
3196 GtkTreePath *top_path;
3197
3198 top_path = gtk_tree_path_copy(path);
3199 *thr_rows = g_slist_prepend(*thr_rows, top_path);
3200 return FALSE;
3201 }
3202
summary_mark_thread_as_read(SummaryView * summaryview)3203 void summary_mark_thread_as_read(SummaryView *summaryview)
3204 {
3205 GList *rows, *cur;
3206 GSList *thr_rows = NULL, *top_rows = NULL, *s_cur;
3207 GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
3208 GtkTreeView *treeview = GTK_TREE_VIEW(summaryview->treeview);
3209 GtkTreeIter iter, parent;
3210 GtkTreePath *path, *top_path;
3211 GHashTable *row_table;
3212 MsgInfo *msginfo;
3213 GSList *msglist = NULL;
3214 FolderSortKey sort_key = SORT_BY_NONE;
3215 FolderSortType sort_type = SORT_ASCENDING;
3216
3217 if (FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP &&
3218 summary_is_read_locked(summaryview))
3219 return;
3220
3221 summary_lock(summaryview);
3222 SORT_BLOCK(SORT_BY_UNREAD);
3223
3224 rows = summary_get_selected_rows(summaryview);
3225
3226 row_table = g_hash_table_new(NULL, NULL);
3227
3228 for (cur = rows; cur != NULL; cur = cur->next) {
3229 path = (GtkTreePath *)cur->data;
3230 gtk_tree_model_get_iter(model, &iter, path);
3231 while (gtk_tree_model_iter_parent(model, &parent, &iter))
3232 iter = parent;
3233 gtk_tree_model_get(model, &iter, S_COL_MSG_INFO, &msginfo, -1);
3234 if (!g_hash_table_lookup(row_table, msginfo)) {
3235 g_hash_table_insert(row_table, msginfo,
3236 GINT_TO_POINTER(1));
3237 top_path = gtk_tree_model_get_path(model, &iter);
3238 top_rows = g_slist_prepend(top_rows, top_path);
3239 gtkut_tree_model_foreach(model, &iter,
3240 prepend_thread_rows_func,
3241 &thr_rows);
3242 }
3243 }
3244 top_rows = g_slist_reverse(top_rows);
3245 thr_rows = g_slist_reverse(thr_rows);
3246
3247 g_hash_table_destroy(row_table);
3248
3249 for (s_cur = thr_rows; s_cur != NULL; s_cur = s_cur->next) {
3250 path = (GtkTreePath *)s_cur->data;
3251 gtk_tree_model_get_iter(model, &iter, path);
3252 summary_mark_row_as_read(summaryview, &iter);
3253 gtk_tree_model_get(model, &iter, S_COL_MSG_INFO, &msginfo, -1);
3254 msglist = g_slist_prepend(msglist, msginfo);
3255 }
3256
3257 if (prefs_common.bold_unread) {
3258 for (s_cur = top_rows; s_cur != NULL; s_cur = s_cur->next) {
3259 path = (GtkTreePath *)s_cur->data;
3260 gtk_tree_model_get_iter(model, &iter, path);
3261 if (gtk_tree_model_iter_has_child(model, &iter) &&
3262 !gtk_tree_view_row_expanded(treeview, path) &&
3263 !summary_have_unread_children(summaryview, &iter)) {
3264 gtk_tree_store_set(GTK_TREE_STORE(model), &iter,
3265 S_COL_BOLD,
3266 PANGO_WEIGHT_NORMAL, -1);
3267 }
3268 }
3269 }
3270 msglist = g_slist_reverse(msglist);
3271
3272 if (FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP) {
3273 imap_msg_list_unset_perm_flags(msglist, MSG_NEW | MSG_UNREAD);
3274 }
3275
3276 g_slist_free(msglist);
3277 g_slist_foreach(top_rows, (GFunc)gtk_tree_path_free, NULL);
3278 g_slist_free(top_rows);
3279 g_slist_foreach(thr_rows, (GFunc)gtk_tree_path_free, NULL);
3280 g_slist_free(thr_rows);
3281
3282 SORT_UNBLOCK(SORT_BY_UNREAD);
3283 summary_unlock(summaryview);
3284
3285 trayicon_set_tooltip(NULL);
3286 trayicon_set_notify(FALSE);
3287 summary_status_show(summaryview);
3288 }
3289
summary_mark_all_read(SummaryView * summaryview)3290 void summary_mark_all_read(SummaryView *summaryview)
3291 {
3292 GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
3293 GtkTreeIter iter;
3294 gboolean valid;
3295 FolderSortKey sort_key = SORT_BY_NONE;
3296 FolderSortType sort_type = SORT_ASCENDING;
3297
3298 if (FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP &&
3299 summary_is_read_locked(summaryview))
3300 return;
3301
3302 summary_lock(summaryview);
3303 SORT_BLOCK(SORT_BY_UNREAD);
3304
3305 if (FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP) {
3306 GSList *msglist;
3307 msglist = summary_get_flagged_msg_list(summaryview,
3308 MSG_NEW | MSG_UNREAD);
3309 imap_msg_list_unset_perm_flags(msglist, MSG_NEW | MSG_UNREAD);
3310 g_slist_free(msglist);
3311 }
3312
3313 valid = gtk_tree_model_get_iter_first(model, &iter);
3314 while (valid) {
3315 summary_mark_row_as_read(summaryview, &iter);
3316 if (prefs_common.bold_unread) {
3317 if (gtk_tree_model_iter_has_child(model, &iter)) {
3318 GtkTreePath *path;
3319
3320 path = gtk_tree_model_get_path(model, &iter);
3321 if (!gtk_tree_view_row_expanded
3322 (GTK_TREE_VIEW(summaryview->treeview),
3323 path))
3324 gtk_tree_store_set
3325 (GTK_TREE_STORE(model), &iter,
3326 S_COL_BOLD,
3327 PANGO_WEIGHT_NORMAL, -1);
3328 gtk_tree_path_free(path);
3329 }
3330 }
3331 valid = gtkut_tree_model_next(model, &iter);
3332 }
3333
3334 SORT_UNBLOCK(SORT_BY_UNREAD);
3335 summary_unlock(summaryview);
3336
3337 trayicon_set_tooltip(NULL);
3338 trayicon_set_notify(FALSE);
3339 summary_status_show(summaryview);
3340 }
3341
summary_mark_row_as_unread(SummaryView * summaryview,GtkTreeIter * iter)3342 static void summary_mark_row_as_unread(SummaryView *summaryview,
3343 GtkTreeIter *iter)
3344 {
3345 MsgInfo *msginfo = NULL;
3346
3347 GET_MSG_INFO(msginfo, iter);
3348
3349 if (MSG_IS_DELETED(msginfo->flags)) {
3350 msginfo->to_folder = NULL;
3351 MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_DELETED);
3352 summaryview->deleted--;
3353 summaryview->folder_item->mark_dirty = TRUE;
3354 }
3355 MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_REPLIED | MSG_FORWARDED);
3356 if (!MSG_IS_UNREAD(msginfo->flags)) {
3357 MSG_SET_PERM_FLAGS(msginfo->flags, MSG_UNREAD);
3358 summaryview->folder_item->unread++;
3359 if (summaryview->on_filter)
3360 summaryview->flt_unread++;
3361 summaryview->folder_item->mark_dirty = TRUE;
3362 if (summaryview->folder_item->stype == F_VIRTUAL) {
3363 msginfo->folder->unread++;
3364 folderview_update_item(msginfo->folder, FALSE);
3365 }
3366 debug_print(_("Message %d is marked as unread\n"),
3367 msginfo->msgnum);
3368 }
3369 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_FLAG_CHANGED);
3370 summary_set_row(summaryview, iter, msginfo);
3371 }
3372
summary_mark_as_unread(SummaryView * summaryview)3373 void summary_mark_as_unread(SummaryView *summaryview)
3374 {
3375 GList *rows, *cur;
3376 GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
3377 GtkTreeIter iter;
3378 FolderSortKey sort_key = SORT_BY_NONE;
3379 FolderSortType sort_type = SORT_ASCENDING;
3380
3381 if (FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP &&
3382 summary_is_read_locked(summaryview))
3383 return;
3384
3385 summary_lock(summaryview);
3386 SORT_BLOCK(SORT_BY_UNREAD);
3387
3388 rows = summary_get_selected_rows(summaryview);
3389 for (cur = rows; cur != NULL; cur = cur->next) {
3390 GtkTreePath *path = (GtkTreePath *)cur->data;
3391
3392 gtk_tree_model_get_iter(model, &iter, path);
3393 summary_mark_row_as_unread(summaryview, &iter);
3394 }
3395
3396 if (FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP) {
3397 GSList *msglist;
3398 msglist = summary_get_selected_msg_list(summaryview);
3399 imap_msg_list_unset_perm_flags(msglist, MSG_REPLIED);
3400 imap_msg_list_set_perm_flags(msglist, MSG_UNREAD);
3401 g_slist_free(msglist);
3402 }
3403
3404 SORT_UNBLOCK(SORT_BY_UNREAD);
3405 summary_unlock(summaryview);
3406
3407 summary_status_show(summaryview);
3408 }
3409
summary_delete_row(SummaryView * summaryview,GtkTreeIter * iter)3410 static void summary_delete_row(SummaryView *summaryview, GtkTreeIter *iter)
3411 {
3412 MsgInfo *msginfo = NULL;
3413
3414 GET_MSG_INFO(msginfo, iter);
3415
3416 if (MSG_IS_DELETED(msginfo->flags)) return;
3417
3418 msginfo->to_folder = NULL;
3419 if (MSG_IS_MOVE(msginfo->flags)) {
3420 summaryview->moved--;
3421 if (summaryview->on_filter)
3422 summaryview->flt_moved--;
3423 }
3424 if (MSG_IS_COPY(msginfo->flags)) {
3425 summaryview->copied--;
3426 if (summaryview->on_filter)
3427 summaryview->flt_copied--;
3428 }
3429 MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_MOVE | MSG_COPY);
3430 MSG_SET_PERM_FLAGS(msginfo->flags, MSG_DELETED);
3431 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_FLAG_CHANGED);
3432 summaryview->deleted++;
3433 if (summaryview->on_filter)
3434 summaryview->flt_deleted++;
3435 summaryview->folder_item->mark_dirty = TRUE;
3436
3437 if (!prefs_common.immediate_exec && summaryview->tmp_flag == 0)
3438 summary_set_row(summaryview, iter, msginfo);
3439
3440 debug_print(_("Message %s/%d is set to delete\n"),
3441 msginfo->folder->path, msginfo->msgnum);
3442 }
3443
summary_delete_foreach_func(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer data)3444 static gboolean summary_delete_foreach_func(GtkTreeModel *model,
3445 GtkTreePath *path,
3446 GtkTreeIter *iter, gpointer data)
3447 {
3448 summary_delete_row((SummaryView *)data, iter);
3449 return FALSE;
3450 }
3451
summary_delete(SummaryView * summaryview)3452 void summary_delete(SummaryView *summaryview)
3453 {
3454 FolderItem *item = summaryview->folder_item;
3455 GList *rows, *cur;
3456 GtkTreeIter last_sel, next;
3457 GtkTreeView *treeview = GTK_TREE_VIEW(summaryview->treeview);
3458 GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
3459 gboolean is_trash;
3460
3461 if (!item || FOLDER_TYPE(item->folder) == F_NEWS) return;
3462
3463 if (summary_is_locked(summaryview)) return;
3464
3465 /* if current folder is trash, ask for confirmation */
3466 is_trash = folder_item_is_trash(item);
3467 if (is_trash) {
3468 AlertValue aval;
3469
3470 aval = alertpanel(_("Delete message(s)"),
3471 _("Do you really want to delete message(s) from the trash?"),
3472 GTK_STOCK_YES, GTK_STOCK_NO, NULL);
3473 if (aval != G_ALERTDEFAULT) return;
3474 }
3475
3476 rows = summary_get_selected_rows(summaryview);
3477 if (!rows)
3478 return;
3479
3480 summaryview->tmp_flag = is_trash ? 1 : 0;
3481
3482 /* next code sets current row focus right. We need to find a row
3483 * that is not deleted. */
3484 for (cur = rows; cur != NULL; cur = cur->next) {
3485 GtkTreePath *path = (GtkTreePath *)cur->data;
3486
3487 gtk_tree_model_get_iter(model, &last_sel, path);
3488 if (gtk_tree_model_iter_has_child(model, &last_sel) &&
3489 !gtk_tree_view_row_expanded(treeview, path)) {
3490 gtkut_tree_model_foreach
3491 (model, &last_sel, summary_delete_foreach_func,
3492 summaryview);
3493 } else
3494 summary_delete_row(summaryview, &last_sel);
3495 }
3496
3497 summaryview->tmp_flag = 0;
3498
3499 if (prefs_common.immediate_exec || is_trash) {
3500 summary_execute(summaryview);
3501 } else {
3502 if (summary_find_nearest_msg(summaryview, &next, &last_sel)) {
3503 summary_select_row
3504 (summaryview, &next,
3505 messageview_is_visible
3506 (summaryview->messageview),
3507 FALSE);
3508 }
3509 summary_status_show(summaryview);
3510 }
3511 }
3512
summary_delete_duplicated_func(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer data)3513 static gboolean summary_delete_duplicated_func(GtkTreeModel *model,
3514 GtkTreePath *path,
3515 GtkTreeIter *iter,
3516 gpointer data)
3517 {
3518 SummaryView *summaryview = (SummaryView *)data;
3519 MsgInfo *msginfo;
3520 GtkTreeIter *found;
3521 GtkTreePath *found_path;
3522
3523 gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
3524
3525 if (!msginfo || !msginfo->msgid || !*msginfo->msgid)
3526 return FALSE;
3527
3528 found = g_hash_table_lookup(summaryview->msgid_table, msginfo->msgid);
3529
3530 if (found) {
3531 found_path = gtk_tree_model_get_path(model, found);
3532 if (gtk_tree_path_compare(path, found_path) != 0)
3533 summary_delete_row(summaryview, iter);
3534 gtk_tree_path_free(found_path);
3535 }
3536
3537 return FALSE;
3538 }
3539
summary_delete_duplicated(SummaryView * summaryview)3540 void summary_delete_duplicated(SummaryView *summaryview)
3541 {
3542 if (!summaryview->folder_item ||
3543 FOLDER_TYPE(summaryview->folder_item->folder) == F_NEWS) return;
3544 if (folder_item_is_trash(summaryview->folder_item)) return;
3545
3546 if (summary_is_locked(summaryview)) return;
3547
3548 main_window_cursor_wait(summaryview->mainwin);
3549 debug_print("Deleting duplicated messages...");
3550 STATUSBAR_PUSH(summaryview->mainwin,
3551 _("Deleting duplicated messages..."));
3552
3553 summary_msgid_table_create(summaryview);
3554
3555 gtk_tree_model_foreach(GTK_TREE_MODEL(summaryview->store),
3556 summary_delete_duplicated_func, summaryview);
3557
3558 summary_msgid_table_destroy(summaryview);
3559
3560 if (prefs_common.immediate_exec)
3561 summary_execute(summaryview);
3562 else
3563 summary_status_show(summaryview);
3564
3565 debug_print("done.\n");
3566 STATUSBAR_POP(summaryview->mainwin);
3567 main_window_cursor_normal(summaryview->mainwin);
3568 }
3569
summary_unmark_row(SummaryView * summaryview,GtkTreeIter * iter)3570 static void summary_unmark_row(SummaryView *summaryview, GtkTreeIter *iter)
3571 {
3572 MsgInfo *msginfo = NULL;
3573
3574 GET_MSG_INFO(msginfo, iter);
3575
3576 msginfo->to_folder = NULL;
3577 if (MSG_IS_DELETED(msginfo->flags)) {
3578 summaryview->deleted--;
3579 if (summaryview->on_filter)
3580 summaryview->flt_deleted--;
3581 }
3582 if (MSG_IS_MOVE(msginfo->flags)) {
3583 summaryview->moved--;
3584 if (summaryview->on_filter)
3585 summaryview->flt_moved--;
3586 }
3587 if (MSG_IS_COPY(msginfo->flags)) {
3588 summaryview->copied--;
3589 if (summaryview->on_filter)
3590 summaryview->flt_copied--;
3591 }
3592 MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_MARKED | MSG_DELETED);
3593 MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_MOVE | MSG_COPY);
3594 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_FLAG_CHANGED);
3595 summaryview->folder_item->mark_dirty = TRUE;
3596 summary_set_row(summaryview, iter, msginfo);
3597
3598 debug_print(_("Message %s/%d is unmarked\n"),
3599 msginfo->folder->path, msginfo->msgnum);
3600 }
3601
summary_unmark(SummaryView * summaryview)3602 void summary_unmark(SummaryView *summaryview)
3603 {
3604 GList *rows, *cur;
3605 GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
3606 GtkTreeIter iter;
3607 FolderSortKey sort_key = SORT_BY_NONE;
3608 FolderSortType sort_type = SORT_ASCENDING;
3609
3610 if (FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP &&
3611 summary_is_read_locked(summaryview))
3612 return;
3613
3614 summary_lock(summaryview);
3615 SORT_BLOCK(SORT_BY_MARK);
3616
3617 rows = summary_get_selected_rows(summaryview);
3618 for (cur = rows; cur != NULL; cur = cur->next) {
3619 GtkTreePath *path = (GtkTreePath *)cur->data;
3620 gtk_tree_model_get_iter(model, &iter, path);
3621 summary_unmark_row(summaryview, &iter);
3622 }
3623
3624 if (FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP) {
3625 GSList *msglist;
3626
3627 msglist = summary_get_selected_msg_list(summaryview);
3628 imap_msg_list_unset_perm_flags(msglist, MSG_MARKED);
3629 g_slist_free(msglist);
3630 }
3631
3632 SORT_UNBLOCK(SORT_BY_MARK);
3633 summary_unlock(summaryview);
3634
3635 summary_status_show(summaryview);
3636 }
3637
summary_move_row_to(SummaryView * summaryview,GtkTreeIter * iter,FolderItem * to_folder)3638 static void summary_move_row_to(SummaryView *summaryview, GtkTreeIter *iter,
3639 FolderItem *to_folder)
3640 {
3641 MsgInfo *msginfo;
3642
3643 g_return_if_fail(to_folder != NULL);
3644 if (to_folder->stype == F_QUEUE || to_folder->stype == F_VIRTUAL)
3645 return;
3646
3647 GET_MSG_INFO(msginfo, iter);
3648
3649 msginfo->to_folder = to_folder;
3650 if (MSG_IS_DELETED(msginfo->flags)) {
3651 summaryview->deleted--;
3652 if (summaryview->on_filter)
3653 summaryview->flt_deleted--;
3654 MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_DELETED);
3655 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_FLAG_CHANGED);
3656 }
3657 MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_COPY);
3658 if (!MSG_IS_MOVE(msginfo->flags)) {
3659 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_MOVE);
3660 summaryview->moved++;
3661 if (summaryview->on_filter)
3662 summaryview->flt_moved++;
3663 }
3664 summaryview->folder_item->mark_dirty = TRUE;
3665 if (!prefs_common.immediate_exec)
3666 summary_set_row(summaryview, iter, msginfo);
3667
3668 debug_print(_("Message %d is set to move to %s\n"),
3669 msginfo->msgnum, to_folder->path);
3670 }
3671
summary_move_foreach_func(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer data)3672 static gboolean summary_move_foreach_func(GtkTreeModel *model,
3673 GtkTreePath *path, GtkTreeIter *iter,
3674 gpointer data)
3675 {
3676 SummaryView *summaryview = (SummaryView *)data;
3677
3678 summary_move_row_to(summaryview, iter, summaryview->to_folder);
3679 return FALSE;
3680 }
3681
summary_move_selected_to(SummaryView * summaryview,FolderItem * to_folder)3682 void summary_move_selected_to(SummaryView *summaryview, FolderItem *to_folder)
3683 {
3684 GList *rows, *cur;
3685 GtkTreeView *treeview = GTK_TREE_VIEW(summaryview->treeview);
3686 GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
3687 GtkTreeIter iter;
3688
3689 if (!to_folder) return;
3690 if (!summaryview->folder_item ||
3691 FOLDER_TYPE(summaryview->folder_item->folder) == F_NEWS)
3692 return;
3693 if (summaryview->folder_item->stype == F_QUEUE ||
3694 to_folder->stype == F_QUEUE || to_folder->stype == F_VIRTUAL)
3695 return;
3696
3697 if (summary_is_locked(summaryview)) return;
3698
3699 if (summaryview->folder_item == to_folder) {
3700 alertpanel_warning(_("Destination is same as current folder."));
3701 return;
3702 }
3703
3704 rows = summary_get_selected_rows(summaryview);
3705 for (cur = rows; cur != NULL; cur = cur->next) {
3706 GtkTreePath *path = (GtkTreePath *)cur->data;
3707
3708 gtk_tree_model_get_iter(model, &iter, path);
3709 if (gtk_tree_model_iter_has_child(model, &iter) &&
3710 !gtk_tree_view_row_expanded(treeview, path)) {
3711 summaryview->to_folder = to_folder;
3712 gtkut_tree_model_foreach
3713 (model, &iter, summary_move_foreach_func,
3714 summaryview);
3715 summaryview->to_folder = NULL;
3716 } else
3717 summary_move_row_to(summaryview, &iter, to_folder);
3718 }
3719
3720 if (prefs_common.immediate_exec)
3721 summary_execute(summaryview);
3722 else {
3723 summary_step(summaryview, GTK_SCROLL_STEP_FORWARD);
3724 summary_status_show(summaryview);
3725 }
3726 }
3727
summary_move_to(SummaryView * summaryview)3728 void summary_move_to(SummaryView *summaryview)
3729 {
3730 FolderItem *to_folder;
3731
3732 if (!summaryview->folder_item ||
3733 FOLDER_TYPE(summaryview->folder_item->folder) == F_NEWS) return;
3734
3735 to_folder = foldersel_folder_sel_full(summaryview->folder_item->folder,
3736 FOLDER_SEL_MOVE, NULL,
3737 _("Select folder to move"));
3738 summary_move_selected_to(summaryview, to_folder);
3739 }
3740
summary_copy_row_to(SummaryView * summaryview,GtkTreeIter * iter,FolderItem * to_folder)3741 static void summary_copy_row_to(SummaryView *summaryview, GtkTreeIter *iter,
3742 FolderItem *to_folder)
3743 {
3744 MsgInfo *msginfo;
3745
3746 g_return_if_fail(to_folder != NULL);
3747 if (to_folder->stype == F_QUEUE || to_folder->stype == F_VIRTUAL)
3748 return;
3749
3750 GET_MSG_INFO(msginfo, iter);
3751
3752 msginfo->to_folder = to_folder;
3753 if (MSG_IS_DELETED(msginfo->flags)) {
3754 summaryview->deleted--;
3755 if (summaryview->on_filter)
3756 summaryview->flt_deleted--;
3757 MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_DELETED);
3758 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_FLAG_CHANGED);
3759 }
3760 MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_MOVE);
3761 if (!MSG_IS_COPY(msginfo->flags)) {
3762 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_COPY);
3763 summaryview->copied++;
3764 if (summaryview->on_filter)
3765 summaryview->flt_copied++;
3766 }
3767 summaryview->folder_item->mark_dirty = TRUE;
3768 if (!prefs_common.immediate_exec)
3769 summary_set_row(summaryview, iter, msginfo);
3770
3771 debug_print(_("Message %d is set to copy to %s\n"),
3772 msginfo->msgnum, to_folder->path);
3773 }
3774
summary_copy_foreach_func(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer data)3775 static gboolean summary_copy_foreach_func(GtkTreeModel *model,
3776 GtkTreePath *path, GtkTreeIter *iter,
3777 gpointer data)
3778 {
3779 SummaryView *summaryview = (SummaryView *)data;
3780
3781 summary_copy_row_to(summaryview, iter, summaryview->to_folder);
3782 return FALSE;
3783 }
3784
summary_copy_selected_to(SummaryView * summaryview,FolderItem * to_folder)3785 void summary_copy_selected_to(SummaryView *summaryview, FolderItem *to_folder)
3786 {
3787 GList *rows, *cur;
3788 GtkTreeView *treeview = GTK_TREE_VIEW(summaryview->treeview);
3789 GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
3790 GtkTreeIter iter;
3791
3792 if (!to_folder) return;
3793 if (!summaryview->folder_item) return;
3794 if (summaryview->folder_item->stype == F_QUEUE ||
3795 to_folder->stype == F_QUEUE || to_folder->stype == F_VIRTUAL)
3796 return;
3797
3798 if (summary_is_locked(summaryview)) return;
3799
3800 if (summaryview->folder_item == to_folder) {
3801 alertpanel_warning
3802 (_("Destination for copy is same as current folder."));
3803 return;
3804 }
3805
3806 rows = summary_get_selected_rows(summaryview);
3807 for (cur = rows; cur != NULL; cur = cur->next) {
3808 GtkTreePath *path = (GtkTreePath *)cur->data;
3809
3810 gtk_tree_model_get_iter(model, &iter, path);
3811 if (gtk_tree_model_iter_has_child(model, &iter) &&
3812 !gtk_tree_view_row_expanded(treeview, path)) {
3813 summaryview->to_folder = to_folder;
3814 gtkut_tree_model_foreach
3815 (model, &iter, summary_copy_foreach_func,
3816 summaryview);
3817 summaryview->to_folder = NULL;
3818 } else
3819 summary_copy_row_to(summaryview, &iter, to_folder);
3820 }
3821
3822 if (prefs_common.immediate_exec)
3823 summary_execute(summaryview);
3824 else {
3825 summary_step(summaryview, GTK_SCROLL_STEP_FORWARD);
3826 summary_status_show(summaryview);
3827 }
3828 }
3829
summary_copy_to(SummaryView * summaryview)3830 void summary_copy_to(SummaryView *summaryview)
3831 {
3832 FolderItem *to_folder;
3833
3834 if (!summaryview->folder_item) return;
3835
3836 to_folder = foldersel_folder_sel_full(summaryview->folder_item->folder,
3837 FOLDER_SEL_COPY, NULL,
3838 _("Select folder to copy"));
3839 summary_copy_selected_to(summaryview, to_folder);
3840 }
3841
summary_add_address(SummaryView * summaryview)3842 void summary_add_address(SummaryView *summaryview)
3843 {
3844 GtkTreeIter iter;
3845 MsgInfo *msginfo = NULL;
3846 gchar from[BUFFSIZE];
3847
3848 if (!summaryview->selected) return;
3849
3850 if (!gtkut_tree_row_reference_get_iter
3851 (GTK_TREE_MODEL(summaryview->store),
3852 summaryview->selected, &iter))
3853 return;
3854
3855 GET_MSG_INFO(msginfo, &iter);
3856 strncpy2(from, msginfo->from, sizeof(from));
3857 eliminate_address_comment(from);
3858 extract_address(from);
3859 addressbook_add_contact(msginfo->fromname, from, NULL);
3860 }
3861
summary_select_all(SummaryView * summaryview)3862 void summary_select_all(SummaryView *summaryview)
3863 {
3864 gtk_tree_selection_select_all(summaryview->selection);
3865 }
3866
summary_unselect_all(SummaryView * summaryview)3867 void summary_unselect_all(SummaryView *summaryview)
3868 {
3869 gtk_tree_selection_unselect_all(summaryview->selection);
3870 }
3871
summary_select_thread(SummaryView * summaryview)3872 void summary_select_thread(SummaryView *summaryview)
3873 {
3874 GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
3875 GtkTreeIter iter, parent, child, next;
3876 GtkTreePath *start_path, *end_path;
3877 gboolean valid;
3878
3879 valid = gtkut_tree_row_reference_get_iter(model, summaryview->selected,
3880 &iter);
3881 if (!valid)
3882 return;
3883
3884 while (gtk_tree_model_iter_parent(model, &parent, &iter))
3885 iter = parent;
3886
3887 if (!gtk_tree_model_iter_children(model, &child, &iter))
3888 return;
3889
3890 start_path = gtk_tree_model_get_path(model, &iter);
3891
3892 for (;;) {
3893 next = iter = child;
3894 while (gtk_tree_model_iter_next(model, &next))
3895 iter = next;
3896 if (!gtk_tree_model_iter_children(model, &child, &iter))
3897 break;
3898 }
3899
3900 end_path = gtk_tree_model_get_path(model, &iter);
3901
3902 gtk_tree_view_expand_row(GTK_TREE_VIEW(summaryview->treeview),
3903 start_path, TRUE);
3904 gtk_tree_selection_select_range(summaryview->selection,
3905 start_path, end_path);
3906
3907 gtk_tree_path_free(end_path);
3908 gtk_tree_path_free(start_path);
3909 }
3910
summary_save_as(SummaryView * summaryview)3911 void summary_save_as(SummaryView *summaryview)
3912 {
3913 GtkTreeIter iter;
3914 MsgInfo *msginfo = NULL;
3915 gchar *filename;
3916 gchar *src, *dest;
3917 FileselFileType types[4] = {{NULL, NULL}};
3918 gint selected_type = 0;
3919 gint result;
3920 gboolean all_headers;
3921
3922 if (!summaryview->selected) return;
3923 if (!gtkut_tree_row_reference_get_iter
3924 (GTK_TREE_MODEL(summaryview->store),
3925 summaryview->selected, &iter))
3926 return;
3927
3928 GET_MSG_INFO(msginfo, &iter);
3929 if (!msginfo) return;
3930
3931 if (msginfo->subject && *msginfo->subject) {
3932 filename = g_strdup_printf("%s.eml", msginfo->subject);
3933 } else {
3934 filename = g_strdup_printf("%u.eml", msginfo->msgnum);
3935 }
3936 subst_for_filename(filename);
3937
3938 types[0].type = _("Original (EML/RFC 822)");
3939 types[0].ext = "eml";
3940 types[1].type = _("Text");
3941 types[1].ext = "txt";
3942 types[2].type = _("Text (UTF-8)");
3943 types[2].ext = "txt";
3944
3945 dest = filesel_save_as_type(filename, types, prefs_common.save_file_type, &selected_type);
3946
3947 g_free(filename);
3948 if (!dest)
3949 return;
3950
3951 debug_print("summary_save_as: selected_type: %d\n", selected_type);
3952
3953 all_headers = summaryview->messageview->textview->show_all_headers;
3954
3955 if (selected_type == 1) {
3956 result = procmsg_save_message_as_text(msginfo, dest, conv_get_locale_charset_str(), all_headers);
3957 } else if (selected_type == 2) {
3958 result = procmsg_save_message_as_text(msginfo, dest, NULL, all_headers);
3959 } else {
3960 src = procmsg_get_message_file(msginfo);
3961 result = copy_file(src, dest, TRUE);
3962 g_free(src);
3963 }
3964
3965 if (result < 0) {
3966 gchar *utf8_dest;
3967
3968 utf8_dest = conv_filename_to_utf8(dest);
3969 alertpanel_error(_("Can't save the file `%s'."),
3970 g_basename(utf8_dest));
3971 g_free(utf8_dest);
3972 }
3973
3974 g_free(dest);
3975
3976 prefs_common.save_file_type = selected_type;
3977 }
3978
summary_print(SummaryView * summaryview)3979 void summary_print(SummaryView *summaryview)
3980 {
3981 GSList *mlist;
3982 gboolean all_headers;
3983
3984 all_headers = summaryview->messageview->textview->show_all_headers;
3985 mlist = summary_get_selected_msg_list(summaryview);
3986 if (!mlist)
3987 return;
3988 printing_print_messages(mlist, all_headers);
3989 g_slist_free(mlist);
3990 }
3991
summary_execute(SummaryView * summaryview)3992 gboolean summary_execute(SummaryView *summaryview)
3993 {
3994 gint val = 0;
3995
3996 if (!summaryview->folder_item) return FALSE;
3997
3998 if (summary_is_locked(summaryview)) return FALSE;
3999 summary_lock(summaryview);
4000
4001 val |= summary_execute_move(summaryview);
4002 val |= summary_execute_copy(summaryview);
4003 val |= summary_execute_delete(summaryview);
4004
4005 summary_unlock(summaryview);
4006
4007 summary_remove_invalid_messages(summaryview);
4008
4009 statusbar_pop_all();
4010 STATUSBAR_POP(summaryview->mainwin);
4011
4012 if (val != 0) {
4013 alertpanel_error(_("Error occurred while processing messages."));
4014 }
4015
4016 return TRUE;
4017 }
4018
summary_remove_invalid_messages(SummaryView * summaryview)4019 static void summary_remove_invalid_messages(SummaryView *summaryview)
4020 {
4021 GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
4022 MsgInfo *disp_msginfo = NULL, *msginfo;
4023 FolderItem *item = summaryview->folder_item;
4024 GtkTreeIter iter, next;
4025 GtkTreePath *path;
4026 gboolean valid;
4027
4028 /* get currently displayed message */
4029 if (summaryview->displayed) {
4030 GtkTreeIter displayed;
4031
4032 valid = gtkut_tree_row_reference_get_iter
4033 (model, summaryview->displayed, &displayed);
4034 if (valid) {
4035 gtk_tree_model_get(model, &displayed,
4036 S_COL_MSG_INFO, &disp_msginfo, -1);
4037 if (MSG_IS_INVALID(disp_msginfo->flags)) {
4038 valid = FALSE;
4039 disp_msginfo = NULL;
4040 }
4041 }
4042 if (!valid) {
4043 /* g_print("displayed became invalid before removing\n"); */
4044 messageview_clear(summaryview->messageview);
4045 gtk_tree_row_reference_free(summaryview->displayed);
4046 summaryview->displayed = NULL;
4047 }
4048 }
4049
4050 if (item->threaded)
4051 summary_modify_threads(summaryview);
4052
4053 /* update selection */
4054 valid = gtkut_tree_row_reference_get_iter(model, summaryview->selected,
4055 &iter);
4056 if (valid) {
4057 valid = summary_find_nearest_msg(summaryview, &next, &iter);
4058 if (valid) {
4059 gboolean display;
4060
4061 gtk_tree_model_get(model, &next,
4062 S_COL_MSG_INFO, &msginfo, -1);
4063 if (disp_msginfo && disp_msginfo == msginfo) {
4064 /* g_print("replace displayed\n"); */
4065 path = gtk_tree_model_get_path(model, &next);
4066 gtk_tree_row_reference_free
4067 (summaryview->displayed);
4068 summaryview->displayed =
4069 gtk_tree_row_reference_new(model, path);
4070 gtk_tree_path_free(path);
4071 }
4072 display = prefs_common.immediate_exec &&
4073 messageview_is_visible
4074 (summaryview->messageview);
4075 summary_select_row
4076 (summaryview, &next, display, FALSE);
4077 if (display)
4078 summary_mark_displayed_read(summaryview, &next);
4079 }
4080 }
4081 if (!valid)
4082 summary_unselect_all(summaryview);
4083
4084 for (valid = gtk_tree_model_get_iter_first(model, &iter);
4085 valid == TRUE; iter = next) {
4086 next = iter;
4087 valid = gtkut_tree_model_next(model, &next);
4088
4089 gtk_tree_model_get(model, &iter, S_COL_MSG_INFO, &msginfo, -1);
4090 if (!MSG_IS_INVALID(msginfo->flags))
4091 continue;
4092
4093 if (gtk_tree_model_iter_has_child(model, &iter)) {
4094 g_warning("summary_remove_invalid_messages(): "
4095 "tried to remove row which has child\n");
4096 continue;
4097 }
4098
4099 gtk_tree_store_remove(GTK_TREE_STORE(model), &iter);
4100 summaryview->all_mlist = g_slist_remove(summaryview->all_mlist,
4101 msginfo);
4102 if (summaryview->flt_mlist)
4103 summaryview->flt_mlist =
4104 g_slist_remove(summaryview->flt_mlist, msginfo);
4105 procmsg_msginfo_free(msginfo);
4106
4107 item->cache_dirty = TRUE;
4108 }
4109
4110 /* selection list becomes invalid if modified */
4111 if (item->cache_dirty)
4112 summary_selection_list_free(summaryview);
4113
4114 if (summaryview->displayed &&
4115 !gtk_tree_row_reference_valid(summaryview->displayed)) {
4116 /* g_print("displayed became invalid after removing. searching disp_msginfo...\n"); */
4117 if (disp_msginfo &&
4118 gtkut_tree_model_find_by_column_data
4119 (model, &iter, NULL, S_COL_MSG_INFO, disp_msginfo)) {
4120 /* g_print("replace displayed\n"); */
4121 path = gtk_tree_model_get_path(model, &iter);
4122 gtk_tree_row_reference_free(summaryview->displayed);
4123 summaryview->displayed =
4124 gtk_tree_row_reference_new(model, path);
4125 gtk_tree_path_free(path);
4126 } else {
4127 messageview_clear(summaryview->messageview);
4128 gtk_tree_row_reference_free(summaryview->displayed);
4129 summaryview->displayed = NULL;
4130 }
4131 }
4132
4133 if (gtk_tree_model_get_iter_first(model, &iter))
4134 gtk_widget_grab_focus(summaryview->treeview);
4135 else {
4136 menu_set_insensitive_all
4137 (GTK_MENU_SHELL(summaryview->popupmenu));
4138 gtk_widget_grab_focus(summaryview->folderview->treeview);
4139 }
4140
4141 summary_write_cache(summaryview);
4142
4143 summary_update_status(summaryview);
4144 summary_status_show(summaryview);
4145 }
4146
summary_execute_move_func(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer data)4147 static gboolean summary_execute_move_func(GtkTreeModel *model,
4148 GtkTreePath *path, GtkTreeIter *iter,
4149 gpointer data)
4150 {
4151 SummaryView *summaryview = data;
4152 MsgInfo *msginfo;
4153
4154 gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
4155
4156 if (msginfo && MSG_IS_MOVE(msginfo->flags) && msginfo->to_folder) {
4157 g_hash_table_insert(summaryview->folder_table,
4158 msginfo->to_folder, GINT_TO_POINTER(1));
4159
4160 summaryview->tmp_mlist =
4161 g_slist_prepend(summaryview->tmp_mlist, msginfo);
4162
4163 MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_MOVE);
4164 summary_set_row(summaryview, iter, msginfo);
4165 }
4166
4167 return FALSE;
4168 }
4169
summary_execute_move(SummaryView * summaryview)4170 static gint summary_execute_move(SummaryView *summaryview)
4171 {
4172 gint val = 0;
4173
4174 summaryview->folder_table = g_hash_table_new(NULL, NULL);
4175
4176 /* search moving messages and execute */
4177 gtk_tree_model_foreach(GTK_TREE_MODEL(summaryview->store),
4178 summary_execute_move_func, summaryview);
4179
4180 if (summaryview->tmp_mlist) {
4181 summaryview->tmp_mlist =
4182 g_slist_reverse(summaryview->tmp_mlist);
4183 val = procmsg_move_messages(summaryview->tmp_mlist);
4184
4185 folderview_update_item_foreach(summaryview->folder_table,
4186 FALSE);
4187
4188 g_slist_free(summaryview->tmp_mlist);
4189 summaryview->tmp_mlist = NULL;
4190 }
4191
4192 g_hash_table_destroy(summaryview->folder_table);
4193 summaryview->folder_table = NULL;
4194
4195 return val;
4196 }
4197
summary_execute_copy_func(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer data)4198 static gboolean summary_execute_copy_func(GtkTreeModel *model,
4199 GtkTreePath *path, GtkTreeIter *iter,
4200 gpointer data)
4201 {
4202 SummaryView *summaryview = data;
4203 MsgInfo *msginfo;
4204
4205 gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
4206
4207 if (msginfo && MSG_IS_COPY(msginfo->flags) && msginfo->to_folder) {
4208 g_hash_table_insert(summaryview->folder_table,
4209 msginfo->to_folder, GINT_TO_POINTER(1));
4210
4211 summaryview->tmp_mlist =
4212 g_slist_prepend(summaryview->tmp_mlist, msginfo);
4213
4214 MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_COPY);
4215 summary_set_row(summaryview, iter, msginfo);
4216 }
4217
4218 return FALSE;
4219 }
4220
summary_execute_copy(SummaryView * summaryview)4221 static gint summary_execute_copy(SummaryView *summaryview)
4222 {
4223 gint val = 0;
4224
4225 summaryview->folder_table = g_hash_table_new(NULL, NULL);
4226
4227 /* search copying messages and execute */
4228 gtk_tree_model_foreach(GTK_TREE_MODEL(summaryview->store),
4229 summary_execute_copy_func, summaryview);
4230
4231 if (summaryview->tmp_mlist) {
4232 summaryview->tmp_mlist =
4233 g_slist_reverse(summaryview->tmp_mlist);
4234 val = procmsg_copy_messages(summaryview->tmp_mlist);
4235
4236 folderview_update_item_foreach(summaryview->folder_table,
4237 FALSE);
4238
4239 g_slist_free(summaryview->tmp_mlist);
4240 summaryview->tmp_mlist = NULL;
4241 }
4242
4243 g_hash_table_destroy(summaryview->folder_table);
4244 summaryview->folder_table = NULL;
4245
4246 return val;
4247 }
4248
summary_execute_delete_func(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer data)4249 static gboolean summary_execute_delete_func(GtkTreeModel *model,
4250 GtkTreePath *path,
4251 GtkTreeIter *iter, gpointer data)
4252 {
4253 SummaryView *summaryview = data;
4254 MsgInfo *msginfo;
4255
4256 gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
4257
4258 if (msginfo && MSG_IS_DELETED(msginfo->flags)) {
4259 summaryview->tmp_mlist =
4260 g_slist_prepend(summaryview->tmp_mlist, msginfo);
4261 }
4262
4263 return FALSE;
4264 }
4265
summary_execute_delete(SummaryView * summaryview)4266 static gint summary_execute_delete(SummaryView *summaryview)
4267 {
4268 FolderItem *trash = NULL;
4269 PrefsAccount *ac;
4270 gint val = 0;
4271
4272 ac = account_find_from_item_property(summaryview->folder_item);
4273 if (ac && ac->set_trash_folder && ac->trash_folder)
4274 trash = folder_find_item_from_identifier(ac->trash_folder);
4275 if (!trash)
4276 trash = summaryview->folder_item->folder->trash;
4277 if (!trash)
4278 folder_get_default_trash();
4279
4280 if (FOLDER_TYPE(summaryview->folder_item->folder) == F_MH) {
4281 g_return_val_if_fail(trash != NULL, 0);
4282 }
4283
4284 /* search deleting messages and execute */
4285 gtk_tree_model_foreach(GTK_TREE_MODEL(summaryview->store),
4286 summary_execute_delete_func, summaryview);
4287
4288 if (!summaryview->tmp_mlist) return 0;
4289
4290 summaryview->tmp_mlist = g_slist_reverse(summaryview->tmp_mlist);
4291
4292 if (summaryview->folder_item != trash)
4293 val = folder_item_move_msgs(trash, summaryview->tmp_mlist);
4294 else
4295 val = folder_item_remove_msgs(trash, summaryview->tmp_mlist);
4296
4297 g_slist_free(summaryview->tmp_mlist);
4298 summaryview->tmp_mlist = NULL;
4299
4300 if (summaryview->folder_item != trash) {
4301 folder_item_scan(trash);
4302 folderview_update_item(trash, FALSE);
4303 }
4304
4305 return val == -1 ? -1 : 0;
4306 }
4307
4308 /* thread functions */
4309
summary_thread_build(SummaryView * summaryview)4310 void summary_thread_build(SummaryView *summaryview)
4311 {
4312 GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
4313 GtkTreeStore *store = summaryview->store;
4314 GtkTreeIter iter, next;
4315 GtkTreePath *path;
4316 GSList *mlist;
4317 GNode *root, *node;
4318 GHashTable *node_table;
4319 MsgInfo *msginfo;
4320 gboolean valid;
4321 FolderSortKey sort_key = SORT_BY_NONE;
4322 FolderSortType sort_type = SORT_ASCENDING;
4323 MsgInfo *selected_msg, *displayed_msg;
4324
4325 if (!summaryview->folder_item)
4326 return;
4327
4328 summary_lock(summaryview);
4329
4330 debug_print(_("Building threads..."));
4331 STATUSBAR_PUSH(summaryview->mainwin, _("Building threads..."));
4332 main_window_cursor_wait(summaryview->mainwin);
4333
4334 g_signal_handlers_block_matched(G_OBJECT(summaryview->treeview),
4335 (GSignalMatchType)G_SIGNAL_MATCH_DATA,
4336 0, 0, NULL, NULL, summaryview);
4337
4338 selected_msg = summary_get_msginfo(summaryview, summaryview->selected);
4339 displayed_msg = summary_get_msginfo
4340 (summaryview, summaryview->displayed);
4341
4342 summaryview->folder_item->threaded = TRUE;
4343
4344 mlist = summary_get_msg_list(summaryview);
4345 root = procmsg_get_thread_tree(mlist);
4346 node_table = g_hash_table_new(NULL, NULL);
4347 for (node = root->children; node != NULL; node = node->next) {
4348 g_hash_table_insert(node_table, node->data, node);
4349 }
4350
4351 if (summaryview->folder_item->sort_key != SORT_BY_NONE) {
4352 sort_key = summaryview->folder_item->sort_key;
4353 sort_type = summaryview->folder_item->sort_type;
4354 summary_sort(summaryview, SORT_BY_NONE, SORT_ASCENDING);
4355 }
4356
4357 valid = gtk_tree_model_get_iter_first(model, &next);
4358 while (valid) {
4359 iter = next;
4360 valid = gtk_tree_model_iter_next(model, &next);
4361
4362 gtk_tree_model_get(model, &iter, S_COL_MSG_INFO, &msginfo, -1);
4363 node = g_hash_table_lookup(node_table, msginfo);
4364 if (node) {
4365 GNode *cur;
4366 GtkTreeIter child;
4367 guint tdate;
4368
4369 for (cur = node->children; cur != NULL;
4370 cur = cur->next) {
4371 summary_insert_gnode(summaryview, store, &child,
4372 &iter, NULL, cur);
4373 }
4374
4375 tdate = procmsg_get_thread_date(node);
4376 gtk_tree_store_set(store, &iter, S_COL_TDATE, tdate, -1);
4377 } else
4378 gtk_tree_store_remove(store, &iter);
4379 }
4380
4381 if (sort_key != SORT_BY_NONE)
4382 summary_sort(summaryview, sort_key, sort_type);
4383
4384 g_hash_table_destroy(node_table);
4385 g_node_destroy(root);
4386 g_slist_free(mlist);
4387
4388 if (prefs_common.expand_thread)
4389 gtk_tree_view_expand_all(GTK_TREE_VIEW(summaryview->treeview));
4390
4391 if (!summaryview->selected ||
4392 (summaryview->selected &&
4393 !gtk_tree_row_reference_valid(summaryview->selected))) {
4394 if (selected_msg &&
4395 gtkut_tree_model_find_by_column_data
4396 (model, &iter, NULL, S_COL_MSG_INFO, selected_msg)) {
4397 summary_select_row(summaryview, &iter, FALSE, TRUE);
4398 }
4399 } else
4400 summary_scroll_to_selected(summaryview, TRUE);
4401
4402 if (summaryview->displayed &&
4403 !gtk_tree_row_reference_valid(summaryview->displayed)) {
4404 if (displayed_msg &&
4405 gtkut_tree_model_find_by_column_data
4406 (model, &iter, NULL, S_COL_MSG_INFO, displayed_msg)) {
4407 path = gtk_tree_model_get_path(model, &iter);
4408 gtk_tree_row_reference_free(summaryview->displayed);
4409 summaryview->displayed =
4410 gtk_tree_row_reference_new(model, path);
4411 gtk_tree_path_free(path);
4412 } else {
4413 messageview_clear(summaryview->messageview);
4414 gtk_tree_row_reference_free(summaryview->displayed);
4415 summaryview->displayed = NULL;
4416 }
4417 }
4418
4419 g_signal_handlers_unblock_matched(G_OBJECT(summaryview->treeview),
4420 (GSignalMatchType)G_SIGNAL_MATCH_DATA,
4421 0, 0, NULL, NULL, summaryview);
4422
4423 debug_print(_("done.\n"));
4424 STATUSBAR_POP(summaryview->mainwin);
4425 main_window_cursor_normal(summaryview->mainwin);
4426
4427 summary_unlock(summaryview);
4428 }
4429
summary_unthread_node_recursive(SummaryView * summaryview,GtkTreeIter * iter,GtkTreeIter * sibling)4430 static void summary_unthread_node_recursive(SummaryView *summaryview,
4431 GtkTreeIter *iter,
4432 GtkTreeIter *sibling)
4433 {
4434 GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
4435 GtkTreeIter iter_, child;
4436 MsgInfo *msginfo;
4437 gboolean valid;
4438
4439 gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
4440 gtk_tree_store_insert_after(GTK_TREE_STORE(model), &iter_,
4441 NULL, sibling);
4442 summary_set_row(summaryview, &iter_, msginfo);
4443 *sibling = iter_;
4444
4445 valid = gtk_tree_model_iter_children(model, &child, iter);
4446 while (valid) {
4447 summary_unthread_node_recursive(summaryview, &child, sibling);
4448 valid = gtk_tree_model_iter_next(model, &child);
4449 }
4450 }
4451
summary_unthread_node(SummaryView * summaryview,GtkTreeIter * iter)4452 static void summary_unthread_node(SummaryView *summaryview, GtkTreeIter *iter)
4453 {
4454 GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
4455 GtkTreeIter child, sibling, next;
4456 gboolean valid;
4457
4458 sibling = *iter;
4459
4460 valid = gtk_tree_model_iter_children(model, &next, iter);
4461 while (valid) {
4462 child = next;
4463 valid = gtk_tree_model_iter_next(model, &next);
4464 summary_unthread_node_recursive(summaryview, &child, &sibling);
4465 gtk_tree_store_remove(GTK_TREE_STORE(model), &child);
4466 }
4467 }
4468
summary_unthread(SummaryView * summaryview)4469 void summary_unthread(SummaryView *summaryview)
4470 {
4471 GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
4472 GtkTreeIter iter, next;
4473 GtkTreePath *path;
4474 gboolean valid;
4475 FolderSortKey sort_key = SORT_BY_NONE;
4476 FolderSortType sort_type = SORT_ASCENDING;
4477 MsgInfo *selected_msg, *displayed_msg;
4478
4479 if (!summaryview->folder_item)
4480 return;
4481
4482 summary_lock(summaryview);
4483
4484 debug_print(_("Unthreading..."));
4485 STATUSBAR_PUSH(summaryview->mainwin, _("Unthreading..."));
4486 main_window_cursor_wait(summaryview->mainwin);
4487
4488 g_signal_handlers_block_matched(G_OBJECT(summaryview->treeview),
4489 (GSignalMatchType)G_SIGNAL_MATCH_DATA,
4490 0, 0, NULL, NULL, summaryview);
4491
4492 selected_msg = summary_get_msginfo(summaryview, summaryview->selected);
4493 displayed_msg = summary_get_msginfo
4494 (summaryview, summaryview->displayed);
4495
4496 summaryview->folder_item->threaded = FALSE;
4497
4498 if (summaryview->folder_item->sort_key != SORT_BY_NONE) {
4499 sort_key = summaryview->folder_item->sort_key;
4500 sort_type = summaryview->folder_item->sort_type;
4501 summary_sort(summaryview, SORT_BY_NONE, SORT_ASCENDING);
4502 }
4503
4504 valid = gtk_tree_model_get_iter_first(model, &next);
4505 while (valid) {
4506 iter = next;
4507 valid = gtk_tree_model_iter_next(model, &next);
4508 gtk_tree_store_set(GTK_TREE_STORE(model), &iter,
4509 S_COL_TDATE, 0, -1);
4510 }
4511
4512 valid = gtk_tree_model_get_iter_first(model, &next);
4513 while (valid) {
4514 iter = next;
4515 valid = gtk_tree_model_iter_next(model, &next);
4516 summary_unthread_node(summaryview, &iter);
4517 }
4518
4519 if (sort_key != SORT_BY_NONE)
4520 summary_sort(summaryview, sort_key, sort_type);
4521
4522 if (!summaryview->selected ||
4523 (summaryview->selected &&
4524 !gtk_tree_row_reference_valid(summaryview->selected))) {
4525 if (selected_msg &&
4526 gtkut_tree_model_find_by_column_data
4527 (model, &iter, NULL, S_COL_MSG_INFO, selected_msg)) {
4528 summary_select_row(summaryview, &iter, FALSE, TRUE);
4529 }
4530 } else
4531 summary_scroll_to_selected(summaryview, TRUE);
4532
4533 if (summaryview->displayed &&
4534 !gtk_tree_row_reference_valid(summaryview->displayed)) {
4535 if (displayed_msg &&
4536 gtkut_tree_model_find_by_column_data
4537 (model, &iter, NULL, S_COL_MSG_INFO, displayed_msg)) {
4538 path = gtk_tree_model_get_path(model, &iter);
4539 gtk_tree_row_reference_free(summaryview->displayed);
4540 summaryview->displayed =
4541 gtk_tree_row_reference_new(model, path);
4542 gtk_tree_path_free(path);
4543 } else {
4544 messageview_clear(summaryview->messageview);
4545 gtk_tree_row_reference_free(summaryview->displayed);
4546 summaryview->displayed = NULL;
4547 }
4548 }
4549
4550 g_signal_handlers_unblock_matched(G_OBJECT(summaryview->treeview),
4551 (GSignalMatchType)G_SIGNAL_MATCH_DATA,
4552 0, 0, NULL, NULL, summaryview);
4553
4554 debug_print(_("done.\n"));
4555 STATUSBAR_POP(summaryview->mainwin);
4556 main_window_cursor_normal(summaryview->mainwin);
4557
4558 summary_unlock(summaryview);
4559 }
4560
summary_has_invalid_node(GtkTreeModel * model,GtkTreeIter * iter)4561 static gboolean summary_has_invalid_node(GtkTreeModel *model, GtkTreeIter *iter)
4562 {
4563 GtkTreeIter child;
4564 MsgInfo *msginfo;
4565 gboolean valid;
4566
4567 gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
4568 if (MSG_IS_INVALID(msginfo->flags))
4569 return TRUE;
4570
4571 valid = gtk_tree_model_iter_children(model, &child, iter);
4572 while (valid) {
4573 if (summary_has_invalid_node(model, &child))
4574 return TRUE;
4575 valid = gtk_tree_model_iter_next(model, &child);
4576 }
4577
4578 return FALSE;
4579 }
4580
summary_get_modified_node(SummaryView * summaryview,GtkTreeIter * iter,GNode * parent,GNode * sibling)4581 static GNode *summary_get_modified_node(SummaryView *summaryview,
4582 GtkTreeIter *iter,
4583 GNode *parent, GNode *sibling)
4584 {
4585 GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
4586 GNode *node = NULL, *new_sibling;
4587 GtkTreeIter child;
4588 MsgInfo *msginfo;
4589 gboolean valid;
4590
4591 gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
4592
4593 if (!MSG_IS_INVALID(msginfo->flags)) {
4594 node = g_node_new(msginfo);
4595 g_node_insert_after(parent, sibling, node);
4596 parent = node;
4597 sibling = NULL;
4598 } else {
4599 summaryview->all_mlist = g_slist_remove(summaryview->all_mlist,
4600 msginfo);
4601 if (summaryview->flt_mlist)
4602 summaryview->flt_mlist =
4603 g_slist_remove(summaryview->flt_mlist, msginfo);
4604 procmsg_msginfo_free(msginfo);
4605 }
4606
4607 valid = gtk_tree_model_iter_children(model, &child, iter);
4608
4609 while (valid) {
4610 new_sibling = summary_get_modified_node(summaryview, &child,
4611 parent, sibling);
4612 if (new_sibling) {
4613 sibling = new_sibling;
4614 if (!node)
4615 node = sibling;
4616 }
4617 valid = gtk_tree_model_iter_next(model, &child);
4618 }
4619
4620 return node;
4621 }
4622
4623 #if 0
4624 static gboolean traverse(GNode *node, gpointer data)
4625 {
4626 gint i;
4627
4628 if (!node->data)
4629 return FALSE;
4630 for (i = 0; i < g_node_depth(node); i++)
4631 g_print(" ");
4632 g_print("%s\n", ((MsgInfo *)node->data)->subject);
4633 return FALSE;
4634 }
4635 #endif
4636
summary_modify_node(SummaryView * summaryview,GtkTreeIter * iter,GtkTreeIter * selected)4637 static void summary_modify_node(SummaryView *summaryview, GtkTreeIter *iter,
4638 GtkTreeIter *selected)
4639 {
4640 GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
4641 MsgInfo *msginfo, *sel_msginfo = NULL;
4642 GNode *root, *cur;
4643 GtkTreeIter iter_, sibling;
4644 GtkTreeIter *sibling_p = NULL;
4645 GtkTreePath *path, *sel_path;
4646 gboolean found = FALSE;
4647
4648 if (!gtk_tree_model_iter_has_child(model, iter))
4649 return;
4650 if (!summary_has_invalid_node(model, iter))
4651 return;
4652
4653 gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
4654
4655 if (selected) {
4656 path = gtk_tree_model_get_path(model, iter);
4657 sel_path = gtk_tree_model_get_path(model, selected);
4658 if (gtk_tree_path_compare(path, sel_path) == 0 ||
4659 gtk_tree_path_is_ancestor(path, sel_path))
4660 gtk_tree_model_get(model, selected,
4661 S_COL_MSG_INFO, &sel_msginfo, -1);
4662 gtk_tree_path_free(sel_path);
4663 gtk_tree_path_free(path);
4664 }
4665
4666 root = g_node_new(NULL);
4667 summary_get_modified_node(summaryview, iter, root, NULL);
4668
4669 /* g_node_traverse(root, G_PRE_ORDER, G_TRAVERSE_ALL, -1, traverse, NULL); */
4670
4671 sibling = *iter;
4672 if (gtk_tree_model_iter_next(model, &sibling))
4673 sibling_p = &sibling;
4674
4675 gtk_tree_store_remove(GTK_TREE_STORE(model), iter);
4676
4677 for (cur = root->children; cur != NULL; cur = cur->next) {
4678 summary_insert_gnode_before(summaryview, GTK_TREE_STORE(model),
4679 &iter_, NULL, sibling_p, cur);
4680 if (summaryview->folder_item->threaded &&
4681 prefs_common.expand_thread) {
4682 path = gtk_tree_model_get_path(model, &iter_);
4683 gtk_tree_view_expand_row
4684 (GTK_TREE_VIEW(summaryview->treeview),
4685 path, TRUE);
4686 gtk_tree_path_free(path);
4687 }
4688 if (sel_msginfo && !found) {
4689 found = gtkut_tree_model_find_by_column_data
4690 (model, selected, &iter_,
4691 S_COL_MSG_INFO, sel_msginfo);
4692 }
4693 }
4694
4695 g_node_destroy(root);
4696
4697 summaryview->folder_item->cache_dirty = TRUE;
4698 }
4699
summary_modify_threads(SummaryView * summaryview)4700 static void summary_modify_threads(SummaryView *summaryview)
4701 {
4702 GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
4703 GtkTreeIter iter, next, selected, new_selected;
4704 GtkTreeIter *selected_p = NULL;
4705 GtkTreePath *prev_path = NULL;
4706 gboolean valid, has_selection;
4707
4708 summary_lock(summaryview);
4709
4710 debug_print("Modifying threads for execution...");
4711
4712 g_signal_handlers_block_by_func(summaryview->selection,
4713 summary_selection_changed, summaryview);
4714
4715 has_selection = gtkut_tree_row_reference_get_iter
4716 (model, summaryview->selected, &selected);
4717 if (has_selection) {
4718 prev_path = gtk_tree_row_reference_get_path
4719 (summaryview->selected);
4720 if (summary_find_nearest_msg(summaryview, &next, &selected)) {
4721 selected = next;
4722 selected_p = &selected;
4723 } else
4724 has_selection = FALSE;
4725 }
4726
4727 valid = gtk_tree_model_get_iter_first(model, &next);
4728 while (valid) {
4729 iter = next;
4730 valid = gtk_tree_model_iter_next(model, &next);
4731 summary_modify_node(summaryview, &iter, selected_p);
4732 }
4733
4734 g_signal_handlers_unblock_by_func(summaryview->selection,
4735 summary_selection_changed,
4736 summaryview);
4737
4738 if (summaryview->folder_item->cache_dirty)
4739 summary_selection_list_free(summaryview);
4740
4741 if (has_selection &&
4742 !gtk_tree_row_reference_valid(summaryview->selected)) {
4743 if (prev_path &&
4744 gtk_tree_model_get_iter(model, &new_selected, prev_path))
4745 selected = new_selected;
4746 summary_select_row(summaryview, &selected, FALSE, FALSE);
4747 }
4748 gtk_tree_path_free(prev_path);
4749
4750 debug_print("done.\n");
4751
4752 summary_unlock(summaryview);
4753 }
4754
summary_expand_threads(SummaryView * summaryview)4755 void summary_expand_threads(SummaryView *summaryview)
4756 {
4757 gtk_tree_view_expand_all(GTK_TREE_VIEW(summaryview->treeview));
4758 }
4759
summary_collapse_threads(SummaryView * summaryview)4760 void summary_collapse_threads(SummaryView *summaryview)
4761 {
4762 gtk_tree_view_collapse_all(GTK_TREE_VIEW(summaryview->treeview));
4763 }
4764
summary_filter_func(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer data)4765 static gboolean summary_filter_func(GtkTreeModel *model, GtkTreePath *path,
4766 GtkTreeIter *iter, gpointer data)
4767 {
4768 SummaryView *summaryview = (SummaryView *)data;
4769 MsgInfo *msginfo;
4770 FilterInfo *fltinfo;
4771
4772 gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
4773
4774 summaryview->flt_count++;
4775 {
4776 gchar msg[1024];
4777 g_snprintf(msg, sizeof(msg), _("Filtering (%d / %d)..."),
4778 summaryview->flt_count, summaryview->flt_total);
4779 STATUSBAR_POP(summaryview->mainwin);
4780 STATUSBAR_PUSH(summaryview->mainwin, msg);
4781 if ((summaryview->flt_count % 100) == 0) {
4782 GTK_EVENTS_FLUSH();
4783 }
4784 }
4785
4786 fltinfo = filter_info_new();
4787 fltinfo->flags = msginfo->flags;
4788 filter_apply_msginfo(prefs_common.fltlist, msginfo, fltinfo);
4789 if (fltinfo->actions[FLT_ACTION_MOVE] ||
4790 fltinfo->actions[FLT_ACTION_COPY] ||
4791 fltinfo->actions[FLT_ACTION_DELETE] ||
4792 fltinfo->actions[FLT_ACTION_EXEC] ||
4793 fltinfo->actions[FLT_ACTION_EXEC_ASYNC] ||
4794 fltinfo->actions[FLT_ACTION_MARK] ||
4795 fltinfo->actions[FLT_ACTION_COLOR_LABEL] ||
4796 fltinfo->actions[FLT_ACTION_MARK_READ] ||
4797 fltinfo->actions[FLT_ACTION_FORWARD] ||
4798 fltinfo->actions[FLT_ACTION_FORWARD_AS_ATTACHMENT] ||
4799 fltinfo->actions[FLT_ACTION_REDIRECT])
4800 summaryview->filtered++;
4801
4802 if (msginfo->flags.perm_flags != fltinfo->flags.perm_flags) {
4803 msginfo->flags = fltinfo->flags;
4804 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_FLAG_CHANGED);
4805 summaryview->folder_item->mark_dirty = TRUE;
4806 summary_set_row(summaryview, iter, msginfo);
4807 if (MSG_IS_IMAP(msginfo->flags)) {
4808 if (fltinfo->actions[FLT_ACTION_MARK])
4809 imap_msg_set_perm_flags(msginfo, MSG_MARKED);
4810 if (fltinfo->actions[FLT_ACTION_MARK_READ])
4811 imap_msg_unset_perm_flags(msginfo,
4812 MSG_NEW|MSG_UNREAD);
4813 }
4814 }
4815
4816 if (fltinfo->actions[FLT_ACTION_MOVE] && fltinfo->move_dest)
4817 summary_move_row_to(summaryview, iter, fltinfo->move_dest);
4818 else if (fltinfo->actions[FLT_ACTION_DELETE])
4819 summary_delete_row(summaryview, iter);
4820
4821 filter_info_free(fltinfo);
4822
4823 return FALSE;
4824 }
4825
summary_filter_junk_func(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer data)4826 static gboolean summary_filter_junk_func(GtkTreeModel *model, GtkTreePath *path,
4827 GtkTreeIter *iter, gpointer data)
4828 {
4829 SummaryView *summaryview = (SummaryView *)data;
4830 MsgInfo *msginfo;
4831 FilterInfo *fltinfo;
4832
4833 gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
4834
4835 summaryview->flt_count++;
4836 {
4837 gchar msg[1024];
4838 g_snprintf(msg, sizeof(msg), _("Filtering (%d / %d)..."),
4839 summaryview->flt_count, summaryview->flt_total);
4840 STATUSBAR_POP(summaryview->mainwin);
4841 STATUSBAR_PUSH(summaryview->mainwin, msg);
4842 if ((summaryview->flt_count % 100) == 0) {
4843 GTK_EVENTS_FLUSH();
4844 }
4845 }
4846
4847 fltinfo = filter_info_new();
4848 fltinfo->flags = msginfo->flags;
4849 filter_apply_msginfo(summaryview->junk_fltlist, msginfo, fltinfo);
4850
4851 if (fltinfo->actions[FLT_ACTION_MOVE] ||
4852 fltinfo->actions[FLT_ACTION_COPY] ||
4853 fltinfo->actions[FLT_ACTION_DELETE] ||
4854 fltinfo->actions[FLT_ACTION_MARK_READ])
4855 summaryview->filtered++;
4856 else if (fltinfo->error == FLT_ERROR_EXEC_FAILED ||
4857 fltinfo->last_exec_exit_status >= 3) {
4858 if (summaryview->flt_count == 1) {
4859 g_warning("summary_filter_junk_func: junk filter command returned %d", fltinfo->last_exec_exit_status);
4860 alertpanel_error
4861 (_("Execution of the junk filter command failed.\n"
4862 "Please check the junk mail control setting."));
4863 }
4864 return TRUE;
4865 }
4866
4867 if (msginfo->flags.perm_flags != fltinfo->flags.perm_flags) {
4868 msginfo->flags = fltinfo->flags;
4869 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_FLAG_CHANGED);
4870 summaryview->folder_item->mark_dirty = TRUE;
4871 summary_set_row(summaryview, iter, msginfo);
4872 if (MSG_IS_IMAP(msginfo->flags)) {
4873 if (fltinfo->actions[FLT_ACTION_MARK_READ])
4874 imap_msg_unset_perm_flags(msginfo,
4875 MSG_NEW|MSG_UNREAD);
4876 }
4877 }
4878
4879 if (fltinfo->actions[FLT_ACTION_MOVE] && fltinfo->move_dest)
4880 summary_move_row_to(summaryview, iter, fltinfo->move_dest);
4881 else if (fltinfo->actions[FLT_ACTION_DELETE])
4882 summary_delete_row(summaryview, iter);
4883
4884 filter_info_free(fltinfo);
4885
4886 return FALSE;
4887 }
4888
summary_filter_real(SummaryView * summaryview,GtkTreeModelForeachFunc func,gboolean selected_only)4889 static void summary_filter_real(SummaryView *summaryview,
4890 GtkTreeModelForeachFunc func,
4891 gboolean selected_only)
4892 {
4893 GList *rows;
4894 FolderSortKey sort_key;
4895 FolderSortType sort_type;
4896
4897 if (!summaryview->folder_item) return;
4898
4899 if (summary_is_locked(summaryview)) return;
4900 summary_lock(summaryview);
4901
4902 STATUSBAR_POP(summaryview->mainwin);
4903
4904 debug_print(_("filtering..."));
4905 STATUSBAR_PUSH(summaryview->mainwin, _("Filtering..."));
4906 main_window_cursor_wait(summaryview->mainwin);
4907 GTK_EVENTS_FLUSH();
4908
4909 sort_key = summaryview->folder_item->sort_key;
4910 sort_type = summaryview->folder_item->sort_type;
4911 if (sort_key != SORT_BY_NONE)
4912 summary_sort(summaryview, SORT_BY_NONE, SORT_ASCENDING);
4913
4914 summaryview->filtered = 0;
4915 summaryview->flt_count = 0;
4916
4917 if (selected_only) {
4918 rows = summary_get_selected_rows(summaryview);
4919 summaryview->flt_total = g_list_length(rows);
4920
4921 gtk_tree_selection_selected_foreach
4922 (summaryview->selection,
4923 (GtkTreeSelectionForeachFunc)func,
4924 summaryview);
4925 } else {
4926 summaryview->flt_total = summaryview->folder_item->total;
4927 gtk_tree_model_foreach(GTK_TREE_MODEL(summaryview->store),
4928 func, summaryview);
4929 }
4930
4931 if (sort_key != SORT_BY_NONE)
4932 summary_sort(summaryview, sort_key, sort_type);
4933
4934 summary_unlock(summaryview);
4935
4936 if (prefs_common.immediate_exec)
4937 summary_execute(summaryview);
4938 else
4939 summary_status_show(summaryview);
4940
4941 folderview_update_all_updated(FALSE);
4942
4943 debug_print(_("done.\n"));
4944 STATUSBAR_POP(summaryview->mainwin);
4945 main_window_cursor_normal(summaryview->mainwin);
4946
4947 if (summaryview->filtered > 0) {
4948 gchar result_msg[BUFFSIZE];
4949 g_snprintf(result_msg, sizeof(result_msg),
4950 _("%d message(s) have been filtered."),
4951 summaryview->filtered);
4952 STATUSBAR_PUSH(summaryview->mainwin, result_msg);
4953 }
4954 summaryview->filtered = 0;
4955 summaryview->flt_count = 0;
4956 summaryview->flt_total = 0;
4957 }
4958
summary_filter(SummaryView * summaryview,gboolean selected_only)4959 void summary_filter(SummaryView *summaryview, gboolean selected_only)
4960 {
4961 if (prefs_common.fltlist)
4962 summary_filter_real(summaryview, summary_filter_func,
4963 selected_only);
4964 }
4965
summary_filter_junk(SummaryView * summaryview,gboolean selected_only)4966 void summary_filter_junk(SummaryView *summaryview, gboolean selected_only)
4967 {
4968 FilterRule *rule;
4969 GSList junk_fltlist = {NULL, NULL};
4970 FolderItem *item = summaryview->folder_item;
4971 FolderItem *junk = NULL;
4972
4973 if (!item)
4974 return;
4975
4976 if (item->folder)
4977 junk = folder_get_junk(item->folder);
4978 rule = filter_junk_rule_create(NULL, junk, TRUE);
4979 if (rule) {
4980 junk_fltlist.data = rule;
4981 summaryview->junk_fltlist = &junk_fltlist;
4982 summary_filter_real(summaryview, summary_filter_junk_func,
4983 selected_only);
4984 summaryview->junk_fltlist = NULL;
4985 filter_rule_free(rule);
4986 }
4987 }
4988
summary_filter_open(SummaryView * summaryview,FilterCreateType type)4989 void summary_filter_open(SummaryView *summaryview, FilterCreateType type)
4990 {
4991 GtkTreeIter iter;
4992 MsgInfo *msginfo = NULL;
4993 gchar *header = NULL;
4994 gchar *key = NULL;
4995
4996 if (!summaryview->selected) return;
4997 if (!gtkut_tree_row_reference_get_iter
4998 (GTK_TREE_MODEL(summaryview->store),
4999 summaryview->selected, &iter))
5000 return;
5001
5002 GET_MSG_INFO(msginfo, &iter);
5003 if (!msginfo) return;
5004
5005 filter_get_keyword_from_msg(msginfo, &header, &key, type);
5006 prefs_filter_open(msginfo, header, key);
5007
5008 g_free(header);
5009 g_free(key);
5010 }
5011
summary_junk_func(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer data)5012 static void summary_junk_func(GtkTreeModel *model, GtkTreePath *path,
5013 GtkTreeIter *iter, gpointer data)
5014 {
5015 FilterRule rule = {NULL, FLT_OR, NULL, NULL, FLT_TIMING_ANY, TRUE};
5016 FilterAction action1 = {FLT_ACTION_EXEC, NULL, 0};
5017 FilterAction action2 = {FLT_ACTION_MOVE, NULL, 0};
5018 FilterAction action3 = {FLT_ACTION_MARK_READ, NULL, 0};
5019 SummaryView *summaryview = (SummaryView *)data;
5020 MsgInfo *msginfo;
5021 FilterInfo *fltinfo;
5022 gchar *file;
5023 gchar *junk_id = NULL;
5024 gint ret;
5025
5026 gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
5027 file = procmsg_get_message_file(msginfo);
5028 g_return_if_fail(file != NULL);
5029
5030 if (summaryview->to_folder)
5031 junk_id = folder_item_get_identifier(summaryview->to_folder);
5032
5033 action1.str_value = prefs_common.junk_learncmd;
5034 action2.str_value = junk_id;
5035
5036 rule.action_list = g_slist_append(rule.action_list, &action1);
5037 if (junk_id)
5038 rule.action_list = g_slist_append(rule.action_list, &action2);
5039 if (prefs_common.mark_junk_as_read)
5040 rule.action_list = g_slist_append(rule.action_list, &action3);
5041
5042 fltinfo = filter_info_new();
5043 fltinfo->flags = msginfo->flags;
5044
5045 ret = filter_action_exec(&rule, msginfo, file, fltinfo);
5046
5047 if (ret < 0 || fltinfo->last_exec_exit_status != 0) {
5048 g_warning("summary_junk_func: junk filter command returned %d",
5049 fltinfo->last_exec_exit_status);
5050 alertpanel_error
5051 (_("Execution of the junk filter command failed.\n"
5052 "Please check the junk mail control setting."));
5053 } else {
5054 if (ret == 0 &&
5055 msginfo->flags.perm_flags != fltinfo->flags.perm_flags) {
5056 msginfo->flags = fltinfo->flags;
5057 summary_set_row(summaryview, iter, msginfo);
5058 if (MSG_IS_IMAP(msginfo->flags)) {
5059 if (fltinfo->actions[FLT_ACTION_MARK_READ])
5060 imap_msg_unset_perm_flags
5061 (msginfo, MSG_NEW | MSG_UNREAD);
5062 }
5063 }
5064 if (ret == 0 && fltinfo->actions[FLT_ACTION_MOVE] &&
5065 fltinfo->move_dest)
5066 summary_move_row_to(summaryview, iter,
5067 fltinfo->move_dest);
5068 }
5069
5070 filter_info_free(fltinfo);
5071 g_slist_free(rule.action_list);
5072 g_free(junk_id);
5073 g_free(file);
5074 }
5075
summary_not_junk_func(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer data)5076 static void summary_not_junk_func(GtkTreeModel *model, GtkTreePath *path,
5077 GtkTreeIter *iter, gpointer data)
5078 {
5079 FilterRule rule = {NULL, FLT_OR, NULL, NULL, FLT_TIMING_ANY, TRUE};
5080 FilterAction action = {FLT_ACTION_EXEC, NULL, 0};
5081 MsgInfo *msginfo;
5082 FilterInfo *fltinfo;
5083 gchar *file;
5084 gint ret;
5085
5086 gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
5087 file = procmsg_get_message_file(msginfo);
5088 g_return_if_fail(file != NULL);
5089
5090 action.str_value = prefs_common.nojunk_learncmd;
5091
5092 rule.action_list = g_slist_append(rule.action_list, &action);
5093
5094 fltinfo = filter_info_new();
5095
5096 ret = filter_action_exec(&rule, msginfo, file, fltinfo);
5097
5098 if (ret < 0 || fltinfo->last_exec_exit_status != 0) {
5099 g_warning("summary_not_junk_func: junk filter command returned %d",
5100 fltinfo->last_exec_exit_status);
5101 alertpanel_error
5102 (_("Execution of the junk filter command failed.\n"
5103 "Please check the junk mail control setting."));
5104 }
5105
5106 filter_info_free(fltinfo);
5107 g_slist_free(rule.action_list);
5108 g_free(file);
5109 }
5110
summary_junk(SummaryView * summaryview)5111 void summary_junk(SummaryView *summaryview)
5112 {
5113 FolderItem *junk = NULL;
5114
5115 if (!prefs_common.enable_junk)
5116 return;
5117 if (!prefs_common.junk_learncmd)
5118 return;
5119
5120 debug_print("summary_junk: setting selected mails as junk\n");
5121
5122 if (prefs_common.junk_folder)
5123 junk = folder_find_item_from_identifier
5124 (prefs_common.junk_folder);
5125 if (!junk &&
5126 summaryview->folder_item && summaryview->folder_item->folder)
5127 junk = folder_get_junk(summaryview->folder_item->folder);
5128 if (!junk)
5129 junk = folder_get_default_junk();
5130 if (!junk) {
5131 g_warning("summary_junk: junk folder not found");
5132 return;
5133 }
5134
5135 summary_lock(summaryview);
5136
5137 summaryview->to_folder = junk;
5138 gtk_tree_selection_selected_foreach(summaryview->selection,
5139 summary_junk_func, summaryview);
5140 summaryview->to_folder = NULL;
5141
5142 summary_unlock(summaryview);
5143
5144 if (junk && prefs_common.immediate_exec)
5145 summary_execute(summaryview);
5146 else {
5147 summary_step(summaryview, GTK_SCROLL_STEP_FORWARD);
5148 summary_status_show(summaryview);
5149 }
5150
5151 folderview_update_all_updated(FALSE);
5152 }
5153
summary_not_junk(SummaryView * summaryview)5154 void summary_not_junk(SummaryView *summaryview)
5155 {
5156 if (!prefs_common.enable_junk)
5157 return;
5158 if (!prefs_common.nojunk_learncmd)
5159 return;
5160
5161 summary_lock(summaryview);
5162
5163 debug_print("Set mail as not junk\n");
5164
5165 gtk_tree_selection_selected_foreach(summaryview->selection,
5166 summary_not_junk_func, summaryview);
5167
5168 summary_unlock(summaryview);
5169 }
5170
summary_reply(SummaryView * summaryview,ComposeMode mode)5171 void summary_reply(SummaryView *summaryview, ComposeMode mode)
5172 {
5173 GSList *mlist;
5174 MsgInfo *msginfo;
5175 MsgInfo *displayed_msginfo = NULL;
5176 gchar *text = NULL;
5177
5178 mlist = summary_get_selected_msg_list(summaryview);
5179 if (!mlist) return;
5180 msginfo = (MsgInfo *)mlist->data;
5181
5182 if (summaryview->displayed) {
5183 GtkTreeIter iter;
5184
5185 if (gtkut_tree_row_reference_get_iter
5186 (GTK_TREE_MODEL(summaryview->store),
5187 summaryview->displayed, &iter)) {
5188 GET_MSG_INFO(displayed_msginfo, &iter);
5189 }
5190 }
5191
5192 /* use selection only if the displayed message is selected */
5193 if (!mlist->next && msginfo == displayed_msginfo) {
5194 TextView *textview;
5195
5196 textview = messageview_get_current_textview
5197 (summaryview->messageview);
5198 if (textview) {
5199 text = gtkut_text_view_get_selection
5200 (GTK_TEXT_VIEW(textview->text));
5201 if (text && *text == '\0') {
5202 g_free(text);
5203 text = NULL;
5204 }
5205 }
5206 }
5207
5208 if (prefs_common.reply_with_quote)
5209 mode |= COMPOSE_WITH_QUOTE;
5210
5211 switch (COMPOSE_MODE(mode)) {
5212 case COMPOSE_REPLY:
5213 case COMPOSE_REPLY_TO_SENDER:
5214 case COMPOSE_REPLY_TO_ALL:
5215 case COMPOSE_REPLY_TO_LIST:
5216 compose_reply(msginfo, summaryview->folder_item, mode, text);
5217 break;
5218 case COMPOSE_FORWARD:
5219 compose_forward(mlist, summaryview->folder_item, FALSE, text);
5220 break;
5221 case COMPOSE_FORWARD_AS_ATTACH:
5222 compose_forward(mlist, summaryview->folder_item, TRUE, NULL);
5223 break;
5224 case COMPOSE_REDIRECT:
5225 compose_redirect(msginfo, summaryview->folder_item);
5226 break;
5227 default:
5228 g_warning("summary_reply(): invalid mode: %d\n", mode);
5229 }
5230
5231 summary_update_selected_rows(summaryview);
5232 g_free(text);
5233 g_slist_free(mlist);
5234 }
5235
5236 /* color label */
5237
5238 #define N_COLOR_LABELS colorlabel_get_color_count()
5239
summary_colorlabel_menu_item_activate_cb(GtkWidget * widget,gpointer data)5240 static void summary_colorlabel_menu_item_activate_cb(GtkWidget *widget,
5241 gpointer data)
5242 {
5243 guint color = GPOINTER_TO_UINT(data);
5244 SummaryView *summaryview;
5245
5246 summaryview = g_object_get_data(G_OBJECT(widget), "summaryview");
5247 g_return_if_fail(summaryview != NULL);
5248
5249 /* "dont_toggle" state set? */
5250 if (g_object_get_data(G_OBJECT(summaryview->colorlabel_menu),
5251 "dont_toggle"))
5252 return;
5253
5254 summary_set_colorlabel(summaryview, color, NULL);
5255 }
5256
5257 /* summary_set_colorlabel() - labelcolor parameter is the color *flag*
5258 * for the messsage; not the color index */
summary_set_colorlabel(SummaryView * summaryview,guint labelcolor,GtkWidget * widget)5259 void summary_set_colorlabel(SummaryView *summaryview, guint labelcolor,
5260 GtkWidget *widget)
5261 {
5262 GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
5263 GList *rows, *cur;
5264 GtkTreeIter iter;
5265 MsgInfo *msginfo;
5266 FolderSortKey sort_key = SORT_BY_NONE;
5267 FolderSortType sort_type = SORT_ASCENDING;
5268
5269 if (FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP &&
5270 summary_is_read_locked(summaryview))
5271 return;
5272
5273 summary_lock(summaryview);
5274 SORT_BLOCK(SORT_BY_LABEL);
5275
5276 rows = summary_get_selected_rows(summaryview);
5277 for (cur = rows; cur != NULL; cur = cur->next) {
5278 gtk_tree_model_get_iter(model, &iter, (GtkTreePath *)cur->data);
5279 gtk_tree_model_get(model, &iter, S_COL_MSG_INFO, &msginfo, -1);
5280
5281 MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_CLABEL_FLAG_MASK);
5282 MSG_SET_COLORLABEL_VALUE(msginfo->flags, labelcolor);
5283 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_FLAG_CHANGED);
5284 summary_set_row(summaryview, &iter, msginfo);
5285 }
5286
5287 if (FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP) {
5288 GSList *msglist;
5289
5290 msglist = summary_get_selected_msg_list(summaryview);
5291 imap_msg_list_set_colorlabel_flags(msglist, labelcolor);
5292 g_slist_free(msglist);
5293 }
5294
5295 if (rows)
5296 summaryview->folder_item->mark_dirty = TRUE;
5297
5298 SORT_UNBLOCK(SORT_BY_LABEL);
5299 summary_unlock(summaryview);
5300 }
5301
summary_colorlabel_menu_item_activate_item_cb(GtkMenuItem * menuitem,gpointer data)5302 static void summary_colorlabel_menu_item_activate_item_cb(GtkMenuItem *menuitem,
5303 gpointer data)
5304 {
5305 SummaryView *summaryview;
5306 GtkMenuShell *menu;
5307 GtkCheckMenuItem **items;
5308 gint n;
5309 GList *menu_cur;
5310 GSList *mlist, *cur;
5311
5312 summaryview = (SummaryView *)data;
5313 g_return_if_fail(summaryview != NULL);
5314
5315 menu = GTK_MENU_SHELL(summaryview->colorlabel_menu);
5316 g_return_if_fail(menu != NULL);
5317
5318 mlist = summary_get_selected_msg_list(summaryview);
5319 if (!mlist) return;
5320
5321 items = (GtkCheckMenuItem **)g_new(GtkWidget *, N_COLOR_LABELS + 1);
5322
5323 /* NOTE: don't return prematurely because we set the "dont_toggle"
5324 * state for check menu items */
5325 g_object_set_data(G_OBJECT(menu), "dont_toggle", GINT_TO_POINTER(1));
5326
5327 /* clear items. get item pointers. */
5328 for (n = 0, menu_cur = menu->children; menu_cur != NULL;
5329 menu_cur = menu_cur->next) {
5330 if (GTK_IS_CHECK_MENU_ITEM(menu_cur->data)) {
5331 gtk_check_menu_item_set_active
5332 (GTK_CHECK_MENU_ITEM(menu_cur->data), FALSE);
5333 items[n] = GTK_CHECK_MENU_ITEM(menu_cur->data);
5334 n++;
5335 }
5336 }
5337
5338 if (n == (N_COLOR_LABELS + 1)) {
5339 /* iterate all messages and set the state of the appropriate
5340 * items */
5341 for (cur = mlist; cur != NULL; cur = cur->next) {
5342 MsgInfo *msginfo = (MsgInfo *)cur->data;
5343 gint clabel;
5344
5345 if (msginfo) {
5346 clabel = MSG_GET_COLORLABEL_VALUE
5347 (msginfo->flags);
5348 if (!gtk_check_menu_item_get_active
5349 (items[clabel]))
5350 gtk_check_menu_item_set_active
5351 (items[clabel], TRUE);
5352 }
5353 }
5354 } else
5355 g_warning("invalid number of color elements (%d)\n", n);
5356
5357 /* reset "dont_toggle" state */
5358 g_object_set_data(G_OBJECT(menu), "dont_toggle", GINT_TO_POINTER(0));
5359
5360 g_free(items);
5361 }
5362
summary_colorlabel_menu_create(SummaryView * summaryview)5363 static void summary_colorlabel_menu_create(SummaryView *summaryview)
5364 {
5365 GtkWidget *label_menuitem;
5366 GtkWidget *menu;
5367 GtkWidget *item;
5368 gint i;
5369
5370 label_menuitem = gtk_item_factory_get_item(summaryview->popupfactory,
5371 "/Color label");
5372 g_signal_connect(G_OBJECT(label_menuitem), "activate",
5373 G_CALLBACK(summary_colorlabel_menu_item_activate_item_cb),
5374 summaryview);
5375 gtk_widget_show(label_menuitem);
5376
5377 menu = gtk_menu_new();
5378
5379 /* create sub items. for the menu item activation callback we pass the
5380 * color flag value as data parameter. Also we attach a data pointer
5381 * so we can always get back the SummaryView pointer. */
5382
5383 item = gtk_check_menu_item_new_with_label(_("None"));
5384 gtk_menu_append(GTK_MENU(menu), item);
5385 g_signal_connect(G_OBJECT(item), "activate",
5386 G_CALLBACK(summary_colorlabel_menu_item_activate_cb),
5387 GUINT_TO_POINTER(0));
5388 g_object_set_data(G_OBJECT(item), "summaryview", summaryview);
5389 gtk_widget_show(item);
5390
5391 item = gtk_menu_item_new();
5392 gtk_menu_append(GTK_MENU(menu), item);
5393 gtk_widget_show(item);
5394
5395 /* create pixmap/label menu items */
5396 for (i = 0; i < N_COLOR_LABELS; i++) {
5397 item = colorlabel_create_check_color_menu_item(i);
5398 gtk_menu_append(GTK_MENU(menu), item);
5399 g_signal_connect(G_OBJECT(item), "activate",
5400 G_CALLBACK(summary_colorlabel_menu_item_activate_cb),
5401 GUINT_TO_POINTER(i + 1));
5402 g_object_set_data(G_OBJECT(item), "summaryview", summaryview);
5403 gtk_widget_show(item);
5404 }
5405
5406 gtk_widget_show(menu);
5407 gtk_menu_item_set_submenu(GTK_MENU_ITEM(label_menuitem), menu);
5408 summaryview->colorlabel_menu = menu;
5409 }
5410
summary_tree_view_create(SummaryView * summaryview)5411 static GtkWidget *summary_tree_view_create(SummaryView *summaryview)
5412 {
5413 GtkWidget *treeview;
5414 GtkTreeStore *store;
5415 GtkTreeSelection *selection;
5416 GtkTreeViewColumn *column;
5417 GtkCellRenderer *renderer;
5418 GtkWidget *image;
5419 SummaryColumnType type;
5420
5421 for (type = 0; type < N_SUMMARY_VISIBLE_COLS; type++)
5422 summaryview->columns[type] = NULL;
5423
5424 store = gtk_tree_store_new(N_SUMMARY_COLS,
5425 GDK_TYPE_PIXBUF,
5426 GDK_TYPE_PIXBUF,
5427 GDK_TYPE_PIXBUF,
5428 G_TYPE_STRING,
5429 G_TYPE_STRING,
5430 G_TYPE_STRING,
5431 G_TYPE_STRING,
5432 G_TYPE_UINT,
5433 G_TYPE_STRING,
5434
5435 G_TYPE_POINTER,
5436
5437 G_TYPE_INT,
5438 G_TYPE_UINT,
5439
5440 GDK_TYPE_COLOR,
5441 G_TYPE_INT);
5442
5443 #define SET_SORT(col, func) \
5444 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), \
5445 col, func, summaryview, NULL)
5446
5447 SET_SORT(S_COL_MARK, summary_cmp_by_mark);
5448 SET_SORT(S_COL_UNREAD, summary_cmp_by_unread);
5449 SET_SORT(S_COL_MIME, summary_cmp_by_mime);
5450 SET_SORT(S_COL_SUBJECT, summary_cmp_by_subject);
5451 SET_SORT(S_COL_FROM, summary_cmp_by_from);
5452 SET_SORT(S_COL_DATE, summary_cmp_by_date);
5453 SET_SORT(S_COL_SIZE, summary_cmp_by_size);
5454 SET_SORT(S_COL_NUMBER, summary_cmp_by_num);
5455 SET_SORT(S_COL_TO, summary_cmp_by_to);
5456 SET_SORT(S_COL_LABEL, summary_cmp_by_label);
5457 SET_SORT(S_COL_TDATE, summary_cmp_by_thread_date);
5458
5459 #undef SET_SORT
5460
5461 treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
5462 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), TRUE);
5463 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(treeview),
5464 prefs_common.enable_rules_hint);
5465 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(treeview), FALSE);
5466 gtk_tree_view_set_search_column(GTK_TREE_VIEW(treeview), S_COL_SUBJECT);
5467 gtk_tree_view_set_reorderable(GTK_TREE_VIEW(treeview), FALSE);
5468 g_object_set(treeview, "fixed-height-mode", TRUE, NULL);
5469
5470 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
5471 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
5472 gtk_tree_selection_set_select_function(selection, summary_select_func,
5473 summaryview, NULL);
5474
5475 #define ADD_COLUMN(title, type, col, text_attr, width, align) \
5476 { \
5477 renderer = gtk_cell_renderer_ ## type ## _new(); \
5478 g_object_set(renderer, "xalign", align, "ypad", 0, NULL); \
5479 column = gtk_tree_view_column_new_with_attributes \
5480 (title, renderer, # type , col, NULL); \
5481 g_object_set_data(G_OBJECT(column), "column_id", \
5482 GINT_TO_POINTER(col)); \
5483 summaryview->columns[col] = column; \
5484 if (text_attr) { \
5485 gtk_tree_view_column_set_attributes \
5486 (column, renderer, \
5487 # type, col, \
5488 "foreground-gdk", S_COL_FOREGROUND, \
5489 "weight", S_COL_BOLD, \
5490 NULL); \
5491 gtk_tree_view_column_set_resizable(column, TRUE); \
5492 } \
5493 gtk_tree_view_column_set_alignment(column, align); \
5494 gtk_tree_view_column_set_sizing \
5495 (column, GTK_TREE_VIEW_COLUMN_FIXED); \
5496 gtk_tree_view_column_set_fixed_width(column, width); \
5497 gtk_tree_view_column_set_min_width(column, 8); \
5498 gtk_tree_view_column_set_clickable(column, TRUE); \
5499 /* gtk_tree_view_column_set_sort_column_id(column, col); */ \
5500 gtk_tree_view_column_set_reorderable(column, TRUE); \
5501 gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); \
5502 g_signal_connect(G_OBJECT(column->button), "clicked", \
5503 G_CALLBACK(summary_column_clicked), \
5504 summaryview); \
5505 g_signal_connect(G_OBJECT(column->button), "size-allocate", \
5506 G_CALLBACK(summary_col_resized), summaryview); \
5507 }
5508
5509 ADD_COLUMN(NULL, pixbuf, S_COL_MARK, FALSE,
5510 SUMMARY_COL_MARK_WIDTH, 0.5);
5511 image = stock_pixbuf_widget(treeview, STOCK_PIXMAP_MARK);
5512 gtk_widget_show(image);
5513 gtk_tree_view_column_set_widget(column, image);
5514 ADD_COLUMN(NULL, pixbuf, S_COL_UNREAD, FALSE,
5515 SUMMARY_COL_UNREAD_WIDTH, 0.5);
5516 image = stock_pixbuf_widget(treeview, STOCK_PIXMAP_MAIL_SMALL);
5517 gtk_widget_show(image);
5518 gtk_tree_view_column_set_widget(column, image);
5519 ADD_COLUMN(NULL, pixbuf, S_COL_MIME, FALSE,
5520 SUMMARY_COL_MIME_WIDTH, 0.5);
5521 image = stock_pixbuf_widget(treeview, STOCK_PIXMAP_CLIP);
5522 gtk_widget_show(image);
5523 gtk_tree_view_column_set_widget(column, image);
5524
5525 ADD_COLUMN(_("Subject"), text, S_COL_SUBJECT, TRUE,
5526 prefs_common.summary_col_size[S_COL_SUBJECT], 0.0);
5527 #if GTK_CHECK_VERSION(2, 14, 0)
5528 gtk_tree_view_column_set_expand(column, TRUE);
5529 #endif
5530 gtk_tree_view_set_expander_column(GTK_TREE_VIEW(treeview), column);
5531 ADD_COLUMN(_("From"), text, S_COL_FROM, TRUE,
5532 prefs_common.summary_col_size[S_COL_FROM], 0.0);
5533 ADD_COLUMN(_("Date"), text, S_COL_DATE, TRUE,
5534 prefs_common.summary_col_size[S_COL_DATE], 0.0);
5535 ADD_COLUMN(_("Size"), text, S_COL_SIZE, TRUE,
5536 prefs_common.summary_col_size[S_COL_SIZE], 1.0);
5537 ADD_COLUMN(_("No."), text, S_COL_NUMBER, TRUE,
5538 prefs_common.summary_col_size[S_COL_NUMBER], 1.0);
5539 ADD_COLUMN(_("To"), text, S_COL_TO, TRUE,
5540 prefs_common.summary_col_size[S_COL_TO], 0.0);
5541
5542 #undef ADD_COLUMN
5543
5544 /* add rightmost empty column */
5545 column = gtk_tree_view_column_new();
5546 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
5547 gtk_tree_view_column_set_min_width(column, 0);
5548 gtk_tree_view_column_set_max_width(column, 0);
5549 gtk_tree_view_column_set_clickable(column, FALSE);
5550 gtk_tree_view_column_set_reorderable(column, FALSE);
5551 gtk_tree_view_set_column_drag_function(GTK_TREE_VIEW(treeview),
5552 summary_column_drop_func,
5553 summaryview, NULL);
5554 gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);
5555
5556 g_object_set_data(G_OBJECT(treeview), "user_data", summaryview);
5557
5558 g_signal_connect(G_OBJECT(treeview), "button_press_event",
5559 G_CALLBACK(summary_button_pressed), summaryview);
5560 g_signal_connect(G_OBJECT(treeview), "button_release_event",
5561 G_CALLBACK(summary_button_released), summaryview);
5562 g_signal_connect(G_OBJECT(treeview), "key_press_event",
5563 G_CALLBACK(summary_key_pressed), summaryview);
5564
5565 g_signal_connect(G_OBJECT(selection), "changed",
5566 G_CALLBACK(summary_selection_changed), summaryview);
5567
5568 g_signal_connect(G_OBJECT(treeview), "row-expanded",
5569 G_CALLBACK(summary_row_expanded), summaryview);
5570 g_signal_connect(G_OBJECT(treeview), "row-collapsed",
5571 G_CALLBACK(summary_row_collapsed), summaryview);
5572
5573 g_signal_connect(G_OBJECT(treeview), "columns-changed",
5574 G_CALLBACK(summary_columns_changed), summaryview);
5575
5576 gtk_tree_view_enable_model_drag_source
5577 (GTK_TREE_VIEW(treeview),
5578 GDK_BUTTON1_MASK, summary_drag_types, N_DRAG_TYPES,
5579 GDK_ACTION_MOVE | GDK_ACTION_COPY);
5580
5581 g_signal_connect_after(G_OBJECT(treeview), "drag-begin",
5582 G_CALLBACK(summary_drag_begin), summaryview);
5583 g_signal_connect_after(G_OBJECT(treeview), "drag-end",
5584 G_CALLBACK(summary_drag_end), summaryview);
5585 g_signal_connect(G_OBJECT(treeview), "drag-data-get",
5586 G_CALLBACK(summary_drag_data_get), summaryview);
5587
5588 return treeview;
5589 }
5590
summary_set_column_order(SummaryView * summaryview)5591 void summary_set_column_order(SummaryView *summaryview)
5592 {
5593 const SummaryColumnState *col_state;
5594 SummaryColumnType type;
5595 GtkTreeViewColumn *column, *last_column = NULL;
5596 gint pos;
5597
5598 g_signal_handlers_block_by_func(summaryview->treeview,
5599 summary_columns_changed, summaryview);
5600
5601 col_state = prefs_summary_column_get_config
5602 (FOLDER_ITEM_IS_SENT_FOLDER(summaryview->folder_item));
5603
5604 for (pos = 0; pos < N_SUMMARY_VISIBLE_COLS; pos++) {
5605 summaryview->col_state[pos] = col_state[pos];
5606 type = col_state[pos].type;
5607 summaryview->col_pos[type] = pos;
5608 column = summaryview->columns[type];
5609
5610 gtk_tree_view_move_column_after
5611 (GTK_TREE_VIEW(summaryview->treeview),
5612 column, last_column);
5613 gtk_tree_view_column_set_visible
5614 (column, col_state[pos].visible);
5615 last_column = column;
5616
5617 debug_print("summary_set_column_order: "
5618 "pos %d : type %d, vislble %d\n",
5619 pos, type, summaryview->col_state[pos].visible);
5620 }
5621
5622 g_signal_handlers_unblock_by_func(summaryview->treeview,
5623 summary_columns_changed, summaryview);
5624 }
5625
summary_get_column_order(SummaryView * summaryview)5626 void summary_get_column_order(SummaryView *summaryview)
5627 {
5628 GtkTreeViewColumn *column;
5629 GList *columns, *cur;
5630 gint pos = 0;
5631 SummaryColumnType type;
5632 gboolean visible;
5633
5634 columns = gtk_tree_view_get_columns
5635 (GTK_TREE_VIEW(summaryview->treeview));
5636
5637 for (cur = columns; cur != NULL && pos < N_SUMMARY_VISIBLE_COLS;
5638 cur = cur->next, pos++) {
5639 column = (GtkTreeViewColumn *)cur->data;
5640 type = GPOINTER_TO_INT
5641 (g_object_get_data(G_OBJECT(column), "column_id"));
5642 if (type < 0 || type >= N_SUMMARY_VISIBLE_COLS) {
5643 g_warning("summary_get_column_order: "
5644 "invalid type: %d\n", type);
5645 break;
5646 }
5647 visible = gtk_tree_view_column_get_visible(column);
5648 summaryview->col_state[pos].type = type;
5649 summaryview->col_state[pos].visible = visible;
5650 summaryview->col_pos[type] = pos;
5651 debug_print("summary_get_column_order: "
5652 "pos: %d, type: %d, visible: %d\n",
5653 pos, type, visible);
5654 }
5655
5656 prefs_summary_column_set_config
5657 (summaryview->col_state,
5658 FOLDER_ITEM_IS_SENT_FOLDER(summaryview->folder_item));
5659
5660 g_list_free(columns);
5661 }
5662
summary_qsearch_reset(SummaryView * summaryview)5663 void summary_qsearch_reset(SummaryView *summaryview)
5664 {
5665 guint selected_msgnum = 0;
5666 guint displayed_msgnum = 0;
5667
5668 if (!summaryview->on_filter)
5669 return;
5670 if (!summaryview->folder_item)
5671 return;
5672
5673 if (summary_is_read_locked(summaryview)) return;
5674
5675 g_signal_handlers_block_matched(G_OBJECT(summaryview->treeview),
5676 (GSignalMatchType)G_SIGNAL_MATCH_DATA,
5677 0, 0, NULL, NULL, summaryview);
5678
5679 quick_search_clear_entry(summaryview->qsearch);
5680 gtk_option_menu_set_history
5681 (GTK_OPTION_MENU(summaryview->qsearch->optmenu), 0);
5682 summaryview->folder_item->qsearch_cond_type = QS_ALL;
5683
5684 selected_msgnum = summary_get_msgnum(summaryview,
5685 summaryview->selected);
5686 displayed_msgnum = summary_get_msgnum(summaryview,
5687 summaryview->displayed);
5688
5689 summaryview->on_filter = FALSE;
5690 g_slist_free(summaryview->flt_mlist);
5691 summaryview->flt_mlist = NULL;
5692 summaryview->total_flt_msg_size = 0;
5693 summaryview->flt_msg_total = 0;
5694 summaryview->flt_deleted = 0;
5695 summaryview->flt_moved = 0;
5696 summaryview->flt_copied = 0;
5697 summaryview->flt_new = 0;
5698 summaryview->flt_unread = 0;
5699
5700 main_window_cursor_wait(summaryview->mainwin);
5701 summary_lock(summaryview);
5702
5703 gtkut_tree_view_fast_clear(GTK_TREE_VIEW(summaryview->treeview),
5704 summaryview->store);
5705 summary_unset_sort_column_id(summaryview);
5706 summaryview->total_size = 0;
5707 summary_set_tree_model_from_list(summaryview, summaryview->all_mlist);
5708 summary_selection_list_free(summaryview);
5709
5710 summary_unlock(summaryview);
5711 main_window_cursor_normal(summaryview->mainwin);
5712
5713 g_signal_handlers_unblock_matched(G_OBJECT(summaryview->treeview),
5714 (GSignalMatchType)G_SIGNAL_MATCH_DATA,
5715 0, 0, NULL, NULL, summaryview);
5716
5717 summary_update_display_state(summaryview, displayed_msgnum,
5718 selected_msgnum);
5719
5720 summary_update_status(summaryview);
5721 summary_status_show(summaryview);
5722 summary_set_menu_sensitive(summaryview);
5723 main_window_set_toolbar_sensitive(summaryview->mainwin);
5724 }
5725
summary_qsearch_clear_entry(SummaryView * summaryview)5726 void summary_qsearch_clear_entry(SummaryView *summaryview)
5727 {
5728 if (summary_is_read_locked(summaryview))
5729 return;
5730 quick_search_clear_entry(summaryview->qsearch);
5731 summary_qsearch(summaryview);
5732 }
5733
summary_qsearch(SummaryView * summaryview)5734 void summary_qsearch(SummaryView *summaryview)
5735 {
5736 QSearchCondType type;
5737 GtkWidget *menuitem;
5738 const gchar *key = NULL;
5739 GSList *flt_mlist;
5740 guint selected_msgnum = 0;
5741 guint displayed_msgnum = 0;
5742
5743 if (!summaryview->folder_item)
5744 return;
5745
5746 if (summary_is_read_locked(summaryview)) return;
5747
5748 menuitem = gtk_menu_get_active(GTK_MENU(summaryview->qsearch->menu));
5749 type = GPOINTER_TO_INT
5750 (g_object_get_data(G_OBJECT(menuitem), MENU_VAL_ID));
5751 summaryview->folder_item->qsearch_cond_type = type;
5752
5753 if (!summaryview->all_mlist)
5754 return;
5755
5756 if (summaryview->qsearch->entry_entered)
5757 key = gtk_entry_get_text
5758 (GTK_ENTRY(summaryview->qsearch->entry));
5759 if (type == QS_ALL && (!key || *key == '\0')) {
5760 summary_qsearch_reset(summaryview);
5761 return;
5762 }
5763
5764 selected_msgnum = summary_get_msgnum(summaryview,
5765 summaryview->selected);
5766 displayed_msgnum = summary_get_msgnum(summaryview,
5767 summaryview->displayed);
5768
5769 summaryview->on_filter = FALSE;
5770 g_slist_free(summaryview->flt_mlist);
5771 summaryview->flt_mlist = NULL;
5772 summaryview->total_flt_msg_size = 0;
5773 summaryview->flt_msg_total = 0;
5774 summaryview->flt_deleted = 0;
5775 summaryview->flt_moved = 0;
5776 summaryview->flt_copied = 0;
5777 summaryview->flt_new = 0;
5778 summaryview->flt_unread = 0;
5779
5780 main_window_cursor_wait(summaryview->mainwin);
5781 summary_lock(summaryview);
5782
5783 flt_mlist = quick_search_filter(summaryview->qsearch, type, key);
5784 summaryview->on_filter = TRUE;
5785 summaryview->flt_mlist = flt_mlist;
5786
5787 g_signal_handlers_block_matched(G_OBJECT(summaryview->treeview),
5788 (GSignalMatchType)G_SIGNAL_MATCH_DATA,
5789 0, 0, NULL, NULL, summaryview);
5790
5791 gtkut_tree_view_fast_clear(GTK_TREE_VIEW(summaryview->treeview),
5792 summaryview->store);
5793 summary_unset_sort_column_id(summaryview);
5794 summaryview->total_size = 0;
5795 summary_set_tree_model_from_list(summaryview, flt_mlist);
5796 summary_selection_list_free(summaryview);
5797
5798 g_signal_handlers_unblock_matched(G_OBJECT(summaryview->treeview),
5799 (GSignalMatchType)G_SIGNAL_MATCH_DATA,
5800 0, 0, NULL, NULL, summaryview);
5801
5802 summary_unlock(summaryview);
5803 summary_update_display_state(summaryview, displayed_msgnum,
5804 selected_msgnum);
5805
5806 main_window_cursor_normal(summaryview->mainwin);
5807 summary_update_status(summaryview);
5808 summary_status_show(summaryview);
5809 summary_set_menu_sensitive(summaryview);
5810 main_window_set_toolbar_sensitive(summaryview->mainwin);
5811 }
5812
summary_mark_displayed_read(SummaryView * summaryview,GtkTreeIter * iter)5813 void summary_mark_displayed_read(SummaryView *summaryview, GtkTreeIter *iter)
5814 {
5815 MsgInfo *msginfo = NULL;
5816 GtkTreeIter iter_;
5817
5818 if (summary_is_read_locked(summaryview)) return;
5819
5820 if (prefs_common.mark_as_read_on_new_window)
5821 return;
5822
5823 if (!iter) {
5824 if (!gtkut_tree_row_reference_get_iter
5825 (GTK_TREE_MODEL(summaryview->store),
5826 summaryview->displayed, &iter_))
5827 return;
5828 iter = &iter_;
5829 }
5830
5831 GET_MSG_INFO(msginfo, iter);
5832 if (!msginfo)
5833 return;
5834
5835 summary_lock(summaryview);
5836
5837 if (MSG_IS_NEW(msginfo->flags) ||
5838 MSG_IS_UNREAD(msginfo->flags)) {
5839 summary_mark_row_as_read(summaryview, iter);
5840 if (MSG_IS_IMAP(msginfo->flags))
5841 imap_msg_unset_perm_flags
5842 (msginfo, MSG_NEW | MSG_UNREAD);
5843 summary_set_row(summaryview, iter, msginfo);
5844 summary_status_show(summaryview);
5845
5846 /* sort order can be changed here */
5847 if (summaryview->folder_item->sort_key == SORT_BY_UNREAD)
5848 summary_selection_list_free(summaryview);
5849 }
5850
5851 summary_unlock(summaryview);
5852 }
5853
5854
5855 /* callback functions */
5856
summary_toggle_pressed(GtkWidget * eventbox,GdkEventButton * event,SummaryView * summaryview)5857 static gboolean summary_toggle_pressed(GtkWidget *eventbox,
5858 GdkEventButton *event,
5859 SummaryView *summaryview)
5860 {
5861 if (!event) return FALSE;
5862
5863 summary_toggle_view(summaryview);
5864 return FALSE;
5865 }
5866
summary_button_pressed(GtkWidget * treeview,GdkEventButton * event,SummaryView * summaryview)5867 static gboolean summary_button_pressed(GtkWidget *treeview,
5868 GdkEventButton *event,
5869 SummaryView *summaryview)
5870 {
5871 GtkTreeIter iter;
5872 GtkTreePath *path;
5873 GtkTreeViewColumn *column = NULL;
5874 gboolean is_selected;
5875 gboolean mod_pressed;
5876 gint px, py;
5877
5878 if (!event) return FALSE;
5879
5880 if (summaryview->folder_item && summaryview->folder_item->folder &&
5881 FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP &&
5882 summary_is_locked(summaryview))
5883 return TRUE;
5884
5885 if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(treeview),
5886 event->x, event->y, &path, &column,
5887 NULL, NULL))
5888 return FALSE;
5889
5890 /* pass through if the border of column titles is clicked */
5891 gtk_widget_get_pointer(treeview, &px, &py);
5892 if (py == (gint)event->y)
5893 return FALSE;
5894
5895 if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(summaryview->store),
5896 &iter, path))
5897 return FALSE;
5898
5899 is_selected = gtk_tree_selection_path_is_selected
5900 (summaryview->selection, path);
5901 mod_pressed = ((event->state &
5902 (GDK_SHIFT_MASK|GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0);
5903
5904 if ((event->button == 1 || event->button == 2)) {
5905 MsgInfo *msginfo;
5906 FolderSortKey sort_key = SORT_BY_NONE;
5907 FolderSortType sort_type = SORT_ASCENDING;
5908
5909 GET_MSG_INFO(msginfo, &iter);
5910
5911 if (column == summaryview->columns[S_COL_MARK]) {
5912 if (MSG_IS_IMAP(msginfo->flags) &&
5913 summary_is_locked(summaryview)) {
5914 gtk_tree_path_free(path);
5915 return TRUE;
5916 }
5917
5918 SORT_BLOCK(SORT_BY_MARK);
5919
5920 if (!MSG_IS_DELETED(msginfo->flags) &&
5921 !MSG_IS_MOVE(msginfo->flags) &&
5922 !MSG_IS_COPY(msginfo->flags)) {
5923 if (MSG_IS_MARKED(msginfo->flags)) {
5924 summary_unmark_row(summaryview, &iter);
5925 if (MSG_IS_IMAP(msginfo->flags)) {
5926 summary_lock(summaryview);
5927 imap_msg_unset_perm_flags
5928 (msginfo, MSG_MARKED);
5929 summary_unlock(summaryview);
5930 }
5931 } else {
5932 summary_mark_row(summaryview, &iter);
5933 if (MSG_IS_IMAP(msginfo->flags)) {
5934 summary_lock(summaryview);
5935 imap_msg_set_perm_flags
5936 (msginfo, MSG_MARKED);
5937 summary_unlock(summaryview);
5938 }
5939 }
5940 }
5941 gtk_tree_path_free(path);
5942
5943 SORT_UNBLOCK(SORT_BY_MARK);
5944
5945 return TRUE;
5946 } else if (column == summaryview->columns[S_COL_UNREAD]) {
5947 if (MSG_IS_IMAP(msginfo->flags) &&
5948 summary_is_locked(summaryview)) {
5949 gtk_tree_path_free(path);
5950 return TRUE;
5951 }
5952
5953 SORT_BLOCK(SORT_BY_UNREAD);
5954
5955 if (MSG_IS_UNREAD(msginfo->flags)) {
5956 summary_mark_row_as_read(summaryview, &iter);
5957 if (MSG_IS_IMAP(msginfo->flags)) {
5958 summary_lock(summaryview);
5959 imap_msg_unset_perm_flags
5960 (msginfo, MSG_NEW | MSG_UNREAD);
5961 summary_unlock(summaryview);
5962 }
5963 trayicon_set_tooltip(NULL);
5964 trayicon_set_notify(FALSE);
5965 summary_status_show(summaryview);
5966 } else if (!MSG_IS_REPLIED(msginfo->flags) &&
5967 !MSG_IS_FORWARDED(msginfo->flags)) {
5968 summary_mark_row_as_unread(summaryview, &iter);
5969 if (MSG_IS_IMAP(msginfo->flags)) {
5970 summary_lock(summaryview);
5971 imap_msg_set_perm_flags
5972 (msginfo, MSG_UNREAD);
5973 summary_unlock(summaryview);
5974 }
5975 summary_status_show(summaryview);
5976 }
5977 gtk_tree_path_free(path);
5978
5979 SORT_UNBLOCK(SORT_BY_UNREAD);
5980
5981 return TRUE;
5982 }
5983 }
5984
5985 if (event->button == 1) {
5986 summaryview->on_button_press = TRUE;
5987
5988 if (summary_get_selection_type(summaryview) ==
5989 SUMMARY_SELECTED_MULTIPLE && is_selected &&
5990 !mod_pressed) {
5991 summaryview->can_toggle_selection = FALSE;
5992 summaryview->pressed_path = gtk_tree_path_copy(path);
5993 } else {
5994 if (event->type == GDK_2BUTTON_PRESS && is_selected)
5995 summary_activate_selected(summaryview);
5996 else if (summary_get_selection_type(summaryview) ==
5997 SUMMARY_SELECTED_SINGLE && is_selected &&
5998 !mod_pressed &&
5999 summary_row_is_displayed(summaryview, &iter)) {
6000 summary_mark_displayed_read(summaryview, &iter);
6001 } else {
6002 summaryview->can_toggle_selection = TRUE;
6003 if (!mod_pressed &&
6004 messageview_is_visible(summaryview->messageview))
6005 summaryview->display_msg = TRUE;
6006 }
6007 }
6008
6009 if (summaryview->on_button_press == FALSE) {
6010 /* button released within sub event loop */
6011 gtk_tree_path_free(path);
6012 return TRUE;
6013 }
6014 } else if (event->button == 2) {
6015 summary_select_row(summaryview, &iter, TRUE, FALSE);
6016 summary_mark_displayed_read(summaryview, &iter);
6017 gtk_tree_path_free(path);
6018 return TRUE;
6019 } else if (event->button == 3) {
6020 /* right clicked */
6021 syl_plugin_signal_emit("summaryview-menu-popup",
6022 summaryview->popupfactory);
6023 gtk_menu_popup(GTK_MENU(summaryview->popupmenu), NULL, NULL,
6024 NULL, NULL, event->button, event->time);
6025 if (is_selected) {
6026 gtk_tree_path_free(path);
6027 return TRUE;
6028 }
6029 }
6030
6031 gtk_tree_path_free(path);
6032
6033 return FALSE;
6034 }
6035
summary_button_released(GtkWidget * treeview,GdkEventButton * event,SummaryView * summaryview)6036 static gboolean summary_button_released(GtkWidget *treeview,
6037 GdkEventButton *event,
6038 SummaryView *summaryview)
6039 {
6040 if (!summaryview->can_toggle_selection && !summaryview->on_drag &&
6041 summaryview->pressed_path) {
6042 summaryview->can_toggle_selection = TRUE;
6043 summaryview->display_msg =
6044 messageview_is_visible(summaryview->messageview);
6045 gtk_tree_view_set_cursor(GTK_TREE_VIEW(treeview),
6046 summaryview->pressed_path,
6047 NULL, FALSE);
6048 }
6049
6050 summaryview->on_button_press = FALSE;
6051 summaryview->can_toggle_selection = TRUE;
6052 summaryview->on_drag = FALSE;
6053 if (summaryview->pressed_path) {
6054 gtk_tree_path_free(summaryview->pressed_path);
6055 summaryview->pressed_path = NULL;
6056 }
6057
6058 return FALSE;
6059 }
6060
summary_pass_key_press_event(SummaryView * summaryview,GdkEventKey * event)6061 void summary_pass_key_press_event(SummaryView *summaryview, GdkEventKey *event)
6062 {
6063 summary_key_pressed(summaryview->treeview, event, summaryview);
6064 }
6065
6066 #define BREAK_ON_MODIFIER_KEY() \
6067 if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) break
6068
summary_key_pressed(GtkWidget * widget,GdkEventKey * event,SummaryView * summaryview)6069 static gboolean summary_key_pressed(GtkWidget *widget, GdkEventKey *event,
6070 SummaryView *summaryview)
6071 {
6072 MessageView *messageview;
6073 TextView *textview;
6074 GtkAdjustment *adj;
6075 gboolean mod_pressed;
6076 gboolean scrolled;
6077
6078 if (!event) return FALSE;
6079
6080 if (summary_is_read_locked(summaryview)) {
6081 switch (event->keyval) {
6082 case GDK_Home:
6083 case GDK_End:
6084 case GDK_Up:
6085 case GDK_Down:
6086 case GDK_Page_Up:
6087 case GDK_Page_Down:
6088 return TRUE;
6089 default:
6090 break;
6091 }
6092 return FALSE;
6093 }
6094
6095 switch (event->keyval) {
6096 case GDK_Left: /* Move focus */
6097 case GDK_KP_Left:
6098 adj = gtk_scrolled_window_get_hadjustment
6099 (GTK_SCROLLED_WINDOW(summaryview->scrolledwin));
6100 if (adj->lower != adj->value)
6101 return FALSE;
6102 /* FALLTHROUGH */
6103 case GDK_Escape:
6104 gtk_widget_grab_focus(summaryview->folderview->treeview);
6105 return TRUE;
6106 default:
6107 break;
6108 }
6109
6110 if (!summaryview->selected)
6111 return FALSE;
6112
6113 messageview = summaryview->messageview;
6114 if (messageview->type == MVIEW_MIME &&
6115 gtk_notebook_get_current_page
6116 (GTK_NOTEBOOK(messageview->notebook)) == 1)
6117 textview = messageview->mimeview->textview;
6118 else
6119 textview = messageview->textview;
6120
6121 mod_pressed =
6122 ((event->state & (GDK_SHIFT_MASK|GDK_MOD1_MASK)) != 0);
6123
6124 switch (event->keyval) {
6125 case GDK_space: /* Page down or go to the next */
6126 case GDK_KP_Space:
6127 if (summaryview->selected &&
6128 !gtkut_tree_row_reference_equal(summaryview->displayed,
6129 summaryview->selected)) {
6130 summary_display_msg_selected(summaryview, FALSE, FALSE);
6131 summary_mark_displayed_read(summaryview, NULL);
6132 } else if (mod_pressed) {
6133 scrolled = textview_scroll_page(textview, TRUE);
6134 if (!scrolled)
6135 summary_select_prev_unread(summaryview);
6136 } else {
6137 scrolled = textview_scroll_page(textview, FALSE);
6138 summary_mark_displayed_read(summaryview, NULL);
6139 if (!scrolled)
6140 summary_select_next_unread(summaryview);
6141 }
6142 return TRUE;
6143 case GDK_BackSpace: /* Page up */
6144 textview_scroll_page(textview, TRUE);
6145 return TRUE;
6146 case GDK_Return: /* Scroll up/down one line */
6147 case GDK_KP_Enter:
6148 if (summaryview->selected &&
6149 !gtkut_tree_row_reference_equal(summaryview->displayed,
6150 summaryview->selected))
6151 summary_display_msg_selected(summaryview, FALSE, FALSE);
6152 else
6153 textview_scroll_one_line(textview, mod_pressed);
6154 summary_mark_displayed_read(summaryview, NULL);
6155 return TRUE;
6156 case GDK_Delete:
6157 case GDK_KP_Delete:
6158 BREAK_ON_MODIFIER_KEY();
6159 summary_delete(summaryview);
6160 return TRUE;
6161 case GDK_F10:
6162 case GDK_Menu:
6163 if (event->keyval == GDK_F10 &&
6164 (event->state & GDK_SHIFT_MASK) == 0)
6165 break;
6166 syl_plugin_signal_emit("summaryview-menu-popup",
6167 summaryview->popupfactory);
6168 gtk_menu_popup(GTK_MENU(summaryview->popupmenu), NULL, NULL,
6169 menu_widget_position, summaryview->treeview,
6170 0, GDK_CURRENT_TIME);
6171 return TRUE;
6172 default:
6173 break;
6174 }
6175
6176 return FALSE;
6177 }
6178
summary_set_bold_recursive(SummaryView * summaryview,GtkTreeIter * iter)6179 static void summary_set_bold_recursive(SummaryView *summaryview,
6180 GtkTreeIter *iter)
6181 {
6182 GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
6183 GtkTreeIter child;
6184 MsgInfo *msginfo;
6185 gboolean valid;
6186
6187 if (!gtk_tree_model_iter_has_child(model, iter))
6188 return;
6189
6190 GET_MSG_INFO(msginfo, iter);
6191 if (!MSG_IS_UNREAD(msginfo->flags)) {
6192 gtk_tree_store_set(summaryview->store, iter,
6193 S_COL_BOLD, PANGO_WEIGHT_NORMAL, -1);
6194 }
6195
6196 valid = gtk_tree_model_iter_children(model, &child, iter);
6197 while (valid) {
6198 summary_set_bold_recursive(summaryview, &child);
6199 valid = gtk_tree_model_iter_next(model, &child);
6200 }
6201 }
6202
summary_row_expanded(GtkTreeView * treeview,GtkTreeIter * iter,GtkTreePath * path,SummaryView * summaryview)6203 static void summary_row_expanded(GtkTreeView *treeview, GtkTreeIter *iter,
6204 GtkTreePath *path, SummaryView *summaryview)
6205 {
6206 gtk_tree_view_expand_row(treeview, path, TRUE);
6207
6208 if (prefs_common.bold_unread)
6209 summary_set_bold_recursive(summaryview, iter);
6210
6211 /* workaround for last row expand problem */
6212 #if GTK_CHECK_VERSION(2, 8, 0)
6213 gtk_widget_queue_resize(GTK_WIDGET(treeview));
6214 #else
6215 g_object_set(treeview, "fixed-height-mode", FALSE, NULL);
6216 gtk_widget_queue_resize(GTK_WIDGET(treeview));
6217 g_object_set(treeview, "fixed-height-mode", TRUE, NULL);
6218 #endif
6219 }
6220
summary_row_collapsed(GtkTreeView * treeview,GtkTreeIter * iter,GtkTreePath * path,SummaryView * summaryview)6221 static void summary_row_collapsed(GtkTreeView *treeview, GtkTreeIter *iter,
6222 GtkTreePath *path, SummaryView *summaryview)
6223 {
6224 if (prefs_common.bold_unread &&
6225 summary_have_unread_children(summaryview, iter)) {
6226 gtk_tree_store_set(summaryview->store, iter,
6227 S_COL_BOLD, PANGO_WEIGHT_BOLD, -1);
6228 }
6229 }
6230
summary_columns_changed(GtkTreeView * treeview,SummaryView * summaryview)6231 static void summary_columns_changed(GtkTreeView *treeview,
6232 SummaryView *summaryview)
6233 {
6234 summary_get_column_order(summaryview);
6235 }
6236
summary_select_func(GtkTreeSelection * selection,GtkTreeModel * model,GtkTreePath * path,gboolean cur_selected,gpointer data)6237 static gboolean summary_select_func(GtkTreeSelection *selection,
6238 GtkTreeModel *model, GtkTreePath *path,
6239 gboolean cur_selected, gpointer data)
6240 {
6241 SummaryView *summaryview = (SummaryView *)data;
6242
6243 return summaryview->can_toggle_selection &&
6244 !summary_is_read_locked(summaryview);
6245 }
6246
summary_display_msg_idle_func(gpointer data)6247 static gboolean summary_display_msg_idle_func(gpointer data)
6248 {
6249 SummaryView *summaryview = (SummaryView *)data;
6250 GtkTreePath *path;
6251 GtkTreeIter iter;
6252
6253 gdk_threads_enter();
6254 path = gtk_tree_row_reference_get_path(summaryview->selected);
6255 if (path) {
6256 gtk_tree_model_get_iter(GTK_TREE_MODEL(summaryview->store),
6257 &iter, path);
6258 gtk_tree_path_free(path);
6259 summary_display_msg(summaryview, &iter);
6260 summary_mark_displayed_read(summaryview, &iter);
6261 }
6262 gdk_threads_leave();
6263
6264 return FALSE;
6265 }
6266
summary_selection_changed(GtkTreeSelection * selection,SummaryView * summaryview)6267 static void summary_selection_changed(GtkTreeSelection *selection,
6268 SummaryView *summaryview)
6269 {
6270 GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
6271 GtkTreeIter iter;
6272 GtkTreePath *path;
6273 GList *list;
6274 gboolean single_selection = FALSE;
6275
6276 summary_selection_list_free(summaryview);
6277 list = summary_get_selected_rows(summaryview);
6278
6279 summary_status_show(summaryview);
6280
6281 gtk_tree_row_reference_free(summaryview->selected);
6282 if (list) {
6283 if (list->next == NULL)
6284 single_selection = TRUE;
6285 path = (GtkTreePath *)list->data;
6286 gtk_tree_model_get_iter(model, &iter, path);
6287 summaryview->selected =
6288 gtk_tree_row_reference_new(model, path);
6289 } else
6290 summaryview->selected = NULL;
6291
6292 if (!single_selection) {
6293 summaryview->display_msg = FALSE;
6294 #if 0
6295 if (summaryview->displayed && prefs_common.always_show_msg) {
6296 messageview_clear(summaryview->messageview);
6297 gtk_tree_row_reference_free(summaryview->displayed);
6298 summaryview->displayed = NULL;
6299 }
6300 #endif
6301 summary_set_menu_sensitive(summaryview);
6302 main_window_set_toolbar_sensitive(summaryview->mainwin);
6303 return;
6304 }
6305
6306 if (summaryview->display_msg ||
6307 (prefs_common.always_show_msg &&
6308 messageview_is_visible(summaryview->messageview))) {
6309 summaryview->display_msg = FALSE;
6310 if (!gtkut_tree_row_reference_equal(summaryview->displayed,
6311 summaryview->selected)) {
6312 if (summaryview->on_button_press)
6313 g_idle_add(summary_display_msg_idle_func,
6314 summaryview);
6315 else
6316 summary_display_msg(summaryview, &iter);
6317 return;
6318 }
6319 }
6320
6321 summary_set_menu_sensitive(summaryview);
6322 main_window_set_toolbar_sensitive(summaryview->mainwin);
6323 }
6324
summary_col_resized(GtkWidget * widget,GtkAllocation * allocation,SummaryView * summaryview)6325 static void summary_col_resized(GtkWidget *widget, GtkAllocation *allocation,
6326 SummaryView *summaryview)
6327 {
6328 SummaryColumnType type;
6329
6330 for (type = 0; type < N_SUMMARY_VISIBLE_COLS; type++) {
6331 if (summaryview->columns[type]->button == widget) {
6332 prefs_common.summary_col_size[type] = allocation->width;
6333 break;
6334 }
6335 }
6336 }
6337
summary_reply_cb(SummaryView * summaryview,guint action,GtkWidget * widget)6338 static void summary_reply_cb(SummaryView *summaryview, guint action,
6339 GtkWidget *widget)
6340 {
6341 summary_reply(summaryview, (ComposeMode)action);
6342 }
6343
summary_show_all_header_cb(SummaryView * summaryview,guint action,GtkWidget * widget)6344 static void summary_show_all_header_cb(SummaryView *summaryview,
6345 guint action, GtkWidget *widget)
6346 {
6347 summary_display_msg_selected(summaryview, FALSE,
6348 GTK_CHECK_MENU_ITEM(widget)->active);
6349 }
6350
summary_add_address_cb(SummaryView * summaryview,guint action,GtkWidget * widget)6351 static void summary_add_address_cb(SummaryView *summaryview,
6352 guint action, GtkWidget *widget)
6353 {
6354 summary_add_address(summaryview);
6355 }
6356
summary_create_filter_cb(SummaryView * summaryview,guint action,GtkWidget * widget)6357 static void summary_create_filter_cb(SummaryView *summaryview,
6358 guint action, GtkWidget *widget)
6359 {
6360 summary_filter_open(summaryview, (FilterCreateType)action);
6361 }
6362
summary_sort_by_column_click(SummaryView * summaryview,SummaryColumnType type)6363 static void summary_sort_by_column_click(SummaryView *summaryview,
6364 SummaryColumnType type)
6365 {
6366 FolderItem *item = summaryview->folder_item;
6367
6368 if (!item) return;
6369
6370 if (item->sort_key == col_to_sort_key[type])
6371 summary_sort(summaryview, col_to_sort_key[type],
6372 item->sort_type == SORT_ASCENDING
6373 ? SORT_DESCENDING : SORT_ASCENDING);
6374 else
6375 summary_sort(summaryview, col_to_sort_key[type],
6376 SORT_ASCENDING);
6377 }
6378
summary_column_clicked(GtkWidget * button,SummaryView * summaryview)6379 static void summary_column_clicked(GtkWidget *button, SummaryView *summaryview)
6380 {
6381 SummaryColumnType type;
6382
6383 for (type = 0; type < N_SUMMARY_VISIBLE_COLS; type++) {
6384 if (summaryview->columns[type]->button == button) {
6385 summary_sort_by_column_click(summaryview, type);
6386 break;
6387 }
6388 }
6389 }
6390
summary_column_drop_func(GtkTreeView * treeview,GtkTreeViewColumn * column,GtkTreeViewColumn * prev_column,GtkTreeViewColumn * next_column,gpointer data)6391 static gboolean summary_column_drop_func(GtkTreeView *treeview,
6392 GtkTreeViewColumn *column,
6393 GtkTreeViewColumn *prev_column,
6394 GtkTreeViewColumn *next_column,
6395 gpointer data)
6396 {
6397 if (next_column == NULL)
6398 return FALSE;
6399 else
6400 return TRUE;
6401 }
6402
summary_drag_begin(GtkWidget * widget,GdkDragContext * drag_context,SummaryView * summaryview)6403 static void summary_drag_begin(GtkWidget *widget, GdkDragContext *drag_context,
6404 SummaryView *summaryview)
6405 {
6406 if (!summaryview->on_button_press)
6407 g_warning("summary_drag_begin: drag began without button press");
6408 summaryview->on_drag = TRUE;
6409 gtk_drag_set_icon_default(drag_context);
6410 }
6411
summary_drag_end(GtkWidget * widget,GdkDragContext * drag_context,SummaryView * summaryview)6412 static void summary_drag_end(GtkWidget *widget, GdkDragContext *drag_context,
6413 SummaryView *summaryview)
6414 {
6415 if (summaryview->drag_list) {
6416 g_free(summaryview->drag_list);
6417 summaryview->drag_list = NULL;
6418 }
6419 }
6420
summary_drag_data_get(GtkWidget * widget,GdkDragContext * drag_context,GtkSelectionData * selection_data,guint info,guint time,SummaryView * summaryview)6421 static void summary_drag_data_get(GtkWidget *widget,
6422 GdkDragContext *drag_context,
6423 GtkSelectionData *selection_data,
6424 guint info,
6425 guint time,
6426 SummaryView *summaryview)
6427 {
6428 GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
6429 GList *rows, *cur;
6430 gchar *mail_list = NULL;
6431 gchar *file, *filename, *fs_filename, *tmp;
6432 gint suffix = 0;
6433 MsgInfo *msginfo;
6434 GtkTreeIter iter;
6435
6436 if (info == DRAG_TYPE_TEXT) {
6437 gtk_selection_data_set(selection_data, selection_data->target,
6438 8, (guchar *)"drag-from-summary", 17);
6439 return;
6440 }
6441
6442 if (!summaryview->drag_list) {
6443 rows = summary_get_selected_rows(summaryview);
6444
6445 for (cur = rows; cur != NULL; cur = cur->next) {
6446 gtk_tree_model_get_iter(model, &iter,
6447 (GtkTreePath *)cur->data);
6448 gtk_tree_model_get(model, &iter, S_COL_MSG_INFO,
6449 &msginfo, -1);
6450 file = procmsg_get_message_file(msginfo);
6451 if (!file) continue;
6452
6453 if (msginfo->subject && *msginfo->subject != '\0') {
6454 filename = g_strdup(msginfo->subject);
6455 subst_for_filename(filename);
6456 } else
6457 filename = g_strdup(g_basename(file));
6458 fs_filename = conv_filename_from_utf8(filename);
6459
6460 suffix = 0;
6461 do {
6462 if (suffix == 0)
6463 tmp = g_strdup_printf
6464 ("%s%c%s.eml", get_tmp_dir(),
6465 G_DIR_SEPARATOR,
6466 fs_filename);
6467 else
6468 tmp = g_strdup_printf
6469 ("%s%c%s_(%d).eml",
6470 get_tmp_dir(),
6471 G_DIR_SEPARATOR, fs_filename,
6472 suffix);
6473
6474 if (is_file_exist(tmp)) {
6475 suffix++;
6476 g_free(tmp);
6477 } else
6478 break;
6479 } while (1);
6480
6481 if (copy_file(file, tmp, FALSE) < 0) {
6482 g_warning("Can't copy '%s'\n", file);
6483 } else {
6484 gchar *uri;
6485
6486 uri = encode_uri(tmp);
6487
6488 if (!mail_list) {
6489 mail_list = uri;
6490 } else {
6491 gchar *list_tmp;
6492
6493 list_tmp = g_strconcat
6494 (mail_list, "\n", uri, NULL);
6495 g_free(mail_list);
6496 g_free(uri);
6497 mail_list = list_tmp;
6498 }
6499 }
6500
6501 g_free(tmp);
6502 g_free(fs_filename);
6503 g_free(filename);
6504 g_free(file);
6505 }
6506
6507 summaryview->drag_list = mail_list;
6508 }
6509
6510 if (summaryview->drag_list) {
6511 gtk_selection_data_set(selection_data,
6512 selection_data->target, 8,
6513 (guchar *)summaryview->drag_list,
6514 strlen(summaryview->drag_list));
6515 }
6516 }
6517
summary_text_adj_value_changed(GtkAdjustment * adj,SummaryView * summaryview)6518 static void summary_text_adj_value_changed(GtkAdjustment *adj,
6519 SummaryView *summaryview)
6520 {
6521 static gdouble prev_vadj = 0.0;
6522
6523 if (summaryview->displayed && adj->value > prev_vadj &&
6524 prefs_common.always_show_msg)
6525 summary_mark_displayed_read(summaryview, NULL);
6526
6527 prev_vadj = adj->value;
6528 }
6529
6530
6531 /* custom compare functions for sorting */
6532
6533 #define CMP_FUNC_DEF(func_name, val) \
6534 static gint func_name(GtkTreeModel *model, \
6535 GtkTreeIter *a, GtkTreeIter *b, gpointer data) \
6536 { \
6537 MsgInfo *msginfo_a = NULL, *msginfo_b = NULL; \
6538 gint ret; \
6539 \
6540 gtk_tree_model_get(model, a, S_COL_MSG_INFO, &msginfo_a, -1); \
6541 gtk_tree_model_get(model, b, S_COL_MSG_INFO, &msginfo_b, -1); \
6542 \
6543 if (!msginfo_a || !msginfo_b) \
6544 return 0; \
6545 \
6546 ret = (val); \
6547 return (ret != 0) ? ret : \
6548 (msginfo_a->date_t - msginfo_b->date_t); \
6549 }
6550
6551 CMP_FUNC_DEF(summary_cmp_by_mark,
6552 MSG_IS_MARKED(msginfo_a->flags) - MSG_IS_MARKED(msginfo_b->flags))
6553 CMP_FUNC_DEF(summary_cmp_by_unread,
6554 MSG_IS_UNREAD(msginfo_a->flags) - MSG_IS_UNREAD(msginfo_b->flags))
6555 CMP_FUNC_DEF(summary_cmp_by_mime,
6556 MSG_IS_MIME(msginfo_a->flags) - MSG_IS_MIME(msginfo_b->flags))
6557 CMP_FUNC_DEF(summary_cmp_by_label,
6558 MSG_GET_COLORLABEL(msginfo_a->flags) -
6559 MSG_GET_COLORLABEL(msginfo_b->flags))
6560 CMP_FUNC_DEF(summary_cmp_by_size, msginfo_a->size - msginfo_b->size)
6561
6562 #undef CMP_FUNC_DEF
6563 #define CMP_FUNC_DEF(func_name, val) \
6564 static gint func_name(GtkTreeModel *model, \
6565 GtkTreeIter *a, GtkTreeIter *b, gpointer data) \
6566 { \
6567 MsgInfo *msginfo_a = NULL, *msginfo_b = NULL; \
6568 \
6569 gtk_tree_model_get(model, a, S_COL_MSG_INFO, &msginfo_a, -1); \
6570 gtk_tree_model_get(model, b, S_COL_MSG_INFO, &msginfo_b, -1); \
6571 \
6572 if (!msginfo_a || !msginfo_b) \
6573 return 0; \
6574 \
6575 return (val); \
6576 }
6577
6578 CMP_FUNC_DEF(summary_cmp_by_num, msginfo_a->msgnum - msginfo_b->msgnum)
6579 CMP_FUNC_DEF(summary_cmp_by_date, msginfo_a->date_t - msginfo_b->date_t)
6580
6581 #undef CMP_FUNC_DEF
6582
summary_cmp_by_thread_date(GtkTreeModel * model,GtkTreeIter * a,GtkTreeIter * b,gpointer data)6583 static gint summary_cmp_by_thread_date(GtkTreeModel *model,
6584 GtkTreeIter *a, GtkTreeIter *b,
6585 gpointer data)
6586 {
6587 MsgInfo *msginfo_a = NULL, *msginfo_b = NULL;
6588 guint tdate_a, tdate_b;
6589
6590 gtk_tree_model_get(model, a, S_COL_MSG_INFO, &msginfo_a, S_COL_TDATE,
6591 &tdate_a, -1);
6592 gtk_tree_model_get(model, b, S_COL_MSG_INFO, &msginfo_b, S_COL_TDATE,
6593 &tdate_b, -1);
6594
6595 if (!msginfo_a || !msginfo_b)
6596 return 0;
6597
6598 if (tdate_a == 0 && tdate_b == 0)
6599 return msginfo_a->date_t - msginfo_b->date_t;
6600 else
6601 return tdate_a - tdate_b;
6602 }
6603
6604 #define CMP_FUNC_DEF(func_name, var_name) \
6605 static gint func_name(GtkTreeModel *model, \
6606 GtkTreeIter *a, GtkTreeIter *b, gpointer data) \
6607 { \
6608 MsgInfo *msginfo_a = NULL, *msginfo_b = NULL; \
6609 gint ret; \
6610 \
6611 gtk_tree_model_get(model, a, S_COL_MSG_INFO, &msginfo_a, -1); \
6612 gtk_tree_model_get(model, b, S_COL_MSG_INFO, &msginfo_b, -1); \
6613 \
6614 if (!msginfo_a || !msginfo_b) \
6615 return 0; \
6616 \
6617 if (msginfo_a->var_name == NULL) \
6618 return -(msginfo_b->var_name != NULL); \
6619 if (msginfo_b->var_name == NULL) \
6620 return (msginfo_a->var_name != NULL); \
6621 \
6622 ret = g_ascii_strcasecmp \
6623 (msginfo_a->var_name, msginfo_b->var_name); \
6624 \
6625 return (ret != 0) ? ret : \
6626 (msginfo_a->date_t - msginfo_b->date_t); \
6627 }
6628
CMP_FUNC_DEF(summary_cmp_by_from,fromname)6629 CMP_FUNC_DEF(summary_cmp_by_from, fromname)
6630
6631 #undef CMP_FUNC_DEF
6632
6633 static gint summary_cmp_by_to(GtkTreeModel *model,
6634 GtkTreeIter *a, GtkTreeIter *b,
6635 gpointer data)
6636 {
6637 MsgInfo *msginfo_a = NULL, *msginfo_b = NULL;
6638 gchar *to_a = NULL, *to_b = NULL;
6639 gint ret;
6640
6641 gtk_tree_model_get(model, a, S_COL_MSG_INFO, &msginfo_a,
6642 S_COL_TO, &to_a, -1);
6643 gtk_tree_model_get(model, b, S_COL_MSG_INFO, &msginfo_b,
6644 S_COL_TO, &to_b, -1);
6645
6646 if (!msginfo_a || !msginfo_b) {
6647 g_free(to_b);
6648 g_free(to_a);
6649 return 0;
6650 }
6651
6652 ret = g_ascii_strcasecmp(to_a ? to_a : "", to_b ? to_b : "");
6653
6654 g_free(to_b);
6655 g_free(to_a);
6656
6657 return (ret != 0) ? ret :
6658 (msginfo_a->date_t - msginfo_b->date_t);
6659 }
6660
summary_cmp_by_subject(GtkTreeModel * model,GtkTreeIter * a,GtkTreeIter * b,gpointer data)6661 static gint summary_cmp_by_subject(GtkTreeModel *model,
6662 GtkTreeIter *a, GtkTreeIter *b,
6663 gpointer data)
6664 {
6665 MsgInfo *msginfo_a = NULL, *msginfo_b = NULL;
6666 gint ret;
6667
6668 gtk_tree_model_get(model, a, S_COL_MSG_INFO, &msginfo_a, -1);
6669 gtk_tree_model_get(model, b, S_COL_MSG_INFO, &msginfo_b, -1);
6670
6671 if (!msginfo_a || !msginfo_b)
6672 return 0;
6673
6674 if (msginfo_a->subject == NULL)
6675 return -(msginfo_b->subject != NULL);
6676 if (msginfo_b->subject == NULL)
6677 return (msginfo_a->subject != NULL);
6678
6679 ret = subject_compare_for_sort(msginfo_a->subject, msginfo_b->subject);
6680
6681 return (ret != 0) ? ret :
6682 (msginfo_a->date_t - msginfo_b->date_t);
6683 }
6684