Branch: refs/heads/master Author: Colomban Wendling ban@herbesfolles.org Committer: Colomban Wendling ban@herbesfolles.org Date: Thu, 18 Dec 2014 22:19:09 UTC Commit: 38bb329fb3584e783564411e50e3a1e91e02b5f9 https://github.com/geany/geany-plugins/commit/38bb329fb3584e783564411e50e3a1...
Log Message: ----------- Merge pull request #179 from wip/webhelper
webhelper: Add support for basic bookmarks
Modified Paths: -------------- webhelper/README webhelper/src/gwh-browser.c webhelper/src/gwh-browser.h webhelper/src/gwh-keybindings.h webhelper/src/gwh-plugin.c webhelper/src/gwh-settings.c
Modified: webhelper/README 13 lines changed, 12 insertions(+), 1 deletions(-) =================================================================== @@ -19,7 +19,8 @@ Prominent features * Possible automatic reloading of the web view upon document saving; * A web inspector/debugging tool for the web view's content (including a JavaScript console, a viewer and editor of processed HTML and CSS, a network - usage analysis tool and many more, thanks to WebKit). + usage analysis tool and many more, thanks to WebKit); +* Basic bookmarks for quicker access to frequently used URL.
Requirements @@ -46,6 +47,16 @@ When loaded into Geany, this plugins adds a web view in the message window (the default), the sidebar or in a separate window. You can find most of the features from this view.
+Bookmarks +--------- + +To add a bookmark, you can click on the website's icon next to its URL and +check the "Bookmark this website" item. You can alternatively use the +configurable keybinding to toggle the bookmark for the current URL. + +Bookmarks are displayed in the drop-down menu of the URL bar, sorted +alphabetically by domain. +
License =======
Modified: webhelper/src/gwh-browser.c 294 lines changed, 288 insertions(+), 6 deletions(-) =================================================================== @@ -22,6 +22,8 @@ #include "config.h"
#include <stdio.h> +#include <stdlib.h> +#include <string.h> #include <glib.h> #include <glib/gi18n-lib.h> #include <gtk/gtk.h> @@ -49,6 +51,28 @@ (GTK_WIDGET_MAPPED ((w))) # endif /* defined (gtk_widget_get_mapped) */ #endif /* GTK_CHECK_VERSION (2, 20, 0) */ +#if ! GTK_CHECK_VERSION (2, 24, 0) +# define GtkComboBoxText GtkComboBox +# define GTK_COMBO_BOX_TEXT GTK_COMBO_BOX +# define GTK_IS_COMBO_BOX_TEXT GTK_IS_COMBO_BOX +# define gtk_combo_box_text_new_with_entry gtk_combo_box_entry_new_text +# define gtk_combo_box_get_entry_text_column(c) \ + (gtk_combo_box_entry_get_text_column (GTK_COMBO_BOX_ENTRY (c))) +# define gtk_combo_box_text_append_text gtk_combo_box_append_text +#endif /* GTK_CHECK_VERSION (2, 24, 0) */ +#if ! GTK_CHECK_VERSION (3, 0, 0) +static void +combo_box_text_remove_all (GtkComboBoxText *combo_box) +{ + GtkListStore *store; + + g_return_if_fail (GTK_IS_COMBO_BOX_TEXT (combo_box)); + + store = GTK_LIST_STORE (gtk_combo_box_get_model (GTK_COMBO_BOX (combo_box))); + gtk_list_store_clear (store); +} +# define gtk_combo_box_text_remove_all combo_box_text_remove_all +#endif /* GTK_CHECK_VERSION (3, 0, 0) */ #if GTK_CHECK_VERSION (3, 0, 0) /* alias GtkObject, we implement the :destroy signal */ # define GtkObject GtkWidget @@ -76,6 +100,7 @@ struct _GwhBrowserPrivate GtkWidget *inspector_web_view;
GtkWidget *url_entry; + GtkWidget *url_combo; GtkToolItem *item_prev; GtkToolItem *item_next; GtkToolItem *item_cancel; @@ -174,6 +199,28 @@ on_settings_browser_last_uri_notify (GObject *object, }
static void +on_settings_browser_bookmarks_notify (GObject *object, + GParamSpec *pspec, + GwhBrowser *self) +{ + gchar **bookmarks; + + g_return_if_fail (GWH_IS_BROWSER (self)); + + gtk_combo_box_text_remove_all (GTK_COMBO_BOX_TEXT (self->priv->url_combo)); + bookmarks = gwh_browser_get_bookmarks (self); + if (bookmarks) { + gchar **p; + + for (p = bookmarks; *p; p++) { + gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (self->priv->url_combo), + *p); + } + g_strfreev (bookmarks); + } +} + +static void on_settings_browser_orientation_notify (GObject *object, GParamSpec *pspec, GwhBrowser *self) @@ -393,6 +440,72 @@ on_url_entry_activate (GtkEntry *entry, }
static void +on_url_combo_active_notify (GtkComboBox *combo, + GParamSpec *pspec, + GwhBrowser *self) +{ + if (gtk_combo_box_get_active (combo) != -1) { + const gchar *uri = gtk_entry_get_text (GTK_ENTRY (self->priv->url_entry)); + + gwh_browser_set_uri (self, uri); + } +} + +static void +on_item_bookmark_toggled (GtkCheckMenuItem *item, + GwhBrowser *self) +{ + if (gtk_check_menu_item_get_active (item)) { + gwh_browser_add_bookmark (self, gwh_browser_get_uri (self)); + } else { + gwh_browser_remove_bookmark (self, gwh_browser_get_uri (self)); + } +} + +static void +on_url_entry_icon_press (GtkEntry *entry, + GtkEntryIconPosition icon_pos, + GdkEventButton *event, + GwhBrowser *self) +{ + if (icon_pos == GTK_ENTRY_ICON_PRIMARY) { + GtkWidget *menu = gtk_menu_new (); + GtkWidget *item; + const gchar *uri = gwh_browser_get_uri (self); + + item = gtk_check_menu_item_new_with_mnemonic (_("Bookmark this website")); + gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), + gwh_browser_has_bookmark (self, uri)); + g_signal_connect (item, "toggled", + G_CALLBACK (on_item_bookmark_toggled), self); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + gtk_widget_show (item); + + gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, + event->button, event->time); + } +} + +static gboolean +on_entry_completion_match_selected (GtkEntryCompletion *comp, + GtkTreeModel *model, + GtkTreeIter *iter, + GwhBrowser *self) +{ + gint column = gtk_entry_completion_get_text_column (comp); + gchar *row; + + gtk_tree_model_get (model, iter, column, &row, -1); + /* set the entry value too in the unlikely case the selected URI is the + * currently viewed one, in which case set_uri() won't change it */ + gtk_entry_set_text (GTK_ENTRY (self->priv->url_entry), row); + gwh_browser_set_uri (self, row); + g_free (row); + + return TRUE; +} + +static void update_history (GwhBrowser *self) { WebKitWebView *web_view = WEBKIT_WEB_VIEW (self->priv->web_view); @@ -671,6 +784,7 @@ gwh_browser_constructed (GObject *object)
/* a bit ugly, fake notifications */ g_object_notify (G_OBJECT (self->priv->settings), "browser-last-uri"); + g_object_notify (G_OBJECT (self->priv->settings), "browser-bookmarks"); g_object_notify (G_OBJECT (self->priv->settings), "browser-orientation"); g_object_notify (G_OBJECT (self->priv->settings), "inspector-window-geometry"); } @@ -799,11 +913,35 @@ gwh_browser_class_init (GwhBrowserClass *klass) g_type_class_add_private (klass, sizeof (GwhBrowserPrivate)); }
+/* a GtkEntryCompletionMatchFunc matching anywhere in the haystack */ +static gboolean +url_completion_match_func (GtkEntryCompletion *comp, + const gchar *key, + GtkTreeIter *iter, + gpointer dummy) +{ + GtkTreeModel *model = gtk_entry_completion_get_model (comp); + gint column = gtk_entry_completion_get_text_column (comp); + gchar *row = NULL; + gboolean match = FALSE; + + gtk_tree_model_get (model, iter, column, &row, -1); + if (row) { + SETPTR (row, g_utf8_normalize (row, -1, G_NORMALIZE_DEFAULT)); + SETPTR (row, g_utf8_casefold (row, -1)); + match = strstr (row, key) != NULL; + g_free (row); + } + + return match; +} + static GtkWidget * create_toolbar (GwhBrowser *self) { - GtkWidget *toolbar; - GtkToolItem *item; + GtkWidget *toolbar; + GtkToolItem *item; + GtkEntryCompletion *comp;
toolbar = g_object_new (GTK_TYPE_TOOLBAR, "icon-size", GTK_ICON_SIZE_MENU, @@ -827,14 +965,26 @@ create_toolbar (GwhBrowser *self) gtk_toolbar_insert (GTK_TOOLBAR (toolbar), self->priv->item_reload, -1); gtk_widget_show (GTK_WIDGET (self->priv->item_reload));
- self->priv->url_entry = gtk_entry_new (); + self->priv->url_combo = gtk_combo_box_text_new_with_entry (); item = gtk_tool_item_new (); gtk_tool_item_set_is_important (item, TRUE); - gtk_container_add (GTK_CONTAINER (item), self->priv->url_entry); + gtk_container_add (GTK_CONTAINER (item), self->priv->url_combo); gtk_tool_item_set_expand (item, TRUE); gtk_toolbar_insert (GTK_TOOLBAR (toolbar), item, -1); gtk_widget_show_all (GTK_WIDGET (item));
+ self->priv->url_entry = gtk_bin_get_child (GTK_BIN (self->priv->url_combo)); + set_location_icon (self, NULL); + gtk_entry_set_icon_tooltip_text (GTK_ENTRY (self->priv->url_entry), + GTK_ENTRY_ICON_PRIMARY, + _("Website information and settings")); + + comp = gtk_entry_completion_new (); + gtk_entry_completion_set_model (comp, gtk_combo_box_get_model (GTK_COMBO_BOX (self->priv->url_combo))); + gtk_entry_completion_set_text_column (comp, gtk_combo_box_get_entry_text_column (GTK_COMBO_BOX (self->priv->url_combo))); + gtk_entry_completion_set_match_func (comp, url_completion_match_func, NULL, NULL); + gtk_entry_set_completion (GTK_ENTRY (self->priv->url_entry), comp); + self->priv->item_inspector = gtk_toggle_tool_button_new_from_stock (GTK_STOCK_INFO); gtk_tool_button_set_label (GTK_TOOL_BUTTON (self->priv->item_inspector), _("Web inspector")); gtk_tool_item_set_tooltip_text (self->priv->item_inspector, _("Toggle web inspector")); @@ -845,8 +995,6 @@ create_toolbar (GwhBrowser *self) gtk_widget_set_sensitive (GTK_WIDGET (self->priv->item_next), FALSE); gtk_widget_set_sensitive (GTK_WIDGET (self->priv->item_cancel), FALSE);
- set_location_icon (self, NULL); - g_signal_connect_swapped (G_OBJECT (self->priv->item_prev), "clicked", G_CALLBACK (webkit_web_view_go_back), self->priv->web_view); @@ -863,6 +1011,12 @@ create_toolbar (GwhBrowser *self) G_CALLBACK (on_item_inspector_toggled), self); g_signal_connect (G_OBJECT (self->priv->url_entry), "activate", G_CALLBACK (on_url_entry_activate), self); + g_signal_connect (G_OBJECT (self->priv->url_entry), "icon-press", + G_CALLBACK (on_url_entry_icon_press), self); + g_signal_connect (G_OBJECT (self->priv->url_combo), "notify::active", + G_CALLBACK (on_url_combo_active_notify), self); + g_signal_connect (G_OBJECT (comp), "match-selected", + G_CALLBACK (on_entry_completion_match_selected), self);
return toolbar; } @@ -1059,6 +1213,8 @@ gwh_browser_init (GwhBrowser *self)
g_signal_connect (self->priv->settings, "notify::browser-last-uri", G_CALLBACK (on_settings_browser_last_uri_notify), self); + g_signal_connect (self->priv->settings, "notify::browser-bookmarks", + G_CALLBACK (on_settings_browser_bookmarks_notify), self); g_signal_connect (self->priv->settings, "notify::browser-orientation", G_CALLBACK (on_settings_browser_orientation_notify), self); g_signal_connect (self->priv->settings, "notify::inspector-detached", @@ -1161,3 +1317,129 @@ gwh_browser_toggle_inspector (GwhBrowser *self)
inspector_set_visible (self, ! INSPECTOR_VISIBLE (self)); } + +gchar ** +gwh_browser_get_bookmarks (GwhBrowser *self) +{ + gchar **bookmarks = NULL; + + g_return_val_if_fail (GWH_IS_BROWSER (self), NULL); + + g_object_get (self->priv->settings, "browser-bookmarks", &bookmarks, NULL); + + return bookmarks; +} + +static void +gwh_browser_set_bookmarks (GwhBrowser *self, + gchar **bookmarks) +{ + g_object_set (self->priv->settings, "browser-bookmarks", bookmarks, NULL); +} + +static gint +strv_index (gchar **strv, + const gchar *str) +{ + g_return_val_if_fail (str != NULL, -1); + + if (strv) { + gint idx; + + for (idx = 0; *strv; strv++, idx++) { + if (strcmp (str, *strv) == 0) { + return idx; + } + } + } + + return -1; +} + +gboolean +gwh_browser_has_bookmark (GwhBrowser *self, + const gchar *uri) +{ + gchar **bookmarks = NULL; + gboolean exists = FALSE; + + g_return_val_if_fail (GWH_IS_BROWSER (self), FALSE); + g_return_val_if_fail (uri != NULL, FALSE); + + bookmarks = gwh_browser_get_bookmarks (self); + exists = strv_index (bookmarks, uri) >= 0; + g_strfreev (bookmarks); + + return exists; +} + +static const gchar * +uri_skip_scheme (const gchar *uri) +{ + if (g_ascii_isalpha (*uri)) { + do { + uri++; + } while (*uri == '+' || *uri == '-' || *uri == '.' || + g_ascii_isalnum (*uri)); + /* this is not strictly correct but good enough for what we do */ + while (*uri == ':' || *uri == '/') + uri++; + } + + return uri; +} + +static int +sort_uris (gconstpointer a, + gconstpointer b) +{ + const gchar *uri1 = uri_skip_scheme (*(const gchar *const *) a); + const gchar *uri2 = uri_skip_scheme (*(const gchar *const *) b); + + return g_ascii_strcasecmp (uri1, uri2); +} + +void +gwh_browser_add_bookmark (GwhBrowser *self, + const gchar *uri) +{ + gchar **bookmarks = NULL; + + g_return_if_fail (GWH_IS_BROWSER (self)); + g_return_if_fail (uri != NULL); + + bookmarks = gwh_browser_get_bookmarks (self); + if (strv_index (bookmarks, uri) < 0) { + gsize length = bookmarks ? g_strv_length (bookmarks) : 0; + + bookmarks = g_realloc (bookmarks, (length + 2) * sizeof *bookmarks); + bookmarks[length] = g_strdup (uri); + bookmarks[length + 1] = NULL; + /* it would be faster to insert directly at the right place but who cares */ + qsort (bookmarks, length + 1, sizeof *bookmarks, sort_uris); + gwh_browser_set_bookmarks (self, bookmarks); + } + g_strfreev (bookmarks); +} + +void +gwh_browser_remove_bookmark (GwhBrowser *self, + const gchar *uri) +{ + gchar **bookmarks = NULL; + gint idx; + + g_return_if_fail (GWH_IS_BROWSER (self)); + g_return_if_fail (uri != NULL); + + bookmarks = gwh_browser_get_bookmarks (self); + idx = strv_index (bookmarks, uri); + if (idx >= 0) { + gsize length = g_strv_length (bookmarks); + + memmove (&bookmarks[idx], &bookmarks[idx + 1], + (length - (gsize) idx) * sizeof *bookmarks); + gwh_browser_set_bookmarks (self, bookmarks); + } + g_strfreev (bookmarks); +}
Modified: webhelper/src/gwh-browser.h 11 lines changed, 11 insertions(+), 0 deletions(-) =================================================================== @@ -83,6 +83,17 @@ G_GNUC_INTERNAL GtkWindow *gwh_browser_get_inspector_transient_for (GwhBrowser *self); G_GNUC_INTERNAL void gwh_browser_toggle_inspector (GwhBrowser *self); +G_GNUC_INTERNAL +gchar **gwh_browser_get_bookmarks (GwhBrowser *self); +G_GNUC_INTERNAL +gboolean gwh_browser_has_bookmark (GwhBrowser *self, + const gchar *uri); +G_GNUC_INTERNAL +void gwh_browser_add_bookmark (GwhBrowser *self, + const gchar *uri); +G_GNUC_INTERNAL +void gwh_browser_remove_bookmark (GwhBrowser *self, + const gchar *uri);
G_END_DECLS
Modified: webhelper/src/gwh-keybindings.h 1 lines changed, 1 insertions(+), 0 deletions(-) =================================================================== @@ -32,6 +32,7 @@ G_BEGIN_DECLS enum { GWH_KB_TOGGLE_INSPECTOR, GWH_KB_SHOW_HIDE_SEPARATE_WINDOW, + GWH_KB_TOGGLE_BOOKMARK, GWH_KB_COUNT };
Modified: webhelper/src/gwh-plugin.c 21 lines changed, 21 insertions(+), 0 deletions(-) =================================================================== @@ -284,6 +284,18 @@ on_kb_show_hide_separate_window (guint key_id) } }
+static void +on_kb_toggle_bookmark (guint key_id) +{ + const gchar *uri = gwh_browser_get_uri (GWH_BROWSER (G_browser)); + + if (gwh_browser_has_bookmark (GWH_BROWSER (G_browser), uri)) { + gwh_browser_remove_bookmark (GWH_BROWSER (G_browser), uri); + } else { + gwh_browser_add_bookmark (GWH_BROWSER (G_browser), uri); + } +} +
static gchar * get_config_filename (void) @@ -312,6 +324,12 @@ load_config (void) _("Last URI visited by the browser"), "about:blank", G_PARAM_READWRITE)); + gwh_settings_install_property (G_settings, g_param_spec_boxed ( + "browser-bookmarks", + _("Bookmarks"), + _("List of bookmarks"), + G_TYPE_STRV, + G_PARAM_READWRITE)); gwh_settings_install_property (G_settings, g_param_spec_enum ( "browser-orientation", _("Browser orientation"), @@ -441,6 +459,9 @@ plugin_init (GeanyData *data) on_kb_show_hide_separate_window, 0, 0, "show_hide_separate_window", _("Show/Hide Web View's Window"), NULL); + keybindings_set_item (gwh_keybindings_get_group (), GWH_KB_TOGGLE_BOOKMARK, + on_kb_toggle_bookmark, 0, 0, "toggle_bookmark", + _("Toggle bookmark for the current website"), NULL); }
void
Modified: webhelper/src/gwh-settings.c 22 lines changed, 22 insertions(+), 0 deletions(-) =================================================================== @@ -291,6 +291,16 @@ key_file_set_value (GKeyFile *kf, g_key_file_set_string (kf, group, key, g_value_get_string (value)); break;
+ case G_TYPE_BOXED: + if (G_VALUE_HOLDS (value, G_TYPE_STRV)) { + gchar **val = g_value_get_boxed (value); + + g_key_file_set_string_list (kf, group, key, (const gchar *const *) val, + val ? g_strv_length (val) : 0); + break; + } + /* fallthrough */ + default: g_set_error (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_INVALID_VALUE, "Unsupported setting type "%s" for setting "%s::%s"", @@ -411,6 +421,18 @@ key_file_get_value (GKeyFile *kf, break; }
+ case G_TYPE_BOXED: + if (G_VALUE_HOLDS (value, G_TYPE_STRV)) { + gchar **val; + + val = g_key_file_get_string_list (kf, group, key, NULL, &err); + if (! err) { + g_value_take_boxed (value, val); + } + break; + } + /* fallthrough */ + default: g_set_error (&err, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_INVALID_VALUE, "Unsupported setting type "%s" for setting "%s::%s"",
-------------- This E-Mail was brought to you by github_commit_mail.py (Source: https://github.com/geany/infrastructure).