Prepare for release 1.3.1.
[xombrero.git] / inputfocus.c
blobeb468410ba7b1d1c6b82269a3461437ed4492b55
1 /*
2 * Copyright (c) 2011 Marco Peereboom <marco@peereboom.us>
3 * Copyright (c) 2012 Josh Rickmar <jrick@devio.us>
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 #include <xombrero.h>
20 #if WEBKIT_CHECK_VERSION(1, 5, 0)
21 /* we got the DOM API we need */
23 void
24 focus_body(WebKitDOMDocument *doc)
26 WebKitDOMNodeList *body = NULL;
27 WebKitDOMNode *n;
28 int i;
30 body = webkit_dom_document_get_elements_by_tag_name(doc, "body");
31 for (i = 0; i < webkit_dom_node_list_get_length(body); ++i) {
32 n = webkit_dom_node_list_item(body, i);
33 webkit_dom_element_focus((WebKitDOMElement *)n);
34 #if WEBKIT_CHECK_VERSION(1, 8, 0)
35 webkit_dom_html_element_click((WebKitDOMHTMLElement *)n);
36 #endif
37 break;
41 int
42 node_is_valid_entry(WebKitDOMNode *n)
44 if (n == NULL)
45 return (FALSE);
47 if (WEBKIT_DOM_IS_HTML_INPUT_ELEMENT(n) &&
48 webkit_dom_html_input_element_check_validity(
49 (WebKitDOMHTMLInputElement *)n))
50 return (TRUE);
51 if (WEBKIT_DOM_IS_HTML_TEXT_AREA_ELEMENT(n) &&
52 webkit_dom_html_text_area_element_check_validity(
53 (WebKitDOMHTMLTextAreaElement *)n))
54 return (TRUE);
56 return (FALSE);
59 int
60 focus_input_document(struct tab *t, WebKitDOMDocument *doc)
62 WebKitDOMNodeList *input = NULL, *textarea = NULL;
63 WebKitDOMNode *n;
64 char *es;
65 int i, rv = 0 /* not found */;
67 WebKitDOMHTMLTextAreaElement *ta;
68 WebKitDOMHTMLInputElement *in;
70 /* we are deliberately ignoring tab index! */
72 /* try input first */
73 input = webkit_dom_document_get_elements_by_tag_name(doc, "input");
74 for (i = 0; i < webkit_dom_node_list_get_length(input); i++) {
75 n = webkit_dom_node_list_item(input, i);
76 in = (WebKitDOMHTMLInputElement*)n;
77 g_object_get(G_OBJECT(in), "type", &es, (char *)NULL);
78 if ((g_strcmp0("text", es) && g_strcmp0("password",es)) ||
79 webkit_dom_html_input_element_get_disabled(in)) {
80 /* skip not text */
81 g_free(es);
82 continue;
84 webkit_dom_element_focus((WebKitDOMElement*)in);
85 #if WEBKIT_CHECK_VERSION(1, 8, 0)
86 webkit_dom_html_element_click((WebKitDOMHTMLElement*)in);
87 #endif
88 g_free(es);
89 rv = 1; /* found */
90 goto done;
93 /* now try textarea */
94 textarea = webkit_dom_document_get_elements_by_tag_name(doc, "textarea");
95 for (i = 0; i < webkit_dom_node_list_get_length(textarea); i++) {
96 n = webkit_dom_node_list_item(textarea, i);
97 ta = (WebKitDOMHTMLTextAreaElement*)n;
98 if (webkit_dom_html_text_area_element_get_disabled(ta)) {
99 /* it is hidden so skip */
100 continue;
102 webkit_dom_element_focus((WebKitDOMElement*)ta);
103 #if WEBKIT_CHECK_VERSION(1, 8, 0)
104 webkit_dom_html_element_click((WebKitDOMHTMLElement*)ta);
105 #endif
106 rv = 1; /* found */
107 goto done;
109 done:
110 if (input)
111 g_object_unref(input);
112 if (textarea)
113 g_object_unref(textarea);
115 return (rv);
118 char *
119 get_element_text(WebKitDOMNode *n)
121 if (WEBKIT_DOM_IS_HTML_INPUT_ELEMENT(n))
122 return (g_strdup(webkit_dom_html_input_element_get_value(
123 (WebKitDOMHTMLInputElement *)n)));
124 else if (WEBKIT_DOM_IS_HTML_TEXT_AREA_ELEMENT(n))
125 return (g_strdup(webkit_dom_html_text_area_element_get_value(
126 (WebKitDOMHTMLTextAreaElement *)n)));
127 return (NULL);
131 focus_input(struct tab *t)
133 WebKitDOMDocument *doc;
134 WebKitDOMNode *n;
135 WebKitDOMNodeList *fl = NULL, *ifl = NULL;
136 WebKitDOMElement *a;
137 int i, fl_count, ifl_count, rv = 0; /* not found */
139 WebKitDOMHTMLFrameElement *frame;
140 WebKitDOMHTMLIFrameElement *iframe;
143 * Here is what we are doing:
145 * If a textbox is already focused, leave it alone.
147 * Try the tab's previous active entry, for example if it was set by
148 * some javascript when the page loaded.
150 * See if we got frames or iframes
152 * if we do focus on input or textarea in frame or in iframe
154 * if we find nothing or there are no frames focus on first input or
155 * text area
158 doc = webkit_web_view_get_dom_document(t->wv);
160 /* try current active element */
161 a = webkit_dom_html_document_get_active_element(
162 (WebKitDOMHTMLDocument*)doc);
163 if (node_is_valid_entry((WebKitDOMNode *)a)) {
164 rv = 1; /* found */
165 goto done;
168 /* try previous active element */
169 if (node_is_valid_entry((WebKitDOMNode *)t->active)) {
170 webkit_dom_element_focus((WebKitDOMElement*)t->active);
171 #if WEBKIT_CHECK_VERSION(1, 8, 0)
172 webkit_dom_html_element_click((WebKitDOMHTMLElement*)t->active);
173 #endif
174 rv = 1; /* found */
175 goto done;
176 } else {
177 if (t->active)
178 g_object_unref(t->active);
179 t->active = NULL;
180 if (t->active_text) {
181 g_free(t->active_text);
182 t->active_text = NULL;
186 /* get frames */
187 fl = webkit_dom_document_get_elements_by_tag_name(doc, "frame");
188 fl_count = webkit_dom_node_list_get_length(fl);
190 /* get iframes */
191 ifl = webkit_dom_document_get_elements_by_tag_name(doc, "iframe");
192 ifl_count = webkit_dom_node_list_get_length(ifl);
194 /* walk frames and look for a text input */
195 for (i = 0; i < fl_count; i++) {
196 n = webkit_dom_node_list_item(fl, i);
197 frame = (WebKitDOMHTMLFrameElement*)n;
198 doc = webkit_dom_html_frame_element_get_content_document(frame);
200 if (focus_input_document(t, doc)) {
201 rv = 1; /* focus */
202 goto done;
206 /* walk iframes and look for a text input */
207 for (i = 0; i < ifl_count; i++) {
208 n = webkit_dom_node_list_item(ifl, i);
209 iframe = (WebKitDOMHTMLIFrameElement*)n;
210 doc = webkit_dom_html_iframe_element_get_content_document(iframe);
212 if (focus_input_document(t, doc)) {
213 rv = 1; /* found */
214 goto done;
218 /* if we made it here nothing got focused so use normal heuristic */
219 if (focus_input_document(t, webkit_web_view_get_dom_document(t->wv)))
220 rv = 1; /* found */
222 done:
223 if (fl)
224 g_object_unref(fl);
225 if (ifl)
226 g_object_unref(ifl);
228 return (rv);
232 dom_is_input(struct tab *t, char **text)
234 WebKitDOMDocument *doc;
235 WebKitDOMElement *a;
236 WebKitDOMHTMLElement *aa;
237 WebKitDOMHTMLObjectElement *object;
239 WebKitDOMHTMLFrameElement *frame;
240 WebKitDOMHTMLIFrameElement *iframe;
242 /* proof positive that OO is stupid */
244 doc = webkit_web_view_get_dom_document(t->wv);
246 /* unwind frames and iframes until the cows come home */
247 for (;;) {
248 a = webkit_dom_html_document_get_active_element(
249 (WebKitDOMHTMLDocument*)doc);
250 if (a == NULL)
251 return (0);
253 frame = (WebKitDOMHTMLFrameElement *)a;
254 if (WEBKIT_DOM_IS_HTML_FRAME_ELEMENT(frame)) {
255 doc = webkit_dom_html_frame_element_get_content_document(
256 frame);
257 continue;
260 iframe = (WebKitDOMHTMLIFrameElement *)a;
261 if (WEBKIT_DOM_IS_HTML_IFRAME_ELEMENT(iframe)) {
262 doc = webkit_dom_html_iframe_element_get_content_document(
263 iframe);
264 continue;
267 object = (WebKitDOMHTMLObjectElement *)a;
268 if (WEBKIT_DOM_IS_HTML_OBJECT_ELEMENT(object)) {
269 doc = webkit_dom_html_object_element_get_content_document(
270 object);
271 continue;
275 * I think this is a total hack because this property isn't
276 * set for textareas or input however, it is set for jquery
277 * textareas that do rich text. Since this works around issues
278 * in RT we'll simply keep it!
280 * This might break some other stuff but for now it helps.
282 aa = (WebKitDOMHTMLElement*)a;
283 if (WEBKIT_DOM_IS_HTML_ELEMENT(aa) &&
284 webkit_dom_html_element_get_is_content_editable(aa)) {
285 if (t->active == NULL) {
286 t->active = a;
287 g_object_ref(t->active);
289 *text = get_element_text((WebKitDOMNode *)a);
290 if (t->active_text == NULL)
291 t->active_text = g_strdup(*text);
292 return (1);
294 break;
297 if (a == NULL)
298 return (0);
300 if (node_is_valid_entry((WebKitDOMNode *)a)) {
301 if (!node_is_valid_entry((WebKitDOMNode *)t->active)) {
302 if (t->active)
303 g_object_unref(t->active);
304 t->active = NULL;
305 if (t->active_text) {
306 g_free(t->active_text);
307 t->active_text = NULL;
310 if (t->active == NULL) {
311 t->active = a;
312 g_object_ref(t->active);
314 *text = get_element_text((WebKitDOMNode *)a);
315 if (t->active_text == NULL)
316 t->active_text = g_strdup(*text);
317 return (1);
320 return (0);
323 void *
324 input_check_mode(struct tab *t)
326 char *text = NULL;
328 if (dom_is_input(t, &text)) {
329 t->mode = XT_MODE_INSERT;
330 return (t->active);
331 } else
332 return (NULL);
336 command_mode(struct tab *t, struct karg *args)
338 WebKitDOMDocument *doc;
339 WebKitDOMElement *a;
341 if (args->i == XT_MODE_COMMAND) {
342 doc = webkit_web_view_get_dom_document(t->wv);
343 a = webkit_dom_html_document_get_active_element(
344 (WebKitDOMHTMLDocument *)doc);
345 if (a) {
346 webkit_dom_element_blur(a);
347 focus_body(doc);
349 t->mode = XT_MODE_COMMAND;
350 } else if (args->i == XT_MODE_INSERT && focus_input(t))
351 t->mode = XT_MODE_INSERT;
352 else if (args->i == XT_MODE_HINT || args->i == XT_MODE_PASSTHROUGH)
353 t->mode = args->i;
355 if (!node_is_valid_entry((WebKitDOMNode *)t->active)) {
356 if (t->active)
357 g_object_unref(t->active);
358 t->active = NULL;
359 if (t->active_text) {
360 g_free(t->active_text);
361 t->active_text = NULL;
365 return (XT_CB_HANDLED);
368 void
369 input_autofocus(struct tab *t)
371 struct karg args = {0};
372 char *text = NULL;
374 if (autofocus_onload &&
375 t->tab_id == gtk_notebook_get_current_page(notebook)) {
376 if (focus_input(t))
377 t->mode = XT_MODE_INSERT;
378 else
379 t->mode = XT_MODE_COMMAND;
380 } else {
381 if (dom_is_input(t, &text)) {
382 if (text != NULL && g_strcmp0(text, t->active_text))
383 args.i = XT_MODE_INSERT;
384 else
385 args.i = XT_MODE_COMMAND;
386 } else
387 args.i = XT_MODE_COMMAND;
388 command_mode(t, &args);
391 if (text)
392 g_free(text);
394 #else /* WEBKIT_CHECK_VERSION */
395 /* incomplete DOM API */
398 * XXX
399 * note that we can't check the return value of run_script so we
400 * have to assume that the command worked; this may leave you in
401 * insertmode when in fact you shouldn't be
403 void
404 input_autofocus(struct tab *t)
406 if (autofocus_onload &&
407 t->tab_id == gtk_notebook_get_current_page(notebook)) {
408 run_script(t, "hints.focusInput();");
409 t->mode = XT_MODE_INSERT;
410 } else {
411 run_script(t, "hints.clearFocus();");
412 t->mode = XT_MODE_COMMAND;
416 void *
417 input_check_mode(struct tab *t)
419 return (NULL);
423 command_mode(struct tab *t, struct karg *args)
425 if (args->i == XT_MODE_COMMAND) {
426 run_script(t, "hints.clearFocus();");
427 t->mode = XT_MODE_COMMAND;
428 } else {
429 run_script(t, "hints.focusInput();");
430 t->mode = XT_MODE_INSERT;
433 return (XT_CB_HANDLED);
435 #endif