[pan2/bug_102402] improvements



commit 208401655a6284512c0303641b5cc91b1c5495f0
Author: Heinrich MÃller <henmull src gnome org>
Date:   Tue Jun 26 23:50:09 2012 +0200

    improvements

 pan/data-impl/data-impl.cc |   34 ++++--
 pan/data-impl/data-impl.h  |   22 ++++-
 pan/data-impl/groups.cc    |   36 ++++++-
 pan/data-impl/server.cc    |   80 +++++++++++--
 pan/data/data.h            |  202 +++++++++++++++++++++++++++++++--
 pan/gui/actions.cc         |    2 +-
 pan/gui/pan.cc             |    2 +-
 pan/gui/server-ui.cc       |  272 +++++++++++++++++++++++++++++++++++++++++++-
 pan/gui/server-ui.h        |    3 +
 pan/gui/task-pane.cc       |    4 +-
 pan/tasks/socket.cc        |    2 +-
 11 files changed, 615 insertions(+), 44 deletions(-)
---
diff --git a/pan/data-impl/data-impl.cc b/pan/data-impl/data-impl.cc
index f6a5375..2aa37a0 100644
--- a/pan/data-impl/data-impl.cc
+++ b/pan/data-impl/data-impl.cc
@@ -77,10 +77,7 @@ DataImpl :: DataImpl (const StringView& cache_ext, Prefs& prefs, bool unit_test,
   _prefs (prefs),
   _descriptions_loaded (false),
   newsrc_autosave_id (0),
-  newsrc_autosave_timeout (0),
-  xfer_autosave_id (0),
-  xfer_autosave_timeout (0)
-
+  newsrc_autosave_timeout (0)
 {
   rebuild_backend ();
 }
@@ -139,10 +136,23 @@ DataImpl :: load_xfer_files (const DataIO& data_io)
     while (!in->fail() && in->getline (line))
     {
       StringView server;
-      line.pop_token(server);
       StringView val;
-      line.pop_token (val);
-      unsigned long bytes (atoi(val.str));
+      XferBytes bytes_array;
+      int cnt(0), line_cnt(1);
+      if (line.pop_token(server))
+      {
+
+        while (line.pop_token(val))
+        {
+          unsigned long bytes (atoi(val.str));
+          bytes_array.bytes[cnt++] = bytes;
+
+        }
+        bytes_array.update_timestamp(line_cnt%2==0 ? MONTHLY : DAILY, time(0));
+        set_server_xfer (server, bytes_array);
+        bytes_array.increment(0);
+        ++line_cnt;
+      }
     }
 
     delete in;
@@ -165,9 +175,15 @@ DataImpl :: save_xfer_files (DataIO& data_io) const
   foreach_const (servers_t, _servers, sit)
   {
     const Quark& server (sit->first);
-    const Server& s (sit->second);
-    out << server<<" "<<s.xfer<<"\n";
+    Server s (sit->second);
+    s.xfer.increment(0);
+    out << server;
+    for (int i=0;i<5;++i)
+      out<<" "<<s.xfer.bytes[i];
+    out<<"\n";
   }
+  out<<"\n";
+
   data_io.write_done (&out);
 
 }
diff --git a/pan/data-impl/data-impl.h b/pan/data-impl/data-impl.h
index ec0809a..6ea1435 100644
--- a/pan/data-impl/data-impl.h
+++ b/pan/data-impl/data-impl.h
@@ -55,7 +55,6 @@
 namespace pan
 {
   typedef std::vector<const Article*> articles_t;
-  typedef Data::PasswordData PasswordData;
 
   /**
    * File-based implementation of the `Data' backend interface.
@@ -157,7 +156,13 @@ namespace pan
       virtual void set_server_article_expiration_age  (const Quark  & server,
                                                        int            days);
 
-      virtual void increment_server_xfer_count(const Quark& server, unsigned long bytes) ;
+      virtual void increment_server_xfer_count (const Quark& server, unsigned long bytes) ;
+
+      virtual void set_server_xfer (const Quark& server, XferBytes& bytes) ;
+
+      virtual void set_server_download_limit (const Quark& server, Rollup& rollup) ;
+
+      virtual void set_server_download_limit_action (const Quark& server, int& action) ;
 
       virtual void save_server_info (const Quark& server);
 
@@ -193,6 +198,10 @@ namespace pan
 
       virtual int get_server_article_expiration_age  (const Quark  & server) const;
 
+      virtual bool get_server_download_limit (const Quark  & server, const TimeType& time_type, Rollup& rollup) const;
+
+      virtual int get_server_download_limit_action (const Quark  & server) const;
+
     /**
     *** GROUPS
     **/
@@ -205,9 +214,14 @@ namespace pan
 
       typedef sorted_vector<Quark,true> unique_sorted_quarks_t;
       typedef sorted_vector<Quark,true> groups_t;
+
+    public:
+
       groups_t _moderated; // groups which are moderated
       groups_t _nopost; // groups which do not allow posting
 
+    private:
+
       typedef sorted_vector<Quark,true,AlphabeticalQuarkOrdering> alpha_groups_t;
       alpha_groups_t _subscribed; // subscribed groups, sorted alphabetically
       alpha_groups_t _unsubscribed; // non-subscribed groups, sorted alphabetically
@@ -318,6 +332,10 @@ namespace pan
       virtual char get_group_permission (const Quark & group) const;
       virtual void group_get_servers (const Quark& group, quarks_t&) const;
       virtual void server_get_groups (const Quark& server, quarks_t&) const;
+      virtual void server_get_groups_sorted (const Quark& servername, groups_t& addme) const;
+
+      virtual void server_get_nopost (const Quark&,  std::set<Quark>& dest) const;
+      virtual void server_get_moderated (const Quark&,  std::set<Quark>& dest) const;
 
     /**
     ***  HEADERS
diff --git a/pan/data-impl/groups.cc b/pan/data-impl/groups.cc
index b9ef49c..a5dc09c 100644
--- a/pan/data-impl/groups.cc
+++ b/pan/data-impl/groups.cc
@@ -27,6 +27,7 @@
 #include <map>
 #include <set>
 #include <vector>
+#include <algorithm> // set_union
 
 #include <glib.h>
 extern "C" {
@@ -232,7 +233,7 @@ DataImpl :: save_newsrc_files (DataIO& data_io) const
     std::string newsrc_string;
     alpha_groups_t::const_iterator sub_it (_subscribed.begin());
     const alpha_groups_t::const_iterator sub_end(_subscribed.end());
-    foreach_const (Server::groups_t, sit->second.groups, git) // for the groups in this server...
+    foreach_const (groups_t, sit->second.groups, git) // for the groups in this server...
     {
       const Quark& group (*git);
 
@@ -508,7 +509,7 @@ DataImpl :: add_groups (const Quark       & server,
 
   {
     AlphabeticalQuarkOrdering o;
-    Server::groups_t groups;
+    groups_t groups;
     std::vector<Quark> tmp;
 
     // make a groups_t from the added groups,
@@ -664,6 +665,37 @@ DataImpl :: server_get_groups (const Quark& servername, quarks_t& addme) const
 }
 
 void
+DataImpl :: server_get_groups_sorted (const Quark& servername, groups_t& addme) const
+{
+  const Server * server (find_server (servername));
+  if (server)
+    addme.insert (server->groups.begin(), server->groups.end());
+}
+
+void
+DataImpl :: server_get_nopost (const Quark& servername, std::set<Quark>& dest) const
+{
+  const Server * server (find_server (servername));
+  if (server && _nopost.size()!=0)
+  {
+    foreach_const (groups_t, server->groups, git)
+      foreach_const (groups_t, _nopost, pit)
+        if (*git==*pit) dest.insert(*pit);
+  }
+}
+void
+DataImpl :: server_get_moderated (const Quark& servername,  std::set<Quark>& dest) const
+{
+  const Server * server (find_server (servername));
+  if (server && _moderated.size()!=0)
+  {
+    foreach_const (groups_t, server->groups, git)
+      foreach_const (groups_t, _moderated, pit)
+        if (*git == *pit) dest.insert(*pit);
+  }
+}
+
+void
 DataImpl :: get_subscribed_groups (std::vector<Quark>& setme) const
 {
   setme.assign (_subscribed.begin(), _subscribed.end());
diff --git a/pan/data-impl/server.cc b/pan/data-impl/server.cc
index 89a93c7..f155785 100644
--- a/pan/data-impl/server.cc
+++ b/pan/data-impl/server.cc
@@ -215,19 +215,44 @@ DataImpl :: increment_server_xfer_count(const Quark& server, unsigned long bytes
 {
   Server * s (find_server (server));
   assert (s);
-//  mut.lock()
-  s->xfer += bytes;
-//  mut.unlock();
+  s->increment_xfer(bytes);
+}
+
+void
+DataImpl :: set_server_xfer (const Quark& server, XferBytes& bytes)
+{
+  Server * s (find_server (server));
+  assert (s);
+  s->xfer = bytes;
+}
+
+void
+DataImpl :: set_server_download_limit (const Quark& server, Rollup& rollup)
+{
+
+  Server * s (find_server (server));
+  assert (s);
+  if (rollup.time_type == DAILY)
+    s->daily_limit = rollup;
+  else
+    s->monthly_limit = rollup;
+}
+
+void
+DataImpl :: set_server_download_limit_action (const Quark& server, int& action)
+{
+
+  Server * s (find_server (server));
+  assert (s);
+  s->limit_action = action;
 }
 
 void
 DataImpl :: save_server_info (const Quark& server)
 {
-//  std::cerr<<"find "<<server<<"\n";
-//  Server * s (find_server (server));
-//  assert (s);
+  Server * s (find_server (server));
+  assert (s);
   save_server_properties (*_data_io, _prefs);
-
 }
 
 
@@ -297,6 +322,32 @@ DataImpl :: get_server_trust (const Quark   & server, int& setme) const
 }
 
 bool
+DataImpl :: get_server_download_limit (const Quark   & server, const TimeType& type, Rollup & setme) const
+{
+  const Server * s (find_server (server));
+  const bool found (s);
+  if (found) {
+    if (type == DAILY)
+      setme = s->daily_limit;
+    else
+      setme = s->monthly_limit;
+  }
+
+  return found;
+}
+int
+DataImpl :: get_server_download_limit_action (const Quark   & server) const
+{
+  const Server * s (find_server (server));
+  const bool found (s);
+  if (found) {
+    return s->limit_action;
+  }
+
+  return 0;
+}
+
+bool
 DataImpl :: get_server_addr (const Quark   & server,
                              std::string   & setme_host,
                              int           & setme_port) const
@@ -486,12 +537,12 @@ DataImpl :: load_server_properties (const DataIO& source)
     s.max_connections = to_int (kv["connection-limit"], 2);
     s.article_expiration_age = to_int(kv["expire-articles-n-days-old"], 31);
     s.rank = to_int(kv["rank"], 1);
-    s.xfer = to_int(kv["rank"], 0);
-    int ssl(to_int(kv["use-ssl"], 0));
-    s.ssl_support = ssl;
+    s.ssl_support = to_int(kv["use-ssl"], 0);
     s.cert = kv["cert"];
-    int trust(to_int(kv["trust"], 0));
-    s.trust = trust;
+    s.trust = to_int(kv["trust"], 0);
+    s.daily_limit = Rollup::parse(kv["daily-limit"]);
+    s.monthly_limit = Rollup::parse(kv["monthly-limit"]);
+    s.limit_action = to_int(kv["limit-action"]);
     s.newsrc_filename = kv["newsrc"];
     if (s.newsrc_filename.empty()) { // set a default filename
       std::ostringstream o;
@@ -556,7 +607,10 @@ else
          << indent(depth) << "<rank>" << s->rank << "</rank>\n"
          << indent(depth) << "<use-ssl>" << s->ssl_support << "</use-ssl>\n"
          << indent(depth) << "<trust>" << s->trust << "</trust>\n"
-         << indent(depth) << "<cert>"    << s->cert << "</cert>\n";
+         << indent(depth) << "<cert>"    << s->cert << "</cert>\n"
+         << indent(depth) << "<daily-limit>" << s->daily_limit.literal() << "</daily-limit>\n"
+         << indent(depth) << "<monthly-limit>" << s->monthly_limit.literal() << "</monthly-limit>\n"
+         << indent(depth) << "<limit-action>" << s->limit_action << "</limit-action>\n";
 
     *out << indent(--depth) << "</server>\n";
   }
diff --git a/pan/data/data.h b/pan/data/data.h
index 007d41b..b0b2922 100644
--- a/pan/data/data.h
+++ b/pan/data/data.h
@@ -24,6 +24,8 @@
 #include <list>
 #include <map>
 #include <vector>
+#include <sstream>
+#include <math.h>
 
 #include <pan/general/macros.h>
 #include <pan/general/quark.h>
@@ -35,6 +37,10 @@
 #include <pan/data/cert-store.h>
 #include <pan/data/server-info.h>
 
+extern "C" {
+  #include <glib/gi18n.h>
+}
+
 
 #ifdef HAVE_GKR
   #include <gnome-keyring-1/gnome-keyring.h>
@@ -173,16 +179,183 @@ namespace pan
     public virtual ArticleReferences
   {
 
+///TODO move to seperate header
+/*##############################*/
+
     public:
+      /* holds gnome keyring password data */
       struct PasswordData
       {
         Quark server;
         StringView user;
-//        StringView pw;
         gchar* pw;
       };
 
-    public:
+      enum TimeType
+      {
+        DAILY, MONTHLY
+      };
+
+      // holds transferred bytes, from 0-4 : B, KB, MB, GB, TB
+      struct XferBytes
+      {
+        unsigned long bytes[5];
+        char* ref[5];
+        time_t timestamp;
+
+        void increment (unsigned long b)
+        {
+          bytes[0] += b;
+          // update array
+          for (int i=0;i<4;++i)
+          {
+            int cnt = bytes[i] / 1024;
+            bytes[i+1] += cnt;
+            bytes[i] -= cnt * 1024;
+          }
+        }
+
+        std::string get_bytes()
+        {
+          std::stringstream ret;
+          for (int i=4;i>0;--i)
+          {
+            if (bytes[i]!=0)
+            {
+              ret << bytes[i]<<" ";
+              ret << ref[i];
+              break;
+            }
+          }
+          return ret.str();
+        }
+
+        std::string type()
+        {
+          int idx=0;
+          for (int i=4;i>0;--i)
+          {
+            if (bytes[i]!=0)
+            {
+              idx = i;
+              break;
+            }
+          }
+          return ref[idx];
+        }
+
+        int type_literal()
+        {
+          int idx=0;
+          for (int i=4;i>0;--i)
+          {
+            if (bytes[i]!=0)
+            {
+              idx = i;
+              break;
+            }
+          }
+          return idx;
+        }
+
+        unsigned long val()
+        {
+          int idx=0;
+          for (int i=4;i>0;--i)
+          {
+            if (bytes[i]!=0)
+            {
+              idx = i;
+              break;
+            }
+          }
+          return bytes[idx];
+        }
+
+        XferBytes() : timestamp(time(0))
+        {
+          ref[0]= _("Bytes");
+          ref[1]= _("KB");
+          ref[2]= _("MB");
+          ref[3]= _("GB");
+          ref[4]= _("TB");
+          for (int i=0;i<5;++i) bytes[i]=0;
+        }
+
+        // reset to 0 if timestamp is too old
+        void update_timestamp (const TimeType& time_type, time_t newtime)
+        {
+          time_t day (24*60*60);
+          bool reset (false);
+          if (time_type==Data::DAILY)
+          {
+            if (newtime-timestamp >= day) reset = true;
+          }
+          else
+          {
+            // hardcode to 30, don't care atm
+            if (newtime-timestamp >= day*30) reset = true;
+          }
+
+          if (reset)
+          {
+            for (int i=0;i<5;++i) bytes[i] = 0;
+          }
+        }
+      };
+
+      /* holds information about monthly rollup (download limit) */
+      struct Rollup
+      {
+
+        int type;
+        unsigned long val;
+        TimeType time_type;
+        time_t timestamp;
+
+        std::string literal() const
+        {
+          std::stringstream ret;
+          ret << type<<"@"<<val<<"$"<<timestamp;
+          return ret.str();
+        }
+
+        static Rollup parse(std::string input)
+        {
+          Rollup out;
+          size_t at (input.find("@"));
+          size_t dollar (input.find("$"));
+          out.type = atoi(input.substr(0,at).c_str());
+          out.val = atoi(input.substr(at+1, dollar).c_str());
+          out.timestamp = (time_t)atoi(input.substr(dollar+1, input.length()).c_str());
+          return out;
+        }
+
+        float percent (XferBytes& bytes)
+        {
+          float ret(0.0f);
+          int diff = type - bytes.type_literal();
+
+          float norm_a = type >= 0 ? val * pow (1024.0f , diff) : val / pow (1024.0f , diff);
+          ret = (bytes.bytes[type]/norm_a)*100.0f;
+          return ret;
+        }
+
+        std::string literal()
+        {
+          char* ref[5] = {_("Bytes"), _("KB"), _("MB"), _("GB"), _("TB") }; // dupe, todo: unite with xferbytes
+          std::stringstream out;
+          out << val<<" "<<ref[type];
+          return out.str();
+        }
+
+        Rollup() : time_type(DAILY), type(3), val(0) {}
+      };
+
+/*##############################*/
+
+      typedef sorted_vector<Quark,true,AlphabeticalQuarkOrdering> groups_t;
+
       struct Server
       {
          std::string username;
@@ -196,13 +369,17 @@ namespace pan
          int rank;
          int ssl_support;
          int trust;
-         typedef sorted_vector<Quark,true,AlphabeticalQuarkOrdering> groups_t;
          groups_t groups;
          gchar* gkr_pw;
-         unsigned long xfer;
+         XferBytes xfer;
+         Rollup daily_limit;
+         Rollup monthly_limit;
+         int limit_action;
+
+         void increment_xfer(unsigned long bytes) { xfer.increment(bytes); }
 
          Server(): port(STD_NNTP_PORT), article_expiration_age(31), max_connections(2),
-                    rank(1), ssl_support(0), trust(0), gkr_pw(NULL), xfer(0) {}
+                    rank(1), ssl_support(0), trust(0), gkr_pw(NULL), limit_action(0) {}
       };
 
     protected:
@@ -258,6 +435,10 @@ namespace pan
       virtual void set_server_limits (const Quark     & server,
                                       int               max_connections) = 0;
 
+      virtual void set_server_download_limit (const Quark& server, Rollup& rollup) = 0;
+
+      virtual void set_server_download_limit_action (const Quark& server, int& action) = 0;
+
       virtual bool get_server_addr (const Quark   & server,
                                     std::string   & setme_address,
                                     int           & setme_port) const = 0;
@@ -273,6 +454,11 @@ namespace pan
 
       virtual int get_server_limits (const Quark & server) const = 0;
 
+      virtual bool get_server_download_limit (const Quark  & server, const TimeType& time_type, Rollup& rollup) const = 0;
+
+      virtual int get_server_download_limit_action (const Quark  & server) const = 0;
+
+
     /*****************************************************************
     ***
     ***  OBSERVERS
@@ -394,10 +580,8 @@ namespace pan
 
       virtual char get_group_permission (const Quark & group) const=0;
 
-      //virtual void group_get_servers (const Quark& group, quarks_t&) const=0;
-
-
-
+      virtual void server_get_nopost (const Quark&,  std::set<Quark>& dest) const=0;
+      virtual void server_get_moderated (const Quark&, std::set<Quark>& dest) const=0;
 
     /*****************************************************************
     ***
diff --git a/pan/gui/actions.cc b/pan/gui/actions.cc
index 1419708..79898c5 100644
--- a/pan/gui/actions.cc
+++ b/pan/gui/actions.cc
@@ -428,7 +428,7 @@ namespace pan
         G_CALLBACK(do_show_profiles_dialog) },
 
       { "show-servers-dialog", GTK_STOCK_NETWORK,
-        N_("Edit _News Servers"), NULL,
+        N_("Show available _News Servers"), NULL,
         NULL,
         G_CALLBACK(do_show_servers_dialog) },
 
diff --git a/pan/gui/pan.cc b/pan/gui/pan.cc
index c99b3fe..30e88de 100644
--- a/pan/gui/pan.cc
+++ b/pan/gui/pan.cc
@@ -905,7 +905,7 @@ main (int argc, char *argv[])
 
     if (nzb && data.get_servers().empty()) {
       std::cerr << _("Please configure Pan's news servers before using it as an nzb client.") << std::endl;
-       return EXIT_FAILURE;
+      return EXIT_FAILURE;
     }
     data.set_newsrc_autosave_timeout( prefs.get_int("newsrc-autosave-timeout-min", 10 ));
 
diff --git a/pan/gui/server-ui.cc b/pan/gui/server-ui.cc
index 4c32098..ca04fc6 100644
--- a/pan/gui/server-ui.cc
+++ b/pan/gui/server-ui.cc
@@ -37,6 +37,7 @@ extern "C" {
 #include "pad.h"
 #include "hig.h"
 #include "gtk-compat.h"
+#include "progress-view.h"
 
 #ifdef HAVE_GNUTLS
   #include <pan/data/cert-store.h>
@@ -57,7 +58,7 @@ namespace
     Data& data;
     Queue& queue;
     Prefs& prefs;
-    Quark server;
+    Quark server; // must be deleted
     StringView cert;
     GtkWidget * dialog;
     GtkWidget * address_entry;
@@ -69,6 +70,11 @@ namespace
     GtkWidget * rank_combo;
     GtkWidget * ssl_combo;
     GtkWidget * always_trust_checkbox;
+    GtkWidget * daily_limit_spin;
+    GtkWidget * daily_limit_combo;
+    GtkWidget * monthly_limit_spin;
+    GtkWidget * monthly_limit_combo;
+    GtkWidget * limit_action_combo;
     ServerEditDialog (Data& d, Queue& q, Prefs& p): data(d), queue(q), prefs(p) {}
   };
 
@@ -117,7 +123,10 @@ namespace
     d->server = server;
 
     int port(STD_NNTP_PORT), max_conn(4), age(31*3), rank(1), ssl(0), trust(0);
+    int daily_limit(0), monthly_limit(0), limit_action(0);
+    int daily_limit_val(0), monthly_limit_val(0);
     std::string addr, user, cert;
+    Data::Rollup daily_rollup, monthly_rollup;
     gchar* pass(NULL);
     if (!server.empty()) {
       d->data.get_server_addr (server, addr, port);
@@ -128,6 +137,13 @@ namespace
       ssl = d->data.get_server_ssl_support(server);
       cert = d->data.get_server_cert(server);
       d->data.get_server_trust (server, trust);
+      d->data.get_server_download_limit(server, Data::DAILY, daily_rollup);
+      d->data.get_server_download_limit(server, Data::MONTHLY, monthly_rollup);
+      daily_limit = daily_rollup.type;
+      daily_limit_val = daily_rollup.val;
+      monthly_limit = monthly_rollup.type;
+      monthly_limit_val = monthly_rollup.val;
+      limit_action = d->data.get_server_download_limit_action (server);
     }
 
     pan_entry_set_text (d->address_entry, addr);
@@ -161,6 +177,46 @@ namespace
       }
     } while (gtk_tree_model_iter_next(model, &iter));
 
+    // set the daily limit combobox
+    combo = GTK_COMBO_BOX (d->daily_limit_combo);
+    model = gtk_combo_box_get_model (combo);
+    if (gtk_tree_model_get_iter_first(model, &iter)) do {
+      int that;
+      gtk_tree_model_get (model, &iter, 1, &that, -1);
+      if (that == daily_limit) {
+        gtk_combo_box_set_active_iter (combo, &iter);
+        break;
+      }
+    } while (gtk_tree_model_iter_next(model, &iter));
+
+    // set the monthly limit combobox
+    combo = GTK_COMBO_BOX (d->monthly_limit_combo);
+    model = gtk_combo_box_get_model (combo);
+    if (gtk_tree_model_get_iter_first(model, &iter)) do {
+      int that;
+      gtk_tree_model_get (model, &iter, 1, &that, -1);
+      if (that == monthly_limit) {
+        gtk_combo_box_set_active_iter (combo, &iter);
+        break;
+      }
+    } while (gtk_tree_model_iter_next(model, &iter));
+
+    // set limit spins
+    pan_spin_button_set (d->daily_limit_spin, daily_limit_val);
+    pan_spin_button_set (d->monthly_limit_spin, monthly_limit_val);
+
+    // set limit action combo
+    combo = GTK_COMBO_BOX (d->limit_action_combo);
+    model = gtk_combo_box_get_model (combo);
+    if (gtk_tree_model_get_iter_first(model, &iter)) do {
+      int that;
+      gtk_tree_model_get (model, &iter, 1, &that, -1);
+      if (that == limit_action) {
+        gtk_combo_box_set_active_iter (combo, &iter);
+        break;
+      }
+    } while (gtk_tree_model_iter_next(model, &iter));
+
 #ifdef HAVE_GNUTLS
     // set ssl combo
     combo = GTK_COMBO_BOX (d->ssl_combo);
@@ -213,7 +269,24 @@ namespace
       int ssl(0);
       int trust(0);
 
-      StringView cert(d->cert);
+      Data::Rollup daily_rollup;
+      daily_rollup.time_type = Data::DAILY;
+      Data::Rollup monthly_rollup;
+      monthly_rollup.time_type = Data::MONTHLY;
+      daily_rollup.val = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON(d->daily_limit_spin));
+      monthly_rollup.val = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON(d->monthly_limit_spin));
+      int daily_limit(0), monthly_limit(0);
+      combo = GTK_COMBO_BOX (d->daily_limit_combo);
+      if (gtk_combo_box_get_active_iter (combo, &iter))
+        gtk_tree_model_get (gtk_combo_box_get_model(combo), &iter, 1, &daily_rollup.type, -1);
+      combo = GTK_COMBO_BOX (d->monthly_limit_combo);
+      if (gtk_combo_box_get_active_iter (combo, &iter))
+        gtk_tree_model_get (gtk_combo_box_get_model(combo), &iter, 1, &monthly_rollup.type, -1);
+
+      int limit_action(0);
+      combo = GTK_COMBO_BOX (d->limit_action_combo);
+      if (gtk_combo_box_get_active_iter (combo, &iter))
+        gtk_tree_model_get (gtk_combo_box_get_model(combo), &iter, 1, &limit_action, -1);
 
 #ifdef HAVE_GNUTLS
       combo = GTK_COMBO_BOX (d->ssl_combo);
@@ -222,6 +295,8 @@ namespace
       trust = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(d->always_trust_checkbox)) ? 1 : 0;
 #endif
 
+      StringView cert(d->cert);
+
       const char * err_msg (0);
       if (addr.empty())
         err_msg = _("Please specify the server's address.");
@@ -246,6 +321,9 @@ namespace
         d->data.set_server_ssl_support(d->server, ssl);
         d->data.set_server_cert(d->server,cert);
         d->data.set_server_trust(d->server,trust);
+        d->data.set_server_download_limit(d->server, daily_rollup);
+        d->data.set_server_download_limit(d->server, monthly_rollup);
+        d->data.set_server_download_limit_action(d->server, limit_action);
 
         d->data.save_server_info(d->server);
 
@@ -291,6 +369,99 @@ pan :: import_sec_from_disk_dialog_new (Data& data, Queue& queue, GtkWindow * wi
 }
 
 GtkWidget*
+pan :: server_stats_dialog_new (Data& data, Queue& queue, Prefs& prefs, GtkWindow * window,
+                                const Quark& server, const Quark& server_name)
+{
+
+  char * title = g_strdup_printf ("Statistics of '%s'", server_name.c_str());
+
+  GtkWidget * dialog = gtk_dialog_new_with_buttons (title,
+                                                    GTK_WINDOW(window),
+                                                    GTK_DIALOG_DESTROY_WITH_PARENT,
+                                                    GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+                                                    GTK_STOCK_OK, GTK_RESPONSE_OK,
+                                                    NULL);
+
+  Data::Server * s (data.find_server(server));
+  Data::groups_t all_groups(s->groups);
+  gchar tmp[1024];
+  int row (0);
+
+  g_free (title);
+  gtk_window_set_role (GTK_WINDOW(dialog), "pan-show-server-stats-dialog");
+
+  GtkWidget * t (HIG::workarea_create ());
+
+  HIG::workarea_add_section_title (t, &row, _("Groups"));
+  HIG::workarea_add_section_spacer (t, row, 3);
+
+  g_snprintf(tmp, sizeof(tmp), "%d", all_groups.size());
+  HIG::workarea_add_row (t, &row, _("Group count:"), gtk_label_new(tmp), NULL);
+
+  std::set<Quark> nopostgroups, moderated;
+
+  data.server_get_nopost(server, nopostgroups);
+  g_snprintf(tmp, sizeof(tmp), "%d", nopostgroups.size());
+  HIG::workarea_add_row (t, &row, _("No posting allowed:"), gtk_label_new(tmp), NULL);
+
+  data.server_get_moderated(server, moderated);
+  g_snprintf(tmp, sizeof(tmp), "%d", moderated.size());
+  HIG::workarea_add_row (t, &row, _("Moderated:"), gtk_label_new(tmp), NULL);
+
+  HIG::workarea_add_section_divider (t, &row);
+
+  HIG::workarea_add_section_title (t, &row, _("Download Meter"));
+  HIG::workarea_add_section_spacer (t, row, 2);
+
+  g_snprintf(tmp, sizeof(tmp), "%s", s->xfer.get_bytes().c_str());
+  HIG::workarea_add_row (t, &row, _("Download Count:"), gtk_label_new(tmp), NULL);
+
+  /// TODO : free dynamic stuff
+
+  ProgressView* pv = new ProgressView();
+  Progress* prog = new Progress(_("Daily Limit"));
+  prog->add_steps (100);
+
+  Data::Rollup rollup;
+
+  if (data.get_server_download_limit (server, Data::DAILY, rollup))
+  {
+    float step = s->daily_limit.percent(s->xfer);
+    prog->set_step ((int)step);
+    std::stringstream tmp;
+    tmp << step<<" % ("<<s->daily_limit.literal()<<")";
+    prog->set_status (tmp.str().c_str());
+  }
+
+  pv->set_progress(prog);
+  HIG::workarea_add_row (t, &row, _("Daily Limit:"), pv->root(), NULL);
+
+  ProgressView* pv2 = new ProgressView();
+  Progress* prog2 = new Progress(_("Monthly Limit"));
+  prog2->add_steps (100);
+
+  if (data.get_server_download_limit (server, Data::MONTHLY, rollup))
+  {
+    float step = s->monthly_limit.percent(s->xfer);
+    prog2->set_step ((int)step);
+    std::stringstream tmp;
+    tmp << step<<" % ("<<s->monthly_limit.literal()<<")";
+    prog2->set_status (tmp.str().c_str());
+  }
+
+  pv2->set_progress(prog2);
+  HIG::workarea_add_row (t, &row, _("Monthly Limit:"), pv2->root(), NULL);
+
+  gtk_box_pack_start (GTK_BOX( gtk_dialog_get_content_area( GTK_DIALOG(dialog))), t, TRUE, TRUE, 0);
+  g_object_set_data_full (G_OBJECT(dialog), "dialog", dialog, 0);
+  g_signal_connect (dialog, "response", G_CALLBACK(gtk_widget_destroy), dialog);
+
+  gtk_widget_show_all (dialog);
+
+  return dialog;
+}
+
+GtkWidget*
 pan :: server_edit_dialog_new (Data& data, Queue& queue, Prefs& prefs, GtkWindow * window, const Quark& server)
 {
   ServerEditDialog * d (new ServerEditDialog (data, queue, prefs));
@@ -398,6 +569,80 @@ pan :: server_edit_dialog_new (Data& data, Queue& queue, Prefs& prefs, GtkWindow
     gtk_widget_set_tooltip_text( e, _("Fallback servers are used for articles that can't be found on the primaries.  One common approach is to use free servers as primaries and subscription servers as fallbacks."));
     HIG::workarea_add_row (t, &row, e, w);
 
+    HIG::workarea_add_section_divider (t, &row);
+    HIG::workarea_add_section_title (t, &row, _("Download Limits"));
+    HIG::workarea_add_section_spacer (t, row, 2);
+
+    struct { int byte; const char * str; } byte_items[] = {
+      { 0, N_("Bytes") },
+      { 1, N_("KB") },
+      { 2, N_("MB") },
+      { 3, N_("GB") },
+      { 4, N_("TB") }
+    };
+    store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
+    for (unsigned int i(0); i<G_N_ELEMENTS(byte_items); ++i) {
+      GtkTreeIter iter;
+      gtk_list_store_append (store,  &iter);
+      gtk_list_store_set (store, &iter, 0, _(byte_items[i].str), 1, byte_items[i].byte, -1);
+    }
+
+    // daily limit
+    GtkWidget* hbox = gtk_hbox_new (FALSE, PAD);
+    a = GTK_ADJUSTMENT (gtk_adjustment_new (1.0, 1.0, 1023, 1.0, 1.0, 0.0));
+    w = d->daily_limit_spin = gtk_spin_button_new (GTK_ADJUSTMENT(a), 1.0, 0u);
+    gtk_box_pack_end (GTK_BOX(hbox), w, TRUE, TRUE, 0);
+    w = d->daily_limit_combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL(store));
+    gtk_box_pack_end (GTK_BOX(hbox), w, TRUE, TRUE, 0);
+    g_object_unref (G_OBJECT(store));
+    gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (w), renderer, true);
+    gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (w), renderer, "text", 0, NULL);
+    gtk_combo_box_set_active (GTK_COMBO_BOX(w), 0);
+    l = gtk_label_new (_("Daily Limit:"));
+    e = gtk_event_box_new ();
+    gtk_container_add (GTK_CONTAINER(e), l);
+    gtk_misc_set_alignment (GTK_MISC(l), 0.0f, 0.5f);
+    HIG::workarea_add_row (t, &row, e, hbox);
+
+    // monthly limit
+    hbox = gtk_hbox_new (FALSE, PAD);
+    a = GTK_ADJUSTMENT (gtk_adjustment_new (1.0, 1.0, 1023, 1.0, 1.0, 0.0));
+    w = d->monthly_limit_spin = gtk_spin_button_new (GTK_ADJUSTMENT(a), 1.0, 0u);
+    gtk_box_pack_end (GTK_BOX(hbox), w, TRUE, TRUE, 0);
+    w = d->monthly_limit_combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL(store));
+    gtk_box_pack_end (GTK_BOX(hbox), w, TRUE, TRUE, 0);
+    g_object_unref (G_OBJECT(store));
+    gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (w), renderer, true);
+    gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (w), renderer, "text", 0, NULL);
+    gtk_combo_box_set_active (GTK_COMBO_BOX(w), 0);
+    l = gtk_label_new (_("Monthly Limit:"));
+    e = gtk_event_box_new ();
+    gtk_container_add (GTK_CONTAINER(e), l);
+    gtk_misc_set_alignment (GTK_MISC(l), 0.0f, 0.5f);
+    HIG::workarea_add_row (t, &row, e, hbox);
+
+    struct { int action; const char * str; } action_items[] = {
+      { 0, N_("Pause Tasks on affected Server") },
+      { 1, N_("Pause entire Queue") },
+      { 2, N_("Warn once and continue") }
+    };
+    store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
+    for (unsigned int i(0); i<G_N_ELEMENTS(action_items); ++i) {
+      GtkTreeIter iter;
+      gtk_list_store_append (store,  &iter);
+      gtk_list_store_set (store, &iter, 0, _(action_items[i].str), 1, action_items[i].action, -1);
+    }
+    w = d->limit_action_combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL(store));
+    g_object_unref (G_OBJECT(store));
+    gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (w), renderer, true);
+    gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (w), renderer, "text", 0, NULL);
+    gtk_combo_box_set_active (GTK_COMBO_BOX(w), 0);
+    l = gtk_label_new (_("Action:"));
+    e = gtk_event_box_new ();
+    gtk_container_add (GTK_CONTAINER(e), l);
+    gtk_misc_set_alignment (GTK_MISC(l), 0.0f, 0.5f);
+    HIG::workarea_add_row (t, &row, e, w);
+
     // ssl 3.0 option
 #ifdef HAVE_GNUTLS
     // select ssl/plaintext
@@ -488,6 +733,7 @@ namespace
     GtkListStore * servers_store;
     GtkWidget * remove_button;
     GtkWidget * edit_button;
+    GtkWidget * stats_button;
     ServerListDialog (Data& d, Queue& q, Prefs& p): data(d), queue(q), prefs(p) {}
   };
 
@@ -511,7 +757,6 @@ namespace
       }
     }
 
-    //std::cerr << LINE_ID << " selected server is " << server << std::endl;
     return server;
   }
 
@@ -534,7 +779,6 @@ namespace
       }
     }
 
-    //std::cerr << LINE_ID << " selected server is " << server << std::endl;
     return server;
   }
 
@@ -544,6 +788,7 @@ namespace
     const bool have_sel (!get_selected_server(d).empty());
     gtk_widget_set_sensitive (d->edit_button, have_sel);
     gtk_widget_set_sensitive (d->remove_button, have_sel);
+    gtk_widget_set_sensitive (d->stats_button, have_sel);
   }
 
   void
@@ -627,6 +872,18 @@ namespace
   }
 
   void
+  stats_button_clicked_cb (GtkButton * button, gpointer data)
+  {
+    const Quark empty_quark;
+    GtkWidget * list_dialog = GTK_WIDGET (data);
+    ServerListDialog * d = (ServerListDialog*) g_object_get_data (G_OBJECT(list_dialog), "dialog");
+    GtkWidget * stats_dialog = server_stats_dialog_new (d->data, d->queue, d->prefs,
+                                                        GTK_WINDOW(list_dialog), get_selected_server(d), get_selected_server_name (d));
+    g_signal_connect (stats_dialog, "destroy", G_CALLBACK(gtk_widget_destroy), stats_dialog);
+    gtk_widget_show_all (stats_dialog);
+  }
+
+  void
   server_edit_dialog_destroy_cb (GtkWidget *, gpointer user_data)
   {
     if (GTK_IS_WIDGET (user_data))
@@ -898,6 +1155,13 @@ pan :: server_list_dialog_new (Data& data, Queue& queue, Prefs& prefs, GtkWindow
   g_signal_connect (w, "clicked", G_CALLBACK(remove_button_clicked_cb), d);
   d->remove_button = w;
 
+  //show stats
+  w = gtk_button_new_with_label (_("Statistics"));
+  gtk_box_pack_start (GTK_BOX (bbox), w, FALSE, FALSE, 0);
+  gtk_widget_set_tooltip_text(w, _("Show Server's Statistics"));
+  g_signal_connect (w, "clicked", G_CALLBACK(stats_button_clicked_cb), d->dialog);
+  d->stats_button = w;
+
   server_tree_view_refresh (d);
   button_refresh (d);
   return d->dialog;
diff --git a/pan/gui/server-ui.h b/pan/gui/server-ui.h
index ec24feb..14005b0 100644
--- a/pan/gui/server-ui.h
+++ b/pan/gui/server-ui.h
@@ -34,6 +34,9 @@ namespace pan
   GtkWidget* server_edit_dialog_new (Data&, Queue&, Prefs&, GtkWindow*, const Quark& server);
 
   /** @ingroup GUI */
+  GtkWidget* server_stats_dialog_new (Data&, Queue&, Prefs&, GtkWindow*, const Quark& server, const Quark& server_name);
+
+  /** @ingroup GUI */
   GtkWidget* server_list_dialog_new (Data&, Queue&, Prefs&, GtkWindow*);
 
   /** @ingroup GUI */
diff --git a/pan/gui/task-pane.cc b/pan/gui/task-pane.cc
index 11cdfcc..645b458 100644
--- a/pan/gui/task-pane.cc
+++ b/pan/gui/task-pane.cc
@@ -153,9 +153,9 @@ TaskPane:: on_tooltip_query(GtkWidget  *widget,
     date = date_maker.get_date_string (ta->get_article().time_posted);
     g_snprintf(buffer,sizeof(buffer),
                _("\n<u>Download</u>\n\n<i>Subject:</i> <b>\"%s\"</b>\n<i>From:</i> <b>%s</b>\n<i>Date:</i> <b>%s</b>\n"
-                 "<i>Groups:</i> <b>%s</b>\n<i>Save Path:</i> <b>%s</b>\nPaused: %d"),
+                 "<i>Groups:</i> <b>%s</b>\n<i>Save Path:</i>\n\n"),
                a.subject.to_string().c_str(), escaped(a.author.to_string()).c_str(), date ? date : _("unknown"),
-               ta->get_groups().c_str(), ta->get_save_path().to_string().c_str(), ta->start_paused());
+               ta->get_groups().c_str(), ta->get_save_path().to_string().c_str());
   }
 
   task_found = tu || ta;
diff --git a/pan/tasks/socket.cc b/pan/tasks/socket.cc
index ff317b9..384adac 100644
--- a/pan/tasks/socket.cc
+++ b/pan/tasks/socket.cc
@@ -66,7 +66,6 @@ Socket :: get_speed_KiBps ()
     const int delta = now - _time_of_last_check;
     const double current_speed = (_bytes_since_last_check/1024.0) / delta;
     _time_of_last_check = now;
-    fire_socket_data_transferred(_bytes_since_last_check);
     _bytes_since_last_check = 0;
 
     _speed_KiBps = (std::fabs(_speed_KiBps)<0.0001)
@@ -88,6 +87,7 @@ void
 Socket :: increment_xfer_byte_count (unsigned long byte_count)
 {
    _bytes_since_last_check += byte_count;
+   fire_socket_data_transferred(byte_count);
 }
 
 void



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