[pan2: 41/268] playing with gtk notebook and stuff... * file upload queue now with TaskUpload* * perhaps this could



commit b066f150fb84a168354b63b0c7cfe1248f404521
Author: Heinrich MÃller <sphemuel stud informatik uni-erlangen de>
Date:   Sun May 29 14:09:51 2011 +0200

    playing with gtk notebook and stuff...
    * file upload queue now with TaskUpload*
    * perhaps this could be improved with different STL containers...
    * upload works flawlessly

 pan/data/Makefile.am              |    6 +-
 pan/data/file-queue.cc            |  118 ---------
 pan/data/file-queue.h             |   93 -------
 pan/gui/group-pane.cc             |    2 +-
 pan/gui/gui.h                     |    1 -
 pan/gui/pan.cc                    |    9 +-
 pan/gui/post-ui.cc                |  491 +++++++++++++++++++++++--------------
 pan/gui/post-ui.h                 |   32 ++-
 pan/tasks/encoder.cc              |   39 ++--
 pan/tasks/encoder.h               |    9 +-
 pan/tasks/nzb.cc                  |  187 +++++++++++----
 pan/tasks/nzb.h                   |    6 +-
 pan/tasks/queue.cc                |    3 +
 pan/tasks/task-upload.cc          |  107 +++++---
 pan/tasks/task-upload.h           |   53 +++--
 pan/usenet-utils/message-check.cc |   26 ++-
 pan/usenet-utils/message-check.h  |    3 +-
 17 files changed, 625 insertions(+), 560 deletions(-)
---
diff --git a/pan/data/Makefile.am b/pan/data/Makefile.am
index e899618..7c0f654 100644
--- a/pan/data/Makefile.am
+++ b/pan/data/Makefile.am
@@ -9,8 +9,7 @@ libdata_a_SOURCES = \
  article-cache.cc \
  data.cc \
  parts.cc \
- xref.cc \
- file-queue.cc
+ xref.cc
 
 noinst_HEADERS = \
  article.h \
@@ -19,8 +18,7 @@ noinst_HEADERS = \
  defgroup.h \
  parts.h \
  server-info.h \
- xref.h \
- file-queue.h
+ xref.h
 
 noinst_PROGRAMS = \
  article-test \
diff --git a/pan/gui/group-pane.cc b/pan/gui/group-pane.cc
index a7e55e2..fe51921 100644
--- a/pan/gui/group-pane.cc
+++ b/pan/gui/group-pane.cc
@@ -148,7 +148,7 @@ void
 GroupPane ::  do_popup_menu (GtkWidget*, GdkEventButton *event, gpointer pane_g)
 {
   GroupPane * self (static_cast<GroupPane*>(pane_g));
-  GtkWidget * menu (self->_action_manager.get_action_widget ("/filequeue-popup"));
+  GtkWidget * menu (self->_action_manager.get_action_widget ("/group-pane-popup"));
   gtk_menu_popup (GTK_MENU(menu), NULL, NULL, NULL, NULL,
                   (event ? event->button : 0),
                   (event ? event->time : 0));
diff --git a/pan/gui/gui.h b/pan/gui/gui.h
index 64eb6e9..4706ed3 100644
--- a/pan/gui/gui.h
+++ b/pan/gui/gui.h
@@ -22,7 +22,6 @@
 #include <pan/general/log.h>
 #include <pan/general/progress.h>
 #include <pan/data/article-cache.h>
-#include <pan/data/file-queue.h>
 #include <pan/tasks/queue.h>
 #include <pan/gui/group-pane.h>
 #include <pan/gui/action-manager.h>
diff --git a/pan/gui/pan.cc b/pan/gui/pan.cc
index 5268ef7..4c31ff8 100644
--- a/pan/gui/pan.cc
+++ b/pan/gui/pan.cc
@@ -36,6 +36,7 @@ extern "C" {
 #include <pan/tasks/socket-impl-gio.h>
 #include <pan/tasks/task-groups.h>
 #include <pan/tasks/task-xover.h>
+#include <pan/tasks/task-xzver-test.h>
 #include <pan/tasks/nzb.h>
 #include <pan/data-impl/data-impl.h>
 #include <pan/icons/pan-pixbufs.h>
@@ -113,8 +114,10 @@ namespace
     DataAndQueue * foo (static_cast<DataAndQueue*>(user_data));
     const quarks_t new_servers (foo->data->get_servers());
     foreach_const (quarks_t, new_servers, it)
-      if (foo->data->get_server_limits(*it))
+      if (foo->data->get_server_limits(*it)) {
+        foo->queue->add_task (new TaskXZVerTest(*foo->data, *it));
         foo->queue->add_task (new TaskGroups (*foo->data, *it));
+      }
     g_free (foo);
   }
 
@@ -175,7 +178,7 @@ namespace
     ~PanKiller() { q.remove_listener(this); }
 
     /** Method from Queue::Listener interface: quits program on zero sized Q*/
-    void on_queue_size_changed (Queue&, int active, int total) 
+    void on_queue_size_changed (Queue&, int active, int total)
       {  if (!active && !total) mainloop_quit();  }
 
     // all below methods from Queue::Listener interface are noops
@@ -278,7 +281,7 @@ main (int argc, char *argv[])
       nzb = true;
     else if (!strcmp (tok, "--version"))
       { std::cerr << "Pan " << VERSION << '\n'; return 0; }
-    else if (!strcmp (tok, "-o") && i<argc-1) 
+    else if (!strcmp (tok, "-o") && i<argc-1)
       nzb_output_path = argv[++i];
     else if (!memcmp (tok, "--output=", 9))
       nzb_output_path = tok+9;
diff --git a/pan/gui/post-ui.cc b/pan/gui/post-ui.cc
index d778284..4a9300c 100644
--- a/pan/gui/post-ui.cc
+++ b/pan/gui/post-ui.cc
@@ -221,7 +221,7 @@ namespace
     { "add-files", GTK_STOCK_OPEN, N_("Add _Files to Queue"), "<control>O", N_("Add Files to Queue"), G_CALLBACK(do_add_files) },
   };
 
-  void do_remove_files       (GtkAction*, gpointer p) { static_cast<PostUI*>(p)->remove_files(); }
+  void do_remove_files       (GtkAction*, gpointer p) {static_cast<PostUI*>(p)->remove_files(); }
   void do_clear_list         (GtkAction*, gpointer p) { static_cast<PostUI*>(p)->clear_list(); }
   void do_select_parts       (GtkAction*, gpointer p) { static_cast<PostUI*>(p)->select_parts(); }
   void do_move_up            (GtkAction*, gpointer p) { static_cast<PostUI*>(p)->move_up(); }
@@ -433,6 +433,12 @@ namespace
     static_cast<PostUI*>(user_data)->close_window ();
     return true; // don't invoke the default handler that destroys the widget
   }
+
+   gboolean delete_parts_cb (GtkWidget* w, GdkEvent*, gpointer user_data)
+  {
+    return false;
+  }
+
 }
 
 void
@@ -478,7 +484,7 @@ PostUI :: check_message (const Quark& server, GMimeMessage * msg)
 
   quarks_t groups_this_server_has;
   _gs.server_get_groups (server, groups_this_server_has);
-  MessageCheck :: message_check (msg, _hidden_headers["X-Draft-Attribution"], groups_this_server_has, errors, goodness);
+  MessageCheck :: message_check (msg, _hidden_headers["X-Draft-Attribution"], groups_this_server_has, errors, goodness, !_file_queue_empty);
 
   if (goodness.is_ok())
     return true;
@@ -580,7 +586,7 @@ PostUI :: add_files ()
 {
   if (!check_charset())
     return;
-  prompt_user_for_queueable_files (_file_queue, GTK_WINDOW (gtk_widget_get_toplevel(_root)), _prefs);
+  prompt_user_for_queueable_files (_file_queue_tasks, GTK_WINDOW (gtk_widget_get_toplevel(_root)), _prefs);
 }
 
 void
@@ -593,17 +599,6 @@ PostUI :: send_now ()
     g_object_unref (G_OBJECT(message));
 }
 
-//void
-//PostUI :: send_binfiles_now()
-//{
-//  if (!check_charset())
-//    return;
-//  GMimeMessage * message (new_message_from_ui (POSTING));
-//
-//  //change headers according to
-//  g_object_unref (G_OBJECT(message));
-//}
-
 void
 PostUI :: done_sending_message (GMimeMessage * message, bool ok)
 {
@@ -808,23 +803,12 @@ PostUI :: maybe_post_message (GMimeMessage * message)
     _post_task = new TaskPost (server, message);
     _post_task->add_listener (this);
     _queue.add_task (_post_task, Queue::TOP);
-  }
-  else
-  {
-    FileQueue::articles_it it = _file_queue.begin();
-    GMimeMessage* msg = new_message_from_ui(POSTING);
-    std::string groups = std::string(g_mime_object_get_header ((GMimeObject*)msg, "Newsgroups"));
-    std::string subject= std::string(g_mime_object_get_header ((GMimeObject*)msg, "Subject"));
-    std::string author;
-    profile.get_from_header (author);
-
-    for (; it != _file_queue.end(); ++it) {
-      _queue.add_task (new TaskUpload (*it,profile.posting_server,groups,subject,author), Queue::BOTTOM);
-    }
-    close_window(true); // dont wait for the upload queue
+  } else {
+     foreach_const (tasks_set, _file_queue_tasks, it)
+        _queue.add_task (*it, Queue::BOTTOM);
+     close_window(true); // dont wait for the upload queue
   }
 
-
   /**
   ***  Maybe remember the charsets
   **/
@@ -1039,7 +1023,6 @@ PostUI :: open_draft ()
       g_object_unref (G_OBJECT(parser));
       g_object_unref (G_OBJECT(stream));
     }
-
   }
   gtk_widget_destroy (d);
 }
@@ -1913,7 +1896,6 @@ PostUI :: create_main_tab ()
   // Newsgroup
 
   ++row;
-  std::cerr<<" create main tab\n";
   g_snprintf (buf, sizeof(buf), "<b>%s:</b>", _("_Newsgroups"));
   l = gtk_label_new_with_mnemonic (buf);
   gtk_label_set_use_markup (GTK_LABEL(l), true);
@@ -1963,6 +1945,199 @@ namespace
   }
 }
 
+namespace
+{
+  GtkWidget * add_button (GtkWidget   * box,
+                                      const gchar * stock_id,
+                                      GCallback     callback,
+                                      gpointer      user_data)
+  {
+    GtkWidget * w = gtk_button_new_from_stock (stock_id);
+    gtk_button_set_relief (GTK_BUTTON(w), GTK_RELIEF_NONE);
+    if (callback)
+      g_signal_connect (w, "clicked", callback, user_data);
+    gtk_box_pack_start (GTK_BOX(box), w, false, false, 0);
+    return w;
+  }
+
+  void
+  render_filename (GtkTreeViewColumn * ,
+                   GtkCellRenderer   * renderer,
+                   GtkTreeModel      * model,
+                   GtkTreeIter       * iter,
+                   gpointer)
+  {
+
+    TaskUpload* fd(0);
+    gtk_tree_model_get (model, iter, 0, &fd, -1);
+    if (fd)
+      g_object_set (renderer, "text", fd->basename().c_str(), NULL);
+  }
+
+}
+
+GtkWidget*
+PostUI :: create_filequeue_tab ()
+{
+  GtkWidget *w ;
+  GtkListStore *list_store;
+  GtkTreeIter   iter;
+  GtkCellRenderer *renderer;
+  GtkWidget * vbox = gtk_vbox_new (false, 0);
+  GtkWidget * buttons = gtk_hbox_new (false, PAD_SMALL);
+
+  // add button row
+  add_button (buttons, GTK_STOCK_GO_UP, G_CALLBACK(up_clicked_cb), this);
+  add_button (buttons, GTK_STOCK_GOTO_TOP, G_CALLBACK(top_clicked_cb), this);
+  gtk_box_pack_start (GTK_BOX(buttons), gtk_vseparator_new(), 0, 0, 0);
+  add_button (buttons, GTK_STOCK_GO_DOWN, G_CALLBACK(down_clicked_cb), this);
+  add_button (buttons, GTK_STOCK_GOTO_BOTTOM, G_CALLBACK(bottom_clicked_cb), this);
+  gtk_box_pack_start (GTK_BOX(buttons), gtk_vseparator_new(), 0, 0, 0);
+  w = add_button (buttons, GTK_STOCK_DELETE, G_CALLBACK(delete_clicked_cb), this);
+  gtk_widget_set_tooltip_text( w, _("Delete from Queue"));
+  pan_box_pack_start_defaults (GTK_BOX(buttons), gtk_event_box_new());
+
+  gtk_box_pack_start (GTK_BOX(vbox), buttons, false, false, 0);
+  gtk_box_pack_start (GTK_BOX(vbox), gtk_hseparator_new(), false, false, 0);
+
+  list_store = gtk_list_store_new (2, G_TYPE_POINTER, G_TYPE_UINT);
+  w = _filequeue_store = gtk_tree_view_new_with_model (GTK_TREE_MODEL(list_store));
+
+  // add columns
+  renderer = gtk_cell_renderer_text_new ();
+  gtk_tree_view_insert_column_with_data_func(
+            GTK_TREE_VIEW(w), 0, (_("Filename")), renderer,
+            render_filename, 0, 0);
+  renderer = gtk_cell_renderer_text_new ();
+  gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (w), 1,
+                          (_("Size (kB)")),renderer,"text", 1,NULL);
+
+  // connect signals for popup menu
+  g_signal_connect (w, "popup-menu", G_CALLBACK(on_popup_menu), this);
+  g_signal_connect (w, "button-press-event", G_CALLBACK(on_button_pressed), this);
+
+  //set hint and selection
+  gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(w),TRUE);
+  GtkTreeSelection * selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (w));
+  gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
+  gtk_tree_view_columns_autosize(GTK_TREE_VIEW(w));
+
+  //append scroll window
+  w = gtk_scrolled_window_new (NULL, NULL);
+  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(w), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+  gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(w), GTK_SHADOW_IN);
+  gtk_container_add (GTK_CONTAINER(w), _filequeue_store);
+  gtk_box_pack_start (GTK_BOX(vbox), w, true, true, 0);
+
+
+  return vbox;
+}
+
+GtkWidget*
+PostUI :: create_parts_tab (TaskUpload* ptr)
+{
+
+  const GtkAttachOptions fill ((GtkAttachOptions)(GTK_FILL));
+  const GtkAttachOptions fe ((GtkAttachOptions)(GTK_FILL|GTK_EXPAND|GTK_SHRINK));
+
+  GtkWidget *w, *l, *store ;
+  char buf[512];
+  int row = -1;
+  GtkCellRenderer * renderer;
+  GtkWidget *t = gtk_table_new (8, 2, false);
+  gtk_table_set_col_spacings (GTK_TABLE(t), PAD);
+
+  ++row;  //empty line
+  l = gtk_label_new (NULL);
+  gtk_table_attach (GTK_TABLE(t), l, 0, 2, row, row+1, fe, fill, 0, 0);
+
+  ++row;  //1
+  g_snprintf (buf, sizeof(buf), "<b>%s:</b>", _("Filename"));
+  l = gtk_label_new (buf);
+  gtk_label_set_use_markup (GTK_LABEL(l), true);
+  gtk_misc_set_alignment (GTK_MISC(l), 0.0f, 0.5f);
+  gtk_table_attach (GTK_TABLE(t), l, 0, 1, row, row+1, GTK_FILL, GTK_FILL, 0, 0);
+  g_snprintf (buf, sizeof(buf), "%s", ptr->basename().c_str());
+  l = gtk_label_new (buf);
+  gtk_misc_set_alignment (GTK_MISC(l), 0.5f, 0.5f);
+  gtk_widget_set_tooltip_text (l, _("The current filename"));
+  gtk_table_attach (GTK_TABLE(t), l, 1, 2, row, row+1, fe, fill, 0, 0);
+
+  ++row;  //2
+  g_snprintf (buf, sizeof(buf), "<b>%s:</b>", _("Subject Line"));
+  l = gtk_label_new (buf);
+  gtk_label_set_use_markup (GTK_LABEL(l), true);
+  gtk_misc_set_alignment (GTK_MISC(l), 0.0f, 0.5f);
+  gtk_table_attach (GTK_TABLE(t), l, 0, 1, row, row+1, GTK_FILL, GTK_FILL, 0, 0);
+  g_snprintf (buf, sizeof(buf), "%s", ptr->subject().c_str());
+  l = gtk_label_new (buf);
+  gtk_misc_set_alignment (GTK_MISC(l), 0.5f, 0.5f);
+  gtk_widget_set_tooltip_text (l, _("The current Subject Line"));
+  gtk_table_attach (GTK_TABLE(t), l, 1, 2, row, row+1, fe, fill, 0, 0);
+
+  ++row;  //3
+  g_snprintf (buf, sizeof(buf), "<b>%s:</b>", _("Lines / File"));
+  l = gtk_label_new (buf);
+  gtk_label_set_use_markup (GTK_LABEL(l), true);
+  gtk_misc_set_alignment (GTK_MISC(l), 0.0f, 0.5f);
+  gtk_table_attach (GTK_TABLE(t), l, 0, 1, row, row+1, GTK_FILL, GTK_FILL, 0, 0);
+  //todo make generic!!
+  g_snprintf (buf, sizeof(buf), "%d", 4000);
+  l = gtk_label_new_with_mnemonic (buf);
+  gtk_misc_set_alignment (GTK_MISC(l), 0.5f, 0.5f);
+  gtk_widget_set_tooltip_text (l, _("The current Number of Lines per File"));
+  gtk_table_attach (GTK_TABLE(t), l, 1, 2, row, row+1, fe, fill, 0, 0);
+
+  //4
+  ++row;
+  l = gtk_label_new (NULL);
+  gtk_table_attach (GTK_TABLE(t), l, 0, 2, row, row+1, fe, fill, 0, 0);
+
+
+  //5
+  ++row;
+  gtk_table_attach (GTK_TABLE(gtk_hseparator_new()), l, 0, 2, row, row+1, fe, fill, 0, 0);
+
+
+  // 6
+  //treeview for parts list
+  w = _parts_store = gtk_tree_view_new_with_model (GTK_TREE_MODEL(gtk_list_store_new (2, G_TYPE_BOOLEAN, G_TYPE_STRING)));
+
+  // add columns
+  renderer = gtk_cell_renderer_toggle_new();
+  gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (w), 0,
+                          (_("Part No.")),renderer,"active", 1,NULL);
+  renderer = gtk_cell_renderer_text_new ();
+  gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (w), 1,
+                          (_("Filename")),renderer,"text", 1,NULL);
+
+  //set hint and selection
+  gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(w),TRUE);
+  gtk_tree_view_columns_autosize(GTK_TREE_VIEW(w));
+
+  //append scroll window
+  w = gtk_scrolled_window_new (NULL, NULL);
+  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(w), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+  gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(w), GTK_SHADOW_IN);
+  gtk_container_add (GTK_CONTAINER(w), _parts_store);
+  ++row;                                              //todo
+  gtk_table_attach (GTK_TABLE(t), w, 0, 2, row, row+2, fe, fill, 0, 0);
+
+  //7: OK and close Buttons
+  ++row;
+
+  return t;
+
+}
+
+GtkWidget*
+PostUI :: create_log_tab ()
+{
+  GtkWidget *w;
+
+  return w;
+}
+
 GtkWidget*
 PostUI :: create_extras_tab ()
 {
@@ -2050,106 +2225,64 @@ gtk_widget_set_tooltip_text (w, _("The email account where mail replies to your
   return t;
 }
 
-namespace
-{
-  GtkWidget * add_button (GtkWidget   * box,
-                                      const gchar * stock_id,
-                                      GCallback     callback,
-                                      gpointer      user_data)
-  {
-    GtkWidget * w = gtk_button_new_from_stock (stock_id);
-    gtk_button_set_relief (GTK_BUTTON(w), GTK_RELIEF_NONE);
-    if (callback)
-      g_signal_connect (w, "clicked", callback, user_data);
-    gtk_box_pack_start (GTK_BOX(box), w, false, false, 0);
-    return w;
-  }
-}
-
 void
 PostUI :: get_selected_files_foreach (GtkTreeModel *model, GtkTreePath *, GtkTreeIter *iter, gpointer list_g)
 {
-  FileQueue::FileData* file (0);
+  TaskUpload* file(0);
   gtk_tree_model_get (model, iter, 0, &file, -1);
-  static_cast<FileQueue*>(list_g)->add (file->filename.c_str(), FileQueue::END);
+  static_cast<tasks_set*>(list_g)->insert (file);
 }
 
-FileQueue::articles_v&
+tasks_set
 PostUI :: get_selected_files () const
 {
-  FileQueue files;
+  tasks_set tasks;
   GtkTreeView * view (GTK_TREE_VIEW (_filequeue_store));
   GtkTreeSelection * sel (gtk_tree_view_get_selection (view));
-  gtk_tree_selection_selected_foreach (sel, get_selected_files_foreach, &files);
-  return files.get_files();
-}
-
-int
-PostUI :: get_top()
-{
-  int pos(0);
-
-  return pos;
-}
-
-int
-PostUI :: get_bottom()
-{
-  int pos(0);
-
-
-  return pos;
+  gtk_tree_selection_selected_foreach (sel, get_selected_files_foreach, &tasks);
+  return tasks;
 }
 
 void
-PostUI :: remove_files(void)
+PostUI :: remove_files (void)
 {
-  _file_queue.remove(get_selected_files());
+  tasks_set tasks = get_selected_files();
+  tasks_set::iterator nit;
+  for (nit = tasks.begin(); nit != tasks.end();nit, ++nit)
+    _file_queue_tasks.erase(*nit);
   update_filequeue_tab();
 }
 
-void
-PostUI :: move_up(void)
-{
-  _file_queue.move_up(get_selected_files(),get_top());
-  update_filequeue_tab();
-}
 
 void
-PostUI :: move_down(void)
+PostUI :: move_up (void)
 {
-  _file_queue.move_down(get_selected_files(),get_bottom());
-  update_filequeue_tab();
+
 }
 
 void
-PostUI :: move_top(void)
+PostUI :: move_down (void)
 {
-  _file_queue.move_top(get_selected_files());
-  update_filequeue_tab();
+
 }
 
 void
-PostUI :: move_bottom(void)
+PostUI :: move_top (void)
 {
-  _file_queue.move_bottom(get_selected_files());
-  update_filequeue_tab();
+
 }
 
 void
-PostUI :: select_parts (void)
+PostUI :: move_bottom (void)
 {
-  //get parts from dialog result
-  //_file_queue.remove_parts(_parts_dialog->selection);
-  update_filequeue_tab();
+
 }
 
 void
 PostUI :: clear_list (void)
 {
-  _file_queue.clear();
-    update_filequeue_tab();
-
+  _file_queue_tasks.clear();
+  update_filequeue_tab();
 }
 
 void PostUI :: up_clicked_cb (GtkButton*, PostUI* pane)
@@ -2173,82 +2306,6 @@ void PostUI :: delete_clicked_cb (GtkButton*, PostUI* pane)
   pane->remove_files ();
 }
 
-namespace
-{
-  void
-  render_filename (GtkTreeViewColumn * ,
-                   GtkCellRenderer   * renderer,
-                   GtkTreeModel      * model,
-                   GtkTreeIter       * iter,
-                   gpointer)
-  {
-
-    FileQueue::FileData* fd;
-    unsigned int size;
-    gtk_tree_model_get (model, iter, 0, &fd, 1, &size, -1);
-    if (fd)
-      g_object_set (renderer, "text", fd->basename.c_str(),
-                    "weight", fd->all_parts ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_LIGHT, NULL);
-  }
-}
-
-GtkWidget*
-PostUI :: create_filequeue_tab ()
-{
-  GtkWidget *w ;
-  GtkListStore *list_store;
-  GtkTreeIter   iter;
-  GtkCellRenderer *renderer;
-  GtkWidget * vbox = gtk_vbox_new (false, 0);
-  GtkWidget * buttons = gtk_hbox_new (false, PAD_SMALL);
-
-  // add button row
-  add_button (buttons, GTK_STOCK_GO_UP, G_CALLBACK(up_clicked_cb), this);
-  add_button (buttons, GTK_STOCK_GOTO_TOP, G_CALLBACK(top_clicked_cb), this);
-  gtk_box_pack_start (GTK_BOX(buttons), gtk_vseparator_new(), 0, 0, 0);
-  add_button (buttons, GTK_STOCK_GO_DOWN, G_CALLBACK(down_clicked_cb), this);
-  add_button (buttons, GTK_STOCK_GOTO_BOTTOM, G_CALLBACK(bottom_clicked_cb), this);
-  gtk_box_pack_start (GTK_BOX(buttons), gtk_vseparator_new(), 0, 0, 0);
-  w = add_button (buttons, GTK_STOCK_DELETE, G_CALLBACK(delete_clicked_cb), this);
-  gtk_widget_set_tooltip_text( w, _("Delete from Queue"));
-  pan_box_pack_start_defaults (GTK_BOX(buttons), gtk_event_box_new());
-
-  gtk_box_pack_start (GTK_BOX(vbox), buttons, false, false, 0);
-  gtk_box_pack_start (GTK_BOX(vbox), gtk_hseparator_new(), false, false, 0);
-
-  list_store = gtk_list_store_new (2, G_TYPE_POINTER, G_TYPE_UINT);
-  w = _filequeue_store = gtk_tree_view_new_with_model (GTK_TREE_MODEL(list_store));
-
-  // add columns
-   renderer = gtk_cell_renderer_text_new ();
-  gtk_tree_view_insert_column_with_data_func(
-            GTK_TREE_VIEW(w), 0, (_("Filename")), renderer,
-            render_filename, 0, 0);
-  renderer = gtk_cell_renderer_text_new ();
-  gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (w), 1,
-                          (_("Size (kB)")),renderer,"text", 1,NULL);
-
-
-  // connect signals for popup menu
-  g_signal_connect (w, "popup-menu", G_CALLBACK(on_popup_menu), this);
-  g_signal_connect (w, "button-press-event", G_CALLBACK(on_button_pressed), this);
-
-  //set hint and selection
-  gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(w),TRUE);
-  GtkTreeSelection * selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (w));
-  gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
-
-  //append scroll window
-  w = gtk_scrolled_window_new (NULL, NULL);
-  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(w), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
-  gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(w), GTK_SHADOW_IN);
-  gtk_container_add (GTK_CONTAINER(w), _filequeue_store);
-  gtk_box_pack_start (GTK_BOX(vbox), w, true, true, 0);
-
-
-  return vbox;
-}
-
 void
 PostUI :: update_filequeue_tab()
 {
@@ -2259,20 +2316,18 @@ PostUI :: update_filequeue_tab()
 
   gtk_list_store_clear(store);
 
-  FileQueue::articles_it it = _file_queue.begin();
+  tasks_set::iterator it = _file_queue_tasks.begin();
 
   int i(0);
-  FileQueue::FileData* fd(0);
-  for (; it != _file_queue.end(); ++it, ++i )
+  for (; it != _file_queue_tasks.end(); ++it )
   {
-    fd = &(*it);
     gtk_list_store_append (store, &iter);
     gtk_list_store_set (store, &iter,
-                      0, fd,
-                      1, (*it).byte_count/1024,
+                      0, ((TaskUpload*)*it),
+                      1, ((TaskUpload*)*it)->get_byte_count()/1024,
                       -1);
   }
-  _file_queue_empty = (i == 0);
+  _file_queue_empty = (_file_queue_tasks.empty());
 }
 
 
@@ -2284,6 +2339,54 @@ PostUI :: ~PostUI ()
   g_object_unref (G_OBJECT(_message));
 }
 
+void
+PostUI :: select_parts ()
+{
+
+  TaskUpload* ptr(0);
+  tasks_set set(get_selected_files());
+  ptr = *set.begin();
+  GtkWidget * w;
+  GtkTreeIter iter;
+  const int total = (int) (((long)ptr->get_byte_count() + (4000*128-1)) / (4000*128));
+
+  std::cerr<<total<<std::endl;
+
+  w = _part_select = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+  g_signal_connect (_part_select, "delete-event", G_CALLBACK(delete_parts_cb), this);
+  gtk_window_set_role (GTK_WINDOW(w), "pan-parts-window");
+  gtk_window_set_title (GTK_WINDOW(w), _("Select Parts"));
+  int x,h;
+  x = 350;
+  h = 450;
+  gtk_window_set_default_size (GTK_WINDOW(w), x, h);
+
+  // populate the window
+  GtkWidget * vbox = gtk_vbox_new (false, PAD_SMALL);
+  gtk_container_add (GTK_CONTAINER(w), vbox);
+
+  GtkWidget * notebook = gtk_notebook_new ();
+  gtk_notebook_append_page (GTK_NOTEBOOK(notebook), create_parts_tab(ptr), gtk_label_new_with_mnemonic(_("_Parts")));
+  gtk_notebook_append_page (GTK_NOTEBOOK(notebook), create_log_tab(),   gtk_label_new_with_mnemonic(_("_Log")));
+  pan_box_pack_start_defaults (GTK_BOX(vbox), notebook);
+
+  //populate parts tab
+  GtkListStore *store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(_parts_store)));
+  for (int i=1;i<=total+100;++i)
+  {
+    gtk_list_store_append (store, &iter);
+    gtk_list_store_set (store, &iter,
+                      0, true,
+                      1, ptr->basename().c_str(), -1);
+  }
+
+    gtk_widget_show_all (w);
+
+
+//  update_parts_tab();
+
+}
+
 PostUI :: PostUI (GtkWindow    * parent,
                   Data         & data,
                   Queue        & queue,
@@ -2311,7 +2414,6 @@ PostUI :: PostUI (GtkWindow    * parent,
   _charset (DEFAULT_CHARSET),
   _group_entry_changed_id (0),
   _group_entry_changed_idle_tag (0),
-  //binpost
   _file_queue_empty(true)
 {
   g_assert (profiles.has_profiles());
@@ -2390,8 +2492,16 @@ PostUI :: create_window (GtkWidget    * parent,
 }
 
 void
-PostUI :: prompt_user_for_queueable_files (FileQueue& queue, GtkWindow * parent, const Prefs& prefs)
+PostUI :: prompt_user_for_queueable_files (tasks_set& queue, GtkWindow * parent, const Prefs& prefs)
 {
+  const Profile profile (get_current_profile ());
+  GMimeMessage * message (new_message_from_ui (POSTING));
+  if (!check_message(profile.posting_server, message))
+  {
+    g_object_unref (G_OBJECT(message));
+    return;
+  }
+
   std::string prev_path = prefs.get_string ("default-save-attachments-path", g_get_home_dir ());
 
   GtkWidget * w = gtk_file_chooser_dialog_new (_("Add files to queue"),
@@ -2409,13 +2519,28 @@ PostUI :: prompt_user_for_queueable_files (FileQueue& queue, GtkWindow * parent,
 		GSList * tmp_list = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER (w));
 		if (_file_queue_empty) _file_queue_empty=!_file_queue_empty;
 		GSList * cur = g_slist_nth (tmp_list,0);
-    const Profile profile (get_current_profile ());
-    const StringView subject(gtk_entry_get_text (GTK_ENTRY(_subject_entry)));
-    struct stat stat_buf;
+    GMimeMessage* msg = new_message_from_ui(POSTING);
+    char * tmp;
+    tmp = (char*)g_mime_object_get_header ((GMimeObject*)msg, "Subject");
+    std::string subject= std::string(tmp ? tmp : "");
+    std::string author;
+    profile.get_from_header (author);
+    quarks_t groups;
+    const char * text = gtk_entry_get_text (GTK_ENTRY(_groups_entry));
+    StringView line(text), groupname;
+    while (line.pop_token (groupname, ',')) {
+      groupname.trim ();
+      if (!groupname.empty())
+        groups.insert(groupname);
+    }
 
     for (; cur; cur = cur->next)
 		{
-      _file_queue.add((char*)cur->data, FileQueue::END);
+		  TaskUpload::needed_t tmp; //todo
+      _file_queue_tasks.insert(		  new TaskUpload(std::string((const char*)cur->data),
+                                     profile.posting_server,
+                                     groups, subject, author, tmp, 0,
+                                     TaskUpload::YENC));
 		}
   	g_slist_free (tmp_list);
   }
diff --git a/pan/gui/post-ui.h b/pan/gui/post-ui.h
index 59fb7de..ded6d83 100644
--- a/pan/gui/post-ui.h
+++ b/pan/gui/post-ui.h
@@ -22,7 +22,6 @@
 
 #include <gmime/gmime-message.h>
 #include <pan/gui/prefs.h>
-#include <pan/data/file-queue.h>
 #include <pan/general/progress.h>
 #include <pan/tasks/queue.h>
 #include <pan/usenet-utils/text-massager.h>
@@ -33,6 +32,10 @@ namespace pan
   class Profiles;
   class TaskPost;
   class FileQueue;
+  class Queue;
+
+
+  typedef std::multiset<TaskUpload*> tasks_set;
 
   /**
    * Dialog for posting new messages Pan's GTK GUI.
@@ -44,7 +47,7 @@ namespace pan
       static PostUI* create_window (GtkWidget*, Data&, Queue&, GroupServer&, Profiles&,
                                     GMimeMessage*, Prefs&, GroupPrefs&);
 
-      void prompt_user_for_queueable_files (FileQueue& queue, GtkWindow * parent, const Prefs& prefs);
+      void prompt_user_for_queueable_files (tasks_set& queue, GtkWindow * parent, const Prefs& prefs);
 
     protected:
       PostUI (GtkWindow*, Data&, Queue&, GroupServer&, Profiles&,
@@ -54,6 +57,7 @@ namespace pan
 
     public:
       GtkWidget * root() { return _root; }
+      GtkWidget * part_select() { return _part_select; }
       void rot13_selection ();
       void wrap_body ();
       void spawn_editor ();
@@ -71,13 +75,13 @@ namespace pan
       void update_filequeue_tab();
 
       //popup action entries
-      void remove_files (void);
-      void clear_list   (void);
-      void select_parts (void);
-      void move_up      (void);
-      void move_down    (void);
-      void move_top     (void);
-      void move_bottom  (void);
+      void remove_files ();
+      void clear_list   ();
+      void select_parts ();
+      void move_up      ();
+      void move_down    ();
+      void move_top     ();
+      void move_bottom  ();
 
       static void do_popup_menu (GtkWidget*, GdkEventButton *event, gpointer pane_g);
       static gboolean on_button_pressed (GtkWidget * treeview, GdkEventButton *event, gpointer userdata);
@@ -105,11 +109,13 @@ namespace pan
       Prefs& _prefs;
       GroupPrefs& _group_prefs;
       GtkWidget * _root;
+      GtkWidget * _part_select;
       GtkWidget * _from_combo;
       GtkWidget * _subject_entry;
       GtkWidget * _groups_entry;
 
       GtkWidget * _filequeue_store;
+      GtkWidget * _parts_store;
 
       GtkWidget * _to_entry;
       GtkWidget * _followupto_entry;
@@ -136,7 +142,7 @@ namespace pan
 
       /* binpost */
       bool _file_queue_empty;
-      FileQueue _file_queue;
+      tasks_set _file_queue_tasks;
 
     private:
       void add_actions (GtkWidget* box);
@@ -152,6 +158,8 @@ namespace pan
       GtkWidget* create_main_tab ();
       GtkWidget* create_extras_tab ();
       GtkWidget* create_filequeue_tab ();
+      GtkWidget* create_parts_tab (TaskUpload* ptr);
+      GtkWidget* create_log_tab ();
 
     private:
       std::string utf8ize (const StringView&) const;
@@ -171,8 +179,10 @@ namespace pan
       void set_spellcheck_enabled (bool);
       void spawn_editor_dead(char *);
 
+    public:
+      tasks_set  get_selected_files () const;
+
     private:
-      FileQueue::articles_v&  get_selected_files () const;
       static void get_selected_files_foreach (GtkTreeModel*,
                       GtkTreePath*, GtkTreeIter*, gpointer);
 
diff --git a/pan/tasks/encoder.cc b/pan/tasks/encoder.cc
index fa857f2..f50aa31 100644
--- a/pan/tasks/encoder.cc
+++ b/pan/tasks/encoder.cc
@@ -56,7 +56,8 @@ Encoder :: ~Encoder()
 
 void
 Encoder :: enqueue (TaskUpload                * task,
-                    const FileQueue::FileData & file_data,
+                    std::string               & filename,
+                    std::string               & basename,
                     std::string               & groups,
                     std::string               & subject,
                     std::string               & author,
@@ -66,7 +67,8 @@ Encoder :: enqueue (TaskUpload                * task,
   disable_progress_update ();
 
   this->task = task;
-  this->file_data = file_data;
+  this->filename = filename;
+  this->basename = basename;
   this->encode_mode = encode_mode;
   this->groups = groups;
   this->subject = subject;
@@ -91,11 +93,7 @@ Encoder :: do_work()
   crc32_t crcptr;
 
   FILE* outfile, * infile ;
-
   std::string uulib(file :: get_uulib_path());
-
-  std::cerr<<"encoder starting...\n";
-
   enable_progress_update();
 
     int res;
@@ -107,31 +105,28 @@ Encoder :: do_work()
       UUSetMsgCallback (this, uu_log);
       UUSetBusyCallback (this, uu_busy_poll, 100);
 
-      const char* filename = file_data.filename.c_str();
-      const char* basename = g_path_get_basename(file_data.filename.c_str());
-
-      g_snprintf(buf,bufsz,"%s/%s.%d", uulib.c_str(), basename, cnt);
+      g_snprintf(buf,bufsz,"%s/%s.%d", uulib.c_str(), basename.c_str(), cnt);
       outfile = fopen(buf,"wb");
       while (1) {
 
         // skip not wanted parts of binary file
-        if (file_data.parts.end() != file_data.parts.find(cnt))
-        {
-          ++cnt;
-          res = UURET_CONT;
-          goto _end;
-        }
+//        if (parts->end() != parts->find(cnt))
+//        {
+//          ++cnt;
+//          res = UURET_CONT;
+//          goto _end;
+//        }
         // 4000 lines SHOULD be OK for ANY nntp server ...
-        res = UUE_PrepPartial (outfile, NULL, (char*)filename,YENC_ENCODED,
-                               (char*)basename,0644, cnt, 4000,
-                               file_data.byte_count, (char*)groups.c_str(),
+        res = UUE_PrepPartial (outfile, NULL, (char*)filename.c_str(),YENC_ENCODED,
+                               (char*)basename.c_str(),0644, cnt, 4000,
+                               0, (char*)groups.c_str(),
                                (char*)author.c_str(), (char*)subject.c_str(),
                                0);
 
         _end:
         if (outfile) fclose(outfile);
         if (res != UURET_CONT) break;
-        g_snprintf(buf,bufsz,"%s/%s.%d", uulib.c_str(), basename, ++cnt);
+        g_snprintf(buf,bufsz,"%s/%s.%d", uulib.c_str(), basename.c_str(), ++cnt);
         outfile = fopen(buf,"wb");
       }
 
@@ -139,7 +134,7 @@ Encoder :: do_work()
       {
         g_snprintf(buf, bufsz,
                    _("Error encoding %s: %s"),
-                   basename,
+                   basename.c_str(),
                    (res==UURET_IOERR)
                    ?  file::pan_strerror (UUGetOption (UUOPT_ERRNO, NULL,
                                                        NULL, 0))
@@ -208,7 +203,7 @@ Encoder :: uu_busy_poll (void * d, uuprogress *p)
   self->mut.lock();
     self->percent = self->get_percentage(*p);
     self->current_file = p->curfile;
-    self->parts  = (int)p->numparts - self->file_data.parts.size();
+    self->total_parts = p->numparts;
   self->mut.unlock();
 
   return self->was_cancelled(); // returning true tells uulib to abort
diff --git a/pan/tasks/encoder.h b/pan/tasks/encoder.h
index d59b760..5808963 100644
--- a/pan/tasks/encoder.h
+++ b/pan/tasks/encoder.h
@@ -59,7 +59,8 @@ namespace pan
       typedef std::vector<std::string> strings_t;
 
       void enqueue (TaskUpload                * task,
-                    const FileQueue::FileData & file_data,
+                    std::string               & filename,
+                    std::string               & basename,
                     std::string               & groups,
                     std::string               & subject,
                     std::string               & author,
@@ -76,17 +77,19 @@ namespace pan
 
     private:
 
+      std::set<int>* parts;
       friend class TaskUpload;
       TaskUpload * task;
       TaskUpload::EncodeMode encode_mode;
-      FileQueue::FileData file_data;
+      std::string   filename;
+      std::string   basename;
       std::string subject, author, groups;
 
       // These are set in the worker thread and polled in the main thread.
       Mutex mut;
       volatile double percent;
       std::string current_file; // the current file we are decoding, with path
-      int parts;
+      int total_parts;
 
       static void uu_log(void *thiz, char *message, int severity);
       double get_percentage (const uuprogress& p) const;
diff --git a/pan/tasks/nzb.cc b/pan/tasks/nzb.cc
index b27e8b4..d8c382e 100644
--- a/pan/tasks/nzb.cc
+++ b/pan/tasks/nzb.cc
@@ -34,6 +34,7 @@ extern "C" {
 #include <pan/general/utf8-utils.h>
 #include "nzb.h"
 #include "task-article.h"
+#include "task-upload.h"
 
 using namespace pan;
 
@@ -46,7 +47,10 @@ namespace
     quarks_t groups;
     std::string text;
     std::string path;
+    std::vector<std::string>  groups_str;    // TaskUpload
+    TaskUpload::needed_t needed_parts;       // TaskUpload
     Article a;
+    Quark posting_server;
     PartBatch parts;
     tasks_t tasks;
     ArticleCache& cache;
@@ -63,11 +67,13 @@ namespace
 
     void file_clear () {
       groups.clear ();
+      groups_str.clear();
       text.clear ();
       path.clear ();
       a.clear ();
       bytes = 0;
       number = 0;
+      needed_parts.clear();     // TaskUpload
     }
   };
 
@@ -90,6 +96,16 @@ namespace
       }
     }
 
+    else if (!strcmp (element_name, "upload")) {
+      mc.file_clear ();
+      for (const char **k(attribute_names), **v(attribute_vals); *k; ++k, ++v) {
+             if (!strcmp (*k,"author"))  mc.a.author = *v;
+        else if (!strcmp (*k,"subject")) mc.a.subject = *v;
+        else if (!strcmp (*k,"server"))  mc.posting_server = Quark(*v);
+      }
+      std::cerr<<"upload begin tag\n";
+    }
+
     else if (!strcmp (element_name, "segment")) {
       mc.bytes = 0;
       mc.number = 0;
@@ -98,6 +114,17 @@ namespace
         else if (!strcmp (*k,"number")) mc.number = atoi (*v);
       }
     }
+
+    else if (!strcmp (element_name, "part")) {
+      mc.bytes = 0;
+      mc.number = 0;
+      for (const char **k(attribute_names), **v(attribute_vals); *k; ++k, ++v) {
+             if (!strcmp (*k,"bytes"))  mc.bytes = strtoul (*v,0,10);
+        else if (!strcmp (*k,"number")) mc.number = atoi (*v);
+      }
+      std::cerr<<"part begin tag\n";
+    }
+
   }
 
   // Called for close tags </foo>
@@ -109,7 +136,10 @@ namespace
     MyContext& mc (*static_cast<MyContext*>(user_data));
 
     if (!strcmp(element_name, "group"))
+    {
       mc.groups.insert (Quark (mc.text));
+      mc.groups_str.push_back(mc.text);
+    }
 
     else if (!strcmp(element_name, "segment") && mc.number && !mc.text.empty()) {
       const std::string mid ("<" + mc.text + ">");
@@ -120,6 +150,15 @@ namespace
       mc.parts.add_part (mc.number, mid, mc.bytes);
     }
 
+    else if (!strcmp(element_name, "part") && mc.number && !mc.text.empty()) {
+      TaskUpload::Needed n;
+      n.bytes = mc.bytes;
+      n.filename = mc.text;
+      n.partno = mc.number;
+      mc.needed_parts.push_back(n);
+      std::cerr<<"part end tag\n";
+    }
+
     else if (!strcmp(element_name,"path"))
       mc.path = mc.text;
 
@@ -135,7 +174,15 @@ namespace
           mc.a.xref.insert (*sit, *git, 0);
       }
       const StringView p (mc.path.empty() ? mc.fallback_path : StringView(mc.path));
+      debug("adding taskarticle from nzb.\n");
       mc.tasks.push_back (new TaskArticle (mc.ranks, mc.gs, mc.a, mc.cache, mc.read, 0, TaskArticle::DECODE, p));
+
+    }
+    else if (!strcmp (element_name, "upload"))
+    {
+      debug("adding taskupload from nzb.\n");
+      mc.tasks.push_back (new TaskUpload (mc.path, const_cast<const Quark&>(mc.posting_server),
+                          mc.groups, mc.a.subject.to_string(), mc.a.author.to_string(), mc.needed_parts, 0, TaskUpload::YENC));
     }
   }
 
@@ -230,65 +277,113 @@ NZB :: nzb_to_xml (std::ostream             & out,
   foreach_const (tasks_t, tasks, it)
   {
     TaskArticle * task (dynamic_cast<TaskArticle*>(*it));
-    if (!task) // not a download task...
-      continue;
-    if (task->get_save_path().empty()) // this task is for reading, not saving...
-      continue;
+    if (task)
+    {
 
-    const Article& a (task->get_article());
-    out << indent(depth++)
-        << "<file" << " poster=\"";
-    escaped (out, a.author.to_view());
-    out  << "\" date=\"" << a.time_posted << "\" subject=\"";
-    escaped (out, a.subject.to_view()) << "\">\n";
+      if (task->get_save_path().empty()) // this task is for reading, not saving...
+        continue;
+
+      const Article& a (task->get_article());
+      out << indent(depth++)
+          << "<file" << " poster=\"";
+      escaped (out, a.author.to_view());
+      out  << "\" date=\"" << a.time_posted << "\" subject=\"";
+      escaped (out, a.subject.to_view()) << "\">\n";
+
+      // path to save this to.
+      // This isn't part of the nzb spec.
+      const Quark& path (task->get_save_path());
+      if (!path.empty()) {
+        out << indent(depth) << "<path>";
+        escaped (out, path.to_view());
+        out << "</path>\n";
+      }
 
-    // path to save this to.
-    // This isn't part of the nzb spec.
-    const Quark& path (task->get_save_path());
-    if (!path.empty()) {
-      out << indent(depth) << "<path>";
-      escaped (out, path.to_view());
-      out << "</path>\n";
+      // what groups was this crossposted in?
+      quarks_t groups;
+      foreach_const (Xref, a.xref, xit)
+        groups.insert (xit->group);
+      out << indent(depth++) << "<groups>\n";
+      foreach_const (quarks_t, groups, git)
+        out << indent(depth) << "<group>" << *git << "</group>\n";
+      out << indent(--depth) << "</groups>\n";
+
+      // now for the parts...
+      out << indent(depth++) << "<segments>\n";
+      for (Article::part_iterator it(a.pbegin()), end(a.pend()); it!=end; ++it)
+      {
+        std::string mid = it.mid ();
+
+        // remove the surrounding < > as per nzb spec
+        if (mid.size()>=2 && mid[0]=='<') {
+          mid.erase (0, 1);
+          mid.resize (mid.size()-1);
+        }
+
+        // serialize this part
+        out << indent(depth)
+            << "<segment" << " bytes=\"" << it.bytes() << '"'
+                          << " number=\"" << it.number() << '"'
+                          << ">";
+        escaped(out, mid);
+        out  << "</segment>\n";
+      }
+      out << indent(--depth) << "</segments>\n";
+      out << indent(--depth) << "</file>\n";
     }
+    else
+    { // handle upload tasks
+      TaskUpload * task (dynamic_cast<TaskUpload*>(*it));
+      // not an upload task, move on
+      if (!task) continue;
 
-    // what groups was this crossposted in?
-    quarks_t groups;
-    foreach_const (Xref, a.xref, xit)
-      groups.insert (xit->group);
-    out << indent(depth++) << "<groups>\n";
-    foreach_const (quarks_t, groups, git)
-      out << indent(depth) << "<group>" << *git << "</group>\n";
-    out << indent(--depth) << "</groups>\n";
-
-    // now for the parts...
-    out << indent(depth++) << "<segments>\n";
-    for (Article::part_iterator it(a.pbegin()), end(a.pend()); it!=end; ++it)
-    {
-      std::string mid = it.mid ();
+      std::vector<std::string> groups;
 
-      // remove the surrounding < > as per nzb spec
-      if (mid.size()>=2 && mid[0]=='<') {
-        mid.erase (0, 1);
-        mid.resize (mid.size()-1);
+      //info: author, subject, load path, parts to encode / post
+      out << indent(depth)
+          << "<upload" << " author=\"";
+      escaped (out, task->_author);
+      out  << "\" subject=\"";
+      escaped (out, task->_subject);
+      out  << "\" server=\"";
+      escaped (out, task->_server.to_string());
+      out  << "\" encoded=\"";
+      char buf[3];
+      g_snprintf(buf,3,"%d",(int)task->_encoder_has_run);
+      escaped (out, StringView(buf)) << "\">\n";
+      ++depth;
+      out << indent(depth)
+          << "<path>" << task->_filename << "</path>\n";
+      out  << indent(depth) << "<groups>\n";
+      ++depth;
+      foreach_const (quarks_t, task->_groups, it)
+      {
+        out << indent(depth)<<"<group>" << (*it).to_string() << "</group>\n";
       }
+      --depth;
+      out  << indent(depth) << "</groups>\n";
+      out  << indent(depth) << "<parts>\n";
+      ++depth;
+
+      foreach_const (TaskUpload::needed_t, task->_needed, it)
+        out << indent(depth)
+            << "<part" << " bytes=\"" << (*it).bytes << '"'
+                       << " number=\"" << (*it).partno << '"'
+                       << ">" << (*it).filename<< "</part>\n";
+      --depth;
+      out  << indent(depth) << "</parts>\n";
+      --depth;
+      out << indent(depth) << "</upload>\n";
+
 
-      // serialize this part
-      out << indent(depth)
-          << "<segment" << " bytes=\"" << it.bytes() << '"'
-                        << " number=\"" << it.number() << '"'
-                        << ">";
-      escaped(out, mid);
-      out  << "</segment>\n";
     }
-    out << indent(--depth) << "</segments>\n";
-    out << indent(--depth) << "</file>\n";
   }
 
   out << indent(--depth) << "</nzb>\n";
   return out;
 }
 
-/* Saves selected files to a chosen XML file */
+/* Saves selected article-info to a chosen XML file */
 std::ostream&
 NZB :: nzb_to_xml_file (std::ostream             & out,
                    const std::vector<Task*> & tasks)
@@ -303,7 +398,7 @@ NZB :: nzb_to_xml_file (std::ostream             & out,
   foreach_const (tasks_t, tasks, it)
   {
     TaskArticle * task (dynamic_cast<TaskArticle*>(*it));
-    if (!task) // not a download task...
+    if (!task) // not a download task, for example an upload task...
       continue;
 
     const Article& a (task->get_article());
diff --git a/pan/tasks/nzb.h b/pan/tasks/nzb.h
index 0d9eec1..fe49dfa 100644
--- a/pan/tasks/nzb.h
+++ b/pan/tasks/nzb.h
@@ -53,10 +53,10 @@ namespace pan
 
     static std::ostream&  nzb_to_xml (std::ostream             & out,
                                       const std::vector<Task*> & tasks);
-                                      
-    
+
+
     static std::ostream&  nzb_to_xml_file (std::ostream             & out,
-                                      const std::vector<Task*> & tasks);
+                                           const std::vector<Task*> & tasks);
 
   };
 }
diff --git a/pan/tasks/queue.cc b/pan/tasks/queue.cc
index ce9de63..f24dc19 100644
--- a/pan/tasks/queue.cc
+++ b/pan/tasks/queue.cc
@@ -550,6 +550,9 @@ Queue :: task_is_active (const Task * task) const
   if (task && task==_decoder_task)
     return true;
 
+  if (task && task==_encoder_task)
+    return true;
+
   bool task_has_nntp (false);
   foreach_const (nntp_to_task_t, _nntp_to_task, it)
     if ((task_has_nntp = task==it->second))
diff --git a/pan/tasks/task-upload.cc b/pan/tasks/task-upload.cc
index c915594..0728fe9 100644
--- a/pan/tasks/task-upload.cc
+++ b/pan/tasks/task-upload.cc
@@ -59,27 +59,33 @@ namespace
 ****
 ***/
 
-TaskUpload :: TaskUpload ( const FileQueue::FileData & file_data,
+TaskUpload :: TaskUpload ( const std::string         & filename,
                            const Quark               & server,
-                           std::string                 groups,
+                           quarks_t                  & groups,
                            std::string                 subject,
                            std::string                 author,
+                           needed_t                  & todo,
                            Progress::Listener        * listener,
                            const TaskUpload::EncodeMode  enc):
-  Task ("UPLOAD", get_description(file_data.filename.c_str(), false)),
-  _file_data(file_data),
-  _basename (file_data.basename),
+  Task ("UPLOAD", get_description(filename.c_str(), true)),
+  _filename(filename),
+  _basename (std::string(g_path_get_basename(filename.c_str()))),
   _server(server),
   _groups(groups),
   _subject (subject),
   _author(author),
   _encoder(0),
   _encoder_has_run (false),
-  _encode_mode(enc)
+  _encode_mode(enc),
+  _extern(todo)
 {
   if (listener != 0)
     add_listener (listener);
 
+  struct stat sb;
+  stat(filename.c_str(),&sb);
+  _bytes = sb.st_size;
+
   update_work ();
 }
 
@@ -88,24 +94,22 @@ TaskUpload :: update_work (NNTP* checkin_pending)
 {
 
   int working(0);
-  foreach (needed_t, _needed, nit)
-  {
+  foreach (needed_t, _needed, nit) {
     Needed& n (*nit);
     if (n.nntp && n.nntp!=checkin_pending)
       ++working;
   }
 
-  if (!_encoder && !_encoder_has_run)
+  if (!_encoder && !_encoder_has_run )
   {
     _state.set_need_encoder();
-  } else if(working)
+  } else if(working )
   {
     _state.set_working();
   } else if (_encoder_has_run && !_needed.empty())
   {
-    set_status_va (_("Uploading %s"), _basename.c_str());
     _state.set_need_nntp(_server);
-  } else if (_needed.empty())
+  } else if (_needed.empty() && _encoder_has_run )
   {
     _state.set_completed();
     set_finished(OK);
@@ -121,8 +125,7 @@ TaskUpload :: use_nntp (NNTP * nntp)
 {
 
   Needed * needed (0);
-  needed_t::iterator it = _needed.begin();
-  for (; it != _needed.end(); ++it)
+  for (needed_t::iterator it(_needed.begin()), end(_needed.end()); !needed && it!=end; ++it)
     if (it->nntp==0)
       needed = &*it;
 
@@ -134,11 +137,13 @@ TaskUpload :: use_nntp (NNTP * nntp)
   else
   {
     needed->nntp = nntp;
-
+    set_status_va (_("Uploading \"%s\" - Part %d of %d"), _basename.c_str(), needed->partno, _total_parts);
     std::stringstream tmp;
     std::ifstream in(needed->filename.c_str(), std::ifstream::in);
-    while (in.good())
-      tmp << (char) in.get();
+    char tmp_char;
+    while (in.good()) {
+      tmp << (tmp_char=(char) in.get());
+    }
     in.close();
     nntp->post(StringView(tmp.str()), this);
     update_work ();
@@ -152,10 +157,16 @@ TaskUpload :: use_nntp (NNTP * nntp)
 void
 TaskUpload :: on_nntp_line  (NNTP               * nntp,
                               const StringView   & line_in)
-{
+{}
 
-std::cerr<<"line "<<line_in<<std::cerr;
 
+// delete cached files to avoid "disk full" problems
+namespace
+{
+  void delete_cache(const TaskUpload::Needed& n)
+  {
+    unlink(n.filename.c_str());
+  }
 }
 
 void
@@ -169,6 +180,8 @@ TaskUpload :: on_nntp_done  (NNTP             * nntp,
     if (it->nntp == nntp)
       break;
 
+  bool post_ok(false);
+
   switch (atoi(response.str))
   {
     case NO_POSTING:
@@ -176,23 +189,28 @@ TaskUpload :: on_nntp_done  (NNTP             * nntp,
       this->stop();
       break;
     case POSTING_FAILED:
-      Log :: add_err_va (_("Posting failed: %s"), response.str);
+      Log :: add_err_va (_("Posting of file %s failed: %s"), _basename.c_str(), response.str);
       break;
     case ARTICLE_POSTED_OK:
-      if (_needed.empty())
-        Log :: add_info_va(_("Posting of file %s succesful: %s"),
-               _file_data.basename.c_str(), response.str);
+      post_ok = true;
       break;
     case TOO_MANY_CONNECTIONS:
       // lockout for 120 secs, but try
       _state.set_need_nntp(nntp->_server);
       break;
+    default:
+      Log :: add_err_va (_("Got unknown response code: %s"),response.str);
+      break;
   }
 
   switch (health)
   {
     case OK:
+      delete_cache(*it);
       _needed.erase (it);
+        if (_needed.empty() && post_ok)
+        Log :: add_info_va(_("Posting of file %s succesful: %s"),
+               _basename.c_str(), response.str);
       break;
 
     case ERR_NETWORK:
@@ -210,7 +228,6 @@ TaskUpload :: on_nntp_done  (NNTP             * nntp,
 ****
 ***/
 
-//todo
 unsigned long
 TaskUpload :: get_bytes_remaining () const
 {
@@ -230,7 +247,16 @@ TaskUpload :: use_encoder (Encoder* encoder)
   _encoder = encoder;
   init_steps(100);
   _state.set_working();
-  _encoder->enqueue (this, _file_data, _groups, _subject, _author, YENC);
+  // build group name
+  std::string groups;
+  quarks_t::iterator it = _groups.begin();
+  int i(0);
+  for (; it != _groups.end(); ++it, ++i)
+  {
+    if (i<_groups.size()&& i>0 && _groups.size()>1) groups += ",";
+    groups += (*it).to_string();
+  }
+  _encoder->enqueue (this, _filename, _basename, groups, _subject, _author, YENC);
   debug ("encoder thread was free, enqueued work");
 }
 
@@ -260,38 +286,37 @@ TaskUpload :: on_worker_done (bool cancelled)
     foreach_const(Encoder::log_t, _encoder->log_infos, it)
       Log :: add_info(it->c_str());
 
-
     if (!_encoder->log_errors.empty())
       set_error (_encoder->log_errors.front());
 
     if (!_encoder->log_severe.empty())
       _state.set_health (ERR_LOCAL);
     else {
-      // get parts number from encoder
-      _parts = _encoder->parts;
       set_step (100);
       _encoder_has_run = true;
+      _total_parts = _encoder->total_parts;
       /*enqueue all parts into the global needed_t list.
         update_work will then assign a pointer to the begin
         which will be used for an nntp upload.
         on nntp_done, the list is decreased by one member
        */
-      static Needed n;
+      Needed n;
       char buf[2048];
       struct stat sb;
 
-      for (int i=1;i<=_parts;i++)
-      {
-        n.partno = i;
-        g_snprintf(buf,sizeof(buf),"%s/%s.%d",
-                   file::get_uulib_path().c_str(),
-                   _basename.c_str(), i);
-        n.filename = buf;
-        stat(buf, &sb);
-        n.bytes = sb.st_size;
-        _needed.push_back (n);
-      }
-      init_steps (_parts);
+        for (int i=1; i<=_total_parts; ++i)
+        {
+          n.partno = i;
+          g_snprintf(buf,sizeof(buf),"%s/%s.%d",
+                     file::get_uulib_path().c_str(),
+                     _basename.c_str(), i);
+          n.filename = buf;
+          stat(buf, &sb);
+          n.bytes = sb.st_size;
+          _needed.push_back (n);
+        }
+
+      init_steps (_total_parts);
       set_step (0);
     }
   }
diff --git a/pan/tasks/task-upload.h b/pan/tasks/task-upload.h
index 17ffaff..7a7b1fd 100644
--- a/pan/tasks/task-upload.h
+++ b/pan/tasks/task-upload.h
@@ -25,14 +25,14 @@
 #define _TaskUpload_h_
 
 #include <pan/general/worker-pool.h>
+#include <pan/general/locking.h>
 #include <pan/data/article.h>
 #include <pan/data/article-cache.h>
 #include <pan/data/data.h>
-#include <pan/data/file-queue.h>
 #include <pan/data/xref.h>
 #include <pan/tasks/nntp.h>
 #include <pan/tasks/task.h>
-#include <deque>
+#include <set>
 
 namespace pan
 {
@@ -46,7 +46,17 @@ namespace pan
                      private NNTP::Listener,
                      private WorkerPool::Worker::Listener
   {
-    public: // life cycle
+    public:
+
+      struct Needed {
+        std::string filename;
+        unsigned long bytes;
+        int partno;
+        NNTP* nntp;
+        Needed (): nntp(0) {}
+      };
+
+      typedef std::deque<Needed> needed_t;
 
       enum EncodeMode
       {
@@ -55,18 +65,24 @@ namespace pan
         PLAIN
       };
 
-      TaskUpload ( const FileQueue::FileData & file_data,
+      // life cycle
+      TaskUpload ( const std::string         & filename,
                    const Quark               & server,
-                   std::string                 groups,
+                   quarks_t                  & groups,
                    std::string                 subject,
                    std::string                 author,
-                   Progress::Listener        * listener=0,
-                   TaskUpload::EncodeMode enc     = YENC);
+                   needed_t                  & todo,
+                   Progress::Listener        * listener= 0,
+                   TaskUpload::EncodeMode enc= YENC);
       virtual ~TaskUpload ();
 
     public: // Task subclass
       unsigned long get_bytes_remaining () const;
       void stop ();
+      const std::string& basename()  { return  _basename; }
+      const std::string& filename()  { return  _filename; }
+      const std::string& subject ()  { return  _subject;  }
+      unsigned long get_byte_count() { return _bytes;     }
 
       /** only call this for tasks in the NEED_DECODE state
        * attempts to acquire the saver thread and start saving
@@ -91,25 +107,22 @@ namespace pan
 
     private: // implementation
       friend class Encoder;
+      friend class NZB;
       Encoder * _encoder;
       bool _encoder_has_run;
-      const FileQueue::FileData _file_data;
-      const std::string _basename;
+      std::string _filename;
+      std::string _basename;
       TaskUpload::EncodeMode _encode_mode;
-      std::string _groups, _subject, _author;
-      int _parts; // filled in by encoder
+      quarks_t _groups;
+      std::string _subject, _author;
+      int _total_parts; // filled in by encoder
+      unsigned long _bytes;
       Mutex mut;
 
     private:
-      struct Needed {
-        std::string filename;
-        unsigned long bytes;
-        int partno;
-        NNTP* nntp;
-        Needed (): nntp(0), partno(0) {}
-      };
-      typedef std::deque<Needed> needed_t;
-      needed_t _needed;
+      needed_t       _needed;
+      needed_t       _extern;
+      std::set<int>  _parts;
 
       void update_work (NNTP * checkin_pending = 0);
   };
diff --git a/pan/usenet-utils/message-check.cc b/pan/usenet-utils/message-check.cc
index 7174818..84f610f 100644
--- a/pan/usenet-utils/message-check.cc
+++ b/pan/usenet-utils/message-check.cc
@@ -267,7 +267,7 @@ namespace
    * (2) Replace carriage returns in both the calculated attribution string
    *     and a temporary copy of the message body, so that we don't have to
    *     worry whether or not the attribution line's been wrapped.
-   * 
+   *
    * (3) Search for an occurance of the attribution string in the body.  If
    *     it's found, remove it from the temporary copy of the body so that
    *     it won't affect our line counts.
@@ -420,7 +420,8 @@ MessageCheck :: message_check (const GMimeMessage * message_const,
                                const StringView   & attribution,
                                const quarks_t     & groups_our_server_has,
                                unique_strings_t   & errors,
-                               Goodness           & goodness)
+                               Goodness           & goodness,
+                               bool                 binpost)
 {
   goodness.clear ();
   errors.clear ();
@@ -445,18 +446,23 @@ MessageCheck :: message_check (const GMimeMessage * message_const,
     errors.insert (_("Warning: Most newsgroups frown upon HTML posts."));
     goodness.raise_to_warn ();
   }
-  check_body (errors, goodness, tm, message, body, attribution);
+  if (!binpost)
+    check_body (errors, goodness, tm, message, body, attribution);
   g_free (body);
-  
+
   // check the optional followup-to...
   bool followup_to_set (false);
   const char * cpch = g_mime_object_get_header ((GMimeObject *) message, "Followup-To");
-  if (cpch && *cpch) {
-    quarks_t groups;
-    get_nntp_rcpts (cpch, groups);
-    followup_to_set = !groups.empty();
-    check_followup_to (errors, goodness, groups_our_server_has, groups);
-  }
+  if (!binpost)
+  {
+    if (cpch && *cpch) {
+      quarks_t groups;
+      get_nntp_rcpts (cpch, groups);
+      followup_to_set = !groups.empty();
+      check_followup_to (errors, goodness, groups_our_server_has, groups);
+    }
+  } else
+    followup_to_set = true;
 
   // check the groups...
   size_t group_qty (0);
diff --git a/pan/usenet-utils/message-check.h b/pan/usenet-utils/message-check.h
index 1528f12..8889ee1 100644
--- a/pan/usenet-utils/message-check.h
+++ b/pan/usenet-utils/message-check.h
@@ -58,7 +58,8 @@ namespace pan
                                  const StringView   & attribution,
                                  const quarks_t     & groups_our_server_has,
                                  unique_strings_t   & errors,
-                                 Goodness           & goodness);
+                                 Goodness           & goodness,
+                                 bool                 binpost = false);
   };
 }
 



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