[epiphany/downloads: 4/5] ephy-download-widget: new widget



commit 98a8cb21084b8a594005b42cdfb26fe8bfc8c04b
Author: Diego Escalante Urrelo <descalante igalia com>
Date:   Wed Jan 26 16:01:43 2011 -0500

    ephy-download-widget: new widget
    
    A widget showing the progress of an EphyDownload and offering the default set
    of actions to take on it: Open, Browse to, Cancel.
    It keeps a ref to the EphyDownload its showing.
    
    Bug #618443

 lib/widgets/Makefile.am            |    5 +
 lib/widgets/ephy-download-widget.c |  531 ++++++++++++++++++++++++++++++++++++
 lib/widgets/ephy-download-widget.h |   68 +++++
 lib/widgets/totem-glow-button.c    |  351 ++++++++++++++++++++++++
 lib/widgets/totem-glow-button.h    |   52 ++++
 src/Makefile.am                    |    2 +
 6 files changed, 1009 insertions(+), 0 deletions(-)
---
diff --git a/lib/widgets/Makefile.am b/lib/widgets/Makefile.am
index 524ecf7..a80cbde 100644
--- a/lib/widgets/Makefile.am
+++ b/lib/widgets/Makefile.am
@@ -1,6 +1,8 @@
 noinst_LTLIBRARIES = libephywidgets.la
 
 libephywidgets_la_SOURCES = \
+	ephy-download-widget.c			\
+	ephy-download-widget.h			\
 	ephy-location-entry.c			\
 	ephy-location-entry.h			\
 	ephy-node-view.c			\
@@ -11,6 +13,8 @@ libephywidgets_la_SOURCES = \
 	ephy-tree-model-node.h			\
 	ephy-tree-model-sort.c			\
 	ephy-tree-model-sort.h			\
+	totem-glow-button.c			\
+	totem-glow-button.h			\
 	ephy-zoom-action.h			\
 	ephy-zoom-action.c			\
 	ephy-zoom-control.c			\
@@ -24,6 +28,7 @@ libephywidgets_la_CPPFLAGS = \
 	-I$(top_builddir)/lib		\
 	-I$(top_builddir)/lib/widgets	\
 	-I$(top_srcdir)/lib		\
+	-I$(top_srcdir)/embed		\
 	-I$(top_srcdir)/lib/egg		\
 	-DSHARE_DIR=\"$(pkgdatadir)\"	\
 	$(AM_CPPFLAGS)
diff --git a/lib/widgets/ephy-download-widget.c b/lib/widgets/ephy-download-widget.c
new file mode 100644
index 0000000..cf1935b
--- /dev/null
+++ b/lib/widgets/ephy-download-widget.c
@@ -0,0 +1,531 @@
+/* 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 <glib/gi18n.h>
+#include <webkit/webkit.h>
+
+G_DEFINE_TYPE (EphyDownloadWidget, ephy_download_widget, GTK_TYPE_HBOX)
+
+#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;
+};
+
+static void
+widget_progress_cb (GObject *object,
+                    GParamSpec *pspec,
+                    EphyDownloadWidget *widget);
+static void
+widget_status_cb (GObject *object,
+                  GParamSpec *pspec,
+                  EphyDownloadWidget *widget);
+
+static gboolean
+widget_error_cb (WebKitDownload *download,
+                 gint error_code,
+                 gint error_detail,
+                 char *reason,
+                 EphyDownloadWidget *widget);
+
+enum
+{
+  PROP_0,
+  PROP_DOWNLOAD
+};
+
+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);
+  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);
+
+  ephy_download_set_widget (widget->priv->download, NULL);
+
+  if (widget->priv->download)
+    g_object_unref (widget->priv->download);
+
+  G_OBJECT_CLASS (ephy_download_widget_parent_class)->dispose (object);
+}
+
+static void
+ephy_download_widget_finalize (GObject *object)
+{
+  LOG ("EphyDownloadWidget %p finalized", object);
+
+  G_OBJECT_CLASS (ephy_download_widget_parent_class)->finalize (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;
+  object_class->finalize = ephy_download_widget_finalize;
+
+  /**
+   * 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);
+}
+
+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_strdup_printf ("%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;
+
+  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));
+
+  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 (basename);
+  gtk_widget_set_sensitive (item, FALSE);
+  gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+  g_free (basename);
+
+  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);
+}
+
+/**
+ * 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;
+  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);
+  dest = g_filename_display_basename (webkit_download_get_destination_uri (download));
+
+  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 (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_NONE);
+  gtk_button_set_relief (GTK_BUTTON (menu), GTK_RELIEF_NONE);
+
+  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);
+}
diff --git a/lib/widgets/ephy-download-widget.h b/lib/widgets/ephy-download-widget.h
new file mode 100644
index 0000000..f2a1d88
--- /dev/null
+++ b/lib/widgets/ephy-download-widget.h
@@ -0,0 +1,68 @@
+/* vim: set foldmethod=marker 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
+ */
+
+#if !defined (__EPHY_EPIPHANY_H_INSIDE__) && !defined (EPIPHANY_COMPILATION)
+#error "Only <epiphany/epiphany.h> can be included directly."
+#endif
+
+#ifndef _EPHY_DOWNLOAD_WIDGET_H
+#define _EPHY_DOWNLOAD_WIDGET_H
+
+#include <glib-object.h>
+#include "ephy-download.h"
+
+G_BEGIN_DECLS
+
+#define EPHY_TYPE_DOWNLOAD_WIDGET              ephy_download_widget_get_type()
+#define EPHY_DOWNLOAD_WIDGET(obj)              (G_TYPE_CHECK_INSTANCE_CAST ((obj), EPHY_TYPE_DOWNLOAD_WIDGET, EphyDownloadWidget))
+#define EPHY_DOWNLOAD_WIDGET_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), EPHY_TYPE_DOWNLOAD_WIDGET, EphyDownloadWidgetClass))
+#define EPHY_IS_DOWNLOAD_WIDGET(obj)           (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EPHY_TYPE_DOWNLOAD_WIDGET))
+#define EPHY_IS_DOWNLOAD_WIDGET_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), EPHY_TYPE_DOWNLOAD_WIDGET))
+#define EPHY_DOWNLOAD_WIDGET_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), EPHY_TYPE_DOWNLOAD_WIDGET, EphyDownloadWidgetClass))
+
+typedef struct _EphyDownloadWidget EphyDownloadWidget;
+typedef struct _EphyDownloadWidgetClass EphyDownloadWidgetClass;
+typedef struct _EphyDownloadWidgetPrivate EphyDownloadWidgetPrivate;
+
+struct _EphyDownloadWidget
+{
+  GtkHBox parent;
+
+  EphyDownloadWidgetPrivate *priv;
+};
+
+struct _EphyDownloadWidgetClass
+{
+  GtkHBoxClass parent_class;
+};
+
+GType          ephy_download_widget_get_type      (void) G_GNUC_CONST;
+
+GtkWidget     *ephy_download_widget_new           (EphyDownload *ephy_download);
+
+EphyDownload  *ephy_download_widget_get_download  (EphyDownloadWidget *widget);
+
+G_END_DECLS
+
+#endif /* _EPHY_DOWNLOAD_WIDGET_H */
diff --git a/lib/widgets/totem-glow-button.c b/lib/widgets/totem-glow-button.c
new file mode 100644
index 0000000..9a0b3fd
--- /dev/null
+++ b/lib/widgets/totem-glow-button.c
@@ -0,0 +1,351 @@
+/*
+ * (C) Copyright 2007 Bastien Nocera <hadess hadess net>
+ *
+ * Glow code from libwnck/libwnck/tasklist.c:
+ * Copyright © 2001 Havoc Pennington
+ * Copyright © 2003 Kim Woelders
+ * Copyright © 2003 Red Hat, Inc.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301  USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <math.h>
+#include <gtk/gtk.h>
+#include "totem-glow-button.h"
+
+#define FADE_OPACITY_DEFAULT 0.6
+#define ENTER_SPEEDUP_RATIO 0.4
+#define FADE_MAX_LOOPS 4
+
+struct _TotemGlowButton {
+	GtkButton parent;
+
+	gdouble glow_start_time;
+	gdouble glow_factor;
+
+	guint button_glow;
+
+	guint glow : 1;
+	guint anim_enabled : 1;
+	guint pointer_entered : 1;
+	/* Set when we don't want to play animation
+	 * anymore in pointer entered mode */
+	guint anim_finished :1;
+};
+
+static void totem_glow_button_set_timeout (TotemGlowButton *button, gboolean set_timeout);
+
+static GtkButtonClass *parent_class;
+
+G_DEFINE_TYPE (TotemGlowButton, totem_glow_button, GTK_TYPE_BUTTON);
+
+static gboolean
+totem_glow_button_glow (TotemGlowButton *button)
+{
+	GtkWidget *buttonw;
+	GTimeVal tv;
+	gdouble now;
+	gfloat fade_opacity, loop_time;
+
+	buttonw = GTK_WIDGET (button);
+
+	if (gtk_widget_get_realized (buttonw) == FALSE)
+		return TRUE;
+
+	if (button->anim_enabled != FALSE) {
+		g_get_current_time (&tv);
+		now = (tv.tv_sec * (1.0 * G_USEC_PER_SEC) +
+		       tv.tv_usec) / G_USEC_PER_SEC;
+
+		/* Hard-coded values */
+		fade_opacity = FADE_OPACITY_DEFAULT;
+		loop_time = 3.0;
+
+		if (button->glow_start_time <= G_MINDOUBLE) {
+			button->glow_start_time = now;
+			/* If the pointer entered, we want to start with 'dark' */
+			if (button->pointer_entered != FALSE) {
+				button->glow_start_time -= loop_time / 4.0;
+			}
+		}
+
+		/* Timing for mouse hover animation
+		   [light]......[dark]......[light]......[dark]...
+		   {mouse hover event}
+		   [dark]..[light]..[dark]..[light]
+		   {mouse leave event}
+		   [light]......[dark]......[light]......[dark]
+		*/
+		if (button->pointer_entered != FALSE) {
+			/* pointer entered animation should be twice as fast */
+			loop_time *= ENTER_SPEEDUP_RATIO;
+		}
+		if ((now - button->glow_start_time) > loop_time * FADE_MAX_LOOPS) {
+			button->anim_finished = TRUE;
+			button->glow_factor = FADE_OPACITY_DEFAULT * 0.5;
+		} else {
+			button->glow_factor = fade_opacity * (0.5 - 0.5 * cos ((now - button->glow_start_time) * M_PI * 2.0 / loop_time));
+		}
+	} else {
+		button->glow_factor = FADE_OPACITY_DEFAULT * 0.5;
+	}
+
+	gtk_widget_queue_draw (GTK_WIDGET (button));
+
+	if (button->anim_finished != FALSE)
+		totem_glow_button_set_timeout (button, FALSE);
+
+	return button->anim_enabled;
+}
+
+static void
+totem_glow_button_clear_glow_start_timeout_id (TotemGlowButton *button)
+{
+	button->button_glow = 0;
+}
+
+static gboolean
+totem_glow_button_draw (GtkWidget *widget,
+			cairo_t   *cr,
+			gpointer   user_data)
+{
+	TotemGlowButton *button;
+	GtkStyleContext *context;
+	GtkAllocation allocation, child_allocation;
+	gint width, height;
+	GtkWidget *child;
+	GdkRGBA acolor;
+
+	button = TOTEM_GLOW_BUTTON (widget);
+
+	if (button->glow_factor == 0.0)
+		return FALSE;
+
+	context = gtk_widget_get_style_context (widget);
+
+	/* push a translucent overlay to paint to, so we can blend later */
+	cairo_push_group_with_content (cr, CAIRO_CONTENT_COLOR_ALPHA);
+
+	gtk_widget_get_allocation (widget, &allocation);
+
+	width = allocation.width;
+	height = allocation.height;
+
+	/* Draw a rectangle with bg[SELECTED] */
+	gtk_style_context_save (context);
+	gtk_style_context_add_class (context, "button");
+	gtk_style_context_set_state (context, GTK_STATE_SELECTED);
+	gtk_style_context_get_background_color (context, GTK_STATE_SELECTED, &acolor);
+	gdk_cairo_set_source_rgba (cr, &acolor);
+	gtk_render_background (context, cr, 0, 0, width, height);
+	gtk_style_context_restore (context);
+
+	/* then the image */
+	cairo_save (cr);
+	child = gtk_bin_get_child (GTK_BIN (button));
+	gtk_widget_get_allocation (child, &child_allocation);
+	cairo_translate (cr,
+			 child_allocation.x - allocation.x,
+			 child_allocation.y - allocation.y);
+	gtk_widget_draw (gtk_bin_get_child (GTK_BIN (button)), cr);
+	cairo_restore (cr);
+
+	/* finally blend it */
+	cairo_pop_group_to_source (cr);
+	cairo_paint_with_alpha (cr, button->glow_factor);
+
+	return FALSE;
+}
+
+static void
+totem_glow_button_map (GtkWidget *buttonw)
+{
+	TotemGlowButton *button;
+
+	(* GTK_WIDGET_CLASS (parent_class)->map) (buttonw);
+
+	button = TOTEM_GLOW_BUTTON (buttonw);
+
+	if (button->glow != FALSE && button->button_glow == 0) {
+		totem_glow_button_set_glow (button, TRUE);
+	}
+}
+
+static void
+totem_glow_button_unmap (GtkWidget *buttonw)
+{
+	TotemGlowButton *button;
+
+	button = TOTEM_GLOW_BUTTON (buttonw);
+
+	if (button->button_glow > 0) {
+		g_source_remove (button->button_glow);
+		button->button_glow = 0;
+	}
+
+	(* GTK_WIDGET_CLASS (parent_class)->unmap) (buttonw);
+}
+
+static void
+totem_glow_button_enter (GtkButton *buttonw)
+{
+	TotemGlowButton *button;
+
+	button = TOTEM_GLOW_BUTTON (buttonw);
+
+	(* GTK_BUTTON_CLASS (parent_class)->enter) (buttonw);
+
+	button->pointer_entered = TRUE;
+	button->anim_finished = FALSE;
+	button->glow_start_time = G_MINDOUBLE;
+	totem_glow_button_set_timeout (button, FALSE);
+}
+
+static void
+totem_glow_button_leave (GtkButton *buttonw)
+{
+	TotemGlowButton *button;
+
+	button = TOTEM_GLOW_BUTTON (buttonw);
+
+	(* GTK_BUTTON_CLASS (parent_class)->leave) (buttonw);
+
+	button->pointer_entered = FALSE;
+	button->glow_start_time = G_MINDOUBLE;
+	button->anim_finished = FALSE;
+	if (button->glow != FALSE)
+		totem_glow_button_set_timeout (button, TRUE);
+}
+
+static void
+totem_glow_button_finalize (GObject *object)
+{
+	TotemGlowButton *button = TOTEM_GLOW_BUTTON (object);
+
+	totem_glow_button_set_glow (button, FALSE);
+
+	G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+totem_glow_button_class_init (TotemGlowButtonClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+	GtkButtonClass *button_class = GTK_BUTTON_CLASS (klass);
+
+	parent_class = g_type_class_peek_parent (klass);
+
+	object_class->finalize = totem_glow_button_finalize;
+	/* Note that we don't use a draw here because we
+	 * want to modify what the button will draw by itself */
+	widget_class->map = totem_glow_button_map;
+	widget_class->unmap = totem_glow_button_unmap;
+	button_class->enter = totem_glow_button_enter;
+	button_class->leave = totem_glow_button_leave;
+}
+
+static void
+totem_glow_button_init (TotemGlowButton *button)
+{
+	button->glow_start_time = 0.0;
+	button->button_glow = 0;
+	button->glow_factor = 0.0;
+
+	g_signal_connect_object (button, "draw",
+				 G_CALLBACK (totem_glow_button_draw),
+				 G_OBJECT (button),
+				 G_CONNECT_AFTER);
+}
+
+GtkWidget *
+totem_glow_button_new (void)
+{
+	return g_object_new (TOTEM_TYPE_GLOW_BUTTON, NULL);
+}
+
+/* We can only add a timeout once, we assert that, though
+ * calling it multiple times to disable the animation is fine */
+static void
+totem_glow_button_set_timeout (TotemGlowButton *button, gboolean set_timeout)
+{
+	if (set_timeout != FALSE) {
+		if (button->button_glow > 0)
+			return;
+
+		button->glow_start_time = 0.0;
+
+		/* The animation doesn't speed up or slow down based on the
+		 * timeout value, but instead will just appear smoother or
+		 * choppier.
+		 */
+		button->button_glow =
+			g_timeout_add_full (G_PRIORITY_DEFAULT_IDLE,
+					    100,
+					    (GSourceFunc) totem_glow_button_glow, button,
+					    (GDestroyNotify) totem_glow_button_clear_glow_start_timeout_id);
+	} else {
+		if (button->button_glow > 0) {
+			g_source_remove (button->button_glow);
+			button->button_glow = 0;
+		}
+		button->glow_factor = 0.0;
+		gtk_widget_queue_draw (GTK_WIDGET (button));
+	}
+}
+
+void
+totem_glow_button_set_glow (TotemGlowButton *button, gboolean glow)
+{
+	GtkSettings *settings;
+	gboolean anim_enabled;
+
+	g_return_if_fail (TOTEM_IS_GLOW_BUTTON (button));
+
+	if (gtk_widget_get_mapped (GTK_WIDGET (button)) == FALSE
+	    && glow != FALSE) {
+		button->glow = glow;
+		return;
+	}
+
+	settings = gtk_settings_get_for_screen
+		(gtk_widget_get_screen (GTK_WIDGET (button)));
+	g_object_get (G_OBJECT (settings),
+		      "gtk-enable-animations", &anim_enabled,
+		      NULL);
+	button->anim_enabled = anim_enabled;
+
+	if (glow == FALSE && button->button_glow == 0 && button->anim_enabled != FALSE)
+		return;
+	if (glow != FALSE && button->button_glow != 0)
+		return;
+
+	button->glow = glow;
+
+	totem_glow_button_set_timeout (button, glow);
+}
+
+gboolean
+totem_glow_button_get_glow (TotemGlowButton *button)
+{
+	return button->glow != FALSE;
+}
+
diff --git a/lib/widgets/totem-glow-button.h b/lib/widgets/totem-glow-button.h
new file mode 100644
index 0000000..138f8c4
--- /dev/null
+++ b/lib/widgets/totem-glow-button.h
@@ -0,0 +1,52 @@
+/*
+ * (C) Copyright 2007 Bastien Nocera <hadess hadess net>
+ *
+ * Glow code from libwnck/libwnck/tasklist.c:
+ * Copyright © 2001 Havoc Pennington
+ * Copyright © 2003 Kim Woelders
+ * Copyright © 2003 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301  USA.
+ */
+
+#ifndef __TOTEM_GLOW_BUTTON_H__
+#define __TOTEM_GLOW_BUTTON_H__
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define TOTEM_TYPE_GLOW_BUTTON     (totem_glow_button_get_type ())
+#define TOTEM_GLOW_BUTTON(obj)     (G_TYPE_CHECK_INSTANCE_CAST ((obj), TOTEM_TYPE_GLOW_BUTTON, TotemGlowButton))
+#define TOTEM_IS_GLOW_BUTTON(obj)  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TOTEM_TYPE_GLOW_BUTTON))
+
+typedef struct _TotemGlowButton TotemGlowButton;
+
+typedef struct _TotemGlowButtonClass {
+  GtkButtonClass parent_class;
+
+  gpointer __bla[4];
+} TotemGlowButtonClass;
+
+GType		totem_glow_button_get_type	(void) G_GNUC_CONST;
+
+GtkWidget *	totem_glow_button_new		(void);
+void		totem_glow_button_set_glow	(TotemGlowButton *button, gboolean glow);
+gboolean	totem_glow_button_get_glow	(TotemGlowButton *button);
+
+G_END_DECLS
+
+#endif /* __TOTEM_GLOW_BUTTON_H__ */
diff --git a/src/Makefile.am b/src/Makefile.am
index dd9a0e1..f399505 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -278,6 +278,7 @@ EPHY_GIR_H_FILES = \
 	$(top_srcdir)/lib/ephy-node-db.h \
 	$(top_srcdir)/lib/ephy-node.h \
 	$(top_srcdir)/lib/ephy-state.h \
+	$(top_srcdir)/lib/widgets/ephy-download-widget.h \
 	$(top_srcdir)/lib/widgets/ephy-search-entry.h \
 	$(top_srcdir)/src/bookmarks/ephy-bookmarks.h \
 	$(top_srcdir)/src/ephy-extension.h \
@@ -308,6 +309,7 @@ EPHY_GIR_C_FILES = \
 	$(top_srcdir)/lib/ephy-node-db.c \
 	$(top_srcdir)/lib/ephy-node.c \
 	$(top_srcdir)/lib/ephy-state.c \
+	$(top_srcdir)/lib/widgets/ephy-download-widget.c \
 	$(top_srcdir)/lib/widgets/ephy-search-entry.c \
 	$(top_srcdir)/src/bookmarks/ephy-bookmarks.c \
 	$(top_srcdir)/src/ephy-extension.c \



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