aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorXan Lopez <xlopez@igalia.com>2011-07-14 06:47:33 +0800
committerXan Lopez <xlopez@igalia.com>2011-08-29 03:23:26 +0800
commitebac66a05be1e71bd978f070fb842fd8b457e7e8 (patch)
tree549cd1797c49a39cf20be47b5567441f208a5fa5
parentbebb9f4299f4a86439ed04b335b6852234af0622 (diff)
downloadgsoc2013-epiphany-ebac66a05be1e71bd978f070fb842fd8b457e7e8.tar
gsoc2013-epiphany-ebac66a05be1e71bd978f070fb842fd8b457e7e8.tar.gz
gsoc2013-epiphany-ebac66a05be1e71bd978f070fb842fd8b457e7e8.tar.bz2
gsoc2013-epiphany-ebac66a05be1e71bd978f070fb842fd8b457e7e8.tar.lz
gsoc2013-epiphany-ebac66a05be1e71bd978f070fb842fd8b457e7e8.tar.xz
gsoc2013-epiphany-ebac66a05be1e71bd978f070fb842fd8b457e7e8.tar.zst
gsoc2013-epiphany-ebac66a05be1e71bd978f070fb842fd8b457e7e8.zip
Add basic support for Web Applications
Allow to save any page as a "Web Application". A new .desktop file will be created, and added to the Shell as a new application. It will launch epiphany in application mode, with its own private profile (inheriting some data from the main profile, like the relevant domain cookies) and in a new process.
-rw-r--r--data/ui/epiphany-ui.xml1
-rw-r--r--embed/ephy-web-view.c203
-rw-r--r--embed/ephy-web-view.h8
-rw-r--r--src/ephy-main.c33
-rw-r--r--src/ephy-toolbars-model.c12
-rw-r--r--src/ephy-window.c28
-rw-r--r--src/window-commands.c191
-rw-r--r--src/window-commands.h3
8 files changed, 453 insertions, 26 deletions
diff --git a/data/ui/epiphany-ui.xml b/data/ui/epiphany-ui.xml
index 1c2fff369..2091defc8 100644
--- a/data/ui/epiphany-ui.xml
+++ b/data/ui/epiphany-ui.xml
@@ -6,6 +6,7 @@
<menuitem name="FileOpenMenu" action="FileOpen"/>
<separator name="FileSep1"/>
<menuitem name="FileSaveAsMenu" action="FileSaveAs"/>
+ <menuitem name="FileSaveAsApplicationMenu" action="FileSaveAsApplication"/>
<separator name="FileSep2"/>
<menuitem name="FilePrintSetupMenu" action="FilePrintSetup"/>
<menuitem name="FilePrintPreviewMenu" action="FilePrintPreview"/>
diff --git a/embed/ephy-web-view.c b/embed/ephy-web-view.c
index fd4577208..6a52f06ae 100644
--- a/embed/ephy-web-view.c
+++ b/embed/ephy-web-view.c
@@ -24,10 +24,12 @@
#include <gio/gio.h>
#include <glib/gi18n.h>
+#include <glib/gstdio.h>
#include <gtk/gtk.h>
#include <string.h>
#include <webkit/webkit.h>
#include <gnome-keyring.h>
+#include <libsoup/soup-gnome.h>
#include "ephy-debug.h"
#include "ephy-embed.h"
@@ -3684,3 +3686,204 @@ ephy_web_view_load_homepage (EphyWebView *view)
return is_empty;
}
+#define EPHY_WEB_APP_TOOLBAR "<?xml version=\"1.0\"?>" \
+ "<toolbars version=\"1.1\">" \
+ " <toolbar name=\"DefaultToolbar\" hidden=\"true\" editable=\"false\">" \
+ " <toolitem name=\"NavigationBack\"/>" \
+ " <toolitem name=\"NavigationForward\"/>" \
+ " <toolitem name=\"ViewReload\"/>" \
+ " <toolitem name=\"ViewCancel\"/>" \
+ " </toolbar>" \
+ "</toolbars>"
+
+#define EPHY_TOOLBARS_XML_FILE "epiphany-toolbars-3.xml"
+
+static char *
+create_desktop_file (EphyWebView *view,
+ const char *profile_dir,
+ const char *title,
+ GdkPixbuf *icon)
+{
+ GKeyFile *file;
+ char *exec_string;
+ char *data;
+ char *filename, *desktop_file_path;
+ char *link_path;
+ GFile *link;
+
+ g_return_val_if_fail (profile_dir, NULL);
+
+ file = g_key_file_new ();
+ g_key_file_set_value (file, "Desktop Entry", "Name", title);
+ exec_string = g_strdup_printf ("jhbuild run epiphany --application-mode --profile=\"%s\" %s",
+ profile_dir,
+ webkit_web_view_get_uri (WEBKIT_WEB_VIEW (view)));
+ g_key_file_set_value (file, "Desktop Entry", "Exec", exec_string);
+ g_free (exec_string);
+ g_key_file_set_value (file, "Desktop Entry", "StartupNotification", "true");
+ g_key_file_set_value (file, "Desktop Entry", "Terminal", "false");
+ g_key_file_set_value (file, "Desktop Entry", "Type", "Application");
+
+ if (icon) {
+ GOutputStream *stream;
+ char *path;
+ GFile *image;
+
+ path = g_build_filename (profile_dir, "app-icon.png", NULL);
+ image = g_file_new_for_path (path);
+
+ stream = (GOutputStream*)g_file_create (image, 0, NULL, NULL);
+ gdk_pixbuf_save_to_stream (icon, stream, "png", NULL, NULL, NULL);
+ g_key_file_set_value (file, "Desktop Entry", "Icon", path);
+
+ g_object_unref (stream);
+ g_object_unref (image);
+ g_free (path);
+ }
+
+ g_key_file_set_value (file, "Desktop Entry", "StartupWMClass", title);
+
+ data = g_key_file_to_data (file, NULL, NULL);
+ filename = g_strconcat (title, ".desktop", NULL);
+ desktop_file_path = g_build_filename (profile_dir, filename, NULL);
+ g_key_file_free (file);
+
+ if (!g_file_set_contents (desktop_file_path, data, -1, NULL)) {
+ g_free (desktop_file_path);
+ desktop_file_path = NULL;
+ }
+
+ g_free (data);
+
+ /* Create a symlink in XDG_DATA_DIR/applications for the Shell to
+ * pick up this application. */
+ link_path = g_build_filename (g_get_user_data_dir (), "applications", filename, NULL);
+ link = g_file_new_for_path (link_path);
+ g_free (link_path);
+ g_file_make_symbolic_link (link, desktop_file_path, NULL, NULL);
+ g_object_unref (link);
+ g_free (filename);
+
+ return desktop_file_path;
+}
+
+static void
+create_cookie_jar_for_domain (EphyWebView *view, const char *directory)
+{
+ SoupSession *session;
+ GSList *cookies, *p;
+ SoupCookieJar *current_jar, *new_jar;
+ char *domain, *filename;
+ SoupURI *uri;
+
+ /* Create the new cookie jar */
+ filename = g_build_filename (directory, "cookies.sqlite", NULL);
+ new_jar = (SoupCookieJar*)soup_cookie_jar_sqlite_new (filename, FALSE);
+ g_free (filename);
+
+ /* The app domain for the current view */
+ uri = soup_uri_new (webkit_web_view_get_uri (WEBKIT_WEB_VIEW (view)));
+ domain = uri->host;
+
+ /* The current cookies */
+ session = webkit_get_default_session ();
+ current_jar = (SoupCookieJar*)soup_session_get_feature (session, SOUP_TYPE_COOKIE_JAR);
+ cookies = soup_cookie_jar_all_cookies (current_jar);
+
+ for (p = cookies; p; p = p->next) {
+ SoupCookie *cookie = (SoupCookie*)p->data;
+
+ if (g_str_has_suffix (cookie->domain, domain))
+ soup_cookie_jar_add_cookie (new_jar, cookie);
+ else
+ soup_cookie_free (cookie);
+ }
+
+ soup_uri_free (uri);
+ g_slist_free (cookies);
+}
+
+char *
+ephy_web_view_create_web_application (EphyWebView *view, const char *title, GdkPixbuf *icon)
+{
+ char *app_dir;
+ char *profile_dir = NULL;
+ char *toolbar_path = NULL;
+ char *desktop_file_path = NULL;
+
+ g_return_val_if_fail (EPHY_IS_WEB_VIEW (view), NULL);
+
+ /* If there's already a WebApp profile for the contents of this
+ * view, do nothing. TODO: create a method to check this and use it
+ * to ask the user if she wants to overwrite the existing WebApp. */
+ app_dir = g_strconcat (EPHY_WEB_APP_PREFIX, title, NULL);
+ profile_dir = g_build_filename (ephy_dot_dir (), app_dir, NULL);
+ g_free (app_dir);
+ if (g_file_test (profile_dir, G_FILE_TEST_IS_DIR))
+ goto out;
+
+ /* Create the profile directory, populate it. */
+ if (g_mkdir (profile_dir, 488) == -1) {
+ LOG ("Failed to create directory %s", profile_dir);
+ goto out;
+ }
+
+ /* Things we need in a WebApp's profile:
+ - Toolbar layout
+ - Our own cookies file, copying the relevant cookies for the
+ app's domain.
+ */
+ toolbar_path = g_build_filename (profile_dir, EPHY_TOOLBARS_XML_FILE, NULL);
+ if (!g_file_set_contents (toolbar_path, EPHY_WEB_APP_TOOLBAR, -1, NULL))
+ goto out;
+
+ create_cookie_jar_for_domain (view, profile_dir);
+
+ /* Create the deskop file. */
+ desktop_file_path = create_desktop_file (view, profile_dir, title, icon);
+
+out:
+ if (toolbar_path)
+ g_free (toolbar_path);
+
+ if (profile_dir)
+ g_free (profile_dir);
+
+ return desktop_file_path;
+}
+
+/**
+ * ephy_web_view_get_snapshot: takes a snapshot of the requested region of a #EphyWebView
+ * @view: the #EphyWebView
+ * @x: the x coordinate of the snapshot
+ * @y: the y coordinate of the snapshot
+ * @width: the width of the snapshot
+ * @height: the height of the snapshot
+ *
+ * Returns: (transfer full): a #GdkPixbuf with a snapshot of the requested area.
+ **/
+GdkPixbuf *
+ephy_web_view_get_snapshot (EphyWebView *view, int x, int y, int width, int height)
+{
+ cairo_surface_t *surface;
+ cairo_t *cr;
+ GdkPixbuf *snapshot;
+ GtkAllocation allocation;
+
+ g_return_val_if_fail (EPHY_IS_WEB_VIEW (view), NULL);
+
+ gtk_widget_get_allocation (GTK_WIDGET (view), &allocation);
+ surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
+ allocation.width,
+ allocation.height);
+ cr = cairo_create (surface);
+ cairo_rectangle (cr, x, y, width, height);
+ cairo_clip (cr);
+ gtk_widget_draw (GTK_WIDGET (view), cr);
+
+ snapshot = gdk_pixbuf_get_from_surface (surface, x, y, width, height);
+ cairo_destroy (cr);
+ cairo_surface_destroy (surface);
+
+ return snapshot;
+}
diff --git a/embed/ephy-web-view.h b/embed/ephy-web-view.h
index 508ed2c99..bf865d3d9 100644
--- a/embed/ephy-web-view.h
+++ b/embed/ephy-web-view.h
@@ -88,6 +88,8 @@ typedef enum {
EPHY_WEB_VIEW_ERROR_PAGE_CRASH
} EphyWebViewErrorPage;
+#define EPHY_WEB_APP_PREFIX "app-"
+
struct _EphyWebView
{
WebKitWebView parent;
@@ -194,6 +196,12 @@ void ephy_web_view_save (EphyWebView
const char *uri);
gboolean ephy_web_view_load_homepage (EphyWebView *view);
+char *
+ephy_web_view_create_web_application (EphyWebView *view, const char *title, GdkPixbuf *icon);
+
+GdkPixbuf *
+ephy_web_view_get_snapshot (EphyWebView *view, int x, int y, int width, int height);
+
G_END_DECLS
#endif
diff --git a/src/ephy-main.c b/src/ephy-main.c
index b85fa7562..a87b4b496 100644
--- a/src/ephy-main.c
+++ b/src/ephy-main.c
@@ -261,9 +261,6 @@ main (int argc,
*/
LIBXML_TEST_VERSION;
- /* sets name to help matching with the .desktop file */
- g_set_prgname ("epiphany");
-
/* If we're given -remote arguments, translate them */
if (argc >= 2 && strcmp (argv[1], "-remote") == 0) {
const char *opening, *closing;
@@ -400,12 +397,6 @@ main (int argc,
if (user_time == 0)
user_time = slowly_and_stupidly_obtain_timestamp (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()));
- /* sets the name to appear in the window list applet when grouping windows */
- g_set_application_name (_("Web Browser"));
-
- /* Set default window icon */
- gtk_window_set_default_icon_name (EPHY_STOCK_EPHY);
-
startup_error_quark = g_quark_from_static_string ("epiphany-startup-error");
/* Start our services */
@@ -426,11 +417,31 @@ main (int argc,
/* Now create the shell */
if (private_instance)
mode = EPHY_EMBED_SHELL_MODE_PRIVATE;
- else if (application_mode)
+ else if (application_mode) {
+ char *app_name;
+
mode = EPHY_EMBED_SHELL_MODE_APPLICATION;
- else
+
+ app_name = g_strrstr (profile_directory, EPHY_WEB_APP_PREFIX);
+ if (app_name) {
+ /* Skip the 'app-' part */
+ app_name += strlen (EPHY_WEB_APP_PREFIX);
+
+ g_set_prgname (app_name);
+ g_set_application_name (app_name);
+
+ /* We need to re-set this because we have already parsed the
+ * options, which inits GTK+ and sets this as a side effect. */
+ gdk_set_program_class (app_name);
+ }
+ } else {
mode = EPHY_EMBED_SHELL_MODE_BROWSER;
+ g_set_prgname ("epiphany");
+ g_set_application_name (_("Web Browser"));
+ gtk_window_set_default_icon_name (EPHY_STOCK_EPHY);
+ }
+
_ephy_shell_create_instance (mode);
startup_flags = get_startup_flags ();
diff --git a/src/ephy-toolbars-model.c b/src/ephy-toolbars-model.c
index 5dc6cf4b1..346aa0253 100644
--- a/src/ephy-toolbars-model.c
+++ b/src/ephy-toolbars-model.c
@@ -19,13 +19,14 @@
*/
#include "config.h"
-
#include "ephy-toolbars-model.h"
+
+#include "eggtypebuiltins.h"
+#include "ephy-debug.h"
+#include "ephy-embed-shell.h"
#include "ephy-file-helpers.h"
#include "ephy-prefs.h"
#include "ephy-settings.h"
-#include "eggtypebuiltins.h"
-#include "ephy-debug.h"
#include <string.h>
@@ -213,8 +214,9 @@ ephy_toolbars_model_load (EphyToolbarsModel *model)
}
}
- /* Ensure we have at least 1 toolbar */
- if (egg_toolbars_model_n_toolbars (eggmodel) < 1)
+ /* Ensure we have at least 1 toolbar unless we are in WebApp mode. */
+ if (ephy_embed_shell_get_mode (embed_shell) != EPHY_EMBED_SHELL_MODE_APPLICATION &&
+ egg_toolbars_model_n_toolbars (eggmodel) < 1)
{
egg_toolbars_model_add_toolbar (eggmodel, 0, "DefaultToolbar");
}
diff --git a/src/ephy-window.c b/src/ephy-window.c
index f8cedf9be..a54ee25ff 100644
--- a/src/ephy-window.c
+++ b/src/ephy-window.c
@@ -131,6 +131,9 @@ static const GtkActionEntry ephy_menu_entries [] = {
{ "FileSaveAs", GTK_STOCK_SAVE_AS, N_("Save _As…"), "<shift><control>S",
N_("Save the current page"),
G_CALLBACK (window_cmd_file_save_as) },
+ { "FileSaveAsApplication", GTK_STOCK_SAVE_AS, N_("Save As _Web Application…"), "<shift><control>A",
+ N_("Save the current page as a Web Application"),
+ G_CALLBACK (window_cmd_file_save_as_application) },
{ "FilePrintSetup", STOCK_PRINT_SETUP, N_("Page Set_up"), NULL,
N_("Setup the page settings for printing"),
G_CALLBACK (window_cmd_file_print_setup) },
@@ -380,19 +383,19 @@ static const struct
const gchar *action;
gboolean fromToolbar;
} extra_keybindings [] = {
- { GDK_KEY_s, GDK_CONTROL_MASK, "FileSaveAs", FALSE },
+ { GDK_KEY_s, GDK_CONTROL_MASK, "FileSaveAs", FALSE },
{ GDK_KEY_R, GDK_CONTROL_MASK |
- GDK_SHIFT_MASK, "ViewReload", FALSE },
+ GDK_SHIFT_MASK, "ViewReload", FALSE },
/* Support all the MSIE tricks as well ;) */
- { GDK_KEY_F5, 0, "ViewReload", FALSE },
- { GDK_KEY_F5, GDK_CONTROL_MASK, "ViewReload", FALSE },
- { GDK_KEY_F5, GDK_SHIFT_MASK, "ViewReload", FALSE },
+ { GDK_KEY_F5, 0, "ViewReload", FALSE },
+ { GDK_KEY_F5, GDK_CONTROL_MASK, "ViewReload", FALSE },
+ { GDK_KEY_F5, GDK_SHIFT_MASK, "ViewReload", FALSE },
{ GDK_KEY_F5, GDK_CONTROL_MASK |
- GDK_SHIFT_MASK, "ViewReload", FALSE },
- { GDK_KEY_KP_Add, GDK_CONTROL_MASK, "ViewZoomIn", FALSE },
- { GDK_KEY_KP_Subtract, GDK_CONTROL_MASK, "ViewZoomOut", FALSE },
- { GDK_KEY_equal, GDK_CONTROL_MASK, "ViewZoomIn", FALSE },
- { GDK_KEY_KP_0, GDK_CONTROL_MASK, "ViewZoomNormal", FALSE },
+ GDK_SHIFT_MASK, "ViewReload", FALSE },
+ { GDK_KEY_KP_Add, GDK_CONTROL_MASK, "ViewZoomIn", FALSE },
+ { GDK_KEY_KP_Subtract, GDK_CONTROL_MASK, "ViewZoomOut", FALSE },
+ { GDK_KEY_equal, GDK_CONTROL_MASK, "ViewZoomIn", FALSE },
+ { GDK_KEY_KP_0, GDK_CONTROL_MASK, "ViewZoomNormal", FALSE },
/* These keys are a bit strange: when pressed with no modifiers, they emit
* KP_PageUp/Down Control; when pressed with Control+Shift they are KP_9/3,
* when NumLock is on they are KP_9/3 and with NumLock and Control+Shift
@@ -804,6 +807,9 @@ get_chromes_visibility (EphyWindow *window,
*show_toolbar = (flags & EPHY_WEB_VIEW_CHROME_TOOLBAR) != 0;
*show_tabsbar = !priv->is_popup;
}
+
+ if (ephy_embed_shell_get_mode (embed_shell) == EPHY_EMBED_SHELL_MODE_APPLICATION)
+ *show_menubar = FALSE;
}
static void
@@ -1586,6 +1592,8 @@ setup_ui_manager (EphyWindow *window)
g_object_set (action, "short_label", _("Open"), NULL);
action = gtk_action_group_get_action (action_group, "FileSaveAs");
g_object_set (action, "short_label", _("Save As"), NULL);
+ action = gtk_action_group_get_action (action_group, "FileSaveAsApplication");
+ g_object_set (action, "short_label", _("Save As Application"), NULL);
action = gtk_action_group_get_action (action_group, "FilePrint");
g_object_set (action, "short_label", _("Print"), NULL);
action = gtk_action_group_get_action (action_group, "FileBookmarkPage");
diff --git a/src/window-commands.c b/src/window-commands.c
index 32043c910..341ea4014 100644
--- a/src/window-commands.c
+++ b/src/window-commands.c
@@ -2,6 +2,7 @@
/*
* Copyright © 2000-2004 Marco Pesenti Gritti
* Copyright © 2009 Collabora Ltd.
+ * Copyright © 2011 Igalia S.L.
*
* 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
@@ -382,6 +383,196 @@ window_cmd_file_save_as (GtkAction *action,
gtk_widget_show (GTK_WIDGET (dialog));
}
+typedef struct {
+ EphyWebView *view;
+ GtkWidget *image;
+ GtkWidget *entry;
+ GtkWidget *spinner;
+ GtkWidget *box;
+ char *icon_href;
+} EphyApplicationDialogData;
+
+static void
+ephy_application_dialog_data_free (EphyApplicationDialogData *data)
+{
+ g_free (data->icon_href);
+ g_slice_free (EphyApplicationDialogData, data);
+}
+
+static void
+take_page_snapshot_and_set_image (EphyApplicationDialogData *data)
+{
+ GdkPixbuf *snapshot;
+ int x, y, w, h;
+
+ x = y = 0;
+ w = h = 128; /* GNOME hi-res icon size. */
+
+ snapshot = ephy_web_view_get_snapshot (data->view, x, y, w, h);
+
+ gtk_image_set_from_pixbuf (GTK_IMAGE (data->image), snapshot);
+ g_object_unref (snapshot);
+}
+
+static void
+download_status_changed_cb (WebKitDownload *download,
+ GParamSpec *spec,
+ EphyApplicationDialogData *data)
+{
+ WebKitDownloadStatus status = webkit_download_get_status (download);
+ const char *destination;
+
+ switch (status)
+ {
+ case WEBKIT_DOWNLOAD_STATUS_FINISHED:
+ destination = g_filename_from_uri (webkit_download_get_destination_uri (download),
+ NULL, NULL);
+ gtk_image_set_from_file (GTK_IMAGE (data->image), destination);
+ break;
+ case WEBKIT_DOWNLOAD_STATUS_ERROR:
+ case WEBKIT_DOWNLOAD_STATUS_CANCELLED:
+ /* Something happened, default to a page snapshot. */
+ take_page_snapshot_and_set_image (data);
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+download_icon_and_set_image (EphyApplicationDialogData *data)
+{
+ WebKitNetworkRequest *request;
+ WebKitDownload *download;
+ char *destination, *destination_uri, *tmp_filename;
+
+ request = webkit_network_request_new (data->icon_href);
+ download = webkit_download_new (request);
+ g_object_unref (request);
+
+ tmp_filename = ephy_file_tmp_filename ("ephy-download-XXXXXX", NULL);
+ destination = g_build_filename (ephy_file_tmp_dir (), tmp_filename, NULL);
+ destination_uri = g_filename_to_uri (destination, NULL, NULL);
+ webkit_download_set_destination_uri (download, destination_uri);
+ g_free (destination);
+ g_free (destination_uri);
+ g_free (tmp_filename);
+
+ g_signal_connect (download, "notify::status",
+ G_CALLBACK (download_status_changed_cb), data);
+
+ webkit_download_start (download);
+}
+
+static void
+fill_default_application_image (EphyApplicationDialogData *data)
+{
+ WebKitDOMDocument *document;
+ WebKitDOMNodeList *links;
+ gulong length, i;
+
+ 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);
+
+ 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));
+ /* 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;
+ }
+ }
+
+ /* 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);
+}
+
+static void
+fill_default_application_title (EphyApplicationDialogData *data)
+{
+ const char *title = ephy_web_view_get_title (data->view);
+ gtk_entry_set_text (GTK_ENTRY (data->entry), title);
+}
+
+void
+window_cmd_file_save_as_application (GtkAction *action,
+ EphyWindow *window)
+{
+ EphyEmbed *embed;
+ GtkWidget *dialog, *box, *image, *entry, *content_area;
+ EphyWebView *view;
+ gboolean response;
+ EphyApplicationDialogData *data;
+
+ embed = ephy_embed_container_get_active_child (EPHY_EMBED_CONTAINER (window));
+ g_return_if_fail (embed != NULL);
+
+ view = EPHY_WEB_VIEW (EPHY_GET_WEBKIT_WEB_VIEW_FROM_EMBED (embed));
+
+ /* Show dialog with icon, title. */
+ dialog = gtk_dialog_new_with_buttons (_("Create Web Application"),
+ GTK_WINDOW (window),
+ 0,
+ GTK_STOCK_CANCEL,
+ GTK_RESPONSE_CANCEL,
+ _("Create"),
+ GTK_RESPONSE_OK,
+ NULL);
+
+ content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+ gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
+ gtk_box_set_spacing (GTK_BOX (content_area), 14); /* 14 + 2 * 5 = 24 */
+
+ box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
+ gtk_container_add (GTK_CONTAINER (content_area), box);
+
+ image = gtk_image_new ();
+ gtk_widget_set_size_request (image, 128, 128);
+ gtk_container_add (GTK_CONTAINER (box), image);
+
+ entry = gtk_entry_new ();
+ gtk_box_pack_end (GTK_BOX (box), entry, FALSE, FALSE, 0);
+
+ data = g_slice_new0 (EphyApplicationDialogData);
+ data->view = view;
+ data->image = image;
+ data->entry = entry;
+
+ fill_default_application_image (data);
+ fill_default_application_title (data);
+
+ gtk_widget_show_all (dialog);
+
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+ response = gtk_dialog_run (GTK_DIALOG (dialog));
+
+ if (response == GTK_RESPONSE_OK)
+ {
+ char *desktop_file;
+
+ /* Create Web Application, including a new profile and .desktop file. */
+ desktop_file = ephy_web_view_create_web_application (view,
+ gtk_entry_get_text (GTK_ENTRY (data->entry)),
+ gtk_image_get_pixbuf (GTK_IMAGE (data->image)));
+
+ /* TODO: show a notification when the app is totally
+ * created and ready to be launched */
+ g_free (desktop_file);
+ }
+
+ ephy_application_dialog_data_free (data);
+ gtk_widget_destroy (dialog);
+}
+
void
window_cmd_file_work_offline (GtkAction *action,
EphyWindow *window)
diff --git a/src/window-commands.h b/src/window-commands.h
index 5b375851b..e20ca1a06 100644
--- a/src/window-commands.h
+++ b/src/window-commands.h
@@ -54,6 +54,9 @@ void window_cmd_file_open (GtkAction *action,
void window_cmd_file_save_as (GtkAction *action,
EphyWindow *window);
+void window_cmd_file_save_as_application (GtkAction *action,
+ EphyWindow *window);
+
void window_cmd_file_print_setup (GtkAction *action,
EphyWindow *window);