diff options
author | Michael Catanzaro <mcatanzaro@gnome.org> | 2016-09-11 09:59:03 -0500 |
---|---|---|
committer | Michael Catanzaro <mcatanzaro@gnome.org> | 2016-10-07 19:33:22 -0500 |
commit | e54cb1c7e97ddca58977a8ee01a138ff57ba6f2c (patch) | |
tree | 164165750b109ae888ce9b55d785fad9ce748e50 /src | |
parent | d71e1687953dca9787c3763928a88d7397081b94 (diff) | |
download | epiphany-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')
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 & ≤, 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, "&", 5) == 0) { + g_string_append_c (result, '&'); + jump = 5; + } else if (g_ascii_strncasecmp (iterator, "<", 4) == 0) { + g_string_append_c (result, '<'); + jump = 4; + } else if (g_ascii_strncasecmp (iterator, ">", 4) == 0) { + g_string_append_c (result, '>'); + jump = 4; + } else if (g_ascii_strncasecmp (iterator, """, 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 |