7af42011f238634bb724cb4206d664b9757c8c0a
[claws.git] / src / summaryview.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2008 Hiroyuki Yamamoto and the Claws Mail team
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 3 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, see <http://www.gnu.org/licenses/>.
17  * 
18  */
19
20 #include "defs.h"
21
22 #include <glib.h>
23 #include <glib/gi18n.h>
24 #include <gdk/gdkkeysyms.h>
25 #include <gtk/gtk.h>
26
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <ctype.h>
31
32 #include "main.h"
33 #include "menu.h"
34 #include "mbox.h"
35 #include "mainwindow.h"
36 #include "folderview.h"
37 #include "summaryview.h"
38 #include "messageview.h"
39 #include "foldersel.h"
40 #include "procmsg.h"
41 #include "procheader.h"
42 #include "sourcewindow.h"
43 #include "prefs_common.h"
44 #include "prefs_summary_column.h"
45 #include "prefs_summary_open.h"
46 #include "prefs_filtering.h"
47 #include "account.h"
48 #include "compose.h"
49 #include "utils.h"
50 #include "gtkutils.h"
51 #include "stock_pixmap.h"
52 #include "filesel.h"
53 #include "alertpanel.h"
54 #include "inputdialog.h"
55 #include "statusbar.h"
56 #include "folder.h"
57 #include "colorlabel.h"
58 #include "inc.h"
59 #include "imap.h"
60 #include "addressbook.h"
61 #include "addr_compl.h"
62 #include "folder_item_prefs.h"
63 #include "filtering.h"
64 #include "string_match.h"
65 #include "toolbar.h"
66 #include "news.h"
67 #include "hooks.h"
68 #include "description_window.h"
69 #include "folderutils.h"
70 #include "quicksearch.h"
71 #include "partial_download.h"
72 #include "tags.h"
73 #include "timing.h"
74 #include "log.h"
75 #include "edittags.h"
76 #include "manual.h"
77
78 #define SUMMARY_COL_MARK_WIDTH          10
79 #define SUMMARY_COL_STATUS_WIDTH        13
80 #define SUMMARY_COL_LOCKED_WIDTH        13
81 #define SUMMARY_COL_MIME_WIDTH          11
82
83 static int normal_row_height = -1;
84 static GtkStyle *bold_style;
85 static GtkStyle *bold_marked_style;
86 static GtkStyle *bold_deleted_style;
87 static GtkStyle *small_style;
88 static GtkStyle *small_marked_style;
89 static GtkStyle *small_deleted_style;
90
91 static GdkPixmap *markxpm;
92 static GdkBitmap *markxpmmask;
93 static GdkPixmap *deletedxpm;
94 static GdkBitmap *deletedxpmmask;
95 static GdkPixmap *movedxpm;
96 static GdkBitmap *movedxpmmask;
97 static GdkPixmap *copiedxpm;
98 static GdkBitmap *copiedxpmmask;
99
100 static GdkPixmap *newxpm;
101 static GdkBitmap *newxpmmask;
102 static GdkPixmap *unreadxpm;
103 static GdkBitmap *unreadxpmmask;
104 static GdkPixmap *repliedxpm;
105 static GdkBitmap *repliedxpmmask;
106 static GdkPixmap *forwardedxpm;
107 static GdkBitmap *forwardedxpmmask;
108 static GdkPixmap *ignorethreadxpm;
109 static GdkBitmap *ignorethreadxpmmask;
110 static GdkPixmap *watchthreadxpm;
111 static GdkBitmap *watchthreadxpmmask;
112 static GdkPixmap *lockedxpm;
113 static GdkBitmap *lockedxpmmask;
114 static GdkPixmap *spamxpm;
115 static GdkBitmap *spamxpmmask;
116
117 static GdkPixmap *clipxpm;
118 static GdkBitmap *clipxpmmask;
119 static GdkPixmap *keyxpm;
120 static GdkBitmap *keyxpmmask;
121 static GdkPixmap *clipkeyxpm;
122 static GdkBitmap *clipkeyxpmmask;
123 static GdkPixmap *keysignxpm;
124 static GdkBitmap *keysignxpmmask;
125 static GdkPixmap *gpgsignedxpm;
126 static GdkBitmap *gpgsignedxpmmask;
127 static GdkPixmap *clipgpgsignedxpm;
128 static GdkBitmap *clipgpgsignedxpmmask;
129
130 static void summary_free_msginfo_func   (GtkCMCTree             *ctree,
131                                          GtkCMCTreeNode         *node,
132                                          gpointer                data);
133 static void summary_set_marks_func      (GtkCMCTree             *ctree,
134                                          GtkCMCTreeNode         *node,
135                                          gpointer                data);
136
137 void  summary_set_menu_sensitive        (SummaryView            *summaryview);
138 guint summary_get_msgnum                (SummaryView            *summaryview,
139                                          GtkCMCTreeNode         *node);
140
141
142 static void summary_set_hide_read_msgs_menu (SummaryView *summaryview,
143                                              guint action);
144
145 static GtkCMCTreeNode *summary_find_prev_msg
146                                         (SummaryView            *summaryview,
147                                          GtkCMCTreeNode         *current_node);
148 static GtkCMCTreeNode *summary_find_next_msg
149                                         (SummaryView            *summaryview,
150                                          GtkCMCTreeNode         *current_node);
151
152 static GtkCMCTreeNode *summary_find_prev_flagged_msg
153                                         (SummaryView    *summaryview,
154                                          GtkCMCTreeNode *current_node,
155                                          MsgPermFlags    flags,
156                                          gboolean        start_from_prev);
157 static GtkCMCTreeNode *summary_find_next_flagged_msg
158                                         (SummaryView    *summaryview,
159                                          GtkCMCTreeNode *current_node,
160                                          MsgPermFlags    flags,
161                                          gboolean        start_from_next);
162
163 static GtkCMCTreeNode *summary_find_msg_by_msgnum
164                                         (SummaryView            *summaryview,
165                                          guint                   msgnum);
166
167 static void summary_update_status       (SummaryView            *summaryview);
168
169 /* display functions */
170 static void summary_status_show         (SummaryView            *summaryview);
171 static void summary_set_column_titles   (SummaryView            *summaryview);
172 static void summary_set_ctree_from_list (SummaryView            *summaryview,
173                                          GSList                 *mlist);
174 static inline void summary_set_header   (SummaryView            *summaryview,
175                                          gchar                  *text[],
176                                          MsgInfo                *msginfo);
177 static void summary_display_msg         (SummaryView            *summaryview,
178                                          GtkCMCTreeNode         *row);
179 static void summary_display_msg_full    (SummaryView            *summaryview,
180                                          GtkCMCTreeNode         *row,
181                                          gboolean                new_window,
182                                          gboolean                all_headers);
183 static void summary_set_row_marks       (SummaryView            *summaryview,
184                                          GtkCMCTreeNode         *row);
185
186 static gboolean summary_set_row_tag     (SummaryView            *summaryview, 
187                                          GtkCMCTreeNode                 *row, 
188                                          gboolean                refresh,
189                                          gboolean                set, 
190                                          gint                    id);
191 /* message handling */
192 static void summary_mark_row            (SummaryView            *summaryview,
193                                          GtkCMCTreeNode         *row);
194 static void summary_lock_row            (SummaryView            *summaryview,
195                                          GtkCMCTreeNode         *row);
196 static void summary_unlock_row          (SummaryView            *summaryview,
197                                          GtkCMCTreeNode         *row);
198 static void summary_mark_row_as_read    (SummaryView            *summaryview,
199                                          GtkCMCTreeNode         *row);
200 static void summary_mark_row_as_unread  (SummaryView            *summaryview,
201                                          GtkCMCTreeNode         *row);
202 static void summary_delete_row          (SummaryView            *summaryview,
203                                          GtkCMCTreeNode         *row);
204 static void summary_unmark_row          (SummaryView            *summaryview,
205                                          GtkCMCTreeNode         *row);
206 static void summary_move_row_to         (SummaryView            *summaryview,
207                                          GtkCMCTreeNode         *row,
208                                          FolderItem             *to_folder);
209 static void summary_copy_row_to         (SummaryView            *summaryview,
210                                          GtkCMCTreeNode         *row,
211                                          FolderItem             *to_folder);
212
213 static gint summary_execute_move        (SummaryView            *summaryview);
214 static void summary_execute_move_func   (GtkCMCTree             *ctree,
215                                          GtkCMCTreeNode         *node,
216                                          gpointer                data);
217 static void summary_execute_copy        (SummaryView            *summaryview);
218 static void summary_execute_copy_func   (GtkCMCTree             *ctree,
219                                          GtkCMCTreeNode         *node,
220                                          gpointer                data);
221 static void summary_execute_delete      (SummaryView            *summaryview);
222 static void summary_execute_delete_func (GtkCMCTree             *ctree,
223                                          GtkCMCTreeNode         *node,
224                                          gpointer                data);
225
226 static void summary_thread_init         (SummaryView            *summaryview);
227
228 static void summary_unthread_for_exec           (SummaryView    *summaryview);
229 static void summary_unthread_for_exec_func      (GtkCMCTree     *ctree,
230                                                  GtkCMCTreeNode *node,
231                                                  gpointer        data);
232
233 void summary_simplify_subject(SummaryView *summaryview, gchar * rexp,
234                               GSList * mlist);
235
236 static void summary_filter_func         (MsgInfo *msginfo);
237
238 static void summary_colorlabel_menu_item_activate_cb
239                                           (GtkWidget    *widget,
240                                            gpointer      data);
241 static void summary_colorlabel_menu_item_activate_item_cb
242                                           (GtkMenuItem  *label_menu_item,
243                                            gpointer      data);
244 static void summary_colorlabel_menu_create(SummaryView  *summaryview,
245                                            gboolean  refresh);
246 static void summary_tags_menu_item_activate_cb
247                                           (GtkWidget    *widget,
248                                            gpointer      data);
249 static void summary_tags_menu_item_activate_item_cb
250                                           (GtkMenuItem  *label_menu_item,
251                                            gpointer      data);
252 static void summary_tags_menu_create(SummaryView        *summaryview,
253                                            gboolean  refresh);
254
255 static GtkWidget *summary_ctree_create  (SummaryView    *summaryview);
256
257 /* callback functions */
258 static gint summary_toggle_pressed      (GtkWidget              *eventbox,
259                                          GdkEventButton         *event,
260                                          SummaryView            *summaryview);
261 #ifdef GENERIC_UMPC
262 static void summary_toggle_multiple_pressed
263                                         (GtkWidget              *widget,
264                                          SummaryView            *summaryview);
265 #endif
266 static gint summary_folder_eventbox_pressed     
267                                         (GtkWidget              *eventbox,
268                                          GdkEventButton         *event,
269                                          SummaryView            *summaryview);
270 static gboolean summary_button_pressed  (GtkWidget              *ctree,
271                                          GdkEventButton         *event,
272                                          SummaryView            *summaryview);
273 static gboolean summary_button_released (GtkWidget              *ctree,
274                                          GdkEventButton         *event,
275                                          SummaryView            *summaryview);
276 static gboolean summary_key_pressed     (GtkWidget              *ctree,
277                                          GdkEventKey            *event,
278                                          SummaryView            *summaryview);
279 static void summary_tree_expanded       (GtkCMCTree             *ctree,
280                                          GtkCMCTreeNode         *node,
281                                          SummaryView            *summaryview);
282 static void summary_tree_collapsed      (GtkCMCTree             *ctree,
283                                          GtkCMCTreeNode         *node,
284                                          SummaryView            *summaryview);
285 static void summary_selected            (GtkCMCTree             *ctree,
286                                          GtkCMCTreeNode         *row,
287                                          gint                    column,
288                                          SummaryView            *summaryview);
289 static void summary_unselected          (GtkCMCTree             *ctree,
290                                          GtkCMCTreeNode         *row,
291                                          gint                    column,
292                                          SummaryView            *summaryview);
293 static void summary_col_resized         (GtkCMCList             *clist,
294                                          gint                    column,
295                                          gint                    width,
296                                          SummaryView            *summaryview);
297 static void summary_reply_cb            (SummaryView            *summaryview,
298                                          guint                   action,
299                                          GtkWidget              *widget);
300 #ifndef GENERIC_UMPC
301 static void summary_show_all_header_cb  (SummaryView            *summaryview,
302                                          guint                   action,
303                                          GtkWidget              *widget);
304
305 static void summary_add_address_cb      (SummaryView            *summaryview,
306                                          guint                   action,
307                                          GtkWidget              *widget);
308 #endif
309 static void summary_create_filter_cb    (SummaryView            *summaryview,
310                                          guint                   action,
311                                          GtkWidget              *widget);
312 #ifndef GENERIC_UMPC
313 static void summary_create_processing_cb(SummaryView            *summaryview,
314                                          guint                   action,
315                                          GtkWidget              *widget);
316 #endif
317 static void summary_mark_clicked        (GtkWidget              *button,
318                                          SummaryView            *summaryview);
319 static void summary_status_clicked      (GtkWidget              *button,
320                                          SummaryView            *summaryview);
321 static void summary_mime_clicked        (GtkWidget              *button,
322                                          SummaryView            *summaryview);
323 static void summary_num_clicked         (GtkWidget              *button,
324                                          SummaryView            *summaryview);
325 static void summary_score_clicked       (GtkWidget *button,
326                                          SummaryView *summaryview);
327 static void summary_size_clicked        (GtkWidget              *button,
328                                          SummaryView            *summaryview);
329 static void summary_date_clicked        (GtkWidget              *button,
330                                          SummaryView            *summaryview);
331 static void summary_from_clicked        (GtkWidget              *button,
332                                          SummaryView            *summaryview);
333 static void summary_to_clicked          (GtkWidget              *button,
334                                          SummaryView            *summaryview);
335 static void summary_subject_clicked     (GtkWidget              *button,
336                                          SummaryView            *summaryview);
337 static void summary_score_clicked       (GtkWidget              *button,
338                                          SummaryView            *summaryview);
339 static void summary_locked_clicked      (GtkWidget              *button,
340                                          SummaryView            *summaryview);
341 static void summary_tags_clicked        (GtkWidget              *button,
342                                          SummaryView            *summaryview);
343
344 static void summary_start_drag          (GtkWidget        *widget, 
345                                          int button,
346                                          GdkEvent *event,
347                                          SummaryView      *summaryview);
348 static void summary_drag_data_get       (GtkWidget        *widget,
349                                          GdkDragContext   *drag_context,
350                                          GtkSelectionData *selection_data,
351                                          guint             info,
352                                          guint             time,
353                                          SummaryView      *summaryview);
354 static void summary_drag_data_received(GtkWidget        *widget,
355                                         GdkDragContext   *drag_context,
356                                         gint              x,
357                                         gint              y,
358                                         GtkSelectionData *data,
359                                         guint             info,
360                                         guint             time,
361                                         SummaryView       *summaryview);
362 static gboolean summary_drag_motion_cb(GtkWidget      *widget,
363                                           GdkDragContext *context,
364                                           gint            x,
365                                           gint            y,
366                                           guint           time,
367                                           SummaryView    *summaryview);
368 static void summary_drag_end(GtkWidget *widget,
369                                           GdkDragContext *drag_context,
370                                           SummaryView    *summaryview);
371 /* custom compare functions for sorting */
372
373 static gint summary_cmp_by_mark         (GtkCMCList             *clist,
374                                          gconstpointer           ptr1,
375                                          gconstpointer           ptr2);
376 static gint summary_cmp_by_status       (GtkCMCList             *clist,
377                                          gconstpointer           ptr1,
378                                          gconstpointer           ptr2);
379 static gint summary_cmp_by_mime         (GtkCMCList             *clist,
380                                          gconstpointer           ptr1,
381                                          gconstpointer           ptr2);
382 static gint summary_cmp_by_num          (GtkCMCList             *clist,
383                                          gconstpointer           ptr1,
384                                          gconstpointer           ptr2);
385 static gint summary_cmp_by_size         (GtkCMCList             *clist,
386                                          gconstpointer           ptr1,
387                                          gconstpointer           ptr2);
388 static gint summary_cmp_by_date         (GtkCMCList             *clist,
389                                          gconstpointer           ptr1,
390                                          gconstpointer           ptr2);
391 static gint summary_cmp_by_thread_date  (GtkCMCList             *clist,
392                                          gconstpointer           ptr1,
393                                          gconstpointer           ptr2);
394 static gint summary_cmp_by_from         (GtkCMCList             *clist,
395                                          gconstpointer           ptr1,
396                                          gconstpointer           ptr2);
397 static gint summary_cmp_by_simplified_subject
398                                         (GtkCMCList             *clist, 
399                                          gconstpointer           ptr1, 
400                                          gconstpointer           ptr2);
401 static gint summary_cmp_by_score        (GtkCMCList             *clist,
402                                          gconstpointer           ptr1,
403                                          gconstpointer           ptr2);
404 static gint summary_cmp_by_label        (GtkCMCList             *clist,
405                                          gconstpointer           ptr1,
406                                          gconstpointer           ptr2);
407 static gint summary_cmp_by_to           (GtkCMCList             *clist,
408                                          gconstpointer           ptr1,
409                                          gconstpointer           ptr2);
410 static gint summary_cmp_by_subject      (GtkCMCList             *clist,
411                                          gconstpointer           ptr1,
412                                          gconstpointer           ptr2);
413 static gint summary_cmp_by_locked       (GtkCMCList             *clist,
414                                          gconstpointer           ptr1, 
415                                          gconstpointer           ptr2);
416 static gint summary_cmp_by_tags         (GtkCMCList             *clist,
417                                          gconstpointer           ptr1, 
418                                          gconstpointer           ptr2);
419
420 static void quicksearch_execute_cb      (QuickSearch    *quicksearch,
421                                          gpointer        data);
422 static void tog_searchbar_cb            (GtkWidget      *w,
423                                          gpointer        data);
424
425 static void summary_find_answers        (SummaryView    *summaryview, 
426                                          MsgInfo        *msg);
427
428 static gboolean summary_update_msg      (gpointer source, gpointer data);
429 static gboolean summary_update_folder_item_hook(gpointer source, gpointer data);
430 static gboolean summary_update_folder_hook(gpointer source, gpointer data);
431 static void summary_set_colorlabel_color (GtkCMCTree            *ctree,
432                                    GtkCMCTreeNode               *node,
433                                    guint                 labelcolor);
434 static void summary_thread_build(SummaryView *summaryview);
435
436 GtkTargetEntry summary_drag_types[2] =
437 {
438         {"text/uri-list", 0, TARGET_MAIL_URI_LIST},
439         {"claws-mail/internal", GTK_TARGET_SAME_APP, TARGET_DUMMY}
440 };
441
442 #define DO_ACTION(name, act) {                                          \
443         if(!strcmp(name, a_name)) {                                     \
444                 act;                                                    \
445         }                                                               \
446 }
447
448 static void summary_menu_cb(GtkAction *action, gpointer data)
449 {
450         SummaryView *summaryview = (SummaryView *)data;
451         const gchar *a_name = gtk_action_get_name(action);
452         
453         DO_ACTION("SummaryViewPopup/Reply", summary_reply_cb(summaryview, COMPOSE_REPLY, NULL));
454 #ifndef GENERIC_UMPC
455         DO_ACTION("SummaryViewPopup/ReplyTo/All", summary_reply_cb(summaryview, COMPOSE_REPLY_TO_ALL, NULL));
456         DO_ACTION("SummaryViewPopup/ReplyTo/Sender", summary_reply_cb(summaryview, COMPOSE_REPLY_TO_SENDER, NULL));
457         DO_ACTION("SummaryViewPopup/ReplyTo/MailingList", summary_reply_cb(summaryview, COMPOSE_REPLY_TO_LIST, NULL));
458 #endif
459         DO_ACTION("SummaryViewPopup/Forward", summary_reply_cb(summaryview, COMPOSE_FORWARD_INLINE, NULL));
460 #ifndef GENERIC_UMPC
461         DO_ACTION("SummaryViewPopup/ForwardAsAtt", summary_reply_cb(summaryview, COMPOSE_FORWARD_AS_ATTACH, NULL));
462         DO_ACTION("SummaryViewPopup/Redirect", summary_reply_cb(summaryview, COMPOSE_REDIRECT, NULL));
463 #endif
464         DO_ACTION("SummaryViewPopup/Move", summary_move_to(summaryview));
465         DO_ACTION("SummaryViewPopup/Copy", summary_copy_to(summaryview));
466         DO_ACTION("SummaryViewPopup/Trash", summary_delete_trash(summaryview));
467 #ifndef GENERIC_UMPC
468         DO_ACTION("SummaryViewPopup/Delete", summary_delete(summaryview));
469 #endif
470         DO_ACTION("SummaryViewPopup/Mark/Mark", summary_mark(summaryview));
471         DO_ACTION("SummaryViewPopup/Mark/Unmark", summary_unmark(summaryview));
472         DO_ACTION("SummaryViewPopup/Mark/MarkUnread", summary_mark_as_unread(summaryview));
473         DO_ACTION("SummaryViewPopup/Mark/MarkRead", summary_mark_as_read(summaryview));
474         DO_ACTION("SummaryViewPopup/Mark/MarkAllRead", summary_mark_all_read(summaryview));
475         DO_ACTION("SummaryViewPopup/Mark/IgnoreThread", summary_ignore_thread(summaryview));
476         DO_ACTION("SummaryViewPopup/Mark/UnignoreThread", summary_unignore_thread(summaryview));
477         DO_ACTION("SummaryViewPopup/Mark/WatchThread", summary_watch_thread(summaryview));
478         DO_ACTION("SummaryViewPopup/Mark/UnwatchThread", summary_unwatch_thread(summaryview));
479         DO_ACTION("SummaryViewPopup/Mark/MarkSpam", summary_mark_as_spam(summaryview, 1, NULL));
480         DO_ACTION("SummaryViewPopup/Mark/MarkHam", summary_mark_as_spam(summaryview, 0, NULL));
481         DO_ACTION("SummaryViewPopup/Mark/Lock", summary_msgs_lock(summaryview));
482         DO_ACTION("SummaryViewPopup/Mark/Unlock", summary_msgs_unlock(summaryview));
483 #ifndef GENERIC_UMPC
484         DO_ACTION("SummaryViewPopup/AddSenderToAB", summary_add_address_cb(summaryview, 0, 0)); 
485 #endif
486         DO_ACTION("SummaryViewPopup/CreateFilterRule/Automatically", summary_create_filter_cb(summaryview, FILTER_BY_AUTO, NULL)); 
487         DO_ACTION("SummaryViewPopup/CreateFilterRule/ByFrom", summary_create_filter_cb(summaryview, FILTER_BY_FROM, NULL)); 
488         DO_ACTION("SummaryViewPopup/CreateFilterRule/ByTo", summary_create_filter_cb(summaryview, FILTER_BY_TO, NULL)); 
489         DO_ACTION("SummaryViewPopup/CreateFilterRule/BySubject", summary_create_filter_cb(summaryview, FILTER_BY_SUBJECT, NULL)); 
490 #ifndef GENERIC_UMPC
491         DO_ACTION("SummaryViewPopup/CreateProcessingRule/Automatically", summary_create_processing_cb(summaryview, FILTER_BY_AUTO, NULL)); 
492         DO_ACTION("SummaryViewPopup/CreateProcessingRule/ByFrom", summary_create_processing_cb(summaryview, FILTER_BY_FROM, NULL)); 
493         DO_ACTION("SummaryViewPopup/CreateProcessingRule/ByTo", summary_create_processing_cb(summaryview, FILTER_BY_TO, NULL)); 
494         DO_ACTION("SummaryViewPopup/CreateProcessingRule/BySubject", summary_create_processing_cb(summaryview, FILTER_BY_SUBJECT, NULL)); 
495 #endif
496         DO_ACTION("SummaryViewPopup/View/OpenInNewWindow", summary_open_msg(summaryview)); 
497         DO_ACTION("SummaryViewPopup/View/MessageSource", summary_view_source(summaryview)); 
498         DO_ACTION("SummaryViewPopup/SaveAs", summary_save_as(summaryview)); 
499 #ifndef GENERIC_UMPC
500         DO_ACTION("SummaryViewPopup/Print", summary_print(summaryview)); 
501 #endif
502 #ifndef GENERIC_UMPC
503         DO_ACTION("SummaryViewPopup/View/AllHeaders", summary_show_all_header_cb(summaryview, 0, 0)); 
504 #endif
505 }
506
507 static GtkActionEntry summary_popup_entries[] =
508 {
509         {"SummaryViewPopup",                            NULL, "SummaryViewPopup" },
510         {"SummaryViewPopup/Reply",                      NULL, N_("_Reply"), "<control>R", NULL, G_CALLBACK(summary_menu_cb) },
511 #ifndef GENERIC_UMPC
512         {"SummaryViewPopup/ReplyTo",                    NULL, "Repl_y to" },
513         {"SummaryViewPopup/ReplyTo/All",                NULL, N_("_all"), "<shift><control>R", NULL, G_CALLBACK(summary_menu_cb) },
514         {"SummaryViewPopup/ReplyTo/Sender",             NULL, N_("_sender"), NULL, NULL, G_CALLBACK(summary_menu_cb) },
515         {"SummaryViewPopup/ReplyTo/MailingList",        NULL, N_("_mailing list"), "<control>L", NULL, G_CALLBACK(summary_menu_cb) },
516         {"SummaryViewPopup/---",                        NULL, "---", NULL, NULL, NULL },
517 #endif
518
519         {"SummaryViewPopup/Forward",                    NULL, N_("_Forward"), "<control><alt>F", NULL, G_CALLBACK(summary_menu_cb) }, /* inline */
520 #ifndef GENERIC_UMPC
521         {"SummaryViewPopup/ForwardAsAtt",               NULL, N_("For_ward as attachment"), NULL, NULL, G_CALLBACK(summary_menu_cb) }, /* as attach */
522         {"SummaryViewPopup/Redirect",                   NULL, N_("Redirect"), NULL, NULL, G_CALLBACK(summary_menu_cb) }, 
523 #endif
524         /* separation */
525         {"SummaryViewPopup/Move",                       NULL, N_("M_ove..."), "<control>O", NULL, G_CALLBACK(summary_menu_cb) }, 
526         {"SummaryViewPopup/Copy",                       NULL, N_("_Copy..."), "<shift><control>O", NULL, G_CALLBACK(summary_menu_cb) }, 
527         {"SummaryViewPopup/Trash",                      NULL, N_("Move to _trash"), "<control>D", NULL, G_CALLBACK(summary_menu_cb) }, 
528 #ifndef GENERIC_UMPC
529         {"SummaryViewPopup/Delete",                     NULL, N_("_Delete..."), NULL, NULL, G_CALLBACK(summary_menu_cb) },
530 #endif
531         /* separation */
532         {"SummaryViewPopup/Mark",                       NULL, "_Mark" },
533         {"SummaryViewPopup/Mark/Mark",                  NULL, N_("_Mark"), NULL, NULL, G_CALLBACK(summary_menu_cb) },
534         {"SummaryViewPopup/Mark/Unmark",                NULL, N_("_Unmark"), NULL, NULL, G_CALLBACK(summary_menu_cb) },
535         {"SummaryViewPopup/Mark/---",                   NULL, "---", NULL, NULL, NULL },
536
537         {"SummaryViewPopup/Mark/MarkUnread",            NULL, N_("Mark as unr_ead"), NULL, NULL, G_CALLBACK(summary_menu_cb) },
538         {"SummaryViewPopup/Mark/MarkRead",              NULL, N_("Mark as rea_d"), NULL, NULL, G_CALLBACK(summary_menu_cb) },
539         {"SummaryViewPopup/Mark/MarkAllRead",           NULL, N_("Mark all read"), NULL, NULL, G_CALLBACK(summary_menu_cb) },
540         {"SummaryViewPopup/Mark/IgnoreThread",          NULL, N_("Ignore thread"), NULL, NULL, G_CALLBACK(summary_menu_cb) },
541         {"SummaryViewPopup/Mark/UnignoreThread",        NULL, N_("Unignore thread"), NULL, NULL, G_CALLBACK(summary_menu_cb) },
542         {"SummaryViewPopup/Mark/WatchThread",           NULL, N_("Watch thread"), NULL, NULL, G_CALLBACK(summary_menu_cb) },
543         {"SummaryViewPopup/Mark/UnwatchThread",         NULL, N_("Unwatch thread"), NULL, NULL, G_CALLBACK(summary_menu_cb) },
544         /* separation */
545
546         {"SummaryViewPopup/Mark/MarkSpam",              NULL, N_("Mark as spam"), NULL, NULL, G_CALLBACK(summary_menu_cb) },
547         {"SummaryViewPopup/Mark/MarkHam",               NULL, N_("Mark as ham"), NULL, NULL, G_CALLBACK(summary_menu_cb) },
548         /* separation */
549
550         {"SummaryViewPopup/Mark/Lock",                  NULL, N_("Lock"), NULL, NULL, G_CALLBACK(summary_menu_cb) },
551         {"SummaryViewPopup/Mark/Unlock",                NULL, N_("Unlock"), NULL, NULL, G_CALLBACK(summary_menu_cb) },
552
553         {"SummaryViewPopup/ColorLabel",                 NULL, "Color la_bel" },
554         {"SummaryViewPopup/Tags",                       NULL, "Ta_gs" },
555
556         /* separation */
557 #ifndef GENERIC_UMPC
558         {"SummaryViewPopup/AddSenderToAB",                      NULL, N_("Add sender to address boo_k"), NULL, NULL, G_CALLBACK(summary_menu_cb) }, 
559 #endif
560         {"SummaryViewPopup/CreateFilterRule",                   NULL, N_("Create f_ilter rule") },
561         {"SummaryViewPopup/CreateFilterRule/Automatically",     NULL, N_("_Automatically"), NULL, NULL, G_CALLBACK(summary_menu_cb) }, 
562         {"SummaryViewPopup/CreateFilterRule/ByFrom",            NULL, N_("By _From"), NULL, NULL, G_CALLBACK(summary_menu_cb) }, 
563         {"SummaryViewPopup/CreateFilterRule/ByTo",              NULL, N_("By _To"), NULL, NULL, G_CALLBACK(summary_menu_cb) }, 
564         {"SummaryViewPopup/CreateFilterRule/BySubject",         NULL, N_("By _Subject"), NULL, NULL, G_CALLBACK(summary_menu_cb) }, 
565
566 #ifndef GENERIC_UMPC
567         {"SummaryViewPopup/CreateProcessingRule",                       NULL, N_("Create processing rule") },
568         {"SummaryViewPopup/CreateProcessingRule/Automatically", NULL, N_("_Automatically"), NULL, NULL, G_CALLBACK(summary_menu_cb) }, 
569         {"SummaryViewPopup/CreateProcessingRule/ByFrom",                NULL, N_("By _From"), NULL, NULL, G_CALLBACK(summary_menu_cb) }, 
570         {"SummaryViewPopup/CreateProcessingRule/ByTo",          NULL, N_("By _To"), NULL, NULL, G_CALLBACK(summary_menu_cb) }, 
571         {"SummaryViewPopup/CreateProcessingRule/BySubject",             NULL, N_("By _Subject"), NULL, NULL, G_CALLBACK(summary_menu_cb) }, 
572
573 #endif
574         /* separation */
575         {"SummaryViewPopup/View",                       NULL, "_View" },
576         {"SummaryViewPopup/View/OpenInNewWindow",       NULL, N_("Open in new _window"), "<control><alt>N", NULL, G_CALLBACK(summary_menu_cb) }, 
577         {"SummaryViewPopup/View/MessageSource",         NULL, N_("Message _source"), "<control>U", NULL, G_CALLBACK(summary_menu_cb) }, 
578         /* separation */
579         {"SummaryViewPopup/SaveAs",                     NULL, N_("_Save as..."), "<control>S", NULL, G_CALLBACK(summary_menu_cb) }, 
580 #ifndef GENERIC_UMPC
581         {"SummaryViewPopup/Print",                      NULL, N_("Print..."), "<control>P", NULL, G_CALLBACK(summary_menu_cb) }, 
582 #endif
583 };  /* see also list in menu_connect_identical_items() in menu.c if this changes */
584
585 #ifndef GENERIC_UMPC
586 static GtkToggleActionEntry summary_popup_toggleentries[] =
587 {
588         {"SummaryViewPopup/View/AllHeaders",            NULL, N_("All _headers"), "<control>H", NULL, G_CALLBACK(summary_menu_cb) }, 
589 };
590 #endif
591
592 static const gchar *const col_label[N_SUMMARY_COLS] = {
593         "",             /* S_COL_MARK    */
594         N_("S"),        /* S_COL_STATUS  */
595         "",             /* S_COL_MIME    */
596         N_("Subject"),  /* S_COL_SUBJECT */
597         N_("From"),     /* S_COL_FROM    */
598         N_("To"),       /* S_COL_TO      */
599         N_("Date"),     /* S_COL_DATE    */
600         N_("Size"),     /* S_COL_SIZE    */
601         N_("#"),        /* S_COL_NUMBER  */
602         N_("Score"),    /* S_COL_SCORE   */
603         "",             /* S_COL_LOCKED  */
604         N_("Tags"),     /* S_COL_TAGS    */
605 };
606
607 void summary_freeze(SummaryView *summaryview)
608 {
609         if (summaryview)
610                 gtk_cmclist_freeze(GTK_CMCLIST(summaryview->ctree));
611 }
612
613 void summary_thaw(SummaryView *summaryview)
614 {
615         if (summaryview)
616                 gtk_cmclist_thaw(GTK_CMCLIST(summaryview->ctree));
617 }
618
619 void summary_grab_focus(SummaryView *summaryview)
620 {
621         if (summaryview)
622                 gtk_widget_grab_focus(summaryview->ctree);
623 }
624
625 GtkWidget *summary_get_main_widget(SummaryView *summaryview)
626 {
627         if (summaryview)
628                 return summaryview->ctree;
629         else
630                 return NULL;
631 }
632
633 #define START_LONG_OPERATION(summaryview,force_freeze) {        \
634         summary_lock(summaryview);                              \
635         main_window_cursor_wait(summaryview->mainwin);          \
636         if (force_freeze || sc_g_list_bigger(GTK_CMCLIST(summaryview->ctree)->selection, 1)) {\
637                 froze = TRUE;                                           \
638                 summary_freeze(summaryview);    \
639         }                                                       \
640         folder_item_update_freeze();                            \
641         inc_lock();                                             \
642         hooks_unregister_hook(MSGINFO_UPDATE_HOOKLIST,          \
643                       summaryview->msginfo_update_callback_id); \
644 }
645 #define END_LONG_OPERATION(summaryview) {                       \
646         inc_unlock();                                           \
647         folder_item_update_thaw();                              \
648         if (froze)                                              \
649                 summary_thaw(summaryview);      \
650         main_window_cursor_normal(summaryview->mainwin);        \
651         summary_unlock(summaryview);                            \
652         summaryview->msginfo_update_callback_id =               \
653                 hooks_register_hook(MSGINFO_UPDATE_HOOKLIST,    \
654                 summary_update_msg, (gpointer) summaryview);    \
655 }
656
657 SummaryView *summary_create(void)
658 {
659         SummaryView *summaryview;
660         GtkWidget *vbox;
661         GtkWidget *scrolledwin;
662         GtkWidget *ctree;
663         GtkWidget *hbox;
664         GtkWidget *hbox_l;
665         GtkWidget *stat_box;
666         GtkWidget *stat_box2;
667         GtkWidget *stat_vbox;
668         GtkWidget *statlabel_folder;
669         GtkWidget *statlabel_select;
670         GtkWidget *statlabel_msgs;
671         GtkWidget *hbox_spc;
672         GtkWidget *toggle_eventbox;
673 #ifdef GENERIC_UMPC
674         GtkWidget *multiple_sel_togbtn;
675 #endif
676         GtkWidget *toggle_arrow;
677         GtkWidget *toggle_search;
678         QuickSearch *quicksearch;
679         CLAWS_TIP_DECL();
680
681         debug_print("Creating summary view...\n");
682         summaryview = g_new0(SummaryView, 1);
683
684 #if !(GTK_CHECK_VERSION(2,12,0))
685         summaryview->tooltips = tips;
686 #endif
687 #define SUMMARY_VBOX_SPACING 3
688         vbox = gtk_vbox_new(FALSE, SUMMARY_VBOX_SPACING);
689         
690         /* create status label */
691         hbox = gtk_hbox_new(FALSE, 0);
692         gtk_widget_show(hbox);
693
694         stat_vbox = gtk_vbox_new(FALSE, 0);
695         gtk_widget_show(stat_vbox);
696
697         stat_box = gtk_hbox_new(FALSE, 0);
698         gtk_widget_show(stat_box);
699         
700         stat_box2 = gtk_hbox_new(FALSE, 0);
701         gtk_widget_show(stat_box2);
702         
703         toggle_search = gtk_toggle_button_new();
704         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_search),
705                                      prefs_common.show_searchbar);
706         gtk_widget_show(toggle_search);
707
708         CLAWS_SET_TIP(toggle_search, _("Toggle quick search bar"));
709         
710         gtk_box_pack_start(GTK_BOX(hbox), toggle_search, FALSE, FALSE, 2);      
711
712         gtk_box_pack_start(GTK_BOX(hbox), stat_vbox, TRUE, TRUE, 0);    
713         gtk_box_pack_start(GTK_BOX(stat_vbox), stat_box, TRUE, TRUE, 0);        
714         gtk_box_pack_start(GTK_BOX(stat_vbox), stat_box2, TRUE, TRUE, 0);       
715
716         hbox_l = gtk_hbox_new(FALSE, 0);
717         gtk_widget_show(hbox_l);
718         gtk_box_pack_start(GTK_BOX(stat_box), hbox_l, TRUE, TRUE, 0);
719  
720         statlabel_folder = gtk_label_new("");
721         gtk_widget_show(statlabel_folder);
722         gtk_box_pack_start(GTK_BOX(hbox_l), statlabel_folder, FALSE, FALSE, 2);
723         statlabel_select = gtk_label_new("");
724         gtk_widget_show(statlabel_select);
725         gtk_box_pack_start(GTK_BOX(hbox_l), statlabel_select, FALSE, FALSE, 12);
726  
727         /* toggle view button */
728         toggle_eventbox = gtk_event_box_new();
729         gtk_widget_show(toggle_eventbox);
730         
731         gtk_box_pack_end(GTK_BOX(hbox), toggle_eventbox, FALSE, FALSE, 4);
732
733         toggle_arrow = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_OUT);
734         gtk_widget_show(toggle_arrow);
735         gtk_container_add(GTK_CONTAINER(toggle_eventbox), toggle_arrow);
736         g_signal_connect(G_OBJECT(toggle_eventbox), "button_press_event",
737                          G_CALLBACK(summary_toggle_pressed),
738                          summaryview);
739
740 #ifdef GENERIC_UMPC
741         multiple_sel_togbtn = gtk_toggle_button_new();
742         gtk_widget_show(multiple_sel_togbtn);
743         gtk_box_pack_end(GTK_BOX(hbox), multiple_sel_togbtn, FALSE, FALSE, 4);
744         CLAWS_SET_TIP(multiple_sel_togbtn,
745                              _("Toggle multiple selection"));
746         g_signal_connect(G_OBJECT(multiple_sel_togbtn), "toggled",
747                          G_CALLBACK(summary_toggle_multiple_pressed),
748                          summaryview);
749 #endif
750         
751         statlabel_msgs = gtk_label_new("");
752         gtk_widget_show(statlabel_msgs);
753         gtk_box_pack_end(GTK_BOX(stat_box), statlabel_msgs, FALSE, FALSE, 4);
754
755         hbox_spc = gtk_hbox_new(FALSE, 0);
756         gtk_widget_show(hbox_spc);
757         gtk_box_pack_end(GTK_BOX(hbox), hbox_spc, FALSE, FALSE, 6);
758
759         scrolledwin = gtk_scrolled_window_new(NULL, NULL);
760         gtk_widget_show(scrolledwin);
761         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
762                                        GTK_POLICY_AUTOMATIC,
763                                        GTK_POLICY_AUTOMATIC);
764         summaryview->mainwidget_book = gtk_notebook_new();
765         gtk_notebook_set_show_tabs(GTK_NOTEBOOK(summaryview->mainwidget_book), FALSE);
766         gtk_notebook_set_show_border(GTK_NOTEBOOK(summaryview->mainwidget_book), FALSE);
767 #ifndef GENERIC_UMPC
768         gtk_container_add(GTK_CONTAINER(summaryview->mainwidget_book),
769                 scrolledwin);
770         gtk_box_pack_start(GTK_BOX(vbox), summaryview->mainwidget_book, TRUE, TRUE, 0);
771 #endif
772         gtk_widget_set_size_request(vbox,
773                              prefs_common.summaryview_width,
774                              prefs_common.summaryview_height);
775
776         ctree = summary_ctree_create(summaryview);
777         gtk_widget_show(ctree);
778
779         gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
780                                             GTK_CMCLIST(ctree)->hadjustment);
781         gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
782                                             GTK_CMCLIST(ctree)->vadjustment);
783         gtk_container_add(GTK_CONTAINER(scrolledwin), ctree);
784
785         /* status label */
786         gtk_widget_show_all(stat_vbox);
787         gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
788
789         /* quick search */
790         quicksearch = quicksearch_new();
791         gtk_box_pack_start(GTK_BOX(vbox), quicksearch_get_widget(quicksearch), FALSE, FALSE, 0);
792
793 #ifdef GENERIC_UMPC
794         gtk_container_add(GTK_CONTAINER(summaryview->mainwidget_book),
795                 scrolledwin);
796         gtk_box_pack_start(GTK_BOX(vbox), summaryview->mainwidget_book, TRUE, TRUE, 0);
797 #endif
798         quicksearch_set_execute_callback(quicksearch, quicksearch_execute_cb, summaryview);
799
800         g_signal_connect (G_OBJECT(toggle_search), "toggled",
801                           G_CALLBACK(tog_searchbar_cb), summaryview);
802
803         /* create popup menu */
804         summaryview->action_group = cm_menu_create_action_group("SummaryViewPopup", summary_popup_entries,
805                         G_N_ELEMENTS(summary_popup_entries), (gpointer)summaryview);
806 #ifndef GENERIC_UMPC
807         gtk_action_group_add_toggle_actions(summaryview->action_group, summary_popup_toggleentries,
808                         G_N_ELEMENTS(summary_popup_toggleentries), (gpointer)summaryview);
809 #endif
810
811         MENUITEM_ADDUI("/Menus", "SummaryViewPopup", "SummaryViewPopup", GTK_UI_MANAGER_MENU)
812         MENUITEM_ADDUI("/Menus/SummaryViewPopup", "Reply", "SummaryViewPopup/Reply", GTK_UI_MANAGER_MENUITEM)
813 #ifndef GENERIC_UMPC
814         MENUITEM_ADDUI("/Menus/SummaryViewPopup", "ReplyTo", "SummaryViewPopup/ReplyTo", GTK_UI_MANAGER_MENU)
815         MENUITEM_ADDUI("/Menus/SummaryViewPopup", "Separator1", "SummaryViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
816 #endif
817         MENUITEM_ADDUI("/Menus/SummaryViewPopup", "Forward", "SummaryViewPopup/Forward", GTK_UI_MANAGER_MENUITEM)
818 #ifndef GENERIC_UMPC
819         MENUITEM_ADDUI("/Menus/SummaryViewPopup", "ForwardAsAtt", "SummaryViewPopup/ForwardAsAtt", GTK_UI_MANAGER_MENUITEM)
820         MENUITEM_ADDUI("/Menus/SummaryViewPopup", "Redirect", "SummaryViewPopup/Redirect", GTK_UI_MANAGER_MENUITEM)
821 #endif
822         MENUITEM_ADDUI("/Menus/SummaryViewPopup", "Separator2", "SummaryViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
823         MENUITEM_ADDUI("/Menus/SummaryViewPopup", "Move", "SummaryViewPopup/Move", GTK_UI_MANAGER_MENUITEM)
824         MENUITEM_ADDUI("/Menus/SummaryViewPopup", "Copy", "SummaryViewPopup/Copy", GTK_UI_MANAGER_MENUITEM)
825         MENUITEM_ADDUI("/Menus/SummaryViewPopup", "Trash", "SummaryViewPopup/Trash", GTK_UI_MANAGER_MENUITEM)
826 #ifndef GENERIC_UMPC
827         MENUITEM_ADDUI("/Menus/SummaryViewPopup", "Delete", "SummaryViewPopup/Delete", GTK_UI_MANAGER_MENUITEM)
828 #endif
829         MENUITEM_ADDUI("/Menus/SummaryViewPopup", "Separator3", "SummaryViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
830         MENUITEM_ADDUI("/Menus/SummaryViewPopup", "Mark", "SummaryViewPopup/Mark", GTK_UI_MANAGER_MENU)
831         MENUITEM_ADDUI("/Menus/SummaryViewPopup", "ColorLabel", "SummaryViewPopup/ColorLabel", GTK_UI_MANAGER_MENU)
832         MENUITEM_ADDUI("/Menus/SummaryViewPopup", "Tags", "SummaryViewPopup/Tags", GTK_UI_MANAGER_MENU)
833
834         MENUITEM_ADDUI("/Menus/SummaryViewPopup", "Separator4", "SummaryViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
835 #ifndef GENERIC_UMPC
836         MENUITEM_ADDUI("/Menus/SummaryViewPopup", "AddSenderToAB", "SummaryViewPopup/AddSenderToAB", GTK_UI_MANAGER_MENUITEM)
837 #endif
838         MENUITEM_ADDUI("/Menus/SummaryViewPopup", "CreateFilterRule", "SummaryViewPopup/CreateFilterRule", GTK_UI_MANAGER_MENU)
839 #ifndef GENERIC_UMPC
840         MENUITEM_ADDUI("/Menus/SummaryViewPopup", "CreateProcessingRule", "SummaryViewPopup/CreateProcessingRule", GTK_UI_MANAGER_MENU)
841 #endif
842         MENUITEM_ADDUI("/Menus/SummaryViewPopup", "Separator5", "SummaryViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
843         MENUITEM_ADDUI("/Menus/SummaryViewPopup", "View", "SummaryViewPopup/View", GTK_UI_MANAGER_MENU)
844         MENUITEM_ADDUI("/Menus/SummaryViewPopup", "SaveAs", "SummaryViewPopup/SaveAs", GTK_UI_MANAGER_MENUITEM)
845 #ifndef GENERIC_UMPC
846         MENUITEM_ADDUI("/Menus/SummaryViewPopup", "Print", "SummaryViewPopup/Print", GTK_UI_MANAGER_MENUITEM)
847 #endif
848         /* last separator, for plugins */
849         MENUITEM_ADDUI("/Menus/SummaryViewPopup", "Separator6", "SummaryViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
850
851         /* submenus - replyto */
852         MENUITEM_ADDUI("/Menus/SummaryViewPopup/ReplyTo", "All", "SummaryViewPopup/ReplyTo/All", GTK_UI_MANAGER_MENUITEM)
853         MENUITEM_ADDUI("/Menus/SummaryViewPopup/ReplyTo", "Sender", "SummaryViewPopup/ReplyTo/Sender", GTK_UI_MANAGER_MENUITEM)
854         MENUITEM_ADDUI("/Menus/SummaryViewPopup/ReplyTo", "MailingList", "SummaryViewPopup/ReplyTo/MailingList", GTK_UI_MANAGER_MENUITEM)
855
856         /* submenus - mark */
857         MENUITEM_ADDUI("/Menus/SummaryViewPopup/Mark", "Mark", "SummaryViewPopup/Mark/Mark", GTK_UI_MANAGER_MENUITEM)
858         MENUITEM_ADDUI("/Menus/SummaryViewPopup/Mark", "Unmark", "SummaryViewPopup/Mark/Unmark", GTK_UI_MANAGER_MENUITEM)
859         MENUITEM_ADDUI("/Menus/SummaryViewPopup/Mark", "Separator1", "SummaryViewPopup/Mark/---", GTK_UI_MANAGER_SEPARATOR)
860         MENUITEM_ADDUI("/Menus/SummaryViewPopup/Mark", "MarkUnread", "SummaryViewPopup/Mark/MarkUnread", GTK_UI_MANAGER_MENUITEM)
861         MENUITEM_ADDUI("/Menus/SummaryViewPopup/Mark", "MarkRead", "SummaryViewPopup/Mark/MarkRead", GTK_UI_MANAGER_MENUITEM)
862         MENUITEM_ADDUI("/Menus/SummaryViewPopup/Mark", "MarkAllRead", "SummaryViewPopup/Mark/MarkAllRead", GTK_UI_MANAGER_MENUITEM)
863         MENUITEM_ADDUI("/Menus/SummaryViewPopup/Mark", "IgnoreThread", "SummaryViewPopup/Mark/IgnoreThread", GTK_UI_MANAGER_MENUITEM)
864         MENUITEM_ADDUI("/Menus/SummaryViewPopup/Mark", "UnignoreThread", "SummaryViewPopup/Mark/UnignoreThread", GTK_UI_MANAGER_MENUITEM)
865         MENUITEM_ADDUI("/Menus/SummaryViewPopup/Mark", "WatchThread", "SummaryViewPopup/Mark/WatchThread", GTK_UI_MANAGER_MENUITEM)
866         MENUITEM_ADDUI("/Menus/SummaryViewPopup/Mark", "UnwatchThread", "SummaryViewPopup/Mark/UnwatchThread", GTK_UI_MANAGER_MENUITEM)
867         MENUITEM_ADDUI("/Menus/SummaryViewPopup/Mark", "Separator2", "SummaryViewPopup/Mark/---", GTK_UI_MANAGER_SEPARATOR)
868         MENUITEM_ADDUI("/Menus/SummaryViewPopup/Mark", "MarkSpam", "SummaryViewPopup/Mark/MarkSpam", GTK_UI_MANAGER_MENUITEM)
869         MENUITEM_ADDUI("/Menus/SummaryViewPopup/Mark", "MarkHam", "SummaryViewPopup/Mark/MarkHam", GTK_UI_MANAGER_MENUITEM)
870         MENUITEM_ADDUI("/Menus/SummaryViewPopup/Mark", "Separator3", "SummaryViewPopup/Mark/---", GTK_UI_MANAGER_SEPARATOR)
871         MENUITEM_ADDUI("/Menus/SummaryViewPopup/Mark", "Lock", "SummaryViewPopup/Mark/Lock", GTK_UI_MANAGER_MENUITEM)
872         MENUITEM_ADDUI("/Menus/SummaryViewPopup/Mark", "Unlock", "SummaryViewPopup/Mark/Unlock", GTK_UI_MANAGER_MENUITEM)
873
874         /* submenus - colorlabel and tags are dynamic */
875         /* submenus - createfilterrule */
876         MENUITEM_ADDUI("/Menus/SummaryViewPopup/CreateFilterRule", "Automatically", "SummaryViewPopup/CreateFilterRule/Automatically", GTK_UI_MANAGER_MENUITEM)
877         MENUITEM_ADDUI("/Menus/SummaryViewPopup/CreateFilterRule", "ByFrom", "SummaryViewPopup/CreateFilterRule/ByFrom", GTK_UI_MANAGER_MENUITEM)
878         MENUITEM_ADDUI("/Menus/SummaryViewPopup/CreateFilterRule", "ByTo", "SummaryViewPopup/CreateFilterRule/ByTo", GTK_UI_MANAGER_MENUITEM)
879         MENUITEM_ADDUI("/Menus/SummaryViewPopup/CreateFilterRule", "BySubject", "SummaryViewPopup/CreateFilterRule/BySubject", GTK_UI_MANAGER_MENUITEM)
880                 
881 #ifndef GENERIC_UMPC
882         /* submenus - createprocessingrule */
883         MENUITEM_ADDUI("/Menus/SummaryViewPopup/CreateProcessingRule", "Automatically", "SummaryViewPopup/CreateProcessingRule/Automatically", GTK_UI_MANAGER_MENUITEM)
884         MENUITEM_ADDUI("/Menus/SummaryViewPopup/CreateProcessingRule", "ByFrom", "SummaryViewPopup/CreateProcessingRule/ByFrom", GTK_UI_MANAGER_MENUITEM)
885         MENUITEM_ADDUI("/Menus/SummaryViewPopup/CreateProcessingRule", "ByTo", "SummaryViewPopup/CreateProcessingRule/ByTo", GTK_UI_MANAGER_MENUITEM)
886         MENUITEM_ADDUI("/Menus/SummaryViewPopup/CreateProcessingRule", "BySubject", "SummaryViewPopup/CreateProcessingRule/BySubject", GTK_UI_MANAGER_MENUITEM)
887 #endif
888                 
889         /* submenus - view */
890         MENUITEM_ADDUI("/Menus/SummaryViewPopup/View", "OpenInNewWindow", "SummaryViewPopup/View/OpenInNewWindow", GTK_UI_MANAGER_MENUITEM)
891         MENUITEM_ADDUI("/Menus/SummaryViewPopup/View", "MessageSource", "SummaryViewPopup/View/MessageSource", GTK_UI_MANAGER_MENUITEM)
892 #ifndef GENERIC_UMPC
893         MENUITEM_ADDUI("/Menus/SummaryViewPopup/View", "AllHeaders", "SummaryViewPopup/View/AllHeaders", GTK_UI_MANAGER_MENUITEM)
894 #endif          
895         summaryview->popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
896                                 gtk_ui_manager_get_widget(gtkut_ui_manager(), "/Menus/SummaryViewPopup")) );
897
898
899         summaryview->vbox = vbox;
900         summaryview->scrolledwin = scrolledwin;
901         summaryview->ctree = ctree;
902         summaryview->hbox = hbox;
903         summaryview->hbox_l = hbox_l;
904         summaryview->hbox_spc = hbox_spc;
905         summaryview->stat_box = stat_box;
906         summaryview->stat_box2 = stat_box2;
907         summaryview->statlabel_folder = statlabel_folder;
908         summaryview->statlabel_select = statlabel_select;
909         summaryview->statlabel_msgs = statlabel_msgs;
910         summaryview->toggle_eventbox = toggle_eventbox;
911         summaryview->toggle_arrow = toggle_arrow;
912 #ifdef GENERIC_UMPC
913         summaryview->multiple_sel_togbtn = multiple_sel_togbtn;
914 #endif
915         summaryview->toggle_search = toggle_search;
916         summaryview->lock_count = 0;
917         summaryview->msginfo_update_callback_id =
918                 hooks_register_hook(MSGINFO_UPDATE_HOOKLIST, summary_update_msg, (gpointer) summaryview);
919         summaryview->folder_item_update_callback_id =
920                 hooks_register_hook(FOLDER_ITEM_UPDATE_HOOKLIST,
921                                 summary_update_folder_item_hook,
922                                 (gpointer) summaryview);
923         summaryview->folder_update_callback_id =
924                 hooks_register_hook(FOLDER_UPDATE_HOOKLIST,
925                                 summary_update_folder_hook,
926                                 (gpointer) summaryview);
927
928         summaryview->target_list = gtk_target_list_new(summary_drag_types, 2);
929
930         summaryview->quicksearch = quicksearch;
931
932         /* CLAWS: need this to get the SummaryView * from
933          * the CList */
934         g_object_set_data(G_OBJECT(ctree), "summaryview", (gpointer)summaryview); 
935
936         gtk_widget_show_all(vbox);
937
938         gtk_widget_show(vbox);
939
940         if (prefs_common.show_searchbar)
941                 quicksearch_show(quicksearch);
942         else
943                 quicksearch_hide(quicksearch);
944         
945         if (prefs_common.layout_mode == WIDE_MSGLIST_LAYOUT ||
946             prefs_common.layout_mode == SMALL_LAYOUT)
947                 gtk_widget_hide(summaryview->toggle_eventbox);
948
949         return summaryview;
950 }
951
952 void summary_relayout(SummaryView *summaryview)
953 {
954         gtk_widget_realize(summaryview->stat_box);
955
956         g_object_ref(summaryview->hbox_l);
957         g_object_ref(summaryview->statlabel_msgs);
958         
959         gtkut_container_remove(GTK_CONTAINER(summaryview->hbox_l->parent), summaryview->hbox_l);
960         gtkut_container_remove(GTK_CONTAINER(summaryview->statlabel_msgs->parent), summaryview->statlabel_msgs);
961
962         switch (prefs_common.layout_mode) {
963         case NORMAL_LAYOUT:
964         case WIDE_LAYOUT:
965         case WIDE_MSGLIST_LAYOUT:
966                 gtk_box_pack_start(GTK_BOX(summaryview->stat_box), summaryview->hbox_l, TRUE, TRUE, 0);
967                 gtk_box_pack_end(GTK_BOX(summaryview->stat_box), summaryview->statlabel_msgs, FALSE, FALSE, 4);
968                 gtk_widget_show_all(summaryview->stat_box);
969                 gtk_widget_show_all(summaryview->stat_box2);
970                 if (prefs_common.layout_mode == WIDE_MSGLIST_LAYOUT ||
971                     prefs_common.layout_mode == SMALL_LAYOUT)
972                         gtk_widget_hide(summaryview->toggle_eventbox);
973                 else
974                         gtk_widget_show(summaryview->toggle_eventbox);
975                 break;
976         case VERTICAL_LAYOUT:
977         case SMALL_LAYOUT:
978                 gtk_box_pack_start(GTK_BOX(summaryview->stat_box), summaryview->hbox_l, TRUE, TRUE, 0);
979                 gtk_box_pack_start(GTK_BOX(summaryview->stat_box2), summaryview->statlabel_msgs, FALSE, FALSE, 4);
980                 gtk_widget_show_all(summaryview->stat_box);
981                 gtk_widget_show_all(summaryview->stat_box2);
982                 if (prefs_common.layout_mode == SMALL_LAYOUT) {
983                         gtk_widget_hide(summaryview->toggle_eventbox);
984                         gtk_widget_hide(summaryview->statlabel_msgs);
985                 } else {
986                         gtk_widget_show(summaryview->toggle_eventbox);
987                         gtk_widget_show(summaryview->statlabel_msgs);
988                 }
989                         
990                 break;
991         }
992         summary_set_column_order(summaryview);
993
994         g_object_unref(summaryview->hbox_l);
995         g_object_unref(summaryview->statlabel_msgs);
996         quicksearch_relayout(summaryview->quicksearch);
997         if (prefs_common.show_searchbar)
998                 quicksearch_show(summaryview->quicksearch);
999         else
1000                 quicksearch_hide(summaryview->quicksearch);
1001 }
1002
1003 static void summary_set_fonts(SummaryView *summaryview)
1004 {
1005         PangoFontDescription *font_desc;
1006         gint size;
1007
1008         font_desc = pango_font_description_from_string(NORMAL_FONT);
1009         if (font_desc) {
1010                 gtk_widget_modify_font(summaryview->ctree, font_desc);
1011                 pango_font_description_free(font_desc);
1012         }
1013
1014         if (!bold_style) {
1015                 bold_style = gtk_style_copy
1016                         (gtk_widget_get_style(summaryview->ctree));
1017
1018                 if (prefs_common.derive_from_normal_font || !BOLD_FONT) {
1019                         font_desc = pango_font_description_from_string(NORMAL_FONT);
1020                         if (font_desc) {
1021                                 pango_font_description_free(bold_style->font_desc);
1022                                 bold_style->font_desc = font_desc;
1023                         }
1024                         pango_font_description_set_weight
1025                                         (bold_style->font_desc, PANGO_WEIGHT_BOLD);
1026                 } else {
1027                         font_desc = pango_font_description_from_string(BOLD_FONT);
1028                         if (font_desc) {
1029                                 pango_font_description_free(bold_style->font_desc);
1030                                 bold_style->font_desc = font_desc;
1031                         }
1032                 }
1033                 bold_marked_style = gtk_style_copy(bold_style);
1034                 bold_marked_style->fg[GTK_STATE_NORMAL] =
1035                         summaryview->color_marked;
1036                 bold_deleted_style = gtk_style_copy(bold_style);
1037                 bold_deleted_style->fg[GTK_STATE_NORMAL] =
1038                         summaryview->color_dim;
1039         }
1040
1041         if (prefs_common.derive_from_normal_font || !SMALL_FONT) {
1042                 font_desc = pango_font_description_new();
1043                 size = pango_font_description_get_size
1044                         (summaryview->ctree->style->font_desc);
1045                 pango_font_description_set_size(font_desc, size * PANGO_SCALE_SMALL);
1046         } else {
1047                 font_desc = pango_font_description_from_string(SMALL_FONT);
1048         }
1049         if (font_desc) {
1050                 gtk_widget_modify_font(summaryview->statlabel_folder, font_desc);
1051                 gtk_widget_modify_font(summaryview->statlabel_select, font_desc);
1052                 gtk_widget_modify_font(summaryview->statlabel_msgs, font_desc);
1053                 pango_font_description_free(font_desc);
1054         }
1055
1056 }
1057
1058 static void summary_set_folder_pixmap(SummaryView *summaryview, StockPixmap icon)
1059 {
1060         GtkWidget *pixmap; 
1061         if (!summaryview->folder_pixmap_eventbox) {
1062                 summaryview->folder_pixmap_eventbox = gtk_event_box_new();
1063                 gtk_widget_show(summaryview->folder_pixmap_eventbox);
1064                 gtk_box_pack_start(GTK_BOX(summaryview->hbox_l), summaryview->folder_pixmap_eventbox, FALSE, FALSE, 4);
1065                 gtk_box_reorder_child(GTK_BOX(summaryview->hbox_l), summaryview->folder_pixmap_eventbox, 0); /* search_toggle before */
1066                 g_signal_connect(G_OBJECT(summaryview->folder_pixmap_eventbox), "button_press_event",
1067                          G_CALLBACK(summary_folder_eventbox_pressed),
1068                          summaryview);
1069         }
1070         if (summaryview->folder_pixmap)
1071                 gtk_widget_destroy(summaryview->folder_pixmap);
1072
1073         pixmap = stock_pixmap_widget(summaryview->hbox_l, icon);
1074         gtk_container_add(GTK_CONTAINER(summaryview->folder_pixmap_eventbox), pixmap);
1075         gtk_widget_show(pixmap);
1076         summaryview->folder_pixmap = pixmap; 
1077 }
1078
1079 void summary_init(SummaryView *summaryview)
1080 {
1081         GtkWidget *pixmap;
1082
1083         gtk_widget_realize(summaryview->ctree);
1084         stock_pixmap_gdk(summaryview->ctree, STOCK_PIXMAP_MARK,
1085                          &markxpm, &markxpmmask);
1086         stock_pixmap_gdk(summaryview->ctree, STOCK_PIXMAP_DELETED,
1087                          &deletedxpm, &deletedxpmmask);
1088         stock_pixmap_gdk(summaryview->ctree, STOCK_PIXMAP_NEW,
1089                          &newxpm, &newxpmmask);
1090         stock_pixmap_gdk(summaryview->ctree, STOCK_PIXMAP_UNREAD,
1091                          &unreadxpm, &unreadxpmmask);
1092         stock_pixmap_gdk(summaryview->ctree, STOCK_PIXMAP_REPLIED,
1093                          &repliedxpm, &repliedxpmmask);
1094         stock_pixmap_gdk(summaryview->ctree, STOCK_PIXMAP_FORWARDED,
1095                          &forwardedxpm, &forwardedxpmmask);
1096         stock_pixmap_gdk(summaryview->ctree, STOCK_PIXMAP_CLIP,
1097                          &clipxpm, &clipxpmmask);
1098         stock_pixmap_gdk(summaryview->ctree, STOCK_PIXMAP_LOCKED,
1099                          &lockedxpm, &lockedxpmmask);
1100         stock_pixmap_gdk(summaryview->ctree, STOCK_PIXMAP_IGNORETHREAD,
1101                          &ignorethreadxpm, &ignorethreadxpmmask);
1102         stock_pixmap_gdk(summaryview->ctree, STOCK_PIXMAP_WATCHTHREAD,
1103                          &watchthreadxpm, &watchthreadxpmmask);
1104         stock_pixmap_gdk(summaryview->ctree, STOCK_PIXMAP_CLIP_KEY,
1105                          &clipkeyxpm, &clipkeyxpmmask);
1106         stock_pixmap_gdk(summaryview->ctree, STOCK_PIXMAP_KEY_SIGN,
1107                          &keysignxpm, &keysignxpmmask);
1108         stock_pixmap_gdk(summaryview->ctree, STOCK_PIXMAP_KEY,
1109                          &keyxpm, &keyxpmmask);
1110         stock_pixmap_gdk(summaryview->ctree, STOCK_PIXMAP_GPG_SIGNED,
1111                          &gpgsignedxpm, &gpgsignedxpmmask);
1112         stock_pixmap_gdk(summaryview->ctree, STOCK_PIXMAP_CLIP_GPG_SIGNED,
1113                          &clipgpgsignedxpm, &clipgpgsignedxpmmask);
1114         stock_pixmap_gdk(summaryview->ctree, STOCK_PIXMAP_SPAM,
1115                          &spamxpm, &spamxpmmask);
1116         stock_pixmap_gdk(summaryview->ctree, STOCK_PIXMAP_MOVED,
1117                          &movedxpm, &movedxpmmask);
1118         stock_pixmap_gdk(summaryview->ctree, STOCK_PIXMAP_COPIED,
1119                          &copiedxpm, &copiedxpmmask);
1120
1121         summary_set_fonts(summaryview);
1122
1123         summary_set_folder_pixmap(summaryview, STOCK_PIXMAP_DIR_OPEN);
1124
1125         pixmap = stock_pixmap_widget(summaryview->hbox, STOCK_PIXMAP_QUICKSEARCH);
1126         gtk_container_add (GTK_CONTAINER(summaryview->toggle_search), pixmap);
1127         gtk_widget_show(pixmap);
1128         summaryview->quick_search_pixmap = pixmap;
1129         
1130 #ifdef GENERIC_UMPC
1131         pixmap = stock_pixmap_widget(summaryview->hbox, STOCK_PIXMAP_SELECTION);
1132         gtk_container_add(GTK_CONTAINER(summaryview->multiple_sel_togbtn), pixmap);
1133         gtk_widget_show(pixmap);
1134         summaryview->multiple_sel_image = pixmap;
1135 #endif
1136
1137         /* Init summaryview prefs */
1138         summaryview->sort_key = SORT_BY_NONE;
1139         summaryview->sort_type = SORT_ASCENDING;
1140
1141         /* Init summaryview extra data */
1142 #ifndef G_OS_WIN32
1143         summaryview->simplify_subject_preg = NULL;
1144 #endif
1145         summary_clear_list(summaryview);
1146         summary_set_column_titles(summaryview);
1147         summary_colorlabel_menu_create(summaryview, FALSE);
1148         summary_tags_menu_create(summaryview, FALSE);
1149         main_create_mailing_list_menu (summaryview->mainwin, NULL);     
1150         summary_set_menu_sensitive(summaryview);
1151
1152 }
1153
1154 #define CURRENTLY_DISPLAYED(m) \
1155 ( (m->msgnum == displayed_msgnum) \
1156   && (!g_ascii_strcasecmp(m->folder->name,item->name)) )
1157
1158 #define FOLDER_SHOWS_TO_HDR(i) \
1159 ( i && (folder_has_parent_of_type(i, F_OUTBOX) \
1160         || folder_has_parent_of_type(i, F_DRAFT) \
1161         || folder_has_parent_of_type(i, F_QUEUE)) )
1162   
1163 static void summary_switch_from_to(SummaryView *summaryview, FolderItem *item)
1164 {
1165         gboolean show_from = FALSE, show_to = FALSE;
1166         gboolean showing_from = FALSE, showing_to = FALSE;
1167         gint from_pos = 0, to_pos = 0;
1168         SummaryColumnState *col_state = summaryview->col_state;
1169         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
1170         
1171         if (!item || (prefs_common.layout_mode == VERTICAL_LAYOUT && prefs_common.two_line_vert) )
1172                 return;
1173         if (FOLDER_SHOWS_TO_HDR(item))
1174                 show_to = TRUE;
1175         else
1176                 show_from = TRUE;
1177         
1178         from_pos = summaryview->col_pos[S_COL_FROM];
1179         to_pos = summaryview->col_pos[S_COL_TO];
1180         showing_from = col_state[from_pos].visible;
1181         showing_to = col_state[to_pos].visible;
1182         
1183         if (showing_from && showing_to) {
1184                 debug_print("showing both\n");
1185                 return;
1186         }
1187
1188         if (!showing_from && !showing_to) {
1189                 debug_print("showing none\n");
1190                 return;
1191         }
1192
1193         debug_print("showing %s %s, must show %s %s\n", 
1194                 showing_from?"From":"",
1195                 showing_to?"To":"",
1196                 show_from?"From":"",
1197                 show_to?"To":"");
1198
1199         if (showing_from == show_from && showing_to == show_to)
1200                 return;
1201         /* else we'll switch both */
1202
1203         debug_print("switching columns\n");
1204         col_state[from_pos].type = S_COL_TO;
1205         col_state[from_pos].visible = show_to;
1206
1207         col_state[to_pos].type = S_COL_FROM;
1208         col_state[to_pos].visible = show_from;
1209
1210         summaryview->col_pos[S_COL_TO] = from_pos;
1211         summaryview->col_pos[S_COL_FROM] = to_pos;
1212
1213         gtk_cmclist_set_column_visibility
1214                 (GTK_CMCLIST(ctree), from_pos, col_state[from_pos].visible);
1215         gtk_cmclist_set_column_visibility
1216                 (GTK_CMCLIST(ctree), to_pos, col_state[to_pos].visible);
1217
1218         summary_set_column_titles(summaryview);
1219 }
1220
1221 static gboolean summaryview_quicksearch_recurse(gpointer data)
1222 {
1223         SummaryView *summaryview = (SummaryView *)data;
1224         main_window_cursor_wait(summaryview->mainwin);
1225         quicksearch_reset_cur_folder_item(summaryview->quicksearch);
1226         quicksearch_search_subfolders(summaryview->quicksearch, 
1227                               summaryview->folderview,
1228                               summaryview->folder_item);
1229         main_window_cursor_normal(summaryview->mainwin);
1230         return FALSE;
1231 }
1232
1233 static gboolean summary_check_consistency(FolderItem *item, GSList *mlist)
1234 {
1235         int u = 0, n = 0, m = 0, t = 0, r = 0, f = 0, l = 0, i = 0, w = 0;
1236         GSList *cur;
1237         START_TIMING("");
1238         for(cur = mlist ; cur != NULL && cur->data != NULL ; cur = g_slist_next(cur)) {
1239                 MsgInfo * msginfo = (MsgInfo *) cur->data;
1240                 t++;
1241                 if (MSG_IS_NEW(msginfo->flags))
1242                         n++;
1243                 if (MSG_IS_UNREAD(msginfo->flags))
1244                         u++;
1245                 if (MSG_IS_MARKED(msginfo->flags))
1246                         m++;            
1247                 if (MSG_IS_REPLIED(msginfo->flags))
1248                         r++;
1249                 if (MSG_IS_FORWARDED(msginfo->flags))
1250                         f++;
1251                 if (MSG_IS_LOCKED(msginfo->flags))
1252                         l++;
1253                 if (MSG_IS_IGNORE_THREAD(msginfo->flags))
1254                         i++;
1255                 if (MSG_IS_WATCH_THREAD(msginfo->flags))
1256                         w++;
1257         }
1258         if (t != item->total_msgs
1259         ||  n != item->new_msgs
1260         ||  u != item->unread_msgs
1261         ||  m != item->marked_msgs
1262         ||  r != item->replied_msgs
1263         ||  f != item->forwarded_msgs
1264         ||  l != item->locked_msgs
1265         ||  i != item->ignored_msgs
1266         ||  w != item->watched_msgs
1267         ||  (m == 0 && item->unreadmarked_msgs != 0)
1268         ||  item->unreadmarked_msgs < 0) {
1269                 debug_print("Inconsistency\n");
1270                 folder_item_scan_full(item, FALSE);
1271                 END_TIMING();
1272                 return FALSE;
1273         } 
1274         END_TIMING();
1275         return TRUE;
1276 }
1277
1278 gboolean summary_show(SummaryView *summaryview, FolderItem *item)
1279 {
1280         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
1281         GtkCMCTreeNode *node = NULL;
1282         GSList *mlist = NULL;
1283         gchar *buf;
1284         gboolean is_refresh;
1285         guint selected_msgnum = 0;
1286         guint displayed_msgnum = 0;
1287         GSList *cur;
1288         GSList *not_killed;
1289         gboolean hidden_removed = FALSE;
1290
1291         if (summary_is_locked(summaryview)) return FALSE;
1292
1293         if (!summaryview->mainwin)
1294                 return FALSE;
1295         START_TIMING("");
1296         summaryview->last_displayed = NULL;
1297         summary_switch_from_to(summaryview, item);
1298
1299         inc_lock();
1300         summary_lock(summaryview);
1301
1302         utils_free_regex();
1303
1304         is_refresh = (item == summaryview->folder_item) ? TRUE : FALSE;
1305
1306         if (item && item->folder->klass->item_opened) {
1307                 item->folder->klass->item_opened(item);
1308         }
1309
1310         if (!is_refresh) {
1311                 main_create_mailing_list_menu (summaryview->mainwin, NULL);
1312                 if (prefs_common.layout_mode == SMALL_LAYOUT) {
1313                         if (item) {
1314                                 mainwindow_enter_folder(summaryview->mainwin);
1315                                 gtk_widget_grab_focus(summaryview->ctree);
1316                         }
1317                 }
1318         }
1319         if (!prefs_common.summary_quicksearch_sticky
1320          && (!prefs_common.summary_quicksearch_recurse
1321           || !quicksearch_is_active(summaryview->quicksearch)
1322           || (item && !quicksearch_is_in_subfolder(summaryview->quicksearch, item)))
1323          && !quicksearch_is_running(summaryview->quicksearch)
1324          && !is_refresh) {
1325                 quicksearch_set(summaryview->quicksearch, prefs_common.summary_quicksearch_type, "");
1326         }
1327
1328         /* STATUSBAR_POP(summaryview->mainwin); */
1329
1330         if (is_refresh) {
1331                 selected_msgnum = summary_get_msgnum(summaryview,
1332                                                      summaryview->selected);
1333                 displayed_msgnum = summary_get_msgnum(summaryview,
1334                                                       summaryview->displayed);
1335         }
1336
1337         /* process the marks if any */
1338         if (summaryview->mainwin->lock_count == 0 &&
1339             (summaryview->moved > 0 || summaryview->copied > 0)) {
1340                 AlertValue val;
1341                 gboolean changed = FALSE;
1342
1343                 val = alertpanel(_("Process mark"),
1344                                  _("Some marks are left. Process them?"),
1345                                  GTK_STOCK_NO, GTK_STOCK_YES, GTK_STOCK_CANCEL);
1346                 if (G_ALERTALTERNATE == val) {
1347                         summary_unlock(summaryview);
1348                         summary_execute(summaryview);
1349                         summary_lock(summaryview);
1350                         changed = TRUE;
1351                 } else if (G_ALERTDEFAULT == val) {
1352                         /* DO NOTHING */
1353                 } else {
1354                         summary_unlock(summaryview);
1355                         inc_unlock();
1356                         END_TIMING();
1357                         return FALSE;
1358                 }
1359                 if (changed || !quicksearch_is_active(summaryview->quicksearch))
1360                         folder_update_op_count();
1361         }
1362         
1363         summary_freeze(summaryview);
1364
1365         summary_clear_list(summaryview);
1366
1367         buf = NULL;
1368         if (!item || !item->path || !folder_item_parent(item) || item->no_select) {
1369                 g_free(buf);
1370                 debug_print("empty folder (%p %s %p %d)\n\n",
1371                                 item, 
1372                                 (item && item->path)?item->path:"(null)",
1373                                 item?folder_item_parent(item):0x0,
1374                                 item?item->no_select:FALSE);
1375                 summary_set_hide_read_msgs_menu(summaryview, FALSE);
1376                 summary_clear_all(summaryview);
1377                 summaryview->folder_item = item;
1378                 summary_thaw(summaryview);
1379                 summary_unlock(summaryview);
1380                 inc_unlock();
1381                 if (item && quicksearch_is_running(summaryview->quicksearch)) {
1382                         main_window_cursor_wait(summaryview->mainwin);
1383                         quicksearch_reset_cur_folder_item(summaryview->quicksearch);
1384                         if (quicksearch_is_active(summaryview->quicksearch))
1385                                 quicksearch_search_subfolders(summaryview->quicksearch, 
1386                                               summaryview->folderview,
1387                                               summaryview->folder_item);
1388                         main_window_cursor_normal(summaryview->mainwin);
1389                 }       
1390                 END_TIMING();           
1391                 return TRUE;
1392         }
1393         g_free(buf);
1394
1395         if (!is_refresh)
1396                 messageview_clear(summaryview->messageview);
1397
1398         summaryview->folder_item = item;
1399         item->opened = TRUE;
1400
1401         buf = g_strdup_printf(_("Scanning folder (%s)..."), item->path);
1402         debug_print("%s\n", buf);
1403         STATUSBAR_PUSH(summaryview->mainwin, buf);
1404         g_free(buf);
1405
1406         main_window_cursor_wait(summaryview->mainwin);
1407
1408         mlist = folder_item_get_msg_list(item);
1409
1410         if (!summary_check_consistency(item, mlist)) {
1411                 debug_print("reloading due to inconsistency\n");
1412                 procmsg_msg_list_free(mlist);
1413                 mlist = folder_item_get_msg_list(item);
1414         }
1415
1416         if (summaryview->folder_item->hide_read_msgs &&
1417             quicksearch_is_active(summaryview->quicksearch) == FALSE) {
1418                 GSList *not_killed;
1419                 
1420                 summary_set_hide_read_msgs_menu(summaryview, TRUE);
1421                 not_killed = NULL;
1422                 for(cur = mlist ; cur != NULL && cur->data != NULL ; cur = g_slist_next(cur)) {
1423                         MsgInfo * msginfo = (MsgInfo *) cur->data;
1424                         
1425                         if (!msginfo->hidden) {
1426                                 if (MSG_IS_UNREAD(msginfo->flags) &&
1427                                     !MSG_IS_IGNORE_THREAD(msginfo->flags))
1428                                         not_killed = g_slist_prepend(not_killed, msginfo);
1429                                 else if (MSG_IS_MARKED(msginfo->flags) ||
1430                                          MSG_IS_LOCKED(msginfo->flags))
1431                                         not_killed = g_slist_prepend(not_killed, msginfo);
1432                                 else if (is_refresh &&
1433                                         (msginfo->msgnum == selected_msgnum ||
1434                                          msginfo->msgnum == displayed_msgnum))
1435                                         not_killed = g_slist_prepend(not_killed, msginfo);
1436                                 else
1437                                         procmsg_msginfo_free(msginfo);
1438                          } else
1439                                 procmsg_msginfo_free(msginfo);
1440                 }
1441                 hidden_removed = TRUE;
1442                 g_slist_free(mlist);
1443                 mlist = not_killed;
1444         } else {
1445                 summary_set_hide_read_msgs_menu(summaryview, FALSE);
1446         }
1447
1448         if (quicksearch_is_active(summaryview->quicksearch)) {
1449                 GSList *not_killed;
1450                 gint interval = quicksearch_is_fast(summaryview->quicksearch) ? 5000:100;
1451                 START_TIMING("quicksearch");
1452                 gint num = 0, total = summaryview->folder_item->total_msgs;
1453                 statusbar_print_all(_("Searching in %s... \n"), 
1454                         summaryview->folder_item->path ? 
1455                         summaryview->folder_item->path : "(null)");
1456                 not_killed = NULL;
1457                 folder_item_update_freeze();
1458                 for (cur = mlist ; cur != NULL && cur->data != NULL ; cur = g_slist_next(cur)) {
1459                         MsgInfo * msginfo = (MsgInfo *) cur->data;
1460
1461                         statusbar_progress_all(num++,total, interval);
1462
1463                         if (!msginfo->hidden && quicksearch_match(summaryview->quicksearch, msginfo))
1464                                 not_killed = g_slist_prepend(not_killed, msginfo);
1465                         else
1466                                 procmsg_msginfo_free(msginfo);
1467                         if (num % interval == 0)
1468                                 GTK_EVENTS_FLUSH();
1469                         if (!quicksearch_is_active(summaryview->quicksearch)) {
1470                                 break;
1471                         }
1472                 }
1473                 folder_item_update_thaw();
1474                 statusbar_progress_all(0,0,0);
1475                 statusbar_pop_all();
1476                 
1477                 hidden_removed = TRUE;
1478                 if (!quicksearch_is_active(summaryview->quicksearch)) {
1479                         debug_print("search cancelled!\n");
1480                         summary_thaw(summaryview);
1481                         STATUSBAR_POP(summaryview->mainwin);
1482                         main_window_cursor_normal(summaryview->mainwin);
1483                         summary_unlock(summaryview);
1484                         inc_unlock();
1485                         summary_show(summaryview, summaryview->folder_item);
1486                         END_TIMING();
1487                         return FALSE;
1488                 }
1489                 g_slist_free(mlist);
1490                 mlist = not_killed;
1491                 END_TIMING();
1492         }
1493
1494         if (!hidden_removed) {
1495                 START_TIMING("removing hidden");
1496                 not_killed = NULL;
1497                 for(cur = mlist ; cur != NULL && cur->data != NULL ; cur = g_slist_next(cur)) {
1498                         MsgInfo * msginfo = (MsgInfo *) cur->data;
1499
1500                         if (!msginfo->hidden)
1501                                 not_killed = g_slist_prepend(not_killed, msginfo);
1502                         else
1503                                 procmsg_msginfo_free(msginfo);
1504                 }
1505                 g_slist_free(mlist);
1506                 mlist = not_killed;
1507                 END_TIMING();
1508         }
1509
1510         STATUSBAR_POP(summaryview->mainwin);
1511
1512         /* set ctree and hash table from the msginfo list, and
1513            create the thread */
1514         summary_set_ctree_from_list(summaryview, mlist);
1515
1516         g_slist_free(mlist);
1517
1518         if (quicksearch_is_active(summaryview->quicksearch) &&
1519             quicksearch_is_running(summaryview->quicksearch)) {
1520                 /* only scan subfolders when quicksearch changed,
1521                  * not when search is the same and folder changed */
1522                 g_timeout_add(100, summaryview_quicksearch_recurse, summaryview);
1523         }
1524
1525         if (is_refresh) {
1526                 if (!quicksearch_is_in_typing(summaryview->quicksearch)) {
1527                         summaryview->last_displayed = summaryview->displayed;
1528                         summaryview->displayed =
1529                                 summary_find_msg_by_msgnum(summaryview,
1530                                                            displayed_msgnum);
1531                         if (!summaryview->displayed)
1532                                 messageview_clear(summaryview->messageview);
1533                         summary_unlock(summaryview);
1534                         summary_select_by_msgnum(summaryview, selected_msgnum);
1535                         summary_lock(summaryview);
1536                         if (!summaryview->selected) {
1537                                 /* no selected message - select first unread
1538                                    message, but do not display it */
1539                                 node = summary_find_next_flagged_msg(summaryview, NULL,
1540                                                                      MSG_UNREAD, FALSE);
1541                                 if (node == NULL && GTK_CMCLIST(ctree)->row_list != NULL)
1542                                         node = gtk_cmctree_node_nth
1543                                                 (ctree,
1544                                                  item->sort_type == SORT_DESCENDING
1545                                                  ? 0 : GTK_CMCLIST(ctree)->rows - 1);
1546                                 summary_unlock(summaryview);
1547                                 summary_select_node(summaryview, node, FALSE, TRUE);
1548                                 summary_lock(summaryview);
1549                         }
1550                 } else {
1551                         /* just select first/last */
1552                         if (GTK_CMCLIST(ctree)->row_list != NULL)
1553                                 node = gtk_cmctree_node_nth
1554                                         (ctree,
1555                                          item->sort_type == SORT_DESCENDING
1556                                          ? 0 : GTK_CMCLIST(ctree)->rows - 1);
1557                         gtk_sctree_select(GTK_SCTREE(ctree), node);
1558                         summaryview->selected = node;
1559                         gtk_cmctree_node_moveto(ctree, node, 0, 0.5, 0);
1560                 }
1561         } else {
1562                 /* backward compat */
1563                 int i = 0;
1564                 gboolean set = FALSE, stop = FALSE;
1565                 for (i = 0; i < 6; i++) {
1566                         EntryAction act = prefs_common.summary_select_prio[i];
1567
1568                         if (act != ACTION_UNSET) {
1569                                 set = TRUE;
1570                                 break;
1571                         }
1572                 }
1573                 if (!set)
1574                         prefs_summary_open_set_defaults();
1575
1576                 for (i = 0; i < 6 && node == NULL; i++) {
1577                         EntryAction act = prefs_common.summary_select_prio[i];
1578                         
1579                         switch(act) {
1580                         case ACTION_MARKED:
1581                                 node = summary_find_next_flagged_msg(summaryview, NULL,
1582                                              MSG_MARKED, FALSE);
1583                                 break;
1584                         case ACTION_NEW:
1585                                 node = summary_find_next_flagged_msg(summaryview, NULL,
1586                                              MSG_NEW, FALSE);
1587                                 break;
1588                         case ACTION_UNREAD:
1589                                 node = summary_find_next_flagged_msg(summaryview, NULL,
1590                                              MSG_UNREAD, FALSE);
1591                                 break;
1592                         case ACTION_LAST_OPENED:
1593                                 if (summaryview->folder_item) {
1594                                         node = summary_find_msg_by_msgnum(summaryview, 
1595                                                         summaryview->folder_item->last_seen);
1596                                 }
1597                                 break;
1598                         case ACTION_LAST_LIST:
1599                                 if (GTK_CMCLIST(ctree)->row_list != NULL) {
1600                                         node = gtk_cmctree_node_nth
1601                                                 (ctree,
1602                                                  item->sort_type == SORT_DESCENDING
1603                                                  ? 0 : GTK_CMCLIST(ctree)->rows - 1);
1604                                 }
1605                                 break;
1606                         case ACTION_FIRST_LIST:
1607                                 if (GTK_CMCLIST(ctree)->row_list != NULL) {
1608                                         node = gtk_cmctree_node_nth
1609                                                 (ctree,
1610                                                  item->sort_type == SORT_ASCENDING
1611                                                  ? 0 : GTK_CMCLIST(ctree)->rows - 1);
1612                                 }
1613                                 break;
1614                         case ACTION_NOTHING:
1615                         case ACTION_UNSET:
1616                                 node = NULL;
1617                                 stop = TRUE;
1618                                 break;
1619                         }
1620                         
1621                         if (stop || node)
1622                                 break;
1623                 }
1624
1625                 summary_unlock(summaryview);
1626                 if (node) {
1627                         gboolean show = (prefs_common.always_show_msg == OPENMSG_ALWAYS) ||
1628                                 (prefs_common.always_show_msg == OPENMSG_WHEN_VIEW_VISIBLE &&
1629                                                 messageview_is_visible(summaryview->messageview));
1630                         summary_select_node(summaryview, node, show, TRUE);
1631                 }
1632                 summary_lock(summaryview);
1633         }
1634
1635         summary_status_show(summaryview);
1636         summary_set_menu_sensitive(summaryview);
1637         toolbar_main_set_sensitive(summaryview->mainwin);
1638         
1639         summary_thaw(summaryview);
1640         debug_print("\n");
1641         STATUSBAR_PUSH(summaryview->mainwin, _("Done."));
1642         STATUSBAR_POP(summaryview->mainwin);
1643         main_window_cursor_normal(summaryview->mainwin);
1644         summary_unlock(summaryview);
1645         inc_unlock();
1646         END_TIMING();
1647         return TRUE;
1648 }
1649
1650 #undef CURRENTLY_DISPLAYED
1651
1652
1653 void summary_clear_list(SummaryView *summaryview)
1654 {
1655         GtkCMCList *clist = GTK_CMCLIST(summaryview->ctree);
1656         gint optimal_width;
1657
1658         summary_freeze(summaryview);
1659
1660         gtk_cmctree_pre_recursive(GTK_CMCTREE(summaryview->ctree),
1661                                 NULL, summary_free_msginfo_func, NULL);
1662
1663         if (summaryview->folder_item) {
1664                 summaryview->folder_item->opened = FALSE;
1665                 summaryview->folder_item = NULL;
1666         }
1667
1668         summaryview->display_msg = FALSE;
1669
1670         summaryview->selected = NULL;
1671         summaryview->displayed = NULL;
1672         summaryview->total_size = 0;
1673         summaryview->deleted = summaryview->moved = 0;
1674         summaryview->copied = 0;
1675         if (summaryview->msgid_table) {
1676                 g_hash_table_destroy(summaryview->msgid_table);
1677                 summaryview->msgid_table = NULL;
1678         }
1679         if (summaryview->subject_table) {
1680                 g_hash_table_destroy(summaryview->subject_table);
1681                 summaryview->subject_table = NULL;
1682         }
1683         summaryview->mlist = NULL;
1684
1685         gtk_cmclist_clear(clist);
1686         if (summaryview->col_pos[S_COL_SUBJECT] == N_SUMMARY_COLS - 1) {
1687                 optimal_width = gtk_cmclist_optimal_column_width
1688                         (clist, summaryview->col_pos[S_COL_SUBJECT]);
1689                 gtk_cmclist_set_column_width
1690                         (clist, summaryview->col_pos[S_COL_SUBJECT],
1691                          optimal_width);
1692         }
1693
1694         summary_thaw(summaryview);
1695 }
1696
1697 void summary_clear_all(SummaryView *summaryview)
1698 {
1699         messageview_clear(summaryview->messageview);
1700         summary_clear_list(summaryview);
1701         summary_set_menu_sensitive(summaryview);
1702         toolbar_main_set_sensitive(summaryview->mainwin);
1703         summary_status_show(summaryview);
1704 }
1705
1706 void summary_lock(SummaryView *summaryview)
1707 {
1708         summaryview->lock_count++;
1709 }
1710
1711 void summary_unlock(SummaryView *summaryview)
1712 {
1713         if (summaryview->lock_count)
1714                 summaryview->lock_count--;
1715 }
1716
1717 gboolean summary_is_locked(SummaryView *summaryview)
1718 {
1719         return summaryview->lock_count > 0;
1720 }
1721
1722 SummarySelection summary_get_selection_type(SummaryView *summaryview)
1723 {
1724         GtkCMCList *clist = GTK_CMCLIST(summaryview->ctree);
1725         SummarySelection selection;
1726
1727         if (!clist->row_list)
1728                 selection = SUMMARY_NONE;
1729         else if (!clist->selection)
1730                 selection = SUMMARY_SELECTED_NONE;
1731         else if (!clist->selection->next)
1732                 selection = SUMMARY_SELECTED_SINGLE;
1733         else
1734                 selection = SUMMARY_SELECTED_MULTIPLE;
1735
1736         return selection;
1737 }
1738
1739 /*!
1740  *\return       MsgInfo * Selected message if there's one selected;
1741  *              if multiple selected, or none, return NULL.
1742  */ 
1743 MsgInfo *summary_get_selected_msg(SummaryView *summaryview)
1744 {
1745         /* summaryview->selected may be valid when multiple 
1746          * messages were selected */
1747         GList *sellist = GTK_CMCLIST(summaryview->ctree)->selection;
1748
1749         if (sellist == NULL || sellist->next) 
1750                 return NULL;
1751         
1752         return GTKUT_CTREE_NODE_GET_ROW_DATA(sellist->data);
1753 }
1754
1755 GSList *summary_get_selected_msg_list(SummaryView *summaryview)
1756 {
1757         GSList *mlist = NULL;
1758         GList *cur;
1759         MsgInfo *msginfo;
1760
1761         for (cur = GTK_CMCLIST(summaryview->ctree)->selection; cur != NULL && cur->data != NULL;
1762              cur = cur->next) {
1763                 msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(cur->data);
1764                 mlist = g_slist_prepend(mlist, msginfo);
1765         }
1766
1767         mlist = g_slist_reverse(mlist);
1768
1769         return mlist;
1770 }
1771
1772 void summary_set_menu_sensitive(SummaryView *summaryview)
1773 {
1774         SensitiveCond state;
1775         gboolean sensitive;
1776         gint i;
1777
1778         static const struct {
1779                 gchar *const entry;
1780                 SensitiveCond cond;
1781         } entry[] = {
1782                 {"SummaryViewPopup/Reply"                       , M_HAVE_ACCOUNT|M_TARGET_EXIST},
1783 #ifndef GENERIC_UMPC
1784                 {"SummaryViewPopup/ReplyTo"                     , M_HAVE_ACCOUNT|M_TARGET_EXIST},
1785                 {"SummaryViewPopup/ReplyTo/All"         , M_HAVE_ACCOUNT|M_TARGET_EXIST},
1786                 {"SummaryViewPopup/ReplyTo/Sender"             , M_HAVE_ACCOUNT|M_TARGET_EXIST},
1787                 {"SummaryViewPopup/ReplyTo/MailingList"       , M_HAVE_ACCOUNT|M_TARGET_EXIST},
1788 #endif
1789
1790                 {"SummaryViewPopup/Forward"                     , M_HAVE_ACCOUNT|M_TARGET_EXIST},
1791 #ifndef GENERIC_UMPC
1792                 {"SummaryViewPopup/ForwardAsAtt"        , M_HAVE_ACCOUNT|M_TARGET_EXIST},
1793                 {"SummaryViewPopup/Redirect"                    , M_HAVE_ACCOUNT|M_TARGET_EXIST},
1794 #endif
1795
1796                 {"SummaryViewPopup/Move"                        , M_TARGET_EXIST|M_ALLOW_DELETE|M_NOT_NEWS},
1797                 {"SummaryViewPopup/Copy"                        , M_TARGET_EXIST|M_EXEC},
1798                 {"SummaryViewPopup/Trash"               , M_TARGET_EXIST|M_ALLOW_DELETE|M_NOT_NEWS},
1799 #ifndef GENERIC_UMPC
1800                 {"SummaryViewPopup/Delete"                      , M_TARGET_EXIST|M_ALLOW_DELETE},
1801 #endif
1802
1803                 {"SummaryViewPopup/Mark"                        , M_TARGET_EXIST},
1804                 {"SummaryViewPopup/Mark/Mark"                   , M_TARGET_EXIST},
1805                 {"SummaryViewPopup/Mark/Unmark"                 , M_TARGET_EXIST},
1806                 {"SummaryViewPopup/Mark/MarkUnread"     , M_TARGET_EXIST},
1807                 {"SummaryViewPopup/Mark/MarkRead"       , M_TARGET_EXIST},
1808                 {"SummaryViewPopup/Mark/MarkAllRead"    , M_TARGET_EXIST},
1809                 {"SummaryViewPopup/Mark/IgnoreThread"           , M_TARGET_EXIST},
1810                 {"SummaryViewPopup/Mark/UnignoreThread"         , M_TARGET_EXIST},
1811                 {"SummaryViewPopup/Mark/WatchThread"            , M_TARGET_EXIST},
1812                 {"SummaryViewPopup/Mark/UnwatchThread"          , M_TARGET_EXIST},
1813                 {"SummaryViewPopup/Mark/Unlock"                 , M_TARGET_EXIST},
1814                 {"SummaryViewPopup/Mark/Lock"                   , M_TARGET_EXIST},
1815                 {"SummaryViewPopup/Mark/MarkSpam"               , M_TARGET_EXIST|M_CAN_LEARN_SPAM},
1816                 {"SummaryViewPopup/Mark/MarkHam"                , M_TARGET_EXIST|M_CAN_LEARN_SPAM},
1817                 {"SummaryViewPopup/ColorLabel"                  , M_TARGET_EXIST},
1818                 {"SummaryViewPopup/Tags"                        , M_TARGET_EXIST},
1819
1820 #ifndef GENERIC_UMPC
1821                 {"SummaryViewPopup/AddSenderToAB"       , M_SINGLE_TARGET_EXIST},
1822 #endif
1823                 {"SummaryViewPopup/CreateFilterRule"            , M_SINGLE_TARGET_EXIST|M_UNLOCKED},
1824 #ifndef GENERIC_UMPC
1825                 {"SummaryViewPopup/CreateProcessingRule"        , M_SINGLE_TARGET_EXIST|M_UNLOCKED},
1826 #endif
1827
1828                 {"SummaryViewPopup/View"                        , M_SINGLE_TARGET_EXIST},
1829                 {"SummaryViewPopup/View/OpenInNewWindow"     , M_SINGLE_TARGET_EXIST},
1830                 {"SummaryViewPopup/View/MessageSource"          , M_SINGLE_TARGET_EXIST},
1831 #ifndef GENERIC_UMPC
1832                 {"SummaryViewPopup/View/AllHeaders"             , M_SINGLE_TARGET_EXIST},
1833 #endif
1834                 {"SummaryViewPopup/SaveAs"                      , M_TARGET_EXIST},
1835 #ifndef GENERIC_UMPC
1836                 {"SummaryViewPopup/Print"                       , M_TARGET_EXIST},
1837 #endif
1838                 {NULL, 0}
1839         };
1840
1841         main_window_set_menu_sensitive(summaryview->mainwin);
1842
1843         state = main_window_get_current_state(summaryview->mainwin);
1844
1845         for (i = 0; entry[i].entry != NULL; i++) {
1846                 sensitive = ((entry[i].cond & state) == entry[i].cond);
1847                 cm_menu_set_sensitive(entry[i].entry, sensitive);
1848         }
1849
1850         summary_lock(summaryview);
1851 #ifndef GENERIC_UMPC
1852         if (summaryview->messageview 
1853         &&  summaryview->messageview->mimeview
1854         &&  summaryview->messageview->mimeview->textview)
1855                 cm_toggle_menu_set_active("SummaryViewPopup/View/AllHeaders",
1856                         summaryview->messageview->mimeview->textview->show_all_headers);
1857 #endif
1858         summary_unlock(summaryview);
1859 }
1860
1861 void summary_select_prev_unread(SummaryView *summaryview)
1862 {
1863         GtkCMCTreeNode *node;
1864         gboolean skip_cur = FALSE;
1865
1866         if (summaryview->displayed 
1867         &&  summaryview->selected == summaryview->displayed) {
1868                 debug_print("skipping current\n");
1869                 skip_cur = TRUE;
1870         }
1871
1872         node = summary_find_prev_flagged_msg
1873                 (summaryview, summaryview->selected, MSG_UNREAD, skip_cur);
1874
1875         if (!node || node == summaryview->selected) {
1876                 AlertValue val = 0;
1877
1878                 switch (prefs_common.next_unread_msg_dialog) {
1879                         case NEXTUNREADMSGDIALOG_ALWAYS:
1880                                 val = alertpanel(_("No more unread messages"),
1881                                                  _("No unread message found. "
1882                                                    "Search from the end?"),
1883                                                  GTK_STOCK_NO, "+"GTK_STOCK_YES, NULL);
1884                                 break;
1885                         case NEXTUNREADMSGDIALOG_ASSUME_YES:
1886                                 val = G_ALERTALTERNATE;
1887                                 break;
1888                         case NEXTUNREADMSGDIALOG_ASSUME_NO:
1889                                 val = !G_ALERTALTERNATE;
1890                                 break;
1891                         default:
1892                                 debug_print(
1893                                         _("Internal error: unexpected value for prefs_common.next_unread_msg_dialog\n"));
1894                 }
1895                 if (val != G_ALERTALTERNATE) return;
1896                 node = summary_find_prev_flagged_msg(summaryview, NULL,
1897                                                      MSG_UNREAD, FALSE);
1898         }
1899
1900         if (!node)
1901                 alertpanel_notice(_("No unread messages."));
1902         else
1903                 summary_select_node(summaryview, node, TRUE, FALSE);
1904 }
1905
1906 void summary_select_next_unread(SummaryView *summaryview)
1907 {
1908         GtkCMCTreeNode *node = summaryview->selected;
1909         gboolean skip_cur = FALSE;
1910         
1911         if (summaryview->displayed 
1912         &&  summaryview->selected == summaryview->displayed) {
1913                 debug_print("skipping cur (%p %p)\n",
1914                         summaryview->displayed, summaryview->selected);
1915                 skip_cur = TRUE;
1916         }
1917
1918
1919         node = summary_find_next_flagged_msg
1920                 (summaryview, node, MSG_UNREAD, skip_cur);
1921         
1922         if (node)
1923                 summary_select_node(summaryview, node, TRUE, FALSE);
1924         else {
1925                 node = summary_find_next_flagged_msg
1926                         (summaryview, NULL, MSG_UNREAD, FALSE);
1927                 if (node == NULL || node == summaryview->selected) {
1928                         AlertValue val = 0;
1929
1930                         switch (prefs_common.next_unread_msg_dialog) {
1931                                 case NEXTUNREADMSGDIALOG_ALWAYS:
1932                                         val = alertpanel(_("No more unread messages"),
1933                                                          _("No unread message found. "
1934                                                            "Go to next folder?"),
1935                                                          GTK_STOCK_NO, "+"GTK_STOCK_YES, NULL);
1936                                         break;
1937                                 case NEXTUNREADMSGDIALOG_ASSUME_YES:
1938                                         val = G_ALERTALTERNATE;
1939                                         break;
1940                                 case NEXTUNREADMSGDIALOG_ASSUME_NO:
1941                                         val = G_ALERTOTHER;
1942                                         break;
1943                                 default:
1944                                         debug_print(
1945                                                 _("Internal error: unexpected value for prefs_common.next_unread_msg_dialog\n"));
1946                         }
1947
1948                         if (val == G_ALERTALTERNATE) {
1949                                 folderview_select_next_unread(summaryview->folderview, TRUE);
1950                                 return;
1951                         } 
1952                         else
1953                                 return;
1954                 } else
1955                         summary_select_node(summaryview, node, TRUE, FALSE);
1956
1957         }
1958 }
1959
1960 void summary_select_prev_new(SummaryView *summaryview)
1961 {
1962         GtkCMCTreeNode *node;
1963         gboolean skip_cur = FALSE;
1964
1965         if (summaryview->displayed 
1966         &&  summaryview->selected == summaryview->displayed) {
1967                 debug_print("skipping current\n");
1968                 skip_cur = TRUE;
1969         }
1970
1971         node = summary_find_prev_flagged_msg
1972                 (summaryview, summaryview->selected, MSG_NEW, skip_cur);
1973
1974         if (!node || node == summaryview->selected) {
1975                 AlertValue val = 0;
1976
1977                 switch (prefs_common.next_unread_msg_dialog) {
1978                         case NEXTUNREADMSGDIALOG_ALWAYS:
1979                                 val = alertpanel(_("No more new messages"),
1980                                                  _("No new message found. "
1981                                                    "Search from the end?"),
1982                                                  GTK_STOCK_NO, "+"GTK_STOCK_YES, NULL);
1983                                 break;
1984                         case NEXTUNREADMSGDIALOG_ASSUME_YES:
1985                                 val = G_ALERTALTERNATE;
1986                                 break;
1987                         case NEXTUNREADMSGDIALOG_ASSUME_NO:
1988                                 val = !G_ALERTALTERNATE;
1989                                 break;
1990                         default:
1991                                 debug_print(
1992                                         _("Internal error: unexpected value for prefs_common.next_unread_msg_dialog\n"));
1993                 }
1994                 if (val != G_ALERTALTERNATE) return;
1995                 node = summary_find_prev_flagged_msg(summaryview, NULL,
1996                                                      MSG_NEW, FALSE);
1997         }
1998
1999         if (!node)
2000                 alertpanel_notice(_("No new messages."));
2001         else
2002                 summary_select_node(summaryview, node, TRUE, FALSE);
2003 }
2004
2005 void summary_select_next_new(SummaryView *summaryview)
2006 {
2007         GtkCMCTreeNode *node = summaryview->selected;
2008         gboolean skip_cur = FALSE;
2009         
2010         if (summaryview->displayed 
2011         &&  summaryview->selected == summaryview->displayed) {
2012                 debug_print("skipping cur (%p %p)\n",
2013                         summaryview->displayed, summaryview->selected);
2014                 skip_cur = TRUE;
2015         }
2016
2017
2018         node = summary_find_next_flagged_msg
2019                 (summaryview, node, MSG_NEW, skip_cur);
2020         
2021         if (node)
2022                 summary_select_node(summaryview, node, TRUE, FALSE);
2023         else {
2024                 node = summary_find_next_flagged_msg
2025                         (summaryview, NULL, MSG_NEW, FALSE);
2026                 if (node == NULL || node == summaryview->selected) {
2027                         AlertValue val = 0;
2028
2029                         switch (prefs_common.next_unread_msg_dialog) {
2030                                 case NEXTUNREADMSGDIALOG_ALWAYS:
2031                                         val = alertpanel(_("No more new messages"),
2032                                                          _("No new message found. "
2033                                                            "Go to next folder?"),
2034                                                          GTK_STOCK_NO, "+"GTK_STOCK_YES, NULL);
2035                                         break;
2036                                 case NEXTUNREADMSGDIALOG_ASSUME_YES:
2037                                         val = G_ALERTALTERNATE;
2038                                         break;
2039                                 case NEXTUNREADMSGDIALOG_ASSUME_NO:
2040                                         val = G_ALERTOTHER;
2041                                         break;
2042                                 default:
2043                                         debug_print(
2044                                                 _("Internal error: unexpected value for prefs_common.next_unread_msg_dialog\n"));
2045                         }
2046
2047                         if (val == G_ALERTALTERNATE) {
2048                                 folderview_select_next_new(summaryview->folderview);
2049                                 return;
2050                         } 
2051                         else
2052                                 return;
2053                 } else
2054                         summary_select_node(summaryview, node, TRUE, FALSE);
2055
2056         }
2057 }
2058
2059 void summary_select_prev_marked(SummaryView *summaryview)
2060 {
2061         GtkCMCTreeNode *node;
2062
2063         node = summary_find_prev_flagged_msg
2064                 (summaryview, summaryview->selected, MSG_MARKED, TRUE);
2065
2066         if (!node) {
2067                 AlertValue val;
2068
2069                 val = alertpanel(_("No more marked messages"),
2070                                  _("No marked message found. "
2071                                    "Search from the end?"),
2072                                  GTK_STOCK_NO, "+"GTK_STOCK_YES, NULL);
2073                 if (val != G_ALERTALTERNATE) return;
2074                 node = summary_find_prev_flagged_msg(summaryview, NULL,
2075                                                      MSG_MARKED, TRUE);
2076         }
2077
2078         if (!node)
2079                 alertpanel_notice(_("No marked messages."));
2080         else
2081                 summary_select_node(summaryview, node, TRUE, FALSE);
2082 }
2083
2084 void summary_select_next_marked(SummaryView *summaryview)
2085 {
2086         GtkCMCTreeNode *node = summaryview->selected;
2087         gboolean skip_cur = FALSE;
2088         
2089         if (summaryview->displayed 
2090         &&  summaryview->selected == summaryview->displayed) {
2091                 debug_print("skipping cur (%p %p)\n",
2092                         summaryview->displayed, summaryview->selected);
2093                 skip_cur = TRUE;
2094         }
2095
2096
2097         node = summary_find_next_flagged_msg
2098                 (summaryview, node, MSG_MARKED, skip_cur);
2099         
2100         if (node)
2101                 summary_select_node(summaryview, node, TRUE, FALSE);
2102         else {
2103                 node = summary_find_next_flagged_msg
2104                         (summaryview, NULL, MSG_MARKED, FALSE);
2105                 if (node == NULL || node == summaryview->selected) {
2106                         AlertValue val = 0;
2107
2108                         switch (prefs_common.next_unread_msg_dialog) {
2109                                 case NEXTUNREADMSGDIALOG_ALWAYS:
2110                                         val = alertpanel(_("No more marked messages"),
2111                                                          _("No marked message found. "
2112                                                            "Go to next folder?"),
2113                                                          GTK_STOCK_NO, "+"GTK_STOCK_YES, NULL);
2114                                         break;
2115                                 case NEXTUNREADMSGDIALOG_ASSUME_YES:
2116                                         val = G_ALERTALTERNATE;
2117                                         break;
2118                                 case NEXTUNREADMSGDIALOG_ASSUME_NO:
2119                                         val = G_ALERTOTHER;
2120                                         break;
2121                                 default:
2122                                         debug_print(
2123                                                 _("Internal error: unexpected value for prefs_common.next_unread_msg_dialog\n"));
2124                         }
2125
2126                         if (val == G_ALERTALTERNATE) {
2127                                 folderview_select_next_marked(summaryview->folderview);
2128                                 return;
2129                         } 
2130                         else
2131                                 return;
2132                 } else
2133                         summary_select_node(summaryview, node, TRUE, FALSE);
2134
2135         }
2136 }
2137
2138 void summary_select_prev_labeled(SummaryView *summaryview)
2139 {
2140         GtkCMCTreeNode *node;
2141
2142         node = summary_find_prev_flagged_msg
2143                 (summaryview, summaryview->selected, MSG_CLABEL_FLAG_MASK, TRUE);
2144
2145         if (!node) {
2146                 AlertValue val;
2147
2148                 val = alertpanel(_("No more labeled messages"),
2149                                  _("No labeled message found. "
2150                                    "Search from the end?"),
2151                                  GTK_STOCK_NO, "+"GTK_STOCK_YES, NULL);
2152                 if (val != G_ALERTALTERNATE) return;
2153                 node = summary_find_prev_flagged_msg(summaryview, NULL,
2154                                                      MSG_CLABEL_FLAG_MASK, TRUE);
2155         }
2156
2157         if (!node)
2158                 alertpanel_notice(_("No labeled messages."));
2159         else
2160                 summary_select_node(summaryview, node, TRUE, FALSE);
2161 }
2162
2163 void summary_select_next_labeled(SummaryView *summaryview)
2164 {
2165         GtkCMCTreeNode *node;
2166
2167         node = summary_find_next_flagged_msg
2168                 (summaryview, summaryview->selected, MSG_CLABEL_FLAG_MASK, TRUE);
2169
2170         if (!node) {
2171                 AlertValue val;
2172
2173                 val = alertpanel(_("No more labeled messages"),
2174                                  _("No labeled message found. "
2175                                    "Search from the beginning?"),
2176                                  GTK_STOCK_NO, "+"GTK_STOCK_YES, NULL);
2177                 if (val != G_ALERTALTERNATE) return;
2178                 node = summary_find_next_flagged_msg(summaryview, NULL,
2179                                                      MSG_CLABEL_FLAG_MASK, TRUE);
2180         }
2181
2182         if (!node)
2183                 alertpanel_notice(_("No labeled messages."));
2184         else
2185                 summary_select_node(summaryview, node, TRUE, FALSE);
2186 }
2187
2188 void summary_select_last_read(SummaryView *summaryview)
2189 {
2190         if (summaryview->last_displayed)
2191                 summary_select_node(summaryview, summaryview->last_displayed, TRUE, FALSE);
2192 }
2193
2194 void summary_select_parent(SummaryView *summaryview)
2195 {
2196         GtkCMCTreeNode *node = NULL;
2197
2198         if (summaryview->selected)
2199                 node = GTK_CMCTREE_ROW(summaryview->selected)->parent;
2200         if (node)
2201                 summary_select_node(summaryview, node, TRUE, FALSE);
2202 }
2203
2204 void summary_select_by_msgnum(SummaryView *summaryview, guint msgnum)
2205 {
2206         GtkCMCTreeNode *node;
2207
2208         node = summary_find_msg_by_msgnum(summaryview, msgnum);
2209         summary_select_node(summaryview, node, FALSE, TRUE);
2210 }
2211
2212 typedef struct _PostponedSelectData
2213 {
2214         GtkCMCTree *ctree;
2215         GtkCMCTreeNode *row;
2216         GtkCMCTreeNode *node;
2217         GtkScrollType type;
2218         gint column;
2219         SummaryView *summaryview;
2220         gboolean display_msg;
2221         gboolean do_refresh;
2222 } PostponedSelectData;
2223
2224 static gboolean summary_select_retry(void *data)
2225 {
2226         PostponedSelectData *psdata = (PostponedSelectData *)data;
2227         debug_print("trying again\n");
2228         if (psdata->row)
2229                 summary_selected(psdata->ctree, psdata->row,
2230                             psdata->column, psdata->summaryview);
2231         else if (psdata->node)
2232                 summary_select_node(psdata->summaryview, psdata->node,
2233                             psdata->display_msg, psdata->do_refresh);
2234         else
2235                 summary_step(psdata->summaryview, psdata->type);
2236         g_free(psdata);
2237         return FALSE;
2238 }
2239
2240 /**
2241  * summary_select_node:
2242  * @summaryview: Summary view.
2243  * @node: Summary tree node.
2244  * @display_msg: TRUE to display the selected message.
2245  * @do_refresh: TRUE to refresh the widget.
2246  *
2247  * Select @node (bringing it into view by scrolling and expanding its
2248  * thread, if necessary) and unselect all others.  If @display_msg is
2249  * TRUE, display the corresponding message in the message view.
2250  * If @do_refresh is TRUE, the widget is refreshed.
2251  **/
2252 void summary_select_node(SummaryView *summaryview, GtkCMCTreeNode *node,
2253                          gboolean display_msg, gboolean do_refresh)
2254 {
2255         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
2256         
2257         if (summary_is_locked(summaryview)
2258         && !GTK_SCTREE(ctree)->selecting_range
2259         && summaryview->messageview->mimeview
2260         && summaryview->messageview->mimeview->type == MIMEVIEW_TEXT
2261         && summaryview->messageview->mimeview->textview->loading) {
2262                 PostponedSelectData *data = g_new0(PostponedSelectData, 1);
2263                 summaryview->messageview->mimeview->textview->stop_loading = TRUE;
2264                 
2265                 data->ctree = ctree;
2266                 data->row = NULL;
2267                 data->node = node;
2268                 data->summaryview = summaryview;
2269                 data->display_msg = display_msg;
2270                 data->do_refresh = do_refresh;
2271                 debug_print("postponing open of message till end of load\n");
2272                 g_timeout_add(100, summary_select_retry, data);
2273                 return;
2274         }
2275         if (summary_is_locked(summaryview)) {
2276                 return;
2277         }
2278         if (!summaryview->folder_item)
2279                 return;
2280         if (node) {
2281                 gtkut_ctree_expand_parent_all(ctree, node);
2282                 if (do_refresh) {
2283                         summary_lock(summaryview);
2284                         GTK_EVENTS_FLUSH();
2285                         summary_unlock(summaryview);
2286                         gtk_widget_grab_focus(GTK_WIDGET(ctree));
2287                         gtk_cmctree_node_moveto(ctree, node, 0, 0.5, 0);
2288                 }
2289                 if (display_msg && summaryview->displayed == node)
2290                         summaryview->displayed = NULL;
2291                 summaryview->display_msg = display_msg;
2292                 gtk_sctree_select(GTK_SCTREE(ctree), node);
2293                 if (summaryview->selected == NULL)
2294                         summaryview->selected = node;
2295         }
2296 }
2297
2298 guint summary_get_msgnum(SummaryView *summaryview, GtkCMCTreeNode *node)
2299 {
2300         GtkCMCTree *ctree =NULL;
2301         MsgInfo *msginfo;
2302
2303         if (!summaryview)
2304                 return 0;
2305         ctree = GTK_CMCTREE(summaryview->ctree);
2306         if (!node)
2307                 return 0;
2308         msginfo = gtk_cmctree_node_get_row_data(ctree, node);
2309         if (msginfo)
2310                 return msginfo->msgnum;
2311         else 
2312                 return -1;
2313 }
2314
2315 static GtkCMCTreeNode *summary_find_prev_msg(SummaryView *summaryview,
2316                                            GtkCMCTreeNode *current_node)
2317 {
2318         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
2319         GtkCMCTreeNode *node;
2320         MsgInfo *msginfo;
2321
2322         if (current_node)
2323                 node = current_node;
2324         else
2325                 node = gtk_cmctree_node_nth(ctree, GTK_CMCLIST(ctree)->rows - 1);
2326
2327         for (; node != NULL; node = GTK_CMCTREE_NODE_PREV(node)) {
2328                 msginfo = gtk_cmctree_node_get_row_data(ctree, node);
2329                 if (msginfo && !MSG_IS_DELETED(msginfo->flags)) break;
2330         }
2331
2332         return node;
2333 }
2334
2335 static GtkCMCTreeNode *summary_find_next_msg(SummaryView *summaryview,
2336                                            GtkCMCTreeNode *current_node)
2337 {
2338         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
2339         GtkCMCTreeNode *node;
2340         MsgInfo *msginfo;
2341
2342         if (current_node)
2343                 node = current_node;
2344         else
2345                 node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
2346
2347         for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
2348                 msginfo = gtk_cmctree_node_get_row_data(ctree, node);
2349                 if (msginfo && !MSG_IS_DELETED(msginfo->flags) 
2350                 && !MSG_IS_MOVE(msginfo->flags)) break;
2351         }
2352
2353         return node;
2354 }
2355
2356 static GtkCMCTreeNode *summary_find_prev_flagged_msg(SummaryView *summaryview,
2357                                                    GtkCMCTreeNode *current_node,
2358                                                    MsgPermFlags flags,
2359                                                    gboolean start_from_prev)
2360 {
2361         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
2362         GtkCMCTreeNode *node;
2363         MsgInfo *msginfo;
2364
2365         if (current_node) {
2366                 if (start_from_prev)
2367                         node = GTK_CMCTREE_NODE_PREV(current_node);
2368                 else
2369                         node = current_node;
2370         } else
2371                 node = gtk_cmctree_node_nth(ctree, GTK_CMCLIST(ctree)->rows - 1);
2372
2373         for (; node != NULL; node = GTK_CMCTREE_NODE_PREV(node)) {
2374                 msginfo = gtk_cmctree_node_get_row_data(ctree, node);
2375                 if (msginfo && (msginfo->flags.perm_flags & flags) != 0) break;
2376         }
2377
2378         return node;
2379 }
2380
2381 static GtkCMCTreeNode *summary_find_next_flagged_msg(SummaryView *summaryview,
2382                                                    GtkCMCTreeNode *current_node,
2383                                                    MsgPermFlags flags,
2384                                                    gboolean start_from_next)
2385 {
2386         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
2387         GtkCMCTreeNode *node;
2388         MsgInfo *msginfo;
2389
2390         if (current_node) {
2391                 if (start_from_next)
2392                         node = gtkut_ctree_node_next(ctree, current_node);
2393                 else
2394                         node = current_node;
2395         } else
2396                 node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
2397
2398         for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
2399                 msginfo = gtk_cmctree_node_get_row_data(ctree, node);
2400                 /* Find msg with matching flags but ignore messages with
2401                    ignore flags, if searching for new or unread messages */
2402                 if ((msginfo && (msginfo->flags.perm_flags & flags) != 0) &&
2403                     !(((flags & (MSG_NEW | MSG_UNREAD)) != 0) && MSG_IS_IGNORE_THREAD(msginfo->flags)) 
2404                         )
2405                         break;
2406         }
2407
2408         return node;
2409 }
2410
2411 static GtkCMCTreeNode *summary_find_msg_by_msgnum(SummaryView *summaryview,
2412                                                 guint msgnum)
2413 {
2414         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
2415         GtkCMCTreeNode *node;
2416         MsgInfo *msginfo;
2417
2418         node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
2419
2420         for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
2421                 msginfo = gtk_cmctree_node_get_row_data(ctree, node);
2422                 if (msginfo && msginfo->msgnum == msgnum) break;
2423         }
2424
2425         return node;
2426 }
2427
2428 static guint attract_hash_func(gconstpointer key)
2429 {
2430         gchar *str;
2431         gchar *p;
2432         guint h;
2433
2434         Xstrdup_a(str, (const gchar *)key, return 0);
2435         trim_subject(str);
2436
2437         p = str;
2438         h = *p;
2439
2440         if (h) {
2441                 for (p += 1; *p != '\0'; p++)
2442                         h = (h << 5) - h + *p;
2443         }
2444
2445         return h;
2446 }
2447
2448 static gint attract_compare_func(gconstpointer a, gconstpointer b)
2449 {
2450         return subject_compare((const gchar *)a, (const gchar *)b) == 0;
2451 }
2452
2453 void summary_attract_by_subject(SummaryView *summaryview)
2454 {
2455         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
2456         GtkCMCList *clist = GTK_CMCLIST(ctree);
2457         GtkCMCTreeNode *src_node;
2458         GtkCMCTreeNode *dst_node, *sibling;
2459         GtkCMCTreeNode *tmp;
2460         MsgInfo *src_msginfo, *dst_msginfo;
2461         GHashTable *subject_table;
2462
2463         debug_print("Attracting messages by subject...");
2464         STATUSBAR_PUSH(summaryview->mainwin,
2465                        _("Attracting messages by subject..."));
2466
2467         main_window_cursor_wait(summaryview->mainwin);
2468         summary_freeze(summaryview);
2469
2470         subject_table = g_hash_table_new(attract_hash_func,
2471                                          attract_compare_func);
2472
2473         for (src_node = GTK_CMCTREE_NODE(clist->row_list);
2474              src_node != NULL;
2475              src_node = tmp) {
2476                 tmp = GTK_CMCTREE_ROW(src_node)->sibling;
2477                 src_msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(src_node);
2478                 if (!src_msginfo) continue;
2479                 if (!src_msginfo->subject) continue;
2480
2481                 /* find attracting node */
2482                 dst_node = g_hash_table_lookup(subject_table,
2483                                                src_msginfo->subject);
2484
2485                 if (dst_node) {
2486                         dst_msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(dst_node);
2487
2488                         /* if the time difference is more than 20 days,
2489                            don't attract */
2490                         if (ABS(src_msginfo->date_t - dst_msginfo->date_t)
2491                             > 60 * 60 * 24 * 20)
2492                                 continue;
2493
2494                         sibling = GTK_CMCTREE_ROW(dst_node)->sibling;
2495                         if (src_node != sibling)
2496                                 gtk_cmctree_move(ctree, src_node, NULL, sibling);
2497                 }
2498
2499                 g_hash_table_insert(subject_table,
2500                                     src_msginfo->subject, src_node);
2501         }
2502
2503         g_hash_table_destroy(subject_table);
2504
2505         gtk_cmctree_node_moveto(ctree, summaryview->selected, 0, 0.5, 0);
2506
2507         summary_thaw(summaryview);
2508
2509         debug_print("done.\n");
2510         STATUSBAR_POP(summaryview->mainwin);
2511
2512         main_window_cursor_normal(summaryview->mainwin);
2513 }
2514
2515 static void summary_free_msginfo_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2516                                       gpointer data)
2517 {
2518         MsgInfo *msginfo = gtk_cmctree_node_get_row_data(ctree, node);
2519
2520         if (msginfo)
2521                 procmsg_msginfo_free(msginfo);
2522 }
2523
2524 static void summary_set_marks_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2525                                    gpointer data)
2526 {
2527         SummaryView *summaryview = data;
2528         MsgInfo *msginfo;
2529
2530         msginfo = gtk_cmctree_node_get_row_data(ctree, node);
2531
2532         if (MSG_IS_DELETED(msginfo->flags))
2533                 summaryview->deleted++;
2534
2535         summaryview->total_size += msginfo->size;
2536
2537         summary_set_row_marks(summaryview, node);
2538 }
2539
2540 static void summary_update_status(SummaryView *summaryview)
2541 {
2542         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
2543         GtkCMCTreeNode *node;
2544         MsgInfo *msginfo;
2545
2546         summaryview->total_size =
2547         summaryview->deleted = summaryview->moved = summaryview->copied = 0;
2548
2549         for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
2550              node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
2551                 msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
2552                 
2553                 if (!msginfo)
2554                         continue;
2555
2556                 if (MSG_IS_DELETED(msginfo->flags))
2557                         summaryview->deleted++;
2558                 if (MSG_IS_MOVE(msginfo->flags))
2559                         summaryview->moved++;
2560                 if (MSG_IS_COPY(msginfo->flags))
2561                         summaryview->copied++;
2562                 summaryview->total_size += msginfo->size;
2563         }
2564 }
2565
2566 static void summary_status_show(SummaryView *summaryview)
2567 {
2568         gchar *str;
2569         gchar *del, *mv, *cp;
2570         gchar *sel;
2571         gchar *spc;
2572         gchar *itstr;
2573         GList *rowlist, *cur;
2574         guint n_selected = 0, n_new = 0, n_unread = 0, n_total = 0;
2575         guint n_marked = 0, n_replied = 0, n_forwarded = 0, n_locked = 0, n_ignored = 0, n_watched = 0;
2576         goffset sel_size = 0, n_size = 0;
2577         MsgInfo *msginfo;
2578         gchar *name;
2579 #if GTK_CHECK_VERSION(2, 12, 0)
2580         gchar *tooltip;
2581 #endif
2582         
2583         if (!summaryview->folder_item) {
2584                 gtk_label_set_text(GTK_LABEL(summaryview->statlabel_folder), "");
2585                 gtk_label_set_text(GTK_LABEL(summaryview->statlabel_select), "");
2586                 gtk_label_set_text(GTK_LABEL(summaryview->statlabel_msgs),   "");
2587                 toolbar_main_set_sensitive(summaryview->mainwin);
2588                 return;
2589         }
2590
2591         rowlist = GTK_CMCLIST(summaryview->ctree)->selection;
2592         for (cur = rowlist; cur != NULL && cur->data != NULL; cur = cur->next) {
2593                 msginfo = gtk_cmctree_node_get_row_data
2594                         (GTK_CMCTREE(summaryview->ctree),
2595                          GTK_CMCTREE_NODE(cur->data));
2596                 if (msginfo) {
2597                         sel_size += msginfo->size;
2598                         n_selected++;
2599                 }
2600         }
2601         
2602         if (summaryview->folder_item->hide_read_msgs 
2603         || quicksearch_is_active(summaryview->quicksearch)) {
2604                 rowlist = GTK_CMCLIST(summaryview->ctree)->row_list;
2605                 for (cur = rowlist; cur != NULL && cur->data != NULL; cur = cur->next) {
2606                         msginfo = gtk_cmctree_node_get_row_data
2607                                 (GTK_CMCTREE(summaryview->ctree),
2608                                  GTK_CMCTREE_NODE(cur));
2609                         if (msginfo) {
2610                                 n_size += msginfo->size;
2611                                 n_total++;
2612                                 if (MSG_IS_NEW(msginfo->flags))
2613                                         n_new++;
2614                                 if (MSG_IS_UNREAD(msginfo->flags))
2615                                         n_unread++;
2616                                 if (MSG_IS_MARKED(msginfo->flags))
2617                                         n_marked++;
2618                                 if (MSG_IS_REPLIED(msginfo->flags))
2619                                         n_replied++;
2620                                 if (MSG_IS_FORWARDED(msginfo->flags))
2621                                         n_forwarded++;
2622                                 if (MSG_IS_LOCKED(msginfo->flags))
2623                                         n_locked++;
2624                                 if (MSG_IS_IGNORE_THREAD(msginfo->flags))
2625                                         n_ignored++;
2626                                 if (MSG_IS_WATCH_THREAD(msginfo->flags))
2627                                         n_watched++;
2628                         }
2629                 }
2630         } else {
2631                 n_new = summaryview->folder_item->new_msgs;
2632                 n_unread = summaryview->folder_item->unread_msgs;
2633                 n_marked = summaryview->folder_item->marked_msgs;
2634                 n_replied = summaryview->folder_item->replied_msgs;
2635                 n_forwarded = summaryview->folder_item->forwarded_msgs;
2636                 n_locked = summaryview->folder_item->locked_msgs;
2637                 n_ignored = summaryview->folder_item->ignored_msgs;
2638                 n_watched = summaryview->folder_item->watched_msgs;
2639                 n_total = summaryview->folder_item->total_msgs;
2640                 n_size = summaryview->total_size;
2641         }
2642
2643         name = folder_item_get_name(summaryview->folder_item);
2644         gtk_label_set_text(GTK_LABEL(summaryview->statlabel_folder), name);
2645         g_free(name);
2646
2647         if (summaryview->deleted)
2648                 del = g_strdup_printf(_("%d deleted"), summaryview->deleted);
2649         else
2650                 del = g_strdup("");
2651         if (summaryview->moved)
2652                 mv = g_strdup_printf(_("%s%d moved"),
2653                                      summaryview->deleted ? _(", ") : "",
2654                                      summaryview->moved);
2655         else
2656                 mv = g_strdup("");
2657         if (summaryview->copied)
2658                 cp = g_strdup_printf(_("%s%d copied"),
2659                                      summaryview->deleted ||
2660                                      summaryview->moved ? _(", ") : "",
2661                                      summaryview->copied);
2662         else
2663                 cp = g_strdup("");
2664
2665         if (summaryview->deleted || summaryview->moved || summaryview->copied)
2666                 spc = "    ";
2667         else
2668                 spc = "";
2669
2670         if (n_selected) {
2671                 sel = g_strdup_printf(" (%s)", to_human_readable((goffset)sel_size));
2672                 if (n_selected == 1)
2673                         itstr = g_strdup(_(" item selected"));
2674                 else
2675                         itstr = g_strdup(_(" items selected"));
2676         } else {
2677                 sel = g_strdup("");
2678                 itstr = g_strdup("");
2679         }
2680                 
2681         if (prefs_common.layout_mode != SMALL_LAYOUT) {
2682                 str = g_strconcat(n_selected ? itos(n_selected) : "",
2683                                                 itstr, sel, spc, del, mv, cp, NULL);
2684                 g_free(sel);
2685                 g_free(del);
2686                 g_free(mv);
2687                 g_free(cp);
2688                 g_free(itstr);
2689                 
2690                 gtk_label_set_text(GTK_LABEL(summaryview->statlabel_select), str);
2691                 g_free(str);
2692
2693                 str = g_strdup_printf(_("%d new, %d unread, %d total (%s)"),
2694                                               n_new, n_unread, n_total,
2695                                               to_human_readable((goffset)n_size));
2696
2697
2698                 gtk_label_set_text(GTK_LABEL(summaryview->statlabel_msgs), str);
2699                 g_free(str);
2700 #if GTK_CHECK_VERSION(2, 12, 0)
2701                 tooltip = g_strdup_printf(_("<b>Message summary</b>\n"
2702                                             "<b>New:</b> %d\n"
2703                                             "<b>Unread:</b> %d\n"
2704                                             "<b>Total:</b> %d\n"
2705                                             "<b>Size:</b> %s\n\n"
2706                                             "<b>Marked:</b> %d\n"
2707                                             "<b>Replied:</b> %d\n"
2708                                             "<b>Forwarded:</b> %d\n"
2709                                             "<b>Locked:</b> %d\n"
2710                                             "<b>Ignored:</b> %d\n"
2711                                             "<b>Watched:</b> %d"),
2712                                               n_new, n_unread, n_total,
2713                                               to_human_readable((goffset)n_size),
2714                                               n_marked,n_replied,n_forwarded,
2715                                               n_locked,n_ignored,n_watched);
2716
2717                 gtk_widget_set_tooltip_markup(GTK_WIDGET(summaryview->statlabel_msgs),
2718                                             tooltip); 
2719                 g_free(tooltip);
2720 #endif
2721         } else {
2722                 gchar *ssize, *tsize;
2723                 if (n_selected) {
2724                         ssize = g_strdup(to_human_readable((goffset)sel_size));
2725                         tsize = g_strdup(to_human_readable((goffset)n_size));
2726                         str = g_strdup_printf(_("%d/%d selected (%s/%s), %d unread"),
2727                                 n_selected, n_total, ssize, tsize, n_unread);
2728                         g_free(ssize);
2729                         g_free(tsize);
2730                 } else
2731                         str = g_strdup_printf(_("%d new, %d unread, %d total (%s)"),
2732                                 n_new, n_unread, n_total, to_human_readable((goffset)n_size));
2733                 g_free(sel);
2734                 g_free(del);
2735                 g_free(mv);
2736                 g_free(cp);
2737                 g_free(itstr);
2738                 
2739                 gtk_label_set_text(GTK_LABEL(summaryview->statlabel_select), str);
2740                 g_free(str);
2741         }
2742
2743         summary_set_menu_sensitive(summaryview);
2744         toolbar_main_set_sensitive(summaryview->mainwin);
2745 }
2746
2747 static void summary_set_column_titles(SummaryView *summaryview)
2748 {
2749         GtkCMCList *clist = GTK_CMCLIST(summaryview->ctree);
2750         FolderItem *item = summaryview->folder_item;
2751         GtkWidget *hbox;
2752         GtkWidget *label;
2753         GtkWidget *arrow;
2754         gint pos;
2755         const gchar *title;
2756         SummaryColumnType type;
2757         GtkJustification justify;
2758
2759         static FolderSortKey sort_by[N_SUMMARY_COLS] = {
2760                 SORT_BY_MARK,
2761                 SORT_BY_STATUS,
2762                 SORT_BY_MIME,
2763                 SORT_BY_SUBJECT,
2764                 SORT_BY_FROM,
2765                 SORT_BY_TO,
2766                 SORT_BY_DATE,
2767                 SORT_BY_SIZE,
2768                 SORT_BY_NUMBER,
2769                 SORT_BY_SCORE,
2770                 SORT_BY_LOCKED,
2771                 SORT_BY_TAGS
2772         };
2773
2774         for (pos = 0; pos < N_SUMMARY_COLS; pos++) {
2775                 type = summaryview->col_state[pos].type;
2776
2777                 /* CLAWS: mime and unread are single char headers */
2778                 justify = (type == S_COL_NUMBER || type == S_COL_SIZE)
2779                         ? GTK_JUSTIFY_RIGHT : GTK_JUSTIFY_LEFT;
2780
2781                 switch (type) {
2782                 case S_COL_SUBJECT:
2783                 case S_COL_FROM:
2784                 case S_COL_TO:
2785                 case S_COL_DATE:
2786                 case S_COL_NUMBER:
2787                         if(type == S_COL_FROM && item != NULL &&
2788                                         FOLDER_SHOWS_TO_HDR(item) &&
2789                                         !summaryview->col_state[summaryview->col_pos[S_COL_TO]].visible)
2790                                 type = S_COL_TO;
2791                         if(type == S_COL_NUMBER)
2792                                 title = gettext(col_label[type]);
2793                         else
2794                                 title = prefs_common_translated_header_name(col_label[type]);
2795                         break;
2796                 default:
2797                         title = gettext(col_label[type]);
2798                 }
2799
2800                 if (type == S_COL_MIME) {
2801                         label = gtk_image_new_from_pixmap(clipxpm, clipxpmmask);
2802                         gtk_widget_show(label);
2803                         gtk_cmclist_set_column_widget(clist, pos, label);
2804                         gtk_sctree_set_column_tooltip(GTK_SCTREE(clist), pos, _("Attachment"));
2805                         continue;
2806                 } else if (type == S_COL_MARK) {
2807                         label = gtk_image_new_from_pixmap(markxpm, markxpmmask);
2808                         gtk_widget_show(label);
2809                         gtk_cmclist_set_column_widget(clist, pos, label);
2810                         gtk_sctree_set_column_tooltip(GTK_SCTREE(clist), pos, _("Mark"));
2811                         continue;
2812                 } else if (type == S_COL_LOCKED) {
2813                         label = gtk_image_new_from_pixmap(lockedxpm, lockedxpmmask);
2814                         gtk_widget_show(label);
2815                         gtk_cmclist_set_column_widget(clist, pos, label);
2816                         gtk_sctree_set_column_tooltip(GTK_SCTREE(clist), pos, _("Locked"));
2817                         continue;
2818                 } else if (type == S_COL_STATUS) {
2819                         gtk_cmclist_set_column_title(clist, pos, title);
2820                         gtk_sctree_set_column_tooltip(GTK_SCTREE(clist), pos, _("Status"));
2821                         continue;
2822                 }
2823
2824                 hbox  = gtk_hbox_new(FALSE, 4);
2825                 label = gtk_label_new(title);
2826 #ifdef GENERIC_UMPC
2827         gtk_widget_set_size_request(hbox, -1, 20);
2828 #endif
2829
2830                 if (justify == GTK_JUSTIFY_RIGHT)
2831                         gtk_box_pack_end(GTK_BOX(hbox), label,
2832                                          FALSE, FALSE, 0);
2833                 else
2834                         gtk_box_pack_start(GTK_BOX(hbox), label,
2835                                            FALSE, FALSE, 0);
2836
2837                 if (summaryview->sort_key == sort_by[type]) {
2838                         arrow = gtk_arrow_new
2839                                 (summaryview->sort_type == SORT_ASCENDING
2840                                  ? GTK_ARROW_DOWN : GTK_ARROW_UP,
2841                                  GTK_SHADOW_IN);
2842                         gtk_widget_set_size_request(GTK_WIDGET(arrow), 10, 10);
2843                         if (justify == GTK_JUSTIFY_RIGHT)
2844                                 gtk_box_pack_start(GTK_BOX(hbox), arrow,
2845                                                    FALSE, FALSE, 0);
2846                         else
2847                                 gtk_box_pack_end(GTK_BOX(hbox), arrow,
2848                                                  FALSE, FALSE, 0);
2849                 }
2850
2851                 gtk_widget_show_all(hbox);
2852                 gtk_cmclist_set_column_widget(clist, pos, hbox);
2853         }
2854 }
2855
2856 void summary_reflect_tags_changes(SummaryView *summaryview)
2857 {
2858         GtkMenuShell *menu;
2859         GList *cur;
2860         GtkCMCTreeNode *node;
2861         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
2862         gboolean froze = FALSE;
2863         gboolean redisplay = FALSE;
2864
2865         /* re-create colorlabel submenu */
2866         menu = GTK_MENU_SHELL(summaryview->tags_menu);
2867         g_return_if_fail(menu != NULL);
2868
2869         /* clear items. get item pointers. */
2870         for (cur = menu->children; cur != NULL && cur->data != NULL; cur = cur->next) {
2871                 gtk_menu_item_set_submenu(GTK_MENU_ITEM(cur->data), NULL);
2872         }
2873         summary_tags_menu_create(summaryview, TRUE);
2874
2875         START_LONG_OPERATION(summaryview, TRUE);
2876         for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list); node != NULL;
2877              node = gtkut_ctree_node_next(ctree, node)) {
2878                 redisplay |= summary_set_row_tag(summaryview,
2879                                            node, TRUE, FALSE, 0);
2880         }
2881         END_LONG_OPERATION(summaryview);
2882         if (redisplay)
2883                 summary_redisplay_msg(summaryview);
2884 }
2885
2886
2887 void summary_reflect_prefs(void)
2888 {
2889         static gchar *last_smallfont = NULL;
2890         static gchar *last_normalfont = NULL;
2891         static gchar *last_boldfont = NULL;
2892         gboolean update_font = FALSE;
2893         SummaryView *summaryview = NULL;
2894
2895         if (!mainwindow_get_mainwindow())
2896                 return;
2897         summaryview = mainwindow_get_mainwindow()->summaryview;
2898
2899         if (!last_smallfont || strcmp(last_smallfont, SMALL_FONT) ||
2900                         !last_normalfont || strcmp(last_normalfont, NORMAL_FONT) ||
2901                         !last_boldfont || strcmp(last_boldfont, BOLD_FONT))
2902                 update_font = TRUE;
2903
2904         g_free(last_smallfont);
2905         last_smallfont = g_strdup(SMALL_FONT);
2906         g_free(last_normalfont);
2907         last_normalfont = g_strdup(NORMAL_FONT);
2908         g_free(last_boldfont);
2909         last_boldfont = g_strdup(BOLD_FONT);
2910
2911         if (update_font) {      
2912                 bold_style = bold_marked_style = bold_deleted_style = 
2913                         small_style = small_marked_style = small_deleted_style = NULL;
2914                 summary_set_fonts(summaryview);
2915         }
2916
2917         summary_set_column_titles(summaryview);
2918         summary_relayout(summaryview);
2919         
2920         if (summaryview->folder_item)
2921                 summary_show(summaryview, summaryview->folder_item);
2922 }
2923
2924 void summary_sort(SummaryView *summaryview,
2925                   FolderSortKey sort_key, FolderSortType sort_type)
2926 {
2927         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
2928         GtkCMCList *clist = GTK_CMCLIST(summaryview->ctree);
2929         GtkCMCListCompareFunc cmp_func = NULL;
2930         START_TIMING("");
2931         g_signal_handlers_block_by_func(G_OBJECT(summaryview->ctree),
2932                                        G_CALLBACK(summary_tree_expanded), summaryview);
2933         summary_freeze(summaryview);
2934
2935         switch (sort_key) {
2936         case SORT_BY_MARK:
2937                 cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_mark;
2938                 break;
2939         case SORT_BY_STATUS:
2940                 cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_status;
2941                 break;
2942         case SORT_BY_MIME:
2943                 cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_mime;
2944                 break;
2945         case SORT_BY_NUMBER:
2946                 cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_num;
2947                 break;
2948         case SORT_BY_SIZE:
2949                 cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_size;
2950                 break;
2951         case SORT_BY_DATE:
2952                 cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_date;
2953                 break;
2954         case SORT_BY_THREAD_DATE:
2955                 cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_thread_date;
2956                 break;
2957         case SORT_BY_FROM:
2958                 cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_from;
2959                 break;
2960         case SORT_BY_SUBJECT:
2961 #ifndef G_OS_WIN32
2962                 if (summaryview->simplify_subject_preg)
2963                         cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_simplified_subject;
2964                 else
2965 #endif
2966                         cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_subject;
2967                 break;
2968         case SORT_BY_SCORE:
2969                 cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_score;
2970                 break;
2971         case SORT_BY_LABEL:
2972                 cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_label;
2973                 break;
2974         case SORT_BY_TO:
2975                 cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_to;
2976                 break;
2977         case SORT_BY_LOCKED:
2978                 cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_locked;
2979                 break;
2980         case SORT_BY_TAGS:
2981                 cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_tags;
2982                 break;
2983         case SORT_BY_NONE:
2984                 break;
2985         default:
2986                 goto unlock;
2987         }
2988
2989         summaryview->sort_key = sort_key;
2990         summaryview->sort_type = sort_type;
2991
2992         summary_set_column_titles(summaryview);
2993         summary_set_menu_sensitive(summaryview);
2994
2995         /* allow fallback to don't sort */
2996         if (summaryview->sort_key == SORT_BY_NONE)
2997                 goto unlock;
2998
2999         if (cmp_func != NULL) {
3000                 debug_print("Sorting summary...");
3001                 STATUSBAR_PUSH(summaryview->mainwin, _("Sorting summary..."));
3002
3003                 main_window_cursor_wait(summaryview->mainwin);
3004
3005                 gtk_cmclist_set_compare_func(clist, cmp_func);
3006
3007                 gtk_cmclist_set_sort_type(clist, (GtkSortType)sort_type);
3008                 gtk_sctree_sort_recursive(ctree, NULL);
3009
3010                 gtk_cmctree_node_moveto(ctree, summaryview->selected, 0, 0.5, 0);
3011
3012                 main_window_cursor_normal(summaryview->mainwin);
3013
3014                 debug_print("done.\n");
3015                 STATUSBAR_POP(summaryview->mainwin);
3016         }
3017 unlock:
3018         summary_thaw(summaryview);
3019         g_signal_handlers_unblock_by_func(G_OBJECT(summaryview->ctree),
3020                                        G_CALLBACK(summary_tree_expanded), summaryview);
3021         END_TIMING();
3022 }
3023
3024 static gboolean summary_update_thread_age(GNode *node, gpointer data)
3025 {
3026         MsgInfo *msginfo = node->data;
3027         time_t *most_recent = (time_t *)data;
3028
3029         if (msginfo->date_t > *most_recent) {
3030                 *most_recent = msginfo->date_t;
3031         }
3032         return FALSE;
3033 }       
3034
3035 static void summary_find_thread_age(GNode *gnode)
3036 {
3037         MsgInfo *msginfo = (MsgInfo *)gnode->data;
3038         time_t most_recent;
3039
3040         if (!msginfo)
3041                 return;
3042         most_recent = msginfo->thread_date = msginfo->date_t;
3043
3044         g_node_traverse(gnode, G_IN_ORDER, G_TRAVERSE_ALL, -1, summary_update_thread_age, &most_recent);
3045
3046         msginfo->thread_date = most_recent;
3047 }
3048
3049 static gboolean summary_insert_gnode_func(GtkCMCTree *ctree, guint depth, GNode *gnode,
3050                                    GtkCMCTreeNode *cnode, gpointer data)
3051 {
3052         SummaryView *summaryview = (SummaryView *)data;
3053         MsgInfo *msginfo = (MsgInfo *)gnode->data;
3054         gchar *text[N_SUMMARY_COLS];
3055         gint *col_pos = summaryview->col_pos;
3056         const gchar *msgid = msginfo->msgid;
3057         GHashTable *msgid_table = summaryview->msgid_table;
3058         gboolean vert = (prefs_common.layout_mode == VERTICAL_LAYOUT);
3059
3060         summary_set_header(summaryview, text, msginfo);
3061
3062         gtk_sctree_set_node_info(ctree, cnode, text[col_pos[S_COL_SUBJECT]], 2,
3063                                 NULL, NULL, NULL, NULL, FALSE, summaryview->threaded && !summaryview->thread_collapsed);
3064 #define SET_TEXT(col) {                                         \
3065         gtk_cmctree_node_set_text(ctree, cnode, col_pos[col],   \
3066                                 text[col_pos[col]]);            \
3067 }
3068
3069         if (summaryview->col_state[summaryview->col_pos[S_COL_NUMBER]].visible)
3070                 SET_TEXT(S_COL_NUMBER);
3071         if (summaryview->col_state[summaryview->col_pos[S_COL_SCORE]].visible)
3072                 SET_TEXT(S_COL_SCORE);
3073         if (summaryview->col_state[summaryview->col_pos[S_COL_SIZE]].visible)
3074                 SET_TEXT(S_COL_SIZE);
3075         if (summaryview->col_state[summaryview->col_pos[S_COL_DATE]].visible)
3076                 SET_TEXT(S_COL_DATE);
3077         if (summaryview->col_state[summaryview->col_pos[S_COL_FROM]].visible)
3078                 SET_TEXT(S_COL_FROM);
3079         if (summaryview->col_state[summaryview->col_pos[S_COL_TO]].visible)
3080                 SET_TEXT(S_COL_TO);
3081         if (summaryview->col_state[summaryview->col_pos[S_COL_TAGS]].visible)
3082                 SET_TEXT(S_COL_TAGS);
3083
3084         if (vert && prefs_common.two_line_vert)
3085                 g_free(text[summaryview->col_pos[S_COL_SUBJECT]]);
3086
3087 #undef SET_TEXT
3088
3089         GTKUT_CTREE_NODE_SET_ROW_DATA(cnode, msginfo);
3090         summary_set_marks_func(ctree, cnode, summaryview);
3091
3092         if (msgid && msgid[0] != '\0')
3093                 g_hash_table_insert(msgid_table, (gchar *)msgid, cnode);
3094
3095         return TRUE;
3096 }
3097
3098 static void summary_set_ctree_from_list(SummaryView *summaryview,
3099                                         GSList *mlist)
3100 {
3101         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
3102         MsgInfo *msginfo;
3103         GtkCMCTreeNode *node = NULL;
3104         GHashTable *msgid_table;
3105         GHashTable *subject_table = NULL;
3106         GSList * cur;
3107         gboolean vert = (prefs_common.layout_mode == VERTICAL_LAYOUT);
3108
3109         START_TIMING("");
3110         
3111         if (!mlist) return;
3112
3113         debug_print("\tSetting summary from message data...\n");
3114         STATUSBAR_PUSH(summaryview->mainwin,
3115                        _("Setting summary from message data..."));
3116         gdk_flush();
3117
3118         g_signal_handlers_block_by_func(G_OBJECT(ctree),
3119                                        G_CALLBACK(summary_tree_expanded), summaryview);
3120
3121         msgid_table = g_hash_table_new(g_str_hash, g_str_equal);
3122         summaryview->msgid_table = msgid_table;
3123
3124         if (prefs_common.thread_by_subject) {
3125                 subject_table = g_hash_table_new(g_str_hash, g_str_equal);
3126                 summaryview->subject_table = subject_table;
3127         } else {
3128                 summaryview->subject_table = NULL;
3129         }
3130
3131         if (prefs_common.use_addr_book)
3132                 start_address_completion(NULL);
3133         
3134         if (summaryview->threaded) {
3135                 GNode *root, *gnode;
3136                 START_TIMING("threaded");
3137                 root = procmsg_get_thread_tree(mlist);
3138
3139                 
3140                 for (gnode = root->children; gnode != NULL;
3141                      gnode = gnode->next) {
3142                         summary_find_thread_age(gnode);
3143                         node = gtk_sctree_insert_gnode
3144                                 (ctree, NULL, node, gnode,
3145                                  summary_insert_gnode_func, summaryview);
3146                 }
3147
3148                 g_node_destroy(root);
3149                 
3150                 END_TIMING();
3151         } else {
3152                 gchar *text[N_SUMMARY_COLS];
3153                 START_TIMING("unthreaded");
3154                 cur = mlist;
3155                 for (; mlist != NULL; mlist = mlist->next) {
3156                         msginfo = (MsgInfo *)mlist->data;
3157
3158                         summary_set_header(summaryview, text, msginfo);
3159
3160                         node = gtk_sctree_insert_node
3161                                 (ctree, NULL, node, text, 2,
3162                                  NULL, NULL, NULL, NULL,
3163                                  FALSE, FALSE);
3164                         if (vert && prefs_common.two_line_vert)
3165                                 g_free(text[summaryview->col_pos[S_COL_SUBJECT]]);
3166
3167                         GTKUT_CTREE_NODE_SET_ROW_DATA(node, msginfo);
3168                         summary_set_marks_func(ctree, node, summaryview);
3169
3170                         if (msginfo->msgid && msginfo->msgid[0] != '\0')
3171                                 g_hash_table_insert(msgid_table,
3172                                                     msginfo->msgid, node);
3173
3174                         if (prefs_common.thread_by_subject)
3175                                 subject_table_insert(subject_table,
3176                                              msginfo->subject,
3177                                              node);
3178                 }
3179                 mlist = cur;
3180                 END_TIMING();
3181         }
3182
3183         if (prefs_common.enable_hscrollbar &&
3184             summaryview->col_pos[S_COL_SUBJECT] == N_SUMMARY_COLS - 1) {
3185                 gint optimal_width;
3186
3187                 optimal_width = gtk_cmclist_optimal_column_width
3188                         (GTK_CMCLIST(ctree), summaryview->col_pos[S_COL_SUBJECT]);
3189                 gtk_cmclist_set_column_width(GTK_CMCLIST(ctree),
3190                                            summaryview->col_pos[S_COL_SUBJECT],
3191                                            optimal_width);
3192         }
3193
3194         if (prefs_common.use_addr_book)
3195                 end_address_completion();
3196
3197         debug_print("done.\n");
3198         STATUSBAR_POP(summaryview->mainwin);
3199         if (debug_get_mode()) {
3200                 debug_print("\tmsgid hash table size = %d\n",
3201                             g_hash_table_size(msgid_table));
3202                 if (prefs_common.thread_by_subject)
3203                         debug_print("\tsubject hash table size = %d\n",
3204                             g_hash_table_size(subject_table));
3205         }
3206
3207         summary_sort(summaryview, summaryview->sort_key, summaryview->sort_type);
3208
3209         node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
3210
3211         if (prefs_common.bold_unread) {
3212                 START_TIMING("bold_unread");
3213                 while (node) {
3214                         GtkCMCTreeNode *next = GTK_CMCTREE_NODE_NEXT(node);
3215                         if (GTK_CMCTREE_ROW(node)->children)
3216                                 summary_set_row_marks(summaryview, node);
3217                         node = next;
3218                 }
3219                 END_TIMING();
3220         }
3221
3222         g_signal_handlers_unblock_by_func(G_OBJECT(ctree),
3223                                        G_CALLBACK(summary_tree_expanded), summaryview);
3224         END_TIMING();
3225 }
3226
3227 static gchar *summary_complete_address(const gchar *addr)
3228 {
3229         gint count;
3230         gchar *res, *tmp, *email_addr;
3231         
3232         if (addr == NULL || !strchr(addr, '@'))
3233                 return NULL;
3234
3235         Xstrdup_a(email_addr, addr, return NULL);
3236         extract_address(email_addr);
3237         if (!*email_addr)
3238                 return NULL;
3239
3240         /*
3241          * completion stuff must be already initialized
3242          */
3243         res = NULL;
3244         if (1 < (count = complete_address(email_addr))) {
3245                 tmp = get_complete_address(1);
3246                 res = procheader_get_fromname(tmp);
3247                 g_free(tmp);
3248         }
3249
3250         return res;
3251 }
3252
3253 static inline void summary_set_header(SummaryView *summaryview, gchar *text[],
3254                                MsgInfo *msginfo)
3255 {
3256         static gchar date_modified[80];
3257         static gchar col_score[11];
3258         static gchar buf[BUFFSIZE], tmp1[BUFFSIZE], tmp2[BUFFSIZE], tmp3[BUFFSIZE];
3259         gint *col_pos = summaryview->col_pos;
3260         gchar *from_text = NULL, *to_text = NULL, *tags_text = NULL;
3261         gboolean should_swap = FALSE;
3262         gboolean vert = (prefs_common.layout_mode == VERTICAL_LAYOUT);
3263 #if GTK_CHECK_VERSION(2,12,0)
3264         static const gchar *color_dim_rgb = NULL;
3265         if (!color_dim_rgb)
3266                 color_dim_rgb = gdk_color_to_string(&summaryview->color_dim);
3267 #else
3268         static const gchar *color_dim_rgb = "#888888";
3269 #endif
3270         text[col_pos[S_COL_FROM]]   = "";
3271         text[col_pos[S_COL_TO]]     = "";
3272         text[col_pos[S_COL_SUBJECT]]= "";
3273         text[col_pos[S_COL_MARK]]   = "";
3274         text[col_pos[S_COL_STATUS]] = "";
3275         text[col_pos[S_COL_MIME]]   = "";
3276         text[col_pos[S_COL_LOCKED]] = "";
3277         text[col_pos[S_COL_DATE]]   = "";
3278         text[col_pos[S_COL_TAGS]]   = "";
3279         if (summaryview->col_state[summaryview->col_pos[S_COL_NUMBER]].visible)
3280                 text[col_pos[S_COL_NUMBER]] = itos(msginfo->msgnum);
3281         else
3282                 text[col_pos[S_COL_NUMBER]] = "";
3283
3284         /* slow! */
3285         if (summaryview->col_state[summaryview->col_pos[S_COL_SIZE]].visible)
3286                 text[col_pos[S_COL_SIZE]] = to_human_readable(msginfo->size);
3287         else
3288                 text[col_pos[S_COL_SIZE]] = "";
3289
3290         if (summaryview->col_state[summaryview->col_pos[S_COL_SCORE]].visible)
3291                 text[col_pos[S_COL_SCORE]] = itos_buf(col_score, msginfo->score);
3292         else
3293                 text[col_pos[S_COL_SCORE]] = "";
3294
3295         if (summaryview->col_state[summaryview->col_pos[S_COL_TAGS]].visible) {
3296                 tags_text = procmsg_msginfo_get_tags_str(msginfo);
3297                 if (!tags_text) {
3298                         text[col_pos[S_COL_TAGS]] = "-";
3299                 } else {
3300                         strncpy2(tmp1, tags_text, sizeof(tmp1));
3301                         tmp1[sizeof(tmp1)-1]='\0';
3302                         g_free(tags_text);
3303                         text[col_pos[S_COL_TAGS]] = tmp1;
3304                 }
3305         } else
3306                 text[col_pos[S_COL_TAGS]] = "";
3307
3308         /* slow! */
3309         if (summaryview->col_state[summaryview->col_pos[S_COL_DATE]].visible || 
3310             (vert && prefs_common.two_line_vert)) {
3311                 if (msginfo->date_t && msginfo->date_t > 0) {
3312                         procheader_date_get_localtime(date_modified,
3313                                                       sizeof(date_modified),
3314                                                       msginfo->date_t);
3315                         text[col_pos[S_COL_DATE]] = date_modified;
3316                 } else if (msginfo->date)
3317                         text[col_pos[S_COL_DATE]] = msginfo->date;
3318                 else
3319                         text[col_pos[S_COL_DATE]] = _("(No Date)");
3320         }
3321         
3322         if (prefs_common.swap_from && msginfo->from && msginfo->to
3323         &&  !summaryview->col_state[summaryview->col_pos[S_COL_TO]].visible) {
3324                 gchar *addr = NULL;
3325                 
3326                 addr = g_strdup(msginfo->from);
3327
3328                 if (addr) {
3329                         extract_address(addr);
3330                         if (account_find_from_address(addr, FALSE)) {
3331                                 should_swap = TRUE;
3332                         }
3333                         g_free(addr);
3334                 }
3335         }
3336
3337         if (!prefs_common.use_addr_book) {
3338                 from_text = msginfo->fromname ? 
3339                                 msginfo->fromname :
3340                                 _("(No From)");
3341         } else {
3342                 gchar *tmp = summary_complete_address(msginfo->from);
3343                 if (tmp) {
3344                         strncpy2(buf, tmp, sizeof(buf));
3345                         g_free(tmp);
3346                         from_text = buf;
3347                 } else {
3348                         from_text = (msginfo->fromname) ?
3349                                         msginfo->fromname: 
3350                                         _("(No From)");
3351                 }
3352         }
3353         
3354         to_text = msginfo->to ? msginfo->to : 
3355                    (msginfo->cc ? msginfo->cc :
3356                      (msginfo->newsgroups ? msginfo->newsgroups : _("(No Recipient)")
3357                      )
3358                    );
3359
3360         text[col_pos[S_COL_TO]] = to_text;
3361         if (!should_swap) {
3362                 text[col_pos[S_COL_FROM]] = from_text;
3363         } else {
3364                 snprintf(tmp2, BUFFSIZE-1, "--> %s", to_text);
3365                 tmp2[BUFFSIZE-1]='\0';
3366                 text[col_pos[S_COL_FROM]] = tmp2;
3367         }
3368         
3369 #ifndef G_OS_WIN32
3370         if (summaryview->simplify_subject_preg != NULL)
3371                 text[col_pos[S_COL_SUBJECT]] = msginfo->subject ? 
3372                         string_remove_match(tmp3, BUFFSIZE, msginfo->subject, 
3373                                         summaryview->simplify_subject_preg) : 
3374                         _("(No Subject)");
3375         else 
3376 #endif
3377                 text[col_pos[S_COL_SUBJECT]] = msginfo->subject ? msginfo->subject :
3378                         _("(No Subject)");
3379         if (vert && prefs_common.two_line_vert) {
3380                 if (!FOLDER_SHOWS_TO_HDR(summaryview->folder_item)) {
3381                         gchar *tmp = g_markup_printf_escaped(_("%s\n<span color='%s' style='italic'>From: %s, on %s</span>"),
3382                                         text[col_pos[S_COL_SUBJECT]],
3383                                         color_dim_rgb,
3384                                         text[col_pos[S_COL_FROM]],
3385                                         text[col_pos[S_COL_DATE]]);
3386                         text[col_pos[S_COL_SUBJECT]] = tmp;
3387                 } else {
3388                         gchar *tmp = g_markup_printf_escaped(_("%s\n<span color='%s' style='italic'>To: %s, on %s</span>"),
3389                                         text[col_pos[S_COL_SUBJECT]],
3390                                         color_dim_rgb,
3391                                         text[col_pos[S_COL_TO]],
3392                                         text[col_pos[S_COL_DATE]]);
3393                         text[col_pos[S_COL_SUBJECT]] = tmp;
3394                 }
3395         }
3396 }
3397
3398 static void summary_display_msg(SummaryView *summaryview, GtkCMCTreeNode *row)
3399 {
3400         summary_display_msg_full(summaryview, row, FALSE, FALSE);
3401 }
3402
3403 static gboolean defer_change(gpointer data);
3404 typedef struct _ChangeData {
3405         MsgInfo *info;
3406         gint op; /* 0, 1, 2 for unset, set, change */
3407         MsgPermFlags set_flags;
3408         MsgTmpFlags  set_tmp_flags;
3409         MsgPermFlags unset_flags;
3410         MsgTmpFlags  unset_tmp_flags;
3411 } ChangeData;
3412
3413 static void summary_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags flags, MsgTmpFlags tmp_flags)
3414 {
3415         if (!msginfo->folder || !msginfo->folder->processing_pending) {
3416                 debug_print("flags: doing unset now\n");
3417                 procmsg_msginfo_unset_flags(msginfo, flags, tmp_flags);
3418         } else {
3419                 ChangeData *unset_data = g_new0(ChangeData, 1);
3420                 unset_data->info = msginfo;
3421                 unset_data->op = 0;
3422                 unset_data->unset_flags = flags;
3423                 unset_data->unset_tmp_flags = tmp_flags;
3424                 debug_print("flags: deferring unset\n");
3425                 g_timeout_add(100, defer_change, unset_data);
3426         }
3427 }
3428
3429 static void summary_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags flags, MsgTmpFlags tmp_flags)
3430 {
3431         if (!msginfo->folder || !msginfo->folder->processing_pending) {
3432                 debug_print("flags: doing set now\n");
3433                 procmsg_msginfo_set_flags(msginfo, flags, tmp_flags);
3434         } else {
3435                 ChangeData *set_data = g_new0(ChangeData, 1);
3436                 set_data->info = msginfo;
3437                 set_data->op = 1;
3438                 set_data->set_flags = flags;
3439                 set_data->set_tmp_flags = tmp_flags;
3440                 debug_print("flags: deferring set\n");
3441                 g_timeout_add(100, defer_change, set_data);
3442         }
3443 }
3444
3445 static void summary_msginfo_change_flags(MsgInfo *msginfo, 
3446                 MsgPermFlags add_flags, MsgTmpFlags add_tmp_flags,
3447                 MsgPermFlags rem_flags, MsgTmpFlags rem_tmp_flags)
3448 {
3449         if (!msginfo->folder || !msginfo->folder->processing_pending) {
3450                 debug_print("flags: doing change now\n");
3451                 procmsg_msginfo_change_flags(msginfo, add_flags, add_tmp_flags,
3452                         rem_flags, rem_tmp_flags);
3453         } else {
3454                 ChangeData *change_data = g_new0(ChangeData, 1);
3455                 change_data->info = msginfo;
3456                 change_data->op = 2;
3457                 change_data->set_flags = add_flags;
3458                 change_data->set_tmp_flags = add_tmp_flags;
3459                 change_data->unset_flags = rem_flags;
3460                 change_data->unset_tmp_flags = rem_tmp_flags;
3461                 debug_print("flags: deferring change\n");
3462                 g_timeout_add(100, defer_change, change_data);
3463         }
3464 }
3465
3466 gboolean defer_change(gpointer data)
3467 {
3468         ChangeData *chg = (ChangeData *)data;
3469         if (chg->info->folder && chg->info->folder->processing_pending) {
3470                 debug_print("flags: trying later\n");
3471                 return TRUE; /* try again */
3472         } else {
3473                 debug_print("flags: finally doing it\n");
3474                 switch(chg->op) {
3475                 case 0:
3476                         procmsg_msginfo_unset_flags(chg->info, chg->unset_flags, chg->unset_tmp_flags);
3477                         break;
3478                 case 1:
3479                         procmsg_msginfo_set_flags(chg->info, chg->set_flags, chg->set_tmp_flags);
3480                         break;
3481                 case 2:
3482                         procmsg_msginfo_change_flags(chg->info, chg->set_flags, chg->set_tmp_flags,
3483                                 chg->unset_flags, chg->unset_tmp_flags);
3484                         break;
3485                 default:
3486                         g_warning("shouldn't happen\n");
3487                 }
3488                 g_free(chg);
3489         }
3490         return FALSE;
3491 }
3492
3493 static void msginfo_mark_as_read (SummaryView *summaryview, MsgInfo *msginfo,
3494                                       GtkCMCTreeNode *row)
3495 {
3496         g_return_if_fail(summaryview != NULL);
3497         g_return_if_fail(msginfo != NULL);
3498         g_return_if_fail(row != NULL);
3499
3500         if (MSG_IS_NEW(msginfo->flags) || MSG_IS_UNREAD(msginfo->flags)) {
3501                 summary_msginfo_unset_flags
3502                         (msginfo, MSG_NEW | MSG_UNREAD, 0);
3503                 summary_set_row_marks(summaryview, row);
3504                 summary_status_show(summaryview);
3505         }
3506 }
3507
3508 typedef struct  {
3509         MsgInfo *msginfo;
3510         SummaryView *summaryview;
3511 } MarkAsReadData;
3512
3513 static int msginfo_mark_as_read_timeout(void *data)
3514 {
3515         MarkAsReadData *mdata = (MarkAsReadData *)data;
3516         if (!mdata)
3517                 return FALSE;
3518         
3519         if (mdata->msginfo == summary_get_selected_msg(mdata->summaryview))
3520                 msginfo_mark_as_read(mdata->summaryview, mdata->msginfo,
3521                                      mdata->summaryview->selected); 
3522
3523         g_free(mdata);
3524
3525         return FALSE;   
3526 }
3527
3528 static void summary_display_msg_full(SummaryView *summaryview,
3529                                      GtkCMCTreeNode *row,
3530                                      gboolean new_window, gboolean all_headers)
3531 {
3532         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
3533         MsgInfo *msginfo;
3534         gint val;
3535         START_TIMING("");
3536         if (!new_window) {
3537                 if (summaryview->displayed == row)
3538                         return;
3539                 else if (summaryview->messageview)
3540                         summaryview->messageview->filtered = FALSE;
3541         }                       
3542         g_return_if_fail(row != NULL);
3543
3544         if (summary_is_locked(summaryview)) return;
3545         summary_lock(summaryview);
3546
3547         STATUSBAR_POP(summaryview->mainwin);
3548         GTK_EVENTS_FLUSH();
3549
3550         msginfo = gtk_cmctree_node_get_row_data(ctree, row);
3551         
3552         g_return_if_fail(msginfo);
3553
3554         if (new_window && prefs_common.layout_mode != SMALL_LAYOUT) {
3555                 MessageView *msgview;
3556
3557                 msgview = messageview_create_with_new_window(summaryview->mainwin);
3558                 val = messageview_show(msgview, msginfo, all_headers);
3559         } else {
3560                 MessageView *msgview;
3561
3562                 if (prefs_common.layout_mode == SMALL_LAYOUT) {
3563                         if (summaryview->ext_messageview == NULL)
3564                                 summaryview->ext_messageview = messageview_create_with_new_window(summaryview->mainwin);
3565                         else
3566                                 gtkut_window_popup(summaryview->ext_messageview->window);
3567                         msgview = summaryview->ext_messageview;
3568                         summaryview->last_displayed = summaryview->displayed;
3569                         summaryview->displayed = row;
3570                         val = messageview_show(msgview, msginfo, all_headers);
3571                         if (GTK_CMCLIST(msgview->mimeview->ctree)->row_list == NULL)
3572                                 gtk_widget_grab_focus(summaryview->ctree);
3573                         gtkut_ctree_node_move_if_on_the_edge(ctree, row,
3574                                 GTK_CMCLIST(summaryview->ctree)->focus_row);
3575                 } else {
3576                         msgview = summaryview->messageview;
3577                         summaryview->last_displayed = summaryview->displayed;
3578                         summaryview->displayed = row;
3579                         if (!messageview_is_visible(msgview)) {
3580                                 main_window_toggle_message_view(summaryview->mainwin);
3581                                 GTK_EVENTS_FLUSH();
3582                         }
3583                         val = messageview_show(msgview, msginfo, all_headers);
3584                         if (GTK_CMCLIST(msgview->mimeview->ctree)->row_list == NULL)
3585                                 gtk_widget_grab_focus(summaryview->ctree);
3586                         gtkut_ctree_node_move_if_on_the_edge(ctree, row,
3587                                 GTK_CMCLIST(summaryview->ctree)->focus_row);
3588                 }
3589         }
3590
3591         if (val == 0 && MSG_IS_UNREAD(msginfo->flags)) {
3592                 if (!prefs_common.mark_as_read_on_new_window &&
3593                     prefs_common.mark_as_read_delay) {
3594                         MarkAsReadData *data = g_new0(MarkAsReadData, 1);
3595                         data->summaryview = summaryview;
3596                         data->msginfo = msginfo;
3597 #if GLIB_CHECK_VERSION(2,14,0)
3598                         g_timeout_add_seconds(prefs_common.mark_as_read_delay,
3599                                 msginfo_mark_as_read_timeout, data);
3600 #else
3601                         g_timeout_add(prefs_common.mark_as_read_delay * 1000,
3602                                 msginfo_mark_as_read_timeout, data);
3603 #endif
3604                 } else if (new_window || !prefs_common.mark_as_read_on_new_window) {
3605                         msginfo_mark_as_read(summaryview, msginfo, row);
3606                 }
3607         }
3608
3609         summary_set_menu_sensitive(summaryview);
3610         toolbar_main_set_sensitive(summaryview->mainwin);
3611         messageview_set_menu_sensitive(summaryview->messageview);
3612
3613         summary_unlock(summaryview);
3614         END_TIMING();
3615 }
3616
3617 void summary_display_msg_selected(SummaryView *summaryview,
3618                                   gboolean all_headers)
3619 {
3620         if (summary_is_locked(summaryview)) return;
3621         summaryview->displayed = NULL;
3622         summary_display_msg_full(summaryview, summaryview->selected, FALSE,
3623                                  all_headers);
3624 }
3625
3626 void summary_redisplay_msg(SummaryView *summaryview)
3627 {
3628         GtkCMCTreeNode *node;
3629
3630         if (summaryview->displayed) {
3631                 node = summaryview->displayed;
3632                 summaryview->displayed = NULL;
3633                 summary_display_msg(summaryview, node);
3634         }
3635 }
3636
3637 void summary_open_msg(SummaryView *summaryview)
3638 {
3639         if (!summaryview->selected) return;
3640         
3641         /* CLAWS: if separate message view, don't open a new window
3642          * but rather use the current separated message view */
3643         summary_display_msg_full(summaryview, summaryview->selected, 
3644                                  TRUE, FALSE);
3645 }
3646
3647 void summary_view_source(SummaryView * summaryview)
3648 {
3649         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
3650         MsgInfo *msginfo;
3651         SourceWindow *srcwin;
3652
3653         if (!summaryview->selected) return;
3654
3655         srcwin = source_window_create();
3656         msginfo = gtk_cmctree_node_get_row_data(ctree, summaryview->selected);
3657         source_window_show_msg(srcwin, msginfo);
3658         source_window_show(srcwin);
3659 #ifdef MAEMO
3660         maemo_window_full_screen_if_needed(GTK_WINDOW(srcwin->window));
3661         maemo_connect_key_press_to_mainwindow(GTK_WINDOW(srcwin->window));
3662 #endif
3663 }
3664
3665 void summary_reedit(SummaryView *summaryview)
3666 {
3667         MsgInfo *msginfo;
3668
3669         if (!summaryview->selected) return;
3670         if (!summaryview->folder_item) return;
3671         if (!FOLDER_SHOWS_TO_HDR(summaryview->folder_item))
3672                 return;
3673
3674         msginfo = gtk_cmctree_node_get_row_data(GTK_CMCTREE(summaryview->ctree),
3675                                               summaryview->selected);
3676         if (!msginfo) return;
3677
3678         compose_reedit(msginfo, FALSE);
3679 }
3680
3681 gboolean summary_step(SummaryView *summaryview, GtkScrollType type)
3682 {
3683         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
3684         GtkCMCTreeNode *node;
3685
3686         if (summary_is_locked(summaryview)
3687         && !GTK_SCTREE(ctree)->selecting_range
3688         && summaryview->messageview->mimeview
3689         && summaryview->messageview->mimeview->type == MIMEVIEW_TEXT
3690         && summaryview->messageview->mimeview->textview->loading) {
3691                 PostponedSelectData *data = g_new0(PostponedSelectData, 1);
3692                 summaryview->messageview->mimeview->textview->stop_loading = TRUE;
3693                 
3694                 data->ctree = ctree;
3695                 data->row = NULL;
3696                 data->node = NULL;
3697                 data->type = type;
3698                 data->summaryview = summaryview;
3699                 debug_print("postponing open of message till end of load\n");
3700                 g_timeout_add(100, summary_select_retry, data);
3701                 return FALSE;
3702         }
3703         if (summary_is_locked(summaryview))
3704                 return FALSE;
3705         if (type == GTK_SCROLL_STEP_FORWARD) {
3706                 node = gtkut_ctree_node_next(ctree, summaryview->selected);
3707                 if (node)
3708                         gtkut_ctree_expand_parent_all(ctree, node);
3709                 else
3710                         return FALSE;
3711         } else {
3712                 if (summaryview->selected) {
3713                         node = GTK_CMCTREE_NODE_PREV(summaryview->selected);
3714                         if (!node) return FALSE;
3715                 }
3716         }
3717
3718         if (messageview_is_visible(summaryview->messageview))
3719                 summaryview->display_msg = TRUE;
3720
3721         g_signal_emit_by_name(G_OBJECT(ctree), "scroll_vertical", type, 0.0);
3722
3723         if (GTK_CMCLIST(ctree)->selection)
3724                 gtk_sctree_set_anchor_row
3725                         (GTK_SCTREE(ctree),
3726                          GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->selection->data));
3727
3728         return TRUE;
3729 }
3730
3731 gboolean summary_is_list(SummaryView *summaryview)
3732 {
3733         return (gtk_notebook_get_current_page(
3734                 GTK_NOTEBOOK(summaryview->mainwidget_book)) == 0);
3735 }
3736
3737 void summary_toggle_view(SummaryView *summaryview)
3738 {
3739         if (prefs_common.layout_mode == SMALL_LAYOUT)
3740                 return;
3741         if (!messageview_is_visible(summaryview->messageview) &&
3742             summaryview->selected && summary_is_list(summaryview))
3743                 summary_display_msg(summaryview,
3744                                     summaryview->selected);
3745         else
3746                 main_window_toggle_message_view(summaryview->mainwin);
3747 }
3748
3749 static gboolean summary_search_unread_recursive(GtkCMCTree *ctree,
3750                                                 GtkCMCTreeNode *node)
3751 {
3752         MsgInfo *msginfo;
3753
3754         if (node) {
3755                 msginfo = gtk_cmctree_node_get_row_data(ctree, node);
3756                 if (msginfo && MSG_IS_UNREAD(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
3757                         return TRUE;
3758                 node = GTK_CMCTREE_ROW(node)->children;
3759         } else
3760                 node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
3761
3762         while (node) {
3763                 if (summary_search_unread_recursive(ctree, node) == TRUE)
3764                         return TRUE;
3765                 node = GTK_CMCTREE_ROW(node)->sibling;
3766         }
3767
3768         return FALSE;
3769 }
3770
3771 static gboolean summary_have_unread_children(SummaryView *summaryview,
3772                                              GtkCMCTreeNode *node)
3773 {
3774         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
3775
3776         if (!node) return FALSE;
3777
3778         node = GTK_CMCTREE_ROW(node)->children;
3779
3780         while (node) {
3781                 if (summary_search_unread_recursive(ctree, node) == TRUE)
3782                         return TRUE;
3783                 node = GTK_CMCTREE_ROW(node)->sibling;
3784         }
3785         return FALSE;
3786 }
3787
3788 static void summary_set_row_marks(SummaryView *summaryview, GtkCMCTreeNode *row)
3789 {
3790         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
3791         GtkStyle *style = NULL;
3792         MsgInfo *msginfo;
3793         MsgFlags flags;
3794         gint *col_pos = summaryview->col_pos;
3795
3796         msginfo = gtk_cmctree_node_get_row_data(ctree, row);
3797         if (!msginfo) return;
3798
3799         flags = msginfo->flags;
3800
3801         gtk_cmctree_node_set_foreground(ctree, row, NULL);
3802
3803         /* set new/unread column */
3804         if (MSG_IS_IGNORE_THREAD(flags)) {
3805                 gtk_cmctree_node_set_pixmap(ctree, row, col_pos[S_COL_STATUS],
3806                                           ignorethreadxpm, ignorethreadxpmmask);
3807         } else if (MSG_IS_WATCH_THREAD(flags)) {
3808                 gtk_cmctree_node_set_pixmap(ctree, row, col_pos[S_COL_STATUS],
3809                                           watchthreadxpm, watchthreadxpmmask);
3810         } else if (MSG_IS_SPAM(flags)) {
3811                 gtk_cmctree_node_set_pixmap(ctree, row, col_pos[S_COL_STATUS],
3812                                           spamxpm, spamxpmmask);
3813         } else if (MSG_IS_NEW(flags)) {
3814                 gtk_cmctree_node_set_pixmap(ctree, row, col_pos[S_COL_STATUS],
3815                                           newxpm, newxpmmask);
3816         } else if (MSG_IS_UNREAD(flags)) {
3817                 gtk_cmctree_node_set_pixmap(ctree, row, col_pos[S_COL_STATUS],
3818                                           unreadxpm, unreadxpmmask);
3819         } else if (MSG_IS_REPLIED(flags)) {
3820                 gtk_cmctree_node_set_pixmap(ctree, row, col_pos[S_COL_STATUS],
3821                                           repliedxpm, repliedxpmmask);
3822         } else if (MSG_IS_FORWARDED(flags)) {
3823                 gtk_cmctree_node_set_pixmap(ctree, row, col_pos[S_COL_STATUS],
3824                                           forwardedxpm, forwardedxpmmask);
3825         } else {
3826                 gtk_cmctree_node_set_text(ctree, row, col_pos[S_COL_STATUS],
3827                                         "");
3828         }
3829
3830         if (prefs_common.bold_unread &&
3831             ((MSG_IS_UNREAD(flags) && !MSG_IS_IGNORE_THREAD(flags)) ||
3832              (!GTK_CMCTREE_ROW(row)->expanded &&
3833               GTK_CMCTREE_ROW(row)->children &&
3834               summary_have_unread_children(summaryview, row))))
3835                 style = bold_style;
3836
3837         /* set mark column */
3838         if (MSG_IS_DELETED(flags)) {
3839                 gtk_cmctree_node_set_pixmap(ctree, row, col_pos[S_COL_MARK],
3840                                           deletedxpm, deletedxpmmask);
3841                 if (style)
3842                         style = bold_deleted_style;
3843                 else {
3844                         style = small_deleted_style;
3845                 }
3846                         gtk_cmctree_node_set_foreground
3847                                 (ctree, row, &summaryview->color_dim);
3848         } else if (MSG_IS_MARKED(flags)) {
3849                 gtk_cmctree_node_set_pixmap(ctree, row, col_pos[S_COL_MARK],
3850                                           markxpm, markxpmmask);
3851         } else if (MSG_IS_MOVE(flags)) {
3852                 gtk_cmctree_node_set_pixmap(ctree, row, col_pos[S_COL_MARK],
3853                                           movedxpm, movedxpmmask);
3854                 if (style)
3855                         style = bold_marked_style;
3856                 else {
3857                         style = small_marked_style;
3858                 }
3859                         gtk_cmctree_node_set_foreground
3860                                 (ctree, row, &summaryview->color_marked);
3861         } else if (MSG_IS_COPY(flags)) {
3862                 gtk_cmctree_node_set_pixmap(ctree, row, col_pos[S_COL_MARK],
3863                                           copiedxpm, copiedxpmmask);
3864                 if (style)
3865                         style = bold_marked_style;
3866                 else {
3867                         style = small_marked_style;
3868                 }
3869                         gtk_cmctree_node_set_foreground
3870                                 (ctree, row, &summaryview->color_marked);
3871         } else {
3872                 gtk_cmctree_node_set_text(ctree, row, col_pos[S_COL_MARK], "");
3873         }
3874
3875         if (MSG_IS_LOCKED(flags)) {
3876                 gtk_cmctree_node_set_pixmap(ctree, row, col_pos[S_COL_LOCKED],
3877                                           lockedxpm, lockedxpmmask);
3878         }
3879         else {
3880                 gtk_cmctree_node_set_text(ctree, row, col_pos[S_COL_LOCKED], "");
3881         }
3882
3883         if (MSG_IS_WITH_ATTACHMENT(flags) && MSG_IS_SIGNED(flags)) {
3884                 gtk_cmctree_node_set_pixmap(ctree, row, col_pos[S_COL_MIME],
3885                                           clipgpgsignedxpm, clipgpgsignedxpmmask);
3886         } else if (MSG_IS_SIGNED(flags)) {
3887                 if (MSG_IS_ENCRYPTED(flags)) {
3888                         gtk_cmctree_node_set_pixmap(ctree, row, col_pos[S_COL_MIME],
3889                                           keysignxpm, keysignxpmmask);
3890                 } else {
3891                         gtk_cmctree_node_set_pixmap(ctree, row, col_pos[S_COL_MIME],
3892                                                   gpgsignedxpm, gpgsignedxpmmask);
3893                 }
3894         } else if (MSG_IS_WITH_ATTACHMENT(flags) && MSG_IS_ENCRYPTED(flags)) {
3895                 gtk_cmctree_node_set_pixmap(ctree, row, col_pos[S_COL_MIME],
3896                                           clipkeyxpm, clipkeyxpmmask);
3897         } else if (MSG_IS_ENCRYPTED(flags)) {
3898                 gtk_cmctree_node_set_pixmap(ctree, row, col_pos[S_COL_MIME],
3899                                           keyxpm, keyxpmmask);
3900         } else if (MSG_IS_WITH_ATTACHMENT(flags)) {
3901                 gtk_cmctree_node_set_pixmap(ctree, row, col_pos[S_COL_MIME],
3902                                           clipxpm, clipxpmmask);
3903         } else {
3904                 gtk_cmctree_node_set_text(ctree, row, col_pos[S_COL_MIME], "");
3905         }
3906         if (!style)
3907                 style = small_style;
3908
3909         gtk_cmctree_node_set_row_style(ctree, row, style);
3910
3911         if (MSG_GET_COLORLABEL(flags))
3912                 summary_set_colorlabel_color(ctree, row, MSG_GET_COLORLABEL_VALUE(flags));
3913 }
3914
3915 static void summary_mark_row(SummaryView *summaryview, GtkCMCTreeNode *row)
3916 {
3917         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
3918         MsgInfo *msginfo;
3919
3920         msginfo = gtk_cmctree_node_get_row_data(ctree, row);
3921         g_return_if_fail(msginfo);
3922         if (MSG_IS_DELETED(msginfo->flags))
3923                 summaryview->deleted--;
3924         if (MSG_IS_MOVE(msginfo->flags))
3925                 summaryview->moved--;
3926         if (MSG_IS_COPY(msginfo->flags))
3927                 summaryview->copied--;
3928
3929         procmsg_msginfo_set_to_folder(msginfo, NULL);
3930         summary_msginfo_change_flags(msginfo, MSG_MARKED, 0, MSG_DELETED, 
3931                 MSG_MOVE | MSG_COPY | MSG_MOVE_DONE);
3932         summary_set_row_marks(summaryview, row);
3933         debug_print("Message %s/%d is marked\n", msginfo->folder->path, msginfo->msgnum);
3934 }
3935
3936 static void summary_lock_row(SummaryView *summaryview, GtkCMCTreeNode *row)
3937 {
3938         gboolean changed = FALSE;
3939         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
3940         MsgInfo *msginfo;
3941
3942         msginfo = gtk_cmctree_node_get_row_data(ctree, row);
3943         g_return_if_fail(msginfo);
3944         if (MSG_IS_DELETED(msginfo->flags))
3945                 summaryview->deleted--;
3946         if (MSG_IS_MOVE(msginfo->flags)) {
3947                 summaryview->moved--;
3948                 changed = TRUE;
3949         }
3950         if (MSG_IS_COPY(msginfo->flags)) {
3951                 summaryview->copied--;
3952                 changed = TRUE;
3953         }
3954         procmsg_msginfo_set_to_folder(msginfo, NULL);
3955         summary_msginfo_change_flags(msginfo, MSG_LOCKED, 0, MSG_DELETED, 
3956                 MSG_MOVE | MSG_COPY | MSG_MOVE_DONE);
3957         
3958         summary_set_row_marks(summaryview, row);
3959         debug_print("Message %d is locked\n", msginfo->msgnum);
3960 }
3961
3962 static void summary_unlock_row(SummaryView *summaryview, GtkCMCTreeNode *row)
3963 {
3964         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
3965         MsgInfo *msginfo;
3966
3967         msginfo = gtk_cmctree_node_get_row_data(ctree, row);
3968         g_return_if_fail(msginfo);
3969         if (!MSG_IS_LOCKED(msginfo->flags))
3970                 return;
3971         procmsg_msginfo_set_to_folder(msginfo, NULL);
3972         summary_msginfo_unset_flags(msginfo, MSG_LOCKED, 0);
3973         summary_set_row_marks(summaryview, row);
3974         debug_print("Message %d is unlocked\n", msginfo->msgnum);
3975 }
3976
3977 void summary_mark(SummaryView *summaryview)
3978 {
3979         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
3980         GList *cur;
3981         gboolean froze = FALSE;
3982
3983         if (summary_is_locked(summaryview))
3984                 return;
3985         START_LONG_OPERATION(summaryview, FALSE);
3986         folder_item_set_batch(summaryview->folder_item, TRUE);
3987         for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
3988                 summary_mark_row(summaryview, GTK_CMCTREE_NODE(cur->data));
3989         folder_item_set_batch(summaryview->folder_item, FALSE);
3990         END_LONG_OPERATION(summaryview);
3991
3992         summary_status_show(summaryview);
3993 }
3994
3995 static void summary_mark_row_as_read(SummaryView *summaryview,
3996                                      GtkCMCTreeNode *row)
3997 {
3998         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
3999         MsgInfo *msginfo;
4000
4001         msginfo = gtk_cmctree_node_get_row_data(ctree, row);
4002         g_return_if_fail(msginfo);
4003
4004         if(!(MSG_IS_NEW(msginfo->flags) || MSG_IS_UNREAD(msginfo->flags)))
4005                 return;
4006
4007         summary_msginfo_unset_flags(msginfo, MSG_NEW | MSG_UNREAD, 0);
4008         summary_set_row_marks(summaryview, row);
4009         debug_print("Message %d is marked as read\n",
4010                 msginfo->msgnum);
4011 }
4012
4013 void summary_mark_as_read(SummaryView *summaryview)
4014 {
4015         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4016         GList *cur;
4017         gboolean froze = FALSE;
4018
4019         if (summary_is_locked(summaryview))
4020                 return;
4021         START_LONG_OPERATION(summaryview, FALSE);
4022         folder_item_set_batch(summaryview->folder_item, TRUE);
4023         for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
4024                 summary_mark_row_as_read(summaryview,
4025                                          GTK_CMCTREE_NODE(cur->data));
4026         folder_item_set_batch(summaryview->folder_item, FALSE);
4027         END_LONG_OPERATION(summaryview);
4028         
4029         summary_status_show(summaryview);
4030 }
4031
4032 void summary_msgs_lock(SummaryView *summaryview)
4033 {
4034         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4035         GList *cur;
4036         gboolean froze = FALSE;
4037
4038         if (summary_is_locked(summaryview))
4039                 return;
4040         START_LONG_OPERATION(summaryview, FALSE);
4041         for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
4042                 summary_lock_row(summaryview,
4043                                          GTK_CMCTREE_NODE(cur->data));
4044         END_LONG_OPERATION(summaryview);
4045         
4046         summary_status_show(summaryview);
4047 }
4048
4049 void summary_msgs_unlock(SummaryView *summaryview)
4050 {
4051         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4052         GList *cur;
4053         gboolean froze = FALSE;
4054
4055         if (summary_is_locked(summaryview))
4056                 return;
4057         START_LONG_OPERATION(summaryview, FALSE);
4058         for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
4059                 summary_unlock_row(summaryview,
4060                                    GTK_CMCTREE_NODE(cur->data));
4061         END_LONG_OPERATION(summaryview);
4062         
4063         summary_status_show(summaryview);
4064 }
4065
4066 void summary_mark_all_read(SummaryView *summaryview)
4067 {
4068         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4069         GtkCMCTreeNode *node;
4070         AlertValue val;
4071         gboolean froze = FALSE;
4072
4073         if (prefs_common.ask_mark_all_read) {
4074                 val = alertpanel_full(_("Mark all as read"),
4075                         _("Do you really want to mark all mails in this "
4076                           "folder as read ?"), GTK_STOCK_NO, GTK_STOCK_YES, NULL,
4077                           TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
4078
4079                 if ((val & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
4080                         return;
4081                 else if (val & G_ALERTDISABLE)
4082                         prefs_common.ask_mark_all_read = FALSE;
4083         }
4084         
4085         if (summary_is_locked(summaryview))
4086                 return;
4087         START_LONG_OPERATION(summaryview, TRUE);
4088         folder_item_set_batch(summaryview->folder_item, TRUE);
4089         for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list); node != NULL;
4090              node = gtkut_ctree_node_next(ctree, node))
4091                 summary_mark_row_as_read(summaryview, node);
4092         folder_item_set_batch(summaryview->folder_item, FALSE);
4093         for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list); node != NULL;
4094              node = gtkut_ctree_node_next(ctree, node)) {
4095                 if (!GTK_CMCTREE_ROW(node)->expanded)
4096                         summary_set_row_marks(summaryview, node);
4097         }
4098         END_LONG_OPERATION(summaryview);
4099         
4100         summary_status_show(summaryview);
4101 }
4102
4103 void summary_mark_as_spam(SummaryView *summaryview, guint action, GtkWidget *widget)
4104 {
4105         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4106         GList *cur;
4107         gboolean is_spam = action;
4108         GSList *msgs = NULL;
4109         gboolean immediate_exec = prefs_common.immediate_exec;
4110         gboolean moved = FALSE;
4111         gboolean froze = FALSE;
4112
4113
4114         if (summary_is_locked(summaryview))
4115                 return;
4116
4117         prefs_common.immediate_exec = FALSE;
4118         START_LONG_OPERATION(summaryview, FALSE);
4119         folder_item_set_batch(summaryview->folder_item, TRUE);
4120         for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next) {
4121                 GtkCMCTreeNode *row = GTK_CMCTREE_NODE(cur->data);
4122                 MsgInfo *msginfo = gtk_cmctree_node_get_row_data(ctree, row);
4123                 if (msginfo)
4124                         msgs = g_slist_prepend(msgs, msginfo);
4125         }
4126         
4127         if (procmsg_spam_learner_learn(NULL, msgs, is_spam) == 0) {
4128                 for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next) {
4129                         GtkCMCTreeNode *row = GTK_CMCTREE_NODE(cur->data);
4130                         MsgInfo *msginfo = gtk_cmctree_node_get_row_data(ctree, row);
4131                         if (!msginfo)
4132                                 continue;
4133                         if (is_spam) {
4134                                 summary_msginfo_change_flags(msginfo, MSG_SPAM, 0, MSG_NEW|MSG_UNREAD, 0);
4135                                 if (procmsg_spam_get_folder(msginfo) != summaryview->folder_item) {
4136                                         summary_move_row_to(summaryview, row,
4137                                                         procmsg_spam_get_folder(msginfo));
4138                                         moved = TRUE;
4139                                 }
4140                         } else {
4141                                 summary_msginfo_unset_flags(msginfo, MSG_SPAM, 0);
4142                         }
4143                         summaryview->display_msg = prefs_common.always_show_msg;
4144         
4145                         summary_set_row_marks(summaryview, row);
4146                 }
4147         } else {
4148                 log_error(LOG_PROTOCOL, _("An error happened while learning.\n"));
4149         }
4150
4151         prefs_common.immediate_exec = immediate_exec;
4152         folder_item_set_batch(summaryview->folder_item, FALSE);
4153         END_LONG_OPERATION(summaryview);
4154
4155         if (prefs_common.immediate_exec && moved) {
4156                 summary_execute(summaryview);
4157         }
4158
4159         if (!moved && msgs) {
4160                 MsgInfo *msginfo = (MsgInfo *)msgs->data;
4161                 toolbar_set_learn_button
4162                         (summaryview->mainwin->toolbar,
4163                          MSG_IS_SPAM(msginfo->flags)?LEARN_HAM:LEARN_SPAM);
4164         }
4165         g_slist_free(msgs);
4166         
4167         summary_status_show(summaryview);       
4168 }
4169
4170
4171 static void summary_mark_row_as_unread(SummaryView *summaryview,
4172                                        GtkCMCTreeNode *row)
4173 {
4174         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4175         MsgInfo *msginfo;
4176
4177         msginfo = gtk_cmctree_node_get_row_data(ctree, row);
4178         g_return_if_fail(msginfo);
4179         if (MSG_IS_DELETED(msginfo->flags)) {
4180                 procmsg_msginfo_set_to_folder(msginfo, NULL);
4181                 summary_msginfo_unset_flags(msginfo, MSG_DELETED, 0);
4182                 summaryview->deleted--;
4183         }
4184
4185         summary_msginfo_set_flags(msginfo, MSG_UNREAD, 0);
4186         debug_print("Message %d is marked as unread\n",
4187                 msginfo->msgnum);
4188
4189         summary_set_row_marks(summaryview, row);
4190 }
4191
4192 void summary_mark_as_unread(SummaryView *summaryview)
4193 {
4194         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4195         GList *cur;
4196         gboolean froze = FALSE;
4197
4198         if (summary_is_locked(summaryview))
4199                 return;
4200         START_LONG_OPERATION(summaryview, FALSE);
4201         folder_item_set_batch(summaryview->folder_item, TRUE);
4202         for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; 
4203                 cur = cur->next)
4204                 summary_mark_row_as_unread(summaryview,
4205                                            GTK_CMCTREE_NODE(cur->data));
4206         folder_item_set_batch(summaryview->folder_item, FALSE);
4207         END_LONG_OPERATION(summaryview);
4208         
4209         summary_status_show(summaryview);
4210 }
4211
4212 static gboolean check_permission(SummaryView *summaryview, MsgInfo * msginfo)
4213 {
4214         GList * cur;
4215         gboolean found;
4216
4217         switch (FOLDER_TYPE(summaryview->folder_item->folder)) {
4218
4219         case F_NEWS:
4220
4221                 /*
4222                   security : checks if one the accounts correspond to
4223                   the author of the post
4224                 */
4225
4226                 found = FALSE;
4227                 for(cur = account_get_list() ; cur != NULL ; cur = cur->next) {
4228                         PrefsAccount * account;
4229                         gchar * from_name;
4230                         
4231                         account = cur->data;
4232                         if (account->name && *account->name)
4233                                 from_name =
4234                                         g_strdup_printf("%s <%s>",
4235                                                         account->name,
4236                                                         account->address);
4237                         else
4238                                 from_name =
4239                                         g_strdup_printf("%s",
4240                                                         account->address);
4241                         
4242                         if (g_utf8_collate(from_name, msginfo->from) == 0) {
4243                                 g_free(from_name);
4244                                 found = TRUE;
4245                                 break;
4246                         }
4247                         g_free(from_name);
4248                 }
4249
4250                 if (!found) {
4251                         alertpanel_error(_("You're not the author of the article.\n"));
4252                 }
4253                 
4254                 return found;
4255
4256         default:
4257                 return TRUE;
4258         }
4259 }
4260
4261 static void summary_delete_row(SummaryView *summaryview, GtkCMCTreeNode *row)
4262 {
4263         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4264         MsgInfo *msginfo;
4265
4266         msginfo = gtk_cmctree_node_get_row_data(ctree, row);
4267         g_return_if_fail(msginfo);
4268
4269         if (MSG_IS_LOCKED(msginfo->flags)) return;
4270
4271         if (MSG_IS_DELETED(msginfo->flags)) return;
4272
4273         if (MSG_IS_MOVE(msginfo->flags))
4274                 summaryview->moved--;
4275         if (MSG_IS_COPY(msginfo->flags))
4276                 summaryview->copied--;
4277
4278         procmsg_msginfo_set_to_folder(msginfo, NULL);
4279         summary_msginfo_change_flags(msginfo, MSG_DELETED, 0, MSG_MARKED, 
4280                 MSG_MOVE | MSG_COPY | MSG_MOVE_DONE);
4281         summaryview->deleted++;
4282
4283         if (!prefs_common.immediate_exec && 
4284             !folder_has_parent_of_type(summaryview->folder_item, F_TRASH))
4285                 summary_set_row_marks(summaryview, row);
4286
4287         debug_print("Message %s/%d is set to delete\n",
4288                     msginfo->folder->path, msginfo->msgnum);
4289 }
4290
4291 void summary_cancel(SummaryView *summaryview)
4292 {
4293         MsgInfo * msginfo;
4294
4295         msginfo = gtk_cmctree_node_get_row_data(GTK_CMCTREE(summaryview->ctree),
4296                                               summaryview->selected);
4297         if (!msginfo) return;
4298
4299         if (!check_permission(summaryview, msginfo))
4300                 return;
4301
4302         news_cancel_article(summaryview->folder_item->folder, msginfo);
4303         
4304         if (summary_is_locked(summaryview)) return;
4305
4306         summary_lock(summaryview);
4307
4308         summary_freeze(summaryview);
4309
4310         summary_update_status(summaryview);
4311         summary_status_show(summaryview);
4312
4313         summary_thaw(summaryview);
4314
4315         summary_unlock(summaryview);
4316 }
4317
4318 void summary_delete(SummaryView *summaryview)
4319 {
4320         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4321         FolderItem *item = summaryview->folder_item;
4322         GList *cur;
4323         GtkCMCTreeNode *sel_last = NULL;
4324         GtkCMCTreeNode *node;
4325         AlertValue aval;
4326         MsgInfo *msginfo;
4327         gboolean froze = FALSE;
4328
4329         if (!item) return;
4330
4331         if (summary_is_locked(summaryview)) return;
4332
4333         if (!summaryview->folder_item) return;
4334
4335         if (!prefs_common.live_dangerously) {
4336                 gchar *buf = NULL;
4337                 int num = g_list_length(GTK_CMCLIST(summaryview->ctree)->selection);
4338                 buf = g_strdup_printf(ngettext(
4339                         "Do you really want to delete the selected message?",
4340                         "Do you really want to delete the %d selected messages?", num), 
4341                         num);
4342                 aval = alertpanel(_("Delete message(s)"),
4343                                   buf,
4344                                   GTK_STOCK_CANCEL, "+"GTK_STOCK_DELETE, NULL);
4345                 g_free(buf);
4346                 if (aval != G_ALERTALTERNATE) return;
4347         }
4348
4349         for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; 
4350              cur = cur->next) {
4351                 GtkCMCTreeNode *row = GTK_CMCTREE_NODE(cur->data);
4352                 msginfo = gtk_cmctree_node_get_row_data(ctree, row);
4353                 if (msginfo && msginfo->total_size != 0 && 
4354                     msginfo->size != (goffset)msginfo->total_size)
4355                         partial_mark_for_delete(msginfo);
4356         }
4357
4358         main_window_cursor_wait(summaryview->mainwin);
4359
4360         /* next code sets current row focus right. We need to find a row
4361          * that is not deleted. */
4362         START_LONG_OPERATION(summaryview, FALSE);
4363         folder_item_set_batch(summaryview->folder_item, TRUE);
4364         for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next) {
4365                 sel_last = GTK_CMCTREE_NODE(cur->data);
4366                 summary_delete_row(summaryview, sel_last);
4367         }
4368         folder_item_set_batch(summaryview->folder_item, FALSE);
4369         END_LONG_OPERATION(summaryview);
4370
4371         node = summary_find_next_msg(summaryview, sel_last);
4372         if (!node)
4373                 node = summary_find_prev_msg(summaryview, sel_last);
4374
4375         summary_select_node(summaryview, node, prefs_common.always_show_msg, TRUE);
4376         
4377         if (prefs_common.immediate_exec || folder_has_parent_of_type(item, F_TRASH)) {
4378                 summary_execute(summaryview);
4379                 /* after deleting, the anchor may be at an invalid row
4380                  * so reset it to the node we found earlier */
4381                 gtk_sctree_set_anchor_row(GTK_SCTREE(ctree), node);
4382         } else
4383                 summary_status_show(summaryview);
4384
4385                 
4386         main_window_cursor_normal(summaryview->mainwin);
4387 }
4388
4389 void summary_delete_trash(SummaryView *summaryview)
4390 {
4391         FolderItem *to_folder = NULL;
4392         PrefsAccount *ac;
4393         if (!summaryview->folder_item ||
4394             FOLDER_TYPE(summaryview->folder_item->folder) == F_NEWS) return;
4395         
4396         if (NULL != (ac = account_find_from_item(summaryview->folder_item)))
4397                 to_folder = account_get_special_folder(ac, F_TRASH);
4398
4399         if (to_folder == NULL)
4400                 to_folder = summaryview->folder_item->folder->trash;
4401         
4402         if (to_folder == NULL || to_folder == summaryview->folder_item
4403             || folder_has_parent_of_type(summaryview->folder_item, F_TRASH))
4404                 summary_delete(summaryview);
4405         else
4406                 summary_move_selected_to(summaryview, to_folder);
4407 }
4408
4409
4410 static void summary_unmark_row(SummaryView *summaryview, GtkCMCTreeNode *row)
4411 {
4412         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4413         MsgInfo *msginfo;
4414
4415         msginfo = gtk_cmctree_node_get_row_data(ctree, row);
4416         g_return_if_fail(msginfo);
4417         if (MSG_IS_DELETED(msginfo->flags))
4418                 summaryview->deleted--;
4419         if (MSG_IS_MOVE(msginfo->flags))
4420                 summaryview->moved--;
4421         if (MSG_IS_COPY(msginfo->flags))
4422                 summaryview->copied--;
4423
4424         procmsg_msginfo_set_to_folder(msginfo, NULL);
4425         summary_msginfo_unset_flags(msginfo, MSG_MARKED | MSG_DELETED, 
4426                 MSG_MOVE | MSG_COPY | MSG_MOVE_DONE);
4427         summary_set_row_marks(summaryview, row);
4428
4429         debug_print("Message %s/%d is unmarked\n",
4430                     msginfo->folder->path, msginfo->msgnum);
4431 }
4432
4433 void summary_unmark(SummaryView *summaryview)
4434 {
4435         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4436         GList *cur;
4437         gboolean froze = FALSE;
4438
4439         if (summary_is_locked(summaryview))
4440                 return;
4441         START_LONG_OPERATION(summaryview, FALSE);
4442         folder_item_set_batch(summaryview->folder_item, TRUE);
4443         for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
4444                 summary_unmark_row(summaryview, GTK_CMCTREE_NODE(cur->data));
4445         folder_item_set_batch(summaryview->folder_item, FALSE);
4446         END_LONG_OPERATION(summaryview);
4447         
4448         summary_status_show(summaryview);
4449 }
4450
4451 static void summary_move_row_to(SummaryView *summaryview, GtkCMCTreeNode *row,
4452                                 FolderItem *to_folder)
4453 {
4454         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4455         MsgInfo *msginfo;
4456
4457         g_return_if_fail(to_folder != NULL);
4458
4459         msginfo = gtk_cmctree_node_get_row_data(ctree, row);
4460         g_return_if_fail(msginfo);
4461         if (MSG_IS_LOCKED(msginfo->flags))
4462                 return;
4463
4464         procmsg_msginfo_set_to_folder(msginfo, to_folder);
4465         if (MSG_IS_DELETED(msginfo->flags))
4466                 summaryview->deleted--;
4467         if (MSG_IS_COPY(msginfo->flags)) {
4468                 summaryview->copied--;
4469         }
4470         if (!MSG_IS_MOVE(msginfo->flags)) {
4471                 summary_msginfo_change_flags(msginfo, 0, MSG_MOVE, MSG_DELETED, 
4472                         MSG_COPY | MSG_MOVE_DONE);
4473                 summaryview->moved++;
4474         } else {
4475                 summary_msginfo_unset_flags(msginfo, MSG_DELETED, MSG_COPY);
4476         }
4477         
4478         if (!prefs_common.immediate_exec) {
4479                 summary_set_row_marks(summaryview, row);
4480         }
4481
4482         debug_print("Message %d is set to move to %s\n",
4483                     msginfo->msgnum, to_folder->path);
4484 }
4485
4486 void summary_move_selected_to(SummaryView *summaryview, FolderItem *to_folder)
4487 {
4488         GList *cur;
4489         GtkCMCTreeNode *sel_last = NULL;
4490         gboolean froze = FALSE;
4491
4492         if (!to_folder) return;
4493         if (!summaryview->folder_item ||
4494             FOLDER_TYPE(summaryview->folder_item->folder) == F_NEWS) return;
4495
4496         if (summary_is_locked(summaryview)) return;
4497
4498         if (summaryview->folder_item == to_folder) {
4499                 alertpanel_error(_("Destination is same as current folder."));
4500                 return;
4501         }
4502
4503         if (to_folder->no_select) {
4504                 alertpanel_error(_("The destination folder can only be used to "
4505                                    "store subfolders."));
4506                 return;
4507         }
4508
4509         START_LONG_OPERATION(summaryview, FALSE); 
4510
4511         for (cur = GTK_CMCLIST(summaryview->ctree)->selection;
4512              cur != NULL && cur->data != NULL; cur = cur->next) {
4513                 sel_last = GTK_CMCTREE_NODE(cur->data);
4514                 summary_move_row_to
4515                         (summaryview, GTK_CMCTREE_NODE(cur->data), to_folder);
4516         }
4517         END_LONG_OPERATION(summaryview);
4518
4519         summaryview->display_msg = (prefs_common.always_show_msg == OPENMSG_ALWAYS) ||
4520                 ((prefs_common.always_show_msg == OPENMSG_WHEN_VIEW_VISIBLE &&
4521                                 messageview_is_visible(summaryview->messageview)));
4522         
4523         if (prefs_common.immediate_exec) {
4524                 summary_execute(summaryview);
4525         } else {
4526                 GtkCMCTreeNode *node = summary_find_next_msg(summaryview, sel_last);
4527                 if (!node)
4528                         node = summary_find_prev_msg(summaryview, sel_last);
4529                 summary_select_node(summaryview, node, summaryview->display_msg, TRUE);
4530                 summary_status_show(summaryview);
4531         }
4532         
4533         if (!summaryview->selected) { /* this was the last message */
4534                 GtkCMCTreeNode *node = gtk_cmctree_node_nth (GTK_CMCTREE(summaryview->ctree), 
4535                                                          GTK_CMCLIST(summaryview->ctree)->rows - 1);
4536                 if (node)
4537                         summary_select_node(summaryview, node, summaryview->display_msg, TRUE);
4538         }
4539
4540 }
4541
4542 void summary_move_to(SummaryView *summaryview)
4543 {
4544         FolderItem *to_folder;
4545
4546         if (!summaryview->folder_item ||
4547             FOLDER_TYPE(summaryview->folder_item->folder) == F_NEWS) return;
4548
4549         to_folder = foldersel_folder_sel(summaryview->folder_item->folder,
4550                                          FOLDER_SEL_MOVE, NULL, FALSE);
4551         summary_move_selected_to(summaryview, to_folder);
4552 }
4553
4554 static void summary_copy_row_to(SummaryView *summaryview, GtkCMCTreeNode *row,
4555                                 FolderItem *to_folder)
4556 {
4557         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4558         MsgInfo *msginfo;
4559
4560         g_return_if_fail(to_folder != NULL);
4561
4562         msginfo = gtk_cmctree_node_get_row_data(ctree, row);
4563         g_return_if_fail(msginfo);
4564         procmsg_msginfo_set_to_folder(msginfo, to_folder);
4565         if (MSG_IS_DELETED(msginfo->flags))
4566                 summaryview->deleted--;
4567         if (MSG_IS_MOVE(msginfo->flags)) {
4568                 summaryview->moved--;
4569         }
4570         
4571         if (!MSG_IS_COPY(msginfo->flags)) {
4572                 summary_msginfo_change_flags(msginfo, 0, MSG_COPY, MSG_DELETED, 
4573                         MSG_MOVE | MSG_MOVE_DONE);
4574                 summaryview->copied++;
4575         } else {
4576                 summary_msginfo_unset_flags(msginfo, MSG_DELETED, MSG_MOVE);
4577         }
4578         if (!prefs_common.immediate_exec) {
4579                 summary_set_row_marks(summaryview, row);
4580         }
4581
4582         debug_print("Message %d is set to copy to %s\n",
4583                     msginfo->msgnum, to_folder->path);
4584 }
4585
4586 void summary_copy_selected_to(SummaryView *summaryview, FolderItem *to_folder)
4587 {
4588         GList *cur;
4589         gboolean froze = FALSE;
4590
4591         if (!to_folder) return;
4592         if (!summaryview->folder_item) return;
4593
4594         if (summary_is_locked(summaryview)) return;
4595
4596         if (summaryview->folder_item == to_folder) {
4597                 alertpanel_error
4598                         (_("Destination to copy is same as current folder."));
4599                 return;
4600         }
4601
4602         if (to_folder->no_select) {
4603                 alertpanel_error(_("The destination folder can only be used to "
4604                                    "store subfolders."));
4605                 return;
4606         }
4607
4608         START_LONG_OPERATION(summaryview, FALSE);
4609
4610         for (cur = GTK_CMCLIST(summaryview->ctree)->selection;
4611              cur != NULL && cur->data != NULL; cur = cur->next)
4612                 summary_copy_row_to
4613                         (summaryview, GTK_CMCTREE_NODE(cur->data), to_folder);
4614
4615         END_LONG_OPERATION(summaryview);
4616
4617         if (prefs_common.immediate_exec)
4618                 summary_execute(summaryview);
4619         else {
4620                 summary_status_show(summaryview);
4621         }
4622 }
4623
4624 void summary_copy_to(SummaryView *summaryview)
4625 {
4626         FolderItem *to_folder;
4627
4628         if (!summaryview->folder_item) return;
4629
4630         to_folder = foldersel_folder_sel(summaryview->folder_item->folder,
4631                                          FOLDER_SEL_COPY, NULL, FALSE);
4632         summary_copy_selected_to(summaryview, to_folder);
4633 }
4634
4635 void summary_add_address(SummaryView *summaryview)
4636 {
4637         MsgInfo *msginfo, *full_msginfo;
4638         gchar *from;
4639         GtkWidget *image = NULL;
4640         GdkPixbuf *picture = NULL;
4641
4642         msginfo = gtk_cmctree_node_get_row_data(GTK_CMCTREE(summaryview->ctree),
4643                                               summaryview->selected);
4644         if (!msginfo || !msginfo->from) 
4645                 return;
4646
4647         Xstrdup_a(from, msginfo->from, return);
4648         eliminate_address_comment(from);
4649         extract_address(from);
4650         
4651         full_msginfo = procmsg_msginfo_get_full_info(msginfo);
4652         if (full_msginfo &&
4653             full_msginfo->extradata &&
4654             full_msginfo->extradata->face) {
4655                 image = face_get_from_header(full_msginfo->extradata->face);
4656         } 
4657 #if HAVE_LIBCOMPFACE
4658         else if (full_msginfo &&
4659                  full_msginfo->extradata &&
4660                  full_msginfo->extradata->xface) {
4661                 image = xface_get_from_header(full_msginfo->extradata->xface,
4662                                 &summaryview->ctree->style->white,
4663                                 summaryview->mainwin->window->window);  
4664         }
4665 #endif
4666         procmsg_msginfo_free(full_msginfo);
4667         if (image)
4668                 picture = gtk_image_get_pixbuf(GTK_IMAGE(image));
4669
4670         addressbook_add_contact(msginfo->fromname, from, NULL, picture);
4671
4672         if (image)
4673                 gtk_widget_destroy(image);
4674 }
4675
4676 void summary_select_all(SummaryView *summaryview)
4677 {
4678         if (!summaryview->folder_item) return;
4679
4680         summary_lock(summaryview);
4681         gtk_cmclist_select_all(GTK_CMCLIST(summaryview->ctree));
4682         summary_unlock(summaryview);
4683         summary_status_show(summaryview);
4684 }
4685
4686 void summary_unselect_all(SummaryView *summaryview)
4687 {
4688         summary_lock(summaryview);
4689         gtk_sctree_unselect_all(GTK_SCTREE(summaryview->ctree));
4690         summary_unlock(summaryview);
4691         summary_status_show(summaryview);
4692 }
4693
4694 void summary_select_thread(SummaryView *summaryview, gboolean delete_thread)
4695 {
4696         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4697         GtkCMCTreeNode *node = NULL;
4698         gboolean froze = FALSE;
4699         GList *cur = NULL;
4700         GList *copy = NULL;
4701         if (!GTK_CMCLIST(summaryview->ctree)->selection) 
4702                 return;
4703
4704
4705         START_LONG_OPERATION(summaryview, FALSE);
4706         copy = g_list_copy(GTK_CMCLIST(summaryview->ctree)->selection);
4707         for (cur = copy; cur != NULL && cur->data != NULL;
4708              cur = cur->next) {
4709                 node = GTK_CMCTREE_NODE(cur->data);
4710                 if (!node)
4711                         continue;
4712                 while (GTK_CMCTREE_ROW(node)->parent != NULL)
4713                         node = GTK_CMCTREE_ROW(node)->parent;
4714
4715                 gtk_cmctree_select_recursive(ctree, node);
4716         }
4717         g_list_free(copy);
4718         END_LONG_OPERATION(summaryview);
4719
4720         if (delete_thread) {
4721                 if (FOLDER_TYPE(summaryview->folder_item->folder) == F_NEWS)
4722                         summary_delete(summaryview);
4723                 else
4724                         summary_delete_trash(summaryview);
4725         }
4726         summary_status_show(summaryview);
4727 }
4728
4729 void summary_save_as(SummaryView *summaryview)
4730 {
4731         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4732         MsgInfo *msginfo;
4733         gchar *filename = NULL;
4734         gchar *src, *dest;
4735         gchar *tmp;
4736
4737         AlertValue aval = 0;
4738
4739         if (!summaryview->selected) return;
4740         msginfo = gtk_cmctree_node_get_row_data(ctree, summaryview->selected);
4741         if (!msginfo) return;
4742
4743         if (msginfo->subject) {
4744                 Xstrdup_a(filename, msginfo->subject, return);
4745                 subst_for_filename(filename);
4746         }
4747         if (filename && !g_utf8_validate(filename, -1, NULL)) {
4748                 gchar *oldstr = filename;
4749                 filename = conv_codeset_strdup(filename,
4750                                                conv_get_locale_charset_str(),
4751                                                CS_UTF_8);
4752                 if (!filename) {
4753                         g_warning("summary_save_as(): failed to convert character set.");
4754                         filename = g_strdup(oldstr);
4755                 }
4756                 dest = filesel_select_file_save(_("Save as"), filename);
4757                 g_free(filename);
4758         } else
4759                 dest = filesel_select_file_save(_("Save as"), filename);
4760         filename = NULL;
4761         if (!dest) return;
4762         if (is_file_exist(dest)) {
4763                 aval = alertpanel(_("Append or Overwrite"),
4764                                   _("Append or overwrite existing file?"),
4765                                   _("_Append"), _("_Overwrite"),
4766                                   GTK_STOCK_CANCEL);
4767                 if (aval != 0 && aval != 1)
4768                         return;
4769         }
4770
4771         src = procmsg_get_message_file(msginfo);
4772         tmp = g_path_get_basename(dest);
4773
4774         if ( aval==0 ) { /* append */
4775                 if (append_file(src, dest, TRUE) < 0) 
4776                         alertpanel_error(_("Couldn't save the file '%s'."), tmp);
4777         } else { /* overwrite */
4778                 if (copy_file(src, dest, TRUE) < 0)
4779                         alertpanel_error(_("Couldn't save the file '%s'."), tmp);
4780         }
4781         g_free(src);
4782         
4783         /*
4784          * If two or more msgs are selected,
4785          * append them to the output file.
4786          */
4787         if (GTK_CMCLIST(ctree)->selection->next) {
4788                 GList *item;
4789                 for (item = GTK_CMCLIST(ctree)->selection->next; item != NULL; item=item->next) {
4790                         msginfo = gtk_cmctree_node_get_row_data(ctree, GTK_CMCTREE_NODE(item->data));
4791                         if (!msginfo) break;
4792                         src = procmsg_get_message_file(msginfo);
4793                         if (append_file(src, dest, TRUE) < 0)
4794                                 alertpanel_error(_("Couldn't save the file '%s'."), tmp);
4795                 }
4796                 g_free(src);
4797         }
4798         g_free(dest);
4799         g_free(tmp);
4800 }
4801
4802 void summary_print(SummaryView *summaryview)
4803 {
4804         GtkCMCList *clist = GTK_CMCLIST(summaryview->ctree);
4805 #if !GTK_CHECK_VERSION(2,10,0)
4806         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4807         MsgInfo *msginfo;
4808         gchar *cmdline = NULL;
4809         gchar *p;
4810 #endif
4811         GList *cur;
4812         gchar *msg = g_strdup_printf(_("You are about to print %d "
4813                                        "messages, one by one. Do you "
4814                                        "want to continue?"), 
4815                                        g_list_length(clist->selection));
4816         if (g_list_length(clist->selection) > 9
4817         &&  alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
4818             != G_ALERTALTERNATE) {
4819                 g_free(msg);
4820                 return;
4821         }
4822         g_free(msg);
4823
4824         if (clist->selection == NULL) return;
4825 #if !GTK_CHECK_VERSION(2,10,0)
4826         cmdline = input_dialog(_("Print"),
4827                                _("Enter the print command line:\n"
4828                                  "('%s' will be replaced with file name)"),
4829                                prefs_common.print_cmd);
4830         if (!cmdline) return;
4831         if (!(p = strchr(cmdline, '%')) || *(p + 1) != 's' ||
4832             strchr(p + 2, '%')) {
4833                 alertpanel_error(_("Print command line is invalid:\n'%s'"),
4834                                  cmdline);
4835                 g_free(cmdline);
4836                 return;
4837         }
4838         for (cur = clist->selection; 
4839              cur != NULL && cur->data != NULL; 
4840              cur = cur->next) {
4841                 msginfo = gtk_cmctree_node_get_row_data
4842                         (ctree, GTK_CMCTREE_NODE(cur->data));
4843                 if (msginfo) 
4844                         procmsg_print_message(msginfo, cmdline);
4845         }
4846
4847         g_free(cmdline);
4848 #else
4849         for (cur = clist->selection; 
4850              cur != NULL && cur->data != NULL; 
4851              cur = cur->next) {
4852                 GtkCMCTreeNode *node = GTK_CMCTREE_NODE(cur->data);
4853                 MsgInfo *msginfo = gtk_cmctree_node_get_row_data(
4854                                         GTK_CMCTREE(summaryview->ctree),
4855                                         node);
4856                 gint sel_start = -1, sel_end = -1, partnum = 0;
4857                 
4858                 if (node == summaryview->displayed) {
4859                         partnum = mimeview_get_selected_part_num(summaryview->messageview->mimeview);
4860                         textview_get_selection_offsets(summaryview->messageview->mimeview->textview,
4861                                 &sel_start, &sel_end);
4862                 } 
4863                 messageview_print(msginfo, summaryview->messageview->all_headers,
4864                         sel_start, sel_end, partnum);
4865         }
4866 #endif
4867 }
4868
4869 gboolean summary_execute(SummaryView *summaryview)
4870 {
4871         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4872         GtkCMCList *clist = GTK_CMCLIST(summaryview->ctree);
4873         GtkCMCTreeNode *node, *next;
4874         GtkCMCTreeNode *new_selected = NULL;
4875         gint move_val = -1;
4876
4877         if (!summaryview->folder_item) return FALSE;
4878
4879         if (summary_is_locked(summaryview)) return FALSE;
4880         summary_lock(summaryview);
4881
4882         summary_freeze(summaryview);
4883
4884         main_window_cursor_wait(summaryview->mainwin);
4885
4886         if (summaryview->threaded)
4887                 summary_unthread_for_exec(summaryview);
4888
4889         folder_item_update_freeze();
4890         move_val = summary_execute_move(summaryview);
4891         summary_execute_copy(summaryview);
4892         summary_execute_delete(summaryview);
4893         
4894         node = GTK_CMCTREE_NODE(clist->row_list);
4895         for (; node != NULL; node = next) {
4896                 next = gtkut_ctree_node_next(ctree, node);
4897                 if (gtk_cmctree_node_get_row_data(ctree, node) != NULL) continue;
4898
4899                 if (node == summaryview->displayed) {
4900                         messageview_clear(summaryview->messageview);
4901                         summaryview->displayed = NULL;
4902                 }
4903                 if (GTK_CMCTREE_ROW(node)->children != NULL) {
4904                         next = NULL;
4905                         if (GTK_CMCTREE_ROW(node)->sibling) {
4906                                 next = GTK_CMCTREE_ROW(node)->sibling;
4907                         } else {
4908                                 GtkCMCTreeNode *parent = NULL;
4909                                 for (parent = GTK_CMCTREE_ROW(node)->parent; parent != NULL;
4910                                      parent = GTK_CMCTREE_ROW(parent)->parent) {
4911                                         if (GTK_CMCTREE_ROW(parent)->sibling) {
4912                                                 next = GTK_CMCTREE_ROW(parent)->sibling;
4913                                         }
4914                                 }
4915                         }
4916                 }
4917
4918                 if (!new_selected &&
4919                     gtkut_ctree_node_is_selected(ctree, node)) {
4920                         summary_unselect_all(summaryview);
4921                         new_selected = summary_find_next_msg(summaryview, node);
4922                         if (!new_selected)
4923                                 new_selected = summary_find_prev_msg
4924                                         (summaryview, node);
4925                 }
4926
4927                 gtk_sctree_remove_node((GtkSCTree *)ctree, node);
4928         }
4929
4930         folder_item_update_thaw();
4931
4932         if (new_selected) {
4933                 summary_unlock(summaryview);
4934                 gtk_sctree_select
4935                         (GTK_SCTREE(ctree), new_selected);
4936                 summary_lock(summaryview);
4937         }
4938
4939         if (summaryview->threaded) {
4940                 summary_thread_build(summaryview);
4941                 summary_thread_init(summaryview);
4942         }
4943
4944         summary_thaw(summaryview);
4945
4946         summaryview->selected = clist->selection ?
4947                 GTK_CMCTREE_NODE(clist->selection->data) : NULL;
4948
4949         if (!GTK_CMCLIST(summaryview->ctree)->row_list) {
4950                 menu_set_insensitive_all
4951                         (GTK_MENU_SHELL(summaryview->popupmenu));
4952                 gtk_widget_grab_focus(summaryview->folderview->ctree);
4953         } else
4954                 gtk_widget_grab_focus(summaryview->ctree);
4955
4956         summary_update_status(summaryview);
4957         summary_status_show(summaryview);
4958
4959         gtk_cmctree_node_moveto(ctree, summaryview->selected, 0, 0.5, 0);
4960
4961         summary_unlock(summaryview);
4962
4963         main_window_cursor_normal(summaryview->mainwin);
4964
4965         if (move_val < 0) 
4966                 summary_show(summaryview, summaryview->folder_item);
4967         return TRUE;
4968 }
4969
4970 static gint summary_execute_move(SummaryView *summaryview)
4971 {
4972         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4973         GSList *cur;
4974         gint val = -1;
4975         /* search moving messages and execute */
4976         gtk_cmctree_pre_recursive(ctree, NULL, summary_execute_move_func,
4977                                 summaryview);
4978
4979         if (summaryview->mlist) {
4980                 hooks_unregister_hook(MSGINFO_UPDATE_HOOKLIST,
4981                         summaryview->msginfo_update_callback_id);
4982                 val = procmsg_move_messages(summaryview->mlist);
4983                 summaryview->msginfo_update_callback_id =
4984                 hooks_register_hook(MSGINFO_UPDATE_HOOKLIST, 
4985                         summary_update_msg, (gpointer) summaryview);
4986
4987                 for (cur = summaryview->mlist; cur != NULL && cur->data != NULL; cur = cur->next)
4988                         procmsg_msginfo_free((MsgInfo *)cur->data);
4989                 g_slist_free(summaryview->mlist);
4990                 summaryview->mlist = NULL;
4991                 return val;
4992         }
4993         return 0;
4994 }
4995
4996 static void summary_execute_move_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
4997                                       gpointer data)
4998 {
4999         SummaryView *summaryview = data;
5000         MsgInfo *msginfo;
5001
5002         msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
5003
5004         if (msginfo && MSG_IS_MOVE(msginfo->flags) && msginfo->to_folder) {
5005                 summaryview->mlist =
5006                         g_slist_prepend(summaryview->mlist, msginfo);
5007                 gtk_cmctree_node_set_row_data(ctree, node, NULL);
5008
5009                 if (msginfo->msgid && *msginfo->msgid &&
5010                     node == g_hash_table_lookup(summaryview->msgid_table,
5011                                                 msginfo->msgid))
5012                         g_hash_table_remove(summaryview->msgid_table,
5013                                             msginfo->msgid);
5014                 if (prefs_common.thread_by_subject &&
5015                     msginfo->subject && *msginfo->subject && 
5016                     node == subject_table_lookup(summaryview->subject_table,
5017                                                  msginfo->subject)) {
5018                         subject_table_remove(summaryview->subject_table,
5019                                              msginfo->subject);
5020                 }                                           
5021         }
5022 }
5023
5024 static void summary_execute_copy(SummaryView *summaryview)
5025 {
5026         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
5027
5028         /* search copying messages and execute */
5029         hooks_unregister_hook(MSGINFO_UPDATE_HOOKLIST,
5030                 summaryview->msginfo_update_callback_id);
5031         gtk_cmctree_pre_recursive(ctree, NULL, summary_execute_copy_func,
5032                                 summaryview);
5033
5034         if (summaryview->mlist) {
5035                 summaryview->mlist = g_slist_reverse(summaryview->mlist);
5036                 procmsg_copy_messages(summaryview->mlist);
5037
5038                 g_slist_free(summaryview->mlist);
5039                 summaryview->mlist = NULL;
5040         }
5041         summaryview->msginfo_update_callback_id =
5042                 hooks_register_hook(MSGINFO_UPDATE_HOOKLIST, 
5043                         summary_update_msg, (gpointer) summaryview);
5044 }
5045
5046 static void summary_execute_copy_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
5047                                       gpointer data)
5048 {
5049         SummaryView *summaryview = data;
5050         MsgInfo *msginfo;
5051
5052         msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
5053
5054         if (msginfo && MSG_IS_COPY(msginfo->flags) && msginfo->to_folder) {
5055                 summaryview->mlist =
5056                         g_slist_prepend(summaryview->mlist, msginfo);
5057
5058                 summary_msginfo_unset_flags(msginfo, 0, MSG_COPY);
5059                 summary_set_row_marks(summaryview, node);
5060         }
5061 }
5062
5063 static void summary_execute_delete(SummaryView *summaryview)
5064 {
5065         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
5066         GSList *cur;
5067
5068         /* search deleting messages and execute */
5069         gtk_cmctree_pre_recursive
5070                 (ctree, NULL, summary_execute_delete_func, summaryview);
5071
5072         if (!summaryview->mlist) return;
5073
5074         hooks_unregister_hook(MSGINFO_UPDATE_HOOKLIST,
5075                 summaryview->msginfo_update_callback_id);
5076
5077         folder_item_remove_msgs(summaryview->folder_item,
5078                                 summaryview->mlist);
5079
5080         summaryview->msginfo_update_callback_id =
5081                 hooks_register_hook(MSGINFO_UPDATE_HOOKLIST, 
5082                         summary_update_msg, (gpointer) summaryview);
5083         for (cur = summaryview->mlist; cur != NULL && cur->data != NULL; cur = cur->next)
5084                 procmsg_msginfo_free((MsgInfo *)cur->data);
5085
5086         g_slist_free(summaryview->mlist);
5087         summaryview->mlist = NULL;
5088 }
5089
5090 static void summary_execute_delete_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
5091                                         gpointer data)
5092 {
5093         SummaryView *summaryview = data;
5094         MsgInfo *msginfo;
5095
5096         msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
5097
5098         if (msginfo && MSG_IS_DELETED(msginfo->flags)) {
5099                 summaryview->mlist =
5100                         g_slist_prepend(summaryview->mlist, msginfo);
5101                 gtk_cmctree_node_set_row_data(ctree, node, NULL);
5102
5103                 if (msginfo->msgid && *msginfo->msgid &&
5104                     node == g_hash_table_lookup(summaryview->msgid_table,
5105                                                 msginfo->msgid)) {
5106                         g_hash_table_remove(summaryview->msgid_table,
5107                                             msginfo->msgid);
5108                 }       
5109                 if (prefs_common.thread_by_subject &&
5110                     msginfo->subject && *msginfo->subject && 
5111                     node == subject_table_lookup(summaryview->subject_table,
5112                                                  msginfo->subject)) {
5113                         subject_table_remove(summaryview->subject_table,
5114                                              msginfo->subject);
5115                 }                                           
5116         }
5117 }
5118
5119 /* thread functions */
5120
5121 static void summary_thread_build(SummaryView *summaryview)
5122 {
5123         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
5124         GtkCMCTreeNode *node;
5125         GtkCMCTreeNode *next;
5126         GtkCMCTreeNode *parent;
5127         MsgInfo *msginfo;
5128         GSList *reflist;
5129
5130         summary_lock(summaryview);
5131
5132         debug_print("Building threads...");
5133         STATUSBAR_PUSH(summaryview->mainwin, _("Building threads..."));
5134         main_window_cursor_wait(summaryview->mainwin);
5135
5136         g_signal_handlers_block_by_func(G_OBJECT(ctree),
5137                                        G_CALLBACK(summary_tree_expanded), summaryview);
5138         summary_freeze(summaryview);
5139
5140         node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
5141         while (node) {
5142                 next = GTK_CMCTREE_ROW(node)->sibling;
5143
5144                 msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
5145
5146                 parent = NULL;
5147
5148                 if (msginfo && msginfo->inreplyto) {
5149                         parent = g_hash_table_lookup(summaryview->msgid_table,
5150                                                      msginfo->inreplyto);
5151                                                      
5152                         if (!parent && msginfo->references) {
5153                                 for (reflist = msginfo->references;
5154                                      reflist != NULL; reflist = reflist->next)
5155                                         if ((parent = g_hash_table_lookup
5156                                                 (summaryview->msgid_table,
5157                                                  reflist->data)))
5158                                                 break;
5159                         }
5160                 }
5161
5162                 if (prefs_common.thread_by_subject && parent == NULL) {
5163                         parent = subject_table_lookup
5164                                 (summaryview->subject_table,
5165                                  msginfo->subject);
5166                 }
5167
5168                 if (parent && parent != node && parent != GTK_CMCTREE_ROW(node)->parent) {
5169                         gtk_cmctree_move(ctree, node, parent, NULL);
5170                 }
5171
5172                 node = next;
5173         }
5174
5175         gtkut_ctree_set_focus_row(ctree, summaryview->selected);
5176
5177         summary_thaw(summaryview);
5178         g_signal_handlers_unblock_by_func(G_OBJECT(ctree),
5179                                          G_CALLBACK(summary_tree_expanded), summaryview);
5180
5181         debug_print("done.\n");
5182         STATUSBAR_POP(summaryview->mainwin);
5183         main_window_cursor_normal(summaryview->mainwin);
5184
5185         summaryview->threaded = TRUE;
5186
5187         summary_unlock(summaryview);
5188 }
5189
5190 static void summary_thread_init(SummaryView *summaryview)
5191 {
5192         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
5193         GtkCMCTreeNode *node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
5194         GtkCMCTreeNode *next;
5195         START_TIMING("");
5196         if (!summaryview->thread_collapsed) {
5197                 g_signal_handlers_block_by_func(G_OBJECT(ctree),
5198                                        G_CALLBACK(summary_tree_expanded), summaryview);
5199                 while (node) {
5200                         next = GTK_CMCTREE_ROW(node)->sibling;
5201                         if (GTK_CMCTREE_ROW(node)->children)
5202                                 gtk_cmctree_expand_recursive(ctree, node);
5203                         node = next;
5204                 }
5205                 g_signal_handlers_unblock_by_func(G_OBJECT(ctree),
5206                                        G_CALLBACK(summary_tree_expanded), summaryview);
5207         } 
5208         END_TIMING();
5209 }
5210
5211 static void summary_unthread_for_exec(SummaryView *summaryview)
5212 {
5213         GtkCMCTreeNode *node;
5214         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
5215         gboolean froze = FALSE;
5216
5217         debug_print("Unthreading for execution...");
5218
5219         START_LONG_OPERATION(summaryview, TRUE);
5220         for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
5221              node != NULL; node = GTK_CMCTREE_NODE_NEXT(node)) {
5222                 summary_unthread_for_exec_func(ctree, node, summaryview);
5223         }
5224
5225         END_LONG_OPERATION(summaryview);
5226
5227         debug_print("done.\n");
5228 }
5229
5230 static void summary_unthread_for_exec_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
5231                                            gpointer data)
5232 {
5233         MsgInfo *msginfo;
5234         GtkCMCTreeNode *top_parent;
5235         GtkCMCTreeNode *child;
5236         GtkCMCTreeNode *sibling;
5237         SummaryView * summaryview = (SummaryView *)data;
5238         msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
5239
5240         if (!msginfo ||
5241             (!MSG_IS_MOVE(msginfo->flags) &&
5242              !MSG_IS_DELETED(msginfo->flags)))
5243                 return;
5244         child = GTK_CMCTREE_ROW(node)->children;
5245         if (!child) return;
5246
5247         if (node == summaryview->selected)
5248                 summaryview->selected = NULL;
5249         if (node == summaryview->displayed)
5250                 summaryview->displayed = NULL;
5251
5252         for (top_parent = node;
5253              GTK_CMCTREE_ROW(top_parent)->parent != NULL;
5254              top_parent = GTK_CMCTREE_ROW(top_parent)->parent)
5255                 ;
5256         sibling = GTK_CMCTREE_ROW(top_parent)->sibling;
5257
5258         GTK_SCTREE(ctree)->sorting = TRUE;
5259         while (child != NULL) {
5260                 GtkCMCTreeNode *next_child;
5261                 MsgInfo *cinfo = GTKUT_CTREE_NODE_GET_ROW_DATA(child);
5262                 
5263                 next_child = GTK_CMCTREE_ROW(child)->sibling;
5264                 
5265                 if (!MSG_IS_MOVE(cinfo->flags) && !MSG_IS_DELETED(cinfo->flags)) {
5266                         gtk_cmctree_move(ctree, child, 
5267                                 NULL, 
5268                                 sibling); 
5269                 } else {
5270                         if (child == summaryview->displayed) {
5271                                 messageview_clear(summaryview->messageview);
5272                                 summaryview->displayed = NULL;
5273                         }
5274                         if (child == summaryview->selected) {
5275                                 messageview_clear(summaryview->messageview);
5276                                 summaryview->selected = NULL;
5277                         }
5278                 }
5279                 child = next_child;
5280         }
5281         GTK_SCTREE(ctree)->sorting = FALSE;
5282 }
5283
5284 void summary_expand_threads(SummaryView *summaryview)
5285 {
5286         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
5287         GtkCMCTreeNode *node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
5288
5289         g_signal_handlers_block_by_func(G_OBJECT(ctree),
5290                                        G_CALLBACK(summary_tree_expanded), summaryview);
5291         summary_freeze(summaryview);
5292
5293         while (node) {
5294                 if (GTK_CMCTREE_ROW(node)->children) {
5295                         gtk_cmctree_expand(ctree, node);
5296                         summary_set_row_marks(summaryview, node);
5297                 }
5298                 node = GTK_CMCTREE_NODE_NEXT(node);
5299         }
5300
5301         summary_thaw(summaryview);
5302         g_signal_handlers_unblock_by_func(G_OBJECT(ctree),
5303                                          G_CALLBACK(summary_tree_expanded), summaryview);
5304
5305         summaryview->thread_collapsed = FALSE;
5306
5307         gtk_cmctree_node_moveto(ctree, summaryview->selected, 0, 0.5, 0);
5308 }
5309
5310 void summary_collapse_threads(SummaryView *summaryview)
5311 {
5312         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
5313         GtkCMCTreeNode *node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
5314
5315         summary_freeze(summaryview);
5316
5317         while (node) {
5318                 if (GTK_CMCTREE_ROW(node)->children)
5319                         gtk_cmctree_collapse(ctree, node);
5320                 node = GTK_CMCTREE_ROW(node)->sibling;
5321         }
5322
5323         summary_thaw(summaryview);
5324         
5325         summaryview->thread_collapsed = TRUE;
5326
5327         gtk_cmctree_node_moveto(ctree, summaryview->selected, 0, 0.5, 0);
5328 }
5329
5330 static void account_rules_radio_button_toggled_cb(GtkToggleButton *btn, gpointer data)
5331 {
5332         prefs_common.apply_per_account_filtering_rules = GPOINTER_TO_INT(data);
5333 }
5334
5335 static gboolean summary_filter_get_mode(void)
5336 /* ask what to do w/ them: skip them, apply them regardless to the account,
5337    use the current account */
5338 {
5339         /* TODO: eventually also propose to use the current folder's default account,
5340            if it is set */
5341         /* TODO: eventually allow to select the account to use from a optmenu */
5342
5343         GtkWidget *vbox;
5344         GtkWidget *account_rules_skip;
5345         GtkWidget *account_rules_force;
5346         GtkWidget *account_rules_user_current;
5347         AlertValue val;
5348
5349         vbox = gtk_vbox_new (FALSE, 0);
5350
5351         account_rules_skip = gtk_radio_button_new_with_label
5352                                                         (NULL, _("Skip these rules"));
5353         account_rules_force = gtk_radio_button_new_with_label_from_widget
5354                                                         (GTK_RADIO_BUTTON(account_rules_skip),
5355                                                         _("Apply these rules regardless of the account they belong to"));
5356         account_rules_user_current = gtk_radio_button_new_with_label_from_widget
5357                                                         (GTK_RADIO_BUTTON(account_rules_skip),
5358                                                         _("Apply these rules if they apply to the current account"));
5359         gtk_box_pack_start (GTK_BOX (vbox), account_rules_skip, FALSE, FALSE, 0);
5360         gtk_box_pack_start (GTK_BOX (vbox), account_rules_force, FALSE, FALSE, 0);
5361         gtk_box_pack_start (GTK_BOX (vbox), account_rules_user_current, FALSE, FALSE, 0);
5362         g_signal_connect(G_OBJECT(account_rules_skip), "toggled",
5363                         G_CALLBACK(account_rules_radio_button_toggled_cb),
5364                         GINT_TO_POINTER(FILTERING_ACCOUNT_RULES_SKIP));
5365         g_signal_connect(G_OBJECT(account_rules_force), "toggled",
5366                         G_CALLBACK(account_rules_radio_button_toggled_cb),
5367                         GINT_TO_POINTER(FILTERING_ACCOUNT_RULES_FORCE));
5368         g_signal_connect(G_OBJECT(account_rules_user_current), "toggled",
5369                         G_CALLBACK(account_rules_radio_button_toggled_cb),
5370                         GINT_TO_POINTER(FILTERING_ACCOUNT_RULES_USE_CURRENT));
5371         switch (prefs_common.apply_per_account_filtering_rules) {
5372         case FILTERING_ACCOUNT_RULES_SKIP:
5373                 gtk_toggle_button_set_active(
5374                                 GTK_TOGGLE_BUTTON(account_rules_skip), TRUE);
5375                 break;
5376         case FILTERING_ACCOUNT_RULES_FORCE:
5377                 gtk_toggle_button_set_active(
5378                                 GTK_TOGGLE_BUTTON(account_rules_force), TRUE);
5379                 break;
5380         case FILTERING_ACCOUNT_RULES_USE_CURRENT:
5381                 gtk_toggle_button_set_active(
5382                                 GTK_TOGGLE_BUTTON(account_rules_user_current), TRUE);
5383                 break;
5384         }
5385
5386         val = alertpanel_with_widget(
5387                         _("Filtering"),
5388                         _("There are some filtering rules that belong to an account.\n"
5389                           "Please choose what to do with these rules:"),
5390                         GTK_STOCK_CANCEL, _("_Filter"), NULL, TRUE, G_ALERTALTERNATE, vbox);
5391
5392         if ((val & ~G_ALERTDISABLE) != G_ALERTALTERNATE) {
5393                 return FALSE;
5394         } else if (val & G_ALERTDISABLE)
5395                 prefs_common.ask_apply_per_account_filtering_rules = FALSE;
5396
5397         return TRUE;
5398 }
5399
5400 void summary_filter(SummaryView *summaryview, gboolean selected_only)
5401 {
5402         GSList *mlist = NULL, *cur_list;
5403         summary_lock(summaryview);
5404
5405         /* are there any per-account filtering rules? */
5406         if (prefs_common.ask_apply_per_account_filtering_rules == TRUE &&
5407                 filtering_peek_per_account_rules(filtering_rules)) {
5408
5409                 if (summary_filter_get_mode() == FALSE) {
5410                         summary_unlock(summaryview);
5411                         return;
5412                 }
5413         }
5414
5415         folder_item_update_freeze();
5416         
5417         debug_print("filtering...");
5418         STATUSBAR_PUSH(summaryview->mainwin, _("Filtering..."));
5419         main_window_cursor_wait(summaryview->mainwin);
5420
5421         summary_freeze(summaryview);
5422
5423         if (selected_only) {
5424                 GList *cur;
5425
5426                 for (cur = GTK_CMCLIST(summaryview->ctree)->selection;
5427                      cur != NULL && cur->data != NULL; cur = cur->next) {
5428                         mlist = g_slist_prepend(mlist, 
5429                                  procmsg_msginfo_new_ref(
5430                                   GTKUT_CTREE_NODE_GET_ROW_DATA(cur->data)));
5431                 }
5432                 mlist = g_slist_reverse(mlist);
5433         } else {
5434                 mlist = folder_item_get_msg_list(summaryview->folder_item);
5435         }
5436         
5437         folder_item_set_batch(summaryview->folder_item, TRUE);
5438         for (cur_list = mlist; cur_list; cur_list = cur_list->next) {
5439                 summary_filter_func((MsgInfo *)cur_list->data);
5440         }
5441         folder_item_set_batch(summaryview->folder_item, FALSE);
5442         
5443         filtering_move_and_copy_msgs(mlist);
5444         
5445         for (cur_list = mlist; cur_list; cur_list = cur_list->next) {
5446                 procmsg_msginfo_free((MsgInfo *)cur_list->data);
5447         }
5448         g_slist_free(mlist);
5449
5450         summary_thaw(summaryview);
5451
5452         folder_item_update_thaw();
5453         debug_print("done.\n");
5454         STATUSBAR_POP(summaryview->mainwin);
5455         main_window_cursor_normal(summaryview->mainwin);
5456
5457         summary_unlock(summaryview);
5458
5459         /* 
5460          * CLAWS: summary_show() only valid after having a lock. ideally
5461          * we want the lock to be context aware...  
5462          */
5463         summary_show(summaryview, summaryview->folder_item);
5464 }
5465
5466 static void summary_filter_func(MsgInfo *msginfo)
5467 {
5468         MailFilteringData mail_filtering_data;
5469
5470         mail_filtering_data.msginfo = msginfo;
5471         mail_filtering_data.msglist = NULL;                     
5472         mail_filtering_data.filtered = NULL;                    
5473         mail_filtering_data.unfiltered = NULL;                  
5474         if (hooks_invoke(MAIL_MANUAL_FILTERING_HOOKLIST, &mail_filtering_data))
5475                 return;
5476
5477         filter_message_by_msginfo(filtering_rules, msginfo, NULL,
5478                         FILTERING_MANUALLY, NULL);
5479 }
5480
5481 void summary_msginfo_filter_open(FolderItem * item, MsgInfo *msginfo,
5482                                  PrefsFilterType type, gint processing_rule)
5483 {
5484         gchar *header = NULL;
5485         gchar *key = NULL;
5486
5487         procmsg_get_filter_keyword(msginfo, &header, &key, type);
5488         
5489         if (processing_rule) {
5490                 if (item == NULL)
5491                         prefs_filtering_open(&pre_global_processing,
5492                                              _("Processing rules to apply before folder rules"),
5493                                              MANUAL_ANCHOR_PROCESSING,
5494                                              header, key, FALSE);
5495                 else
5496                         prefs_filtering_open(&item->prefs->processing,
5497                                              _("Processing configuration"),
5498                                              MANUAL_ANCHOR_PROCESSING,
5499                                              header, key, FALSE);
5500         }
5501         else {
5502                 prefs_filtering_open(&filtering_rules,
5503                                 _("Filtering configuration"),
5504                                 MANUAL_ANCHOR_FILTERING,
5505                                 header, key, TRUE);
5506         }
5507         
5508         g_free(header);
5509         g_free(key);
5510 }
5511
5512 void summary_filter_open(SummaryView *summaryview, PrefsFilterType type,
5513                          gint processing_rule)
5514 {
5515         MsgInfo *msginfo;
5516         FolderItem * item;
5517         
5518         if (!summaryview->selected) return;
5519
5520         msginfo = gtk_cmctree_node_get_row_data(GTK_CMCTREE(summaryview->ctree),
5521                                               summaryview->selected);
5522         if (!msginfo) return;
5523         
5524         item = summaryview->folder_item;
5525         summary_msginfo_filter_open(item, msginfo, type, processing_rule);
5526 }
5527
5528 /* color label */
5529
5530 #define N_COLOR_LABELS colorlabel_get_color_count()
5531
5532 static void summary_colorlabel_menu_item_activate_cb(GtkWidget *widget,
5533                                                      gpointer data)
5534 {
5535         guint color = GPOINTER_TO_UINT(data);
5536         SummaryView *summaryview;
5537
5538         summaryview = g_object_get_data(G_OBJECT(widget), "summaryview");
5539         g_return_if_fail(summaryview != NULL);
5540
5541         /* "dont_toggle" state set? */
5542         if (g_object_get_data(G_OBJECT(summaryview->colorlabel_menu),
5543                                 "dont_toggle"))
5544                 return;
5545
5546         summary_set_colorlabel(summaryview, color, NULL);
5547 }
5548
5549 /* summary_set_colorlabel_color() - labelcolor parameter is the color *flag*
5550  * for the messsage; not the color index */
5551 void summary_set_colorlabel_color(GtkCMCTree *ctree, GtkCMCTreeNode *node,
5552                                   guint labelcolor)
5553 {
5554         GdkColor color;
5555         GtkStyle *style, *prev_style, *ctree_style;
5556         MsgInfo *msginfo;
5557         gint color_index;
5558
5559         msginfo = gtk_cmctree_node_get_row_data(ctree, node);
5560         g_return_if_fail(msginfo);
5561
5562         color_index = labelcolor == 0 ? -1 : (gint)labelcolor - 1;
5563         ctree_style = gtk_widget_get_style(GTK_WIDGET(ctree));
5564         prev_style = gtk_cmctree_node_get_row_style(ctree, node);
5565
5566         if (color_index < 0 || color_index >= N_COLOR_LABELS) {
5567                 if (!prev_style) return;
5568                 style = gtk_style_copy(prev_style);
5569                 color = ctree_style->fg[GTK_STATE_NORMAL];
5570                 style->fg[GTK_STATE_NORMAL] = color;
5571                 color = ctree_style->fg[GTK_STATE_SELECTED];
5572                 style->fg[GTK_STATE_SELECTED] = color;
5573         } else {
5574                 if (prev_style)
5575                         style = gtk_style_copy(prev_style);
5576                 else
5577                         style = gtk_style_copy(ctree_style);
5578                 color = colorlabel_get_color(color_index);
5579                 style->fg[GTK_STATE_NORMAL] = color;
5580                 /* get the average of label color and selected fg color
5581                    for visibility */
5582                 style->fg[GTK_STATE_SELECTED].red   = (color.red   + 3*ctree_style->fg[GTK_STATE_SELECTED].red  ) / 4;
5583                 style->fg[GTK_STATE_SELECTED].green = (color.green + 3*ctree_style->fg[GTK_STATE_SELECTED].green) / 4;
5584                 style->fg[GTK_STATE_SELECTED].blue  = (color.blue  + 3*ctree_style->fg[GTK_STATE_SELECTED].blue ) / 4;
5585         }
5586
5587         gtk_cmctree_node_set_row_style(ctree, node, style);
5588         g_object_unref(style);
5589 }
5590
5591 static void summary_set_row_colorlabel(SummaryView *summaryview, GtkCMCTreeNode *row, guint labelcolor)
5592 {
5593         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
5594         MsgInfo *msginfo;
5595
5596         msginfo = gtk_cmctree_node_get_row_data(ctree, row);
5597         g_return_if_fail(msginfo);
5598
5599         summary_msginfo_change_flags(msginfo, MSG_COLORLABEL_TO_FLAGS(labelcolor), 0, 
5600                                         MSG_CLABEL_FLAG_MASK, 0);
5601         summary_set_row_marks(summaryview, row);
5602 }
5603
5604 void summary_set_colorlabel(SummaryView *summaryview, guint labelcolor,
5605                             GtkWidget *widget)
5606 {
5607         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
5608         GList *cur;
5609         gboolean froze = FALSE;
5610
5611         START_LONG_OPERATION(summaryview, FALSE);
5612         for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
5613                 summary_set_row_colorlabel(summaryview,
5614                                            GTK_CMCTREE_NODE(cur->data), labelcolor);
5615         END_LONG_OPERATION(summaryview);
5616 }
5617
5618 static gboolean summary_set_row_tag(SummaryView *summaryview, GtkCMCTreeNode *row, gboolean refresh, gboolean set, gint id)
5619 {
5620         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
5621         MsgInfo *msginfo;
5622         gchar *tags_str = NULL;
5623         msginfo = gtk_cmctree_node_get_row_data(ctree, row);
5624         g_return_val_if_fail(msginfo, FALSE);
5625
5626         procmsg_msginfo_update_tags(msginfo, set, id);
5627         
5628         if (summaryview->col_state[summaryview->col_pos[S_COL_TAGS]].visible) {
5629                 tags_str = procmsg_msginfo_get_tags_str(msginfo);
5630                 gtk_cmctree_node_set_text(ctree, row, 
5631                                 summaryview->col_pos[S_COL_TAGS],
5632                                 tags_str?tags_str:"-");
5633                 g_free(tags_str);
5634         }
5635
5636         summary_set_row_marks(summaryview, row);
5637         if (row == summaryview->displayed) {
5638                 return TRUE;
5639         }
5640         return FALSE;
5641 }
5642
5643 void summary_set_tag(SummaryView *summaryview, gint tag_id,
5644                             GtkWidget *widget)
5645 {
5646         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
5647         GList *cur;
5648         gboolean set = tag_id > 0;
5649         gint real_id = set? tag_id:-tag_id;
5650         gboolean froze = FALSE;
5651         gboolean redisplay = FALSE;
5652
5653         if (summary_is_locked(summaryview))
5654                 return;
5655         START_LONG_OPERATION(summaryview, FALSE);
5656         folder_item_set_batch(summaryview->folder_item, TRUE);
5657         for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next) {
5658                 redisplay |= summary_set_row_tag(summaryview,
5659                                            GTK_CMCTREE_NODE(cur->data), FALSE, set, real_id);
5660         }
5661         folder_item_set_batch(summaryview->folder_item, FALSE);
5662         END_LONG_OPERATION(summaryview);
5663         if (redisplay)
5664                 summary_redisplay_msg(summaryview);
5665 }
5666
5667 static void summary_tags_menu_item_activate_cb(GtkWidget *widget,
5668                                                      gpointer data)
5669 {
5670         gint id = GPOINTER_TO_INT(data);
5671         gboolean set = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
5672         SummaryView *summaryview;
5673
5674         summaryview = g_object_get_data(G_OBJECT(widget), "summaryview");
5675         g_return_if_fail(summaryview != NULL);
5676
5677         /* "dont_toggle" state set? */
5678         if (g_object_get_data(G_OBJECT(summaryview->tags_menu),
5679                                 "dont_toggle"))
5680                 return;
5681
5682         if (!set)
5683                 id = -id;
5684         summary_set_tag(summaryview, id, NULL);
5685 }
5686
5687 static void summary_colorlabel_menu_item_activate_item_cb(GtkMenuItem *menu_item,
5688                                                           gpointer data)
5689 {
5690         SummaryView *summaryview;
5691         GtkMenuShell *menu;
5692         GtkCheckMenuItem **items;
5693         gint n;
5694         GList *cur, *sel;
5695
5696         summaryview = (SummaryView *)data;
5697         g_return_if_fail(summaryview != NULL);
5698
5699         sel = GTK_CMCLIST(summaryview->ctree)->selection;
5700         if (!sel) return;
5701
5702         menu = GTK_MENU_SHELL(summaryview->colorlabel_menu);
5703         
5704         g_return_if_fail(menu != NULL);
5705
5706         Xalloca(items, (N_COLOR_LABELS + 1) * sizeof(GtkWidget *), return);
5707
5708         /* NOTE: don't return prematurely because we set the "dont_toggle"
5709          * state for check menu items */
5710         g_object_set_data(G_OBJECT(menu), "dont_toggle",
5711                           GINT_TO_POINTER(1));
5712
5713         /* clear items. get item pointers. */
5714         for (n = 0, cur = menu->children; cur != NULL && cur->data != NULL; cur = cur->next) {
5715                 if (GTK_IS_CHECK_MENU_ITEM(cur->data)) {
5716                         gtk_check_menu_item_set_active
5717                                 (GTK_CHECK_MENU_ITEM(cur->data), FALSE);
5718                         items[n] = GTK_CHECK_MENU_ITEM(cur->data);
5719                         n++;
5720                 }
5721         }
5722
5723         if (n == (N_COLOR_LABELS + 1)) {
5724                 /* iterate all messages and set the state of the appropriate
5725                  * items */
5726                 for (; sel != NULL; sel = sel->next) {
5727                         MsgInfo *msginfo;
5728                         gint clabel;
5729
5730                         msginfo = gtk_cmctree_node_get_row_data
5731                                 (GTK_CMCTREE(summaryview->ctree),
5732                                  GTK_CMCTREE_NODE(sel->data));
5733                         if (msginfo) {
5734                                 clabel = MSG_GET_COLORLABEL_VALUE(msginfo->flags);
5735                                 if (!items[clabel]->active)
5736                                         gtk_check_menu_item_set_active
5737                                                 (items[clabel], TRUE);
5738                         }
5739                 }
5740         } else
5741                 g_warning("invalid number of color elements (%d)\n", n);
5742
5743         /* reset "dont_toggle" state */
5744         g_object_set_data(G_OBJECT(menu), "dont_toggle",
5745                           GINT_TO_POINTER(0));
5746 }
5747
5748 static void summary_colorlabel_menu_create(SummaryView *summaryview, gboolean refresh)
5749 {
5750         GtkWidget *label_menuitem;
5751         GtkWidget *menu;
5752         GtkWidget *item;
5753         gint i;
5754         gchar *accel_path = NULL;
5755
5756         label_menuitem = gtk_ui_manager_get_widget(gtkut_ui_manager(), "/Menus/SummaryViewPopup/ColorLabel");
5757         g_signal_connect(G_OBJECT(label_menuitem), "activate",
5758                          G_CALLBACK(summary_colorlabel_menu_item_activate_item_cb),
5759                            summaryview);
5760         gtk_widget_show(label_menuitem);
5761
5762         menu = gtk_menu_new();
5763
5764         gtk_menu_set_accel_group (GTK_MENU (menu), 
5765                 gtk_ui_manager_get_accel_group(mainwindow_get_mainwindow()->ui_manager));
5766
5767         /* create sub items. for the menu item activation callback we pass the
5768          * index of label_colors[] as data parameter. for the None color we
5769          * pass an invalid (high) value. also we attach a data pointer so we
5770          * can always get back the SummaryView pointer. */
5771
5772         item = gtk_check_menu_item_new_with_label(_("None"));
5773         gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
5774         g_signal_connect(G_OBJECT(item), "activate",
5775                          G_CALLBACK(summary_colorlabel_menu_item_activate_cb),
5776                            GUINT_TO_POINTER(0));
5777         g_object_set_data(G_OBJECT(item), "summaryview", summaryview);
5778         gtk_widget_show(item);
5779
5780         accel_path = g_strdup_printf("<ClawsColorLabels>/None");
5781         gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
5782         g_free(accel_path);
5783         gtk_accel_map_add_entry("<ClawsColorLabels>/None", GDK_0, GDK_CONTROL_MASK);
5784
5785         item = gtk_menu_item_new();
5786         gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
5787         gtk_widget_show(item);
5788
5789         /* create pixmap/label menu items */
5790         for (i = 0; i < N_COLOR_LABELS; i++) {
5791                 item = colorlabel_create_check_color_menu_item(
5792                         i, refresh, SUMMARY_COLORMENU);
5793                 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
5794                 g_signal_connect(G_OBJECT(item), "activate",
5795                                  G_CALLBACK(summary_colorlabel_menu_item_activate_cb),
5796                                  GUINT_TO_POINTER(i + 1));
5797                 g_object_set_data(G_OBJECT(item), "summaryview",
5798                                   summaryview);
5799                 gtk_widget_show(item);
5800                 accel_path = g_strdup_printf("<ClawsColorLabels>/%d", i+1);
5801                 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
5802                 if (i < 9)
5803                         gtk_accel_map_add_entry(accel_path, GDK_1+i, GDK_CONTROL_MASK);
5804                 g_free(accel_path);
5805                 g_signal_connect (gtk_ui_manager_get_accel_group(mainwindow_get_mainwindow()->ui_manager), 
5806                         "accel-changed", G_CALLBACK (mainwin_accel_changed_cb), item);
5807         }
5808
5809         gtk_widget_show(menu);
5810         gtk_menu_item_set_submenu(GTK_MENU_ITEM(label_menuitem), menu);
5811         summaryview->colorlabel_menu = menu;
5812 }
5813
5814 static void summary_tags_menu_item_activate_item_cb(GtkMenuItem *menu_item,
5815                                                           gpointer data)
5816 {
5817         GtkMenuShell *menu;
5818         GList *cur;
5819         GList *sel;
5820         GHashTable *menu_table = g_hash_table_new_full(
5821                                         g_direct_hash,
5822                                         g_direct_equal,
5823                                         NULL, NULL);
5824         GHashTable *menu_allsel_table = g_hash_table_new_full(
5825                                         g_direct_hash,
5826                                         g_direct_equal,
5827                                         NULL, NULL);
5828         gint sel_len;
5829         SummaryView *summaryview = (SummaryView *)data;
5830         g_return_if_fail(summaryview != NULL);
5831
5832         sel = GTK_CMCLIST(summaryview->ctree)->selection;
5833         if (!sel) return;
5834
5835         menu = GTK_MENU_SHELL(summaryview->tags_menu);
5836         g_return_if_fail(menu != NULL);
5837
5838         /* NOTE: don't return prematurely because we set the "dont_toggle"
5839          * state for check menu items */
5840         g_object_set_data(G_OBJECT(menu), "dont_toggle",
5841                           GINT_TO_POINTER(1));
5842
5843         /* clear items. get item pointers. */
5844         for (cur = menu->children; cur != NULL && cur->data != NULL; cur = cur->next) {
5845                 if (GTK_IS_CHECK_MENU_ITEM(cur->data)) {
5846                         gint id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cur->data),
5847                                 "tag_id"));
5848                         gtk_check_menu_item_set_active
5849                                 (GTK_CHECK_MENU_ITEM(cur->data), FALSE);
5850                                 
5851                         g_hash_table_insert(menu_table, GINT_TO_POINTER(id), GTK_CHECK_MENU_ITEM(cur->data));
5852                         g_hash_table_insert(menu_allsel_table, GINT_TO_POINTER(id), GINT_TO_POINTER(0));
5853                 }
5854         }
5855
5856         /* iterate all messages and set the state of the appropriate
5857          * items */
5858         sel_len = 0;
5859         for (; sel != NULL; sel = sel->next) {
5860                 MsgInfo *msginfo;
5861                 GSList *tags = NULL;
5862                 gint id;
5863                 GtkCheckMenuItem *item;
5864                 msginfo = gtk_cmctree_node_get_row_data
5865                         (GTK_CMCTREE(summaryview->ctree),
5866                          GTK_CMCTREE_NODE(sel->data));
5867                 sel_len++;
5868                 if (msginfo) {
5869                         tags =  msginfo->tags;
5870                         if (!tags)
5871                                 continue;
5872
5873                         for (; tags; tags = tags->next) {
5874                                 gint num_checked = GPOINTER_TO_INT(g_hash_table_lookup(menu_allsel_table, tags->data));
5875                                 id = GPOINTER_TO_INT(tags->data);
5876                                 item = g_hash_table_lookup(menu_table, GINT_TO_POINTER(tags->data));
5877                                 if (item && !item->active) {
5878                                         gtk_check_menu_item_set_active
5879                                                 (item, TRUE);
5880                                 }
5881                                 num_checked++;
5882                                 g_hash_table_replace(menu_allsel_table, tags->data, GINT_TO_POINTER(num_checked));
5883                         }
5884                 }
5885         }
5886
5887         for (cur = menu->children; cur != NULL && cur->data != NULL; cur = cur->next) {
5888                 if (GTK_IS_CHECK_MENU_ITEM(cur->data)) {
5889                         gint id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cur->data),
5890                                 "tag_id"));
5891                         gint num_checked = GPOINTER_TO_INT(g_hash_table_lookup(menu_allsel_table, GINT_TO_POINTER(id)));
5892                         if (num_checked < sel_len && num_checked > 0)
5893                                 gtk_check_menu_item_set_inconsistent(GTK_CHECK_MENU_ITEM(cur->data), TRUE);
5894                         else
5895                                 gtk_check_menu_item_set_inconsistent(GTK_CHECK_MENU_ITEM(cur->data), FALSE);
5896                 }
5897         }
5898         g_hash_table_destroy(menu_table);
5899         g_hash_table_destroy(menu_allsel_table);
5900         /* reset "dont_toggle" state */
5901         g_object_set_data(G_OBJECT(menu), "dont_toggle",
5902                           GINT_TO_POINTER(0));
5903 }
5904
5905 void summaryview_destroy(SummaryView *summaryview)
5906 {
5907 #ifndef G_OS_WIN32
5908         if(summaryview->simplify_subject_preg) {
5909                 regfree(summaryview->simplify_subject_preg);
5910                 g_free(summaryview->simplify_subject_preg);
5911                 summaryview->simplify_subject_preg = NULL;
5912         }
5913 #endif
5914 }
5915 static void summary_tags_menu_item_apply_tags_activate_cb(GtkWidget *widget,
5916                                                      gpointer data)
5917 {
5918         SummaryView *summaryview;
5919
5920         summaryview = g_object_get_data(G_OBJECT(widget), "summaryview");
5921         g_return_if_fail(summaryview != NULL);
5922
5923         /* "dont_toggle" state set? */
5924         if (g_object_get_data(G_OBJECT(summaryview->tags_menu),
5925                                 "dont_toggle"))
5926                 return;
5927         
5928         tag_apply_open(summary_get_selection(summaryview));     
5929 }
5930
5931 static gint summary_tag_cmp_list(gconstpointer a, gconstpointer b)
5932 {
5933         gint id_a = GPOINTER_TO_INT(a);
5934         gint id_b = GPOINTER_TO_INT(b);
5935         const gchar *tag_a = tags_get_tag(id_a);
5936         const gchar *tag_b = tags_get_tag(id_b);
5937         
5938         if (tag_a == NULL)
5939                 return tag_b == NULL ? 0:1;
5940         
5941         if (tag_b == NULL)
5942                 return tag_a == NULL ? 0:1;
5943
5944         return g_utf8_collate(tag_a, tag_b);
5945 }
5946
5947 static void summary_tags_menu_create(SummaryView *summaryview, gboolean refresh)
5948 {
5949
5950         GtkWidget *label_menuitem;
5951         GtkWidget *menu;
5952         GtkWidget *item;
5953         GSList *cur = tags_get_list();
5954         GSList *orig = NULL;
5955         gboolean existing_tags = FALSE;
5956         gchar *accel_path = NULL;
5957
5958         cur = orig = g_slist_sort(cur, summary_tag_cmp_list);
5959         label_menuitem = gtk_ui_manager_get_widget(gtkut_ui_manager(), "/Menus/SummaryViewPopup/Tags");
5960         g_signal_connect(G_OBJECT(label_menuitem), "activate",
5961                          G_CALLBACK(summary_tags_menu_item_activate_item_cb),
5962                            summaryview);
5963
5964         gtk_widget_show(label_menuitem);
5965
5966         menu = gtk_menu_new();
5967
5968         gtk_menu_set_accel_group (GTK_MENU (menu), 
5969                 gtk_ui_manager_get_accel_group(gtkut_ui_manager()));
5970
5971         /* create tags menu items */
5972         for (; cur; cur = cur->next) {
5973                 gint id = GPOINTER_TO_INT(cur->data);
5974                 const gchar *tag = tags_get_tag(id);
5975                 item = gtk_check_menu_item_new_with_label(tag);
5976                 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
5977                 g_signal_connect(G_OBJECT(item), "activate",
5978                                  G_CALLBACK(summary_tags_menu_item_activate_cb),
5979                                  GINT_TO_POINTER(id));
5980                 g_object_set_data(G_OBJECT(item), "summaryview",
5981                                   summaryview);
5982                 g_object_set_data(G_OBJECT(item), "tag_id",
5983                                   GINT_TO_POINTER(id));
5984                 gtk_widget_show(item);
5985                 accel_path = g_strconcat("<ClawsTags>/",tag, NULL);
5986                 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
5987                 g_free(accel_path);
5988                 existing_tags = TRUE;
5989         }
5990         if (existing_tags) {
5991                 /* separator */
5992                 item = gtk_menu_item_new();
5993                 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
5994                 gtk_widget_show(item);
5995         }
5996
5997         item = gtk_menu_item_new_with_label(_("Apply tags..."));
5998         gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
5999         g_signal_connect(G_OBJECT(item), "activate",
6000                          G_CALLBACK(summary_tags_menu_item_apply_tags_activate_cb),
6001                          NULL);
6002         g_object_set_data(G_OBJECT(item), "summaryview",
6003                           summaryview);
6004         gtk_widget_show(item);
6005         accel_path = g_strdup_printf("<ClawsTags>/ApplyTags");
6006         gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
6007         g_free(accel_path);
6008
6009         g_slist_free(orig);
6010         gtk_widget_show(menu);
6011         gtk_menu_item_set_submenu(GTK_MENU_ITEM(label_menuitem), menu);
6012         summaryview->tags_menu = menu;
6013 }
6014
6015 static gboolean summary_popup_menu(GtkWidget *widget, gpointer data)
6016 {
6017         SummaryView *summaryview = (SummaryView *)data;
6018         summaryview->display_msg = messageview_is_visible(summaryview->messageview);
6019
6020         gtk_menu_popup(GTK_MENU(summaryview->popupmenu), 
6021                        NULL, NULL, NULL, NULL, 
6022                        3, gtk_get_current_event_time());
6023
6024         return TRUE;
6025 }
6026
6027 #if GTK_CHECK_VERSION(2,12,0) && !GENERIC_UMPC
6028 static gchar *summaryview_get_tooltip_text(SummaryView *summaryview, MsgInfo *info, gint column)
6029 {
6030         MsgFlags flags;
6031         if (!info)
6032                 return NULL;
6033
6034         flags = info->flags;
6035
6036         switch(summaryview->col_state[column].type) {
6037                 case S_COL_STATUS:
6038                         if (MSG_IS_IGNORE_THREAD(flags)) {
6039                                 return _("Ignored thread");
6040                         } else if (MSG_IS_WATCH_THREAD(flags)) {
6041                                 return _("Watched thread");
6042                         } else if (MSG_IS_SPAM(flags)) {
6043                                 return _("Spam");
6044                         } else if (MSG_IS_NEW(flags)) {
6045                                 return _("New");
6046                         } else if (MSG_IS_UNREAD(flags)) {
6047                                 return _("Unread");
6048                         } else if (MSG_IS_REPLIED(flags)) {
6049                                 return _("Replied - click to see reply");
6050                         } else if (MSG_IS_FORWARDED(flags)) {
6051                                 return _("Forwarded");
6052                         } else {
6053                                 return NULL;
6054                         }
6055                 case S_COL_MARK:
6056                         if (MSG_IS_DELETED(flags)) {
6057                                 return _("Deleted");
6058                         } else if (MSG_IS_MARKED(flags)) {
6059                                 return _("Marked");
6060                         } else if (MSG_IS_MOVE(flags)) {
6061                                 return _("To be moved");
6062                         } else if (MSG_IS_COPY(flags)) {
6063                                 return _("To be copied");
6064                         } else {
6065                                 return NULL;
6066                         }
6067                 case S_COL_LOCKED:
6068                         if (MSG_IS_LOCKED(flags)) {
6069                                 return _("Locked");
6070                         } else {
6071                                 return NULL;
6072                         }
6073                 case S_COL_MIME:
6074                         if (MSG_IS_WITH_ATTACHMENT(flags) && MSG_IS_SIGNED(flags)) {
6075                                 return _("Signed, has attachment(s)");
6076                         } else if (MSG_IS_SIGNED(flags)) {
6077                                 return _("Signed");
6078                         } else if (MSG_IS_WITH_ATTACHMENT(flags) && MSG_IS_ENCRYPTED(flags)) {
6079                                 return _("Encrypted, has attachment(s)");
6080                         } else if (MSG_IS_ENCRYPTED(flags)) {
6081                                 return _("Encrypted");
6082                         } else if (MSG_IS_WITH_ATTACHMENT(flags)) {
6083                                 return _("Has attachment(s)");
6084                         } else {
6085                                 return NULL;
6086                         }
6087                 default:
6088                         return NULL;
6089         }
6090 }
6091 static gboolean tooltip_cb (GtkWidget  *widget,
6092                             gint        x,
6093                             gint        y,
6094                             gboolean    keyboard_mode,
6095                             GtkTooltip *tooltip,
6096                             gpointer    user_data) 
6097 {
6098         GtkCMCTree *ctree = GTK_CMCTREE(widget);
6099         SummaryView *summaryview = (SummaryView *)user_data;
6100         gint row = -1, column = -1;
6101         int offset = prefs_common.show_col_headers ? 24:0;
6102         GtkCMCTreeNode *node = NULL;
6103         gchar *text = NULL;
6104         gchar *formatted = NULL;
6105         MsgInfo *info = NULL;
6106         GdkRectangle rect;
6107         gboolean vert = (prefs_common.layout_mode == VERTICAL_LAYOUT);
6108
6109         if (!prefs_common.show_tooltips)
6110                 return FALSE;
6111
6112         if (y - offset < 0)
6113                 return FALSE;
6114
6115         if (!gtk_cmclist_get_selection_info(GTK_CMCLIST(ctree), x, y - offset,
6116                                           &row, &column))
6117                 return FALSE;
6118
6119         if ((node = gtk_cmctree_node_nth(ctree, row)) == NULL)
6120                 return FALSE;
6121
6122         if ((info = gtk_cmctree_node_get_row_data(ctree, node)) == NULL)
6123                 return FALSE;
6124
6125         switch (gtk_cmctree_node_get_cell_type(ctree, node, column)) {
6126                 case GTK_CMCELL_TEXT:
6127                         if (gtk_cmctree_node_get_text(ctree, node, column, &text) != TRUE)
6128                                 return FALSE;
6129                         break;
6130                 case GTK_CMCELL_PIXTEXT:
6131                         if (gtk_cmctree_node_get_pixtext(ctree, node, column, &text, 
6132                                 NULL, NULL, NULL) != TRUE)
6133                                 return FALSE;
6134                         break;
6135                 default: 
6136                         if ((text = summaryview_get_tooltip_text(summaryview, info, column)) == NULL)
6137                                 return FALSE;
6138         }
6139         
6140         if (!text || !*text)
6141                 return FALSE;
6142
6143         formatted = g_strdup(text);
6144         g_strstrip(formatted);
6145
6146         if (!vert)      
6147                 gtk_tooltip_set_text (tooltip, formatted);
6148         else if (prefs_common.two_line_vert)
6149                 gtk_tooltip_set_markup (tooltip, formatted);
6150         g_free(formatted);
6151         
6152         rect.x = x - 2;
6153         rect.y = y - 2;
6154         rect.width = 12;
6155         rect.height= 12;        
6156         gtk_tooltip_set_tip_area(tooltip, &rect);
6157         
6158         return TRUE;
6159 }
6160 #endif
6161 static GtkWidget *summary_ctree_create(SummaryView *summaryview)
6162 {
6163         GtkWidget *ctree;
6164         gint *col_pos = summaryview->col_pos;
6165         SummaryColumnState *col_state;
6166         gchar *titles[N_SUMMARY_COLS];
6167         SummaryColumnType type;
6168         gint pos;
6169         gboolean vert = (prefs_common.layout_mode == VERTICAL_LAYOUT);
6170
6171         memset(titles, 0, sizeof(titles));
6172
6173         col_state = prefs_summary_column_get_config();
6174         for (pos = 0; pos < N_SUMMARY_COLS; pos++) {
6175                 summaryview->col_state[pos] = col_state[pos];
6176                 type = col_state[pos].type;
6177                 col_pos[type] = pos;
6178                 titles[pos] = "dummy";
6179         }
6180         col_state = summaryview->col_state;
6181
6182         ctree = gtk_sctree_new_with_titles
6183                 (N_SUMMARY_COLS, col_pos[S_COL_SUBJECT], titles);
6184
6185         /* get normal row height */
6186         gtk_cmclist_set_row_height(GTK_CMCLIST(ctree), 0);
6187         normal_row_height = GTK_CMCLIST(ctree)->row_height;
6188
6189         if (prefs_common.show_col_headers == FALSE)
6190                 gtk_cmclist_column_titles_hide(GTK_CMCLIST(ctree));
6191
6192         gtk_cmclist_set_selection_mode(GTK_CMCLIST(ctree), GTK_SELECTION_EXTENDED);
6193         gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), col_pos[S_COL_MARK],
6194                                            GTK_JUSTIFY_CENTER);
6195         gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), col_pos[S_COL_STATUS],
6196                                            GTK_JUSTIFY_CENTER);
6197         gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), col_pos[S_COL_LOCKED],
6198                                            GTK_JUSTIFY_CENTER);
6199         gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), col_pos[S_COL_MIME],
6200                                            GTK_JUSTIFY_CENTER);
6201         gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), col_pos[S_COL_SIZE],
6202                                            GTK_JUSTIFY_RIGHT);
6203         gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), col_pos[S_COL_NUMBER],
6204                                            GTK_JUSTIFY_RIGHT);
6205         gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), col_pos[S_COL_SCORE],
6206                                            GTK_JUSTIFY_RIGHT);
6207         gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_MARK],
6208                                    prefs_common.summary_col_size[S_COL_MARK]);
6209         gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_STATUS],
6210                                    prefs_common.summary_col_size[S_COL_STATUS]);
6211         gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_LOCKED],
6212                                    prefs_common.summary_col_size[S_COL_LOCKED]);
6213         gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_MIME],
6214                                    prefs_common.summary_col_size[S_COL_MIME]);
6215         gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_SUBJECT],
6216                                    prefs_common.summary_col_size[S_COL_SUBJECT]);
6217         gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_FROM],
6218                                    prefs_common.summary_col_size[S_COL_FROM]);
6219         gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_TO],
6220                                    prefs_common.summary_col_size[S_COL_TO]);
6221         gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_DATE],
6222                                    prefs_common.summary_col_size[S_COL_DATE]);
6223         gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_SIZE],
6224                                    prefs_common.summary_col_size[S_COL_SIZE]);
6225         gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_NUMBER],
6226                                    prefs_common.summary_col_size[S_COL_NUMBER]);
6227         gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_SCORE],
6228                                    prefs_common.summary_col_size[S_COL_SCORE]);
6229         gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_TAGS],
6230                                    prefs_common.summary_col_size[S_COL_TAGS]);
6231
6232         if (prefs_common.enable_dotted_lines) {
6233                 gtk_cmctree_set_line_style(GTK_CMCTREE(ctree), GTK_CMCTREE_LINES_DOTTED);
6234                 gtk_cmctree_set_expander_style(GTK_CMCTREE(ctree),
6235                                      GTK_CMCTREE_EXPANDER_SQUARE);
6236         } else {
6237                 gtk_cmctree_set_line_style(GTK_CMCTREE(ctree), GTK_CMCTREE_LINES_NONE);
6238                 gtk_cmctree_set_expander_style(GTK_CMCTREE(ctree),
6239                                      GTK_CMCTREE_EXPANDER_TRIANGLE);
6240         }
6241
6242         gtk_sctree_set_stripes(GTK_SCTREE(ctree), prefs_common.use_stripes_in_summaries);
6243
6244         gtk_cmctree_set_indent(GTK_CMCTREE(ctree), 12);
6245         g_object_set_data(G_OBJECT(ctree), "summaryview", (gpointer)summaryview); 
6246
6247         for (pos = 0; pos < N_SUMMARY_COLS; pos++) {
6248                 GTK_WIDGET_UNSET_FLAGS(GTK_CMCLIST(ctree)->column[pos].button,
6249                                        GTK_CAN_FOCUS);
6250                 if (((pos == summaryview->col_pos[S_COL_FROM] && !FOLDER_SHOWS_TO_HDR(summaryview->folder_item)) ||
6251                      (pos == summaryview->col_pos[S_COL_TO] && FOLDER_SHOWS_TO_HDR(summaryview->folder_item)) ||
6252                      pos == summaryview->col_pos[S_COL_DATE]) && vert &&
6253                             prefs_common.two_line_vert)
6254                         gtk_cmclist_set_column_visibility
6255                                 (GTK_CMCLIST(ctree), pos, FALSE);
6256                 else
6257                         gtk_cmclist_set_column_visibility
6258                                 (GTK_CMCLIST(ctree), pos, col_state[pos].visible);
6259         }
6260         if (prefs_common.two_line_vert)
6261                 gtk_sctree_set_use_markup(GTK_SCTREE(ctree), summaryview->col_pos[S_COL_SUBJECT], vert);
6262
6263         /* connect signal to the buttons for sorting */
6264 #define CLIST_BUTTON_SIGNAL_CONNECT(col, func) \
6265         g_signal_connect \
6266                 (G_OBJECT(GTK_CMCLIST(ctree)->column[col_pos[col]].button), \
6267                  "clicked", \
6268                  G_CALLBACK(func), \
6269                  summaryview)
6270
6271         CLIST_BUTTON_SIGNAL_CONNECT(S_COL_MARK   , summary_mark_clicked);
6272         CLIST_BUTTON_SIGNAL_CONNECT(S_COL_STATUS , summary_status_clicked);
6273         CLIST_BUTTON_SIGNAL_CONNECT(S_COL_MIME   , summary_mime_clicked);
6274         CLIST_BUTTON_SIGNAL_CONNECT(S_COL_NUMBER , summary_num_clicked);
6275         CLIST_BUTTON_SIGNAL_CONNECT(S_COL_SIZE   , summary_size_clicked);
6276         CLIST_BUTTON_SIGNAL_CONNECT(S_COL_DATE   , summary_date_clicked);
6277         CLIST_BUTTON_SIGNAL_CONNECT(S_COL_FROM   , summary_from_clicked);
6278         CLIST_BUTTON_SIGNAL_CONNECT(S_COL_TO     , summary_to_clicked);
6279         CLIST_BUTTON_SIGNAL_CONNECT(S_COL_SUBJECT, summary_subject_clicked);
6280         CLIST_BUTTON_SIGNAL_CONNECT(S_COL_SCORE,   summary_score_clicked);
6281         CLIST_BUTTON_SIGNAL_CONNECT(S_COL_LOCKED,  summary_locked_clicked);
6282         CLIST_BUTTON_SIGNAL_CONNECT(S_COL_TAGS,    summary_tags_clicked);
6283         
6284 #undef CLIST_BUTTON_SIGNAL_CONNECT
6285
6286         g_signal_connect(G_OBJECT(ctree), "tree_select_row",
6287                          G_CALLBACK(summary_selected), summaryview);
6288         g_signal_connect(G_OBJECT(ctree), "tree_unselect_row",
6289                          G_CALLBACK(summary_unselected), summaryview);
6290         g_signal_connect(G_OBJECT(ctree), "button_press_event",
6291                          G_CALLBACK(summary_button_pressed),
6292                          summaryview);
6293 #ifndef MAEMO
6294         g_signal_connect(G_OBJECT(ctree), "popup-menu",
6295                          G_CALLBACK(summary_popup_menu), summaryview);
6296 #else
6297         gtk_widget_tap_and_hold_setup(GTK_WIDGET(ctree), NULL, NULL,
6298                         GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
6299         g_signal_connect(G_OBJECT(ctree), "tap-and-hold",
6300                          G_CALLBACK(summary_popup_menu), summaryview);
6301 #endif
6302         g_signal_connect(G_OBJECT(ctree), "button_release_event",
6303                          G_CALLBACK(summary_button_released),
6304                          summaryview);
6305         g_signal_connect(G_OBJECT(ctree), "key_press_event",
6306                          G_CALLBACK(summary_key_pressed), summaryview);
6307         g_signal_connect(G_OBJECT(ctree), "resize_column",
6308                          G_CALLBACK(summary_col_resized), summaryview);
6309         g_signal_connect(G_OBJECT(ctree), "open_row",
6310                          G_CALLBACK(summary_open_row), summaryview);
6311
6312         g_signal_connect_after(G_OBJECT(ctree), "tree_expand",
6313                                G_CALLBACK(summary_tree_expanded),
6314                                summaryview);
6315         g_signal_connect_after(G_OBJECT(ctree), "tree_collapse",
6316                                G_CALLBACK(summary_tree_collapsed),
6317                                summaryview);
6318
6319         g_signal_connect(G_OBJECT(ctree), "start_drag",
6320                          G_CALLBACK(summary_start_drag),
6321                          summaryview);
6322         g_signal_connect(G_OBJECT(ctree), "drag_data_get",
6323                          G_CALLBACK(summary_drag_data_get),
6324                          summaryview);
6325         g_signal_connect(G_OBJECT(ctree), "drag_end",
6326                          G_CALLBACK(summary_drag_end),
6327                          summaryview);
6328
6329         gtk_drag_dest_set(ctree, GTK_DEST_DEFAULT_ALL & ~GTK_DEST_DEFAULT_HIGHLIGHT,
6330                           summary_drag_types, 2,
6331                           GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_DEFAULT);
6332
6333         g_signal_connect(G_OBJECT(ctree), "drag_data_received",
6334                          G_CALLBACK(summary_drag_data_received),
6335                          summaryview);
6336
6337         g_signal_connect(G_OBJECT(ctree), "drag_motion",
6338                          G_CALLBACK(summary_drag_motion_cb),
6339                          summaryview);
6340
6341 #if GTK_CHECK_VERSION(2,12,0) && !GENERIC_UMPC
6342         g_object_set (G_OBJECT(ctree), "has-tooltip", TRUE, NULL);
6343         g_signal_connect(G_OBJECT(ctree), "query-tooltip", 
6344                          G_CALLBACK(tooltip_cb),
6345                         summaryview);
6346 #endif
6347         return ctree;
6348 }
6349
6350 void summary_set_column_order(SummaryView *summaryview)
6351 {
6352         GtkWidget *ctree;
6353         GtkWidget *scrolledwin = summaryview->scrolledwin;
6354         FolderItem *item;
6355         guint selected_msgnum = summary_get_msgnum(summaryview, summaryview->selected);
6356         guint displayed_msgnum = summary_get_msgnum(summaryview, summaryview->displayed);
6357
6358         item = summaryview->folder_item;
6359
6360         summary_clear_all(summaryview);
6361         gtk_widget_destroy(summaryview->ctree);
6362
6363         summaryview->ctree = ctree = summary_ctree_create(summaryview);
6364         summary_set_fonts(summaryview);
6365         summary_set_column_titles(summaryview);
6366         gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
6367                                             GTK_CMCLIST(ctree)->hadjustment);
6368         gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
6369                                             GTK_CMCLIST(ctree)->vadjustment);
6370         gtk_container_add(GTK_CONTAINER(scrolledwin), ctree);
6371         gtk_widget_show(ctree);
6372
6373         summary_show(summaryview, item);
6374
6375         summary_select_by_msgnum(summaryview, selected_msgnum);
6376
6377         summaryview->selected = summary_find_msg_by_msgnum(summaryview, selected_msgnum);
6378         summaryview->displayed = summary_find_msg_by_msgnum(summaryview, displayed_msgnum);
6379         summaryview->last_displayed = summaryview->displayed;
6380         if (!summaryview->displayed)
6381                 messageview_clear(summaryview->messageview);
6382         else
6383                 summary_redisplay_msg(summaryview);
6384
6385         if (prefs_common.layout_mode == VERTICAL_LAYOUT &&
6386             prefs_common.two_line_vert) {
6387                 gtk_cmclist_set_row_height(GTK_CMCLIST(summaryview->ctree), 2*normal_row_height + 2);           
6388         } else {
6389                 gtk_cmclist_set_row_height(GTK_CMCLIST(summaryview->ctree), 0);         
6390         }
6391 }
6392
6393
6394 /* callback functions */
6395
6396 static gint summary_folder_eventbox_pressed(GtkWidget *eventbox, GdkEventButton *event,
6397                                             SummaryView *summaryview)
6398 {
6399         if (event) {
6400                 gtk_widget_grab_focus(summaryview->folderview->ctree);
6401                 mainwindow_exit_folder(summaryview->mainwin);
6402         }
6403         return TRUE;
6404 }
6405
6406 static gint summary_toggle_pressed(GtkWidget *eventbox, GdkEventButton *event,
6407                                    SummaryView *summaryview)
6408 {
6409         if (event)  
6410                 summary_toggle_view(summaryview);
6411         return TRUE;
6412 }
6413 #ifdef GENERIC_UMPC
6414 static void summary_toggle_multiple_pressed(GtkWidget *widget,
6415                                    SummaryView *summaryview)
6416 {
6417         GTK_SCTREE(summaryview->ctree)->force_additive_sel = 
6418                 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
6419 }
6420 #endif
6421 static gboolean summary_button_pressed(GtkWidget *ctree, GdkEventButton *event,
6422                                        SummaryView *summaryview)
6423 {
6424         if (!event) return FALSE;
6425
6426         if (event->button == 3) {
6427                 summaryview->display_msg = messageview_is_visible(summaryview->messageview);
6428                 /* right clicked */
6429                 cm_menu_set_sensitive("SummaryViewPopup/Trash", !folder_has_parent_of_type(summaryview->folder_item, F_TRASH));
6430                 gtk_menu_popup(GTK_MENU(summaryview->popupmenu), NULL, NULL,
6431                                NULL, NULL, event->button, event->time);
6432         } else if (event->button == 2) {
6433                 summaryview->display_msg = messageview_is_visible(summaryview->messageview);
6434         } else if (event->button == 1) {
6435                 if (!prefs_common.emulate_emacs &&
6436                     messageview_is_visible(summaryview->messageview))
6437                         summaryview->display_msg = TRUE;
6438         }
6439
6440         return FALSE;
6441 }
6442
6443 static gboolean summary_button_released(GtkWidget *ctree, GdkEventButton *event,
6444                                         SummaryView *summaryview)
6445 {
6446         return FALSE;
6447 }
6448
6449 gboolean summary_pass_key_press_event(SummaryView *summaryview, GdkEventKey *event)
6450 {
6451         if (summary_is_list(summaryview))
6452                 return summary_key_pressed(summaryview->ctree, event, summaryview);
6453         else
6454                 return FALSE;
6455 }
6456
6457 #define BREAK_ON_MODIFIER_KEY() \
6458         if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) break
6459
6460 static gboolean summary_key_pressed(GtkWidget *widget, GdkEventKey *event,
6461                                     SummaryView *summaryview)
6462 {
6463         GtkCMCTree *ctree = GTK_CMCTREE(widget);
6464         GtkCMCTreeNode *node;
6465         MessageView *messageview;
6466         TextView *textview;
6467         GtkAdjustment *adj;
6468         gboolean mod_pressed;
6469
6470         if (!event) 
6471                 return TRUE;
6472
6473         if (quicksearch_has_focus(summaryview->quicksearch))
6474                 return FALSE;
6475
6476         messageview = summaryview->messageview;
6477         textview = messageview->mimeview->textview;
6478
6479         mod_pressed =
6480                 ((event->state & (GDK_SHIFT_MASK|GDK_MOD1_MASK)) != 0);
6481
6482         if (summaryview->selected) {
6483                 gboolean handled = FALSE;
6484                 switch (event->keyval) {
6485                 case GDK_space:         /* Page down or go to the next */
6486                         handled = TRUE;
6487                         if (event->state & GDK_CONTROL_MASK) {
6488                                 handled = FALSE;
6489                                 break;
6490                         }
6491                         if (event->state & GDK_SHIFT_MASK) 
6492                                 mimeview_scroll_page(messageview->mimeview, TRUE);
6493                         else {
6494                                 if (summaryview->displayed != summaryview->selected) {
6495                                         summary_display_msg(summaryview,
6496                                                             summaryview->selected);
6497                                         break;
6498                                 }
6499                                 if (mod_pressed) {
6500                                         if (!mimeview_scroll_page(messageview->mimeview, TRUE))
6501                                                 summary_select_prev_unread(summaryview);
6502                                 } else {
6503                                         if (!mimeview_scroll_page(messageview->mimeview, FALSE))
6504                                                 summary_select_next_unread(summaryview);
6505                                 }                               
6506                         }
6507                         break;
6508                 case GDK_BackSpace:     /* Page up */
6509                         handled = TRUE;
6510                         mimeview_scroll_page(messageview->mimeview, TRUE);
6511                         break;
6512                 case GDK_Return:        /* Scroll up/down one line */
6513                 case GDK_KP_Enter:
6514                         handled = TRUE;
6515                         if (summaryview->displayed != summaryview->selected) {
6516 #ifndef GENERIC_UMPC
6517                                 summary_display_msg(summaryview,
6518                                                     summaryview->selected);
6519 #else
6520                                 summary_open_row(NULL, summaryview);
6521 #endif
6522                                 break;
6523                         }
6524                         mimeview_scroll_one_line(messageview->mimeview, mod_pressed);
6525                         break;
6526                 }
6527                 
6528                 if (handled)
6529                         return FALSE;
6530         }
6531         if (summary_is_locked(summaryview)) 
6532                 return TRUE;
6533
6534         switch (event->keyval) {
6535         case GDK_Left:          /* Move focus */
6536                 adj = gtk_scrolled_window_get_hadjustment
6537                         (GTK_SCROLLED_WINDOW(summaryview->scrolledwin));
6538                 if (adj->lower != adj->value)
6539                         break;
6540                 /* FALLTHROUGH */       
6541         case GDK_Escape:
6542                 gtk_widget_grab_focus(summaryview->folderview->ctree);
6543                 mainwindow_exit_folder(summaryview->mainwin);
6544                 return TRUE;
6545         case GDK_Home:
6546         case GDK_End:
6547                 if ((node = summaryview->selected) != NULL) {
6548                         GtkCMCTreeNode *next = NULL;
6549                         next = (event->keyval == GDK_Home)
6550                                         ? gtk_cmctree_node_nth(ctree, 0)
6551                                         : gtk_cmctree_node_nth(ctree, 
6552                                                 g_list_length(GTK_CMCLIST(ctree)->row_list)-1);
6553                         if (next) {
6554                                 gtk_sctree_select_with_state
6555                                         (GTK_SCTREE(ctree), next, (event->state & ~GDK_CONTROL_MASK) );
6556
6557                                 /* Deprecated - what are the non-deprecated equivalents? */
6558                                 if (gtk_cmctree_node_is_visible(GTK_CMCTREE(ctree), next) != GTK_VISIBILITY_FULL)
6559                                         gtk_cmctree_node_moveto(GTK_CMCTREE(ctree), next, 0, 0, 0);
6560                                 summaryview->selected = next;
6561                         }
6562                 }
6563                 return TRUE;
6564         default:
6565                 break;
6566         }
6567
6568         if (!summaryview->selected) {
6569                 node = gtk_cmctree_node_nth(ctree, 0);
6570                 if (node)
6571                         gtk_sctree_select(GTK_SCTREE(ctree), node);
6572                 else
6573                         return TRUE;
6574         }
6575
6576         switch (event->keyval) {
6577         case GDK_Delete:
6578                 BREAK_ON_MODIFIER_KEY();
6579                 summary_delete_trash(summaryview);
6580                 break;
6581         case GDK_y:
6582         case GDK_t:
6583         case GDK_l:
6584         case GDK_o:
6585         case GDK_c:
6586         case GDK_a:
6587                 if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) == 0) {
6588                         g_signal_stop_emission_by_name(G_OBJECT(widget), 
6589                                        "key_press_event");
6590                         mimeview_pass_key_press_event(messageview->mimeview,
6591                                                       event);
6592                         break;
6593                 }
6594         default:
6595                 break;
6596         }
6597         return FALSE;
6598 }
6599
6600 static void quicksearch_execute_cb(QuickSearch *quicksearch, gpointer data)
6601 {
6602         SummaryView *summaryview = data;
6603
6604         summary_show(summaryview, summaryview->folder_item);
6605 }
6606
6607 static void tog_searchbar_cb(GtkWidget *w, gpointer data)
6608 {
6609         SummaryView *summaryview = (SummaryView *)data;
6610
6611         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w))) {
6612                 prefs_common.show_searchbar = TRUE;
6613                 quicksearch_show(summaryview->quicksearch);
6614         } else {
6615                 prefs_common.show_searchbar = FALSE;
6616                 quicksearch_hide(summaryview->quicksearch);
6617         }
6618 }
6619
6620 void summaryview_activate_quicksearch(SummaryView *summaryview, gboolean show) 
6621 {
6622         prefs_common.show_searchbar = show;
6623         gtk_toggle_button_set_active(
6624                 GTK_TOGGLE_BUTTON(summaryview->toggle_search), 
6625                 show);
6626         if (show) {
6627                 quicksearch_show(summaryview->quicksearch);
6628         } else {
6629                 quicksearch_hide(summaryview->quicksearch);
6630                 summary_grab_focus(summaryview);
6631         }
6632 }
6633
6634 void summary_open_row(GtkSCTree *sctree, SummaryView *summaryview)
6635 {
6636         if (FOLDER_SHOWS_TO_HDR(summaryview->folder_item))
6637                 summary_reedit(summaryview);
6638         else
6639                 summary_open_msg(summaryview);
6640
6641         summaryview->display_msg = FALSE;
6642 }
6643
6644 static void summary_tree_expanded(GtkCMCTree *ctree, GtkCMCTreeNode *node,
6645                                   SummaryView *summaryview)
6646 {
6647         summary_set_row_marks(summaryview, node);
6648         if (prefs_common.bold_unread) {
6649                 while (node) {
6650                         GtkCMCTreeNode *next = GTK_CMCTREE_NODE_NEXT(node);
6651                         if (GTK_CMCTREE_ROW(node)->children)
6652                                 summary_set_row_marks(summaryview, node);
6653                         node = next;
6654                 }
6655         }
6656 }
6657
6658 static void summary_tree_collapsed(GtkCMCTree *ctree, GtkCMCTreeNode *node,
6659                                    SummaryView *summaryview)
6660 {
6661         summary_set_row_marks(summaryview, node);
6662 }
6663
6664 static void summary_unselected(GtkCMCTree *ctree, GtkCMCTreeNode *row,
6665                                gint column, SummaryView *summaryview)
6666 {
6667         if (summary_is_locked(summaryview)
6668         ||  GTK_SCTREE(ctree)->selecting_range) {
6669                 return;
6670         }
6671
6672         summary_status_show(summaryview);
6673 }
6674
6675 static void summary_selected(GtkCMCTree *ctree, GtkCMCTreeNode *row,
6676                              gint column, SummaryView *summaryview)
6677 {
6678         MsgInfo *msginfo;
6679         gboolean marked_unread = FALSE;
6680
6681         if (summary_is_locked(summaryview)
6682         && !GTK_SCTREE(ctree)->selecting_range
6683         && summaryview->messageview
6684         && summaryview->messageview->mimeview
6685         && summaryview->messageview->mimeview->type == MIMEVIEW_TEXT
6686         && summaryview->messageview->mimeview->textview->loading) {
6687                 PostponedSelectData *data = g_new0(PostponedSelectData, 1);
6688                 summaryview->messageview->mimeview->textview->stop_loading = TRUE;
6689                 
6690                 data->ctree = ctree;
6691                 data->row = row;
6692                 data->node = NULL;
6693                 data->column = column;
6694                 data->summaryview = summaryview;
6695                 debug_print("postponing open of message till end of load\n");
6696                 g_timeout_add(100, summary_select_retry, data);
6697                 return;
6698         }
6699         if (summary_is_locked(summaryview)
6700         ||  GTK_SCTREE(ctree)->selecting_range) {
6701                 return;
6702         }
6703
6704         summary_status_show(summaryview);
6705
6706         if (GTK_CMCLIST(ctree)->selection &&
6707             GTK_CMCLIST(ctree)->selection->next) {
6708                 summaryview->display_msg = FALSE;
6709                 summary_set_menu_sensitive(summaryview);
6710                 toolbar_main_set_sensitive(summaryview->mainwin);
6711                 return;
6712         }
6713
6714         summaryview->selected = row;
6715
6716         msginfo = gtk_cmctree_node_get_row_data(ctree, row);
6717         g_return_if_fail(msginfo != NULL);
6718
6719         main_create_mailing_list_menu (summaryview->mainwin, msginfo);
6720         toolbar_set_learn_button
6721                 (summaryview->mainwin->toolbar,
6722                  MSG_IS_SPAM(msginfo->flags)?LEARN_HAM:LEARN_SPAM);
6723
6724         switch (column < 0 ? column : summaryview->col_state[column].type) {
6725         case S_COL_MARK:
6726                 if (!MSG_IS_DELETED(msginfo->flags) &&
6727                     !MSG_IS_MOVE(msginfo->flags) &&
6728                     !MSG_IS_COPY(msginfo->flags)) {
6729                         if (MSG_IS_MARKED(msginfo->flags)) {
6730                                 summary_unmark_row(summaryview, row);
6731                                 summary_status_show(summaryview);
6732                         } else {
6733                                 summary_mark_row(summaryview, row);
6734                                 summary_status_show(summaryview);
6735                         }
6736                 }
6737                 break;
6738         case S_COL_STATUS:
6739                 if (MSG_IS_UNREAD(msginfo->flags)) {
6740                         summary_mark_row_as_read(summaryview, row);
6741                         summary_status_show(summaryview);
6742                 } else if (MSG_IS_SPAM(msginfo->flags)) {
6743                                 if (procmsg_spam_learner_learn(msginfo, NULL, FALSE) == 0)
6744                                         summary_msginfo_unset_flags(msginfo, MSG_SPAM, 0);
6745                                 else
6746                                         log_error(LOG_PROTOCOL, _("An error happened while learning.\n"));
6747                 } else if (!MSG_IS_REPLIED(msginfo->flags) &&
6748                          !MSG_IS_FORWARDED(msginfo->flags)) {
6749                         marked_unread = TRUE;
6750                 } else if (MSG_IS_REPLIED(msginfo->flags)) {
6751                         summary_find_answers(summaryview, msginfo);
6752                         return;
6753                 } 
6754                 break;
6755         case S_COL_LOCKED:
6756                 if (MSG_IS_LOCKED(msginfo->flags)) {
6757                         summary_unlock_row(summaryview, row);
6758                         summary_status_show(summaryview);
6759                 }
6760                 else {
6761                         summary_lock_row(summaryview, row);
6762                         summary_status_show(summaryview);
6763                 }
6764                 break;
6765         default:
6766                 break;
6767         }
6768
6769         if (summaryview->display_msg ||
6770             (prefs_common.always_show_msg &&
6771              messageview_is_visible(summaryview->messageview))) {
6772                 summaryview->display_msg = FALSE;
6773                 if (summaryview->displayed != row) {
6774                         summary_display_msg(summaryview, row);
6775                         if (marked_unread) {
6776                                 summary_mark_row_as_unread(summaryview, row);
6777                                 summary_status_show(summaryview);
6778                         } 
6779                         return;
6780                 }
6781         }
6782         
6783         if (marked_unread) {
6784                 summary_mark_row_as_unread(summaryview, row);
6785                 summary_status_show(summaryview);
6786         } 
6787
6788         summary_set_menu_sensitive(summaryview);
6789         toolbar_main_set_sensitive(summaryview->mainwin);
6790 }
6791
6792 static void summary_col_resized(GtkCMCList *clist, gint column, gint width,
6793                                 SummaryView *summaryview)
6794 {
6795         SummaryColumnType type = summaryview->col_state[column].type;
6796
6797         prefs_common.summary_col_size[type] = width;
6798 }
6799
6800
6801 /*
6802  * \brief get List of msginfo selected in SummaryView
6803  *
6804  * \param summaryview
6805  *
6806  * \return GSList holding MsgInfo
6807  */
6808 GSList *summary_get_selection(SummaryView *summaryview)
6809 {
6810         GList *sel = NULL;
6811         GSList *msginfo_list = NULL;
6812         
6813         g_return_val_if_fail(summaryview != NULL, NULL);
6814
6815         sel = GTK_CMCLIST(summaryview->ctree)->selection;
6816
6817         g_return_val_if_fail(sel != NULL, NULL);
6818
6819         for ( ; sel != NULL; sel = sel->next)
6820                 msginfo_list = 
6821                         g_slist_prepend(msginfo_list, 
6822                                        gtk_cmctree_node_get_row_data(GTK_CMCTREE(summaryview->ctree),
6823                                                                    GTK_CMCTREE_NODE(sel->data)));
6824         return g_slist_reverse(msginfo_list);
6825 }
6826
6827 static void summary_reply_cb(SummaryView *summaryview, guint action,
6828                              GtkWidget *widget)
6829 {
6830         MessageView *msgview = (MessageView*)summaryview->messageview;
6831         GSList *msginfo_list;
6832
6833         g_return_if_fail(msgview != NULL);
6834
6835         msginfo_list = summary_get_selection(summaryview);
6836         g_return_if_fail(msginfo_list != NULL);
6837         compose_reply_from_messageview(msgview, msginfo_list, action);
6838         g_slist_free(msginfo_list);
6839 }
6840 #ifndef GENERIC_UMPC
6841 static void summary_show_all_header_cb(SummaryView *summaryview,
6842                                        guint action, GtkWidget *widget)
6843 {
6844         GtkWidget *menuitem = gtk_ui_manager_get_widget(gtkut_ui_manager(), "/Menus/SummaryViewPopup/View/AllHeaders");
6845         if (!menuitem)
6846                 return;
6847         summaryview->messageview->all_headers =
6848                         GTK_CHECK_MENU_ITEM(menuitem)->active;
6849         summary_display_msg_selected(summaryview,
6850                                      GTK_CHECK_MENU_ITEM(menuitem)->active);
6851 }
6852
6853 static void summary_add_address_cb(SummaryView *summaryview,
6854                                    guint action, GtkWidget *widget)
6855 {
6856         summary_add_address(summaryview);
6857 }
6858 #endif
6859 static void summary_create_filter_cb(SummaryView *summaryview,
6860                                      guint action, GtkWidget *widget)
6861 {
6862         summary_filter_open(summaryview, (PrefsFilterType)action, 0);
6863 }
6864 #ifndef GENERIC_UMPC
6865 static void summary_create_processing_cb(SummaryView *summaryview,
6866                                          guint action, GtkWidget *widget)
6867 {
6868         summary_filter_open(summaryview, (PrefsFilterType)action, 1);
6869 }
6870 #endif
6871 static void summary_sort_by_column_click(SummaryView *summaryview,
6872                                          FolderSortKey sort_key)
6873 {
6874         GtkCMCTreeNode *node = NULL;
6875         START_TIMING("");
6876         if (summaryview->sort_key == sort_key)
6877                 summary_sort(summaryview, sort_key,
6878                              summaryview->sort_type == SORT_ASCENDING
6879                              ? SORT_DESCENDING : SORT_ASCENDING);
6880         else
6881                 summary_sort(summaryview, sort_key, SORT_ASCENDING);
6882
6883         node = GTK_CMCTREE_NODE(GTK_CMCLIST(summaryview->ctree)->row_list);
6884
6885         summary_freeze(summaryview);
6886         if (prefs_common.bold_unread) {
6887                 while (node) {
6888                         GtkCMCTreeNode *next = GTK_CMCTREE_NODE_NEXT(node);
6889                         if (GTK_CMCTREE_ROW(node)->children)
6890                                 summary_set_row_marks(summaryview, node);
6891                         node = next;
6892                 }
6893         }
6894         summary_thaw(summaryview);
6895         END_TIMING();
6896 }
6897
6898 static void summary_mark_clicked(GtkWidget *button, SummaryView *summaryview)
6899 {
6900         summary_sort_by_column_click(summaryview, SORT_BY_MARK);
6901 }
6902
6903 static void summary_status_clicked(GtkWidget *button, SummaryView *summaryview)
6904 {
6905         summary_sort_by_column_click(summaryview, SORT_BY_STATUS);
6906 }
6907
6908 static void summary_mime_clicked(GtkWidget *button, SummaryView *summaryview)
6909 {
6910         summary_sort_by_column_click(summaryview, SORT_BY_MIME);
6911 }
6912
6913 static void summary_num_clicked(GtkWidget *button, SummaryView *summaryview)
6914 {
6915         summary_sort_by_column_click(summaryview, SORT_BY_NUMBER);
6916 }
6917
6918 static void summary_size_clicked(GtkWidget *button, SummaryView *summaryview)
6919 {
6920         summary_sort_by_column_click(summaryview, SORT_BY_SIZE);
6921 }
6922
6923 static void summary_date_clicked(GtkWidget *button, SummaryView *summaryview)
6924 {
6925         summary_sort_by_column_click(summaryview, SORT_BY_DATE);
6926 }
6927
6928 static void summary_from_clicked(GtkWidget *button, SummaryView *summaryview)
6929 {
6930         if (summaryview->col_state[summaryview->col_pos[S_COL_FROM]].visible)
6931                 summary_sort_by_column_click(summaryview, SORT_BY_FROM);
6932         else
6933                 summary_sort_by_column_click(summaryview, SORT_BY_TO);
6934 }
6935
6936 static void summary_to_clicked(GtkWidget *button, SummaryView *summaryview)
6937 {
6938         if (summaryview->col_state[summaryview->col_pos[S_COL_TO]].visible)
6939                 summary_sort_by_column_click(summaryview, SORT_BY_TO);
6940         else
6941                 summary_sort_by_column_click(summaryview, SORT_BY_FROM);
6942 }
6943
6944 static void summary_subject_clicked(GtkWidget *button,
6945                                     SummaryView *summaryview)
6946 {
6947         summary_sort_by_column_click(summaryview, SORT_BY_SUBJECT);
6948 }
6949
6950 static void summary_score_clicked(GtkWidget *button,
6951                                   SummaryView *summaryview)
6952 {
6953         summary_sort_by_column_click(summaryview, SORT_BY_SCORE);
6954 }
6955
6956 static void summary_locked_clicked(GtkWidget *button,
6957                                    SummaryView *summaryview)
6958 {
6959         summary_sort_by_column_click(summaryview, SORT_BY_LOCKED);
6960 }
6961
6962 static void summary_tags_clicked(GtkWidget *button,
6963                                    SummaryView *summaryview)
6964 {
6965         summary_sort_by_column_click(summaryview, SORT_BY_TAGS);
6966 }
6967
6968 static void summary_start_drag(GtkWidget *widget, gint button, GdkEvent *event,
6969                                SummaryView *summaryview)
6970 {
6971         GdkDragContext *context;
6972
6973         g_return_if_fail(summaryview != NULL);
6974         g_return_if_fail(summaryview->folder_item != NULL);
6975         g_return_if_fail(summaryview->folder_item->folder != NULL);
6976
6977         if (summaryview->selected == NULL) return;
6978
6979         context = gtk_drag_begin(widget, summaryview->target_list,
6980                                  GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_DEFAULT, button, event);
6981         gtk_drag_set_icon_default(context);
6982         if (prefs_common.layout_mode == SMALL_LAYOUT) {
6983                 GtkWidget *paned = GTK_WIDGET_PTR(summaryview)->parent;
6984                 if (paned && GTK_IS_PANED(paned)) {
6985                         mainwindow_reset_paned(GTK_PANED(paned));
6986                 }
6987         }
6988 }
6989
6990 static gboolean summary_return_to_list(void *data)
6991 {
6992         SummaryView *summaryview = (SummaryView *)data;
6993         mainwindow_enter_folder(summaryview->mainwin);
6994         return FALSE;
6995 }
6996
6997 static void summary_drag_end       (GtkWidget       *widget, 
6998                                    GdkDragContext   *drag_context,
6999                                    SummaryView      *summaryview)
7000 {
7001         if (prefs_common.layout_mode == SMALL_LAYOUT) {
7002                 g_timeout_add(250, summary_return_to_list, summaryview);
7003         }
7004 }
7005
7006 static void summary_drag_data_get(GtkWidget        *widget,
7007                                   GdkDragContext   *drag_context,
7008                                   GtkSelectionData *selection_data,
7009                                   guint             info,
7010                                   guint             time,
7011                                   SummaryView      *summaryview)
7012 {
7013         if (info == TARGET_MAIL_URI_LIST) {
7014                 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
7015                 GList *cur;
7016                 MsgInfo *msginfo;
7017                 gchar *mail_list = NULL, *tmp1, *tmp2;
7018
7019                 for (cur = GTK_CMCLIST(ctree)->selection;
7020                      cur != NULL && cur->data != NULL; cur = cur->next) {
7021                         msginfo = gtk_cmctree_node_get_row_data
7022                                 (ctree, GTK_CMCTREE_NODE(cur->data));
7023                         tmp2 = procmsg_get_message_file(msginfo);
7024                         if (!tmp2) continue;
7025                         if (msginfo->subject) {
7026                                 gchar *san_subject = g_strdup(msginfo->subject);
7027                                 gchar *dest = NULL;
7028                                 subst_for_filename(san_subject);
7029                                 dest = g_strdup_printf("%s%s%s.%d.txt",
7030                                                 get_tmp_dir(),
7031                                                 G_DIR_SEPARATOR_S,
7032                                                 san_subject, msginfo->msgnum);
7033                                 g_free(san_subject);
7034                                 san_subject = g_filename_from_utf8(dest, -1, NULL, NULL, NULL);
7035                                 g_free(dest);
7036                                 dest = san_subject;
7037                                 if (copy_file(tmp2, dest, TRUE) == 0) {
7038                                         g_free(tmp2);
7039                                         tmp2 = dest;
7040                                 }
7041                         } 
7042                         tmp1 = g_filename_to_uri(tmp2, NULL, NULL);
7043                         g_free(tmp2);
7044                         tmp2 = g_strconcat(tmp1, "\r\n", NULL);
7045                         g_free(tmp1);
7046                         tmp1 = tmp2;
7047
7048                         if (!mail_list) {
7049                                 mail_list = tmp1;
7050                         } else {
7051                                 tmp2 = g_strconcat(mail_list, tmp1, NULL);
7052                                 g_free(mail_list);
7053                                 g_free(tmp1);
7054                                 mail_list = tmp2;
7055                         }
7056                 }
7057
7058                 if (mail_list != NULL) {
7059                         gtk_selection_data_set(selection_data,
7060                                                selection_data->target, 8,
7061                                                mail_list, strlen(mail_list));
7062                         g_free(mail_list);
7063                 } 
7064         } else if (info == TARGET_DUMMY) {
7065                 if (GTK_CMCLIST(summaryview->ctree)->selection)
7066                         gtk_selection_data_set(selection_data,
7067                                                selection_data->target, 8,
7068                                                "Dummy-Summaryview", 
7069                                                strlen("Dummy-Summaryview")+1);
7070         }
7071 }
7072
7073 static gboolean summary_drag_motion_cb(GtkWidget      *widget,
7074                                           GdkDragContext *context,
7075                                           gint            x,
7076                                           gint            y,
7077                                           guint           time,
7078                                           SummaryView    *summaryview)
7079 {
7080         FolderItem *item = summaryview->folder_item;
7081         if (!(item && item->folder && folder_item_parent(item) != NULL
7082                     && FOLDER_CLASS(item->folder)->add_msg != NULL)) {
7083                 gdk_drag_status(context, 0, time);
7084                 return FALSE;
7085         } else if (gtk_drag_get_source_widget(context) ==
7086                 mainwindow_get_mainwindow()->folderview->ctree) {
7087                 /* no folders */
7088                 gdk_drag_status(context, 0, time);
7089                 return FALSE;
7090         } else if (gtk_drag_get_source_widget(context) ==
7091                 summaryview->ctree) {
7092                 /* not from same folder */
7093                 gdk_drag_status(context, 0, time);
7094                 return FALSE;
7095         } else {
7096                 gdk_drag_status(context, GDK_ACTION_COPY, time);
7097                 return TRUE;
7098         }
7099 }
7100
7101 static void summary_drag_data_received(GtkWidget        *widget,
7102                                         GdkDragContext   *drag_context,
7103                                         gint              x,
7104                                         gint              y,
7105                                         GtkSelectionData *data,
7106                                         guint             info,
7107                                         guint             time,
7108                                         SummaryView       *summaryview)
7109 {
7110         if (info == TARGET_MAIL_URI_LIST) {
7111                 FolderItem *item = summaryview->folder_item;
7112                 if (!item) {
7113                         gtk_drag_finish(drag_context, FALSE, FALSE, time);                      
7114                         return;
7115                 } else {
7116                         folderview_finish_dnd(data->data, drag_context, time, item);
7117                 }
7118         }
7119 }
7120
7121
7122 /* custom compare functions for sorting */
7123
7124 static gint summary_cmp_by_date(GtkCMCList *clist,
7125                       gconstpointer ptr1, gconstpointer ptr2)
7126 {
7127         MsgInfo *msginfo1 = ((GtkCMCListRow *)ptr1)->data;
7128         MsgInfo *msginfo2 = ((GtkCMCListRow *)ptr2)->data;
7129         gint res;
7130         if (!msginfo1 || !msginfo2)
7131                 return -1;
7132
7133         res = (msginfo1->date_t - msginfo2->date_t);
7134         return res;
7135 }
7136
7137 #define CMP_FUNC_DEF(func_name, val)                                     \
7138 static gint func_name(GtkCMCList *clist,                                 \
7139                       gconstpointer ptr1, gconstpointer ptr2)            \
7140 {                                                                        \
7141         MsgInfo *msginfo1 = ((GtkCMCListRow *)ptr1)->data;               \
7142         MsgInfo *msginfo2 = ((GtkCMCListRow *)ptr2)->data;               \
7143         gint res;                                                        \
7144         if (!msginfo1 || !msginfo2)                                      \
7145                 return -1;                                               \
7146                                                                          \
7147         res = (val);                                                     \
7148         return (res != 0) ? res:summary_cmp_by_date(clist, ptr1, ptr2);  \
7149 }
7150
7151 CMP_FUNC_DEF(summary_cmp_by_mark,
7152              MSG_IS_MARKED(msginfo1->flags) - MSG_IS_MARKED(msginfo2->flags))
7153 CMP_FUNC_DEF(summary_cmp_by_status,
7154              (-(MSG_IS_SPAM(msginfo1->flags))+(MSG_IS_UNREAD(msginfo1->flags)<<1)+(MSG_IS_NEW(msginfo1->flags)<<2)) 
7155              - (-(MSG_IS_SPAM(msginfo2->flags))+(MSG_IS_UNREAD(msginfo2->flags)<<1)+(MSG_IS_NEW(msginfo2->flags)<<2)) )
7156 CMP_FUNC_DEF(summary_cmp_by_mime,
7157              MSG_IS_WITH_ATTACHMENT(msginfo1->flags) - MSG_IS_WITH_ATTACHMENT(msginfo2->flags))
7158 CMP_FUNC_DEF(summary_cmp_by_label,
7159              MSG_GET_COLORLABEL(msginfo1->flags) -
7160              MSG_GET_COLORLABEL(msginfo2->flags))
7161 CMP_FUNC_DEF(summary_cmp_by_locked,
7162              MSG_IS_LOCKED(msginfo1->flags) - MSG_IS_LOCKED(msginfo2->flags))
7163
7164 CMP_FUNC_DEF(summary_cmp_by_num, msginfo1->msgnum - msginfo2->msgnum)
7165 CMP_FUNC_DEF(summary_cmp_by_size, msginfo1->size - msginfo2->size)
7166
7167 #undef CMP_FUNC_DEF
7168
7169 static gint summary_cmp_by_subject(GtkCMCList *clist,
7170                                    gconstpointer ptr1,
7171                                    gconstpointer ptr2)
7172 {
7173         MsgInfo *msginfo1 = ((GtkCMCListRow *)ptr1)->data;
7174         MsgInfo *msginfo2 = ((GtkCMCListRow *)ptr2)->data;
7175         gint res;
7176
7177         if (!msginfo1->subject)
7178                 return (msginfo2->subject != NULL);
7179         if (!msginfo2->subject)
7180                 return -1;
7181
7182         res = subject_compare_for_sort
7183                 (msginfo1->subject, msginfo2->subject);
7184         return (res != 0)? res: summary_cmp_by_date(clist, ptr1, ptr2);
7185 }
7186
7187 static gint summary_cmp_by_thread_date(GtkCMCList *clist,
7188                                    gconstpointer ptr1,
7189                                    gconstpointer ptr2)
7190 {
7191         MsgInfo *msginfo1 = ((GtkCMCListRow *)ptr1)->data;
7192         MsgInfo *msginfo2 = ((GtkCMCListRow *)ptr2)->data;
7193         gint thread_diff = msginfo1->thread_date - msginfo2->thread_date;
7194         
7195         if (msginfo1->thread_date > 0 && msginfo2->thread_date > 0)
7196                 return thread_diff;
7197         else 
7198                 return msginfo1->date_t - msginfo2->date_t;
7199 }
7200
7201 static gint summary_cmp_by_from(GtkCMCList *clist, gconstpointer ptr1,
7202                                 gconstpointer ptr2)
7203 {
7204         const gchar *str1, *str2;
7205         const GtkCMCListRow *r1 = (const GtkCMCListRow *) ptr1;
7206         const GtkCMCListRow *r2 = (const GtkCMCListRow *) ptr2;
7207         MsgInfo *msginfo1 = ((GtkCMCListRow *)ptr1)->data;
7208         MsgInfo *msginfo2 = ((GtkCMCListRow *)ptr2)->data;
7209         const SummaryView *sv = g_object_get_data(G_OBJECT(clist), "summaryview");
7210         gint res;
7211
7212         g_return_val_if_fail(sv, -1);
7213         if (sv->col_state[sv->col_pos[S_COL_FROM]].visible) {
7214                 str1 = GTK_CMCELL_TEXT(r1->cell[sv->col_pos[S_COL_FROM]])->text;
7215                 str2 = GTK_CMCELL_TEXT(r2->cell[sv->col_pos[S_COL_FROM]])->text;
7216         } else {
7217                 str1 = msginfo1->from;
7218                 str2 = msginfo2->from;
7219         }
7220
7221         if (!str1)
7222                 return str2 != NULL;
7223  
7224         if (!str2)
7225                 return -1;
7226  
7227         res = g_utf8_collate(str1, str2);
7228         return (res != 0)? res: summary_cmp_by_date(clist, ptr1, ptr2);
7229 }
7230  
7231 static gint summary_cmp_by_to(GtkCMCList *clist, gconstpointer ptr1,
7232                                 gconstpointer ptr2)
7233 {
7234         const gchar *str1, *str2;
7235         const GtkCMCListRow *r1 = (const GtkCMCListRow *) ptr1;
7236         const GtkCMCListRow *r2 = (const GtkCMCListRow *) ptr2;
7237         MsgInfo *msginfo1 = ((GtkCMCListRow *)ptr1)->data;
7238         MsgInfo *msginfo2 = ((GtkCMCListRow *)ptr2)->data;
7239         const SummaryView *sv = g_object_get_data(G_OBJECT(clist), "summaryview");
7240         gint res;
7241         g_return_val_if_fail(sv, -1);
7242         
7243         if (sv->col_state[sv->col_pos[S_COL_TO]].visible) {
7244                 str1 = GTK_CMCELL_TEXT(r1->cell[sv->col_pos[S_COL_TO]])->text;
7245                 str2 = GTK_CMCELL_TEXT(r2->cell[sv->col_pos[S_COL_TO]])->text;
7246         } else {
7247                 str1 = msginfo1->to;
7248                 str2 = msginfo2->to;
7249         }
7250
7251         if (!str1)
7252                 return str2 != NULL;
7253  
7254         if (!str2)
7255                 return -1;
7256  
7257         res = g_utf8_collate(str1, str2);
7258         return (res != 0)? res: summary_cmp_by_date(clist, ptr1, ptr2);
7259 }
7260  
7261 static gint summary_cmp_by_tags(GtkCMCList *clist, gconstpointer ptr1,
7262                                 gconstpointer ptr2)
7263 {
7264         gchar *str1, *str2;
7265         const GtkCMCListRow *r1 = (const GtkCMCListRow *) ptr1;
7266         const GtkCMCListRow *r2 = (const GtkCMCListRow *) ptr2;
7267         const SummaryView *sv = g_object_get_data(G_OBJECT(clist), "summaryview");
7268         MsgInfo *msginfo1 = ((GtkCMCListRow *)ptr1)->data;
7269         MsgInfo *msginfo2 = ((GtkCMCListRow *)ptr2)->data;
7270         gint res;
7271         g_return_val_if_fail(sv, -1);
7272         
7273         if (sv->col_state[sv->col_pos[S_COL_TAGS]].visible) {
7274                 str1 = g_strdup(GTK_CMCELL_TEXT(r1->cell[sv->col_pos[S_COL_TAGS]])->text);
7275                 str2 = g_strdup(GTK_CMCELL_TEXT(r2->cell[sv->col_pos[S_COL_TAGS]])->text);
7276         } else {
7277                 str1 = procmsg_msginfo_get_tags_str(msginfo1);
7278                 str2 = procmsg_msginfo_get_tags_str(msginfo2);
7279         }
7280
7281         if (!str1) {
7282                 res = (str2 != NULL);
7283                 g_free(str2);
7284                 return res;
7285         }
7286         if (!str2) {
7287                 g_free(str1);
7288                 return -1;
7289         }
7290  
7291         res = g_utf8_collate(str1, str2);
7292         g_free(str1);
7293         g_free(str2);
7294         return (res != 0)? res: summary_cmp_by_date(clist, ptr1, ptr2);
7295 }
7296  
7297 static gint summary_cmp_by_simplified_subject
7298         (GtkCMCList *clist, gconstpointer ptr1, gconstpointer ptr2)
7299 {
7300         const FolderItemPrefs *prefs;
7301         const gchar *str1, *str2;
7302         const GtkCMCListRow *r1 = (const GtkCMCListRow *) ptr1;
7303         const GtkCMCListRow *r2 = (const GtkCMCListRow *) ptr2;
7304         const MsgInfo *msginfo1 = r1->data;
7305         const MsgInfo *msginfo2 = r2->data;
7306         const SummaryView *sv = g_object_get_data(G_OBJECT(clist), "summaryview");
7307         gint res;
7308
7309         g_return_val_if_fail(sv, -1);
7310         g_return_val_if_fail(msginfo1 != NULL && msginfo2 != NULL, -1);
7311         
7312         if (sv->col_state[sv->col_pos[S_COL_SUBJECT]].visible) {
7313                 str1 = GTK_CMCELL_TEXT(r1->cell[sv->col_pos[S_COL_SUBJECT]])->text;
7314                 str2 = GTK_CMCELL_TEXT(r2->cell[sv->col_pos[S_COL_SUBJECT]])->text;
7315         } else {
7316                 str1 = msginfo1->subject;
7317                 str2 = msginfo2->subject;
7318         }
7319
7320         if (!str1)
7321                 return str2 != NULL;
7322
7323         if (!str2)
7324                 return -1;
7325
7326         prefs = msginfo1->folder->prefs;
7327         if (!prefs)
7328                 prefs = msginfo2->folder->prefs;
7329         if (!prefs)
7330                 return -1;
7331         
7332         res = subject_compare_for_sort(str1, str2);
7333         return (res != 0)? res: summary_cmp_by_date(clist, ptr1, ptr2);
7334 }
7335
7336 static gint summary_cmp_by_score(GtkCMCList *clist,
7337                                  gconstpointer ptr1, gconstpointer ptr2)
7338 {
7339         MsgInfo *msginfo1 = ((GtkCMCListRow *)ptr1)->data;
7340         MsgInfo *msginfo2 = ((GtkCMCListRow *)ptr2)->data;
7341         int diff;
7342
7343         /* if score are equal, sort by date */
7344
7345         diff = msginfo1->score - msginfo2->score;
7346         if (diff != 0)
7347                 return diff;
7348         else
7349                 return summary_cmp_by_date(clist, ptr1, ptr2);
7350 }
7351
7352 static void summary_ignore_thread_func(GtkCMCTree *ctree, GtkCMCTreeNode *row, gpointer data)
7353 {
7354         SummaryView *summaryview = (SummaryView *) data;
7355         MsgInfo *msginfo;
7356
7357         msginfo = gtk_cmctree_node_get_row_data(ctree, row);
7358         g_return_if_fail(msginfo);
7359
7360         summary_msginfo_unset_flags(msginfo, MSG_WATCH_THREAD, 0);
7361         summary_msginfo_change_flags(msginfo, MSG_IGNORE_THREAD, 0, MSG_NEW | MSG_UNREAD, 0);
7362
7363         summary_set_row_marks(summaryview, row);
7364         debug_print("Message %d is marked as ignore thread\n",
7365             msginfo->msgnum);
7366 }
7367
7368 void summary_ignore_thread(SummaryView *summaryview)
7369 {
7370         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
7371         GList *cur;
7372         gboolean froze = FALSE;
7373
7374         START_LONG_OPERATION(summaryview, FALSE);
7375         for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
7376                 gtk_cmctree_pre_recursive(ctree, GTK_CMCTREE_NODE(cur->data), 
7377                                         GTK_CMCTREE_FUNC(summary_ignore_thread_func), 
7378                                         summaryview);
7379
7380         END_LONG_OPERATION(summaryview);
7381
7382         summary_status_show(summaryview);
7383 }
7384
7385 static void summary_unignore_thread_func(GtkCMCTree *ctree, GtkCMCTreeNode *row, gpointer data)
7386 {
7387         SummaryView *summaryview = (SummaryView *) data;
7388         MsgInfo *msginfo;
7389
7390         msginfo = gtk_cmctree_node_get_row_data(ctree, row);
7391         g_return_if_fail(msginfo);
7392
7393         summary_msginfo_unset_flags(msginfo, MSG_IGNORE_THREAD, 0);
7394
7395         summary_set_row_marks(summaryview, row);
7396         debug_print("Message %d is marked as unignore thread\n",
7397             msginfo->msgnum);
7398 }
7399
7400 void summary_unignore_thread(SummaryView *summaryview)
7401 {
7402         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
7403         GList *cur;
7404         gboolean froze = FALSE;
7405
7406         START_LONG_OPERATION(summaryview, FALSE);
7407         for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
7408                 gtk_cmctree_pre_recursive(ctree, GTK_CMCTREE_NODE(cur->data), 
7409                                         GTK_CMCTREE_FUNC(summary_unignore_thread_func), 
7410                                         summaryview);
7411
7412         END_LONG_OPERATION(summaryview);
7413
7414         summary_status_show(summaryview);
7415 }
7416
7417 static void summary_check_ignore_thread_func
7418                 (GtkCMCTree *ctree, GtkCMCTreeNode *row, gpointer data)
7419 {
7420         MsgInfo *msginfo;
7421         gint *found_ignore = (gint *) data;
7422
7423         if (*found_ignore) return;
7424         else {
7425                 msginfo = gtk_cmctree_node_get_row_data(ctree, row);
7426                 *found_ignore = msginfo && MSG_IS_IGNORE_THREAD(msginfo->flags);
7427         }               
7428 }
7429
7430 void summary_toggle_ignore_thread(SummaryView *summaryview)
7431 {
7432         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
7433         GList *cur;
7434         gint found_ignore = 0;
7435
7436         for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
7437                 gtk_cmctree_pre_recursive(ctree, GTK_CMCTREE_NODE(cur->data),
7438                                         GTK_CMCTREE_FUNC(summary_check_ignore_thread_func),
7439                                         &found_ignore);
7440
7441         if (found_ignore) 
7442                 summary_unignore_thread(summaryview);
7443         else 
7444                 summary_ignore_thread(summaryview);
7445 }
7446
7447 static void summary_watch_thread_func(GtkCMCTree *ctree, GtkCMCTreeNode *row, gpointer data)
7448 {
7449         SummaryView *summaryview = (SummaryView *) data;
7450         MsgInfo *msginfo;
7451
7452         msginfo = gtk_cmctree_node_get_row_data(ctree, row);
7453         g_return_if_fail(msginfo);
7454
7455         summary_msginfo_change_flags(msginfo, MSG_WATCH_THREAD, 0, MSG_IGNORE_THREAD, 0);
7456
7457         summary_set_row_marks(summaryview, row);
7458         debug_print("Message %d is marked as watch thread\n",
7459             msginfo->msgnum);
7460 }
7461
7462 void summary_watch_thread(SummaryView *summaryview)
7463 {
7464         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
7465         GList *cur;
7466         gboolean froze = FALSE;
7467
7468         START_LONG_OPERATION(summaryview, FALSE);
7469         for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
7470                 gtk_cmctree_pre_recursive(ctree, GTK_CMCTREE_NODE(cur->data), 
7471                                         GTK_CMCTREE_FUNC(summary_watch_thread_func), 
7472                                         summaryview);
7473
7474         END_LONG_OPERATION(summaryview);
7475
7476         summary_status_show(summaryview);
7477 }
7478
7479 static void summary_unwatch_thread_func(GtkCMCTree *ctree, GtkCMCTreeNode *row, gpointer data)
7480 {
7481         SummaryView *summaryview = (SummaryView *) data;
7482         MsgInfo *msginfo;
7483
7484         msginfo = gtk_cmctree_node_get_row_data(ctree, row);
7485         g_return_if_fail(msginfo);
7486
7487         summary_msginfo_unset_flags(msginfo, MSG_WATCH_THREAD, 0);
7488
7489         summary_set_row_marks(summaryview, row);
7490         debug_print("Message %d is marked as unwatch thread\n",
7491             msginfo->msgnum);
7492 }
7493
7494 void summary_unwatch_thread(SummaryView *summaryview)
7495 {
7496         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
7497         GList *cur;
7498         gboolean froze = FALSE;
7499
7500         START_LONG_OPERATION(summaryview, FALSE);
7501         for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
7502                 gtk_cmctree_pre_recursive(ctree, GTK_CMCTREE_NODE(cur->data), 
7503                                         GTK_CMCTREE_FUNC(summary_unwatch_thread_func), 
7504                                         summaryview);
7505
7506         END_LONG_OPERATION(summaryview);
7507
7508         summary_status_show(summaryview);
7509 }
7510
7511 static void summary_check_watch_thread_func
7512                 (GtkCMCTree *ctree, GtkCMCTreeNode *row, gpointer data)
7513 {
7514         MsgInfo *msginfo;
7515         gint *found_watch = (gint *) data;
7516
7517         if (*found_watch) return;
7518         else {
7519                 msginfo = gtk_cmctree_node_get_row_data(ctree, row);
7520                 *found_watch = msginfo && MSG_IS_WATCH_THREAD(msginfo->flags);
7521         }               
7522 }
7523
7524 void summary_toggle_watch_thread(SummaryView *summaryview)
7525 {
7526         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
7527         GList *cur;
7528         gint found_watch = 0;
7529
7530         for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
7531                 gtk_cmctree_pre_recursive(ctree, GTK_CMCTREE_NODE(cur->data),
7532                                         GTK_CMCTREE_FUNC(summary_check_watch_thread_func),
7533                                         &found_watch);
7534
7535         if (found_watch) 
7536                 summary_unwatch_thread(summaryview);
7537         else 
7538                 summary_watch_thread(summaryview);
7539 }
7540
7541 void summary_toggle_show_read_messages(SummaryView *summaryview)
7542 {
7543         FolderItemUpdateData source;
7544         if (summaryview->folder_item->hide_read_msgs)
7545                 summaryview->folder_item->hide_read_msgs = 0;
7546         else
7547                 summaryview->folder_item->hide_read_msgs = 1;
7548
7549         source.item = summaryview->folder_item;
7550         source.update_flags = F_ITEM_UPDATE_NAME;
7551         source.msg = NULL;
7552         hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &source);
7553         summary_show(summaryview, summaryview->folder_item);
7554 }
7555  
7556 static void summary_set_hide_read_msgs_menu (SummaryView *summaryview,
7557                                              guint action)
7558 {
7559         GtkWidget *widget;
7560
7561         widget = gtk_ui_manager_get_widget(summaryview->mainwin->ui_manager, "/Menu/View/HideReadMessages");
7562         g_object_set_data(G_OBJECT(widget), "dont_toggle",
7563                           GINT_TO_POINTER(1));
7564         gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM(widget), action);
7565         g_object_set_data(G_OBJECT(widget), "dont_toggle",
7566                           GINT_TO_POINTER(0));
7567 }
7568
7569 void summary_reflect_prefs_pixmap_theme(SummaryView *summaryview)
7570 {
7571         GtkWidget *ctree = summaryview->ctree;
7572         GtkWidget *pixmap; 
7573
7574         stock_pixmap_gdk(ctree, STOCK_PIXMAP_MARK, &markxpm, &markxpmmask);
7575         stock_pixmap_gdk(ctree, STOCK_PIXMAP_DELETED, &deletedxpm, &deletedxpmmask);
7576         stock_pixmap_gdk(ctree, STOCK_PIXMAP_NEW, &newxpm, &newxpmmask);
7577         stock_pixmap_gdk(ctree, STOCK_PIXMAP_UNREAD, &unreadxpm, &unreadxpmmask);
7578         stock_pixmap_gdk(ctree, STOCK_PIXMAP_REPLIED, &repliedxpm, &repliedxpmmask);
7579         stock_pixmap_gdk(ctree, STOCK_PIXMAP_FORWARDED, &forwardedxpm, &forwardedxpmmask);
7580         stock_pixmap_gdk(ctree, STOCK_PIXMAP_CLIP, &clipxpm, &clipxpmmask);
7581         stock_pixmap_gdk(ctree, STOCK_PIXMAP_LOCKED, &lockedxpm, &lockedxpmmask);
7582         stock_pixmap_gdk(ctree, STOCK_PIXMAP_IGNORETHREAD, &ignorethreadxpm, &ignorethreadxpmmask);
7583         stock_pixmap_gdk(ctree, STOCK_PIXMAP_WATCHTHREAD, &watchthreadxpm, &watchthreadxpmmask);
7584         stock_pixmap_gdk(ctree, STOCK_PIXMAP_CLIP_KEY, &clipkeyxpm, &clipkeyxpmmask);
7585         stock_pixmap_gdk(ctree, STOCK_PIXMAP_KEY, &keyxpm, &keyxpmmask);
7586         stock_pixmap_gdk(ctree, STOCK_PIXMAP_KEY_SIGN, &keysignxpm, &keysignxpmmask);
7587         stock_pixmap_gdk(ctree, STOCK_PIXMAP_GPG_SIGNED, &gpgsignedxpm, &gpgsignedxpmmask);
7588         stock_pixmap_gdk(ctree, STOCK_PIXMAP_CLIP_GPG_SIGNED, &clipgpgsignedxpm, &clipgpgsignedxpmmask);
7589         stock_pixmap_gdk(ctree, STOCK_PIXMAP_SPAM, &spamxpm, &spamxpmmask);
7590         stock_pixmap_gdk(ctree, STOCK_PIXMAP_MOVED, &movedxpm, &movedxpmmask);
7591         stock_pixmap_gdk(ctree, STOCK_PIXMAP_COPIED, &copiedxpm, &copiedxpmmask);
7592
7593         summary_set_folder_pixmap(summaryview, STOCK_PIXMAP_DIR_OPEN);
7594
7595         pixmap = stock_pixmap_widget(summaryview->hbox, STOCK_PIXMAP_QUICKSEARCH);
7596         gtk_container_remove (GTK_CONTAINER(summaryview->toggle_search), 
7597                               summaryview->quick_search_pixmap);
7598         gtk_container_add(GTK_CONTAINER(summaryview->toggle_search), pixmap);
7599         gtk_widget_show(pixmap);
7600         summaryview->quick_search_pixmap = pixmap;
7601
7602 #ifdef GENERIC_UMPC
7603         pixmap = stock_pixmap_widget(summaryview->hbox, STOCK_PIXMAP_SELECTION);
7604         gtk_container_remove (GTK_CONTAINER(summaryview->multiple_sel_togbtn), 
7605                               summaryview->multiple_sel_image);
7606         gtk_container_add(GTK_CONTAINER(summaryview->multiple_sel_togbtn), pixmap);
7607         gtk_widget_show(pixmap);
7608         summaryview->multiple_sel_togbtn = pixmap;
7609 #endif
7610
7611         folderview_unselect(summaryview->folderview);
7612         folderview_select(summaryview->folderview, summaryview->folder_item);
7613         summary_set_column_titles(summaryview);
7614 }
7615
7616 void summary_reflect_prefs_custom_colors(SummaryView *summaryview)
7617 {
7618         GtkMenuShell *menu;
7619         GList *cur;
7620
7621         /* re-create colorlabel submenu */
7622         menu = GTK_MENU_SHELL(summaryview->colorlabel_menu);
7623         g_return_if_fail(menu != NULL);
7624
7625         /* clear items. get item pointers. */
7626         for (cur = menu->children; cur != NULL && cur->data != NULL; cur = cur->next) {
7627                 gtk_menu_item_set_submenu(GTK_MENU_ITEM(cur->data), NULL);
7628         }
7629         summary_colorlabel_menu_create(summaryview, TRUE);
7630 }
7631
7632 /*
7633  * Harvest addresses for selected messages in summary view.
7634  */
7635 void summary_harvest_address(SummaryView *summaryview)
7636 {
7637         GtkCMCTree *ctree = GTK_CMCTREE( summaryview->ctree );
7638         GList *cur;
7639         GList *msgList;
7640         MsgInfo *msginfo;
7641
7642         msgList = NULL;
7643         for( cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next ) {
7644                 msginfo = gtk_cmctree_node_get_row_data( ctree, GTK_CMCTREE_NODE(cur->data) );
7645                 if (!msginfo)
7646                         continue;
7647                 msgList = g_list_append( msgList, GUINT_TO_POINTER( msginfo->msgnum ) );
7648         }
7649         addressbook_harvest( summaryview->folder_item, TRUE, msgList );
7650         g_list_free( msgList );
7651 }
7652
7653 #ifndef G_OS_WIN32
7654 static regex_t *summary_compile_simplify_regexp(gchar *simplify_subject_regexp)
7655 {
7656         int err;
7657         gchar buf[BUFFSIZE];
7658         regex_t *preg = NULL;
7659         
7660         preg = g_new0(regex_t, 1);
7661
7662         err = string_match_precompile(simplify_subject_regexp, 
7663                                       preg, REG_EXTENDED);
7664         if (err) {
7665                 regerror(err, preg, buf, BUFFSIZE);
7666                 alertpanel_error(_("Regular expression (regexp) error:\n%s"), buf);
7667                 g_free(preg);
7668                 preg = NULL;
7669         }
7670         
7671         return preg;
7672 }
7673 #endif
7674 void summary_set_prefs_from_folderitem(SummaryView *summaryview, FolderItem *item)
7675 {
7676         FolderSortKey sort_key;
7677         FolderSortType sort_type;
7678         g_return_if_fail(summaryview != NULL);
7679         g_return_if_fail(item != NULL);
7680
7681         /* Subject simplification */
7682 #ifndef G_OS_WIN32
7683         if(summaryview->simplify_subject_preg) {
7684                 regfree(summaryview->simplify_subject_preg);
7685                 g_free(summaryview->simplify_subject_preg);
7686                 summaryview->simplify_subject_preg = NULL;
7687         }
7688         if(item->prefs && item->prefs->simplify_subject_regexp && 
7689            item->prefs->simplify_subject_regexp[0] && item->prefs->enable_simplify_subject)
7690                 summaryview->simplify_subject_preg = summary_compile_simplify_regexp(item->prefs->simplify_subject_regexp);
7691 #endif
7692         /* Sorting */
7693         sort_key = item->sort_key;
7694         sort_type = item->sort_type;
7695
7696         folder_get_sort_type(item->folder, &sort_key, &sort_type);
7697
7698         summaryview->sort_key = sort_key;
7699         summaryview->sort_type = sort_type;
7700
7701         /* Threading */
7702         summaryview->threaded = item->threaded;
7703         summaryview->thread_collapsed = item->thread_collapsed;
7704
7705         /* Scoring */
7706 }
7707
7708 void summary_save_prefs_to_folderitem(SummaryView *summaryview, FolderItem *item)
7709 {
7710         /* Sorting */
7711         item->sort_key = summaryview->sort_key;
7712         item->sort_type = summaryview->sort_type;
7713
7714         /* Threading */
7715         item->threaded = summaryview->threaded;
7716         item->thread_collapsed = summaryview->thread_collapsed;
7717 }
7718
7719 static gboolean summary_update_msg(gpointer source, gpointer data) 
7720 {
7721         MsgInfoUpdate *msginfo_update = (MsgInfoUpdate *) source;
7722         SummaryView *summaryview = (SummaryView *)data;
7723         GtkCMCTreeNode *node;
7724
7725         g_return_val_if_fail(msginfo_update != NULL, TRUE);
7726         g_return_val_if_fail(summaryview != NULL, FALSE);
7727
7728         if (msginfo_update->msginfo->folder != summaryview->folder_item)
7729                 return FALSE;
7730
7731         if (msginfo_update->flags & MSGINFO_UPDATE_FLAGS) {
7732                 node = gtk_cmctree_find_by_row_data(
7733                                 GTK_CMCTREE(summaryview->ctree), NULL, 
7734                                 msginfo_update->msginfo);
7735
7736                 if (node) 
7737                         summary_set_row_marks(summaryview, node);
7738         }
7739
7740         return FALSE;
7741 }
7742
7743 void summary_update_unread(SummaryView *summaryview, FolderItem *removed_item)
7744 {
7745         guint new, unread, unreadmarked, marked, total;
7746         guint replied, forwarded, locked, ignored, watched;
7747         static gboolean tips_initialized = FALSE;
7748 #if !(GTK_CHECK_VERSION(2,12,0))
7749         GtkTooltips *tips = summaryview->tooltips;
7750 #endif
7751
7752         if (prefs_common.layout_mode != SMALL_LAYOUT) {
7753                 if (tips_initialized) {
7754                         summary_set_folder_pixmap(summaryview, STOCK_PIXMAP_DIR_OPEN);
7755                         CLAWS_SET_TIP(summaryview->folder_pixmap_eventbox,
7756                              NULL);
7757                         tips_initialized = FALSE;
7758                 } 
7759                 return;
7760         }
7761         folder_count_total_msgs(&new, &unread, &unreadmarked, &marked, &total,
7762                                 &replied, &forwarded, &locked, &ignored,
7763                                 &watched);
7764         if (removed_item) {
7765                 total -= removed_item->total_msgs;
7766                 new -= removed_item->new_msgs;
7767                 unread -= removed_item->unread_msgs;
7768         }
7769         
7770         if (new > 0 || unread > 0) {
7771                 tips_initialized = TRUE;
7772                 summary_set_folder_pixmap(summaryview, STOCK_PIXMAP_DIR_OPEN_HRM);
7773                 CLAWS_SET_TIP(summaryview->folder_pixmap_eventbox,
7774                              _("Go back to the folder list (You have unread messages)"));
7775         } else {
7776                 tips_initialized = TRUE;
7777                 summary_set_folder_pixmap(summaryview, STOCK_PIXMAP_DIR_OPEN);
7778                 CLAWS_SET_TIP(summaryview->folder_pixmap_eventbox,
7779                              _("Go back to the folder list"));
7780         }
7781 }
7782
7783 static gboolean summary_update_folder_item_hook(gpointer source, gpointer data)
7784 {
7785         FolderItemUpdateData *hookdata = (FolderItemUpdateData *)source;
7786         SummaryView *summaryview = (SummaryView *)data;
7787
7788         g_return_val_if_fail(hookdata != NULL, FALSE);
7789         g_return_val_if_fail(hookdata->item != NULL, FALSE);
7790         g_return_val_if_fail(summaryview != NULL, FALSE);
7791
7792         if (hookdata->update_flags & F_ITEM_UPDATE_NAME) {
7793                 gchar *name = folder_item_get_name(hookdata->item);
7794                 gtk_label_set_text(GTK_LABEL(summaryview->statlabel_folder), name);
7795                 g_free(name);
7796         }
7797         summary_update_unread(summaryview, NULL);
7798
7799         return FALSE;
7800 }
7801
7802 static gboolean summary_update_folder_hook(gpointer source, gpointer data)
7803 {
7804         FolderUpdateData *hookdata;
7805         SummaryView *summaryview = (SummaryView *)data;
7806         hookdata = source;
7807         if (hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM)
7808                 summary_update_unread(summaryview, hookdata->item);
7809         else
7810                 summary_update_unread(summaryview, NULL);
7811
7812         return FALSE;
7813 }
7814
7815 /*!
7816  *\brief        change summaryview to display your answer(s) to a message
7817  *
7818  *\param        summaryview The SummaryView ;)
7819  *\param        msginfo The message for which answers are searched
7820  *
7821  */
7822 static void summary_find_answers (SummaryView *summaryview, MsgInfo *msg)
7823 {
7824         FolderItem *sent_folder = NULL;
7825         PrefsAccount *account = NULL;
7826         GtkCMCTreeNode *node = NULL;
7827         char *buf = NULL;
7828         if (msg == NULL || msg->msgid == NULL)
7829                 return;
7830         
7831         account = account_get_reply_account(msg, prefs_common.reply_account_autosel);
7832         if (account == NULL) 
7833                 return;
7834         sent_folder = account_get_special_folder
7835                                 (account, F_OUTBOX);
7836         
7837         buf = g_strdup_printf("inreplyto matchcase \"%s\"", msg->msgid);
7838
7839         if (sent_folder != summaryview->folder_item) {
7840                 folderview_select(summaryview->mainwin->folderview, sent_folder);
7841         }
7842         
7843         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(summaryview->toggle_search), TRUE);
7844
7845         quicksearch_set(summaryview->quicksearch, QUICK_SEARCH_EXTENDED, buf);
7846         g_free(buf);
7847
7848         node = gtk_cmctree_node_nth(GTK_CMCTREE(summaryview->ctree), 0);
7849         if (node)
7850                 summary_select_node(summaryview, node, TRUE, TRUE);
7851 }
7852
7853 gint summaryview_export_mbox_list(SummaryView *summaryview)
7854 /* return values: -2 skipped, -1 error, 0 OK */
7855 {
7856         GSList *list = summary_get_selected_msg_list(summaryview);
7857         gchar *mbox = filesel_select_file_save(_("Export to mbox file"), NULL);
7858         gint ret;
7859         
7860         if (mbox == NULL)
7861                 return -2;
7862         if (list == NULL)
7863                 return -1;
7864                 
7865         ret = export_list_to_mbox(list, mbox);
7866         
7867         g_slist_free(list);
7868         g_free(mbox);
7869         
7870         return ret;
7871 }
7872
7873 void summaryview_lock(SummaryView *summaryview, FolderItem *item)
7874 {
7875         if (!summaryview || !summaryview->folder_item || !item) {
7876                 return;
7877         }
7878
7879         if (summaryview->folder_item->folder == item->folder) {
7880                 gtk_widget_set_sensitive(summaryview->ctree, FALSE);
7881         }
7882 }
7883 void summaryview_unlock(SummaryView *summaryview, FolderItem *item)
7884 {
7885         gtk_widget_set_sensitive(summaryview->ctree, TRUE);
7886 }