Logo Search packages:      
Sourcecode: uget version File versions  Download package

ug_category.c

/*
 *
 *   Copyright (C) 2005-2009 by Raymond Huang
 *   plushuang at users.sourceforge.net
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *
 *  This library 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
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *  ---
 *
 *  In addition, as a special exception, the copyright holders give
 *  permission to link the code of portions of this program with the
 *  OpenSSL library under certain conditions as described in each
 *  individual source file, and distribute linked combinations
 *  including the two.
 *  You must obey the GNU Lesser General Public License in all respects
 *  for all of the code used other than OpenSSL.  If you modify
 *  file(s) with this exception, you may extend this exception to your
 *  version of the file(s), but you are not obligated to do so.  If you
 *  do not wish to do so, delete this exception statement from your
 *  version.  If you delete this exception statement from all source
 *  files in the program, then also delete it here.
 *
 */

#include <string.h>

#include <ug_path.h>
#include <ug_utils.h>
#include <ug_plugin.h>
#include <ug_category.h>
#include <ug_data_app.h>
#include <ug_list_view.h>

#include <glib/gi18n.h>

static void       ug_category_init (UgCategory* category);
static void       ug_category_finalize (UgCategory* category);
static void       ug_download_store_in_markup (UgCategory* category, GMarkupParseContext* context);
static void       ug_download_store_to_markup (UgCategory* category, UgMarkup* markup);

static UgDataEntry      category_data_entry[] =
{
      {"name",                G_STRUCT_OFFSET (UgCategory, name),                   UG_DATA_TYPE_STRING,    NULL, NULL},
      {"capacity",            G_STRUCT_OFFSET (UgCategory, capacity),               UG_DATA_TYPE_INT,       NULL, NULL},
      {"RunningLimit",  G_STRUCT_OFFSET (UgCategory, running_limit),    UG_DATA_TYPE_INT,       NULL, NULL},
      {"VisibleColumn", G_STRUCT_OFFSET (UgCategory, visible_column),   UG_DATA_TYPE_CUSTOM,    (UgInMarkupFunc) ug_data_in_markup,                   (UgToMarkupFunc) ug_data_to_markup},
      {"VisibleSummary",      G_STRUCT_OFFSET (UgCategory, visible_summary),  UG_DATA_TYPE_CUSTOM,    (UgInMarkupFunc) ug_data_in_markup,                   (UgToMarkupFunc) ug_data_to_markup},
      {"DownloadDefault",     G_STRUCT_OFFSET (UgCategory, download_default), UG_DATA_TYPE_CUSTOM,    (UgInMarkupFunc) ug_dataset_in_markup,                (UgToMarkupFunc) ug_dataset_to_markup},
      {"DownloadStore", 0,                                                                      UG_DATA_TYPE_CUSTOM,    (UgInMarkupFunc) ug_download_store_in_markup,   (UgToMarkupFunc) ug_download_store_to_markup},
      {NULL}
};

static UgDataEntry      visible_column_data_entry[] =
{
      {"RulesHint",     G_STRUCT_OFFSET (UgCategoryVisibleColumn, rules_hint),      UG_DATA_TYPE_INT, NULL, NULL},
      {"completed",     G_STRUCT_OFFSET (UgCategoryVisibleColumn, completed), UG_DATA_TYPE_INT, NULL, NULL},
      {"total",         G_STRUCT_OFFSET (UgCategoryVisibleColumn, total),           UG_DATA_TYPE_INT, NULL, NULL},
      {"percent",       G_STRUCT_OFFSET (UgCategoryVisibleColumn, percent),         UG_DATA_TYPE_INT, NULL, NULL},
      {"elapsed",       G_STRUCT_OFFSET (UgCategoryVisibleColumn, elapsed),         UG_DATA_TYPE_INT, NULL, NULL},
      {"left",          G_STRUCT_OFFSET (UgCategoryVisibleColumn, left),            UG_DATA_TYPE_INT, NULL, NULL},
      {"speed",         G_STRUCT_OFFSET (UgCategoryVisibleColumn, speed),           UG_DATA_TYPE_INT, NULL, NULL},
      {"retry",         G_STRUCT_OFFSET (UgCategoryVisibleColumn, retry),           UG_DATA_TYPE_INT, NULL, NULL},
      {"category",      G_STRUCT_OFFSET (UgCategoryVisibleColumn, category),  UG_DATA_TYPE_INT, NULL, NULL},
      {"URL",                 G_STRUCT_OFFSET (UgCategoryVisibleColumn, url),             UG_DATA_TYPE_INT, NULL, NULL},
      {NULL}
};

static UgDataEntry      visible_summary_data_entry[] =
{
      {"self",          G_STRUCT_OFFSET (UgCategoryVisibleSummary, self),           UG_DATA_TYPE_INT, NULL, NULL},
      {"name",          G_STRUCT_OFFSET (UgCategoryVisibleSummary, name),           UG_DATA_TYPE_INT, NULL, NULL},
      {"folder",        G_STRUCT_OFFSET (UgCategoryVisibleSummary, folder),         UG_DATA_TYPE_INT, NULL, NULL},
      {"category",      G_STRUCT_OFFSET (UgCategoryVisibleSummary, category), UG_DATA_TYPE_INT, NULL, NULL},
//    {"elapsed",       G_STRUCT_OFFSET (UgCategoryVisibleSummary, elapsed),  UG_DATA_TYPE_INT, NULL, NULL},
      {"URL",                 G_STRUCT_OFFSET (UgCategoryVisibleSummary, url),            UG_DATA_TYPE_INT, NULL, NULL},
      {"message",       G_STRUCT_OFFSET (UgCategoryVisibleSummary, message),  UG_DATA_TYPE_INT, NULL, NULL},
      {NULL}
};

static UgDataClass category_data_class =
{
      "category",
      NULL,
      sizeof (UgCategory),
      category_data_entry,

      (UgInitFunc)            ug_category_init,
      (UgFinalizeFunc)  ug_category_finalize,
      (UgAssignFunc)          NULL,
};

static UgDataClass visible_column_data_class =
{
      "VisibleDownloadColumns",
      NULL,
      sizeof (UgCategoryVisibleColumn),
      visible_column_data_entry,

      (UgInitFunc)            NULL,
      (UgFinalizeFunc)  NULL,
      (UgAssignFunc)          NULL,
};

static UgDataClass visible_summary_data_class =
{
      "VisibleSummaryItems",
      NULL,
      sizeof (UgCategoryVisibleSummary),
      visible_summary_data_entry,

      (UgInitFunc)            NULL,
      (UgFinalizeFunc)  NULL,
      (UgAssignFunc)          NULL,
};

static void       ug_category_init  (UgCategory* category)
{
      category->download_store = gtk_list_store_new (1, G_TYPE_POINTER);
      category->download_view  = ug_download_view_new ();
      gtk_tree_view_set_model (category->download_view, GTK_TREE_MODEL (category->download_store));

      category->download_scroll = gtk_scrolled_window_new (NULL, NULL);
      gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (category->download_scroll),
                                           GTK_SHADOW_IN);
      gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (category->download_scroll),
                                      GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
      gtk_container_add (GTK_CONTAINER(category->download_scroll), GTK_WIDGET(category->download_view));
      gtk_widget_show_all (category->download_scroll);

      category->list_icon = UG_LIST_ICON_CATEGORY;
      // initsalize UgCategoryVisibleColumn
      category->visible_column.data_class = &visible_column_data_class;
      category->visible_column.rules_hint = TRUE;
      category->visible_column.completed  = TRUE;
      category->visible_column.total            = TRUE;
      category->visible_column.percent    = TRUE;
      category->visible_column.elapsed    = TRUE;
      category->visible_column.left       = TRUE;
      category->visible_column.speed            = TRUE;
      category->visible_column.retry            = TRUE;
//    category->visible_column.category   = TRUE;           // FALSE
//    category->visible_column.url        = TRUE;           // FALSE
      // initsalize UgCategoryVisibleSummary
      category->visible_summary.data_class= &visible_summary_data_class;
      category->visible_summary.self            = TRUE;
      category->visible_summary.name            = TRUE;
//    category->visible_summary.category  = TRUE;           // FALSE
      category->visible_summary.folder    = TRUE;
//    category->visible_summary.elapsed   = TRUE;
//    category->visible_summary.url       = TRUE;           // FALSE
      category->visible_summary.message   = TRUE;
}

static void       ug_category_finalize    (UgCategory* category)
{
      UgDataset*        dataset;
      GtkTreeModel*     model;
      GtkTreeIter       iter;
      gboolean          valid;

      model = GTK_TREE_MODEL (category->download_store);
      valid = gtk_tree_model_get_iter_first (model, &iter);
      while (valid) {
            gtk_tree_model_get (model, &iter, 0, &dataset, -1);
            valid = gtk_tree_model_iter_next (model, &iter);
            ug_category_delete (category, dataset);
      }

      gtk_widget_destroy (category->download_scroll);
      g_object_unref (category->download_store);
      g_free (category->name);
}

UgCategory* ug_category_new (const gchar* name, UgCategory* total_list)
{
      UgCategory* queuing;
      UgCategory* completed;
      UgCategory* recycled;

      queuing = ug_data_new (&category_data_class);
      ug_str_set (&queuing->name, name, -1);
      queuing->running_limit = 3;
      queuing->download_default = ug_dataset_new_app ();
//    queuing->list_icon = UG_LIST_ICON_CATEGORY;

      completed = ug_data_new (&category_data_class);
      completed->name = g_strdup (UG_CATEGORY_COMPLETED_NAME);
      completed->list_icon = UG_LIST_ICON_COMPLETED;
      completed->capacity = 300;
      completed->visible_column.completed = FALSE;
      completed->visible_column.percent   = FALSE;
//    completed->visible_column.elapsed   = FALSE;
      completed->visible_column.left            = FALSE;
      completed->visible_column.speed           = FALSE;

      recycled = ug_data_new (&category_data_class);
      recycled->name  = g_strdup (UG_CATEGORY_RECYCLED_NAME);
      recycled->list_icon = UG_LIST_ICON_RECYCLED;
      recycled->capacity = 300;
      recycled->visible_column.elapsed    = FALSE;
      recycled->visible_column.left       = FALSE;
      recycled->visible_column.speed            = FALSE;

      // UgCategory : 3 in 1. queuing, completed, recycled
      queuing->queuing     = queuing;
      queuing->completed   = completed;
      queuing->recycled    = recycled;

      completed->queuing   = queuing;
      completed->completed = completed;
      completed->recycled  = recycled;

      recycled->queuing    = queuing;
      recycled->completed  = completed;
      recycled->recycled   = recycled;

      if (total_list) {
            queuing->total_list   = total_list->queuing;
            completed->total_list = total_list->completed;
            recycled->total_list  = total_list->recycled;
      }
      return queuing;
}

void  ug_category_free (UgCategory* category)
{
      if (category == category->queuing) {
            ug_data_free (category->completed);
            ug_data_free (category->recycled);
      }
      ug_data_free (category);
}

gint  ug_category_count_selected (UgCategory* category)
{
      GtkTreeSelection* selection;

      selection = gtk_tree_view_get_selection (category->download_view);
      return gtk_tree_selection_count_selected_rows (selection);
}

UgDataset*  ug_category_get_selected (UgCategory* category)
{
      GtkTreeSelection* selection;
      GtkTreeModel*           model;
      GtkTreeIter             iter;
      UgDataset*              dataset;
      UgDataset*              result;
      GList*                        path_list;
      GList*                        link;

      selection = gtk_tree_view_get_selection (category->download_view);
      path_list = gtk_tree_selection_get_selected_rows (selection, &model);
      result = NULL;

      for (link = g_list_last (path_list);  link;  link = link->prev) {
            if (gtk_tree_model_get_iter (model, &iter, link->data) == FALSE)
                  continue;
            gtk_tree_model_get (model, &iter, 0, &dataset, -1);
            ug_data_list_unlink (dataset);      // remove link
            result = ug_data_list_prepend (result, dataset);
      }

      g_list_foreach (path_list, (GFunc) gtk_tree_path_free, NULL);
      g_list_free (path_list);
      return result;
}

UgDataset*  ug_category_get_cursor  (UgCategory* category)
{
      UgDataset*        dataset;
      GtkTreePath*      path;
      GtkTreeIter       iter;

      gtk_tree_view_get_cursor (category->download_view, &path, NULL);
      if (path == NULL)
            return NULL;
      gtk_tree_model_get_iter ((GtkTreeModel*) category->download_store, &iter, path);
      gtk_tree_path_free (path);
      gtk_tree_model_get ((GtkTreeModel*) category->download_store, &iter, 0, &dataset, -1);
      return dataset;
}

void  ug_category_append  (UgCategory* category, UgDataset* dataset)
{
      UgCategory*       queuing;
      UgCategory*       total_list;
      UgDataApp*        appdata;

      // swap category and total_list if need
      if (category->total_list) {
            total_list = category->total_list;
            queuing    = category->queuing;
      }
      else {
            total_list = category;
            queuing    = NULL;
            category   = NULL;
      }

      while (dataset) {
            appdata = UG_DATASET_APP (dataset);
            if (category) {
                  appdata->category = category;
                  ug_str_set (&appdata->category_name, queuing->name, -1);
                  gtk_list_store_append (category->download_store, &appdata->category_iter);
                  gtk_list_store_set (category->download_store, &appdata->category_iter, 0, dataset, -1);
            }
            if (total_list) {
                  appdata->total_list = total_list;
                  gtk_list_store_append (total_list->download_store, &appdata->total_list_iter);
                  gtk_list_store_set (total_list->download_store, &appdata->total_list_iter, 0, dataset, -1);
            }
            // get next one
            dataset = dataset->next;
      }
}

void  ug_category_prepend (UgCategory* category, UgDataset* dataset)
{
      UgCategory*       queuing;
      UgCategory*       total_list;
      UgDataApp*        appdata;

      // swap category and total_list if need
      if (category->total_list) {
            total_list = category->total_list;
            queuing    = category->queuing;
      }
      else {
            total_list = category;
            queuing    = NULL;
            category   = NULL;
      }

      while (dataset) {
            appdata = UG_DATASET_APP (dataset);
            if (category) {
                  appdata->category = category;
                  ug_str_set (&appdata->category_name, queuing->name, -1);
                  gtk_list_store_prepend (category->download_store, &appdata->category_iter);
                  gtk_list_store_set (category->download_store, &appdata->category_iter, 0, dataset, -1);
            }
            if (total_list) {
                  appdata->total_list = total_list;
                  gtk_list_store_prepend (total_list->download_store, &appdata->total_list_iter);
                  gtk_list_store_set (total_list->download_store, &appdata->total_list_iter, 0, dataset, -1);
            }
            // get next one
            dataset = dataset->next;
      }
}

void  ug_category_delete  (UgCategory* category, UgDataset* dataset)
{
      UgCategory*       total_list;
      UgDataset*        next;
      UgDataApp*        appdata;
      UgDataCommon*     common;
      gchar*                  path;

      while (dataset) {
            common            = UG_DATASET_COMMON (dataset);
            appdata           = UG_DATASET_APP (dataset);
            category    = appdata->category;
            total_list  = appdata->total_list;
            // remove from running list
            if (appdata->plugin) {
                  if (total_list) {
                        total_list->running = g_list_remove (total_list->running, dataset);
                        total_list->running_count--;
                  }
                  if (category) {
                        category->running = g_list_remove (category->running, dataset);
                        category->running_count--;
                  }
                  ug_plugin_set_state (appdata->plugin, UG_STATE_STOP);
                  ug_plugin_unref (appdata->plugin);
                  appdata->plugin = NULL;
            }
            g_free (appdata->category_name);
            appdata->category_name = NULL;
            // remove from GtkListStore
            if (category) {
                  gtk_list_store_remove (category->download_store, &appdata->category_iter);
//                appdata->category = NULL;
            }
            if (total_list) {
                  gtk_list_store_remove (total_list->download_store, &appdata->total_list_iter);
//                appdata->total_list = NULL;
            }
            // delete temp file
            if (common->folder)
                  path = g_strconcat (common->folder, G_DIR_SEPARATOR_S, common->file, ".ug_", NULL);
            else
                  path = g_strconcat (common->file, ".ug_", NULL);
            ug_delete_file (path);
            g_free (path);
            // get next one and free current
            next = dataset->next;
            ug_data_free (dataset);
            dataset = next;
      }
}

void  ug_category_move_to  (UgCategory* category, UgDataset* dataset, UgCategory* target)
{
      UgCategory* total_list;
      UgDataset*  next;
      UgDataApp*  appdata;

      while (dataset) {
            appdata    = UG_DATASET_APP (dataset);
            category   = appdata->category;
            total_list = appdata->total_list;
            // if target is in total list
            if (category && target->total_list == NULL) {
                  if (target == target->queuing)                  // queuing
                        target = category->queuing;
                  else  if (target == target->completed)    // completed
                        target = category->completed;
                  else                                                  // recycled
                        target = category->recycled;
            }
            if (target == category || target == total_list) {
                  dataset = dataset->next;
                  continue;
            }

            // add/remove running list
            if (appdata->plugin) {
                  if (category) {
                        category->running = g_list_remove (category->running, dataset);
                        category->running_count--;
                  }
                  // if target is queuing
                  if (target == target->queuing) {
                        target->running = g_list_prepend (target->running, dataset);
                        target->running_count++;
                  }
                  else {
                        if (total_list) {
                              total_list->running = g_list_remove (total_list->running, dataset);
                              total_list->running_count--;
                        }
                        ug_plugin_set_state (appdata->plugin, UG_STATE_STOP);
                        ug_plugin_unref (appdata->plugin);
                        appdata->plugin = NULL;
                        appdata->list_icon = UG_LIST_ICON_FILE;
                  }
            }

            g_free (appdata->category_name);
            appdata->category_name = NULL;
            // remove from GtkListStore
            if (category) {
                  gtk_list_store_remove (category->download_store, &appdata->category_iter);
                  appdata->category = NULL;
            }
            if (total_list) {
                  gtk_list_store_remove (total_list->download_store, &appdata->total_list_iter);
                  appdata->total_list = NULL;
            }
            // get next one and move current
            next = dataset->next;
            ug_data_list_unlink (dataset);      // remove link
            // if target is queuing, append it
            if (target == target->queuing)
                  ug_category_append (target, dataset);
            else
                  ug_category_prepend (target, dataset);
            dataset = next;
      }
}

gboolean    ug_category_move_selected_up (UgCategory* category)
{
      GtkTreeSelection* selection;
      GtkTreeModel*           model;
      GtkTreeIter             iter;
      UgDataApp*              appdata_prev;
      UgDataApp*              appdata;
      UgDataset*              dataset;
      GList*                        path_list;
      GList*                        link;
      gint                    index_prev;
      gint*                   indices;
      gboolean                result = FALSE;

      selection = gtk_tree_view_get_selection (category->download_view);
      path_list = gtk_tree_selection_get_selected_rows (selection, &model);
      index_prev = -1;

      for (link = path_list;  link;  link = link->next) {
            indices = gtk_tree_path_get_indices (link->data);
            if (*indices == index_prev+1) {
                  index_prev++;
                  continue;
            }
            index_prev = *indices -1;
            // get prev dataset
            if (gtk_tree_path_prev (link->data) == FALSE)
                  break;
            if (gtk_tree_model_get_iter (model, &iter, link->data) == FALSE)
                  break;
            gtk_tree_model_get (model, &iter, 0, &dataset, -1);
            appdata_prev = UG_DATASET_APP (dataset);
            // get current dataset
            gtk_tree_model_iter_next (model, &iter);
            gtk_tree_model_get (model, &iter, 0, &dataset, -1);
            appdata = UG_DATASET_APP (dataset);

            if (appdata->category == appdata_prev->category)
                  gtk_list_store_swap (appdata->category->download_store, &appdata->category_iter, &appdata_prev->category_iter);
            gtk_list_store_swap (appdata->total_list->download_store, &appdata->total_list_iter, &appdata_prev->total_list_iter);
            result = TRUE;
      }
      // scroll to first item
      gtk_tree_view_scroll_to_cell (category->download_view, path_list->data, NULL, FALSE, 0.0, 0.0);

      g_list_foreach (path_list, (GFunc) gtk_tree_path_free, NULL);
      g_list_free (path_list);
      return result;
}

gboolean    ug_category_move_selected_down (UgCategory* category)
{
      GtkTreeSelection* selection;
      GtkTreeModel*           model;
      GtkTreeIter             iter;
      UgDataApp*              appdata_next;
      UgDataApp*              appdata;
      UgDataset*              dataset;
      GList*                        path_list;
      GList*                        link;
      gint                    index_next;
      gint*                   indices;
      gboolean                result = FALSE;

      selection = gtk_tree_view_get_selection (category->download_view);
      path_list = gtk_tree_selection_get_selected_rows (selection, &model);
      index_next = gtk_tree_model_iter_n_children (model, NULL);
      // scroll to last item
      link = g_list_last (path_list);
      gtk_tree_path_next (link->data);
      gtk_tree_view_scroll_to_cell (category->download_view, link->data, NULL, FALSE, 0.0, 0.0);
      gtk_tree_path_prev (link->data);

      for (;  link;  link = link->prev) {
            indices = gtk_tree_path_get_indices (link->data);
            if (*indices == index_next-1) {
                  index_next--;
                  continue;
            }
            index_next = *indices +1;
            // get current dataset
            if (gtk_tree_model_get_iter (model, &iter, link->data) == FALSE)
                  break;
            gtk_tree_model_get (model, &iter, 0, &dataset, -1);
            appdata = UG_DATASET_APP (dataset);
            // get next dataset
            if (gtk_tree_model_iter_next (model, &iter) == FALSE)
                  break;
            gtk_tree_model_get (model, &iter, 0, &dataset, -1);
            appdata_next = UG_DATASET_APP (dataset);

            if (appdata->category == appdata_next->category)
                  gtk_list_store_swap (appdata->category->download_store, &appdata->category_iter, &appdata_next->category_iter);
            gtk_list_store_swap (appdata->total_list->download_store, &appdata->total_list_iter, &appdata_next->total_list_iter);
            result = TRUE;
      }

      g_list_foreach (path_list, (GFunc) gtk_tree_path_free, NULL);
      g_list_free (path_list);
      return result;
}

gboolean    ug_category_move_selected_to_top (UgCategory* category)
{
      GtkTreeSelection* selection;
      GtkTreeModel*           model;
      GtkTreeIter             iter;
      UgDataApp*              appdata;
      UgDataset*              dataset;
      UgDataset*              temp;
      GList*                        path_list;
      GList*                        link;
      gint                    index;
      gint*                   indices;
      gboolean                result;

      selection = gtk_tree_view_get_selection (category->download_view);
      path_list = gtk_tree_selection_get_selected_rows (selection, &model);

      result = FALSE;
      dataset = NULL;
      for (link = path_list, index=0;  link;  link = link->next, index++) {
            indices = gtk_tree_path_get_indices (link->data);
            if (*indices != index)
                  result = TRUE;
            gtk_tree_model_get_iter (model, &iter, link->data);
            gtk_tree_model_get (model, &iter, 0, &temp, -1);
            ug_data_list_unlink (temp);   // remove link
            dataset = ug_data_list_prepend (dataset, temp);
      }

      g_list_foreach (path_list, (GFunc) gtk_tree_path_free, NULL);
      g_list_free (path_list);

      if (result == TRUE) {
            for (; dataset; dataset = dataset->next) {
                  appdata = UG_DATASET_APP (dataset);
                  if (appdata->category)
                        gtk_list_store_move_after (appdata->category->download_store, &appdata->category_iter, NULL);
                  if (appdata->total_list)
                        gtk_list_store_move_after (appdata->total_list->download_store, &appdata->total_list_iter, NULL);
            }
      }
      // scroll to top
      gtk_tree_view_scroll_to_point (category->download_view, -1, 0);

      return result;
}

gboolean    ug_category_move_selected_to_bottom (UgCategory* category)
{
      GtkTreeSelection* selection;
      GtkTreeModel*           model;
      GtkTreePath*            path;
      GtkTreeIter             iter;
      UgDataApp*              appdata;
      UgDataset*              dataset;
      UgDataset*              temp;
      GList*                        path_list;
      GList*                        link;
      gint                    index;
      gint*                   indices;
      gboolean                result;

      selection = gtk_tree_view_get_selection (category->download_view);
      path_list = gtk_tree_selection_get_selected_rows (selection, &model);

      index = gtk_tree_model_iter_n_children (model, NULL) - 1;
      result = FALSE;
      dataset = NULL;
      for (link = g_list_last (path_list);  link;  link = link->prev, index--) {
            indices = gtk_tree_path_get_indices (link->data);
            if (*indices != index)
                  result = TRUE;
            gtk_tree_model_get_iter (model, &iter, link->data);
            gtk_tree_model_get (model, &iter, 0, &temp, -1);
            ug_data_list_unlink (temp);   // remove link
            dataset = ug_data_list_prepend (dataset, temp);
      }

      g_list_foreach (path_list, (GFunc) gtk_tree_path_free, NULL);
      g_list_free (path_list);

      if (result == TRUE) {
            for (; dataset; dataset = dataset->next) {
                  appdata = UG_DATASET_APP (dataset);
                  if (appdata->category)
                        gtk_list_store_move_before (appdata->category->download_store, &appdata->category_iter, NULL);
                  if (appdata->total_list)
                        gtk_list_store_move_before (appdata->total_list->download_store, &appdata->total_list_iter, NULL);
            }
      }
      // scroll to bottom
      index = gtk_tree_model_iter_n_children (model, NULL) -1;
      path  = gtk_tree_path_new_from_indices (index, -1);
      gtk_tree_view_scroll_to_cell (category->download_view, path, NULL, FALSE, 0.0, 0.0);
      gtk_tree_path_free (path);

      return result;
}

// used by ug_category_queue_refresh ()
static void plugin_callback (gpointer plugin, const UgMessage* msg, UgDataset* dataset)
{
      UgProgress*       progress;
      UgDataApp*        appdata = UG_DATASET_APP (dataset);
      UgDataCommon*     common  = UG_DATASET_COMMON (dataset);

      switch (msg->type) {
      case UG_MESSAGE_STATE:
            appdata->plugin_state = msg->data.v_int;
            break;

      case UG_MESSAGE_PROGRESS:
            progress = ug_dataset_realloc (dataset, UgProgressClass, 0);
            ug_plugin_get (plugin, UG_DATA_TYPE_INSTANCE, progress);
            break;

      case UG_MESSAGE_ERROR:
            appdata->message_type = msg->type;
            ug_str_set (&appdata->message, msg->string, -1);
            appdata->list_icon = UG_LIST_ICON_ERROR;
            break;

      case UG_MESSAGE_WARNING:
            appdata->message_type = msg->type;
            ug_str_set (&appdata->message, msg->string, -1);
            break;

      case UG_MESSAGE_INFO:
            switch (msg->code) {
            case UG_MESSAGE_INFO_TRANSMIT:
                  appdata->list_icon = UG_LIST_ICON_EXECUTE;
                  break;

            case UG_MESSAGE_INFO_RETRY:
                  common->retry_count++;
                  appdata->list_icon = UG_LIST_ICON_REFRESH;
                  break;

            case UG_MESSAGE_INFO_FINISH:
                  appdata->list_icon = UG_LIST_ICON_COMPLETED;
                  break;

            case UG_MESSAGE_INFO_RESUMABLE:
            case UG_MESSAGE_INFO_NOT_RESUMABLE:
                  ug_str_set (&appdata->message, msg->string, -1);
                  break;

            default:
                  break;
            }
            break;

      case UG_MESSAGE_DATA:
            switch (msg->code) {
            case UG_MESSAGE_DATA_FILE_CHANGED:
                  if (msg->data.v_string)
                        ug_str_set (&common->file, msg->data.v_string, -1);
                  break;

            case UG_MESSAGE_DATA_URL_CHANGED:
            // HTTP message
            case UG_MESSAGE_DATA_HTTP_LOCATION:       // redirection
                  if (msg->data.v_string)
                        ug_str_set (&common->url, msg->data.v_string, -1);
                  break;

            default:
                  break;
            }

      default:
            break;
      }
}

gboolean    ug_category_queue_refresh (UgCategory* category, GRegex* regex)
{
      UgCategory* completed;
      UgDataset*  dataset;
      UgDataApp*  appdata;
      UgPathPart* pathpart;
      GList*            link;
      GList*            link_next;
      gboolean    updated = FALSE;

      completed = category->completed;
      for (link = category->running;  link;  link = link_next) {
            link_next = link->next;
            dataset   = link->data;
            appdata   = UG_DATASET_APP (dataset);
            ug_plugin_dispatch (appdata->plugin, (UgCallback) plugin_callback, dataset);

//          ug_plugin_get_state (appdata->plugin, &appdata->plugin_state);          // plugin_callback () has do this
            if (appdata->plugin_state == UG_STATE_STOP) {
                  ug_plugin_unref (appdata->plugin);
                  appdata->plugin = NULL;
                  if (appdata->list_icon != UG_LIST_ICON_ERROR && appdata->list_icon != UG_LIST_ICON_COMPLETED)
                        appdata->list_icon = UG_LIST_ICON_PAUSED;
                  category->running = g_list_remove (category->running, dataset);
                  category->running_count--;
                  updated = TRUE;
            }
            if (appdata->list_icon == UG_LIST_ICON_COMPLETED) {
                  ug_data_list_unlink (dataset);      // remove link
                  ug_category_move_to (category, dataset, completed);
                  updated = TRUE;
                  // match file type
                  pathpart = ug_path_part_new (UG_DATASET_COMMON(dataset)->file, -1);
                  if (pathpart->file_ext_len > 0  &&  regex  &&
                      g_regex_match_full (regex, pathpart->file_ext, pathpart->file_ext_len, 0, 0, NULL, NULL))
                  {
                        ug_launch_default_app (UG_DATASET_COMMON(dataset)->folder, UG_DATASET_COMMON(dataset)->file);
                  }
                  ug_path_part_free (pathpart);
            }
      }
      return updated;
}

gboolean    ug_category_queue_run (UgCategory* category)
{
      GtkTreeModel*     model;
      GtkTreeIter       iter;
      UgDataset*        dataset;
      UgDataApp*        appdata;
      gboolean          valid;
      gboolean          updated = FALSE;

      model = GTK_TREE_MODEL (category->download_store);
      valid = gtk_tree_model_get_iter_first (model, &iter);
      while (valid) {
            if (category->running_count >= category->running_limit)
                  break;
            gtk_tree_model_get (model, &iter, 0, &dataset, -1);
            appdata = UG_DATASET_APP (dataset);
            if (appdata->plugin == NULL && appdata->list_icon == UG_LIST_ICON_FILE) {
                  appdata->plugin = ug_plugin_new_by_data (dataset);
                  if (appdata->plugin) {
                        ug_plugin_set_state (appdata->plugin, UG_STATE_RUNNING);
                        appdata->list_icon = UG_LIST_ICON_EXECUTE;
                        category->running = g_list_prepend (category->running, dataset);
                        category->running_count++;
                        UG_DATASET_COMMON (dataset)->retry_count = 0;
                        updated = TRUE;
                  }
            }
            valid = gtk_tree_model_iter_next (model, &iter);
      }

      if (category->running_count) {
            if (category->list_icon != UG_LIST_ICON_EXECUTE) {
                  category->list_icon = UG_LIST_ICON_EXECUTE;
                  updated = TRUE;
            }
      }
      else if (category->list_icon == UG_LIST_ICON_EXECUTE) {
            category->list_icon = UG_LIST_ICON_CATEGORY;
            updated = TRUE;
      }

      return updated;
}

gboolean    ug_category_clear_excess (UgCategory* category)
{
      UgDataset*        dataset;
      UgDataset*        datalist;
      GtkTreeModel*     model;
      GtkTreeIter       iter;
      gboolean          valid;
      guint             count;

      datalist = NULL;
      model = GTK_TREE_MODEL (category->download_store);
      count = gtk_tree_model_iter_n_children (model, NULL);
      if (count <= category->capacity)
            return FALSE;
      valid = gtk_tree_model_iter_nth_child (model, &iter, NULL, category->capacity);
      while (valid) {
            gtk_tree_model_get (model, &iter, 0, &dataset, -1);
            valid = gtk_tree_model_iter_next (model, &iter);
            ug_data_list_unlink (dataset);      // remove link
            datalist = ug_data_list_prepend (datalist, dataset);
      }

      if (datalist) {
            ug_category_delete (category, datalist);
            return TRUE;
      }
      return FALSE;
}

gboolean    ug_category_stop_running      (UgCategory* category)
{
      UgDataset*  dataset;
      UgDataApp*  appdata;
      GList*            link;

      for (link = category->running;  link;  link = link->next) {
            dataset   = link->data;
            appdata   = UG_DATASET_APP (dataset);
            ug_plugin_set_state (appdata->plugin, UG_STATE_STOP);
      }

      return (category->running) ? TRUE : FALSE;
}

// queuing, completed, and recycled 3 in 1 functions
static void ug_category_apply_visible_1 (UgCategory* category)
{
      GtkTreeViewColumn*      column;

      gtk_tree_view_set_rules_hint (category->download_view, category->visible_column.rules_hint);
      // queue columns
      column = gtk_tree_view_get_column (category->download_view, UG_DOWNLOAD_COLUMN_COMPLETE);
      gtk_tree_view_column_set_visible (column, category->visible_column.completed);
      column = gtk_tree_view_get_column (category->download_view, UG_DOWNLOAD_COLUMN_TOTAL);
      gtk_tree_view_column_set_visible (column, category->visible_column.total);
      column = gtk_tree_view_get_column (category->download_view, UG_DOWNLOAD_COLUMN_PERCENT);
      gtk_tree_view_column_set_visible (column, category->visible_column.percent);
      column = gtk_tree_view_get_column (category->download_view, UG_DOWNLOAD_COLUMN_ELAPSED);
      gtk_tree_view_column_set_visible (column, category->visible_column.elapsed);
      column = gtk_tree_view_get_column (category->download_view, UG_DOWNLOAD_COLUMN_LEFT);
      gtk_tree_view_column_set_visible (column, category->visible_column.left);
      column = gtk_tree_view_get_column (category->download_view, UG_DOWNLOAD_COLUMN_SPEED);
      gtk_tree_view_column_set_visible (column, category->visible_column.speed);
      column = gtk_tree_view_get_column (category->download_view, UG_DOWNLOAD_COLUMN_RETRY);
      gtk_tree_view_column_set_visible (column, category->visible_column.retry);
      column = gtk_tree_view_get_column (category->download_view, UG_DOWNLOAD_COLUMN_CATEGORY);
      gtk_tree_view_column_set_visible (column, category->visible_column.category);
      column = gtk_tree_view_get_column (category->download_view, UG_DOWNLOAD_COLUMN_URL);
      gtk_tree_view_column_set_visible (column, category->visible_column.url);
}

void  ug_category_apply_visible (UgCategory* category)
{
      ug_category_apply_visible_1 (category->queuing);
      ug_category_apply_visible_1 (category->completed);
      ug_category_apply_visible_1 (category->recycled);
}

static void ug_category_assign_visible_1 (UgCategory* dest, UgCategory* src)
{
      dest->visible_column  = src->visible_column;
      dest->visible_summary = src->visible_summary;
}

void  ug_category_assign_visible (UgCategory* dest, UgCategory* src)
{
      ug_category_assign_visible_1 (dest->queuing, src->queuing);
      ug_category_assign_visible_1 (dest->completed, src->completed);
      ug_category_assign_visible_1 (dest->recycled,  src->recycled);
}

void  ug_category_clear_running_state (UgCategory* category)
{
      UgDataset*        dataset;
      UgDataApp*        appdata;
      GtkTreeModel*     model;
      GtkTreeIter       iter;
      gboolean          valid;

      model = GTK_TREE_MODEL (category->download_store);
      valid = gtk_tree_model_get_iter_first (model, &iter);
      while (valid) {
            gtk_tree_model_get (model, &iter, 0, &dataset, -1);
            valid = gtk_tree_model_iter_next (model, &iter);
            appdata = UG_DATASET_APP (dataset);
            if (appdata->list_icon == UG_LIST_ICON_EXECUTE || appdata->list_icon == UG_LIST_ICON_REFRESH)
                  appdata->list_icon = UG_LIST_ICON_FILE;
      }
}

// ----------------------------------------------------------------------------
// markup input/output for category

static void       ug_category_start_element (GMarkupParseContext* context,
                                           const gchar*           element_name,
                                           const gchar**    attr_names,
                                           const gchar**    attr_values,
                                           UgCategory*            category,
                                           GError**               error)
{
      if (strcmp (element_name, "queuing") == 0)
            g_markup_parse_context_push (context, &ug_data_parser, category->queuing);
      else if (strcmp (element_name, "completed") == 0)
            g_markup_parse_context_push (context, &ug_data_parser, category->completed);
      else if (strcmp (element_name, "recycled") == 0)
            g_markup_parse_context_push (context, &ug_data_parser, category->recycled);
      else  // Skip this element, don't parse anything.
            g_markup_parse_context_push (context, &ug_markup_skip_parser, GINT_TO_POINTER (6));
}

// UgCategory*  user_data
static GMarkupParser    ug_category_parser =
{
      (gpointer) ug_category_start_element,
      (gpointer) g_markup_parse_context_pop,
      NULL, NULL, NULL
};

void  ug_category_in_markup   (UgCategory** category_ptr, GMarkupParseContext* context)
{
      UgCategory* category = *category_ptr;

      if (category == category->queuing)
            g_markup_parse_context_push (context, &ug_category_parser, category->completed);
      else  // Skip this element, don't parse anything.
            g_markup_parse_context_push (context, &ug_markup_skip_parser, GINT_TO_POINTER (6));
}

void  ug_category_to_markup   (UgCategory** category_ptr, UgMarkup* markup)
{
      UgCategory* category = *category_ptr;

      if (category == category->queuing) {
            ug_markup_output_element_start (markup, "queuing");
            ug_data_to_markup ((UgData*) category->queuing, markup);
            ug_markup_output_element_end (markup, "queuing");

            ug_markup_output_element_start (markup, "completed");
            ug_data_to_markup ((UgData*) category->completed, markup);
            ug_markup_output_element_end (markup, "completed");

            ug_markup_output_element_start (markup, "recycled");
            ug_data_to_markup ((UgData*) category->recycled, markup);
            ug_markup_output_element_end (markup, "recycled");
      }
}


// ----------------------------------------------------------------------------
// markup input/output for category->download_store

static void ug_download_store_start_element (GMarkupParseContext* context,
                                             const gchar*         element_name,
                                             const gchar**        attr_names,
                                             const gchar**        attr_values,
                                             UgCategory*          category,
                                             GError**             error)
{
      UgDataset*        dataset;

      if (strcmp (element_name, "download") == 0) {
            dataset = ug_dataset_new_app ();
            ug_category_append (category, dataset);
            g_markup_parse_context_push (context, &ug_dataset_parser, dataset);
      }
      else {
            // Skip this element, don't parse anything.
            g_markup_parse_context_push (context, &ug_markup_skip_parser, GINT_TO_POINTER (6));
      }
}

// UgCategory*  user_data
static GMarkupParser    ug_download_store_parser =
{
      (gpointer) ug_download_store_start_element,
      (gpointer) g_markup_parse_context_pop,
      NULL, NULL, NULL
};

static void ug_download_store_in_markup (UgCategory* category, GMarkupParseContext* context)
{
      g_markup_parse_context_push (context, &ug_download_store_parser, category);
}

static void ug_download_store_to_markup (UgCategory* category, UgMarkup* markup)
{
      GtkTreeModel*     model;
      GtkTreeIter       iter;
      UgDataset*        dataset;
      gboolean          valid;

      model = GTK_TREE_MODEL (category->download_store);
      valid = gtk_tree_model_get_iter_first (model, &iter);
      while (valid) {
            gtk_tree_model_get (model, &iter, 0, &dataset, -1);
            valid = gtk_tree_model_iter_next (model, &iter);
            // output markup
            ug_markup_output_element_start (markup, "download");
            ug_data_to_markup ((UgData*) dataset, markup);
            ug_markup_output_element_end (markup, "download");
      }
}


Generated by  Doxygen 1.6.0   Back to index