.TP
.B complete
Start a completion according to the current first chars in the inputbox. If
-complation is already started, switch to the next completion item.
+completion is already started, switch to the next completion item.
+
+Following completions are available:
+.RS
+.nr step 1 1
+.IP \n[step] 3
+Commands `:'
+.IP \n+[step]
+Settings `:set '
+.IP \n+[step]
+Url-History and Bookmarks `:open \fIQUERY\fP' or `:tabopen \fIQUERY\fP'.
+If \fIQUERY\fP is given, this will be used to fetch matching urls from history
+file, or to find bookmarks from bookmark file. The urls from history will only
+be matched by their url, the bookmarks only by their tags. If multiple tags
+are given to get the bookmarks, only those bookmarks will be returned, that
+matches all the given tags or that don't have any tag set.
+.RE
.TP
.B complete-back
If complation is already started, switch to the previous completion item.
extern VbCore vb;
+typedef struct {
+ char *uri;
+ char **argv;
+ int argc;
+} Bookmark;
+
+static GList *load(const char *file);
+static gboolean contains_all_args(char **argv_src, unsigned int s, char **argv_query, unsigned int q);
+static void free_bookmark(Bookmark *bm);
+
/**
* Write a new bookmark entry to the end of bookmark file.
*/
g_string_free(string, true);
}
}
+
+/**
+ * Retrieves all bookmark uri matching the given space separated tags string.
+ * Don't forget to free the returned list.
+ */
+GList *bookmark_get_by_tags(const char *tags)
+{
+ GList *res = NULL;
+ GList *src = load(vb.files[FILES_BOOKMARK]);
+ char **parts;
+ unsigned int len;
+
+ parts = g_strsplit(tags, " ", 0);
+ len = g_strv_length(parts);
+
+ for (GList *l = src; l; l = l->next) {
+ Bookmark *bm = (Bookmark*)l->data;
+ if (contains_all_args(bm->argv, bm->argc, parts, len)) {
+ res = g_list_prepend(res, g_strdup(bm->uri));
+ }
+ }
+
+ g_list_free_full(src, (GDestroyNotify)free_bookmark);
+ g_strfreev(parts);
+
+ return res;
+}
+
+static GList *load(const char *file)
+{
+ /* read the items from file */
+ GList *list = NULL;
+ char buf[512] = {0}, **argv = NULL;
+ int argc = 0;
+ FILE *f;
+
+ if (!(f = fopen(file, "r"))) {
+ return list;
+ }
+
+ file_lock_set(fileno(f), F_RDLCK);
+ while (fgets(buf, sizeof(buf), f)) {
+ char *p;
+ Bookmark *bm;
+
+ g_strstrip(buf);
+ /* skip empty lines */
+ if (!*buf) {
+ continue;
+ }
+
+ /* create bookmark */
+ bm = g_new(Bookmark, 1);
+ if ((p = strchr(buf, ' '))) {
+ *p = '\0';
+ /* parse tags */
+ if (g_shell_parse_argv(p + 1, &argc, &argv, NULL)) {
+ bm->uri = g_strdup(buf);
+ bm->argv = argv;
+ bm->argc = argc;
+ } else {
+ continue;
+ }
+ } else {
+ bm->uri = g_strdup(buf);
+ bm->argv = NULL;
+ bm->argc = 0;
+ }
+
+ list = g_list_prepend(list, bm);
+ }
+ file_lock_set(fileno(f), F_UNLCK);
+ fclose(f);
+
+ return list;
+}
+
+/**
+ * Checks if the given source array of pointer contains all those entries
+ * given as array of search strings.
+ */
+static gboolean contains_all_args(char **argv_src, unsigned int s, char **argv_query, unsigned int q)
+{
+ unsigned int i, n;
+
+ if (!s || !q) {
+ return true;
+ }
+
+ /* iterate over all query parts */
+ for (i = 0; i < q; i++) {
+ gboolean found = false;
+ for (n = 0; n < s; n++) {
+ if (!strcmp(argv_query[i], argv_src[n])) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static void free_bookmark(Bookmark *bm)
+{
+ g_free(bm->uri);
+ g_strfreev(bm->argv);
+ g_free(bm);
+}
#include "completion.h"
#include "util.h"
#include "history.h"
+#include "bookmark.h"
extern VbCore vb;
static GList *init_completion(GList *target, GList *source,
Comp_Func func, const char *input, const char *prefix);
+static GList *prepend_bookmark_completion(GList *target, GList *source, const char *prefix);
+static void pack_boxes(GList *list);
static GList *update(GList *completion, GList *active, gboolean back);
static void show(gboolean back);
static void set_entry_text(Completion *completion);
gtk_box_pack_start(GTK_BOX(vb.gui.box), vb.gui.compbox, false, false, 0);
/* TODO move these decision to a more generic place */
+ /* TODO simplify this logic - seperate the list preparation, filtering and
+ * gtk box creation from another */
if (!strncmp(input, ":set ", 5)) {
source = g_hash_table_get_keys(vb.settings);
source = g_list_sort(source, (GCompareFunc)g_strcmp0);
vb.comps.completions, source, (Comp_Func)util_strcasestr, &input[6], ":open "
);
history_list_free(&source);
+
+ /* prepend the bookmark items */
+ source = bookmark_get_by_tags(&input[6]);
+ vb.comps.completions = prepend_bookmark_completion(
+ vb.comps.completions, source, ":open "
+ );
+ history_list_free(&source);
} else if (!strncmp(input, ":tabopen ", 9)) {
source = history_get_all(HISTORY_URL);
vb.comps.completions = init_completion(
vb.comps.completions, source, (Comp_Func)util_strcasestr, &input[9], ":tabopen "
);
history_list_free(&source);
+
+ /* prepend the bookmark items */
+ source = bookmark_get_by_tags(&input[9]);
+ vb.comps.completions = prepend_bookmark_completion(
+ vb.comps.completions, source, ":tabopen "
+ );
+ history_list_free(&source);
} else {
source = g_hash_table_get_keys(vb.behave.commands);
source = g_list_sort(source, (GCompareFunc)g_strcmp0);
if (!vb.comps.completions) {
return false;
}
+ pack_boxes(vb.comps.completions);
show(back);
return TRUE;
}
}
if (match) {
- Completion *completion = get_new(data, prefix);
- gtk_box_pack_start(GTK_BOX(vb.gui.compbox), completion->event, TRUE, TRUE, 0);
/* use prepend because that faster */
- target = g_list_prepend(target, completion);
+ target = g_list_prepend(target, get_new(data, prefix));
}
}
return target;
}
+static GList *prepend_bookmark_completion(GList *target, GList *source, const char *prefix)
+{
+ for (GList *l = source; l; l = l->next) {
+ target = g_list_prepend(target, get_new(l->data, prefix));
+ }
+
+ return target;
+}
+
+static void pack_boxes(GList *list)
+{
+ for (GList *l = list; l; l = l->next) {
+ Completion *c = (Completion*)l->data;
+ gtk_box_pack_start(GTK_BOX(vb.gui.compbox), c->event, TRUE, TRUE, 0);
+ }
+}
+
static GList *update(GList *completion, GList *active, gboolean back)
{
GList *old, *new;