diff options
author | Daniel Carl <danielcarl@gmx.de> | 2013-02-27 23:53:13 +0100 |
---|---|---|
committer | Daniel Carl <danielcarl@gmx.de> | 2013-03-01 14:31:01 +0100 |
commit | 83cec60d012284c28eb8e8f23aa83dce8c128af2 (patch) | |
tree | a2997402b16a020bebf39f4b30c6f35ba3ae7f8e /src/hints.c | |
parent | 057fff36432ee382ed688733ffa332907a4c5aef (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.c | 519 |
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; -} |