[pan2: 132/268] fixed threading segfault



commit 11f3a9b5c15f51ca7255d892f721e3fd02f774ac
Author: Heinrich MÃller <sphemuel stud informatik uni-erlangen de>
Date:   Mon Jul 18 15:34:19 2011 +0200

    fixed threading segfault

 pan.cbp                        |    5 ++-
 pan/data-impl/Makefile.am      |    2 +
 pan/data-impl/article-filter.h |    1 +
 pan/data-impl/data-impl.h      |    5 ++
 pan/data-impl/my-tree.cc       |  105 ++++++++++++++++++++++++++++++++++++++--
 pan/data-impl/rules-filter.cc  |   77 +++++++++++++++++++++++++++++
 pan/data-impl/rules-filter.h   |   50 +++++++++++++++++++
 pan/data/data.h                |    3 +
 pan/gui/gui.cc                 |   69 ++++++++++++++++++++++++++
 pan/gui/gui.h                  |    4 +-
 pan/gui/header-pane.cc         |   52 ++++++++++++++++++--
 pan/gui/header-pane.h          |    4 ++
 pan/gui/prefs-ui.cc            |   71 +++++++++++++++++++++++++++
 pan/gui/prefs-ui.h             |    4 +-
 pan/gui/prefs.h                |    5 ++-
 pan/icons/icon_read_group.png  |  Bin 541 -> 1230 bytes
 pan/tasks/task-upload.cc       |   10 ++--
 pan/usenet-utils/Makefile.am   |    2 +
 pan/usenet-utils/rules-info.cc |   77 +++++++++++++++++++++++++++++
 pan/usenet-utils/rules-info.h  |   85 ++++++++++++++++++++++++++++++++
 20 files changed, 612 insertions(+), 19 deletions(-)
---
diff --git a/pan.cbp b/pan.cbp
index 4c546b9..5e43ecb 100644
--- a/pan.cbp
+++ b/pan.cbp
@@ -55,6 +55,8 @@
 		<Unit filename="pan/data-impl/my-tree.cc" />
 		<Unit filename="pan/data-impl/profiles.cc" />
 		<Unit filename="pan/data-impl/profiles.h" />
+		<Unit filename="pan/data-impl/rules-filter.cc" />
+		<Unit filename="pan/data-impl/rules-filter.h" />
 		<Unit filename="pan/data-impl/server.cc" />
 		<Unit filename="pan/data-impl/speed-test-load-group.cc" />
 		<Unit filename="pan/data-impl/task-archive.cc" />
@@ -255,8 +257,9 @@
 		<Unit filename="pan/usenet-utils/numbers-test.cc" />
 		<Unit filename="pan/usenet-utils/numbers.cc" />
 		<Unit filename="pan/usenet-utils/numbers.h" />
-		<Unit filename="pan/usenet-utils/rng.cc" />
 		<Unit filename="pan/usenet-utils/rng.h" />
+		<Unit filename="pan/usenet-utils/rules-info.cc" />
+		<Unit filename="pan/usenet-utils/rules-info.h" />
 		<Unit filename="pan/usenet-utils/scorefile-test.cc" />
 		<Unit filename="pan/usenet-utils/scorefile.cc" />
 		<Unit filename="pan/usenet-utils/scorefile.h" />
diff --git a/pan/data-impl/Makefile.am b/pan/data-impl/Makefile.am
index ed09bd2..fbd513b 100644
--- a/pan/data-impl/Makefile.am
+++ b/pan/data-impl/Makefile.am
@@ -4,6 +4,7 @@ noinst_LIBRARIES = libpandata.a
 
 libpandata_a_SOURCES = \
  article-filter.cc \
+ rules-filter.cc \
  data-io.cc \
  data-impl.cc \
  groups.cc \
@@ -16,6 +17,7 @@ libpandata_a_SOURCES = \
 
 noinst_HEADERS = \
  article-filter.h \
+ rules-filter.h \
  data-impl.h \
  data-io.h \
  defgroup.h \
diff --git a/pan/data-impl/article-filter.h b/pan/data-impl/article-filter.h
index 9b6eec2..3a740ee 100644
--- a/pan/data-impl/article-filter.h
+++ b/pan/data-impl/article-filter.h
@@ -22,6 +22,7 @@
 
 #include <pan/general/quark.h>
 #include <pan/usenet-utils/filter-info.h>
+#include <pan/usenet-utils/rules-info.h>
 #include <pan/usenet-utils/scorefile.h>
 #include <pan/data/article.h>
 #include <pan/data/data.h>
diff --git a/pan/data-impl/data-impl.h b/pan/data-impl/data-impl.h
index b5b1ad6..ab00f7d 100644
--- a/pan/data-impl/data-impl.h
+++ b/pan/data-impl/data-impl.h
@@ -40,6 +40,7 @@
 #include <pan/tasks/queue.h>
 #include <pan/data-impl/data-io.h>
 #include <pan/data-impl/article-filter.h>
+#include <pan/data-impl/rules-filter.h>
 #include <pan/data-impl/profiles.h>
 #include <pan/data-impl/memchunk.h>
 
@@ -440,6 +441,7 @@ namespace pan
           virtual size_t size () const;
           virtual void set_filter (const ShowType      show_type = SHOW_ARTICLES,
                                    const FilterInfo  * criteria  = 0);
+          virtual void set_rules (const RulesInfo * criteria = 0);
 
         public:
           void articles_changed (const quarks_t& mids, bool do_refilter);
@@ -452,6 +454,7 @@ namespace pan
           nodes_t _nodes;
           MemChunk<ArticleNode> _node_chunk;
           FilterInfo _filter;
+          RulesInfo _rules;
           Data::ShowType _show_type;
           struct NodeMidCompare;
           struct TwoNodes;
@@ -461,6 +464,7 @@ namespace pan
           void accumulate_descendants (unique_nodes_t&, const ArticleNode*) const;
           void add_articles (const const_nodes_v&);
           void apply_filter (const const_nodes_v&);
+          void apply_rules (const const_nodes_v& candidates);
       };
 
       std::set<MyTree*> _trees;
@@ -621,6 +625,7 @@ namespace pan
     public:
 
       const ArticleFilter _article_filter;
+      const RulesFilter   _rules_filter;
 
     private:
       guint newsrc_autosave_id;
diff --git a/pan/data-impl/my-tree.cc b/pan/data-impl/my-tree.cc
index fa0e3cc..ebe1caf 100644
--- a/pan/data-impl/my-tree.cc
+++ b/pan/data-impl/my-tree.cc
@@ -78,19 +78,28 @@ DataImpl :: MyTree :: get_article (const Quark& mid) const
 }
 
 size_t
-DataImpl :: MyTree :: size () const 
+DataImpl :: MyTree :: size () const
 {
   return _nodes.size();
 }
 
 void
+DataImpl :: MyTree :: set_rules (const RulesInfo * criteria )
+{
+  if (criteria)
+    _rules = *criteria;
+  else
+    _rules.clear ();
+}
+
+void
 DataImpl :: MyTree :: set_filter (const Data::ShowType    show_type,
                                   const FilterInfo      * criteria)
 {
   // set the filter...
   if (criteria)
     _filter = *criteria;
-  else 
+  else
     _filter.clear ();
   _show_type = show_type;
 
@@ -122,6 +131,7 @@ DataImpl :: MyTree :: MyTree (DataImpl              & data_impl,
   _data.ref_group (_group);
   _data._trees.insert (this);
   set_filter (show_type, filter);
+  // set_rules (rules)
 }
 
 DataImpl :: MyTree :: ~MyTree ()
@@ -132,7 +142,7 @@ DataImpl :: MyTree :: ~MyTree ()
 }
 
 /****
-*****  
+*****
 ****/
 
 struct
@@ -154,6 +164,89 @@ DataImpl :: MyTree :: NodeMidCompare
     { return a->_mid < b; }
 };
 
+void
+DataImpl :: MyTree :: apply_rules (const const_nodes_v& candidates)
+{
+  NodeMidCompare compare;
+  const_nodes_v pass;
+  const_nodes_v fail;
+  pass.reserve (candidates.size());
+  fail.reserve (candidates.size());
+
+  // apply the rules to the whole tree
+  foreach_const (const_nodes_v, candidates, it) {
+    if (!(*it)->_article)
+      continue;
+    else if (_data._rules_filter.test_article (_data, _rules, _group, *(*it)->_article))
+      pass.push_back (*it);
+    else
+      fail.push_back (*it);
+  }
+  //std::cerr << LINE_ID << " " << pass.size() << " of "
+  //          << (pass.size() + fail.size()) << " articles pass\n";
+
+  //  maybe include threads or subthreads...
+  if (_show_type == Data::SHOW_THREADS)
+  {
+    foreach (const_nodes_v, pass, it) {
+      const ArticleNode *& n (*it);
+      while (n->_parent)
+        n = n->_parent;
+    }
+    std::sort (pass.begin(), pass.end(), compare);
+    pass.erase (std::unique (pass.begin(), pass.end()), pass.end());
+    //std::cerr << LINE_ID << " reduces to " << pass.size() << " threads\n";
+  }
+
+  if (_show_type == Data::SHOW_THREADS || _show_type == Data::SHOW_SUBTHREADS)
+  {
+    unique_nodes_t d;
+    foreach_const (const_nodes_v, pass, it)
+      accumulate_descendants (d, *it);
+    //std::cerr << LINE_ID << " expands into " << d.size() << " articles\n";
+
+    const_nodes_v fail2;
+    pass.clear ();
+    foreach_const (unique_nodes_t, d, it) {
+      const Article * a ((*it)->_article);
+      if (a->score > -9999 || _data._rules_filter.test_article (_data, _rules, _group, *a))
+        pass.push_back (*it); // pass is now sorted by mid because d was too
+      else
+        fail2.push_back (*it); // fail2 is sorted by mid because d was too
+    }
+
+    // fail cleanup: add fail2 and remove duplicates.
+    // both are sorted by mid, so set_union will do the job
+    const_nodes_v tmp;
+    tmp.reserve (fail.size() + fail2.size());
+    std::set_union (fail.begin(), fail.end(),
+                    fail2.begin(), fail2.end(),
+                    inserter (tmp, tmp.begin()), compare);
+    fail.swap (tmp);
+
+    // fail cleanup: remove newly-passing articles
+    tmp.clear ();
+    std::set_difference (fail.begin(), fail.end(),
+                         pass.begin(), pass.end(),
+                         inserter (tmp, tmp.begin()), compare);
+    fail.swap (tmp);
+    //std::cerr << LINE_ID << ' ' << pass.size() << " of "
+    //          << (pass.size() + fail.size())
+    //          << " make it past the show-thread block\n";
+  }
+
+
+  // passing articles not in the tree should be added...
+  add_articles (pass);
+
+  // failing articles in the tree should be removed...
+  quarks_t mids;
+  foreach_const (const_nodes_v, fail, it)
+    mids.insert (mids.end(), (*it)->_mid);
+  remove_articles (mids);
+
+}
+
 
 // candidates holds GroupHeader's ArticleNodes pointers
 // candidates are sorted by Mid (see NodeMidCompare)
@@ -219,7 +312,7 @@ DataImpl :: MyTree :: apply_filter (const const_nodes_v& candidates)
                     fail2.begin(), fail2.end(),
                     inserter (tmp, tmp.begin()), compare);
     fail.swap (tmp);
-	 
+
     // fail cleanup: remove newly-passing articles
     tmp.clear ();
     std::set_difference (fail.begin(), fail.end(),
@@ -227,7 +320,7 @@ DataImpl :: MyTree :: apply_filter (const const_nodes_v& candidates)
                          inserter (tmp, tmp.begin()), compare);
     fail.swap (tmp);
     //std::cerr << LINE_ID << ' ' << pass.size() << " of "
-    //          << (pass.size() + fail.size()) 
+    //          << (pass.size() + fail.size())
     //          << " make it past the show-thread block\n";
   }
 
@@ -331,6 +424,7 @@ DataImpl :: MyTree :: articles_changed (const quarks_t& mids, bool do_refilter)
     const_nodes_v nodes;
     _data.find_nodes (mids, _data.get_group_headers(_group)->_nodes, nodes);
     apply_filter (nodes);
+    apply_rules (nodes);
   }
 
   // fire an update event for any of those mids in our tree...
@@ -350,6 +444,7 @@ DataImpl :: MyTree :: add_articles (const quarks_t& mids)
   const_nodes_v nodes;
   _data.find_nodes (mids, _data.get_group_headers(_group)->_nodes, nodes);
   apply_filter (nodes);
+  apply_rules (nodes);
 }
 
 
diff --git a/pan/data-impl/rules-filter.cc b/pan/data-impl/rules-filter.cc
new file mode 100644
index 0000000..05ca5c2
--- /dev/null
+++ b/pan/data-impl/rules-filter.cc
@@ -0,0 +1,77 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Pan - A Newsreader for Gtk+
+ * Copyright (C) 2002-2006  Charles Kerr <charles rebelbase com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <config.h>
+#include <cassert>
+#include <pan/general/debug.h>
+#include <pan/general/macros.h>
+#include <pan/data/data.h>
+#include <gmime/gmime.h>
+#include <glib/gprintf.h>
+#include "rules-filter.h"
+
+using namespace pan;
+
+bool
+RulesFilter :: test_article (const Data        & data,
+                             const RulesInfo   & rules,
+                             const Quark       & group,
+                             const Article     & article) const
+{
+  bool pass (false);
+  const ArticleCache& cache(data.get_cache());
+
+  switch (rules._type)
+  {
+    case RulesInfo::AGGREGATE_AND:
+      pass = true;
+      foreach_const (RulesInfo::aggregates_t, rules._aggregates, it) {
+        // assume test passes if test needs body but article not cached
+        if (!it->_needs_body || cache.contains(article.message_id) )
+          if (!test_article (data, *it, group, article)) {
+            pass = false;
+            break;
+          }
+      }
+      break;
+
+    case RulesInfo::AGGREGATE_OR:
+      if (rules._aggregates.empty())
+        pass = true;
+      else {
+        pass = false;
+        foreach_const (RulesInfo::aggregates_t, rules._aggregates, it) {
+          // assume test fails if test needs body but article not cached
+          if (!it->_needs_body || cache.contains(article.message_id) )
+            if (test_article (data, *it, group, article)) {
+              pass = true;
+              break;
+            }
+        }
+      }
+      break;
+
+    case RulesInfo::MARK_READ:
+      pass = article.score == Article::COMPLETE;
+    break;
+  }
+
+  return pass;
+}
+
diff --git a/pan/data-impl/rules-filter.h b/pan/data-impl/rules-filter.h
new file mode 100644
index 0000000..c0f4b40
--- /dev/null
+++ b/pan/data-impl/rules-filter.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Pan - A Newsreader for Gtk+
+ * Copyright (C) 2002-2006  Charles Kerr <charles rebelbase com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef __RulesFilter_h__
+#define __RulesFilter_h__
+
+#include <pan/general/quark.h>
+#include <pan/usenet-utils/filter-info.h>
+#include <pan/usenet-utils/rules-info.h>
+#include <pan/usenet-utils/scorefile.h>
+#include <pan/data/article.h>
+#include <pan/data/data.h>
+
+namespace pan
+{
+  /**
+   * @ingroup data_impl
+   */
+  class RulesFilter
+  {
+
+    public:
+
+      RulesFilter() {  }
+
+      bool test_article (const Data& data,
+                         const RulesInfo   & rules,
+                         const Quark& group,
+                         const Article& article) const;
+
+  };
+}
+
+#endif
diff --git a/pan/data/data.h b/pan/data/data.h
index 28c16e1..e2db9be 100644
--- a/pan/data/data.h
+++ b/pan/data/data.h
@@ -37,6 +37,7 @@
 namespace pan
 {
   class FilterInfo;
+  class RulesInfo;
 
   /**
    * Data Interface class for seeing the mapping between groups and servers.
@@ -468,6 +469,8 @@ namespace pan
 
           virtual void set_filter (const ShowType     show_type = SHOW_ARTICLES,
                                    const FilterInfo * filter_or_null_to_reset = 0) = 0;
+
+          virtual void set_rules (const RulesInfo * filterme = 0) = 0;
       };
 
        /**
diff --git a/pan/gui/gui.cc b/pan/gui/gui.cc
index cc22eb3..fb1aa0a 100644
--- a/pan/gui/gui.cc
+++ b/pan/gui/gui.cc
@@ -904,6 +904,7 @@ void GUI :: do_select_article_body ()
 void GUI :: do_show_preferences_dialog ()
 {
   PrefsDialog * dialog = new PrefsDialog (_prefs, get_window(_root));
+  g_signal_connect (dialog->root(), "destroy", G_CALLBACK(prefs_dialog_destroyed_cb), this);
   gtk_widget_show (dialog->root());
 }
 void GUI :: do_show_group_preferences_dialog ()
@@ -1066,6 +1067,74 @@ void GUI :: server_list_dialog_destroyed (GtkWidget *)
   }
 }
 
+void GUI ::  prefs_dialog_destroyed_cb (GtkWidget * w, gpointer self)
+{
+  static_cast<GUI*>(self)->prefs_dialog_destroyed (w);
+}
+
+/*
+    w = score_handler_new (prefs, "rules-delete-score-value", "never", b);
+    w = score_handler_new (prefs, "rules-mark-read-value", "never", b);
+    w = score_handler_new (prefs, "rules-mark-unread-value", "never", b);
+    w = score_handler_new (prefs, "rules-autocache-value", "never", b);
+    w = score_handler_new (prefs, "rules-auto-dl-value", "never", b);
+*/
+
+#define NUM_RULES 6
+
+//int GUI :: score_int_from_string(std::string val, const char* rules[])
+//{
+//  const char* tmp (val.c_str());
+//  int res(-1);
+//  for (int i=0;i<NUM_RULES;++i) {
+//    if (!strcmp(rules[i],tmp)) { res = i; break; }
+//  }
+//  return res;
+//}
+
+void GUI :: prefs_dialog_destroyed (GtkWidget *)
+{
+
+  const Quark& group (_header_pane->get_group());
+  if (!group.empty() && _prefs._rules_changed)
+  {
+    _prefs._rules_changed = !_prefs._rules_changed;
+    std::string text;
+    int no(0);
+    _header_pane->rules(no);
+  }
+//  // apply filters
+//  if (_prefs._rules_changed && !group.empty())
+//  {
+//    _prefs._rules_changed = !_prefs._rules_changed;
+//    std::map<int, const char*> rules_map;
+//
+//    const char* rules [NUM_RULES] = { "never",
+//                              "watched",
+//                              "high" ,
+//                              "medium",
+//                              "low" ,
+//                              "ignored" };
+//    for (int i=0;i<NUM_RULES;++i)
+//      rules_map.insert(std::pair<int,const char*>(i,rules[i]));
+//    int val_delete(score_int_from_string(_prefs.get_string("rules-delete-score-value", "never"),rules));
+//    int val_read(score_int_from_string(_prefs.get_string("rules-mark-read-value", "never"),rules));
+//    int val_unread(score_int_from_string(_prefs.get_string("rules-mark-unread-value", "never"),rules));
+//    int val_cache(score_int_from_string(_prefs.get_string("rules-autocache-value", "never"),rules));
+//    int val_dl(score_int_from_string(_prefs.get_string("rules-auto-dl-value", "never"),rules));
+//
+//    std::cerr<<val_delete<<" "<<val_read<<std::endl;
+//
+//    std::vector<const Article*> articles_v (_header_pane->get_full_selection_v());
+//
+//    foreach (std::vector<const Article*>, articles_v, vit)
+//    {
+////      foreach
+//    }
+//  }
+}
+
+
 void GUI :: do_show_servers_dialog ()
 {
   GtkWidget * w = server_list_dialog_new (_data, _queue, get_window(_root));
diff --git a/pan/gui/gui.h b/pan/gui/gui.h
index 5470a09..542aa13 100644
--- a/pan/gui/gui.h
+++ b/pan/gui/gui.h
@@ -244,7 +244,9 @@ namespace pan
       static void add_widget (GtkUIManager*, GtkWidget*, gpointer);
       static void server_list_dialog_destroyed_cb (GtkWidget*, gpointer);
       void server_list_dialog_destroyed (GtkWidget*);
-
+      static void prefs_dialog_destroyed_cb (GtkWidget * w, gpointer self);
+      void prefs_dialog_destroyed (GtkWidget* w);
+      int score_int_from_string(std::string val, const char* rules[]);
   };
 }
 
diff --git a/pan/gui/header-pane.cc b/pan/gui/header-pane.cc
index 6f2b330..3e54483 100644
--- a/pan/gui/header-pane.cc
+++ b/pan/gui/header-pane.cc
@@ -131,7 +131,7 @@ namespace
                       const Quark         & message_id)
   {
     int offset (ICON_EMPTY);
-      
+
     if (queue.contains (message_id))
       offset = ICON_QUEUED;
     else if (cache.contains (message_id))
@@ -319,7 +319,7 @@ HeaderPane :: create_row (const EvolutionDateMaker & e,
 
   std::pair<mid_to_row_t::iterator,bool> result (_mid_to_row.insert (row));
   g_assert (result.second);
-  
+
   return row;
 }
 
@@ -684,7 +684,7 @@ HeaderPane :: on_tree_change (const Data::ArticleTree::Diffs& diffs)
     }
     _tree_store->insert_sorted (tmp);
   }
-     
+
   // reparent...
   if (do_thread && !diffs.reparented.empty()) {
     PanTreeStore::parent_to_children_t tmp;
@@ -916,7 +916,7 @@ HeaderPane :: on_button_pressed (GtkWidget * treeview, GdkEventButton *event, gp
     GtkTreeSelection * selection = gtk_tree_view_get_selection(tv);
     GtkTreePath *path;
     if (gtk_tree_view_get_path_at_pos (tv,
-                                       (gint) event->x, 
+                                       (gint) event->x,
                                        (gint) event->y,
                                        &path, NULL, NULL, NULL))
     {
@@ -1024,6 +1024,34 @@ namespace
     AUTHOR,
     MESSAGE_ID
   };
+
+  enum
+  {
+    RULES_MARK_READ,
+    RULES_MARK_UNREAD,
+    RULES_AUTOCACHE,
+    RULES_AUTODL,
+    RULES_DELETE
+  };
+}
+
+void
+HeaderPane :: rebuild_rules (int mode)
+{
+
+  RulesInfo &f (_rules);
+  f.set_type_aggregate_and ();
+  RulesInfo tmp;
+
+  if (mode == RULES_MARK_READ) {
+    tmp.set_type_mark_read ();
+    f._aggregates.push_back (tmp);
+  }
+//   if (mode == RULES_MARK_UNREAD) {
+//    tmp.set_type_mark_unread ();
+//    f._aggregates.push_back (tmp);
+//  }
+
 }
 
 void
@@ -1036,6 +1064,7 @@ HeaderPane :: rebuild_filter (const std::string& text, int mode)
   d.text = text;
 
   FilterInfo &f (_filter);
+
   f.set_type_aggregate_and ();
 
   // entry field filter...
@@ -1174,6 +1203,19 @@ HeaderPane :: filter (const std::string& text, int mode)
   }
 }
 
+void
+HeaderPane :: rules(int mode)
+{
+  if (_prefs.get_string("rules-mark-read-value","never") != "never")
+    rebuild_rules(RULES_MARK_READ);
+
+  if (_rules._aggregates.empty())
+    _atree->set_rules();
+  else
+    _atree->set_rules(&_rules);
+
+}
+
 namespace
 {
   // the text typed by the user.
@@ -1869,7 +1911,7 @@ namespace
 }
 
 /**
-*** 
+***
 **/
 
 void
diff --git a/pan/gui/header-pane.h b/pan/gui/header-pane.h
index 70de04c..532226d 100644
--- a/pan/gui/header-pane.h
+++ b/pan/gui/header-pane.h
@@ -26,6 +26,7 @@
 #include <pan/data/article-cache.h>
 #include <pan/data/data.h>
 #include <pan/usenet-utils/filter-info.h>
+#include <pan/usenet-utils/rules-info.h>
 #include <pan/usenet-utils/gnksa.h>
 #include <pan/tasks/queue.h>
 #include <pan/gui/action-manager.h>
@@ -297,15 +298,18 @@ namespace pan
       GtkWidget * _tree_view;
       PanTreeStore * _tree_store;
       FilterInfo _filter;
+      RulesInfo _rules;
       Data::ShowType _show_type;
       guint _selection_changed_idle_tag;
 
     private:
       void rebuild_filter (const std::string&, int);
+      void rebuild_rules (int mode);
       void refresh_font ();
 
     public: // public so that anonymous namespace can reach -- don't call
       void filter (const std::string& text, int mode);
+      void rules (int mode);
       static void do_popup_menu (GtkWidget*, GdkEventButton*, gpointer);
       static void on_row_activated (GtkTreeView*, GtkTreePath*, GtkTreeViewColumn*, gpointer);
       static gboolean on_button_pressed (GtkWidget*, GdkEventButton*, gpointer);
diff --git a/pan/gui/prefs-ui.cc b/pan/gui/prefs-ui.cc
index d9fbc2c..3144e34 100644
--- a/pan/gui/prefs-ui.cc
+++ b/pan/gui/prefs-ui.cc
@@ -96,6 +96,7 @@ namespace
     Prefs *prefs = static_cast<Prefs*>(data);
     prefs->set_int(key, gtk_spin_button_get_value_as_int(spin));
   }
+
   GtkWidget* new_spin_button (const char *key, int low, int high, Prefs &prefs)
   {
     guint tm = prefs.get_int(key, 5 );
@@ -177,6 +178,7 @@ namespace
   {
     Prefs * prefs (static_cast<Prefs*>(user_data));
     const char * key = (const char*) g_object_get_data (G_OBJECT(c), PREFS_KEY);
+    prefs->_rules_changed = strcmp(key,"rules-");
     const int column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT(c), "column"));
     const int row (gtk_combo_box_get_active (c));
     GtkTreeModel * m = gtk_combo_box_get_model (c);
@@ -212,6 +214,7 @@ namespace
       if (mode == strings[i][1])
         sel_index = i;
     }
+
     GtkWidget * c = gtk_combo_box_new_with_model (GTK_TREE_MODEL(store));
     GtkCellRenderer * renderer (gtk_cell_renderer_text_new ());
     gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (c), renderer, true);
@@ -239,6 +242,48 @@ namespace
     return h;
   }
 
+/*
+      Scores:
+    "Scores of 9999 or more:"
+    "Scores from 5000 to 9998:"
+    "Scores from 1 to 4999:"
+    "Scores from -9998 to -1:" */
+  GtkWidget* score_handler_new (Prefs& prefs,
+                              const char * mode_key,
+                              const char * mode_fallback,
+                              GtkWidget *& setme_mnemonic_target)
+  {
+    // build the combo box...
+    const std::string mode (prefs.get_string (mode_key, mode_fallback));
+    GtkListStore * store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
+    const char* strings[6][2] = { { N_("Disabled"),"never" },
+                                  { N_("9999 or more"), "watched" },
+                                  { N_("5000 to 9998"), "high" },
+                                  { N_("1 to 4999"),    "medium" },
+                                  { N_("-9998 to -1"),  "low" },
+                                  { N_("-9999 or less"),"ignored" }};
+    int sel_index (0);
+    for (size_t i=0; i<G_N_ELEMENTS(strings); ++i) {
+      GtkTreeIter iter;
+      gtk_list_store_append (store, &iter);
+      gtk_list_store_set (store, &iter, 0, _(strings[i][0]), 1, strings[i][1], -1);
+      if (mode == strings[i][1])
+        sel_index = i;
+    }
+
+    GtkWidget * c = gtk_combo_box_new_with_model (GTK_TREE_MODEL(store));
+    GtkCellRenderer * renderer (gtk_cell_renderer_text_new ());
+    gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (c), renderer, true);
+    gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (c), renderer, "text", 0, NULL);
+    gtk_combo_box_set_active (GTK_COMBO_BOX(c), sel_index);
+    g_object_set_data_full (G_OBJECT(c), PREFS_KEY, g_strdup(mode_key), g_free);
+    g_object_set_data (G_OBJECT(c), "column", GINT_TO_POINTER(1));
+    g_signal_connect (c, "changed", G_CALLBACK(set_prefs_string_from_combobox), &prefs);
+
+    setme_mnemonic_target = c;
+    return c;
+  }
+
   void font_set_cb (GtkFontButton* b, gpointer prefs_gpointer)
   {
     const char * key = (const char*) g_object_get_data (G_OBJECT(b), PREFS_KEY);
@@ -442,6 +487,7 @@ PrefsDialog :: PrefsDialog (Prefs& prefs, GtkWindow* parent):
   g_signal_connect_swapped (dialog, "destroy", G_CALLBACK(delete_prefs_dialog), this);
   GtkWidget * notebook = gtk_notebook_new ();
 
+  // Behaviour
   int row (0);
   GtkWidget *h, *w, *l, *b, *t;
   t = HIG :: workarea_create ();
@@ -489,6 +535,7 @@ PrefsDialog :: PrefsDialog (Prefs& prefs, GtkWindow* parent):
   HIG :: workarea_finish (t, &row);
   gtk_notebook_append_page (GTK_NOTEBOOK(notebook), t, gtk_label_new_with_mnemonic(_("_Behavior")));
 
+  // Layout
   row = 0;
   t = HIG :: workarea_create ();
   HIG :: workarea_add_section_title (t, &row, _("Pane Layout"));
@@ -525,6 +572,7 @@ PrefsDialog :: PrefsDialog (Prefs& prefs, GtkWindow* parent):
   HIG :: workarea_finish (t, &row);
   gtk_notebook_append_page (GTK_NOTEBOOK(notebook), t, gtk_label_new_with_mnemonic(_("_Layout")));
 
+  // Headers
   row = 0;
   t = HIG :: workarea_create ();
   HIG :: workarea_add_section_title (t, &row, _("Header Pane Columns"));
@@ -535,6 +583,26 @@ PrefsDialog :: PrefsDialog (Prefs& prefs, GtkWindow* parent):
 
   row = 0;
   t = HIG :: workarea_create ();
+
+    gtk_widget_set_tooltip_text (t, _("This menu lets you configure Pan to take certain actions on your behalf automatically, based on a post's score."));
+
+    w = score_handler_new (prefs, "rules-delete-score-value", "never", b);
+    HIG :: workarea_add_row (t, &row, _("_Delete Posts scoring at: "), w);
+    w = score_handler_new (prefs, "rules-mark-read-value", "never", b);
+    HIG :: workarea_add_row (t, &row, _("Mark Posts _read scoring at: "), w);
+    w = score_handler_new (prefs, "rules-mark-unread-value", "never", b);
+    HIG :: workarea_add_row (t, &row, _("Mark Posts _unread scoring at: "), w);
+    w = score_handler_new (prefs, "rules-autocache-value", "never", b);
+    HIG :: workarea_add_row (t, &row, _("_Cache Posts scoring at: "), w);
+    w = score_handler_new (prefs, "rules-auto-dl-value", "never", b);
+    HIG :: workarea_add_row (t, &row, _("Download _attachments of Posts scoring at: "), w);
+
+  HIG :: workarea_finish (t, &row);
+  gtk_notebook_append_page (GTK_NOTEBOOK(notebook), t, gtk_label_new_with_mnemonic(_("_Actions")));
+
+  // Fonts
+  row = 0;
+  t = HIG :: workarea_create ();
   HIG :: workarea_add_section_title (t, &row, _("Fonts"));
     HIG :: workarea_add_section_spacer (t, row, 4);
     l = new_check_button (_("Use custom font in Group Pane:"), "group-pane-font-enabled", false, prefs);
@@ -558,6 +626,7 @@ PrefsDialog :: PrefsDialog (Prefs& prefs, GtkWindow* parent):
   HIG :: workarea_finish (t, &row);
   gtk_notebook_append_page (GTK_NOTEBOOK(notebook), t, gtk_label_new_with_mnemonic(_("_Fonts")));
 
+  // Colors
   row = 0;
   t = HIG :: workarea_create ();
   HIG :: workarea_add_section_title (t, &row, _("Header Pane"));
@@ -605,6 +674,7 @@ PrefsDialog :: PrefsDialog (Prefs& prefs, GtkWindow* parent):
   HIG :: workarea_finish (t, &row);
   gtk_notebook_append_page (GTK_NOTEBOOK(notebook), t, gtk_label_new_with_mnemonic(_("_Colors")));
 
+  // Applications
   row = 0;
   t = HIG :: workarea_create ();
   HIG :: workarea_add_section_title (t, &row, _("Preferred Applications"));
@@ -620,6 +690,7 @@ PrefsDialog :: PrefsDialog (Prefs& prefs, GtkWindow* parent):
   HIG :: workarea_finish (t, &row);
   gtk_notebook_append_page (GTK_NOTEBOOK(notebook), t, gtk_label_new_with_mnemonic(_("A_pplications")));
 
+  // Upload Options
   row = 0;
   t = HIG :: workarea_create ();
   HIG :: workarea_add_section_title (t, &row, _("Encoding Options"));
diff --git a/pan/gui/prefs-ui.h b/pan/gui/prefs-ui.h
index ad32711..449e113 100644
--- a/pan/gui/prefs-ui.h
+++ b/pan/gui/prefs-ui.h
@@ -28,8 +28,8 @@ namespace pan
   class PrefsDialog
   {
     public:
-      PrefsDialog (Prefs&, GtkWindow*);
-      ~PrefsDialog () {}
+      PrefsDialog (Prefs&, GtkWindow*) ;
+      ~PrefsDialog () { }
       GtkWidget* root() { return _root; }
 
     private:
diff --git a/pan/gui/prefs.h b/pan/gui/prefs.h
index 0969fca..a4527d3 100644
--- a/pan/gui/prefs.h
+++ b/pan/gui/prefs.h
@@ -101,7 +101,7 @@ namespace pan
       bool get_geometry (const StringView&, int&, int&, int&, int&) const;
 
     public:
-      Prefs () {}
+      Prefs () { _rules_changed = false; }
       virtual void save () const {}
       virtual ~Prefs () {}
 
@@ -128,6 +128,9 @@ namespace pan
       mutable colors_t _colors;
       typedef std::map<std::string,int> ints_t;
       mutable ints_t _ints;
+
+    public:
+      bool _rules_changed;
   };
 }
 
diff --git a/pan/icons/icon_read_group.png b/pan/icons/icon_read_group.png
index afe2b09..32e7035 100644
Binary files a/pan/icons/icon_read_group.png and b/pan/icons/icon_read_group.png differ
diff --git a/pan/tasks/task-upload.cc b/pan/tasks/task-upload.cc
index 6d16951..dc2d927 100644
--- a/pan/tasks/task-upload.cc
+++ b/pan/tasks/task-upload.cc
@@ -162,7 +162,7 @@ TaskUpload :: update_work (NNTP* checkin_pending)
   {
     _state.set_working();
   }
-  else if ((_encoder_has_run && !_needed.empty()))
+  else if (_encoder_has_run && !_needed.empty())
   {
       _state.set_need_nntp(_server);
   }
@@ -264,6 +264,7 @@ TaskUpload :: on_nntp_done (NNTP * nntp,
   tmp.date = time(NULL);
   tmp.is_child = true;
   bool found(false);
+  bool post_ok(false);
 
   needed_t::iterator it;
   for (it=_needed.begin(); it!=_needed.end(); ++it)
@@ -274,13 +275,14 @@ TaskUpload :: on_nntp_done (NNTP * nntp,
 
   if (!found) return;
 
-  bool post_ok(false);
+  if (_queue_pos == -1) { _needed.erase(it); goto _end; }
+
   switch (health)
   {
     case OK:
       std::cerr<<"OK "<<_queue_pos<<std::endl;
-//      increment_step(it->second.bytes);
-//      _needed.erase (it);
+      increment_step(it->second.bytes);
+      _needed.erase (it);
       post_ok = true;
       break;
     case ERR_NETWORK:
diff --git a/pan/usenet-utils/Makefile.am b/pan/usenet-utils/Makefile.am
index 73e833a..081e894 100644
--- a/pan/usenet-utils/Makefile.am
+++ b/pan/usenet-utils/Makefile.am
@@ -4,6 +4,7 @@ noinst_LIBRARIES = libusenetutils.a
 
 libusenetutils_a_SOURCES = \
  filter-info.cc \
+ rules-info.cc \
  gnksa.cc \
  message-check.cc \
  mime-utils.cc \
@@ -15,6 +16,7 @@ libusenetutils_a_SOURCES = \
 noinst_HEADERS = \
  defgroup.h \
  filter-info.h \
+ rules-info.h \
  gnksa.h \
  message-check.h \
  mime-utils.h \
diff --git a/pan/usenet-utils/rules-info.cc b/pan/usenet-utils/rules-info.cc
new file mode 100644
index 0000000..e6d2ec2
--- /dev/null
+++ b/pan/usenet-utils/rules-info.cc
@@ -0,0 +1,77 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Pan - A Newsreader for Gtk+
+ * Copyright (C) 2002-2006  Charles Kerr <charles rebelbase com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <config.h>
+extern "C" {
+  #include <glib.h>
+  #include <glib/gi18n.h>
+}
+#include <pan/general/macros.h>
+#include "rules-info.h"
+
+using namespace pan;
+
+/***
+****
+***/
+
+void
+RulesInfo :: clear ()
+{
+  _type = RulesInfo::TYPE_ERR;
+  _aggregates.clear ();
+  _negate = false;
+  _needs_body = false;
+}
+
+void
+RulesInfo :: set_type_is (Type type) {
+   clear ();
+   _type = type;
+}
+
+void
+RulesInfo :: set_type_le (Type type, unsigned long le) {
+  clear ();
+  _type = type;
+  _negate = true;
+}
+
+void
+RulesInfo :: set_type_aggregate_and () {
+   clear ();
+   _type = AGGREGATE_AND;
+}
+void
+RulesInfo :: set_type_aggregate_or () {
+   clear ();
+   _type = AGGREGATE_OR;
+}
+
+/****
+*****
+****/
+
+
+void
+RulesInfo :: set_type_mark_read ()
+{
+   set_type_is (MARK_READ);
+}
+
diff --git a/pan/usenet-utils/rules-info.h b/pan/usenet-utils/rules-info.h
new file mode 100644
index 0000000..3165c82
--- /dev/null
+++ b/pan/usenet-utils/rules-info.h
@@ -0,0 +1,85 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Pan - A Newsreader for Gtk+
+ * Copyright (C) 2002-2006  Charles Kerr <charles rebelbase com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef __Rules_Info_h__
+#define __Rules_Info_h__
+
+#include <deque>
+#include <pan/general/quark.h>
+#include <pan/general/string-view.h>
+#include <pan/general/text-match.h>
+
+namespace pan
+{
+  /**
+   * Interface class describing a filter that can be applied to a set of articles.
+   * @ingroup usenet_utils
+   */
+  class RulesInfo
+  {
+    public:
+
+      /** The different type of filters we support. */
+      enum Type {
+        TYPE_ERR,
+        AGGREGATE_AND,
+        AGGREGATE_OR,
+        MARK_READ,
+        MARK_UNREAD,
+        AUTOCACHE,
+        AUTODOWNLOAD,
+        DELETE
+      };
+
+    public:
+      bool empty() const { return _type == TYPE_ERR; }
+      RulesInfo () { clear(); }
+      virtual ~RulesInfo () { }
+
+    public:
+
+      /** Defines what type of filter this is. */
+      Type _type;
+
+      /** Convenience typedef. */
+      typedef std::deque<RulesInfo> aggregates_t;
+
+      /** When `_type' is AGGREGATE_OR or AGGREGATE_AND,
+          these are the filters being or'ed or and'ed together. */
+      aggregates_t _aggregates;
+
+      /** When this is true, the results of the test should be negated. */
+      bool _negate;
+
+      /** When this is true the test needs the article body. */
+      bool _needs_body;
+
+    private:
+      void set_type_is (Type type);
+      void set_type_le (Type type, unsigned long le);
+
+    public:
+      void clear ();
+      void set_type_aggregate_and ();
+      void set_type_aggregate_or ();
+      void set_type_mark_read ();
+  };
+}
+
+#endif



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]