summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMichael Catanzaro <mcatanzaro@gnome.org>2016-09-11 09:59:03 -0500
committerMichael Catanzaro <mcatanzaro@gnome.org>2016-10-07 19:33:22 -0500
commite54cb1c7e97ddca58977a8ee01a138ff57ba6f2c (patch)
tree164165750b109ae888ce9b55d785fad9ce748e50 /src
parentd71e1687953dca9787c3763928a88d7397081b94 (diff)
downloadepiphany-e54cb1c7e97ddca58977a8ee01a138ff57ba6f2c.tar.gz
Relicense to GPLv3+
To use GMP. Because a few GPLv3+ files had already snuck in by mistake. And because http://www.gnu.org/licenses/rms-why-gplv3.html
Diffstat (limited to 'src')
-rw-r--r--src/bookmarks/ephy-add-bookmark-popover.c12
-rw-r--r--src/bookmarks/ephy-add-bookmark-popover.h12
-rw-r--r--src/bookmarks/ephy-bookmark-action-group.c169
-rw-r--r--src/bookmarks/ephy-bookmark-action.c442
-rw-r--r--src/bookmarks/ephy-bookmark-action.h73
-rw-r--r--src/bookmarks/ephy-bookmark-properties-grid.c24
-rw-r--r--src/bookmarks/ephy-bookmark-properties-grid.h24
-rw-r--r--src/bookmarks/ephy-bookmark-properties.c519
-rw-r--r--src/bookmarks/ephy-bookmark-properties.h40
-rw-r--r--src/bookmarks/ephy-bookmark-row.c24
-rw-r--r--src/bookmarks/ephy-bookmark-row.h24
-rw-r--r--src/bookmarks/ephy-bookmark.c24
-rw-r--r--src/bookmarks/ephy-bookmark.h24
-rw-r--r--src/bookmarks/ephy-bookmarks-editor.c1721
-rw-r--r--src/bookmarks/ephy-bookmarks-editor.h39
-rw-r--r--src/bookmarks/ephy-bookmarks-export.c494
-rw-r--r--src/bookmarks/ephy-bookmarks-export.h33
-rw-r--r--src/bookmarks/ephy-bookmarks-import.c853
-rw-r--r--src/bookmarks/ephy-bookmarks-import.h47
-rw-r--r--src/bookmarks/ephy-bookmarks-manager.c24
-rw-r--r--src/bookmarks/ephy-bookmarks-manager.h24
-rw-r--r--src/bookmarks/ephy-bookmarks-menu.c241
-rw-r--r--src/bookmarks/ephy-bookmarks-popover.c24
-rw-r--r--src/bookmarks/ephy-bookmarks-popover.h24
-rw-r--r--src/bookmarks/ephy-bookmarks-ui.c365
-rw-r--r--src/bookmarks/ephy-bookmarks-ui.h61
-rw-r--r--src/bookmarks/ephy-bookmarks.c1594
-rw-r--r--src/bookmarks/ephy-bookmarks.h136
-rw-r--r--src/bookmarks/ephy-link-action.c174
-rw-r--r--src/bookmarks/ephy-link-action.h79
-rw-r--r--src/bookmarks/ephy-nodes-cover.c196
-rw-r--r--src/bookmarks/ephy-open-tabs-action.c134
-rw-r--r--src/bookmarks/ephy-topic-action-group.c124
-rw-r--r--src/bookmarks/ephy-topic-action-group.h33
-rw-r--r--src/bookmarks/ephy-topic-action.c331
-rw-r--r--src/bookmarks/ephy-topic-action.h69
-rw-r--r--src/bookmarks/ephy-topics-entry.c597
-rw-r--r--src/bookmarks/ephy-topics-entry.h34
-rw-r--r--src/bookmarks/ephy-topics-palette.c337
-rw-r--r--src/bookmarks/ephy-topics-palette.h44
-rw-r--r--src/bookmarks/ephy-window-action.c108
-rw-r--r--src/bookmarks/ephy-window-action.h55
-rw-r--r--src/clear-data-dialog.c12
-rw-r--r--src/clear-data-dialog.h12
-rw-r--r--src/cookies-dialog.c12
-rw-r--r--src/cookies-dialog.h12
-rw-r--r--src/ephy-action-helper.c12
-rw-r--r--src/ephy-action-helper.h12
-rw-r--r--src/ephy-completion-model.c10
-rw-r--r--src/ephy-completion-model.h10
-rw-r--r--src/ephy-encoding-dialog.c12
-rw-r--r--src/ephy-encoding-dialog.h12
-rw-r--r--src/ephy-encoding-row.c14
-rw-r--r--src/ephy-encoding-row.h14
-rw-r--r--src/ephy-header-bar.c12
-rw-r--r--src/ephy-header-bar.h12
-rw-r--r--src/ephy-history-window.c10
-rw-r--r--src/ephy-history-window.h10
-rw-r--r--src/ephy-link.c12
-rw-r--r--src/ephy-link.h12
-rw-r--r--src/ephy-location-controller.c12
-rw-r--r--src/ephy-location-controller.h12
-rw-r--r--src/ephy-lockdown.c12
-rw-r--r--src/ephy-lockdown.h12
-rw-r--r--src/ephy-main.c12
-rw-r--r--src/ephy-notebook.c12
-rw-r--r--src/ephy-notebook.h12
-rw-r--r--src/ephy-private.h12
-rw-r--r--src/ephy-session.c12
-rw-r--r--src/ephy-session.h12
-rw-r--r--src/ephy-shell.c12
-rw-r--r--src/ephy-shell.h12
-rw-r--r--src/ephy-sync-crypto.c12
-rw-r--r--src/ephy-sync-crypto.h12
-rw-r--r--src/ephy-sync-secret.c12
-rw-r--r--src/ephy-sync-secret.h12
-rw-r--r--src/ephy-sync-service.c12
-rw-r--r--src/ephy-sync-service.h12
-rw-r--r--src/ephy-sync-utils.c12
-rw-r--r--src/ephy-sync-utils.h12
-rw-r--r--src/ephy-title-box.h45
-rw-r--r--src/ephy-window.c12
-rw-r--r--src/ephy-window.h12
-rw-r--r--src/languages.h10
-rw-r--r--src/passwords-dialog.c12
-rw-r--r--src/passwords-dialog.h12
-rw-r--r--src/popup-commands.c12
-rw-r--r--src/popup-commands.h12
-rw-r--r--src/prefs-dialog.c12
-rw-r--r--src/prefs-dialog.h12
-rw-r--r--src/profile-migrator/ephy-profile-migrator.c12
-rw-r--r--src/resources/error.html14
-rw-r--r--src/search-provider/ephy-search-provider-main.c24
-rw-r--r--src/search-provider/ephy-search-provider.c24
-rw-r--r--src/search-provider/ephy-search-provider.h24
-rw-r--r--src/window-commands.c14
-rw-r--r--src/window-commands.h12
97 files changed, 9724 insertions, 409 deletions
diff --git a/src/bookmarks/ephy-add-bookmark-popover.c b/src/bookmarks/ephy-add-bookmark-popover.c
index 881e747c1..01c3d2a6a 100644
--- a/src/bookmarks/ephy-add-bookmark-popover.c
+++ b/src/bookmarks/ephy-add-bookmark-popover.c
@@ -2,18 +2,20 @@
/*
* Copyright © 2016 Iulian-Gabriel Radu <iulian.radu67@gmail.com>
*
- * This program is free software; you can redistribute it and/or modify
+ * This file is part of Epiphany.
+ *
+ * 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, or (at your option)
- * any later version.
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
diff --git a/src/bookmarks/ephy-add-bookmark-popover.h b/src/bookmarks/ephy-add-bookmark-popover.h
index 82f4fc14b..41d19dee8 100644
--- a/src/bookmarks/ephy-add-bookmark-popover.h
+++ b/src/bookmarks/ephy-add-bookmark-popover.h
@@ -2,18 +2,20 @@
/*
* Copyright © 2016 Iulian-Gabriel Radu <iulian.radu67@gmail.com>
*
- * This program is free software; you can redistribute it and/or modify
+ * This file is part of Epiphany.
+ *
+ * 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, or (at your option)
- * any later version.
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
diff --git a/src/bookmarks/ephy-bookmark-action-group.c b/src/bookmarks/ephy-bookmark-action-group.c
new file mode 100644
index 000000000..099f881e8
--- /dev/null
+++ b/src/bookmarks/ephy-bookmark-action-group.c
@@ -0,0 +1,169 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright © 2005 Peter Harvey
+ *
+ * This file is part of Epiphany.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "ephy-shell.h"
+#include "ephy-bookmark-action-group.h"
+#include "ephy-bookmark-action.h"
+#include "ephy-bookmarks.h"
+#include "ephy-bookmarks-ui.h"
+#include "ephy-link.h"
+#include "ephy-node.h"
+#include "ephy-node-common.h"
+#include "ephy-debug.h"
+
+#include <gtk/gtk.h>
+#include <string.h>
+
+static void
+smart_added_cb (EphyNode *parent,
+ EphyNode *child,
+ GtkActionGroup *action_group)
+{
+ GtkAction *action;
+ char name[EPHY_BOOKMARK_ACTION_NAME_BUFFER_SIZE];
+
+ EPHY_BOOKMARK_ACTION_NAME_PRINTF (name, child);
+
+ action = gtk_action_group_get_action (action_group, name);
+
+ if (action != NULL) {
+ ephy_bookmark_action_updated ((EphyBookmarkAction *)action);
+ }
+}
+
+static void
+smart_removed_cb (EphyNode *parent,
+ EphyNode *child,
+ guint index,
+ GtkActionGroup *action_group)
+{
+ GtkAction *action;
+ char name[EPHY_BOOKMARK_ACTION_NAME_BUFFER_SIZE];
+
+ EPHY_BOOKMARK_ACTION_NAME_PRINTF (name, child);
+
+ action = gtk_action_group_get_action (action_group, name);
+
+ if (action != NULL) {
+ ephy_bookmark_action_updated ((EphyBookmarkAction *)action);
+ }
+}
+
+static void
+node_changed_cb (EphyNode *parent,
+ EphyNode *child,
+ guint property_id,
+ GtkActionGroup *action_group)
+{
+ GtkAction *action;
+ char name[EPHY_BOOKMARK_ACTION_NAME_BUFFER_SIZE];
+
+ EPHY_BOOKMARK_ACTION_NAME_PRINTF (name, child);
+
+ action = gtk_action_group_get_action (action_group, name);
+
+ if (action != NULL) {
+ ephy_bookmark_action_updated ((EphyBookmarkAction *)action);
+ }
+}
+
+static void
+node_added_cb (EphyNode *parent,
+ EphyNode *child,
+ GtkActionGroup *action_group)
+{
+ GtkAction *action;
+ char name[EPHY_BOOKMARK_ACTION_NAME_BUFFER_SIZE];
+ char accel[256];
+
+ EPHY_BOOKMARK_ACTION_NAME_PRINTF (name, child);
+
+ action = ephy_bookmark_action_new (child, name);
+
+ g_signal_connect_swapped (action, "open-link",
+ G_CALLBACK (ephy_link_open), action_group);
+
+ g_snprintf (accel, sizeof (accel), "<Actions>/%s/%s",
+ gtk_action_group_get_name (action_group),
+ name);
+ gtk_action_set_accel_path (action, accel);
+ gtk_action_group_add_action (action_group, action);
+ g_object_unref (action);
+}
+
+static void
+node_removed_cb (EphyNode *parent,
+ EphyNode *child,
+ guint index,
+ GtkActionGroup *action_group)
+{
+ GtkAction *action;
+ char name[EPHY_BOOKMARK_ACTION_NAME_BUFFER_SIZE];
+
+ EPHY_BOOKMARK_ACTION_NAME_PRINTF (name, child);
+
+ action = gtk_action_group_get_action (action_group, name);
+
+ if (action != NULL) {
+ gtk_action_group_remove_action (action_group, action);
+ }
+}
+
+GtkActionGroup *
+ephy_bookmark_group_new (EphyNode *node)
+{
+ EphyBookmarks *bookmarks;
+ EphyNode *smart;
+ GPtrArray *children;
+ GtkActionGroup *action_group;
+ guint i;
+
+ bookmarks = ephy_shell_get_bookmarks (ephy_shell_get_default ());
+ smart = ephy_bookmarks_get_smart_bookmarks (bookmarks);
+
+ action_group = (GtkActionGroup *)ephy_link_action_group_new ("BA");
+
+ children = ephy_node_get_children (node);
+ for (i = 0; i < children->len; i++) {
+ node_added_cb (node, g_ptr_array_index (children, i),
+ action_group);
+ }
+
+ ephy_node_signal_connect_object (node, EPHY_NODE_CHILD_ADDED,
+ (EphyNodeCallback)node_added_cb,
+ (GObject *)action_group);
+ ephy_node_signal_connect_object (node, EPHY_NODE_CHILD_REMOVED,
+ (EphyNodeCallback)node_removed_cb,
+ (GObject *)action_group);
+ ephy_node_signal_connect_object (node, EPHY_NODE_CHILD_CHANGED,
+ (EphyNodeCallback)node_changed_cb,
+ (GObject *)action_group);
+
+ ephy_node_signal_connect_object (smart, EPHY_NODE_CHILD_ADDED,
+ (EphyNodeCallback)smart_added_cb,
+ (GObject *)action_group);
+ ephy_node_signal_connect_object (smart, EPHY_NODE_CHILD_REMOVED,
+ (EphyNodeCallback)smart_removed_cb,
+ (GObject *)action_group);
+
+ return action_group;
+}
diff --git a/src/bookmarks/ephy-bookmark-action.c b/src/bookmarks/ephy-bookmark-action.c
new file mode 100644
index 000000000..e5dbcb166
--- /dev/null
+++ b/src/bookmarks/ephy-bookmark-action.c
@@ -0,0 +1,442 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright © 2003, 2004 Marco Pesenti Gritti
+ * Copyright © 2003, 2004 Christian Persch
+ * Copyright © 2005 Peter Harvey
+ *
+ * This file is part of Epiphany.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+#include "ephy-bookmark-action.h"
+
+#include "ephy-bookmarks-ui.h"
+#include "ephy-bookmarks.h"
+#include "ephy-debug.h"
+#include "ephy-dnd.h"
+#include "ephy-embed-prefs.h"
+#include "ephy-favicon-helpers.h"
+#include "ephy-gui.h"
+#include "ephy-shell.h"
+#include "ephy-string.h"
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include <string.h>
+
+/* FIXME tweak this, or make it configurable? (bug 148093) */
+#define LABEL_WIDTH_CHARS 32
+
+#define EPHY_BOOKMARK_ACTION_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), EPHY_TYPE_BOOKMARK_ACTION, EphyBookmarkActionPrivate))
+
+struct _EphyBookmarkActionPrivate {
+ EphyNode *node;
+ gboolean smart_url;
+ guint cache_handler;
+};
+
+enum {
+ PROP_0,
+ PROP_BOOKMARK,
+ PROP_TOOLTIP,
+ PROP_LOCATION,
+ PROP_SMART_URL,
+ PROP_ICON,
+ LAST_PROP
+};
+
+static GParamSpec *obj_properties[LAST_PROP];
+
+typedef struct {
+ GObject *weak_ptr;
+ GtkWidget *entry;
+ EphyLinkFlags flags;
+} ClipboardCtx;
+
+G_DEFINE_TYPE (EphyBookmarkAction, ephy_bookmark_action, EPHY_TYPE_LINK_ACTION)
+
+static void
+favicon_changed_cb (WebKitFaviconDatabase *database,
+ const char *page_address,
+ const char *icon_address,
+ EphyBookmarkAction *action)
+{
+ const char *icon;
+
+ icon = ephy_node_get_property_string (action->priv->node,
+ EPHY_NODE_BMK_PROP_ICON);
+ if (g_strcmp0 (icon, icon_address) == 0) {
+ g_signal_handler_disconnect (database, action->priv->cache_handler);
+ action->priv->cache_handler = 0;
+
+ g_object_notify_by_pspec (G_OBJECT (action), obj_properties[PROP_ICON]);
+ }
+}
+
+static void
+async_get_favicon_pixbuf_callback (GObject *source, GAsyncResult *result, gpointer user_data)
+{
+ GtkWidget *proxy = GTK_WIDGET (user_data);
+ WebKitFaviconDatabase *database = WEBKIT_FAVICON_DATABASE (source);
+ GdkPixbuf *pixbuf = NULL;
+
+ cairo_surface_t *icon_surface = webkit_favicon_database_get_favicon_finish (database, result, NULL);
+ if (icon_surface) {
+ pixbuf = ephy_pixbuf_get_from_surface_scaled (icon_surface, FAVICON_SIZE, FAVICON_SIZE);
+ cairo_surface_destroy (icon_surface);
+ }
+
+ if (pixbuf) {
+ if (GTK_IS_MENU_ITEM (proxy)) {
+ GtkWidget *image;
+
+ image = gtk_image_new_from_pixbuf (pixbuf);
+ gtk_widget_show (image);
+
+ gtk_image_menu_item_set_image
+ (GTK_IMAGE_MENU_ITEM (proxy), image);
+ gtk_image_menu_item_set_always_show_image (GTK_IMAGE_MENU_ITEM (proxy),
+ TRUE);
+ }
+ g_object_unref (pixbuf);
+ }
+
+ g_object_unref (proxy);
+}
+
+static void
+ephy_bookmark_action_sync_icon (GtkAction *action,
+ GParamSpec *pspec,
+ GtkWidget *proxy)
+{
+ EphyBookmarkAction *bma = EPHY_BOOKMARK_ACTION (action);
+ const char *page_location;
+ WebKitFaviconDatabase *database;
+ EphyEmbedShell *shell = ephy_embed_shell_get_default ();
+
+ g_return_if_fail (bma->priv->node != NULL);
+
+ page_location = ephy_node_get_property_string (bma->priv->node,
+ EPHY_NODE_BMK_PROP_LOCATION);
+
+ database = webkit_web_context_get_favicon_database (ephy_embed_shell_get_web_context (shell));
+
+ if (page_location && *page_location) {
+ webkit_favicon_database_get_favicon (database, page_location,
+ 0, async_get_favicon_pixbuf_callback,
+ g_object_ref (proxy));
+ if (bma->priv->cache_handler == 0) {
+ bma->priv->cache_handler =
+ g_signal_connect_object
+ (database, "favicon-changed",
+ G_CALLBACK (favicon_changed_cb),
+ action, 0);
+ }
+ }
+}
+
+void
+ephy_bookmark_action_activate (EphyBookmarkAction *action,
+ GtkWidget *widget,
+ EphyLinkFlags flags)
+{
+ EphyBookmarkActionPrivate *priv = action->priv;
+ EphyBookmarks *bookmarks;
+ const char *location;
+ char *address = NULL, *text = NULL;
+
+ g_return_if_fail (priv->node != NULL);
+
+ location = ephy_node_get_property_string
+ (priv->node, EPHY_NODE_BMK_PROP_LOCATION);
+ g_return_if_fail (location != NULL);
+
+ bookmarks = ephy_shell_get_bookmarks (ephy_shell_get_default ());
+
+ if (GTK_IS_EDITABLE (widget)) {
+ text = gtk_editable_get_chars (GTK_EDITABLE (widget), 0, -1);
+ }
+
+ /* The entered search term is empty, and we have a smart bookmark */
+ if ((text == NULL || text[0] == '\0') && strstr (location, "%s") != NULL) {
+ char *scheme;
+ char *host_name;
+
+ scheme = g_uri_parse_scheme (location);
+ host_name = ephy_string_get_host_name (location);
+ address = g_strconcat (scheme,
+ "://",
+ host_name,
+ NULL);
+ g_free (scheme);
+ g_free (host_name);
+ }
+
+ if (address == NULL) {
+ address = ephy_bookmarks_resolve_address (bookmarks, location, text);
+ }
+ g_return_if_fail (address != NULL);
+
+ flags |= EPHY_LINK_BOOKMARK;
+
+ ephy_link_open (EPHY_LINK (action), address, NULL, flags);
+
+ g_free (address);
+ g_free (text);
+}
+
+static void
+activate_cb (GtkWidget *widget,
+ EphyBookmarkAction *action)
+{
+ gboolean control = FALSE;
+ GdkEvent *event;
+
+ event = gtk_get_current_event ();
+ if (event) {
+ if (event->type == GDK_KEY_PRESS ||
+ event->type == GDK_KEY_RELEASE) {
+ control = (event->key.state & gtk_accelerator_get_default_mod_mask ()) == GDK_CONTROL_MASK;
+ }
+
+ gdk_event_free (event);
+ }
+
+ ephy_bookmark_action_activate
+ (action, widget, (control || ephy_gui_is_middle_click ()) ? EPHY_LINK_NEW_TAB : 0);
+}
+
+static void
+connect_proxy (GtkAction *action,
+ GtkWidget *proxy)
+{
+ LOG ("Connecting action %p to proxy %p", action, proxy);
+
+ GTK_ACTION_CLASS (ephy_bookmark_action_parent_class)->connect_proxy (action, proxy);
+
+ ephy_bookmark_action_sync_icon (action, NULL, proxy);
+ g_signal_connect_object (action, "notify::icon",
+ G_CALLBACK (ephy_bookmark_action_sync_icon), proxy, 0);
+
+ if (GTK_IS_MENU_ITEM (proxy)) {
+ GtkLabel *label;
+
+ label = GTK_LABEL (gtk_bin_get_child (GTK_BIN (proxy)));
+
+ gtk_label_set_use_underline (label, FALSE);
+ gtk_label_set_ellipsize (label, PANGO_ELLIPSIZE_END);
+ gtk_label_set_max_width_chars (label, LABEL_WIDTH_CHARS);
+
+ g_signal_connect (proxy, "activate", G_CALLBACK (activate_cb), action);
+ }
+}
+
+void
+ephy_bookmark_action_updated (EphyBookmarkAction *action)
+{
+ GValue value = { 0, };
+ EphyBookmarks *bookmarks = ephy_shell_get_bookmarks (ephy_shell_get_default ());
+ EphyNode *smart = ephy_bookmarks_get_smart_bookmarks (bookmarks);
+ EphyNode *node = action->priv->node;
+ const char *title;
+
+ g_return_if_fail (action != NULL);
+ g_return_if_fail (node != NULL);
+
+ g_object_freeze_notify (G_OBJECT (action));
+
+ /* Set smart_url */
+ action->priv->smart_url = ephy_node_has_child (smart, node);
+ g_object_notify_by_pspec (G_OBJECT (action), obj_properties[PROP_SMART_URL]);
+
+ /* Set title */
+ title = ephy_node_get_property_string (node, EPHY_NODE_BMK_PROP_TITLE);
+ g_value_init (&value, G_TYPE_STRING);
+ g_value_set_static_string (&value, title);
+ g_object_set_property (G_OBJECT (action), "label", &value);
+ g_value_unset (&value);
+
+ /* Notify all other properties */
+ g_object_notify_by_pspec (G_OBJECT (action), obj_properties[PROP_LOCATION]);
+ g_object_notify_by_pspec (G_OBJECT (action), obj_properties[PROP_ICON]);
+
+ g_object_thaw_notify (G_OBJECT (action));
+
+ /* We could force a tooltip re-query with gtk_tooltip_trigger_tooltip_query
+ * here, but it's not really worth it. Just show the updated tip next time
+ * the tip is queried.
+ */
+}
+
+EphyNode *
+ephy_bookmark_action_get_bookmark (EphyBookmarkAction *action)
+{
+ return action->priv->node;
+}
+
+void
+ephy_bookmark_action_set_bookmark (EphyBookmarkAction *action,
+ EphyNode *node)
+{
+ EphyBookmarkActionPrivate *priv = action->priv;
+ GObject *object = G_OBJECT (action);
+
+ g_return_if_fail (node != NULL);
+
+ priv->node = node;
+
+ g_object_freeze_notify (object);
+
+ g_object_notify_by_pspec (G_OBJECT (action), obj_properties[PROP_BOOKMARK]);
+ ephy_bookmark_action_updated (action);
+
+ g_object_thaw_notify (object);
+}
+
+static void
+ephy_bookmark_action_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ EphyBookmarkAction *action = EPHY_BOOKMARK_ACTION (object);
+
+ switch (prop_id) {
+ case PROP_BOOKMARK:
+ ephy_bookmark_action_set_bookmark (action, g_value_get_pointer (value));
+ break;
+ case PROP_TOOLTIP:
+ case PROP_LOCATION:
+ case PROP_SMART_URL:
+ case PROP_ICON:
+ /* not writable */
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ephy_bookmark_action_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ EphyBookmarkAction *action = EPHY_BOOKMARK_ACTION (object);
+ EphyBookmarkActionPrivate *priv = action->priv;
+
+ g_return_if_fail (priv->node != NULL);
+
+ switch (prop_id) {
+ case PROP_BOOKMARK:
+ g_value_set_pointer (value, priv->node);
+ break;
+ case PROP_TOOLTIP:
+ case PROP_LOCATION:
+ g_value_set_string (value,
+ ephy_node_get_property_string (priv->node,
+ EPHY_NODE_BMK_PROP_LOCATION));
+ break;
+ case PROP_SMART_URL:
+ g_value_set_boolean (value, priv->smart_url);
+ break;
+ case PROP_ICON:
+ g_value_set_string (value,
+ ephy_node_get_property_string (priv->node,
+ EPHY_NODE_BMK_PROP_ICON));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ephy_bookmark_action_init (EphyBookmarkAction *action)
+{
+ action->priv = EPHY_BOOKMARK_ACTION_GET_PRIVATE (action);
+
+ action->priv->cache_handler = 0;
+}
+
+static void
+ephy_bookmark_action_dispose (GObject *object)
+{
+ EphyBookmarkAction *action = (EphyBookmarkAction *)object;
+ EphyBookmarkActionPrivate *priv = action->priv;
+
+ if (priv->cache_handler != 0) {
+ EphyEmbedShell *shell = ephy_embed_shell_get_default ();
+ WebKitFaviconDatabase *database;
+
+ database = webkit_web_context_get_favicon_database (ephy_embed_shell_get_web_context (shell));
+ g_signal_handler_disconnect (database, priv->cache_handler);
+ priv->cache_handler = 0;
+ }
+
+ G_OBJECT_CLASS (ephy_bookmark_action_parent_class)->dispose (object);
+}
+
+static void
+ephy_bookmark_action_class_init (EphyBookmarkActionClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+ GtkActionClass *action_class = GTK_ACTION_CLASS (class);
+
+ action_class->menu_item_type = GTK_TYPE_IMAGE_MENU_ITEM;
+ action_class->connect_proxy = connect_proxy;
+
+ object_class->dispose = ephy_bookmark_action_dispose;
+ object_class->set_property = ephy_bookmark_action_set_property;
+ object_class->get_property = ephy_bookmark_action_get_property;
+
+ obj_properties[PROP_BOOKMARK] =
+ g_param_spec_pointer ("bookmark", NULL, NULL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
+
+ /* overwrite GtkActionClass::tooltip, so we can use the url as tooltip */
+ obj_properties[PROP_TOOLTIP] =
+ g_param_spec_string ("tooltip", NULL, NULL,
+ NULL,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+ obj_properties[PROP_LOCATION] =
+ g_param_spec_string ("location", NULL, NULL,
+ NULL,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+ obj_properties[PROP_SMART_URL] =
+ g_param_spec_boolean ("smarturl", NULL, NULL,
+ FALSE,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+ obj_properties[PROP_ICON] =
+ g_param_spec_string ("icon", NULL, NULL,
+ NULL,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (object_class, LAST_PROP, obj_properties);
+
+ g_type_class_add_private (object_class, sizeof (EphyBookmarkActionPrivate));
+}
+
+GtkAction *
+ephy_bookmark_action_new (EphyNode *node,
+ const char *name)
+{
+ g_assert (name != NULL);
+
+ return GTK_ACTION (g_object_new (EPHY_TYPE_BOOKMARK_ACTION,
+ "name", name,
+ "bookmark", node,
+ NULL));
+}
diff --git a/src/bookmarks/ephy-bookmark-action.h b/src/bookmarks/ephy-bookmark-action.h
new file mode 100644
index 000000000..ba42442d1
--- /dev/null
+++ b/src/bookmarks/ephy-bookmark-action.h
@@ -0,0 +1,73 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright © 2003, 2004 Marco Pesenti Gritti
+ * Copyright © 2003, 2004 Christian Persch
+ *
+ * This file is part of Epiphany.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "ephy-link.h"
+#include "ephy-link-action.h"
+#include "ephy-node.h"
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define EPHY_TYPE_BOOKMARK_ACTION (ephy_bookmark_action_get_type ())
+#define EPHY_BOOKMARK_ACTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EPHY_TYPE_BOOKMARK_ACTION, EphyBookmarkAction))
+#define EPHY_BOOKMARK_ACTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EPHY_TYPE_BOOKMARK_ACTION, EphyBookmarkActionClass))
+#define EPHY_IS_BOOKMARK_ACTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EPHY_TYPE_BOOKMARK_ACTION))
+#define EPHY_IS_BOOKMARK_ACTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), EPHY_TYPE_BOOKMARK_ACTION))
+#define EPHY_BOOKMARK_ACTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), EPHY_TYPE_BOOKMARK_ACTION, EphyBookmarkActionClass))
+
+typedef struct _EphyBookmarkAction EphyBookmarkAction;
+typedef struct _EphyBookmarkActionPrivate EphyBookmarkActionPrivate;
+typedef struct _EphyBookmarkActionClass EphyBookmarkActionClass;
+
+struct _EphyBookmarkAction
+{
+ EphyLinkAction parent_instance;
+
+ /*< private >*/
+ EphyBookmarkActionPrivate *priv;
+};
+
+struct _EphyBookmarkActionClass
+{
+ EphyLinkActionClass parent_class;
+};
+
+
+GType ephy_bookmark_action_get_type (void);
+
+GtkAction *ephy_bookmark_action_new (EphyNode *node,
+ const char *name);
+
+void ephy_bookmark_action_set_bookmark (EphyBookmarkAction *action,
+ EphyNode *node);
+
+EphyNode *ephy_bookmark_action_get_bookmark (EphyBookmarkAction *action);
+
+void ephy_bookmark_action_updated (EphyBookmarkAction *action);
+
+void ephy_bookmark_action_activate (EphyBookmarkAction *action,
+ GtkWidget *widget,
+ EphyLinkFlags flags);
+
+G_END_DECLS
diff --git a/src/bookmarks/ephy-bookmark-properties-grid.c b/src/bookmarks/ephy-bookmark-properties-grid.c
index e7f04396f..8280b41a7 100644
--- a/src/bookmarks/ephy-bookmark-properties-grid.c
+++ b/src/bookmarks/ephy-bookmark-properties-grid.c
@@ -1,19 +1,21 @@
/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
- * Copyright © 2016 Iulian-Gabriel Radu <iulian.radu67@gmail.com>
+ * Copyright © 2016 Iulian-Gabriel Radu <iulian.radu67@gmail.com>
*
- * 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 of the License, or
- * (at your option) any later version.
+ * This file is part of Epiphany.
*
- * 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.
+ * 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 3 of the License, or
+ * (at your option) any later version.
*
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * 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, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
diff --git a/src/bookmarks/ephy-bookmark-properties-grid.h b/src/bookmarks/ephy-bookmark-properties-grid.h
index 0bfea4345..763f7b67d 100644
--- a/src/bookmarks/ephy-bookmark-properties-grid.h
+++ b/src/bookmarks/ephy-bookmark-properties-grid.h
@@ -1,19 +1,21 @@
/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
- * Copyright © 2016 Iulian-Gabriel Radu <iulian.radu67@gmail.com>
+ * Copyright © 2016 Iulian-Gabriel Radu <iulian.radu67@gmail.com>
*
- * 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 of the License, or
- * (at your option) any later version.
+ * This file is part of Epiphany.
*
- * 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.
+ * 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 3 of the License, or
+ * (at your option) any later version.
*
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * 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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
diff --git a/src/bookmarks/ephy-bookmark-properties.c b/src/bookmarks/ephy-bookmark-properties.c
new file mode 100644
index 000000000..89094750d
--- /dev/null
+++ b/src/bookmarks/ephy-bookmark-properties.c
@@ -0,0 +1,519 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright © 2002 Marco Pesenti Gritti <mpeseng@tin.it>
+ * Copyright © 2005, 2006 Peter A. Harvey
+ * Copyright © 2006 Christian Persch
+ *
+ * This file is part of Epiphany.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "ephy-bookmark-properties.h"
+
+#include "ephy-bookmarks-ui.h"
+#include "ephy-topics-entry.h"
+#include "ephy-topics-palette.h"
+#include "ephy-node-common.h"
+#include "ephy-debug.h"
+#include "ephy-shell.h"
+#include "ephy-initial-state.h"
+#include "ephy-gui.h"
+#include "ephy-dnd.h"
+#include "ephy-prefs.h"
+#include "ephy-settings.h"
+#include "ephy-uri-helpers.h"
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include <string.h>
+
+struct _EphyBookmarkProperties {
+ GtkDialog parent_instance;
+
+ /* construct properties */
+ EphyBookmarks *bookmarks;
+ EphyNode *bookmark;
+ gboolean creating;
+
+ /* counters */
+ gint duplicate_count;
+ gint duplicate_idle;
+
+ /* from UI file */
+ GtkGrid *grid;
+ GtkEntry *title_entry;
+ GtkEntry *adress_entry;
+ GtkLabel *topics_label;
+ GtkExpander *topics_expander;
+ GtkTreeView *topics_tree_view;
+ GtkScrolledWindow *topics_scrolled_window;
+ GtkLabel *warning_label;
+
+ /* model */
+ EphyTopicsPalette *topics_model;
+};
+
+enum {
+ PROP_0,
+ PROP_BOOKMARKS,
+ PROP_BOOKMARK,
+ PROP_CREATING,
+ LAST_PROP
+};
+
+static GParamSpec *obj_properties[LAST_PROP];
+
+G_DEFINE_TYPE (EphyBookmarkProperties, ephy_bookmark_properties, GTK_TYPE_DIALOG)
+
+static gboolean
+update_warning (EphyBookmarkProperties *properties)
+{
+ char *label;
+
+ properties->duplicate_idle = 0;
+ properties->duplicate_count = ephy_bookmarks_get_similar
+ (properties->bookmarks, properties->bookmark, NULL, NULL);
+
+ /* Translators: This string is used when counting bookmarks that
+ * are similar to each other */
+ label = g_strdup_printf (ngettext ("%d bookmark is similar", "%d bookmarks are similar", properties->duplicate_count), properties->duplicate_count);
+ gtk_label_set_text (properties->warning_label, label);
+ g_free (label);
+
+ return FALSE;
+}
+
+static void
+update_warning_idle (EphyBookmarkProperties *properties)
+{
+ if (properties->duplicate_idle != 0) {
+ g_source_remove (properties->duplicate_idle);
+ }
+
+ properties->duplicate_idle = g_timeout_add
+ (500, (GSourceFunc)update_warning, properties);
+ g_source_set_name_by_id (properties->duplicate_idle, "[epiphany] update_warning");
+}
+
+static void
+node_added_cb (EphyNode *bookmarks,
+ EphyNode *bookmark,
+ EphyBookmarkProperties *properties)
+{
+ update_warning_idle (properties);
+}
+
+static void
+node_changed_cb (EphyNode *bookmarks,
+ EphyNode *bookmark,
+ guint property,
+ EphyBookmarkProperties *properties)
+{
+ if (property == EPHY_NODE_BMK_PROP_LOCATION) {
+ update_warning_idle (properties);
+ }
+}
+
+static void
+node_removed_cb (EphyNode *bookmarks,
+ EphyNode *bookmark,
+ guint index,
+ EphyBookmarkProperties *properties)
+{
+ update_warning_idle (properties);
+}
+
+static void
+node_destroy_cb (EphyNode *bookmark,
+ GtkWidget *dialog)
+{
+ EPHY_BOOKMARK_PROPERTIES (dialog)->creating = FALSE;
+ gtk_widget_destroy (dialog);
+}
+
+static void
+ephy_bookmark_properties_set_bookmark (EphyBookmarkProperties *properties,
+ EphyNode *bookmark)
+{
+ LOG ("Set bookmark");
+
+ if (properties->bookmark) {
+ ephy_node_signal_disconnect_object (properties->bookmark,
+ EPHY_NODE_DESTROY,
+ (EphyNodeCallback)node_destroy_cb,
+ G_OBJECT (properties));
+ }
+
+ properties->bookmark = bookmark;
+
+ ephy_node_signal_connect_object (properties->bookmark,
+ EPHY_NODE_DESTROY,
+ (EphyNodeCallback)node_destroy_cb,
+ G_OBJECT (properties));
+}
+
+static void
+ephy_bookmark_properties_destroy_cb (GtkDialog *dialog,
+ gpointer data)
+{
+ EphyBookmarkProperties *properties = EPHY_BOOKMARK_PROPERTIES (dialog);
+
+ if (properties->creating) {
+ ephy_node_unref (properties->bookmark);
+ properties->creating = FALSE;
+ }
+
+ if (properties->duplicate_idle != 0) {
+ g_source_remove (properties->duplicate_idle);
+ properties->duplicate_idle = 0;
+ }
+}
+
+static void
+ephy_bookmark_properties_response_cb (GtkDialog *dialog,
+ int response_id,
+ gpointer data)
+{
+ EphyBookmarkProperties *properties = EPHY_BOOKMARK_PROPERTIES (dialog);
+
+ switch (response_id) {
+ case GTK_RESPONSE_ACCEPT:
+ properties->creating = FALSE;
+ break;
+ default:
+ break;
+ }
+
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+}
+
+static void
+update_entry (EphyBookmarkProperties *properties,
+ GtkWidget *entry,
+ guint prop)
+{
+ GValue value = { 0, };
+ char *text;
+
+ text = gtk_editable_get_chars (GTK_EDITABLE (entry), 0, -1);
+ g_value_init (&value, G_TYPE_STRING);
+ g_value_take_string (&value, text);
+ ephy_node_set_property (properties->bookmark,
+ prop,
+ &value);
+ g_value_unset (&value);
+}
+
+static void
+update_window_title (EphyBookmarkProperties *properties)
+{
+ char *title;
+ const char *tmp;
+
+ tmp = ephy_node_get_property_string (properties->bookmark,
+ EPHY_NODE_BMK_PROP_TITLE);
+
+ if (properties->creating)
+ title = g_strdup (_("Add Bookmark"));
+ else
+ title = g_strdup_printf (_("“%s” Properties"), tmp);
+
+ gtk_window_set_title (GTK_WINDOW (properties), title);
+ g_free (title);
+}
+
+static void
+title_entry_changed_cb (GtkWidget *entry,
+ EphyBookmarkProperties *properties)
+{
+ update_entry (properties, entry, EPHY_NODE_BMK_PROP_TITLE);
+ update_window_title (properties);
+}
+
+static void
+on_topic_edited (GtkCellRendererText *renderer,
+ const char *path_str,
+ const char *new_text,
+ EphyBookmarkProperties *properties)
+{
+ if (*new_text != 0) {
+ EphyNode *node;
+ node = ephy_bookmarks_add_keyword (properties->bookmarks, new_text);
+ ephy_bookmarks_set_keyword (properties->bookmarks, node, properties->bookmark);
+ } else {
+ ephy_topics_palette_update_list (properties->topics_model);
+ }
+}
+
+static void
+on_topic_toggled (GtkCellRendererToggle *cell_renderer,
+ gchar *path,
+ EphyBookmarkProperties *properties)
+{
+ EphyNode *topic;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ model = gtk_tree_view_get_model (properties->topics_tree_view);
+
+ g_return_if_fail (gtk_tree_model_get_iter_from_string (model, &iter, path));
+
+ gtk_tree_model_get (model, &iter, EPHY_TOPICS_PALETTE_COLUMN_NODE, &topic, -1);
+
+ /* Protect against toggling separators. */
+ if (topic == NULL)
+ return;
+
+ if (ephy_node_has_child (topic, properties->bookmark)) {
+ ephy_bookmarks_unset_keyword (properties->bookmarks,
+ topic,
+ properties->bookmark);
+ } else {
+ ephy_bookmarks_set_keyword (properties->bookmarks,
+ topic,
+ properties->bookmark);
+ }
+}
+
+static gboolean
+is_separator (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ EphyNode *node;
+ gtk_tree_model_get (model, iter, EPHY_TOPICS_PALETTE_COLUMN_NODE, &node, -1);
+ return (node == NULL);
+}
+
+static void
+location_entry_changed_cb (GtkWidget *entry,
+ EphyBookmarkProperties *properties)
+{
+ ephy_bookmarks_set_address (properties->bookmarks,
+ properties->bookmark,
+ gtk_entry_get_text (GTK_ENTRY (entry)));
+}
+
+static void
+ephy_bookmark_properties_init (EphyBookmarkProperties *properties)
+{
+}
+
+static GObject *
+ephy_bookmark_properties_constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_params)
+{
+ GObject *object;
+ EphyBookmarkProperties *properties;
+
+ gboolean lockdown;
+ const char *tmp;
+ char *decoded_url;
+ GtkWidget *entry;
+
+ object = G_OBJECT_CLASS (ephy_bookmark_properties_parent_class)->constructor (type,
+ n_construct_properties,
+ construct_params);
+ properties = EPHY_BOOKMARK_PROPERTIES (object);
+
+ gtk_widget_init_template (GTK_WIDGET (properties));
+
+ if (!properties->creating) {
+ ephy_initial_state_add_window (GTK_WIDGET (properties),
+ "bookmark_properties",
+ 290, 280, FALSE,
+ EPHY_INITIAL_STATE_WINDOW_SAVE_POSITION |
+ EPHY_INITIAL_STATE_WINDOW_SAVE_SIZE);
+ }
+ /* Lockdown */
+ lockdown = g_settings_get_boolean (EPHY_SETTINGS_LOCKDOWN,
+ EPHY_PREFS_LOCKDOWN_BOOKMARK_EDITING);
+
+ update_window_title (properties);
+
+ gtk_editable_set_editable (GTK_EDITABLE (properties->title_entry), !lockdown);
+ tmp = ephy_node_get_property_string (properties->bookmark, EPHY_NODE_BMK_PROP_TITLE);
+ gtk_entry_set_text (properties->title_entry, tmp);
+
+ gtk_editable_set_editable (GTK_EDITABLE (properties->adress_entry), !lockdown);
+ tmp = ephy_node_get_property_string (properties->bookmark, EPHY_NODE_BMK_PROP_LOCATION);
+ decoded_url = ephy_uri_decode (tmp);
+ gtk_entry_set_text (properties->adress_entry, decoded_url);
+ g_free (decoded_url);
+
+ entry = ephy_topics_entry_new (properties->bookmarks, properties->bookmark);
+ gtk_editable_set_editable (GTK_EDITABLE (entry), !lockdown);
+ gtk_label_set_mnemonic_widget (properties->topics_label, entry);
+ gtk_widget_show (entry);
+ gtk_grid_attach (properties->grid, entry, 1, 2, 1, 1);
+ gtk_widget_set_hexpand (entry, TRUE);
+
+ gtk_tree_view_set_row_separator_func (properties->topics_tree_view, is_separator, NULL, NULL);
+
+ properties->topics_model = ephy_topics_palette_new (properties->bookmarks, properties->bookmark);
+ gtk_tree_view_set_model (properties->topics_tree_view, GTK_TREE_MODEL (properties->topics_model));
+
+ /* TODO bind; and the entry?! */
+ gtk_widget_set_sensitive (GTK_WIDGET (properties->topics_scrolled_window), !lockdown);
+
+ ephy_initial_state_add_expander (GTK_WIDGET (properties->topics_expander), "bookmark_properties_list", FALSE);
+
+ if (properties->creating) {
+ gtk_dialog_add_button (GTK_DIALOG (properties), _("_Cancel"), GTK_RESPONSE_CANCEL);
+ gtk_dialog_add_button (GTK_DIALOG (properties), _("_Add"), GTK_RESPONSE_ACCEPT);
+ gtk_dialog_set_default_response (GTK_DIALOG (properties), GTK_RESPONSE_ACCEPT);
+ }
+
+ update_warning (properties);
+
+ return object;
+}
+
+static void
+ephy_bookmark_properties_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ EphyBookmarkProperties *properties = EPHY_BOOKMARK_PROPERTIES (object);
+ EphyNode *bookmarks;
+
+ switch (prop_id) {
+ case PROP_BOOKMARKS:
+ properties->bookmarks = g_value_get_object (value);
+ bookmarks = ephy_bookmarks_get_bookmarks (properties->bookmarks);
+ ephy_node_signal_connect_object (bookmarks,
+ EPHY_NODE_CHILD_ADDED,
+ (EphyNodeCallback)node_added_cb,
+ object);
+ ephy_node_signal_connect_object (bookmarks,
+ EPHY_NODE_CHILD_REMOVED,
+ (EphyNodeCallback)node_removed_cb,
+ object);
+ ephy_node_signal_connect_object (bookmarks,
+ EPHY_NODE_CHILD_CHANGED,
+ (EphyNodeCallback)node_changed_cb,
+ object);
+ break;
+ case PROP_BOOKMARK:
+ ephy_bookmark_properties_set_bookmark
+ (properties, g_value_get_pointer (value));
+ break;
+ case PROP_CREATING:
+ properties->creating = g_value_get_boolean (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+ephy_bookmark_properties_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ EphyBookmarkProperties *properties = EPHY_BOOKMARK_PROPERTIES (object);
+
+ switch (prop_id) {
+ case PROP_BOOKMARK:
+ g_value_set_object (value, properties);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+ephy_bookmark_properties_class_init (EphyBookmarkPropertiesClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->constructor = ephy_bookmark_properties_constructor;
+ object_class->set_property = ephy_bookmark_properties_set_property;
+ object_class->get_property = ephy_bookmark_properties_get_property;
+
+ obj_properties[PROP_BOOKMARKS] =
+ g_param_spec_object ("bookmarks",
+ "bookmarks",
+ "bookmarks",
+ EPHY_TYPE_BOOKMARKS,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
+
+ obj_properties[PROP_BOOKMARK] =
+ g_param_spec_pointer ("bookmark",
+ "bookmark",
+ "bookmark",
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT);
+
+ obj_properties[PROP_CREATING] =
+ g_param_spec_boolean ("creating",
+ "creating",
+ "creating",
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+
+ g_object_class_install_properties (object_class, LAST_PROP, obj_properties);
+
+ /* from UI file */
+ gtk_widget_class_set_template_from_resource (widget_class,
+ "/org/gnome/epiphany/bookmark-properties.ui");
+
+ gtk_widget_class_bind_template_child (widget_class, EphyBookmarkProperties, grid);
+ gtk_widget_class_bind_template_child (widget_class, EphyBookmarkProperties, title_entry);
+ gtk_widget_class_bind_template_child (widget_class, EphyBookmarkProperties, adress_entry);
+ gtk_widget_class_bind_template_child (widget_class, EphyBookmarkProperties, topics_label);
+ gtk_widget_class_bind_template_child (widget_class, EphyBookmarkProperties, topics_expander);
+ gtk_widget_class_bind_template_child (widget_class, EphyBookmarkProperties, topics_tree_view);
+ gtk_widget_class_bind_template_child (widget_class, EphyBookmarkProperties, topics_scrolled_window);
+ gtk_widget_class_bind_template_child (widget_class, EphyBookmarkProperties, warning_label);
+
+ gtk_widget_class_bind_template_callback (widget_class, title_entry_changed_cb);
+ gtk_widget_class_bind_template_callback (widget_class, on_topic_toggled); /* TODO make the row activatable instead of a little togglebutton */
+ gtk_widget_class_bind_template_callback (widget_class, on_topic_edited); /* TODO topics’ names are not editable from there, they are synced with bookmarks... but this is used for... updating the entry \o/ yay \o/ */
+ gtk_widget_class_bind_template_callback (widget_class, location_entry_changed_cb);
+ gtk_widget_class_bind_template_callback (widget_class, ephy_bookmark_properties_response_cb);
+ gtk_widget_class_bind_template_callback (widget_class, ephy_bookmark_properties_destroy_cb);
+}
+
+/* public API */
+
+GtkWidget *
+ephy_bookmark_properties_new (EphyBookmarks *bookmarks,
+ EphyNode *bookmark,
+ gboolean creating)
+{
+ g_assert (bookmarks != NULL);
+
+ return GTK_WIDGET (g_object_new (EPHY_TYPE_BOOKMARK_PROPERTIES,
+ "bookmarks", bookmarks,
+ "bookmark", bookmark,
+ "creating", creating,
+ "use-header-bar", TRUE,
+ NULL));
+}
+
+EphyNode *
+ephy_bookmark_properties_get_node (EphyBookmarkProperties *properties)
+{
+ return properties->bookmark;
+}
diff --git a/src/bookmarks/ephy-bookmark-properties.h b/src/bookmarks/ephy-bookmark-properties.h
new file mode 100644
index 000000000..4caf4a041
--- /dev/null
+++ b/src/bookmarks/ephy-bookmark-properties.h
@@ -0,0 +1,40 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright © 2002 Marco Pesenti Gritti <mpeseng@tin.it>
+ * Copyright © 2005, 2006 Peter A. Harvey
+ * Copyright © 2006 Christian Persch
+ *
+ * This file is part of Epiphany.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "ephy-bookmarks.h"
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define EPHY_TYPE_BOOKMARK_PROPERTIES (ephy_bookmark_properties_get_type ())
+G_DECLARE_FINAL_TYPE (EphyBookmarkProperties, ephy_bookmark_properties, EPHY, BOOKMARK_PROPERTIES, GtkDialog);
+
+GtkWidget *ephy_bookmark_properties_new (EphyBookmarks *bookmarks,
+ EphyNode *bookmark,
+ gboolean creating);
+
+EphyNode *ephy_bookmark_properties_get_node (EphyBookmarkProperties *properties);
+
+G_END_DECLS
diff --git a/src/bookmarks/ephy-bookmark-row.c b/src/bookmarks/ephy-bookmark-row.c
index e5cef95a9..5cc0b42a1 100644
--- a/src/bookmarks/ephy-bookmark-row.c
+++ b/src/bookmarks/ephy-bookmark-row.c
@@ -1,19 +1,21 @@
/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
- * Copyright © 2016 Iulian-Gabriel Radu <iulian.radu67@gmail.com>
+ * Copyright © 2016 Iulian-Gabriel Radu <iulian.radu67@gmail.com>
*
- * 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 of the License, or
- * (at your option) any later version.
+ * This file is part of Epiphany.
*
- * 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.
+ * 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 3 of the License, or
+ * (at your option) any later version.
*
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * 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, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
diff --git a/src/bookmarks/ephy-bookmark-row.h b/src/bookmarks/ephy-bookmark-row.h
index 5778ec606..d4c9a7cb0 100644
--- a/src/bookmarks/ephy-bookmark-row.h
+++ b/src/bookmarks/ephy-bookmark-row.h
@@ -1,19 +1,21 @@
/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
- * Copyright © 2016 Iulian-Gabriel Radu <iulian.radu67@gmail.com>
+ * Copyright © 2016 Iulian-Gabriel Radu <iulian.radu67@gmail.com>
*
- * 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 of the License, or
- * (at your option) any later version.
+ * This file is part of Epiphany.
*
- * 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.
+ * 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 3 of the License, or
+ * (at your option) any later version.
*
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * 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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
diff --git a/src/bookmarks/ephy-bookmark.c b/src/bookmarks/ephy-bookmark.c
index 322da1a71..96ddd6d88 100644
--- a/src/bookmarks/ephy-bookmark.c
+++ b/src/bookmarks/ephy-bookmark.c
@@ -1,19 +1,21 @@
/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
- * Copyright © 2016 Iulian-Gabriel Radu <iulian.radu67@gmail.com>
+ * Copyright © 2016 Iulian-Gabriel Radu <iulian.radu67@gmail.com>
*
- * 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 3 of the License, or
- * (at your option) any later version.
+ * This file is part of Epiphany.
*
- * 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.
+ * 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 3 of the License, or
+ * (at your option) any later version.
*
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * 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, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
diff --git a/src/bookmarks/ephy-bookmark.h b/src/bookmarks/ephy-bookmark.h
index 6fb9d82b8..e9458207c 100644
--- a/src/bookmarks/ephy-bookmark.h
+++ b/src/bookmarks/ephy-bookmark.h
@@ -1,19 +1,21 @@
/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
- * Copyright © 2016 Iulian-Gabriel Radu <iulian.radu67@gmail.com>
+ * Copyright © 2016 Iulian-Gabriel Radu <iulian.radu67@gmail.com>
*
- * 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 3 of the License, or
- * (at your option) any later version.
+ * This file is part of Epiphany.
*
- * 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.
+ * 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 3 of the License, or
+ * (at your option) any later version.
*
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * 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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
diff --git a/src/bookmarks/ephy-bookmarks-editor.c b/src/bookmarks/ephy-bookmarks-editor.c
new file mode 100644
index 000000000..d4b327a59
--- /dev/null
+++ b/src/bookmarks/ephy-bookmarks-editor.c
@@ -0,0 +1,1721 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright © 2003, 2004 Marco Pesenti Gritti <mpeseng@tin.it>
+ *
+ * This file is part of Epiphany.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+#include "ephy-bookmarks-editor.h"
+
+#include "ephy-bookmark-action.h"
+#include "ephy-bookmarks-export.h"
+#include "ephy-bookmarks-import.h"
+#include "ephy-bookmarks-ui.h"
+#include "ephy-debug.h"
+#include "ephy-dnd.h"
+#include "ephy-embed-prefs.h"
+#include "ephy-file-chooser.h"
+#include "ephy-file-helpers.h"
+#include "ephy-gui.h"
+#include "ephy-node-common.h"
+#include "ephy-node-view.h"
+#include "ephy-prefs.h"
+#include "ephy-session.h"
+#include "ephy-settings.h"
+#include "ephy-shell.h"
+#include "ephy-initial-state.h"
+#include "ephy-uri-helpers.h"
+#include "ephy-topic-action.h"
+#include "ephy-window.h"
+#include "popup-commands.h"
+#include "window-commands.h"
+
+#include <gdk/gdkkeysyms.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include <string.h>
+
+static const GtkTargetEntry topic_drag_dest_types [] =
+{
+ { (char *)EPHY_DND_URI_LIST_TYPE, 0, 0 }
+};
+
+static const GtkTargetEntry bmk_drag_types [] =
+{
+ { (char *)EPHY_DND_URL_TYPE, 0, 0 },
+ { (char *)EPHY_DND_URI_LIST_TYPE, 0, 1 },
+ { (char *)EPHY_DND_TEXT_TYPE, 0, 2 }
+};
+
+static const GtkTargetEntry topic_drag_types [] =
+{
+ { (char *)EPHY_DND_TOPIC_TYPE, 0, 0 }
+};
+
+static const struct {
+ const char *name;
+ const char *extension;
+}
+export_formats [] =
+/* Don't add or reoder those entries without updating export_dialog_response_cb too! */
+{
+ { N_("Web (RDF)"), "rdf" },
+ { N_("Mozilla (HTML)"), "html" }
+};
+
+static void ephy_bookmarks_editor_finalize (GObject *object);
+static void ephy_bookmarks_editor_dispose (GObject *object);
+static void ephy_bookmarks_editor_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void ephy_bookmarks_editor_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void ephy_bookmarks_editor_update_menu (EphyBookmarksEditor *editor);
+
+static void cmd_open_bookmarks_in_tabs (GtkAction *action,
+ EphyBookmarksEditor *editor);
+static void cmd_open_bookmarks_in_browser (GtkAction *action,
+ EphyBookmarksEditor *editor);
+static void cmd_delete (GtkAction *action,
+ EphyBookmarksEditor *editor);
+static void cmd_bookmark_properties (GtkAction *action,
+ EphyBookmarksEditor *editor);
+static void cmd_bookmarks_import (GtkAction *action,
+ EphyBookmarksEditor *editor);
+static void cmd_bookmarks_export (GtkAction *action,
+ EphyBookmarksEditor *editor);
+static void cmd_add_topic (GtkAction *action,
+ EphyBookmarksEditor *editor);
+static void cmd_rename (GtkAction *action,
+ EphyBookmarksEditor *editor);
+static void cmd_close (GtkAction *action,
+ EphyBookmarksEditor *editor);
+static void cmd_cut (GtkAction *action,
+ EphyBookmarksEditor *editor);
+static void cmd_copy (GtkAction *action,
+ EphyBookmarksEditor *editor);
+static void cmd_paste (GtkAction *action,
+ EphyBookmarksEditor *editor);
+static void cmd_select_all (GtkAction *action,
+ EphyBookmarksEditor *editor);
+static void cmd_help_contents (GtkAction *action,
+ EphyBookmarksEditor *editor);
+
+#define RESERVED_STRING N_("Remove from this topic")
+
+struct _EphyBookmarksEditor {
+ GtkWindow parent_instance;
+
+ EphyBookmarks *bookmarks;
+ GtkWidget *bm_view;
+ GtkWidget *key_view;
+ EphyNodeFilter *bookmarks_filter;
+ GtkWidget *search_entry;
+ GtkWidget *main_vbox;
+ GtkWidget *window;
+ GtkUIManager *ui_merge;
+ GtkActionGroup *action_group;
+ int priority_col;
+
+ GtkTreeViewColumn *title_col;
+ GtkTreeViewColumn *address_col;
+};
+
+enum {
+ PROP_0,
+ PROP_BOOKMARKS,
+ LAST_PROP
+};
+
+static GParamSpec *obj_properties[LAST_PROP];
+
+static void
+help_about (GtkAction *action, EphyWindow *window)
+{
+ window_cmd_show_about (NULL, NULL, window);
+}
+
+static const GtkActionEntry ephy_bookmark_popup_entries [] = {
+ /* Toplevel */
+ { "File", NULL, N_("_File") },
+ { "Edit", NULL, N_("_Edit") },
+ { "View", NULL, N_("_View") },
+ { "Help", NULL, N_("_Help") },
+ { "PopupAction", NULL, "" },
+
+ /* File Menu*/
+ { "NewTopic", NULL, N_("_New Topic"), "<control>N",
+ N_("Create a new topic"),
+ G_CALLBACK (cmd_add_topic) },
+ { "OpenInWindow", NULL, N_("Open in New _Window"), "<control>O",
+ N_("Open the selected bookmark in a new window"),
+ G_CALLBACK (cmd_open_bookmarks_in_browser) },
+ { "OpenInTab", NULL, N_("Open in New _Tab"), "<shift><control>O",
+ N_("Open the selected bookmark in a new tab"),
+ G_CALLBACK (cmd_open_bookmarks_in_tabs) },
+ { "Rename", NULL, N_("_Rename…"), "F2",
+ N_("Rename the selected bookmark or topic"), G_CALLBACK (cmd_rename) },
+ { "Properties", NULL, N_("_Properties"), "<alt>Return",
+ N_("View or modify the properties of the selected bookmark"),
+ G_CALLBACK (cmd_bookmark_properties) },
+ { "Import", NULL, N_("_Import Bookmarks…"), NULL,
+ N_("Import bookmarks from another browser or a bookmarks file"),
+ G_CALLBACK (cmd_bookmarks_import) },
+ { "Export", NULL, N_("_Export Bookmarks…"), NULL,
+ N_("Export bookmarks to a file"),
+ G_CALLBACK (cmd_bookmarks_export) },
+ { "Close", NULL, N_("_Close"), "<control>W",
+ N_("Close the bookmarks window"),
+ G_CALLBACK (cmd_close) },
+
+ /* Edit Menu */
+ { "Cut", NULL, N_("Cu_t"), "<control>X",
+ N_("Cut the selection"),
+ G_CALLBACK (cmd_cut) },
+ { "Copy", NULL, N_("_Copy"), "<control>C",
+ N_("Copy the selection"),
+ G_CALLBACK (cmd_copy) },
+ { "Paste", NULL, N_("_Paste"), "<control>V",
+ N_("Paste the clipboard"),
+ G_CALLBACK (cmd_paste) },
+ { "Delete", NULL, N_("_Delete"), "<control>T",
+ N_("Delete the selected bookmark or topic"),
+ G_CALLBACK (cmd_delete) },
+ { "SelectAll", NULL, N_("Select _All"), "<control>A",
+ N_("Select all bookmarks or text"),
+ G_CALLBACK (cmd_select_all) },
+
+ /* Help Menu */
+ { "HelpContents", NULL, N_("_Contents"), "F1",
+ N_("Display bookmarks help"),
+ G_CALLBACK (cmd_help_contents) },
+ { "HelpAbout", NULL, N_("_About"), NULL,
+ N_("Display credits for the web browser creators"),
+ G_CALLBACK (help_about) },
+};
+
+static const GtkToggleActionEntry ephy_bookmark_toggle_entries [] = {
+ /* View Menu */
+ { "ViewTitle", NULL, N_("_Title"), NULL,
+ N_("Show the title column"), NULL, TRUE },
+ { "ViewAddress", NULL, N_("Address"), NULL,
+ N_("Show the address column"), NULL, FALSE }
+};
+
+G_DEFINE_TYPE (EphyBookmarksEditor, ephy_bookmarks_editor, GTK_TYPE_WINDOW)
+
+static void
+entry_selection_changed_cb (GtkWidget *widget, GParamSpec *pspec, EphyBookmarksEditor *editor)
+{
+ ephy_bookmarks_editor_update_menu (editor);
+}
+
+static void
+add_entry_monitor (EphyBookmarksEditor *editor, GtkWidget *entry)
+{
+ g_signal_connect (G_OBJECT (entry),
+ "notify::selection-bound",
+ G_CALLBACK (entry_selection_changed_cb),
+ editor);
+ g_signal_connect (G_OBJECT (entry),
+ "notify::cursor-position",
+ G_CALLBACK (entry_selection_changed_cb),
+ editor);
+}
+
+static void
+add_text_renderer_monitor (EphyBookmarksEditor *editor)
+{
+ GtkWidget *entry;
+
+ entry = gtk_window_get_focus (GTK_WINDOW (editor));
+ g_return_if_fail (GTK_IS_EDITABLE (entry));
+
+ add_entry_monitor (editor, entry);
+}
+
+static void
+cmd_add_topic (GtkAction *action,
+ EphyBookmarksEditor *editor)
+{
+ EphyNode *node;
+
+ node = ephy_bookmarks_add_keyword (editor->bookmarks,
+ _("Type a topic"));
+ gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (editor->key_view)));
+ ephy_node_view_select_node (EPHY_NODE_VIEW (editor->key_view), node);
+ ephy_node_view_edit (EPHY_NODE_VIEW (editor->key_view), TRUE);
+ add_text_renderer_monitor (editor);
+}
+
+static void
+cmd_close (GtkAction *action,
+ EphyBookmarksEditor *editor)
+{
+ gtk_widget_hide (GTK_WIDGET (editor));
+}
+
+static void
+cmd_rename (GtkAction *action,
+ EphyBookmarksEditor *editor)
+{
+ if (ephy_node_view_is_target (EPHY_NODE_VIEW (editor->bm_view))) {
+ ephy_node_view_edit (EPHY_NODE_VIEW (editor->bm_view), FALSE);
+ } else if (ephy_node_view_is_target (EPHY_NODE_VIEW (editor->key_view))) {
+ ephy_node_view_edit (EPHY_NODE_VIEW (editor->key_view), FALSE);
+ }
+ add_text_renderer_monitor (editor);
+}
+
+static GtkWidget *
+get_target_window (EphyBookmarksEditor *editor)
+{
+ if (editor->window) {
+ return editor->window;
+ } else {
+ return GTK_WIDGET (gtk_application_get_active_window (GTK_APPLICATION (ephy_shell_get_default ())));
+ }
+}
+
+static void
+load_bookmark_in_tab (EphyNode *node,
+ EphyEmbed *embed)
+{
+ const char *location;
+
+ location = ephy_node_get_property_string (node,
+ EPHY_NODE_BMK_PROP_LOCATION);
+ ephy_web_view_load_url (ephy_embed_get_web_view (embed), location);
+}
+
+static void
+cmd_open_bookmarks_in_tabs (GtkAction *action,
+ EphyBookmarksEditor *editor)
+{
+ EphyWindow *window;
+ GList *selection;
+ GList *l;
+
+ window = EPHY_WINDOW (get_target_window (editor));
+ selection = ephy_node_view_get_selection (EPHY_NODE_VIEW (editor->bm_view));
+
+ for (l = selection; l; l = l->next) {
+ EphyNode *node = l->data;
+ EphyEmbed *new_embed;
+
+ new_embed = ephy_shell_new_tab (ephy_shell_get_default (),
+ window, NULL,
+ 0);
+ load_bookmark_in_tab (node, new_embed);
+
+ /* if there was no target window, a new one was opened. Get it
+ * from the new tab so we open the remaining links in the
+ * same window. See bug 138343.
+ */
+ if (window == NULL) {
+ window = EPHY_WINDOW
+ (gtk_widget_get_toplevel (GTK_WIDGET (new_embed)));
+ }
+ }
+
+ g_list_free (selection);
+}
+
+static void
+cmd_open_bookmarks_in_browser (GtkAction *action,
+ EphyBookmarksEditor *editor)
+{
+ EphyWindow *window;
+ GList *selection;
+ GList *l;
+
+ window = ephy_window_new ();
+ selection = ephy_node_view_get_selection (EPHY_NODE_VIEW (editor->bm_view));
+
+ for (l = selection; l; l = l->next) {
+ EphyNode *node = l->data;
+ EphyEmbed *embed;
+
+ embed = ephy_shell_new_tab (ephy_shell_get_default (),
+ window, NULL, 0);
+ load_bookmark_in_tab (node, embed);
+ }
+
+ g_list_free (selection);
+}
+
+static GtkWidget *
+delete_topic_dialog_construct (GtkWindow *parent,
+ const char *topic)
+{
+ GtkWidget *dialog;
+
+ dialog = gtk_message_dialog_new (GTK_WINDOW (parent),
+ GTK_DIALOG_MODAL,
+ GTK_MESSAGE_WARNING,
+ GTK_BUTTONS_CANCEL,
+ _("Delete topic “%s”?"),
+ topic);
+
+ gtk_window_set_title (GTK_WINDOW (dialog), _("Delete this topic?"));
+ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+ _("Deleting this topic will cause all its bookmarks to become "
+ "uncategorized, unless they also belong to other topics. "
+ "The bookmarks will not be deleted."));
+ gtk_dialog_add_button (GTK_DIALOG (dialog), _("_Delete Topic"), GTK_RESPONSE_ACCEPT);
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CANCEL);
+
+ gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (parent)),
+ GTK_WINDOW (dialog));
+
+ return dialog;
+}
+
+static void
+cmd_delete (GtkAction *action,
+ EphyBookmarksEditor *editor)
+{
+ if (ephy_node_view_is_target (EPHY_NODE_VIEW (editor->bm_view))) {
+ ephy_node_view_remove (EPHY_NODE_VIEW (editor->bm_view));
+ } else if (ephy_node_view_is_target (EPHY_NODE_VIEW (editor->key_view))) {
+ int priority;
+ GList *selected;
+ EphyNode *node;
+
+ selected = ephy_node_view_get_selection (EPHY_NODE_VIEW (editor->key_view));
+ node = selected->data;
+ priority = ephy_node_get_property_int (node, EPHY_NODE_KEYWORD_PROP_PRIORITY);
+
+ if (priority == -1) priority = EPHY_NODE_NORMAL_PRIORITY;
+
+ if (priority == EPHY_NODE_NORMAL_PRIORITY) {
+ GtkWidget *dialog;
+ const char *title;
+ int response;
+ GPtrArray *children;
+
+ children = ephy_node_get_children (node);
+
+ /* Do not warn if the topic is empty */
+ if (children->len == 0) {
+ ephy_node_view_remove (EPHY_NODE_VIEW (editor->key_view));
+ } else {
+ title = ephy_node_get_property_string (node, EPHY_NODE_KEYWORD_PROP_NAME);
+ dialog = delete_topic_dialog_construct (GTK_WINDOW (editor), title);
+
+ response = gtk_dialog_run (GTK_DIALOG (dialog));
+
+ gtk_widget_destroy (dialog);
+
+ if (response == GTK_RESPONSE_ACCEPT) {
+ ephy_node_view_remove (EPHY_NODE_VIEW (editor->key_view));
+ }
+ }
+ }
+ g_list_free (selected);
+ }
+}
+
+static GSList *
+add_bookmarks_files (const char *dir,
+ const char *filename,
+ int max_depth)
+{
+ GSList *list;
+ char *path;
+
+ path = g_build_filename (g_get_home_dir (), dir, NULL);
+ list = ephy_file_find (path, filename, max_depth);
+ g_free (path);
+
+ return list;
+}
+
+static void
+add_bookmarks_source (const char *file,
+ GtkListStore *store)
+{
+ GtkTreeIter iter;
+ char **path;
+ char *description = NULL;
+ int len, i;
+
+ path = g_strsplit (file, G_DIR_SEPARATOR_S, -1);
+ g_return_if_fail (path != NULL);
+
+ len = g_strv_length (path);
+
+ for (i = len - 2; i >= 0 && description == NULL; --i) {
+ const char *p = (const char *)path[i];
+
+ g_return_if_fail (p != NULL);
+
+ if (strcmp (p, "firefox") == 0) {
+ const char *profile = NULL, *dot;
+
+ if (path[i + 1] != NULL) {
+ dot = strchr (path[i + 1], '.');
+ profile = dot ? dot + 1 : path[i + 1];
+ }
+
+ if (profile != NULL && strcmp (profile, "default") != 0) {
+ /* FIXME: proper i18n after freeze */
+ description = g_strdup_printf ("%s “%s”", _("Firefox"), profile);
+ } else {
+ description = g_strdup (_("Firefox"));
+ }
+ } else if (strcmp (p, ".firefox") == 0) {
+ description = g_strdup (_("Firebird"));
+ } else if (strcmp (p, ".phoenix") == 0) {
+ description = g_strdup (_("Firebird"));
+ } else if (strcmp (p, ".mozilla") == 0) {
+ /* Translators: The %s is the name of a Mozilla profile. */
+ description = g_strdup_printf (_("Mozilla “%s” profile"), path[i + 1]);
+ } else if (strcmp (p, ".galeon") == 0) {
+ description = g_strdup (_("Galeon"));
+ } else if (strcmp (p, "konqueror") == 0) {
+ description = g_strdup (_("Konqueror"));
+ }
+ }
+
+ if (description != NULL) {
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter, 0, description, 1, file, -1);
+
+ g_free (description);
+ }
+
+ g_strfreev (path);
+}
+
+static void
+import_bookmarks (EphyBookmarksEditor *editor,
+ const char *filename)
+{
+ if (ephy_bookmarks_import (editor->bookmarks, filename) == FALSE) {
+ GtkWidget *dialog;
+ char *basename;
+
+ basename = g_filename_display_basename (filename);
+ dialog = gtk_message_dialog_new (GTK_WINDOW (editor),
+ GTK_DIALOG_MODAL,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_OK,
+ _("Import failed"));
+
+ gtk_window_set_title (GTK_WINDOW (dialog), _("Import Failed"));
+ gtk_message_dialog_format_secondary_text
+ (GTK_MESSAGE_DIALOG (dialog),
+ _("The bookmarks from “%s” could not be imported "
+ "because the file is corrupted or of an "
+ "unsupported type."),
+ basename);
+
+ gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (editor)),
+ GTK_WINDOW (dialog));
+
+ gtk_dialog_run (GTK_DIALOG (dialog));
+
+ g_free (basename);
+ gtk_widget_destroy (dialog);
+ }
+}
+
+static void
+import_from_file_response_cb (GtkWidget *dialog,
+ int response,
+ EphyBookmarksEditor *editor)
+{
+ char *filename;
+
+ gtk_widget_hide (dialog);
+
+ if (response == GTK_RESPONSE_ACCEPT) {
+ filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
+
+ if (filename != NULL) {
+ import_bookmarks (editor, filename);
+
+ g_free (filename);
+ }
+ }
+
+ gtk_widget_destroy (dialog);
+}
+
+static void
+import_dialog_response_cb (GtkDialog *dialog,
+ int response,
+ EphyBookmarksEditor *editor)
+{
+ if (response == GTK_RESPONSE_OK) {
+ GtkTreeIter iter;
+ const char *filename;
+ GtkWidget *combo;
+ GtkTreeModel *model;
+ GValue value = { 0, };
+
+ combo = g_object_get_data (G_OBJECT (dialog), "combo_box");
+ model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo));
+ gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo), &iter);
+ gtk_tree_model_get_value (model, &iter, 1, &value);
+ filename = g_value_get_string (&value);
+
+ if (filename == NULL) {
+ EphyFileChooser *chooser;
+ GtkFileFilter *filter;
+
+ chooser = ephy_file_chooser_new (_("Import Bookmarks from File"),
+ GTK_WIDGET (editor),
+ GTK_FILE_CHOOSER_ACTION_OPEN,
+ EPHY_FILE_FILTER_NONE);
+
+ ephy_file_chooser_add_mime_filter
+ (chooser,
+ _("Firefox/Mozilla bookmarks"),
+ "application/x-mozilla-bookmarks", NULL);
+
+ ephy_file_chooser_add_mime_filter
+ (chooser, _("Galeon/Konqueror bookmarks"),
+ "application/x-xbel", NULL);
+
+ ephy_file_chooser_add_mime_filter
+ (chooser, _("Web bookmarks"),
+ "text/rdf", "application/rdf+xml", NULL);
+
+ filter = ephy_file_chooser_add_pattern_filter (chooser,
+ _("All files"),
+ "*", NULL);
+
+ gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (chooser),
+ filter);
+
+ g_signal_connect (chooser, "response",
+ G_CALLBACK (import_from_file_response_cb), editor);
+
+ gtk_widget_show (GTK_WIDGET (chooser));
+ } else {
+ import_bookmarks (editor, filename);
+ }
+
+ g_value_unset (&value);
+ }
+
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+}
+
+static void
+export_format_combo_changed_cb (GtkComboBox *combo,
+ GtkFileChooser *chooser)
+{
+ char *filename, *basename, *dot, *newname;
+ guint i;
+ gint format;
+
+ filename = gtk_file_chooser_get_filename (chooser);
+ if (filename == NULL) return;
+
+ basename = g_path_get_basename (filename);
+ if (basename == NULL || basename[0] == '\0') {
+ g_free (filename);
+ g_free (basename);
+ return;
+ }
+
+ format = gtk_combo_box_get_active (GTK_COMBO_BOX (combo));
+ g_return_if_fail (format >= 0 && (guint)format < G_N_ELEMENTS (export_formats));
+
+ dot = strrchr (basename, '.');
+ if (dot != NULL) {
+ for (i = 0; i < G_N_ELEMENTS (export_formats); ++i) {
+ if (strcmp (dot + 1, export_formats[i].extension) == 0) {
+ *dot = '\0';
+ break;
+ }
+ }
+ }
+
+ newname = g_strconcat (basename, ".",
+ export_formats[format].extension,
+ NULL);
+
+ gtk_file_chooser_set_current_name (chooser, newname);
+
+ g_free (filename);
+ g_free (basename);
+ g_free (newname);
+}
+
+static void
+export_dialog_response_cb (GtkWidget *dialog,
+ int response,
+ EphyBookmarksEditor *editor)
+{
+ GtkWidget *combo;
+ char *filename;
+ int format;
+
+ if (response != GTK_RESPONSE_ACCEPT) {
+ gtk_widget_destroy (dialog);
+ return;
+ }
+
+ filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
+ if (filename == NULL) return;
+
+ if (!ephy_gui_check_location_writable (GTK_WIDGET (dialog), filename)) {
+ g_free (filename);
+ return;
+ }
+
+ combo = g_object_get_data (G_OBJECT (dialog), "format-combo");
+ g_return_if_fail (combo != NULL);
+
+ format = gtk_combo_box_get_active (GTK_COMBO_BOX (combo));
+ g_return_if_fail (format >= 0 && (guint)format < G_N_ELEMENTS (export_formats));
+
+ gtk_widget_destroy (dialog);
+
+ /* 0 for ephy RDF format, 1 for mozilla HTML format */
+ if (format == 0) {
+ ephy_bookmarks_export_rdf (editor->bookmarks, filename);
+ } else if (format == 1) {
+ ephy_bookmarks_export_mozilla (editor->bookmarks, filename);
+ }
+
+ g_free (filename);
+}
+
+static void
+cmd_bookmarks_export (GtkAction *action,
+ EphyBookmarksEditor *editor)
+{
+ GtkWidget *dialog, *hbox, *label, *combo;
+ guint format;
+ char *filename;
+
+ dialog = GTK_WIDGET (ephy_file_chooser_new (_("Export Bookmarks"),
+ GTK_WIDGET (editor),
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+ EPHY_FILE_FILTER_NONE));
+
+ gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog), TRUE);
+
+ filename = g_strdup_printf ("%s.%s", _("Bookmarks"), export_formats[0].extension);
+ gtk_file_chooser_set_current_name
+ (GTK_FILE_CHOOSER (dialog), filename);
+ g_free (filename);
+
+ /* Make a format selection combo & label */
+ label = gtk_label_new_with_mnemonic (_("File f_ormat:"));
+
+ combo = gtk_combo_box_text_new ();
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), combo);
+
+ for (format = 0; format < G_N_ELEMENTS (export_formats); ++format) {
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo),
+ _(export_formats[format].name));
+ }
+
+ g_object_set_data (G_OBJECT (dialog), "format-combo", combo);
+ g_signal_connect (combo, "changed",
+ G_CALLBACK (export_format_combo_changed_cb), dialog);
+ gtk_combo_box_set_active (GTK_COMBO_BOX (combo), 0);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (hbox), combo, FALSE, FALSE, 0);
+ gtk_widget_show_all (hbox);
+
+ gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (dialog), hbox);
+
+ gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (editor)),
+ GTK_WINDOW (dialog));
+
+ g_signal_connect (dialog, "response",
+ G_CALLBACK (export_dialog_response_cb), editor);
+ gtk_widget_show (dialog);
+}
+
+static void
+cmd_bookmarks_import (GtkAction *action,
+ EphyBookmarksEditor *editor)
+{
+ GtkWidget *dialog;
+ GtkWidget *content_area;
+ GtkWidget *label;
+ GtkWidget *vbox;
+ GtkWidget *combo;
+ GtkCellRenderer *cell;
+ GtkListStore *store;
+ GtkTreeIter iter;
+ GtkTreeModel *sortmodel;
+ GSList *files;
+
+ dialog = gtk_dialog_new_with_buttons (_("Import Bookmarks"),
+ GTK_WINDOW (editor),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ _("_Cancel"),
+ GTK_RESPONSE_CANCEL,
+ _("I_mport"),
+ GTK_RESPONSE_OK,
+ NULL);
+ content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+ gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
+ gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
+ gtk_box_set_spacing (GTK_BOX (content_area), 2);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 5);
+ gtk_widget_show (vbox);
+ gtk_box_pack_start (GTK_BOX (content_area), vbox,
+ TRUE, TRUE, 0);
+
+ label = gtk_label_new (_("Import bookmarks from:"));
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+ gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+ gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0);
+ gtk_widget_show (label);
+
+ store = GTK_LIST_STORE (gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING));
+
+ files = add_bookmarks_files (FIREFOX_BOOKMARKS_DIR_0, "bookmarks.html", 2);
+ files = g_slist_concat (add_bookmarks_files (FIREFOX_BOOKMARKS_DIR_1, "bookmarks.html", 2), files);
+ /* FIREFOX_BOOKMARKS_DIR_2 is subdir of MOZILLA_BOOKMARKS_DIR, so don't search it twice */
+ files = g_slist_concat (add_bookmarks_files (MOZILLA_BOOKMARKS_DIR, "bookmarks.html", 2), files);
+ files = g_slist_concat (add_bookmarks_files (GALEON_BOOKMARKS_DIR, "bookmarks.xbel", 0), files);
+ files = g_slist_concat (add_bookmarks_files (KDE_BOOKMARKS_DIR, "bookmarks.xml", 0), files);
+
+ g_slist_foreach (files, (GFunc)add_bookmarks_source, store);
+ g_slist_foreach (files, (GFunc)g_free, NULL);
+ g_slist_free (files);
+
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter, 0, _("File"), 1, NULL, -1);
+
+ sortmodel = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (store));
+ g_object_unref (store);
+
+ gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sortmodel), 0, GTK_SORT_ASCENDING);
+
+ combo = gtk_combo_box_new ();
+ gtk_combo_box_set_model (GTK_COMBO_BOX (combo), sortmodel);
+ g_object_set_data (G_OBJECT (dialog), "combo_box", combo);
+ g_object_unref (sortmodel);
+
+ cell = gtk_cell_renderer_text_new ();
+ gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, TRUE);
+ gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), cell,
+ "text", 0,
+ NULL);
+
+ gtk_box_pack_start (GTK_BOX (vbox), combo, TRUE, TRUE, 0);
+ gtk_combo_box_set_active (GTK_COMBO_BOX (combo), 0);
+
+ gtk_widget_show (combo);
+
+ g_signal_connect (dialog, "response",
+ G_CALLBACK (import_dialog_response_cb),
+ editor);
+
+ gtk_widget_show (dialog);
+}
+
+static void
+cmd_bookmark_properties (GtkAction *action,
+ EphyBookmarksEditor *editor)
+{
+ GList *selection;
+ GList *l;
+
+ selection = ephy_node_view_get_selection (EPHY_NODE_VIEW (editor->bm_view));
+
+ for (l = selection; l; l = l->next) {
+ EphyNode *node = l->data;
+
+ ephy_bookmarks_ui_show_bookmark (GTK_WINDOW (editor), node);
+ }
+
+ g_list_free (selection);
+}
+
+static void
+cmd_cut (GtkAction *action,
+ EphyBookmarksEditor *editor)
+{
+ GtkWidget *widget = gtk_window_get_focus (GTK_WINDOW (editor));
+
+ if (GTK_IS_EDITABLE (widget)) {
+ gtk_editable_cut_clipboard (GTK_EDITABLE (widget));
+ }
+}
+
+static void
+cmd_copy (GtkAction *action,
+ EphyBookmarksEditor *editor)
+{
+ GtkWidget *widget = gtk_window_get_focus (GTK_WINDOW (editor));
+
+ if (GTK_IS_EDITABLE (widget)) {
+ gtk_editable_copy_clipboard (GTK_EDITABLE (widget));
+ } else if (ephy_node_view_is_target (EPHY_NODE_VIEW (editor->bm_view))) {
+ GList *selection;
+
+ selection = ephy_node_view_get_selection (EPHY_NODE_VIEW (editor->bm_view));
+
+ if (g_list_length (selection) == 1) {
+ const char *tmp;
+ EphyNode *node = selection->data;
+ tmp = ephy_node_get_property_string (node, EPHY_NODE_BMK_PROP_LOCATION);
+ gtk_clipboard_set_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD), tmp, -1);
+ }
+
+ g_list_free (selection);
+ }
+}
+
+static void
+cmd_paste (GtkAction *action,
+ EphyBookmarksEditor *editor)
+{
+ GtkWidget *widget = gtk_window_get_focus (GTK_WINDOW (editor));
+
+ if (GTK_IS_EDITABLE (widget)) {
+ gtk_editable_paste_clipboard (GTK_EDITABLE (widget));
+ }
+}
+
+static void
+cmd_select_all (GtkAction *action,
+ EphyBookmarksEditor *editor)
+{
+ GtkWidget *widget = gtk_window_get_focus (GTK_WINDOW (editor));
+ GtkWidget *bm_view = editor->bm_view;
+
+ if (GTK_IS_EDITABLE (widget)) {
+ gtk_editable_select_region (GTK_EDITABLE (widget), 0, -1);
+ } else if (ephy_node_view_is_target (EPHY_NODE_VIEW (bm_view))) {
+ GtkTreeSelection *sel;
+
+ sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (bm_view));
+ gtk_tree_selection_select_all (sel);
+ }
+}
+
+static void
+cmd_help_contents (GtkAction *action,
+ EphyBookmarksEditor *editor)
+{
+ ephy_gui_help (GTK_WIDGET (editor), "bookmark");
+}
+
+static void
+ephy_bookmarks_editor_show (GtkWidget *widget)
+{
+ EphyBookmarksEditor *editor = EPHY_BOOKMARKS_EDITOR (widget);
+
+ gtk_widget_grab_focus (editor->search_entry);
+
+ GTK_WIDGET_CLASS (ephy_bookmarks_editor_parent_class)->show (widget);
+}
+
+static void
+ephy_bookmarks_editor_class_init (EphyBookmarksEditorClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->finalize = ephy_bookmarks_editor_finalize;
+ object_class->dispose = ephy_bookmarks_editor_dispose;
+
+ object_class->set_property = ephy_bookmarks_editor_set_property;
+ object_class->get_property = ephy_bookmarks_editor_get_property;
+
+ widget_class->show = ephy_bookmarks_editor_show;
+
+ obj_properties[PROP_BOOKMARKS] =
+ g_param_spec_object ("bookmarks",
+ "Bookmarks set",
+ "Bookmarks set",
+ EPHY_TYPE_BOOKMARKS,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
+
+ g_object_class_install_properties (object_class, LAST_PROP, obj_properties);
+}
+
+static void
+ephy_bookmarks_editor_finalize (GObject *object)
+{
+ /* FIXME: This should all be done in dispose, not finalize, but got to make sure that's safe. */
+ EphyBookmarksEditor *editor = EPHY_BOOKMARKS_EDITOR (object);
+
+ g_object_unref (G_OBJECT (editor->bookmarks_filter));
+
+ g_object_unref (editor->action_group);
+ g_object_unref (editor->ui_merge);
+
+ if (editor->window) {
+ GtkWidget **window = &editor->window;
+ g_object_remove_weak_pointer
+ (G_OBJECT (editor->window),
+ (gpointer *)window);
+ }
+
+ G_OBJECT_CLASS (ephy_bookmarks_editor_parent_class)->finalize (object);
+}
+
+static void
+ephy_bookmarks_editor_node_activated_cb (GtkWidget *view,
+ EphyNode *node,
+ EphyBookmarksEditor *editor)
+{
+ EphyEmbed *embed;
+
+ embed = ephy_shell_new_tab (ephy_shell_get_default (),
+ NULL, NULL, 0);
+ load_bookmark_in_tab (node, embed);
+}
+
+static void
+ephy_bookmarks_editor_node_middle_clicked_cb (GtkWidget *view,
+ EphyNode *node,
+ EphyBookmarksEditor *editor)
+{
+ EphyWindow *window;
+ EphyEmbed *embed;
+
+ window = EPHY_WINDOW (get_target_window (editor));
+
+ embed = ephy_shell_new_tab (ephy_shell_get_default (),
+ window, NULL, 0);
+ load_bookmark_in_tab (node, embed);
+}
+
+static void
+ephy_bookmarks_editor_update_menu (EphyBookmarksEditor *editor)
+{
+ gboolean open_in_window, open_in_tab,
+ rename, delete, properties;
+ const gchar *open_in_window_label, *open_in_tab_label, *copy_label;
+ gboolean bmk_focus, key_focus;
+ gboolean key_selection, bmk_selection, single_bmk_selected;
+ gboolean key_normal = FALSE;
+ gboolean cut, copy, paste, select_all;
+ gboolean mutable = TRUE;
+ gboolean fullscreen_lockdown;
+
+ GtkActionGroup *action_group;
+ GtkAction *action;
+ GList *selected;
+ GtkWidget *focus_widget;
+ int num_bmk_selected;
+
+ LOG ("Update menu sensitivity");
+
+ bmk_focus = ephy_node_view_is_target
+ (EPHY_NODE_VIEW (editor->bm_view));
+ key_focus = ephy_node_view_is_target
+ (EPHY_NODE_VIEW (editor->key_view));
+ focus_widget = gtk_window_get_focus (GTK_WINDOW (editor));
+
+ num_bmk_selected = gtk_tree_selection_count_selected_rows
+ (gtk_tree_view_get_selection (GTK_TREE_VIEW (editor->bm_view)));
+ bmk_selection = num_bmk_selected > 0;
+ single_bmk_selected = num_bmk_selected == 1;
+
+ key_selection = gtk_tree_selection_count_selected_rows
+ (gtk_tree_view_get_selection (GTK_TREE_VIEW (editor->key_view))) > 0;
+
+ if (GTK_IS_EDITABLE (focus_widget)) {
+ gboolean has_selection;
+
+ has_selection = gtk_editable_get_selection_bounds
+ (GTK_EDITABLE (focus_widget), NULL, NULL);
+
+ cut = has_selection;
+ copy = has_selection;
+ paste = TRUE;
+ select_all = TRUE;
+ } else {
+ cut = FALSE;
+ copy = (bmk_focus && single_bmk_selected);
+ paste = FALSE;
+ select_all = bmk_focus;
+ }
+
+ selected = ephy_node_view_get_selection (EPHY_NODE_VIEW (editor->key_view));
+ if (key_focus && selected) {
+ EphyNode *node = selected->data;
+ int priority;
+ char name[EPHY_TOPIC_ACTION_NAME_BUFFER_SIZE];
+
+ priority = ephy_node_get_property_int
+ (node, EPHY_NODE_KEYWORD_PROP_PRIORITY);
+ if (priority == -1) priority = EPHY_NODE_NORMAL_PRIORITY;
+ key_normal = (priority == EPHY_NODE_NORMAL_PRIORITY);
+
+ EPHY_TOPIC_ACTION_NAME_PRINTF (name, node);
+ }
+ g_list_free (selected);
+
+ selected = ephy_node_view_get_selection (EPHY_NODE_VIEW (editor->bm_view));
+ if (bmk_focus && selected) {
+ EphyNode *node = selected->data;
+ char name[EPHY_BOOKMARK_ACTION_NAME_BUFFER_SIZE];
+
+ if (node == NULL) return;
+
+ mutable = !ephy_node_get_property_boolean (node, EPHY_NODE_BMK_PROP_IMMUTABLE);
+
+ EPHY_BOOKMARK_ACTION_NAME_PRINTF (name, node);
+ }
+ g_list_free (selected);
+
+ open_in_window_label = ngettext ("Open in New _Window",
+ "Open in New _Windows",
+ num_bmk_selected);
+ open_in_tab_label = ngettext ("Open in New _Tab",
+ "Open in New _Tabs",
+ num_bmk_selected);
+
+ if (bmk_focus) {
+ copy_label = _("_Copy Address");
+ } else {
+ copy_label = _("_Copy");
+ }
+
+ fullscreen_lockdown = g_settings_get_boolean (EPHY_SETTINGS_LOCKDOWN,
+ EPHY_PREFS_LOCKDOWN_FULLSCREEN);
+ open_in_window = (bmk_focus && bmk_selection && !fullscreen_lockdown);
+ open_in_tab = (bmk_focus && bmk_selection);
+ rename = (bmk_focus && single_bmk_selected && mutable) ||
+ (key_selection && key_focus && key_normal);
+ delete = (bmk_focus && bmk_selection && mutable) ||
+ (key_selection && key_focus && key_normal);
+ properties = bmk_focus && single_bmk_selected && mutable;
+
+ action_group = editor->action_group;
+ action = gtk_action_group_get_action (action_group, "OpenInWindow");
+ gtk_action_set_sensitive (action, open_in_window);
+ g_object_set (action, "label", open_in_window_label, NULL);
+ action = gtk_action_group_get_action (action_group, "OpenInTab");
+ gtk_action_set_sensitive (action, open_in_tab);
+ g_object_set (action, "label", open_in_tab_label, NULL);
+ action = gtk_action_group_get_action (action_group, "Rename");
+ gtk_action_set_sensitive (action, rename);
+ action = gtk_action_group_get_action (action_group, "Delete");
+ gtk_action_set_sensitive (action, delete);
+ action = gtk_action_group_get_action (action_group, "Properties");
+ gtk_action_set_sensitive (action, properties);
+ action = gtk_action_group_get_action (action_group, "Cut");
+ gtk_action_set_sensitive (action, cut);
+ action = gtk_action_group_get_action (action_group, "Copy");
+ gtk_action_set_sensitive (action, copy);
+ g_object_set (action, "label", copy_label, NULL);
+ action = gtk_action_group_get_action (action_group, "Paste");
+ gtk_action_set_sensitive (action, paste);
+ action = gtk_action_group_get_action (action_group, "SelectAll");
+ g_object_set (action, "sensitive", select_all, NULL);
+}
+
+static gboolean
+view_focus_cb (EphyNodeView *view,
+ GdkEventFocus *event,
+ EphyBookmarksEditor *editor)
+{
+ ephy_bookmarks_editor_update_menu (editor);
+
+ return FALSE;
+}
+
+static void
+add_focus_monitor (EphyBookmarksEditor *editor, GtkWidget *widget)
+{
+ g_signal_connect (G_OBJECT (widget),
+ "focus_in_event",
+ G_CALLBACK (view_focus_cb),
+ editor);
+ g_signal_connect (G_OBJECT (widget),
+ "focus_out_event",
+ G_CALLBACK (view_focus_cb),
+ editor);
+}
+
+static void
+remove_focus_monitor (EphyBookmarksEditor *editor, GtkWidget *widget)
+{
+ g_signal_handlers_disconnect_by_func (G_OBJECT (widget),
+ G_CALLBACK (view_focus_cb),
+ editor);
+}
+
+static gboolean
+ephy_bookmarks_editor_show_popup_cb (GtkWidget *view,
+ EphyBookmarksEditor *editor)
+{
+ GtkWidget *widget;
+
+ widget = gtk_ui_manager_get_widget (editor->ui_merge,
+ "/EphyBookmarkEditorPopup");
+ ephy_node_view_popup (EPHY_NODE_VIEW (view), widget);
+
+ return TRUE;
+}
+
+static void
+ephy_bookmarks_editor_dispose (GObject *object)
+{
+ EphyBookmarksEditor *editor;
+ GList *selection;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (EPHY_IS_BOOKMARKS_EDITOR (object));
+
+ editor = EPHY_BOOKMARKS_EDITOR (object);
+
+ if (editor->key_view != NULL) {
+ remove_focus_monitor (editor, editor->key_view);
+ remove_focus_monitor (editor, editor->bm_view);
+ remove_focus_monitor (editor, editor->search_entry);
+
+ selection = ephy_node_view_get_selection (EPHY_NODE_VIEW (editor->key_view));
+ if (selection == NULL || selection->data == NULL) {
+ editor->key_view = NULL;
+ G_OBJECT_CLASS (ephy_bookmarks_editor_parent_class)->dispose (object);
+ return;
+ }
+
+ g_list_free (selection);
+
+ editor->key_view = NULL;
+ }
+
+ G_OBJECT_CLASS (ephy_bookmarks_editor_parent_class)->dispose (object);
+}
+
+static void
+bookmarks_filter (EphyBookmarksEditor *editor,
+ EphyNode *keyword)
+{
+ ephy_node_filter_empty (editor->bookmarks_filter);
+ ephy_node_filter_add_expression (editor->bookmarks_filter,
+ ephy_node_filter_expression_new (EPHY_NODE_FILTER_EXPRESSION_HAS_PARENT,
+ keyword),
+ 0);
+ ephy_node_filter_done_changing (editor->bookmarks_filter);
+}
+
+static gboolean
+key_pressed_cb (EphyNodeView *view,
+ GdkEventKey *event,
+ EphyBookmarksEditor *editor)
+{
+ GtkAction *action;
+
+ if (event->keyval == GDK_KEY_Delete || event->keyval == GDK_KEY_KP_Delete) {
+ action = gtk_action_group_get_action (editor->action_group, "Delete");
+ if (gtk_action_get_sensitive (action)) {
+ cmd_delete (NULL, editor);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static void
+keyword_node_selected_cb (EphyNodeView *view,
+ EphyNode *node,
+ EphyBookmarksEditor *editor)
+{
+ EphyNode *bookmarks;
+
+ if (node == NULL) {
+ bookmarks = ephy_bookmarks_get_bookmarks (editor->bookmarks);
+ ephy_node_view_select_node (EPHY_NODE_VIEW (editor->key_view), bookmarks);
+ } else {
+ gtk_entry_set_text (GTK_ENTRY (editor->search_entry), "");
+ bookmarks_filter (editor, node);
+ }
+}
+
+static gboolean
+keyword_node_show_popup_cb (GtkWidget *view, EphyBookmarksEditor *editor)
+{
+ GtkWidget *widget;
+
+ widget = gtk_ui_manager_get_widget (editor->ui_merge,
+ "/EphyBookmarkKeywordPopup");
+ ephy_node_view_popup (EPHY_NODE_VIEW (view), widget);
+
+ return TRUE;
+}
+
+static void
+search_entry_changed_cb (GtkWidget *entry, EphyBookmarksEditor *editor)
+{
+ EphyNode *all;
+ const char *search_text;
+
+ g_signal_handlers_block_by_func
+ (G_OBJECT (editor->key_view),
+ G_CALLBACK (keyword_node_selected_cb),
+ editor);
+ all = ephy_bookmarks_get_bookmarks (editor->bookmarks);
+ ephy_node_view_select_node (EPHY_NODE_VIEW (editor->key_view),
+ all);
+ g_signal_handlers_unblock_by_func
+ (G_OBJECT (editor->key_view),
+ G_CALLBACK (keyword_node_selected_cb),
+ editor);
+
+ search_text = gtk_entry_get_text (GTK_ENTRY (entry));
+
+ ephy_node_filter_empty (editor->bookmarks_filter);
+ ephy_node_filter_add_expression (editor->bookmarks_filter,
+ ephy_node_filter_expression_new (EPHY_NODE_FILTER_EXPRESSION_STRING_PROP_CONTAINS,
+ EPHY_NODE_BMK_PROP_TITLE,
+ search_text),
+ 0);
+ ephy_node_filter_add_expression (editor->bookmarks_filter,
+ ephy_node_filter_expression_new (EPHY_NODE_FILTER_EXPRESSION_STRING_PROP_CONTAINS,
+ EPHY_NODE_BMK_PROP_LOCATION,
+ search_text),
+ 0);
+ ephy_node_filter_add_expression (editor->bookmarks_filter,
+ ephy_node_filter_expression_new (EPHY_NODE_FILTER_EXPRESSION_STRING_PROP_CONTAINS,
+ EPHY_NODE_BMK_PROP_KEYWORDS,
+ search_text),
+ 0);
+ ephy_node_filter_done_changing (editor->bookmarks_filter);
+}
+
+static GtkWidget *
+build_search_box (EphyBookmarksEditor *editor)
+{
+ GtkWidget *box;
+ GtkWidget *entry;
+
+ box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_container_set_border_width (GTK_CONTAINER (box), 6);
+ gtk_widget_show (box);
+
+ entry = gtk_search_entry_new ();
+ editor->search_entry = entry;
+
+ g_signal_connect (G_OBJECT (entry), "changed",
+ G_CALLBACK (search_entry_changed_cb),
+ editor);
+ add_entry_monitor (editor, entry);
+ add_focus_monitor (editor, entry);
+
+ gtk_widget_show_all (entry);
+
+ gtk_box_pack_start (GTK_BOX (box),
+ entry, TRUE, TRUE, 0);
+
+ return box;
+}
+
+static void
+add_widget (GtkUIManager *merge, GtkWidget *widget, EphyBookmarksEditor *editor)
+{
+ gtk_box_pack_start (GTK_BOX (editor->main_vbox),
+ widget, FALSE, FALSE, 0);
+ gtk_widget_show (widget);
+}
+
+static gboolean
+delete_event_cb (EphyBookmarksEditor *editor)
+{
+ gtk_widget_hide (GTK_WIDGET (editor));
+
+ return TRUE;
+}
+
+static void
+node_dropped_cb (EphyNodeView *view,
+ EphyNode *node,
+ const char * const *uris,
+ EphyBookmarksEditor *editor)
+{
+ EphyNode *bmk;
+ int i;
+
+ g_return_if_fail (uris != NULL);
+
+ for (i = 0; uris[i] != NULL; i++) {
+ bmk = ephy_bookmarks_find_bookmark (editor->bookmarks, uris[i]);
+
+ if (bmk != NULL) {
+ ephy_bookmarks_set_keyword (editor->bookmarks, node, bmk);
+ }
+ }
+}
+
+/* TODO: Favicons */
+
+static void
+provide_favicon (EphyNode *node, GValue *value, gpointer user_data)
+{
+ GdkPixbuf *favicon = NULL;
+
+ /* TODO: Favicons */
+ g_value_init (value, GDK_TYPE_PIXBUF);
+ g_value_take_object (value, favicon);
+}
+
+static void
+view_selection_changed_cb (GtkWidget *view, EphyBookmarksEditor *editor)
+{
+ ephy_bookmarks_editor_update_menu (editor);
+}
+
+static void
+provide_keyword_uri (EphyNode *node, GValue *value, gpointer data)
+{
+ EphyBookmarks *bookmarks = EPHY_BOOKMARKS_EDITOR (data)->bookmarks;
+ char *uri;
+
+ uri = ephy_bookmarks_get_topic_uri (bookmarks, node);
+
+ g_value_init (value, G_TYPE_STRING);
+ g_value_set_string (value, uri);
+ g_free (uri);
+}
+
+static void
+decode_bookmark_uri (EphyNode *node,
+ GValue *value,
+ gpointer user_data)
+{
+ const char *url = g_value_get_string (value);
+ g_value_take_string (value, ephy_uri_decode (url));
+}
+
+
+static void
+ephy_bookmarks_editor_construct (EphyBookmarksEditor *editor)
+{
+ GtkTreeSelection *selection;
+ GtkWidget *hpaned, *vbox;
+ GtkWidget *bm_view, *key_view;
+ GtkWidget *scrolled_window;
+ EphyNode *node;
+ GtkUIManager *ui_merge;
+ GtkActionGroup *action_group;
+ GtkAction *action;
+ int col_id, url_col_id, title_col_id;
+
+ ephy_gui_ensure_window_group (GTK_WINDOW (editor));
+
+ gtk_window_set_title (GTK_WINDOW (editor), _("Bookmarks"));
+
+ g_signal_connect (editor, "delete_event",
+ G_CALLBACK (delete_event_cb), NULL);
+
+ editor->main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ gtk_widget_show (editor->main_vbox);
+ gtk_container_add (GTK_CONTAINER (editor), editor->main_vbox);
+
+ ui_merge = gtk_ui_manager_new ();
+ g_signal_connect (ui_merge, "add_widget", G_CALLBACK (add_widget), editor);
+ action_group = gtk_action_group_new ("PopupActions");
+ gtk_action_group_set_translation_domain (action_group, NULL);
+ gtk_action_group_add_actions (action_group, ephy_bookmark_popup_entries,
+ G_N_ELEMENTS (ephy_bookmark_popup_entries), editor);
+ gtk_action_group_add_toggle_actions (action_group, ephy_bookmark_toggle_entries,
+ G_N_ELEMENTS (ephy_bookmark_toggle_entries), editor);
+
+ gtk_ui_manager_insert_action_group (ui_merge,
+ action_group, 0);
+ gtk_ui_manager_add_ui_from_resource (ui_merge,
+ "/org/gnome/epiphany/epiphany-bookmark-editor-ui.xml",
+ NULL);
+ gtk_window_add_accel_group (GTK_WINDOW (editor),
+ gtk_ui_manager_get_accel_group (ui_merge));
+ gtk_ui_manager_ensure_update (ui_merge);
+
+ editor->ui_merge = ui_merge;
+ editor->action_group = action_group;
+
+ hpaned = gtk_paned_new (GTK_ORIENTATION_HORIZONTAL);
+ gtk_container_set_border_width (GTK_CONTAINER (hpaned), 0);
+ gtk_box_pack_end (GTK_BOX (editor->main_vbox), hpaned,
+ TRUE, TRUE, 0);
+ gtk_widget_show (hpaned);
+
+ g_assert (editor->bookmarks);
+
+ node = ephy_bookmarks_get_keywords (editor->bookmarks);
+
+ scrolled_window = g_object_new (GTK_TYPE_SCROLLED_WINDOW,
+ "hadjustment", NULL,
+ "vadjustment", NULL,
+ "hscrollbar_policy", GTK_POLICY_AUTOMATIC,
+ "vscrollbar_policy", GTK_POLICY_AUTOMATIC,
+ "shadow_type", GTK_SHADOW_IN,
+ NULL);
+ gtk_paned_pack1 (GTK_PANED (hpaned), GTK_WIDGET (scrolled_window), FALSE, TRUE);
+ gtk_widget_show (scrolled_window);
+
+ /* Keywords View */
+ key_view = ephy_node_view_new (node, NULL);
+ add_focus_monitor (editor, key_view);
+ col_id = ephy_node_view_add_data_column (EPHY_NODE_VIEW (key_view),
+ G_TYPE_STRING, -1,
+ provide_keyword_uri, editor);
+ ephy_node_view_add_column (EPHY_NODE_VIEW (key_view), _("Topics"),
+ G_TYPE_STRING,
+ EPHY_NODE_KEYWORD_PROP_NAME,
+ EPHY_NODE_VIEW_SHOW_PRIORITY |
+ EPHY_NODE_VIEW_EDITABLE |
+ EPHY_NODE_VIEW_SEARCHABLE, NULL, NULL);
+ ephy_node_view_enable_drag_source (EPHY_NODE_VIEW (key_view),
+ topic_drag_types,
+ G_N_ELEMENTS (topic_drag_types),
+ col_id, -1);
+ ephy_node_view_enable_drag_dest (EPHY_NODE_VIEW (key_view),
+ topic_drag_dest_types,
+ G_N_ELEMENTS (topic_drag_dest_types));
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (key_view));
+ gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
+ g_signal_connect (G_OBJECT (selection),
+ "changed",
+ G_CALLBACK (view_selection_changed_cb),
+ editor);
+ ephy_node_view_set_priority (EPHY_NODE_VIEW (key_view),
+ (EphyNodeViewPriority)EPHY_NODE_KEYWORD_PROP_PRIORITY);
+ ephy_node_view_set_sort (EPHY_NODE_VIEW (key_view), G_TYPE_STRING,
+ EPHY_NODE_KEYWORD_PROP_NAME, GTK_SORT_ASCENDING);
+ gtk_container_add (GTK_CONTAINER (scrolled_window), key_view);
+ gtk_widget_set_size_request (key_view, 130, -1);
+ gtk_widget_show (key_view);
+ editor->key_view = key_view;
+ g_signal_connect (G_OBJECT (key_view),
+ "key_press_event",
+ G_CALLBACK (key_pressed_cb),
+ editor);
+ g_signal_connect (G_OBJECT (key_view),
+ "node_selected",
+ G_CALLBACK (keyword_node_selected_cb),
+ editor);
+ g_signal_connect (G_OBJECT (key_view),
+ "node_dropped",
+ G_CALLBACK (node_dropped_cb),
+ editor);
+ g_signal_connect (G_OBJECT (key_view),
+ "popup_menu",
+ G_CALLBACK (keyword_node_show_popup_cb),
+ editor);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ gtk_paned_pack2 (GTK_PANED (hpaned), vbox, TRUE, TRUE);
+ gtk_widget_show (vbox);
+
+ gtk_box_pack_start (GTK_BOX (editor->main_vbox),
+ build_search_box (editor),
+ FALSE, FALSE, 0);
+ add_focus_monitor (editor, editor->search_entry);
+
+ scrolled_window = g_object_new (GTK_TYPE_SCROLLED_WINDOW,
+ "hadjustment", NULL,
+ "vadjustment", NULL,
+ "hscrollbar_policy", GTK_POLICY_AUTOMATIC,
+ "vscrollbar_policy", GTK_POLICY_AUTOMATIC,
+ "shadow_type", GTK_SHADOW_IN,
+ NULL);
+ gtk_box_pack_start (GTK_BOX (vbox), scrolled_window, TRUE, TRUE, 0);
+ gtk_widget_show (scrolled_window);
+
+ node = ephy_bookmarks_get_bookmarks (editor->bookmarks);
+ editor->bookmarks_filter = ephy_node_filter_new ();
+
+ /* Bookmarks View */
+ bm_view = ephy_node_view_new (node, editor->bookmarks_filter);
+ add_focus_monitor (editor, bm_view);
+ title_col_id = ephy_node_view_add_column
+ (EPHY_NODE_VIEW (bm_view), _("Title"),
+ G_TYPE_STRING, EPHY_NODE_BMK_PROP_TITLE,
+ EPHY_NODE_VIEW_EDITABLE |
+ EPHY_NODE_VIEW_SEARCHABLE |
+ EPHY_NODE_VIEW_SORTABLE |
+ EPHY_NODE_VIEW_ELLIPSIZED,
+ provide_favicon, &(editor->title_col));
+ gtk_tree_view_column_set_min_width (editor->title_col, 300);
+ gtk_tree_view_column_set_resizable (editor->title_col, TRUE);
+
+ url_col_id = ephy_node_view_add_column_full
+ (EPHY_NODE_VIEW (bm_view), _("Address"),
+ G_TYPE_STRING, EPHY_NODE_BMK_PROP_LOCATION,
+ EPHY_NODE_VIEW_SORTABLE |
+ EPHY_NODE_VIEW_ELLIPSIZED,
+ decode_bookmark_uri, NULL,
+ NULL, &(editor->address_col));
+ gtk_tree_view_column_set_min_width (editor->address_col, 300);
+ gtk_tree_view_column_set_resizable (editor->address_col, TRUE);
+
+ ephy_node_view_enable_drag_source (EPHY_NODE_VIEW (bm_view),
+ bmk_drag_types,
+ G_N_ELEMENTS (bmk_drag_types),
+ url_col_id, title_col_id);
+ ephy_node_view_set_sort (EPHY_NODE_VIEW (bm_view), G_TYPE_STRING,
+ EPHY_NODE_BMK_PROP_TITLE, GTK_SORT_ASCENDING);
+ gtk_container_add (GTK_CONTAINER (scrolled_window), bm_view);
+ gtk_widget_show (bm_view);
+ editor->bm_view = bm_view;
+
+ action = gtk_action_group_get_action (action_group, "ViewTitle");
+ g_settings_bind (EPHY_SETTINGS_STATE,
+ EPHY_PREFS_STATE_BOOKMARKS_VIEW_TITLE,
+ action, "active",
+ G_SETTINGS_BIND_DEFAULT);
+ g_settings_bind (EPHY_SETTINGS_STATE,
+ EPHY_PREFS_STATE_BOOKMARKS_VIEW_TITLE,
+ editor->title_col, "visible",
+ G_SETTINGS_BIND_DEFAULT);
+
+ action = gtk_action_group_get_action (action_group, "ViewAddress");
+ g_settings_bind (EPHY_SETTINGS_STATE,
+ EPHY_PREFS_STATE_BOOKMARKS_VIEW_ADDRESS,
+ action, "active",
+ G_SETTINGS_BIND_DEFAULT);
+ g_settings_bind (EPHY_SETTINGS_STATE,
+ EPHY_PREFS_STATE_BOOKMARKS_VIEW_ADDRESS,
+ editor->address_col, "visible",
+ G_SETTINGS_BIND_DEFAULT);
+
+ g_signal_connect (G_OBJECT (bm_view),
+ "key_press_event",
+ G_CALLBACK (key_pressed_cb),
+ editor);
+ g_signal_connect (G_OBJECT (bm_view),
+ "node_activated",
+ G_CALLBACK (ephy_bookmarks_editor_node_activated_cb),
+ editor);
+ g_signal_connect (G_OBJECT (bm_view),
+ "node_middle_clicked",
+ G_CALLBACK (ephy_bookmarks_editor_node_middle_clicked_cb),
+ editor);
+ g_signal_connect (G_OBJECT (bm_view),
+ "popup_menu",
+ G_CALLBACK (ephy_bookmarks_editor_show_popup_cb),
+ editor);
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (bm_view));
+ g_signal_connect (G_OBJECT (selection),
+ "changed",
+ G_CALLBACK (view_selection_changed_cb),
+ editor);
+
+ ephy_initial_state_add_window (GTK_WIDGET (editor),
+ "bookmarks_editor",
+ 450, 400, FALSE,
+ EPHY_INITIAL_STATE_WINDOW_SAVE_SIZE | EPHY_INITIAL_STATE_WINDOW_SAVE_POSITION);
+ ephy_initial_state_add_paned (GTK_WIDGET (hpaned),
+ "bookmarks_paned",
+ 130);
+
+ /* Lockdown settings */
+ action = gtk_action_group_get_action (action_group, "Export");
+ gtk_action_set_sensitive (action,
+ g_settings_get_boolean
+ (EPHY_SETTINGS_LOCKDOWN,
+ EPHY_PREFS_LOCKDOWN_SAVE_TO_DISK) == FALSE);
+}
+
+void
+ephy_bookmarks_editor_set_parent (EphyBookmarksEditor *ebe,
+ GtkWidget *window)
+{
+ GtkWidget **w;
+ if (ebe->window) {
+ w = &ebe->window;
+ g_object_remove_weak_pointer
+ (G_OBJECT (ebe->window),
+ (gpointer *)w);
+ }
+
+ ebe->window = window;
+ w = &ebe->window;
+
+ g_object_add_weak_pointer
+ (G_OBJECT (ebe->window),
+ (gpointer *)w);
+}
+
+GtkWidget *
+ephy_bookmarks_editor_new (EphyBookmarks *bookmarks)
+{
+ EphyBookmarksEditor *editor;
+
+ g_assert (bookmarks != NULL);
+
+ editor = EPHY_BOOKMARKS_EDITOR (g_object_new
+ (EPHY_TYPE_BOOKMARKS_EDITOR,
+ "bookmarks", bookmarks,
+ NULL));
+
+ ephy_bookmarks_editor_construct (editor);
+
+ return GTK_WIDGET (editor);
+}
+
+static void
+ephy_bookmarks_editor_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ EphyBookmarksEditor *editor = EPHY_BOOKMARKS_EDITOR (object);
+
+ switch (prop_id) {
+ case PROP_BOOKMARKS:
+ editor->bookmarks = g_value_get_object (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+ephy_bookmarks_editor_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ EphyBookmarksEditor *editor = EPHY_BOOKMARKS_EDITOR (object);
+
+ switch (prop_id) {
+ case PROP_BOOKMARKS:
+ g_value_set_object (value, editor->bookmarks);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+ephy_bookmarks_editor_init (EphyBookmarksEditor *editor)
+{
+}
diff --git a/src/bookmarks/ephy-bookmarks-editor.h b/src/bookmarks/ephy-bookmarks-editor.h
new file mode 100644
index 000000000..f83fdf8f5
--- /dev/null
+++ b/src/bookmarks/ephy-bookmarks-editor.h
@@ -0,0 +1,39 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright © 2002 Jorn Baayen <jorn@nl.linux.org>
+ *
+ * This file is part of Epiphany.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+
+#include "ephy-node-view.h"
+#include "ephy-bookmarks.h"
+
+G_BEGIN_DECLS
+
+#define EPHY_TYPE_BOOKMARKS_EDITOR (ephy_bookmarks_editor_get_type ())
+
+G_DECLARE_FINAL_TYPE (EphyBookmarksEditor, ephy_bookmarks_editor, EPHY, BOOKMARKS_EDITOR, GtkWindow)
+
+GtkWidget *ephy_bookmarks_editor_new (EphyBookmarks *bookmarks);
+
+void ephy_bookmarks_editor_set_parent (EphyBookmarksEditor *ebe,
+ GtkWidget *window);
+
+G_END_DECLS
diff --git a/src/bookmarks/ephy-bookmarks-export.c b/src/bookmarks/ephy-bookmarks-export.c
new file mode 100644
index 000000000..1d47fcd55
--- /dev/null
+++ b/src/bookmarks/ephy-bookmarks-export.c
@@ -0,0 +1,494 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright © 2003 Marco Pesenti Gritti
+ * Copyright © 2003 Christian Persch
+ *
+ * This file is part of Epiphany.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "ephy-bookmarks-export.h"
+#include "ephy-node-common.h"
+#include "ephy-file-helpers.h"
+#include "ephy-string.h"
+#include "ephy-debug.h"
+
+#include <libxml/globals.h>
+#include <libxml/tree.h>
+#include <libxml/xmlwriter.h>
+#include <libxslt/xslt.h>
+#include <libxslt/transform.h>
+#include <libxslt/xsltutils.h>
+
+static inline xmlChar *
+sanitise_string (const xmlChar *string)
+{
+ xmlChar *copy, *p;
+
+ if (!string)
+ return xmlStrdup ((const xmlChar *)"");
+
+ /* http://www.w3.org/TR/REC-xml/#sec-well-formed :
+ Character Range
+ [2] Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] |
+ [#xE000-#xFFFD] | [#x10000-#x10FFFF]
+ any Unicode character, excluding the surrogate blocks, FFFE, and FFFF.
+ */
+
+ copy = xmlStrdup (string);
+ for (p = copy; *p; p++) {
+ xmlChar c = *p;
+ if (G_UNLIKELY (c < 0x20 && c != 0xd && c != 0xa && c != 0x9)) {
+ *p = 0x20;
+ }
+ }
+
+ return copy;
+}
+
+static int
+write_topics_list (EphyNode *topics,
+ EphyNode *bmk,
+ xmlTextWriterPtr writer)
+{
+ GPtrArray *children;
+ GList *keywords = NULL, *l;
+ guint i;
+ int ret = 0;
+
+ children = ephy_node_get_children (topics);
+ for (i = 0; i < children->len; i++) {
+ EphyNode *kid;
+ int priority;
+
+ kid = g_ptr_array_index (children, i);
+
+ priority = ephy_node_get_property_int (kid, EPHY_NODE_KEYWORD_PROP_PRIORITY);
+ if (priority == -1) priority = EPHY_NODE_NORMAL_PRIORITY;
+
+ if (priority == EPHY_NODE_NORMAL_PRIORITY &&
+ ephy_node_has_child (kid, bmk)) {
+ keywords = g_list_prepend (keywords, kid);
+ }
+ }
+
+ for (l = keywords; l != NULL; l = l->next) {
+ EphyNode *node = l->data;
+ const char *name;
+ xmlChar *safeName;
+
+ name = ephy_node_get_property_string
+ (node, EPHY_NODE_KEYWORD_PROP_NAME);
+ safeName = sanitise_string ((const xmlChar *)name);
+
+ ret = xmlTextWriterWriteElementNS
+ (writer,
+ (xmlChar *)"dc",
+ (xmlChar *)"subject",
+ NULL,
+ safeName);
+ xmlFree (safeName);
+
+ if (ret < 0) break;
+ }
+
+ g_list_free (keywords);
+
+ return ret >= 0 ? 0 : -1;
+}
+
+static int
+write_rdf (EphyBookmarks *bookmarks,
+ GFile *file,
+ xmlTextWriterPtr writer)
+{
+ EphyNode *bmks, *topics, *smart_bmks;
+ GPtrArray *children;
+ char *file_uri;
+ guint i;
+ gint ret;
+ xmlChar *safeString;
+#ifdef ENABLE_ZEROCONF
+ EphyNode *local;
+#endif
+
+ START_PROFILER ("Writing RDF")
+
+ ret = xmlTextWriterStartDocument (writer, "1.0", NULL, NULL);
+ if (ret < 0) goto out;
+
+ ret = xmlTextWriterStartElementNS
+ (writer,
+ (xmlChar *)"rdf",
+ (xmlChar *)"RDF",
+ (xmlChar *)"http://www.w3.org/1999/02/22-rdf-syntax-ns#");
+ if (ret < 0) goto out;
+
+ ret = xmlTextWriterWriteAttribute
+ (writer,
+ (xmlChar *)"xmlns",
+ (xmlChar *)"http://purl.org/rss/1.0/");
+ if (ret < 0) goto out;
+
+ ret = xmlTextWriterWriteAttributeNS
+ (writer,
+ (xmlChar *)"xmlns",
+ (xmlChar *)"dc",
+ NULL,
+ (xmlChar *)"http://purl.org/dc/elements/1.1/");
+ if (ret < 0) goto out;
+
+ ret = xmlTextWriterWriteAttributeNS
+ (writer,
+ (xmlChar *)"xmlns",
+ (xmlChar *)"ephy",
+ NULL,
+ (xmlChar *)"http://gnome.org/ns/epiphany#");
+ if (ret < 0) goto out;
+
+ ret = xmlTextWriterStartElement (writer, (xmlChar *)"channel");
+ if (ret < 0) goto out;
+
+ /* FIXME: sanitise file_uri? */
+ file_uri = g_file_get_uri (file);
+ safeString = sanitise_string ((const xmlChar *)file_uri);
+ g_free (file_uri);
+
+ ret = xmlTextWriterWriteAttributeNS
+ (writer,
+ (xmlChar *)"rdf",
+ (xmlChar *)"about",
+ NULL,
+ safeString);
+ xmlFree (safeString);
+ if (ret < 0) goto out;
+
+ ret = xmlTextWriterWriteElement
+ (writer,
+ (xmlChar *)"title",
+ (xmlChar *)"Web bookmarks");
+ if (ret < 0) goto out;
+
+ ret = xmlTextWriterWriteElement
+ (writer,
+ (xmlChar *)"link",
+ (xmlChar *)"https://wiki.gnome.org/Apps/Web");
+ if (ret < 0) goto out;
+
+ ret = xmlTextWriterStartElement (writer, (xmlChar *)"items");
+ if (ret < 0) goto out;
+
+ ret = xmlTextWriterStartElementNS
+ (writer,
+ (xmlChar *)"rdf",
+ (xmlChar *)"Seq",
+ NULL);
+ if (ret < 0) goto out;
+
+ bmks = ephy_bookmarks_get_bookmarks (bookmarks);
+ topics = ephy_bookmarks_get_keywords (bookmarks);
+ smart_bmks = ephy_bookmarks_get_smart_bookmarks (bookmarks);
+#ifdef ENABLE_ZEROCONF
+ local = ephy_bookmarks_get_local (bookmarks);
+#endif
+
+ children = ephy_node_get_children (bmks);
+ for (i = 0; i < children->len; i++) {
+ EphyNode *kid;
+ const char *url;
+ char *link = NULL;
+ gboolean smart_url;
+ xmlChar *safeLink;
+
+ kid = g_ptr_array_index (children, i);
+
+#ifdef ENABLE_ZEROCONF
+ /* Don't export the local bookmarks */
+ if (ephy_node_has_child (local, kid)) continue;
+#endif
+
+ smart_url = ephy_node_has_child (smart_bmks, kid);
+ url = ephy_node_get_property_string
+ (kid, EPHY_NODE_BMK_PROP_LOCATION);
+ if (smart_url && url) {
+ char *scheme;
+ char *host_name;
+
+ scheme = g_uri_parse_scheme (url);
+ host_name = ephy_string_get_host_name (url);
+ link = g_strconcat (scheme,
+ "://",
+ host_name,
+ NULL);
+
+ g_free (scheme);
+ g_free (host_name);
+ }
+
+ safeLink = sanitise_string (link ? (const xmlChar *)link : (const xmlChar *)url);
+ g_free (link);
+
+ ret = xmlTextWriterStartElementNS
+ (writer,
+ (xmlChar *)"rdf",
+ (xmlChar *)"li",
+ NULL);
+ if (ret < 0) break;
+
+ ret = xmlTextWriterWriteAttributeNS
+ (writer,
+ (xmlChar *)"rdf",
+ (xmlChar *)"resource",
+ NULL,
+ safeLink);
+ xmlFree (safeLink);
+ if (ret < 0) break;
+
+ ret = xmlTextWriterEndElement (writer); /* rdf:li */
+ if (ret < 0) break;
+ }
+ if (ret < 0) goto out;
+
+ ret = xmlTextWriterEndElement (writer); /* rdf:Seq */
+ if (ret < 0) goto out;
+
+ ret = xmlTextWriterEndElement (writer); /* items */
+ if (ret < 0) goto out;
+
+ ret = xmlTextWriterEndElement (writer); /* channel */
+ if (ret < 0) goto out;
+
+ children = ephy_node_get_children (bmks);
+ for (i = 0; i < children->len; i++) {
+ EphyNode *kid;
+ const char *url, *title;
+ char *link = NULL;
+ gboolean smart_url;
+ xmlChar *safeLink, *safeTitle;
+
+ kid = g_ptr_array_index (children, i);
+
+#ifdef ENABLE_ZEROCONF
+ /* Don't export the local bookmarks */
+ if (ephy_node_has_child (local, kid)) continue;
+#endif
+
+ smart_url = ephy_node_has_child (smart_bmks, kid);
+ url = ephy_node_get_property_string
+ (kid, EPHY_NODE_BMK_PROP_LOCATION);
+ title = ephy_node_get_property_string
+ (kid, EPHY_NODE_BMK_PROP_TITLE);
+
+ if (smart_url && url) {
+ char *scheme;
+ char *host_name;
+
+ scheme = g_uri_parse_scheme (url);
+ host_name = ephy_string_get_host_name (url);
+
+ link = g_strconcat (scheme,
+ "://",
+ host_name,
+ NULL);
+ g_free (scheme);
+ g_free (host_name);
+ }
+
+ if (link == NULL) {
+ link = g_strdup (url);
+ }
+
+ ret = xmlTextWriterStartElement (writer, (xmlChar *)"item");
+ if (ret < 0) break;
+
+ safeLink = sanitise_string ((const xmlChar *)link);
+ g_free (link);
+
+ ret = xmlTextWriterWriteAttributeNS
+ (writer,
+ (xmlChar *)"rdf",
+ (xmlChar *)"about",
+ NULL,
+ safeLink);
+ if (ret < 0) {
+ xmlFree (safeLink);
+ break;
+ }
+
+ safeTitle = sanitise_string ((const xmlChar *)title);
+ ret = xmlTextWriterWriteElement
+ (writer,
+ (xmlChar *)"title",
+ safeTitle);
+ xmlFree (safeTitle);
+ if (ret < 0) break;
+
+ ret = xmlTextWriterWriteElement
+ (writer,
+ (xmlChar *)"link",
+ safeLink);
+ xmlFree (safeLink);
+ if (ret < 0) break;
+
+ if (smart_url) {
+ xmlChar *safeSmartLink;
+
+ safeSmartLink = sanitise_string ((const xmlChar *)url);
+ ret = xmlTextWriterWriteElementNS
+ (writer,
+ (xmlChar *)"ephy",
+ (xmlChar *)"smartlink",
+ NULL,
+ safeSmartLink);
+ xmlFree (safeSmartLink);
+ if (ret < 0) break;
+ }
+
+ ret = write_topics_list (topics, kid, writer);
+ if (ret < 0) break;
+
+ ret = xmlTextWriterEndElement (writer); /* item */
+ }
+ if (ret < 0) goto out;
+
+ ret = xmlTextWriterEndElement (writer); /* rdf:RDF */
+ if (ret < 0) goto out;
+
+ ret = xmlTextWriterEndDocument (writer);
+
+ out:
+ STOP_PROFILER ("Writing RDF")
+
+ return ret;
+}
+
+void
+ephy_bookmarks_export_rdf (EphyBookmarks *bookmarks,
+ const char *file_path)
+{
+ xmlTextWriterPtr writer;
+ xmlBufferPtr buf;
+ GFile *file;
+ int ret;
+
+ LOG ("Exporting as RDF to %s", file_path);
+
+ START_PROFILER ("Exporting as RDF")
+
+ buf = xmlBufferCreate ();
+ if (buf == NULL) {
+ return;
+ }
+ /* FIXME: do we want to turn on compression here? */
+ writer = xmlNewTextWriterMemory (buf, 0);
+ if (writer == NULL) {
+ xmlBufferFree (buf);
+ return;
+ }
+
+ ret = xmlTextWriterSetIndent (writer, 1);
+ if (ret < 0) goto out;
+
+ ret = xmlTextWriterSetIndentString (writer, (xmlChar *)" ");
+ if (ret < 0) goto out;
+
+ file = g_file_new_for_path (file_path);
+ ret = write_rdf (bookmarks, file, writer);
+ g_object_unref (file);
+ if (ret < 0) goto out;
+
+ xmlFreeTextWriter (writer);
+ out:
+ if (ret >= 0) {
+ if (g_file_set_contents (file_path,
+ (const char *)buf->content,
+ buf->use,
+ NULL) == FALSE) {
+ ret = -1;
+ }
+ }
+
+ xmlBufferFree (buf);
+
+ STOP_PROFILER ("Exporting as RDF")
+
+ LOG ("Exporting as RDF %s.", ret >= 0 ? "succeeded" : "FAILED");
+}
+
+void
+ephy_bookmarks_export_mozilla (EphyBookmarks *bookmarks,
+ const char *filename)
+{
+ xsltStylesheetPtr cur = NULL;
+ xmlTextWriterPtr writer;
+ xmlDocPtr doc = NULL, res;
+ char *tmp_file_path, *template;
+ GFile *tmp_file;
+ int ret = -1;
+
+ LOG ("Exporting as Mozilla to %s", filename);
+
+ template = g_build_filename (g_get_tmp_dir (),
+ "export-bookmarks-XXXXXX", NULL);
+ tmp_file_path = ephy_file_tmp_filename (template, "rdf");
+ g_free (template);
+ if (tmp_file_path == NULL) return;
+
+ writer = xmlNewTextWriterDoc (&doc, 0);
+ if (writer == NULL || doc == NULL) {
+ g_free (tmp_file_path);
+ return;
+ }
+
+ START_PROFILER ("Exporting as Mozilla");
+
+ tmp_file = g_file_new_for_path (tmp_file_path);
+ ret = write_rdf (bookmarks, tmp_file, writer);
+ g_object_unref (tmp_file);
+
+ if (ret < 0) goto out;
+
+ /* Set up libxml stuff */
+ xmlLoadExtDtdDefaultValue = 1;
+ xmlSubstituteEntitiesDefault (1);
+
+ cur = xsltParseStylesheetFile ((const xmlChar *)ephy_file ("epiphany-bookmarks-html.xsl"));
+ if (cur == NULL) goto out;
+
+ res = xsltApplyStylesheet (cur, doc, NULL);
+ if (res == NULL) {
+ xsltFreeStylesheet (cur);
+ goto out;
+ }
+
+ ret = xsltSaveResultToFilename (filename, res, cur, FALSE);
+
+ xsltFreeStylesheet (cur);
+ xmlFreeDoc (res);
+
+ /* Clean up libxslt stuff */
+ xsltCleanupGlobals ();
+
+ out:
+ xmlFreeTextWriter (writer);
+ xmlFreeDoc (doc);
+ g_free (tmp_file_path);
+
+ STOP_PROFILER ("Exporting as Mozilla")
+
+ LOG ("Exporting as Mozilla %s.", ret >= 0 ? "succeeded" : "FAILED");
+}
diff --git a/src/bookmarks/ephy-bookmarks-export.h b/src/bookmarks/ephy-bookmarks-export.h
new file mode 100644
index 000000000..119dc4407
--- /dev/null
+++ b/src/bookmarks/ephy-bookmarks-export.h
@@ -0,0 +1,33 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright © 2003 Marco Pesenti Gritti
+ *
+ * This file is part of Epiphany.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "ephy-bookmarks.h"
+
+G_BEGIN_DECLS
+
+void ephy_bookmarks_export_rdf (EphyBookmarks *bookmarks,
+ const char *filename);
+
+void ephy_bookmarks_export_mozilla (EphyBookmarks *bookmarks,
+ const char *filename);
+
+G_END_DECLS
diff --git a/src/bookmarks/ephy-bookmarks-import.c b/src/bookmarks/ephy-bookmarks-import.c
new file mode 100644
index 000000000..a196b96ea
--- /dev/null
+++ b/src/bookmarks/ephy-bookmarks-import.c
@@ -0,0 +1,853 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright © 2003, 2004 Marco Pesenti Gritti
+ * Copyright © 2003, 2004, 2005 Christian Persch
+ *
+ * This file is part of Epiphany.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+#include "ephy-bookmarks-import.h"
+
+#include "ephy-debug.h"
+#include "ephy-prefs.h"
+#include "ephy-settings.h"
+
+#include <gio/gio.h>
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <libxml/HTMLtree.h>
+#include <libxml/xmlreader.h>
+#include <string.h>
+
+/**
+ * NSItemType: netscape bookmark item type
+ */
+typedef enum {
+ NS_SITE,
+ NS_NOTES,
+ NS_FOLDER,
+ NS_FOLDER_END,
+ NS_SEPARATOR,
+ NS_UNKNOWN
+} NSItemType;
+
+static EphyNode *
+bookmark_add (EphyBookmarks *bookmarks,
+ const char *title,
+ const char *address)
+{
+ if (!ephy_bookmarks_find_bookmark (bookmarks, address)) {
+ return ephy_bookmarks_add (bookmarks, title, address);
+ } else {
+ return NULL;
+ }
+}
+
+gboolean
+ephy_bookmarks_import (EphyBookmarks *bookmarks,
+ const char *filename)
+{
+ const char *type;
+ char *basename;
+ gboolean success = FALSE;
+ GFile *file;
+ GFileInfo *file_info;
+
+ if (g_settings_get_boolean (EPHY_SETTINGS_LOCKDOWN,
+ EPHY_PREFS_LOCKDOWN_BOOKMARK_EDITING))
+ return FALSE;
+
+ g_return_val_if_fail (filename != NULL, FALSE);
+
+ file = g_file_new_for_path (filename);
+ file_info = g_file_query_info (file,
+ G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
+ 0, NULL, NULL);
+ type = g_file_info_get_content_type (file_info);
+
+ g_debug ("Importing bookmarks of type %s", type ? type : "(null)");
+
+ if (type != NULL && (strcmp (type, "application/rdf+xml") == 0 ||
+ strcmp (type, "text/rdf") == 0)) {
+ success = ephy_bookmarks_import_rdf (bookmarks, filename);
+ } else if ((type != NULL && strcmp (type, "application/x-xbel") == 0) ||
+ strstr (filename, GALEON_BOOKMARKS_DIR) != NULL ||
+ strstr (filename, KDE_BOOKMARKS_DIR) != NULL) {
+ success = ephy_bookmarks_import_xbel (bookmarks, filename);
+ } else if ((type != NULL && strcmp (type, "application/x-mozilla-bookmarks") == 0) ||
+ (type != NULL && strcmp (type, "text/html") == 0) ||
+ strstr (filename, MOZILLA_BOOKMARKS_DIR) != NULL ||
+ strstr (filename, FIREFOX_BOOKMARKS_DIR_0) != NULL ||
+ strstr (filename, FIREFOX_BOOKMARKS_DIR_1) != NULL ||
+ strstr (filename, FIREFOX_BOOKMARKS_DIR_2) != NULL) {
+ success = ephy_bookmarks_import_mozilla (bookmarks, filename);
+ } else if (type == NULL) {
+ basename = g_file_get_basename (file);
+
+ if (g_str_has_suffix (basename, ".rdf")) {
+ success = ephy_bookmarks_import_rdf (bookmarks, filename);
+ } else if (g_str_has_suffix (basename, ".xbel")) {
+ success = ephy_bookmarks_import_xbel (bookmarks, filename);
+ } else if (g_str_has_suffix (basename, ".html")) {
+ success = ephy_bookmarks_import_mozilla (bookmarks, filename);
+ } else {
+ /* else FIXME: put up some UI to warn user about unrecognised format? */
+ g_warning ("Couldn't determine the type of the bookmarks file %s!\n", filename);
+ }
+
+ g_free (basename);
+ }
+
+ g_object_unref (file_info);
+ g_object_unref (file);
+
+ return success;
+}
+
+/* XBEL import */
+
+typedef enum {
+ STATE_START,
+ STATE_STOP,
+ STATE_XBEL,
+ STATE_FOLDER,
+ STATE_BOOKMARK,
+ STATE_TITLE,
+ STATE_DESC,
+ STATE_INFO,
+ STATE_METADATA,
+ STATE_SMARTURL
+} EphyXBELImporterState;
+
+static int
+xbel_parse_bookmark (EphyBookmarks *eb, xmlTextReaderPtr reader, EphyNode **ret_node)
+{
+ EphyXBELImporterState state = STATE_BOOKMARK;
+ EphyNode *node;
+ xmlChar *title = NULL;
+ xmlChar *address = NULL;
+ int ret = 1;
+
+ while (ret == 1) {
+ const xmlChar *tag;
+ xmlReaderTypes type;
+
+ tag = xmlTextReaderConstName (reader);
+ g_return_val_if_fail (tag != NULL, ret);
+
+ type = xmlTextReaderNodeType (reader);
+
+ if (xmlStrEqual (tag, (xmlChar *)"#text")) {
+ if (state == STATE_TITLE && title == NULL) {
+ title = xmlTextReaderValue (reader);
+ } else if (state == STATE_SMARTURL) {
+ xmlFree (address);
+ address = xmlTextReaderValue (reader);
+ } else {
+ /* eat it */
+ }
+ } else if (xmlStrEqual (tag, (xmlChar *)"bookmark")) {
+ if (type == XML_READER_TYPE_ELEMENT && state == STATE_BOOKMARK && address == NULL) {
+ address = xmlTextReaderGetAttribute (reader, (xmlChar *)"href");
+ } else if (type == XML_READER_TYPE_END_ELEMENT && state == STATE_BOOKMARK) {
+ /* we're done */
+
+ break;
+ }
+ } else if (xmlStrEqual (tag, (xmlChar *)"title")) {
+ if (type == XML_READER_TYPE_ELEMENT && state == STATE_BOOKMARK && title == NULL) {
+ state = STATE_TITLE;
+ } else if (type == XML_READER_TYPE_END_ELEMENT && state == STATE_TITLE) {
+ state = STATE_BOOKMARK;
+ }
+ } else if (xmlStrEqual (tag, (xmlChar *)"desc")) {
+ if (type == XML_READER_TYPE_ELEMENT && state == STATE_BOOKMARK) {
+ state = STATE_DESC;
+ } else if (type == XML_READER_TYPE_END_ELEMENT && state == STATE_DESC) {
+ state = STATE_BOOKMARK;
+ }
+ } else if (xmlStrEqual (tag, (xmlChar *)"info")) {
+ if (type == XML_READER_TYPE_ELEMENT && state == STATE_BOOKMARK) {
+ state = STATE_INFO;
+ } else if (type == XML_READER_TYPE_END_ELEMENT && state == STATE_INFO) {
+ state = STATE_BOOKMARK;
+ }
+ } else if (xmlStrEqual (tag, (xmlChar *)"metadata")) {
+ if (type == XML_READER_TYPE_ELEMENT && state == STATE_INFO) {
+ state = STATE_METADATA;
+ } else if (type == XML_READER_TYPE_END_ELEMENT && state == STATE_METADATA) {
+ state = STATE_INFO;
+ }
+ } else if (xmlStrEqual (tag, (xmlChar *)"smarturl")) {
+ if (type == XML_READER_TYPE_ELEMENT && state == STATE_METADATA) {
+ state = STATE_SMARTURL;
+ } else if (type == XML_READER_TYPE_END_ELEMENT && state == STATE_SMARTURL) {
+ state = STATE_METADATA;
+ }
+ }
+
+ /* next one, please */
+ ret = xmlTextReaderRead (reader);
+ }
+
+ if (address == NULL) {
+ return ret;
+ }
+
+ if (title == NULL) {
+ title = xmlStrdup ((xmlChar *)_("Untitled"));
+ }
+
+ node = bookmark_add (eb, (const char *)title, (const char *)address);
+ if (node == NULL) {
+ /* probably a duplicate */
+ node = ephy_bookmarks_find_bookmark (eb, (const char *)address);
+ }
+
+ xmlFree (title);
+ xmlFree (address);
+
+ *ret_node = node;
+
+ return ret;
+}
+
+static int
+xbel_parse_folder (EphyBookmarks *eb, xmlTextReaderPtr reader, GList *folders)
+{
+ EphyXBELImporterState state = STATE_FOLDER;
+ char *folder = NULL;
+ int ret;
+
+ ret = xmlTextReaderRead (reader);
+
+ while (ret == 1) {
+ const xmlChar *tag;
+ xmlReaderTypes type;
+
+ tag = xmlTextReaderConstName (reader);
+ type = xmlTextReaderNodeType (reader);
+
+ if (tag == NULL) {
+ /* shouldn't happen but does anyway :) */
+ } else if (xmlStrEqual (tag, (xmlChar *)"#text")) {
+ if (state == STATE_TITLE && folder == NULL) {
+ folder = (char *)xmlTextReaderValue (reader);
+
+ folders = g_list_prepend (folders, folder);
+ } else {
+ /* eat it */
+ }
+ } else if (xmlStrEqual (tag, (xmlChar *)"bookmark") && type == 1 && state == STATE_FOLDER) {
+ EphyNode *node = NULL, *keyword;
+ GList *l;
+
+ ret = xbel_parse_bookmark (eb, reader, &node);
+
+ for (l = folders; l != NULL; l = l->next) {
+ const char *title;
+
+ title = l->data ? (char *)l->data : "";
+
+ keyword = ephy_bookmarks_find_keyword (eb, title, FALSE);
+
+ if (keyword == NULL && title[0] != '\0') {
+ keyword = ephy_bookmarks_add_keyword (eb, title);
+ }
+
+ if (node != NULL && keyword != NULL) {
+ ephy_bookmarks_set_keyword (eb, keyword, node);
+ }
+ }
+
+ if (ret != 1) break;
+ } else if ((xmlStrEqual (tag, (xmlChar *)"folder"))
+ && state == STATE_FOLDER) {
+ if (type == XML_READER_TYPE_ELEMENT) {
+ ret = xbel_parse_folder (eb, reader, folders);
+
+ if (ret != 1) break;
+ } else if (type == XML_READER_TYPE_END_ELEMENT) {
+ /* we're done */
+
+ break;
+ }
+ } else if (xmlStrEqual (tag, (xmlChar *)"title")) {
+ if (type == XML_READER_TYPE_ELEMENT && state == STATE_FOLDER) {
+ state = STATE_TITLE;
+ } else if (type == XML_READER_TYPE_END_ELEMENT && state == STATE_TITLE) {
+ state = STATE_FOLDER;
+ }
+ } else if (xmlStrEqual (tag, (xmlChar *)"info")) {
+ if (type == XML_READER_TYPE_ELEMENT && state == STATE_FOLDER) {
+ state = STATE_INFO;
+ } else if (type == XML_READER_TYPE_END_ELEMENT && state == STATE_INFO) {
+ state = STATE_FOLDER;
+ }
+ } else if (xmlStrEqual (tag, (xmlChar *)"desc")) {
+ if (type == XML_READER_TYPE_ELEMENT && state == STATE_FOLDER) {
+ state = STATE_DESC;
+ } else if (type == XML_READER_TYPE_END_ELEMENT && state == STATE_DESC) {
+ state = STATE_FOLDER;
+ }
+ } else {
+ /* eat it */
+ }
+
+ /* next one, please */
+ ret = xmlTextReaderRead (reader);
+ }
+
+ if (folder) {
+ folders = g_list_remove (folders, folder);
+ g_free (folder);
+ }
+
+ return ret;
+}
+
+static int
+xbel_parse_xbel (EphyBookmarks *eb, xmlTextReaderPtr reader)
+{
+ EphyXBELImporterState state = STATE_XBEL;
+ int ret;
+
+ ret = xmlTextReaderRead (reader);
+
+ while (ret == 1 && state != STATE_STOP) {
+ const xmlChar *tag;
+ xmlReaderTypes type;
+
+ tag = xmlTextReaderConstName (reader);
+ type = xmlTextReaderNodeType (reader);
+
+ if (tag == NULL) {
+ /* shouldn't happen but does anyway :( */
+ } else if (xmlStrEqual (tag, (xmlChar *)"bookmark") && type == XML_READER_TYPE_ELEMENT
+ && state == STATE_XBEL) {
+ EphyNode *node = NULL;
+
+ /* this will eat the </bookmark> too */
+ ret = xbel_parse_bookmark (eb, reader, &node);
+
+ if (ret != 1) break;
+ } else if (xmlStrEqual (tag, (xmlChar *)"folder") && type == XML_READER_TYPE_ELEMENT
+ && state == STATE_XBEL) {
+ /* this will eat the </folder> too */
+ ret = xbel_parse_folder (eb, reader, NULL);
+
+ if (ret != 1) break;
+ } else if ((xmlStrEqual (tag, (xmlChar *)"xbel")) && type == XML_READER_TYPE_ELEMENT
+ && state == STATE_START) {
+ state = STATE_XBEL;
+ } else if ((xmlStrEqual (tag, (xmlChar *)"xbel")) && type == XML_READER_TYPE_END_ELEMENT
+ && state == STATE_XBEL) {
+ state = STATE_STOP;
+ } else if (xmlStrEqual (tag, (xmlChar *)"title")) {
+ if (type == XML_READER_TYPE_ELEMENT && state == STATE_XBEL) {
+ state = STATE_TITLE;
+ } else if (type == XML_READER_TYPE_END_ELEMENT && state == STATE_TITLE) {
+ state = STATE_XBEL;
+ }
+ } else if (xmlStrEqual (tag, (xmlChar *)"info")) {
+ if (type == XML_READER_TYPE_ELEMENT && state == STATE_XBEL) {
+ state = STATE_INFO;
+ } else if (type == XML_READER_TYPE_END_ELEMENT && state == STATE_INFO) {
+ state = STATE_XBEL;
+ }
+ } else if (xmlStrEqual (tag, (xmlChar *)"desc")) {
+ if (type == XML_READER_TYPE_ELEMENT && state == STATE_XBEL) {
+ state = STATE_DESC;
+ } else if (type == XML_READER_TYPE_END_ELEMENT && state == STATE_DESC) {
+ state = STATE_XBEL;
+ }
+ }
+
+ /* next one, please */
+ ret = xmlTextReaderRead (reader);
+ }
+
+ return ret;
+}
+
+/* Mozilla/Netscape import */
+
+static gchar *
+gul_general_read_line_from_file (FILE *f)
+{
+ gchar *line = g_strdup ("");
+ gchar *t;
+ gchar *buf = g_new0 (gchar, 256);
+ while (!(strchr (buf, '\n') || feof (f))) {
+ if (fgets (buf, 256, f)) {
+ t = line;
+ line = g_strconcat (line, buf, NULL);
+ g_free (t);
+ }
+ }
+ g_free (buf);
+ return line;
+}
+
+/**
+ * Parses a line of a mozilla/netscape bookmark file. File must be open.
+ */
+/* this has been tested fairly well */
+static NSItemType
+ns_get_bookmark_item (FILE *f, GString *name, GString *url)
+{
+ char *line = NULL;
+ GRegex *regex;
+ GMatchInfo *match_info;
+ int ret = NS_UNKNOWN;
+ char *match_url = NULL;
+ char *match_name = NULL;
+
+ line = gul_general_read_line_from_file (f);
+
+ /*
+ * Regex parsing of the html file:
+ * 1. check if it's a bookmark, or a folder, or the end of a folder,
+ * note that only ONE of this things is going to happen
+ * 2. assign to the GStrings
+ * 3. return the ret val to tell our caller what we found, by default
+ * we don't know (NS_UNKWOWN).
+ */
+
+ /* check if it's a bookmark */
+ regex = g_regex_new
+ ("<a href=\"(?P<url>[^\"]*).*?>\\s*(?P<name>.*?)\\s*</a>",
+ G_REGEX_CASELESS, G_REGEX_MATCH_NOTEMPTY, NULL);
+ g_regex_match (regex, line, 0, &match_info);
+
+ if (g_match_info_matches (match_info)) {
+ match_url = g_match_info_fetch_named (match_info, "url");
+ match_name = g_match_info_fetch_named (match_info, "name");
+ ret = NS_SITE;
+ goto end;
+ }
+ g_match_info_free (match_info);
+ g_regex_unref (regex);
+
+ /* check if it's a folder start */
+ regex = g_regex_new ("<h3.*>(?P<name>\\w.*)</h3>",
+ G_REGEX_CASELESS, G_REGEX_MATCH_NOTEMPTY, NULL);
+
+ g_regex_match (regex, line, 0, &match_info);
+ if (g_match_info_matches (match_info)) {
+ match_name = g_match_info_fetch_named (match_info, "name");
+ ret = NS_FOLDER;
+ goto end;
+ }
+ g_match_info_free (match_info);
+ g_regex_unref (regex);
+
+ /* check if it's a folder end */
+ regex = g_regex_new ("</dl>",
+ G_REGEX_CASELESS, G_REGEX_MATCH_NOTEMPTY, NULL);
+
+ g_regex_match (regex, line, 0, &match_info);
+ if (g_match_info_matches (match_info)) {
+ ret = NS_FOLDER_END;
+ goto end;
+ }
+
+ /* now let's use the collected stuff */
+ end:
+ /* Due to the goto we'll always have an unfreed @match_info and
+ * @regex. Note that this two free/unrefs correspond to the last
+ * if() block too.
+ */
+ g_match_info_free (match_info);
+ g_regex_unref (regex);
+
+ if (match_name) {
+ g_string_assign (name, match_name);
+ g_free (match_name);
+ }
+
+ if (match_url) {
+ g_string_assign (url, match_url);
+ g_free (match_url);
+ }
+
+ g_free (line);
+ return ret;
+}
+
+/*
+ * This function replaces some weird elements
+ * like &amp; &le;, etc..
+ * More info : http://www.w3.org/TR/html4/charset.html#h-5.3.2
+ * NOTE : We don't support &#D or &#xH.
+ * Patch courtesy of Almer S. Tigelaar <almer1@dds.nl>
+ *
+ * NOTE: The returned string must be freed.
+ */
+static char *
+ns_parse_bookmark_item (GString *string)
+{
+ char *iterator, *temp;
+ int cnt = 0;
+ GString *result = g_string_new (NULL);
+
+ g_return_val_if_fail (string != NULL, NULL);
+ g_return_val_if_fail (string->str != NULL, NULL);
+
+ iterator = string->str;
+
+ for (cnt = 0, iterator = string->str;
+ cnt <= (int)(strlen (string->str));
+ cnt++, iterator++) {
+ if (*iterator == '&') {
+ int jump = 0;
+ int i;
+
+ if (g_ascii_strncasecmp (iterator, "&amp;", 5) == 0) {
+ g_string_append_c (result, '&');
+ jump = 5;
+ } else if (g_ascii_strncasecmp (iterator, "&lt;", 4) == 0) {
+ g_string_append_c (result, '<');
+ jump = 4;
+ } else if (g_ascii_strncasecmp (iterator, "&gt;", 4) == 0) {
+ g_string_append_c (result, '>');
+ jump = 4;
+ } else if (g_ascii_strncasecmp (iterator, "&quot;", 6) == 0) {
+ g_string_append_c (result, '\"');
+ jump = 6;
+ } else {
+ /* It must be some numeric thing now */
+
+ iterator++;
+
+ if (iterator && *iterator == '#') {
+ int val;
+ char *num, *tmp;
+
+ iterator++;
+
+ val = atoi (iterator);
+
+ tmp = g_strdup_printf ("%d", val);
+ jump = strlen (tmp);
+ g_free (tmp);
+
+ num = g_strdup_printf ("%c", (char)val);
+ g_string_append (result, num);
+ g_free (num);
+ }
+ }
+
+ for (i = jump - 1; i > 0; i--) {
+ iterator++;
+ if (*iterator == '\0')
+ break;
+ }
+ } else {
+ g_string_append_c (result, *iterator);
+ }
+ }
+ temp = result->str;
+ g_string_free (result, FALSE);
+ return temp;
+}
+
+gboolean
+ephy_bookmarks_import_mozilla (EphyBookmarks *bookmarks,
+ const char *filename)
+{
+ FILE *bf; /* bookmark file */
+ GString *name, *url;
+ char *parsedname;
+ GList *folders = NULL;
+ gboolean retval = TRUE;
+
+ if (g_settings_get_boolean (EPHY_SETTINGS_LOCKDOWN,
+ EPHY_PREFS_LOCKDOWN_BOOKMARK_EDITING))
+ return FALSE;
+
+ if (!(bf = fopen (filename, "r"))) {
+ g_warning ("Failed to open file: %s\n", filename);
+ return FALSE;
+ }
+
+ name = g_string_new (NULL);
+ url = g_string_new (NULL);
+
+ while (!feof (bf)) {
+ EphyNode *node;
+ NSItemType t;
+ t = ns_get_bookmark_item (bf, name, url);
+ switch (t) {
+ case NS_FOLDER:
+ folders = g_list_append (folders, ns_parse_bookmark_item (name));
+ break;
+ case NS_FOLDER_END:
+ if (folders) {
+ GList *last = g_list_last (folders);
+
+ /* remove last entry */
+ g_free (last->data);
+ folders = g_list_delete_link (folders, last);
+ }
+ break;
+ case NS_SITE:
+ parsedname = ns_parse_bookmark_item (name);
+
+ node = bookmark_add (bookmarks, parsedname, url->str);
+
+ if (node == NULL) {
+ node = ephy_bookmarks_find_bookmark (bookmarks, url->str);
+ if (node == NULL) {
+ g_warning ("%s: `node' is NULL", G_STRFUNC);
+ retval = FALSE;
+ goto out;
+ }
+ }
+
+ if (folders != NULL) {
+ EphyNode *keyword;
+ GList *l;
+
+ for (l = folders; l != NULL; l = l->next) {
+ keyword = ephy_bookmarks_find_keyword (bookmarks, l->data, FALSE);
+ if (keyword == NULL) {
+ keyword = ephy_bookmarks_add_keyword (bookmarks, l->data);
+ }
+
+ ephy_bookmarks_set_keyword (bookmarks, keyword, node);
+ }
+ }
+
+ g_free (parsedname);
+
+ break;
+ case NS_NOTES:
+ case NS_SEPARATOR:
+ case NS_UNKNOWN:
+ default:
+ break;
+ }
+ }
+ out:
+ fclose (bf);
+ g_string_free (name, TRUE);
+ g_string_free (url, TRUE);
+
+ return retval;
+}
+
+gboolean
+ephy_bookmarks_import_xbel (EphyBookmarks *bookmarks,
+ const char *filename)
+{
+ xmlTextReaderPtr reader;
+ int ret;
+
+ if (g_settings_get_boolean (EPHY_SETTINGS_LOCKDOWN,
+ EPHY_PREFS_LOCKDOWN_BOOKMARK_EDITING))
+ return FALSE;
+
+ if (g_file_test (filename, G_FILE_TEST_EXISTS) == FALSE) {
+ return FALSE;
+ }
+
+ reader = xmlNewTextReaderFilename (filename);
+ if (reader == NULL) {
+ return FALSE;
+ }
+
+ ret = xbel_parse_xbel (bookmarks, reader);
+
+ xmlFreeTextReader (reader);
+
+ return ret >= 0 ? TRUE : FALSE;
+}
+
+static void
+parse_rdf_lang_tag (xmlNode *child,
+ xmlChar **value,
+ int *best_match)
+{
+ const char * const *locales;
+ const char *this_language;
+ xmlChar *lang;
+ xmlChar *content;
+ int i;
+
+ if (*best_match == 0)
+ /* there's no way we can do better */
+ return;
+
+ content = xmlNodeGetContent (child);
+ if (!content)
+ return;
+
+ lang = xmlNodeGetLang (child);
+ if (lang == NULL) {
+ const char *translated;
+
+ translated = _((char *)content);
+ if ((char *)content != translated) {
+ /* if we have a translation for the content of the
+ * node, then we just use this */
+ if (*value) xmlFree (*value);
+ *value = (xmlChar *)g_strdup (translated);
+ *best_match = 0;
+
+ xmlFree (content);
+ return;
+ }
+
+ this_language = "C";
+ } else
+ this_language = (char *)lang;
+
+ locales = g_get_language_names ();
+
+ for (i = 0; locales[i] && i < *best_match; i++) {
+ if (!strcmp (locales[i], this_language)) {
+ /* if we've already encountered a less accurate
+ * translation, then free it */
+ if (*value) xmlFree (*value);
+
+ *value = content;
+ *best_match = i;
+
+ break;
+ }
+ }
+
+ if (lang) xmlFree (lang);
+ if (*value != content) xmlFree (content);
+}
+
+static void
+parse_rdf_item (EphyBookmarks *bookmarks,
+ xmlNodePtr node)
+{
+ xmlChar *title = NULL;
+ int best_match_title = INT_MAX;
+ xmlChar *link = NULL;
+ int best_match_link = INT_MAX;
+ /* we consider that it's better to use a non-localized smart link than
+ * a localized link */
+ gboolean use_smartlink = FALSE;
+ xmlChar *subject = NULL;
+ GList *subjects = NULL, *l = NULL;
+ xmlNode *child;
+ EphyNode *bmk = NULL;
+
+ child = node->children;
+
+ link = xmlGetProp (node, (xmlChar *)"about");
+
+ while (child != NULL) {
+ if (xmlStrEqual (child->name, (xmlChar *)"title")) {
+ parse_rdf_lang_tag (child, &title, &best_match_title);
+ } else if (xmlStrEqual (child->name, (xmlChar *)"link") &&
+ !use_smartlink) {
+ parse_rdf_lang_tag (child, &link, &best_match_link);
+ } else if (child->ns &&
+ xmlStrEqual (child->ns->prefix, (xmlChar *)"ephy") &&
+ xmlStrEqual (child->name, (xmlChar *)"smartlink")) {
+ if (!use_smartlink) {
+ use_smartlink = TRUE;
+ best_match_link = INT_MAX;
+ }
+
+ parse_rdf_lang_tag (child, &link, &best_match_link);
+ } else if (child->ns &&
+ xmlStrEqual (child->ns->prefix, (xmlChar *)"dc") &&
+ xmlStrEqual (child->name, (xmlChar *)"subject")) {
+ subject = xmlNodeGetContent (child);
+ if (subject)
+ subjects = g_list_prepend (subjects, subject);
+ }
+
+ child = child->next;
+ }
+
+ if (link)
+ bmk = bookmark_add (bookmarks, (char *)title, (char *)link);
+
+ if (bmk) {
+ l = subjects;
+ }
+
+ for (; l != NULL; l = l->next) {
+ char *topic_name = l->data;
+ EphyNode *topic;
+
+ topic = ephy_bookmarks_find_keyword (bookmarks, topic_name, FALSE);
+
+ if (topic == NULL) {
+ topic = ephy_bookmarks_add_keyword (bookmarks, topic_name);
+ }
+
+ if (topic != NULL) {
+ ephy_bookmarks_set_keyword (bookmarks, topic, bmk);
+ }
+ }
+
+ xmlFree (title);
+ xmlFree (link);
+
+ g_list_foreach (subjects, (GFunc)xmlFree, NULL);
+ g_list_free (subjects);
+}
+
+gboolean
+ephy_bookmarks_import_rdf (EphyBookmarks *bookmarks,
+ const char *filename)
+{
+ xmlDocPtr doc;
+ xmlNodePtr child;
+ xmlNodePtr root;
+
+ if (g_settings_get_boolean (EPHY_SETTINGS_LOCKDOWN,
+ EPHY_PREFS_LOCKDOWN_BOOKMARK_EDITING))
+ return FALSE;
+
+ if (g_file_test (filename, G_FILE_TEST_EXISTS) == FALSE)
+ return FALSE;
+
+ doc = xmlParseFile (filename);
+ if (doc == NULL) {
+ /* FIXME: maybe put up a warning dialogue here, because this
+ * is a severe dataloss?
+ */
+ g_warning ("Failed to re-import the bookmarks. All bookmarks lost!\n");
+ return FALSE;
+ }
+
+ root = xmlDocGetRootElement (doc);
+
+ child = root->children;
+
+ while (child != NULL) {
+ if (xmlStrEqual (child->name, (xmlChar *)"item")) {
+ parse_rdf_item (bookmarks, child);
+ }
+
+ child = child->next;
+ }
+
+ xmlFreeDoc (doc);
+
+ return TRUE;
+}
diff --git a/src/bookmarks/ephy-bookmarks-import.h b/src/bookmarks/ephy-bookmarks-import.h
new file mode 100644
index 000000000..21ac67821
--- /dev/null
+++ b/src/bookmarks/ephy-bookmarks-import.h
@@ -0,0 +1,47 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright © 2003, 2004 Marco Pesenti Gritti
+ * Copyright © 2003, 2004, 2005 Christian Persch
+ *
+ * This file is part of Epiphany.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "ephy-bookmarks.h"
+
+G_BEGIN_DECLS
+
+#define MOZILLA_BOOKMARKS_DIR ".mozilla"
+#define FIREFOX_BOOKMARKS_DIR_0 ".phoenix"
+#define FIREFOX_BOOKMARKS_DIR_1 ".firefox"
+#define FIREFOX_BOOKMARKS_DIR_2 ".mozilla/firefox"
+#define GALEON_BOOKMARKS_DIR ".galeon"
+#define KDE_BOOKMARKS_DIR ".kde/share/apps/konqueror"
+
+gboolean ephy_bookmarks_import (EphyBookmarks *bookmarks,
+ const char *filename);
+
+gboolean ephy_bookmarks_import_mozilla (EphyBookmarks *bookmarks,
+ const char *filename);
+
+gboolean ephy_bookmarks_import_xbel (EphyBookmarks *bookmarks,
+ const char *filename);
+
+gboolean ephy_bookmarks_import_rdf (EphyBookmarks *bookmarks,
+ const char *filename);
+
+G_END_DECLS
diff --git a/src/bookmarks/ephy-bookmarks-manager.c b/src/bookmarks/ephy-bookmarks-manager.c
index e20c01f13..9cd0c6c58 100644
--- a/src/bookmarks/ephy-bookmarks-manager.c
+++ b/src/bookmarks/ephy-bookmarks-manager.c
@@ -1,19 +1,21 @@
/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
- * Copyright © 2016 Iulian-Gabriel Radu <iulian.radu67@gmail.com>
+ * Copyright © 2016 Iulian-Gabriel Radu <iulian.radu67@gmail.com>
*
- * 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 3 of the License, or
- * (at your option) any later version.
+ * This file is part of Epiphany.
*
- * 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.
+ * 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 3 of the License, or
+ * (at your option) any later version.
*
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * 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, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
diff --git a/src/bookmarks/ephy-bookmarks-manager.h b/src/bookmarks/ephy-bookmarks-manager.h
index ab79df3cb..4d2a59c41 100644
--- a/src/bookmarks/ephy-bookmarks-manager.h
+++ b/src/bookmarks/ephy-bookmarks-manager.h
@@ -1,19 +1,21 @@
/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
- * Copyright © 2016 Iulian-Gabriel Radu <iulian.radu67@gmail.com>
+ * Copyright © 2016 Iulian-Gabriel Radu <iulian.radu67@gmail.com>
*
- * 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 3 of the License, or
- * (at your option) any later version.
+ * This file is part of Epiphany.
*
- * 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.
+ * 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 3 of the License, or
+ * (at your option) any later version.
*
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * 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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
diff --git a/src/bookmarks/ephy-bookmarks-menu.c b/src/bookmarks/ephy-bookmarks-menu.c
new file mode 100644
index 000000000..502336827
--- /dev/null
+++ b/src/bookmarks/ephy-bookmarks-menu.c
@@ -0,0 +1,241 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright © 2003, 2004 Marco Pesenti Gritti
+ * Copyright © 2003, 2004 Christian Persch
+ * Copyright © 2004 Peter Harvey
+ *
+ * This file is part of Epiphany.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "ephy-bookmarks-menu.h"
+#include "ephy-bookmarks-ui.h"
+#include "ephy-bookmark-action.h"
+#include "ephy-open-tabs-action.h"
+#include "ephy-topic-action.h"
+#include "ephy-nodes-cover.h"
+#include "ephy-node-common.h"
+#include "ephy-link.h"
+#include "ephy-shell.h"
+#include "ephy-string.h"
+#include "ephy-gui.h"
+#include "ephy-debug.h"
+
+#include <string.h>
+
+#define MIN_MENU_SIZE 3
+#define MAX_MENU_SIZE 21
+
+enum {
+ BUILD_SUBDIVIS = 1 << 0,
+ BUILD_SUBMENUS = 1 << 1,
+ BUILD_CHILD_SUBDIVIS = 1 << 2,
+ BUILD_CHILD_SUBMENUS = 1 << 3
+};
+
+/* Construct a block of bookmark actions. Note that no bookmark action appears
+ * more than once in a menu, so no need to supply names. */
+static void
+append_bookmarks (GMenu *menu,
+ const GPtrArray *bookmarks)
+{
+ EphyNode *child;
+ const gchar *action_name;
+ GVariant *location;
+
+ long i;
+
+ for (i = 0; i < bookmarks->len; i++) {
+ child = g_ptr_array_index (bookmarks, i);
+
+ location = g_variant_new_string (ephy_node_get_property_string (child, EPHY_NODE_BMK_PROP_LOCATION));
+ action_name = g_action_print_detailed_name ("win.open-bookmark", location);
+ g_variant_unref (location);
+
+ g_menu_append (menu,
+ ephy_node_get_property_string (child, EPHY_NODE_BMK_PROP_TITLE),
+ action_name);
+
+ }
+}
+
+/* Build a menu of the given bookmarks categorised by the given topics.
+ * Shows categorisation using subdivisions, submenus, or a mix of both. */
+static void
+append_menu (GMenu *menu, const GPtrArray *topics, const GPtrArray *bookmarks, guint flags)
+{
+ GPtrArray *uncovered;
+ guint i, j;
+ GMenu *submenu, *section;
+
+ gboolean use_subdivis = flags & BUILD_SUBDIVIS;
+ gboolean use_submenus = flags & BUILD_SUBMENUS;
+
+ if (use_subdivis || use_submenus) {
+ GPtrArray *subset, *covering, *subdivisions, *submenus, *unused;
+ GArray *sizes = 0;
+ EphyNode *topic;
+ gint size, total;
+ gboolean separate = FALSE;
+ const gchar *name;
+
+ /* Get the subtopics, uncovered bookmarks, and subtopic sizes. */
+ sizes = g_array_sized_new (FALSE, FALSE, sizeof (int), topics->len);
+ uncovered = g_ptr_array_sized_new (bookmarks->len);
+ covering = ephy_nodes_get_covering (topics, bookmarks, 0, uncovered, sizes);
+
+ /* Preallocate arrays for submenus, subdivisions, and bookmark subsets. */
+ subdivisions = g_ptr_array_sized_new (topics->len);
+ submenus = g_ptr_array_sized_new (topics->len);
+ subset = g_ptr_array_sized_new (bookmarks->len);
+ unused = g_ptr_array_sized_new (bookmarks->len);
+
+ /* Get the total number of items in the menu. */
+ total = uncovered->len;
+ for (i = 0; i < covering->len; i++)
+ total += g_array_index (sizes, int, i);
+
+ /* Seperate covering into list of submenus and subdivisions */
+ for (i = 0; i < covering->len; i++) {
+ topic = g_ptr_array_index (covering, i);
+ size = g_array_index (sizes, int, i);
+
+ if (!use_submenus || (use_subdivis && (size < MIN_MENU_SIZE || total < MAX_MENU_SIZE))) {
+ g_ptr_array_add (subdivisions, topic);
+ } else {
+ g_ptr_array_add (submenus, topic);
+ total = total - size + 1;
+ }
+ }
+
+ /* Sort the list of submenus and subdivisions. */
+ g_ptr_array_sort (submenus, ephy_bookmarks_compare_topic_pointers);
+ g_ptr_array_sort (subdivisions, ephy_bookmarks_compare_topic_pointers);
+
+ if (flags & BUILD_CHILD_SUBDIVIS) flags |= BUILD_SUBDIVIS;
+ if (flags & BUILD_CHILD_SUBMENUS) flags |= BUILD_SUBMENUS;
+
+ /* Create each of the submenus. */
+ for (i = 0; i < submenus->len; i++) {
+ topic = g_ptr_array_index (submenus, i);
+ ephy_nodes_get_covered (topic, bookmarks, subset);
+
+ name = ephy_node_get_property_string (topic, EPHY_NODE_KEYWORD_PROP_NAME);
+
+ submenu = g_menu_new ();
+ g_menu_append_submenu (menu, name, G_MENU_MODEL (submenu));
+ append_menu (G_MENU (submenu), topics, subset, flags);
+
+ separate = TRUE;
+ }
+
+ /* Build a list of bookmarks which don't appear in any subdivision yet. */
+ for (i = 0; i < bookmarks->len; i++) {
+ g_ptr_array_add (unused, g_ptr_array_index (bookmarks, i));
+ }
+
+ /* Create each of the subdivisions. */
+ for (i = 0; i < subdivisions->len; i++) {
+ topic = g_ptr_array_index (subdivisions, i);
+ ephy_nodes_get_covered (topic, unused, subset);
+ g_ptr_array_sort (subset, ephy_bookmarks_compare_bookmark_pointers);
+
+ if (separate) {
+ section = g_menu_new ();
+ g_menu_append_section (menu, NULL, G_MENU_MODEL (section));
+ }
+
+ append_bookmarks (G_MENU (section), subset);
+ separate = TRUE;
+
+ /* Record that each bookmark has been added. */
+ for (j = 0; j < subset->len; j++) {
+ g_ptr_array_remove_fast (unused, g_ptr_array_index (subset, j));
+ }
+ }
+
+ g_array_free (sizes, TRUE);
+ g_ptr_array_free (covering, TRUE);
+ g_ptr_array_free (subdivisions, TRUE);
+ g_ptr_array_free (submenus, TRUE);
+ g_ptr_array_free (subset, TRUE);
+ g_ptr_array_free (unused, TRUE);
+ } else {
+ uncovered = g_ptr_array_sized_new (bookmarks->len);
+ for (i = 0; i < bookmarks->len; i++)
+ g_ptr_array_add (uncovered, g_ptr_array_index (bookmarks, i));
+ g_ptr_array_sort (uncovered, ephy_bookmarks_compare_bookmark_pointers);
+ }
+
+ /* Create the final subdivision (uncovered bookmarks). */
+ g_ptr_array_sort (uncovered, ephy_bookmarks_compare_bookmark_pointers);
+ append_bookmarks (menu, uncovered);
+ g_ptr_array_free (uncovered, TRUE);
+}
+
+void
+ephy_bookmarks_menu_build (GMenu *menu, EphyNode *parent)
+{
+ GPtrArray *children, *topics;
+ EphyBookmarks *eb;
+ EphyNode *node;
+ gint priority;
+ guint flags, id, i;
+
+ eb = ephy_shell_get_bookmarks (ephy_shell_get_default ());
+
+ children = ephy_node_get_children (ephy_bookmarks_get_keywords (eb));
+ topics = g_ptr_array_sized_new (children->len);
+ for (i = 0; i < children->len; i++) {
+ node = g_ptr_array_index (children, i);
+ priority = ephy_node_get_property_int (node, EPHY_NODE_KEYWORD_PROP_PRIORITY);
+ if (priority == EPHY_NODE_NORMAL_PRIORITY)
+ g_ptr_array_add (topics, node);
+ }
+
+ /* If no parent was supplied, use the default 'All' */
+ node = parent ? parent : ephy_bookmarks_get_bookmarks (eb);
+ children = ephy_node_get_children (node);
+
+ /* Determine what kind of menu we want. */
+ id = ephy_node_get_id (node);
+ switch (id) {
+ case FAVORITES_NODE_ID:
+ flags = 0;
+ break;
+ case BOOKMARKS_NODE_ID:
+ flags = BUILD_SUBMENUS | BUILD_CHILD_SUBDIVIS;
+ break;
+ default:
+ flags = BUILD_SUBMENUS | BUILD_SUBDIVIS | BUILD_CHILD_SUBDIVIS;
+ /* flags = BUILD_SUBDIVIS; */
+ break;
+ }
+
+ /* If this menu is the 'All' menu, be sure to include the 'local' topic. */
+ if (id == BOOKMARKS_NODE_ID) {
+ EphyNode *local_node;
+
+ local_node = ephy_bookmarks_get_local (eb);
+ if (local_node != NULL) {
+ g_ptr_array_add (topics, ephy_bookmarks_get_local (eb));
+ }
+
+ append_menu (menu, topics, children, flags);
+ g_ptr_array_free (topics, TRUE);
+ }
+}
diff --git a/src/bookmarks/ephy-bookmarks-popover.c b/src/bookmarks/ephy-bookmarks-popover.c
index 8a42537f0..62f6dbbec 100644
--- a/src/bookmarks/ephy-bookmarks-popover.c
+++ b/src/bookmarks/ephy-bookmarks-popover.c
@@ -1,19 +1,21 @@
/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
- * Copyright © 2016 Iulian-Gabriel Radu <iulian.radu67@gmail.com>
+ * Copyright © 2016 Iulian-Gabriel Radu <iulian.radu67@gmail.com>
*
- * 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 of the License, or
- * (at your option) any later version.
+ * This file is part of Epiphany.
*
- * 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.
+ * 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 3 of the License, or
+ * (at your option) any later version.
*
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * 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, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
diff --git a/src/bookmarks/ephy-bookmarks-popover.h b/src/bookmarks/ephy-bookmarks-popover.h
index 824a5d57d..cb155ff17 100644
--- a/src/bookmarks/ephy-bookmarks-popover.h
+++ b/src/bookmarks/ephy-bookmarks-popover.h
@@ -1,19 +1,21 @@
/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
- * Copyright © 2016 Iulian-Gabriel Radu <iulian.radu67@gmail.com>
+ * Copyright © 2016 Iulian-Gabriel Radu <iulian.radu67@gmail.com>
*
- * 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 of the License, or
- * (at your option) any later version.
+ * This file is part of Epiphany.
*
- * 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.
+ * 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 3 of the License, or
+ * (at your option) any later version.
*
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * 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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
diff --git a/src/bookmarks/ephy-bookmarks-ui.c b/src/bookmarks/ephy-bookmarks-ui.c
new file mode 100644
index 000000000..609515e9a
--- /dev/null
+++ b/src/bookmarks/ephy-bookmarks-ui.c
@@ -0,0 +1,365 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright © 2005 Peter Harvey
+ * Copyright © 2006 Christian Persch
+ *
+ * This file is part of Epiphany.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+#include "ephy-bookmarks-ui.h"
+
+#include "ephy-bookmark-action-group.h"
+#include "ephy-bookmark-action.h"
+#include "ephy-bookmark-properties.h"
+#include "ephy-bookmarks-menu.h"
+#include "ephy-bookmarks.h"
+#include "ephy-debug.h"
+#include "ephy-dnd.h"
+#include "ephy-embed-shell.h"
+#include "ephy-file-helpers.h"
+#include "ephy-gui.h"
+#include "ephy-header-bar.h"
+#include "ephy-link.h"
+#include "ephy-node-common.h"
+#include "ephy-open-tabs-action.h"
+#include "ephy-prefs.h"
+#include "ephy-settings.h"
+#include "ephy-shell.h"
+#include "ephy-string.h"
+#include "ephy-topic-action-group.h"
+#include "ephy-topic-action.h"
+
+#include <string.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#define BM_WINDOW_DATA_KEY "bookmarks-window-data"
+
+typedef struct {
+ GMenu *bookmarks_menu;
+ guint toolbar_menu;
+} BookmarksWindowData;
+
+enum {
+ RESPONSE_SHOW_PROPERTIES = 1,
+ RESPONSE_NEW_BOOKMARK = 2
+};
+
+static GHashTable *properties_dialogs = 0;
+
+static GMenu *
+find_bookmarks_menu (EphyWindow *window)
+{
+ GtkWidget *page_menu_button;
+ GMenuModel *page_menu;
+ gint n_items, i;
+
+ /* Page menu */
+ page_menu_button = ephy_header_bar_get_page_menu_button (EPHY_HEADER_BAR (ephy_window_get_header_bar (window)));
+ page_menu = gtk_menu_button_get_menu_model (GTK_MENU_BUTTON (page_menu_button));
+
+ /* Number of sections in the model */
+ n_items = g_menu_model_get_n_items (page_menu);
+
+ for (i = 0; i < n_items; i++) {
+ GVariant *section_label;
+
+ /* Looking for the bookmarks section */
+ section_label = g_menu_model_get_item_attribute_value (page_menu, i, "id", G_VARIANT_TYPE_STRING);
+ if (section_label != NULL && g_strcmp0 (g_variant_get_string (section_label, NULL), "bookmarks-section") == 0) {
+ GMenuModel *bookmarks_section_model;
+ GMenuModel *bookmarks_menu_model;
+
+ /* Bookmarks section should contain the bookmarks menu */
+ bookmarks_section_model = g_menu_model_get_item_link (page_menu, i, G_MENU_LINK_SECTION);
+ bookmarks_menu_model = g_menu_model_get_item_link (bookmarks_section_model, 0, G_MENU_LINK_SUBMENU);
+
+ return G_MENU (bookmarks_menu_model);
+ }
+ }
+
+ return NULL;
+}
+
+static bool
+activate_bookmarks_menu (GSimpleAction *action,
+ GdkEvent *event,
+ gpointer user_data)
+{
+ GMenu *menu;
+ BookmarksWindowData *data = g_object_get_data (G_OBJECT (user_data), BM_WINDOW_DATA_KEY);
+
+ if (event->type != GDK_BUTTON_PRESS)
+ return G_SOURCE_REMOVE;
+
+ if (data && !data->bookmarks_menu) {
+ menu = g_menu_new ();
+ ephy_bookmarks_menu_build (menu, 0);
+
+ data->bookmarks_menu = G_MENU (find_bookmarks_menu (EPHY_WINDOW (user_data)));
+ if (data->bookmarks_menu == NULL)
+ return G_SOURCE_REMOVE;
+
+ g_menu_append_section (data->bookmarks_menu, NULL, G_MENU_MODEL (menu));
+ }
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+erase_bookmarks_menu (EphyWindow *window)
+{
+ BookmarksWindowData *data;
+
+ data = g_object_get_data (G_OBJECT (window), BM_WINDOW_DATA_KEY);
+
+ if (data != NULL && data->bookmarks_menu != NULL) {
+ g_menu_remove_all (data->bookmarks_menu);
+ g_clear_object (&data->bookmarks_menu);
+ }
+}
+
+static void
+tree_changed_cb (EphyBookmarks *bookmarks,
+ EphyWindow *window)
+{
+ erase_bookmarks_menu (window);
+}
+
+static void
+node_added_cb (EphyNode *parent,
+ EphyNode *child,
+ EphyWindow *window)
+{
+ erase_bookmarks_menu (window);
+}
+
+static void
+node_changed_cb (EphyNode *parent,
+ EphyNode *child,
+ guint property_id,
+ EphyWindow *window)
+{
+ if (property_id == EPHY_NODE_KEYWORD_PROP_NAME ||
+ property_id == EPHY_NODE_BMK_PROP_TITLE) {
+ erase_bookmarks_menu (window);
+ }
+}
+
+static void
+node_removed_cb (EphyNode *parent,
+ EphyNode *child,
+ guint index,
+ EphyWindow *window)
+{
+ erase_bookmarks_menu (window);
+}
+
+void
+ephy_bookmarks_ui_attach_window (EphyWindow *window)
+{
+ EphyBookmarks *eb;
+ EphyNode *bookmarks;
+ EphyNode *topics;
+ BookmarksWindowData *data;
+ GtkUIManager *manager;
+ GtkActionGroup *actions;
+ GtkWidget *page_menu_button;
+
+ eb = ephy_shell_get_bookmarks (ephy_shell_get_default ());
+ bookmarks = ephy_bookmarks_get_bookmarks (eb);
+ topics = ephy_bookmarks_get_keywords (eb);
+ data = g_object_get_data (G_OBJECT (window), BM_WINDOW_DATA_KEY);
+ g_return_if_fail (data == NULL);
+
+ manager = gtk_ui_manager_new ();
+
+ data = g_new0 (BookmarksWindowData, 1);
+ g_object_set_data_full (G_OBJECT (window), BM_WINDOW_DATA_KEY, data, g_free);
+
+ /* Create the self-maintaining action groups for bookmarks and topics */
+ actions = ephy_bookmark_group_new (bookmarks);
+ gtk_ui_manager_insert_action_group (manager, actions, -1);
+ g_signal_connect_object (actions, "open-link",
+ G_CALLBACK (ephy_link_open), G_OBJECT (window),
+ G_CONNECT_SWAPPED | G_CONNECT_AFTER);
+ g_object_unref (actions);
+
+ actions = ephy_topic_action_group_new (topics, manager);
+ gtk_ui_manager_insert_action_group (manager, actions, -1);
+ g_object_unref (actions);
+
+ actions = ephy_open_tabs_group_new (topics);
+ gtk_ui_manager_insert_action_group (manager, actions, -1);
+ g_signal_connect_object (actions, "open-link",
+ G_CALLBACK (ephy_link_open), G_OBJECT (window),
+ G_CONNECT_SWAPPED | G_CONNECT_AFTER);
+ g_object_unref (actions);
+
+ /* Add signal handlers for the bookmark database */
+ ephy_node_signal_connect_object (bookmarks, EPHY_NODE_CHILD_ADDED,
+ (EphyNodeCallback)node_added_cb,
+ G_OBJECT (window));
+ ephy_node_signal_connect_object (topics, EPHY_NODE_CHILD_ADDED,
+ (EphyNodeCallback)node_added_cb,
+ G_OBJECT (window));
+
+ ephy_node_signal_connect_object (bookmarks, EPHY_NODE_CHILD_REMOVED,
+ (EphyNodeCallback)node_removed_cb,
+ G_OBJECT (window));
+ ephy_node_signal_connect_object (topics, EPHY_NODE_CHILD_REMOVED,
+ (EphyNodeCallback)node_removed_cb,
+ G_OBJECT (window));
+
+ ephy_node_signal_connect_object (bookmarks, EPHY_NODE_CHILD_CHANGED,
+ (EphyNodeCallback)node_changed_cb,
+ G_OBJECT (window));
+ ephy_node_signal_connect_object (topics, EPHY_NODE_CHILD_CHANGED,
+ (EphyNodeCallback)node_changed_cb,
+ G_OBJECT (window));
+
+ g_signal_connect_object (eb, "tree_changed",
+ G_CALLBACK (tree_changed_cb),
+ G_OBJECT (window), 0);
+
+ page_menu_button = ephy_header_bar_get_page_menu_button (EPHY_HEADER_BAR (ephy_window_get_header_bar (window)));
+ g_signal_connect (GTK_WIDGET (page_menu_button), "button-press-event", G_CALLBACK (activate_bookmarks_menu), window);
+}
+
+void
+ephy_bookmarks_ui_detach_window (EphyWindow *window)
+{
+ EphyBookmarks *eb = ephy_shell_get_bookmarks (ephy_shell_get_default ());
+ EphyNode *bookmarks = ephy_bookmarks_get_bookmarks (eb);
+ EphyNode *topics = ephy_bookmarks_get_keywords (eb);
+
+ BookmarksWindowData *data = g_object_get_data (G_OBJECT (window), BM_WINDOW_DATA_KEY);
+ GtkWidget *page_menu_button;
+
+ g_return_if_fail (data != 0);
+
+ if (data->bookmarks_menu) {
+ g_menu_remove_all (data->bookmarks_menu);
+ g_object_unref (data->bookmarks_menu);
+ data->bookmarks_menu = NULL;
+ }
+
+ g_object_set_data (G_OBJECT (window), BM_WINDOW_DATA_KEY, 0);
+
+ ephy_node_signal_disconnect_object (bookmarks, EPHY_NODE_CHILD_ADDED,
+ (EphyNodeCallback)node_added_cb,
+ G_OBJECT (window));
+ ephy_node_signal_disconnect_object (topics, EPHY_NODE_CHILD_ADDED,
+ (EphyNodeCallback)node_added_cb,
+ G_OBJECT (window));
+
+ ephy_node_signal_disconnect_object (bookmarks, EPHY_NODE_CHILD_REMOVED,
+ (EphyNodeCallback)node_removed_cb,
+ G_OBJECT (window));
+ ephy_node_signal_disconnect_object (topics, EPHY_NODE_CHILD_REMOVED,
+ (EphyNodeCallback)node_removed_cb,
+ G_OBJECT (window));
+
+ ephy_node_signal_disconnect_object (bookmarks, EPHY_NODE_CHILD_CHANGED,
+ (EphyNodeCallback)node_changed_cb,
+ G_OBJECT (window));
+ ephy_node_signal_disconnect_object (topics, EPHY_NODE_CHILD_CHANGED,
+ (EphyNodeCallback)node_changed_cb,
+ G_OBJECT (window));
+
+ g_signal_handlers_disconnect_by_func
+ (G_OBJECT (eb), G_CALLBACK (tree_changed_cb), G_OBJECT (window));
+
+ page_menu_button = ephy_header_bar_get_page_menu_button (EPHY_HEADER_BAR (ephy_window_get_header_bar (window)));
+ g_signal_handlers_disconnect_by_func (GTK_WIDGET (page_menu_button), G_CALLBACK (activate_bookmarks_menu), window);
+}
+
+static void
+properties_dialog_destroy_cb (EphyBookmarkProperties *dialog,
+ EphyNode *bookmark)
+{
+ g_hash_table_remove (properties_dialogs, bookmark);
+}
+
+void
+ephy_bookmarks_ui_add_bookmark (GtkWindow *parent,
+ const char *location,
+ const char *title)
+{
+ EphyBookmarks *bookmarks;
+ EphyNode *bookmark;
+ GtkWidget *dialog;
+
+ if (g_settings_get_boolean (EPHY_SETTINGS_LOCKDOWN,
+ EPHY_PREFS_LOCKDOWN_BOOKMARK_EDITING))
+ return;
+
+ bookmarks = ephy_shell_get_bookmarks (ephy_shell_get_default ());
+ bookmark = ephy_bookmarks_add (bookmarks, title, location);
+
+ if (properties_dialogs == 0) {
+ properties_dialogs = g_hash_table_new (g_direct_hash, g_direct_equal);
+ }
+
+ dialog = ephy_bookmark_properties_new (bookmarks, bookmark, TRUE);
+
+ g_assert (parent != NULL);
+
+ gtk_window_group_add_window (ephy_gui_ensure_window_group (parent),
+ GTK_WINDOW (dialog));
+ gtk_window_set_transient_for (GTK_WINDOW (dialog), parent);
+
+ g_signal_connect (dialog, "destroy",
+ G_CALLBACK (properties_dialog_destroy_cb), bookmark);
+ g_hash_table_insert (properties_dialogs,
+ bookmark, dialog);
+
+ gtk_window_present_with_time (GTK_WINDOW (dialog),
+ gtk_get_current_event_time ());
+}
+
+void
+ephy_bookmarks_ui_show_bookmark (GtkWindow *parent, EphyNode *bookmark)
+{
+ EphyBookmarks *bookmarks;
+ GtkWidget *dialog;
+
+ bookmarks = ephy_shell_get_bookmarks (ephy_shell_get_default ());
+
+ g_return_if_fail (EPHY_IS_BOOKMARKS (bookmarks));
+ g_return_if_fail (EPHY_IS_NODE (bookmark));
+
+ if (properties_dialogs == 0) {
+ properties_dialogs = g_hash_table_new (g_direct_hash, g_direct_equal);
+ }
+
+ dialog = g_hash_table_lookup (properties_dialogs, bookmark);
+
+ if (dialog == NULL) {
+ dialog = ephy_bookmark_properties_new (bookmarks, bookmark, FALSE);
+
+ g_signal_connect (dialog, "destroy",
+ G_CALLBACK (properties_dialog_destroy_cb), bookmark);
+ g_hash_table_insert (properties_dialogs,
+ bookmark, dialog);
+ }
+
+ gtk_window_set_transient_for (GTK_WINDOW (dialog), parent);
+
+ gtk_window_present_with_time (GTK_WINDOW (dialog),
+ gtk_get_current_event_time ());
+}
diff --git a/src/bookmarks/ephy-bookmarks-ui.h b/src/bookmarks/ephy-bookmarks-ui.h
new file mode 100644
index 000000000..067889fdb
--- /dev/null
+++ b/src/bookmarks/ephy-bookmarks-ui.h
@@ -0,0 +1,61 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright © 2005 Peter Harvey
+ *
+ * This file is part of Epiphany.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "ephy-window.h"
+#include "ephy-node.h"
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define EPHY_BOOKMARKS_UI_ACTION_NAME_BUFFER_SIZE 32
+
+#define EPHY_BOOKMARK_ACTION_NAME_BUFFER_SIZE EPHY_BOOKMARKS_UI_ACTION_NAME_BUFFER_SIZE
+#define EPHY_BOOKMARK_ACTION_NAME_FORMAT "Bmk%u"
+#define EPHY_BOOKMARK_ACTION_NAME_FORMAT_ARG(node) (ephy_node_get_id (node))
+#define EPHY_BOOKMARK_ACTION_NAME_PRINTF(buffer,node) (g_snprintf (buffer, sizeof (buffer), EPHY_BOOKMARK_ACTION_NAME_FORMAT, EPHY_BOOKMARK_ACTION_NAME_FORMAT_ARG (node)))
+#define EPHY_BOOKMARK_ACTION_NAME_STRDUP_PRINTF(node) (g_strdup_printf (EPHY_BOOKMARK_ACTION_NAME_FORMAT, EPHY_BOOKMARK_ACTION_NAME_FORMAT_ARG (node)))
+
+#define EPHY_TOPIC_ACTION_NAME_BUFFER_SIZE EPHY_BOOKMARKS_UI_ACTION_NAME_BUFFER_SIZE
+#define EPHY_TOPIC_ACTION_NAME_FORMAT "Tp%u"
+#define EPHY_TOPIC_ACTION_NAME_FORMAT_ARG(node) (ephy_node_get_id (node))
+#define EPHY_TOPIC_ACTION_NAME_PRINTF(buffer,node) (g_snprintf (buffer, sizeof (buffer), EPHY_TOPIC_ACTION_NAME_FORMAT, EPHY_TOPIC_ACTION_NAME_FORMAT_ARG (node)))
+#define EPHY_TOPIC_ACTION_NAME_STRDUP_PRINTF(node) (g_strdup_printf (EPHY_TOPIC_ACTION_NAME_FORMAT, EPHY_TOPIC_ACTION_NAME_FORMAT_ARG (node)))
+
+#define EPHY_OPEN_TABS_ACTION_NAME_BUFFER_SIZE EPHY_BOOKMARKS_UI_ACTION_NAME_BUFFER_SIZE
+#define EPHY_OPEN_TABS_ACTION_NAME_FORMAT "OpTb%u"
+#define EPHY_OPEN_TABS_ACTION_NAME_FORMAT_ARG(node) (ephy_node_get_id (node))
+#define EPHY_OPEN_TABS_ACTION_NAME_PRINTF(buffer,node) (g_snprintf (buffer, sizeof (buffer), EPHY_OPEN_TABS_ACTION_NAME_FORMAT, EPHY_OPEN_TABS_ACTION_NAME_FORMAT_ARG (node)))
+#define EPHY_OPEN_TABS_ACTION_NAME_STRDUP_PRINTF(node) (g_strdup_printf (EPHY_OPEN_TABS_ACTION_NAME_FORMAT, EPHY_OPEN_TABS_ACTION_NAME_FORMAT_ARG (node)))
+
+void ephy_bookmarks_ui_attach_window (EphyWindow *window);
+
+void ephy_bookmarks_ui_detach_window (EphyWindow *window);
+
+void ephy_bookmarks_ui_add_bookmark (GtkWindow *parent,
+ const char *location,
+ const char *title);
+
+void ephy_bookmarks_ui_show_bookmark (GtkWindow *parent,
+ EphyNode *bookmark);
+
+G_END_DECLS
diff --git a/src/bookmarks/ephy-bookmarks.c b/src/bookmarks/ephy-bookmarks.c
new file mode 100644
index 000000000..a82114b2b
--- /dev/null
+++ b/src/bookmarks/ephy-bookmarks.c
@@ -0,0 +1,1594 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright © 2002-2004 Marco Pesenti Gritti
+ * Copyright © 2003, 2004 Christian Persch
+ *
+ * This file is part of Epiphany.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+#include "ephy-bookmarks.h"
+
+#include "ephy-bookmark-properties.h"
+#include "ephy-bookmarks-export.h"
+#include "ephy-bookmarks-import.h"
+#include "ephy-bookmarks-type-builtins.h"
+#include "ephy-debug.h"
+#include "ephy-embed-shell.h"
+#include "ephy-file-helpers.h"
+#include "ephy-history-service.h"
+#include "ephy-node-common.h"
+#include "ephy-prefs.h"
+#include "ephy-profile-utils.h"
+#include "ephy-settings.h"
+#include "ephy-shell.h"
+#include "ephy-signal-accumulator.h"
+#include "ephy-tree-model-node.h"
+
+#include <avahi-common/error.h>
+#include <avahi-gobject/ga-client.h>
+#include <avahi-gobject/ga-enums.h>
+#include <avahi-gobject/ga-service-browser.h>
+#include <avahi-gobject/ga-service-resolver.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include <string.h>
+
+#define EPHY_BOOKMARKS_XML_ROOT "ephy_bookmarks"
+#define EPHY_BOOKMARKS_XML_VERSION "1.03"
+#define BOOKMARKS_SAVE_DELAY 3 /* seconds */
+#define UPDATE_URI_DATA_KEY "updated-uri"
+
+static const char zeroconf_protos[3][6] =
+{
+ "http",
+ "https",
+ "ftp"
+};
+
+struct _EphyBookmarks {
+ GObject parent_instance;
+
+ gboolean init_defaults;
+ gboolean dirty;
+ guint save_timeout_id;
+ char *xml_file;
+ char *rdf_file;
+ EphyNodeDb *db;
+ EphyNode *bookmarks;
+ EphyNode *keywords;
+ EphyNode *notcategorized;
+ EphyNode *smartbookmarks;
+ EphyNode *lower_fav;
+ double lower_score;
+
+ /* Local sites */
+ EphyNode *local;
+ GaClient *ga_client;
+ GaServiceBrowser *browse_handles[G_N_ELEMENTS (zeroconf_protos)];
+ GHashTable *resolve_handles;
+};
+
+static const char *default_topics [] =
+{
+ N_("Entertainment"),
+ N_("News"),
+ N_("Shopping"),
+ N_("Sports"),
+ N_("Travel"),
+ N_("Work")
+};
+
+/* Signals */
+enum {
+ TREE_CHANGED,
+ LAST_SIGNAL
+};
+
+static guint ephy_bookmarks_signals[LAST_SIGNAL];
+
+static void ephy_bookmarks_finalize (GObject *object);
+static void ephy_local_bookmarks_start_client (EphyBookmarks *bookmarks);
+
+G_DEFINE_TYPE (EphyBookmarks, ephy_bookmarks, G_TYPE_OBJECT)
+
+static void
+ephy_bookmarks_init_defaults (EphyBookmarks *eb)
+{
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (default_topics); i++) {
+ ephy_bookmarks_add_keyword (eb, _(default_topics[i]));
+ }
+
+ ephy_bookmarks_import_rdf (eb, PKGDATADIR "/default-bookmarks.rdf");
+}
+
+static void
+ephy_bookmarks_class_init (EphyBookmarksClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = ephy_bookmarks_finalize;
+
+ ephy_bookmarks_signals[TREE_CHANGED] =
+ g_signal_new ("tree-changed",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 0);
+}
+
+static gboolean
+save_filter (EphyNode *node,
+ EphyBookmarks *bookmarks)
+{
+ return node != bookmarks->bookmarks &&
+ node != bookmarks->notcategorized &&
+ node != bookmarks->local;
+}
+
+static gboolean
+save_filter_local (EphyNode *node,
+ EphyBookmarks *bookmarks)
+{
+ return !ephy_node_has_child (bookmarks->local, node);
+}
+
+static void
+ephy_bookmarks_save (EphyBookmarks *eb)
+{
+ LOG ("Saving bookmarks");
+
+ ephy_node_db_write_to_xml_safe
+ (eb->db,
+ (xmlChar *)eb->xml_file,
+ (xmlChar *)EPHY_BOOKMARKS_XML_ROOT,
+ (xmlChar *)EPHY_BOOKMARKS_XML_VERSION,
+ (xmlChar *)"Do not rely on this file, it's only for internal use. Use bookmarks.rdf instead.",
+ eb->keywords, (EphyNodeFilterFunc)save_filter, eb,
+ eb->bookmarks, (EphyNodeFilterFunc)save_filter_local, eb,
+ NULL);
+
+ /* Export bookmarks in rdf */
+ ephy_bookmarks_export_rdf (eb, eb->rdf_file);
+}
+
+static gboolean
+save_bookmarks_delayed (EphyBookmarks *bookmarks)
+{
+ ephy_bookmarks_save (bookmarks);
+ bookmarks->dirty = FALSE;
+ bookmarks->save_timeout_id = 0;
+
+ return FALSE;
+}
+
+static void
+ephy_bookmarks_save_delayed (EphyBookmarks *bookmarks, int delay)
+{
+ if (!bookmarks->dirty) {
+ bookmarks->dirty = TRUE;
+
+ if (delay > 0) {
+ bookmarks->save_timeout_id =
+ g_timeout_add_seconds (BOOKMARKS_SAVE_DELAY,
+ (GSourceFunc)save_bookmarks_delayed,
+ bookmarks);
+ g_source_set_name_by_id (bookmarks->save_timeout_id, "[epiphany] save_bookmarks_delayed");
+ } else {
+ bookmarks->save_timeout_id =
+ g_idle_add ((GSourceFunc)save_bookmarks_delayed,
+ bookmarks);
+ }
+ }
+}
+
+static void
+icon_updated_cb (WebKitFaviconDatabase *favicon_database,
+ const char *address,
+ const char *icon,
+ EphyBookmarks *eb)
+{
+ ephy_bookmarks_set_icon (eb, address, icon);
+}
+
+static void
+ephy_setup_history_notifiers (EphyBookmarks *eb)
+{
+ WebKitFaviconDatabase *favicon_database;
+ EphyEmbedShell *shell = ephy_embed_shell_get_default ();
+
+ favicon_database = webkit_web_context_get_favicon_database (ephy_embed_shell_get_web_context (shell));
+ g_signal_connect (favicon_database, "favicon-changed",
+ G_CALLBACK (icon_updated_cb), eb);
+}
+
+static void
+update_bookmark_keywords (EphyBookmarks *eb, EphyNode *bookmark)
+{
+ GPtrArray *children;
+ guint i;
+ GString *list;
+ const char *title;
+ char *normalized_keywords, *case_normalized_keywords;
+
+ list = g_string_new (NULL);
+
+ children = ephy_node_get_children (eb->keywords);
+ for (i = 0; i < children->len; i++) {
+ EphyNode *kid;
+
+ kid = g_ptr_array_index (children, i);
+
+ if (kid != eb->notcategorized &&
+ kid != eb->bookmarks &&
+ kid != eb->local &&
+ ephy_node_has_child (kid, bookmark)) {
+ const char *topic;
+ topic = ephy_node_get_property_string
+ (kid, EPHY_NODE_KEYWORD_PROP_NAME);
+ g_string_append (list, topic);
+ g_string_append (list, " ");
+ }
+ }
+
+ title = ephy_node_get_property_string
+ (bookmark, EPHY_NODE_BMK_PROP_TITLE);
+ g_string_append (list, " ");
+ g_string_append (list, title);
+
+ normalized_keywords = g_utf8_normalize (list->str, -1, G_NORMALIZE_ALL);
+ case_normalized_keywords = g_utf8_casefold (normalized_keywords, -1);
+
+ ephy_node_set_property_string (bookmark, EPHY_NODE_BMK_PROP_KEYWORDS,
+ case_normalized_keywords);
+
+ g_string_free (list, TRUE);
+ g_free (normalized_keywords);
+ g_free (case_normalized_keywords);
+}
+
+static void
+bookmarks_changed_cb (EphyNode *node,
+ EphyNode *child,
+ guint property_id,
+ EphyBookmarks *eb)
+{
+ if (property_id == EPHY_NODE_BMK_PROP_TITLE) {
+ update_bookmark_keywords (eb, child);
+ }
+
+ ephy_bookmarks_save_delayed (eb, BOOKMARKS_SAVE_DELAY);
+}
+
+static void
+bookmarks_removed_cb (EphyNode *node,
+ EphyNode *child,
+ guint old_index,
+ EphyBookmarks *eb)
+{
+ ephy_bookmarks_save_delayed (eb, BOOKMARKS_SAVE_DELAY);
+}
+
+static gboolean
+bookmark_is_categorized (EphyBookmarks *eb, EphyNode *bookmark)
+{
+ GPtrArray *children;
+ guint i;
+
+ children = ephy_node_get_children (eb->keywords);
+ for (i = 0; i < children->len; i++) {
+ EphyNode *kid;
+
+ kid = g_ptr_array_index (children, i);
+
+ if (kid != eb->notcategorized &&
+ kid != eb->bookmarks &&
+ kid != eb->local &&
+ ephy_node_has_child (kid, bookmark)) {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static void
+topics_removed_cb (EphyNode *node,
+ EphyNode *child,
+ guint old_index,
+ EphyBookmarks *eb)
+{
+ GPtrArray *children;
+ guint i;
+
+ children = ephy_node_get_children (child);
+ for (i = 0; i < children->len; i++) {
+ EphyNode *kid;
+
+ kid = g_ptr_array_index (children, i);
+
+ if (!bookmark_is_categorized (eb, kid) &&
+ !ephy_node_has_child (eb->notcategorized, kid)) {
+ ephy_node_add_child
+ (eb->notcategorized, kid);
+ }
+
+ update_bookmark_keywords (eb, kid);
+ }
+}
+
+static void
+fix_hierarchy_topic (EphyBookmarks *eb,
+ EphyNode *topic)
+{
+ GPtrArray *children;
+ EphyNode *bookmark;
+ const char *name;
+ char **split;
+ guint i, j;
+
+ children = ephy_node_get_children (topic);
+ name = ephy_node_get_property_string (topic, EPHY_NODE_KEYWORD_PROP_NAME);
+ split = g_strsplit (name, "->", -1);
+
+ for (i = 0; split[i]; i++) {
+ if (split[i][0] == '\0') continue;
+
+ topic = ephy_bookmarks_find_keyword (eb, split[i], FALSE);
+ if (topic == NULL) {
+ topic = ephy_bookmarks_add_keyword (eb, split[i]);
+ }
+ for (j = 0; j < children->len; j++) {
+ bookmark = g_ptr_array_index (children, j);
+ ephy_bookmarks_set_keyword (eb, topic, bookmark);
+ }
+ }
+
+ g_strfreev (split);
+}
+
+static void
+fix_hierarchy (EphyBookmarks *eb)
+{
+ GPtrArray *topics;
+ EphyNode *topic;
+ const char *name;
+ int i;
+
+ topics = ephy_node_get_children (eb->keywords);
+ for (i = (int)topics->len - 1; i >= 0; i--) {
+ topic = (EphyNode *)g_ptr_array_index (topics, i);
+ name = ephy_node_get_property_string
+ (topic, EPHY_NODE_KEYWORD_PROP_NAME);
+ if (strstr (name, "->") != NULL) {
+ fix_hierarchy_topic (eb, topic);
+ ephy_node_remove_child (eb->keywords, topic);
+ }
+ }
+}
+
+static void
+backup_file (const char *original_filename, const char *extension)
+{
+ char *template, *backup_filename;
+ int result = 0;
+
+ if (g_file_test (original_filename, G_FILE_TEST_EXISTS) == FALSE) {
+ return;
+ }
+
+ template = g_strconcat (original_filename, ".backup-XXXXXX", NULL);
+ backup_filename = ephy_file_tmp_filename (template, extension);
+
+ if (backup_filename != NULL) {
+ result = rename (original_filename, backup_filename);
+ }
+
+ if (result >= 0) {
+ g_message ("Your old bookmarks file was backed up as \"%s\".\n",
+ backup_filename);
+ } else {
+ g_warning ("Backup failed! Your old bookmarks file was lost.\n");
+ }
+
+ g_free (template);
+ g_free (backup_filename);
+}
+
+/* C&P adapted from gnome-vfs-dns-sd.c */
+static GHashTable *
+decode_txt_record (AvahiStringList *input_text)
+{
+ GHashTable *hash;
+ guint i;
+ int len;
+ char *key, *value, *end;
+ char *key_dup, *value_dup;
+ char *raw_txt;
+ size_t raw_txt_len;
+
+ if (!input_text)
+ return NULL;
+
+ raw_txt_len = avahi_string_list_serialize (input_text, NULL, 0);
+ raw_txt = g_malloc (raw_txt_len);
+ raw_txt_len = avahi_string_list_serialize (input_text, raw_txt, raw_txt_len);
+
+ if (raw_txt == NULL)
+ return NULL;
+
+ hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+
+ i = 0;
+ while (i < raw_txt_len) {
+ len = raw_txt[i++];
+
+ if (i + len > raw_txt_len) {
+ break;
+ }
+
+ if (len == 0) {
+ continue;
+ }
+
+ key = &raw_txt[i];
+ end = &raw_txt[i + len];
+ i += len;
+
+ if (*key == '=') {
+ /* 6.4 - silently ignore keys starting with = */
+ continue;
+ }
+
+ value = memchr (key, '=', len);
+ if (value) {
+ key_dup = g_strndup (key, value - key);
+ value++; /* Skip '=' */
+ value_dup = g_strndup (value, end - value);
+ } else {
+ key_dup = g_strndup (key, len);
+ value_dup = NULL;
+ }
+ if (!g_hash_table_lookup_extended (hash,
+ key_dup,
+ NULL, NULL)) {
+ g_hash_table_insert (hash,
+ key_dup,
+ value_dup);
+ } else {
+ g_free (key_dup);
+ g_free (value_dup);
+ }
+ }
+
+ return hash;
+}
+
+/* End of copied code */
+
+static char *
+get_id_for_response (const char *type,
+ const char *domain,
+ const char *name)
+{
+ /* FIXME: limit length! */
+ return g_strdup_printf ("%s\1%s\1%s",
+ type,
+ domain,
+ name);
+}
+
+typedef struct {
+ EphyBookmarks *bookmarks;
+ GaServiceResolver *resolver;
+ EphyNode *node;
+ char *name;
+ char *type;
+ char *domain;
+} ResolveData;
+
+static void
+resolver_found_cb (GaServiceResolver *resolver,
+ int interface,
+ GaProtocol protocol,
+ const char *name,
+ const char *type,
+ const char *domain,
+ const char *host_name,
+ const AvahiAddress *address,
+ guint16 port,
+ AvahiStringList *txt,
+ glong flags,
+ ResolveData *data)
+{
+ EphyBookmarks *bookmarks = data->bookmarks;
+ GValue value = { 0, };
+ const char *path = NULL;
+ char host[128];
+ GHashTable *text_table;
+ char *url;
+ gboolean was_immutable;
+ gboolean is_new_node = FALSE;
+ guint i;
+
+ LOG ("resolver_found_cb resolver %p\n", resolver);
+
+ was_immutable = ephy_node_db_is_immutable (bookmarks->db);
+ ephy_node_db_set_immutable (bookmarks->db, FALSE);
+
+ /* Find the protocol */
+ for (i = 0; i < G_N_ELEMENTS (zeroconf_protos); ++i) {
+ char proto[20];
+
+ g_snprintf (proto, sizeof (proto), "_%s._tcp", zeroconf_protos[i]);
+ if (strcmp (type, proto) == 0) break;
+ }
+ if (i == G_N_ELEMENTS (zeroconf_protos)) return;
+
+ if (address == NULL) {
+ g_warning ("Zeroconf failed to resolve host %s", name);
+ return;
+ }
+
+ text_table = decode_txt_record (txt);
+
+ if (text_table != NULL) {
+ path = g_hash_table_lookup (text_table, "path");
+ }
+ if (path == NULL || path[0] == '\0') {
+ path = "/";
+ }
+
+ avahi_address_snprint (host, sizeof (host), address);
+
+ LOG ("0conf RESOLVED type=%s domain=%s name=%s => proto=%s host=%s port=%d path=%s\n",
+ type, domain, name,
+ zeroconf_protos[i], host, port, path);
+
+ was_immutable = ephy_node_db_is_immutable (bookmarks->db);
+ ephy_node_db_set_immutable (bookmarks->db, FALSE);
+
+ if (data->node == NULL) {
+ is_new_node = TRUE;
+
+ data->node = ephy_node_new (bookmarks->db);
+ g_assert (data->node != NULL);
+
+ /* don't allow dragging this node */
+ ephy_node_set_is_drag_source (data->node, FALSE);
+
+ g_value_init (&value, G_TYPE_STRING);
+ g_value_take_string (&value,
+ get_id_for_response (data->type,
+ data->domain,
+ data->name));
+ ephy_node_set_property (data->node, EPHY_NODE_BMK_PROP_SERVICE_ID, &value);
+ g_value_unset (&value);
+
+ /* FIXME: limit length! */
+ ephy_node_set_property_string (data->node,
+ EPHY_NODE_BMK_PROP_TITLE,
+ name);
+
+ ephy_node_set_property_boolean (data->node,
+ EPHY_NODE_BMK_PROP_IMMUTABLE,
+ TRUE);
+ }
+
+ /* FIXME: limit length! */
+ url = g_strdup_printf ("%s://%s:%d%s", zeroconf_protos[i], host, port, path);
+
+ g_value_init (&value, G_TYPE_STRING);
+ g_value_take_string (&value, url);
+ ephy_node_set_property (data->node, EPHY_NODE_BMK_PROP_LOCATION, &value);
+ g_value_unset (&value);
+
+ if (is_new_node) {
+ ephy_node_add_child (bookmarks->bookmarks, data->node);
+ ephy_node_add_child (bookmarks->local, data->node);
+ }
+
+ ephy_node_db_set_immutable (bookmarks->db, was_immutable);
+
+ if (text_table != NULL) {
+ g_hash_table_unref (text_table);
+ }
+}
+
+static void
+resolver_failure_cb (GaServiceResolver *resolver,
+ GError *error,
+ ResolveData *data)
+{
+ LOG ("resolver_failure_cb resolver %p: %s\n", resolver, error ? error->message : "(null)");
+
+ /* Remove the node, if present */
+ if (data->node != NULL) {
+ EphyBookmarks *bookmarks = data->bookmarks;
+ gboolean was_immutable;
+
+ was_immutable = ephy_node_db_is_immutable (bookmarks->db);
+ ephy_node_db_set_immutable (bookmarks->db, FALSE);
+ ephy_node_unref (data->node);
+ data->node = NULL;
+ ephy_node_db_set_immutable (bookmarks->db, was_immutable);
+ }
+}
+
+static void
+resolve_data_free (ResolveData *data)
+{
+ if (data->resolver)
+ g_object_unref (data->resolver);
+
+ g_free (data->type);
+ g_free (data->name);
+ g_free (data->domain);
+ g_slice_free (ResolveData, data);
+}
+
+static void
+browser_new_service_cb (GaServiceBrowser *browser,
+ int interface,
+ GaProtocol protocol,
+ const char *name,
+ const char *type,
+ const char *domain,
+ glong flags,
+ EphyBookmarks *bookmarks)
+{
+ ResolveData *data;
+ char *node_id;
+ GError *error = NULL;
+
+ node_id = get_id_for_response (type, domain, name);
+
+ LOG ("0conf ADD: type=%s domain=%s name=%s\n",
+ type, domain, name);
+
+ if (g_hash_table_lookup (bookmarks->resolve_handles, node_id) != NULL) {
+ g_free (node_id);
+ return;
+ }
+
+ data = g_slice_new0 (ResolveData);
+ data->bookmarks = bookmarks;
+ data->node = NULL;
+ data->type = g_strdup (type);
+ data->name = g_strdup (name);
+ data->domain = g_strdup (domain);
+
+ data->resolver = ga_service_resolver_new (AVAHI_IF_UNSPEC,
+ AVAHI_PROTO_UNSPEC,
+ name, type, domain,
+ AVAHI_PROTO_UNSPEC,
+ GA_LOOKUP_USE_MULTICAST);
+ g_signal_connect (data->resolver, "found",
+ G_CALLBACK (resolver_found_cb), data);
+ g_signal_connect (data->resolver, "failure",
+ G_CALLBACK (resolver_failure_cb), data);
+ if (!ga_service_resolver_attach (data->resolver,
+ bookmarks->ga_client,
+ &error)) {
+ g_warning ("Unable to resolve Zeroconf service %s: %s", name, error ? error->message : "(null)");
+ g_clear_error (&error);
+ resolve_data_free (data);
+ g_free (node_id);
+ return;
+ }
+
+ g_hash_table_insert (bookmarks->resolve_handles,
+ node_id /* transfer ownership */, data);
+}
+
+static void
+browser_removed_service_cb (GaServiceBrowser *browser,
+ int interface,
+ GaProtocol protocol,
+ const char *name,
+ const char *type,
+ const char *domain,
+ glong flags,
+ EphyBookmarks *bookmarks)
+{
+ char *node_id;
+ ResolveData *data;
+
+ node_id = get_id_for_response (type, domain, name);
+ data = g_hash_table_lookup (bookmarks->resolve_handles, node_id);
+ /* shouldn't really happen, but let's play safe */
+ if (!data) {
+ g_free (node_id);
+ return;
+ }
+
+ if (data->node != NULL) {
+ gboolean was_immutable;
+
+ was_immutable = ephy_node_db_is_immutable (bookmarks->db);
+ ephy_node_db_set_immutable (bookmarks->db, FALSE);
+ ephy_node_unref (data->node);
+ data->node = NULL;
+ ephy_node_db_set_immutable (bookmarks->db, was_immutable);
+ }
+
+ g_hash_table_remove (bookmarks->resolve_handles, node_id);
+ g_free (node_id);
+}
+
+static void
+start_browsing (GaClient *ga_client,
+ EphyBookmarks *bookmarks)
+{
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (zeroconf_protos); ++i) {
+ GaServiceBrowser *browser = NULL;
+ char proto[20];
+
+ g_snprintf (proto, sizeof (proto), "_%s._tcp", zeroconf_protos[i]);
+
+ browser = ga_service_browser_new (proto);
+ g_signal_connect (browser, "new-service",
+ G_CALLBACK (browser_new_service_cb), bookmarks);
+ g_signal_connect (browser, "removed-service",
+ G_CALLBACK (browser_removed_service_cb), bookmarks);
+ if (!ga_service_browser_attach (browser,
+ ga_client,
+ NULL)) {
+ g_warning ("Unable to start Zeroconf subsystem");
+ g_object_unref (browser);
+ return;
+ }
+
+ bookmarks->browse_handles[i] = browser;
+ }
+}
+
+static void
+ga_client_state_changed_cb (GaClient *ga_client,
+ GaClientState state,
+ EphyBookmarks *bookmarks)
+{
+ if (state == GA_CLIENT_STATE_FAILURE) {
+ if (avahi_client_errno (ga_client->avahi_client) == AVAHI_ERR_DISCONNECTED) {
+ g_object_unref (bookmarks->ga_client);
+ bookmarks->ga_client = NULL;
+
+ ephy_local_bookmarks_start_client (bookmarks);
+ }
+ }
+ if (state == GA_CLIENT_STATE_S_RUNNING) {
+ start_browsing (ga_client, bookmarks);
+ }
+}
+
+static void
+ephy_local_bookmarks_start_client (EphyBookmarks *bookmarks)
+{
+ GaClient *ga_client;
+
+ ga_client = ga_client_new (GA_CLIENT_FLAG_NO_FAIL);
+ g_signal_connect (ga_client, "state-changed",
+ G_CALLBACK (ga_client_state_changed_cb),
+ bookmarks);
+ if (!ga_client_start (ga_client, NULL)) {
+ g_warning ("Unable to start Zeroconf subsystem");
+ g_object_unref (ga_client);
+ return;
+ }
+ bookmarks->ga_client = ga_client;
+}
+
+static void
+ephy_local_bookmarks_init (EphyBookmarks *bookmarks)
+{
+ bookmarks->resolve_handles = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free,
+ (GDestroyNotify)resolve_data_free);
+ ephy_local_bookmarks_start_client (bookmarks);
+}
+
+static void
+ephy_local_bookmarks_stop (EphyBookmarks *bookmarks)
+{
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (zeroconf_protos); ++i) {
+ if (bookmarks->browse_handles[i] != NULL) {
+ g_object_unref (bookmarks->browse_handles[i]);
+ bookmarks->browse_handles[i] = NULL;
+ }
+ }
+
+ if (bookmarks->resolve_handles != NULL) {
+ g_hash_table_destroy (bookmarks->resolve_handles);
+ bookmarks->resolve_handles = NULL;
+ }
+
+ if (bookmarks->local != NULL) {
+ ephy_node_unref (bookmarks->local);
+ bookmarks->local = NULL;
+ }
+
+ if (bookmarks->ga_client != NULL) {
+ g_object_unref (bookmarks->ga_client);
+ bookmarks->ga_client = NULL;
+ }
+}
+
+static void
+ephy_bookmarks_init (EphyBookmarks *eb)
+{
+ EphyNodeDb *db;
+
+ /* Translators: this topic contains all bookmarks */
+ const char *bk_all = C_("bookmarks", "All");
+
+ /* Translators: this topic contains the not categorized
+ bookmarks */
+ const char *bk_not_categorized = C_("bookmarks", "Not Categorized");
+
+ /* Translators: this is an automatic topic containing local
+ * websites bookmarks autodiscovered with zeroconf. */
+ const char *bk_local_sites = C_("bookmarks", "Nearby Sites");
+
+ db = ephy_node_db_new (EPHY_NODE_DB_BOOKMARKS);
+ eb->db = db;
+
+ eb->xml_file = g_build_filename (ephy_dot_dir (),
+ EPHY_BOOKMARKS_FILE,
+ NULL);
+ eb->rdf_file = g_build_filename (ephy_dot_dir (),
+ EPHY_BOOKMARKS_FILE_RDF,
+ NULL);
+
+ /* Bookmarks */
+ eb->bookmarks = ephy_node_new_with_id (db, BOOKMARKS_NODE_ID);
+
+ ephy_node_set_property_string (eb->bookmarks,
+ EPHY_NODE_KEYWORD_PROP_NAME,
+ bk_all);
+ ephy_node_signal_connect_object (eb->bookmarks,
+ EPHY_NODE_CHILD_REMOVED,
+ (EphyNodeCallback)bookmarks_removed_cb,
+ G_OBJECT (eb));
+ ephy_node_signal_connect_object (eb->bookmarks,
+ EPHY_NODE_CHILD_CHANGED,
+ (EphyNodeCallback)bookmarks_changed_cb,
+ G_OBJECT (eb));
+
+ /* Keywords */
+ eb->keywords = ephy_node_new_with_id (db, KEYWORDS_NODE_ID);
+ ephy_node_set_property_int (eb->bookmarks,
+ EPHY_NODE_KEYWORD_PROP_PRIORITY,
+ EPHY_NODE_ALL_PRIORITY);
+
+ ephy_node_signal_connect_object (eb->keywords,
+ EPHY_NODE_CHILD_REMOVED,
+ (EphyNodeCallback)topics_removed_cb,
+ G_OBJECT (eb));
+
+ ephy_node_add_child (eb->keywords,
+ eb->bookmarks);
+
+ /* Not categorized */
+ eb->notcategorized = ephy_node_new_with_id (db, BMKS_NOTCATEGORIZED_NODE_ID);
+
+
+ ephy_node_set_property_string (eb->notcategorized,
+ EPHY_NODE_KEYWORD_PROP_NAME,
+ bk_not_categorized);
+
+ ephy_node_set_property_int (eb->notcategorized,
+ EPHY_NODE_KEYWORD_PROP_PRIORITY,
+ EPHY_NODE_SPECIAL_PRIORITY);
+
+ ephy_node_add_child (eb->keywords, eb->notcategorized);
+
+ /* Local Websites */
+ eb->local = ephy_node_new_with_id (db, BMKS_LOCAL_NODE_ID);
+
+ /* don't allow drags to this topic */
+ ephy_node_set_is_drag_dest (eb->local, FALSE);
+
+
+ ephy_node_set_property_string (eb->local,
+ EPHY_NODE_KEYWORD_PROP_NAME,
+ bk_local_sites);
+ ephy_node_set_property_int (eb->local,
+ EPHY_NODE_KEYWORD_PROP_PRIORITY,
+ EPHY_NODE_SPECIAL_PRIORITY);
+
+ ephy_node_add_child (eb->keywords, eb->local);
+ ephy_local_bookmarks_init (eb);
+
+ /* Smart bookmarks */
+ eb->smartbookmarks = ephy_node_new_with_id (db, SMARTBOOKMARKS_NODE_ID);
+
+ if (g_file_test (eb->xml_file, G_FILE_TEST_EXISTS) == FALSE
+ && g_file_test (eb->rdf_file, G_FILE_TEST_EXISTS) == FALSE) {
+ eb->init_defaults = TRUE;
+ } else if (ephy_node_db_load_from_file (eb->db, eb->xml_file,
+ (xmlChar *)EPHY_BOOKMARKS_XML_ROOT,
+ (xmlChar *)EPHY_BOOKMARKS_XML_VERSION) == FALSE) {
+ /* save the corrupted files so the user can late try to
+ * manually recover them. See bug #128308.
+ */
+
+ g_warning ("Could not read bookmarks file \"%s\", trying to "
+ "re-import bookmarks from \"%s\"\n",
+ eb->xml_file, eb->rdf_file);
+
+ backup_file (eb->xml_file, "xml");
+
+ if (ephy_bookmarks_import_rdf (eb, eb->rdf_file) == FALSE) {
+ backup_file (eb->rdf_file, "rdf");
+
+ eb->init_defaults = TRUE;
+ }
+ }
+
+ if (eb->init_defaults) {
+ ephy_bookmarks_init_defaults (eb);
+ }
+
+ fix_hierarchy (eb);
+
+ g_settings_bind (EPHY_SETTINGS_LOCKDOWN,
+ EPHY_PREFS_LOCKDOWN_BOOKMARK_EDITING,
+ eb->db, "immutable",
+ G_SETTINGS_BIND_GET);
+
+ ephy_setup_history_notifiers (eb);
+}
+
+static void
+ephy_bookmarks_finalize (GObject *object)
+{
+ EphyBookmarks *eb = EPHY_BOOKMARKS (object);
+
+ if (eb->save_timeout_id != 0) {
+ g_source_remove (eb->save_timeout_id);
+ }
+
+ ephy_bookmarks_save (eb);
+
+ ephy_local_bookmarks_stop (eb);
+
+ ephy_node_unref (eb->bookmarks);
+ ephy_node_unref (eb->keywords);
+ ephy_node_unref (eb->notcategorized);
+ ephy_node_unref (eb->smartbookmarks);
+
+ g_object_unref (eb->db);
+
+ g_free (eb->xml_file);
+ g_free (eb->rdf_file);
+
+ LOG ("Bookmarks finalized");
+
+ G_OBJECT_CLASS (ephy_bookmarks_parent_class)->finalize (object);
+}
+
+EphyBookmarks *
+ephy_bookmarks_new (void)
+{
+ return EPHY_BOOKMARKS (g_object_new (EPHY_TYPE_BOOKMARKS, NULL));
+}
+
+static void
+update_has_smart_address (EphyBookmarks *bookmarks, EphyNode *bmk, const char *address)
+{
+ EphyNode *smart_bmks;
+ gboolean smart = FALSE, with_options = FALSE;
+
+ smart_bmks = bookmarks->smartbookmarks;
+
+ if (address) {
+ smart = strstr (address, "%s") != NULL;
+ with_options = strstr (address, "%s%{") != NULL;
+ }
+
+ /* if we have a smart bookmark with width specification,
+ * remove from smart bookmarks first to force an update
+ * in the toolbar
+ */
+ if (smart && with_options) {
+ if (ephy_node_has_child (smart_bmks, bmk)) {
+ ephy_node_remove_child (smart_bmks, bmk);
+ }
+ ephy_node_add_child (smart_bmks, bmk);
+ } else if (smart) {
+ if (!ephy_node_has_child (smart_bmks, bmk)) {
+ ephy_node_add_child (smart_bmks, bmk);
+ }
+ } else {
+ if (ephy_node_has_child (smart_bmks, bmk)) {
+ ephy_node_remove_child (smart_bmks, bmk);
+ }
+ }
+}
+
+EphyNode *
+ephy_bookmarks_add (EphyBookmarks *eb,
+ const char *title,
+ const char *url)
+{
+ EphyNode *bm;
+ WebKitFaviconDatabase *favicon_database;
+ EphyEmbedShell *shell = ephy_embed_shell_get_default ();
+
+ bm = ephy_node_new (eb->db);
+
+ if (bm == NULL) return NULL;
+
+ if (url == NULL) return NULL;
+ ephy_node_set_property_string (bm, EPHY_NODE_BMK_PROP_LOCATION, url);
+
+ if (title == NULL || title[0] == '\0') {
+ title = _("Untitled");
+ }
+ ephy_node_set_property_string (bm, EPHY_NODE_BMK_PROP_TITLE, title);
+
+ favicon_database = webkit_web_context_get_favicon_database (ephy_embed_shell_get_web_context (shell));
+ if (favicon_database != NULL) {
+ char *icon = webkit_favicon_database_get_favicon_uri (favicon_database, url);
+ if (icon != NULL) {
+ ephy_node_set_property_string
+ (bm, EPHY_NODE_BMK_PROP_ICON, icon);
+ g_free (icon);
+ }
+ }
+
+ update_has_smart_address (eb, bm, url);
+ update_bookmark_keywords (eb, bm);
+
+ ephy_node_add_child (eb->bookmarks, bm);
+ ephy_node_add_child (eb->notcategorized, bm);
+
+ ephy_bookmarks_save_delayed (eb, 0);
+
+ return bm;
+}
+
+void
+ephy_bookmarks_set_address (EphyBookmarks *eb,
+ EphyNode *bookmark,
+ const char *address)
+{
+ ephy_node_set_property_string (bookmark, EPHY_NODE_BMK_PROP_LOCATION,
+ address);
+
+ update_has_smart_address (eb, bookmark, address);
+}
+
+EphyNode *
+ephy_bookmarks_find_bookmark (EphyBookmarks *eb,
+ const char *url)
+{
+ GPtrArray *children;
+ guint i;
+
+ g_return_val_if_fail (EPHY_IS_BOOKMARKS (eb), NULL);
+ g_return_val_if_fail (eb->bookmarks != NULL, NULL);
+ g_return_val_if_fail (url != NULL, NULL);
+
+ children = ephy_node_get_children (eb->bookmarks);
+ for (i = 0; i < children->len; i++) {
+ EphyNode *kid;
+ const char *location;
+
+ kid = g_ptr_array_index (children, i);
+ location = ephy_node_get_property_string
+ (kid, EPHY_NODE_BMK_PROP_LOCATION);
+
+ if (location != NULL && strcmp (url, location) == 0) {
+ return kid;
+ }
+ }
+
+ return NULL;
+}
+
+static gboolean
+is_similar (const char *url1, const char *url2)
+{
+ int i, j;
+
+ for (i = 0; url1[i]; i++)
+ if (url1[i] == '#' || url1[i] == '?')
+ break;
+ while (i > 0 && url1[i] == '/')
+ i--;
+
+ for (j = 0; url2[j]; j++)
+ if (url2[j] == '#' || url2[j] == '?')
+ break;
+ while (j > 0 && url2[j] == '/')
+ j--;
+
+ if (i != j) return FALSE;
+ if (strncmp (url1, url2, i) != 0) return FALSE;
+ return TRUE;
+}
+
+gint
+ephy_bookmarks_get_similar (EphyBookmarks *eb,
+ EphyNode *bookmark,
+ GPtrArray *identical,
+ GPtrArray *similar)
+{
+ GPtrArray *children;
+ const char *url;
+ guint i, result;
+
+ g_return_val_if_fail (EPHY_IS_BOOKMARKS (eb), -1);
+ g_return_val_if_fail (eb->bookmarks != NULL, -1);
+ g_return_val_if_fail (bookmark != NULL, -1);
+
+ url = ephy_node_get_property_string
+ (bookmark, EPHY_NODE_BMK_PROP_LOCATION);
+
+ g_return_val_if_fail (url != NULL, -1);
+
+ result = 0;
+
+ children = ephy_node_get_children (eb->bookmarks);
+ for (i = 0; i < children->len; i++) {
+ EphyNode *kid;
+ const char *location;
+
+ kid = g_ptr_array_index (children, i);
+ if (kid == bookmark) {
+ continue;
+ }
+
+ location = ephy_node_get_property_string
+ (kid, EPHY_NODE_BMK_PROP_LOCATION);
+
+ if (location != NULL) {
+ if (identical != NULL && strcmp (url, location) == 0) {
+ g_ptr_array_add (identical, kid);
+ result++;
+ } else if (is_similar (url, location)) {
+ if (similar != NULL) {
+ g_ptr_array_add (similar, kid);
+ }
+ result++;
+ }
+ }
+ }
+
+ return result;
+}
+
+void
+ephy_bookmarks_set_icon (EphyBookmarks *eb,
+ const char *url,
+ const char *icon)
+{
+ EphyNode *node;
+
+ g_return_if_fail (icon != NULL);
+
+ node = ephy_bookmarks_find_bookmark (eb, url);
+ if (node == NULL) return;
+
+ ephy_node_set_property_string (node, EPHY_NODE_BMK_PROP_ICON, icon);
+}
+
+
+void
+ephy_bookmarks_set_usericon (EphyBookmarks *eb,
+ const char *url,
+ const char *icon)
+{
+ EphyNode *node;
+
+ g_return_if_fail (icon != NULL);
+
+ node = ephy_bookmarks_find_bookmark (eb, url);
+ if (node == NULL) return;
+
+ ephy_node_set_property_string (node, EPHY_NODE_BMK_PROP_USERICON,
+ icon);
+}
+
+
+/* name must end with '=' */
+static char *
+get_option (char *start,
+ const char *name,
+ char **optionsend)
+{
+ char *end, *p;
+
+ *optionsend = start;
+
+ if (start == NULL || start[0] != '%' || start[1] != '{') return NULL;
+ start += 2;
+
+ end = strstr (start, "}");
+ if (end == NULL) return NULL;
+ *optionsend = end + 1;
+
+ start = strstr (start, name);
+ if (start == NULL || start > end) return NULL;
+ start += strlen (name);
+
+ /* Find end of option, either ',' or '}' */
+ end = strstr (start, ",");
+ if (end == NULL || end >= *optionsend) end = *optionsend - 1;
+
+ /* limit option length and sanity-check it */
+ if (end - start > 32) return NULL;
+ for (p = start; p < end; ++p) {
+ if (!g_ascii_isalnum (*p)) return NULL;
+ }
+
+ return g_strndup (start, end - start);
+}
+
+char *
+ephy_bookmarks_resolve_address (EphyBookmarks *eb,
+ const char *address,
+ const char *content)
+{
+ GString *result;
+ char *pos, *oldpos, *arg, *escaped_arg, *encoding, *optionsend;
+
+ g_return_val_if_fail (EPHY_IS_BOOKMARKS (eb), NULL);
+ g_return_val_if_fail (address != NULL, NULL);
+
+ if (content == NULL) content = "";
+
+ result = g_string_new_len (NULL, strlen (content) + strlen (address));
+
+ /* substitute %s's */
+ oldpos = (char *)address;
+ while ((pos = strstr (oldpos, "%s")) != NULL) {
+ g_string_append_len (result, oldpos, pos - oldpos);
+ pos += 2;
+
+ encoding = get_option (pos, "encoding=", &optionsend);
+ if (encoding != NULL) {
+ GError *error = NULL;
+
+ arg = g_convert (content, strlen (content), encoding,
+ "UTF-8", NULL, NULL, &error);
+ if (error != NULL) {
+ g_warning ("Error when converting arg to encoding '%s': %s\n",
+ encoding, error->message);
+ g_error_free (error);
+ } else {
+ escaped_arg = g_uri_escape_string (arg, NULL, TRUE);
+ g_string_append (result, escaped_arg);
+ g_free (escaped_arg);
+ g_free (arg);
+ }
+
+ g_free (encoding);
+ } else {
+ arg = g_uri_escape_string (content, NULL, TRUE);
+ g_string_append (result, arg);
+ g_free (arg);
+ }
+
+ oldpos = optionsend;
+ }
+ g_string_append (result, oldpos);
+
+ return g_string_free (result, FALSE);
+}
+
+guint
+ephy_bookmarks_get_smart_bookmark_width (EphyNode *bookmark)
+{
+ const char *url;
+ char *option, *end, *number;
+ guint width;
+
+ url = ephy_node_get_property_string (bookmark, EPHY_NODE_BMK_PROP_LOCATION);
+ if (url == NULL) return 0;
+
+ /* this takes the first %s, but that's okay since we only support one text entry box */
+ option = strstr (url, "%s%{");
+ if (option == NULL) return 0;
+ option += 2;
+
+ number = get_option (option, "width=", &end);
+ if (number == NULL) return 0;
+
+ width = (guint)g_ascii_strtoull (number, NULL, 10);
+ g_free (number);
+
+ return CLAMP (width, 1, 64);
+}
+
+EphyNode *
+ephy_bookmarks_add_keyword (EphyBookmarks *eb,
+ const char *name)
+{
+ EphyNode *key;
+
+ key = ephy_node_new (eb->db);
+
+ if (key == NULL) return NULL;
+
+ ephy_node_set_property_string (key, EPHY_NODE_KEYWORD_PROP_NAME,
+ name);
+ ephy_node_set_property_int (key, EPHY_NODE_KEYWORD_PROP_PRIORITY,
+ EPHY_NODE_NORMAL_PRIORITY);
+
+ ephy_node_add_child (eb->keywords, key);
+
+ return key;
+}
+
+void
+ephy_bookmarks_remove_keyword (EphyBookmarks *eb,
+ EphyNode *keyword)
+{
+ ephy_node_remove_child (eb->keywords, keyword);
+}
+
+char *
+ephy_bookmarks_get_topic_uri (EphyBookmarks *eb,
+ EphyNode *node)
+{
+ char *uri;
+
+ if (ephy_bookmarks_get_bookmarks (eb) == node) {
+ uri = g_strdup ("topic://Special/All");
+ } else if (ephy_bookmarks_get_not_categorized (eb) == node) {
+ uri = g_strdup ("topic://Special/NotCategorized");
+ } else if (ephy_bookmarks_get_local (eb) == node) {
+ /* Note: do not change to "Nearby" because of existing custom toolbars */
+ uri = g_strdup ("topic://Special/Local");
+ } else {
+ const char *name;
+
+ name = ephy_node_get_property_string
+ (node, EPHY_NODE_KEYWORD_PROP_NAME);
+
+ uri = g_strdup_printf ("topic://%s", name);
+ }
+
+ return uri;
+}
+
+EphyNode *
+ephy_bookmarks_find_keyword (EphyBookmarks *eb,
+ const char *name,
+ gboolean partial_match)
+{
+ EphyNode *node;
+ GPtrArray *children;
+ guint i;
+ const char *topic_name;
+
+ g_return_val_if_fail (name != NULL, NULL);
+
+ topic_name = name;
+
+ if (g_utf8_strlen (name, -1) == 0) {
+ LOG ("Empty name, no keyword matches.");
+ return NULL;
+ }
+
+ if (strcmp (name, "topic://Special/All") == 0) {
+ return ephy_bookmarks_get_bookmarks (eb);
+ } else if (strcmp (name, "topic://Special/NotCategorized") == 0) {
+ return ephy_bookmarks_get_not_categorized (eb);
+ } else if (strcmp (name, "topic://Special/Local") == 0) {
+ return ephy_bookmarks_get_local (eb);
+ } else if (g_str_has_prefix (name, "topic://")) {
+ topic_name += strlen ("topic://");
+ }
+
+ children = ephy_node_get_children (eb->keywords);
+ node = NULL;
+ for (i = 0; i < children->len; i++) {
+ EphyNode *kid;
+ const char *key;
+
+ kid = g_ptr_array_index (children, i);
+ key = ephy_node_get_property_string (kid, EPHY_NODE_KEYWORD_PROP_NAME);
+
+ if ((partial_match && g_str_has_prefix (key, topic_name) > 0) ||
+ (!partial_match && strcmp (key, topic_name) == 0)) {
+ node = kid;
+ }
+ }
+
+ return node;
+}
+
+gboolean
+ephy_bookmarks_has_keyword (EphyBookmarks *eb,
+ EphyNode *keyword,
+ EphyNode *bookmark)
+{
+ return ephy_node_has_child (keyword, bookmark);
+}
+
+void
+ephy_bookmarks_set_keyword (EphyBookmarks *eb,
+ EphyNode *keyword,
+ EphyNode *bookmark)
+{
+ if (ephy_node_has_child (keyword, bookmark)) return;
+
+ ephy_node_add_child (keyword, bookmark);
+
+ if (ephy_node_has_child (eb->notcategorized, bookmark)) {
+ LOG ("Remove from categorized bookmarks");
+ ephy_node_remove_child
+ (eb->notcategorized, bookmark);
+ }
+
+ update_bookmark_keywords (eb, bookmark);
+
+ g_signal_emit (G_OBJECT (eb), ephy_bookmarks_signals[TREE_CHANGED], 0);
+}
+
+void
+ephy_bookmarks_unset_keyword (EphyBookmarks *eb,
+ EphyNode *keyword,
+ EphyNode *bookmark)
+{
+ if (!ephy_node_has_child (keyword, bookmark)) return;
+
+ ephy_node_remove_child (keyword, bookmark);
+
+ if (!bookmark_is_categorized (eb, bookmark) &&
+ !ephy_node_has_child (eb->notcategorized, bookmark)) {
+ LOG ("Add to not categorized bookmarks");
+ ephy_node_add_child
+ (eb->notcategorized, bookmark);
+ }
+
+ update_bookmark_keywords (eb, bookmark);
+
+ g_signal_emit (G_OBJECT (eb), ephy_bookmarks_signals[TREE_CHANGED], 0);
+}
+
+/**
+ * ephy_bookmarks_get_smart_bookmarks:
+ *
+ * Return value: (transfer none):
+ **/
+EphyNode *
+ephy_bookmarks_get_smart_bookmarks (EphyBookmarks *eb)
+{
+ return eb->smartbookmarks;
+}
+
+/**
+ * ephy_bookmarks_get_keywords:
+ *
+ * Return value: (transfer none):
+ **/
+EphyNode *
+ephy_bookmarks_get_keywords (EphyBookmarks *eb)
+{
+ return eb->keywords;
+}
+
+/**
+ * ephy_bookmarks_get_bookmarks:
+ *
+ * Return value: (transfer none):
+ **/
+EphyNode *
+ephy_bookmarks_get_bookmarks (EphyBookmarks *eb)
+{
+ return eb->bookmarks;
+}
+
+/**
+ * ephy_bookmarks_get_local:
+ *
+ * Return value: (transfer none):
+ **/
+EphyNode *
+ephy_bookmarks_get_local (EphyBookmarks *eb)
+{
+ return eb->local;
+}
+
+/**
+ * ephy_bookmarks_get_not_categorized:
+ *
+ * Return value: (transfer none):
+ **/
+EphyNode *
+ephy_bookmarks_get_not_categorized (EphyBookmarks *eb)
+{
+ return eb->notcategorized;
+}
+
+/**
+ * ephy_bookmarks_get_from_id:
+ *
+ * Return value: (transfer none):
+ **/
+EphyNode *
+ephy_bookmarks_get_from_id (EphyBookmarks *eb, long id)
+{
+ return ephy_node_db_get_node_from_id (eb->db, id);
+}
+
+int
+ephy_bookmarks_compare_topics (gconstpointer a, gconstpointer b)
+{
+ EphyNode *node_a = (EphyNode *)a;
+ EphyNode *node_b = (EphyNode *)b;
+ const char *title1, *title2;
+ int priority1, priority2;
+
+ priority1 = ephy_node_get_property_int (node_a, EPHY_NODE_KEYWORD_PROP_PRIORITY);
+ priority2 = ephy_node_get_property_int (node_b, EPHY_NODE_KEYWORD_PROP_PRIORITY);
+
+ if (priority1 > priority2) return 1;
+ if (priority1 < priority2) return -1;
+
+ title1 = ephy_node_get_property_string (node_a, EPHY_NODE_KEYWORD_PROP_NAME);
+ title2 = ephy_node_get_property_string (node_b, EPHY_NODE_KEYWORD_PROP_NAME);
+
+ if (title1 == title2) return 0;
+ if (title1 == NULL) return -1;
+ if (title2 == NULL) return 1;
+ return g_utf8_collate (title1, title2);
+}
+
+int
+ephy_bookmarks_compare_topic_pointers (gconstpointer a, gconstpointer b)
+{
+ EphyNode *node_a = *(EphyNode **)a;
+ EphyNode *node_b = *(EphyNode **)b;
+
+ return ephy_bookmarks_compare_topics (node_a, node_b);
+}
+
+int
+ephy_bookmarks_compare_bookmarks (gconstpointer a, gconstpointer b)
+{
+ EphyNode *node_a = (EphyNode *)a;
+ EphyNode *node_b = (EphyNode *)b;
+ const char *title1, *title2;
+
+ title1 = ephy_node_get_property_string (node_a, EPHY_NODE_BMK_PROP_TITLE);
+ title2 = ephy_node_get_property_string (node_b, EPHY_NODE_BMK_PROP_TITLE);
+
+ if (title1 == title2) return 0;
+ if (title1 == NULL) return -1;
+ if (title2 == NULL) return 1;
+ return g_utf8_collate (title1, title2);
+}
+
+int
+ephy_bookmarks_compare_bookmark_pointers (gconstpointer a, gconstpointer b)
+{
+ EphyNode *node_a = *(EphyNode **)a;
+ EphyNode *node_b = *(EphyNode **)b;
+
+ return ephy_bookmarks_compare_bookmarks (node_a, node_b);
+}
diff --git a/src/bookmarks/ephy-bookmarks.h b/src/bookmarks/ephy-bookmarks.h
new file mode 100644
index 000000000..7e115e9a8
--- /dev/null
+++ b/src/bookmarks/ephy-bookmarks.h
@@ -0,0 +1,136 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright © 2000-2004 Marco Pesenti Gritti
+ * Copyright © 2003, 2004 Christian Persch
+ *
+ * This file is part of Epiphany.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <glib-object.h>
+
+#include "ephy-node.h"
+
+G_BEGIN_DECLS
+
+/* Name of a default smart bookmark, to NOT be displayed in the search engine
+ * combo in the preferences dialog. This should exactly match the smart bookmark
+ * in default-bookmarks.rdf. */
+#define DEFAULT_SMART_BOOKMARK_TEXT _("Search the web")
+
+#define EPHY_TYPE_BOOKMARKS (ephy_bookmarks_get_type ())
+
+G_DECLARE_FINAL_TYPE (EphyBookmarks, ephy_bookmarks, EPHY, BOOKMARKS, GObject)
+
+typedef enum
+{
+ EPHY_NODE_BMK_PROP_TITLE = 2,
+ EPHY_NODE_BMK_PROP_LOCATION = 3,
+ EPHY_NODE_BMK_PROP_KEYWORDS = 4,
+ EPHY_NODE_KEYWORD_PROP_NAME = 5,
+ EPHY_NODE_BMK_PROP_USERICON = 6,
+ EPHY_NODE_BMK_PROP_ICON = 7,
+ EPHY_NODE_KEYWORD_PROP_PRIORITY = 8,
+ EPHY_NODE_BMK_PROP_SERVICE_ID = 14,
+ EPHY_NODE_BMK_PROP_IMMUTABLE = 15
+} EphyBookmarkProperty;
+
+EphyBookmarks *ephy_bookmarks_new (void);
+
+EphyNode *ephy_bookmarks_get_from_id (EphyBookmarks *eb,
+ long id);
+
+/* Bookmarks */
+
+EphyNode *ephy_bookmarks_add (EphyBookmarks *eb,
+ const char *title,
+ const char *url);
+
+EphyNode* ephy_bookmarks_find_bookmark (EphyBookmarks *eb,
+ const char *url);
+
+gint ephy_bookmarks_get_similar (EphyBookmarks *eb,
+ EphyNode *bookmark,
+ GPtrArray *identical,
+ GPtrArray *similar);
+
+void ephy_bookmarks_set_icon (EphyBookmarks *eb,
+ const char *url,
+ const char *icon);
+
+void ephy_bookmarks_set_usericon (EphyBookmarks *eb,
+ const char *url,
+ const char *icon);
+
+void ephy_bookmarks_set_address (EphyBookmarks *eb,
+ EphyNode *bookmark,
+ const char *address);
+
+char *ephy_bookmarks_resolve_address (EphyBookmarks *eb,
+ const char *address,
+ const char *parameter);
+
+guint ephy_bookmarks_get_smart_bookmark_width
+ (EphyNode *bookmark);
+
+
+/* Keywords */
+
+EphyNode *ephy_bookmarks_add_keyword (EphyBookmarks *eb,
+ const char *name);
+
+EphyNode *ephy_bookmarks_find_keyword (EphyBookmarks *eb,
+ const char *name,
+ gboolean partial_match);
+
+void ephy_bookmarks_remove_keyword (EphyBookmarks *eb,
+ EphyNode *keyword);
+
+gboolean ephy_bookmarks_has_keyword (EphyBookmarks *eb,
+ EphyNode *keyword,
+ EphyNode *bookmark);
+
+void ephy_bookmarks_set_keyword (EphyBookmarks *eb,
+ EphyNode *keyword,
+ EphyNode *bookmark);
+
+void ephy_bookmarks_unset_keyword (EphyBookmarks *eb,
+ EphyNode *keyword,
+ EphyNode *bookmark);
+
+char *ephy_bookmarks_get_topic_uri (EphyBookmarks *eb,
+ EphyNode *node);
+
+/* Root */
+
+EphyNode *ephy_bookmarks_get_keywords (EphyBookmarks *eb);
+
+EphyNode *ephy_bookmarks_get_bookmarks (EphyBookmarks *eb);
+
+EphyNode *ephy_bookmarks_get_not_categorized (EphyBookmarks *eb);
+
+EphyNode *ephy_bookmarks_get_smart_bookmarks (EphyBookmarks *eb);
+
+EphyNode *ephy_bookmarks_get_local (EphyBookmarks *eb);
+
+/* Comparison functions, useful for sorting lists and arrays. */
+int ephy_bookmarks_compare_topics (gconstpointer a, gconstpointer b);
+int ephy_bookmarks_compare_topic_pointers (gconstpointer a, gconstpointer b);
+int ephy_bookmarks_compare_bookmarks (gconstpointer a, gconstpointer b);
+int ephy_bookmarks_compare_bookmark_pointers (gconstpointer a, gconstpointer b);
+
+G_END_DECLS
diff --git a/src/bookmarks/ephy-link-action.c b/src/bookmarks/ephy-link-action.c
new file mode 100644
index 000000000..d6fc6792d
--- /dev/null
+++ b/src/bookmarks/ephy-link-action.c
@@ -0,0 +1,174 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright © 2004 Christian Persch
+ * Copyright © 2005 Philip Langdale
+ *
+ * This file is part of Epiphany.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+#include "ephy-link-action.h"
+
+#include "ephy-debug.h"
+#include "ephy-gui.h"
+#include "ephy-link.h"
+#include "ephy-window-action.h"
+
+#include <gtk/gtk.h>
+
+G_DEFINE_TYPE_WITH_CODE (EphyLinkAction, ephy_link_action, EPHY_TYPE_WINDOW_ACTION,
+ G_IMPLEMENT_INTERFACE (EPHY_TYPE_LINK,
+ NULL))
+
+#define EPHY_LINK_ACTION_GET_PRIVATE(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object), EPHY_TYPE_LINK_ACTION, EphyLinkActionPrivate))
+
+struct _EphyLinkActionPrivate {
+ guint button;
+};
+
+static gboolean
+proxy_button_press_event_cb (GtkButton *button,
+ GdkEventButton *event,
+ EphyLinkAction *action)
+{
+ action->priv->button = event->button;
+
+ return FALSE;
+}
+
+static GtkWidget *
+get_event_widget (GtkWidget *proxy)
+{
+ GtkWidget *widget;
+
+ /*
+ * Finding the interesting widget requires internal knowledge of
+ * the widgets in question. This can't be helped, but by keeping
+ * the sneaky code in one place, it can easily be updated.
+ */
+ if (GTK_IS_MENU_ITEM (proxy)) {
+ /* Menu items already forward middle clicks */
+ widget = NULL;
+ } else if (GTK_IS_TOOL_BUTTON (proxy)) {
+ /* The tool button's button is the direct child */
+ widget = gtk_bin_get_child (GTK_BIN (proxy));
+ } else if (GTK_IS_BUTTON (proxy)) {
+ widget = proxy;
+ } else {
+ /* Don't touch anything we don't know about */
+ widget = NULL;
+ }
+
+ return widget;
+}
+
+static void
+ephy_link_action_connect_proxy (GtkAction *action, GtkWidget *proxy)
+{
+ GtkWidget *widget;
+
+ LOG ("Connect link action proxy");
+
+ widget = get_event_widget (proxy);
+ if (widget) {
+ g_signal_connect (widget, "button-press-event",
+ G_CALLBACK (proxy_button_press_event_cb),
+ action);
+ }
+
+ GTK_ACTION_CLASS (ephy_link_action_parent_class)->connect_proxy (action, proxy);
+}
+
+static void
+ephy_link_action_disconnect_proxy (GtkAction *action, GtkWidget *proxy)
+{
+ GtkWidget *widget;
+
+ LOG ("Disconnect link action proxy");
+
+ widget = get_event_widget (proxy);
+ if (widget) {
+ g_signal_handlers_disconnect_by_func (widget,
+ G_CALLBACK (proxy_button_press_event_cb),
+ action);
+ }
+
+ GTK_ACTION_CLASS (ephy_link_action_parent_class)->disconnect_proxy (action, proxy);
+}
+
+static void
+ephy_link_action_init (EphyLinkAction *action)
+{
+ action->priv = EPHY_LINK_ACTION_GET_PRIVATE (action);
+}
+
+static void
+ephy_link_action_class_init (EphyLinkActionClass *class)
+{
+ GtkActionClass *action_class = GTK_ACTION_CLASS (class);
+
+ action_class->connect_proxy = ephy_link_action_connect_proxy;
+ action_class->disconnect_proxy = ephy_link_action_disconnect_proxy;
+
+ g_type_class_add_private (G_OBJECT_CLASS (class), sizeof (EphyLinkActionPrivate));
+}
+
+/**
+ * ephy_link_action_get_button:
+ * @action: an #EphyLinkAction
+ *
+ * This method stores the mouse button number that last activated, or
+ * is activating, the @action. This is useful because #GtkButton's
+ * cannot be clicked with a middle click by default, so inside
+ * Epiphany we fake this by forwarding a left click (button 1) event
+ * instead of a middle click (button 2) to the button. That makes the
+ * EphyGUI methods like ephy_gui_is_middle_click not work here, so we
+ * need to ask the @action directly about the button that activated
+ * it.
+ *
+ * Returns: the button number that last activated (or is activating) the @action
+ **/
+guint
+ephy_link_action_get_button (EphyLinkAction *action)
+{
+ g_return_val_if_fail (EPHY_IS_LINK_ACTION (action), 0);
+
+ return action->priv->button;
+}
+
+G_DEFINE_TYPE_WITH_CODE (EphyLinkActionGroup, ephy_link_action_group, GTK_TYPE_ACTION_GROUP,
+ G_IMPLEMENT_INTERFACE (EPHY_TYPE_LINK,
+ NULL))
+
+static void
+ephy_link_action_group_class_init (EphyLinkActionGroupClass *klass)
+{
+ /* Empty, needed for G_DEFINE_TYPE macro */
+}
+
+static void
+ephy_link_action_group_init (EphyLinkActionGroup *action_group)
+{
+ /* Empty, needed for G_DEFINE_TYPE macro */
+}
+
+EphyLinkActionGroup *
+ephy_link_action_group_new (const char *name)
+{
+ return g_object_new (EPHY_TYPE_LINK_ACTION_GROUP,
+ "name", name,
+ NULL);
+}
diff --git a/src/bookmarks/ephy-link-action.h b/src/bookmarks/ephy-link-action.h
new file mode 100644
index 000000000..c9ee08716
--- /dev/null
+++ b/src/bookmarks/ephy-link-action.h
@@ -0,0 +1,79 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright © 2004 Christian Persch
+ *
+ * This file is part of Epiphany.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "ephy-window-action.h"
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define EPHY_TYPE_LINK_ACTION (ephy_link_action_get_type ())
+#define EPHY_LINK_ACTION(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EPHY_TYPE_LINK_ACTION, EphyLinkAction))
+#define EPHY_LINK_ACTION_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), EPHY_TYPE_LINK_ACTION, EphyLinkActionClass))
+#define EPHY_IS_LINK_ACTION(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EPHY_TYPE_LINK_ACTION))
+#define EPHY_IS_LINK_ACTION_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EPHY_TYPE_LINK_ACTION))
+#define EPHY_LINK_ACTION_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), EPHY_TYPE_LINK_ACTION, EphyLinkActionClass))
+
+#define EPHY_TYPE_LINK_ACTION_GROUP (ephy_link_action_group_get_type ())
+#define EPHY_LINK_ACTION_GROUP(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EPHY_TYPE_LINK_ACTION_GROUP, EphyLinkActionGroup))
+#define EPHY_LINK_ACTION_GROUP_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), EPHY_TYPE_LINK_ACTION_GROUP, EphyLinkActionGroupClass))
+#define EPHY_IS_LINK_ACTION_GROUP(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EPHY_TYPE_LINK_ACTION_GROUP))
+#define EPHY_IS_LINK_ACTION_GROUP_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EPHY_TYPE_LINK_ACTION_GROUP))
+#define EPHY_LINK_ACTION_GROUP_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), EPHY_TYPE_LINK_ACTION_GROUP, EphyLinkActionGroupClass))
+
+typedef struct _EphyLinkAction EphyLinkAction;
+typedef struct _EphyLinkActionClass EphyLinkActionClass;
+typedef struct _EphyLinkActionPrivate EphyLinkActionPrivate;
+
+typedef struct _EphyLinkActionGroup EphyLinkActionGroup;
+typedef struct _EphyLinkActionGroupClass EphyLinkActionGroupClass;
+
+struct _EphyLinkAction
+{
+ EphyWindowAction parent_instance;
+
+ EphyLinkActionPrivate *priv;
+};
+
+struct _EphyLinkActionClass
+{
+ EphyWindowActionClass parent_class;
+};
+
+struct _EphyLinkActionGroup
+{
+ GtkActionGroup parent_instance;
+};
+
+struct _EphyLinkActionGroupClass
+{
+ GtkActionGroupClass parent_class;
+};
+
+GType ephy_link_action_get_type (void);
+guint ephy_link_action_get_button (EphyLinkAction *action);
+
+GType ephy_link_action_group_get_type (void);
+
+EphyLinkActionGroup *ephy_link_action_group_new (const char *name);
+
+G_END_DECLS
diff --git a/src/bookmarks/ephy-nodes-cover.c b/src/bookmarks/ephy-nodes-cover.c
new file mode 100644
index 000000000..622e9e8a4
--- /dev/null
+++ b/src/bookmarks/ephy-nodes-cover.c
@@ -0,0 +1,196 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright © 2004 Peter Harvey <pah06@uow.edu.au>
+ *
+ * This file is part of Epiphany.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "ephy-nodes-cover.h"
+
+/* Count the number of node entries which are children of parent. */
+gint
+ephy_nodes_count_covered (EphyNode *parent, const GPtrArray *children)
+{
+ guint i, len = 0;
+ EphyNode *child;
+
+ for (i = 0; i < children->len; i++) {
+ child = g_ptr_array_index (children, i);
+ if (ephy_node_has_child (parent, child)) {
+ len++;
+ }
+ }
+ return len;
+}
+
+/* Removes from the array of children those which are children of the given parent. */
+gint
+ephy_nodes_remove_covered (EphyNode *parent, GPtrArray *children)
+{
+ guint i, len = children->len;
+ EphyNode *child;
+
+ for (i = 0; i < children->len; i++) {
+ child = g_ptr_array_index (children, i);
+ if (ephy_node_has_child (parent, child)) {
+ g_ptr_array_remove_index_fast (children, i);
+ i--;
+ }
+ }
+ return len - children->len;
+}
+
+/* Removes from the array of children those which are children of the given parent. */
+gint
+ephy_nodes_remove_not_covered (EphyNode *parent, GPtrArray *children)
+{
+ guint i, len = children->len;
+ EphyNode *child;
+
+ for (i = 0; i < children->len; i++) {
+ child = g_ptr_array_index (children, i);
+ if (!ephy_node_has_child (parent, child)) {
+ g_ptr_array_remove_index_fast (children, i);
+ i--;
+ }
+ }
+ return len - children->len;
+}
+
+/* Returns the subset of children which are childs of the given parent.
+ * Stores the result in the given _covered array if non-null. */
+GPtrArray *
+ephy_nodes_get_covered (EphyNode *parent, const GPtrArray *children, GPtrArray *_covered)
+{
+ GPtrArray *covered = _covered ? _covered : g_ptr_array_sized_new (children->len);
+ EphyNode *child;
+ guint i;
+
+ covered->len = 0;
+ for (i = 0; i < children->len; i++) {
+ child = g_ptr_array_index (children, i);
+ if (ephy_node_has_child (parent, child)) {
+ g_ptr_array_add (covered, child);
+ }
+ }
+
+ return covered;
+}
+
+/* Returns true if the parent covers all the children. */
+gboolean
+ephy_nodes_covered (EphyNode *parent, const GPtrArray *children)
+{
+ EphyNode *child;
+ guint i;
+
+ for (i = 0; i < children->len; i++) {
+ child = g_ptr_array_index (children, i);
+ if (!ephy_node_has_child (parent, child)) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static gint
+compare_chosen (const guint *a, const guint *b, guint *count_c)
+{
+ return (count_c[*b] - count_c[*a]);
+}
+
+/* Returns the subset of parents which provide a covering of children.
+ * Arguments other than parents and children arguments are only used if non-null.
+ * Uses the _covering array to store the subset of parents.
+ * Uses the _uncovered array to store those children which couldn't be covered.
+ * Uses the _sizes array to store the number of children covered by each parent. */
+GPtrArray *
+ephy_nodes_get_covering (const GPtrArray *parents, const GPtrArray *children,
+ GPtrArray *_covering, GPtrArray *_uncovered, GArray *_sizes)
+{
+ GPtrArray *uncovered = _uncovered ? _uncovered : g_ptr_array_sized_new (children->len);
+ GPtrArray *covering = _covering ? _covering : g_ptr_array_sized_new (parents->len);
+ GArray *chosen = g_array_sized_new (FALSE, FALSE, sizeof (guint), parents->len);
+ GArray *sizes = _sizes;
+
+ /* Create arrays to store the number of children each parent has which
+ * are currently not covered, and the number of children it has total. */
+ guint *count_u = g_malloc (sizeof (guint) * parents->len);
+ guint *count_c = g_malloc (sizeof (guint) * parents->len);
+
+ EphyNode *parent;
+ guint i, p;
+
+ /* Empty all the returning arrays. */
+ uncovered->len = 0;
+ covering->len = 0;
+ if (sizes) sizes->len = 0;
+
+ /* Initialise the array of uncovered bookmarks. */
+ for (i = 0; i < children->len; i++) {
+ g_ptr_array_add (uncovered, g_ptr_array_index (children, i));
+ }
+
+ /* Initialise the count_u and count_c arrays.
+ * NB: count_u[0] is set to 0 if the parent node
+ covers the entire set of children. */
+ for (i = 0, p = 0; i < parents->len; i++) {
+ parent = g_ptr_array_index (parents, i);
+ count_c[i] = ephy_nodes_count_covered (parent, children);
+ count_u[i] = (count_c[i] < children->len) ? count_c[i] : 0;
+ if (count_u[i] > count_u[p]) p = i;
+ }
+
+ /* While there are more suitable topics... */
+ while (p < parents->len && count_u[p]) {
+ /* Update the arrays of uncovered bookmarks and covering topics. */
+ parent = g_ptr_array_index (parents, p);
+ ephy_nodes_remove_covered (parent, uncovered);
+ g_array_append_val (chosen, p);
+
+ /* Find the next most suitable topic. */
+ count_u[p] = 0;
+ for (i = 0; i < parents->len; i++) {
+ /* Lazy update the count_u[i] array. */
+ if (count_u[i] > count_u[p] || (count_u[i] == count_u[p] && count_c[i] < count_c[p])) {
+ parent = g_ptr_array_index (parents, i);
+ count_u[i] = ephy_nodes_count_covered (parent, uncovered);
+ }
+
+ if (count_u[i] > count_u[p] || (count_u[i] == count_u[p] && count_c[i] < count_c[p])) {
+ p = i;
+ }
+ }
+ }
+
+ g_array_sort_with_data (chosen, (GCompareDataFunc)compare_chosen, count_c);
+
+ for (i = 0; i < chosen->len; i++) {
+ p = g_array_index (chosen, guint, i);
+ g_ptr_array_add (covering, g_ptr_array_index (parents, p));
+ if (sizes) g_array_append_val (sizes, count_c[p]);
+ }
+
+ if (_uncovered != uncovered) g_ptr_array_free (uncovered, TRUE);
+ g_array_free (chosen, TRUE);
+ g_free (count_u);
+ g_free (count_c);
+
+ return covering;
+}
diff --git a/src/bookmarks/ephy-open-tabs-action.c b/src/bookmarks/ephy-open-tabs-action.c
new file mode 100644
index 000000000..d92a64394
--- /dev/null
+++ b/src/bookmarks/ephy-open-tabs-action.c
@@ -0,0 +1,134 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright © 2004 Peter Harvey <pah06@uow.edu.au>
+ *
+ * This file is part of Epiphany.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include "ephy-bookmarks.h"
+#include "ephy-bookmarks-ui.h"
+#include "ephy-node-common.h"
+#include "ephy-link-action.h"
+#include "ephy-link.h"
+
+#include "ephy-open-tabs-action.h"
+
+static void
+activate_cb (GtkAction *action,
+ gpointer dummy)
+{
+ GObject *object = G_OBJECT (action);
+ EphyLink *link;
+ EphyNode *node;
+ GPtrArray *children;
+ EphyEmbed *embed = NULL;
+ const char *url;
+ guint i;
+
+ link = g_object_get_data (object, "ephy-link");
+ node = g_object_get_data (object, "ephy-node");
+
+ children = ephy_node_get_children (node);
+ for (i = 0; i < children->len; ++i) {
+ node = g_ptr_array_index (children, i);
+
+ url = ephy_node_get_property_string (node, EPHY_NODE_BMK_PROP_LOCATION);
+ embed = ephy_link_open (link, url, embed,
+ EPHY_LINK_NEW_TAB | ephy_link_flags_from_current_event ());
+ }
+}
+
+static void
+node_added_cb (EphyNode *parent,
+ EphyNode *child,
+ GtkActionGroup *action_group)
+{
+ GObject *action_object;
+ GtkAction *action;
+ char name[EPHY_OPEN_TABS_ACTION_NAME_BUFFER_SIZE];
+ char accel[256];
+
+ EPHY_OPEN_TABS_ACTION_NAME_PRINTF (name, child);
+
+ /* FIXME !!!! */
+ action = gtk_action_new (name, _("Open in New _Tabs"),
+ _("Open the bookmarks in this topic in new tabs"), NULL);
+ action_object = (GObject *)action;
+
+ g_object_set_data (action_object, "ephy-node", child);
+ g_object_set_data (action_object, "ephy-link", EPHY_LINK (action_group));
+
+ g_signal_connect (action, "activate",
+ G_CALLBACK (activate_cb), NULL);
+
+ g_snprintf (accel, sizeof (accel), "<Actions>/%s/%s",
+ gtk_action_group_get_name (action_group),
+ name);
+
+ gtk_action_set_accel_path (action, accel);
+ gtk_action_group_add_action (action_group, action);
+ g_object_unref (action);
+}
+
+static void
+node_removed_cb (EphyNode *parent,
+ EphyNode *child,
+ guint index,
+ GtkActionGroup *action_group)
+{
+ GtkAction *action;
+ char name[EPHY_OPEN_TABS_ACTION_NAME_BUFFER_SIZE];
+
+ EPHY_OPEN_TABS_ACTION_NAME_PRINTF (name, child);
+
+ action = gtk_action_group_get_action (action_group, name);
+
+ if (action != NULL) {
+ gtk_action_group_remove_action (action_group, action);
+ }
+}
+
+GtkActionGroup *
+ephy_open_tabs_group_new (EphyNode *node)
+{
+ GPtrArray *children;
+ GtkActionGroup *action_group;
+ guint i;
+
+ children = ephy_node_get_children (node);
+ action_group = (GtkActionGroup *)ephy_link_action_group_new ("OpenTabsActions");
+
+ for (i = 0; i < children->len; i++) {
+ node_added_cb (node, g_ptr_array_index (children, i),
+ action_group);
+ }
+
+ ephy_node_signal_connect_object (node, EPHY_NODE_CHILD_ADDED,
+ (EphyNodeCallback)node_added_cb,
+ (GObject *)action_group);
+ ephy_node_signal_connect_object (node, EPHY_NODE_CHILD_REMOVED,
+ (EphyNodeCallback)node_removed_cb,
+ (GObject *)action_group);
+
+ return action_group;
+}
diff --git a/src/bookmarks/ephy-topic-action-group.c b/src/bookmarks/ephy-topic-action-group.c
new file mode 100644
index 000000000..d8885a2e9
--- /dev/null
+++ b/src/bookmarks/ephy-topic-action-group.c
@@ -0,0 +1,124 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright © 2005 Peter Harvey
+ *
+ * This file is part of Epiphany.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "ephy-topic-action-group.h"
+#include "ephy-topic-action.h"
+#include "ephy-node.h"
+#include "ephy-node-common.h"
+#include "ephy-bookmarks.h"
+#include "ephy-bookmarks-ui.h"
+#include "ephy-debug.h"
+
+#include <gtk/gtk.h>
+
+static void
+node_changed_cb (EphyNode *parent,
+ EphyNode *child,
+ guint property_id,
+ GtkActionGroup *action_group)
+{
+ GtkAction *action;
+ char name[EPHY_TOPIC_ACTION_NAME_BUFFER_SIZE];
+
+ EPHY_TOPIC_ACTION_NAME_PRINTF (name, child);
+
+ action = gtk_action_group_get_action (action_group, name);
+
+ if (property_id == EPHY_NODE_KEYWORD_PROP_NAME) {
+ ephy_topic_action_updated (EPHY_TOPIC_ACTION (action));
+ }
+}
+
+static void
+node_added_cb (EphyNode *parent,
+ EphyNode *child,
+ GtkActionGroup *action_group)
+{
+ GtkUIManager *manager;
+ GtkAction *action;
+ char name[EPHY_TOPIC_ACTION_NAME_BUFFER_SIZE];
+ char accel[256];
+
+ EPHY_TOPIC_ACTION_NAME_PRINTF (name, child);
+
+ manager = g_object_get_data ((GObject *)action_group, "ui-manager");
+
+ action = ephy_topic_action_new (child, manager, name);
+
+ g_snprintf (accel, sizeof (accel), "<Actions>/%s/%s",
+ gtk_action_group_get_name (action_group),
+ name);
+ gtk_action_set_accel_path (action, accel);
+
+ gtk_action_group_add_action (action_group, action);
+ g_object_unref (action);
+
+ ephy_topic_action_updated ((EphyTopicAction *)action);
+}
+
+static void
+node_removed_cb (EphyNode *parent,
+ EphyNode *child, guint index,
+ GtkActionGroup *action_group)
+{
+ GtkAction *action;
+ char name[EPHY_TOPIC_ACTION_NAME_BUFFER_SIZE];
+
+ EPHY_TOPIC_ACTION_NAME_PRINTF (name, child);
+
+ action = gtk_action_group_get_action (action_group, name);
+
+ if (action) {
+ gtk_action_group_remove_action (action_group, action);
+ }
+}
+
+GtkActionGroup *
+ephy_topic_action_group_new (EphyNode *node,
+ GtkUIManager *manager)
+{
+ GPtrArray *children;
+ GtkActionGroup *action_group;
+ guint i;
+
+ children = ephy_node_get_children (node);
+ action_group = gtk_action_group_new ("TpAc");
+
+ g_object_set_data ((GObject *)action_group, "ui-manager", manager);
+
+ for (i = 0; i < children->len; i++) {
+ node_added_cb (node, g_ptr_array_index (children, i),
+ action_group);
+ }
+
+ ephy_node_signal_connect_object (node, EPHY_NODE_CHILD_ADDED,
+ (EphyNodeCallback)node_added_cb,
+ (GObject *)action_group);
+ ephy_node_signal_connect_object (node, EPHY_NODE_CHILD_REMOVED,
+ (EphyNodeCallback)node_removed_cb,
+ (GObject *)action_group);
+ ephy_node_signal_connect_object (node, EPHY_NODE_CHILD_CHANGED,
+ (EphyNodeCallback)node_changed_cb,
+ (GObject *)action_group);
+
+ return (GtkActionGroup *)action_group;
+}
diff --git a/src/bookmarks/ephy-topic-action-group.h b/src/bookmarks/ephy-topic-action-group.h
new file mode 100644
index 000000000..86d6cfeee
--- /dev/null
+++ b/src/bookmarks/ephy-topic-action-group.h
@@ -0,0 +1,33 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright © 2005 Peter Harvey
+ *
+ * This file is part of Epiphany.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "ephy-link-action.h"
+#include "ephy-node.h"
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+GtkActionGroup *ephy_topic_action_group_new (EphyNode *node,
+ GtkUIManager *manager);
+
+G_END_DECLS
diff --git a/src/bookmarks/ephy-topic-action.c b/src/bookmarks/ephy-topic-action.c
new file mode 100644
index 000000000..cb48565fd
--- /dev/null
+++ b/src/bookmarks/ephy-topic-action.c
@@ -0,0 +1,331 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright © 2003, 2004 Marco Pesenti Gritti
+ * Copyright © 2003, 2004 Christian Persch
+ *
+ * This file is part of Epiphany.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "ephy-topic-action.h"
+#include "ephy-node.h"
+#include "ephy-node-common.h"
+#include "ephy-nodes-cover.h"
+#include "ephy-bookmarks.h"
+#include "ephy-bookmarks-ui.h"
+#include "ephy-bookmarks-menu.h"
+#include "ephy-shell.h"
+#include "ephy-gui.h"
+#include "ephy-debug.h"
+#include "ephy-dnd.h"
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include <string.h>
+
+#define EPHY_TOPIC_ACTION_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), EPHY_TYPE_TOPIC_ACTION, EphyTopicActionPrivate))
+
+struct _EphyTopicActionPrivate {
+ EphyNode *node;
+ GtkUIManager *manager;
+ guint merge_id;
+};
+
+enum {
+ PROP_0,
+ PROP_TOPIC,
+ PROP_MANAGER,
+ LAST_PROP
+};
+
+static GParamSpec *obj_properties[LAST_PROP];
+
+G_DEFINE_TYPE (EphyTopicAction, ephy_topic_action, GTK_TYPE_ACTION)
+
+static void
+ephy_topic_action_sync_label (GtkAction *action,
+ GParamSpec *pspec,
+ GtkWidget *proxy)
+{
+ GtkWidget *label = NULL;
+ GValue value = { 0, };
+ const char *label_text;
+
+ g_value_init (&value, G_TYPE_STRING);
+ g_object_get_property (G_OBJECT (action), "label", &value);
+
+ label_text = g_value_get_string (&value);
+
+ if (GTK_IS_MENU_ITEM (proxy)) {
+ label = gtk_bin_get_child (GTK_BIN (proxy));
+ } else {
+ g_warning ("Unknown widget");
+ return;
+ }
+
+ g_return_if_fail (label != NULL);
+
+ if (label_text) {
+ gtk_label_set_label (GTK_LABEL (label), label_text);
+ }
+
+ g_value_unset (&value);
+}
+
+static void
+erase_popup (EphyTopicAction *action)
+{
+ EphyTopicActionPrivate *priv = action->priv;
+
+ if (priv->merge_id != 0) {
+ gtk_ui_manager_remove_ui (priv->manager, priv->merge_id);
+ priv->merge_id = 0;
+ }
+}
+
+static void
+child_added_cb (EphyNode *node, EphyNode *child, GObject *object)
+{
+ EphyTopicAction *action = EPHY_TOPIC_ACTION (object);
+ erase_popup (action);
+}
+
+static void
+child_changed_cb (EphyNode *node,
+ EphyNode *child,
+ guint property,
+ GObject *object)
+{
+ EphyTopicAction *action = EPHY_TOPIC_ACTION (object);
+
+ erase_popup (action);
+}
+
+static void
+child_removed_cb (EphyNode *node,
+ EphyNode *child,
+ guint index,
+ GObject *object)
+{
+ EphyTopicAction *action = EPHY_TOPIC_ACTION (object);
+
+ erase_popup (action);
+}
+
+static void
+menu_destroy_cb (GtkWidget *menuitem,
+ gpointer user_data)
+{
+ /* Save the submenu from similar destruction,
+ * because it doesn't rightly belong to this menuitem. */
+ gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), NULL);
+}
+
+static void
+menu_init_cb (GtkWidget *menuitem,
+ EphyTopicAction *action)
+{
+ if (gtk_menu_item_get_submenu (GTK_MENU_ITEM (menuitem)) == NULL) {
+ g_signal_connect (menuitem, "destroy",
+ G_CALLBACK (menu_destroy_cb), NULL);
+ }
+}
+
+static void
+connect_proxy (GtkAction *action,
+ GtkWidget *proxy)
+{
+ GTK_ACTION_CLASS (ephy_topic_action_parent_class)->connect_proxy (action, proxy);
+
+ ephy_topic_action_sync_label (action, NULL, proxy);
+ g_signal_connect_object (action, "notify::label",
+ G_CALLBACK (ephy_topic_action_sync_label), proxy, 0);
+
+ if (GTK_IS_MENU_ITEM (proxy)) {
+ g_signal_connect (proxy, "map",
+ G_CALLBACK (menu_init_cb), action);
+ }
+}
+
+void
+ephy_topic_action_updated (EphyTopicAction *action)
+{
+ EphyTopicActionPrivate *priv = action->priv;
+ GValue value = { 0, };
+ const char *title;
+ int priority;
+
+ g_return_if_fail (priv->node != NULL);
+
+ priority = ephy_node_get_property_int
+ (priv->node, EPHY_NODE_KEYWORD_PROP_PRIORITY);
+
+ if (priority == EPHY_NODE_ALL_PRIORITY) {
+ title = _("Bookmarks");
+ } else {
+ title = ephy_node_get_property_string
+ (priv->node, EPHY_NODE_KEYWORD_PROP_NAME);
+ }
+
+ g_value_init (&value, G_TYPE_STRING);
+ g_value_set_static_string (&value, title);
+ g_object_set_property (G_OBJECT (action), "label", &value);
+ g_object_set_property (G_OBJECT (action), "tooltip", &value);
+ g_value_unset (&value);
+}
+
+EphyNode *
+ephy_topic_action_get_topic (EphyTopicAction *action)
+{
+ EphyTopicActionPrivate *priv = action->priv;
+
+ return priv->node;
+}
+
+void
+ephy_topic_action_set_topic (EphyTopicAction *action,
+ EphyNode *node)
+{
+ EphyTopicActionPrivate *priv = action->priv;
+ GObject *object = G_OBJECT (action);
+
+ g_return_if_fail (node != NULL);
+
+ if (priv->node == node) return;
+
+ if (priv->node != NULL) {
+ ephy_node_signal_disconnect_object
+ (priv->node, EPHY_NODE_CHILD_ADDED,
+ (EphyNodeCallback)child_added_cb, object);
+ ephy_node_signal_disconnect_object
+ (priv->node, EPHY_NODE_CHILD_CHANGED,
+ (EphyNodeCallback)child_changed_cb, object);
+ ephy_node_signal_disconnect_object
+ (priv->node, EPHY_NODE_CHILD_REMOVED,
+ (EphyNodeCallback)child_removed_cb, object);
+ }
+
+ ephy_node_signal_connect_object (node, EPHY_NODE_CHILD_ADDED,
+ (EphyNodeCallback)child_added_cb,
+ object);
+ ephy_node_signal_connect_object (node, EPHY_NODE_CHILD_CHANGED,
+ (EphyNodeCallback)child_changed_cb,
+ object);
+ ephy_node_signal_connect_object (node, EPHY_NODE_CHILD_REMOVED,
+ (EphyNodeCallback)child_removed_cb,
+ object);
+
+ priv->node = node;
+
+ erase_popup (action);
+
+ g_object_freeze_notify (object);
+ g_object_notify_by_pspec (object, obj_properties[PROP_TOPIC]);
+ ephy_topic_action_updated (action);
+ g_object_thaw_notify (object);
+}
+
+static void
+ephy_topic_action_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ EphyTopicAction *action = EPHY_TOPIC_ACTION (object);
+ EphyTopicActionPrivate *priv = action->priv;
+
+ switch (prop_id) {
+ case PROP_TOPIC:
+ ephy_topic_action_set_topic (action, g_value_get_pointer (value));
+ break;
+ case PROP_MANAGER:
+ priv->manager = g_value_get_object (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ephy_topic_action_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ EphyTopicAction *action = EPHY_TOPIC_ACTION (object);
+ EphyTopicActionPrivate *priv = action->priv;
+
+ switch (prop_id) {
+ case PROP_TOPIC:
+ g_value_set_pointer (value, priv->node);
+ break;
+ case PROP_MANAGER:
+ g_value_set_object (value, priv->manager);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ephy_topic_action_init (EphyTopicAction *action)
+{
+ action->priv = EPHY_TOPIC_ACTION_GET_PRIVATE (action);
+}
+
+static void
+ephy_topic_action_class_init (EphyTopicActionClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+ GtkActionClass *action_class = GTK_ACTION_CLASS (class);
+
+ action_class->connect_proxy = connect_proxy;
+
+ object_class->set_property = ephy_topic_action_set_property;
+ object_class->get_property = ephy_topic_action_get_property;
+
+ obj_properties[PROP_TOPIC] =
+ g_param_spec_pointer ("topic",
+ "Topic",
+ "Topic",
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
+
+ obj_properties[PROP_MANAGER] =
+ g_param_spec_object ("manager",
+ "Manager",
+ "UI Manager",
+ GTK_TYPE_UI_MANAGER,
+ G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
+
+ g_object_class_install_properties (object_class, LAST_PROP, obj_properties);
+
+ g_type_class_add_private (object_class, sizeof (EphyTopicActionPrivate));
+}
+
+GtkAction *
+ephy_topic_action_new (EphyNode *node,
+ GtkUIManager *manager,
+ const char *name)
+{
+ g_assert (name != NULL);
+
+ return GTK_ACTION (g_object_new (EPHY_TYPE_TOPIC_ACTION,
+ "name", name,
+ "topic", node,
+ "manager", manager,
+ NULL));
+}
diff --git a/src/bookmarks/ephy-topic-action.h b/src/bookmarks/ephy-topic-action.h
new file mode 100644
index 000000000..3322255c8
--- /dev/null
+++ b/src/bookmarks/ephy-topic-action.h
@@ -0,0 +1,69 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright © 2003, 2004 Marco Pesenti Gritti
+ * Copyright © 2003, 2004 Christian Persch
+ *
+ * This file is part of Epiphany.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "ephy-link-action.h"
+#include "ephy-node.h"
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define EPHY_TYPE_TOPIC_ACTION (ephy_topic_action_get_type ())
+#define EPHY_TOPIC_ACTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EPHY_TYPE_TOPIC_ACTION, EphyTopicAction))
+#define EPHY_TOPIC_ACTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EPHY_TYPE_TOPIC_ACTION, EphyTopicActionClass))
+#define EPHY_IS_TOPIC_ACTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EPHY_TYPE_TOPIC_ACTION))
+#define EPHY_IS_TOPIC_ACTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), EPHY_TYPE_TOPIC_ACTION))
+#define EPHY_TOPIC_ACTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), EPHY_TYPE_TOPIC_ACTION, EphyTopicActionClass))
+
+typedef struct _EphyTopicAction EphyTopicAction;
+typedef struct _EphyTopicActionPrivate EphyTopicActionPrivate;
+typedef struct _EphyTopicActionClass EphyTopicActionClass;
+
+struct _EphyTopicAction
+{
+ GtkAction parent_instance;
+
+ /*< private >*/
+ EphyTopicActionPrivate *priv;
+};
+
+struct _EphyTopicActionClass
+{
+ EphyLinkActionClass parent_class;
+};
+
+
+GType ephy_topic_action_get_type (void);
+
+GtkAction *ephy_topic_action_new (EphyNode *node,
+ GtkUIManager *manager,
+ const char *name);
+
+void ephy_topic_action_set_topic (EphyTopicAction *action,
+ EphyNode *node);
+
+EphyNode *ephy_topic_action_get_topic (EphyTopicAction *action);
+
+void ephy_topic_action_updated (EphyTopicAction *action);
+
+G_END_DECLS
diff --git a/src/bookmarks/ephy-topics-entry.c b/src/bookmarks/ephy-topics-entry.c
new file mode 100644
index 000000000..df73ddf6c
--- /dev/null
+++ b/src/bookmarks/ephy-topics-entry.c
@@ -0,0 +1,597 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright © 2002-2004 Marco Pesenti Gritti <mpeseng@tin.it>
+ * Copyright © 2005, 2006 Peter Harvey <pah06@uow.edu.au>
+ *
+ * This file is part of Epiphany.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "ephy-topics-entry.h"
+#include "ephy-nodes-cover.h"
+#include "ephy-node-common.h"
+#include "ephy-bookmarks.h"
+#include "ephy-debug.h"
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include <string.h>
+
+struct _EphyTopicsEntry {
+ GtkEntry parent_instance;
+
+ EphyBookmarks *bookmarks;
+ EphyNode *bookmark;
+ GtkListStore *store;
+ GtkEntryCompletion *completion;
+ gboolean lock;
+ char *create;
+ char *key;
+};
+
+enum {
+ PROP_0,
+ PROP_BOOKMARKS,
+ PROP_BOOKMARK,
+ LAST_PROP
+};
+
+static GParamSpec *obj_properties[LAST_PROP];
+
+enum {
+ COLUMN_NODE,
+ COLUMN_KEY,
+ COLUMN_TITLE,
+ COLUMNS
+};
+
+G_DEFINE_TYPE (EphyTopicsEntry, ephy_topics_entry, GTK_TYPE_ENTRY)
+
+static EphyNode *
+find_topic (EphyTopicsEntry * entry,
+ const char *key)
+{
+ EphyNode *node = NULL;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GValue value = { 0, };
+ gboolean valid;
+
+ /* Loop through our table and set/unset topics appropriately */
+ model = GTK_TREE_MODEL (entry->store);
+ valid = gtk_tree_model_get_iter_first (model, &iter);
+ while (valid && node == NULL) {
+ gtk_tree_model_get_value (model, &iter, COLUMN_KEY, &value);
+ if (strcmp (g_value_get_string (&value), key) == 0) {
+ g_value_unset (&value);
+ gtk_tree_model_get_value (model, &iter, COLUMN_NODE, &value);
+ node = g_value_get_pointer (&value);
+ }
+ g_value_unset (&value);
+ valid = gtk_tree_model_iter_next (model, &iter);
+ }
+
+ return node;
+}
+
+static void
+insert_text (EphyTopicsEntry *entry,
+ const char *title)
+{
+ GtkEditable *editable = GTK_EDITABLE (entry);
+
+ const gchar *text = gtk_entry_get_text (GTK_ENTRY (entry));
+ const gchar *midpoint = g_utf8_offset_to_pointer (text, gtk_editable_get_position (editable));
+ const gchar *start = g_utf8_strrchr (text, (gssize)(midpoint - text), ',');
+ const gchar *end = g_utf8_strchr (midpoint, -1, ',');
+ int startpos, endpos;
+
+ if (start == NULL)
+ startpos = 0;
+ else if (g_unichar_isspace (g_utf8_get_char (g_utf8_next_char (start))))
+ startpos = g_utf8_pointer_to_offset (text, start) + 2;
+ else
+ startpos = g_utf8_pointer_to_offset (text, start) + 1;
+
+ if (end == NULL)
+ endpos = -1;
+ else if (g_unichar_isspace (g_utf8_get_char (g_utf8_next_char (end))))
+ endpos = g_utf8_pointer_to_offset (text, end) + 2;
+ else
+ endpos = g_utf8_pointer_to_offset (text, end) + 1;
+
+ /* Replace the text in the current position with the title */
+ gtk_editable_delete_text (editable, startpos, endpos);
+ gtk_editable_insert_text (editable, title, strlen (title), &startpos);
+ gtk_editable_insert_text (editable, ", ", 2, &startpos);
+ gtk_editable_set_position (editable, startpos);
+}
+
+/* Updates the text entry and the completion model to match the database */
+static void
+update_widget (EphyTopicsEntry *entry)
+{
+ GtkEditable *editable = GTK_EDITABLE (entry);
+
+ EphyNode *node;
+ GPtrArray *children, *topics;
+ GtkTreeIter iter;
+ guint i;
+ gint priority, pos;
+ const char *title;
+ char *tmp1, *tmp2;
+ gboolean is_focus;
+
+ /* Prevent any changes to the database */
+ if (entry->lock) return;
+ entry->lock = TRUE;
+
+ node = ephy_bookmarks_get_keywords (entry->bookmarks);
+ children = ephy_node_get_children (node);
+ topics = g_ptr_array_sized_new (children->len);
+
+ for (i = 0; i < children->len; i++) {
+ node = g_ptr_array_index (children, i);
+
+ priority = ephy_node_get_property_int
+ (node, EPHY_NODE_KEYWORD_PROP_PRIORITY);
+ if (priority != EPHY_NODE_NORMAL_PRIORITY)
+ continue;
+
+ g_ptr_array_add (topics, node);
+ }
+
+ g_ptr_array_sort (topics, ephy_bookmarks_compare_topic_pointers);
+ gtk_list_store_clear (entry->store);
+
+ g_object_get (entry, "is-focus", &is_focus, NULL);
+ if (!is_focus) {
+ gtk_editable_delete_text (editable, 0, -1);
+ }
+
+ for (pos = 0, i = 0; i < topics->len; i++) {
+ node = g_ptr_array_index (topics, i);
+ title = ephy_node_get_property_string (node, EPHY_NODE_KEYWORD_PROP_NAME);
+
+ if (!is_focus && ephy_node_has_child (node, entry->bookmark)) {
+ if (pos > 0)
+ gtk_editable_insert_text (editable, ", ", -1, &pos);
+ gtk_editable_insert_text (editable, title, -1, &pos);
+ }
+
+ tmp1 = g_utf8_casefold (title, -1);
+ tmp2 = g_utf8_normalize (tmp1, -1, G_NORMALIZE_DEFAULT);
+ gtk_list_store_append (entry->store, &iter);
+ gtk_list_store_set (entry->store, &iter,
+ COLUMN_NODE, node,
+ COLUMN_TITLE, title,
+ COLUMN_KEY, tmp2,
+ -1);
+ g_free (tmp2);
+ g_free (tmp1);
+ }
+
+ if (!is_focus) {
+ gtk_editable_set_position (editable, -1);
+ }
+
+ g_ptr_array_free (topics, TRUE);
+
+ entry->lock = FALSE;
+}
+
+/* Updates the bookmarks database to match what is in the text entry */
+static void
+update_database (EphyTopicsEntry *entry)
+{
+ EphyNode *node;
+ const char *text;
+ char **split;
+ char *tmp;
+ gint i;
+
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GValue value = { 0, };
+ gboolean valid;
+
+ /* Prevent any changes to the text entry or completion model */
+ if (entry->lock) return;
+ entry->lock = TRUE;
+
+ /* Get the list of strings input by the user */
+ text = gtk_entry_get_text (GTK_ENTRY (entry));
+ split = g_strsplit (text, ",", 0);
+ for (i = 0; split[i]; i++) {
+ g_strstrip (split[i]);
+
+ tmp = g_utf8_casefold (split[i], -1);
+ g_free (split[i]);
+
+ split[i] = g_utf8_normalize (tmp, -1, G_NORMALIZE_DEFAULT);
+ g_free (tmp);
+ }
+
+ /* Loop through the completion model and set/unset topics appropriately */
+ model = GTK_TREE_MODEL (entry->store);
+ valid = gtk_tree_model_get_iter_first (model, &iter);
+ while (valid) {
+ gtk_tree_model_get_value (model, &iter, COLUMN_NODE, &value);
+ node = g_value_get_pointer (&value);
+ g_value_unset (&value);
+
+ gtk_tree_model_get_value (model, &iter, COLUMN_KEY, &value);
+ text = g_value_get_string (&value);
+
+ for (i = 0; split[i]; i++)
+ if (strcmp (text, split[i]) == 0)
+ break;
+
+ if (split[i]) {
+ split[i][0] = 0;
+ ephy_bookmarks_set_keyword (entry->bookmarks, node, entry->bookmark);
+ } else {
+ ephy_bookmarks_unset_keyword (entry->bookmarks, node, entry->bookmark);
+ }
+
+ g_value_unset (&value);
+ valid = gtk_tree_model_iter_next (model, &iter);
+ }
+
+ g_strfreev (split);
+
+ entry->lock = FALSE;
+}
+
+/* Updates the search key and topic creation action */
+static void
+update_key (EphyTopicsEntry *entry)
+{
+ GtkEditable *editable = GTK_EDITABLE (entry);
+ char *input;
+
+ const gchar *text = gtk_entry_get_text (GTK_ENTRY (entry));
+ const gchar *midpoint = g_utf8_offset_to_pointer (text, gtk_editable_get_position (editable));
+ const gchar *start = g_utf8_strrchr (text, (gssize)(midpoint - text), ',');
+ const gchar *end = g_utf8_strchr (midpoint, -1, ',');
+
+ if (start == NULL)
+ start = text;
+ else if (g_unichar_isspace (g_utf8_get_char (g_utf8_next_char (start))))
+ start = g_utf8_next_char (g_utf8_next_char (start));
+ else
+ start = g_utf8_next_char (start);
+
+ if (end == NULL)
+ end = text + strlen (text);
+
+ /* If there was something we could create, then delete the action. */
+ if (entry->create)
+ gtk_entry_completion_delete_action (entry->completion, 0);
+
+ g_free (entry->create);
+ g_free (entry->key);
+ entry->create = 0;
+ entry->key = 0;
+
+ /* Set the entry->create and entry->key appropriately. */
+ if (start != end) {
+ input = g_strndup (start, end - start);
+ g_strstrip (input);
+ entry->create = input;
+
+ input = g_utf8_casefold (input, -1);
+ entry->key = g_utf8_normalize (input, -1, G_NORMALIZE_DEFAULT);
+ g_free (input);
+
+ if (entry->create[0] == '\0' ||
+ find_topic (entry, entry->key) != NULL) {
+ g_free (entry->create);
+ entry->create = 0;
+ }
+ /* If there is something we can create, then setup the action. */
+ else {
+ input = g_strdup_printf (_("Create topic “%s”"), entry->create);
+ gtk_entry_completion_insert_action_text (entry->completion, 0, input);
+ g_free (input);
+ }
+ }
+}
+
+static gboolean
+match_func (GtkEntryCompletion *completion,
+ const gchar *key,
+ GtkTreeIter *iter,
+ gpointer user_data)
+{
+ EphyTopicsEntry *entry = EPHY_TOPICS_ENTRY (gtk_entry_completion_get_entry (completion));
+ GtkTreeModel *model = gtk_entry_completion_get_model (completion);
+
+ gboolean result;
+ GValue value = { 0, };
+ EphyNode *node;
+
+ if (entry->key == NULL) {
+ return FALSE;
+ }
+
+ /* If no node at all (this happens for unknown reasons) then don't show. */
+ gtk_tree_model_get_value (model, iter, COLUMN_NODE, &value);
+ node = g_value_get_pointer (&value);
+ g_value_unset (&value);
+ if (node == NULL) {
+ result = FALSE;
+ }
+ /* If it's already selected, don't show it unless we're editing it. */
+ else if (ephy_node_has_child (node, entry->bookmark)) {
+ gtk_tree_model_get_value (model, iter, COLUMN_KEY, &value);
+ result = (strcmp (g_value_get_string (&value), entry->key) == 0);
+ g_value_unset (&value);
+ }
+ /* If it's not selected, show it if it matches. */
+ else {
+ gtk_tree_model_get_value (model, iter, COLUMN_KEY, &value);
+ result = (g_str_has_prefix (g_value_get_string (&value), entry->key));
+ g_value_unset (&value);
+ }
+
+ return result;
+}
+
+static void
+action_cb (GtkEntryCompletion *completion,
+ gint index,
+ gpointer user_data)
+{
+ EphyTopicsEntry *entry = EPHY_TOPICS_ENTRY (gtk_entry_completion_get_entry (completion));
+ char *title;
+
+ title = g_strdup (entry->create);
+
+ ephy_bookmarks_add_keyword (entry->bookmarks, title);
+ update_widget (entry);
+
+ insert_text (entry, title);
+ g_free (title);
+}
+
+static gboolean
+match_selected_cb (GtkEntryCompletion *completion,
+ GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gpointer user_data)
+{
+ EphyTopicsEntry *entry = EPHY_TOPICS_ENTRY (gtk_entry_completion_get_entry (completion));
+ GValue value = { 0, };
+
+ gtk_tree_model_get_value (model, iter, COLUMN_TITLE, &value);
+ insert_text (entry, g_value_get_string (&value));
+ g_value_unset (&value);
+
+ return TRUE;
+}
+
+static void
+activate_cb (GtkEditable *editable,
+ gpointer user_data)
+{
+ EphyTopicsEntry *entry = EPHY_TOPICS_ENTRY (editable);
+ GtkEntryCompletion *completion = gtk_entry_get_completion (GTK_ENTRY (entry));
+
+ GValue value = { 0, };
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ gboolean valid;
+
+ if (entry->key == NULL || entry->key[0] == '\0') {
+ gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);
+ return;
+ } else {
+ gtk_entry_set_activates_default (GTK_ENTRY (entry), FALSE);
+ }
+
+ /* Loop through the completion model and find the first item to use, if any. */
+ model = GTK_TREE_MODEL (entry->store);
+ valid = gtk_tree_model_get_iter_first (model, &iter);
+ while (valid && !match_func (completion, NULL, &iter, NULL)) {
+ valid = gtk_tree_model_iter_next (model, &iter);
+ }
+
+ if (valid) {
+ gtk_tree_model_get_value (model, &iter, COLUMN_TITLE, &value);
+
+ /* See if there were any others. */
+ valid = gtk_tree_model_iter_next (model, &iter);
+ while (valid && !match_func (completion, NULL, &iter, NULL)) {
+ valid = gtk_tree_model_iter_next (model, &iter);
+ }
+
+ if (!valid) {
+ insert_text (EPHY_TOPICS_ENTRY (editable), g_value_get_string (&value));
+ g_value_unset (&value);
+ }
+ }
+}
+
+static void
+tree_changed_cb (EphyBookmarks *bookmarks,
+ EphyTopicsEntry *entry)
+{
+ update_widget (entry);
+}
+
+static void
+node_added_cb (EphyNode *parent,
+ EphyNode *child,
+ GObject *object)
+{
+ update_widget (EPHY_TOPICS_ENTRY (object));
+}
+
+static void
+node_changed_cb (EphyNode *parent,
+ EphyNode *child,
+ guint property_id,
+ GObject *object)
+{
+ update_widget (EPHY_TOPICS_ENTRY (object));
+}
+
+static void
+node_removed_cb (EphyNode *parent,
+ EphyNode *child,
+ guint index,
+ GObject *object)
+{
+ update_widget (EPHY_TOPICS_ENTRY (object));
+}
+
+static void
+ephy_topics_entry_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ EphyTopicsEntry *entry = EPHY_TOPICS_ENTRY (object);
+ EphyNode *node;
+
+ switch (prop_id) {
+ case PROP_BOOKMARKS:
+ entry->bookmarks = g_value_get_object (value);
+ node = ephy_bookmarks_get_keywords (entry->bookmarks);
+ ephy_node_signal_connect_object (node, EPHY_NODE_CHILD_ADDED,
+ (EphyNodeCallback)node_added_cb, object);
+ ephy_node_signal_connect_object (node, EPHY_NODE_CHILD_CHANGED,
+ (EphyNodeCallback)node_changed_cb, object);
+ ephy_node_signal_connect_object (node, EPHY_NODE_CHILD_REMOVED,
+ (EphyNodeCallback)node_removed_cb, object);
+ g_signal_connect_object (entry->bookmarks, "tree-changed",
+ G_CALLBACK (tree_changed_cb), entry,
+ G_CONNECT_AFTER);
+ break;
+ case PROP_BOOKMARK:
+ entry->bookmark = g_value_get_pointer (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static GObject *
+ephy_topics_entry_constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_params)
+{
+ GObject *object;
+ EphyTopicsEntry *entry;
+
+ object = G_OBJECT_CLASS (ephy_topics_entry_parent_class)->constructor (type,
+ n_construct_properties,
+ construct_params);
+ entry = EPHY_TOPICS_ENTRY (object);
+
+ entry->store = gtk_list_store_new (3, G_TYPE_POINTER, G_TYPE_STRING, G_TYPE_STRING);
+ entry->completion = gtk_entry_completion_new ();
+
+ gtk_entry_completion_set_model (entry->completion, GTK_TREE_MODEL (entry->store));
+ gtk_entry_completion_set_text_column (entry->completion, COLUMN_TITLE);
+ gtk_entry_completion_set_popup_completion (entry->completion, TRUE);
+ gtk_entry_completion_set_popup_single_match (entry->completion, TRUE);
+ gtk_entry_completion_set_match_func (entry->completion, match_func, NULL, NULL);
+ gtk_entry_set_completion (GTK_ENTRY (entry), entry->completion);
+
+ g_signal_connect (entry->completion, "match-selected",
+ G_CALLBACK (match_selected_cb), NULL);
+ g_signal_connect (entry->completion, "action-activated",
+ G_CALLBACK (action_cb), NULL);
+
+ g_signal_connect (object, "activate",
+ G_CALLBACK (activate_cb), NULL);
+
+ g_signal_connect (object, "changed",
+ G_CALLBACK (update_database), NULL);
+ g_signal_connect (object, "notify::is-focus",
+ G_CALLBACK (update_widget), NULL);
+ g_signal_connect (object, "notify::cursor-position",
+ G_CALLBACK (update_key), NULL);
+ g_signal_connect (object, "notify::text",
+ G_CALLBACK (update_key), NULL);
+
+ update_key (entry);
+ update_widget (entry);
+
+ return object;
+}
+
+static void
+ephy_topics_entry_init (EphyTopicsEntry *entry)
+{
+}
+
+static void
+ephy_topics_entry_finalize (GObject *object)
+{
+ EphyTopicsEntry *entry = EPHY_TOPICS_ENTRY (object);
+
+ g_free (entry->create);
+ g_free (entry->key);
+
+ G_OBJECT_CLASS (ephy_topics_entry_parent_class)->finalize (object);
+}
+
+GtkWidget *
+ephy_topics_entry_new (EphyBookmarks *bookmarks,
+ EphyNode *bookmark)
+{
+ EphyTopicsEntry *entry;
+
+ g_assert (bookmarks != NULL);
+
+ entry = EPHY_TOPICS_ENTRY (g_object_new
+ (EPHY_TYPE_TOPICS_ENTRY,
+ "bookmarks", bookmarks,
+ "bookmark", bookmark,
+ NULL));
+
+ return GTK_WIDGET (entry);
+}
+
+static void
+ephy_topics_entry_class_init (EphyTopicsEntryClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->set_property = ephy_topics_entry_set_property;
+ object_class->constructor = ephy_topics_entry_constructor;
+ object_class->finalize = ephy_topics_entry_finalize;
+
+ obj_properties[PROP_BOOKMARKS] =
+ g_param_spec_object ("bookmarks",
+ "Bookmarks set",
+ "Bookmarks set",
+ EPHY_TYPE_BOOKMARKS,
+ G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+ obj_properties[PROP_BOOKMARK] =
+ g_param_spec_pointer ("bookmark",
+ "Bookmark",
+ "Bookmark",
+ G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (object_class, LAST_PROP, obj_properties);
+}
diff --git a/src/bookmarks/ephy-topics-entry.h b/src/bookmarks/ephy-topics-entry.h
new file mode 100644
index 000000000..4ca18e2b9
--- /dev/null
+++ b/src/bookmarks/ephy-topics-entry.h
@@ -0,0 +1,34 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright © 2002 Marco Pesenti Gritti <mpeseng@tin.it>
+ *
+ * This file is part of Epiphany.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "ephy-bookmarks.h"
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define EPHY_TYPE_TOPICS_ENTRY (ephy_topics_entry_get_type ())
+G_DECLARE_FINAL_TYPE (EphyTopicsEntry, ephy_topics_entry, EPHY, TOPICS_ENTRY, GtkEntry);
+
+GtkWidget *ephy_topics_entry_new (EphyBookmarks *bookmarks, EphyNode *bookmark);
+
+G_END_DECLS
diff --git a/src/bookmarks/ephy-topics-palette.c b/src/bookmarks/ephy-topics-palette.c
new file mode 100644
index 000000000..1b6c1254a
--- /dev/null
+++ b/src/bookmarks/ephy-topics-palette.c
@@ -0,0 +1,337 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright © 2002-2004 Marco Pesenti Gritti <mpeseng@tin.it>
+ * Copyright © 2005 Peter Harvey <pah06@uow.edu.au>
+ *
+ * This file is part of Epiphany.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "ephy-topics-palette.h"
+#include "ephy-nodes-cover.h"
+#include "ephy-node-common.h"
+#include "ephy-bookmarks.h"
+#include "ephy-debug.h"
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+
+struct _EphyTopicsPalette {
+ GtkListStore parent_instance;
+
+ /* construct properties */
+ EphyBookmarks *bookmarks;
+ EphyNode *bookmark;
+
+ /* non-construct property */
+ int mode;
+};
+
+enum {
+ PROP_0,
+ PROP_BOOKMARKS,
+ PROP_BOOKMARK,
+ PROP_MODE,
+ LAST_PROP
+};
+
+static GParamSpec *obj_properties[LAST_PROP];
+
+enum {
+ MODE_GROUPED,
+ MODE_LIST,
+ MODES
+};
+
+G_DEFINE_TYPE (EphyTopicsPalette, ephy_topics_palette, GTK_TYPE_LIST_STORE)
+
+static void
+append_topics (EphyTopicsPalette *self,
+ GtkTreeIter *iter,
+ gboolean *valid,
+ gboolean *first,
+ GPtrArray *topics)
+{
+ EphyNode *node;
+ const char *title;
+ guint i;
+
+ if (topics->len == 0) {
+ return;
+ }
+
+ if (!*first) {
+ if (!*valid) gtk_list_store_append (GTK_LIST_STORE (self), iter);
+ gtk_list_store_set (GTK_LIST_STORE (self), iter,
+ EPHY_TOPICS_PALETTE_COLUMN_TITLE, NULL,
+ EPHY_TOPICS_PALETTE_COLUMN_NODE, NULL,
+ -1);
+ *valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (self), iter);
+ }
+
+ for (i = 0; i < topics->len; i++) {
+ node = g_ptr_array_index (topics, i);
+ title = ephy_node_get_property_string (node, EPHY_NODE_KEYWORD_PROP_NAME);
+
+ if (!*valid) gtk_list_store_append (GTK_LIST_STORE (self), iter);
+ gtk_list_store_set (GTK_LIST_STORE (self), iter,
+ EPHY_TOPICS_PALETTE_COLUMN_TITLE, title,
+ EPHY_TOPICS_PALETTE_COLUMN_NODE, node,
+ EPHY_TOPICS_PALETTE_COLUMN_SELECTED, ephy_node_has_child (node, self->bookmark),
+ -1);
+ *valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (self), iter);
+ *first = FALSE;
+ }
+}
+
+void
+ephy_topics_palette_update_list (EphyTopicsPalette *self)
+{
+ GPtrArray *children, *bookmarks, *topics;
+ EphyNode *node;
+ GtkTreeIter iter;
+ guint i;
+ gint priority;
+ gboolean valid, first;
+
+ valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (self), &iter);
+ first = TRUE;
+
+ if (self->mode == MODE_LIST) {
+ /* Allocate and fill the suggestions array. */
+ node = ephy_bookmarks_get_keywords (self->bookmarks);
+ children = ephy_node_get_children (node);
+ topics = g_ptr_array_sized_new (children->len);
+ for (i = 0; i < children->len; i++) {
+ node = g_ptr_array_index (children, i);
+
+ priority = ephy_node_get_property_int (node, EPHY_NODE_KEYWORD_PROP_PRIORITY);
+ if (priority != EPHY_NODE_NORMAL_PRIORITY)
+ continue;
+
+ g_ptr_array_add (topics, node);
+ }
+
+ g_ptr_array_sort (topics, ephy_bookmarks_compare_topic_pointers);
+ append_topics (self, &iter, &valid, &first, topics);
+ g_ptr_array_free (topics, TRUE);
+ } else if (self->mode == MODE_GROUPED) {
+ GPtrArray *suggested, *selected;
+
+ /* Allocate and fill the bookmarks array. */
+ node = ephy_bookmarks_get_bookmarks (self->bookmarks);
+ children = ephy_node_get_children (node);
+ bookmarks = g_ptr_array_sized_new (children->len);
+ for (i = 0; i < children->len; i++) {
+ g_ptr_array_add (bookmarks, g_ptr_array_index (children, i));
+ }
+
+ /* Allocate and fill the topics array. */
+ node = ephy_bookmarks_get_keywords (self->bookmarks);
+ children = ephy_node_get_children (node);
+ topics = g_ptr_array_sized_new (children->len);
+ suggested = g_ptr_array_sized_new (children->len);
+ selected = g_ptr_array_sized_new (children->len);
+ for (i = 0; i < children->len; i++) {
+ node = g_ptr_array_index (children, i);
+
+ priority = ephy_node_get_property_int (node, EPHY_NODE_KEYWORD_PROP_PRIORITY);
+ if (priority != EPHY_NODE_NORMAL_PRIORITY)
+ continue;
+
+ /* We'll consider only bookmarks covered by the same topics as our bookmark. */
+ if (ephy_node_has_child (node, self->bookmark)) {
+ ephy_nodes_remove_not_covered (node, bookmarks);
+ g_ptr_array_add (selected, node);
+ }
+ /* We'll onsider only topics that are not already selected for our bookmark. */
+ else{
+ g_ptr_array_add (topics, node);
+ }
+ }
+
+ /* Get the minimum cover of topics for the bookmarks. */
+ suggested = ephy_nodes_get_covering (topics, bookmarks, suggested, 0, 0);
+
+ for (i = 0; i < suggested->len; i++) {
+ g_ptr_array_remove_fast (topics, g_ptr_array_index (suggested, i));
+ }
+
+ /* Add any topics which cover the bookmarks completely in their own right, or
+ have no bookmarks currently associated with it. */
+ for (i = 0; i < topics->len; i++) {
+ node = g_ptr_array_index (topics, i);
+ if (!ephy_node_has_child (node, self->bookmark) &&
+ ephy_nodes_covered (node, bookmarks)) {
+ g_ptr_array_add (suggested, node);
+ g_ptr_array_remove_index_fast (topics, i);
+ i--;
+ }
+ }
+
+ g_ptr_array_sort (selected, ephy_bookmarks_compare_topic_pointers);
+ g_ptr_array_sort (suggested, ephy_bookmarks_compare_topic_pointers);
+ g_ptr_array_sort (topics, ephy_bookmarks_compare_topic_pointers);
+ append_topics (self, &iter, &valid, &first, selected);
+ append_topics (self, &iter, &valid, &first, suggested);
+ append_topics (self, &iter, &valid, &first, topics);
+ g_ptr_array_free (selected, TRUE);
+ g_ptr_array_free (suggested, TRUE);
+ g_ptr_array_free (bookmarks, TRUE);
+ g_ptr_array_free (topics, TRUE);
+ }
+
+ while (valid) {
+ valid = gtk_list_store_remove (GTK_LIST_STORE (self), &iter);
+ }
+}
+
+static void
+tree_changed_cb (EphyBookmarks *bookmarks,
+ EphyTopicsPalette *self)
+{
+ ephy_topics_palette_update_list (self);
+}
+
+static void
+node_added_cb (EphyNode *parent,
+ EphyNode *child,
+ EphyTopicsPalette *self)
+{
+ ephy_topics_palette_update_list (self);
+}
+
+static void
+node_changed_cb (EphyNode *parent,
+ EphyNode *child,
+ guint property_id,
+ EphyTopicsPalette *self)
+{
+ ephy_topics_palette_update_list (self);
+}
+
+static void
+node_removed_cb (EphyNode *parent,
+ EphyNode *child,
+ guint index,
+ EphyTopicsPalette *self)
+{
+ ephy_topics_palette_update_list (self);
+}
+
+static void
+ephy_topics_palette_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ EphyTopicsPalette *self = EPHY_TOPICS_PALETTE (object);
+ EphyNode *node;
+
+ switch (prop_id) {
+ case PROP_BOOKMARKS:
+ self->bookmarks = g_value_get_object (value);
+ node = ephy_bookmarks_get_keywords (self->bookmarks);
+ ephy_node_signal_connect_object (node, EPHY_NODE_CHILD_ADDED,
+ (EphyNodeCallback)node_added_cb, object);
+ ephy_node_signal_connect_object (node, EPHY_NODE_CHILD_CHANGED,
+ (EphyNodeCallback)node_changed_cb, object);
+ ephy_node_signal_connect_object (node, EPHY_NODE_CHILD_REMOVED,
+ (EphyNodeCallback)node_removed_cb, object);
+ g_signal_connect_object (self->bookmarks, "tree-changed",
+ G_CALLBACK (tree_changed_cb), self,
+ G_CONNECT_AFTER);
+ break;
+ case PROP_BOOKMARK:
+ self->bookmark = g_value_get_pointer (value);
+ break;
+ case PROP_MODE:
+ self->mode = g_value_get_int (value);
+ ephy_topics_palette_update_list (self);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static GObject *
+ephy_topics_palette_constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_params)
+
+{
+ GObject *object;
+ GType types[3] = { G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_BOOLEAN };
+
+ object = G_OBJECT_CLASS (ephy_topics_palette_parent_class)->constructor (type,
+ n_construct_properties,
+ construct_params);
+ gtk_list_store_set_column_types (GTK_LIST_STORE (object), 3, types);
+ ephy_topics_palette_update_list (EPHY_TOPICS_PALETTE (object));
+
+ return object;
+}
+
+static void
+ephy_topics_palette_init (EphyTopicsPalette *self)
+{
+}
+
+EphyTopicsPalette *
+ephy_topics_palette_new (EphyBookmarks *bookmarks,
+ EphyNode *bookmark)
+{
+ g_assert (bookmarks != NULL);
+
+ return g_object_new (EPHY_TYPE_TOPICS_PALETTE,
+ "bookmarks", bookmarks,
+ "bookmark", bookmark,
+ NULL);
+}
+
+static void
+ephy_topics_palette_class_init (EphyTopicsPaletteClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->set_property = ephy_topics_palette_set_property;
+ object_class->constructor = ephy_topics_palette_constructor;
+
+ obj_properties[PROP_BOOKMARKS] =
+ g_param_spec_object ("bookmarks",
+ "Bookmarks set",
+ "Bookmarks set",
+ EPHY_TYPE_BOOKMARKS,
+ G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+ obj_properties[PROP_BOOKMARK] =
+ g_param_spec_pointer ("bookmark",
+ "Bookmark",
+ "Bookmark",
+ G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+ obj_properties[PROP_MODE] =
+ g_param_spec_int ("mode",
+ "Mode",
+ "Mode",
+ 0, MODES - 1, 0,
+ G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (object_class, LAST_PROP, obj_properties);
+}
diff --git a/src/bookmarks/ephy-topics-palette.h b/src/bookmarks/ephy-topics-palette.h
new file mode 100644
index 000000000..36d115717
--- /dev/null
+++ b/src/bookmarks/ephy-topics-palette.h
@@ -0,0 +1,44 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright © 2002 Marco Pesenti Gritti <mpeseng@tin.it>
+ *
+ * This file is part of Epiphany.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "ephy-bookmarks.h"
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define EPHY_TYPE_TOPICS_PALETTE (ephy_topics_palette_get_type ())
+G_DECLARE_FINAL_TYPE (EphyTopicsPalette, ephy_topics_palette, EPHY, TOPICS_PALETTE, GtkListStore);
+
+enum
+{
+ EPHY_TOPICS_PALETTE_COLUMN_TITLE,
+ EPHY_TOPICS_PALETTE_COLUMN_NODE,
+ EPHY_TOPICS_PALETTE_COLUMN_SELECTED,
+ EPHY_TOPICS_PALETTE_COLUMNS
+};
+
+EphyTopicsPalette *ephy_topics_palette_new (EphyBookmarks *bookmarks,
+ EphyNode *bookmark);
+
+void ephy_topics_palette_update_list (EphyTopicsPalette *self);
+
+G_END_DECLS
diff --git a/src/bookmarks/ephy-window-action.c b/src/bookmarks/ephy-window-action.c
new file mode 100644
index 000000000..fb85be411
--- /dev/null
+++ b/src/bookmarks/ephy-window-action.c
@@ -0,0 +1,108 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright © 2012 Igalia S.L.
+ *
+ * This file is part of Epiphany.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+#include "ephy-window-action.h"
+
+#include <gtk/gtk.h>
+
+#define EPHY_WINDOW_ACTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), EPHY_TYPE_WINDOW_ACTION, EphyWindowActionPrivate))
+
+struct _EphyWindowActionPrivate {
+ EphyWindow *window;
+};
+
+enum {
+ PROP_0,
+ PROP_WINDOW,
+ LAST_PROP
+};
+
+static GParamSpec *obj_properties[LAST_PROP];
+
+G_DEFINE_TYPE (EphyWindowAction, ephy_window_action, GTK_TYPE_ACTION)
+
+static void
+ephy_window_action_init (EphyWindowAction *action)
+{
+ action->priv = EPHY_WINDOW_ACTION_GET_PRIVATE (action);
+}
+
+static void
+ephy_window_action_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ EphyWindowAction *action = EPHY_WINDOW_ACTION (object);
+
+ switch (property_id) {
+ case PROP_WINDOW:
+ action->priv->window = EPHY_WINDOW (g_value_get_object (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+ephy_window_action_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ EphyWindowAction *action = EPHY_WINDOW_ACTION (object);
+
+ switch (property_id) {
+ case PROP_WINDOW:
+ g_value_set_object (value, action->priv->window);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+ephy_window_action_class_init (EphyWindowActionClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+ object_class->set_property = ephy_window_action_set_property;
+ object_class->get_property = ephy_window_action_get_property;
+
+ obj_properties[PROP_WINDOW] =
+ g_param_spec_object ("window",
+ NULL,
+ NULL,
+ G_TYPE_OBJECT,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT);
+
+ g_object_class_install_properties (object_class, LAST_PROP, obj_properties);
+
+ g_type_class_add_private (object_class, sizeof (EphyWindowActionPrivate));
+}
+
+EphyWindow *
+ephy_window_action_get_window (EphyWindowAction *action)
+{
+ g_return_val_if_fail (EPHY_IS_WINDOW_ACTION (action), NULL);
+
+ return action->priv->window;
+}
diff --git a/src/bookmarks/ephy-window-action.h b/src/bookmarks/ephy-window-action.h
new file mode 100644
index 000000000..fc347cbae
--- /dev/null
+++ b/src/bookmarks/ephy-window-action.h
@@ -0,0 +1,55 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright © 2012 Igalia S.L.
+ *
+ * This file is part of Epiphany.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "ephy-window.h"
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define EPHY_TYPE_WINDOW_ACTION (ephy_window_action_get_type ())
+#define EPHY_WINDOW_ACTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EPHY_TYPE_WINDOW_ACTION, EphyWindowAction))
+#define EPHY_WINDOW_ACTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EPHY_TYPE_WINDOW_ACTION, EphyWindowActionClass))
+#define EPHY_IS_WINDOW_ACTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EPHY_TYPE_WINDOW_ACTION))
+#define EPHY_IS_WINDOW_ACTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), EPHY_TYPE_WINDOW_ACTION))
+#define EPHY_WINDOW_ACTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), EPHY_TYPE_WINDOW_ACTION, EphyWindowActionClass))
+
+typedef struct _EphyWindowAction EphyWindowAction;
+typedef struct _EphyWindowActionPrivate EphyWindowActionPrivate;
+typedef struct _EphyWindowActionClass EphyWindowActionClass;
+
+struct _EphyWindowAction {
+ GtkAction parent;
+
+ /*< private >*/
+ EphyWindowActionPrivate *priv;
+};
+
+struct _EphyWindowActionClass {
+ GtkActionClass parent_class;
+};
+
+GType ephy_window_action_get_type (void);
+
+EphyWindow *ephy_window_action_get_window (EphyWindowAction *action);
+
+G_END_DECLS
diff --git a/src/clear-data-dialog.c b/src/clear-data-dialog.c
index 7b20f9a84..fa39b1b3f 100644
--- a/src/clear-data-dialog.c
+++ b/src/clear-data-dialog.c
@@ -2,18 +2,20 @@
/*
* Copyright © 2013 Red Hat, Inc.
*
- * This program is free software; you can redistribute it and/or modify
+ * This file is part of Epiphany.
+ *
+ * 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, or (at your option)
- * any later version.
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
diff --git a/src/clear-data-dialog.h b/src/clear-data-dialog.h
index e63f1e869..220885335 100644
--- a/src/clear-data-dialog.h
+++ b/src/clear-data-dialog.h
@@ -2,18 +2,20 @@
/*
* Copyright © 2013 Red Hat, Inc.
*
- * This program is free software; you can redistribute it and/or modify
+ * This file is part of Epiphany.
+ *
+ * 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, or (at your option)
- * any later version.
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
diff --git a/src/cookies-dialog.c b/src/cookies-dialog.c
index 7b6dac873..b8e138451 100644
--- a/src/cookies-dialog.c
+++ b/src/cookies-dialog.c
@@ -2,18 +2,20 @@
/*
* Copyright © 2013 Red Hat, Inc.
*
- * This program is free software; you can redistribute it and/or modify
+ * This file is part of Epiphany.
+ *
+ * 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, or (at your option)
- * any later version.
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
diff --git a/src/cookies-dialog.h b/src/cookies-dialog.h
index 8a444cd32..9724430b0 100644
--- a/src/cookies-dialog.h
+++ b/src/cookies-dialog.h
@@ -2,18 +2,20 @@
/*
* Copyright © 2013 Red Hat, Inc.
*
- * This program is free software; you can redistribute it and/or modify
+ * This file is part of Epiphany.
+ *
+ * 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, or (at your option)
- * any later version.
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
diff --git a/src/ephy-action-helper.c b/src/ephy-action-helper.c
index 60750f941..7e8c051b8 100644
--- a/src/ephy-action-helper.c
+++ b/src/ephy-action-helper.c
@@ -2,18 +2,20 @@
/*
* Copyright © 2005 Christian Persch
*
- * This program is free software; you can redistribute it and/or modify
+ * This file is part of Epiphany.
+ *
+ * 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, or (at your option)
- * any later version.
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
diff --git a/src/ephy-action-helper.h b/src/ephy-action-helper.h
index eedb13e62..055287fb7 100644
--- a/src/ephy-action-helper.h
+++ b/src/ephy-action-helper.h
@@ -2,18 +2,20 @@
/*
* Copyright © 2005 Christian Persch
*
- * This program is free software; you can redistribute it and/or modify
+ * This file is part of Epiphany.
+ *
+ * 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, or (at your option)
- * any later version.
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
diff --git a/src/ephy-completion-model.c b/src/ephy-completion-model.c
index e5fdea15e..66f6ee0a7 100644
--- a/src/ephy-completion-model.c
+++ b/src/ephy-completion-model.c
@@ -2,18 +2,20 @@
/*
* Copyright © 2012 Igalia S.L.
*
- * This program is free software; you can redistribute it and/or modify
+ * This file is part of Epiphany.
+ *
+ * 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
+ * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
diff --git a/src/ephy-completion-model.h b/src/ephy-completion-model.h
index 92c726d75..912c3669e 100644
--- a/src/ephy-completion-model.h
+++ b/src/ephy-completion-model.h
@@ -2,18 +2,20 @@
/*
* Copyright © 2012 Igalia S.L.
*
- * This program is free software; you can redistribute it and/or modify
+ * This file is part of Epiphany.
+ *
+ * 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
+ * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
diff --git a/src/ephy-encoding-dialog.c b/src/ephy-encoding-dialog.c
index a0c94aed4..d35a57399 100644
--- a/src/ephy-encoding-dialog.c
+++ b/src/ephy-encoding-dialog.c
@@ -4,18 +4,20 @@
* Copyright © 2003, 2004 Christian Persch
* Copyright © 2012 Igalia S.L.
*
- * This program is free software; you can redistribute it and/or modify
+ * This file is part of Epiphany.
+ *
+ * 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, or (at your option)
- * any later version.
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
diff --git a/src/ephy-encoding-dialog.h b/src/ephy-encoding-dialog.h
index 8d47822d4..f0b2bbdb6 100644
--- a/src/ephy-encoding-dialog.h
+++ b/src/ephy-encoding-dialog.h
@@ -3,18 +3,20 @@
* Copyright © 2000, 2001, 2002, 2003 Marco Pesenti Gritti
* Copyright © 2003 Christian Persch
*
- * This program is free software; you can redistribute it and/or modify
+ * This file is part of Epiphany.
+ *
+ * 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, or (at your option)
- * any later version.
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
diff --git a/src/ephy-encoding-row.c b/src/ephy-encoding-row.c
index ad5894fa1..a109fbf36 100644
--- a/src/ephy-encoding-row.c
+++ b/src/ephy-encoding-row.c
@@ -2,20 +2,20 @@
/*
* Copyright © 2015 Arnaud Bonatti
*
- * This program is free software; you can redistribute it and/or modify
+ * This file is part of Epiphany.
+ *
+ * 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, or (at your option)
- * any later version.
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * 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 this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
diff --git a/src/ephy-encoding-row.h b/src/ephy-encoding-row.h
index 01128197c..d6007d8f3 100644
--- a/src/ephy-encoding-row.h
+++ b/src/ephy-encoding-row.h
@@ -2,20 +2,20 @@
/*
* Copyright © 2015 Arnaud Bonatti
*
- * This program is free software; you can redistribute it and/or modify
+ * This file is part of Epiphany.
+ *
+ * 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, or (at your option)
- * any later version.
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * 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 this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
diff --git a/src/ephy-header-bar.c b/src/ephy-header-bar.c
index 1292ef382..d8543dbc0 100644
--- a/src/ephy-header-bar.c
+++ b/src/ephy-header-bar.c
@@ -4,18 +4,20 @@
* Copyright © 2013 Yosef Or Boczko <yoseforb@gmail.com>
* Copyright © 2016 Iulian-Gabriel Radu <iulian.radu67@gmail.com>
*
- * This program is free software; you can redistribute it and/or modify
+ * This file is part of Epiphany.
+ *
+ * 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, or (at your option)
- * any later version.
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
diff --git a/src/ephy-header-bar.h b/src/ephy-header-bar.h
index 910526ae1..90eb6253f 100644
--- a/src/ephy-header-bar.h
+++ b/src/ephy-header-bar.h
@@ -2,18 +2,20 @@
/*
* Copyright © 2012 Igalia S.L.
*
- * This program is free software; you can redistribute it and/or modify
+ * This file is part of Epiphany.
+ *
+ * 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, or (at your option)
- * any later version.
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
diff --git a/src/ephy-history-window.c b/src/ephy-history-window.c
index f8f86a97e..36caa23ad 100644
--- a/src/ephy-history-window.c
+++ b/src/ephy-history-window.c
@@ -4,18 +4,20 @@
* Copyright © 2003, 2004 Christian Persch
* Copyright © 2012 Igalia S.L
*
- * This program is free software; you can redistribute it and/or modify
+ * This file is part of Epiphany.
+ *
+ * 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
+ * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
diff --git a/src/ephy-history-window.h b/src/ephy-history-window.h
index 29eac8b21..f7fe6df21 100644
--- a/src/ephy-history-window.h
+++ b/src/ephy-history-window.h
@@ -2,18 +2,20 @@
/*
* Copyright © 2003 Marco Pesenti Gritti <mpeseng@tin.it>
*
- * This program is free software; you can redistribute it and/or modify
+ * This file is part of Epiphany.
+ *
+ * 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
+ * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
diff --git a/src/ephy-link.c b/src/ephy-link.c
index 05888e37d..6a65a4644 100644
--- a/src/ephy-link.c
+++ b/src/ephy-link.c
@@ -2,18 +2,20 @@
/*
* Copyright © 2004 Christian Persch
*
- * This program is free software; you can redistribute it and/or modify
+ * This file is part of Epiphany.
+ *
+ * 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, or (at your option)
- * any later version.
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
diff --git a/src/ephy-link.h b/src/ephy-link.h
index 68f318390..762e7ff2a 100644
--- a/src/ephy-link.h
+++ b/src/ephy-link.h
@@ -2,18 +2,20 @@
/*
* Copyright © 2004 Christian Persch
*
- * This program is free software; you can redistribute it and/or modify
+ * This file is part of Epiphany.
+ *
+ * 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, or (at your option)
- * any later version.
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
diff --git a/src/ephy-location-controller.c b/src/ephy-location-controller.c
index 44f591735..ba0cb4bc9 100644
--- a/src/ephy-location-controller.c
+++ b/src/ephy-location-controller.c
@@ -3,18 +3,20 @@
* Copyright © 2003, 2004 Marco Pesenti Gritti
* Copyright © 2003, 2004 Christian Persch
*
- * This program is free software; you can redistribute it and/or modify
+ * This file is part of Epiphany.
+ *
+ * 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, or (at your option)
- * any later version.
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
diff --git a/src/ephy-location-controller.h b/src/ephy-location-controller.h
index 420d7ee16..abbb94412 100644
--- a/src/ephy-location-controller.h
+++ b/src/ephy-location-controller.h
@@ -2,18 +2,20 @@
/*
* Copyright © 2003 Marco Pesenti Gritti
*
- * This program is free software; you can redistribute it and/or modify
+ * This file is part of Epiphany.
+ *
+ * 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, or (at your option)
- * any later version.
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
diff --git a/src/ephy-lockdown.c b/src/ephy-lockdown.c
index 5b149da27..47f70cefb 100644
--- a/src/ephy-lockdown.c
+++ b/src/ephy-lockdown.c
@@ -4,18 +4,20 @@
* Copyright © 2003, 2004, 2005 Christian Persch
* Copyright © 2010, 2012 Igalia S.L.
*
- * This program is free software; you can redistribute it and/or modify
+ * This file is part of Epiphany.
+ *
+ * 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, or (at your option)
- * any later version.
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
diff --git a/src/ephy-lockdown.h b/src/ephy-lockdown.h
index 3d655c46f..18a8768ff 100644
--- a/src/ephy-lockdown.h
+++ b/src/ephy-lockdown.h
@@ -3,18 +3,20 @@
* Copyright © 2003, 2004 Marco Pesenti Gritti
* Copyright © 2003, 2004, 2005 Christian Persch
*
- * This program is free software; you can redistribute it and/or modify
+ * This file is part of Epiphany.
+ *
+ * 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, or (at your option)
- * any later version.
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
diff --git a/src/ephy-main.c b/src/ephy-main.c
index 667064d51..7623a8221 100644
--- a/src/ephy-main.c
+++ b/src/ephy-main.c
@@ -4,18 +4,20 @@
* Copyright © 2006, 2008 Christian Persch
* Copyright © 2011, 2012 Igalia S.L.
*
- * This program is free software; you can redistribute it and/or modify
+ * This file is part of Epiphany.
+ *
+ * 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, or (at your option)
- * any later version.
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
diff --git a/src/ephy-notebook.c b/src/ephy-notebook.c
index e59931900..b77d6ef4e 100644
--- a/src/ephy-notebook.c
+++ b/src/ephy-notebook.c
@@ -4,18 +4,20 @@
* Copyright © 2003, 2004 Marco Pesenti Gritti
* Copyright © 2003, 2004, 2005 Christian Persch
*
- * This program is free software; you can redistribute it and/or modify
+ * This file is part of Epiphany.
+ *
+ * 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, or (at your option)
- * any later version.
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
diff --git a/src/ephy-notebook.h b/src/ephy-notebook.h
index 801889af8..f92b4decc 100644
--- a/src/ephy-notebook.h
+++ b/src/ephy-notebook.h
@@ -4,18 +4,20 @@
* Copyright © 2003 Marco Pesenti Gritti
* Copyright © 2003, 2004 Christian Persch
*
- * This program is free software; you can redistribute it and/or modify
+ * This file is part of Epiphany.
+ *
+ * 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, or (at your option)
- * any later version.
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
diff --git a/src/ephy-private.h b/src/ephy-private.h
index 9041ae60e..7ca1aaef1 100644
--- a/src/ephy-private.h
+++ b/src/ephy-private.h
@@ -2,18 +2,20 @@
/*
* Copyright © 2012 Igalia S.L.
*
- * This program is free software; you can redistribute it and/or modify
+ * This file is part of Epiphany.
+ *
+ * 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, or (at your option)
- * any later version.
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
diff --git a/src/ephy-session.c b/src/ephy-session.c
index 46c7f4ad2..d1b424a5f 100644
--- a/src/ephy-session.c
+++ b/src/ephy-session.c
@@ -5,18 +5,20 @@
* Copyright © 2003, 2004, 2005, 2006, 2008 Christian Persch
* Copyright © 2012 Igalia S.L.
*
- * This program is free software; you can redistribute it and/or modify
+ * This file is part of Epiphany.
+ *
+ * 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, or (at your option)
- * any later version.
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
diff --git a/src/ephy-session.h b/src/ephy-session.h
index 8dcb63978..94c2f5c45 100644
--- a/src/ephy-session.h
+++ b/src/ephy-session.h
@@ -4,18 +4,20 @@
* Copyright © 2003 Marco Pesenti Gritti
* Copyright © 2003 Christian Persch
*
- * This program is free software; you can redistribute it and/or modify
+ * This file is part of Epiphany.
+ *
+ * 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, or (at your option)
- * any later version.
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
diff --git a/src/ephy-shell.c b/src/ephy-shell.c
index 53edb586b..ffee46fd8 100644
--- a/src/ephy-shell.c
+++ b/src/ephy-shell.c
@@ -4,18 +4,20 @@
* Copyright © 2003, 2004, 2006 Christian Persch
* Copyright © 2011 Igalia S.L.
*
- * This program is free software; you can redistribute it and/or modify
+ * This file is part of Epiphany.
+ *
+ * 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, or (at your option)
- * any later version.
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
diff --git a/src/ephy-shell.h b/src/ephy-shell.h
index 3ec9e6039..74fd03908 100644
--- a/src/ephy-shell.h
+++ b/src/ephy-shell.h
@@ -4,18 +4,20 @@
* Copyright © 2003, 2004, 2006 Christian Persch
* Copyright © 2011 Igalia S.L.
*
- * This program is free software; you can redistribute it and/or modify
+ * This file is part of Epiphany.
+ *
+ * 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, or (at your option)
- * any later version.
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
diff --git a/src/ephy-sync-crypto.c b/src/ephy-sync-crypto.c
index 8cdeb42a8..b92fad64e 100644
--- a/src/ephy-sync-crypto.c
+++ b/src/ephy-sync-crypto.c
@@ -2,18 +2,20 @@
/*
* Copyright © 2016 Gabriel Ivascu <ivascu.gabriel59@gmail.com>
*
- * This program is free software; you can redistribute it and/or modify
+ * This file is part of Epiphany.
+ *
+ * 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, or (at your option)
- * any later version.
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
diff --git a/src/ephy-sync-crypto.h b/src/ephy-sync-crypto.h
index be1260226..18368c48c 100644
--- a/src/ephy-sync-crypto.h
+++ b/src/ephy-sync-crypto.h
@@ -2,18 +2,20 @@
/*
* Copyright © 2016 Gabriel Ivascu <ivascu.gabriel59@gmail.com>
*
- * This program is free software; you can redistribute it and/or modify
+ * This file is part of Epiphany.
+ *
+ * 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, or (at your option)
- * any later version.
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
diff --git a/src/ephy-sync-secret.c b/src/ephy-sync-secret.c
index ce4c49af1..a1ea2b9bb 100644
--- a/src/ephy-sync-secret.c
+++ b/src/ephy-sync-secret.c
@@ -2,18 +2,20 @@
/*
* Copyright © 2016 Gabriel Ivascu <ivascu.gabriel59@gmail.com>
*
- * This program is free software; you can redistribute it and/or modify
+ * This file is part of Epiphany.
+ *
+ * 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, or (at your option)
- * any later version.
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
diff --git a/src/ephy-sync-secret.h b/src/ephy-sync-secret.h
index f807d2cb8..9d035f9ac 100644
--- a/src/ephy-sync-secret.h
+++ b/src/ephy-sync-secret.h
@@ -2,18 +2,20 @@
/*
* Copyright © 2016 Gabriel Ivascu <ivascu.gabriel59@gmail.com>
*
- * This program is free software; you can redistribute it and/or modify
+ * This file is part of Epiphany.
+ *
+ * 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, or (at your option)
- * any later version.
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
diff --git a/src/ephy-sync-service.c b/src/ephy-sync-service.c
index c35240b27..84b23f50c 100644
--- a/src/ephy-sync-service.c
+++ b/src/ephy-sync-service.c
@@ -2,18 +2,20 @@
/*
* Copyright © 2016 Gabriel Ivascu <ivascu.gabriel59@gmail.com>
*
- * This program is free software; you can redistribute it and/or modify
+ * This file is part of Epiphany.
+ *
+ * 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, or (at your option)
- * any later version.
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
diff --git a/src/ephy-sync-service.h b/src/ephy-sync-service.h
index 51d261d10..dbfe3ce8e 100644
--- a/src/ephy-sync-service.h
+++ b/src/ephy-sync-service.h
@@ -2,18 +2,20 @@
/*
* Copyright © 2016 Gabriel Ivascu <ivascu.gabriel59@gmail.com>
*
- * This program is free software; you can redistribute it and/or modify
+ * This file is part of Epiphany.
+ *
+ * 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, or (at your option)
- * any later version.
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
diff --git a/src/ephy-sync-utils.c b/src/ephy-sync-utils.c
index e76e66761..74d5f0037 100644
--- a/src/ephy-sync-utils.c
+++ b/src/ephy-sync-utils.c
@@ -2,18 +2,20 @@
/*
* Copyright © 2016 Gabriel Ivascu <ivascu.gabriel59@gmail.com>
*
- * This program is free software; you can redistribute it and/or modify
+ * This file is part of Epiphany.
+ *
+ * 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, or (at your option)
- * any later version.
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
diff --git a/src/ephy-sync-utils.h b/src/ephy-sync-utils.h
index cfd9f6c6b..0eb35a11f 100644
--- a/src/ephy-sync-utils.h
+++ b/src/ephy-sync-utils.h
@@ -2,18 +2,20 @@
/*
* Copyright © 2016 Gabriel Ivascu <ivascu.gabriel59@gmail.com>
*
- * This program is free software; you can redistribute it and/or modify
+ * This file is part of Epiphany.
+ *
+ * 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, or (at your option)
- * any later version.
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
diff --git a/src/ephy-title-box.h b/src/ephy-title-box.h
new file mode 100644
index 000000000..5e562a175
--- /dev/null
+++ b/src/ephy-title-box.h
@@ -0,0 +1,45 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright © 2013, 2014 Yosef Or Boczko <yoseforb@gnome.org>
+ *
+ * This file is part of Epiphany.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+#include <webkit2/webkit2.h>
+
+#include "ephy-window.h"
+
+G_BEGIN_DECLS
+
+#define EPHY_TYPE_TITLE_BOX (ephy_title_box_get_type ())
+
+G_DECLARE_FINAL_TYPE (EphyTitleBox, ephy_title_box, EPHY, TITLE_BOX, GtkBox)
+
+EphyTitleBox *ephy_title_box_new (void);
+
+void ephy_title_box_set_web_view (EphyTitleBox *title_box,
+ WebKitWebView *web_view);
+
+void ephy_title_box_set_security_level (EphyTitleBox *title_box,
+ EphySecurityLevel security_level);
+
+void ephy_title_box_set_address (EphyTitleBox *title_box,
+ const char *address);
+
+G_END_DECLS
diff --git a/src/ephy-window.c b/src/ephy-window.c
index b363f0ed3..c9c8362f4 100644
--- a/src/ephy-window.c
+++ b/src/ephy-window.c
@@ -5,18 +5,20 @@
* Copyright © 2011 Igalia S.L.
* Copyright © 2016 Iulian-Gabriel Radu
*
- * This program is free software; you can redistribute it and/or modify
+ * This file is part of Epiphany.
+ *
+ * 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, or (at your option)
- * any later version.
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
diff --git a/src/ephy-window.h b/src/ephy-window.h
index 905541ba4..6de4fab4a 100644
--- a/src/ephy-window.h
+++ b/src/ephy-window.h
@@ -2,18 +2,20 @@
/*
* Copyright © 2000-2003 Marco Pesenti Gritti
*
- * This program is free software; you can redistribute it and/or modify
+ * This file is part of Epiphany.
+ *
+ * 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, or (at your option)
- * any later version.
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
diff --git a/src/languages.h b/src/languages.h
index 2d88f6666..1dc4ac837 100644
--- a/src/languages.h
+++ b/src/languages.h
@@ -2,18 +2,20 @@
/*
* Copyright © 2005 Christian Persch
*
- * This program is free software; you can redistribute it and/or modify
+ * This file is part of Epiphany.
+ *
+ * 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
+ * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
diff --git a/src/passwords-dialog.c b/src/passwords-dialog.c
index e3b1e1af3..1121ffdb7 100644
--- a/src/passwords-dialog.c
+++ b/src/passwords-dialog.c
@@ -2,18 +2,20 @@
/*
* Copyright © 2013 Red Hat, Inc.
*
- * This program is free software; you can redistribute it and/or modify
+ * This file is part of Epiphany.
+ *
+ * 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, or (at your option)
- * any later version.
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
diff --git a/src/passwords-dialog.h b/src/passwords-dialog.h
index 49e7b5eb6..02a256d8b 100644
--- a/src/passwords-dialog.h
+++ b/src/passwords-dialog.h
@@ -2,18 +2,20 @@
/*
* Copyright © 2013 Red Hat, Inc.
*
- * This program is free software; you can redistribute it and/or modify
+ * This file is part of Epiphany.
+ *
+ * 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, or (at your option)
- * any later version.
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
diff --git a/src/popup-commands.c b/src/popup-commands.c
index 74554c3b9..c15be8910 100644
--- a/src/popup-commands.c
+++ b/src/popup-commands.c
@@ -2,18 +2,20 @@
/*
* Copyright © 2000-2003 Marco Pesenti Gritti
*
- * This program is free software; you can redistribute it and/or modify
+ * This file is part of Epiphany.
+ *
+ * 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, or (at your option)
- * any later version.
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
diff --git a/src/popup-commands.h b/src/popup-commands.h
index 295cde55c..216d9335e 100644
--- a/src/popup-commands.h
+++ b/src/popup-commands.h
@@ -2,18 +2,20 @@
/*
* Copyright © 2000, 2001, 2002 Marco Pesenti Gritti
*
- * This program is free software; you can redistribute it and/or modify
+ * This file is part of Epiphany.
+ *
+ * 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, or (at your option)
- * any later version.
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
diff --git a/src/prefs-dialog.c b/src/prefs-dialog.c
index c45e026b5..f48d67645 100644
--- a/src/prefs-dialog.c
+++ b/src/prefs-dialog.c
@@ -4,18 +4,20 @@
* Copyright © 2003, 2004, 2005 Christian Persch
* Copyright © 2010 Igalia S.L.
*
- * This program is free software; you can redistribute it and/or modify
+ * This file is part of Epiphany.
+ *
+ * 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, or (at your option)
- * any later version.
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
diff --git a/src/prefs-dialog.h b/src/prefs-dialog.h
index 89f60d70f..fc2e21039 100644
--- a/src/prefs-dialog.h
+++ b/src/prefs-dialog.h
@@ -3,18 +3,20 @@
* Copyright © 2000-2004 Marco Pesenti Gritti
* Copyright © 2003-2004 Christian Persch
*
- * This program is free software; you can redistribute it and/or modify
+ * This file is part of Epiphany.
+ *
+ * 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, or (at your option)
- * any later version.
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
diff --git a/src/profile-migrator/ephy-profile-migrator.c b/src/profile-migrator/ephy-profile-migrator.c
index 96882b272..69a467db3 100644
--- a/src/profile-migrator/ephy-profile-migrator.c
+++ b/src/profile-migrator/ephy-profile-migrator.c
@@ -2,18 +2,20 @@
/*
* Copyright © 2009 Xan López
*
- * This program is free software; you can redistribute it and/or modify
+ * This file is part of Epiphany.
+ *
+ * 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, or (at your option)
- * any later version.
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
*/
/* Portions of this file based on Chromium code.
diff --git a/src/resources/error.html b/src/resources/error.html
index 439d74504..7dfc0a88d 100644
--- a/src/resources/error.html
+++ b/src/resources/error.html
@@ -3,18 +3,20 @@
Copyright © 2010, 2011 Vinicius Depizzol
Copyright © 2016 Gabriel Ivascu
- This program is free software; you can redistribute it and/or modify
+ This file is part of Epiphany.
+
+ 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, or (at your option)
- any later version.
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
- This program is distributed in the hope that it will be useful,
+ 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
+ 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, see <http://www.gnu.org/licenses/>.
+ along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
-->
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="%s" lang="%s" dir="%s">
<head>
diff --git a/src/search-provider/ephy-search-provider-main.c b/src/search-provider/ephy-search-provider-main.c
index 39080284f..b4a620584 100644
--- a/src/search-provider/ephy-search-provider-main.c
+++ b/src/search-provider/ephy-search-provider-main.c
@@ -1,19 +1,21 @@
/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
- * Copyright (c) 2013 Igalia S.L.
+ * Copyright (c) 2013 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 the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
+ * This file is part of Epiphany.
*
- * 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.
+ * 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 3 of the License, or
+ * (at your option) any later version.
*
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * 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, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
diff --git a/src/search-provider/ephy-search-provider.c b/src/search-provider/ephy-search-provider.c
index 06ee94f9f..627017ead 100644
--- a/src/search-provider/ephy-search-provider.c
+++ b/src/search-provider/ephy-search-provider.c
@@ -1,19 +1,21 @@
/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
- * Copyright (c) 2013 Giovanni Campagna <scampa.giovanni@gmail.com>
+ * Copyright (c) 2013 Giovanni Campagna <scampa.giovanni@gmail.com>
*
- * 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 of the License, or (at your
- * option) any later version.
+ * This file is part of Epiphany.
*
- * 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.
+ * 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 3 of the License, or
+ * (at your option) any later version.
*
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * 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, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
diff --git a/src/search-provider/ephy-search-provider.h b/src/search-provider/ephy-search-provider.h
index b55a67384..a64f45f14 100644
--- a/src/search-provider/ephy-search-provider.h
+++ b/src/search-provider/ephy-search-provider.h
@@ -1,19 +1,21 @@
/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
- * Copyright (c) 2013 Giovanni Campagna <scampa.giovanni@gmail.com>
+ * Copyright (c) 2013 Giovanni Campagna <scampa.giovanni@gmail.com>
*
- * 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 of the License, or (at your
- * option) any later version.
+ * This file is part of Epiphany.
*
- * 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.
+ * 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 3 of the License, or
+ * (at your option) any later version.
*
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * 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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
diff --git a/src/window-commands.c b/src/window-commands.c
index f4d98b8cd..869753661 100644
--- a/src/window-commands.c
+++ b/src/window-commands.c
@@ -5,18 +5,20 @@
* Copyright © 2011 Igalia S.L.
* Copyright © 2016 Iulian-Gabriel Radu
*
- * This program is free software; you can redistribute it and/or modify
+ * This file is part of Epiphany.
+ *
+ * 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, or (at your option)
- * any later version.
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
@@ -258,7 +260,7 @@ window_cmd_show_about (GSimpleAction *action,
"logo-icon-name", "org.gnome.Epiphany",
"website", "https://wiki.gnome.org/Apps/Web",
"website-label", _("Web Website"),
- "license-type", GTK_LICENSE_GPL_2_0,
+ "license-type", GTK_LICENSE_GPL_3_0,
"wrap-license", TRUE,
NULL);
diff --git a/src/window-commands.h b/src/window-commands.h
index d9ea18f03..df9779aeb 100644
--- a/src/window-commands.h
+++ b/src/window-commands.h
@@ -2,18 +2,20 @@
/*
* Copyright © 2000-2003 Marco Pesenti Gritti
*
- * This program is free software; you can redistribute it and/or modify
+ * This file is part of Epiphany.
+ *
+ * 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, or (at your option)
- * any later version.
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once