[pan2] added download meter (rough basic impl) https://bugzilla.gnome.org/show_bug.cgi?id=102402



commit e954815e5987bcbc4ae9e9dde5e6c0a5b0b5a4fe
Author: Heinrich MÃller <henmull src gnome org>
Date:   Sun Sep 23 19:15:15 2012 +0200

    added download meter (rough basic impl)
    https://bugzilla.gnome.org/show_bug.cgi?id=102402

 pan.cbp                          |   69 ++-----------
 pan/data-impl/Makefile.am        |    6 +-
 pan/data-impl/data-impl.cc       |   50 +++++++++-
 pan/data-impl/data-impl.h        |   13 ++-
 pan/data-impl/data-io.cc         |   18 ++++
 pan/data-impl/data-io.h          |    2 +
 pan/data-impl/download-meter.cc  |  208 ++++++++++++++++++++++++++++++++++++++
 pan/data-impl/download-meter.h   |   89 ++++++++++++++++
 pan/data/data.h                  |   51 +++++++++-
 pan/general/progress.cc          |    4 +-
 pan/general/progress.h           |   11 ++-
 pan/gui/Makefile.am              |    6 +-
 pan/gui/actions.cc               |    6 +
 pan/gui/dl-prefs.cc              |  193 +++++++++++++++++++++++++++++++++++
 pan/gui/dl-prefs.h               |   61 +++++++++++
 pan/gui/gui.cc                   |   35 +++++--
 pan/gui/gui.h                    |   14 ++-
 pan/gui/pan-ui.h                 |    1 +
 pan/gui/pan.cc                   |    7 +-
 pan/gui/prefs-ui.cc              |   20 +---
 pan/gui/prefs.cc                 |   23 ++++
 pan/gui/prefs.h                  |   15 +++
 pan/gui/progress-view.cc         |   16 +++
 pan/gui/progress-view.h          |    3 +
 pan/tasks/nntp-pool.cc           |   10 +-
 pan/tasks/nntp-pool.h            |   12 +-
 pan/tasks/nntp.cc                |    6 +
 pan/tasks/nntp.h                 |   10 ++-
 pan/tasks/queue.cc               |   20 ++++-
 pan/tasks/queue.h                |   13 ++-
 pan/tasks/socket-impl-gio.cc     |    1 +
 pan/tasks/socket-impl-openssl.cc |    1 +
 pan/tasks/socket.h               |    9 ++-
 33 files changed, 887 insertions(+), 116 deletions(-)
---
diff --git a/pan.cbp b/pan.cbp
index 704b4b4..6c7bda6 100644
--- a/pan.cbp
+++ b/pan.cbp
@@ -58,20 +58,16 @@
 		<Compiler>
 			<Add option="-Wall" />
 		</Compiler>
-		<Unit filename="config.h">
-			<Option target="all_linux" />
-		</Unit>
+		<Unit filename="config.h" />
 		<Unit filename="pan/data-impl/add-server.cc" />
 		<Unit filename="pan/data-impl/article-filter.cc" />
 		<Unit filename="pan/data-impl/article-filter.h" />
-		<Unit filename="pan/data-impl/cert-store.h">
-			<Option target="all_linux" />
-		</Unit>
 		<Unit filename="pan/data-impl/data-impl.cc" />
 		<Unit filename="pan/data-impl/data-impl.h" />
 		<Unit filename="pan/data-impl/data-io.cc" />
 		<Unit filename="pan/data-impl/data-io.h" />
-		<Unit filename="pan/data-impl/defgroup.h" />
+		<Unit filename="pan/data-impl/download-meter.cc" />
+		<Unit filename="pan/data-impl/download-meter.h" />
 		<Unit filename="pan/data-impl/groups.cc" />
 		<Unit filename="pan/data-impl/headers-test.cc" />
 		<Unit filename="pan/data-impl/headers.cc" />
@@ -83,9 +79,6 @@
 		<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/sql-db.h">
-			<Option target="all_linux" />
-		</Unit>
 		<Unit filename="pan/data-impl/task-archive.cc" />
 		<Unit filename="pan/data-impl/xover.cc" />
 		<Unit filename="pan/data/article-cache.cc" />
@@ -97,19 +90,14 @@
 		<Unit filename="pan/data/cert-store.h" />
 		<Unit filename="pan/data/data.cc" />
 		<Unit filename="pan/data/data.h" />
-		<Unit filename="pan/data/defgroup.h" />
 		<Unit filename="pan/data/encode-cache.cc" />
 		<Unit filename="pan/data/encode-cache.h" />
 		<Unit filename="pan/data/parts.cc" />
 		<Unit filename="pan/data/parts.h" />
 		<Unit filename="pan/data/server-info.h" />
-		<Unit filename="pan/data/xover-cache.h">
-			<Option target="all_linux" />
-		</Unit>
 		<Unit filename="pan/data/xref-test.cc" />
 		<Unit filename="pan/data/xref.cc" />
 		<Unit filename="pan/data/xref.h" />
-		<Unit filename="pan/general/compression.h" />
 		<Unit filename="pan/general/debug.cc" />
 		<Unit filename="pan/general/debug.h" />
 		<Unit filename="pan/general/defgroup.h" />
@@ -117,9 +105,6 @@
 		<Unit filename="pan/general/e-util.h" />
 		<Unit filename="pan/general/file-util.cc" />
 		<Unit filename="pan/general/file-util.h" />
-		<Unit filename="pan/general/gdk-threads.h">
-			<Option target="all_linux" />
-		</Unit>
 		<Unit filename="pan/general/line-reader.cc" />
 		<Unit filename="pan/general/line-reader.h" />
 		<Unit filename="pan/general/locking.h" />
@@ -128,9 +113,6 @@
 		<Unit filename="pan/general/macros.h" />
 		<Unit filename="pan/general/map-vector.h" />
 		<Unit filename="pan/general/messages.h" />
-		<Unit filename="pan/general/non-gui.h">
-			<Option target="all_linux" />
-		</Unit>
 		<Unit filename="pan/general/progress-test.cc" />
 		<Unit filename="pan/general/progress.cc" />
 		<Unit filename="pan/general/progress.h" />
@@ -147,7 +129,6 @@
 		<Unit filename="pan/general/text-match.cc" />
 		<Unit filename="pan/general/text-match.h" />
 		<Unit filename="pan/general/time-elapsed.h" />
-		<Unit filename="pan/general/typedefs.h" />
 		<Unit filename="pan/general/utf8-utils.cc" />
 		<Unit filename="pan/general/utf8-utils.h" />
 		<Unit filename="pan/general/worker-pool.cc" />
@@ -161,6 +142,9 @@
 		<Unit filename="pan/gui/defgroup.h" />
 		<Unit filename="pan/gui/dl-headers-ui.cc" />
 		<Unit filename="pan/gui/dl-headers-ui.h" />
+		<Unit filename="pan/gui/dl-prefs.cc" />
+		<Unit filename="pan/gui/dl-prefs.h" />
+		<Unit filename="pan/gui/download-meter.h" />
 		<Unit filename="pan/gui/e-action-combo-box.c">
 			<Option compilerVar="CC" />
 		</Unit>
@@ -188,9 +172,6 @@
 		<Unit filename="pan/gui/group-prefs.cc" />
 		<Unit filename="pan/gui/group-prefs.h" />
 		<Unit filename="pan/gui/gtk-compat.h" />
-		<Unit filename="pan/gui/gtk_compat.h">
-			<Option target="all_linux" />
-		</Unit>
 		<Unit filename="pan/gui/gui.cc" />
 		<Unit filename="pan/gui/gui.h" />
 		<Unit filename="pan/gui/header-pane.cc" />
@@ -253,8 +234,6 @@
 		<Unit filename="pan/gui/xface.h" />
 		<Unit filename="pan/icons/pan-pixbufs-internal.h" />
 		<Unit filename="pan/icons/pan-pixbufs.h" />
-		<Unit filename="pan/icons/pan_icons/pan-pixbufs-internal.h" />
-		<Unit filename="pan/icons/pan_icons/pan-pixbufs.h" />
 		<Unit filename="pan/tasks/adaptable-set-test.cc" />
 		<Unit filename="pan/tasks/adaptable-set.cc" />
 		<Unit filename="pan/tasks/adaptable-set.h" />
@@ -287,9 +266,6 @@
 		<Unit filename="pan/tasks/task-article.h" />
 		<Unit filename="pan/tasks/task-groups.cc" />
 		<Unit filename="pan/tasks/task-groups.h" />
-		<Unit filename="pan/tasks/task-multipost.h">
-			<Option target="all_linux" />
-		</Unit>
 		<Unit filename="pan/tasks/task-post.cc" />
 		<Unit filename="pan/tasks/task-post.h" />
 		<Unit filename="pan/tasks/task-upload.cc" />
@@ -339,58 +315,35 @@
 		<Unit filename="pan/usenet-utils/url-find.h" />
 		<Unit filename="uulib/crc32.c">
 			<Option compilerVar="CC" />
-			<Option target="all_linux" />
-		</Unit>
-		<Unit filename="uulib/crc32.h">
-			<Option target="all_linux" />
 		</Unit>
+		<Unit filename="uulib/crc32.h" />
 		<Unit filename="uulib/fptools.c">
 			<Option compilerVar="CC" />
-			<Option target="all_linux" />
-		</Unit>
-		<Unit filename="uulib/fptools.h">
-			<Option target="all_linux" />
 		</Unit>
+		<Unit filename="uulib/fptools.h" />
 		<Unit filename="uulib/uucheck.c">
 			<Option compilerVar="CC" />
-			<Option target="all_linux" />
-		</Unit>
-		<Unit filename="uulib/uudeview.h">
-			<Option target="all_linux" />
 		</Unit>
+		<Unit filename="uulib/uudeview.h" />
 		<Unit filename="uulib/uuencode.c">
 			<Option compilerVar="CC" />
-			<Option target="all_linux" />
-		</Unit>
-		<Unit filename="uulib/uuencode2.c">
-			<Option compilerVar="CC" />
-			<Option target="all_linux" />
-		</Unit>
-		<Unit filename="uulib/uuint.h">
-			<Option target="all_linux" />
 		</Unit>
+		<Unit filename="uulib/uuint.h" />
 		<Unit filename="uulib/uulib.c">
 			<Option compilerVar="CC" />
-			<Option target="all_linux" />
 		</Unit>
 		<Unit filename="uulib/uunconc.c">
 			<Option compilerVar="CC" />
-			<Option target="all_linux" />
 		</Unit>
 		<Unit filename="uulib/uuscan.c">
 			<Option compilerVar="CC" />
-			<Option target="all_linux" />
 		</Unit>
 		<Unit filename="uulib/uustring.c">
 			<Option compilerVar="CC" />
-			<Option target="all_linux" />
-		</Unit>
-		<Unit filename="uulib/uustring.h">
-			<Option target="all_linux" />
 		</Unit>
+		<Unit filename="uulib/uustring.h" />
 		<Unit filename="uulib/uuutil.c">
 			<Option compilerVar="CC" />
-			<Option target="all_linux" />
 		</Unit>
 		<Extensions>
 			<envvars />
diff --git a/pan/data-impl/Makefile.am b/pan/data-impl/Makefile.am
index b038814..30f97f0 100644
--- a/pan/data-impl/Makefile.am
+++ b/pan/data-impl/Makefile.am
@@ -16,7 +16,8 @@ libpandata_a_SOURCES = \
  server.cc \
  my-tree.cc \
  task-archive.cc \
- xover.cc
+ xover.cc \
+ download-meter.cc
 
 
 noinst_HEADERS = \
@@ -26,7 +27,8 @@ noinst_HEADERS = \
  data-io.h \
  defgroup.h \
  profiles.h \
- memchunk.h
+ memchunk.h \
+ download-meter.h
 
 #noinst_PROGRAMS = \
 # add-server \
diff --git a/pan/data-impl/data-impl.cc b/pan/data-impl/data-impl.cc
index 30cadfc..c4095d4 100644
--- a/pan/data-impl/data-impl.cc
+++ b/pan/data-impl/data-impl.cc
@@ -69,16 +69,17 @@ namespace
 
 DataImpl :: DataImpl (const StringView& cache_ext, Prefs& prefs, bool unit_test, int cache_megs, DataIO * io):
   ProfilesImpl (*io),
+  DownloadMeterImpl(prefs, *this),
   _cache (get_cache_path(), cache_ext, cache_megs),
   _encode_cache (get_encode_cache_path(), cache_megs),
   _certstore(*this),
   _unit_test (unit_test),
   _data_io (io),
-  _prefs (prefs),
   _descriptions_loaded (false),
   newsrc_autosave_id (0),
   newsrc_autosave_timeout (0),
-  _rules_filter (prefs.get_flag("rules-autocache-mark-read", false), prefs.get_flag("rules-auto-dl-mark-read", false),
+  _rules_filter (prefs.get_flag("rules-autocache-mark-read", false),
+                 prefs.get_flag("rules-auto-dl-mark-read", false),
                  prefs.get_flag("rules-autocache-mark-read", false))
 
 {
@@ -104,6 +105,7 @@ DataImpl :: rebuild_backend ()
     load_newsrc_files (*_data_io);
     load_group_xovers (*_data_io);
     load_group_permissions (*_data_io);
+    load_download_stats (*_data_io);
 
     _descriptions.clear ();
     _descriptions_loaded = false;
@@ -116,7 +118,6 @@ DataImpl :: rebuild_backend ()
 DataImpl :: ~DataImpl ()
 {
   save_state ();
-
 }
 
 void
@@ -127,6 +128,7 @@ DataImpl :: save_state ()
     debug ("data-impl dtor saving xov, newsrc...");
     save_group_xovers (*_data_io);
     save_newsrc_files (*_data_io);
+    save_download_stats (*_data_io);
   }
 }
 
@@ -179,3 +181,45 @@ DataImpl :: password_decrypt (PasswordData& pw) const
 #endif
 
 
+/***
+ **  Download stats
+ ***/
+
+ void
+DataImpl :: load_download_stats (const DataIO& data_io)
+{
+
+  LineReader * in (data_io.read_download_stats ());
+  StringView s, line;
+  uint64_t bytes (0ul);
+  while (in && !in->fail() && in->getline(line))
+  {
+    if (line.len && *line.str=='#')
+    {
+      continue;
+    }
+    else
+    {
+      bytes = atoll(line.str);
+      break;
+    }
+  }
+
+  dl_meter_init (bytes);
+
+  delete in;
+}
+
+void
+DataImpl :: save_download_stats(DataIO& data_io) const
+{
+  if (_unit_test)
+    return;
+
+  std::ostream& out (*data_io.write_download_stats ());
+
+  out << "# Download stats (single uint64_t showing bytes downloaded so far\n";
+  out << dl_meter_get_bytes() <<"\n";
+
+  data_io.write_done (&out);
+}
diff --git a/pan/data-impl/data-impl.h b/pan/data-impl/data-impl.h
index 14ff1d1..c742269 100644
--- a/pan/data-impl/data-impl.h
+++ b/pan/data-impl/data-impl.h
@@ -44,6 +44,7 @@
 #include <pan/data-impl/article-filter.h>
 #include <pan/data-impl/rules-filter.h>
 #include <pan/data-impl/profiles.h>
+#include <pan/data-impl/download-meter.h>
 #include <pan/data-impl/memchunk.h>
 #include <pan/gui/prefs.h>
 
@@ -68,7 +69,9 @@ namespace pan
   class DataImpl:
     public Data,
     public TaskArchive,
-    public ProfilesImpl
+    public ProfilesImpl,
+    public DownloadMeterImpl
+//    public DownloadMeter::Listener
   {
 
     /**
@@ -689,6 +692,14 @@ namespace pan
         newsrc_autosave_id = 0;
       }
 
+    /**
+    ***  Download stats
+    **/
+    public:
+
+      void load_download_stats (const DataIO&);
+      void save_download_stats (DataIO&) const;
+
   };
 }
 
diff --git a/pan/data-impl/data-io.cc b/pan/data-impl/data-io.cc
index 8f3aa8f..7267811 100644
--- a/pan/data-impl/data-io.cc
+++ b/pan/data-impl/data-io.cc
@@ -80,6 +80,12 @@ namespace
     g_free (filename);
     return retval;
   }
+
+  std::string get_download_stats_filename ()
+  {
+    return get_pan_home_file ("downloads.stats");
+  }
+
 }
 
 std::string
@@ -151,6 +157,12 @@ DataIO :: read_group_permissions () const
 }
 
 LineReader*
+DataIO :: read_download_stats () const
+{
+   return read_file (get_download_stats_filename ());
+}
+
+LineReader*
 DataIO :: read_group_xovers () const
 {
   return read_file (get_group_xovers_filename ());
@@ -258,6 +270,12 @@ DataIO :: write_group_headers (const Quark& group)
 }
 
 std::ostream*
+DataIO :: write_download_stats ()
+{
+   return write_file (get_download_stats_filename ());
+}
+
+std::ostream*
 DataIO :: write_file (const StringView& filename)
 {
   return get_ostream (filename);
diff --git a/pan/data-impl/data-io.h b/pan/data-impl/data-io.h
index ab0b2cc..c832bea 100644
--- a/pan/data-impl/data-io.h
+++ b/pan/data-impl/data-io.h
@@ -50,12 +50,14 @@ namespace pan
     virtual LineReader* read_group_headers (const Quark& group) const;
     virtual LineReader* read_group_descriptions () const;
     virtual LineReader* read_group_permissions () const;
+    virtual LineReader* read_download_stats () const;
 
     virtual std::ostream* write_tasks ();
     virtual std::ostream* write_server_properties ();
     virtual std::ostream* write_group_xovers ();
     virtual std::ostream* write_group_descriptions ();
     virtual std::ostream* write_group_permissions ();
+    virtual std::ostream* write_download_stats ();
     virtual std::ostream* write_group_headers (const Quark& group);
     virtual void write_done (std::ostream*);
 
diff --git a/pan/data-impl/download-meter.cc b/pan/data-impl/download-meter.cc
new file mode 100644
index 0000000..a0910aa
--- /dev/null
+++ b/pan/data-impl/download-meter.cc
@@ -0,0 +1,208 @@
+/* -*- 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 <pan/data-impl/download-meter.h>
+#include <pan/data-impl/data-impl.h>
+#include <iostream>
+#include <math.h>
+
+extern "C" {
+  #include <glib/gi18n.h>
+}
+
+using namespace pan;
+
+
+DownloadMeterImpl :: ~DownloadMeterImpl()
+{
+}
+
+
+DownloadMeterImpl :: DownloadMeterImpl (Prefs& prefs, Data& data) :
+                _view(new ProgressView()),
+                _progress(new Progress("Downloaded bytes")),
+                _prefs(prefs),
+                _data(data),
+                _downloaded_bytes(0ul),
+                _limit(1ul)
+{
+
+  dl_meter_update ();
+
+  _view->set_progress(_progress);
+  _progress->init_steps(100);
+  _progress->set_status(_("DL Init ...."));
+
+  GtkWidget* w = _button = gtk_button_new();
+  gtk_widget_set_tooltip_text (w, _("Open Download Meter Preferences"));
+  gtk_button_set_relief (GTK_BUTTON(w), GTK_RELIEF_NONE);
+
+  gtk_container_add (GTK_CONTAINER(w), _view->root());
+  GtkWidget* frame = _widget = gtk_frame_new (NULL);
+  gtk_container_set_border_width (GTK_CONTAINER(frame), 0);
+  gtk_frame_set_shadow_type (GTK_FRAME(frame), GTK_SHADOW_IN);
+  gtk_container_add (GTK_CONTAINER(frame), w);
+
+  gtk_widget_set_visible(_widget, prefs.get_flag("dl-meter-show", true));
+
+}
+
+namespace
+{
+
+  std::string mnemonic(int cnt)
+  {
+    std::string ret;
+
+    switch (cnt)
+    {
+      default:
+      case 0:
+        ret = _("Bytes");
+        break;
+      case 1:
+        ret = _("KB");
+        break;
+      case 2:
+        ret = _("MB");
+        break;
+      case 3:
+        ret = _("GB");
+        break;
+      case 4:
+        ret = _("TB");
+        break;
+    }
+
+    return ret;
+  }
+
+  // black magic to get declarative value (mb, gb)
+  std::string get_value(uint64_t bytes)
+  {
+    int cnt (log10f(bytes)/log10f(1024));
+    uint64_t factor = ::pow(1024ul, (uint64_t)cnt);
+    float rest = cnt > 0.0f ? (((float)bytes / (float)factor)/10.0f) : 0.0f;
+    std::stringstream str;
+
+    uint64_t ret = factor == 0ul ? ret : bytes / factor;
+    str << (ret+rest) << " " << mnemonic(cnt);
+    return str.str();
+  }
+
+}
+
+void
+DownloadMeterImpl :: set_status ()
+{
+  // check if limit reached
+  if (_downloaded_bytes > _limit && _limit > 0)
+  {
+    fire_dl_limit_reached ();
+    if (_prefs.get_flag("warn-dl-limit-reached", true))
+      _view->set_color("red");
+  }
+  else
+  {
+    _view->reset_color ();
+  }
+
+  _progress->set_status_va(_("DL %s"), get_value(_downloaded_bytes).c_str());
+
+  if (_limit > 0)
+    _progress->set_step (100ul * _downloaded_bytes/_limit);
+
+}
+
+void
+DownloadMeterImpl :: dl_meter_add (uint64_t bytes)
+{
+    _downloaded_bytes += bytes;
+    fire_xfer_bytes(bytes);
+    set_status ();
+}
+
+void
+DownloadMeterImpl :: dl_meter_reset ()
+{
+  std::cerr<<"reset\n";
+  _downloaded_bytes = 0;
+  set_status ();
+  fire_reset_xfer_bytes ();
+}
+
+void
+DownloadMeterImpl :: dl_meter_init (uint64_t bytes)
+{
+   _downloaded_bytes = bytes;
+}
+
+void
+DownloadMeterImpl :: dl_meter_update ()
+{
+  std::string limit_type (_prefs.get_string("dl-limit-type", ""));
+
+  int type_idx (0);
+  if (limit_type == "mb")
+      type_idx = 2; // 1024*1024
+  if (limit_type == "gb")
+      type_idx = 3; // 1024*1024*1024
+
+  int limit (_prefs.get_int("dl-limit", 1024));
+  _limit = limit * ::pow(1024, type_idx);
+
+  set_status ();
+}
+
+void
+DownloadMeterImpl :: fire_reset_xfer_bytes ()
+{
+  for (lit it(_listeners.begin()), end(_listeners.end()); it!=end; )
+    (*it++)->on_reset_xfer_bytes ();
+}
+
+void
+DownloadMeterImpl :: fire_dl_limit_reached ()
+{
+  for (lit it(_listeners.begin()), end(_listeners.end()); it!=end; )
+    (*it++)->on_dl_limit_reached ();
+}
+
+
+void
+DownloadMeterImpl :: fire_xfer_bytes (uint64_t bytes)
+{
+  for (lit it(_listeners.begin()), end(_listeners.end()); it!=end; )
+    (*it++)->on_xfer_bytes (bytes);
+}
+
+uint64_t
+DownloadMeterImpl :: dl_meter_get_limit()
+{
+  return _limit;
+}
+
+void
+DownloadMeterImpl :: dl_meter_set_limit (uint64_t l)
+{
+  _limit = l;
+  set_status();
+}
+
diff --git a/pan/data-impl/download-meter.h b/pan/data-impl/download-meter.h
new file mode 100644
index 0000000..dfe304e
--- /dev/null
+++ b/pan/data-impl/download-meter.h
@@ -0,0 +1,89 @@
+/* -*- 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 _DOWNLOAD_METER_IMPL_H_
+#define _DOWNLOAD_METER_IMPL_H_
+
+#include <string>
+#include <pan/general/quark.h>
+#include <pan/general/string-view.h>
+#include <pan/gui/prefs.h>
+#include <pan/gui/progress-view.h>
+#include <pan/general/progress.h>
+#include <pan/data/data.h>
+
+namespace pan
+{
+  class DownloadMeterImpl :
+            public virtual DownloadMeter
+  {
+    public:
+
+      DownloadMeterImpl (Prefs&, Data&);
+      virtual ~DownloadMeterImpl() ;
+
+      virtual void dl_meter_add(const uint64_t bytes) ;
+      virtual void dl_meter_init (uint64_t bytes) ;
+      virtual void dl_meter_reset () ;
+      virtual void dl_meter_update () ;
+
+    private:
+
+      typedef std::set<DownloadMeter::Listener*> listeners_t;
+      typedef listeners_t::const_iterator lit;
+      listeners_t _listeners;
+
+      ProgressView* _view;
+      Progress* _progress;
+      uint64_t _limit;
+      uint64_t _downloaded_bytes;
+      GtkWidget* _widget;
+      GtkWidget* _button;
+      Prefs& _prefs;
+      Data& _data;
+
+    public:
+
+      virtual void add_listener (DownloadMeter::Listener * l)    { _listeners.insert(l); }
+      virtual void remove_listener (DownloadMeter::Listener * l) { _listeners.erase(l);  }
+
+      virtual ProgressView* get_view() { return _view; }
+      virtual GtkWidget* get_widget () { return _widget; }
+      virtual GtkWidget* get_button () { return _button; }
+
+    public:
+
+      virtual uint64_t dl_meter_get_limit();
+      virtual void dl_meter_set_limit (uint64_t l);
+
+      virtual uint64_t dl_meter_get_bytes() const { return _downloaded_bytes; }
+
+    private:
+
+      virtual void fire_xfer_bytes (uint64_t bytes);
+      virtual void fire_reset_xfer_bytes ();
+      virtual void fire_dl_limit_reached ();
+
+      void set_status ();
+
+
+  };
+}
+
+#endif
diff --git a/pan/data/data.h b/pan/data/data.h
index 7d4f4a8..684961d 100644
--- a/pan/data/data.h
+++ b/pan/data/data.h
@@ -35,6 +35,7 @@
 #include <pan/data/cert-store.h>
 #include <pan/data/server-info.h>
 #include <pan/gui/prefs.h>
+#include <pan/gui/progress-view.h>
 
 #ifdef HAVE_GKR
   #include <gnome-keyring-1/gnome-keyring.h>
@@ -152,6 +153,51 @@ namespace pan
       Profiles () {}
   };
 
+  class DownloadMeter
+  {
+    public:
+
+      virtual ~DownloadMeter () {}
+
+      struct Listener {
+          Listener () {}
+          virtual ~Listener () {}
+          virtual void on_xfer_bytes (uint64_t) = 0;
+          virtual void on_reset_xfer_bytes () = 0;
+          virtual void on_dl_limit_reached () = 0;
+        };
+
+      virtual void add_listener (Listener * l) = 0;
+      virtual void remove_listener (Listener * l) = 0;
+
+    protected:
+
+      DownloadMeter() {}
+
+    public:
+      virtual void dl_meter_add (uint64_t bytes) = 0;
+      virtual void dl_meter_update () = 0;
+
+    public:
+
+      virtual ProgressView* get_view() = 0;
+      virtual GtkWidget* get_widget () = 0;
+      virtual GtkWidget* get_button () = 0;
+
+    public:
+
+      virtual uint64_t dl_meter_get_limit() = 0;
+      virtual void dl_meter_set_limit (uint64_t l) = 0;
+      virtual void dl_meter_reset () = 0;
+
+    public:
+
+      virtual void fire_xfer_bytes (uint64_t bytes) = 0;
+      virtual void fire_reset_xfer_bytes () = 0;
+      virtual void fire_dl_limit_reached () = 0;
+
+  };
+
   /**
    * The main interface class for Pan's data backend.
    *
@@ -167,6 +213,7 @@ namespace pan
    */
   class Data:
     public virtual ServerInfo,
+    public virtual DownloadMeter,
     public virtual GroupServer,
     public virtual ArticleRead,
     public virtual Profiles,
@@ -178,7 +225,6 @@ namespace pan
       {
         Quark server;
         StringView user;
-//        StringView pw;
         gchar* pw;
       };
 
@@ -668,6 +714,9 @@ namespace pan
                                    const Quark         & server,
                                    const uint64_t   low) = 0;
 
+      /** Sets the queue interface */
+      virtual void set_queue (Queue* q) = 0;
+
   };
 }
 
diff --git a/pan/general/progress.cc b/pan/general/progress.cc
index 4bbcc4c..ac157d3 100644
--- a/pan/general/progress.cc
+++ b/pan/general/progress.cc
@@ -123,6 +123,7 @@ Progress :: set_step (int step)
   const int old_of_100 (get_progress_of_100());
    _step = step;
   const int new_of_100 (get_progress_of_100());
+
   if (old_of_100 != new_of_100)
     fire_percentage (new_of_100);
 }
@@ -136,13 +137,12 @@ Progress :: increment_step (int increment)
 int
 Progress :: get_progress_of_100 () const
 {
-  int p = (int)(!_steps ? 0 : (_step*100.0)/_steps);
+  int p = (int)(!_steps ? 0 : (_step*100ul)/_steps);
   if (p < 0) p = 0;
   if (p > 100) p = 100;
   return p;
 }
 
-
 void
 Progress :: set_finished (int status)
 {
diff --git a/pan/general/progress.h b/pan/general/progress.h
index 37d9365..999004f 100644
--- a/pan/general/progress.h
+++ b/pan/general/progress.h
@@ -26,6 +26,11 @@
 #include <pan/general/debug.h>
 #include <pan/general/string-view.h>
 
+extern "C"
+{
+    #include <stdint.h>
+}
+
 namespace pan
 {
   class StringView;
@@ -72,10 +77,10 @@ namespace pan
       std::string _description; // used for default describe()
       std::string _status_text; // the last status text emitted
       std::vector<std::string> _errors; // the emitted error strings
-      int _steps; // number of steps total
-      int _step; // number of steps completed so far
+      uint64_t _steps; // number of steps total
+      uint64_t _step; // number of steps completed so far
       int _done; // value of set_finished()
-      bool _active; 
+      bool _active;
 
     public:
 
diff --git a/pan/gui/Makefile.am b/pan/gui/Makefile.am
index 67c0090..39cb9a6 100644
--- a/pan/gui/Makefile.am
+++ b/pan/gui/Makefile.am
@@ -23,6 +23,7 @@ libpangui_a_SOURCES = \
  pan-tree.cc \
  post-ui.cc \
  prefs.cc \
+ dl-prefs.cc \
  prefs-file.cc \
  prefs-ui.cc \
  progress-view.cc \
@@ -35,7 +36,7 @@ libpangui_a_SOURCES = \
  server-ui.cc \
  task-pane.cc \
  xface.c \
- url.cc
+ url.cc 
 
 noinst_HEADERS = \
  action-manager.h \
@@ -65,6 +66,7 @@ noinst_HEADERS = \
  pan.ui.h \
  pan.ui.ssl.h \
  prefs.h \
+ dl-prefs.h \
  prefs-file.h \
  prefs-ui.h \
  post-ui.h \
@@ -84,7 +86,7 @@ noinst_HEADERS = \
  url.h \
  wait.h \
  xface.h \
- pan-colors.h
+ pan-colors.h 
 
 EXTRA_DIST = \
  panrc.rc
diff --git a/pan/gui/actions.cc b/pan/gui/actions.cc
index c8e136f..1539c50 100644
--- a/pan/gui/actions.cc
+++ b/pan/gui/actions.cc
@@ -121,6 +121,7 @@ namespace pan
   void do_import_tasks                 (GtkAction*) { pan_ui->do_import_tasks(); }
   void do_cancel_latest_task           (GtkAction*) { pan_ui->do_cancel_latest_task(); }
   void do_show_task_window             (GtkAction*) { pan_ui->do_show_task_window(); }
+  void do_show_dl_meter_prefs          (GtkAction*) { pan_ui->do_show_dl_meter_prefs(); }
   void do_show_log_window              (GtkAction*) { pan_ui->do_show_log_window(); }
   void do_quit                         (GtkAction*) { pan_ui->do_quit(); }
   void do_clear_header_pane            (GtkAction*) { pan_ui->do_clear_header_pane(); }
@@ -386,6 +387,11 @@ namespace pan
         NULL,
         G_CALLBACK(do_show_task_window) },
 
+      { "show-dl-meter-prefs", NULL,
+        N_("_Download Meter Preferences"), NULL,
+        NULL,
+        G_CALLBACK(do_show_dl_meter_prefs) },
+
       { "show-log-dialog", GTK_STOCK_DIALOG_INFO,
         N_("_Event Log"), NULL,
         NULL,
diff --git a/pan/gui/dl-prefs.cc b/pan/gui/dl-prefs.cc
new file mode 100644
index 0000000..5e53bb6
--- /dev/null
+++ b/pan/gui/dl-prefs.cc
@@ -0,0 +1,193 @@
+/* -*- 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/gi18n.h>
+  #include "gtk-compat.h"
+}
+#include <pan/general/debug.h>
+#include <pan/general/macros.h>
+#include <pan/icons/pan-pixbufs.h>
+#include "hig.h"
+#include "pad.h"
+#include "pan-file-entry.h"
+#include "dl-prefs.h"
+#include "url.h"
+#include "gtk-compat.h"
+
+using namespace pan;
+
+namespace pan
+{
+
+  namespace
+  {
+    #define PREFS_KEY "prefs-key"
+    #define PREFS_VAL "prefs-val"
+
+    void toggled_cb (GtkToggleButton * toggle, gpointer prefs_gpointer)
+    {
+      const char * key = (const char*) g_object_get_data (G_OBJECT(toggle), PREFS_KEY);
+      if (key)
+        static_cast<Prefs*>(prefs_gpointer)->set_flag (key, gtk_toggle_button_get_active(toggle));
+    }
+
+    GtkWidget* new_check_button (const char* mnemonic, const char* key, bool fallback, Prefs& prefs)
+    {
+      GtkWidget * t = gtk_check_button_new_with_mnemonic (mnemonic);
+      g_object_set_data_full (G_OBJECT(t), PREFS_KEY, g_strdup(key), g_free);
+      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(t), prefs.get_flag (key, fallback));
+      g_signal_connect (t, "toggled", G_CALLBACK(toggled_cb), &prefs);
+      return t;
+    }
+
+    void response_cb (GtkDialog * dialog, int, gpointer)
+    {
+      gtk_widget_destroy (GTK_WIDGET(dialog));
+    }
+
+    void delete_dialog (gpointer castme)
+    {
+      DLMeterDialog* meterdialog(static_cast<DLMeterDialog*>(castme));
+      meterdialog->meter().dl_meter_update();
+      delete meterdialog;
+    }
+
+    void spin_value_changed_cb( GtkSpinButton *spin, gpointer data)
+    {
+      const char * key = (const char*) g_object_get_data (G_OBJECT(spin), PREFS_KEY);
+      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 );
+      GtkAdjustment *adj = (GtkAdjustment*) gtk_adjustment_new(tm, low, high, 1.0, 1.0, 0.0);
+      GtkWidget *w = gtk_spin_button_new( adj, 1.0, 0);
+      g_object_set_data_full(G_OBJECT(w), PREFS_KEY, g_strdup(key), g_free);
+      g_signal_connect (w, "value_changed", G_CALLBACK(spin_value_changed_cb), &prefs);
+      return w;
+    }
+
+    void set_prefs_string_from_combobox (GtkComboBox * c, gpointer user_data)
+    {
+      Prefs * prefs (static_cast<Prefs*>(user_data));
+      const char * key = (const char*) g_object_get_data (G_OBJECT(c), PREFS_KEY);
+
+      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);
+      GtkTreeIter i;
+      if (gtk_tree_model_iter_nth_child (m, &i, 0, row)) {
+        char * val (0);
+        gtk_tree_model_get (m, &i, column, &val, -1);
+        prefs->set_string (key, val);
+        g_free (val);
+      }
+    }
+
+    GtkWidget* new_bytes_combo_box (Prefs& prefs,
+                                    const char * mode_key)
+      {
+
+        const char* strings[2][2] =
+        {
+          {N_("MB"), "mb"},
+          {N_("GB"), "gb"}
+        };
+
+        const std::string mode (prefs.get_string (mode_key, "mb"));
+        GtkListStore * store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
+
+        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);
+
+        gtk_widget_show_all(c);
+
+        return c;
+      }
+
+      void reset_dl_limit_cb (GtkButton *, gpointer user_data)
+      {
+        DLMeterDialog* pd (static_cast<DLMeterDialog*>(user_data));
+        pd->prefs().set_int ("dl-limit", 0);
+        pd->meter().dl_meter_reset ();
+      }
+
+  }
+
+  DLMeterDialog :: DLMeterDialog (Prefs& prefs, DownloadMeter& meter, GtkWindow* parent):
+    _prefs (prefs), _meter(meter)
+  {
+
+    GtkWidget * dialog = gtk_dialog_new_with_buttons (_("Pan: Download Meter Preferences"), parent,
+                                                      GTK_DIALOG_DESTROY_WITH_PARENT,
+                                                      GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
+                                                      NULL);
+    gtk_window_set_role (GTK_WINDOW(dialog), "pan-dl-preferences-dialog");
+    g_signal_connect (dialog, "response", G_CALLBACK(response_cb), this);
+    g_signal_connect_swapped (dialog, "destroy", G_CALLBACK(delete_dialog), this);
+
+    // Behavior
+    int row (0);
+    GtkWidget *h, *w, *l, *b, *t;
+    t = HIG :: workarea_create ();
+    HIG::workarea_add_section_title (t, &row, _("Download Limit reached"));
+
+      HIG :: workarea_add_section_spacer (t, row, 2);
+      w = new_check_button (_("Warn"), "warn-dl-limit-reached", true, prefs);
+      HIG :: workarea_add_wide_control (t, &row, w);
+      w = new_check_button (_("Disconnect from server"), "disconnect-on-dl-limit-reached", true, prefs);
+      HIG :: workarea_add_wide_control (t, &row, w);
+      w = _spin = new_spin_button ("dl-limit", 1, 1024, prefs);
+      HIG :: workarea_add_wide_control (t, &row, w);
+      w = new_bytes_combo_box(prefs, "dl-limit-type");
+      HIG :: workarea_add_wide_control (t, &row, w);
+      w = gtk_button_new_with_label (_("Reset"));
+      g_signal_connect (w, "clicked", G_CALLBACK(reset_dl_limit_cb), this);
+      HIG :: workarea_add_wide_control (t, &row, w);
+
+    HIG :: workarea_finish (t, &row);
+
+    gtk_widget_show_all(t);
+
+    gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area( GTK_DIALOG(dialog))), t, true, true, 0);
+
+    _root = dialog;
+
+  }
+
+}
diff --git a/pan/gui/dl-prefs.h b/pan/gui/dl-prefs.h
new file mode 100644
index 0000000..b642f5b
--- /dev/null
+++ b/pan/gui/dl-prefs.h
@@ -0,0 +1,61 @@
+/* -*- 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 DL_PREFS_UI_H
+#define DL_PREFS_UI_H
+
+#include "gtk-compat.h"
+#include <pan/gui/prefs.h>
+#include <pan/data/data.h>
+
+namespace pan
+{
+
+  class DLMeterDialog
+  {
+
+    public:
+
+    public:
+      DLMeterDialog (Prefs&, DownloadMeter&, GtkWindow*) ;
+      ~DLMeterDialog () { }
+
+      Prefs& prefs () { return _prefs; }
+
+      DownloadMeter& meter () { return _meter; }
+
+      GtkWidget* root() { return _root; }
+
+      uint64_t get_limit () { return _meter.dl_meter_get_limit(); }
+
+      GtkWidget* get_enable () { return _enable; }
+
+      GtkWidget* get_spin () { return _spin; }
+
+    private:
+      Prefs& _prefs;
+      GtkWidget* _root;
+      DownloadMeter& _meter;
+      GtkWidget* _enable;
+      GtkWidget* _spin;
+
+  };
+}
+
+#endif
diff --git a/pan/gui/gui.cc b/pan/gui/gui.cc
index 528a73b..b726a06 100644
--- a/pan/gui/gui.cc
+++ b/pan/gui/gui.cc
@@ -61,6 +61,7 @@ extern "C" {
 #endif
 
 #include "prefs-ui.h"
+#include "dl-prefs.h"
 #include "progress-view.h"
 #include "profiles-dialog.h"
 #include "post-ui.h"
@@ -178,6 +179,11 @@ void GUI :: show_task_window_cb (GtkWidget *, gpointer gui_gpointer)
   static_cast<GUI*>(gui_gpointer)->activate_action ("show-task-window");
 }
 
+void GUI :: show_download_meter_prefs_cb (GtkWidget *, gpointer gui_gpointer)
+{
+  static_cast<GUI*>(gui_gpointer)->activate_action ("show-dl-meter-prefs");
+}
+
 void
 GUI :: root_realized_cb (GtkWidget*, gpointer self_gpointer)
 {
@@ -207,7 +213,7 @@ GUI :: root_realized_cb (GtkWidget*, gpointer self_gpointer)
   }
 }
 
-GUI :: GUI (Data& data, Queue& queue, Prefs& prefs, GroupPrefs& group_prefs):
+GUI :: GUI (Data& data, Queue& queue, Prefs& prefs, GroupPrefs& group_prefs, DownloadMeter& meter):
   _data (data),
   _queue (queue),
   _prefs (prefs),
@@ -227,7 +233,8 @@ GUI :: GUI (Data& data, Queue& queue, Prefs& prefs, GroupPrefs& group_prefs):
   _queue_size_label (0),
   _queue_size_button (0),
   _taskbar (0),
-  _certstore(data.get_certstore())
+  _certstore(data.get_certstore()),
+  _meter(meter)
 {
 
   char * filename = g_build_filename (file::get_pan_home().c_str(), "pan.ui", NULL);
@@ -295,6 +302,16 @@ GUI :: GUI (Data& data, Queue& queue, Prefs& prefs, GroupPrefs& group_prefs):
   gtk_container_add (GTK_CONTAINER(frame), w);
   gtk_box_pack_start (GTK_BOX(status_bar), frame, FALSE, FALSE, 0);
 
+  // download meter
+  w = _meter.get_widget();
+  gtk_box_pack_start (GTK_BOX(status_bar), w, FALSE, FALSE, 0);
+  g_signal_connect (_meter.get_button(), "clicked", G_CALLBACK(show_download_meter_prefs_cb), this);
+
+  // drag and drop for message-ids
+  //  gtk_drag_dest_set(_workarea_bin,GTK_DEST_DEFAULT_ALL,target_list,3,GDK_ACTION_COPY);
+  //  gtk_drag_dest_add_text_targets(_workarea_bin);
+  //  gtk_drag_dest_add_uri_targets(_workarea_bin);
+
   // queue
   w = _queue_size_label = gtk_label_new (NULL);
   gtk_misc_set_padding (GTK_MISC(w), PAD, 0);
@@ -303,11 +320,6 @@ GUI :: GUI (Data& data, Queue& queue, Prefs& prefs, GroupPrefs& group_prefs):
   gtk_button_set_relief (GTK_BUTTON(w), GTK_RELIEF_NONE);
   g_signal_connect (w, "clicked", G_CALLBACK(show_task_window_cb), this);
 
-  // drag and drop for message-ids
-//  gtk_drag_dest_set(_workarea_bin,GTK_DEST_DEFAULT_ALL,target_list,3,GDK_ACTION_COPY);
-//  gtk_drag_dest_add_text_targets(_workarea_bin);
-//  gtk_drag_dest_add_uri_targets(_workarea_bin);
-
   gtk_container_add (GTK_CONTAINER(w), _queue_size_label);
   frame = gtk_frame_new (NULL);
   gtk_container_set_border_width (GTK_CONTAINER(frame), 0);
@@ -857,6 +869,13 @@ void GUI :: do_show_task_window ()
   toggle_visible (task_pane->root());
 }
 
+void GUI :: do_show_dl_meter_prefs()
+{
+  DLMeterDialog * dialog = new DLMeterDialog (_prefs, _meter, get_window(_root));
+  g_signal_connect (dialog->root(), "destroy", G_CALLBACK(prefs_dialog_destroyed_cb), this);
+  gtk_widget_show (dialog->root());
+}
+
 namespace
 {
   void set_bin_child (GtkWidget * w, GtkWidget * new_child)
@@ -1123,6 +1142,8 @@ void GUI :: prefs_dialog_destroyed (GtkWidget *)
   }
   _cache.set_max_megs(_prefs.get_int("cache-size-megs",10));
 
+  gtk_widget_set_visible(_meter.get_widget(), _prefs.get_flag("dl-meter-show", true));
+
 }
 
 
diff --git a/pan/gui/gui.h b/pan/gui/gui.h
index ff3cfd2..b5b9633 100644
--- a/pan/gui/gui.h
+++ b/pan/gui/gui.h
@@ -32,7 +32,9 @@
 #include <pan/gui/prefs.h>
 #include <pan/gui/group-prefs.h>
 #include <pan/gui/wait.h>
-//#include <pan/general/typedefs.h>
+#include <pan/data-impl/download-meter.h>
+
+#include <stdint.h>
 
 #include "gtk-compat.h"
 
@@ -63,7 +65,7 @@ namespace pan
 
       typedef std::vector<Quark> mid_sequence_t;
 
-      GUI (Data& data, Queue&, Prefs&, GroupPrefs&);
+      GUI (Data& data, Queue&, Prefs&, GroupPrefs&, DownloadMeter&);
       virtual ~GUI ();
       GtkWidget* root () { return _root; }
       typedef std::vector<std::string> strings_t;
@@ -106,6 +108,7 @@ namespace pan
       virtual void do_import_tasks ();
       virtual void do_cancel_latest_task ();
       virtual void do_show_task_window ();
+      virtual void do_show_dl_meter_prefs();
       virtual void do_show_log_window ();
       virtual void do_select_all_articles ();
       virtual void do_unselect_all_articles ();
@@ -239,6 +242,7 @@ namespace pan
       Prefs& _prefs;
       GroupPrefs& _group_prefs;
       CertStore& _certstore;
+      DownloadMeter& _meter;
 
     private:
       GtkWidget * _root;
@@ -260,9 +264,12 @@ namespace pan
       GtkWidget * _event_log_button;
       GtkWidget * _taskbar;
       std::vector<ProgressView*> _views;
+      ProgressView* _meter_view;
       std::list<Task*> _active_tasks;
       std::string _charset;
 
+      GtkWidget* _meter_button;
+
       void set_charset (const StringView& v);
 
       void upkeep ();
@@ -273,6 +280,7 @@ namespace pan
 
       static void show_event_log_cb (GtkWidget*, gpointer);
       static void show_task_window_cb (GtkWidget*, gpointer);
+      static void show_download_meter_prefs_cb (GtkWidget*, gpointer);
 
       void score_add (int);
 
@@ -283,6 +291,8 @@ namespace pan
     public:
       BodyPane* body_pane() { return _body_pane; }
 
+      GtkWidget* meter_button () { return _meter_button; }
+
     private:
       static void add_widget (GtkUIManager*, GtkWidget*, gpointer);
       static void server_list_dialog_destroyed_cb (GtkWidget*, gpointer);
diff --git a/pan/gui/pan-ui.h b/pan/gui/pan-ui.h
index fe33114..e9eca00 100644
--- a/pan/gui/pan-ui.h
+++ b/pan/gui/pan-ui.h
@@ -90,6 +90,7 @@ namespace pan
     virtual void do_quit () = 0;
 
     virtual void do_show_task_window () = 0;
+    virtual void do_show_dl_meter_prefs() = 0;
     virtual void do_show_log_window () = 0;
     virtual void do_show_preferences_dialog () = 0;
     virtual void do_show_group_preferences_dialog () = 0;
diff --git a/pan/gui/pan.cc b/pan/gui/pan.cc
index 5eaea8c..bcc12e5 100644
--- a/pan/gui/pan.cc
+++ b/pan/gui/pan.cc
@@ -967,9 +967,7 @@ main (int argc, char *argv[])
     // instantiate the queue...
     WorkerPool worker_pool (4, true);
     SocketCreator socket_creator(data, certstore);
-    Queue queue (data, data, &socket_creator, certstore, prefs, worker_pool, false, 32768);
-
-    data.set_queue (&queue);
+    Queue queue (data, data, data, data, &socket_creator, certstore, prefs, worker_pool, false, 32768);
 
 #ifdef HAVE_DBUS
     Pan pan(data, queue, cache, encode_cache, prefs, group_prefs);
@@ -1075,8 +1073,7 @@ main (int argc, char *argv[])
       gtk_window_set_resizable (GTK_WINDOW(window), true);
       gtk_window_set_default_icon (pixbuf);
 
-
-      gui_ptr = new GUI (data, queue, prefs, group_prefs);
+      gui_ptr = new GUI (data, queue, prefs, group_prefs, data);
 
 #ifdef HAVE_LIBNOTIFY
       if (!notify_is_initted ())
diff --git a/pan/gui/prefs-ui.cc b/pan/gui/prefs-ui.cc
index aaa2de3..812a858 100644
--- a/pan/gui/prefs-ui.cc
+++ b/pan/gui/prefs-ui.cc
@@ -271,7 +271,6 @@ namespace pan
 
   }
 
-
   void save_accels()
   {
 
@@ -995,6 +994,8 @@ PrefsDialog :: PrefsDialog (Prefs& prefs, GtkWindow* parent):
     HIG :: workarea_add_section_title (t, &row, _("Task Pane"));
     w = new_check_button (_("Show Task Pane info popups"), "show-taskpane-popups", true, prefs);
     HIG :: workarea_add_wide_control (t, &row, w);
+    w = new_check_button (_("Show Download Meter"), "dl-meter-show", true, prefs);
+    HIG :: workarea_add_wide_control (t, &row, w);
     HIG::workarea_add_section_divider (t, &row);
   HIG :: workarea_finish (t, &row);
   gtk_notebook_append_page (GTK_NOTEBOOK(notebook), t, new_label_with_icon(_("_Panes"), _("Panes"), icon_prefs_panes, prefs));
@@ -1290,24 +1291,15 @@ PrefsDialog :: PrefsDialog (Prefs& prefs, GtkWindow* parent):
 
   HIG :: workarea_finish (t, &row);
 
-  GtkWidget* scroll = gtk_scrolled_window_new (NULL, NULL);
-  gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(scroll), GTK_SHADOW_IN);
-  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroll),
-                                  GTK_POLICY_AUTOMATIC,
-                                  GTK_POLICY_AUTOMATIC);
-  gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW(scroll), t);
+  gtk_notebook_append_page (GTK_NOTEBOOK(notebook), t, new_label_with_icon(_("_Shortcuts"), _("Shortcuts"), icon_prefs_hotkeys, prefs));
 
-  gtk_widget_show_all (scroll);
-
-  gtk_notebook_append_page (GTK_NOTEBOOK(notebook), scroll, new_label_with_icon(_("_Shortcuts"), _("Shortcuts"), icon_prefs_hotkeys, prefs));
-
-  scroll = gtk_scrolled_window_new (NULL, NULL);
+  GtkWidget* scroll = gtk_scrolled_window_new (NULL, NULL);
   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroll),
                                   GTK_POLICY_AUTOMATIC,
-                                  GTK_POLICY_AUTOMATIC);
+                                   GTK_POLICY_AUTOMATIC);
   gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW(scroll), notebook);
 
-  GdkScreen* screen =gdk_screen_get_default ();
+  GdkScreen* screen = gdk_screen_get_default ();
   gtk_widget_set_size_request (scroll, gdk_screen_get_width(screen) - 400, gdk_screen_get_height(screen) - 200);
 
   gtk_widget_show_all (scroll);
diff --git a/pan/gui/prefs.cc b/pan/gui/prefs.cc
index 174f76b..5d21af7 100644
--- a/pan/gui/prefs.cc
+++ b/pan/gui/prefs.cc
@@ -164,6 +164,9 @@ Prefs :: to_string (int depth, std::string& setme) const
   foreach_const (ints_t, _ints, it)
     out << indent(depth) << "<int name='" << escaped(it->first) << "' value='" << it->second << "'/>\n";
 
+  foreach_const (longs_t, _longs, it)
+    out << indent(depth) << "<long name='" << escaped(it->first) << "' value='" << it->second << "'/>\n";
+
   foreach_const (strings_t, _strings, it)
     out << indent(depth) << "<string name='" << escaped(it->first) << "' value='" << escaped(it->second) << "'/>\n";
 
@@ -298,6 +301,26 @@ Prefs :: set_int (const StringView& key, int value)
 }
 
 /***
+****  LONG64
+***/
+
+uint64_t
+Prefs :: get_long64 (const StringView& key, uint64_t fallback) const
+{
+  if (!_ints.count (key))
+    _longs[key] = fallback;
+  return _longs[key];
+}
+
+void
+Prefs :: set_long64 (const StringView& key, uint64_t value)
+{
+  _longs[key] = value;
+  fire_long64_changed (key, value);
+}
+
+
+/***
 ****  STRINGS
 ***/
 
diff --git a/pan/gui/prefs.h b/pan/gui/prefs.h
index 26e178b..748834a 100644
--- a/pan/gui/prefs.h
+++ b/pan/gui/prefs.h
@@ -29,6 +29,10 @@
 #include <pan/gui/pan-colors.h>
 #include "gtk-compat.h"
 
+extern "C" {
+  #include <stdint.h>
+}
+
 namespace pan
 {
   /**
@@ -45,6 +49,7 @@ namespace pan
         virtual void on_prefs_string_changed (const StringView& key, const StringView& value) = 0;
         virtual void on_prefs_color_changed (const StringView& key, const GdkColor& color) = 0;
         virtual void on_prefs_hotkey_changed (const StringView& key, const StringView& value) {}
+        virtual void on_prefs_long64_changed(const StringView& key, const uint64_t& value) {}
       };
       void add_listener (Listener* l) { _listeners.insert(l); }
       void remove_listener (Listener* l) {_listeners.erase(l); }
@@ -62,6 +67,10 @@ namespace pan
         for (listeners_t::iterator it(_listeners.begin()), end(_listeners.end()); it!=end; )
           (*it++)->on_prefs_int_changed (key, value);
       }
+      void fire_long64_changed (const StringView& key, uint64_t value) {
+        for (listeners_t::iterator it(_listeners.begin()), end(_listeners.end()); it!=end; )
+          (*it++)->on_prefs_long64_changed (key, value);
+      }
       void fire_string_changed (const StringView& key, const StringView& value) {
         for (listeners_t::iterator it(_listeners.begin()), end(_listeners.end()); it!=end; )
           (*it++)->on_prefs_string_changed (key, value);
@@ -86,10 +95,14 @@ namespace pan
     public:
       bool get_flag (const StringView& key, bool fallback) const;
       void set_flag (const StringView& key, bool);
+
       int get_int (const StringView& key, int fallback) const;
       int get_int_min  (const StringView& key, int fallback) const;
       void set_int (const StringView& key, int);
 
+      uint64_t get_long64 (const StringView& key, uint64_t fallback) const;
+      void set_long64 (const StringView& key, uint64_t value);
+
       std::string get_string (const StringView& key, const StringView& fallback) const;
       void set_string (const StringView& key, const StringView&);
 
@@ -137,6 +150,8 @@ namespace pan
       mutable colors_t _colors;
       typedef std::map<std::string,int> ints_t;
       mutable ints_t _ints;
+      typedef std::map<std::string,uint64_t> longs_t;
+      mutable longs_t _longs;
 
     public:
       colors_t& get_colors() { return _colors; }
diff --git a/pan/gui/progress-view.cc b/pan/gui/progress-view.cc
index e7f745a..e6167c3 100644
--- a/pan/gui/progress-view.cc
+++ b/pan/gui/progress-view.cc
@@ -129,3 +129,19 @@ ProgressView :: update_text_soon ()
   if (!_progress_status_idle_tag)
        _progress_status_idle_tag = g_timeout_add (333, on_progress_status_idle, this);
 }
+
+void ProgressView :: set_color (const std::string& color)
+{
+  GtkStyle* style = gtk_style_new ();
+  gdk_color_parse (color.c_str(), &style->bg[GTK_STATE_PRELIGHT]);
+  gtk_widget_set_style (_progressbar, style);
+  g_object_unref (style);
+
+}
+
+void ProgressView :: reset_color ()
+{
+  GtkStyle* style = gtk_style_new ();
+  gtk_widget_set_style (_progressbar, style);
+  g_object_unref (style);
+}
diff --git a/pan/gui/progress-view.h b/pan/gui/progress-view.h
index 5449f90..e0debbb 100644
--- a/pan/gui/progress-view.h
+++ b/pan/gui/progress-view.h
@@ -43,6 +43,8 @@ namespace pan
       void set_progress (Progress *);
       Progress* get_progress () { return _progress; }
       const Progress* get_progress () const { return _progress; }
+      void set_color (const std::string& color);
+      void reset_color ();
 
     private: // inherited from progress listener
       virtual void on_progress_step (Progress&, int percentage);
@@ -63,6 +65,7 @@ namespace pan
       GtkWidget * _progressbar;
       std::string _last_status;
       Progress * _progress;
+
   };
 }
 
diff --git a/pan/tasks/nntp-pool.cc b/pan/tasks/nntp-pool.cc
index 17a7c09..4b06e4c 100644
--- a/pan/tasks/nntp-pool.cc
+++ b/pan/tasks/nntp-pool.cc
@@ -41,7 +41,8 @@ NNTP_Pool :: NNTP_Pool (const Quark        & server,
                         ServerInfo         & server_info,
                         Prefs              & prefs,
                         SocketCreator      * creator,
-                        CertStore          & store):
+                        CertStore          & store,
+                        DownloadMeter      & meter):
 
   _server_info (server_info),
   _prefs (prefs),
@@ -50,7 +51,8 @@ NNTP_Pool :: NNTP_Pool (const Quark        & server,
   _pending_connections (0),
   _active_count (0),
   _time_to_allow_new_connections (0),
-  _certstore(store)
+  _certstore(store),
+  _meter(meter)
 {
 }
 
@@ -191,11 +193,11 @@ NNTP_Pool :: on_socket_created (const StringView  & host,
     {
       std::string pw (pass ? pass : "");
       if (pass) g_free(pass);
-      nntp = new NNTP (_server, user, pw, socket);
+      nntp = new NNTP (_server, user, pw, _meter, socket);
     }
     else
     {
-      nntp = new NNTP (_server, user, pass, socket);
+      nntp = new NNTP ( _server, user, pass, _meter, socket);
     }
     nntp->handshake (this);
   }
diff --git a/pan/tasks/nntp-pool.h b/pan/tasks/nntp-pool.h
index 7399b72..6bd78b1 100644
--- a/pan/tasks/nntp-pool.h
+++ b/pan/tasks/nntp-pool.h
@@ -28,9 +28,10 @@
 #include <pan/tasks/nntp.h>
 #include <pan/tasks/socket-impl-main.h>
 #include <pan/gui/prefs.h>
+#include <pan/data/data.h>
 
 #ifdef HAVE_GNUTLS
-#include <pan/data/cert-store.h>
+  #include <pan/data/cert-store.h>
 #endif
 
 namespace pan {
@@ -48,7 +49,7 @@ class NNTP_Pool: public NNTP::Source,
 public:
 
 	NNTP_Pool(const Quark & server, ServerInfo & server_info, Prefs& prefs, SocketCreator *,
-			CertStore &);
+			CertStore &, DownloadMeter& meter);
 	virtual ~NNTP_Pool();
 
 	virtual void check_in(NNTP*, Health);
@@ -87,10 +88,8 @@ private:
 
 private:
 	// Socket::Creator::Listener
-	virtual void on_socket_created(const StringView& host, int port, bool ok,
-			Socket*);
-	virtual void on_socket_shutdown(const StringView& host, int port, Socket*) {
-	}
+	virtual void on_socket_created(const StringView& host, int port, bool ok,	Socket*);
+	virtual void on_socket_shutdown(const StringView& host, int port, Socket*) {}
 #ifdef HAVE_GNUTLS
 private:
 	// CertStore::Listener
@@ -116,6 +115,7 @@ private:
 	int _pending_connections;
 	CertStore& _certstore;
 	Prefs& _prefs;
+	DownloadMeter& _meter;
 
 	struct PoolItem {
 		NNTP * nntp;
diff --git a/pan/tasks/nntp.cc b/pan/tasks/nntp.cc
index fc6b560..cf426c4 100644
--- a/pan/tasks/nntp.cc
+++ b/pan/tasks/nntp.cc
@@ -265,6 +265,12 @@ NNTP :: on_socket_error (Socket * sock UNUSED)
    fire_done_func (ERR_NETWORK, StringView());
 }
 
+void
+NNTP :: on_socket_bytes_transferred (uint64_t bytes, Socket*)
+{
+   _meter.dl_meter_add (bytes);
+}
+
 namespace
 {
    void
diff --git a/pan/tasks/nntp.h b/pan/tasks/nntp.h
index c74ae06..95b3ea1 100644
--- a/pan/tasks/nntp.h
+++ b/pan/tasks/nntp.h
@@ -27,6 +27,7 @@
 #include <pan/general/string-view.h>
 #include <pan/tasks/health.h>
 #include <pan/tasks/socket.h>
+#include <pan/data/data.h>
 
 namespace
 {
@@ -137,7 +138,8 @@ namespace pan
                                     const Quark        & group         UNUSED,
                                     unsigned long        estimated_qty UNUSED,
                                     uint64_t             low           UNUSED,
-                                    uint64_t             high          UNUSED) {}
+                                    uint64_t             high
+                                           UNUSED) {}
 
        };
 
@@ -146,8 +148,10 @@ namespace pan
         NNTP (const Quark        & server,
               const std::string  & username,
               const std::string  & password,
-              Socket         * socket):
+              DownloadMeter       & meter,
+              Socket              * socket):
           _server(server),
+          _meter(meter),
           _socket(socket),
           _socket_error(false),
           _listener(0),
@@ -305,6 +309,7 @@ namespace pan
       Quark _group;
       Quark _request_group;
       Socket * _socket;
+      DownloadMeter& _meter;
       bool _socket_error;
 
     protected:
@@ -331,6 +336,7 @@ namespace pan
       virtual bool on_socket_response (Socket*, const StringView& line);
       virtual void on_socket_error (Socket*);
       virtual void on_socket_abort (Socket*);
+      virtual void on_socket_bytes_transferred (uint64_t bytes, Socket*) ;
 
     public:
 
diff --git a/pan/tasks/queue.cc b/pan/tasks/queue.cc
index cab893f..a9a59c8 100644
--- a/pan/tasks/queue.cc
+++ b/pan/tasks/queue.cc
@@ -35,6 +35,8 @@ using namespace pan;
 
 Queue :: Queue (ServerInfo         & server_info,
                 TaskArchive        & archive,
+                Data               & data,
+                DownloadMeter      & meter,
                 SocketCreator      * socket_creator,
                 CertStore          & certstore,
                 Prefs              & prefs,
@@ -54,11 +56,15 @@ Queue :: Queue (ServerInfo         & server_info,
   _needs_saving (false),
   _last_time_saved (0),
   _archive (archive),
+  _meter (meter),
   _certstore(certstore),
   _uploads_total(0),
   _downloads_total(0)
 {
 
+  data.set_queue(this);
+  meter.add_listener(this);
+
   tasks_t tasks;
   _archive.load_tasks (tasks);
   add_tasks (tasks, BOTTOM);
@@ -69,6 +75,7 @@ Queue :: Queue (ServerInfo         & server_info,
 Queue :: ~Queue ()
 {
   _tasks.remove_listener (this);
+  _meter.remove_listener(this);
 
   foreach (pools_t, _pools, it)
     delete it->second;
@@ -106,7 +113,7 @@ Queue :: get_pool (const Quark& servername)
   }
   else // have to build one
   {
-    pool = new NNTP_Pool (servername, _server_info, _prefs, _socket_creator, _certstore);
+    pool = new NNTP_Pool (servername, _server_info, _prefs, _socket_creator, _certstore, _meter);
     pool->add_listener (this);
     _pools[servername] = pool;
   }
@@ -979,3 +986,14 @@ Queue :: get_stats (unsigned long   & queued_count,
   }
 }
 
+void
+Queue :: on_dl_limit_reached ()
+{
+  set_online (false);
+}
+
+void
+Queue :: on_reset_xfer_bytes ()
+{
+  set_online (true);
+}
diff --git a/pan/tasks/queue.h b/pan/tasks/queue.h
index 04e3b8a..de3be6b 100644
--- a/pan/tasks/queue.h
+++ b/pan/tasks/queue.h
@@ -36,6 +36,7 @@
 #include <pan/tasks/task-weak-ordering.h>
 #include <pan/tasks/socket-impl-main.h>
 #include <pan/gui/prefs.h>
+#include <pan/data/data.h>
 
 #ifdef HAVE_GNUTLS
   #include <pan/data/cert-store.h>
@@ -72,10 +73,11 @@ namespace pan
     public Task::DecoderSource,
     public Task::EncoderSource,
     private NNTP_Pool::Listener,
-    private AdaptableSet<Task*, TaskWeakOrdering>::Listener
+    private AdaptableSet<Task*, TaskWeakOrdering>::Listener,
+    private DownloadMeter::Listener
   {
     public:
-      Queue (ServerInfo&, TaskArchive&, SocketCreator*, CertStore&, Prefs&, WorkerPool&,
+      Queue (ServerInfo&, TaskArchive&, Data&, DownloadMeter&, SocketCreator*, CertStore&, Prefs&, WorkerPool&,
              bool online, int save_delay_secs);
       virtual ~Queue ();
 
@@ -255,6 +257,7 @@ namespace pan
       int _uploads_total, _downloads_total;
       CertStore& _certstore;
       Prefs& _prefs;
+      DownloadMeter& _meter;
 
     private:
       typedef AdaptableSet<Task*, TaskWeakOrdering> TaskSet;
@@ -262,6 +265,12 @@ namespace pan
       virtual void on_set_items_added  (TaskSet&, TaskSet::items_t&, int index);
       virtual void on_set_item_removed (TaskSet&, Task*&, int index);
       virtual void on_set_item_moved   (TaskSet&, Task*&, int index, int old_index);
+
+    public:
+
+      virtual void on_xfer_bytes (uint64_t) {}
+      virtual void on_reset_xfer_bytes () ;
+      virtual void on_dl_limit_reached () ;
   };
 }
 
diff --git a/pan/tasks/socket-impl-gio.cc b/pan/tasks/socket-impl-gio.cc
index fb2eb36..69e7456 100644
--- a/pan/tasks/socket-impl-gio.cc
+++ b/pan/tasks/socket-impl-gio.cc
@@ -336,6 +336,7 @@ GIOChannelSocket :: do_read ()
       if (g_str_has_suffix (g->str, "\r\n"))
         g_string_truncate (g, g->len-2);
       more = _listener->on_socket_response (this, StringView (g->str, g->len));
+      _listener->on_socket_bytes_transferred(g->len, this);
     }
     else if (status == G_IO_STATUS_AGAIN)
     {
diff --git a/pan/tasks/socket-impl-openssl.cc b/pan/tasks/socket-impl-openssl.cc
index 5a15436..390b0f2 100644
--- a/pan/tasks/socket-impl-openssl.cc
+++ b/pan/tasks/socket-impl-openssl.cc
@@ -618,6 +618,7 @@ GIOChannelSocketGnuTLS :: do_read ()
       if (g_str_has_suffix (g->str, "\r\n"))
         g_string_truncate (g, g->len-2);
       more = _listener->on_socket_response (this, StringView (g->str, g->len));
+      _listener->on_socket_bytes_transferred(g->len, this);
     }
     else if (status == G_IO_STATUS_AGAIN)
     {
diff --git a/pan/tasks/socket.h b/pan/tasks/socket.h
index 6947229..af26a62 100644
--- a/pan/tasks/socket.h
+++ b/pan/tasks/socket.h
@@ -23,6 +23,10 @@
 #include <string>
 #include <config.h>
 
+extern "C" {
+  #include <stdint.h>
+}
+
 #ifdef HAVE_GNUTLS
   #include <gnutls/gnutls.h>
 #endif
@@ -30,6 +34,7 @@
 namespace pan
 {
   class StringView;
+  class Quark;
   class WorkerPool;
   class Data;
 
@@ -49,9 +54,11 @@ namespace pan
       /** Interface class for objects that listen to a Socket's events */
       struct Listener {
         virtual ~Listener () {}
+
         virtual bool on_socket_response (Socket*, const StringView& line) = 0;
         virtual void on_socket_error (Socket*) = 0;
         virtual void on_socket_abort (Socket*) = 0;
+        virtual void on_socket_bytes_transferred (uint64_t bytes, Socket*) = 0;
       };
 
     public:
@@ -90,7 +97,7 @@ namespace pan
           virtual ~Listener () {}
           virtual void on_socket_created (const StringView& host, int port, bool ok, Socket*) = 0;
           virtual void on_socket_shutdown (const StringView& host, int port, Socket*) = 0;
-        };
+      };
 
         virtual ~Creator () { }
         virtual void create_socket (Data&, const StringView& host, int port, WorkerPool&, Listener*, bool) = 0;



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