make S-f open favorites
[xxxterm.git] / xxxterm.c
blob8c11b0e3a96cd8b17e56ba6311704d452be71a13
1 /* $xxxterm$ */
2 /*
3 * Copyright (c) 2010 Marco Peereboom <marco@peereboom.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.
19 * TODO:
20 * inverse color browsing
21 * favs
22 * - add favicon
23 * download files status
24 * multi letter commands
25 * pre and post counts for commands
26 * fav icon
27 * close tab X
28 * autocompletion on various inputs
29 * create privacy browsing
30 * - encrypted local data
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <err.h>
36 #include <pwd.h>
37 #include <string.h>
38 #include <unistd.h>
39 #include <util.h>
41 #include <sys/queue.h>
42 #include <sys/types.h>
43 #include <sys/stat.h>
45 #include <gtk/gtk.h>
46 #include <gdk/gdkkeysyms.h>
47 #include <webkit/webkit.h>
48 #include <libsoup/soup.h>
49 #include <JavaScriptCore/JavaScript.h>
51 #include "javascript.h"
54 javascript.h borrowed from vimprobable2 under the following license:
56 Copyright (c) 2009 Leon Winter
57 Copyright (c) 2009 Hannes Schueller
58 Copyright (c) 2009 Matto Fransen
60 Permission is hereby granted, free of charge, to any person obtaining a copy
61 of this software and associated documentation files (the "Software"), to deal
62 in the Software without restriction, including without limitation the rights
63 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
64 copies of the Software, and to permit persons to whom the Software is
65 furnished to do so, subject to the following conditions:
67 The above copyright notice and this permission notice shall be included in
68 all copies or substantial portions of the Software.
70 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
71 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
72 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
73 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
74 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
75 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
76 THE SOFTWARE.
79 static char *version = "$xxxterm$";
81 #define XT_DEBUG
82 /* #define XT_DEBUG */
83 #ifdef XT_DEBUG
84 #define DPRINTF(x...) do { if (swm_debug) fprintf(stderr, x); } while (0)
85 #define DNPRINTF(n,x...) do { if (swm_debug & n) fprintf(stderr, x); } while (0)
86 #define XT_D_MOVE 0x0001
87 #define XT_D_KEY 0x0002
88 #define XT_D_TAB 0x0004
89 #define XT_D_URL 0x0008
90 #define XT_D_CMD 0x0010
91 #define XT_D_NAV 0x0020
92 #define XT_D_DOWNLOAD 0x0040
93 #define XT_D_CONFIG 0x0080
94 #define XT_D_JS 0x0100
95 u_int32_t swm_debug = 0
96 | XT_D_MOVE
97 | XT_D_KEY
98 | XT_D_TAB
99 | XT_D_URL
100 | XT_D_CMD
101 | XT_D_NAV
102 | XT_D_DOWNLOAD
103 | XT_D_CONFIG
104 | XT_D_JS
106 #else
107 #define DPRINTF(x...)
108 #define DNPRINTF(n,x...)
109 #endif
111 #define LENGTH(x) (sizeof x / sizeof x[0])
112 #define CLEAN(mask) (mask & ~(GDK_MOD2_MASK) & \
113 ~(GDK_BUTTON1_MASK) & \
114 ~(GDK_BUTTON2_MASK) & \
115 ~(GDK_BUTTON3_MASK) & \
116 ~(GDK_BUTTON4_MASK) & \
117 ~(GDK_BUTTON5_MASK))
119 struct tab {
120 TAILQ_ENTRY(tab) entry;
121 GtkWidget *vbox;
122 GtkWidget *label;
123 GtkWidget *spinner;
124 GtkWidget *uri_entry;
125 GtkWidget *search_entry;
126 GtkWidget *toolbar;
127 GtkWidget *browser_win;
128 GtkWidget *cmd;
129 GtkToolItem *backward;
130 GtkToolItem *forward;
131 GtkToolItem *stop;
132 guint tab_id;
133 WebKitWebView *wv;
135 /* adjustments for browser */
136 GtkScrollbar *sb_h;
137 GtkScrollbar *sb_v;
138 GtkAdjustment *adjust_h;
139 GtkAdjustment *adjust_v;
141 /* flags */
142 int focus_wv;
143 int ctrl_click;
144 gchar *hover;
146 /* hints */
147 int hints_on;
148 int hint_mode;
149 #define XT_HINT_NONE (0)
150 #define XT_HINT_NUMERICAL (1)
151 #define XT_HINT_ALPHANUM (2)
152 char hint_buf[128];
153 char hint_num[128];
155 /* search */
156 char *search_text;
157 int search_forward;
159 /* settings */
160 WebKitWebSettings *settings;
161 int font_size;
162 gchar *user_agent;
164 TAILQ_HEAD(tab_list, tab);
166 struct karg {
167 int i;
168 char *s;
171 /* defines */
172 #define XT_NAME ("XXXTerm")
173 #define XT_DIR (".xxxterm")
174 #define XT_CONF_FILE ("xxxterm.conf")
175 #define XT_FAVS_FILE ("favorites")
176 #define XT_CB_HANDLED (TRUE)
177 #define XT_CB_PASSTHROUGH (FALSE)
179 /* actions */
180 #define XT_MOVE_INVALID (0)
181 #define XT_MOVE_DOWN (1)
182 #define XT_MOVE_UP (2)
183 #define XT_MOVE_BOTTOM (3)
184 #define XT_MOVE_TOP (4)
185 #define XT_MOVE_PAGEDOWN (5)
186 #define XT_MOVE_PAGEUP (6)
187 #define XT_MOVE_HALFDOWN (7)
188 #define XT_MOVE_HALFUP (8)
189 #define XT_MOVE_LEFT (9)
190 #define XT_MOVE_FARLEFT (10)
191 #define XT_MOVE_RIGHT (11)
192 #define XT_MOVE_FARRIGHT (12)
194 #define XT_TAB_LAST (-4)
195 #define XT_TAB_FIRST (-3)
196 #define XT_TAB_PREV (-2)
197 #define XT_TAB_NEXT (-1)
198 #define XT_TAB_INVALID (0)
199 #define XT_TAB_NEW (1)
200 #define XT_TAB_DELETE (2)
201 #define XT_TAB_DELQUIT (3)
202 #define XT_TAB_OPEN (4)
204 #define XT_NAV_INVALID (0)
205 #define XT_NAV_BACK (1)
206 #define XT_NAV_FORWARD (2)
207 #define XT_NAV_RELOAD (3)
209 #define XT_FOCUS_INVALID (0)
210 #define XT_FOCUS_URI (1)
211 #define XT_FOCUS_SEARCH (2)
213 #define XT_SEARCH_INVALID (0)
214 #define XT_SEARCH_NEXT (1)
215 #define XT_SEARCH_PREV (2)
217 #define XT_FONT_SET (0)
219 /* globals */
220 extern char *__progname;
221 struct passwd *pwd;
222 GtkWidget *main_window;
223 GtkNotebook *notebook;
224 struct tab_list tabs;
226 /* mime types */
227 struct mime_type {
228 char *mt_type;
229 char *mt_action;
230 int mt_default;
231 TAILQ_ENTRY(mime_type) entry;
233 TAILQ_HEAD(mime_type_list, mime_type);
235 /* uri aliases */
236 struct alias {
237 char *a_name;;
238 char *a_uri;
239 TAILQ_ENTRY(alias) entry;
241 TAILQ_HEAD(alias_list, alias);
243 /* settings */
244 int showtabs = 1; /* show tabs on notebook */
245 int showurl = 1; /* show url toolbar on notebook */
246 int tabless = 0; /* allow only 1 tab */
247 int ctrl_click_focus = 0; /* ctrl click gets focus */
248 int cookies_enabled = 1; /* enable cookies */
249 int read_only_cookies = 0; /* enable to not write cookies */
250 int enable_scripts = 0;
251 int enable_plugins = 0;
252 int default_font_size = 12;
253 int fancy_bar = 1; /* fancy toolbar */
255 char *home = "http://www.peereboom.us";
256 char *search_string = NULL;
257 char *http_proxy = NULL;
258 SoupURI *proxy_uri = NULL;
259 char work_dir[PATH_MAX];
260 char cookie_file[PATH_MAX];
261 char download_dir[PATH_MAX];
262 SoupSession *session;
263 SoupCookieJar *cookiejar;
265 struct mime_type_list mtl;
266 struct alias_list aliases;
268 /* protos */
269 void create_new_tab(char *, int);
270 void delete_tab(struct tab *);
271 void adjustfont_webkit(struct tab *, int);
272 int run_script(struct tab *, char *);
274 struct valid_url_types {
275 char *type;
276 } vut[] = {
277 { "http://" },
278 { "https://" },
279 { "ftp://" },
280 { "file://" },
284 valid_url_type(char *url)
286 int i;
288 for (i = 0; i < LENGTH(vut); i++)
289 if (!strncasecmp(vut[i].type, url, strlen(vut[i].type)))
290 return (0);
292 return (1);
295 char *
296 match_alias(char *url_in)
298 struct alias *a;
299 char *arg;
300 char *url_out = NULL;
302 arg = url_in;
303 if (strsep(&arg, " \t") == NULL)
304 errx(1, "match_alias: NULL URL");
306 TAILQ_FOREACH(a, &aliases, entry) {
307 if (!strcmp(url_in, a->a_name))
308 break;
311 if (a != NULL) {
312 DNPRINTF(XT_D_URL, "match_alias: matched alias %s\n",
313 a->a_name);
314 if (arg != NULL)
315 url_out = g_strdup_printf(a->a_uri, arg);
316 else
317 url_out = g_strdup(a->a_uri);
320 return (url_out);
323 char *
324 guess_url_type(char *url_in)
326 struct stat sb;
327 char *url_out = NULL;
329 url_out = match_alias(url_in);
330 if (url_out != NULL)
331 return (url_out);
333 /* XXX not sure about this heuristic */
334 if (stat(url_in, &sb) == 0)
335 url_out = g_strdup_printf("file://%s", url_in);
336 else
337 url_out = g_strdup_printf("http://%s", url_in); /* guess http */
339 DNPRINTF(XT_D_URL, "guess_url_type: guessed %s\n", url_out);
341 return (url_out);
344 void
345 add_alias(char *line)
347 char *l, *alias;
348 struct alias *a;
350 if (line == NULL)
351 errx(1, "add_alias");
352 l = line;
354 a = g_malloc(sizeof(*a));
356 if ((alias = strsep(&l, " \t,")) == NULL || l == NULL)
357 errx(1, "add_alias: incomplete alias definition");
359 if (strlen(alias) == 0 || strlen(l) == 0)
360 errx(1, "add_alias: invalid alias definition");
362 a->a_name = g_strdup(alias);
363 a->a_uri = g_strdup(l);
365 DNPRINTF(XT_D_CONFIG, "add_alias: %s for %s\n", a->a_name, a->a_uri);
367 TAILQ_INSERT_TAIL(&aliases, a, entry);
370 void
371 add_mime_type(char *line)
373 char *mime_type;
374 char *l = NULL;
375 struct mime_type *m;
377 /* XXX this could be smarter */
379 if (line == NULL)
380 errx(1, "add_mime_type");
381 l = line;
383 m = g_malloc(sizeof(*m));
385 if ((mime_type = strsep(&l, " \t,")) == NULL || l == NULL)
386 errx(1, "add_mime_type: invalid mime_type");
388 if (mime_type[strlen(mime_type) - 1] == '*') {
389 mime_type[strlen(mime_type) - 1] = '\0';
390 m->mt_default = 1;
391 } else
392 m->mt_default = 0;
394 if (strlen(mime_type) == 0 || strlen(l) == 0)
395 errx(1, "add_mime_type: invalid mime_type");
397 m->mt_type = g_strdup(mime_type);
398 m->mt_action = g_strdup(l);
400 DNPRINTF(XT_D_CONFIG, "add_mime_type: type %s action %s default %d\n",
401 m->mt_type, m->mt_action, m->mt_default);
403 TAILQ_INSERT_TAIL(&mtl, m, entry);
406 struct mime_type *
407 find_mime_type(char *mime_type)
409 struct mime_type *m, *def = NULL, *rv = NULL;
411 TAILQ_FOREACH(m, &mtl, entry) {
412 if (m->mt_default &&
413 !strncmp(mime_type, m->mt_type, strlen(m->mt_type)))
414 def = m;
416 if (m->mt_default == 0 && !strcmp(mime_type, m->mt_type)) {
417 rv = m;
418 break;
422 if (rv == NULL)
423 rv = def;
425 return (rv);
428 #define WS "\n= \t"
429 void
430 config_parse(char *filename)
432 FILE *config;
433 char *line, *cp, *var, *val;
434 size_t len, lineno = 0;
436 DNPRINTF(XT_D_CONFIG, "config_parse: filename %s\n", filename);
438 TAILQ_INIT(&mtl);
439 TAILQ_INIT(&aliases);
441 if (filename == NULL)
442 return;
444 if ((config = fopen(filename, "r")) == NULL) {
445 warn("config_parse: cannot open %s", filename);
446 return;
449 for (;;) {
450 if ((line = fparseln(config, &len, &lineno, NULL, 0)) == NULL)
451 if (feof(config) || ferror(config))
452 break;
454 cp = line;
455 cp += (long)strspn(cp, WS);
456 if (cp[0] == '\0') {
457 /* empty line */
458 free(line);
459 continue;
462 if ((var = strsep(&cp, WS)) == NULL || cp == NULL)
463 break;
465 cp += (long)strspn(cp, WS);
467 if ((val = strsep(&cp, "\0")) == NULL)
468 break;
470 DNPRINTF(XT_D_CONFIG, "config_parse: %s=%s\n",var ,val);
472 /* get settings */
473 if (!strcmp(var, "home"))
474 home = g_strdup(val);
475 else if (!strcmp(var, "ctrl_click_focus"))
476 ctrl_click_focus = atoi(val);
477 else if (!strcmp(var, "read_only_cookies"))
478 read_only_cookies = atoi(val);
479 else if (!strcmp(var, "cookies_enabled"))
480 cookies_enabled = atoi(val);
481 else if (!strcmp(var, "enable_scripts"))
482 enable_scripts = atoi(val);
483 else if (!strcmp(var, "enable_plugins"))
484 enable_plugins = atoi(val);
485 else if (!strcmp(var, "default_font_size"))
486 default_font_size = atoi(val);
487 else if (!strcmp(var, "fancy_bar"))
488 fancy_bar = atoi(val);
489 else if (!strcmp(var, "mime_type"))
490 add_mime_type(val);
491 else if (!strcmp(var, "alias"))
492 add_alias(val);
493 else if (!strcmp(var, "http_proxy")) {
494 if (http_proxy)
495 g_free(http_proxy);
496 http_proxy = g_strdup(val);
497 } else if (!strcmp(var, "search_string")) {
498 if (search_string)
499 g_free(search_string);
500 search_string = g_strdup(val);
501 } else if (!strcmp(var, "download_dir")) {
502 if (val[0] == '~')
503 snprintf(download_dir, sizeof download_dir,
504 "%s/%s", pwd->pw_dir, &val[1]);
505 else
506 strlcpy(download_dir, val, sizeof download_dir);
507 } else
508 errx(1, "invalid conf file entry: %s=%s", var, val);
510 free(line);
513 fclose(config);
516 char *
517 js_ref_to_string(JSContextRef context, JSValueRef ref)
519 char *s = NULL;
520 size_t l;
521 JSStringRef jsref;
523 jsref = JSValueToStringCopy(context, ref, NULL);
524 if (jsref == NULL)
525 return (NULL);
527 l = JSStringGetMaximumUTF8CStringSize(jsref);
528 s = g_malloc(l);
529 if (s)
530 JSStringGetUTF8CString(jsref, s, l);
531 JSStringRelease(jsref);
533 return (s);
536 void
537 disable_hints(struct tab *t)
539 bzero(t->hint_buf, sizeof t->hint_buf);
540 bzero(t->hint_num, sizeof t->hint_num);
541 run_script(t, "vimprobable_clear()");
542 t->hints_on = 0;
543 t->hint_mode = XT_HINT_NONE;
546 void
547 enable_hints(struct tab *t)
549 bzero(t->hint_buf, sizeof t->hint_buf);
550 run_script(t, "vimprobable_show_hints()");
551 t->hints_on = 1;
552 t->hint_mode = XT_HINT_NONE;
555 #define XT_JS_OPEN ("open;")
556 #define XT_JS_OPEN_LEN (strlen(XT_JS_OPEN))
557 #define XT_JS_FIRE ("fire;")
558 #define XT_JS_FIRE_LEN (strlen(XT_JS_FIRE))
559 #define XT_JS_FOUND ("found;")
560 #define XT_JS_FOUND_LEN (strlen(XT_JS_FOUND))
563 run_script(struct tab *t, char *s)
565 JSGlobalContextRef ctx;
566 WebKitWebFrame *frame;
567 JSStringRef str;
568 JSValueRef val, exception;
569 char *es, buf[128];
571 DNPRINTF(XT_D_JS, "run_script: tab %d %s\n",
572 t->tab_id, s == (char *)JS_HINTING ? "JS_HINTING" : s);
574 frame = webkit_web_view_get_main_frame(t->wv);
575 ctx = webkit_web_frame_get_global_context(frame);
577 str = JSStringCreateWithUTF8CString(s);
578 val = JSEvaluateScript(ctx, str, JSContextGetGlobalObject(ctx),
579 NULL, 0, &exception);
580 JSStringRelease(str);
582 DNPRINTF(XT_D_JS, "run_script: val %p\n", val);
583 if (val == NULL) {
584 es = js_ref_to_string(ctx, exception);
585 DNPRINTF(XT_D_JS, "run_script: exception %s\n", es);
586 g_free(es);
587 return (1);
588 } else {
589 es = js_ref_to_string(ctx, val);
590 DNPRINTF(XT_D_JS, "run_script: val %s\n", es);
592 /* handle return value right here */
593 if (!strncmp(es, XT_JS_OPEN, XT_JS_OPEN_LEN)) {
594 disable_hints(t);
595 webkit_web_view_load_uri(t->wv, &es[XT_JS_OPEN_LEN]);
598 if (!strncmp(es, XT_JS_FIRE, XT_JS_FIRE_LEN)) {
599 snprintf(buf, sizeof buf, "vimprobable_fire(%s)",
600 &es[XT_JS_FIRE_LEN]);
601 run_script(t, buf);
602 disable_hints(t);
605 if (!strncmp(es, XT_JS_FOUND, XT_JS_FOUND_LEN)) {
606 if (atoi(&es[XT_JS_FOUND_LEN]) == 0)
607 disable_hints(t);
610 g_free(es);
613 return (0);
617 hint(struct tab *t, struct karg *args)
620 DNPRINTF(XT_D_JS, "hint: tab %d\n", t->tab_id);
622 if (t->hints_on == 0)
623 enable_hints(t);
624 else
625 disable_hints(t);
627 return (0);
631 quit(struct tab *t, struct karg *args)
633 gtk_main_quit();
635 return (1);
639 focus(struct tab *t, struct karg *args)
641 if (t == NULL || args == NULL)
642 errx(1, "focus");
644 if (args->i == XT_FOCUS_URI)
645 gtk_widget_grab_focus(GTK_WIDGET(t->uri_entry));
646 else if (args->i == XT_FOCUS_SEARCH)
647 gtk_widget_grab_focus(GTK_WIDGET(t->search_entry));
649 return (0);
653 help(struct tab *t, struct karg *args)
655 if (t == NULL)
656 errx(1, "help");
658 webkit_web_view_load_string(t->wv,
659 "<html><body><h1>XXXTerm</h1></body></html>",
660 NULL,
661 NULL,
662 NULL);
664 return (0);
668 favorites(struct tab *t, struct karg *args)
670 char file[PATH_MAX];
671 FILE *f, *h;
672 char *uri = NULL, *title = NULL;
673 size_t len, lineno = 0;
674 int i, failed = 0;
676 if (t == NULL)
677 errx(1, "favorites");
679 /* XXX run a digest over the favorites file instead of always generating it */
681 /* open favorites */
682 snprintf(file, sizeof file, "%s/%s/%s",
683 pwd->pw_dir, XT_DIR, XT_FAVS_FILE);
684 if ((f = fopen(file, "r")) == NULL) {
685 warn("favorites");
686 return (1);
689 /* open favorites html */
690 snprintf(file, sizeof file, "%s/%s/%s.html",
691 pwd->pw_dir, XT_DIR, XT_FAVS_FILE);
692 if ((h = fopen(file, "w+")) == NULL) {
693 warn("favorites.html");
694 return (1);
697 fprintf(h, "<html><body>Favorites:<p>\n<ol>\n");
699 for (i = 1;;) {
700 if ((title = fparseln(f, &len, &lineno, NULL, 0)) == NULL)
701 if (feof(f) || ferror(f))
702 break;
703 if (len == 0) {
704 free(title);
705 title = NULL;
706 continue;
709 if ((uri = fparseln(f, &len, &lineno, NULL, 0)) == NULL)
710 if (feof(f) || ferror(f)) {
711 failed = 1;
712 break;
715 fprintf(h, "<li><a href=\"%s\">%s</a><br>\n", uri, title);
717 free(uri);
718 uri = NULL;
719 free(title);
720 title = NULL;
721 i++;
724 if (uri)
725 free(uri);
726 if (title)
727 free(title);
729 fprintf(h, "</ol></body></html>");
730 fclose(f);
731 fclose(h);
733 if (failed) {
734 webkit_web_view_load_string(t->wv,
735 "<html><body>Invalid favorites file</body></html>",
736 NULL,
737 NULL,
738 NULL);
739 } else {
740 snprintf(file, sizeof file, "file://%s/%s/%s.html",
741 pwd->pw_dir, XT_DIR, XT_FAVS_FILE);
742 webkit_web_view_load_uri(t->wv, file);
745 return (0);
749 favadd(struct tab *t, struct karg *args)
751 char file[PATH_MAX];
752 FILE *f;
753 char *line = NULL;
754 size_t urilen, linelen;
755 WebKitWebFrame *frame;
756 const gchar *uri, *title;
758 if (t == NULL)
759 errx(1, "favadd");
761 snprintf(file, sizeof file, "%s/%s/%s",
762 pwd->pw_dir, XT_DIR, XT_FAVS_FILE);
763 if ((f = fopen(file, "r+")) == NULL) {
764 warn("favorites");
765 return (1);
768 title = webkit_web_view_get_title(t->wv);
769 frame = webkit_web_view_get_main_frame(t->wv);
770 uri = webkit_web_frame_get_uri(frame);
771 if (title == NULL)
772 title = uri;
774 if (title == NULL || uri == NULL) {
775 webkit_web_view_load_string(t->wv,
776 "<html><body>can't add page to favorites</body></html>",
777 NULL,
778 NULL,
779 NULL);
780 goto done;
783 urilen = strlen(uri);
785 while (!feof(f)) {
786 line = fparseln(f, &linelen, NULL, NULL, 0);
787 if (linelen == urilen && !strcmp(line, uri))
788 goto done;
789 free(line);
790 line = NULL;
793 fprintf(f, "\n%s\n%s", title, uri);
794 done:
795 if (line)
796 free(line);
797 fclose(f);
799 return (0);
803 navaction(struct tab *t, struct karg *args)
805 DNPRINTF(XT_D_NAV, "navaction: tab %d opcode %d\n",
806 t->tab_id, args->i);
808 switch (args->i) {
809 case XT_NAV_BACK:
810 webkit_web_view_go_back(t->wv);
811 break;
812 case XT_NAV_FORWARD:
813 webkit_web_view_go_forward(t->wv);
814 break;
815 case XT_NAV_RELOAD:
816 webkit_web_view_reload(t->wv);
817 break;
819 return (XT_CB_PASSTHROUGH);
823 move(struct tab *t, struct karg *args)
825 GtkAdjustment *adjust;
826 double pi, si, pos, ps, upper, lower, max;
828 switch (args->i) {
829 case XT_MOVE_DOWN:
830 case XT_MOVE_UP:
831 case XT_MOVE_BOTTOM:
832 case XT_MOVE_TOP:
833 case XT_MOVE_PAGEDOWN:
834 case XT_MOVE_PAGEUP:
835 case XT_MOVE_HALFDOWN:
836 case XT_MOVE_HALFUP:
837 adjust = t->adjust_v;
838 break;
839 default:
840 adjust = t->adjust_h;
841 break;
844 pos = gtk_adjustment_get_value(adjust);
845 ps = gtk_adjustment_get_page_size(adjust);
846 upper = gtk_adjustment_get_upper(adjust);
847 lower = gtk_adjustment_get_lower(adjust);
848 si = gtk_adjustment_get_step_increment(adjust);
849 pi = gtk_adjustment_get_page_increment(adjust);
850 max = upper - ps;
852 DNPRINTF(XT_D_MOVE, "move: opcode %d %s pos %f ps %f upper %f lower %f "
853 "max %f si %f pi %f\n",
854 args->i, adjust == t->adjust_h ? "horizontal" : "vertical",
855 pos, ps, upper, lower, max, si, pi);
857 switch (args->i) {
858 case XT_MOVE_DOWN:
859 case XT_MOVE_RIGHT:
860 pos += si;
861 gtk_adjustment_set_value(adjust, MIN(pos, max));
862 break;
863 case XT_MOVE_UP:
864 case XT_MOVE_LEFT:
865 pos -= si;
866 gtk_adjustment_set_value(adjust, MAX(pos, lower));
867 break;
868 case XT_MOVE_BOTTOM:
869 case XT_MOVE_FARRIGHT:
870 gtk_adjustment_set_value(adjust, max);
871 break;
872 case XT_MOVE_TOP:
873 case XT_MOVE_FARLEFT:
874 gtk_adjustment_set_value(adjust, lower);
875 break;
876 case XT_MOVE_PAGEDOWN:
877 pos += pi;
878 gtk_adjustment_set_value(adjust, MIN(pos, max));
879 break;
880 case XT_MOVE_PAGEUP:
881 pos -= pi;
882 gtk_adjustment_set_value(adjust, MAX(pos, lower));
883 break;
884 case XT_MOVE_HALFDOWN:
885 pos += pi / 2;
886 gtk_adjustment_set_value(adjust, MIN(pos, max));
887 break;
888 case XT_MOVE_HALFUP:
889 pos -= pi / 2;
890 gtk_adjustment_set_value(adjust, MAX(pos, lower));
891 break;
892 default:
893 return (XT_CB_PASSTHROUGH);
896 DNPRINTF(XT_D_MOVE, "move: new pos %f %f\n", pos, MIN(pos, max));
898 return (XT_CB_HANDLED);
901 char *
902 getparams(char *cmd, char *cmp)
904 char *rv = NULL;
906 if (cmd && cmp) {
907 if (!strncmp(cmd, cmp, strlen(cmp))) {
908 rv = cmd + strlen(cmp);
909 while (*rv == ' ')
910 rv++;
911 if (strlen(rv) == 0)
912 rv = NULL;
916 return (rv);
920 tabaction(struct tab *t, struct karg *args)
922 int rv = XT_CB_HANDLED;
923 char *url = NULL, *newuri = NULL;
925 DNPRINTF(XT_D_TAB, "tabaction: %p %d %d\n", t, args->i, t->focus_wv);
927 if (t == NULL)
928 return (XT_CB_PASSTHROUGH);
930 switch (args->i) {
931 case XT_TAB_NEW:
932 if ((url = getparams(args->s, "tabnew")))
933 create_new_tab(url, 1);
934 else
935 create_new_tab(NULL, 1);
936 break;
937 case XT_TAB_DELETE:
938 delete_tab(t);
939 break;
940 case XT_TAB_DELQUIT:
941 if (gtk_notebook_get_n_pages(notebook) > 1)
942 delete_tab(t);
943 else
944 quit(t, args);
945 break;
946 case XT_TAB_OPEN:
947 if ((url = getparams(args->s, "open")) ||
948 ((url = getparams(args->s, "op"))) ||
949 ((url = getparams(args->s, "o"))))
951 else {
952 rv = XT_CB_PASSTHROUGH;
953 goto done;
956 if (valid_url_type(url)) {
957 newuri = guess_url_type(url);
958 url = newuri;
960 webkit_web_view_load_uri(t->wv, url);
961 if (newuri)
962 g_free(newuri);
963 break;
964 default:
965 rv = XT_CB_PASSTHROUGH;
966 goto done;
969 done:
970 if (args->s) {
971 g_free(args->s);
972 args->s = NULL;
975 return (rv);
979 resizetab(struct tab *t, struct karg *args)
981 if (t == NULL || args == NULL)
982 errx(1, "resizetab");
984 DNPRINTF(XT_D_TAB, "resizetab: tab %d %d\n",
985 t->tab_id, args->i);
987 adjustfont_webkit(t, args->i);
989 return (XT_CB_HANDLED);
993 movetab(struct tab *t, struct karg *args)
995 struct tab *tt;
996 int x;
998 if (t == NULL || args == NULL)
999 errx(1, "movetab");
1001 DNPRINTF(XT_D_TAB, "movetab: tab %d opcode %d\n",
1002 t->tab_id, args->i);
1004 if (args->i == XT_TAB_INVALID)
1005 return (XT_CB_PASSTHROUGH);
1007 if (args->i < XT_TAB_INVALID) {
1008 /* next or previous tab */
1009 if (TAILQ_EMPTY(&tabs))
1010 return (XT_CB_PASSTHROUGH);
1012 switch (args->i) {
1013 case XT_TAB_NEXT:
1014 gtk_notebook_next_page(notebook);
1015 break;
1016 case XT_TAB_PREV:
1017 gtk_notebook_prev_page(notebook);
1018 break;
1019 case XT_TAB_FIRST:
1020 gtk_notebook_set_current_page(notebook, 0);
1021 break;
1022 case XT_TAB_LAST:
1023 gtk_notebook_set_current_page(notebook, -1);
1024 break;
1025 default:
1026 return (XT_CB_PASSTHROUGH);
1029 return (XT_CB_HANDLED);
1032 /* jump to tab */
1033 x = args->i - 1;
1034 if (t->tab_id == x) {
1035 DNPRINTF(XT_D_TAB, "movetab: do nothing\n");
1036 return (XT_CB_HANDLED);
1039 TAILQ_FOREACH(tt, &tabs, entry) {
1040 if (tt->tab_id == x) {
1041 gtk_notebook_set_current_page(notebook, x);
1042 DNPRINTF(XT_D_TAB, "movetab: going to %d\n", x);
1043 if (tt->focus_wv)
1044 gtk_widget_grab_focus(GTK_WIDGET(tt->wv));
1048 return (XT_CB_HANDLED);
1052 command(struct tab *t, struct karg *args)
1054 char *s = NULL;
1055 GdkColor color;
1057 if (t == NULL || args == NULL)
1058 errx(1, "command");
1060 if (args->i == '/')
1061 s = "/";
1062 else if (args->i == '?')
1063 s = "?";
1064 else if (args->i == ':')
1065 s = ":";
1066 else {
1067 warnx("invalid command %c\n", args->i);
1068 return (XT_CB_PASSTHROUGH);
1071 DNPRINTF(XT_D_CMD, "command: type %s\n", s);
1073 gtk_entry_set_text(GTK_ENTRY(t->cmd), s);
1074 gdk_color_parse("white", &color);
1075 gtk_widget_modify_base(t->cmd, GTK_STATE_NORMAL, &color);
1076 gtk_widget_show(t->cmd);
1077 gtk_widget_grab_focus(GTK_WIDGET(t->cmd));
1078 gtk_editable_set_position(GTK_EDITABLE(t->cmd), -1);
1080 return (XT_CB_HANDLED);
1084 search(struct tab *t, struct karg *args)
1086 gboolean d;
1088 if (t == NULL || args == NULL)
1089 errx(1, "search");
1090 if (t->search_text == NULL)
1091 return (XT_CB_PASSTHROUGH);
1093 DNPRINTF(XT_D_CMD, "search: tab %d opc %d forw %d text %s\n",
1094 t->tab_id, args->i, t->search_forward, t->search_text);
1096 switch (args->i) {
1097 case XT_SEARCH_NEXT:
1098 d = t->search_forward;
1099 break;
1100 case XT_SEARCH_PREV:
1101 d = !t->search_forward;
1102 break;
1103 default:
1104 return (XT_CB_PASSTHROUGH);
1107 webkit_web_view_search_text(t->wv, t->search_text, FALSE, d, TRUE);
1109 return (XT_CB_HANDLED);
1113 mnprintf(char **buf, int *len, char *fmt, ...)
1115 int x, old_len;
1116 va_list ap;
1118 va_start(ap, fmt);
1120 old_len = *len;
1121 x = vsnprintf(*buf, *len, fmt, ap);
1122 if (x == -1)
1123 err(1, "mnprintf");
1124 if (old_len < x)
1125 errx(1, "mnprintf: buffer overflow");
1127 *buf += x;
1128 *len -= x;
1130 va_end(ap);
1132 return (0);
1136 set(struct tab *t, struct karg *args)
1138 struct mime_type *m;
1139 char b[16 * 1024], *s, *pars;
1140 int l;
1142 if (t == NULL || args == NULL)
1143 errx(1, "set");
1145 DNPRINTF(XT_D_CMD, "set: tab %d\n",
1146 t->tab_id);
1148 s = b;
1149 l = sizeof b;
1151 if ((pars = getparams(args->s, "set")) == NULL) {
1152 mnprintf(&s, &l, "<html><body><pre>");
1153 mnprintf(&s, &l, "ctrl_click_focus\t= %d<br>", ctrl_click_focus);
1154 mnprintf(&s, &l, "cookies_enabled\t\t= %d<br>", cookies_enabled);
1155 mnprintf(&s, &l, "default_font_size\t= %d<br>", default_font_size);
1156 mnprintf(&s, &l, "enable_plugins\t\t= %d<br>", enable_plugins);
1157 mnprintf(&s, &l, "enable_scripts\t\t= %d<br>", enable_scripts);
1158 mnprintf(&s, &l, "fancy_bar\t\t= %d<br>", fancy_bar);
1159 mnprintf(&s, &l, "home\t\t\t= %s<br>", home);
1160 TAILQ_FOREACH(m, &mtl, entry) {
1161 mnprintf(&s, &l, "mime_type\t\t= %s%s,%s<br>",
1162 m->mt_type, m->mt_default ? "*" : "", m->mt_action);
1164 mnprintf(&s, &l, "proxy_uri\t\t= %s<br>", proxy_uri);
1165 mnprintf(&s, &l, "read_only_cookies\t= %d<br>", read_only_cookies);
1166 mnprintf(&s, &l, "search_string\t\t= %s<br>", search_string);
1167 mnprintf(&s, &l, "showurl\t\t\t= %d<br>", showurl);
1168 mnprintf(&s, &l, "showtabs\t\t= %d<br>", showtabs);
1169 mnprintf(&s, &l, "tabless\t\t\t= %d<br>", tabless);
1170 mnprintf(&s, &l, "download_dir\t\t= %s<br>", download_dir);
1171 mnprintf(&s, &l, "</pre></body></html>");
1173 webkit_web_view_load_string(t->wv,
1175 NULL,
1176 NULL,
1177 "about:config");
1178 goto done;
1181 /* XXX this sucks donkey balls and is a POC only */
1182 int x;
1183 char *e;
1184 if (!strncmp(pars, "enable_scripts ", strlen("enable_scripts"))) {
1185 s = pars + strlen("enable_scripts");
1186 x = strtol(s, &e, 10);
1187 if (s[0] == '\0' || *e != '\0')
1188 webkit_web_view_load_string(t->wv,
1189 "<html><body>invalid value</body></html>",
1190 NULL,
1191 NULL,
1192 "about:error");
1194 enable_scripts = x;
1195 g_object_set((GObject *)t->settings,
1196 "enable-scripts", enable_scripts, (char *)NULL);
1197 webkit_web_view_set_settings(t->wv, t->settings);
1200 done:
1201 if (args->s) {
1202 g_free(args->s);
1203 args->s = NULL;
1206 return (XT_CB_PASSTHROUGH);
1209 /* inherent to GTK not all keys will be caught at all times */
1210 struct key {
1211 guint mask;
1212 guint modkey;
1213 guint key;
1214 int (*func)(struct tab *, struct karg *);
1215 struct karg arg;
1216 } keys[] = {
1217 { 0, 0, GDK_slash, command, {.i = '/'} },
1218 { GDK_SHIFT_MASK, 0, GDK_question, command, {.i = '?'} },
1219 { GDK_SHIFT_MASK, 0, GDK_colon, command, {.i = ':'} },
1220 { GDK_CONTROL_MASK, 0, GDK_q, quit, {0} },
1222 /* search */
1223 { 0, 0, GDK_n, search, {.i = XT_SEARCH_NEXT} },
1224 { GDK_SHIFT_MASK, 0, GDK_N, search, {.i = XT_SEARCH_PREV} },
1226 /* focus */
1227 { 0, 0, GDK_F6, focus, {.i = XT_FOCUS_URI} },
1228 { 0, 0, GDK_F7, focus, {.i = XT_FOCUS_SEARCH} },
1230 /* hinting */
1231 { 0, 0, GDK_f, hint, {.i = 0} },
1233 /* navigation */
1234 { 0, 0, GDK_BackSpace, navaction, {.i = XT_NAV_BACK} },
1235 { GDK_MOD1_MASK, 0, GDK_Left, navaction, {.i = XT_NAV_BACK} },
1236 { GDK_SHIFT_MASK, 0, GDK_BackSpace, navaction, {.i = XT_NAV_FORWARD} },
1237 { GDK_MOD1_MASK, 0, GDK_Right, navaction, {.i = XT_NAV_FORWARD} },
1238 { 0, 0, GDK_F5, navaction, {.i = XT_NAV_RELOAD} },
1239 { GDK_CONTROL_MASK, 0, GDK_r, navaction, {.i = XT_NAV_RELOAD} },
1240 { GDK_CONTROL_MASK, 0, GDK_l, navaction, {.i = XT_NAV_RELOAD} },
1241 { GDK_SHIFT_MASK, 0, GDK_F, favorites, {0} },
1243 /* vertical movement */
1244 { 0, 0, GDK_j, move, {.i = XT_MOVE_DOWN} },
1245 { 0, 0, GDK_Down, move, {.i = XT_MOVE_DOWN} },
1246 { 0, 0, GDK_Up, move, {.i = XT_MOVE_UP} },
1247 { 0, 0, GDK_k, move, {.i = XT_MOVE_UP} },
1248 { GDK_SHIFT_MASK, 0, GDK_G, move, {.i = XT_MOVE_BOTTOM} },
1249 { 0, 0, GDK_End, move, {.i = XT_MOVE_BOTTOM} },
1250 { 0, 0, GDK_Home, move, {.i = XT_MOVE_TOP} },
1251 { 0, GDK_g, GDK_g, move, {.i = XT_MOVE_TOP} }, /* XXX make this work */
1252 { 0, 0, GDK_space, move, {.i = XT_MOVE_PAGEDOWN} },
1253 { GDK_CONTROL_MASK, 0, GDK_f, move, {.i = XT_MOVE_PAGEDOWN} },
1254 { GDK_CONTROL_MASK, 0, GDK_d, move, {.i = XT_MOVE_HALFDOWN} },
1255 { 0, 0, GDK_Page_Down, move, {.i = XT_MOVE_PAGEDOWN} },
1256 { 0, 0, GDK_Page_Up, move, {.i = XT_MOVE_PAGEUP} },
1257 { GDK_CONTROL_MASK, 0, GDK_b, move, {.i = XT_MOVE_PAGEUP} },
1258 { GDK_CONTROL_MASK, 0, GDK_u, move, {.i = XT_MOVE_HALFUP} },
1259 /* horizontal movement */
1260 { 0, 0, GDK_l, move, {.i = XT_MOVE_RIGHT} },
1261 { 0, 0, GDK_Right, move, {.i = XT_MOVE_RIGHT} },
1262 { 0, 0, GDK_Left, move, {.i = XT_MOVE_LEFT} },
1263 { 0, 0, GDK_h, move, {.i = XT_MOVE_LEFT} },
1264 { GDK_SHIFT_MASK, 0, GDK_dollar, move, {.i = XT_MOVE_FARRIGHT} },
1265 { 0, 0, GDK_0, move, {.i = XT_MOVE_FARLEFT} },
1267 /* tabs */
1268 { GDK_CONTROL_MASK, 0, GDK_t, tabaction, {.i = XT_TAB_NEW} },
1269 { GDK_CONTROL_MASK, 0, GDK_w, tabaction, {.i = XT_TAB_DELETE} },
1270 { GDK_CONTROL_MASK, 0, GDK_1, movetab, {.i = 1} },
1271 { GDK_CONTROL_MASK, 0, GDK_2, movetab, {.i = 2} },
1272 { GDK_CONTROL_MASK, 0, GDK_3, movetab, {.i = 3} },
1273 { GDK_CONTROL_MASK, 0, GDK_4, movetab, {.i = 4} },
1274 { GDK_CONTROL_MASK, 0, GDK_5, movetab, {.i = 5} },
1275 { GDK_CONTROL_MASK, 0, GDK_6, movetab, {.i = 6} },
1276 { GDK_CONTROL_MASK, 0, GDK_7, movetab, {.i = 7} },
1277 { GDK_CONTROL_MASK, 0, GDK_8, movetab, {.i = 8} },
1278 { GDK_CONTROL_MASK, 0, GDK_9, movetab, {.i = 9} },
1279 { GDK_CONTROL_MASK, 0, GDK_0, movetab, {.i = 10} },
1280 { GDK_CONTROL_MASK|GDK_SHIFT_MASK, 0, GDK_less, movetab, {.i = XT_TAB_FIRST} },
1281 { GDK_CONTROL_MASK|GDK_SHIFT_MASK, 0, GDK_greater, movetab, {.i = XT_TAB_LAST} },
1282 { GDK_CONTROL_MASK, 0, GDK_minus, resizetab, {.i = -1} },
1283 { GDK_CONTROL_MASK|GDK_SHIFT_MASK, 0, GDK_plus, resizetab, {.i = 1} },
1284 { GDK_CONTROL_MASK, 0, GDK_equal, resizetab, {.i = 1} },
1287 struct cmd {
1288 char *cmd;
1289 int params;
1290 int (*func)(struct tab *, struct karg *);
1291 struct karg arg;
1292 } cmds[] = {
1293 { "q!", 0, quit, {0} },
1294 { "qa", 0, quit, {0} },
1295 { "qa!", 0, quit, {0} },
1296 { "help", 0, help, {0} },
1298 /* favorites */
1299 { "fav", 0, favorites, {0} },
1300 { "favadd", 0, favadd, {0} },
1302 { "1", 0, move, {.i = XT_MOVE_TOP} },
1304 /* tabs */
1305 { "o", 1, tabaction, {.i = XT_TAB_OPEN} },
1306 { "op", 1, tabaction, {.i = XT_TAB_OPEN} },
1307 { "open", 1, tabaction, {.i = XT_TAB_OPEN} },
1308 { "tabnew", 1, tabaction, {.i = XT_TAB_NEW} },
1309 { "tabedit", 1, tabaction, {.i = XT_TAB_NEW} },
1310 { "tabe", 1, tabaction, {.i = XT_TAB_NEW} },
1311 { "tabclose", 0, tabaction, {.i = XT_TAB_DELETE} },
1312 { "tabc", 0, tabaction, {.i = XT_TAB_DELETE} },
1313 { "quit", 0, tabaction, {.i = XT_TAB_DELQUIT} },
1314 { "q", 0, tabaction, {.i = XT_TAB_DELQUIT} },
1315 /* XXX add count to these commands */
1316 { "tabfirst", 0, movetab, {.i = XT_TAB_FIRST} },
1317 { "tabfir", 0, movetab, {.i = XT_TAB_FIRST} },
1318 { "tabrewind", 0, movetab, {.i = XT_TAB_FIRST} },
1319 { "tabr", 0, movetab, {.i = XT_TAB_FIRST} },
1320 { "tablast", 0, movetab, {.i = XT_TAB_LAST} },
1321 { "tabl", 0, movetab, {.i = XT_TAB_LAST} },
1322 { "tabprevious", 0, movetab, {.i = XT_TAB_PREV} },
1323 { "tabp", 0, movetab, {.i = XT_TAB_PREV} },
1324 { "tabnext", 0, movetab, {.i = XT_TAB_NEXT} },
1325 { "tabn", 0, movetab, {.i = XT_TAB_NEXT} },
1327 /* settings */
1328 { "set", 1, set, {0} },
1331 gboolean
1332 tab_close_cb(GtkWidget *button, struct tab *t)
1334 DNPRINTF(XT_D_TAB, "tab_close_cb: tab %d\n", t->tab_id);
1336 delete_tab(t);
1338 return (FALSE);
1341 void
1342 focus_uri_entry_cb(GtkWidget* w, GtkDirectionType direction, struct tab *t)
1344 DNPRINTF(XT_D_URL, "focus_uri_entry_cb: tab %d focus_wv %d\n",
1345 t->tab_id, t->focus_wv);
1347 if (t == NULL)
1348 errx(1, "focus_uri_entry_cb");
1350 /* focus on wv instead */
1351 if (t->focus_wv)
1352 gtk_widget_grab_focus(GTK_WIDGET(t->wv));
1355 void
1356 activate_uri_entry_cb(GtkWidget* entry, struct tab *t)
1358 const gchar *uri = gtk_entry_get_text(GTK_ENTRY(entry));
1359 char *newuri = NULL;
1361 DNPRINTF(XT_D_URL, "activate_uri_entry_cb: %s\n", uri);
1363 if (t == NULL)
1364 errx(1, "activate_uri_entry_cb");
1366 if (uri == NULL)
1367 errx(1, "uri");
1369 uri += strspn(uri, "\t ");
1371 if (valid_url_type((char *)uri)) {
1372 newuri = guess_url_type((char *)uri);
1373 uri = newuri;
1376 webkit_web_view_load_uri(t->wv, uri);
1377 gtk_widget_grab_focus(GTK_WIDGET(t->wv));
1379 if (newuri)
1380 g_free(newuri);
1383 void
1384 activate_search_entry_cb(GtkWidget* entry, struct tab *t)
1386 const gchar *search = gtk_entry_get_text(GTK_ENTRY(entry));
1387 char *newuri = NULL;
1389 DNPRINTF(XT_D_URL, "activate_search_entry_cb: %s\n", search);
1391 if (t == NULL)
1392 errx(1, "activate_search_entry_cb");
1394 if (search_string == NULL) {
1395 warnx("no search_string");
1396 return;
1399 newuri = g_strdup_printf(search_string, search);
1401 webkit_web_view_load_uri(t->wv, newuri);
1402 gtk_widget_grab_focus(GTK_WIDGET(t->wv));
1404 if (newuri)
1405 g_free(newuri);
1408 void
1409 notify_load_status_cb(WebKitWebView* wview, GParamSpec* pspec, struct tab *t)
1411 GdkColor color;
1412 WebKitWebFrame *frame;
1413 const gchar *uri;
1415 if (t == NULL)
1416 errx(1, "notify_load_status_cb");
1418 switch (webkit_web_view_get_load_status(wview)) {
1419 case WEBKIT_LOAD_PROVISIONAL:
1420 gtk_widget_show(t->spinner);
1421 gtk_spinner_start(GTK_SPINNER(t->spinner));
1422 gtk_label_set_text(GTK_LABEL(t->label), "Loading");
1424 gtk_widget_set_sensitive(GTK_WIDGET(t->stop), TRUE);
1425 t->focus_wv = 1;
1427 /* take focus if we are visible */
1428 if (gtk_notebook_get_current_page(notebook) == t->tab_id)
1429 gtk_widget_grab_focus(GTK_WIDGET(t->wv));
1431 break;
1433 case WEBKIT_LOAD_COMMITTED:
1434 frame = webkit_web_view_get_main_frame(wview);
1435 uri = webkit_web_frame_get_uri(frame);
1436 if (uri)
1437 gtk_entry_set_text(GTK_ENTRY(t->uri_entry), uri);
1439 /* color uri_entry */
1440 if (uri && !strncmp(uri, "https://", strlen("https://")))
1441 gdk_color_parse("green", &color);
1442 else
1443 gdk_color_parse("white", &color);
1444 gtk_widget_modify_base(t->uri_entry, GTK_STATE_NORMAL, &color);
1446 break;
1448 case WEBKIT_LOAD_FIRST_VISUALLY_NON_EMPTY_LAYOUT:
1449 uri = webkit_web_view_get_title(wview);
1450 if (uri == NULL) {
1451 frame = webkit_web_view_get_main_frame(wview);
1452 uri = webkit_web_frame_get_uri(frame);
1454 gtk_label_set_text(GTK_LABEL(t->label), uri);
1455 gtk_window_set_title(GTK_WINDOW(main_window), uri);
1456 break;
1458 case WEBKIT_LOAD_FINISHED:
1459 #if WEBKIT_CHECK_VERSION(1, 1, 18)
1460 case WEBKIT_LOAD_FAILED:
1461 #endif
1462 gtk_spinner_stop(GTK_SPINNER(t->spinner));
1463 gtk_widget_hide(t->spinner);
1464 default:
1465 gtk_widget_set_sensitive(GTK_WIDGET(t->stop), FALSE);
1466 break;
1469 gtk_widget_set_sensitive(GTK_WIDGET(t->backward),
1470 webkit_web_view_can_go_back(wview));
1472 gtk_widget_set_sensitive(GTK_WIDGET(t->forward),
1473 webkit_web_view_can_go_forward(wview));
1476 void
1477 webview_load_finished_cb(WebKitWebView *wv, WebKitWebFrame *wf, struct tab *t)
1479 run_script(t, JS_HINTING);
1482 void
1483 webview_progress_changed_cb(WebKitWebView *wv, int progress, struct tab *t)
1485 gtk_entry_set_progress_fraction(GTK_ENTRY(t->uri_entry),
1486 progress == 100 ? 0 : (double)progress / 100);
1490 webview_nw_cb(WebKitWebView *wv, WebKitWebFrame *wf,
1491 WebKitNetworkRequest *request, WebKitWebNavigationAction *na,
1492 WebKitWebPolicyDecision *pd, struct tab *t)
1494 char *uri;
1496 if (t == NULL)
1497 errx(1, "webview_nw_cb");
1499 DNPRINTF(XT_D_NAV, "webview_nw_cb: %s\n",
1500 webkit_network_request_get_uri(request));
1502 /* open in current tab */
1503 uri = (char *)webkit_network_request_get_uri(request);
1504 webkit_web_view_load_uri(t->wv, uri);
1505 webkit_web_policy_decision_ignore(pd);
1507 return (TRUE); /* we made the decission */
1511 webview_npd_cb(WebKitWebView *wv, WebKitWebFrame *wf,
1512 WebKitNetworkRequest *request, WebKitWebNavigationAction *na,
1513 WebKitWebPolicyDecision *pd, struct tab *t)
1515 char *uri;
1517 if (t == NULL)
1518 errx(1, "webview_npd_cb");
1520 DNPRINTF(XT_D_NAV, "webview_npd_cb: ctrl_click %d %s\n",
1521 t->ctrl_click,
1522 webkit_network_request_get_uri(request));
1524 uri = (char *)webkit_network_request_get_uri(request);
1525 if (t->ctrl_click) {
1526 t->ctrl_click = 0;
1527 create_new_tab(uri, ctrl_click_focus);
1528 webkit_web_policy_decision_ignore(pd);
1529 return (TRUE); /* we made the decission */
1532 webkit_web_policy_decision_use(pd);
1533 return (TRUE); /* we made the decission */
1536 WebKitWebView *
1537 webview_cwv_cb(WebKitWebView *wv, WebKitWebFrame *wf, struct tab *t)
1539 if (t == NULL)
1540 errx(1, "webview_cwv_cb");
1542 DNPRINTF(XT_D_NAV, "webview_cwv_cb: %s\n",
1543 webkit_web_view_get_uri(wv));
1545 return (wv);
1549 webview_event_cb(GtkWidget *w, GdkEventButton *e, struct tab *t)
1551 /* we can not eat the event without throwing gtk off so defer it */
1553 /* catch middle click */
1554 if (e->type == GDK_BUTTON_RELEASE && e->button == 2) {
1555 t->ctrl_click = 1;
1556 goto done;
1559 /* catch ctrl click */
1560 if (e->type == GDK_BUTTON_RELEASE &&
1561 CLEAN(e->state) == GDK_CONTROL_MASK)
1562 t->ctrl_click = 1;
1563 else
1564 t->ctrl_click = 0;
1565 done:
1566 return (XT_CB_PASSTHROUGH);
1570 run_mimehandler(struct tab *t, char *mime_type, WebKitNetworkRequest *request)
1572 struct mime_type *m;
1574 m = find_mime_type(mime_type);
1575 if (m == NULL)
1576 return (1);
1578 switch (fork()) {
1579 case -1:
1580 err(1, "fork");
1581 /* NOTREACHED */
1582 case 0:
1583 break;
1584 default:
1585 return (0);
1588 /* child */
1589 execlp(m->mt_action, m->mt_action,
1590 webkit_network_request_get_uri(request), (void *)NULL);
1592 _exit(0);
1594 /* NOTREACHED */
1595 return (0);
1599 webview_mimetype_cb(WebKitWebView *wv, WebKitWebFrame *frame,
1600 WebKitNetworkRequest *request, char *mime_type,
1601 WebKitWebPolicyDecision *decision, struct tab *t)
1603 if (t == NULL)
1604 errx(1, "webview_mimetype_cb");
1606 DNPRINTF(XT_D_DOWNLOAD, "webview_mimetype_cb: tab %d mime %s\n",
1607 t->tab_id, mime_type);
1609 if (run_mimehandler(t, mime_type, request) == 0) {
1610 webkit_web_policy_decision_ignore(decision);
1611 gtk_widget_grab_focus(GTK_WIDGET(t->wv));
1612 return (TRUE);
1615 if (webkit_web_view_can_show_mime_type(wv, mime_type) == FALSE) {
1616 webkit_web_policy_decision_download(decision);
1617 return (TRUE);
1620 return (FALSE);
1624 webview_download_cb(WebKitWebView *wv, WebKitDownload *download, struct tab *t)
1626 const gchar *filename;
1627 char *uri = NULL;
1629 if (download == NULL || t == NULL)
1630 errx(1, "webview_download_cb: invalid pointers");
1632 filename = webkit_download_get_suggested_filename(download);
1633 if (filename == NULL)
1634 return (FALSE); /* abort download */
1636 uri = g_strdup_printf("file://%s/%s", download_dir, filename);
1638 DNPRINTF(XT_D_DOWNLOAD, "webview_download_cb: tab %d filename %s "
1639 "local %s\n",
1640 t->tab_id, filename, uri);
1642 webkit_download_set_destination_uri(download, uri);
1643 webkit_download_start(download);
1645 if (uri)
1646 g_free(uri);
1648 return (TRUE); /* start download */
1651 /* XXX currently unused */
1652 void
1653 webview_hover_cb(WebKitWebView *wv, gchar *title, gchar *uri, struct tab *t)
1655 DNPRINTF(XT_D_KEY, "webview_hover_cb: %s %s\n", title, uri);
1657 if (t == NULL)
1658 errx(1, "webview_hover_cb");
1660 if (uri) {
1661 if (t->hover) {
1662 g_free(t->hover);
1663 t->hover = NULL;
1665 t->hover = g_strdup(uri);
1666 } else if (t->hover) {
1667 g_free(t->hover);
1668 t->hover = NULL;
1673 webview_keypress_cb(GtkWidget *w, GdkEventKey *e, struct tab *t)
1675 int i;
1676 char s[2], buf[128];
1677 const char *errstr = NULL;
1678 long long link;
1680 /* don't use w directly; use t->whatever instead */
1682 if (t == NULL)
1683 errx(1, "webview_keypress_cb");
1685 DNPRINTF(XT_D_KEY, "webview_keypress_cb: keyval 0x%x mask 0x%x t %p\n",
1686 e->keyval, e->state, t);
1688 if (t->hints_on) {
1689 /* ESC */
1690 if (CLEAN(e->state) == 0 && e->keyval == GDK_Escape) {
1691 disable_hints(t);
1692 return (XT_CB_HANDLED);
1695 /* RETURN */
1696 if (CLEAN(e->state) == 0 && e->keyval == GDK_Return) {
1697 link = strtonum(t->hint_num, 1, 1000, &errstr);
1698 if (errstr) {
1699 /* we have a string */
1700 } else {
1701 /* we have a number */
1702 snprintf(buf, sizeof buf, "vimprobable_fire(%s)",
1703 t->hint_num);
1704 run_script(t, buf);
1706 disable_hints(t);
1709 /* BACKSPACE */
1710 /* XXX unfuck this */
1711 if (CLEAN(e->state) == 0 && e->keyval == GDK_BackSpace) {
1712 if (t->hint_mode == XT_HINT_NUMERICAL) {
1713 /* last input was numerical */
1714 int l;
1715 l = strlen(t->hint_num);
1716 if (l > 0) {
1717 l--;
1718 if (l == 0) {
1719 disable_hints(t);
1720 enable_hints(t);
1721 } else {
1722 t->hint_num[l] = '\0';
1723 goto num;
1726 } else if (t->hint_mode == XT_HINT_ALPHANUM) {
1727 /* last input was alphanumerical */
1728 int l;
1729 l = strlen(t->hint_buf);
1730 if (l > 0) {
1731 l--;
1732 if (l == 0) {
1733 disable_hints(t);
1734 enable_hints(t);
1735 } else {
1736 t->hint_buf[l] = '\0';
1737 goto anum;
1740 } else {
1741 /* bogus */
1742 disable_hints(t);
1746 /* numerical input */
1747 if (CLEAN(e->state) == 0 &&
1748 ((e->keyval >= GDK_0 && e->keyval <= GDK_9) || (e->keyval >= GDK_KP_0 && e->keyval <= GDK_KP_9))) {
1749 snprintf(s, sizeof s, "%c", e->keyval);
1750 strlcat(t->hint_num, s, sizeof t->hint_num);
1751 DNPRINTF(XT_D_JS, "webview_keypress_cb: numerical %s\n",
1752 t->hint_num);
1753 num:
1754 link = strtonum(t->hint_num, 1, 1000, &errstr);
1755 if (errstr) {
1756 DNPRINTF(XT_D_JS, "webview_keypress_cb: invalid link number\n");
1757 disable_hints(t);
1758 } else {
1759 snprintf(buf, sizeof buf, "vimprobable_update_hints(%s)",
1760 t->hint_num);
1761 t->hint_mode = XT_HINT_NUMERICAL;
1762 run_script(t, buf);
1765 /* empty the counter buffer */
1766 bzero(t->hint_buf, sizeof t->hint_buf);
1767 return (XT_CB_HANDLED);
1770 /* alphanumerical input */
1771 if (
1772 (CLEAN(e->state) == 0 && e->keyval >= GDK_a && e->keyval <= GDK_z) ||
1773 (CLEAN(e->state) == GDK_SHIFT_MASK && e->keyval >= GDK_A && e->keyval <= GDK_Z) ||
1774 (CLEAN(e->state) == 0 && ((e->keyval >= GDK_0 && e->keyval <= GDK_9) ||
1775 ((e->keyval >= GDK_KP_0 && e->keyval <= GDK_KP_9) && (t->hint_mode != XT_HINT_NUMERICAL))))) {
1776 snprintf(s, sizeof s, "%c", e->keyval);
1777 strlcat(t->hint_buf, s, sizeof t->hint_buf);
1778 DNPRINTF(XT_D_JS, "webview_keypress_cb: alphanumerical %s\n",
1779 t->hint_buf);
1780 anum:
1781 snprintf(buf, sizeof buf, "vimprobable_cleanup()");
1782 run_script(t, buf);
1784 snprintf(buf, sizeof buf, "vimprobable_show_hints('%s')",
1785 t->hint_buf);
1786 t->hint_mode = XT_HINT_ALPHANUM;
1787 run_script(t, buf);
1789 /* empty the counter buffer */
1790 bzero(t->hint_num, sizeof t->hint_num);
1791 return (XT_CB_HANDLED);
1794 return (XT_CB_HANDLED);
1797 for (i = 0; i < LENGTH(keys); i++)
1798 if (e->keyval == keys[i].key && CLEAN(e->state) ==
1799 keys[i].mask) {
1800 keys[i].func(t, &keys[i].arg);
1801 return (XT_CB_HANDLED);
1804 return (XT_CB_PASSTHROUGH);
1808 cmd_keyrelease_cb(GtkEntry *w, GdkEventKey *e, struct tab *t)
1810 const gchar *c = gtk_entry_get_text(w);
1811 GdkColor color;
1812 int forward = TRUE;
1814 DNPRINTF(XT_D_CMD, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
1815 e->keyval, e->state, t);
1817 if (t == NULL)
1818 errx(1, "cmd_keyrelease_cb");
1820 DNPRINTF(XT_D_CMD, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
1821 e->keyval, e->state, t);
1823 if (c[0] == ':')
1824 goto done;
1825 if (strlen(c) == 1)
1826 goto done;
1828 if (c[0] == '/')
1829 forward = TRUE;
1830 else if (c[0] == '?')
1831 forward = FALSE;
1832 else
1833 goto done;
1835 /* search */
1836 if (webkit_web_view_search_text(t->wv, &c[1], FALSE, forward, TRUE) ==
1837 FALSE) {
1838 /* not found, mark red */
1839 gdk_color_parse("red", &color);
1840 gtk_widget_modify_base(t->cmd, GTK_STATE_NORMAL, &color);
1841 /* unmark and remove selection */
1842 webkit_web_view_unmark_text_matches(t->wv);
1843 /* my kingdom for a way to unselect text in webview */
1844 } else {
1845 /* found, highlight all */
1846 webkit_web_view_unmark_text_matches(t->wv);
1847 webkit_web_view_mark_text_matches(t->wv, &c[1], FALSE, 0);
1848 webkit_web_view_set_highlight_text_matches(t->wv, TRUE);
1849 gdk_color_parse("white", &color);
1850 gtk_widget_modify_base(t->cmd, GTK_STATE_NORMAL, &color);
1852 done:
1853 return (XT_CB_PASSTHROUGH);
1856 #if 0
1858 cmd_complete(struct tab *t, char *s)
1860 int i;
1861 GtkEntry *w = GTK_ENTRY(t->cmd);
1863 DNPRINTF(XT_D_CMD, "cmd_keypress_cb: complete %s\n", s);
1865 for (i = 0; i < LENGTH(cmds); i++) {
1866 if (!strncasecmp(cmds[i].cmd, s, strlen(s))) {
1867 fprintf(stderr, "match %s %d\n", cmds[i].cmd, strcasecmp(cmds[i].cmd, s));
1868 #if 0
1869 gtk_entry_set_text(w, ":");
1870 gtk_entry_append_text(w, cmds[i].cmd);
1871 gtk_editable_set_position(GTK_EDITABLE(w), -1);
1872 #endif
1876 return (0);
1878 #endif
1881 cmd_keypress_cb(GtkEntry *w, GdkEventKey *e, struct tab *t)
1883 int rv = XT_CB_HANDLED;
1884 const gchar *c = gtk_entry_get_text(w);
1886 if (t == NULL)
1887 errx(1, "cmd_keypress_cb");
1889 DNPRINTF(XT_D_CMD, "cmd_keypress_cb: keyval 0x%x mask 0x%x t %p\n",
1890 e->keyval, e->state, t);
1892 /* sanity */
1893 if (c == NULL)
1894 e->keyval = GDK_Escape;
1895 else if (!(c[0] == ':' || c[0] == '/' || c[0] == '?'))
1896 e->keyval = GDK_Escape;
1898 switch (e->keyval) {
1899 #if 0
1900 case GDK_Tab:
1901 if (c[0] != ':')
1902 goto done;
1904 if (strchr (c, ' ')) {
1905 /* par completion */
1906 fprintf(stderr, "completeme par\n");
1907 goto done;
1910 cmd_complete(t, (char *)&c[1]);
1912 goto done;
1913 #endif
1914 case GDK_BackSpace:
1915 if (!(!strcmp(c, ":") || !strcmp(c, "/") || !strcmp(c, "?")))
1916 break;
1917 /* FALLTHROUGH */
1918 case GDK_Escape:
1919 gtk_widget_hide(t->cmd);
1920 gtk_widget_grab_focus(GTK_WIDGET(t->wv));
1921 goto done;
1924 rv = XT_CB_PASSTHROUGH;
1925 done:
1926 return (rv);
1930 cmd_focusout_cb(GtkWidget *w, GdkEventFocus *e, struct tab *t)
1932 if (t == NULL)
1933 errx(1, "cmd_focusout_cb");
1935 DNPRINTF(XT_D_CMD, "cmd_focusout_cb: tab %d focus_wv %d\n",
1936 t->tab_id, t->focus_wv);
1938 /* abort command when losing focus */
1939 gtk_widget_hide(t->cmd);
1940 if (t->focus_wv)
1941 gtk_widget_grab_focus(GTK_WIDGET(t->wv));
1942 else
1943 gtk_widget_grab_focus(GTK_WIDGET(t->uri_entry));
1945 return (XT_CB_PASSTHROUGH);
1948 void
1949 cmd_activate_cb(GtkEntry *entry, struct tab *t)
1951 int i;
1952 char *s;
1953 const gchar *c = gtk_entry_get_text(entry);
1955 if (t == NULL)
1956 errx(1, "cmd_activate_cb");
1958 DNPRINTF(XT_D_CMD, "cmd_activate_cb: tab %d %s\n", t->tab_id, c);
1960 /* sanity */
1961 if (c == NULL)
1962 goto done;
1963 else if (!(c[0] == ':' || c[0] == '/' || c[0] == '?'))
1964 goto done;
1965 if (strlen(c) < 2)
1966 goto done;
1967 s = (char *)&c[1];
1969 if (c[0] == '/' || c[0] == '?') {
1970 if (t->search_text) {
1971 g_free(t->search_text);
1972 t->search_text = NULL;
1975 t->search_text = g_strdup(s);
1976 t->search_forward = c[0] == '/';
1978 goto done;
1981 for (i = 0; i < LENGTH(cmds); i++)
1982 if (cmds[i].params) {
1983 if (!strncmp(s, cmds[i].cmd, strlen(cmds[i].cmd))) {
1984 cmds[i].arg.s = g_strdup(s);
1985 cmds[i].func(t, &cmds[i].arg);
1987 } else {
1988 if (!strcmp(s, cmds[i].cmd))
1989 cmds[i].func(t, &cmds[i].arg);
1992 done:
1993 gtk_widget_hide(t->cmd);
1996 void
1997 backward_cb(GtkWidget *w, struct tab *t)
1999 if (t == NULL)
2000 errx(1, "backward_cb");
2002 DNPRINTF(XT_D_NAV, "backward_cb: tab %d\n", t->tab_id);
2004 webkit_web_view_go_back(t->wv);
2007 void
2008 forward_cb(GtkWidget *w, struct tab *t)
2010 if (t == NULL)
2011 errx(1, "forward_cb");
2013 DNPRINTF(XT_D_NAV, "forward_cb: tab %d\n", t->tab_id);
2015 webkit_web_view_go_forward(t->wv);
2018 void
2019 stop_cb(GtkWidget *w, struct tab *t)
2021 WebKitWebFrame *frame;
2023 if (t == NULL)
2024 errx(1, "stop_cb");
2026 DNPRINTF(XT_D_NAV, "stop_cb: tab %d\n", t->tab_id);
2028 frame = webkit_web_view_get_main_frame(t->wv);
2029 if (frame == NULL) {
2030 warnx("stop_cb: no frame");
2031 return;
2034 webkit_web_frame_stop_loading(frame);
2037 void
2038 setup_webkit(struct tab *t)
2040 g_object_set((GObject *)t->settings,
2041 "user-agent", t->user_agent, (char *)NULL);
2042 g_object_set((GObject *)t->settings,
2043 "enable-scripts", enable_scripts, (char *)NULL);
2044 g_object_set((GObject *)t->settings,
2045 "enable-plugins", enable_plugins, (char *)NULL);
2046 adjustfont_webkit(t, XT_FONT_SET);
2048 webkit_web_view_set_settings(t->wv, t->settings);
2051 GtkWidget *
2052 create_browser(struct tab *t)
2054 GtkWidget *w;
2055 gchar *strval;
2057 if (t == NULL)
2058 errx(1, "create_browser");
2060 t->sb_h = GTK_SCROLLBAR(gtk_hscrollbar_new(NULL));
2061 t->sb_v = GTK_SCROLLBAR(gtk_vscrollbar_new(NULL));
2062 t->adjust_h = gtk_range_get_adjustment(GTK_RANGE(t->sb_h));
2063 t->adjust_v = gtk_range_get_adjustment(GTK_RANGE(t->sb_v));
2065 w = gtk_scrolled_window_new(t->adjust_h, t->adjust_v);
2066 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(w),
2067 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2069 t->wv = WEBKIT_WEB_VIEW(webkit_web_view_new());
2070 gtk_container_add(GTK_CONTAINER(w), GTK_WIDGET(t->wv));
2072 /* set defaults */
2073 t->settings = webkit_web_settings_new();
2075 g_object_get((GObject *)t->settings, "user-agent", &strval, (char *)NULL);
2076 t->user_agent = g_strdup_printf("%s %s+", strval, version);
2077 g_free(strval);
2079 setup_webkit(t);
2081 return (w);
2084 GtkWidget *
2085 create_window(void)
2087 GtkWidget *w;
2089 w = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2090 gtk_window_set_default_size(GTK_WINDOW(w), 1024, 768);
2091 gtk_widget_set_name(w, "xxxterm");
2092 gtk_window_set_wmclass(GTK_WINDOW(w), "xxxterm", "XXXTerm");
2093 g_signal_connect(G_OBJECT(w), "delete_event",
2094 G_CALLBACK (gtk_main_quit), NULL);
2096 return (w);
2099 GtkWidget *
2100 create_toolbar(struct tab *t)
2102 GtkWidget *toolbar = gtk_toolbar_new();
2103 GtkToolItem *i;
2105 #if GTK_CHECK_VERSION(2,15,0)
2106 gtk_orientable_set_orientation(GTK_ORIENTABLE(toolbar),
2107 GTK_ORIENTATION_HORIZONTAL);
2108 #else
2109 gtk_toolbar_set_orientation(GTK_TOOLBAR(toolbar),
2110 GTK_ORIENTATION_HORIZONTAL);
2111 #endif
2112 gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_BOTH_HORIZ);
2114 if (fancy_bar) {
2115 /* backward button */
2116 t->backward = gtk_tool_button_new_from_stock(GTK_STOCK_GO_BACK);
2117 gtk_widget_set_sensitive(GTK_WIDGET(t->backward), FALSE);
2118 g_signal_connect(G_OBJECT(t->backward), "clicked",
2119 G_CALLBACK(backward_cb), t);
2120 gtk_toolbar_insert(GTK_TOOLBAR(toolbar), t->backward, -1);
2122 /* forward button */
2123 t->forward =
2124 gtk_tool_button_new_from_stock(GTK_STOCK_GO_FORWARD);
2125 gtk_widget_set_sensitive(GTK_WIDGET(t->forward), FALSE);
2126 g_signal_connect(G_OBJECT(t->forward), "clicked",
2127 G_CALLBACK(forward_cb), t);
2128 gtk_toolbar_insert(GTK_TOOLBAR(toolbar), t->forward, -1);
2130 /* stop button */
2131 t->stop = gtk_tool_button_new_from_stock(GTK_STOCK_STOP);
2132 gtk_widget_set_sensitive(GTK_WIDGET(t->stop), FALSE);
2133 g_signal_connect(G_OBJECT(t->stop), "clicked",
2134 G_CALLBACK(stop_cb), t);
2135 gtk_toolbar_insert(GTK_TOOLBAR(toolbar), t->stop, -1);
2138 /* uri entry */
2139 i = gtk_tool_item_new();
2140 gtk_tool_item_set_expand(i, TRUE);
2141 t->uri_entry = gtk_entry_new();
2142 gtk_container_add(GTK_CONTAINER(i), t->uri_entry);
2143 g_signal_connect(G_OBJECT(t->uri_entry), "activate",
2144 G_CALLBACK(activate_uri_entry_cb), t);
2145 gtk_toolbar_insert(GTK_TOOLBAR(toolbar), i, -1);
2147 /* search entry */
2148 if (fancy_bar && search_string) {
2149 i = gtk_tool_item_new();
2150 t->search_entry = gtk_entry_new();
2151 gtk_entry_set_width_chars(GTK_ENTRY(t->search_entry), 30);
2152 gtk_container_add(GTK_CONTAINER(i), t->search_entry);
2153 g_signal_connect(G_OBJECT(t->search_entry), "activate",
2154 G_CALLBACK(activate_search_entry_cb), t);
2155 gtk_toolbar_insert(GTK_TOOLBAR(toolbar), i, -1);
2158 return (toolbar);
2161 void
2162 delete_tab(struct tab *t)
2164 DNPRINTF(XT_D_TAB, "delete_tab: %p\n", t);
2166 if (t == NULL)
2167 return;
2169 TAILQ_REMOVE(&tabs, t, entry);
2170 if (TAILQ_EMPTY(&tabs))
2171 create_new_tab(NULL, 1);
2173 gtk_widget_destroy(t->vbox);
2175 g_free(t->user_agent);
2176 g_free(t);
2178 TAILQ_FOREACH(t, &tabs, entry)
2179 t->tab_id = gtk_notebook_page_num(notebook, t->vbox);
2182 void
2183 adjustfont_webkit(struct tab *t, int adjust)
2185 if (t == NULL)
2186 errx(1, "adjustfont_webkit");
2188 if (adjust == XT_FONT_SET)
2189 t->font_size = default_font_size;
2191 t->font_size += adjust;
2192 g_object_set((GObject *)t->settings, "default-font-size",
2193 t->font_size, (char *)NULL);
2194 g_object_get((GObject *)t->settings, "default-font-size",
2195 &t->font_size, (char *)NULL);
2198 void
2199 create_new_tab(char *title, int focus)
2201 struct tab *t;
2202 int load = 1;
2203 char *newuri = NULL;
2204 GtkWidget *image, *b, *button;
2206 DNPRINTF(XT_D_TAB, "create_new_tab: title %s focus %d\n", title, focus);
2208 if (tabless && !TAILQ_EMPTY(&tabs)) {
2209 DNPRINTF(XT_D_TAB, "create_new_tab: new tab rejected\n");
2210 return;
2213 t = g_malloc0(sizeof *t);
2214 TAILQ_INSERT_TAIL(&tabs, t, entry);
2216 if (title == NULL) {
2217 title = "(untitled)";
2218 load = 0;
2219 } else {
2220 if (valid_url_type(title)) {
2221 newuri = guess_url_type(title);
2222 title = newuri;
2226 t->vbox = gtk_vbox_new(FALSE, 0);
2228 /* label + button for tab */
2229 b = gtk_hbox_new(FALSE, 0);
2230 t->spinner = gtk_spinner_new ();
2231 t->label = gtk_label_new(title);
2232 image = gtk_image_new_from_stock(GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU);
2233 button = gtk_button_new();
2234 gtk_button_set_image(GTK_BUTTON(button), image);
2235 gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
2236 gtk_button_set_focus_on_click(GTK_BUTTON(button), FALSE);
2237 gtk_widget_set_size_request(t->label, 100, -1);
2238 gtk_box_pack_start(GTK_BOX(b), t->spinner, FALSE, FALSE, 0);
2239 gtk_box_pack_start(GTK_BOX(b), t->label, FALSE, FALSE, 0);
2240 gtk_box_pack_start(GTK_BOX(b), button, FALSE, FALSE, 0);
2242 /* toolbar */
2243 t->toolbar = create_toolbar(t);
2244 gtk_box_pack_start(GTK_BOX(t->vbox), t->toolbar, FALSE, FALSE, 0);
2246 /* browser */
2247 t->browser_win = create_browser(t);
2248 gtk_box_pack_start(GTK_BOX(t->vbox), t->browser_win, TRUE, TRUE, 0);
2250 /* command entry */
2251 t->cmd = gtk_entry_new();
2252 gtk_entry_set_inner_border(GTK_ENTRY(t->cmd), NULL);
2253 gtk_entry_set_has_frame(GTK_ENTRY(t->cmd), FALSE);
2254 gtk_box_pack_end(GTK_BOX(t->vbox), t->cmd, FALSE, FALSE, 0);
2256 /* and show it all */
2257 gtk_widget_show_all(b);
2258 gtk_widget_show_all(t->vbox);
2259 t->tab_id = gtk_notebook_append_page(notebook, t->vbox, b);
2261 /* turn spinner off if we are a new tab without uri */
2262 if (!load) {
2263 gtk_spinner_stop(GTK_SPINNER(t->spinner));
2264 gtk_widget_hide(t->spinner);
2267 /* make notebook tabs reorderable */
2268 gtk_notebook_set_tab_reorderable(notebook, t->vbox, TRUE);
2270 g_object_connect((GObject*)t->cmd,
2271 "signal::key-press-event", (GCallback)cmd_keypress_cb, t,
2272 "signal::key-release-event", (GCallback)cmd_keyrelease_cb, t,
2273 "signal::focus-out-event", (GCallback)cmd_focusout_cb, t,
2274 "signal::activate", (GCallback)cmd_activate_cb, t,
2275 (char *)NULL);
2277 g_object_connect((GObject*)t->wv,
2278 "signal-after::key-press-event", (GCallback)webview_keypress_cb, t,
2279 /* "signal::hovering-over-link", (GCallback)webview_hover_cb, t, */
2280 "signal::download-requested", (GCallback)webview_download_cb, t,
2281 "signal::mime-type-policy-decision-requested", (GCallback)webview_mimetype_cb, t,
2282 "signal::navigation-policy-decision-requested", (GCallback)webview_npd_cb, t,
2283 "signal::new-window-policy-decision-requested", (GCallback)webview_nw_cb, t,
2284 "signal::create-web-view", (GCallback)webview_cwv_cb, t,
2285 "signal::event", (GCallback)webview_event_cb, t,
2286 "signal::load-finished", (GCallback)webview_load_finished_cb, t,
2287 "signal::load-progress-changed", (GCallback)webview_progress_changed_cb, t,
2288 (char *)NULL);
2289 g_signal_connect(t->wv, "notify::load-status",
2290 G_CALLBACK(notify_load_status_cb), t);
2292 /* hijack the unused keys as if we were the browser */
2293 g_object_connect((GObject*)t->toolbar,
2294 "signal-after::key-press-event", (GCallback)webview_keypress_cb, t,
2295 (char *)NULL);
2297 g_signal_connect(G_OBJECT(t->uri_entry), "focus",
2298 G_CALLBACK(focus_uri_entry_cb), t);
2300 g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(tab_close_cb), t);
2302 /* hide stuff */
2303 gtk_widget_hide(t->cmd);
2304 if (showurl == 0)
2305 gtk_widget_hide(t->toolbar);
2307 if (focus) {
2308 gtk_notebook_set_current_page(notebook, t->tab_id);
2309 DNPRINTF(XT_D_TAB, "create_new_tab: going to tab: %d\n",
2310 t->tab_id);
2313 if (load)
2314 webkit_web_view_load_uri(t->wv, title);
2315 else
2316 gtk_widget_grab_focus(GTK_WIDGET(t->uri_entry));
2318 if (newuri)
2319 g_free(newuri);
2322 void
2323 notebook_switchpage_cb(GtkNotebook *nb, GtkNotebookPage *nbp, guint pn,
2324 gpointer *udata)
2326 struct tab *t;
2327 const gchar *uri;
2329 DNPRINTF(XT_D_TAB, "notebook_switchpage_cb: tab: %d\n", pn);
2331 TAILQ_FOREACH(t, &tabs, entry) {
2332 if (t->tab_id == pn) {
2333 DNPRINTF(XT_D_TAB, "notebook_switchpage_cb: going to "
2334 "%d\n", pn);
2336 uri = webkit_web_view_get_title(t->wv);
2337 if (uri == NULL)
2338 uri = XT_NAME;
2339 gtk_window_set_title(GTK_WINDOW(main_window), uri);
2341 gtk_widget_hide(t->cmd);
2346 void
2347 create_canvas(void)
2349 GtkWidget *vbox;
2351 vbox = gtk_vbox_new(FALSE, 0);
2352 notebook = GTK_NOTEBOOK(gtk_notebook_new());
2353 if (showtabs == 0)
2354 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(notebook), FALSE);
2355 gtk_notebook_set_scrollable(notebook, TRUE);
2357 gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(notebook), TRUE, TRUE, 0);
2359 g_object_connect((GObject*)notebook,
2360 "signal::switch-page", (GCallback)notebook_switchpage_cb, NULL,
2361 (char *)NULL);
2363 main_window = create_window();
2364 gtk_container_add(GTK_CONTAINER(main_window), vbox);
2365 gtk_window_set_title(GTK_WINDOW(main_window), XT_NAME);
2366 gtk_widget_show_all(main_window);
2369 void
2370 setup_cookies(char *file)
2372 if (cookiejar) {
2373 soup_session_remove_feature(session,
2374 (SoupSessionFeature*)cookiejar);
2375 g_object_unref(cookiejar);
2376 cookiejar = NULL;
2379 if (cookies_enabled == 0)
2380 return;
2382 cookiejar = soup_cookie_jar_text_new(file, read_only_cookies);
2383 soup_session_add_feature(session, (SoupSessionFeature*)cookiejar);
2386 void
2387 setup_proxy(char *uri)
2389 if (proxy_uri) {
2390 g_object_set(session, "proxy_uri", NULL, (char *)NULL);
2391 soup_uri_free(proxy_uri);
2392 proxy_uri = NULL;
2394 if (http_proxy) {
2395 if (http_proxy != uri) {
2396 g_free(http_proxy);
2397 http_proxy = NULL;
2401 if (uri) {
2402 http_proxy = g_strdup(uri);
2403 DNPRINTF(XT_D_CONFIG, "setup_proxy: %s\n", uri);
2404 proxy_uri = soup_uri_new(http_proxy);
2405 g_object_set(session, "proxy-uri", proxy_uri, (char *)NULL);
2409 void
2410 usage(void)
2412 fprintf(stderr,
2413 "%s [-STVt][-f file] url ...\n", __progname);
2414 exit(0);
2418 main(int argc, char *argv[])
2420 struct stat sb;
2421 int c, focus = 1;
2422 char conf[PATH_MAX] = { '\0' };
2423 char file[PATH_MAX];
2424 char *env_proxy = NULL;
2425 FILE *f = NULL;
2427 while ((c = getopt(argc, argv, "STVf:t")) != -1) {
2428 switch (c) {
2429 case 'S':
2430 showurl = 0;
2431 break;
2432 case 'T':
2433 showtabs = 0;
2434 break;
2435 case 'V':
2436 errx(0 , "Version: %s", version);
2437 break;
2438 case 'f':
2439 strlcpy(conf, optarg, sizeof(conf));
2440 break;
2441 case 't':
2442 tabless = 1;
2443 break;
2444 default:
2445 usage();
2446 /* NOTREACHED */
2449 argc -= optind;
2450 argv += optind;
2452 TAILQ_INIT(&tabs);
2454 /* prepare gtk */
2455 gtk_init(&argc, &argv);
2456 if (!g_thread_supported())
2457 g_thread_init(NULL);
2459 pwd = getpwuid(getuid());
2460 if (pwd == NULL)
2461 errx(1, "invalid user %d", getuid());
2463 /* set download dir */
2464 strlcpy(download_dir, pwd->pw_dir, sizeof download_dir);
2466 /* read config file */
2467 if (strlen(conf) == 0)
2468 snprintf(conf, sizeof conf, "%s/.%s",
2469 pwd->pw_dir, XT_CONF_FILE);
2470 config_parse(conf);
2472 /* download dir */
2473 if (!strcmp(download_dir, pwd->pw_dir))
2474 strlcat(download_dir, "/downloads", sizeof download_dir);
2476 if (stat(download_dir, &sb)) {
2477 if (mkdir(download_dir, S_IRWXU) == -1)
2478 err(1, "mkdir download_dir");
2480 if (S_ISDIR(sb.st_mode) == 0)
2481 errx(1, "%s not a dir", download_dir);
2482 if (((sb.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO))) != S_IRWXU) {
2483 warnx("fixing invalid permissions on %s", download_dir);
2484 if (chmod(download_dir, S_IRWXU) == -1)
2485 err(1, "chmod");
2488 /* working directory */
2489 snprintf(work_dir, sizeof work_dir, "%s/%s", pwd->pw_dir, XT_DIR);
2490 if (stat(work_dir, &sb)) {
2491 if (mkdir(work_dir, S_IRWXU) == -1)
2492 err(1, "mkdir work_dir");
2494 if (S_ISDIR(sb.st_mode) == 0)
2495 errx(1, "%s not a dir", work_dir);
2496 if (((sb.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO))) != S_IRWXU) {
2497 warnx("fixing invalid permissions on %s", work_dir);
2498 if (chmod(work_dir, S_IRWXU) == -1)
2499 err(1, "chmod");
2502 /* favorites file */
2503 snprintf(file, sizeof file, "%s/%s", work_dir, XT_FAVS_FILE);
2504 if (stat(file, &sb)) {
2505 warnx("favorites file doesn't exist, creating it");
2506 if ((f = fopen(file, "w")) == NULL)
2507 err(1, "favorites");
2508 fclose(f);
2511 /* cookies */
2512 session = webkit_get_default_session();
2513 snprintf(file, sizeof file, "%s/cookies.txt", work_dir);
2514 setup_cookies(file);
2516 /* proxy */
2517 env_proxy = getenv("http_proxy");
2518 if (env_proxy)
2519 setup_proxy(env_proxy);
2520 else
2521 setup_proxy(http_proxy);
2523 create_canvas();
2525 while (argc) {
2526 create_new_tab(argv[0], focus);
2527 focus = 0;
2529 argc--;
2530 argv++;
2532 if (focus == 1)
2533 create_new_tab(home, 1);
2535 gtk_main();
2537 return (0);