summaryrefslogtreecommitdiff
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
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.
-rw-r--r--Makefile7
-rw-r--r--config.mk1
-rw-r--r--src/dom.c85
-rw-r--r--src/dom.h6
-rw-r--r--src/hint.js386
-rw-r--r--src/hints.c519
-rw-r--r--src/hints.h33
-rwxr-xr-xsrc/js2h.sh12
-rw-r--r--src/main.c6
-rw-r--r--src/main.h10
-rw-r--r--src/setting.c2
11 files changed, 515 insertions, 552 deletions
diff --git a/Makefile b/Makefile
index 4f032b7..b088b3c 100644
--- a/Makefile
+++ b/Makefile
@@ -12,6 +12,11 @@ options:
@echo "CPPFLAGS = $(CPPFLAGS)"
@echo "LDFLAGS = $(LDFLAGS)"
+src/hints.o: src/hint.js.h
+src/hint.js.h: src/hint.js
+ @echo "minify $<"
+ @cat $< | src/js2h.sh > $@
+
$(TARGET): $(OBJ)
@echo "$(CC) $@"
@$(CC) $(OBJ) -o $(TARGET) $(LDFLAGS)
@@ -44,7 +49,7 @@ uninstall:
clean:
@$(MAKE) -C doc clean
- $(RM) $(OBJ) $(DOBJ) $(TARGET) $(DTARGET)
+ $(RM) $(OBJ) $(DOBJ) $(TARGET) $(DTARGET) src/hint.js.h
dist: distclean
@echo "Creating tarball."
diff --git a/config.mk b/config.mk
index a3ad6a4..78b6638 100644
--- a/config.mk
+++ b/config.mk
@@ -31,6 +31,7 @@ CFLAGS += -std=c99
CFLAGS += -pedantic
CFLAGS += -Wmissing-declarations
CFLAGS += -Wmissing-parameter-type
+CFLAGS += -Wno-overlength-strings
#CFLAGS += -Wstrict-prototypes
LDFLAGS += $(shell pkg-config --libs $(LIBS)) -lX11 -lXext
diff --git a/src/dom.c b/src/dom.c
index 9ebba04..8bccebd 100644
--- a/src/dom.c
+++ b/src/dom.c
@@ -40,76 +40,6 @@ void dom_check_auto_insert(void)
}
}
-void dom_element_set_style(Element* element, const char* format, ...)
-{
- va_list args;
- va_start(args, format);
- char* value = g_strdup_vprintf(format, args);
- CssDeclaration* css = webkit_dom_element_get_style(element);
- if (css) {
- webkit_dom_css_style_declaration_set_css_text(css, value, NULL);
- }
- g_free(value);
-}
-
-void dom_element_style_set_property(Element* element, const char* property, const char* style)
-{
- CssDeclaration* css = webkit_dom_element_get_style(element);
- if (css) {
- webkit_dom_css_style_declaration_set_property(css, property, style, "", NULL);
- }
-}
-
-gboolean dom_element_is_visible(Window* win, Element* element)
-{
- if (webkit_dom_html_element_get_hidden(WEBKIT_DOM_HTML_ELEMENT(element))) {
- return FALSE;
- }
-
- CssDeclaration* style = webkit_dom_dom_window_get_computed_style(win, element, "");
- if (style_compare_property(style, "display", "none")) {
- return FALSE;
- }
- if (style_compare_property(style, "visibility", "hidde")) {
- return FALSE;
- }
-
- return TRUE;
-}
-
-DomBoundingRect dom_elemen_get_bounding_rect(Element* element)
-{
- DomBoundingRect rect;
- rect.left = 0;
- rect.top = 0;
- for (Element* e = element; e; e = webkit_dom_element_get_offset_parent(e)) {
- rect.left += webkit_dom_element_get_offset_left(e);
- rect.top += webkit_dom_element_get_offset_top(e);
- }
- rect.right = rect.left + webkit_dom_element_get_offset_width(element);
- rect.bottom = rect.top + webkit_dom_element_get_offset_height(element);
-
- return rect;
-}
-
-void dom_dispatch_mouse_event(Document* doc, Element* element, char* type, gushort button)
-{
- Event* event = webkit_dom_document_create_event(doc, "MouseEvents", NULL);
- webkit_dom_mouse_event_init_mouse_event(
- WEBKIT_DOM_MOUSE_EVENT(event),
- type,
- TRUE,
- TRUE,
- webkit_dom_document_get_default_view(doc),
- 1, 1, 1, 0, 0,
- FALSE, FALSE, FALSE, FALSE,
- button,
- WEBKIT_DOM_EVENT_TARGET(element)
- );
-
- webkit_dom_node_dispatch_event(WEBKIT_DOM_NODE(element), event, NULL);
-}
-
/**
* Indicates if the given dom element is an editable element like text input,
* password or textarea.
@@ -136,21 +66,6 @@ gboolean dom_is_editable(Element* element)
return FALSE;
}
-/**
- * Retrieves the src or href attribute of the given element.
- */
-char* dom_element_get_source(Element* elem)
-{
- char* url = NULL;
-
- url = webkit_dom_html_anchor_element_get_href(WEBKIT_DOM_HTML_ANCHOR_ELEMENT(elem));
- if (!url) {
- url = webkit_dom_html_image_element_get_src(WEBKIT_DOM_HTML_IMAGE_ELEMENT(elem));
- }
-
- return url;
-}
-
static gboolean dom_auto_insert(Element* element)
{
if (dom_is_editable(element)) {
diff --git a/src/dom.h b/src/dom.h
index e08f462..88897f0 100644
--- a/src/dom.h
+++ b/src/dom.h
@@ -29,7 +29,6 @@
#define Window WebKitDOMDOMWindow
#define NodeList WebKitDOMNodeList
#define Node WebKitDOMNode
-#define Style WebKitDOMCSSStyleDeclaration
#define HtmlElement WebKitDOMHTMLElement
#define Element WebKitDOMElement
#define CssDeclaration WebKitDOMCSSStyleDeclaration
@@ -49,11 +48,6 @@ typedef struct {
} DomBoundingRect;
void dom_check_auto_insert(void);
-void dom_element_set_style(Element* element, const char* format, ...);
-void dom_element_style_set_property(Element* element, const char* property, const char* style);
-gboolean dom_element_is_visible(Window* win, Element* element);
-DomBoundingRect dom_elemen_get_bounding_rect(Element* element);
-void dom_dispatch_mouse_event(Document* doc, Element* element, char* type, gushort button);
gboolean dom_is_editable(Element* element);
char* dom_element_get_source(Element* elem);
diff --git a/src/hint.js b/src/hint.js
new file mode 100644
index 0000000..1738f78
--- /dev/null
+++ b/src/hint.js
@@ -0,0 +1,386 @@
+VimpHints = function Hints(bg, bgf, fg, style) {
+ var config = {
+ maxHints: 200,
+ hintCss: style,
+ hintClass: "__hint",
+ hintClassFocus: "__hint_container",
+ eBg: bg,
+ eBgf: bgf,
+ eFg: fg,
+ };
+
+ var hCont;
+ var curFocusNum = 1;
+ var hints = [];
+ var mode;
+
+ this.create = 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.clear();
+
+ 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;
+
+ hCont = doc.createElement("div");
+ hCont.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.maxHints) {
+ 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.style.left = leftpos - 3 + "px";
+ hint.style.top = toppos - 3 + "px";
+ text = doc.createTextNode(hintCount + 1);
+ hint.appendChild(text);
+
+ hCont.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.eFg;
+ elem.style.background = config.eBg;
+ }
+
+ doc.documentElement.appendChild(hCont);
+
+ /* 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);
+
+ _focus(1);
+ if (hintCount == 1) {
+ /* just one hinted element - might as well follow it */
+ return this.fire(1);
+ }
+ };
+
+ /* set focus to next avaiable hint */
+ this.focusNext = function()
+ {
+ var index = _getHintIdByNumber(curFocusNum);
+
+ if (typeof(hints[index + 1]) != "undefined") {
+ _focus(hints[index + 1].number);
+ } else {
+ _focus(hints[0].number);
+ }
+ };
+
+ /* set focus to previous avaiable hint */
+ this.focusPrev = function()
+ {
+ var index = _getHintIdByNumber(curFocusNum);
+ if (index != 0 && typeof(hints[index - 1].number) != "undefined") {
+ _focus(hints[index - 1].number);
+ } else {
+ _focus(hints[hints.length - 1].number);
+ }
+ };
+
+ /* filters hints matching given number */
+ this.update = function(n)
+ {
+ if (n == 0) {
+ return this.create();
+ }
+ /* 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 _focus(n);
+ }
+ };
+
+ /* remove all hints and set previous style to them */
+ this.clear = 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 = [];
+ hCont.parentNode.removeChild(hCont);
+ window.onkeyup = null;
+ };
+
+ /* fires the modeevent on hint with given number */
+ this.fire = function(n)
+ {
+ var doc, result;
+ if (!n) {
+ var n = curFocusNum;
+ }
+ var hint = _getHintByNumber(n);
+ if (typeof(hint.elem) == "undefined") {
+ return "DONE:";
+ }
+
+ var el = hint.elem;
+ var tag = el.nodeName.toLowerCase();
+
+ this.clear();
+
+ 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:";
+ }
+
+ result = "DONE:";
+ switch (mode) {
+ case "f": _open(el); break;
+ case "F": _openNewWindow(el); break;
+ case "i": _open(el); break;
+ case "I": _openNewWindow(el); break;
+ default: result = "DATA:" + _getElemtSource(el);
+ }
+
+ return result;
+ };
+
+ /* set focus on hint with given number */
+ function _focus(n)
+ {
+ /* reset previous focused hint */
+ var hint = _getHintByNumber(curFocusNum);
+ if (hint !== null) {
+ hint.elem.className = hint.elem.className.replace(config.hintClassFocus, config.hintClass);
+ hint.elem.style.background = config.eBg;
+ _mouseEvent(hint.elem, "mouseout");
+ }
+
+ curFocusNum = n;
+
+ /* mark new hint as focused */
+ var hint = _getHintByNumber(curFocusNum);
+ if (hint !== null) {
+ hint.elem.className = hint.elem.className.replace(config.hintClass, config.hintClassFocus);
+ hint.elem.style.background = config.eBgf;
+ _mouseEvent(hint.elem, "mouseover");
+ }
+ }
+
+ /* 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");
+ }
+ _mouseEvent(elem, "moudedown");
+ _mouseEvent(elem, "click");
+ }
+
+ /* opens given element into new window */
+ function _openNewWindow(elem)
+ {
+ var oldTarget = elem.target;
+
+ /* set target to open in new window */
+ elem.target = "_blank";
+ _mouseEvent(elem, "moudedown");
+ _mouseEvent(elem, "click");
+ elem.target = oldTarget;
+ }
+
+ function _mouseEvent(elem, name)
+ {
+ doc = elem.ownerDocument;
+ view = elem.contentWindow;
+
+ var evObj = doc.createEvent("MouseEvents");
+ evObj.initMouseEvent(name, true, true, view, 0, 0, 0, 0, 0, false, false, false, false, 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;
+ }
+}
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;
-}
diff --git a/src/hints.h b/src/hints.h
index 9264765..a9131c6 100644
--- a/src/hints.h
+++ b/src/hints.h
@@ -22,36 +22,23 @@
#include "main.h"
-#define HINTS_GET_TYPE(type) ((type) & (HINTS_TYPE_LAST))
-#define HINTS_GET_PROCESSING(type) ((type) & ~(HINTS_TYPE_LAST | HINTS_PROCESS | HINTS_TARGET_BLANK))
-
-/*
-bits 1 and 2 form the hint type
-3: 0 = click hint 1 = process source
-4: 0 = open current 1 = open in new window
-all further bits are used for processing types
-*/
+#define HINTS_GET_TYPE(type) ((type) & (HINTS_TYPE_LINK | HINTS_TYPE_IMAGE))
+#define HINTS_GET_PROCESSING(type) ((type) & ~(HINTS_TYPE_LINK | HINTS_TYPE_IMAGE | HINTS_PROCESS | HINTS_TARGET_BLANK))
+
typedef enum {
- HINTS_TYPE_LINK,
- HINTS_TYPE_IMAGE,
- HINTS_TYPE_DEFAULT,
- HINTS_TYPE_FORM,
- HINTS_TYPE_LAST = HINTS_TYPE_FORM,
+ HINTS_TYPE_LINK = (1 << 1),
+ HINTS_TYPE_IMAGE = (1 << 2),
+ HINTS_TYPE_LAST = HINTS_TYPE_IMAGE,
} HintsType;
enum {
- HINTS_CLICK,
- HINTS_PROCESS = (1 << 2)
-};
-
-enum {
- HINTS_TARGET_CURRENT,
- HINTS_TARGET_BLANK = (1 << 3)
+ HINTS_PROCESS = (1 << 3),
+ HINTS_TARGET_BLANK = (1 << 4)
};
typedef enum {
- HINTS_PROCESS_INPUT = (1 << 4),
- HINTS_PROCESS_YANK = (1 << 5),
+ HINTS_PROCESS_INPUT = (1 << 5),
+ HINTS_PROCESS_YANK = (1 << 6),
} HintsProcess;
void hints_init(void);
diff --git a/src/js2h.sh b/src/js2h.sh
new file mode 100755
index 0000000..80ce526
--- /dev/null
+++ b/src/js2h.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+echo -n '#define HINTS_JS "'
+cat $1 | \
+ sed -e "s|/\*[^*]*\*\+\([^/][^*]*\*\+\)*/||g" | \
+ tr '\n\r\t' ' ' | \
+ sed -e "s| \+| |g" \
+ -e "s|^//.*$||" \
+ -e "s| \([-?<>:=(){};+\&\"',\|]\)|\1|g" \
+ -e "s|\([-?<>:=(){};+\&\"',\|]\) |\1|g" \
+ -e 's|"|\\"|g'
+echo -n "\"\n"
diff --git a/src/main.c b/src/main.c
index 40a3378..c54daa4 100644
--- a/src/main.c
+++ b/src/main.c
@@ -123,6 +123,9 @@ static void vp_webview_load_status_cb(WebKitWebView* view, GParamSpec* pspec, gp
vp_set_status(VP_STATUS_NORMAL);
}
+ /* inject the hinting javascript */
+ hints_init();
+
/* run user script file */
vp_run_user_script();
@@ -665,9 +668,6 @@ static void vp_init(void)
/* initialize the keybindings */
keybind_init();
- /* initialize the hints */
- hints_init();
-
/* initialize settings */
setting_init();
diff --git a/src/main.h b/src/main.h
index eb6df36..c93cc7e 100644
--- a/src/main.h
+++ b/src/main.h
@@ -270,10 +270,10 @@ typedef struct {
VpColor comp_bg[VP_COMP_LAST];
PangoFontDescription* comp_font[VP_COMP_LAST];
/* hint style */
- char* hint_bg;
- char* hint_bg_focus;
- char* hint_fg;
- char* hint_style;
+ char* hint_bg;
+ char* hint_bg_focus;
+ char* hint_fg;
+ char* hint_style;
/* status bar */
VpColor status_bg[VP_STATUS_LAST];
VpColor status_fg[VP_STATUS_LAST];
@@ -281,8 +281,6 @@ typedef struct {
} Style;
typedef struct {
- GList* list;
- gulong focusNum;
gulong num;
guint mode;
guint prefixLength;
diff --git a/src/setting.c b/src/setting.c
index 8a8a444..38e6898 100644
--- a/src/setting.c
+++ b/src/setting.c
@@ -113,7 +113,7 @@ static Setting default_settings[] = {
{NULL, "hint-bg", TYPE_CHAR, setting_hint_style, {.s = "#ff0"}},
{NULL, "hint-bg-focus", TYPE_CHAR, setting_hint_style, {.s = "#8f0"}},
{NULL, "hint-fg", TYPE_CHAR, setting_hint_style, {.s = "#000"}},
- {NULL, "hint-style", TYPE_CHAR, setting_hint_style, {.s = "font-family:monospace;font-weight:bold;color:#000;background-color:#fff;margin:0;padding:0px 1px;border:1px solid #444;opacity:0.7;"}},
+ {NULL, "hint-style", TYPE_CHAR, setting_hint_style, {.s = "position:absolute;z-index:100000;font-family:monospace;font-weight:bold;font-size:10px;color:#000;background-color:#fff;margin:0;padding:0px 1px;border:1px solid #444;opacity:0.7;"}},
{NULL, "strict-ssl", TYPE_BOOLEAN, setting_strict_ssl, {.i = 1}},
{NULL, "ca-bundle", TYPE_CHAR, setting_ca_bundle, {.s = "/etc/ssl/certs/ca-certificates.crt"}},
{NULL, "home-page", TYPE_CHAR, setting_home_page, {.s = "https://github.com/fanglingsu/vimp"}},