Strip whitespace for config inputs
[xombrero.git] / inputfocus.c
blobe6be51fb4ffef598e52fe634a47cb3c44179018b
1 /*
2 * Copyright (c) 2011 Marco Peereboom <marco@peereboom.us>
3 * Copyright (c) 2012, 2013 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);
159 #if WEBKIT_CHECK_VERSION(2, 0, 0)
160 /* This check is broken on old webkit */
161 if (!WEBKIT_DOM_IS_HTML_DOCUMENT(doc)) {
162 show_oops(t, "%s: DOM node is not a valid HTML document",
163 __func__);
164 goto done;
166 #endif
168 /* try current active element */
169 a = webkit_dom_html_document_get_active_element(
170 (WebKitDOMHTMLDocument*)doc);
171 if (node_is_valid_entry((WebKitDOMNode *)a)) {
172 rv = 1; /* found */
173 goto done;
176 /* try previous active element */
177 if (node_is_valid_entry((WebKitDOMNode *)t->active)) {
178 webkit_dom_element_focus((WebKitDOMElement*)t->active);
179 #if WEBKIT_CHECK_VERSION(1, 8, 0)
180 webkit_dom_html_element_click((WebKitDOMHTMLElement*)t->active);
181 #endif
182 rv = 1; /* found */
183 goto done;
184 } else {
185 if (t->active)
186 g_object_unref(t->active);
187 t->active = NULL;
188 if (t->active_text) {
189 g_free(t->active_text);
190 t->active_text = NULL;
194 /* get frames */
195 fl = webkit_dom_document_get_elements_by_tag_name(doc, "frame");
196 fl_count = webkit_dom_node_list_get_length(fl);
198 /* get iframes */
199 ifl = webkit_dom_document_get_elements_by_tag_name(doc, "iframe");
200 ifl_count = webkit_dom_node_list_get_length(ifl);
202 /* walk frames and look for a text input */
203 for (i = 0; i < fl_count; i++) {
204 n = webkit_dom_node_list_item(fl, i);
205 frame = (WebKitDOMHTMLFrameElement*)n;
206 doc = webkit_dom_html_frame_element_get_content_document(frame);
208 if (focus_input_document(t, doc)) {
209 rv = 1; /* focus */
210 goto done;
214 /* walk iframes and look for a text input */
215 for (i = 0; i < ifl_count; i++) {
216 n = webkit_dom_node_list_item(ifl, i);
217 iframe = (WebKitDOMHTMLIFrameElement*)n;
218 doc = webkit_dom_html_iframe_element_get_content_document(iframe);
220 if (focus_input_document(t, doc)) {
221 rv = 1; /* found */
222 goto done;
226 /* if we made it here nothing got focused so use normal heuristic */
227 if (focus_input_document(t, webkit_web_view_get_dom_document(t->wv)))
228 rv = 1; /* found */
230 done:
231 if (fl)
232 g_object_unref(fl);
233 if (ifl)
234 g_object_unref(ifl);
236 return (rv);
240 dom_is_input(struct tab *t, char **text)
242 WebKitDOMDocument *doc;
243 WebKitDOMElement *a;
244 WebKitDOMHTMLElement *aa;
245 WebKitDOMHTMLObjectElement *object;
247 WebKitDOMHTMLFrameElement *frame;
248 WebKitDOMHTMLIFrameElement *iframe;
250 /* proof positive that OO is stupid */
252 doc = webkit_web_view_get_dom_document(t->wv);
254 /* unwind frames and iframes until the cows come home */
255 for (;;) {
256 #if WEBKIT_CHECK_VERSION(2, 0, 0)
257 if (!WEBKIT_DOM_IS_HTML_DOCUMENT(doc))
258 return (0);
259 #endif
260 a = webkit_dom_html_document_get_active_element(
261 (WebKitDOMHTMLDocument*)doc);
262 if (a == NULL)
263 return (0);
265 frame = (WebKitDOMHTMLFrameElement *)a;
266 if (WEBKIT_DOM_IS_HTML_FRAME_ELEMENT(frame)) {
267 doc = webkit_dom_html_frame_element_get_content_document(
268 frame);
269 continue;
272 iframe = (WebKitDOMHTMLIFrameElement *)a;
273 if (WEBKIT_DOM_IS_HTML_IFRAME_ELEMENT(iframe)) {
274 doc = webkit_dom_html_iframe_element_get_content_document(
275 iframe);
276 continue;
279 object = (WebKitDOMHTMLObjectElement *)a;
280 if (WEBKIT_DOM_IS_HTML_OBJECT_ELEMENT(object)) {
281 doc = webkit_dom_html_object_element_get_content_document(
282 object);
283 continue;
287 * I think this is a total hack because this property isn't
288 * set for textareas or input however, it is set for jquery
289 * textareas that do rich text. Since this works around issues
290 * in RT we'll simply keep it!
292 * This might break some other stuff but for now it helps.
294 aa = (WebKitDOMHTMLElement*)a;
295 if (WEBKIT_DOM_IS_HTML_ELEMENT(aa) &&
296 webkit_dom_html_element_get_is_content_editable(aa)) {
297 if (t->active == NULL) {
298 t->active = a;
299 g_object_ref(t->active);
301 *text = get_element_text((WebKitDOMNode *)a);
302 if (t->active_text == NULL)
303 t->active_text = g_strdup(*text);
304 return (1);
306 break;
309 if (a == NULL)
310 return (0);
312 if (node_is_valid_entry((WebKitDOMNode *)a)) {
313 if (!node_is_valid_entry((WebKitDOMNode *)t->active)) {
314 if (t->active)
315 g_object_unref(t->active);
316 t->active = NULL;
317 if (t->active_text) {
318 g_free(t->active_text);
319 t->active_text = NULL;
322 if (t->active == NULL) {
323 t->active = a;
324 g_object_ref(t->active);
326 *text = get_element_text((WebKitDOMNode *)a);
327 if (t->active_text == NULL)
328 t->active_text = g_strdup(*text);
329 return (1);
332 return (0);
335 void *
336 input_check_mode(struct tab *t)
338 char *text = NULL;
340 if (dom_is_input(t, &text)) {
341 t->mode = XT_MODE_INSERT;
342 return (t->active);
343 } else
344 return (NULL);
348 command_mode(struct tab *t, struct karg *args)
350 WebKitDOMDocument *doc;
351 WebKitDOMElement *a;
353 if (args->i == XT_MODE_COMMAND) {
354 doc = webkit_web_view_get_dom_document(t->wv);
355 #if WEBKIT_CHECK_VERSION(2, 0, 0)
356 if (!WEBKIT_DOM_IS_HTML_DOCUMENT(doc)) {
357 show_oops(t, "%s: DOM node is not a valid HTML "
358 "document", __func__);
359 return (XT_CB_HANDLED);
361 #endif
362 a = webkit_dom_html_document_get_active_element(
363 (WebKitDOMHTMLDocument *)doc);
364 if (a) {
365 webkit_dom_element_blur(a);
366 focus_body(doc);
368 t->mode = XT_MODE_COMMAND;
369 } else if (args->i == XT_MODE_INSERT && focus_input(t))
370 t->mode = XT_MODE_INSERT;
371 else if (args->i == XT_MODE_HINT || args->i == XT_MODE_PASSTHROUGH)
372 t->mode = args->i;
374 if (!node_is_valid_entry((WebKitDOMNode *)t->active)) {
375 if (t->active)
376 g_object_unref(t->active);
377 t->active = NULL;
378 if (t->active_text) {
379 g_free(t->active_text);
380 t->active_text = NULL;
384 return (XT_CB_HANDLED);
387 void
388 input_autofocus(struct tab *t)
390 struct karg args = {0};
391 char *text = NULL;
393 if (autofocus_onload &&
394 t->tab_id == gtk_notebook_get_current_page(notebook)) {
395 if (focus_input(t))
396 t->mode = XT_MODE_INSERT;
397 else
398 t->mode = XT_MODE_COMMAND;
399 } else {
400 if (dom_is_input(t, &text)) {
401 if (text != NULL && g_strcmp0(text, t->active_text))
402 args.i = XT_MODE_INSERT;
403 else
404 args.i = XT_MODE_COMMAND;
405 } else
406 args.i = XT_MODE_COMMAND;
407 command_mode(t, &args);
410 if (text)
411 g_free(text);
413 #else /* WEBKIT_CHECK_VERSION */
414 /* incomplete DOM API */
417 * XXX
418 * note that we can't check the return value of run_script so we
419 * have to assume that the command worked; this may leave you in
420 * insertmode when in fact you shouldn't be
422 void
423 input_autofocus(struct tab *t)
425 if (autofocus_onload &&
426 t->tab_id == gtk_notebook_get_current_page(notebook)) {
427 run_script(t, "hints.focusInput();");
428 t->mode = XT_MODE_INSERT;
429 } else {
430 run_script(t, "hints.clearFocus();");
431 t->mode = XT_MODE_COMMAND;
435 void *
436 input_check_mode(struct tab *t)
438 return (NULL);
442 command_mode(struct tab *t, struct karg *args)
444 if (args->i == XT_MODE_COMMAND) {
445 run_script(t, "hints.clearFocus();");
446 t->mode = XT_MODE_COMMAND;
447 } else {
448 run_script(t, "hints.focusInput();");
449 t->mode = XT_MODE_INSERT;
452 return (XT_CB_HANDLED);
454 #endif