summaryrefslogtreecommitdiff
path: root/src/hints.c
diff options
context:
space:
mode:
authorDaniel Carl <danielcarl@gmx.de>2013-02-27 23:53:13 +0100
committerDaniel Carl <danielcarl@gmx.de>2013-03-01 14:31:01 +0100
commit83cec60d012284c28eb8e8f23aa83dce8c128af2 (patch)
treea2997402b16a020bebf39f4b30c6f35ba3ae7f8e /src/hints.c
parent057fff36432ee382ed688733ffa332907a4c5aef (diff)
Replaced hinting by javascript hinting.
The previous approach to use the dom api to generate the hints was much slower than the javascript solution. I think the javascript way is also a little bit more flexible and easier to implement. But now we have to concern about data sharing between c-layer an the javascript.
Diffstat (limited to 'src/hints.c')
-rw-r--r--src/hints.c519
1 files changed, 92 insertions, 427 deletions
diff --git a/src/hints.c b/src/hints.c
index f1df38d..2042694 100644
--- a/src/hints.c
+++ b/src/hints.c
@@ -22,469 +22,146 @@
#include "hints.h"
#include "dom.h"
#include "command.h"
+#include "hint.js.h"
+/* TODO use this in hinting script */
#define MAX_HINTS 200
#define HINT_CONTAINER_ID "__hint_container"
#define HINT_CLASS "__hint"
-#define HINT_CONTAINER_STYLE "line-height:1em;"
-#define HINT_STYLE "z-index:100000;position:absolute;left:%lipx;top:%lipx;%s"
-
-typedef struct {
- gulong num;
- Element* elem; /* hinted element */
- char* elemColor; /* element color */
- char* elemBackgroundColor; /* element background color */
- Element* hint; /* numbered hint element */
- Element* container;
-} Hint;
-
-static Element* hints_get_hint_container(Document* doc);
-static void hints_create_for_window(const char* input, Window* win, gulong hintCount);
-static void hints_focus(const gulong num);
-static void hints_fire(const gulong num);
-static void hints_click_fired_hint(guint mode, Element* elem);
-static void hints_process_fired_hint(guint mode, const char* uri);
-static Hint* hints_get_hint_by_number(const gulong num);
-static GList* hints_get_hint_list_by_number(const gulong num);
-static char* hints_get_xpath(const char* input);
+static void hints_run_script(char* js);
+static void hints_fire(void);
static void hints_observe_input(gboolean observe);
static gboolean hints_changed_callback(GtkEditable *entry, gpointer data);
static gboolean hints_keypress_callback(WebKitWebView* webview, GdkEventKey* event);
-static gboolean hints_num_has_prefix(gulong num, gulong prefix);
-
void hints_init(void)
{
- Hints* hints = &vp.hints;
-
- hints->list = NULL;
- hints->focusNum = 0;
- hints->num = 0;
- hints->prefixLength = 0;
+ char* value = NULL;
+ char* error = NULL;
+ vp_eval_script(webkit_web_view_get_main_frame(vp.gui.webview), HINTS_JS, &value, &error);
+ g_free(value);
+ g_free(error);
}
void hints_clear(void)
{
- Hints* hints = &vp.hints;
-
- /* free the list of hints */
- if (hints->list) {
- GList* link;
- for (link = hints->list; link != NULL; link = link->next) {
- Hint* hint = (Hint*)link->data;
-
- /* reset the previous color of the hinted elements */
- dom_element_style_set_property(hint->elem, "color", hint->elemColor);
- dom_element_style_set_property(hint->elem, "background-color", hint->elemBackgroundColor);
-
- /* remove the hint element from hint container */
- Node* parent = webkit_dom_node_get_parent_node(WEBKIT_DOM_NODE(hint->hint));
- webkit_dom_node_remove_child(parent, WEBKIT_DOM_NODE(hint->hint), NULL);
- g_free(hint);
- }
-
- g_list_free(hints->list);
-
- /* use hints_init to unset previous data */
- hints_init();
- }
-
hints_observe_input(FALSE);
+ if (GET_CLEAN_MODE() == VP_MODE_HINTING) {
+ char* js = g_strdup("hints.clear();");
+ char* value = NULL;
+ char* error = NULL;
- /* simulate the mouse out of the last focused hint */
- g_signal_emit_by_name(vp.gui.webview, "hovering-over-link", NULL, NULL);
+ vp_eval_script(webkit_web_view_get_main_frame(vp.gui.webview), js, &value, &error);
+ g_free(value);
+ g_free(error);
+ g_free(js);
+ }
}
void hints_create(const char* input, guint mode, const guint prefixLength)
{
- Hints* hints = &vp.hints;
- Document* doc;
-
- hints_clear();
- hints->mode = mode;
- hints->prefixLength = prefixLength;
+ char* js = NULL;
+ char type;
+ if (GET_CLEAN_MODE() != VP_MODE_HINTING) {
+ Style* style = &core.style;
+ vp.hints.prefixLength = prefixLength;
+ vp.hints.mode = mode;
+ vp.hints.num = 0;
+
+ js = g_strdup_printf(
+ "hints = new VimpHints('%s', '%s', '%s', '%s');",
+ style->hint_bg,
+ style->hint_bg_focus,
+ style->hint_fg,
+ style->hint_style
+ );
+ hints_run_script(js);
+ g_free(js);
- doc = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(vp.gui.webview));
- if (!doc) {
- return;
+ hints_observe_input(TRUE);
}
- hints_create_for_window(input, webkit_dom_document_get_default_view(doc), 0);
- hints_focus(1);
-
- if (g_list_length(hints->list) == 1) {
- /* only one element hinted - we can fire it */
- hints_fire(1);
+ /* convert the mode into the type chare used in the hint script */
+ if (mode & HINTS_PROCESS) {
+ type = 'd';
+ } else if (mode & HINTS_TYPE_IMAGE) {
+ type = (HINTS_TARGET_BLANK & mode) ? 'I' : 'i';
} else {
- /* add event hanlder for inputbox */
- hints_observe_input(TRUE);
+ type = (HINTS_TARGET_BLANK & mode) ? 'F' : 'f';
}
+
+ js = g_strdup_printf("hints.create('%s', '%c');", input ? input : "", type);
+ hints_run_script(js);
+ g_free(js);
}
void hints_update(const gulong num)
{
- Hints* hints = &vp.hints;
- Hint* hint = NULL;
- GList* next = NULL;
- GList* link = hints->list;
-
- if (num == 0) {
- /* recreate the hints */
- hints_create(NULL, hints->mode, hints->prefixLength);
- return;
- }
-
- while (link != NULL) {
- hint = (Hint*)link->data;
- if (!hints_num_has_prefix(hint->num, num)) {
- /* reset the previous color of the hinted elements */
- dom_element_style_set_property(hint->elem, "color", hint->elemColor);
- dom_element_style_set_property(hint->elem, "background-color", hint->elemBackgroundColor);
-
- /* remove the hint element from hint container */
- Node* parent = webkit_dom_node_get_parent_node(WEBKIT_DOM_NODE(hint->hint));
- webkit_dom_node_remove_child(parent, WEBKIT_DOM_NODE(hint->hint), NULL);
- g_free(hint);
-
- /* store next element before remove current */
- next = g_list_next(link);
- hints->list = g_list_remove_link(hints->list, link);
- link = next;
- } else {
- link = g_list_next(link);
- }
- }
- if (g_list_length(hints->list) == 1) {
- hints_fire(num);
- } else {
- hints_focus(num);
- }
+ char* js = g_strdup_printf("hints.update(%lu);", num);
+ hints_run_script(js);
+ g_free(js);
}
void hints_focus_next(const gboolean back)
{
- Hints* hints = &vp.hints;
- Hint* hint = NULL;
- GList* list = hints_get_hint_list_by_number(hints->focusNum);
-
- list = back ? g_list_previous(list) : g_list_next(list);
- if (list != NULL) {
- hint = (Hint*)list->data;
- } else {
- /* if we reached begin or end start on the opposite side */
- list = back ? g_list_last(hints->list) : g_list_first(hints->list);
- hint = (Hint*)list->data;
- }
- hints_focus(hint->num);
+ char* js = g_strdup(back ? "hints.focusPrev()" : "hints.focusNext();");
+ hints_run_script(js);
+ g_free(js);
}
-/**
- * Retrieves an existing or new created hint container for given document.
- */
-static Element* hints_get_hint_container(Document* doc)
+static void hints_run_script(char* js)
{
- Element* container;
-
- /* first try to find a previous container */
- container = webkit_dom_document_get_element_by_id(doc, HINT_CONTAINER_ID);
+ char* value = NULL;
+ char* error = NULL;
+ int mode = vp.hints.mode;
- if (!container) {
- /* create the hint container element */
- container = webkit_dom_document_create_element(doc, "p", NULL);
- dom_element_set_style(container, HINT_CONTAINER_STYLE);
+ vp_eval_script(webkit_web_view_get_main_frame(vp.gui.webview), js, &value, &error);
+ if (error) {
+ fprintf(stderr, "%s\n", error);
+ g_free(error);
- webkit_dom_html_element_set_id(WEBKIT_DOM_HTML_ELEMENT(container), HINT_CONTAINER_ID);
- }
+ vp_set_mode(VP_MODE_NORMAL, FALSE);
- return container;
-}
-
-static void hints_create_for_window(const char* input, Window* win, gulong hintCount)
-{
- Hints* hints = &vp.hints;
- Element* container = NULL;
- NodeList* list = NULL;
- Document* doc = NULL;
- gulong i, listLength;
-
- doc = webkit_dom_dom_window_get_document(win);
- list = webkit_dom_document_get_elements_by_tag_name(doc, "body");
- if (!list) {
- return;
- }
- Node* body = webkit_dom_node_list_item(list, 0);
-
- WebKitDOMXPathNSResolver* ns_resolver = webkit_dom_document_create_ns_resolver(doc, body);
- if (!ns_resolver) {
return;
}
-
- char* xpath = hints_get_xpath(input);
- WebKitDOMXPathResult* result = webkit_dom_document_evaluate(
- doc, xpath, WEBKIT_DOM_NODE(doc), ns_resolver, 7, NULL, NULL
- );
- g_free(xpath);
-
- if (!result) {
- return;
- }
-
- /* get the bounds */
- gulong win_height = webkit_dom_dom_window_get_inner_height(win);
- gulong win_width = webkit_dom_dom_window_get_inner_width(win);
- gulong minY = webkit_dom_dom_window_get_scroll_y(win);
- gulong minX = webkit_dom_dom_window_get_scroll_x(win);
- gulong maxX = minX + win_width;
- gulong maxY = minY + win_height;
-
- /* create the hint container element */
- container = hints_get_hint_container(doc);
- webkit_dom_node_append_child(body, WEBKIT_DOM_NODE(container), NULL);
-
- listLength = webkit_dom_xpath_result_get_snapshot_length(result, NULL);
-
- for (i = 0; i < listLength && hintCount < MAX_HINTS; i++) {
- /* TODO this cast causes a bug if hinting is started after a link was
- * opened into new window via middle mouse click or right click
- * context menu */
- Element* elem = WEBKIT_DOM_ELEMENT(webkit_dom_xpath_result_snapshot_item(result, i, NULL));
- if (!dom_element_is_visible(win, elem)) {
- continue;
- }
- DomBoundingRect rect = dom_elemen_get_bounding_rect(elem);
- if (rect.left > maxX || rect.right < minX || rect.top > maxY || rect.bottom < minY) {
- continue;
- }
-
- hintCount++;
-
- /* create the hint element */
- Element* hint = webkit_dom_document_create_element(doc, "span", NULL);
- CssDeclaration* css_style = webkit_dom_element_get_style(elem);
-
- Hint* newHint = g_new0(Hint, 1);
- newHint->num = hintCount;
- newHint->elem = elem;
- newHint->elemColor = webkit_dom_css_style_declaration_get_property_value(css_style, "color");
- newHint->elemBackgroundColor = webkit_dom_css_style_declaration_get_property_value(css_style, "background-color");
- newHint->hint = hint;
- hints->list = g_list_append(hints->list, newHint);
-
- gulong left = rect.left - 3;
- gulong top = rect.top - 3;
- dom_element_set_style(hint, HINT_STYLE, left, top, core.style.hint_style);
-
- char* num = g_strdup_printf("%li", newHint->num);
- webkit_dom_html_element_set_inner_text(WEBKIT_DOM_HTML_ELEMENT(hint), num, NULL);
- webkit_dom_html_element_set_class_name(WEBKIT_DOM_HTML_ELEMENT(hint), HINT_CLASS);
- g_free(num);
-
- /* change the style of the hinted element */
- dom_element_style_set_property(newHint->elem, "background-color", core.style.hint_bg);
- dom_element_style_set_property(newHint->elem, "color", core.style.hint_fg);
-
- webkit_dom_node_append_child(WEBKIT_DOM_NODE(container), WEBKIT_DOM_NODE(hint), NULL);
- }
-
- /* call this function for every found frame or iframe too */
- list = webkit_dom_document_get_elements_by_tag_name(doc, "IFRAME");
- listLength = webkit_dom_node_list_get_length(list);
- for (i = 0; i < listLength; i++) {
- Node* iframe = webkit_dom_node_list_item(list, i);
- Window* window = webkit_dom_html_iframe_element_get_content_window(WEBKIT_DOM_HTML_IFRAME_ELEMENT(iframe));
- DomBoundingRect rect = dom_elemen_get_bounding_rect(WEBKIT_DOM_ELEMENT(iframe));
-
- if (rect.left > maxX || rect.right < minX || rect.top > maxY || rect.bottom < minY) {
- continue;
- }
-
- hints_create_for_window(input, window, hintCount);
- }
-}
-
-static void hints_focus(const gulong num)
-{
- Document* doc = NULL;
-
- Hint* hint = hints_get_hint_by_number(vp.hints.focusNum);
- if (hint) {
- /* reset previous focused element */
- dom_element_style_set_property(hint->elem, "background-color", core.style.hint_bg);
-
- doc = webkit_dom_node_get_owner_document(WEBKIT_DOM_NODE(hint->elem));
- dom_dispatch_mouse_event(doc, hint->elem, "mouseout", 0);
- }
-
- hint = hints_get_hint_by_number(num);
- if (hint) {
- /* mark new hint as focused */
- dom_element_style_set_property(hint->elem, "background-color", core.style.hint_bg_focus);
-
- doc = webkit_dom_node_get_owner_document(WEBKIT_DOM_NODE(hint->elem));
- dom_dispatch_mouse_event(doc, hint->elem, "mouseover", 0);
- webkit_dom_element_blur(hint->elem);
-
- const char* tag = webkit_dom_element_get_tag_name(hint->elem);
- if (!g_ascii_strcasecmp(tag, "a")) {
- /* simulate the hovering over the hinted element this is done to show
- * the hinted elements url in the url bar */
- g_signal_emit_by_name(
- vp.gui.webview,
- "hovering-over-link",
- "",
- dom_element_get_source(hint->elem)
- );
- } else {
- /* if hinted element has no url unhover the previous element */
- g_signal_emit_by_name(vp.gui.webview, "hovering-over-link", NULL, NULL);
- }
- }
-
- vp.hints.focusNum = num;
-}
-
-static void hints_fire(const gulong num)
-{
- Hints* hints = &vp.hints;
- Hint* hint = hints_get_hint_by_number(num);
- if (!hint) {
+ if (!value) {
return;
}
- if (dom_is_editable(hint->elem)) {
- webkit_dom_element_focus(hint->elem);
- vp_set_mode(VP_MODE_INSERT, FALSE);
- } else {
- if (hints->mode & HINTS_PROCESS) {
- hints_process_fired_hint(hints->mode, dom_element_get_source(hint->elem));
- } else {
- hints_click_fired_hint(hints->mode, hint->elem);
-
- /* remove the hint filter input and witch to normal mode */
- vp_set_mode(VP_MODE_NORMAL, TRUE);
+ if (!strncmp(value, "DONE:", 5)) {
+ hints_observe_input(FALSE);
+ vp_set_mode(VP_MODE_NORMAL, TRUE);
+ } else if (!strncmp(value, "INSERT:", 7)) {
+ hints_observe_input(FALSE);
+ vp_set_mode(VP_MODE_INSERT, TRUE);
+ } else if (!strncmp(value, "DATA:", 5)) {
+ hints_observe_input(FALSE);
+ HintsProcess type = HINTS_GET_PROCESSING(mode);
+ Arg a = {0};
+ switch (type) {
+ case HINTS_PROCESS_INPUT:
+ a.s = g_strconcat((mode & HINTS_TARGET_BLANK) ? ":tabopen " : ":open ", (value + 5), NULL);
+ command_input(&a);
+ g_free(a.s);
+ break;
+
+ case HINTS_PROCESS_YANK:
+ a.i = COMMAND_YANK_PRIMARY | COMMAND_YANK_SECONDARY;
+ a.s = g_strdup((value + 5));
+ command_yank(&a);
+ g_free(a.s);
+ break;
}
}
- hints_clear();
+ g_free(value);
}
-/**
- * Perform a mouse click to given element.
- */
-static void hints_click_fired_hint(guint mode, Element* elem)
+static void hints_fire(void)
{
- char* target = webkit_dom_element_get_attribute(elem, "target");
- if (mode & HINTS_TARGET_BLANK) { /* open in new window */
- webkit_dom_element_set_attribute(elem, "target", "_blank", NULL);
- } else if (g_strcmp0(target, "_blank") == 0) { /* remove possible target attribute */
- webkit_dom_element_remove_attribute(elem, "target");
- }
-
- /* dispatch click event */
- Document* doc = webkit_dom_node_get_owner_document(WEBKIT_DOM_NODE(elem));
- dom_dispatch_mouse_event(doc, elem, "click", 0);
-
- /* reset previous target attribute */
- if (target && strlen(target)) {
- webkit_dom_element_set_attribute(elem, "target", target, NULL);
- } else {
- webkit_dom_element_remove_attribute(elem, "target");
- }
-}
-
-/**
- * Handle fired hints that are not opened via simulated mouse click.
- */
-static void hints_process_fired_hint(guint mode, const char* uri)
-{
- HintsProcess type = HINTS_GET_PROCESSING(mode);
- Arg a = {0};
- switch (type) {
- case HINTS_PROCESS_INPUT:
- a.s = g_strconcat((mode & HINTS_TARGET_BLANK) ? ":tabopen " : ":open ", uri, NULL);
- command_input(&a);
- g_free(a.s);
- break;
-
- case HINTS_PROCESS_YANK:
- a.i = COMMAND_YANK_PRIMARY | COMMAND_YANK_SECONDARY;
- a.s = g_strdup(uri);
- command_yank(&a);
- g_free(a.s);
- break;
- }
-}
-
-static Hint* hints_get_hint_by_number(const gulong num)
-{
- GList* list = hints_get_hint_list_by_number(num);
- if (list) {
- return (Hint*)list->data;
- }
-
- return NULL;
-}
-
-static GList* hints_get_hint_list_by_number(const gulong num)
-{
- GList* link;
- for (link = vp.hints.list; link != NULL; link = link->next) {
- Hint* hint = (Hint*)link->data;
- /* TODO check if it would be faster to use the sorting of the numbers
- * in the list to get the items */
- if (hint->num == num) {
- return link;
- }
- }
-
- return NULL;
-}
-
-/**
- * Retreives the xpath epression according to current hinting mode and filter
- * input text.
- *
- * The returned string have to be freed.
- */
-static char* hints_get_xpath(const char* input)
-{
- char* xpath = NULL;
-
- switch (HINTS_GET_TYPE(vp.hints.mode)) {
- case HINTS_TYPE_LINK:
- if (input == NULL) {
- xpath = g_strdup(
- "//*[@onclick or @onmouseover or @onmousedown or @onmouseup or @oncommand or @class='lk' or @ role='link'] | "
- "//a[@href] | "
- "//input[not(@type='hidden')] | "
- "//textarea | "
- "//button | "
- "//select | "
- "//area"
- );
- } else {
- xpath = g_strdup_printf(
- "//*[(@onclick or @onmouseover or @onmousedown or @onmouseup or @oncommand or @class='lk' or @role='link') and contains(., '%s')] | "
- "//a[@href and contains(., '%s')] | "
- "//input[not(@type='hidden') and contains(., '%s')] | "
- "//textarea[contains(., '%s')] | "
- "//button[contains(@value, '%s')] | "
- "//select[contains(., '%s')] | "
- "//area[contains(., '%s')]",
- input, input, input, input, input, input, input
- );
- }
- break;
-
- case HINTS_TYPE_IMAGE:
- if (input == NULL) {
- xpath = g_strdup("//img[@src]");
- } else {
- xpath = g_strdup_printf("//img[@src and contains(., '%s')]", input);
- }
- break;
- }
-
- return xpath;
+ hints_observe_input(FALSE);
+ char* js = g_strdup("hints.fire();");
+ hints_run_script(js);
+ g_free(js);
}
static void hints_observe_input(gboolean observe)
@@ -525,7 +202,7 @@ static gboolean hints_keypress_callback(WebKitWebView* webview, GdkEventKey* eve
guint state = CLEAN_STATE_WITH_SHIFT(event);
if (keyval == GDK_Return) {
- hints_fire(hints->focusNum);
+ hints_fire();
return TRUE;
}
if (keyval == GDK_BackSpace && (state & GDK_SHIFT_MASK) && (state & GDK_CONTROL_MASK)) {
@@ -545,15 +222,3 @@ static gboolean hints_keypress_callback(WebKitWebView* webview, GdkEventKey* eve
return FALSE;
}
-
-static gboolean hints_num_has_prefix(gulong num, gulong prefix)
-{
- if (prefix == num) {
- return TRUE;
- }
- if (num >= 10) {
- return hints_num_has_prefix(num / 10, prefix);
- }
-
- return FALSE;
-}