[epiphany] Use better icons for webapps



commit 787d591681df40c8b10c450273d1c827b6eb818b
Author: William Jon McCann <jmccann redhat com>
Date:   Mon Dec 10 12:31:12 2012 +0100

    Use better icons for webapps
    
    https://bugzilla.gnome.org/show_bug.cgi?id=657755

 lib/ephy-web-app-utils.c |  228 ++++++++++++++++++++++++++++++++++++++++++++++
 lib/ephy-web-app-utils.h |   10 ++
 src/window-commands.c    |  196 +++++++++++++++++++++++++++++++++-------
 3 files changed, 402 insertions(+), 32 deletions(-)
---
diff --git a/lib/ephy-web-app-utils.c b/lib/ephy-web-app-utils.c
index 1bd1a2f..203a771 100644
--- a/lib/ephy-web-app-utils.c
+++ b/lib/ephy-web-app-utils.c
@@ -35,6 +35,234 @@
 
 #define EPHY_WEB_APP_DESKTOP_FILE_PREFIX "epiphany-"
 
+static char *
+resolve_uri (WebKitWebView *view,
+             const char    *uri)
+{
+  SoupURI *base;
+  SoupURI *new;
+  const char *base_uri;
+  char *ret;
+
+  if (uri == NULL)
+    return NULL;
+
+  base_uri = webkit_web_view_get_uri (view);
+  if (base_uri == NULL)
+    return NULL;
+
+  base = soup_uri_new (base_uri);
+  new = soup_uri_new_with_base (base, uri);
+  soup_uri_free (base);
+  ret = soup_uri_to_string (new, FALSE);
+  soup_uri_free (new);
+
+  return ret;
+}
+
+#ifdef HAVE_WEBKIT2
+/* TODO: DOM Bindindgs */
+#else
+static gboolean
+get_icon_from_mstile (WebKitWebView *view,
+                      char         **uri_out,
+                      char         **color_out)
+{
+  gboolean ret;
+  WebKitDOMDocument *document;
+  WebKitDOMNodeList *metas;
+  gulong length, i;
+  char *image = NULL;
+  char *color = NULL;
+
+  document = webkit_web_view_get_dom_document (view);
+  metas = webkit_dom_document_get_elements_by_tag_name (document, "meta");
+  length = webkit_dom_node_list_get_length (metas);
+
+  for (i = 0; i < length; i++) {
+    WebKitDOMNode *node = webkit_dom_node_list_item (metas, i);
+    char *name;
+
+    name = webkit_dom_html_meta_element_get_name (WEBKIT_DOM_HTML_META_ELEMENT (node));
+    if (g_strcmp0 (name, "msapplication-TileImage") == 0) {
+      if (image == NULL)
+        image = webkit_dom_html_meta_element_get_content (WEBKIT_DOM_HTML_META_ELEMENT (node));
+    } else if (g_strcmp0 (name, "msapplication-TileColor") == 0) {
+      if (color == NULL)
+        color = webkit_dom_html_meta_element_get_content (WEBKIT_DOM_HTML_META_ELEMENT (node));
+    }
+  }
+
+  ret = (image != NULL && *image != '\0');
+
+  if (uri_out != NULL)
+    *uri_out = g_strdup (image);
+  if (color_out != NULL)
+    *color_out = g_strdup (color);
+
+  g_free (image);
+  g_free (color);
+
+  return ret;
+}
+
+static gboolean
+get_icon_from_ogp (WebKitWebView *view,
+                   char         **uri_out,
+                   char         **color_out)
+{
+  gboolean ret;
+  WebKitDOMDocument *document;
+  WebKitDOMNodeList *metas;
+  gulong length, i;
+  char *image = NULL;
+  char *color = NULL;
+
+  document = webkit_web_view_get_dom_document (view);
+  metas = webkit_dom_document_get_elements_by_tag_name (document, "meta");
+  length = webkit_dom_node_list_get_length (metas);
+
+  for (i = 0; i < length && image == NULL; i++) {
+    WebKitDOMNode *node = webkit_dom_node_list_item (metas, i);
+    char *property;
+    char *itemprop;
+
+    property = webkit_dom_element_get_attribute (WEBKIT_DOM_ELEMENT (node), "property");
+    itemprop = webkit_dom_element_get_attribute (WEBKIT_DOM_ELEMENT (node), "itemprop");
+    if (g_strcmp0 (property, "og:image") == 0 ||
+        g_strcmp0 (itemprop, "image") == 0) {
+      image = webkit_dom_html_meta_element_get_content (WEBKIT_DOM_HTML_META_ELEMENT (node));
+    }
+    g_free (property);
+    g_free (itemprop);
+  }
+
+  ret = (image != NULL && *image != '\0');
+
+  if (uri_out != NULL)
+    *uri_out = g_strdup (image);
+  if (color_out != NULL)
+    *color_out = g_strdup (color);
+
+  return ret;
+}
+
+static gboolean
+get_icon_from_touch_icon (WebKitWebView *view,
+                          char         **uri_out,
+                          char         **color_out)
+{
+  gboolean ret;
+  WebKitDOMDocument *document;
+  WebKitDOMNodeList *links;
+  gulong length, i;
+  char *image = NULL;
+  char *color = NULL;
+
+  document = webkit_web_view_get_dom_document (view);
+  links = webkit_dom_document_get_elements_by_tag_name (document, "link");
+  length = webkit_dom_node_list_get_length (links);
+
+  for (i = 0; i < length && image == NULL; i++) {
+    char *rel;
+    WebKitDOMNode *node = webkit_dom_node_list_item (links, i);
+
+    rel = webkit_dom_html_link_element_get_rel (WEBKIT_DOM_HTML_LINK_ELEMENT (node));
+    /* TODO: support more than one possible icon. */
+    if (g_strcmp0 (rel, "apple-touch-icon") == 0 ||
+        g_strcmp0 (rel, "apple-touch-icon-precomposed") == 0) {
+      image = webkit_dom_html_link_element_get_href (WEBKIT_DOM_HTML_LINK_ELEMENT (node));
+    }
+    g_free (rel);
+  }
+
+  ret = (image != NULL && *image != '\0');
+
+  if (uri_out != NULL)
+    *uri_out = g_strdup (image);
+  if (color_out != NULL)
+    *color_out = g_strdup (color);
+
+  return ret;
+}
+
+static gboolean
+get_icon_from_favicon (WebKitWebView *view,
+                       char         **uri_out,
+                       char         **color_out)
+{
+  gboolean ret;
+  WebKitDOMDocument *document;
+  WebKitDOMNodeList *links;
+  gulong length, i;
+  char *image = NULL;
+  char *color = NULL;
+
+  document = webkit_web_view_get_dom_document (view);
+  links = webkit_dom_document_get_elements_by_tag_name (document, "link");
+  length = webkit_dom_node_list_get_length (links);
+
+  for (i = 0; i < length; i++) {
+    char *rel;
+    WebKitDOMNode *node = webkit_dom_node_list_item (links, i);
+
+    rel = webkit_dom_html_link_element_get_rel (WEBKIT_DOM_HTML_LINK_ELEMENT (node));
+    if (g_strcmp0 (rel, "shortcut-icon") == 0 ||
+        g_strcmp0 (rel, "shortcut icon") == 0 ||
+        g_strcmp0 (rel, "icon shortcut") == 0 ||
+        g_strcmp0 (rel, "icon") == 0) {
+      image = webkit_dom_html_link_element_get_href (WEBKIT_DOM_HTML_LINK_ELEMENT (node));
+    }
+    g_free (rel);
+  }
+
+  ret = (image != NULL && *image != '\0');
+
+  if (uri_out != NULL)
+    *uri_out = g_strdup (image);
+  if (color_out != NULL)
+    *color_out = g_strdup (color);
+
+  return ret;
+}
+#endif /* HAVE_WEBKIT2 */
+
+gboolean
+ephy_web_view_get_best_icon (WebKitWebView *view,
+                             char         **uri,
+                             GdkRGBA       *rgba)
+{
+  gboolean ret = FALSE;
+  char *image = NULL;
+  char *color = NULL;
+
+#ifdef HAVE_WEBKIT2
+  /* TODO: DOM Bindindgs */
+#else
+
+  /* First try to get a mstile, then try OGP, then favicon */
+
+  ret = get_icon_from_mstile (view, &image, &color);
+  if (! ret)
+    ret = get_icon_from_ogp (view, &image, &color);
+  if (! ret)
+    ret = get_icon_from_touch_icon (view, &image, &color);
+  if (! ret)
+    ret = get_icon_from_favicon (view, &image, &color);
+
+#endif
+
+  if (uri != NULL)
+    *uri = resolve_uri (view, image);
+  if (rgba != NULL && color != NULL)
+    gdk_rgba_parse (rgba, color);
+
+  g_free (image);
+  g_free (color);
+
+  return ret;
+}
+
 /* This is necessary because of gnome-shell's guessing of a .desktop
    filename from WM_CLASS property. */
 static char *
diff --git a/lib/ephy-web-app-utils.h b/lib/ephy-web-app-utils.h
index 54968df..adb254e 100644
--- a/lib/ephy-web-app-utils.h
+++ b/lib/ephy-web-app-utils.h
@@ -27,6 +27,12 @@
 #include <glib.h>
 #include <gtk/gtk.h>
 
+#ifdef HAVE_WEBKIT2
+#include <webkit2/webkit2.h>
+#else
+#include <webkit/webkit.h>
+#endif
+
 G_BEGIN_DECLS
 
 typedef struct {
@@ -52,6 +58,10 @@ void     ephy_web_application_free_application_list (GList *list);
 
 gboolean ephy_web_application_exists (const char *name);
 
+gboolean ephy_web_view_get_best_icon (WebKitWebView *view,
+                                      char         **uri,
+                                      GdkRGBA       *rgba);
+
 G_END_DECLS
 
 #endif
diff --git a/src/window-commands.c b/src/window-commands.c
index bd6902f..da93934 100644
--- a/src/window-commands.c
+++ b/src/window-commands.c
@@ -65,6 +65,8 @@
 #include <webkit/webkit.h>
 #endif
 
+#define DEFAULT_ICON_SIZE 144
+
 void
 window_cmd_file_print (GtkAction *action,
 		       EphyWindow *window)
@@ -370,6 +372,7 @@ typedef struct {
 	GtkWidget *spinner;
 	GtkWidget *box;
 	char *icon_href;
+	GdkRGBA icon_rgba;
 } EphyApplicationDialogData;
 
 static void
@@ -380,18 +383,154 @@ ephy_application_dialog_data_free (EphyApplicationDialogData *data)
 }
 
 static void
+rounded_rectangle (cairo_t *cr,
+                   gdouble  aspect,
+                   gdouble  x,
+                   gdouble  y,
+                   gdouble  corner_radius,
+                   gdouble  width,
+                   gdouble  height)
+{
+        gdouble radius;
+        gdouble degrees;
+
+        radius = corner_radius / aspect;
+        degrees = G_PI / 180.0;
+
+        cairo_new_sub_path (cr);
+        cairo_arc (cr,
+                   x + width - radius,
+                   y + radius,
+                   radius,
+                   -90 * degrees,
+                   0 * degrees);
+        cairo_arc (cr,
+                   x + width - radius,
+                   y + height - radius,
+                   radius,
+                   0 * degrees,
+                   90 * degrees);
+        cairo_arc (cr,
+                   x + radius,
+                   y + height - radius,
+                   radius,
+                   90 * degrees,
+                   180 * degrees);
+        cairo_arc (cr,
+                   x + radius,
+                   y + radius,
+                   radius,
+                   180 * degrees,
+                   270 * degrees);
+        cairo_close_path (cr);
+}
+
+static GdkPixbuf *
+frame_pixbuf (GdkPixbuf  *pixbuf,
+	      GdkRGBA    *rgba,
+	      int         width,
+	      int         height)
+{
+	GdkPixbuf *framed;
+	cairo_surface_t *surface;
+	cairo_t *cr;
+	int frame_width;
+	int radius;
+
+	surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+					      width, height);
+	cr = cairo_create (surface);
+
+	frame_width = 0;
+	radius = 20;
+
+	rounded_rectangle (cr,
+			   1.0,
+			   frame_width + 0.5,
+			   frame_width + 0.5,
+			   radius,
+			   width - frame_width * 2 - 1,
+			   height - frame_width * 2 - 1);
+	if (rgba != NULL)
+			cairo_set_source_rgba (cr,
+					       rgba->red,
+					       rgba->green,
+					       rgba->blue,
+					       rgba->alpha);
+	else
+		cairo_set_source_rgba (cr, 0.5, 0.5, 0.5, 0.3);
+	cairo_fill_preserve (cr);
+
+	if (pixbuf != NULL) {
+		GdkPixbuf *scaled;
+		int w;
+		int h;
+
+		w = gdk_pixbuf_get_width (pixbuf);
+		h = gdk_pixbuf_get_height (pixbuf);
+
+		if (w < 48 || h < 48) {
+			scaled = gdk_pixbuf_scale_simple (pixbuf, w * 3, h * 3, GDK_INTERP_NEAREST);
+		} else if (w > width || h > height) {
+			double ws, hs, s;
+
+			ws = (double) width / w;
+			hs = (double) height / h;
+			s = MIN (ws, hs);
+			scaled = gdk_pixbuf_scale_simple (pixbuf, w * s, h * s, GDK_INTERP_BILINEAR);
+		} else {
+			scaled = g_object_ref (pixbuf);
+		}
+
+		w = gdk_pixbuf_get_width (scaled);
+		h = gdk_pixbuf_get_height (scaled);
+
+		gdk_cairo_set_source_pixbuf (cr, scaled,
+					     (width - w) / 2,
+					     (height - h) / 2);
+		g_object_unref (scaled);
+		cairo_fill (cr);
+	}
+
+	framed = gdk_pixbuf_get_from_surface (surface, 0, 0, width, height);
+	cairo_destroy (cr);
+	cairo_surface_destroy (surface);
+
+	return framed;
+}
+
+static void
 take_page_snapshot_and_set_image (EphyApplicationDialogData *data)
 {
 	GdkPixbuf *snapshot;
+	GdkPixbuf *framed;
 	int x, y, w, h;
 
 	x = y = 0;
-	w = h = 128; /* GNOME hi-res icon size. */
+	w = h = DEFAULT_ICON_SIZE;
 
 	snapshot = ephy_web_view_get_snapshot (data->view, x, y, w, h);
-
-	gtk_image_set_from_pixbuf (GTK_IMAGE (data->image), snapshot);
+	framed = frame_pixbuf (snapshot, NULL, w, h);
 	g_object_unref (snapshot);
+	gtk_image_set_from_pixbuf (GTK_IMAGE (data->image), framed);
+	g_object_unref (framed);
+}
+
+static void
+set_app_icon_from_filename (EphyApplicationDialogData *data,
+			    const char *filename)
+{
+	GdkPixbuf *pixbuf;
+	GdkPixbuf *framed;
+
+	pixbuf = gdk_pixbuf_new_from_file (filename, NULL);
+	if (pixbuf == NULL)
+		return;
+
+	framed = frame_pixbuf (pixbuf, &data->icon_rgba, DEFAULT_ICON_SIZE, DEFAULT_ICON_SIZE);
+	g_object_unref (pixbuf);
+	gtk_image_set_from_pixbuf (GTK_IMAGE (data->image), framed);
+	g_object_unref (framed);
 }
 
 #ifdef HAVE_WEBKIT2
@@ -402,7 +541,7 @@ download_finished_cb (WebKitDownload *download,
 	char *filename;
 
 	filename = g_filename_from_uri (webkit_download_get_destination (download), NULL, NULL);
-	gtk_image_set_from_file (GTK_IMAGE (data->image), filename);
+	set_app_icon_from_filename (data, filename);
 	g_free (filename);
 }
 
@@ -429,7 +568,7 @@ download_status_changed_cb (WebKitDownload *download,
 	case WEBKIT_DOWNLOAD_STATUS_FINISHED:
 		filename = g_filename_from_uri (webkit_download_get_destination_uri (download),
 						   NULL, NULL);
-		gtk_image_set_from_file (GTK_IMAGE (data->image), filename);
+		set_app_icon_from_filename (data, filename);
 		g_free (filename);
 		break;
 	case WEBKIT_DOWNLOAD_STATUS_ERROR:
@@ -486,39 +625,28 @@ download_icon_and_set_image (EphyApplicationDialogData *data)
 #endif
 }
 
+
 static void
 fill_default_application_image (EphyApplicationDialogData *data)
 {
-#ifdef HAVE_WEBKIT2
-	/* TODO: DOM Bindindgs */
-#else
-	WebKitDOMDocument *document;
-	WebKitDOMNodeList *links;
-	gulong length, i;
+	gboolean res;
 
-	document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (data->view));
-	links = webkit_dom_document_get_elements_by_tag_name (document, "link");
-	length = webkit_dom_node_list_get_length (links);
+	data->icon_rgba.red = 0.5;
+	data->icon_rgba.green = 0.5;
+	data->icon_rgba.blue = 0.5;
+	data->icon_rgba.alpha = 0.3;
 
-	for (i = 0; i < length; i++)
+	res = ephy_web_view_get_best_icon (WEBKIT_WEB_VIEW (data->view),
+					   &data->icon_href,
+					   &data->icon_rgba);
+	if (res)
 	{
-		char *rel;
-		WebKitDOMNode *node = webkit_dom_node_list_item (links, i);
-		rel = webkit_dom_html_link_element_get_rel (WEBKIT_DOM_HTML_LINK_ELEMENT (node));
-		/* TODO: support more than one possible icon. */
-		if (g_strcmp0 (rel, "apple-touch-icon") == 0 ||
-		    g_strcmp0 (rel, "apple-touch-icon-precomposed") == 0)
-		{
-			data->icon_href = webkit_dom_html_link_element_get_href (WEBKIT_DOM_HTML_LINK_ELEMENT (node));
-			download_icon_and_set_image (data);
-			g_free (rel);
-			return;
-		}
+		download_icon_and_set_image (data);
+	}
+	else
+	{
+		take_page_snapshot_and_set_image (data);
 	}
-#endif
-	/* If we make it here, no "apple-touch-icon" link was
-	 * found. Take a snapshot of the page. */
-	take_page_snapshot_and_set_image (data);
 }
 
 typedef struct {
@@ -718,6 +846,7 @@ window_cmd_file_save_as_application (GtkAction *action,
 	GtkWidget *dialog, *box, *image, *entry, *content_area;
 	EphyWebView *view;
 	EphyApplicationDialogData *data;
+	GdkPixbuf *pixbuf;
 
 	embed = ephy_embed_container_get_active_child (EPHY_EMBED_CONTAINER (window));
 	g_return_if_fail (embed != NULL);
@@ -742,8 +871,11 @@ window_cmd_file_save_as_application (GtkAction *action,
 	gtk_container_add (GTK_CONTAINER (content_area), box);
 
 	image = gtk_image_new ();
-	gtk_widget_set_size_request (image, 128, 128);
+	gtk_widget_set_size_request (image, DEFAULT_ICON_SIZE, DEFAULT_ICON_SIZE);
 	gtk_container_add (GTK_CONTAINER (box), image);
+	pixbuf = frame_pixbuf (NULL, NULL, DEFAULT_ICON_SIZE, DEFAULT_ICON_SIZE);
+	gtk_image_set_from_pixbuf (GTK_IMAGE (image), pixbuf);
+	g_object_unref (pixbuf);
 
 	entry = gtk_entry_new ();
 	gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);



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