summaryrefslogtreecommitdiff
path: root/src/bookmark.c
diff options
context:
space:
mode:
authorDaniel Carl <danielcarl@gmx.de>2017-02-28 00:09:19 +0100
committerDaniel Carl <danielcarl@gmx.de>2017-02-28 00:10:33 +0100
commitca8dfe7dee5f4c7474f0723ec65cf9a3978f083e (patch)
tree8cc03496ca6e49e6c7f1f5d6cd1b07d9c10f018a /src/bookmark.c
parentfdb7d1fda3e34590c86579a635da1deafed32307 (diff)
Allow to manage bookmarks and queue.
Diffstat (limited to 'src/bookmark.c')
-rw-r--r--src/bookmark.c329
1 files changed, 329 insertions, 0 deletions
diff --git a/src/bookmark.c b/src/bookmark.c
new file mode 100644
index 0000000..110d72d
--- /dev/null
+++ b/src/bookmark.c
@@ -0,0 +1,329 @@
+/**
+ * vimb - a webkit based vim like browser.
+ *
+ * Copyright (C) 2012-2017 Daniel Carl
+ *
+ * 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 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see http://www.gnu.org/licenses/.
+ */
+
+#include <string.h>
+
+#include "config.h"
+#include "main.h"
+#include "bookmark.h"
+#include "util.h"
+#include "completion.h"
+
+typedef struct {
+ char *uri;
+ char *title;
+ char *tags;
+} Bookmark;
+
+extern struct Vimb vb;
+
+static GList *load(const char *file);
+static gboolean bookmark_contains_all_tags(Bookmark *bm, char **query,
+ unsigned int qlen);
+static Bookmark *line_to_bookmark(const char *uri, const char *data);
+static void free_bookmark(Bookmark *bm);
+
+/**
+ * Write a new bookmark entry to the end of bookmark file.
+ */
+gboolean bookmark_add(const char *uri, const char *title, const char *tags)
+{
+ const char *file = vb.files[FILES_BOOKMARK];
+ if (tags) {
+ return util_file_append(file, "%s\t%s\t%s\n", uri, title ? title : "", tags);
+ }
+ if (title) {
+ return util_file_append(file, "%s\t%s\n", uri, title);
+ }
+ return util_file_append(file, "%s\n", uri);
+}
+
+gboolean bookmark_remove(const char *uri)
+{
+ char **lines, *line, *p;
+ int len, i;
+ GString *new;
+ gboolean removed = false;
+
+ if (!uri) {
+ return false;
+ }
+
+ lines = util_get_lines(vb.files[FILES_BOOKMARK]);
+ if (lines) {
+ new = g_string_new(NULL);
+ len = g_strv_length(lines) - 1;
+ for (i = 0; i < len; i++) {
+ line = lines[i];
+ g_strstrip(line);
+ /* ignore the title or bookmark tags and test only the uri */
+ if ((p = strchr(line, '\t'))) {
+ *p = '\0';
+ if (!strcmp(uri, line)) {
+ removed = true;
+ continue;
+ } else {
+ /* reappend the tags */
+ *p = '\t';
+ }
+ }
+ if (!strcmp(uri, line)) {
+ removed = true;
+ continue;
+ }
+ g_string_append_printf(new, "%s\n", line);
+ }
+ g_strfreev(lines);
+ g_file_set_contents(vb.files[FILES_BOOKMARK], new->str, -1, NULL);
+ g_string_free(new, true);
+ }
+
+ return removed;
+}
+
+gboolean bookmark_fill_completion(GtkListStore *store, const char *input)
+{
+ gboolean found = false;
+ char **parts;
+ unsigned int len;
+ GtkTreeIter iter;
+ GList *src = NULL;
+ Bookmark *bm;
+
+ src = load(vb.files[FILES_BOOKMARK]);
+ src = g_list_reverse(src);
+ if (!input || !*input) {
+ /* without any tags return all bookmarked items */
+ for (GList *l = src; l; l = l->next) {
+ bm = (Bookmark*)l->data;
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(
+ store, &iter,
+ COMPLETION_STORE_FIRST, bm->uri,
+#ifdef FEATURE_TITLE_IN_COMPLETION
+ COMPLETION_STORE_SECOND, bm->title,
+#endif
+ -1
+ );
+ found = true;
+ }
+ } else {
+ parts = g_strsplit(input, " ", 0);
+ len = g_strv_length(parts);
+
+ for (GList *l = src; l; l = l->next) {
+ bm = (Bookmark*)l->data;
+ if (bookmark_contains_all_tags(bm, parts, len)) {
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(
+ store, &iter,
+ COMPLETION_STORE_FIRST, bm->uri,
+#ifdef FEATURE_TITLE_IN_COMPLETION
+ COMPLETION_STORE_SECOND, bm->title,
+#endif
+ -1
+ );
+ found = true;
+ }
+ }
+ g_strfreev(parts);
+ }
+
+ g_list_free_full(src, (GDestroyNotify)free_bookmark);
+
+ return found;
+}
+
+gboolean bookmark_fill_tag_completion(GtkListStore *store, const char *input)
+{
+ gboolean found;
+ unsigned int len, i;
+ char **tags, *tag;
+ GList *src = NULL, *taglist = NULL;
+ Bookmark *bm;
+
+ /* get all distinct tags from bookmark file */
+ src = load(vb.files[FILES_BOOKMARK]);
+ for (GList *l = src; l; l = l->next) {
+ bm = (Bookmark*)l->data;
+ /* if bookmark contains no tags we can go to the next bookmark */
+ if (!bm->tags) {
+ continue;
+ }
+
+ tags = g_strsplit(bm->tags, " ", -1);
+ len = g_strv_length(tags);
+ for (i = 0; i < len; i++) {
+ tag = tags[i];
+ /* add tag only if it isn't already in the list */
+ if (!g_list_find_custom(taglist, tag, (GCompareFunc)strcmp)) {
+ taglist = g_list_prepend(taglist, g_strdup(tag));
+ }
+ }
+ g_strfreev(tags);
+ }
+
+ /* generate the completion with the found tags */
+ found = util_fill_completion(store, input, taglist);
+
+ g_list_free_full(src, (GDestroyNotify)free_bookmark);
+ g_list_free_full(taglist, (GDestroyNotify)g_free);
+
+ return found;
+}
+
+#ifdef FEATURE_QUEUE
+/**
+ * Push a uri to the end of the queue.
+ *
+ * @uri: URI to put into the queue
+ */
+gboolean bookmark_queue_push(const char *uri)
+{
+ return util_file_append(vb.files[FILES_QUEUE], "%s\n", uri);
+}
+
+/**
+ * Push a uri to the beginning of the queue.
+ *
+ * @uri: URI to put into the queue
+ */
+gboolean bookmark_queue_unshift(const char *uri)
+{
+ return util_file_prepend(vb.files[FILES_QUEUE], "%s\n", uri);
+}
+
+/**
+ * Retrieves the oldest entry from queue.
+ *
+ * @item_count: will be filled with the number of remaining items in queue.
+ * Returned uri must be freed with g_free.
+ */
+char *bookmark_queue_pop(int *item_count)
+{
+ return util_file_pop_line(vb.files[FILES_QUEUE], item_count);
+}
+
+/**
+ * Removes all contents from the queue file.
+ */
+gboolean bookmark_queue_clear(void)
+{
+ FILE *f;
+ if ((f = fopen(vb.files[FILES_QUEUE], "w"))) {
+ fclose(f);
+ return true;
+ }
+ return false;
+}
+#endif /* FEATURE_QUEUE */
+
+static GList *load(const char *file)
+{
+ return util_file_to_unique_list(file, (Util_Content_Func)line_to_bookmark, 0);
+}
+
+/**
+ * Checks if the given bookmark matches all given query strings as prefix. If
+ * the bookmark has no tags, the matching is done on the '/' splited URL.
+ *
+ * @bm: bookmark to test if it matches
+ * @query: char array with tags to search for
+ * @qlen: length of given query char array
+ *
+ * Return: true if the bookmark contained all tags
+ */
+static gboolean bookmark_contains_all_tags(Bookmark *bm, char **query,
+ unsigned int qlen)
+{
+ const char *separators;
+ char *cursor;
+ unsigned int i;
+ gboolean found;
+
+ if (!qlen) {
+ return true;
+ }
+
+ if (bm->tags) {
+ /* If there are tags - use them for matching. */
+ separators = " ";
+ cursor = bm->tags;
+ } else {
+ /* No tags available - matching is based on the path parts of the URL. */
+ separators = "./";
+ cursor = bm->uri;
+ }
+
+ /* iterate over all query parts */
+ for (i = 0; i < qlen; i++) {
+ found = false;
+
+ /* we want to do a prefix match on all bookmark tags - so we check for
+ * a match on string begin - if this fails we move the cursor to the
+ * next space and do the test again */
+ while (cursor && *cursor) {
+ /* match was not found at current cursor position */
+ if (g_str_has_prefix(cursor, query[i])) {
+ found = true;
+ break;
+ }
+ /* If match was not found at the cursor position - move cursor
+ * behind the next separator char. */
+ if ((cursor = strpbrk(cursor, separators))) {
+ cursor++;
+ }
+ }
+
+ if (!found) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static Bookmark *line_to_bookmark(const char *uri, const char *data)
+{
+ char *p;
+ Bookmark *bm;
+
+ /* data part may consist of title or title<tab>tags */
+ bm = g_slice_new(Bookmark);
+ bm->uri = g_strdup(uri);
+ if (data && (p = strchr(data, '\t'))) {
+ *p = '\0';
+ bm->title = g_strdup(data);
+ bm->tags = g_strdup(p + 1);
+ } else {
+ bm->title = g_strdup(data);
+ bm->tags = NULL;
+ }
+
+ return bm;
+}
+
+static void free_bookmark(Bookmark *bm)
+{
+ g_free(bm->uri);
+ g_free(bm->title);
+ g_free(bm->tags);
+ g_slice_free(Bookmark, bm);
+}
+