Logo Search packages:      
Sourcecode: galeon version File versions

downloader-view.c

/*
 *  Copyright (C) 2000, 2001, 2002 Marco Pesenti Gritti
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "downloader-view.h"
#include "eel-gconf-extensions.h"
#include "gul-gui.h"
#include "prefs-strings.h"
#include "gul-general.h"
#include "gul-ellipsizing-label.h"
#include "galeon-embed-utils.h"

#include <gtk/gtktreeview.h>
#include <gtk/gtkliststore.h>
#include <gtk/gtktreeviewcolumn.h>
#include <gtk/gtktreeselection.h>
#include <gtk/gtktable.h>
#include <gtk/gtkdialog.h>
#include <gtk/gtkcellrenderertext.h>
#include <gtk/gtkmessagedialog.h>
#include <gtk/gtkprogressbar.h>
#include <gtk/gtkexpander.h>
#include <gtk/gtkmain.h>

#include <glib/gi18n.h>
#include <libgnomeui/gnome-dialog-util.h>
#include <libgnomeui/gnome-dialog.h>
#include <libgnomevfs/gnome-vfs-mime-handlers.h>
#include <libgnomevfs/gnome-vfs-utils.h>

#define KILOBYTE_FACTOR (1024.0)
#define MEGABYTE_FACTOR (1024.0 * 1024.0)

/* WHACK WHACK WHACK FIXME please
 * (nevermind the type mismatch, we'd typecast it away anyway)
 * This works, but isn't really nice.  We shouldn't be referencing outside
 * embed/ directory, but I just can't seem to find a better solution...
 */
extern GObject *galeon_shell;

enum
{
      COL_PERCENT,
      COL_FILENAME,
      COL_SIZE,
      COL_REMAINING,
      COL_DETAILS,
      COL_REMAINING_TIME
};

#define DOWNLOADER_VIEW_GET_PRIVATE(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object), \
                               TYPE_DOWNLOADER_VIEW, DownloaderViewPrivate))


struct DownloaderViewPrivate
{
      GtkTreeModel *model;
      gboolean show_details;

      /* Widgets */
      GtkWidget *treeview;
      GtkWidget *details_file;
      GtkWidget *details_location;
      GtkWidget *details_status;
      GtkWidget *details_elapsed;
      GtkWidget *details_remaining;
      GtkWidget *details_progress;
      GtkWidget *details_button;
      GtkWidget *keep_open_check;
      GtkWidget *pause_button;
      GtkWidget *resume_button;
      GtkWidget *abort_button;
      GtkWidget *open_button;
};

#define TYPE_DOWNLOAD   (download_get_type ())

typedef enum
{
      DOWNLOAD_STATUS_DOWNLOADING,
      DOWNLOAD_STATUS_PAUSED,
      DOWNLOAD_STATUS_RESUMING,
      DOWNLOAD_STATUS_COMPLETED,
      DOWNLOAD_STATUS_CANCELLED
} DownloadStatus;

#define STATUS_IS_UNFINISHED(status) \
      ((status) != DOWNLOAD_STATUS_COMPLETED && \
       (status) != DOWNLOAD_STATUS_CANCELLED)

typedef struct _Download DownloadDetails;
struct _Download
{
      GObject parent;

      DownloaderView *view;
      GtkTreeRowReference *ref;

      guint  elapsed;         /* seconds */
      guint  remaining; /* seconds (G_MAXUINT if indeterminate) */
      guint  speed;           /* bytes per second */
      gulong size_total;      /* bytes (G_MAXULONG if indeterminate) */
      gulong size_done; /* bytes */
      gfloat progress;  /* 0.0 .. 1.0 (or < 0 if indeterminate) */

      gboolean can_pause;
      gchar *filename;
      gchar *source;
      gchar *dest;
      DownloadStatus status;
};

enum {
      CANCEL,
      PAUSE,
      RESUME,
        LAST_SIGNAL
};

static GObjectClass *download_parent_class = NULL;
static guint download_signals[LAST_SIGNAL] = { 0 };

static void
download_init (Download *self)
{
}

static void
download_finalize (GObject *object)
{
      Download *self = (Download *) object;

      g_free (self->filename);
      g_free (self->source);
      g_free (self->dest);
}

static void
download_class_init (DownloadClass *klass)
{
      GObjectClass *object_class = G_OBJECT_CLASS(klass);

      download_parent_class = g_type_class_peek_parent (klass);

      object_class->finalize = download_finalize;

        /* signals */
        download_signals[CANCEL] =
                g_signal_new ("cancel",
                              G_OBJECT_CLASS_TYPE (object_class),
                              G_SIGNAL_RUN_LAST,
                              G_STRUCT_OFFSET (DownloadClass, cancel),
                              NULL, NULL,
                              g_cclosure_marshal_VOID__VOID,
                              G_TYPE_NONE, 0);
      download_signals[PAUSE] =
                g_signal_new ("pause",
                              G_OBJECT_CLASS_TYPE (object_class),
                              G_SIGNAL_RUN_LAST,
                              G_STRUCT_OFFSET (DownloadClass, pause),
                              NULL, NULL,
                              g_cclosure_marshal_VOID__VOID,
                              G_TYPE_NONE, 0);
      download_signals[RESUME] =
                g_signal_new ("resume",
                              G_OBJECT_CLASS_TYPE (object_class),
                              G_SIGNAL_RUN_LAST,
                              G_STRUCT_OFFSET (DownloadClass, resume),
                              NULL, NULL,
                              g_cclosure_marshal_VOID__VOID,
                              G_TYPE_NONE, 0);
}

static GType
download_get_type (void)
{
      static GType type = 0;

      if (type == 0)
      {
            static const GTypeInfo info =
            {
                  sizeof (DownloadClass),
                  NULL, /* base_init */
                  NULL, /* base_finalize */
                  (GClassInitFunc) download_class_init,
                  NULL, /* class_finalize */
                  NULL, /* class_data */
                  sizeof (Download),
                  0,    /* n_preallocs */
                  (GInstanceInitFunc) download_init
            };

            type = g_type_register_static (G_TYPE_OBJECT, "Download", &info, 0);
      }

      return type;
}

typedef struct
{
      gboolean can_resume;
      gboolean can_pause;
      gboolean can_abort;
      gboolean can_open;
      DownloaderViewPrivate *priv;
} ControlsInfo;

enum
{
      PROP_TREEVIEW,
      PROP_KEEP_OPEN,
      PROP_DETAILS_FRAME,
      PROP_DETAILS_SEPARATOR,
      PROP_DETAILS_TABLE,
      PROP_DETAILS_STATUS,
      PROP_DETAILS_ELAPSED,
      PROP_DETAILS_REMAINING,
      PROP_DETAILS_PROGRESS,
      PROP_PAUSE_BUTTON,
      PROP_RESUME_BUTTON,
      PROP_ABORT_BUTTON,
      PROP_OPEN_BUTTON,
      PROP_DETAILS_BUTTON,
      PROP_DOWNLOAD_VBOX
};

static const
GaleonDialogProperty properties [] =
{
        { PROP_TREEVIEW, "clist", NULL, PT_NORMAL, NULL },
      { PROP_KEEP_OPEN, "keep_open_check", CONF_DOWNLOADING_KEEP_OPEN, PT_NORMAL, NULL },
      { PROP_DETAILS_FRAME, "details_frame", NULL, PT_NORMAL, NULL },
      { PROP_DETAILS_SEPARATOR, "details_separator", NULL, PT_NORMAL, NULL },
      { PROP_DETAILS_TABLE, "details_table", NULL, PT_NORMAL, NULL },
      { PROP_DETAILS_STATUS, "details_status", NULL, PT_NORMAL, NULL },
      { PROP_DETAILS_ELAPSED, "details_elapsed", NULL, PT_NORMAL, NULL },
      { PROP_DETAILS_REMAINING, "details_remaining", NULL, PT_NORMAL, NULL },
      { PROP_DETAILS_PROGRESS, "details_progress", NULL, PT_NORMAL, NULL },
      { PROP_PAUSE_BUTTON, "pause_button", NULL, PT_NORMAL, NULL },
      { PROP_RESUME_BUTTON, "resume_button", NULL, PT_NORMAL, NULL },
      { PROP_ABORT_BUTTON, "abort_button", NULL, PT_NORMAL, NULL },
      { PROP_OPEN_BUTTON, "open_button", NULL, PT_NORMAL, NULL },
      { PROP_DETAILS_BUTTON, "details_expander", CONF_DOWNLOADING_SHOW_DETAILS, PT_NORMAL, NULL },
      { PROP_DOWNLOAD_VBOX, "download_vbox", NULL, PT_NORMAL, NULL },
      
        { -1, NULL, NULL }
};

static void
downloader_view_build_ui (DownloaderView *dv);
static void
downloader_view_class_init (DownloaderViewClass *klass);
static void
downloader_view_finalize (GObject *object);
static void
downloader_view_init (DownloaderView *dv);

/* Callbacks */
void 
download_dialog_resume_cb (GtkButton *button, DownloaderView *dv);
void 
download_dialog_pause_cb (GtkButton *button, DownloaderView *dv);
void 
download_dialog_abort_cb (GtkButton *button, DownloaderView *dv);
static void
downloader_treeview_selection_changed_cb (GtkTreeSelection *selection,
                                DownloaderView *dv);
gboolean
download_dialog_delete_cb (GtkWidget *window, GdkEventAny *event,
                     DownloaderView *dv);
void
download_dialog_details_cb(GtkExpander *expander, 
                     GParamSpec *pspec,
                     DownloaderView *dv);
void
download_dialog_open_cb (GtkWidget *button, 
                   DownloaderView *dv);

static GObjectClass *parent_class = NULL;

GType
downloader_view_get_type (void)
{
       static GType downloader_view_type = 0;

        if (downloader_view_type == 0)
        {
                static const GTypeInfo our_info =
                {
                        sizeof (DownloaderViewClass),
                        NULL, /* base_init */
                        NULL, /* base_finalize */
                        (GClassInitFunc) downloader_view_class_init,
                        NULL, /* class_finalize */
                        NULL, /* class_data */
                        sizeof (DownloaderView),
                        0,    /* n_preallocs */
                        (GInstanceInitFunc) downloader_view_init
                };

                downloader_view_type = g_type_register_static (GALEON_TYPE_DIALOG,
                                                               "DownloaderView",
                                                               &our_info, 0);
        }

        return downloader_view_type;
}

static void
format_time (gchar *buffer, glong time)
{
      gint secs, hours, mins;

      secs = (gint)(time + .5);
      hours = secs / 3600;
      secs -= hours * 3600;
      mins = secs / 60;
      secs -= mins * 60;

      if (hours)
      {
            sprintf (buffer, "%u:%02u.%02u", hours, mins, secs);
      }
      else
      {
            sprintf (buffer, "%02u.%02u", mins, secs);
      }
}

static void
downloader_view_class_init (DownloaderViewClass *klass)
{
      GObjectClass *object_class = G_OBJECT_CLASS (klass);
      GaleonDialogClass *galeon_dialog_class;
        
        parent_class = g_type_class_peek_parent (klass);
        galeon_dialog_class = GALEON_DIALOG_CLASS (klass);

        object_class->finalize = downloader_view_finalize;

      g_type_class_add_private (klass, sizeof (DownloaderViewPrivate));
}

static gboolean
destroy_details_foreach (GtkTreeModel *model,
                     GtkTreePath  *path,
                   GtkTreeIter  *iter,
                   gpointer      user_data)
{
      gtk_list_store_set (GTK_LIST_STORE(model), iter, COL_DETAILS, NULL, -1);
      return FALSE;
}

static void
downloader_view_init (DownloaderView *dv)
{
        dv->priv = DOWNLOADER_VIEW_GET_PRIVATE (dv);
      
      downloader_view_build_ui (dv);

      /* make sure galeon doesn't exit if the user closes the last window
       * while we're still downloading stuff */
      g_object_ref (galeon_shell);
}

static void
downloader_view_finalize (GObject *object)
{
        DownloaderView *dv;

        g_return_if_fail (object != NULL);
        g_return_if_fail (IS_DOWNLOADER_VIEW (object));

        dv = DOWNLOADER_VIEW (object);

        g_return_if_fail (dv->priv != NULL);

      gtk_tree_model_foreach (dv->priv->model, destroy_details_foreach, NULL);

      g_object_unref (dv->priv->model);

        G_OBJECT_CLASS (parent_class)->finalize (object);

      /* we're done, you may go away now */
      g_object_unref (galeon_shell);
}

DownloaderView *
downloader_view_new (void)
{
      return DOWNLOADER_VIEW (g_object_new 
                  (TYPE_DOWNLOADER_VIEW, NULL));
}

static void
controls_info_foreach (GtkTreeModel *model,
                   GtkTreePath  *path,
                   GtkTreeIter  *iter,
                   ControlsInfo *info)
{
      DownloadDetails *details;
      
      gtk_tree_model_get (model, iter, COL_DETAILS, &details, -1);

      info->can_abort  |= STATUS_IS_UNFINISHED(details->status);
      info->can_pause  |= (details->status == DOWNLOAD_STATUS_DOWNLOADING && details->can_pause);
      info->can_resume |= (details->status == DOWNLOAD_STATUS_PAUSED);
      info->can_open   |= (details->status == DOWNLOAD_STATUS_COMPLETED);

      g_object_unref (details);
}

static void
downloader_view_update_controls (DownloaderViewPrivate *priv)
{
      GtkTreeSelection *selection;
      ControlsInfo *info;

      selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(priv->treeview));

      info = g_new0 (ControlsInfo, 1);
      info->priv = priv;

      /* initial conditions */
      info->can_pause = info->can_resume = info->can_abort 
            = info->can_open = FALSE;

      if (selection)
      {
            gtk_tree_selection_selected_foreach 
                  (selection, 
                   (GtkTreeSelectionForeachFunc)controls_info_foreach, 
                   info);
      }

      /* setup buttons */
      gtk_widget_set_sensitive (priv->pause_button, info->can_pause);
      gtk_widget_set_sensitive (priv->resume_button, info->can_resume);
      gtk_widget_set_sensitive (priv->abort_button, info->can_abort);
      gtk_widget_set_sensitive (priv->open_button, info->can_open);

      g_free (info);
}

static char *
format_progress (gulong done, gulong total)
{
      if (total < KILOBYTE_FACTOR)
            return g_strdup_printf (_("%u of %u bytes"),
                                (guint)done, (guint)total);
      else if (total < MEGABYTE_FACTOR)
            return g_strdup_printf (_("%.1f of %.1f KB"),
                                done/KILOBYTE_FACTOR,
                              total/KILOBYTE_FACTOR);
      else
            return g_strdup_printf (_("%.1f of %.1f MB"),
                                done/MEGABYTE_FACTOR,
                              total/MEGABYTE_FACTOR);
}

static void
format_speed (char *buf, gulong len, guint speed)
{
      if (speed < MEGABYTE_FACTOR)
      {
            g_snprintf (buf, len, _("%.1f KB/s"), speed/KILOBYTE_FACTOR);
      }
      else
      {
            g_snprintf (buf, len, _("%.1f MB/s"), speed/MEGABYTE_FACTOR);
      }
}

static char *
format_status (guint speed, gulong size_done, gulong size_total)
{
      char *status;

      if (size_total < G_MAXULONG)
      {
            status = format_progress (size_done, size_total);
      }
      else
      {
            status = gnome_vfs_format_file_size_for_display (size_done);
      }

      if (speed > 0)
      {
            char spd[16];
            char *tmp;

            format_speed (spd, sizeof(spd), speed);

            /* To translators: the following is for download progress, for
             * example: "1.0 of 4.2 MB at 42.0 KB/s" or
             * "1.2 MB at 14.6 KB/s"
             */
            tmp = g_strdup_printf (_("%s at %s"), status, spd);
            g_free (status);
            status = tmp;
      }

      return status;
}

static void
downloader_view_update_details (DownloaderViewPrivate *priv, 
                        DownloadDetails *details)
{
      gchar buffer[50];
      char *status;

      gul_ellipsizing_label_set_text 
            (GUL_ELLIPSIZING_LABEL (priv->details_location),
             details->source);
      gul_ellipsizing_label_set_text 
            (GUL_ELLIPSIZING_LABEL (priv->details_file),
             details->filename);

      status = format_status (details->speed, details->size_done, details->size_total);
      gtk_label_set_text (GTK_LABEL (priv->details_status), status);
      g_free (status);

      format_time (buffer, details->elapsed);
      gtk_label_set_text (GTK_LABEL (priv->details_elapsed), buffer);

      if (details->remaining < G_MAXUINT)
      {
            format_time (buffer, details->remaining);
      }
      else
      {
            sprintf (buffer, _("Unknown"));
      }
      gtk_label_set_text (GTK_LABEL (priv->details_remaining), buffer);

      if (details->progress >= 0)
      {
            gtk_progress_bar_set_fraction
                  (GTK_PROGRESS_BAR (priv->details_progress),
                   details->progress);
      }
      else
      {
            gtk_progress_bar_pulse
                  (GTK_PROGRESS_BAR (priv->details_progress));
      }
}

static gboolean
get_selected_row (DownloaderViewPrivate *priv, GtkTreeIter *iter)
{
      GList *refs;
      GtkTreeRowReference *node;
      GtkTreePath *path;
      
      refs = gul_gui_treeview_get_selection_refs 
            (GTK_TREE_VIEW(priv->treeview));
      if (refs == NULL) return FALSE;
      
      node = refs->data;
      path = gtk_tree_row_reference_get_path (node);
      gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->model), iter, path);
      gtk_tree_path_free (path);

      gul_gui_treeview_free_selection_refs (refs);

      return TRUE;
}

static void
downloader_view_set_download_info (DownloaderViewPrivate *priv, 
                           DownloadDetails *details,
                           GtkTreeIter *iter)
{
      gchar buffer[50];
      char *size;
      GtkTreePath *path;
      GtkTreePath *selected_path = NULL;
      GtkTreeIter selected_iter;
      GtkTreeSelection *selection;

      selection = gtk_tree_view_get_selection 
            (GTK_TREE_VIEW(priv->treeview));

      if (get_selected_row (priv, &selected_iter))
      {     
            selected_path = gtk_tree_model_get_path 
                  (priv->model, &selected_iter);
      }

      path = gtk_tree_row_reference_get_path (details->ref);
      
      gtk_list_store_set (GTK_LIST_STORE (priv->model),
                      iter, 
                      COL_FILENAME, details->filename,
                      -1);

      /* Progress */
      if (details->progress >= 0)
      {
            sprintf (buffer, "%.1f%%", details->progress * 100.0);
      }
      else
      {
            sprintf (buffer, "---");
      }
      gtk_list_store_set (GTK_LIST_STORE (priv->model),
                      iter,
                      COL_PERCENT, buffer,
                      -1);
      
      /* Total */
      if (details->size_total < G_MAXULONG)
      {
            size = gnome_vfs_format_file_size_for_display (details->size_total);
      }
      else
      {
            size = g_strdup (_("Unknown")); 
      }
      gtk_list_store_set (GTK_LIST_STORE (priv->model),
                      iter,
                      COL_SIZE, size,
                      -1);
      g_free (size);

      /* Remaining */
      if (details->remaining < G_MAXUINT)
      {
            format_time (buffer, details->remaining);
      }
      else
      {
            sprintf (buffer, _("Unknown"));
      }
      gtk_list_store_set (GTK_LIST_STORE (priv->model),
                      iter,
                      COL_REMAINING, buffer,
                      COL_REMAINING_TIME, details->remaining,
                      -1);

      if (gtk_tree_path_compare (path, selected_path) == 0)
      {
            downloader_view_update_details (priv, details);
      }
      gtk_tree_path_free (selected_path);
      gtk_tree_path_free (path);
}

static void
ensure_selected_row (DownloaderView *dv)
{
      GtkTreeIter iter;
      GtkTreeSelection *selection;

      g_return_if_fail (IS_DOWNLOADER_VIEW(dv));
      
      selection = gtk_tree_view_get_selection 
            (GTK_TREE_VIEW(dv->priv->treeview));
      if (get_selected_row (dv->priv, &iter))
      {
            /* there is already a selection */
            return;
      }

      if (gtk_tree_model_get_iter_first (dv->priv->model, &iter))
      {
            gtk_tree_selection_select_iter (selection, &iter);
      }
}

Download *
downloader_view_add_download (DownloaderView *dv,
                        const char *source,
                        const char *dest)
{
      GtkTreeIter iter;
      GtkTreePath *path;
      DownloadDetails *details;
      GtkTreeSelection *selection;
      
      details = g_object_new (TYPE_DOWNLOAD, NULL);
      details->view = dv;
      details->filename = g_path_get_basename (dest);
      details->source = g_strdup (source);
      details->dest = g_strdup (dest);
      details->elapsed = 0;
      details->remaining = G_MAXUINT;
      details->speed = 0;
      details->size_total = G_MAXULONG;
      details->size_done = 0;
      details->progress = -1;
      dv->priv->show_details = FALSE;

      gtk_list_store_append (GTK_LIST_STORE (dv->priv->model), 
                         &iter);

      path = gtk_tree_model_get_path (GTK_TREE_MODEL(dv->priv->model), &iter);
      details->ref = gtk_tree_row_reference_new
            (GTK_TREE_MODEL (dv->priv->model), path);
      gtk_tree_path_free (path);

      gtk_list_store_set (GTK_LIST_STORE (dv->priv->model),
                      &iter,
                      COL_DETAILS, details,
                      -1);

      selection = gtk_tree_view_get_selection 
            (GTK_TREE_VIEW(dv->priv->treeview));
      gtk_tree_selection_unselect_all (selection);
      gtk_tree_selection_select_iter (selection, &iter);
      
      downloader_view_set_download_info (dv->priv, details, &iter);

      galeon_dialog_show (GALEON_DIALOG(dv));

      g_object_unref (details); /* the model holds our reference(??) */
      return details;
}

static void
downloader_view_remove_download (DownloaderView *dv,
                         DownloadDetails *details)
{
      GtkTreeIter iter;
      GValue keep_open = {0, };
      GtkTreePath *path;

      g_return_if_fail (details);

      path = gtk_tree_row_reference_get_path (details->ref);
      gtk_tree_model_get_iter (GTK_TREE_MODEL (dv->priv->model), 
                         &iter, path);
      gtk_tree_path_free (path);

      gtk_list_store_remove (GTK_LIST_STORE (dv->priv->model), &iter);

        galeon_dialog_get_value (GALEON_DIALOG(dv), PROP_KEEP_OPEN, &keep_open);
      
      if (!g_value_get_boolean (&keep_open) &&
          !gtk_tree_model_get_iter_first (dv->priv->model, &iter))
      {
            g_object_unref (dv);
      }
      else
      {
            ensure_selected_row (dv);
      }
}

void
download_set_progress (Download     *details,
                   guint       elapsed,
                   guint       remaining,
                   guint       speed,
                   gulong      size_done,
                   gulong      size_total)
{
      details->elapsed = elapsed;
      details->remaining = remaining;
      details->speed = speed;
      details->size_done = size_done;

      if (details->status == DOWNLOAD_STATUS_RESUMING)
      {
            details->status = DOWNLOAD_STATUS_DOWNLOADING;
      }

      download_set_size_total (details, size_total);
}

void
download_set_size_total (DownloadDetails *details, gulong size_total)
{
      DownloaderView *dv;
      GtkTreePath *path;
      GtkTreeIter  iter;

      details->size_total = size_total;
      if (size_total > 0 && size_total < G_MAXULONG)
      {
            details->progress = (gfloat)details->size_done / size_total;
      }

      dv = details->view;

      path = gtk_tree_row_reference_get_path (details->ref);
      gtk_tree_model_get_iter (GTK_TREE_MODEL (dv->priv->model), 
                         &iter, path);
      gtk_tree_path_free (path);

      downloader_view_set_download_info (dv->priv, details, &iter); 
}

void
download_set_can_pause (DownloadDetails *details, gboolean can_pause)
{
      details->can_pause = can_pause;
      downloader_view_update_controls (details->view->priv);
}

static void
download_set_status (DownloadDetails *details, DownloadStatus status)
{
      DownloaderView *dv;
      GtkTreePath *path;
      GtkTreeIter iter;
      GValue keep_open = {0, };

      g_return_if_fail (details);
      dv = details->view;

      details->status = status;

      path = gtk_tree_row_reference_get_path (details->ref);
      gtk_tree_model_get_iter (GTK_TREE_MODEL (dv->priv->model), 
                         &iter, path);
      gtk_tree_path_free (path);

      downloader_view_set_download_info (dv->priv, details, &iter);
      downloader_view_update_controls (dv->priv);
            
        galeon_dialog_get_value (GALEON_DIALOG(dv), PROP_KEEP_OPEN, &keep_open);
      
      if (!STATUS_IS_UNFINISHED(status) &&
          !g_value_get_boolean (&keep_open))
      {
            downloader_view_remove_download (dv, details);
      }
} 

void
download_paused (Download *self)
{
      download_set_status (self, DOWNLOAD_STATUS_PAUSED);
}

void
download_resumed (Download *self)
{
      download_set_status (self, DOWNLOAD_STATUS_RESUMING);
}

void
download_completed (Download *self)
{
      if (self->size_total < G_MAXULONG)
      {
            self->size_done = self->size_total;
      }
      else
      {
            self->size_total = self->size_done;
      }
      self->remaining = 0;
      self->progress = 1;

      download_set_status (self, DOWNLOAD_STATUS_COMPLETED);
}

void
download_cancelled (Download *self)
{
      self->remaining = 0;
      download_set_status (self, DOWNLOAD_STATUS_CANCELLED);
}

static void
downloader_view_build_ui (DownloaderView *dv)
{
      DownloaderViewPrivate *priv = dv->priv;
      GtkListStore *liststore;
      GtkTreeViewColumn *column;
      GtkCellRenderer *renderer;
      GtkTreeSelection *selection;
      GtkWidget *details_table;
      GtkWidget *download_vbox;
      GaleonDialog *d = GALEON_DIALOG (dv);

      galeon_dialog_construct (d,
                                 properties,
                                 "galeon.glade", 
                                 "download_manager_dialog");
      
      /* lookup needed widgets */
      priv->treeview = galeon_dialog_get_control (d, PROP_TREEVIEW);
      priv->details_status = galeon_dialog_get_control (d, PROP_DETAILS_STATUS);
      priv->details_elapsed = galeon_dialog_get_control (d, PROP_DETAILS_ELAPSED);
      priv->details_remaining = galeon_dialog_get_control (d, PROP_DETAILS_REMAINING);
      priv->details_progress = galeon_dialog_get_control (d, PROP_DETAILS_PROGRESS);
      priv->keep_open_check = galeon_dialog_get_control (d, PROP_KEEP_OPEN);
      priv->details_button = galeon_dialog_get_control (d, PROP_DETAILS_BUTTON);
      priv->pause_button = galeon_dialog_get_control (d, PROP_PAUSE_BUTTON);
      priv->resume_button = galeon_dialog_get_control (d, PROP_RESUME_BUTTON);
      priv->abort_button = galeon_dialog_get_control (d, PROP_ABORT_BUTTON);
      priv->open_button = galeon_dialog_get_control (d, PROP_OPEN_BUTTON);
      details_table = galeon_dialog_get_control (d, PROP_DETAILS_TABLE);

      /* See bug 139561 */
      download_vbox = galeon_dialog_get_control (d, PROP_DOWNLOAD_VBOX);
      gtk_widget_set_redraw_on_allocate (GTK_WIDGET (download_vbox), TRUE);

      /* Set inital visibility state of details */
      download_dialog_details_cb (GTK_EXPANDER (priv->details_button), NULL, dv);

      priv->details_location = gul_ellipsizing_label_new ("");
      priv->details_file = gul_ellipsizing_label_new ("");
      gtk_table_attach_defaults (GTK_TABLE(details_table), priv->details_location,
                           1, 2, 0, 1);
      gtk_table_attach_defaults (GTK_TABLE(details_table), priv->details_file,
                           1, 2, 1, 2);
      gtk_misc_set_alignment (GTK_MISC(priv->details_location), 0, 0);
      gtk_misc_set_alignment (GTK_MISC(priv->details_file), 0, 0);
      gtk_label_set_selectable (GTK_LABEL(priv->details_location), TRUE);
      gtk_label_set_selectable (GTK_LABEL(priv->details_file), TRUE);
      gtk_widget_show (priv->details_location);
      gtk_widget_show (priv->details_file);

      liststore = gtk_list_store_new (6,
                              G_TYPE_STRING,
                              G_TYPE_STRING,
                              G_TYPE_STRING,
                              G_TYPE_STRING,
                              TYPE_DOWNLOAD,
                              G_TYPE_INT);

      gtk_tree_view_set_model (GTK_TREE_VIEW(priv->treeview), 
                         GTK_TREE_MODEL (liststore));
      gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(priv->treeview),
                                 TRUE);

      renderer = gtk_cell_renderer_text_new ();
      
      gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW(priv->treeview),
                                         0, _("%"),
                                         renderer,
                                         "text", 0,
                                         NULL);
      column = gtk_tree_view_get_column (GTK_TREE_VIEW(priv->treeview), 0);
        gtk_tree_view_column_set_resizable (column, TRUE);
        gtk_tree_view_column_set_reorderable (column, TRUE);
        gtk_tree_view_column_set_sort_column_id (column, COL_PERCENT);

      gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW(priv->treeview),
                                         COL_FILENAME, _("Filename"),
                                         renderer,
                                         "text", COL_FILENAME,
                                         NULL);
      column = gtk_tree_view_get_column (GTK_TREE_VIEW(priv->treeview), 
                                 COL_FILENAME);
        gtk_tree_view_column_set_resizable (column, TRUE);
        gtk_tree_view_column_set_reorderable (column, TRUE);
        gtk_tree_view_column_set_sort_column_id (column, COL_FILENAME);

      gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW(priv->treeview),
                                         COL_SIZE, _("Size"),
                                         renderer,
                                         "text", COL_SIZE,
                                         NULL);
      column = gtk_tree_view_get_column (GTK_TREE_VIEW(priv->treeview),
                                 COL_SIZE);
        gtk_tree_view_column_set_resizable (column, TRUE);
        gtk_tree_view_column_set_reorderable (column, TRUE);
        gtk_tree_view_column_set_sort_column_id (column, COL_SIZE);

      gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW(priv->treeview),
                                         COL_REMAINING, _("Remaining"),
                                         renderer,
                                         "text", COL_REMAINING,
                                         NULL);
      column = gtk_tree_view_get_column (GTK_TREE_VIEW(priv->treeview),
                                 COL_REMAINING);
        gtk_tree_view_column_set_resizable (column, TRUE);
        gtk_tree_view_column_set_reorderable (column, TRUE);
        gtk_tree_view_column_set_sort_column_id (column, COL_REMAINING_TIME);

      selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(priv->treeview));
      gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
        g_signal_connect (G_OBJECT (selection), "changed",
                          G_CALLBACK (downloader_treeview_selection_changed_cb), dv);

      priv->model = GTK_TREE_MODEL (liststore);
}

static void
resume_selection_foreach (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter,
                    DownloaderView *dv)
{
      DownloadDetails *details;
      
      gtk_tree_model_get (model, iter, COL_DETAILS, &details, -1);
      g_return_if_fail (details);

      if (details->status == DOWNLOAD_STATUS_PAUSED)
      {
            g_signal_emit (G_OBJECT (details), download_signals[RESUME], 0);
      }
      g_object_unref (details);
}

void 
download_dialog_resume_cb (GtkButton *button, DownloaderView *dv)
{
      GtkTreeSelection *selection;

      selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(dv->priv->treeview));

      gtk_tree_selection_selected_foreach 
            (selection,
             (GtkTreeSelectionForeachFunc)resume_selection_foreach,
             (gpointer)dv);
}

static void
pause_selection_foreach (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter,
                   DownloaderView *dv)
{
      DownloadDetails *details;
      
      gtk_tree_model_get (model, iter, COL_DETAILS, &details, -1);
      g_return_if_fail (details);
      
      if (details->status == DOWNLOAD_STATUS_DOWNLOADING && details->can_pause)
      {
            g_signal_emit (G_OBJECT (details), download_signals[PAUSE], 0);
      }
      g_object_unref (details);
}

void 
download_dialog_pause_cb (GtkButton *button, DownloaderView *dv)
{
      GtkTreeSelection *selection;

      selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(dv->priv->treeview));

      gtk_tree_selection_selected_foreach 
            (selection,
             (GtkTreeSelectionForeachFunc)pause_selection_foreach,
             (gpointer)dv);
}

void 
download_dialog_abort_cb (GtkButton *button, DownloaderView *dv)
{
      GList *refs, *l;
      
      refs = gul_gui_treeview_get_selection_refs (GTK_TREE_VIEW(dv->priv->treeview));

        for (l = refs; l != NULL; l = l->next)
        {
                GtkTreeRowReference *node = l->data;
            GtkTreeIter iter;
            GtkTreePath *path;
            DownloadDetails *details;
                  
            path = gtk_tree_row_reference_get_path (node);
                gtk_tree_model_get_iter (GTK_TREE_MODEL (dv->priv->model), &iter,
                               path);
            gtk_tree_path_free (path);
      
            gtk_tree_model_get (dv->priv->model, &iter, COL_DETAILS, &details, -1);
            g_return_if_fail (details);
                        
            g_signal_emit (G_OBJECT (details), download_signals[CANCEL], 0);
            
            g_object_unref (details);
        }

      gul_gui_treeview_free_selection_refs (refs);
}

static void
downloader_treeview_selection_changed_cb (GtkTreeSelection *selection,
                                DownloaderView *dv)
{
      GtkTreeIter iter;
      DownloadDetails *details = NULL;
      GtkWidget *details_frame;
      DownloaderViewPrivate *priv= dv->priv;

      details_frame = galeon_dialog_get_control (GALEON_DIALOG(dv), 
                                       PROP_DETAILS_FRAME);
      
      if (get_selected_row (priv, &iter))
      {
            gtk_tree_model_get (priv->model, &iter, COL_DETAILS, &details, -1);
            g_return_if_fail (details);

            gtk_widget_set_sensitive (details_frame, TRUE);

            downloader_view_update_details (priv, details);
            downloader_view_update_controls (priv);
            g_object_unref (details);
      }
      else
      {
            gtk_label_set_text (GTK_LABEL (priv->details_location), "");
            gtk_label_set_text (GTK_LABEL (priv->details_file), "");
            gtk_label_set_text (GTK_LABEL (priv->details_status), "");
            gtk_label_set_text (GTK_LABEL (priv->details_elapsed), "");
            gtk_label_set_text (GTK_LABEL (priv->details_remaining), "");
            gtk_progress_bar_set_fraction
                  (GTK_PROGRESS_BAR (priv->details_progress),
                   0);
            
            gtk_widget_set_sensitive (details_frame, FALSE);
      }
}

static gboolean
alive_download_foreach (GtkTreeModel *model,
                    GtkTreePath  *path,
                  GtkTreeIter  *iter,
                  gpointer      user_data)
{
      gboolean        *alive = user_data;
      DownloadDetails *details;
      
      gtk_tree_model_get (model, iter, COL_DETAILS, &details, -1);
      g_return_val_if_fail (details != NULL, TRUE);

      if (STATUS_IS_UNFINISHED(details->status))
      {
            *alive = TRUE;
      }

      g_object_unref (details);

      return *alive;
}

static gboolean
delete_pending_foreach (GtkTreeModel *model,
                    GtkTreePath  *path,
                  GtkTreeIter  *iter,
                  gpointer      user_data)
{
      DownloadDetails *details;
      
      gtk_tree_model_get (model, iter, COL_DETAILS, &details, -1);
      g_return_val_if_fail (details != NULL, TRUE);

      g_signal_emit (G_OBJECT (details), download_signals[CANCEL], 0);

      g_object_unref (details);

      return FALSE;
}

gboolean
download_dialog_delete_cb (GtkWidget *window, GdkEventAny *event,
                     DownloaderView *dv)
{
      GtkWidget *dialog;
      gboolean choice;
      gboolean alive_download = FALSE;

      gtk_tree_model_foreach (dv->priv->model, alive_download_foreach, &alive_download);

      if (!alive_download) return FALSE;
            
      /* build question dialog */
      dialog = gtk_message_dialog_new (
             GTK_WINDOW(window),
             GTK_DIALOG_MODAL,
             GTK_MESSAGE_WARNING,
             GTK_BUTTONS_YES_NO,
             _("Cancel all pending downloads?"));

      /* run it */
      choice = gtk_dialog_run (GTK_DIALOG (dialog));
      gtk_widget_destroy (dialog);
      
      /* do the appropriate thing */
      if (choice == GTK_RESPONSE_YES)
      {
            gtk_tree_model_foreach (dv->priv->model, delete_pending_foreach, NULL);
            return FALSE;
      }

      return TRUE;
}

void
download_dialog_details_cb(GtkExpander *expander, 
                     GParamSpec *pspec,
                     DownloaderView *dv)
{
      GtkWidget *details_frame;
      GtkWidget *details_separator;
      GtkWidget *dialog;

      details_frame = galeon_dialog_get_control(GALEON_DIALOG(dv), 
                                      PROP_DETAILS_FRAME);
      details_separator = galeon_dialog_get_control(GALEON_DIALOG(dv), 
                                          PROP_DETAILS_SEPARATOR);

      dialog = gtk_widget_get_toplevel (GTK_WIDGET(expander));

      if (gtk_expander_get_expanded (expander))
      {
            gtk_widget_show(GTK_WIDGET(details_frame));
            gtk_widget_show(GTK_WIDGET(details_separator));
            gtk_window_set_resizable(GTK_WINDOW(dialog),
                               TRUE);
            dv->priv->show_details = TRUE;
      }
      else
      {
            gtk_widget_hide(GTK_WIDGET(details_frame));
            gtk_widget_hide(GTK_WIDGET(details_separator));
            gtk_window_set_resizable(GTK_WINDOW(dialog),
                               FALSE);
            dv->priv->show_details = FALSE;
      }

}

static void
open_selection_foreach (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter,
                  DownloaderView *dv)
{
      DownloadDetails *details;
      GnomeVFSMimeApplication *app;
      char *mime;
      
      gtk_tree_model_get (model, iter, COL_DETAILS, &details, -1);
      g_return_if_fail (details);

      if (details->status != DOWNLOAD_STATUS_COMPLETED) goto out;
      
      mime = gnome_vfs_get_mime_type (details->dest);
      g_return_if_fail (mime != NULL);
      
      app = gnome_vfs_mime_get_default_application (mime);
      if (app)
      {            
            /* FIXME: check return value */
            /* Current event time is fine here as it was called via a user
             * button click time */
            gul_general_launch_application (app, details->dest,
                                    gtk_get_current_event_time());
      }
      else
      {
            GtkWidget *parent;
            parent = gtk_widget_get_toplevel (dv->priv->open_button);
            galeon_embed_utils_nohandler_dialog_run (parent);
      }
      
      gnome_vfs_mime_application_free (app);
      g_free(mime);

out:
      g_object_unref (details);
}

void
download_dialog_open_cb (GtkWidget *button, 
                   DownloaderView *dv)
{
      GtkTreeSelection *selection;

      selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(dv->priv->treeview));

      gtk_tree_selection_selected_foreach 
            (selection,
             (GtkTreeSelectionForeachFunc)open_selection_foreach,
             (gpointer)dv);
}

Generated by  Doxygen 1.6.0   Back to index