2006-02-24 [colin] 2.0.0cvs82
authorColin Leroy <colin@colino.net>
Fri, 24 Feb 2006 17:48:13 +0000 (17:48 +0000)
committerColin Leroy <colin@colino.net>
Fri, 24 Feb 2006 17:48:13 +0000 (17:48 +0000)
* src/Makefile.am
* src/mainwindow.c
* src/mainwindow.h
* src/procmsg.c
* src/procmsg.h
* src/stock_pixmap.c
* src/stock_pixmap.h
* src/summaryview.c
* src/summaryview.h
* src/gtk/icon_legend.c
* src/pixmaps/spam.xpm ** NEW FILE **
* src/plugins/spamassassin/spamassassin.c
* src/plugins/spamassassin/spamassassin_gtk.c
Add spam learning interface (Mark/Mark as
(spam|ham) menus)

16 files changed:
ChangeLog
PATCHSETS
configure.ac
src/Makefile.am
src/gtk/icon_legend.c
src/mainwindow.c
src/mainwindow.h
src/pixmaps/spam.xpm [new file with mode: 0644]
src/plugins/spamassassin/spamassassin.c
src/plugins/spamassassin/spamassassin_gtk.c
src/procmsg.c
src/procmsg.h
src/stock_pixmap.c
src/stock_pixmap.h
src/summaryview.c
src/summaryview.h

index cd071da..33ebae9 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,21 @@
+2006-02-24 [colin]     2.0.0cvs82
+
+       * src/Makefile.am
+       * src/mainwindow.c
+       * src/mainwindow.h
+       * src/procmsg.c
+       * src/procmsg.h
+       * src/stock_pixmap.c
+       * src/stock_pixmap.h
+       * src/summaryview.c
+       * src/summaryview.h
+       * src/gtk/icon_legend.c
+       * src/pixmaps/spam.xpm  ** NEW FILE **
+       * src/plugins/spamassassin/spamassassin.c
+       * src/plugins/spamassassin/spamassassin_gtk.c
+               Add spam learning interface (Mark/Mark as 
+               (spam|ham) menus)
+
 2006-02-24 [wwp]       2.0.0cvs81
 
        * src/compose.c
index b8fad1a..b95e02a 100644 (file)
--- a/PATCHSETS
+++ b/PATCHSETS
 ( cvs diff -u -r 1.101.2.20 -r 1.101.2.21 src/news.c;  cvs diff -u -r 1.21.2.3 -r 1.21.2.4 src/news.h;  cvs diff -u -r 1.2.2.15 -r 1.2.2.16 src/news_gtk.c;  ) > 2.0.0cvs79.patchset
 ( cvs diff -u -r 1.274.2.98 -r 1.274.2.99 src/mainwindow.c;  ) > 2.0.0cvs80.patchset
 ( cvs diff -u -r 1.382.2.246 -r 1.382.2.247 src/compose.c;  cvs diff -u -r 1.50.2.21 -r 1.50.2.22 src/compose.h;  cvs diff -u -r 1.60.2.13 -r 1.60.2.14 src/filtering.c;  ) > 2.0.0cvs81.patchset
+( cvs diff -u -r 1.155.2.35 -r 1.155.2.36 src/Makefile.am;  cvs diff -u -r 1.274.2.99 -r 1.274.2.100 src/mainwindow.c;  cvs diff -u -r 1.39.2.12 -r 1.39.2.13 src/mainwindow.h;  cvs diff -u -r 1.150.2.51 -r 1.150.2.52 src/procmsg.c;  cvs diff -u -r 1.60.2.23 -r 1.60.2.24 src/procmsg.h;  cvs diff -u -r 1.25.2.20 -r 1.25.2.21 src/stock_pixmap.c;  cvs diff -u -r 1.18.2.14 -r 1.18.2.15 src/stock_pixmap.h;  cvs diff -u -r 1.395.2.173 -r 1.395.2.174 src/summaryview.c;  cvs diff -u -r 1.68.2.15 -r 1.68.2.16 src/summaryview.h;  cvs diff -u -r 1.1.2.6 -r 1.1.2.7 src/gtk/icon_legend.c;  diff -u /dev/null src/pixmaps/spam.xpm;  cvs diff -u -r 1.18.2.17 -r 1.18.2.18 src/plugins/spamassassin/spamassassin.c;  cvs diff -u -r 1.23.2.18 -r 1.23.2.19 src/plugins/spamassassin/spamassassin_gtk.c;  ) > 2.0.0cvs82.patchset
index 2507137..fa6a821 100644 (file)
@@ -11,7 +11,7 @@ MINOR_VERSION=0
 MICRO_VERSION=0
 INTERFACE_AGE=0
 BINARY_AGE=0
-EXTRA_VERSION=81
+EXTRA_VERSION=82
 EXTRA_RELEASE=
 EXTRA_GTK2_VERSION=
 
index 8a867b5..345d65d 100644 (file)
@@ -371,6 +371,7 @@ EXTRA_DIST = \
        pixmaps/quicksearch.xpm \
        pixmaps/replied.xpm \
        pixmaps/search.xpm \
+       pixmaps/spam.xpm \
        pixmaps/sylpheed_icon.xpm \
        pixmaps/sylpheed_logo.xpm \
        pixmaps/sylpheed.xpm \
index 10dc0db..1eaf8a1 100644 (file)
@@ -38,7 +38,7 @@
 #include "stock_pixmap.h"
 #include "prefs_gtk.h"
 
-#define ICONS 15
+#define ICONS 16
 
 StockPixmap legend_icons[ICONS] = {
        STOCK_PIXMAP_NEW,
@@ -53,6 +53,7 @@ StockPixmap legend_icons[ICONS] = {
        STOCK_PIXMAP_MARK,
        STOCK_PIXMAP_LOCKED,
        STOCK_PIXMAP_IGNORETHREAD,
+       STOCK_PIXMAP_SPAM,
        STOCK_PIXMAP_DIR_OPEN, 
        STOCK_PIXMAP_DIR_OPEN_HRM,
        STOCK_PIXMAP_DIR_OPEN_MARK,
@@ -71,6 +72,7 @@ static gchar *legend_icon_desc[] = {
        N_("Marked message"),
        N_("Locked message"),
        N_("Message is in an ignored thread"),
+       N_("Message is spam"),
        N_("Folder (normal, opened)"),
        N_("Folder with read messages hidden"),
        N_("Folder contains marked emails"),
index becb632..cc6ab9e 100644 (file)
@@ -253,6 +253,9 @@ static void mark_as_read_cb         (MainWindow     *mainwin,
 static void mark_all_read_cb           (MainWindow     *mainwin,
                                         guint           action,
                                         GtkWidget      *widget);
+static void mark_as_spam_cb            (MainWindow     *mainwin, 
+                                        guint           action,
+                                        GtkWidget      *widget);
 
 static void reedit_cb                  (MainWindow     *mainwin,
                                         guint           action,
@@ -721,9 +724,11 @@ static GtkItemFactoryEntry mainwin_entries[] =
        {N_("/_Message/_Mark/_Unmark"),         "U", unmark_cb, 0, NULL},
        {N_("/_Message/_Mark/---"),             NULL, NULL, 0, "<Separator>"},
        {N_("/_Message/_Mark/Mark as unr_ead"), "<shift>exclam", mark_as_unread_cb, 0, NULL},
-       {N_("/_Message/_Mark/Mark as rea_d"),
-                                               NULL, mark_as_read_cb, 0, NULL},
+       {N_("/_Message/_Mark/Mark as rea_d"),   NULL, mark_as_read_cb, 0, NULL},
        {N_("/_Message/_Mark/Mark all _read"),  NULL, mark_all_read_cb, 0, NULL},
+       {N_("/_Message/_Mark/---"),             NULL, NULL, 0, "<Separator>"},
+       {N_("/_Message/_Mark/Mark as _spam"),   NULL, mark_as_spam_cb, 1, NULL},
+       {N_("/_Message/_Mark/Mark as _ham"),    NULL, mark_as_spam_cb, 0, NULL},
        {N_("/_Message/---"),                   NULL, NULL, 0, "<Separator>"},
        {N_("/_Message/Re-_edit"),              NULL, reedit_cb, 0, NULL},
 
@@ -1825,6 +1830,10 @@ SensitiveCond main_window_get_current_state(MainWindow *mainwin)
                        break;
                }
        }
+       
+       if (procmsg_spam_can_learn()) {
+               state |= M_CAN_LEARN_SPAM;
+       }
 
        if (inc_is_active())
                state |= M_INC_ACTIVE;
@@ -1898,6 +1907,8 @@ void main_window_set_menu_sensitive(MainWindow *mainwin)
                {"/Message/Delete..."             , M_TARGET_EXIST|M_ALLOW_DELETE},
                {"/Message/Cancel a news message" , M_TARGET_EXIST|M_ALLOW_DELETE|M_NEWS},
                {"/Message/Mark"                  , M_TARGET_EXIST},
+               {"/Message/Mark/Mark as spam"     , M_TARGET_EXIST|M_CAN_LEARN_SPAM},
+               {"/Message/Mark/Mark as ham"      , M_TARGET_EXIST|M_CAN_LEARN_SPAM},
                {"/Message/Re-edit"               , M_HAVE_ACCOUNT|M_ALLOW_REEDIT},
 
                {"/Tools/Add sender to address book"   , M_SINGLE_TARGET_EXIST},
@@ -2830,6 +2841,12 @@ static void mark_all_read_cb(MainWindow *mainwin, guint action,
        summary_mark_all_read(mainwin->summaryview);
 }
 
+static void mark_as_spam_cb(MainWindow *mainwin, guint action,
+                            GtkWidget *widget)
+{
+       summary_mark_as_spam(mainwin->summaryview, action, NULL);
+}
+
 static void reedit_cb(MainWindow *mainwin, guint action, GtkWidget *widget)
 {
        summary_reedit(mainwin->summaryview);
index d392a47..82e7384 100644 (file)
@@ -48,7 +48,8 @@ typedef enum
        M_HAVE_NEWS_ACCOUNT   = 1 << 12,
        M_HIDE_READ_MSG       = 1 << 13,
        M_DELAY_EXEC          = 1 << 14,
-       M_NOT_NEWS            = 1 << 15
+       M_NOT_NEWS            = 1 << 15,
+       M_CAN_LEARN_SPAM      = 1 << 16
 } SensitiveCond;
 
 typedef enum
diff --git a/src/pixmaps/spam.xpm b/src/pixmaps/spam.xpm
new file mode 100644 (file)
index 0000000..ba147e6
--- /dev/null
@@ -0,0 +1,17 @@
+/* XPM */
+static char * spam_xpm[] = {
+"10 10 4 1",
+"      c None",
+".     c #DF0B18",
+"+     c #CC0000",
+"@     c #D5050C",
+"  .++++.  ",
+" ++....+@ ",
+".+.   .++.",
+"+.   .+..+",
+"+.  .+. .+",
+"+. .+.  .+",
+"+..+.   .+",
+".++.   .+.",
+" ++....++ ",
+"  .++++.  "};
index 735946c..10ab2ea 100644 (file)
@@ -138,6 +138,7 @@ static gboolean msg_is_spam(FILE *fp)
        }
 
        if (transport_setup(&trans, flags) != EX_OK) {
+               log_error("Spamassassin plugin couldn't connect to spamd.\n");
                debug_print("failed to setup transport\n");
                return FALSE;
        }
@@ -175,9 +176,10 @@ static gboolean mail_filtering_hook(gpointer source, gpointer data)
        int pid = 0;
        int status;
 
-       if (config.transport == SPAMASSASSIN_DISABLED)
+       if (config.transport == SPAMASSASSIN_DISABLED) {
+               log_error("Spamassassin plugin is disabled by its preferences.\n");
                return FALSE;
-
+       }
        debug_print("Filtering message %d\n", msginfo->msgnum);
        if (message_callback != NULL)
                message_callback(_("SpamAssassin: filtering message..."));
@@ -222,7 +224,7 @@ static gboolean mail_filtering_hook(gpointer source, gpointer data)
 
        if (is_spam) {
                debug_print("message is spam\n");
-                           
+               procmsg_msginfo_set_flags(msginfo, MSG_SPAM, 0);
                if (config.receive_spam) {
                        FolderItem *save_folder;
 
@@ -232,14 +234,17 @@ static gboolean mail_filtering_hook(gpointer source, gpointer data)
                                save_folder = folder_get_default_trash();
 
                        procmsg_msginfo_unset_flags(msginfo, ~0, 0);
+                       procmsg_msginfo_set_flags(msginfo, MSG_SPAM, 0);
                        folder_item_move_msg(save_folder, msginfo);
                } else {
                        folder_item_remove_msg(msginfo->folder, msginfo->msgnum);
                }
 
                return TRUE;
+       } else {
+               debug_print("message is ham\n");
+               procmsg_msginfo_unset_flags(msginfo, MSG_SPAM, 0);
        }
-       
        return FALSE;
 }
 
@@ -248,6 +253,53 @@ SpamAssassinConfig *spamassassin_get_config(void)
        return &config;
 }
 
+void spamassassin_learn(MsgInfo *msginfo, GSList *msglist, gboolean spam)
+{
+       gchar *cmd = NULL;
+       gchar *file = NULL;
+       gboolean async = FALSE;
+
+       if (msginfo == NULL && msglist == NULL)
+               return;
+
+       if (msginfo) {
+               file = procmsg_get_message_file(msginfo);
+               if (file == NULL)
+                       return;
+               cmd = g_strdup_printf("sa-learn %s %s", 
+                       spam?"--spam":"--ham", file);
+       }
+       if (msglist) {
+               GSList *cur;
+               MsgInfo *info;
+               cmd = g_strdup_printf("sa-learn %s", spam?"--spam":"--ham");
+               for (cur = msglist; cur; cur = cur->next) {
+                       info = (MsgInfo *)cur->data;
+                       gchar *tmpcmd = NULL;
+                       gchar *tmpfile = get_tmp_file();
+                       
+                       if (tmpfile &&
+                           copy_file(procmsg_get_message_file(info), tmpfile, TRUE) == 0) {                    
+                               tmpcmd = g_strconcat
+                                       (cmd, " ", tmpfile, NULL);
+                               g_free(cmd);
+                               cmd = tmpcmd;
+                       }
+                       if (tmpfile)
+                               g_free(tmpfile);
+               }
+               async = TRUE;
+       }
+       if (cmd == NULL)
+               return;
+       debug_print("%s\n",cmd);
+       /* only run async if we have a list, or we could end up
+        * forking lots of perl processes and bury the machine */
+       execute_command_line(cmd, async);
+       g_free(cmd);
+       
+}
+
 void spamassassin_save_config(void)
 {
        PrefFile *pfile;
@@ -308,9 +360,16 @@ gint plugin_init(gchar **error)
        prefs_read_config(param, "SpamAssassin", rcpath, NULL);
        g_free(rcpath);
        spamassassin_gtk_init();
-
+       
+       procmsg_register_spam_learner(spamassassin_learn);
+       procmsg_spam_set_folder(config.save_folder);
+       
        debug_print("Spamassassin plugin loaded\n");
 
+       if (config.transport == SPAMASSASSIN_DISABLED) {
+               log_error("Spamassassin plugin is loaded but disabled by its preferences.\n");
+       }
+
        return 0;
        
 }
@@ -321,7 +380,8 @@ void plugin_done(void)
        g_free(config.hostname);
        g_free(config.save_folder);
        spamassassin_gtk_done();
-
+       procmsg_unregister_spam_learner(spamassassin_learn);
+       procmsg_spam_set_folder(NULL);
        debug_print("Spamassassin plugin unloaded\n");
 }
 
index f94a4f0..eefbb2c 100644 (file)
@@ -439,6 +439,7 @@ static void spamassassin_save_func(PrefsPage *_page)
        /* timeout */
        config->timeout = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(page->timeout));
 
+       procmsg_spam_set_folder(config->save_folder);
        spamassassin_save_config();
 }
 
index 0ee9100..56a23b5 100644 (file)
@@ -2066,3 +2066,47 @@ MsgInfo *procmsg_msginfo_new_from_mimeinfo(MsgInfo *src_msginfo, MimeInfo *mimei
 
        return tmp_msginfo;
 }
+
+static GSList *spam_learners = NULL;
+
+void procmsg_register_spam_learner (void (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
+{
+       if (!g_slist_find(spam_learners, learn_func))
+               spam_learners = g_slist_append(spam_learners, learn_func);
+}
+
+void procmsg_unregister_spam_learner (void (*learn_func)(MsgInfo *info, GSList *list, gboolean spam))
+{
+       spam_learners = g_slist_remove(spam_learners, learn_func);
+}
+
+gboolean procmsg_spam_can_learn(void)
+{
+       return g_slist_length(spam_learners) > 0;
+}
+
+void procmsg_spam_learner_learn (MsgInfo *info, GSList *list, gboolean spam)
+{
+       GSList *cur = spam_learners;
+       for (; cur; cur = cur->next) {
+               void ((*func)(MsgInfo *info, GSList *list, gboolean spam)) = cur->data;
+               func(info, list, spam);
+       }
+}
+
+static gchar *spam_folder_item = NULL;
+void procmsg_spam_set_folder (const char *item_identifier)
+{
+       if (spam_folder_item)
+               g_free(spam_folder_item);
+       if (item_identifier)
+               spam_folder_item = g_strdup(item_identifier);
+       else
+               spam_folder_item = NULL;
+}
+
+FolderItem *procmsg_spam_get_folder (void)
+{
+       FolderItem *item = spam_folder_item ? folder_find_item_from_identifier(spam_folder_item) : NULL;
+       return item ? item : folder_get_default_trash();
+}
index 33b1cf0..6c7c710 100644 (file)
@@ -74,6 +74,7 @@ typedef GSList MsgNumberList;
 #define MSG_IGNORE_THREAD      (1U << 10)   /* ignore threads */
 #define MSG_LOCKED             (1U << 11)   /* msg is locked  */
 #define MSG_RETRCPT_SENT       (1U << 12)   /* new one */ 
+#define MSG_SPAM               (1U << 13)   /* new one */ 
                                                
 /* RESERVED */
 #define        MSG_RESERVED_CLAWS      (1U << 30)   /* for sylpheed-claws */
@@ -144,6 +145,7 @@ typedef guint32 MsgTmpFlags;
 #define MSG_IS_IGNORE_THREAD(msg)      (((msg).perm_flags & MSG_IGNORE_THREAD) != 0)
 #define MSG_IS_RETRCPT_PENDING(msg)    (((msg).perm_flags & MSG_RETRCPT_PENDING) != 0)
 #define MSG_IS_RETRCPT_SENT(msg)       (((msg).perm_flags & MSG_RETRCPT_SENT) != 0)
+#define MSG_IS_SPAM(msg)               (((msg).perm_flags & MSG_SPAM) != 0)
 
 #define MSGINFO_UPDATE_HOOKLIST "msginfo_update"
 #define MAIL_FILTERING_HOOKLIST "mail_filtering_hooklist"
@@ -324,4 +326,11 @@ MsgInfo *procmsg_msginfo_new_from_mimeinfo
                                        (MsgInfo        *src_msginfo, 
                                         MimeInfo       *mimeinfo);
 
+void procmsg_register_spam_learner (void (*learn_func)(MsgInfo *info, GSList *list, gboolean spam));
+void procmsg_unregister_spam_learner (void (*learn_func)(MsgInfo *info, GSList *list, gboolean spam));
+gboolean procmsg_spam_can_learn                (void);
+void procmsg_spam_set_folder           (const char *item_identifier);
+FolderItem *procmsg_spam_get_folder    (void);
+void procmsg_spam_learner_learn        (MsgInfo *msginfo, GSList *msglist, gboolean spam);
+
 #endif /* __PROCMSG_H__ */
index 5e6a440..90b3400 100644 (file)
 #include "pixmaps/drafts_close_mark.xpm"
 #include "pixmaps/drafts_open_mark.xpm"
 #include "pixmaps/dir_noselect.xpm"
+#include "pixmaps/spam.xpm"
 #include "pixmaps/empty.xpm"
 
 typedef struct _StockPixmapData        StockPixmapData;
@@ -294,6 +295,7 @@ static StockPixmapData pixmaps[] =
        {trash_btn_xpm                          , NULL, NULL, "trash_btn", NULL},
        {sylpheed_logo_xpm                      , NULL, NULL, "sylpheed_logo", NULL},
         {dir_noselect_xpm                       , NULL, NULL, "dir_noselect" , NULL},
+        {spam_xpm                               , NULL, NULL, "spam" , NULL},
         {empty_xpm                              , NULL, NULL, "empty" , NULL}
 };
 
index a567f41..8da74f7 100644 (file)
@@ -152,6 +152,7 @@ typedef enum
        STOCK_PIXMAP_TRASH,
        STOCK_PIXMAP_SYLPHEED_LOGO,
        STOCK_PIXMAP_DIR_NOSELECT, 
+       STOCK_PIXMAP_SPAM,
        STOCK_PIXMAP_EMPTY,              /* last entry */
        N_STOCK_PIXMAPS
 } StockPixmap;
index 1cbec07..01b1cf2 100644 (file)
@@ -118,6 +118,8 @@ static GdkPixmap *ignorethreadxpm;
 static GdkBitmap *ignorethreadxpmmask;
 static GdkPixmap *lockedxpm;
 static GdkBitmap *lockedxpmmask;
+static GdkPixmap *spamxpm;
+static GdkBitmap *spamxpmmask;
 
 static GdkPixmap *clipxpm;
 static GdkBitmap *clipxpmmask;
@@ -437,6 +439,10 @@ static GtkItemFactoryEntry summary_popup_entries[] =
        {N_("/_Mark/Mark all read"),    NULL, summary_mark_all_read, 0, NULL},
        {N_("/_Mark/Ignore thread"),    NULL, summary_ignore_thread, 0, NULL},
        {N_("/_Mark/Unignore thread"),  NULL, summary_unignore_thread, 0, NULL},
+       {N_("/_Mark/---"),              NULL, NULL, 0, "<Separator>"},
+       {N_("/_Mark/Mark as _spam"),    NULL, summary_mark_as_spam, 1, NULL},
+       {N_("/_Mark/Mark as _ham"),     NULL, summary_mark_as_spam, 0, NULL},
+       {N_("/_Mark/---"),              NULL, NULL, 0, "<Separator>"},
        {N_("/_Mark/Lock"),             NULL, summary_msgs_lock, 0, NULL},
        {N_("/_Mark/Unlock"),           NULL, summary_msgs_unlock, 0, NULL},
        {N_("/Color la_bel"),           NULL, NULL,             0, NULL},
@@ -720,6 +726,8 @@ void summary_init(SummaryView *summaryview)
                         &gpgsignedxpm, &gpgsignedxpmmask);
        stock_pixmap_gdk(summaryview->ctree, STOCK_PIXMAP_CLIP_GPG_SIGNED,
                         &clipgpgsignedxpm, &clipgpgsignedxpmmask);
+       stock_pixmap_gdk(summaryview->ctree, STOCK_PIXMAP_SPAM,
+                        &spamxpm, &spamxpmmask);
 
        summary_set_fonts(summaryview);
 
@@ -1327,6 +1335,8 @@ static void summary_set_menu_sensitive(SummaryView *summaryview)
                {"/Mark/Ignore thread"          , M_TARGET_EXIST},
                {"/Mark/Lock"                   , M_TARGET_EXIST},
                {"/Mark/Unlock"                 , M_TARGET_EXIST},
+               {"/Mark/Mark as spam"           , M_TARGET_EXIST|M_CAN_LEARN_SPAM},
+               {"/Mark/Mark as ham"            , M_TARGET_EXIST|M_CAN_LEARN_SPAM},
                {"/Color label"                 , M_TARGET_EXIST},
 
                {"/Add sender to address book"  , M_SINGLE_TARGET_EXIST},
@@ -2738,7 +2748,7 @@ static void summary_display_msg_full(SummaryView *summaryview,
        GTK_EVENTS_FLUSH();
 
        msginfo = gtk_ctree_node_get_row_data(ctree, row);
-
+       
        if (new_window) {
                MessageView *msgview;
 
@@ -2978,6 +2988,9 @@ static void summary_set_row_marks(SummaryView *summaryview, GtkCTreeNode *row)
                }
                        gtk_ctree_node_set_foreground
                                (ctree, row, &summaryview->color_dim);
+       } else if (MSG_IS_SPAM(flags)) {
+               gtk_ctree_node_set_pixmap(ctree, row, col_pos[S_COL_MARK],
+                                         spamxpm, spamxpmmask);
        } else if (MSG_IS_MARKED(flags)) {
                gtk_ctree_node_set_pixmap(ctree, row, col_pos[S_COL_MARK],
                                          markxpm, markxpmmask);
@@ -3201,6 +3214,45 @@ void summary_mark_all_read(SummaryView *summaryview)
        summary_status_show(summaryview);
 }
 
+void summary_mark_as_spam(SummaryView *summaryview, guint action, GtkWidget *widget)
+{
+       GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
+       GList *cur;
+       gboolean is_spam = action;
+       GSList *msgs = NULL;
+       gboolean immediate_exec = prefs_common.immediate_exec;
+       
+       prefs_common.immediate_exec = FALSE;
+
+       START_LONG_OPERATION(summaryview);
+
+       for (cur = GTK_CLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next) {
+               GtkCTreeNode *row = GTK_CTREE_NODE(cur->data);
+               MsgInfo *msginfo = gtk_ctree_node_get_row_data(ctree, row);
+               if (is_spam) {
+                       summary_msginfo_change_flags(msginfo, MSG_SPAM, 0, MSG_NEW|MSG_UNREAD, 0);
+                       summary_move_row_to(summaryview, row, procmsg_spam_get_folder());
+               } else {
+                       summary_msginfo_unset_flags(msginfo, MSG_SPAM, 0);
+               }
+               msgs = g_slist_prepend(msgs, msginfo);
+       }
+       
+       procmsg_spam_learner_learn(NULL, msgs, is_spam);
+       g_slist_free(msgs);
+
+       prefs_common.immediate_exec = immediate_exec;
+
+       END_LONG_OPERATION(summaryview);
+
+       if (prefs_common.immediate_exec) {
+               summary_execute(summaryview);
+       }
+
+       summary_status_show(summaryview);       
+}
+
+
 static void summary_mark_row_as_unread(SummaryView *summaryview,
                                       GtkCTreeNode *row)
 {
@@ -5016,6 +5068,9 @@ static void summary_selected(GtkCTree *ctree, GtkCTreeNode *row,
                    !MSG_IS_COPY(msginfo->flags)) {
                        if (MSG_IS_MARKED(msginfo->flags)) {
                                summary_unmark_row(summaryview, row);
+                       } else if (MSG_IS_SPAM(msginfo->flags)) {
+                               summary_msginfo_unset_flags(msginfo, MSG_SPAM, 0);
+                               procmsg_spam_learner_learn(msginfo, NULL, FALSE);
                        } else {
                                summary_mark_row(summaryview, row);
                        }
@@ -5673,6 +5728,7 @@ void summary_reflect_prefs_pixmap_theme(SummaryView *summaryview)
        stock_pixmap_gdk(ctree, STOCK_PIXMAP_KEY, &keyxpm, &keyxpmmask);
        stock_pixmap_gdk(ctree, STOCK_PIXMAP_GPG_SIGNED, &gpgsignedxpm, &gpgsignedxpmmask);
        stock_pixmap_gdk(ctree, STOCK_PIXMAP_CLIP_GPG_SIGNED, &clipgpgsignedxpm, &clipgpgsignedxpmmask);
+       stock_pixmap_gdk(ctree, STOCK_PIXMAP_SPAM, &spamxpm, &spamxpmmask);
 
        pixmap = stock_pixmap_widget(summaryview->hbox, STOCK_PIXMAP_DIR_OPEN);
        gtk_box_pack_start(GTK_BOX(summaryview->hbox), pixmap, FALSE, FALSE, 4);
index 90507b5..39265d5 100644 (file)
@@ -251,6 +251,9 @@ void summary_mark_as_read     (SummaryView          *summaryview);
 void summary_msgs_lock           (SummaryView          *summaryview);
 void summary_msgs_unlock         (SummaryView          *summaryview);
 void summary_mark_all_read       (SummaryView          *summaryview);
+void summary_mark_as_spam        (SummaryView          *summaryview, 
+                                  guint                 action, 
+                                  GtkWidget            *widget);
 void summary_add_address         (SummaryView          *summaryview);
 void summary_select_all                  (SummaryView          *summaryview);
 void summary_unselect_all        (SummaryView          *summaryview);