commit 35ed20e3d46752766ec8f286884d525ed753830d
Author: Kyle Milz <kmilz@ucalgary.ca>
Date: Wed, 19 Sep 2012 17:15:55 -0600
viking initial commit.
Diffstat:
A | Makefile | | | 81 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | commands.c | | | 1509 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | commands.h | | | 99 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | config.h | | | 155 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | data/desktop/viking.desktop | | | 10 | ++++++++++ |
A | data/desktop/viking.jpg | | | 0 | |
A | hinting.js | | | 455 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | jsmn/Makefile | | | 26 | ++++++++++++++++++++++++++ |
A | jsmn/jsmn.c | | | 226 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | jsmn/jsmn.h | | | 64 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | keymap.h | | | 137 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | main.c | | | 1533 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | main.h | | | 74 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | utilities.c | | | 1070 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | utilities.h | | | 43 | +++++++++++++++++++++++++++++++++++++++++++ |
A | viking.h | | | 197 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
16 files changed, 5679 insertions(+), 0 deletions(-)
diff --git a/Makefile b/Makefile
@@ -0,0 +1,81 @@
+TARGET = viking
+
+# Objectfiles, needed for $(TARGET)
+OBJ = main.o utilities.o commands.o
+# Manpages
+MAN1 = evi2.1
+MAN5 = evirc.5
+# Used libraries to get needed CFLAGS and LDFLAGS form pkg-config
+LIBS = elementary ewebkit
+# Files to removo by clean target
+CLEAN = $(TARGET) $(OBJ) $(DEPS) javascript.h
+# Files to install by install target or remove by uninstall target
+MANINSTALL = $(addprefix $(MANDIR)/man1/,$(MAN1)) \
+ $(addprefix $(MANDIR)/man5/,$(MAN5))
+INSTALL = $(BINDIR)/$(TARGET) $(MANINSTALL)
+
+# DEBUG build? Off by default
+V_DEBUG = 1
+
+CFLAGS += `pkg-config --cflags $(LIBS)`
+LDFLAGS += `pkg-config --libs $(LIBS)` -Ljsmn -ljsmn
+
+# TA: This is a pretty stringent list of warnings to bail on!
+ifeq ($(V_DEBUG),1)
+CFLAGS += -g -ggdb -Wstrict-prototypes
+CFLAGS += -Wno-long-long -Wall -Wmissing-declarations
+endif
+
+PREFIX ?= /usr/local
+BINDIR ?= $(PREFIX)/bin
+MANDIR ?= $(PREFIX)/share/man
+# Mode bits for normal not executable files
+FMOD ?= 0644
+# Mode bits for directories
+DMOD ?= 0755
+# Mode bits for executables
+EXECMOD ?= 0755
+# Destination directory to install files
+DESTDIR ?= /
+
+# auto garerated dependancies for object files
+DEPS = $(OBJ:%.o=%.d)
+
+all: $(TARGET)
+
+-include $(DEPS)
+
+main.o: javascript.h
+javascript.h: hinting.js
+ perl ./js-merge-helper.pl
+
+$(TARGET): $(OBJ)
+ $(CC) $^ $(LDFLAGS) -o $@
+
+.PHONY: clean install uninstall
+clean:
+ -rm -f $(CLEAN)
+install: $(addprefix $(DESTDIR)/,$(INSTALL))
+uninstall:
+ rm -f $(addprefix $(DESTDIR)/,$(INSTALL))
+
+# pattern rule to inslall executabels
+$(DESTDIR)/$(BINDIR)/%: ./%
+ -[ -e '$(@D)' ] || mkdir -p '$(@D)' && chmod $(DMOD) '$(@D)'
+ cp -f '$<' '$@'
+ -strip -s '$@'
+ chmod $(EXECMOD) '$@'
+
+# pattern rules to install manpages
+$(DESTDIR)/$(MANDIR)/man1/%: ./%
+ -[ -e '$(@D)' ] || mkdir -p '$(@D)' && chmod $(DMOD) '$(@D)'
+ cp -f '$<' '$@'
+ chmod $(FMOD) '$@'
+
+$(DESTDIR)/$(MANDIR)/man5/%: ./%
+ -[ -e '$(@D)' ] || mkdir -p '$(@D)' && chmod $(DMOD) '$(@D)'
+ cp -f '$<' '$@'
+ chmod $(FMOD) '$@'
+
+%.o: %.c
+ $(CC) -MMD -c $(CFLAGS) $< -o $@
diff --git a/commands.c b/commands.c
@@ -0,0 +1,1509 @@
+
+#define _GNU_SOURCE
+#include <Elementary.h>
+#include <EWebKit.h>
+#include <libsoup/soup.h>
+
+#include "viking.h"
+#include "commands.h"
+#include "utilities.h"
+#include "main.h"
+
+extern unsigned int scrollstep;
+extern unsigned int pagingkeep;
+extern char defaultsearch[MAX_SETTING_SIZE];
+
+
+void fill_suggline(char * suggline, const char * command, const char *fill_with);
+Evas_Object * fill_eventbox(const char * completion_line);
+
+
+
+void fill_suggline(char * suggline, const char * command, const char *fill_with) {
+ memset(suggline, 0, 512);
+ strncpy(suggline, command, 512);
+ strncat(suggline, " ", 1);
+ strncat(suggline, fill_with, 512 - strlen(suggline) - 1);
+}
+
+Evas_Object *fill_eventbox(const char * completion_line)
+{
+ /*
+ GtkBox * row;
+ Evas_Object *row_eventbox, *el;
+ GdkColor color;
+ char *markup, *markup_tmp;
+
+ row = GTK_BOX(gtk_hbox_new(FALSE, 0));
+ row_eventbox = gtk_event_box_new();
+ gdk_color_parse(completionbgcolor[0], &color);
+ gtk_widget_modify_bg(row_eventbox, GTK_STATE_NORMAL, &color);
+ el = gtk_label_new(NULL);
+ markup_tmp = g_markup_escape_text(completion_line, strlen(completion_line));
+ markup = g_strconcat("<span font=\"", completionfont[0], "\" color=\"", completioncolor[0], "\">",
+ markup_tmp, "</span>", NULL);
+ gtk_label_set_markup(GTK_LABEL(el), markup);
+ g_free(markup_tmp);
+ g_free(markup);
+ gtk_misc_set_alignment(GTK_MISC(el), 0, 0);
+ gtk_box_pack_start(row, el, TRUE, TRUE, 2);
+ gtk_container_add(GTK_CONTAINER(row_eventbox), GTK_WIDGET(row));
+ return row_eventbox;
+ */
+ return NULL;
+}
+
+Eina_Bool
+complete(const Arg *arg, void *data)
+{
+#if 0
+ char *p, *s, *markup, *entry, *searchfor, command[32] = "", suggline[512] = "", **suggurls;
+ char *str;
+ size_t listlen, len, cmdlen;
+ int i, spacepos;
+ Listelement *elementlist = NULL, *elementpointer;
+ Eina_Bool highlight = FALSE;
+ // GtkBox *row;
+ // GtkWidget *row_eventbox, *el;
+ // GtkBox *_table;
+ // GdkColor color;
+ // static GtkWidget *table, *top_border;
+ Evas_Object *row, *row_eventbox, *el, *_table;
+ Evas_Object *table, *top_border;
+ static char *prefix;
+ static char **suggestions;
+ static Evas_Object **widgets;
+ static int n = 0, m, current = -1;
+ App_Data *ad = data;
+
+ str = elm_entry_entry_get(ad->url);
+ // str = (char*)gtk_entry_get_text(GTK_ENTRY(inputbox));
+ len = strlen(str);
+
+ /* Get the length of the list of commands for completion. We need this to
+ * malloc/realloc correctly.
+ */
+ listlen = LENGTH(commands);
+
+ if ((len == 0 || str[0] != ':') && arg->i != HideCompletion)
+ return TRUE;
+ /*
+ if (prefix) {
+ if (arg->i != HideCompletion && widgets && current != -1 && !strcmp(&str[1], suggestions[current])) {
+ gdk_color_parse(completionbgcolor[0], &color);
+ gtk_widget_modify_bg(widgets[current], GTK_STATE_NORMAL, &color);
+ current = (n + current + (arg->i == DirectionPrev ? -1 : 1)) % n;
+ if ((arg->i == DirectionNext && current == 0)
+ || (arg->i == DirectionPrev && current == n - 1))
+ current = -1;
+ } else {
+ free(widgets);
+ free(suggestions);
+ free(prefix);
+ gtk_widget_destroy(GTK_WIDGET(table));
+ gtk_widget_destroy(GTK_WIDGET(top_border));
+ table = NULL;
+ widgets = NULL;
+ suggestions = NULL;
+ prefix = NULL;
+ n = 0;
+ current = -1;
+ if (arg->i == HideCompletion)
+ return TRUE;
+ }
+ } else if (arg->i == HideCompletion)
+ return TRUE;
+ */
+ if (!widgets) {
+ prefix = strdup(str);
+ // widgets = malloc(sizeof(GtkWidget*) * listlen);
+ widgets = malloc(sizeof(Evas_Object*) * listlen);
+ suggestions = malloc(sizeof(char*) * listlen);
+ top_border = gtk_event_box_new();
+ gtk_widget_set_size_request(GTK_WIDGET(top_border), 0, 1);
+
+ // gdk_color_parse(completioncolor[2], &color);
+ // gtk_widget_modify_bg(top_border, GTK_STATE_NORMAL, &color);
+ elm_bg_color_set(top_border, completioncolor[2][0], completioncolor[2][1], completioncolor[2][2]);
+
+ table = gtk_event_box_new();
+ gdk_color_parse(completionbgcolor[0], &color);
+ _table = GTK_BOX(gtk_vbox_new(FALSE, 0));
+ highlight = len > 1;
+ if (strchr(str, ' ') == NULL) {
+ /* command completion */
+ listlen = LENGTH(commands);
+ for (i = 0; i < listlen; i++) {
+ if (commands[i].cmd == NULL)
+ break;
+ cmdlen = strlen(commands[i].cmd);
+ if (!highlight || (n < MAX_LIST_SIZE && len - 1 <= cmdlen && !strncmp(&str[1], commands[i].cmd, len - 1))) {
+ p = s = malloc(sizeof(char*) * (highlight ? sizeof(COMPLETION_TAG_OPEN) + sizeof(COMPLETION_TAG_CLOSE) - 1 : 1) + cmdlen);
+ if (highlight) {
+ memcpy(p, COMPLETION_TAG_OPEN, sizeof(COMPLETION_TAG_OPEN) - 1);
+ memcpy((p += sizeof(COMPLETION_TAG_OPEN) - 1), &str[1], len - 1);
+ memcpy((p += len - 1), COMPLETION_TAG_CLOSE, sizeof(COMPLETION_TAG_CLOSE) - 1);
+ p += sizeof(COMPLETION_TAG_CLOSE) - 1;
+ }
+ memcpy(p, &commands[i].cmd[len - 1], cmdlen - len + 2);
+ row = GTK_BOX(gtk_hbox_new(FALSE, 0));
+ row_eventbox = gtk_event_box_new();
+ gtk_widget_modify_bg(row_eventbox, GTK_STATE_NORMAL, &color);
+ el = gtk_label_new(NULL);
+ markup = g_strconcat("<span font=\"", completionfont[0], "\" color=\"", completioncolor[0], "\">", s, "</span>", NULL);
+ free(s);
+ gtk_label_set_markup(GTK_LABEL(el), markup);
+ g_free(markup);
+ gtk_misc_set_alignment(GTK_MISC(el), 0, 0);
+ gtk_box_pack_start(row, el, TRUE, TRUE, 2);
+ gtk_container_add(GTK_CONTAINER(row_eventbox), GTK_WIDGET(row));
+ gtk_box_pack_start(_table, GTK_WIDGET(row_eventbox), FALSE, FALSE, 0);
+ suggestions[n] = commands[i].cmd;
+ widgets[n++] = row_eventbox;
+ }
+ }
+ } else {
+ entry = (char *)malloc(512 * sizeof(char));
+ if (entry == NULL) {
+ return FALSE;
+ }
+ memset(entry, 0, 512);
+ suggurls = malloc(sizeof(char*) * listlen);
+ if (suggurls == NULL) {
+ return FALSE;
+ }
+ spacepos = strcspn(str, " ");
+ searchfor = (str + spacepos + 1);
+ strncpy(command, (str + 1), spacepos - 1);
+ if (strlen(command) == 3 && strncmp(command, "set", 3) == 0) {
+ /* browser settings */
+ listlen = LENGTH(browsersettings);
+ for (i = 0; i < listlen; i++) {
+ if (n < MAX_LIST_SIZE && strstr(browsersettings[i].name, searchfor) != NULL) {
+ /* match */
+ fill_suggline(suggline, command, browsersettings[i].name);
+ /* FIXME(HP): This memory is never freed */
+ suggurls[n] = (char *)malloc(sizeof(char) * 512 + 1);
+ strncpy(suggurls[n], suggline, 512);
+ suggestions[n] = suggurls[n];
+ row_eventbox = fill_eventbox(suggline);
+ gtk_box_pack_start(_table, GTK_WIDGET(row_eventbox), FALSE, FALSE, 0);
+ widgets[n++] = row_eventbox;
+ }
+
+ }
+ } else if (strlen(command) == 2 && strncmp(command, "qt", 2) == 0) {
+ /* completion on tags */
+ spacepos = strcspn(str, " ");
+ searchfor = (str + spacepos + 1);
+ elementlist = complete_list(searchfor, 1, elementlist);
+ } else {
+ /* URL completion: bookmarks */
+ elementlist = complete_list(searchfor, 0, elementlist);
+ m = count_list(elementlist);
+ if (m < MAX_LIST_SIZE) {
+ /* URL completion: history */
+ elementlist = complete_list(searchfor, 2, elementlist);
+ }
+ }
+ elementpointer = elementlist;
+ while (elementpointer != NULL) {
+ fill_suggline(suggline, command, elementpointer->element);
+ /* FIXME(HP): This memory is never freed */
+ suggurls[n] = (char *)malloc(sizeof(char) * 512 + 1);
+ strncpy(suggurls[n], suggline, 512);
+ suggestions[n] = suggurls[n];
+ row_eventbox = fill_eventbox(suggline);
+ gtk_box_pack_start(_table, GTK_WIDGET(row_eventbox), FALSE, FALSE, 0);
+ widgets[n++] = row_eventbox;
+ elementpointer = elementpointer->next;
+ if (n >= MAX_LIST_SIZE)
+ break;
+ }
+ free_list(elementlist);
+ if (suggurls != NULL) {
+ free(suggurls);
+ suggurls = NULL;
+ }
+ if (entry != NULL) {
+ free(entry);
+ entry = NULL;
+ }
+ }
+ /* TA: FIXME - this needs rethinking entirely. */
+ {
+ GtkWidget **widgets_temp = realloc(widgets, sizeof(*widgets) * n);
+ if (widgets_temp == NULL && widgets == NULL) {
+ fprintf(stderr, "Couldn't realloc() widgets\n");
+ exit(1);
+ }
+ widgets = widgets_temp;
+ char **suggestions_temp = realloc(suggestions, sizeof(*suggestions) * n);
+ if (suggestions_temp == NULL && suggestions == NULL) {
+ fprintf(stderr, "Couldn't realloc() suggestions\n");
+ exit(1);
+ }
+ suggestions = suggestions_temp;
+ }
+ if (!n) {
+ gdk_color_parse(completionbgcolor[1], &color);
+ gtk_widget_modify_bg(table, GTK_STATE_NORMAL, &color);
+ el = gtk_label_new(NULL);
+ gtk_misc_set_alignment(GTK_MISC(el), 0, 0);
+ markup = g_strconcat("<span font=\"", completionfont[1], "\" color=\"", completioncolor[1], "\">No Completions</span>", NULL);
+ gtk_label_set_markup(GTK_LABEL(el), markup);
+ g_free(markup);
+ gtk_box_pack_start(_table, GTK_WIDGET(el), FALSE, FALSE, 0);
+ }
+ gtk_box_pack_start(box, GTK_WIDGET(top_border), FALSE, FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(table), GTK_WIDGET(_table));
+ gtk_box_pack_start(box, GTK_WIDGET(table), FALSE, FALSE, 0);
+ gtk_widget_show_all(GTK_WIDGET(window));
+ if (!n)
+ return TRUE;
+ current = arg->i == DirectionPrev ? n - 1 : 0;
+ }
+ if (current != -1) {
+ gdk_color_parse(completionbgcolor[2], &color);
+ gtk_widget_modify_bg(GTK_WIDGET(widgets[current]), GTK_STATE_NORMAL, &color);
+ s = g_strconcat(":", suggestions[current], NULL);
+ gtk_entry_set_text(GTK_ENTRY(inputbox), s);
+ g_free(s);
+ } else
+ gtk_entry_set_text(GTK_ENTRY(inputbox), prefix);
+ gtk_editable_set_position(GTK_EDITABLE(inputbox), -1);
+#endif
+ return TRUE;
+}
+
+Eina_Bool
+descend(const Arg *arg, void *data)
+{
+ App_Data *ad = data;
+ // char *source = (char*)webkit_web_view_get_uri(webview), *p = &source[0], *new;
+ const char *source = elm_web_uri_get(ad->current_web);
+ const char *p = &source[0];
+ char *new;
+
+ int i, len;
+ ad->count = ad->count ? ad->count : 1;
+
+ if (!source)
+ return TRUE;
+ if (arg->i == Rootdir) {
+ for (i = 0; i < 3; i++) /* get to the third slash */
+ if (!(p = strchr(++p, '/')))
+ return TRUE; /* if we cannot find it quit */
+ } else {
+ len = strlen(source);
+ if (!len) /* if string is empty quit */
+ return TRUE;
+ p = source + len; /* start at the end */
+ if (*(p - 1) == '/') /* /\/$/ is not an additional level */
+ ++(ad->count);
+ for (i = 0; i < ad->count; i++)
+ while(*(p--) != '/' || *p == '/') /* count /\/+/ as one slash */
+ if (p == source) /* if we reach the first char pointer quit */
+ return TRUE;
+ ++p; /* since we do p-- in the while, we are pointing at
+ the char before the slash, so +1 */
+ }
+ len = p - source + 1; /* new length = end - start + 1 */
+ new = malloc(len + 1);
+ memcpy(new, source, len);
+ new[len] = '\0';
+ // webkit_web_view_load_uri(webview, new);
+ elm_web_uri_set(ad->current_web, new);
+ free(new);
+ return TRUE;
+}
+
+Eina_Bool
+input(const Arg *arg, void *data)
+{
+ // int pos = 0;
+ const char *url;
+ // int index = Info;
+ Arg a;
+ App_Data *ad = data;
+ ad->count = 0;
+
+ /* if inputbox hidden, show it again */
+ // if (!gtk_widget_get_visible(inputbox))
+ // gtk_widget_set_visible(inputbox, TRUE);
+ if (!evas_object_visible_get(ad->url))
+ evas_object_show(ad->url);
+
+ update_state(data);
+
+ /* Set the colour and font back to the default, so that we don't still
+ * maintain a red colour from a warning from an end of search indicator,
+ * etc.
+ */
+ // set_widget_font_and_color(ad->url, urlboxfont[index], urlboxbgcolor[index], urlboxcolor[index]);
+
+ /* to avoid things like :open URL :open URL2 or :open :open URL */
+ // gtk_entry_set_text(GTK_ENTRY(inputbox), "");
+ elm_entry_entry_set(ad->url, "");
+
+ /*
+ gtk_editable_insert_text(GTK_EDITABLE(inputbox), arg->s, -1, &pos);
+ if (arg->i & InsertCurrentURL && (url = webkit_web_view_get_uri(webview)))
+ gtk_editable_insert_text(GTK_EDITABLE(inputbox), url, -1, &pos);
+
+ gtk_widget_grab_focus(inputbox);
+ gtk_editable_set_position(GTK_EDITABLE(inputbox), -1);
+ */
+
+ elm_entry_entry_set(ad->url, arg->s);
+ if (arg->i & InsertCurrentURL && (url = elm_web_uri_get(ad->current_web)))
+ elm_entry_entry_append(ad->url, url);
+
+ elm_object_focus_set(ad->url, EINA_TRUE);
+ elm_entry_cursor_end_set(ad->url);
+
+ if (arg->s[0] == '.' || arg->s[0] == ',' || arg->s[0] == ';') {
+ ad->mode = ModeHints;
+ memset(ad->followTarget, 0, 8);
+ strncpy(ad->followTarget, "current", 8);
+ a.i = Silent;
+
+ switch (arg->s[0]) {
+ case '.':
+ a.s = strdup("hints.createHints('', 'f');");
+ break;
+
+ case ',':
+ a.s = strdup("hints.createHints('', 'F');");
+ break;
+
+ case ';':
+ a.s = NULL;
+ if (arg->s[1]) {
+
+ switch (arg->s[1]) {
+ case 's':
+ a.s = strdup("hints.createHints('', 's');");
+ break;
+ case 'y':
+ a.s = strdup("hints.createHints('', 'y');");
+ break;
+ case 'o':
+ a.s = strdup("hints.createHints('', 'f');");
+ break;
+ case 't': case 'w':
+ a.s = strdup("hints.createHints('', 'F');");
+ break;
+ case 'O': case 'T': case 'W':
+ a.s = strdup("hints.createHints('', 'O');");
+ break;
+ case 'i':
+ a.s = strdup("hints.createHints('', 'i');");
+ break;
+ case 'I':
+ a.s = strdup("hints.createHints('', 'I');");
+ break;
+ }
+
+ }
+ break;
+ }
+
+ ad->count = 0;
+ if (a.s) {
+ script(&a, data);
+ free(a.s);
+ }
+ }
+
+ return TRUE;
+}
+
+Eina_Bool
+navigate(const Arg *arg, void *data)
+{
+ App_Data *ad = data;
+ if (arg->i & NavigationForwardBack)
+ // webkit_web_view_go_back_or_forward(webview, (arg->i == NavigationBack ? -1 : 1) * (count ? count : 1));
+ elm_web_navigate(ad->current_web, (arg->i == NavigationBack ? -1 : 1) * (ad->count ? ad->count : 1));
+ else if (arg->i & NavigationReloadActions)
+ // (arg->i == NavigationReload ? webkit_web_view_reload : webkit_web_view_reload_bypass_cache)(webview);
+ (arg->i == NavigationReload ? elm_web_reload : elm_web_reload_full)(ad->current_web);
+ else
+ // webkit_web_view_stop_loading(webview);
+ elm_web_stop(ad->current_web);
+ return TRUE;
+}
+
+Eina_Bool
+number(const Arg *arg, void *data)
+{
+ App_Data *ad = data;
+ // const char *source = webkit_web_view_get_uri(webview);
+ const char *source = elm_web_uri_get(ad->current_web);
+ char *uri, *p, *new;
+ int number, diff = (ad->count ? ad->count : 1) * (arg->i == Increment ? 1 : -1);
+
+ if (!source)
+ return TRUE;
+ uri = g_strdup(source); /* copy string */
+ p =& uri[0];
+ while(*p != '\0') /* goto the end of the string */
+ ++p;
+ --p;
+ while(*p >= '0' && *p <= '9') /* go back until non number char is reached */
+ --p;
+ if (*(++p) == '\0') { /* if no numbers were found abort */
+ free(uri);
+ return TRUE;
+ }
+ number = atoi(p) + diff; /* apply diff on number */
+ *p = '\0';
+ new = strdup_printf("%s%d", uri, number); /* create new uri */
+ // webkit_web_view_load_uri(webview, new);
+ elm_web_uri_set(ad->current_web, new);
+ free(new);
+ free(uri);
+ return TRUE;
+}
+
+Eina_Bool
+open_arg(const Arg *arg, void *data)
+{
+ // char *argv[64];
+ char *s = arg->s, *p = NULL, *new;
+ Arg a = { .i = NavigationReload };
+ int len;
+ char *search_uri, *search_term;
+ App_Data *ad = data;
+
+ /*
+ if (embed) {
+ argv[0] = *args;
+ argv[1] = "-e";
+ argv[2] = winid;
+ argv[3] = arg->s;
+ argv[4] = NULL;
+ } else {
+ argv[0] = *args;
+ argv[1] = arg->s;
+ argv[2] = NULL;
+ }
+ */
+
+ if (!arg->s)
+ navigate(&a, data);
+ else if (arg->i == TargetCurrent) {
+ while(*s == ' ') /* strip leading whitespace */
+ ++s;
+ p = (s + strlen(s) - 1);
+ while(*p == ' ') /* strip trailing whitespace */
+ --p;
+ *(p + 1) = '\0';
+ len = strlen(s);
+ new = NULL;
+ /* check for external handlers */
+ if (open_handler(s))
+ return TRUE;
+ /* check for search engines */
+ p = strchr(s, ' ');
+ if (p) { /* check for search engines */
+ *p = '\0';
+ search_uri = find_uri_for_searchengine(s);
+ if (search_uri != NULL) {
+ search_term = soup_uri_encode(p+1, "&");
+ new = strdup_printf(search_uri, search_term);
+ free(search_term);
+ }
+ *p = ' ';
+ }
+ if (!new) {
+ if (len > 3 && strstr(s, "://")) { /* valid url? */
+ p = new = g_malloc(len + 1);
+ while(*s != '\0') { /* strip whitespaces */
+ if (*s != ' ')
+ *(p++) = *s;
+ ++s;
+ }
+ *p = '\0';
+ } else if (strcspn(s, "/") == 0 || strcspn(s, "./") == 0) { /* prepend "file://" */
+ new = g_malloc(sizeof("file://") + len);
+ strcpy(new, "file://");
+ memcpy(&new[sizeof("file://") - 1], s, len + 1);
+ } else if (p || !strchr(s, '.')) { /* whitespaces or no dot? */
+ search_uri = find_uri_for_searchengine(defaultsearch);
+ if (search_uri != NULL) {
+ search_term = soup_uri_encode(s, "&");
+ new = g_strdup_printf(search_uri, search_term);
+ g_free(search_term);
+ }
+ } else { /* prepend "http://" */
+ new = g_malloc(sizeof("http://") + len);
+ strcpy(new, "http://");
+ memcpy(&new[sizeof("http://") - 1], s, len + 1);
+ }
+ }
+ // webkit_web_view_load_uri(webview, new);
+ elm_web_uri_set(ad->current_web, new);
+ free(new);
+ } // else
+ // g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL);
+ return TRUE;
+}
+
+Eina_Bool
+open_remembered(const Arg *arg, void *data)
+{
+ App_Data *ad = data;
+
+ Arg a = {arg->i, ad->rememberedURI};
+
+ if (strcmp(ad->rememberedURI, "")) {
+ open_arg(&a, data);
+ }
+ return TRUE;
+}
+
+Eina_Bool
+yank(const Arg *arg, void *data) {
+ /*
+ const char *url, *feedback, *content;
+
+ if (arg->i & SourceSelection) {
+ webkit_web_view_copy_clipboard(webview);
+ if (arg->i & ClipboardPrimary)
+ content = gtk_clipboard_wait_for_text(clipboards[0]);
+ if (!content && arg->i & ClipboardGTK)
+ content = gtk_clipboard_wait_for_text(clipboards[1]);
+ if (content) {
+ feedback = g_strconcat("Yanked ", content, NULL);
+ g_free((gpointer *)content);
+ give_feedback(feedback);
+ g_free((gpointer *)feedback);
+ }
+ } else {
+ if (arg->i & SourceURL) {
+ url = webkit_web_view_get_uri(webview);
+ } else {
+ url = arg->s;
+ }
+ if (!url)
+ return TRUE;
+ feedback = g_strconcat("Yanked ", url, NULL);
+ give_feedback(feedback);
+ if (arg->i & ClipboardPrimary)
+ gtk_clipboard_set_text(clipboards[0], url, -1);
+ if (arg->i & ClipboardGTK)
+ gtk_clipboard_set_text(clipboards[1], url, -1);
+ }
+ */
+ return TRUE;
+}
+
+Eina_Bool
+paste(const Arg *arg, void *data) {
+ /*
+ Arg a = { .i = arg->i & TargetNew, .s = NULL };
+
+ // If we're over a link, open it in a new target.
+ if (strlen(rememberedURI) > 0) {
+ Arg new_target = { .i = TargetNew, .s = arg->s };
+ open_arg(&new_target);
+ return TRUE;
+ }
+
+ if (arg->i & ClipboardPrimary)
+ a.s = gtk_clipboard_wait_for_text(clipboards[0]);
+ if (!a.s && arg->i & ClipboardGTK)
+ a.s = gtk_clipboard_wait_for_text(clipboards[1]);
+ if (a.s) {
+ open_arg(&a);
+ g_free(a.s);
+ }
+ */
+ return TRUE;
+}
+
+Eina_Bool
+tab_quit(const Arg *arg, void *data)
+{
+ FILE *f;
+ char *filename;
+ App_Data *ad = data;
+ if (!ad->current_web)
+ return EINA_TRUE;
+
+ // const char *uri = webkit_web_view_get_uri(webview);
+ const char *uri = elm_web_uri_get(ad->current_web);
+ if (uri != NULL) {
+ /* write last URL into status file for recreation with "u" */
+ filename = strdup_printf(CLOSED_URL_FILENAME);
+ f = fopen(filename, "w");
+ free(filename);
+ if (f != NULL) {
+ fprintf(f, "%s", uri);
+ fclose(f);
+ }
+ }
+
+ evas_object_del(ad->current_web);
+ // elm_naviframe_item_pop(ad->naviframe);
+ // Elm_Object_Item *obj_item = elm_naviframe_top_item_get(ad->naviframe);
+ // if (obj_item) ad->current_web = elm_object_item_widget_get(obj_item);
+ // if (obj_item) ad->current_web = elm_object_item_part_content_get(obj_item, NULL);
+ // else quit(NULL, data);
+
+ return EINA_TRUE;
+}
+
+Eina_Bool
+quit(const Arg *arg, void *data)
+{
+ App_Data *ad = data;
+ ad->exiting = EINA_TRUE;
+ elm_shutdown();
+ return 0;
+}
+
+Eina_Bool
+revive(const Arg *arg, void *data)
+{
+ App_Data *ad = data;
+
+ FILE *f;
+ char *filename;
+ char buffer[512] = "";
+ Arg a = { .i = TargetNew, .s = NULL };
+ /* get the URL of the window which has been closed last */
+ filename = strdup_printf(CLOSED_URL_FILENAME);
+ f = fopen(filename, "r");
+ free(filename);
+ if (f != NULL) {
+ fgets(buffer, 512, f);
+ fclose(f);
+ }
+ if (strlen(buffer) > 0) {
+ a.s = buffer;
+ open_arg(&a, data);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+Eina_Bool
+print_frame(const Arg *arg, void *data)
+{
+ /* can webkit-efl even do this?
+ *
+ WebKitWebFrame *frame = webkit_web_view_get_main_frame(webview);
+ webkit_web_frame_print (frame);
+ */
+ return TRUE;
+}
+
+Eina_Bool
+search(const Arg *arg, void *data)
+{
+ App_Data *ad = data;
+
+ ad->count = ad->count ? ad->count : 1;
+ Eina_Bool success, direction = arg->i & DirectionPrev;
+ Arg a;
+
+ if (arg->s) {
+ free(ad->search_handle);
+ ad->search_handle = strdup(arg->s);
+ }
+ if (!ad->search_handle)
+ return TRUE;
+ if (arg->i & DirectionAbsolute)
+ ad->search_direction = direction;
+ else
+ direction ^= ad->search_direction;
+ do {
+ // success = webkit_web_view_search_text(webview, search_handle, arg->i & CaseSensitive, direction, FALSE);
+ success = elm_web_text_search(ad->current_web, ad->search_handle, arg->i & CaseSensitive, direction, FALSE);
+ if (!success) {
+ if (arg->i & Wrapping) {
+ // success = webkit_web_view_search_text(webview, search_handle, arg->i & CaseSensitive, direction, TRUE);
+ success = elm_web_text_search(ad->current_web, ad->search_handle, arg->i & CaseSensitive, direction, EINA_TRUE);
+ if (success) {
+ a.i = Warning;
+ a.s = strdup_printf("search hit %s, continuing at %s",
+ direction ? "BOTTOM" : "TOP",
+ direction ? "TOP" : "BOTTOM");
+ echo(&a, data);
+ free(a.s);
+ } else
+ break;
+ } else
+ break;
+ }
+ } while(--(ad->count));
+ if (!success) {
+ a.i = Error;
+ a.s = strdup_printf("<bold>Pattern not found: %s", ad->search_handle);
+ echo(&a, data);
+ free(a.s);
+ }
+ return TRUE;
+}
+
+Eina_Bool
+set(const Arg *arg, void *data)
+{
+ App_Data *ad = data;
+ Arg a = { .i = Info | NoAutoHide };
+
+ switch (arg->i) {
+ case ModeNormal:
+ if (ad->search_handle) {
+ ad->search_handle = NULL;
+ // webkit_web_view_unmark_text_matches(webview);
+ elm_web_text_matches_unmark_all(ad->current_web);
+ }
+ // gtk_entry_set_text(GTK_ENTRY(inputbox), "");
+ // gtk_widget_grab_focus(GTK_WIDGET(webview));
+ elm_entry_entry_set(ad->url, "");
+ elm_object_focus_set(ad->current_web, EINA_TRUE);
+ break;
+ case ModePassThrough:
+ a.s = strdup("-- PASS THROUGH --");
+ echo(&a, data);
+ free(a.s);
+ break;
+ case ModeSendKey:
+ a.s = strdup("-- PASS TROUGH (next) --");
+ echo(&a, data);
+ free(a.s);
+ break;
+ case ModeInsert: /* should not be called manually but automatically */
+ a.s = strdup("-- INSERT --");
+ echo(&a, data);
+ free(a.s);
+ break;
+ default:
+ return TRUE;
+ }
+ ad->mode = arg->i;
+ return TRUE;
+}
+
+/*
+gchar*
+jsapi_ref_to_string(JSContextRef context, JSValueRef ref) {
+ JSStringRef string_ref;
+ gchar *string;
+ size_t length;
+
+ string_ref = JSValueToStringCopy(context, ref, NULL);
+ length = JSStringGetMaximumUTF8CStringSize(string_ref);
+ string = g_new(gchar, length);
+ JSStringGetUTF8CString(string_ref, string, length);
+ JSStringRelease(string_ref);
+ return string;
+}
+
+void
+jsapi_evaluate_script(const gchar *script, gchar **value, gchar **message) {
+ WebKitWebFrame *frame = webkit_web_view_get_main_frame(webview);
+ JSGlobalContextRef context = webkit_web_frame_get_global_context(frame);
+ JSStringRef str;
+ JSValueRef val, exception;
+
+ str = JSStringCreateWithUTF8CString(script);
+ val = JSEvaluateScript(context, str, JSContextGetGlobalObject(context), NULL, 0, &exception);
+ JSStringRelease(str);
+ if (!val)
+ *message = jsapi_ref_to_string(context, exception);
+ else
+ *value = jsapi_ref_to_string(context, val);
+}
+*/
+
+Eina_Bool
+quickmark(const Arg *a, void *data)
+{
+ int i, b;
+ App_Data *ad = data;
+ b = atoi(a->s);
+ char *fn = strdup_printf(QUICKMARK_FILE);
+ FILE *fp;
+ fp = fopen(fn, "r");
+ free(fn);
+ fn = NULL;
+ char buf[100];
+
+ if (fp != NULL && b < 10) {
+ for( i=0; i < b; ++i ) {
+ if (feof(fp)) {
+ break;
+ }
+ fgets(buf, 100, fp);
+ }
+ char *ptr = strrchr(buf, '\n');
+ *ptr = '\0';
+ Arg x = { .s = buf };
+ if (strlen(buf))
+ return open_arg(&x, data);
+ else {
+ x.i = Error;
+ x.s = strdup_printf("Quickmark %d not defined", b);
+ echo(&x, data);
+ free(x.s);
+ return EINA_FALSE;
+ }
+ } else { return EINA_FALSE; }
+}
+
+Eina_Bool
+script(const Arg *arg, void *data)
+{
+ // gchar *value = NULL, *message = NULL;
+ const char *value;
+ char text[1024] = "";
+ Arg a;
+ // WebKitNetworkRequest *request;
+ // WebKitDownload *download;
+ App_Data *ad = data;
+ Evas_Object *view = elm_web_webkit_view_get(ad->current_web);
+ Evas_Object *frame = ewk_view_frame_main_get(view);
+
+ if (!arg->s) {
+ set_error("Missing argument.", data);
+ return EINA_FALSE;
+ }
+ /*
+ jsapi_evaluate_script(arg->s, &value, &message);
+ if (message) {
+ set_error(message);
+ g_free(message);
+ return FALSE;
+ }
+ g_free(message);
+ */
+ value = ewk_frame_script_execute(frame, arg->s);
+ if (arg->i != Silent && value) {
+ a.i = arg->i;
+ a.s = strdup(value);
+ echo(&a, data);
+ free(a.s);
+ }
+ /* switch mode according to scripts return value */
+ if (value) {
+ if (strncmp(value, "done;", 5) == 0) {
+ a.i = ModeNormal;
+ set(&a, data);
+ } else if (strncmp(value, "insert;", 7) == 0) {
+ a.i = ModeInsert;
+ set(&a, data);
+ ad->manual_focus = TRUE;
+ } else if (strncmp(value, "save;", 5) == 0) {
+ /* forced download */
+ a.i = ModeNormal;
+ set(&a, data);
+ printf("script() js returned save, not implemented.\n");
+ // request = webkit_network_request_new((value + 5));
+ // download = webkit_download_new(request);
+ // webview_download_cb(webview, download, (gpointer *)NULL);
+ } else if (strncmp(value, "yank;", 5) == 0) {
+ /* yank link URL to clipboard */
+ a.i = ModeNormal;
+ set(&a, echo);
+ printf("script() js returned yank, not implemented.\n");
+ // a.i = ClipboardPrimary | ClipboardGTK;
+ // a.s = (value + 5);
+ // yank(&a);
+ } else if (strncmp(value, "colon;", 6) == 0) {
+ /* use link URL for colon command */
+ // strncpy(text, (char *)gtk_entry_get_text(GTK_ENTRY(inputbox)), 1023);
+ strncpy(text, elm_entry_entry_get(ad->url), 1023);
+ a.i = ModeNormal;
+ set(&a, data);
+ switch (text[1]) {
+ case 'O':
+ // a.s = g_strconcat(":open ", (value + 6), NULL);
+ a.s = strdup_printf(":open %s", value + 6);
+ break;
+ case 'T': case 'W':
+ // a.s = g_strconcat(":tabopen ", (value + 6), NULL);
+ a.s = strdup_printf(":tabopen %s", value + 6);
+ break;
+ }
+ if (a.s) {
+ input(&a, data);
+ free(a.s);
+ }
+ } else if (strncmp(value, "error;", 6) == 0) {
+ a.i = Error;
+ set(&a, data);
+ }
+ }
+ // free(value);
+ eina_stringshare_del(value);
+ return TRUE;
+}
+
+Eina_Bool
+scroll(const Arg *arg, void *data)
+{
+ // GtkAdjustment *adjust = (arg->i & OrientationHoriz) ? adjust_h : adjust_v;
+ // int max = gtk_adjustment_get_upper(adjust) - gtk_adjustment_get_page_size(adjust);
+ // float val = gtk_adjustment_get_value(adjust) / max * 100;
+
+ int x, y, w, h, page_w, page_h;
+ float val;
+ Eina_Bool horizontal = EINA_FALSE;
+ App_Data *ad = data;
+ Evas_Object *view = elm_web_webkit_view_get(ad->current_web);
+ Evas_Object *frame = ewk_view_frame_main_get(view);
+
+ ewk_frame_visible_content_geometry_get(frame, EINA_FALSE, &x, &y, &page_w, &page_h);
+ ewk_frame_scroll_pos_get(frame, &x, &y);
+ ewk_frame_scroll_size_get(frame, &w, &h);
+
+ if (arg->i & OrientationHoriz)
+ horizontal = EINA_TRUE;
+
+ val = (float) (horizontal? x : y) / h * 100;
+
+ int direction = (arg->i & (1 << 2)) ? 1 : -1;
+
+ if ((direction == 1 && val < 100) || (direction == -1 && val > 0)) {
+ if (arg->i & ScrollMove) {
+ int total = direction * /* direction */
+ ((arg->i & UnitLine || (arg->i & UnitBuffer && ad->count)) ? (scrollstep * (ad->count ? ad->count : 1)) : (
+ // arg->i & UnitBuffer ? gtk_adjustment_get_page_size(adjust) / 2 :
+ arg->i & UnitBuffer ? (horizontal ? page_w : page_h) / 2 :
+ // (count ? count : 1) * (gtk_adjustment_get_page_size(adjust) -
+ (ad->count ? ad->count : 1) * ((horizontal ? page_w : page_h) -
+ // (gtk_adjustment_get_page_size(adjust) > pagingkeep ? pagingkeep : 0)))));
+ ((horizontal ? page_w : page_h) > pagingkeep ? pagingkeep : 0))));
+
+ // gtk_adjustment_set_value(adjust, gtk_adjustment_get_value(adjust) +
+ if (horizontal)
+ ewk_frame_scroll_set(frame, x + total, y);
+ else
+ ewk_frame_scroll_set(frame, x, y + total);
+
+ }
+ else {
+ // gtk_adjustment_set_value(adjust,
+ // ((direction == 1) ? gtk_adjustment_get_upper : gtk_adjustment_get_lower)(adjust));
+ if (horizontal)
+ ewk_frame_scroll_set(frame, ((direction == 1) ? w : 0), y);
+ else
+ ewk_frame_scroll_set(frame, x, ((direction == 1) ? h : 0));
+ }
+ update_state(data);
+ }
+ return TRUE;
+}
+
+Eina_Bool
+zoom(const Arg *arg, void *data)
+{
+ App_Data *ad = data;
+
+ // webkit_web_view_set_full_content_zoom(webview, (arg->i & ZoomFullContent) > 0);
+ // webkit_web_view_set_zoom_level(ad->current_tab->web, (arg->i & ZoomOut) ?
+ elm_web_zoom_set(ad->current_web, (arg->i & ZoomOut) ?
+ // webkit_web_view_get_zoom_level(webview) +
+ elm_web_zoom_get(ad->current_web) +
+ (((float)(ad->count ? ad->count : 1)) * (arg->i & (1 << 1) ? 1.0 : -1.0) * ad->zoomstep) :
+ (ad->count ? (float)ad->count / 100.0 : 1.0));
+ return TRUE;
+}
+
+/*
+Eina_Bool
+fake_key_event(const Arg *a, void *data) {
+ if(!embed) {
+ return FALSE;
+ }
+ Arg err;
+ err.i = Error;
+ Display *xdpy;
+ if ( (xdpy = XOpenDisplay(NULL)) == NULL ) {
+ err.s = g_strdup("Couldn't find the XDisplay.");
+ echo(&err);
+ g_free(err.s);
+ return FALSE;
+ }
+
+ XKeyEvent xk;
+ xk.display = xdpy;
+ xk.subwindow = None;
+ xk.time = CurrentTime;
+ xk.same_screen = True;
+ xk.x = xk.y = xk.x_root = xk.y_root = 1;
+ xk.window = embed;
+ xk.state = a->i;
+
+ if( ! a->s ) {
+ err.s = g_strdup("Zero pointer as argument! Check your config.h");
+ echo(&err);
+ g_free(err.s);
+ return FALSE;
+ }
+
+ KeySym keysym;
+ if( (keysym = XStringToKeysym(a->s)) == NoSymbol ) {
+ err.s = g_strdup_printf("Couldn't translate %s to keysym", a->s );
+ echo(&err);
+ g_free(err.s);
+ return FALSE;
+ }
+
+ if( (xk.keycode = XKeysymToKeycode(xdpy, keysym)) == NoSymbol ) {
+ err.s = g_strdup("Couldn't translate keysym to keycode");
+ echo(&err);
+ g_free(err.s);
+ return FALSE;
+ }
+
+ xk.type = KeyPress;
+ if( !XSendEvent(xdpy, embed, True, KeyPressMask, (XEvent *)&xk) ) {
+ err.s = g_strdup("XSendEvent failed");
+ echo(&err);
+ g_free(err.s);
+ return FALSE;
+ }
+ XFlush(xdpy);
+
+ return TRUE;
+}
+*/
+
+Eina_Bool
+bookmark(const Arg *arg, void *data)
+{
+ FILE *f;
+ char *filename;
+ App_Data *ad = data;
+ const char *uri = elm_web_uri_get(ad->current_web);
+ const char *title = elm_web_title_get(ad->current_web);
+ filename = strdup_printf(BOOKMARKS_STORAGE_FILENAME);
+ f = fopen(filename, "a");
+ free(filename);
+ if (uri == NULL || strlen(uri) == 0) {
+ set_error("No URI found to bookmark.", data);
+ return EINA_FALSE;
+ }
+ if (f != NULL) {
+ fprintf(f, "%s", uri);
+ if (title != NULL) {
+ fprintf(f, "%s", " ");
+ fprintf(f, "%s", title);
+ }
+ if (arg->s && strlen(arg->s)) {
+ build_taglist(arg, f);
+ }
+ fprintf(f, "%s", "\n");
+ fclose(f);
+ give_feedback("Bookmark saved", data);
+ return EINA_TRUE;
+ } else {
+ set_error("Bookmarks file not found.", data);
+ return EINA_FALSE;
+ }
+}
+
+Eina_Bool
+view_source(const Arg * arg, void *data)
+{
+ printf("view_source() source mode not implemented yet.\n");
+ /*
+ gboolean current_mode = webkit_web_view_get_view_source_mode(webview);
+ webkit_web_view_set_view_source_mode(webview, !current_mode);
+ webkit_web_view_reload(webview);
+ */
+ return TRUE;
+}
+
+Eina_Bool
+focus_input(const Arg *arg, void *data)
+{
+ static Arg a;
+ App_Data *ad = data;
+
+ a.s = strdup("hints.focusInput();");
+ a.i = Silent;
+ script(&a, data);
+ free(a.s);
+ update_state(data);
+ ad->manual_focus = TRUE;
+ return TRUE;
+}
+
+/*
+Eina_Bool
+browser_settings(const Arg *arg, void *data) {
+ char line[255];
+ if (!arg->s) {
+ set_error("Missing argument.");
+ return FALSE;
+ }
+ strncpy(line, arg->s, 254);
+ if (process_set_line(line))
+ return TRUE;
+ else {
+ set_error("Invalid setting.");
+ return FALSE;
+ }
+}
+*/
+
+void
+toggle_proxy(Eina_Bool onoff)
+{
+ // SoupURI *proxy_uri;
+ char *filename, *new;
+
+ if (onoff == FALSE) {
+ // g_object_set(session, "proxy-uri", NULL, NULL);
+ ewk_network_proxy_uri_set(NULL);
+ } else {
+ filename = getenv("http_proxy");
+
+ /* Fallthrough to checking HTTP_PROXY as well, since this can also be
+ * defined.
+ */
+ if (filename == NULL)
+ filename = getenv("HTTP_PROXY");
+
+ if (filename != NULL && 0 < strlen(filename)) {
+ new = strstr(filename, "http://") ? strdup(filename) : strdup_printf("http://%s", filename);
+ // proxy_uri = soup_uri_new(new);
+
+ // g_object_set(session, "proxy-uri", proxy_uri, NULL);
+ printf("toggle_proxy(%i, %s)\n", onoff, new);
+ ewk_network_proxy_uri_set(new);
+
+ // soup_uri_free(proxy_uri);
+ free(new);
+ }
+ }
+}
+
+Eina_Bool
+process_set_line(char *line) {
+#if 0
+ char *c;
+ int listlen, i;
+ gboolean boolval;
+ WebKitWebSettings *settings;
+
+ settings = webkit_web_view_get_settings(webview);
+ my_pair.line = line;
+ c = search_word(0);
+ if (!strlen(my_pair.what))
+ return FALSE;
+
+ while (isspace(*c) && *c)
+ c++;
+
+ if (*c == ':' || *c == '=')
+ c++;
+
+ my_pair.line = c;
+ c = search_word(1);
+
+ listlen = LENGTH(browsersettings);
+ for (i = 0; i < listlen; i++) {
+ if (strlen(browsersettings[i].name) == strlen(my_pair.what) && strncmp(browsersettings[i].name, my_pair.what, strlen(my_pair.what)) == 0) {
+ /* mandatory argument not provided */
+ if (strlen(my_pair.value) == 0)
+ return FALSE;
+ /* process qmark? */
+ if (strlen(my_pair.what) == 5 && strncmp("qmark", my_pair.what, 5) == 0) {
+ return (process_save_qmark(my_pair.value, webview));
+ }
+ /* interpret boolean values */
+ if (browsersettings[i].boolval) {
+ if (strncmp(my_pair.value, "on", 2) == 0 || strncmp(my_pair.value, "true", 4) == 0 || strncmp(my_pair.value, "ON", 2) == 0 || strncmp(my_pair.value, "TRUE", 4) == 0) {
+ boolval = TRUE;
+ } else if (strncmp(my_pair.value, "off", 3) == 0 || strncmp(my_pair.value, "false", 5) == 0 || strncmp(my_pair.value, "OFF", 3) == 0 || strncmp(my_pair.value, "FALSE", 5) == 0) {
+ boolval = FALSE;
+ } else {
+ return FALSE;
+ }
+ } else if (browsersettings[i].colourval) {
+ /* interpret as hexadecimal colour */
+ if (!parse_colour(my_pair.value)) {
+ return FALSE;
+ }
+ }
+ if (browsersettings[i].var != NULL) {
+ strncpy(browsersettings[i].var, my_pair.value, MAX_SETTING_SIZE);
+ if (strlen(my_pair.value) > MAX_SETTING_SIZE - 1) {
+ /* in this case, \0 will not have been copied */
+ browsersettings[i].var[MAX_SETTING_SIZE - 1] = '\0';
+ /* in case this string is also used for a webkit setting, make sure it's consistent */
+ my_pair.value[MAX_SETTING_SIZE - 1] = '\0';
+ give_feedback("String too long; automatically truncated!");
+ }
+ }
+ if (strlen(browsersettings[i].webkit) > 0) {
+ /* activate appropriate webkit setting */
+ if (browsersettings[i].boolval) {
+ g_object_set((GObject*)settings, browsersettings[i].webkit, boolval, NULL);
+ } else if (browsersettings[i].intval) {
+ g_object_set((GObject*)settings, browsersettings[i].webkit, atoi(my_pair.value), NULL);
+ } else {
+ g_object_set((GObject*)settings, browsersettings[i].webkit, my_pair.value, NULL);
+ }
+ webkit_web_view_set_settings(webview, settings);
+ }
+
+ if (strlen(my_pair.what) == 14) {
+ if (strncmp("acceptlanguage", my_pair.what, 14) == 0) {
+ g_object_set(G_OBJECT(session), "accept-language", acceptlanguage, NULL);
+ } else if (strncmp("completioncase", my_pair.what, 14) == 0) {
+ complete_case_sensitive = boolval;
+ }
+ } else if (strlen(my_pair.what) == 5 && strncmp("proxy", my_pair.what, 5) == 0) {
+ toggle_proxy(boolval);
+ } else if (strlen(my_pair.what) == 10 && strncmp("scrollbars", my_pair.what, 10) == 0) {
+ toggle_scrollbars(boolval);
+ } else if (strlen(my_pair.what) == 9 && strncmp("statusbar", my_pair.what, 9) == 0) {
+ gtk_widget_set_visible(GTK_WIDGET(statusbar), boolval);
+ } else if (strlen(my_pair.what) == 8 && strncmp("inputbox", my_pair.what, 8) == 0) {
+ gtk_widget_set_visible(inputbox, boolval);
+ } else if (strlen(my_pair.what) == 11 && strncmp("escapeinput", my_pair.what, 11) == 0) {
+ escape_input_on_load = boolval;
+ }
+
+ /* SSL certificate checking */
+ if (strlen(my_pair.what) == 9 && strncmp("strictssl", my_pair.what, 9) == 0) {
+ if (boolval) {
+ strict_ssl = TRUE;
+ g_object_set(G_OBJECT(session), "ssl-strict", TRUE, NULL);
+ } else {
+ strict_ssl = FALSE;
+ g_object_set(G_OBJECT(session), "ssl-strict", FALSE, NULL);
+ }
+ }
+ if (strlen(my_pair.what) == 8 && strncmp("cabundle", my_pair.what, 8) == 0) {
+ g_object_set(G_OBJECT(session), SOUP_SESSION_SSL_CA_FILE, ca_bundle, NULL);
+ }
+ if (strlen(my_pair.what) == 10 && strncmp("windowsize", my_pair.what, 10) == 0) {
+ set_default_winsize(my_pair.value);
+ }
+
+ /* reload page? */
+ if (browsersettings[i].reload)
+ webkit_web_view_reload(webview);
+ return TRUE;
+ }
+ }
+#endif
+ return FALSE;
+}
+
+Eina_Bool
+search_tag(const Arg * a, void *data)
+{
+ FILE *f;
+ char *filename;
+ App_Data *ad = data;
+ const char *tag = a->s;
+ char s[BUFFERSIZE], foundtag[40], url[BUFFERSIZE];
+ int t, i, intag, k;
+
+ if (!tag) {
+ /* The user must give us something to load up. */
+ set_error("Bookmark tag required with this option.", data);
+ return FALSE;
+ }
+
+ if (strlen(tag) > MAXTAGSIZE) {
+ set_error("Tag too long.", data);
+ return FALSE;
+ }
+
+ filename = strdup_printf(BOOKMARKS_STORAGE_FILENAME);
+ f = fopen(filename, "r");
+ free(filename);
+ if (f == NULL) {
+ set_error("Couldn't open bookmarks file.", data);
+ return FALSE;
+ }
+ while (fgets(s, BUFFERSIZE-1, f)) {
+ intag = 0;
+ t = strlen(s) - 1;
+ while (isspace(s[t]))
+ t--;
+ if (s[t] != ']') continue;
+ while (t > 0) {
+ if (s[t] == ']') {
+ if (!intag)
+ intag = t;
+ else
+ intag = 0;
+ } else {
+ if (s[t] == '[') {
+ if (intag) {
+ i = 0;
+ k = t + 1;
+ while (k < intag)
+ foundtag[i++] = s[k++];
+ foundtag[i] = '\0';
+ /* foundtag now contains the tag */
+ if (strlen(foundtag) < MAXTAGSIZE && strcmp(tag, foundtag) == 0) {
+ i = 0;
+ while (isspace(s[i])) i++;
+ k = 0;
+ while (s[i] && !isspace(s[i])) url[k++] = s[i++];
+ url[k] = '\0';
+ Arg x = { .i = TargetNew, .s = url };
+ open_arg(&x, data);
+ }
+ }
+ intag = 0;
+ }
+ }
+ t--;
+ }
+ }
+ return EINA_TRUE;
+}
+
+Eina_Bool
+list(const Arg *arg, void *data)
+{
+ char *url, *count_str;
+ App_Data *ad = data;
+
+ Eina_List *l;
+ Tab_Data *td;
+ Evas_Object *icon, *row, *label;
+ Evas *e = evas_object_evas_get(ad->win);
+
+ elm_box_clear(ad->event_box);
+
+ EINA_LIST_FOREACH(ad->buffer_list, l, td) {
+ // EINA_LIST_FOREACH(elm_naviframe_items_get(ad->naviframe), l, i) {
+ // web = elm_object_item_part_content_get(i, NULL);
+ url = strdup_printf("<font=Monospace font_size=12>%s", elm_web_uri_get(td->web));
+
+ row = elm_box_add(ad->win);
+ elm_box_homogeneous_set(row, EINA_FALSE);
+ elm_box_horizontal_set(row, EINA_TRUE);
+ evas_object_size_hint_weight_set(row, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+ evas_object_size_hint_align_set(row, 0.0, EVAS_HINT_FILL);
+ elm_box_pack_end(ad->event_box, row);
+ evas_object_show(row);
+
+ label = elm_label_add(ad->win);
+ evas_object_size_hint_weight_set(row, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+ evas_object_size_hint_align_set(row, 0.0, EVAS_HINT_FILL);
+ elm_box_pack_end(row, label);
+ evas_object_show(label);
+ count_str = strdup_printf("<font=Monospace font_size=12>%i ", td->buf_number);
+ elm_object_text_set(label, count_str);
+ free(count_str);
+
+ // e = evas_object_evas_get(ad->win);
+ icon = ewk_settings_icon_database_icon_object_get(elm_web_uri_get(td->web), e);
+ // evas_object_image_filled_set(icon, EINA_FALSE);
+ // int w, h;
+
+ // evas_object_image_scale_hint_set(icon, EVAS_IMAGE_SCALE_HINT_NONE);
+ evas_object_size_hint_weight_set(icon, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+ evas_object_size_hint_align_set(icon, 0.0, EVAS_HINT_FILL);
+
+ // evas_object_image_size_get(icon, &w, &h);
+ // printf("icon for %s has size (%i, %i)\n", url, w, h);
+
+ evas_object_size_hint_max_set(icon, 16, 16);
+ evas_object_size_hint_min_set(icon, 16, 16);
+ // evas_object_size_hint_aspect_set(icon, EVAS_ASPECT_CONTROL_BOTH, 0, 0);
+ elm_box_pack_end(row, icon);
+ evas_object_show(icon);
+
+ label = elm_label_add(ad->win);
+ evas_object_size_hint_weight_set(row, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+ evas_object_size_hint_align_set(row, 0.0, EVAS_HINT_FILL);
+ elm_box_pack_end(row, label);
+ evas_object_show(label);
+
+ // url = elm_web_uri_get(web);
+ elm_object_text_set(label, url);
+ free(url);
+ }
+
+ evas_object_show(ad->event_box);
+
+
+ return EINA_TRUE;
+}
+
+Eina_Bool
+switch_buffer(const Arg *arg, void *data)
+{
+ App_Data *ad = data;
+ Eina_List *l;
+ Tab_Data *td;
+ // int arg_count = atoi(arg->s);
+
+
+ EINA_LIST_FOREACH(ad->buffer_list, l, td) {
+ /*
+ if (arg_count == td->buf_number) {
+ buffer_current_set(td);
+ return EINA_TRUE;
+ }
+ */
+ if (ad->count == td->buf_number) {
+ buffer_current_set(td);
+ return EINA_TRUE;
+ }
+ }
+
+ return EINA_FALSE;
+}
+
+Eina_Bool
+inspector(const Arg *arg, void *data)
+{
+ App_Data *ad = data;
+ Evas_Object *view = elm_web_webkit_view_get(ad->current_web);
+
+ printf("inspector(), arg-> is %i!\n", arg->i);
+
+ if (arg->i & ShowInspector && !ewk_view_web_inspector_view_get(view)) {
+ ewk_view_setting_enable_developer_extras_set(view, EINA_TRUE);
+
+ Evas_Object *inspector_view = ewk_view_single_add(evas_object_evas_get(ad->win));
+ ewk_view_theme_set(inspector_view, "/usr/share/ewebkit-0/themes/default.edj");
+
+
+ // Evas_Object *web = elm_web_add(ad->win);
+ // elm_web_inwin_mode_set(web, EINA_TRUE);
+ evas_object_size_hint_weight_set(inspector_view, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+ evas_object_size_hint_align_set(inspector_view, EVAS_HINT_FILL, EVAS_HINT_FILL);
+
+ // // Evas_Object *inspector_view = elm_web_webkit_view_get(web);
+
+ ewk_view_web_inspector_view_set(view, inspector_view);
+ evas_object_show(inspector_view);
+
+
+ elm_box_pack_end(ad->web_inspector, inspector_view);
+
+ ewk_view_web_inspector_show(view);
+
+ evas_object_size_hint_weight_set(ad->web_inspector, EVAS_HINT_EXPAND, 1.0);
+ evas_object_show(ad->web_inspector);
+ }
+ else if (arg->i == HideInspector && ewk_view_web_inspector_view_get(view)) {
+ ewk_view_web_inspector_close(view);
+ ewk_view_setting_enable_developer_extras_set(view, EINA_FALSE);
+ elm_box_clear(ad->web_inspector);
+ evas_object_size_hint_weight_set(ad->web_inspector, EVAS_HINT_EXPAND, 0.0);
+ }
+
+ return EINA_TRUE;
+}
+
diff --git a/commands.h b/commands.h
@@ -0,0 +1,99 @@
+
+// #include "config.h"
+// #include "utilities.h"
+
+Eina_Bool complete(const Arg *, void *);
+Eina_Bool script(const Arg *, void *);
+Eina_Bool open_arg(const Arg *, void *);
+Eina_Bool quit(const Arg *, void *);
+Eina_Bool add_tab(const Arg *, void *);
+Eina_Bool view_source(const Arg *, void *);
+Eina_Bool bookmark(const Arg *, void *);
+Eina_Bool search_tag(const Arg *, void *);
+Eina_Bool print_frame(const Arg *, void *);
+Eina_Bool scroll(const Arg *arg, void *);
+Eina_Bool open_remembered(const Arg *, void *);
+// Eina_Bool browser_settings(const Arg *, void *);
+Eina_Bool navigate(const Arg *arg, void *);
+Eina_Bool yank(const Arg *, void *);
+Eina_Bool zoom(const Arg *, void *);
+Eina_Bool descend(const Arg *, void *);
+Eina_Bool paste(const Arg *, void *);
+Eina_Bool number(const Arg *, void *data);
+Eina_Bool search(const Arg *, void *);
+Eina_Bool input(const Arg *, void *);
+Eina_Bool focus_input(const Arg *, void *);
+Eina_Bool quickmark(const Arg *, void *);
+Eina_Bool tab_quit(const Arg *, void *);
+Eina_Bool revive(const Arg *, void *);
+Eina_Bool set(const Arg *, void *);
+Eina_Bool list(const Arg *, void *);
+Eina_Bool switch_buffer(const Arg *, void *);
+Eina_Bool inspector(const Arg *, void *);
+// static Eina_Bool fake_key_event(const Arg *, void *);
+
+Eina_Bool process_set_line(char *line);
+void toggle_proxy(Eina_Bool onoff);
+
+extern char startpage[MAX_SETTING_SIZE];
+
+/* command mapping */
+static Command commands[COMMANDSIZE] = {
+ /* command, function, argument */
+ { "b", switch_buffer, {0} },
+ { "ba", navigate, {NavigationBack} },
+ { "back", navigate, {NavigationBack} },
+ { "ec", script, {Info} },
+ { "echo", script, {Info} },
+ { "echoe", script, {Error} },
+ { "echoerr", script, {Error} },
+ { "fw", navigate, {NavigationForward} },
+ { "fo", navigate, {NavigationForward} },
+ { "forward", navigate, {NavigationForward} },
+ { "javascript", script, {Silent} },
+ { "o", open_arg, {TargetCurrent} },
+ { "open", open_arg, {TargetCurrent} },
+ { "q", quit, {0} },
+ { "quit", quit, {0} },
+ { "re", navigate, {NavigationReload} },
+ { "re!", navigate, {NavigationForceReload} },
+ { "reload", navigate, {NavigationReload} },
+ { "reload!", navigate, {NavigationForceReload} },
+ { "qt", search_tag, {0} },
+ { "st", navigate, {NavigationCancel} },
+ { "stop", navigate, {NavigationCancel} },
+ { "t", add_tab, {0} },
+ { "tabopen", open_arg, {TargetNew} },
+ { "print", print_frame, {0} },
+ { "bma", bookmark, {0} },
+ { "bookmark", bookmark, {0} },
+ { "source", view_source, {0} },
+// { "set", browser_settings, {0} },
+// { "map", mappings, {0} },
+ { "jumpleft", scroll, {ScrollJumpTo | DirectionLeft} },
+ { "jumpright", scroll, {ScrollJumpTo | DirectionRight} },
+ { "jumptop", scroll, {ScrollJumpTo | DirectionTop} },
+ { "jumpbottom", scroll, {ScrollJumpTo | DirectionBottom} },
+ { "pageup", scroll, {ScrollMove | DirectionTop | UnitPage} },
+ { "pagedown", scroll, {ScrollMove | DirectionBottom | UnitPage} },
+ { "navigationback", navigate, {NavigationBack} },
+ { "navigationforward", navigate, {NavigationForward} },
+ { "scrollleft", scroll, {ScrollMove | DirectionLeft | UnitLine} },
+ { "scrollright", scroll, {ScrollMove | DirectionRight | UnitLine} },
+ { "scrollup", scroll, {ScrollMove | DirectionTop | UnitLine} },
+ { "scrolldown", scroll, {ScrollMove | DirectionBottom | UnitLine} },
+ { "ls", list, {0} },
+};
+
+
+
+/* mouse bindings
+ you can use MOUSE_BUTTON_1 to MOUSE_BUTTON_5
+*/
+//static Mouse mouse[] = {
+ /* modmask, modkey, button, function, argument */
+// {0, 0, 2, paste, {TargetCurrent | ClipboardPrimary | ClipboardGTK, rememberedURI} },
+// {"Control", 0, 2, paste, {TargetNew | ClipboardPrimary | ClipboardGTK} },
+// {"Control", 0, 1, open_remembered, {TargetNew} },
+//};
+
diff --git a/config.h b/config.h
@@ -0,0 +1,155 @@
+/*
+ (c) 2009 by Leon Winter
+ (c) 2009-2012 by Hannes Schueller
+ (c) 2009-2010 by Matto Fransen
+ (c) 2010-2011 by Hans-Peter Deifel
+ (c) 2010-2011 by Thomas Adam
+ (c) 2011 by Albert Kim
+ see LICENSE file
+*/
+
+/* Vimprobable version number */
+#define VERSION "1"
+#define INTERNAL_VERSION "Evi/"VERSION
+
+/* general settings */
+char startpage[MAX_SETTING_SIZE] = "http://www.getaddrinfo.net/";
+// static char useragent[MAX_SETTING_SIZE] = "Vimprobable2/" VERSION;
+// static char acceptlanguage[MAX_SETTING_SIZE] = "";
+static const Eina_Bool enablePlugins = TRUE; /* TRUE keeps plugins enabled */
+static const Eina_Bool enableJava = TRUE; /* FALSE disables Java applets */
+static const Eina_Bool enablePagecache = FALSE; /* TRUE turns on the page cache. */
+static Eina_Bool escape_input_on_load = TRUE; /* TRUE will disable automatic focusing of input fields via Javascript*/
+
+/* appearance */
+// static char statusbgcolor[MAX_SETTING_SIZE] = "#000"; /* background color for status bar */
+// static char statuscolor[MAX_SETTING_SIZE] = "#FFF"; /* color for status bar */
+// static char sslbgcolor[MAX_SETTING_SIZE] = "#b0ff00"; /* background color for status bar with SSL url */
+// static char sslinvalidbgcolor[MAX_SETTING_SIZE]= "#ff0000"; /* background color for status bar with unverified SSL url */
+// static char sslcolor[MAX_SETTING_SIZE] = "#0F0"; /* color for status bar with SSL url */
+
+ /* normal, warning, error */
+static const char *urlboxfont[] = { "monospace normal 8", "monospace normal 8", "monospace bold 8"};
+static const char *urlboxcolor[] = { NULL, "#ff0000", "#ffffff" };
+static const char *urlboxbgcolor[] = { NULL, NULL, "#ff0000" };
+
+ /* normal, error */
+// static const char *completionfont[] = { "monospace normal 8", "monospace bold 8" };
+ /* topborder color */
+static const unsigned char completioncolor[][3] = { {0, 0, 0}, {255, 0, 255}, {0, 0, 0} };
+/* current row background */
+static const unsigned char completionbgcolor[][3] = { {255, 255, 255}, {255, 255, 255}, {255, 240, 0} };
+/* pango markup for prefix highliting: opening, closing */
+#define COMPLETION_TAG_OPEN "<b>"
+#define COMPLETION_TAG_CLOSE "</b>"
+
+static const char statusfont[] = "monospace bold 8"; /* font for status bar */
+static const int progressbartick = 20;
+static const char progressborderleft = '[';
+static const char progressbartickchar = '=';
+static const char progressbarcurrent = '>';
+static const char progressbarspacer = ' ';
+static const char progressborderright = ']';
+
+/* external handlers:
+ * the handle (first string) contain what the handled links have to start with
+ * the handlers (second string) contain the external applications which should be called for this sort of link
+ * %s can be used as a placeholder for the link argument after the handler
+ * e.g.: "mailto:user@example.org
+ * "handle" is "mailto:"
+ * "%s" will translate to "user@example.org"
+ */
+static URIHandler uri_handlers[] = {
+ { "mailto:", "x-terminal-emulator -e mutt %s" },
+ { "ftp://", "x-terminal-emulator -e wget ftp://%s" },
+};
+
+/* cookies */
+#define ENABLE_COOKIE_SUPPORT
+#define COOKIES_STORAGE_FILENAME "%s/evi/cookies", config_base
+#define COOKIES_STORAGE_READONLY FALSE /* if TRUE new cookies will be lost if you quit */
+
+/* downloads directory */
+#define DOWNLOADS_PATH "%s", getenv("HOME")
+
+/* font size */
+#define DEFAULT_FONT_SIZE 12
+
+/* user styles */
+#define USER_STYLESHEET "%s/evi/style.css", config_base
+
+/* ssl */
+// static Eina_Bool strict_ssl = TRUE; /* FALSE will accept any SSL certificate at face value */
+static char ca_bundle[MAX_SETTING_SIZE] = "/etc/ssl/certs/ca-certificates.crt";
+
+/* proxy */
+static const Eina_Bool use_proxy = TRUE; /* TRUE if you're going to use a proxy (whose address
+ is specified in http_proxy environment variable), false otherwise */
+/* scrolling */
+unsigned int scrollstep = 40; /* cursor difference in pixel */
+unsigned int pagingkeep = 40; /* pixels kept when paging */
+#define DISABLE_SCROLLBAR
+
+/* searching */
+#define ENABLE_MATCH_HIGHLITING
+static const int searchoptions = CaseInsensitive | Wrapping;
+Eina_Bool complete_case_sensitive = TRUE;
+
+/* search engines */
+static Searchengine searchengines[] = {
+ { "d", "https://duckduckgo.com/?q=%s&t=Vimprobable" },
+ { "i", "http://ixquick.com/do/metasearch.pl?query=%s" },
+ { "w", "https://secure.wikimedia.org/wikipedia/en/w/index.php?title=Special%%3ASearch&search=%s&go=Go" },
+ { "wd", "https://secure.wikimedia.org/wikipedia/de/w/index.php?title=Special%%3ASearch&search=%s&go=Go" },
+ { "dd", "https://duckduckgo.com/html/?q=%s&t=Vimprobable" },
+};
+
+char defaultsearch[MAX_SETTING_SIZE] = "d";
+
+#if 0
+/* settings (arguments of :set command) */
+static Setting browsersettings[] = {
+/* public name, internal variwebkit setting integer ? bool? colour? reload page? */
+ { "useragent", useragent, "user-agent", FALSE, FALSE, FALSE, FALSE },
+ { "scripts", NULL, "enable-scripts", FALSE, TRUE, FALSE, FALSE },
+ { "plugins", NULL, "enable-plugins", FALSE, TRUE, FALSE, FALSE },
+ { "pagecache", NULL, "enable-page-cache", FALSE, TRUE, FALSE, FALSE },
+ { "java", NULL, "enable-java-applet", FALSE, TRUE, FALSE, FALSE },
+ { "images", NULL, "auto-load-images", FALSE, TRUE, FALSE, FALSE },
+ { "shrinkimages", NULL, "auto-shrink-images", FALSE, TRUE, FALSE, FALSE },
+ { "cursivefont", NULL, "cursive-font-family", FALSE, FALSE, FALSE, FALSE },
+ { "defaultencoding", NULL, "default-encoding", FALSE, FALSE, FALSE, FALSE },
+ { "defaultfont", NULL, "default-font-family", FALSE, FALSE, FALSE, FALSE },
+ { "fontsize", NULL, "default-font-size", TRUE, FALSE, FALSE, FALSE },
+ { "monofontsize", NULL, "default-monospace-font-size", TRUE, FALSE, FALSE, FALSE },
+ { "caret", NULL, "enable-caret-browsing", FALSE, TRUE, FALSE, FALSE },
+ { "fantasyfont", NULL, "fantasy-font-family", FALSE, FALSE, FALSE, FALSE },
+ { "minimumfontsize", NULL, "minimum-font-size", TRUE, FALSE, FALSE, FALSE },
+ { "monofont", NULL, "monospace-font-family", FALSE, FALSE, FALSE, FALSE },
+ { "backgrounds", NULL, "print-backgrounds", FALSE, TRUE, FALSE, FALSE },
+ { "sansfont", NULL, "sans-serif-font-family", FALSE, FALSE, FALSE, FALSE },
+ { "seriffont", NULL, "serif-font-family", FALSE, FALSE, FALSE, FALSE },
+ { "stylesheet", NULL, "user-stylesheet-uri", FALSE, FALSE, FALSE, FALSE },
+ { "resizetextareas", NULL, "resizable-text-areas", FALSE, TRUE, FALSE, FALSE },
+ { "webinspector", NULL, "enable-developer-extras", FALSE, TRUE, FALSE, FALSE },
+
+ { "homepage", startpage, "", FALSE, FALSE, FALSE, FALSE },
+ { "statusbgcolor", statusbgcolor,"", FALSE, FALSE, TRUE, TRUE },
+ { "statuscolor", statuscolor, "", FALSE, FALSE, TRUE, TRUE },
+ { "sslbgcolor", sslbgcolor, "", FALSE, FALSE, TRUE, TRUE },
+ { "sslcolor", sslcolor, "", FALSE, FALSE, TRUE, TRUE },
+ { "acceptlanguage", acceptlanguage, "", FALSE, FALSE, FALSE, FALSE },
+ { "defaultsearch", defaultsearch, "", FALSE, FALSE, FALSE, FALSE },
+ { "qmark", NULL, "", FALSE, FALSE, FALSE, FALSE },
+ { "proxy", NULL, "", FALSE, TRUE, FALSE, FALSE },
+ { "windowsize", NULL, "", FALSE, FALSE, FALSE, FALSE },
+ { "scrollbars", NULL, "", FALSE, TRUE, FALSE, FALSE },
+ { "statusbar", NULL, "", FALSE, TRUE, FALSE, FALSE },
+ { "inputbox", NULL, "", FALSE, TRUE, FALSE, FALSE },
+ { "completioncase", NULL, "", FALSE, TRUE, FALSE, FALSE },
+ { "escapeinput", NULL, "", FALSE, TRUE, FALSE, FALSE },
+ { "strictssl", NULL, "", FALSE, TRUE, FALSE, FALSE },
+ { "cabundle", ca_bundle, "", FALSE, FALSE, FALSE, FALSE },
+};
+#endif
+
diff --git a/data/desktop/viking.desktop b/data/desktop/viking.desktop
@@ -0,0 +1,10 @@
+[Desktop Entry]
+Encoding=UTF-8
+Type=Application
+Name=Evi
+GenericName=Enlightenment Web Browser
+Comment=Browse the web with vi like keybindings
+Exec=evi
+Icon=browser
+StartupWMClass=MPlayer
+Categories=Application;WebBrowser;
diff --git a/data/desktop/viking.jpg b/data/desktop/viking.jpg
Binary files differ.
diff --git a/hinting.js b/hinting.js
@@ -0,0 +1,455 @@
+/*
+ (c) 2009 by Leon Winter
+ (c) 2009, 2010 by Hannes Schueller
+ (c) 2010 by Hans-Peter Deifel
+ (c) 2011 by Daniel Carl
+ see LICENSE file
+*/
+function Hints() {
+ var config = {
+ maxAllowedHints: 500,
+ hintCss: "z-index:100000;font-family:monospace;font-size:10px;"
+ + "font-weight:bold;color:white;background-color:red;"
+ + "padding:0px 1px;position:absolute;",
+ hintClass: "hinting_mode_hint",
+ hintClassFocus: "hinting_mode_hint_focus",
+ elemBackground: "#ff0",
+ elemBackgroundFocus: "#8f0",
+ elemColor: "#000"
+ };
+
+ var hintContainer;
+ var currentFocusNum = 1;
+ var hints = [];
+ var mode;
+
+ this.createHints = function(inputText, hintMode)
+ {
+ if (hintMode) {
+ mode = hintMode;
+ }
+
+ var topwin = window;
+ var top_height = topwin.innerHeight;
+ var top_width = topwin.innerWidth;
+ var xpath_expr;
+
+ var hintCount = 0;
+ this.clearHints();
+
+ function helper (win, offsetX, offsetY) {
+ var doc = win.document;
+
+ var win_height = win.height;
+ var win_width = win.width;
+
+ /* Bounds */
+ var minX = offsetX < 0 ? -offsetX : 0;
+ var minY = offsetY < 0 ? -offsetY : 0;
+ var maxX = offsetX + win_width > top_width ? top_width - offsetX : top_width;
+ var maxY = offsetY + win_height > top_height ? top_height - offsetY : top_height;
+
+ var scrollX = win.scrollX;
+ var scrollY = win.scrollY;
+
+ hintContainer = doc.createElement("div");
+ hintContainer.id = "hint_container";
+
+ xpath_expr = _getXpathXpression(inputText);
+
+ var res = doc.evaluate(xpath_expr, doc,
+ function (p) {
+ return "http://www.w3.org/1999/xhtml";
+ }, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
+
+ /* generate basic hint element which will be cloned and updated later */
+ var hintSpan = doc.createElement("span");
+ hintSpan.setAttribute("class", config.hintClass);
+ hintSpan.style.cssText = config.hintCss;
+
+ /* due to the different XPath result type, we will need two counter variables */
+ var rect, elem, text, node, show_text;
+ for (var i = 0; i < res.snapshotLength; i++)
+ {
+ if (hintCount >= config.maxAllowedHints)
+ break;
+
+ elem = res.snapshotItem(i);
+ rect = elem.getBoundingClientRect();
+ if (!rect || rect.left > maxX || rect.right < minX || rect.top > maxY || rect.bottom < minY)
+ continue;
+
+ var style = topwin.getComputedStyle(elem, "");
+ if (style.display == "none" || style.visibility != "visible")
+ continue;
+
+ var leftpos = Math.max((rect.left + scrollX), scrollX);
+ var toppos = Math.max((rect.top + scrollY), scrollY);
+
+ /* making this block DOM compliant */
+ var hint = hintSpan.cloneNode(false);
+ hint.setAttribute("id", "vimprobablehint" + hintCount);
+ hint.style.left = leftpos + "px";
+ hint.style.top = toppos + "px";
+ text = doc.createTextNode(hintCount + 1);
+ hint.appendChild(text);
+
+ hintContainer.appendChild(hint);
+ hintCount++;
+ hints.push({
+ elem: elem,
+ number: hintCount,
+ span: hint,
+ background: elem.style.background,
+ foreground: elem.style.color}
+ );
+
+ /* make the link black to ensure it's readable */
+ elem.style.color = config.elemColor;
+ elem.style.background = config.elemBackground;
+ }
+
+ doc.documentElement.appendChild(hintContainer);
+
+ /* recurse into any iframe or frame element */
+ var frameTags = ["frame","iframe"];
+ for (var f = 0; f < frameTags.length; ++f) {
+ var frames = doc.getElementsByTagName(frameTags[f]);
+ for (var i = 0, nframes = frames.length; i < nframes; ++i) {
+ elem = frames[i];
+ rect = elem.getBoundingClientRect();
+ if (!elem.contentWindow || !rect || rect.left > maxX || rect.right < minX || rect.top > maxY || rect.bottom < minY)
+ continue;
+ helper(elem.contentWindow, offsetX + rect.left, offsetY + rect.top);
+ }
+ }
+ }
+
+ helper(topwin, 0, 0);
+
+ this.clearFocus();
+ this.focusHint(1);
+ if (hintCount == 1) {
+ /* just one hinted element - might as well follow it */
+ return this.fire(1);
+ }
+ };
+
+ /* set focus on hint with given number */
+ this.focusHint = function(n)
+ {
+ /* reset previous focused hint */
+ var hint = _getHintByNumber(currentFocusNum);
+ if (hint !== null) {
+ hint.elem.className = hint.elem.className.replace(config.hintClassFocus, config.hintClass);
+ hint.elem.style.background = config.elemBackground;
+ }
+
+ currentFocusNum = n;
+
+ /* mark new hint as focused */
+ var hint = _getHintByNumber(currentFocusNum);
+ if (hint !== null) {
+ hint.elem.className = hint.elem.className.replace(config.hintClass, config.hintClassFocus);
+ hint.elem.style.background = config.elemBackgroundFocus;
+ }
+ };
+
+ /* set focus to next avaiable hint */
+ this.focusNextHint = function()
+ {
+ var index = _getHintIdByNumber(currentFocusNum);
+
+ if (typeof(hints[index + 1]) != "undefined") {
+ this.focusHint(hints[index + 1].number);
+ } else {
+ this.focusHint(hints[0].number);
+ }
+ };
+
+ /* set focus to previous avaiable hint */
+ this.focusPreviousHint = function()
+ {
+ var index = _getHintIdByNumber(currentFocusNum);
+ if (index != 0 && typeof(hints[index - 1].number) != "undefined") {
+ this.focusHint(hints[index - 1].number);
+ } else {
+ this.focusHint(hints[hints.length - 1].number);
+ }
+ };
+
+ /* filters hints matching given number */
+ this.updateHints = function(n)
+ {
+ if (n == 0) {
+ return this.createHints();
+ }
+ /* remove none matching hints */
+ var remove = [];
+ for (var i = 0; i < hints.length; ++i) {
+ var hint = hints[i];
+ if (0 != hint.number.toString().indexOf(n.toString())) {
+ remove.push(hint.number);
+ }
+ }
+
+ for (var i = 0; i < remove.length; ++i) {
+ _removeHint(remove[i]);
+ }
+
+ if (hints.length === 1) {
+ return this.fire(hints[0].number);
+ } else {
+ return this.focusHint(n);
+ }
+ };
+
+ this.clearFocus = function()
+ {
+ if (document.activeElement && document.activeElement.blur) {
+ document.activeElement.blur();
+ }
+ };
+
+ /* remove all hints and set previous style to them */
+ this.clearHints = function()
+ {
+ if (hints.length == 0) {
+ return;
+ }
+ for (var i = 0; i < hints.length; ++i) {
+ var hint = hints[i];
+ if (typeof(hint.elem) != "undefined") {
+ hint.elem.style.background = hint.background;
+ hint.elem.style.color = hint.foreground;
+ hint.span.parentNode.removeChild(hint.span);
+ }
+ }
+ hints = [];
+ hintContainer.parentNode.removeChild(hintContainer);
+ window.onkeyup = null;
+ };
+
+ /* fires the modeevent on hint with given number */
+ this.fire = function(n)
+ {
+ var doc, result;
+ if (!n) {
+ var n = currentFocusNum;
+ }
+ var hint = _getHintByNumber(n);
+ if (typeof(hint.elem) == "undefined")
+ return "done;";
+
+ var el = hint.elem;
+ var tag = el.nodeName.toLowerCase();
+
+ this.clearHints();
+
+ if (tag == "iframe" || tag == "frame" || tag == "textarea" || tag == "input" && (el.type == "text" || el.type == "password" || el.type == "checkbox" || el.type == "radio") || tag == "select") {
+ el.focus();
+ if (tag == "input" || tag == "textarea") {
+ return "insert;"
+ }
+ return "done;";
+ }
+
+ switch (mode)
+ {
+ case "f": case "i": result = _open(el); break;
+ case "F": case "I": result = _openNewWindow(el); break;
+ case "s": result = "save;" + _getElemtSource(el); break;
+ case "y": result = "yank;" + _getElemtSource(el); break;
+ case "O": result = "colon;" + _getElemtSource(el); break;
+ default: result = _getElemtSource(el);
+ }
+
+ return result;
+ };
+
+ this.focusInput = function()
+ {
+ if (document.getElementsByTagName("body")[0] === null || typeof(document.getElementsByTagName("body")[0]) != "object")
+ return;
+
+ /* prefixing html: will result in namespace error */
+ var hinttags = "//input[@type='text'] | //input[@type='password'] | //textarea";
+ var r = document.evaluate(hinttags, document,
+ function(p) {
+ return "http://www.w3.org/1999/xhtml";
+ }, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
+ var i;
+ var j = 0;
+ var k = 0;
+ var first = null;
+ for (i = 0; i < r.snapshotLength; i++) {
+ var elem = r.snapshotItem(i);
+ if (k == 0) {
+ if (elem.style.display != "none" && elem.style.visibility != "hidden") {
+ first = elem;
+ } else {
+ k--;
+ }
+ }
+ if (j == 1 && elem.style.display != "none" && elem.style.visibility != "hidden") {
+ elem.focus();
+ var tag = elem.nodeName.toLowerCase();
+ if (tag == "textarea" || tag == "input") {
+ return "insert;";
+ }
+ break;
+ }
+ if (elem == document.activeElement) {
+ j = 1;
+ }
+ k++;
+ }
+ /* no appropriate field found focused - focus the first one */
+ if (j == 0 && first !== null) {
+ first.focus();
+ var tag = elem.nodeName.toLowerCase();
+ if (tag == "textarea" || tag == "input") {
+ return "insert;";
+ }
+ }
+ };
+
+ /* retrieves text content fro given element */
+ function _getTextFromElement(el)
+ {
+ if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement) {
+ text = el.value;
+ } else if (el instanceof HTMLSelectElement) {
+ if (el.selectedIndex >= 0) {
+ text = el.item(el.selectedIndex).text;
+ } else{
+ text = "";
+ }
+ } else {
+ text = el.textContent;
+ }
+ return text.toLowerCase();;
+ }
+
+ /* retrieves the hint for given hint number */
+ function _getHintByNumber(n)
+ {
+ var index = _getHintIdByNumber(n);
+ if (index !== null) {
+ return hints[index];
+ }
+ return null;
+ }
+
+ /* retrieves the id of hint with given number */
+ function _getHintIdByNumber(n)
+ {
+ for (var i = 0; i < hints.length; ++i) {
+ var hint = hints[i];
+ if (hint.number === n) {
+ return i;
+ }
+ }
+ return null;
+ }
+
+ /* removes hint with given number from hints array */
+ function _removeHint(n)
+ {
+ var index = _getHintIdByNumber(n);
+ if (index === null) {
+ return;
+ }
+ var hint = hints[index];
+ if (hint.number === n) {
+ hint.elem.style.background = hint.background;
+ hint.elem.style.color = hint.foreground;
+ hint.span.parentNode.removeChild(hint.span);
+
+ /* remove hints from all hints */
+ hints.splice(index, 1);
+ }
+ }
+
+ /* opens given element */
+ function _open(elem)
+ {
+ if (elem.target == "_blank") {
+ elem.removeAttribute("target");
+ }
+ _clickElement(elem);
+ return "done;";
+ }
+
+ /* opens given element into new window */
+ function _openNewWindow(elem)
+ {
+ var oldTarget = elem.target;
+
+ /* set target to open in new window */
+ elem.target = "_blank";
+ _clickElement(elem);
+ elem.target = oldTarget;
+
+ return "done;";
+ }
+
+ /* fire moudedown and click event on given element */
+ function _clickElement(elem)
+ {
+ doc = elem.ownerDocument;
+ view = elem.contentWindow;
+
+ var evObj = doc.createEvent("MouseEvents");
+ evObj.initMouseEvent("mousedown", true, true, view, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, null);
+ elem.dispatchEvent(evObj);
+
+ var evObj = doc.createEvent("MouseEvents");
+ evObj.initMouseEvent("click", true, true, view, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, null);
+ elem.dispatchEvent(evObj);
+ }
+
+ /* retrieves the url of given element */
+ function _getElemtSource(elem)
+ {
+ var url = elem.href || elem.src;
+ return url;
+ }
+
+ /* retrieves the xpath expression according to mode */
+ function _getXpathXpression(text)
+ {
+ var expr;
+ if (typeof(text) == "undefined") {
+ text = "";
+ }
+ switch (mode) {
+ case "f":
+ case "F":
+ if (text == "") {
+ expr = "//*[@onclick or @onmouseover or @onmousedown or @onmouseup or @oncommand or @class='lk' or @role='link' or @href] | //input[not(@type='hidden')] | //a[href] | //area | //textarea | //button | //select";
+ } else {
+ expr = "//*[(@onclick or @onmouseover or @onmousedown or @onmouseup or @oncommand or @class='lk' or @role='link' or @href) and contains(., '" + text + "')] | //input[not(@type='hidden') and contains(., '" + text + "')] | //a[@href and contains(., '" + text + "')] | //area[contains(., '" + text + "')] | //textarea[contains(., '" + text + "')] | //button[contains(@value, '" + text + "')] | //select[contains(., '" + text + "')]";
+ }
+ break;
+ case "i":
+ case "I":
+ if (text == "") {
+ expr = "//img[@src]";
+ } else {
+ expr = "//img[@src and contains(., '" + text + "')]";
+ }
+ break;
+ default:
+ if (text == "") {
+ expr = "//*[@role='link' or @href] | //a[href] | //area | //img[not(ancestor::a)]";
+ } else {
+ expr = "//*[(@role='link' or @href) and contains(., '" + text + "')] | //a[@href and contains(., '" + text + "')] | //area[contains(., '" + text + "')] | //img[not(ancestor::a) and contains(., '" + text + "')]";
+ }
+ break;
+ }
+ return expr;
+ }
+
+}
+hints = new Hints();
diff --git a/jsmn/Makefile b/jsmn/Makefile
@@ -0,0 +1,26 @@
+# You can put your build options here
+-include config.mk
+
+all: libjsmn.a
+
+libjsmn.a: jsmn.o
+ $(AR) rc $@ $^
+
+%.o: %.c jsmn.h
+ $(CC) -c $(CFLAGS) $< -o $@
+
+test: jsmn_test
+ ./jsmn_test
+
+jsmn_test: jsmn_test.o
+ $(CC) -L. -ljsmn $< -o $@
+
+jsmn_test.o: jsmn_test.c libjsmn.a
+
+clean:
+ rm -f jsmn.o jsmn_test.o
+ rm -f jsmn_test
+ rm -f libjsmn.a
+
+.PHONY: all clean test
+
diff --git a/jsmn/jsmn.c b/jsmn/jsmn.c
@@ -0,0 +1,226 @@
+#include <stdlib.h>
+
+#include "jsmn.h"
+
+/**
+ * Allocates a fresh unused token from the token pull.
+ */
+static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser,
+ jsmntok_t *tokens, size_t num_tokens) {
+ unsigned int i;
+ for (i = parser->toknext; i < num_tokens; i++) {
+ if (tokens[i].start == -1 && tokens[i].end == -1) {
+ parser->toknext = i + 1;
+ return &tokens[i];
+ }
+ }
+ return NULL;
+}
+
+/**
+ * Fills token type and boundaries.
+ */
+static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type,
+ int start, int end) {
+ token->type = type;
+ token->start = start;
+ token->end = end;
+ token->size = 0;
+}
+
+/**
+ * Fills next available token with JSON primitive.
+ */
+static jsmnerr_t jsmn_parse_primitive(jsmn_parser *parser, const char *js,
+ jsmntok_t *tokens, size_t num_tokens) {
+ jsmntok_t *token;
+ int start;
+
+ start = parser->pos;
+
+ for (; js[parser->pos] != '\0'; parser->pos++) {
+ switch (js[parser->pos]) {
+#ifndef JSMN_STRICT
+ /* In strict mode primitive must be followed by "," or "}" or "]" */
+ case '\t' : case '\r' : case '\n' : case ' ' : case ':':
+#endif
+ case ',' : case ']' : case '}' :
+ goto found;
+ }
+ if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
+ parser->pos = start;
+ return JSMN_ERROR_INVAL;
+ }
+ }
+#ifdef JSMN_STRICT
+ /* In strict mode primitive must be followed by a comma/object/array */
+ parser->pos = start;
+ return JSMN_ERROR_PART;
+#endif
+
+found:
+ token = jsmn_alloc_token(parser, tokens, num_tokens);
+ if (token == NULL) {
+ parser->pos = start;
+ return JSMN_ERROR_NOMEM;
+ }
+ jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
+ parser->pos--;
+ return JSMN_SUCCESS;
+}
+
+/**
+ * Filsl next token with JSON string.
+ */
+static jsmnerr_t jsmn_parse_string(jsmn_parser *parser, const char *js,
+ jsmntok_t *tokens, size_t num_tokens) {
+ jsmntok_t *token;
+
+ int start = parser->pos;
+
+ parser->pos++;
+
+ /* Skip starting quote */
+ for (; js[parser->pos] != '\0'; parser->pos++) {
+ char c = js[parser->pos];
+
+ /* Quote: end of string */
+ if (c == '\"') {
+ token = jsmn_alloc_token(parser, tokens, num_tokens);
+ if (token == NULL) {
+ parser->pos = start;
+ return JSMN_ERROR_NOMEM;
+ }
+ jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos);
+ return JSMN_SUCCESS;
+ }
+
+ /* Backslash: Quoted symbol expected */
+ if (c == '\\') {
+ parser->pos++;
+ switch (js[parser->pos]) {
+ /* Allowed escaped symbols */
+ case '\"': case '/' : case '\\' : case 'b' :
+ case 'f' : case 'r' : case 'n' : case 't' :
+ break;
+ /* Allows escaped symbol \uXXXX */
+ case 'u':
+ /* TODO */
+ break;
+ /* Unexpected symbol */
+ default:
+ parser->pos = start;
+ return JSMN_ERROR_INVAL;
+ }
+ }
+ }
+ parser->pos = start;
+ return JSMN_ERROR_PART;
+}
+
+/**
+ * Parse JSON string and fill tokens.
+ */
+jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, jsmntok_t *tokens,
+ unsigned int num_tokens) {
+ jsmnerr_t r;
+ int i;
+ unsigned int tokindex;
+ jsmntok_t *token;
+
+ /* initialize the rest of tokens (they could be reallocated) */
+ for (tokindex = parser->toknext; tokindex < num_tokens; tokindex++) {
+ jsmn_fill_token(&tokens[tokindex], JSMN_PRIMITIVE, -1, -1);
+ }
+
+ for (; js[parser->pos] != '\0'; parser->pos++) {
+ char c;
+ jsmntype_t type;
+
+ c = js[parser->pos];
+ switch (c) {
+ case '{': case '[':
+ token = jsmn_alloc_token(parser, tokens, num_tokens);
+ if (token == NULL)
+ return JSMN_ERROR_NOMEM;
+ if (parser->toksuper != -1)
+ tokens[parser->toksuper].size++;
+ token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
+ token->start = parser->pos;
+ parser->toksuper = parser->toknext - 1;
+ break;
+ case '}': case ']':
+ type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
+ for (i = parser->toknext - 1; i >= 0; i--) {
+ token = &tokens[i];
+ if (token->start != -1 && token->end == -1) {
+ if (token->type != type) {
+ return JSMN_ERROR_INVAL;
+ }
+ parser->toksuper = -1;
+ token->end = parser->pos + 1;
+ break;
+ }
+ }
+ /* Error if unmatched closing bracket */
+ if (i == -1) return JSMN_ERROR_INVAL;
+ for (; i >= 0; i--) {
+ token = &tokens[i];
+ if (token->start != -1 && token->end == -1) {
+ parser->toksuper = i;
+ break;
+ }
+ }
+ break;
+ case '\"':
+ r = jsmn_parse_string(parser, js, tokens, num_tokens);
+ if (r < 0) return r;
+ if (parser->toksuper != -1)
+ tokens[parser->toksuper].size++;
+ break;
+ case '\t' : case '\r' : case '\n' : case ':' : case ',': case ' ':
+ break;
+#ifdef JSMN_STRICT
+ /* In strict mode primitives are: numbers and booleans */
+ case '-': case '0': case '1' : case '2': case '3' : case '4':
+ case '5': case '6': case '7' : case '8': case '9':
+ case 't': case 'f': case 'n' :
+#else
+ /* In non-strict mode every unquoted value is a primitive */
+ default:
+#endif
+ r = jsmn_parse_primitive(parser, js, tokens, num_tokens);
+ if (r < 0) return r;
+ if (parser->toksuper != -1)
+ tokens[parser->toksuper].size++;
+ break;
+
+#ifdef JSMN_STRICT
+ /* Unexpected char in strict mode */
+ default:
+ return JSMN_ERROR_INVAL;
+#endif
+
+ }
+ }
+
+ for (i = parser->toknext - 1; i >= 0; i--) {
+ /* Unmatched opened object or array */
+ if (tokens[i].start != -1 && tokens[i].end == -1) {
+ return JSMN_ERROR_PART;
+ }
+ }
+
+ return JSMN_SUCCESS;
+}
+
+/**
+ * Creates a new parser based over a given buffer with an array of tokens
+ * available.
+ */
+void jsmn_init(jsmn_parser *parser) {
+ parser->pos = 0;
+ parser->toknext = 0;
+ parser->toksuper = -1;
+}
+
diff --git a/jsmn/jsmn.h b/jsmn/jsmn.h
@@ -0,0 +1,64 @@
+#ifndef __JSMN_H_
+#define __JSMN_H_
+
+/**
+ * JSON type identifier. Basic types are:
+ * o Object
+ * o Array
+ * o String
+ * o Other primitive: number, boolean (true/false) or null
+ */
+typedef enum {
+ JSMN_PRIMITIVE = 0,
+ JSMN_OBJECT = 1,
+ JSMN_ARRAY = 2,
+ JSMN_STRING = 3
+} jsmntype_t;
+
+typedef enum {
+ /* Not enough tokens were provided */
+ JSMN_ERROR_NOMEM = -1,
+ /* Invalid character inside JSON string */
+ JSMN_ERROR_INVAL = -2,
+ /* The string is not a full JSON packet, more bytes expected */
+ JSMN_ERROR_PART = -3,
+ /* Everything was fine */
+ JSMN_SUCCESS = 0
+} jsmnerr_t;
+
+/**
+ * JSON token description.
+ * @param type type (object, array, string etc.)
+ * @param start start position in JSON data string
+ * @param end end position in JSON data string
+ */
+typedef struct {
+ jsmntype_t type;
+ int start;
+ int end;
+ int size;
+} jsmntok_t;
+
+/**
+ * JSON parser. Contains an array of token blocks available. Also stores
+ * the string being parsed now and current position in that string
+ */
+typedef struct {
+ unsigned int pos; /* offset in the JSON string */
+ int toknext; /* next token to allocate */
+ int toksuper; /* suporior token node, e.g parent object or array */
+} jsmn_parser;
+
+/**
+ * Create JSON parser over an array of tokens
+ */
+void jsmn_init(jsmn_parser *parser);
+
+/**
+ * Run JSON parser. It parses a JSON data string into and array of tokens, each describing
+ * a single JSON object.
+ */
+jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js,
+ jsmntok_t *tokens, unsigned int num_tokens);
+
+#endif /* __JSMN_H_ */
diff --git a/keymap.h b/keymap.h
@@ -0,0 +1,137 @@
+/*
+ (c) 2009 by Leon Winter
+ (c) 2009-2011 by Hannes Schueller
+ (c) 2009-2010 by Matto Fransen
+ (c) 2010-2011 by Hans-Peter Deifel
+ (c) 2010-2011 by Thomas Adam
+ (c) 2011 by Albert Kim
+ see LICENSE file
+*/
+
+#ifndef _KEYMAP
+#define _KEYMAP
+
+/* key bindings for normal mode
+ Note: GDK_VoidSymbol is a wildcard so it matches on every modkey
+*/
+static Key keys[] = {
+ /* modmask, modkey, key, function, argument */
+ { 0, 'q', "1", quickmark, { .s = "1" } },
+ { 0, 'q', "2", quickmark, { .s = "2" } },
+ { 0, 'q', "3", quickmark, { .s = "3" } },
+ { 0, 'q', "4", quickmark, { .s = "4" } },
+ { 0, 'q', "5", quickmark, { .s = "5" } },
+ { 0, 'q', "6", quickmark, { .s = "6" } },
+ { 0, 'q', "7", quickmark, { .s = "7" } },
+ { 0, 'q', "8", quickmark, { .s = "8" } },
+ { 0, 'q', "9", quickmark, { .s = "9" } },
+ { 0, 0, "0", scroll, {ScrollJumpTo | DirectionLeft} },
+ { 0, 0, "$", scroll, {ScrollJumpTo | DirectionRight} },
+ { 0, 'g', "g", scroll, {ScrollJumpTo | DirectionTop} },
+ { 0, 0, "G", scroll, {ScrollJumpTo | DirectionBottom} },
+ { 0, 0, "h", scroll, {ScrollMove | DirectionLeft | UnitLine} },
+ { 0, 0, "j", scroll, {ScrollMove | DirectionBottom | UnitLine} },
+ { 0, 0, "k", scroll, {ScrollMove | DirectionTop | UnitLine} },
+ { 0, 0, "l", scroll, {ScrollMove | DirectionRight | UnitLine} },
+ { 0, 0, " ", scroll, {ScrollMove | DirectionBottom | UnitPage} },
+ { "Shift", 0, " ", scroll, {ScrollMove | DirectionTop | UnitPage} },
+ { "Control", 0, "b", scroll, {ScrollMove | DirectionTop | UnitPage} },
+ { "Control", 0, "f", scroll, {ScrollMove | DirectionBottom | UnitPage} },
+ { "Control", 0, "d", scroll, {ScrollMove | DirectionBottom | UnitBuffer} },
+ { "Control", 0, "u", scroll, {ScrollMove | DirectionTop | UnitBuffer} },
+ { "Control", 0, "e", scroll, {ScrollMove | DirectionBottom | UnitLine} },
+ { "Control", 0, "y", scroll, {ScrollMove | DirectionTop | UnitLine} },
+/*
+ { 0, "g", "t", fake_key_event, { .s = "l", .i = ShiftMask + ControlMask } },
+ { 0, "g", "T", fake_key_event, { .s = "h", .i = ShiftMask + ControlMask } },
+ { 0, "g", "1", fake_key_event, { .s = "1", .i = ControlMask } },
+ { 0, "g", "2", fake_key_event, { .s = "2", .i = ControlMask } },
+ { 0, "g", "3", fake_key_event, { .s = "3", .i = ControlMask } },
+ { 0, "g", "4", fake_key_event, { .s = "4", .i = ControlMask } },
+ { 0, "g", "5", fake_key_event, { .s = "5", .i = ControlMask } },
+ { 0, "g", "6", fake_key_event, { .s = "6", .i = ControlMask } },
+ { 0, "g", "7", fake_key_event, { .s = "7", .i = ControlMask } },
+ { 0, "g", "8", fake_key_event, { .s = "8", .i = ControlMask } },
+ { 0, "g", "9", fake_key_event, { .s = "9", .i = ControlMask } },
+ { 0, "g", "0", fake_key_event, { .s = "0", .i = ControlMask } },
+*/
+ { "Control", 0, "i", navigate, {NavigationBack} },
+ { "Control", 0, "o", navigate, {NavigationForward} },
+ { 0, 0, "H", navigate, {NavigationBack} },
+ { 0, 0, "L", navigate, {NavigationForward} },
+ { 0, 0, "r", navigate, {NavigationReload} },
+ { 0, 0, "R", navigate, {NavigationForceReload} },
+ { "Control", 0, "c", navigate, {NavigationCancel} },
+
+ { 0, 0, "plus", zoom, {ZoomIn | ZoomText} },
+ { 0, 0, "minus", zoom, {ZoomOut | ZoomText} },
+// { 0, 0, GDK_KP_Add, zoom, {ZoomIn | ZoomText} },
+// { 0, 0, GDK_KP_Subtract, zoom, {ZoomOut | ZoomText} },
+ { 0, 'z', "i", zoom, {ZoomIn | ZoomText} },
+ { 0, 'z', "o", zoom, {ZoomOut | ZoomText} },
+ { 0, 'z', "z", zoom, {ZoomReset | ZoomText} },
+ { 0, 'z', "I", zoom, {ZoomIn | ZoomFullContent} },
+ { 0, 'z', "O", zoom, {ZoomOut | ZoomFullContent} },
+ { 0, 'z', "Z", zoom, {ZoomReset | ZoomFullContent} },
+
+ { 0, 0, "y", yank, {SourceURL | ClipboardPrimary | ClipboardGTK} },
+ { 0, 0, "Y", yank, {SourceSelection| ClipboardPrimary | ClipboardGTK} },
+
+ { 0, 'g', "u", descend, {NthSubdir} },
+ { 0, 'g', "U", descend, {Rootdir} },
+
+ { 0, 'g', "h", open_arg, {TargetCurrent, startpage} },
+ { 0, 'g', "H", open_arg, {TargetNew, startpage} },
+
+ { 0, 0, "p", paste, {TargetCurrent | ClipboardPrimary | ClipboardGTK} },
+ { 0, 0, "P", paste, {TargetNew | ClipboardPrimary | ClipboardGTK} },
+
+ { "Control", 0, "a", number, {Increment} },
+ { "Control", 0, "x", number, {Decrement} },
+
+ { 0, 0, "n", search, {DirectionNext | CaseInsensitive | Wrapping} },
+ { 0, 0, "N", search, {DirectionPrev | CaseInsensitive | Wrapping} },
+
+ { 0, 0, "colon", input, {.s = ":" } },
+ { 0, 0, "o", input, {.s = ":open "} },
+ { 0, 0, "O", input, {.s = ":open ", .i = InsertCurrentURL} },
+ { 0, 0, "t", add_tab, {.s = ":open "} },
+ { 0, 0, "T", input, {.s = ":tabopen ", .i = InsertCurrentURL} },
+ { 0, 0, "slash", input, {.s = "/"} },
+// { 0, 0, GDK_KP_Divide, input, {.s = "/"} },
+ { 0, 0, "question", input, {.s = "?"} },
+
+ { 0, 0, "period", input, {.s = "."} },
+ { 0, 0, "comma", input, {.s = ","} },
+ { 0, ';', "i", input, {.s = ";i"} },
+ { 0, ';', "s", input, {.s = ";s"} },
+ { 0, ';', "y", input, {.s = ";y"} },
+ { 0, ';', "o", input, {.s = ";o"} },
+ { 0, ';', "t", input, {.s = ";t"} },
+ { 0, ';', "w", input, {.s = ";w"} },
+ { 0, ';', "I", input, {.s = ";I"} },
+ { 0, ';', "O", input, {.s = ";O"} },
+ { 0, ';', "T", input, {.s = ";T"} },
+ { 0, ';', "W", input, {.s = ";W"} },
+
+ { 0, '*', "Escape", set, {ModeNormal} },
+ { "Control", '*', "bracketleft", set, {ModeNormal} },
+ { "Control", 0, "z", set, {ModePassThrough} },
+ { "Control", 0, "v", set, {ModeSendKey} },
+ { 0, 0, "f", input, {.s = "."} },
+ { 0, 0, "F", input, {.s = ","} },
+
+ { 0, 'g', "i", focus_input,{} },
+ { 0, 0, "u", revive, {} },
+
+ { 0, 0, "x", tab_quit, {0} },
+ // { 0, 0, "X", revive_last_tab, {0} },
+ { 0, 0, "b", switch_buffer, {0}},
+ { 0, 0, "i", inspector, { .i = ShowInspector}},
+ { 0, 0, "I", inspector, { .i = HideInspector}},
+ /* leave this last line as last */
+ { 0, 0, 0, 0, {0} },
+};
+
+
+#endif
diff --git a/main.c b/main.c
@@ -0,0 +1,1533 @@
+/*
+ (c) 2009 by Leon Winter
+ (c) 2009-2012 by Hannes Schueller
+ (c) 2009-2010 by Matto Fransen
+ (c) 2010-2011 by Hans-Peter Deifel
+ (c) 2010-2011 by Thomas Adam
+ (c) 2011 by Albert Kim
+ (c) 2011 by Daniel Carl
+ (c) 2012 by Matthew Carter
+ see LICENSE file
+*/
+
+#define _GNU_SOURCE
+#include <Elementary.h>
+#include <EWebKit.h>
+
+#include "viking.h"
+#include "main.h"
+#include "commands.h"
+#include "utilities.h"
+#include "javascript.h"
+#include "config.h"
+#include "keymap.h"
+
+#include <stdio.h> /* asprintf() */
+#include <string.h> /* strcmp */
+#include <stdlib.h> /* free() */
+
+
+EAPI_MAIN int elm_main(int argc, char *argv[]);
+
+#define IS_ESCAPE(ev) ((!strcmp(ev->keyname, "Escape")) || \
+ (!strcmp(ev->keyname, "[") && evas_key_modifier_is_set(ev->modifiers, "Control")))
+
+
+/* callbacks here */
+static void inputbox_activate_cb (void *, Evas_Object *, void *);
+static void inputbox_changed_cb (void *, Evas_Object *, void *);
+static Eina_Bool commandhistoryfetch (const Arg *, void *);
+static void inputbox_keypress_cb (void *, Evas *, Evas_Object *, void *);
+static void inputbox_keyrelease_cb (void *, Evas *, Evas_Object *, void *);
+
+static void webview_console_cb(void *, Evas_Object *, const char *, unsigned int, const char *);
+static void webview_hoverlink_cb (void *, Evas_Object *, void*);
+static void webview_hoverlink_out_cb (void *, Evas_Object *, void*);
+static void webview_load_committed_cb (void *, Evas_Object *, void*);
+static void webview_load_finished_cb (void *, Evas_Object *, void*);
+static void history(void *);
+static void webview_progress_changed_cb(void *, Evas_Object *, void *);
+static void webview_title_changed_cb (void *, Evas_Object *, void *);
+static void webview_download_cb (void *, Evas_Object *, void *);
+static void inputmethod_changed_cb (void *, Evas_Object *, void *);
+static void icon_received_cb (void *, Evas_Object *, void *);
+static void webview_inspector_cb (void *, Evas_Object *, void *);
+static Eina_Bool process_keypress (void *, void*);
+static void webview_keypress_cb (void *, Evas *, Evas_Object *, void *);
+static void webview_mousewheel_cb (void *, Evas *, Evas_Object *, void *);
+
+/*
+static WebKitWebView* inspector_inspect_web_view_cb(gpointer inspector, WebKitWebView* web_view);
+static Eina_Bool webview_mimetype_cb(WebKitWebView *webview, WebKitWebFrame *frame, WebKitNetworkRequest *request, char *mime_type, WebKitWebPolicyDecision *decision, gpointer user_data);
+static void webview_open_js_window_cb(WebKitWebView* temp_view, GParamSpec param_spec);
+static Eina_Bool webview_new_window_cb(WebKitWebView *webview, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *action, WebKitWebPolicyDecision *decision, gpointer user_data);
+static WebKitWebView* webview_open_in_new_window_cb(WebKitWebView *webview, WebKitWebFrame *frame, gpointer user_data);
+*/
+
+
+/* functions */
+static void ascii_bar(int total, int state, char *string);
+static void update_url(const char *, void *);
+// static char *jsapi_ref_to_string(JSContextRef context, JSValueRef ref);
+// static void jsapi_evaluate_script(const gchar *script, gchar **value, gchar **message, void *data);
+static void set_widget_font_and_color(Evas_Object *widget, const char *font_str, const char *bg_color_str, const char *fg_color_str);
+
+
+
+static void
+ascii_bar(int total, int state, char *string)
+{
+ int i;
+
+ for (i = 0; i < state; i++)
+ string[i] = progressbartickchar;
+ string[i++] = progressbarcurrent;
+ for (; i < total; i++)
+ string[i] = progressbarspacer;
+ string[i] = '\0';
+}
+
+void
+update_state(void *data)
+{
+ // char *markup;
+ App_Data *ad = data;
+ Evas_Object *view = elm_web_webkit_view_get(ad->current_web);
+ Evas_Object *frame = ewk_view_frame_main_get(view);
+ // int download_count = g_list_length(activeDownloads);
+ int download_count = eina_list_count(ad->active_downloads);
+ // GString *status = g_string_new("");
+ char status[512] = "";
+
+ /* construct the status line */
+
+ strcat(status, "<font=Monospace font_size=12 color=#FFF backing=on backing_color=#000>");
+
+ /* count, modkey and input buffer */
+ // g_string_append_printf(status, "%.0d", count);
+ char *count_formatted = strdup_printf("%.0d", ad->count);
+ strcat(status, count_formatted);
+ free(count_formatted);
+
+ // if (ad->current_modkey) g_string_append_c(status, ad->current_modkey);
+ if (ad->current_modkey) strcat(status, &ad->current_modkey);
+
+ if (ad->active_downloads) {
+ // g_string_append_printf(status, " %d active %s", download_count,
+ // (download_count == 1) ? "download" : "downloads");
+ char *download_status = strdup_printf(" %d active %s", download_count,
+ (download_count == 1) ? "download" : "downloads");
+ strcat(status, download_status);
+ free(download_status);
+ }
+
+ /* the progressbar */
+ int progress = -1;
+ char progressbar[progressbartick + 1];
+
+ /*
+ if (activeDownloads) {
+ progress = 0;
+ Eina_List *ptr;
+
+ for (ptr = activeDownloads; ptr; ptr = eina_list_next(ptr)) {
+ progress += 100 * webkit_download_get_progress(ptr->data);
+ }
+
+ progress /= download_count;
+
+ } else if (webkit_web_view_get_load_status(webview) != WEBKIT_LOAD_FINISHED
+ && webkit_web_view_get_load_status(webview) != WEBKIT_LOAD_FAILED) {
+
+ // progress = webkit_web_view_get_progress(webview) * 100;
+ }
+ */
+
+ if (ewk_view_load_progress_get(view) != 0.0f)
+ progress = ewk_view_load_progress_get(view) * 100;
+
+ if (progress >= 0) {
+ ascii_bar(progressbartick, progress * progressbartick / 100, progressbar);
+ // g_string_append_printf(status, " %c%s%c",
+ // progressborderleft, progressbar, progressborderright);
+ char *load_progress = strdup_printf(" %c%s%c",
+ progressborderleft, progressbar, progressborderright);
+ strcat(status, load_progress);
+ free(load_progress);
+ }
+
+ /* put a notification if we are zoomed */
+ double zoom = elm_web_zoom_get(ad->current_web);
+ if (zoom != 1) {
+ char *zoom_str = strdup_printf(" %0.2lfx", zoom);
+ strcat(status, zoom_str);
+ free(zoom_str);
+ }
+
+ /* always show the current scroll position */
+ int x, y, w, h, val;
+ ewk_frame_scroll_pos_get(frame, &x, &y);
+ ewk_frame_scroll_size_get(frame, &w, &h);
+
+ val = (int) ((float) y / h * 100);
+
+ if (h == 0)
+ strcat(status, " All");
+ else if (val == 0)
+ strcat(status, " Top");
+ else if (val == 100)
+ strcat(status, " Bot");
+ else {
+ char *percent_scrolled = strdup_printf(" %d%%", val);
+ strcat(status, percent_scrolled);
+ free(percent_scrolled);
+ }
+
+ elm_object_text_set(ad->status_state, status);
+
+ int state_width, window_width;
+ evas_object_geometry_get(ad->status_state, NULL, NULL, &state_width, NULL);
+ evas_object_geometry_get(ad->win, NULL, NULL, &window_width, NULL);
+
+ elm_label_wrap_width_set(ad->status_url, window_width - state_width);
+
+ // update_url(elm_web_uri_get(ad->current_web), data);
+ // elm_box_recalculate(ad->status_bar);
+ // recalculate_status_bar(data);
+}
+
+static void
+update_url(const char *uri, void *data)
+{
+ char *markup;
+ char before[] = " [";
+ char after[] = "]";
+
+ if (uri == NULL)
+ return;
+
+ // Evas_Object *view, *frame;
+ App_Data *ad = data;
+ Eina_Bool back = elm_web_back_possible_get(ad->current_web);
+ Eina_Bool fwd = elm_web_forward_possible_get(ad->current_web);
+
+ Evas_Object *view = elm_web_webkit_view_get(ad->current_web);
+ Evas_Object *frame = ewk_view_frame_main_get(view);
+ char *url_color = "#FFF";
+
+ if (!strncmp(uri, "https://", 8)) {
+ Ewk_Certificate_Status cert_status = ewk_frame_certificate_status_get(frame);
+ if (cert_status & EWK_CERTIFICATE_STATUS_TRUSTED)
+ url_color = "#0F0";
+ else if (cert_status & EWK_CERTIFICATE_STATUS_UNTRUSTED)
+ url_color = "#F00";
+ }
+
+ if (!back && !fwd)
+ before[0] = after[0] = '\0';
+
+ markup = strdup_printf("<font=Monospace font_size=12 color=%s backing=on backing_color=#000>%s%s%s%s%s",
+ url_color, uri, before, back ? "+" : "", fwd ? "-" : "", after);
+
+
+ int state_width, window_width;
+ evas_object_geometry_get(ad->status_state, NULL, NULL, &state_width, NULL);
+ evas_object_geometry_get(ad->win, NULL, NULL, &window_width, NULL);
+ elm_label_wrap_width_set(ad->status_url, window_width - state_width);
+
+ elm_object_text_set(ad->status_url, markup);
+ // elm_label_line_wrap_set(ad->status_url, ELM_WRAP_CHAR);
+ // elm_label_ellipsis_set(ad->status_url, EINA_TRUE);
+
+ free(markup);
+}
+
+Eina_Bool
+echo(const Arg *arg, void *data)
+{
+ App_Data *ad = data;
+ int index = !arg->s ? 0 : arg->i & (~NoAutoHide);
+
+ if (index < Info || index > Error)
+ return TRUE;
+
+// printf("echo(%s)\n", arg->s);
+ // if (!gtk_widget_is_focus(GTK_WIDGET(inputbox))) {
+// if (!elm_object_focus_get(ad->url)) {
+ set_widget_font_and_color(ad->url, urlboxfont[index], urlboxbgcolor[index], urlboxcolor[index]);
+ // gtk_entry_set_text(GTK_ENTRY(inputbox), !arg->s ? "" : arg->s);
+ elm_entry_entry_set(ad->url, !arg->s ? "" : arg->s);
+// }
+
+ return TRUE;
+}
+
+void
+webview_title_changed_cb(void *data, Evas_Object *obj, void *event_info)
+{
+ /*
+ const char *title = "";
+ Ewk_Text_With_Direction *dir_text = event_info;
+ if (event_info) title = dir_text->string;
+ char buf[20] = "";
+
+ if (title)
+ strncpy(buf, title, sizeof(buf) - 1);
+ elm_object_item_text_set(td->tab, buf);
+ */
+}
+
+void
+webview_progress_changed_cb(void *data, Evas_Object *obj, void *event_info)
+{
+ Tab_Data *td = data;
+ /* *event_info is a double between 0.0 and 1.0 */
+ update_state(td->app);
+}
+
+void
+webview_load_committed_cb(void *data, Evas_Object *obj, void *event_info)
+{
+ Arg a = { .i = Silent, .s = strdup(JS_SETUP_HINTS) };
+ Tab_Data *td = data;
+
+ // Evas_Object *view = elm_web_webkit_view_get(td->web);
+ // Evas_Object *frame = ewk_view_frame_main_get(view);
+
+ // userscript_hooks_start(elm_web_uri_get(td->web));
+
+ script(&a, td->app);
+ free(a.s);
+
+ /*
+ FILE *fp = fopen("/home/kyle/.config/evi/userscript/adblockplus/contentScript1.js", "r");
+ if (!fp)
+ printf("error opening file.\n");
+ fseek(fp, 0L, SEEK_END);
+ int len = ftell(fp);
+ char *buf = malloc(len);
+ rewind(fp);
+ fread(buf, len, 1, fp);
+ fclose(fp);
+
+ ewk_frame_script_execute(frame, buf);
+
+ free(buf);
+ */
+
+ if (td->app->mode == ModeInsert || td->app->mode == ModeHints) {
+ Arg a = { .i = ModeNormal };
+ set(&a, td->app);
+ }
+ td->app->manual_focus = FALSE;
+}
+
+void
+history(void *data)
+{
+ FILE *f;
+ char *filename;
+ // const char *uri = webkit_web_view_get_uri(webview);
+ // const char *title = webkit_web_view_get_title(webview);
+ App_Data *ad = data;
+ const char *uri = elm_web_uri_get(ad->current_web);
+ const char *title = elm_web_title_get(ad->current_web);
+ char *entry, buffer[512], *new;
+ int n, i = 0;
+ Eina_Bool finished = EINA_FALSE;
+ if (uri != NULL) {
+ if (title != NULL) {
+ entry = malloc((strlen(uri) + strlen(title) + 2) * sizeof(char));
+ memset(entry, 0, strlen(uri) + strlen(title) + 2);
+ } else {
+ entry = malloc((strlen(uri) + 1) * sizeof(char));
+ memset(entry, 0, strlen(uri) + 1);
+ }
+ if (entry != NULL) {
+ strncpy(entry, uri, strlen(uri));
+ if (title != NULL) {
+ strncat(entry, " ", 1);
+ strncat(entry, title, strlen(title));
+ }
+ n = strlen(entry);
+ filename = strdup_printf(HISTORY_STORAGE_FILENAME);
+ f = fopen(filename, "r");
+ if (f != NULL) {
+ new = (char *)malloc(HISTORY_MAX_ENTRIES * 512 * sizeof(char) + 1);
+ if (new != NULL) {
+ memset(new, 0, HISTORY_MAX_ENTRIES * 512 * sizeof(char) + 1);
+ /* newest entries go on top */
+ strncpy(new, entry, strlen(entry));
+ strncat(new, "\n", 1);
+ /* retain at most HISTORY_MAX_ENTIRES - 1 old entries */
+ while (finished != TRUE) {
+ if ((char *)NULL == fgets(buffer, 512, f)) {
+ /* check if end of file was reached / error occured */
+ if (!feof(f)) {
+ break;
+ }
+ /* end of file reached */
+ finished = TRUE;
+ continue;
+ }
+ /* compare line (-1 because of newline character) */
+ if (n != strlen(buffer) - 1 || strncmp(entry, buffer, n) != 0) {
+ /* if the URI is already in history; we put it on top and skip it here */
+ strncat(new, buffer, 512);
+ i++;
+ }
+ if ((i + 1) >= HISTORY_MAX_ENTRIES) {
+ break;
+ }
+ }
+ fclose(f);
+ }
+ f = fopen(filename, "w");
+ free(filename);
+ if (f != NULL) {
+ fprintf(f, "%s", new);
+ fclose(f);
+ }
+ if (new != NULL) {
+ free(new);
+ new = NULL;
+ }
+ }
+ }
+ if (entry != NULL) {
+ free(entry);
+ entry = NULL;
+ }
+ }
+}
+
+void
+webview_load_finished_cb(void *data, Evas_Object *obj, void *event_info)
+{
+ /* WebKitWebSettings *settings = webkit_web_view_get_settings(webview); */
+ Tab_Data *td = data;
+ App_Data *ad = td->app;
+ Eina_Bool scripts = 1;
+ Elm_Web_Frame_Load_Error *frame_error = event_info;
+
+// Evas_Object *view = elm_web_webkit_view_get(td->web);
+// Evas_Object *frame = ewk_view_frame_main_get(view);
+
+ if (frame_error)
+ fprintf(stderr, "Error http code %i on domain %s,\n%s\n", frame_error->code, frame_error->failing_url, frame_error->description);
+
+ /* g_object_get(settings, "enable-scripts", &scripts, NULL); */
+ if (escape_input_on_load && scripts && !ad->manual_focus && !elm_object_focus_get(ad->url)) {
+ Arg a = { .i = Silent, .s = strdup("hints.clearFocus();") };
+ script(&a, ad);
+ free(a.s);
+ a.i = ModeNormal;
+ a.s = NULL;
+ set(&a, ad);
+ }
+ if (HISTORY_MAX_ENTRIES > 0)
+ history(ad);
+ update_state(ad);
+
+ // userscript_hooks_end(elm_web_uri_get(td->web));
+
+ /*
+ FILE *fp = fopen("/home/kyle/.config/evi/userscript/adblockplus/contentScript2.js", "r");
+ if (!fp)
+ printf("error opening file.\n");
+ fseek(fp, 0L, SEEK_END);
+ int len = ftell(fp);
+ char *buf = malloc(len);
+ rewind(fp);
+ fread(buf, len, 1, fp);
+ fclose(fp);
+
+ ewk_frame_script_execute(frame, buf);
+
+ free(buf);
+ */
+
+
+ // elm_object_focus_set(td->web, EINA_TRUE);
+}
+
+#if 0
+void
+webview_open_js_window_cb(WebKitWebView* temp_view, GParamSpec param_spec) {
+ /* retrieve the URI of the temporary webview */
+ Arg a = { .i = TargetNew, .s = (char*)webkit_web_view_get_uri(temp_view) };
+ /* clean up */
+ webkit_web_view_stop_loading(temp_view);
+ gtk_widget_destroy(GTK_WIDGET(temp_view));
+ /* open the requested window */
+ open_arg(&a);
+}
+
+static WebKitWebView *
+webview_open_in_new_window_cb(WebKitWebView *webview, WebKitWebFrame *frame, gpointer user_data) {
+ /* create a temporary webview to execute the script in */
+ WebKitWebView *temp_view = WEBKIT_WEB_VIEW(webkit_web_view_new());
+ /* wait until the new webview receives its new URI */
+ g_object_connect(temp_view, "signal::notify::uri", G_CALLBACK(webview_open_js_window_cb), NULL, NULL);
+ return temp_view;
+}
+
+Eina_Bool
+webview_new_window_cb(WebKitWebView *webview, WebKitWebFrame *frame, WebKitNetworkRequest *request,
+ WebKitWebNavigationAction *action, WebKitWebPolicyDecision *decision, gpointer user_data) {
+ Arg a = { .i = TargetNew, .s = (char*)webkit_network_request_get_uri(request) };
+ open_arg(&a);
+ webkit_web_policy_decision_ignore(decision);
+ return TRUE;
+}
+#endif
+
+/*
+Eina_Bool
+webview_mimetype_cb(WebKitWebView *webview, WebKitWebFrame *frame, WebKitNetworkRequest *request,
+ char *mime_type, WebKitWebPolicyDecision *decision, gpointer user_data) {
+ if (webkit_web_view_can_show_mime_type(webview, mime_type) == FALSE) {
+ webkit_web_policy_decision_download(decision);
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+static WebKitWebView*
+inspector_inspect_web_view_cb(gpointer inspector, WebKitWebView* web_view) {
+ gchar* inspector_title;
+ GtkWidget* inspector_window;
+ GtkWidget* inspector_view;
+
+ // just enough code to show the inspector - no signal handling etc.
+ inspector_title = strdup_printf("Inspect page - %s - Vimprobable2", webkit_web_view_get_uri(web_view));
+ if (embed) {
+ inspector_window = gtk_plug_new(embed);
+ } else {
+ inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_wmclass(window, "vimprobable2", "Vimprobable2");
+ }
+ gtk_window_set_title(GTK_WINDOW(inspector_window), inspector_title);
+ free(inspector_title);
+ inspector_view = webkit_web_view_new();
+ gtk_container_add(GTK_CONTAINER(inspector_window), inspector_view);
+ gtk_widget_show_all(inspector_window);
+ return WEBKIT_WEB_VIEW(inspector_view);
+}
+*/
+
+
+static void
+webview_inspector_cb(void *data, Evas_Object *obj, void *event_info)
+{
+ // Tab_Data *td = data;
+
+ printf("would have show inspector!\n");
+
+}
+
+void
+download_complete_cb(void *data, const char *file, int status)
+{
+ App_Data *ad = data;
+ Arg a;
+ a.i = Info;
+ a.s = strdup_printf("Download %s finished, status %i", file, status);
+ echo(&a, data);
+ free(a.s);
+
+ ad->active_downloads = eina_list_remove(ad->active_downloads, file);
+}
+
+int
+download_progress_cb(void *data, const char *file, long int dltotal, long int dlnow, long int ultotal, long int ulnow)
+{
+ printf("download_progress() file %s done %f\n", file, (float)dlnow/dltotal);
+ update_state(data);
+
+ return 0;
+}
+
+static void
+webview_download_cb(void *data, Evas_Object *obj, void *event_info)
+{
+ Tab_Data *td = data;
+ App_Data *ad = td->app;
+
+ Ewk_Download *dl = event_info;
+ char *suggested_name = strdup(strrchr(dl->url, '/'));
+
+ char *full_path = strdup_printf("/home/kyle/%s", suggested_name);
+
+ ecore_file_download(dl->url, full_path, download_complete_cb,
+ download_progress_cb, ad, NULL);
+
+ ad->active_downloads = eina_list_append(ad->active_downloads, strdup(suggested_name));
+
+ free(full_path);
+ free(suggested_name);
+
+ update_state(ad);
+}
+
+static void
+webview_mousewheel_cb(void *data, Evas *e, Evas_Object *obj, void *event_info)
+{
+ Tab_Data *td = data;
+ update_state(td->app);
+}
+
+static Eina_Bool
+process_keypress(void *event_info, void *data)
+{
+ KeyList *walk;
+ Key key;
+ // int keyval;
+ // GdkModifierType irrelevant;
+ App_Data *ad = data;
+ Evas_Event_Key_Down *ev = event_info;
+
+ // Get a mask of modifiers that shouldn't be considered for this event.
+ // E.g.: It shouldn't matter whether ';' is shifted or not.
+ // gdk_keymap_translate_keyboard_state(keymap, event->hardware_keycode,
+ // event->state, event->group, &keyval, NULL, NULL, &irrelevant);
+
+ walk = ad->keylistroot;
+ while (walk != NULL) {
+ key = walk->Element;
+ // if (current->Element.mask == (CLEAN(event->state) & ~irrelevant)
+ if ((key.mask == 0 || evas_key_modifier_is_set(ev->modifiers, key.mask))
+ && (key.modkey == ad->current_modkey
+ || (!key.modkey && !ad->current_modkey)
+ || key.modkey == '*' ) // wildcard
+ && !strcmp(key.key, ev->key)
+ && key.func)
+ if (key.func(&key.arg, data)) {
+ // printf("process_keypress() function for mask = %s modkey = %c key = %s called.\n", key.mask, ad->current_modkey, key.key);
+ ad->current_modkey = ad->count = 0;
+ update_state(data);
+ return TRUE;
+ }
+ walk = walk->next;
+ }
+ return FALSE;
+}
+
+static void
+webview_keypress_cb(void *data, Evas *e, Evas_Object *obj, void *event_info)
+{
+ Arg a = { .i = ModeNormal, .s = NULL };
+ // int keyval;
+ Evas_Event_Key_Down *ev = event_info;
+ Tab_Data *td = data;
+ App_Data *ad = td->app;
+ // GdkModifierType irrelevant;
+
+ /* Get a mask of modifiers that shouldn't be considered for this event.
+ * E.g.: It shouldn't matter whether ';' is shifted or not. */
+ // gdk_keymap_translate_keyboard_state(keymap, event->hardware_keycode,
+ // event->state, event->group, &keyval, NULL, NULL, &irrelevant);
+
+ switch (ad->mode) {
+ case ModeNormal:
+ // if ((CLEAN(event->state) & ~irrelevant) == 0) {
+ if (IS_ESCAPE(ev)) {
+ a.i = Info;
+ a.s = strdup("");
+ echo(&a, ad);
+ elm_box_clear(ad->event_box);
+ evas_object_hide(ad->event_box);
+ free(a.s);
+ } else if (ad->current_modkey == 0 &&
+ (( strcmp(ev->key, "1") >= 0 && strcmp(ev->key, "9") <= 0)
+ || (!strcmp(ev->key, "0") && ad->count))) {
+ // ad->count = (ad->count ? ad->count * 10 : 0) + (ev->key - "0");
+ ad->count = (ad->count ? ad->count * 10 : 0) + atoi(ev->key);
+ update_state(ad);
+ return;
+ } else if (strchr(ad->modkeys, ev->key[0]) && ad->current_modkey != ev->key[0]) {
+ ad->current_modkey = ev->key[0];
+ // ad->current_modkey = strdup(ev->key);
+
+ update_state(ad);
+ return;
+ }
+ // }
+ /* keybindings */
+ if (process_keypress(event_info, ad) == TRUE) return;
+
+ break;
+ case ModeInsert:
+ if (IS_ESCAPE(ev)) {
+ a.i = Silent;
+ a.s = strdup("hints.clearFocus();");
+ script(&a, ad);
+ free(a.s);
+ a.i = ModeNormal;
+ set(&a, ad);
+ // ewk_view_input_method_state_set(td->web, FALSE);
+ return;
+ }
+ case ModePassThrough:
+ if (IS_ESCAPE(ev)) {
+ echo(&a, ad);
+ set(&a, ad);
+ return;
+ }
+ break;
+ case ModeSendKey:
+ echo(&a, ad);
+ set(&a, ad);
+ break;
+ }
+}
+
+void
+set_widget_font_and_color(Evas_Object *widget, const char *font_str, const char *bg_color_str, const char *fg_color_str)
+{
+ /*
+ GdkColor fg_color;
+ GdkColor bg_color;
+ PangoFontDescription *font;
+
+ font = pango_font_description_from_string(font_str);
+ gtk_widget_modify_font(widget, font);
+ pango_font_description_free(font);
+
+ if (fg_color_str)
+ gdk_color_parse(fg_color_str, &fg_color);
+ if (bg_color_str)
+ gdk_color_parse(bg_color_str, &bg_color);
+
+ gtk_widget_modify_text(widget, GTK_STATE_NORMAL, fg_color_str ? &fg_color : NULL);
+ gtk_widget_modify_base(widget, GTK_STATE_NORMAL, bg_color_str ? &bg_color : NULL);
+ */
+
+ return;
+}
+
+void
+icon_received_cb(void *data, Evas_Object *obj, void *event_info)
+{
+ printf("icon_received_cb()\n");
+}
+
+void
+webview_hoverlink_cb(void *data, Evas_Object *obj, void *event_info)
+{
+ Tab_Data *td = data;
+ const char *uri = elm_web_uri_get(td->web);
+ /* event_info is a char *link[2] where the first string contains the URL
+ * and the second the title of the link */
+ char **link = event_info;
+ // char *markup;
+
+ memset(td->app->rememberedURI, 0, 1024);
+ if (link && link[0]) {
+ // markup = g_markup_printf_escaped("<span font=\"%s\">Link: %s</span>", statusfont, link[0]);
+ // markup = strdup_printf("<font=Monospace font_size=10 color=#FF0>%s", link[0]);
+ // gtk_label_set_markup(GTK_LABEL(td->app->status_url), markup);
+ // elm_object_text_set(td->app->status_url, markup);
+ update_url(link[0], td->app);
+ strcpy(td->app->rememberedURI, link[0]);
+ // free(markup);
+ } else
+ update_url(uri, td->app);
+}
+
+static void
+webview_hoverlink_out_cb(void *data, Evas_Object *obj, void *event_info)
+{
+ Tab_Data *td = data;
+ update_url(elm_web_uri_get(td->web), td->app);
+}
+
+static void
+webview_console_cb(void *data, Evas_Object *obj, const char *message, unsigned int line, const char *source)
+{
+ Arg a;
+ Tab_Data *td = data;
+
+ /* Don't change internal mode if the browser doesn't have focus to prevent inconsistent states */
+ // if (gtk_window_has_toplevel_focus(window)) {
+ if (elm_object_focus_get(td->app->win)) {
+ if (!strcmp(message, "hintmode_off") || !strcmp(message, "insertmode_off")) {
+ a.i = ModeNormal;
+ set(&a, td->app);
+ return;
+ } else if (!strcmp(message, "insertmode_on")) {
+ a.i = ModeInsert;
+ set(&a, td->app);
+ return;
+ }
+ }
+}
+
+void
+inputbox_activate_cb(void *data, Evas_Object *obj, void *event_info)
+{
+ const char *text;
+ App_Data *ad = data;
+ uint16_t length = strlen(elm_entry_entry_get(ad->url));
+ Arg a;
+ Eina_Bool forward = FALSE;
+
+ // printf("In inputbox_activate\n");
+
+ a.i = HideCompletion;
+ complete(&a, data);
+ if (length == 0)
+ return;
+ text = elm_entry_entry_get(ad->url);
+ if (length > 1 && text[0] == ':') {
+ process_line((text + 1), data);
+ } else if (length > 1 && ((forward = text[0] == '/') || text[0] == '?')) {
+ // webkit_web_view_unmark_text_matches(webview);
+ elm_web_text_matches_unmark_all(ad->current_web);
+
+ // webkit_web_view_mark_text_matches(webview, &text[1], FALSE, 0);
+ // webkit_web_view_set_highlight_text_matches(webview, TRUE);
+ elm_web_text_matches_mark(ad->current_web, &text[1], EINA_FALSE, EINA_TRUE, 0);
+
+ ad->count = 0;
+ ad->search_direction = forward;
+ ad->search_handle = strdup(&text[1]);
+ } else if (text[0] == '.' || text[0] == ',' || text[0] == ';') {
+ a.i = Silent;
+ a.s = strdup("hints.fire();");
+ script(&a, data);
+ free(a.s);
+ update_state(data);
+ } else
+ return;
+ if (!ad->echo_active)
+ // gtk_entry_set_text(entry, "");
+ elm_entry_entry_set(ad->url, "");
+ // gtk_widget_grab_focus(GTK_WIDGET(webview));
+ /* process_line above may have deleted the tab, check if its still around */
+ if (ad->current_web)
+ elm_object_focus_set(ad->current_web, EINA_TRUE);
+}
+
+static Eina_Bool
+commandhistoryfetch(const Arg *arg, void *data)
+{
+ App_Data *ad = data;
+ char *command;
+ const int length = eina_list_count(ad->commandhistory);
+
+ if (length > 0) {
+ if (arg->i == DirectionPrev) {
+ ad->commandpointer = (length + ad->commandpointer - 1) % length;
+ } else {
+ ad->commandpointer = (length + ad->commandpointer + 1) % length;
+ }
+
+ command = eina_list_nth(ad->commandhistory, ad->commandpointer);
+ // gtk_entry_set_text(GTK_ENTRY(inputbox), g_strconcat(":", command, NULL));
+ // gtk_editable_set_position(GTK_EDITABLE(inputbox), -1);
+ command = strdup_printf(":%s", command);
+ elm_entry_entry_set(ad->url, command);
+ free(command);
+ elm_entry_cursor_line_end_set(ad->url);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+inputbox_keypress_cb(void *data, Evas *e, Evas_Object *obj, void *event_info)
+{
+ Arg a;
+ int numval;
+ Evas_Event_Key_Down *ev = event_info;
+ App_Data *ad = data;
+
+ if (ad->mode == ModeHints) {
+ // if (event->keyval == GDK_ISO_Left_Tab) {
+ if (!strcmp(ev->keyname, "Tab") && evas_key_modifier_is_set(ev->modifiers, "Shift")) {
+ a.i = Silent;
+ a.s = strdup("hints.focusPreviousHint();");
+ script(&a, data);
+ free(a.s);
+ update_state(data);
+ return;
+ }
+ // if (event->keyval == GDK_Tab) {
+ if (!strcmp(ev->keyname, "Tab")) {
+ a.i = Silent;
+ a.s = strdup("hints.focusNextHint();");
+ script(&a, data);
+ free(a.s);
+ update_state(data);
+ return;
+ }
+ // if (event->keyval == GDK_Return) {
+ if (!strcmp(ev->keyname, "Return")) {
+ a.i = Silent;
+ a.s = strdup_printf("hints.fire();");
+ script(&a, data);
+ free(a.s);
+ update_state(data);
+ return;
+ }
+ }
+ if (!strcmp(ev->keyname, "[") || !strcmp(ev->keyname, "Escape")) {
+ a.i = HideCompletion;
+ complete(&a, data);
+ a.i = ModeNormal;
+ ad->commandpointer = 0;
+ set(&a, data);
+ return;
+ }
+ else if (!strcmp(ev->keyname, "Tab") && evas_key_modifier_is_set(ev->modifiers, "Shift")) {
+ a.i = DirectionPrev;
+ complete(&a, data);
+ return;
+ }
+ else if (!strcmp(ev->keyname, "Tab")) {
+ a.i = DirectionNext;
+ complete(&a, data);
+ return;
+ }
+ else if (!strcmp(ev->keyname, "Up")) {
+ a.i = DirectionPrev;
+ commandhistoryfetch(&a, data);
+ return;
+ }
+ else if (!strcmp(ev->keyname, "Down")) {
+ a.i = DirectionNext;
+ commandhistoryfetch(&a, data);
+ return;
+ }
+
+ /*
+ switch (event->keyval) {
+ case GDK_bracketleft:
+ case GDK_Escape:
+ if (!IS_ESCAPE(event)) break;
+ a.i = HideCompletion;
+ complete(&a, data);
+ a.i = ModeNormal;
+ commandpointer = 0;
+ return set(&a, ad);
+ break;
+ case GDK_Tab:
+ a.i = DirectionNext;
+ return complete(&a, data);
+ break;
+ case GDK_Up:
+ a.i = DirectionPrev;
+ return commandhistoryfetch(&a);
+ break;
+ case GDK_Down:
+ a.i = DirectionNext;
+ return commandhistoryfetch(&a);
+ break;
+ case GDK_ISO_Left_Tab:
+ a.i = DirectionPrev;
+ return complete(&a, data);
+ break;
+ }
+ */
+
+ if (ad->mode == ModeHints) {
+#if 0
+ if ((CLEAN(event->state) & GDK_SHIFT_MASK) &&
+ (CLEAN(event->state) & GDK_CONTROL_MASK) &&
+ (event->keyval == GDK_BackSpace)) {
+ count /= 10;
+ a.i = Silent;
+ a.s = g_strdup_printf("hints.updateHints(%d);", count);
+ script(&a, data);
+ g_free(a.s);
+ update_state();
+ return TRUE;
+ }
+#endif
+
+ // numval = g_unichar_digit_value((gunichar) gdk_keyval_to_unicode(event->keyval));
+ numval = atoi(ev->key);
+ if ((numval >= 1 && numval <= 9) || (numval == 0 && ad->count)) {
+ /* allow a zero as non-first number */
+ ad->count = (ad->count ? ad->count * 10 : 0) + numval;
+ a.i = Silent;
+ a.s = strdup_printf("hints.updateHints(%d);", ad->count);
+ script(&a, data);
+ free(a.s);
+ update_state(data);
+ return;
+ }
+ }
+}
+
+static void
+inputmethod_changed_cb(void *data, Evas_Object *obj, void *event_info)
+{
+ const char *value;
+ Eina_Bool *enabled = event_info;
+ Tab_Data *td = data;
+ App_Data *ad = td->app;
+ Evas_Object *view = elm_web_webkit_view_get(td->web);
+ Evas_Object *frame = ewk_view_frame_main_get(view);
+
+ if (ad->mode == ModeNormal && enabled) {
+ Arg a = { .i = ModeInsert };
+ set(&a, ad);
+ ad->manual_focus = TRUE;
+ } else if (ad->mode == ModeInsert && !enabled) {
+ Arg a = { .i = ModeNormal };
+ set(&a, ad);
+ } else {
+ // char *value = NULL, *message = NULL;
+ // jsapi_evaluate_script("window.getSelection().focusNode", &value, &message, ad);
+ value = ewk_frame_script_execute(frame, "window.getSelection().focusNode");
+ if (value && !strcmp(value, "[object HTMLFormElement]")) {
+ Arg a = { .i = ModeInsert, .s = NULL };
+ set(&a, ad);
+ ad->manual_focus = TRUE;
+ }
+ // free(value);
+ eina_stringshare_del(value);
+ // free(message);
+ }
+}
+
+/*
+static void
+// notify_event_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data) {
+notify_event_cb(void *data, Evas *e, Evas_Object *obj, void *event_info)
+{
+ int i;
+ // WebKitHitTestResult *result;
+ // WebKitHitTestResultContext context;
+ Ewk_Hit_Test *result;
+ Ewk_Hit_Test_Result_Context context;
+
+ Evas_Event_Mouse_Up *ev = event_info;
+ Tab_Data *td = data;
+ App_Data *ad = td->app;
+
+ Evas_Object *view = elm_web_webkit_view_get(td->web);
+ Evas_Object *frame = ewk_view_frame_main_get(view);
+
+ // if (mode == ModeNormal && event->type == GDK_BUTTON_RELEASE) {
+ if (ad->mode == ModeNormal) {
+ // handle mouse click events
+ for (i = 0; i < LENGTH(mouse); i++) {
+ // if (mouse[i].mask == CLEAN(event->button.state)
+ if ( (mouse[i].mask == 0 || evas_key_modifier_is_set(ev->modifiers, mouse[i].mask))
+ // && (mouse[i].modkey == current_modkey
+ && ((ad->current_modkey && !strcmp(mouse[i].modkey, ad->current_modkey))
+ || (!mouse[i].modkey && !ad->current_modkey)
+ // || mouse[i].modkey == GDK_VoidSymbol) // wildcard
+ || !strcmp(mouse[i].modkey, "*")) // wildcard
+ // && mouse[i].button == event->button.button
+ && (mouse[i].button & ev->button)
+ && mouse[i].func) {
+ if (mouse[i].func(&mouse[i].arg, ad)) {
+ // current_modkey = count = 0;
+ ad->current_modkey = NULL;
+ ad->count = 0;
+ update_state(ad);
+ return TRUE;
+ }
+ }
+ }
+ // result = webkit_web_view_get_hit_test_result(WEBKIT_WEB_VIEW(widget), (GdkEventButton*)event);
+ result = ewk_frame_hit_test_new(frame, ev->canvas.x, ev->canvas.y);
+ context = result->context;
+
+ // g_object_get(result, "context", &context, NULL);
+ // if (context & WEBKIT_HIT_TEST_RESULT_CONTEXT_EDITABLE) {
+ if (context & EWK_HIT_TEST_RESULT_CONTEXT_EDITABLE) {
+ Arg a = { .i = ModeInsert };
+ set(&a, ad);
+ ad->manual_focus = TRUE;
+ }
+ // } else if (mode == ModeInsert && event->type == GDK_BUTTON_RELEASE) {
+ } else if (ad->mode == ModeInsert) {
+ //result = webkit_web_view_get_hit_test_result(WEBKIT_WEB_VIEW(widget), (GdkEventButton*)event);
+ result = ewk_frame_hit_test_new(frame, ev->output.x, ev->output.y);
+
+ // g_object_get(result, "context", &context, NULL);
+ // if (!(context & WEBKIT_HIT_TEST_RESULT_CONTEXT_EDITABLE)) {
+ if (!(context & EWK_HIT_TEST_RESULT_CONTEXT_EDITABLE)) {
+ Arg a = { .i = ModeNormal };
+ set(&a, ad);
+ }
+ } else {
+ char *value = NULL, *message = NULL;
+ jsapi_evaluate_script("window.getSelection().focusNode", &value, &message, ad);
+ if (value && !strcmp(value, "[object HTMLFormElement]")) {
+ Arg a = { .i = ModeInsert, .s = NULL };
+ set(&a, ad);
+ ad->manual_focus = TRUE;
+ }
+ free(value);
+ free(message);
+ }
+ ewk_frame_hit_test_free(result);
+ return FALSE;
+}
+*/
+
+static void
+inputbox_keyrelease_cb(void *data, Evas *e, Evas_Object *obj, void *event_info)
+{
+ Arg a;
+ App_Data *ad = data;
+ // guint16 length = gtk_entry_get_text_length(entry);
+ uint16_t length = strlen(elm_entry_entry_get(ad->url));
+
+ if (!length) {
+ a.i = HideCompletion;
+ complete(&a, data);
+ a.i = ModeNormal;
+ set(&a, data);
+ }
+}
+
+/* used for incremental search */
+static void
+inputbox_changed_cb(void *data, Evas_Object *obj, void *event_info)
+{
+ Arg a;
+ App_Data *ad = data;
+ const char *text = elm_entry_entry_get(ad->url);
+ uint16_t length = strlen(text);
+ Eina_Bool forward = FALSE;
+
+ /* Update incremental search if the user changes the search text.
+ *
+ * Note: gtk_widget_is_focus() is a poor way to check if the change comes
+ * from the user. But if the entry is focused and the text is set
+ * through gtk_entry_set_text() in some asyncrounous operation,
+ * I would consider that a bug.
+ */
+
+ // if (gtk_widget_is_focus(GTK_WIDGET(entry)) && length > 1 && ((forward = text[0] == '/') || text[0] == '?')) {
+ if (elm_object_focus_get(ad->url) && length > 1 && ((forward = text[0] == '/') || text[0] == '?')) {
+ // webkit_web_view_unmark_text_matches(webview);
+ elm_web_text_matches_unmark_all(ad->current_web);
+
+ // webkit_web_view_search_text(webview, &text[1], searchoptions & CaseSensitive, forward, searchoptions & Wrapping);
+ elm_web_text_search(ad->current_web, &text[1], EINA_TRUE, forward, EINA_FALSE);
+ return;
+ // } else if (gtk_widget_is_focus(GTK_WIDGET(entry)) && length >= 1 &&
+ } else if (elm_object_focus_get(ad->url) && length >= 1 &&
+ (text[0] == '.' || text[0] == ',' || text[0] == ';')) {
+ a.i = Silent;
+ switch (text[0]) {
+ case '.':
+ // a.s = strconcat("hints.createHints('", text + 1, "', 'f');", NULL);
+ a.s = strdup_printf("hints.createHints('%s', 'f');", text + 1);
+ break;
+
+ case ',':
+ // a.s = strconcat("hints.createHints('", text + 1, "', 'F');", NULL);
+ a.s = strdup_printf("hints.createHints('%s', 'F');", text + 1);
+ break;
+
+ case ';':
+ a.s = NULL;
+ switch (text[1]) {
+ case 's':
+ // a.s = strconcat("hints.createHints('", text + 2, "', 's');", NULL);
+ a.s = strdup_printf("hints.createHints('%s', 's');", text + 2);
+ break;
+ case 'y':
+ // a.s = strconcat("hints.createHints('", text + 2, "', 'y');", NULL);
+ a.s = strdup_printf("hints.createHints('%s', 'y');", text + 2);
+ break;
+ case 'o':
+ // a.s = strconcat("hints.createHints('", text + 2, "', 'f');", NULL);
+ a.s = strdup_printf("hints.createHints('%s', 'f');", text + 2);
+ break;
+ case 't': case 'w':
+ // a.s = strconcat("hints.createHints('", text + 2, "', 'F');", NULL);
+ a.s = strdup_printf("hints.createHints('%s', 'F');", text + 2);
+ break;
+ case 'O': case 'T': case 'W':
+ // a.s = strconcat("hints.createHints('", text + 2, "', 'O');", NULL);
+ a.s = strdup_printf("hints.createHints('%s', 'O');", text + 2);
+ break;
+ case 'i':
+ // a.s = strconcat("hints.createHints('", text + 2, "', 'i');", NULL);
+ a.s = strdup_printf("hints.createHints('%s', 'i');", text + 2);
+ break;
+ case 'I':
+ // a.s = strconcat("hints.createHints('", text + 2, "', 'I');", NULL);
+ a.s = strdup_printf("hints.createHints('%s', 'I');", text + 2);
+ break;
+ }
+ break;
+ }
+ ad->count = 0;
+ if (a.s) {
+ script(&a, data);
+ free(a.s);
+ }
+
+ return;
+ } else if (length == 0 && ad->followTarget[0]) {
+ ad->mode = ModeNormal;
+ a.i = Silent;
+ a.s = strdup("hints.clearHints();");
+ script(&a, data);
+ free(a.s);
+ ad->count = 0;
+ update_state(data);
+ }
+}
+
+
+
+Tab_Data *tab_add(App_Data *);
+
+static Evas_Object *
+webview_create_window_cb(void *data, Evas_Object *obj, Eina_Bool js, const Elm_Web_Window_Features *wf)
+{
+ App_Data *ad = data;
+ Tab_Data *td;
+
+ td = tab_add(ad);
+ buffer_current_set(td);
+ return td->web;
+}
+
+static void
+_win_del_request_cb(void *data, Evas_Object *obj, void *event_info)
+{
+ App_Data *ad = data;
+ ad->exiting = EINA_TRUE;
+}
+
+void
+buffer_current_set(Tab_Data *td)
+{
+ App_Data *ad = td->app;
+ // const char *uri;
+ Evas_Object *view;
+
+ if (td->web == td->app->current_web)
+ return;
+
+ if (ad->current_web) {
+ view = elm_web_webkit_view_get(ad->current_web);
+ ewk_view_visibility_state_set(view, EWK_PAGE_VISIBILITY_STATE_HIDDEN, 0);
+ }
+
+ ad->current_web = td->web;
+
+ if (ad->current_web) {
+ view = elm_web_webkit_view_get(ad->current_web);
+ ewk_view_visibility_state_set(view, EWK_PAGE_VISIBILITY_STATE_VISIBLE, 0);
+ }
+
+ // uri = elm_web_uri_get(td->web);
+ update_url(elm_web_uri_get(td->web), td->app);
+ // elm_object_text_set(td->app->url, uri);
+ // if (uri) update_url(uri, td->app);
+
+
+ elm_naviframe_item_simple_promote(td->app->naviframe, td->web);
+}
+
+static void
+_web_free_cb(void *data, Evas *e, Evas_Object *obj, void *event_info)
+{
+ Tab_Data *td = data;
+ App_Data *ad = td->app;
+ elm_naviframe_item_pop(ad->naviframe);
+
+ if (!ad->exiting)
+ ad->current_web = NULL;
+
+ ad->buffer_list = eina_list_remove(ad->buffer_list, td);
+
+ if (eina_list_count(ad->buffer_list)) {
+ buffer_current_set(eina_list_nth(ad->buffer_list, 0));
+ }
+
+ free(td);
+}
+
+static void
+_uri_changed_cb(void *data, Evas_Object *obj, void *event_info)
+{
+ Tab_Data *td = data;
+ const char *uri = event_info;
+
+ if (td->web != td->app->current_web)
+ return;
+
+ update_url(uri, td->app);
+ // elm_object_text_set(td->app->url, uri);
+}
+
+/*
+static void
+_tb_item_del_cb(void *data, Evas_Object *obj, void *event_info)
+{
+ Tab_Data *td = data;
+ // if (!td->app->exiting && !elm_toolbar_selected_item_get(obj))
+ if (!td->app->exiting)
+ {
+ // td->app->current_tab = NULL;
+ // elm_entry_icon_visible_set(td->app->url, EINA_FALSE);
+ // if (td->app->search_box)
+ // evas_object_del(td->app->search_box);
+ }
+ // td->tab = NULL;
+}
+*/
+
+Eina_Bool
+add_tab(const Arg *arg, void *data)
+{
+ App_Data *ad = data;
+ Tab_Data *td = tab_add(ad);
+ buffer_current_set(td);
+ elm_object_text_set(ad->status_url, "");
+ elm_object_text_set(ad->status_state, "");
+ input(arg, data);
+ // elm_object_focus_set(ad->url, EINA_TRUE);
+
+ return EINA_TRUE;
+}
+
+Tab_Data *
+tab_add(App_Data *ad)
+{
+ Tab_Data *td;
+
+ td = calloc(1, sizeof(Tab_Data));
+ if (!td) return NULL;
+
+ ad->buf_total++;
+
+ td->app = ad;
+ td->web = elm_web_add(ad->win);
+ td->buf_number = ad->buf_total;
+ td->inspector_enabled = EINA_FALSE;
+
+ ad->buffer_list = eina_list_append(ad->buffer_list, td);
+
+ elm_web_window_create_hook_set(td->web, webview_create_window_cb, ad);
+ elm_web_console_message_hook_set(td->web, webview_console_cb, td);
+ elm_web_useragent_set(td->web, "Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/534.7 (KHTML, like Gecko) Chrome/7.0.514.0 Safari/534.7");
+ elm_web_history_enabled_set(td->web, EINA_TRUE);
+ elm_web_tab_propagate_set(td->web, EINA_FALSE);
+
+ elm_web_inwin_mode_set(td->web, EINA_TRUE);
+ evas_object_size_hint_weight_set(td->web, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+ evas_object_size_hint_align_set(td->web, EVAS_HINT_FILL, EVAS_HINT_FILL);
+
+ elm_naviframe_item_simple_push(ad->naviframe, td->web);
+ // elm_object_item_del_cb_set(td->tab, _tb_item_del_cb);
+
+ evas_object_data_set(td->web, "tab_data", td);
+
+ /* signals */
+ evas_object_smart_callback_add(td->web, "title,changed", webview_title_changed_cb, td);
+ evas_object_smart_callback_add(td->web, "uri,changed", _uri_changed_cb, td);
+ evas_object_smart_callback_add(td->web, "load,progress", webview_progress_changed_cb, td);
+ evas_object_smart_callback_add(td->web, "load,started", webview_load_committed_cb, td);
+ evas_object_smart_callback_add(td->web, "load,finished", webview_load_finished_cb, td);
+ evas_object_smart_callback_add(td->web, "download,request", webview_download_cb, td);
+ evas_object_smart_callback_add(td->web, "link,hover,in", webview_hoverlink_cb, td);
+ evas_object_smart_callback_add(td->web, "link,hover,out", webview_hoverlink_out_cb, td);
+ evas_object_smart_callback_add(td->web, "inputmethod,changed", inputmethod_changed_cb, td);
+ // evas_object_smart_callback_add(td->web, "xss,detected", xss_detected_cb, td);
+ evas_object_smart_callback_add(td->web, "icon,changed", icon_received_cb, td);
+ evas_object_smart_callback_add(td->web, "inspector,view,create", webview_inspector_cb, td);
+
+ evas_object_event_callback_add(td->web, EVAS_CALLBACK_FREE, _web_free_cb, td);
+ evas_object_event_callback_add(td->web, EVAS_CALLBACK_KEY_DOWN, webview_keypress_cb, td);
+ // evas_object_event_callback_add(td->web, EVAS_CALLBACK_MOUSE_UP, notify_event_cb, td);
+ evas_object_event_callback_add(td->web, EVAS_CALLBACK_MOUSE_WHEEL, webview_mousewheel_cb, td);
+
+ // Evas_Object *view = elm_web_webkit_view_get(ad->current_web);
+
+// ewk_view_setting_include_link_in_focus_chain_set(view, EINA_FALSE);
+
+ // static Ewk_View_Smart_Class api = EWK_VIEW_SMART_CLASS_INIT_NAME_VERSION("evi");
+ // ewk_view_tiled_smart_set(&api);
+
+ return td;
+}
+
+
+EAPI_MAIN int
+elm_main(int argc, char *argv[])
+{
+ Evas_Object *win, *bg, *box, *status_bar, *url, *naviframe, *status_url;
+ Evas_Object *status_state, *event_box, *web_inspector;
+ Evas *e;
+ Evas_Modifier_Mask mask;
+ Arg a;
+ App_Data *ad;
+ unsigned int i;
+
+ if (!elm_need_web())
+ return -1;
+
+ ad = calloc(1, sizeof(App_Data));
+ if (!ad) return -1;
+
+ elm_policy_set(ELM_POLICY_QUIT, ELM_POLICY_QUIT_LAST_WINDOW_CLOSED);
+
+ ewk_network_tls_ca_certificates_path_set(ca_bundle);
+
+ win = elm_win_add(NULL, "evi", ELM_WIN_BASIC);
+ elm_win_autodel_set(win, EINA_TRUE);
+ // elm_win_shaped_set(win, EINA_TRUE);
+ // elm_win_screen_constrain_set(win, EINA_TRUE);
+
+ ad->modkeys = calloc(LENGTH(keys) + 1, sizeof(char));
+ ad->modkeys[0] = '\0';
+ e = evas_object_evas_get(win);
+
+ for (i = 0; i < LENGTH(keys); i++) {
+ if (keys[i].modkey && !strchr(ad->modkeys, keys[i].modkey)) {
+ // strcat(ad->modkeys, keys[i].modkey);
+ ad->modkeys[strlen(ad->modkeys)] = keys[i].modkey;
+ ad->modkeys[strlen(ad->modkeys) + 1] = '\0';
+ }
+
+ mask = evas_key_modifier_mask_get(e, keys[i].mask);
+ if (keys[i].key && !evas_object_key_grab(win, keys[i].key, mask, 0, EINA_FALSE))
+ fprintf(stderr, "Could not grab trigger for mask = %s, key = %s\n", keys[i].mask , keys[i].key);
+ }
+
+ fprintf(stderr, "Modkey array looks like %s\n", ad->modkeys);
+
+ evas_object_smart_callback_add(win, "delete,request", _win_del_request_cb, ad);
+
+ ewk_settings_icon_database_path_set("./");
+ ewk_cookies_file_set("cookies.txt");
+
+ bg = elm_bg_add(win);
+ evas_object_size_hint_weight_set(bg, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+ elm_win_resize_object_add(win, bg);
+ // elm_bg_color_set(bg, 255, 255, 255);
+ evas_object_show(bg);
+
+ box = elm_box_add(win);
+ evas_object_size_hint_weight_set(box, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+ elm_win_resize_object_add(win, box);
+ evas_object_show(box);
+
+ naviframe = elm_naviframe_add(win);
+ evas_object_size_hint_weight_set(naviframe, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+ evas_object_size_hint_align_set(naviframe, EVAS_HINT_FILL, EVAS_HINT_FILL);
+ elm_box_pack_end(box, naviframe);
+ evas_object_show(naviframe);
+
+ /* status bar container */
+ status_bar = elm_box_add(win);
+ elm_box_homogeneous_set(status_bar, EINA_FALSE);
+ elm_box_horizontal_set(status_bar, EINA_TRUE);
+ evas_object_size_hint_weight_set(status_bar, EVAS_HINT_EXPAND, 0.0);
+ evas_object_size_hint_align_set(status_bar, EVAS_HINT_FILL, EVAS_HINT_FILL);
+ elm_box_pack_end(box, status_bar);
+ evas_object_show(status_bar);
+
+ /* status bar url */
+ status_url = elm_label_add(win);
+ evas_object_size_hint_weight_set(status_url, 0.0, EVAS_HINT_EXPAND);
+ evas_object_size_hint_align_set(status_url, 0.0, EVAS_HINT_FILL);
+ elm_box_pack_end(status_bar, status_url);
+ evas_object_show(status_url);
+
+ /* status bar state (load progress, mod keys, scroll %) */
+ status_state = elm_label_add(win);
+ evas_object_size_hint_weight_set(status_state, 1.0, EVAS_HINT_EXPAND);
+ evas_object_size_hint_align_set(status_state, 1.0, EVAS_HINT_FILL);
+ elm_box_pack_end(status_bar, status_state);
+ evas_object_show(status_state);
+
+ /* invisible until activated web inspector */
+ web_inspector = elm_box_add(win);
+ evas_object_size_hint_align_set(web_inspector, EVAS_HINT_FILL, EVAS_HINT_FILL);
+ elm_box_pack_end(box, web_inspector);
+ evas_object_hide(web_inspector);
+
+ /* invisible event box, populated on tab completions and :ls */
+ event_box = elm_box_add(win);
+ evas_object_size_hint_align_set(event_box, EVAS_HINT_FILL, EVAS_HINT_FILL);
+ elm_box_pack_end(box, event_box);
+ evas_object_hide(event_box);
+
+ /* main entry widget */
+ url = elm_entry_add(win);
+ elm_entry_single_line_set(url, EINA_TRUE);
+ elm_entry_scrollable_set(url, EINA_TRUE);
+ evas_object_size_hint_weight_set(url, EVAS_HINT_EXPAND, 0.0);
+ evas_object_size_hint_align_set(url, EVAS_HINT_FILL, EVAS_HINT_FILL);
+ elm_box_pack_end(box, url);
+ elm_entry_text_style_user_push(url, "DEFAULT='font=Monospace font_size=12'");
+ evas_object_show(url);
+
+ evas_object_smart_callback_add(url, "activated", inputbox_activate_cb, ad);
+ evas_object_smart_callback_add(url, "changed", inputbox_changed_cb, ad);
+ evas_object_event_callback_add(url, EVAS_CALLBACK_KEY_DOWN, inputbox_keypress_cb, ad);
+ evas_object_event_callback_add(url, EVAS_CALLBACK_KEY_UP, inputbox_keyrelease_cb, ad);
+
+ ad->win = win;
+ ad->main_box = box;
+ ad->naviframe = naviframe;
+ ad->url = url;
+ ad->status_url = status_url;
+ ad->status_state = status_state;
+ ad->event_box = event_box;
+ ad->web_inspector = web_inspector;
+
+ ad->buf_total = 0;
+
+ a.i = TargetCurrent;
+ a.s = strdup_printf(":open %s", startpage);
+ add_tab(&a, ad);
+ free(a.s);
+
+ evas_object_resize(win, 480, 640);
+ evas_object_show(win);
+
+ make_searchengines_list(searchengines, LENGTH(searchengines));
+ make_uri_handlers_list(uri_handlers, LENGTH(uri_handlers));
+
+ ad->keylistroot = make_keyslist();
+
+ memset(ad->rememberedURI, 0, 1024);
+ memset(ad->followTarget, 0, 8);
+ ad->error_msg = NULL;
+ ad->config_base = "./";
+ ad->manual_focus = FALSE;
+ ad->zoomstep = 0.1f;
+ ad->current_modkey = 0;
+
+ ad->commandhistory = NULL;
+ ad->commandpointer = 0;
+
+ ad->mode = ModeNormal;
+ ad->count = 0;
+ ad->echo_active = EINA_TRUE;
+
+
+ toggle_proxy(use_proxy);
+
+ elm_run();
+ elm_shutdown();
+ ewk_shutdown();
+
+ free(ad->modkeys);
+ eina_list_free(ad->buffer_list);
+
+ return 0;
+}
+
+ELM_MAIN()
+
diff --git a/main.h b/main.h
@@ -0,0 +1,74 @@
+/*
+ (c) 2009 by Leon Winter
+ (c) 2009, 2010 by Hannes Schueller
+ (c) 2009, 2010 by Matto Fransen
+ (c) 2010 by Hans-Peter Deifel
+ (c) 2010 by Thomas Adam
+ see LICENSE file
+*/
+
+#ifndef MAIN_H
+#define MAIN_H
+
+typedef struct _Tab_Data Tab_Data;
+
+void update_state(void *);
+Eina_Bool echo(const Arg *arg, void *);
+void buffer_current_set(Tab_Data *td);
+
+typedef struct
+{
+ Evas_Object *win;
+ Evas_Object *main_box;
+ Evas_Object *naviframe;
+ Evas_Object *url; /* "inputbox" */
+ Evas_Object *event_box;
+ Evas_Object *status_bar;
+ Evas_Object *status_url;
+ Evas_Object *status_state;
+ Evas_Object *web_inspector;
+
+ unsigned int buf_total;
+
+ char current_modkey;
+ char *modkeys;
+ KeyList *keylistroot;
+
+ unsigned int mode;
+ unsigned int count;
+ float zoomstep;
+ char *search_handle;
+ Eina_Bool search_direction;
+ Eina_Bool echo_active;
+ // WebKitWebInspector *inspector;
+
+ char rememberedURI[1024];
+ char followTarget[8];
+ char *error_msg;
+ char *config_base;
+ Eina_Bool manual_focus;
+
+ Eina_List *buffer_list;
+ Eina_List *active_downloads;
+ Eina_List *commandhistory;
+ int commandpointer;
+
+ // SoupSession *session;
+
+ Evas_Object *current_web;
+
+ Eina_Bool exiting : 1;
+} App_Data;
+
+struct _Tab_Data
+{
+ Evas_Object *web;
+ App_Data *app;
+ unsigned int buf_number;
+
+ Eina_Bool inspector_enabled;
+ Evas_Object *web_inspector;
+};
+
+#endif
+
diff --git a/utilities.c b/utilities.c
@@ -0,0 +1,1070 @@
+/*
+ (c) 2009 by Leon Winter
+ (c) 2009-2012 by Hannes Schueller
+ (c) 2009-2010 by Matto Fransen
+ (c) 2010-2011 by Hans-Peter Deifel
+ (c) 2010-2011 by Thomas Adam
+ see LICENSE file
+*/
+
+
+
+#define _GNU_SOURCE
+#include <Elementary.h>
+
+#include <stdlib.h> /* malloc */
+#include <stdarg.h> /* vsnprintf */
+#include <stdio.h> /* vsnprintf */
+#include <ctype.h> /* tolower */
+#include <unistd.h> /* realloc */
+
+#include "viking.h"
+#include "main.h"
+#include "utilities.h"
+#include "commands.h"
+#include "keymap.h"
+
+#include "jsmn/jsmn.h"
+
+/* these all need to go */
+extern Eina_Bool complete_case_sensitive;
+extern char *config_base;
+
+static Eina_List *dynamic_searchengines = NULL, *dynamic_uri_handlers = NULL;
+
+
+
+void add_modkeys(char key);
+int get_modkey(char key);
+void strdown(char *str);
+
+char *strdup_printf(const char *fmt, ...)
+{
+ va_list args;
+ va_start (args,fmt);
+
+ /* arbitrary size */
+ char *buf = malloc(1024);
+
+ vsnprintf(buf, 1024, fmt, args);
+ va_end(args);
+
+ return buf;
+}
+
+void strdown(char *str)
+{
+ int i;
+
+ for (i = 0; str[i]; i++) {
+ str[i] = tolower(str[i]);
+ }
+}
+
+char *
+search_word(int whichword)
+{
+ int k = 0;
+ static char word[240];
+ char *c = my_pair.line;
+
+ while (isspace(*c) && *c)
+ c++;
+
+ switch (whichword) {
+ case 0:
+ while (*c && !isspace (*c) && *c != '=' && k < 240) {
+ word[k++] = *c;
+ c++;
+ }
+ word[k] = '\0';
+ strncpy(my_pair.what, word, 20);
+ break;
+ case 1:
+ while (*c && k < 240) {
+ word[k++] = *c;
+ c++;
+ }
+ word[k] = '\0';
+ strncpy(my_pair.value, word, 240);
+ break;
+ }
+
+ return c;
+}
+
+void save_command_history(char *line, void *data)
+{
+ char *c = line;
+ App_Data *ad = data;
+
+ while (isspace(*c) && *c)
+ c++;
+ if (!strlen(c))
+ return;
+
+ // if (COMMANDHISTSIZE <= g_list_length(commandhistory)) {
+ if (COMMANDHISTSIZE <= eina_list_count(ad->commandhistory)) {
+ /* if list is too long - remove items from beginning */
+ // commandhistory = g_list_delete_link(commandhistory, g_list_first(commandhistory));
+ ad->commandhistory = eina_list_remove(ad->commandhistory, eina_list_nth(ad->commandhistory, 0));
+ }
+ // commandhistory = g_list_append(commandhistory, g_strdup(c));
+ ad->commandhistory = eina_list_append(ad->commandhistory, strdup(c));
+}
+
+/*
+Eina_Bool
+process_save_qmark(const char *bm, WebKitWebView *webview)
+{
+ FILE *fp;
+ const char *filename;
+ const char *uri = webkit_web_view_get_uri(webview);
+ char qmarks[10][101];
+ char buf[100];
+ int i, mark, l=0;
+ Arg a;
+ mark = -1;
+ mark = atoi(bm);
+ if ( mark < 1 || mark > 9 )
+ {
+ a.i = Error;
+ a.s = g_strdup_printf("Invalid quickmark, only 1-9");
+ echo(&a);
+ g_free(a.s);
+ return TRUE;
+ }
+ if ( uri == NULL ) return FALSE;
+ for( i=0; i < 9; ++i ) strcpy( qmarks[i], "");
+
+ filename = g_strdup_printf(QUICKMARK_FILE);
+
+ // get current quickmarks
+
+ fp = fopen(filename, "r");
+ if (fp != NULL){
+ for( i=0; i < 10; ++i ) {
+ if (feof(fp)) {
+ break;
+ }
+ fgets(buf, 100, fp);
+ l = 0;
+ while (buf[l] && l < 100 && buf[l] != '\n') {
+ qmarks[i][l]=buf[l];
+ l++;
+ }
+ qmarks[i][l]='\0';
+ }
+ fclose(fp);
+ }
+
+ // save quickmarks
+ strcpy( qmarks[mark-1], uri );
+ fp = fopen(filename, "w");
+ g_free((gpointer *)filename);
+ if (fp == NULL) return FALSE;
+ for( i=0; i < 10; ++i )
+ fprintf(fp, "%s\n", qmarks[i]);
+ fclose(fp);
+ a.i = Error;
+ a.s = g_strdup_printf("Saved as quickmark %d: %s", mark, uri);
+ echo(&a);
+ g_free(a.s);
+
+ return TRUE;
+}
+*/
+
+KeyList *
+make_keyslist(void)
+{
+ int i;
+ KeyList *ptr, *current;
+ KeyList *keylistroot = NULL;
+
+ ptr = NULL;
+ current = NULL;
+ i = 0;
+ while ( keys[i].key != 0 )
+ {
+ current = malloc(sizeof(KeyList));
+ if (current == NULL) {
+ printf("Not enough memory\n");
+ exit(-1);
+ }
+ current->Element = keys[i];
+ current->next = NULL;
+ if (keylistroot == NULL) keylistroot = current;
+ if (ptr != NULL) ptr->next = current;
+ ptr = current;
+ i++;
+ }
+
+ printf("make_keylist() processed %i keys.\n", i);
+ return keylistroot;
+}
+
+Eina_Bool
+parse_colour(char *color) {
+ char goodcolor[8];
+ int colorlen;
+
+ colorlen = (int)strlen(color);
+
+ goodcolor[0] = '#';
+ goodcolor[7] = '\0';
+
+ /* help the user a bit by making string like
+ #a10 and strings like ffffff full 6digit
+ strings with # in front :)
+ */
+
+ if (color[0] == '#') {
+ switch (colorlen) {
+ case 7:
+ strncpy(goodcolor, color, 7);
+ break;
+ case 4:
+ goodcolor[1] = color[1];
+ goodcolor[2] = color[1];
+ goodcolor[3] = color[2];
+ goodcolor[4] = color[2];
+ goodcolor[5] = color[3];
+ goodcolor[6] = color[3];
+ break;
+ case 2:
+ goodcolor[1] = color[1];
+ goodcolor[2] = color[1];
+ goodcolor[3] = color[1];
+ goodcolor[4] = color[1];
+ goodcolor[5] = color[1];
+ goodcolor[6] = color[1];
+ break;
+ }
+ } else {
+ switch (colorlen) {
+ case 6:
+ strncpy(&goodcolor[1], color, 6);
+ break;
+ case 3:
+ goodcolor[1] = color[0];
+ goodcolor[2] = color[0];
+ goodcolor[3] = color[1];
+ goodcolor[4] = color[1];
+ goodcolor[5] = color[2];
+ goodcolor[6] = color[2];
+ break;
+ case 1:
+ goodcolor[1] = color[0];
+ goodcolor[2] = color[0];
+ goodcolor[3] = color[0];
+ goodcolor[4] = color[0];
+ goodcolor[5] = color[0];
+ goodcolor[6] = color[0];
+ break;
+ }
+ }
+
+ if (strlen (goodcolor) != 7) {
+ return FALSE;
+ } else {
+ strncpy(color, goodcolor, 8);
+ return TRUE;
+ }
+}
+
+Eina_Bool
+process_line_arg(const Arg *arg, void *data) {
+ return process_line(arg->s, data);
+}
+
+#if 0
+Eina_Bool
+changemapping(Key *search_key, int maprecord, char *cmd) {
+ KeyList *current, *newkey;
+ Arg a = { .s = cmd };
+
+ /* sanity check */
+ if (maprecord < 0 && cmd == NULL) {
+ /* possible states:
+ * - maprecord >= 0 && cmd == NULL: mapping to internal symbol
+ * - maprecord < 0 && cmd != NULL: mapping to command line
+ * - maprecord >= 0 && cmd != NULL: cmd will be ignored, treated as mapping to internal symbol
+ * - anything else (gets in here): an error, hence we return FALSE */
+ return FALSE;
+ }
+
+ // current = keylistroot;
+ current = NULL;
+
+ if (current)
+ while (current->next != NULL) {
+ if (
+ current->Element.mask == search_key->mask &&
+ current->Element.modkey == search_key->modkey &&
+ current->Element.key == search_key->key
+ ) {
+ if (maprecord >= 0) {
+ /* mapping to an internal signal */
+ current->Element.func = commands[maprecord].func;
+ current->Element.arg = commands[maprecord].arg;
+ } else {
+ /* mapping to a command line */
+ current->Element.func = process_line_arg;
+ current->Element.arg = a;
+ }
+ return TRUE;
+ }
+ current = current->next;
+ }
+ newkey = malloc(sizeof(KeyList));
+ if (newkey == NULL) {
+ printf("Not enough memory\n");
+ exit (-1);
+ }
+ newkey->Element.mask = search_key->mask;
+ newkey->Element.modkey = search_key->modkey;
+ newkey->Element.key = search_key->key;
+ if (maprecord >= 0) {
+ /* mapping to an internal signal */
+ newkey->Element.func = commands[maprecord].func;
+ newkey->Element.arg = commands[maprecord].arg;
+ } else {
+ /* mapping to a command line */
+ newkey->Element.func = process_line_arg;
+ newkey->Element.arg = a;
+ }
+ add_modkeys(newkey->Element.modkey);
+
+ newkey->next = NULL;
+
+ // if (keylistroot == NULL) keylistroot = newkey;
+
+ if (current != NULL) current->next = newkey;
+
+ return TRUE;
+}
+
+void add_modkeys(char key)
+{
+ unsigned int k, len;
+ extern char *modkeys;
+ len = strlen( modkeys );
+ while (k < len )
+ {
+ if ( modkeys[k] == key ) return;
+ k++;
+ }
+ modkeys = realloc(modkeys, len + 1);
+ modkeys[len++] = key;
+ modkeys[len] = '\0';
+}
+#endif
+
+Eina_Bool
+mappings(const Arg *arg, void *data) {
+ char line[255];
+
+ if (!arg->s) {
+ set_error("Missing argument.", data);
+ return FALSE;
+ }
+ strncpy(line, arg->s, 254);
+ if (process_map_line(line))
+ return TRUE;
+ else {
+ set_error("Invalid mapping.", data);
+ return FALSE;
+ }
+}
+
+int
+get_modkey(char key) {
+ /*
+ switch (key) {
+ case '1':
+ return GDK_MOD1_MASK;
+ case '2':
+ return GDK_MOD2_MASK;
+ case '3':
+ return GDK_MOD3_MASK;
+ case '4':
+ return GDK_MOD4_MASK;
+ case '5':
+ return GDK_MOD5_MASK;
+ default:
+ return FALSE;
+ }
+ */
+ return 0;
+}
+
+Eina_Bool
+process_mapping(char *keystring, int maprecord, char *cmd) {
+#if 0
+ Key search_key;
+
+ search_key.mask = 0;
+ search_key.modkey = 0;
+ search_key.key = 0;
+
+ if (strlen(keystring) == 1) {
+ search_key.key = keystring[0];
+ }
+
+ if (strlen(keystring) == 2) {
+ search_key.modkey= keystring[0];
+ search_key.key = keystring[1];
+ }
+
+ /* process stuff like <S-v> for Shift-v or <C-v> for Ctrl-v (strlen == 5),
+ stuff like <S-v>a for Shift-v,a or <C-v>a for Ctrl-v,a (strlen == 6 && keystring[4] == '>')
+ stuff like <M1-v> for Mod1-v (strlen == 6 && keystring[5] == '>')
+ or stuff like <M1-v>a for Mod1-v,a (strlen = 7)
+ */
+ if (
+ ((strlen(keystring) == 5 || strlen(keystring) == 6) && keystring[0] == '<' && keystring[4] == '>') ||
+ ((strlen(keystring) == 6 || strlen(keystring) == 7) && keystring[0] == '<' && keystring[5] == '>')
+ ) {
+ switch (toupper(keystring[1])) {
+ case 'S':
+ search_key.mask = "Shift";
+ if (strlen(keystring) == 5) {
+ keystring[3] = toupper(keystring[3]);
+ } else {
+ keystring[3] = tolower(keystring[3]);
+ keystring[5] = toupper(keystring[5]);
+ }
+ break;
+ case 'C':
+ search_key.mask = "Control";
+ break;
+ case 'M':
+ // search_key.mask = get_modkey(keystring[2]);
+ break;
+ }
+ if (!search_key.mask)
+ return FALSE;
+ if (strlen(keystring) == 5) {
+ search_key.key = keystring[3];
+ } else if (strlen(keystring) == 7) {
+ search_key.modkey = keystring[4];
+ search_key.key = keystring[6];
+ } else {
+ if (!strcmp(search_key.mask, "Shift") || !strcmp(search_key.mask, "Control")) {
+ search_key.modkey = keystring[3];
+ search_key.key = keystring[5];
+ } else {
+ search_key.key = keystring[4];
+ }
+ }
+ }
+
+ /* process stuff like a<S-v> for a,Shift-v or a<C-v> for a,Ctrl-v (strlen == 6)
+ or stuff like a<M1-v> for a,Mod1-v (strlen == 7)
+ */
+ if (
+ (strlen(keystring) == 6 && keystring[1] == '<' && keystring[5] == '>') ||
+ (strlen(keystring) == 7 && keystring[1] == '<' && keystring[6] == '>')
+ ) {
+ switch (toupper(keystring[2])) {
+ case 'S':
+ search_key.mask = "Shift";
+ keystring[4] = toupper(keystring[4]);
+ break;
+ case 'C':
+ search_key.mask = "Control";
+ break;
+ case 'M':
+ // search_key.mask = get_modkey(keystring[3]);
+ break;
+ }
+ if (!search_key.mask)
+ return FALSE;
+ search_key.modkey= keystring[0];
+ if (strlen(keystring) == 6) {
+ search_key.key = keystring[4];
+ } else {
+ search_key.key = keystring[5];
+ }
+ }
+ // return (changemapping(&search_key, maprecord, cmd));
+#endif
+ return EINA_TRUE;
+}
+
+Eina_Bool
+process_map_line(char *line) {
+ int listlen, i;
+ char *c, *cmd;
+ my_pair.line = line;
+ c = search_word(0);
+
+ if (!strlen(my_pair.what))
+ return FALSE;
+ while (isspace(*c) && *c)
+ c++;
+
+ if (*c == ':' || *c == '=')
+ c++;
+ my_pair.line = c;
+ c = search_word(1);
+ if (!strlen(my_pair.value))
+ return FALSE;
+ listlen = LENGTH(commands);
+ for (i = 0; i < listlen; i++) {
+ /* commands is fixed size */
+ if (commands[i].cmd == NULL)
+ break;
+ if (strlen(commands[i].cmd) == strlen(my_pair.value) && strncmp(commands[i].cmd, my_pair.value, strlen(my_pair.value)) == 0) {
+ /* map to an internal symbol */
+ return process_mapping(my_pair.what, i, NULL);
+ }
+ }
+ /* if this is reached, the mapping is not for one of the internal symbol - test for command line structure */
+ if (strlen(my_pair.value) > 1 && strncmp(my_pair.value, ":", 1) == 0) {
+ /* The string begins with a colon, like a command line, but it's not _just_ a colon,
+ * i.e. increasing the pointer by one will not go 'out of bounds'.
+ * We don't actually check that the command line after the = is valid.
+ * This is user responsibility, the worst case is the new mapping simply doing nothing.
+ * Since we will pass the command to the same function which also handles the config file lines,
+ * we have to strip the colon itself (a colon counts as a commented line there - like in vim).
+ * Last, but not least, the second argument being < 0 signifies to the function that this is a
+ * command line mapping, not a mapping to an existing internal symbol. */
+ cmd = (char *)malloc(sizeof(char) * strlen(my_pair.value));
+ memset(cmd, 0, strlen(my_pair.value));
+ strncpy(cmd, (my_pair.value + 1), strlen(my_pair.value) - 1);
+ return process_mapping(my_pair.what, -1, cmd);
+ }
+ return FALSE;
+}
+
+Eina_Bool
+build_taglist(const Arg *arg, FILE *f) {
+ int k = 0, in_tag = 0;
+ int t = 0, marker = 0;
+ char foundtab[MAXTAGSIZE+1];
+ while (arg->s[k]) {
+ if (!isspace(arg->s[k]) && !in_tag) {
+ in_tag = 1;
+ marker = k;
+ }
+ if (isspace(arg->s[k]) && in_tag) {
+ /* found a tag */
+ t = 0;
+ while (marker < k && t < MAXTAGSIZE) foundtab[t++] = arg->s[marker++];
+ foundtab[t] = '\0';
+ fprintf(f, " [%s]", foundtab);
+ in_tag = 0;
+ }
+ k++;
+ }
+ if (in_tag) {
+ t = 0;
+ while (marker < strlen(arg->s) && t < MAXTAGSIZE) foundtab[t++] = arg->s[marker++];
+ foundtab[t] = '\0';
+ fprintf(f, " [%s]", foundtab );
+ }
+ return TRUE;
+}
+
+void
+set_error(const char *error, void *data) {
+ App_Data *ad = data;
+ /* it should never happen that set_error is called more than once,
+ * but to avoid any potential memory leaks, we ignore any subsequent
+ * error if the current one has not been shown */
+ if (ad->error_msg == NULL) {
+ ad->error_msg = strdup_printf("%s", error);
+ }
+}
+
+void
+give_feedback(const char *feedback, void *data)
+{
+ Arg a = { .i = Info };
+
+ a.s = strdup_printf("%s", feedback);
+ echo(&a, data);
+ free(a.s);
+}
+
+Listelement *
+complete_list(const char *searchfor, const int mode, Listelement *elementlist, void *data)
+{
+ FILE *f;
+ char *filename;
+ Listelement *candidatelist = NULL, *candidatepointer = NULL;
+ char s[255] = "", readelement[MAXTAGSIZE + 1] = "";
+ int i, t, n = 0;
+ App_Data *ad = data;
+
+ if (mode == 2) {
+ /* open in history file */
+ filename = strdup_printf(HISTORY_STORAGE_FILENAME);
+ } else {
+ /* open in bookmark file (for tags and bookmarks) */
+ filename = strdup_printf(BOOKMARKS_STORAGE_FILENAME);
+ }
+ f = fopen(filename, "r");
+ if (f == NULL) {
+ free(filename);
+ return (elementlist);
+ }
+
+ while (fgets(s, 254, f)) {
+ if (mode == 1) {
+ /* just tags (could be more than one per line) */
+ i = 0;
+ while (s[i] && i < 254) {
+ while (s[i] != '[' && s[i])
+ i++;
+ if (s[i] != '[')
+ continue;
+ i++;
+ t = 0;
+ while (s[i] != ']' && s[i] && t < MAXTAGSIZE)
+ readelement[t++] = s[i++];
+ readelement[t] = '\0';
+ candidatelist = add_list(readelement, candidatelist);
+ i++;
+ }
+ } else {
+ /* complete string (bookmarks & history) */
+ candidatelist = add_list(s, candidatelist);
+ }
+ candidatepointer = candidatelist;
+ while (candidatepointer != NULL) {
+ strncpy(s, candidatepointer->element, sizeof(s));
+ if (!complete_case_sensitive) {
+ strdown(s);
+ }
+ if (!strlen(searchfor) || strstr(s, searchfor) != NULL) {
+ /* only use string up to the first space */
+ memset(readelement, 0, MAXTAGSIZE + 1);
+ if (strchr(candidatepointer->element, ' ') != NULL) {
+ i = strcspn(candidatepointer->element, " ");
+ if (i > MAXTAGSIZE)
+ i = MAXTAGSIZE;
+ strncpy(readelement, candidatepointer->element, i);
+ } else {
+ strncpy(readelement, candidatepointer->element, MAXTAGSIZE);
+ }
+ /* in the case of URLs without title, remove the line break */
+ if (readelement[strlen(readelement) - 1] == '\n') {
+ readelement[strlen(readelement) - 1] = '\0';
+ }
+ elementlist = add_list(readelement, elementlist);
+ n = count_list(elementlist);
+ }
+ if (n >= MAX_LIST_SIZE)
+ break;
+ candidatepointer = candidatepointer->next;
+ }
+ free_list(candidatelist);
+ candidatelist = NULL;
+ if (n >= MAX_LIST_SIZE)
+ break;
+ }
+ free(filename);
+ return (elementlist);
+}
+
+Listelement *
+add_list(const char *element, Listelement *elementlist)
+{
+ int n, i = 0;
+ Listelement *newelement, *elementpointer, *lastelement;
+
+ if (elementlist == NULL) { /* first element */
+ newelement = malloc(sizeof(Listelement));
+ if (newelement == NULL)
+ return (elementlist);
+ strncpy(newelement->element, element, 254);
+ newelement->next = NULL;
+ return newelement;
+ }
+ elementpointer = elementlist;
+ n = strlen(element);
+
+ /* check if element is already in list */
+ while (elementpointer != NULL) {
+ if (strlen(elementpointer->element) == n &&
+ strncmp(elementpointer->element, element, n) == 0)
+ return (elementlist);
+ lastelement = elementpointer;
+ elementpointer = elementpointer->next;
+ i++;
+ }
+ /* add to list */
+ newelement = malloc(sizeof(Listelement));
+ if (newelement == NULL)
+ return (elementlist);
+ lastelement->next = newelement;
+ strncpy(newelement->element, element, 254);
+ newelement->next = NULL;
+ return elementlist;
+}
+
+void
+free_list(Listelement *elementlist)
+{
+ Listelement *elementpointer;
+
+ while (elementlist != NULL) {
+ elementpointer = elementlist->next;
+ free(elementlist);
+ elementlist = elementpointer;
+ }
+}
+
+int
+count_list(Listelement *elementlist)
+{
+ Listelement *elementpointer = elementlist;
+ int n = 0;
+
+ while (elementpointer != NULL) {
+ n++;
+ elementpointer = elementpointer->next;
+ }
+
+ return n;
+}
+
+/* split the string at the first occurence of whitespace and return the
+ * position of the second half.
+ * Unlike strtok, the substrings can be empty and the second string is
+ * stripped of trailing and leading whitespace.
+ * Return -1 if `string' contains no whitespace */
+static int split_string_at_whitespace(char *string)
+{
+ int index = strcspn(string, "\n\t ");
+ if (string[index] != '\0') {
+ string[index++] = 0;
+ // g_strstrip(string+index);
+ return index;
+ }
+ return -1;
+}
+
+/* return TRUE, if the string contains exactly one unescaped %s and no other
+ * printf directives */
+static Eina_Bool sanity_check_search_url(const char *string)
+{
+ int was_percent_char = 0, percent_s_count = 0;
+
+ for (; *string; string++) {
+ switch (*string) {
+ case '%':
+ was_percent_char = !was_percent_char;
+ break;
+ case 's':
+ if (was_percent_char)
+ percent_s_count++;
+ was_percent_char = 0;
+ break;
+ default:
+ if (was_percent_char)
+ return FALSE;
+ was_percent_char = 0;
+ break;
+ }
+ }
+
+ return !was_percent_char && percent_s_count == 1;
+}
+
+void make_searchengines_list(Searchengine *searchengines, int length)
+{
+ int i;
+ for (i = 0; i < length; i++, searchengines++) {
+ dynamic_searchengines = eina_list_prepend(dynamic_searchengines, searchengines);
+ }
+}
+
+/* find a searchengine with a given handle and return its URI or NULL if
+ * nothing is found.
+ * The returned string is internal and must not be freed or modified. */
+char *find_uri_for_searchengine(const char *handle)
+{
+ Eina_List *l;
+
+ if (dynamic_searchengines != NULL) {
+ for (l = dynamic_searchengines; l; l = eina_list_next(l)) {
+ Searchengine *s = (Searchengine*)l->data;
+ if (!strcmp(s->handle, handle)) {
+ return s->uri;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+Eina_Bool
+process_line(const char *line, void *data)
+{
+ const char *c = line;
+ char *command_hist;
+ int i;
+ App_Data *ad = data;
+ size_t len, length = strlen(line);
+ Eina_Bool found = EINA_FALSE, success = EINA_FALSE;
+ Arg a;
+
+ while (isspace(*c))
+ c++;
+ /* Ignore blank lines. */
+ if (c[0] == '\0')
+ return EINA_TRUE;
+
+ command_hist = strdup(c);
+ for (i = 0; i < LENGTH(commands); i++) {
+ if (commands[i].cmd == NULL)
+ break;
+ len = strlen(commands[i].cmd);
+ if (length >= len && !strncmp(c, commands[i].cmd, len) && (c[len] == ' ' || !c[len])) {
+ found = EINA_TRUE;
+ a.i = commands[i].arg.i;
+ //printf("length, len, c, commands[i].arg.s = %zu, %zu, %s, %s\n",
+ // length, len, c, commands[i].arg.s);
+ if (length > len + 1)
+ a.s = strdup(&c[len + 1]);
+ else if (!(length > len + 1) && commands[i].arg.s)
+ a.s = strdup(commands[i].arg.s);
+ else
+ a.s = NULL;
+ success = commands[i].func(&a, data);
+ free(a.s);
+ break;
+ }
+ }
+
+ save_command_history(command_hist, data);
+ free(command_hist);
+
+ if (!found) {
+ a.i = Error;
+ a.s = strdup_printf("Not a browser command: %s", c);
+ echo(&a, data);
+ free(a.s);
+ } else if (!success) {
+ a.i = Error;
+ if (ad->error_msg != NULL) {
+ a.s = strdup_printf("%s", ad->error_msg);
+ free(ad->error_msg);
+ ad->error_msg = NULL;
+ } else {
+ a.s = strdup_printf("Unknown error. Please file a bug report!");
+ }
+ echo(&a, data);
+ free(a.s);
+ }
+ return success;
+}
+
+enum ConfigFileError
+read_rcfile(const char *config, void *data)
+{
+ int t, linum = 0, index;
+ char s[255], *buffer;
+ FILE *fpin;
+ Eina_Bool found_malformed_lines = FALSE;
+ Searchengine *new;
+ URIHandler *newhandler;
+
+ if (access(config, F_OK) != 0)
+ return FILE_NOT_FOUND;
+
+ fpin = fopen(config, "r");
+ if (fpin == NULL)
+ return READING_FAILED;
+
+ while (fgets(s, 254, fpin)) {
+ linum++;
+ /*
+ * ignore lines that begin with #, / and such
+ */
+ if (!isalpha(s[0]))
+ continue;
+ t = strlen(s);
+ s[t - 1] = '\0';
+ if (strncmp(s, "searchengine", 12) == 0) {
+ buffer = (s + 12);
+ while (buffer[0] == ' ')
+ buffer++;
+ /* split line at whitespace */
+ index = split_string_at_whitespace(buffer);
+ if (index < 0 || buffer[0] == '\0' || buffer[index] == '\0'
+ || !sanity_check_search_url(buffer+index)) {
+ fprintf(stderr, "searchengines: syntax error on line %d\n", linum);
+ found_malformed_lines = TRUE;
+ continue;
+ }
+ new = malloc(sizeof(Searchengine));
+ if (new == NULL) {
+ fprintf(stderr, "Memory exhausted while loading search engines.\n");
+ exit(EXIT_FAILURE);
+ }
+ new->handle = strdup(buffer);
+ new->uri = strdup(buffer+index);
+ dynamic_searchengines = eina_list_prepend(dynamic_searchengines, new);
+ } else if (strncmp(s, "handler", 7) == 0) {
+ buffer = (s + 7);
+ while (buffer[0] == ' ')
+ buffer++;
+ /* split line at whitespace */
+ index = split_string_at_whitespace(buffer);
+ if (index < 0 || buffer[0] == '\0' || buffer[index] == '\0'
+ || !sanity_check_search_url(buffer+index)) { /* this sanity check is also valid for handler definitions */
+ fprintf(stderr, "URI handlers: syntax error on line %d\n", linum);
+ found_malformed_lines = TRUE;
+ continue;
+ }
+ newhandler = malloc(sizeof(URIHandler));
+ if (newhandler == NULL) {
+ fprintf(stderr, "Memory exhausted while loading uri handlers.\n");
+ exit(EXIT_FAILURE);
+ }
+ newhandler->handle = strdup(buffer);
+ newhandler->handler = strdup(buffer+index);
+ dynamic_uri_handlers = eina_list_prepend(dynamic_uri_handlers, newhandler);
+ } else {
+ if (!process_line(s, data))
+ found_malformed_lines = TRUE;
+ }
+ }
+ fclose(fpin);
+ return found_malformed_lines ? SYNTAX_ERROR : SUCCESS;
+}
+
+void make_uri_handlers_list(URIHandler *uri_handlers, int length)
+{
+ int i;
+ for (i = 0; i < length; i++, uri_handlers++) {
+ dynamic_uri_handlers = eina_list_prepend(dynamic_uri_handlers, uri_handlers);
+ }
+}
+
+Eina_Bool
+open_handler(char *uri) {
+ /*
+ char *argv[64];
+ char *p = NULL, *arg, arg_temp[MAX_SETTING_SIZE], *temp, temp2[MAX_SETTING_SIZE] = "", *temp3;
+ int j;
+ Eina_List *l;
+
+ p = strchr(uri, ':');
+ if (p) {
+ if (dynamic_uri_handlers != NULL) {
+ for (l = dynamic_uri_handlers; l; l = eina_list_next(l)) {
+ URIHandler *s = (URIHandler *)l->data;
+ if (strlen(uri) >= strlen(s->handle) && strncmp(s->handle, uri, strlen(s->handle)) == 0) {
+ if (strlen(s->handler) > 0) {
+ arg = (uri + strlen(s->handle));
+ strncpy(temp2, s->handler, MAX_SETTING_SIZE);
+ temp = strtok(temp2, " ");
+ j = 0;
+ while (temp != NULL) {
+ if (strstr(temp, "%s")) {
+ temp3 = temp;
+ memset(arg_temp, 0, MAX_SETTING_SIZE);
+ while (strncmp(temp3, "%s", 2) != 0) {
+ strncat(arg_temp, temp3, 1);
+ temp3++;
+ }
+ strcat(arg_temp, arg);
+ temp3++;
+ temp3++;
+ strcat(arg_temp, temp3);
+ argv[j] = arg_temp;
+ } else {
+ argv[j] = temp;
+ }
+ temp = strtok(NULL, " ");
+ j++;
+ }
+ argv[j] = NULL;
+ // g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL);
+ }
+ return TRUE;
+ }
+ }
+ }
+ }
+ */
+ return FALSE;
+}
+
+void
+parse(const char *buf)
+{
+ int r;
+ jsmn_parser p;
+ jsmntok_t tok[10];
+
+ jsmn_init(&p);
+ r = jsmn_parse(&p, buf, tok, 10);
+
+ for (int i = 0; i < 10; i++) {
+ // printf("token %i starts @ %i, ends @ %i, size %i, type %i\n", i, tok[i].start, tok[i].end, tok[i].size, tok[i].type);
+ char *tmp = strndup(buf + tok[i].start, tok[i].end - tok[i].start);
+ // printf("token %i = %s\n", i, tmp);
+ free(tmp);
+ }
+
+ // printf("parser pos is %i\n", p.pos);
+
+}
+
+void
+userscript_hooks_start(const char *uri)
+{
+ // const char *js;
+
+ printf("userscript_hooks_start() uri = %s\n", uri);
+
+ char *base_dir = "/home/kyle/.config/evi/userscript/";
+ // Eina_List *files_to_parse;
+ Eina_List *l;
+ Eina_List *userscript_dir = ecore_file_ls(base_dir);
+ char *path;
+ char full_path[1024] = "";
+
+ EINA_LIST_FOREACH(userscript_dir, l, path) {
+
+ memset(full_path, 0, 1024);
+ strcat(full_path, base_dir);
+ strcat(full_path, path);
+
+ if (ecore_file_is_dir(full_path)) {
+ strcat(full_path, "/manifest.json");
+
+ if (ecore_file_can_read(full_path)) {
+
+ printf("parsing %s ...\n", full_path);
+
+ FILE *fp = fopen(full_path, "r");
+ fseek(fp, 0L, SEEK_END);
+ int len = ftell(fp);
+ char *buf = malloc(len);
+ rewind(fp);
+ fread(buf, len, 1, fp);
+ fclose(fp);
+
+ parse(buf);
+
+ free(buf);
+ // files_to_parse = eina_list_append(files_to_parse, full_path);
+ // printf("would have parsed %s\n", full_path);
+ }
+ }
+ }
+}
+
+void
+userscript_hooks_end(const char *uri)
+{
+}
+
diff --git a/utilities.h b/utilities.h
@@ -0,0 +1,43 @@
+/*
+ (c) 2009 by Leon Winter
+ (c) 2009-2012 by Hannes Schueller
+ (c) 2009-2010 by Matto Fransen
+ (c) 2010-2011 by Hans-Peter Deifel
+ (c) 2010-2011 by Thomas Adam
+ see LICENSE file
+*/
+
+/* config file */
+#define RCFILE "%s/evi/evirc", config_base
+
+/* max entries in command history */
+#define COMMANDHISTSIZE 50
+
+char *strdup_printf(const char *, ...);
+enum ConfigFileError read_rcfile(const char *config, void *);
+void save_command_history(char *line, void *);
+// Eina_Bool process_save_qmark(const char *bm, WebKitWebView *webview);
+KeyList *make_keyslist(void);
+Eina_Bool parse_colour(char *color);
+Eina_Bool mappings(const Arg *, void *);
+Eina_Bool process_mapping(char *keystring, int maprecord, char *cmd);
+Eina_Bool process_map_line(char *line);
+Eina_Bool changemapping(Key *search_key, int maprecord, char *cmd);
+Eina_Bool process_line(const char *line, void *);
+Eina_Bool process_line_arg(const Arg *, void *);
+Eina_Bool build_taglist(const Arg *arg, FILE *f);
+void set_error(const char *error, void *);
+void give_feedback(const char *feedback, void *);
+Listelement * complete_list(const char *, const int, Listelement *, void *);
+Listelement * add_list(const char *element, Listelement *elementlist);
+int count_list(Listelement *elementlist);
+void free_list(Listelement *elementlist);
+char * search_word(int whichword);
+void userscript_hooks_start(const char*);
+void userscript_hooks_end(const char*);
+
+char *find_uri_for_searchengine(const char *handle);
+void make_searchengines_list(Searchengine *searchengines, int length);
+void make_uri_handlers_list(URIHandler *uri_handlers, int length);
+Eina_Bool open_handler(char *uri);
+
diff --git a/viking.h b/viking.h
@@ -0,0 +1,197 @@
+/*
+ (c) 2009 by Leon Winter
+ (c) 2009-2012 by Hannes Schueller
+ (c) 2009-2010 by Matto Fransen
+ (c) 2010-2011 by Hans-Peter Deifel
+ (c) 2010-2011 by Thomas Adam
+ see LICENSE file
+*/
+
+/* macros */
+#define LENGTH(x) (sizeof(x)/sizeof(x[0]))
+
+/* enums */
+enum { ModeNormal, ModePassThrough, ModeSendKey, ModeInsert, ModeHints }; /* modes */
+enum { TargetCurrent, TargetNew }; /* target */
+/* bitmask,
+ 1 << 0: 0 = jumpTo, 1 = scroll
+ 1 << 1: 0 = top/down, 1 = left/right
+ 1 << 2: 0 = top/left, 1 = bottom/right
+ 1 << 3: 0 = paging/halfpage, 1 = line
+ 1 << 4: 0 = paging, 1 = halfpage aka buffer
+*/
+enum { ScrollJumpTo, ScrollMove };
+enum { OrientationVert, OrientationHoriz = (1 << 1) };
+enum { DirectionTop,
+ DirectionBottom = (1 << 2),
+ DirectionLeft = OrientationHoriz,
+ DirectionRight = OrientationHoriz | (1 << 2)
+};
+enum { UnitPage,
+ UnitLine = (1 << 3),
+ UnitBuffer = (1 << 4)
+};
+/* bitmask:
+ 1 << 0: 0 = Reload/Cancel 1 = Forward/Back
+ Forward/Back:
+ 1 << 1: 0 = Forward 1 = Back
+ Reload/Cancel:
+ 1 << 1: 0 = Cancel 1 = Force/Normal Reload
+ 1 << 2: 0 = Force Reload 1 = Reload
+*/
+enum { NavigationForwardBack = 1, NavigationReloadActions = (1 << 1) };
+enum { NavigationCancel,
+ NavigationBack = (NavigationForwardBack | 1 << 1),
+ NavigationForward = (NavigationForwardBack),
+ NavigationReload = (NavigationReloadActions | 1 << 2),
+ NavigationForceReload = NavigationReloadActions
+};
+/* bitmask:
+ 1 << 1: ClipboardPrimary (X11)
+ 1 << 2: ClipboardGTK
+ 1 << 3: SourceURL
+ 1 << 4: SourceSelection
+*/
+enum { ClipboardPrimary = 1 << 1, ClipboardGTK = 1 << 2 };
+enum { SourceSelection = 1 << 4, SourceURL = 1 << 3 };
+/* bitmask:
+ 1 << 0: 0 = ZoomReset 1 = ZoomIn/Out
+ 1 << 1: 0 = ZoomOut 1 = ZoomIn
+ 1 << 2: 0 = TextZoom 1 = FullContentZoom
+*/
+enum { ZoomReset,
+ ZoomOut,
+ ZoomIn = ZoomOut | (1 << 1)
+};
+enum { ZoomText, ZoomFullContent = (1 << 2) };
+/* bitmask:
+ 0 = Info, 1 = Warning, 2 = Error
+ 1 << 2: 0 = AutoHide 1 = NoAutoHide
+ relevant for script:
+ 1 << 3: 1 = Silent (no echo)
+*/
+enum { Info, Warning, Error, NoAutoHide = 1 << 2, Silent = 1 << 3 };
+enum { NthSubdir, Rootdir };
+enum { InsertCurrentURL = 1 };
+enum { Increment, Decrement };
+/* bitmask:
+ 1 << 0: 0 = DirectionNext 1 = DirectionPrev (Relative)
+ 1 << 0: 0 = DirectionBackwards 1 = DirectionForward (Absolute)
+
+ 1 << 1: 0 = DirectionRelative 1 = DirectionAbsolute
+
+ 1 << 2: 0 = CaseInsensitive 1 = CaseSensitive
+ 1 << 3: 0 = No Wrapping 1 = Wrapping
+*/
+enum { DirectionRelative, DirectionAbsolute = 1 << 1 };
+enum { DirectionNext, DirectionPrev, HideCompletion };
+enum { DirectionBackwards = DirectionAbsolute, DirectionForward =
+ (1 << 0) | DirectionAbsolute };
+enum { CaseInsensitive, CaseSensitive = 1 << 2 };
+enum { Wrapping = 1 << 3 };
+enum { HideInspector, ShowInspector };
+
+/* structs here */
+typedef struct {
+ int i;
+ char *s;
+} Arg;
+
+typedef struct {
+ const char *mask;
+ char modkey;
+ const char *key;
+ Eina_Bool(*func) (const Arg * func, void *);
+ Arg arg;
+} Key;
+
+typedef struct {
+ void *next;
+ Key Element;
+} KeyList;
+
+typedef struct {
+ const char *mask;
+ const char *modkey;
+ unsigned int button;
+ Eina_Bool(*func) (const Arg *, void *);
+ const Arg arg;
+} Mouse;
+
+typedef struct {
+ char *name;
+ char (*var);
+ char *webkit;
+ Eina_Bool intval;
+ Eina_Bool boolval;
+ Eina_Bool colourval;
+ Eina_Bool reload;
+} Setting;
+
+typedef struct {
+ char *cmd;
+ Eina_Bool(*func) (const Arg *, void *);
+ const Arg arg;
+} Command;
+
+typedef struct {
+ char *handle;
+ char *uri;
+} Searchengine;
+
+typedef struct {
+ char *handle;
+ char *handler;
+} URIHandler;
+
+struct map_pair {
+ char *line;
+ char what[20];
+ char value[240];
+} my_pair;
+
+typedef struct {
+ void *next;
+ char element[255];
+} Listelement;
+
+enum ConfigFileError {
+ SUCCESS = 0,
+ FILE_NOT_FOUND = -1,
+ READING_FAILED = -2,
+ SYNTAX_ERROR = -3
+};
+
+
+/* constants */
+#define MOUSE_BUTTON_1 1
+#define MOUSE_BUTTON_2 2
+#define MOUSE_BUTTON_3 3
+#define MOUSE_BUTTON_4 4
+#define MOUSE_BUTTON_5 5
+#define BUFFERSIZE 255
+#define MAXTAGSIZE 200
+
+/* bookmarks */
+#define BOOKMARKS_STORAGE_FILENAME "%s/vimprobable/bookmarks", ad->config_base
+
+/* quickmarks */
+#define QUICKMARK_FILE "%s/vimprobable/quickmarks", ad->config_base
+
+/* history */
+#define HISTORY_MAX_ENTRIES 1000
+#define HISTORY_STORAGE_FILENAME "%s/vimprobable/history", ad->config_base
+#define CLOSED_URL_FILENAME "%s/vimprobable/closed", ad->config_base
+
+/* Command size */
+#define COMMANDSIZE 1024
+
+/* maximum size of internal string variable handled by :set
+ * if you set this to anything lower than 8, colour values
+ * will stop working */
+#define MAX_SETTING_SIZE 1024
+
+/* completion list size */
+#define MAX_LIST_SIZE 40
+
+