/* vim: set sw=2 ts=2 sts=2 et: */ /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* * ephy-download.c * This file is part of Epiphany * * Copyright © 2011 - Igalia S.L. * * Epiphany 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 of the License, or * (at your option) any later version. * * Epiphany 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 Epiphany; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA */ #include "config.h" #include "ephy-download-widget.h" #include "ephy-debug.h" #include "ephy-embed-shell.h" #include "ephy-download.h" #include "totem-glow-button.h" #include #include G_DEFINE_TYPE (EphyDownloadWidget, ephy_download_widget, GTK_TYPE_BOX) #define DOWNLOAD_WIDGET_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), EPHY_TYPE_DOWNLOAD_WIDGET, EphyDownloadWidgetPrivate)) struct _EphyDownloadWidgetPrivate { EphyDownload *download; GtkWidget *remaining; GtkWidget *button; GtkWidget *menu; }; enum { PROP_0, PROP_DOWNLOAD }; static GdkPixbuf * get_icon_from_download (WebKitDownload *download) { WebKitNetworkResponse *response; SoupMessage *message; const char *content_type; GIcon *gicon; GtkIconInfo *icon_info; response = webkit_download_get_network_response (download); message = webkit_network_response_get_message (response); content_type = soup_message_headers_get_content_type (message->response_headers, NULL); if (content_type != NULL) { gicon = g_content_type_get_icon (content_type); } else { gicon = g_icon_new_for_string ("package-x-generic", NULL); } icon_info = gtk_icon_theme_lookup_by_gicon (gtk_icon_theme_get_default (), gicon, 32, GTK_ICON_LOOKUP_GENERIC_FALLBACK); if (icon_info == NULL) icon_info = gtk_icon_theme_lookup_icon (gtk_icon_theme_get_default (), "package-x-generic", 32, GTK_ICON_LOOKUP_GENERIC_FALLBACK); return gtk_icon_info_load_icon (icon_info, NULL); } static char * format_interval (gdouble interval) { int hours, mins, secs; hours = (int) (interval / 3600); interval -= hours * 3600; mins = (int) (interval / 60); interval -= mins * 60; secs = (int) interval; if (hours > 0) { if (mins > 0) return g_strdup_printf (ngettext ("%u:%02u hour left", "%u:%02u hours left", hours), hours, mins); else return g_strdup_printf (ngettext ("%u hour left", "%u hours left", hours), hours); } else { if (mins > 0) return g_strdup_printf (ngettext ("%u:%02u minute left", "%u:%02u minutes left", mins), mins, secs); else return g_strdup_printf (ngettext ("%u second left", "%u seconds left", secs), secs); } } static gdouble get_remaining_time (WebKitDownload *download) { gint64 total, cur; gdouble elapsed_time; gdouble remaining_time; gdouble per_byte_time; total = webkit_download_get_total_size (download); cur = webkit_download_get_current_size (download); elapsed_time = webkit_download_get_elapsed_time (download); if (cur <= 0) return -1.0; per_byte_time = elapsed_time / cur; remaining_time = per_byte_time * (total - cur); return remaining_time; } static void download_clicked_cb (GtkButton *button, EphyDownloadWidget *widget) { WebKitDownloadStatus status; EphyDownload *download; download = widget->priv->download; status = webkit_download_get_status (ephy_download_get_webkit_download (download)); if (status != WEBKIT_DOWNLOAD_STATUS_FINISHED) return; if (ephy_download_do_download_action (download, EPHY_DOWNLOAD_ACTION_AUTO)) gtk_widget_destroy (GTK_WIDGET (widget)); } static void widget_progress_cb (GObject *object, GParamSpec *pspec, EphyDownloadWidget *widget) { WebKitDownload *download; char *destination; gdouble time; char *remaining; int progress; char *remaining_label; char *remaining_tooltip; download = WEBKIT_DOWNLOAD (object); destination = g_filename_display_basename (webkit_download_get_destination_uri (download)); progress = webkit_download_get_progress (download) * 100; time = get_remaining_time (download); if (time > 0) remaining = format_interval (time); else remaining = g_strdup (_("Finished")); remaining_label = g_strdup_printf ("%d%% (%s)", progress, remaining); remaining_tooltip = g_markup_printf_escaped ("%s\n%s", destination, remaining_label); gtk_label_set_text (GTK_LABEL (widget->priv->remaining), remaining_label); gtk_widget_set_tooltip_text (GTK_WIDGET (widget), remaining_tooltip); g_free (destination); g_free (remaining); g_free (remaining_label); g_free (remaining_tooltip); } static void widget_status_cb (GObject *object, GParamSpec *pspec, EphyDownloadWidget *widget) { WebKitDownloadStatus status; status = webkit_download_get_status (WEBKIT_DOWNLOAD (object)); if (status == WEBKIT_DOWNLOAD_STATUS_FINISHED) totem_glow_button_set_glow (TOTEM_GLOW_BUTTON (widget->priv->button), TRUE); } static gboolean widget_error_cb (WebKitDownload *download, gint error_code, gint error_detail, char *reason, EphyDownloadWidget *widget) { char *error_msg; g_signal_handlers_disconnect_by_func (download, widget_status_cb, widget); g_signal_handlers_disconnect_by_func (download, widget_progress_cb, widget); error_msg = g_strdup_printf (_("Error downloading: %s"), reason); gtk_label_set_text (GTK_LABEL (widget->priv->remaining), error_msg); gtk_widget_set_tooltip_text (GTK_WIDGET (widget), error_msg); g_free (error_msg); totem_glow_button_set_glow (TOTEM_GLOW_BUTTON (widget->priv->button), TRUE); return FALSE; } static void open_activate_cb (GtkMenuItem *item, EphyDownloadWidget *widget) { if (ephy_download_do_download_action (widget->priv->download, EPHY_DOWNLOAD_ACTION_OPEN)) gtk_widget_destroy (GTK_WIDGET (widget)); } static void folder_activate_cb (GtkMenuItem *item, EphyDownloadWidget *widget) { if (ephy_download_do_download_action (widget->priv->download, EPHY_DOWNLOAD_ACTION_BROWSE_TO)) gtk_widget_destroy (GTK_WIDGET (widget)); } static void cancel_activate_cb (GtkMenuItem *item, EphyDownloadWidget *widget) { ephy_download_cancel (widget->priv->download); gtk_widget_destroy (GTK_WIDGET (widget)); } static void download_menu_clicked_cb (GtkWidget *button, GdkEventButton *event, EphyDownloadWidget *widget) { WebKitDownloadStatus status; gboolean finished; GtkWidget *item; GtkWidget *menu; GtkWidget *box; GList *children = NULL; char *basename, *name; WebKitDownload *download; download = ephy_download_get_webkit_download (widget->priv->download); status = webkit_download_get_status (download); finished = (status == WEBKIT_DOWNLOAD_STATUS_FINISHED); basename = g_filename_display_basename (webkit_download_get_destination_uri (download)); name = g_uri_unescape_string (basename, NULL); box = gtk_widget_get_parent (button); children = gtk_container_get_children (GTK_CONTAINER (box)); totem_glow_button_set_glow (TOTEM_GLOW_BUTTON (children->data), FALSE); g_list_free (children); menu = gtk_menu_new (); item = gtk_menu_item_new_with_label (name); gtk_widget_set_sensitive (item, FALSE); gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); g_free (basename); g_free (name); item = gtk_menu_item_new_with_label (_("Cancel")); gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); gtk_widget_set_sensitive (item, !finished); g_signal_connect (item, "activate", G_CALLBACK (cancel_activate_cb), widget); item = gtk_separator_menu_item_new (); gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); item = gtk_menu_item_new_with_label (_("Open")); gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); gtk_widget_set_sensitive (item, finished); g_signal_connect (item, "activate", G_CALLBACK (open_activate_cb), widget); item = gtk_menu_item_new_with_label (_("Show in folder")); gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); gtk_widget_set_sensitive (item, finished); g_signal_connect (item, "activate", G_CALLBACK (folder_activate_cb), widget); gtk_widget_show_all (menu); gtk_menu_attach_to_widget (GTK_MENU (menu), button, NULL); gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, event->button, event->time); } static void ephy_download_widget_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { EphyDownloadWidget *widget; widget = EPHY_DOWNLOAD_WIDGET (object); switch (property_id) { case PROP_DOWNLOAD: g_value_set_object (value, ephy_download_widget_get_download (widget)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void ephy_download_widget_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { EphyDownloadWidget *widget; widget = EPHY_DOWNLOAD_WIDGET (object); switch (property_id) { case PROP_DOWNLOAD: widget->priv->download = g_object_ref (g_value_get_object (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void ephy_download_widget_dispose (GObject *object) { EphyDownloadWidget *widget; WebKitDownload *download; LOG ("EphyDownloadWidget %p dispose", object); widget = EPHY_DOWNLOAD_WIDGET (object); if (widget->priv->download != NULL) { download = ephy_download_get_webkit_download (widget->priv->download); g_signal_handlers_disconnect_by_func (download, widget_progress_cb, widget); g_signal_handlers_disconnect_by_func (download, widget_status_cb, widget); g_signal_handlers_disconnect_by_func (download, widget_error_cb, widget); g_object_unref (widget->priv->download); widget->priv->download = NULL; } G_OBJECT_CLASS (ephy_download_widget_parent_class)->dispose (object); } static void ephy_download_widget_class_init (EphyDownloadWidgetClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (klass, sizeof (EphyDownloadWidgetPrivate)); object_class->get_property = ephy_download_widget_get_property; object_class->set_property = ephy_download_widget_set_property; object_class->dispose = ephy_download_widget_dispose; /** * EphyDownloadWidget::download: * * The EphyDownload that this widget is showing. */ g_object_class_install_property (object_class, PROP_DOWNLOAD, g_param_spec_object ("download", "An EphyDownload object", "The EphyDownload shown by this widget", G_TYPE_OBJECT, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); } static void ephy_download_widget_init (EphyDownloadWidget *self) { self->priv = DOWNLOAD_WIDGET_PRIVATE (self); gtk_orientable_set_orientation (GTK_ORIENTABLE (self), GTK_ORIENTATION_HORIZONTAL); } /** * ephy_download_widget_get_download: * @widget: an #EphyDownloadWidget * * Gets the #EphyDownload that @widget is showing. * * Returns: (transfer none): an #EphyDownload. **/ EphyDownload * ephy_download_widget_get_download (EphyDownloadWidget *widget) { g_return_val_if_fail (EPHY_IS_DOWNLOAD_WIDGET (widget), NULL); return widget->priv->download; } /** * ephy_download_widget_new: * @ephy_download: the #EphyDownload that @widget is wrapping * * Creates an #EphyDownloadWidget to wrap @ephy_download. It also associates * @ephy_download to it. * * Returns: a new #EphyDownloadWidget **/ GtkWidget * ephy_download_widget_new (EphyDownload *ephy_download) { EphyDownloadWidget *widget; GtkWidget *grid; GtkWidget *icon; GtkWidget *text; GtkWidget *button; GtkWidget *menu; GtkWidget *remain; char *dest, *basename; GdkPixbuf *pixbuf = NULL; WebKitDownload *download; g_return_val_if_fail (EPHY_IS_DOWNLOAD (ephy_download), NULL); widget = g_object_new (EPHY_TYPE_DOWNLOAD_WIDGET, "download", ephy_download, NULL); download = ephy_download_get_webkit_download (ephy_download); pixbuf = get_icon_from_download (download); basename = g_filename_display_basename (webkit_download_get_destination_uri (download)); dest = g_uri_unescape_string (basename, NULL); grid = gtk_grid_new (); button = totem_glow_button_new (); menu = gtk_button_new (); icon = gtk_image_new_from_pixbuf (pixbuf); text = gtk_label_new (dest); gtk_misc_set_alignment (GTK_MISC (text), 0, 0.5); gtk_label_set_ellipsize (GTK_LABEL (text), PANGO_ELLIPSIZE_END); remain = gtk_label_new (_("Starting…")); gtk_misc_set_alignment (GTK_MISC (remain), 0, 0.5); gtk_label_set_ellipsize (GTK_LABEL (remain), PANGO_ELLIPSIZE_END); gtk_grid_attach (GTK_GRID (grid), icon, 0, 0, 1, 2); gtk_grid_attach (GTK_GRID (grid), text, 1, 0, 1, 1); gtk_grid_attach (GTK_GRID (grid), remain, 1, 1, 1, 1); gtk_widget_set_tooltip_text (GTK_WIDGET (widget), dest); if (pixbuf) g_object_unref (pixbuf); g_free (basename); g_free (dest); widget->priv->button = button; widget->priv->remaining = remain; widget->priv->menu = menu; g_signal_connect (download, "notify::progress", G_CALLBACK (widget_progress_cb), widget); g_signal_connect (download, "notify::status", G_CALLBACK (widget_status_cb), widget); g_signal_connect (download, "error", G_CALLBACK (widget_error_cb), widget); gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_HALF); gtk_button_set_relief (GTK_BUTTON (menu), GTK_RELIEF_NORMAL); gtk_container_add (GTK_CONTAINER (button), grid); gtk_container_add (GTK_CONTAINER (menu), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE)); gtk_box_pack_start (GTK_BOX (widget), button, FALSE, FALSE, 0); gtk_box_pack_end (GTK_BOX (widget), menu, FALSE, FALSE, 0); g_signal_connect (button, "clicked", G_CALLBACK (download_clicked_cb), widget); g_signal_connect (menu, "button-press-event", G_CALLBACK (download_menu_clicked_cb), widget); gtk_widget_show_all (button); gtk_widget_show_all (menu); ephy_download_set_widget (ephy_download, GTK_WIDGET (widget)); return GTK_WIDGET (widget); }