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.
21 * inverse color browsing
23 * download files status
24 * multi letter commands
34 #include <sys/queue.h>
35 #include <sys/types.h>
39 #include <gdk/gdkkeysyms.h>
40 #include <webkit/webkit.h>
41 #include <libsoup/soup.h>
43 static char *version
= "$xxxterm$";
46 /* #define XT_DEBUG */
48 #define DPRINTF(x...) do { if (swm_debug) fprintf(stderr, x); } while (0)
49 #define DNPRINTF(n,x...) do { if (swm_debug & n) fprintf(stderr, x); } while (0)
50 #define XT_D_MOVE 0x0001
51 #define XT_D_KEY 0x0002
52 #define XT_D_TAB 0x0004
53 #define XT_D_URL 0x0008
54 #define XT_D_CMD 0x0010
55 #define XT_D_NAV 0x0020
56 #define XT_D_DOWNLOAD 0x0040
57 u_int32_t swm_debug
= 0
68 #define DNPRINTF(n,x...)
71 #define LENGTH(x) (sizeof x / sizeof x[0])
72 #define CLEAN(mask) (mask & ~(GDK_MOD2_MASK) & \
73 ~(GDK_BUTTON1_MASK) & \
74 ~(GDK_BUTTON2_MASK) & \
75 ~(GDK_BUTTON3_MASK) & \
76 ~(GDK_BUTTON4_MASK) & \
80 TAILQ_ENTRY(tab
) entry
;
85 GtkWidget
*browser_win
;
89 /* adjustments for browser */
92 GtkAdjustment
*adjust_h
;
93 GtkAdjustment
*adjust_v
;
102 TAILQ_HEAD(tab_list
, tab
);
110 #define XT_CB_HANDLED (TRUE)
111 #define XT_CB_PASSTHROUGH (FALSE)
114 #define XT_MOVE_INVALID (0)
115 #define XT_MOVE_DOWN (1)
116 #define XT_MOVE_UP (2)
117 #define XT_MOVE_BOTTOM (3)
118 #define XT_MOVE_TOP (4)
119 #define XT_MOVE_PAGEDOWN (5)
120 #define XT_MOVE_PAGEUP (6)
121 #define XT_MOVE_LEFT (7)
122 #define XT_MOVE_FARLEFT (8)
123 #define XT_MOVE_RIGHT (9)
124 #define XT_MOVE_FARRIGHT (10)
126 #define XT_TAB_PREV (-2)
127 #define XT_TAB_NEXT (-1)
128 #define XT_TAB_INVALID (0)
129 #define XT_TAB_NEW (1)
130 #define XT_TAB_DELETE (2)
131 #define XT_TAB_DELQUIT (3)
132 #define XT_TAB_OPEN (4)
134 #define XT_NAV_INVALID (0)
135 #define XT_NAV_BACK (1)
136 #define XT_NAV_FORWARD (2)
139 extern char *__progname
;
140 GtkWidget
*main_window
;
141 GtkNotebook
*notebook
;
142 struct tab_list tabs
;
145 int showtabs
= 1; /* show tabs on notebook */
146 int showurl
= 1; /* show url toolbar on notebook */
147 int tabless
= 0; /* allow only 1 tab */
150 void create_new_tab(char *, int);
151 void delete_tab(struct tab
*);
153 struct valid_url_types
{
163 valid_url_type(char *url
)
167 for (i
= 0; i
< LENGTH(vut
); i
++)
168 if (!strncasecmp(vut
[i
].type
, url
, strlen(vut
[i
].type
)))
175 guess_url_type(char *url_in
)
178 char *url_out
= NULL
;
180 /* XXX not sure about this heuristic */
181 if (stat(url_in
, &sb
) == 0) {
182 if (asprintf(&url_out
, "file://%s", url_in
) == -1)
183 err(1, "aprintf file");
186 if (asprintf(&url_out
, "http://%s", url_in
) == -1)
187 err(1, "aprintf http");
191 err(1, "asprintf pointer");
193 DNPRINTF(XT_D_URL
, "guess_url_type: guessed %s\n", url_out
);
199 quit(struct tab
*t
, struct karg
*args
)
207 navaction(struct tab
*t
, struct karg
*args
)
209 DNPRINTF(XT_D_NAV
, "navaction: tab %d opcode %d\n",
214 webkit_web_view_go_back(t
->wv
);
217 webkit_web_view_go_forward(t
->wv
);
220 return (XT_CB_PASSTHROUGH
);
224 move(struct tab
*t
, struct karg
*args
)
226 GtkAdjustment
*adjust
;
227 double pi
, si
, pos
, ps
, upper
, lower
, max
;
234 case XT_MOVE_PAGEDOWN
:
236 adjust
= t
->adjust_v
;
239 adjust
= t
->adjust_h
;
243 pos
= gtk_adjustment_get_value(adjust
);
244 ps
= gtk_adjustment_get_page_size(adjust
);
245 upper
= gtk_adjustment_get_upper(adjust
);
246 lower
= gtk_adjustment_get_lower(adjust
);
247 si
= gtk_adjustment_get_step_increment(adjust
);
248 pi
= gtk_adjustment_get_page_increment(adjust
);
251 DNPRINTF(XT_D_MOVE
, "move: opcode %d %s pos %f ps %f upper %f lower %f "
252 "max %f si %f pi %f\n",
253 args
->i
, adjust
== t
->adjust_h
? "horizontal" : "vertical",
254 pos
, ps
, upper
, lower
, max
, si
, pi
);
260 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
265 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
268 case XT_MOVE_FARRIGHT
:
269 gtk_adjustment_set_value(adjust
, max
);
272 case XT_MOVE_FARLEFT
:
273 gtk_adjustment_set_value(adjust
, lower
);
275 case XT_MOVE_PAGEDOWN
:
277 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
281 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
284 return (XT_CB_PASSTHROUGH
);
287 DNPRINTF(XT_D_MOVE
, "move: new pos %f %f\n", pos
, MIN(pos
, max
));
289 return (XT_CB_HANDLED
);
293 getparams(char *cmd
, char *cmp
)
298 if (!strncmp(cmd
, cmp
, strlen(cmp
))) {
299 rv
= cmd
+ strlen(cmp
);
311 tabaction(struct tab
*t
, struct karg
*args
)
313 int rv
= XT_CB_HANDLED
;
314 char *url
= NULL
, *newuri
= NULL
;
316 DNPRINTF(XT_D_TAB
, "tabaction: %p %d %d\n", t
, args
->i
, t
->focus_wv
);
319 return (XT_CB_PASSTHROUGH
);
323 if ((url
= getparams(args
->s
, "tabnew")))
324 create_new_tab(url
, 1);
326 create_new_tab(NULL
, 1);
332 if (gtk_notebook_get_n_pages(notebook
) > 1)
338 if ((url
= getparams(args
->s
, "open")) ||
339 ((url
= getparams(args
->s
, "op"))) ||
340 ((url
= getparams(args
->s
, "o"))))
343 rv
= XT_CB_PASSTHROUGH
;
347 if (valid_url_type(url
)) {
348 newuri
= guess_url_type(url
);
351 webkit_web_view_load_uri(t
->wv
, url
);
356 rv
= XT_CB_PASSTHROUGH
;
370 movetab(struct tab
*t
, struct karg
*args
)
375 DNPRINTF(XT_D_TAB
, "movetab: %p %d\n", t
, args
->i
);
378 return (XT_CB_PASSTHROUGH
);
380 if (args
->i
== XT_TAB_INVALID
)
381 return (XT_CB_PASSTHROUGH
);
383 if (args
->i
< XT_TAB_INVALID
) {
384 /* next or previous tab */
385 if (TAILQ_EMPTY(&tabs
))
386 return (XT_CB_PASSTHROUGH
);
388 if (args
->i
== XT_TAB_NEXT
)
389 gtk_notebook_next_page(notebook
);
391 gtk_notebook_prev_page(notebook
);
393 return (XT_CB_HANDLED
);
398 if (t
->tab_id
== x
) {
399 DNPRINTF(XT_D_TAB
, "movetab: do nothing\n");
400 return (XT_CB_HANDLED
);
403 TAILQ_FOREACH(tt
, &tabs
, entry
) {
404 if (tt
->tab_id
== x
) {
405 gtk_notebook_set_current_page(notebook
, x
);
406 DNPRINTF(XT_D_TAB
, "movetab: going to %d\n", x
);
408 gtk_widget_grab_focus(GTK_WIDGET(tt
->wv
));
412 return (XT_CB_HANDLED
);
416 command(struct tab
*t
, struct karg
*args
)
418 DNPRINTF(XT_D_CMD
, "command:\n");
420 gtk_entry_set_text(GTK_ENTRY(t
->cmd
), ":");
421 gtk_widget_show(t
->cmd
);
422 gtk_widget_grab_focus(GTK_WIDGET(t
->cmd
));
423 gtk_editable_set_position(GTK_EDITABLE(t
->cmd
), -1);
425 return (XT_CB_HANDLED
);
428 /* inherent to GTK not all keys will be caught at all times */
433 int (*func
)(struct tab
*, struct karg
*);
436 { GDK_SHIFT_MASK
, 0, GDK_colon
, command
, {0} },
437 { GDK_CONTROL_MASK
, 0, GDK_q
, quit
, {0} },
440 { 0, 0, GDK_BackSpace
, navaction
, {.i
= XT_NAV_BACK
} },
441 { GDK_MOD1_MASK
, 0, GDK_Left
, navaction
, {.i
= XT_NAV_BACK
} },
442 { GDK_SHIFT_MASK
, 0, GDK_BackSpace
, navaction
, {.i
= XT_NAV_FORWARD
} },
443 { GDK_MOD1_MASK
, 0, GDK_Right
, navaction
, {.i
= XT_NAV_FORWARD
} },
445 /* vertical movement */
446 { 0, 0, GDK_j
, move
, {.i
= XT_MOVE_DOWN
} },
447 { 0, 0, GDK_Down
, move
, {.i
= XT_MOVE_DOWN
} },
448 { 0, 0, GDK_Up
, move
, {.i
= XT_MOVE_UP
} },
449 { 0, 0, GDK_k
, move
, {.i
= XT_MOVE_UP
} },
450 { GDK_SHIFT_MASK
, 0, GDK_G
, move
, {.i
= XT_MOVE_BOTTOM
} },
451 { 0, 0, GDK_End
, move
, {.i
= XT_MOVE_BOTTOM
} },
452 { 0, 0, GDK_Home
, move
, {.i
= XT_MOVE_TOP
} },
453 { 0, GDK_g
, GDK_g
, move
, {.i
= XT_MOVE_TOP
} }, /* XXX make this work */
454 { 0, 0, GDK_space
, move
, {.i
= XT_MOVE_PAGEDOWN
} },
455 { 0, 0, GDK_Page_Down
, move
, {.i
= XT_MOVE_PAGEDOWN
} },
456 { 0, 0, GDK_Page_Up
, move
, {.i
= XT_MOVE_PAGEUP
} },
457 /* horizontal movement */
458 { 0, 0, GDK_l
, move
, {.i
= XT_MOVE_RIGHT
} },
459 { 0, 0, GDK_Right
, move
, {.i
= XT_MOVE_RIGHT
} },
460 { 0, 0, GDK_Left
, move
, {.i
= XT_MOVE_LEFT
} },
461 { 0, 0, GDK_h
, move
, {.i
= XT_MOVE_LEFT
} },
462 { GDK_SHIFT_MASK
, 0, GDK_dollar
, move
, {.i
= XT_MOVE_FARRIGHT
} },
463 { 0, 0, GDK_0
, move
, {.i
= XT_MOVE_FARLEFT
} },
466 { GDK_CONTROL_MASK
, 0, GDK_t
, tabaction
, {.i
= XT_TAB_NEW
} },
467 { GDK_CONTROL_MASK
, 0, GDK_w
, tabaction
, {.i
= XT_TAB_DELETE
} },
468 { GDK_CONTROL_MASK
, 0, GDK_1
, movetab
, {.i
= 1} },
469 { GDK_CONTROL_MASK
, 0, GDK_2
, movetab
, {.i
= 2} },
470 { GDK_CONTROL_MASK
, 0, GDK_3
, movetab
, {.i
= 3} },
471 { GDK_CONTROL_MASK
, 0, GDK_4
, movetab
, {.i
= 4} },
472 { GDK_CONTROL_MASK
, 0, GDK_5
, movetab
, {.i
= 5} },
473 { GDK_CONTROL_MASK
, 0, GDK_6
, movetab
, {.i
= 6} },
474 { GDK_CONTROL_MASK
, 0, GDK_7
, movetab
, {.i
= 7} },
475 { GDK_CONTROL_MASK
, 0, GDK_8
, movetab
, {.i
= 8} },
476 { GDK_CONTROL_MASK
, 0, GDK_9
, movetab
, {.i
= 9} },
477 { GDK_CONTROL_MASK
, 0, GDK_0
, movetab
, {.i
= 10} },
483 int (*func
)(struct tab
*, struct karg
*);
486 { "q!", 0, quit
, {0} },
487 { "qa", 0, quit
, {0} },
488 { "qa!", 0, quit
, {0} },
491 { "o", 1, tabaction
, {.i
= XT_TAB_OPEN
} },
492 { "op", 1, tabaction
, {.i
= XT_TAB_OPEN
} },
493 { "open", 1, tabaction
, {.i
= XT_TAB_OPEN
} },
494 { "tabnew", 1, tabaction
, {.i
= XT_TAB_NEW
} },
495 { "tabedit", 0, tabaction
, {.i
= XT_TAB_NEW
} },
496 { "tabe", 0, tabaction
, {.i
= XT_TAB_NEW
} },
497 { "tabclose", 0, tabaction
, {.i
= XT_TAB_DELETE
} },
498 { "quit", 0, tabaction
, {.i
= XT_TAB_DELQUIT
} },
499 { "q", 0, tabaction
, {.i
= XT_TAB_DELQUIT
} },
500 /* XXX add count to these commands and add tabl and friends */
501 { "tabprevious", 0, movetab
, {.i
= XT_TAB_PREV
} },
502 { "tabp", 0, movetab
, {.i
= XT_TAB_PREV
} },
503 { "tabnext", 0, movetab
, {.i
= XT_TAB_NEXT
} },
504 { "tabn", 0, movetab
, {.i
= XT_TAB_NEXT
} },
508 focus_uri_entry_cb(GtkWidget
* w
, GtkDirectionType direction
, struct tab
*t
)
510 DNPRINTF(XT_D_URL
, "focus_uri_entry_cb: tab %d focus_wv %d\n",
511 t
->tab_id
, t
->focus_wv
);
514 errx(1, "focus_uri_entry_cb");
516 /* focus on wv instead */
518 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
522 activate_uri_entry_cb(GtkWidget
* entry
, struct tab
*t
)
524 const gchar
*uri
= gtk_entry_get_text(GTK_ENTRY(entry
));
527 DNPRINTF(XT_D_URL
, "activate_uri_entry_cb: %s\n", uri
);
530 errx(1, "activate_uri_entry_cb");
535 if (valid_url_type((char *)uri
)) {
536 newuri
= guess_url_type((char *)uri
);
540 webkit_web_view_load_uri(t
->wv
, uri
);
541 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
548 notify_load_status_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
550 WebKitWebFrame
*frame
;
554 errx(1, "notify_load_status_cb");
556 switch (webkit_web_view_get_load_status(wview
)) {
557 case WEBKIT_LOAD_COMMITTED
:
558 frame
= webkit_web_view_get_main_frame(wview
);
559 uri
= webkit_web_frame_get_uri(frame
);
561 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), uri
);
564 /* take focus if we are visible */
565 if (gtk_notebook_get_current_page(notebook
) == t
->tab_id
)
566 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
568 case WEBKIT_LOAD_FIRST_VISUALLY_NON_EMPTY_LAYOUT
:
569 uri
= webkit_web_view_get_title(wview
);
571 frame
= webkit_web_view_get_main_frame(wview
);
572 uri
= webkit_web_frame_get_uri(frame
);
574 gtk_label_set_text(GTK_LABEL(t
->label
), uri
);
576 case WEBKIT_LOAD_PROVISIONAL
:
577 case WEBKIT_LOAD_FINISHED
:
578 case WEBKIT_LOAD_FAILED
:
585 webview_npd_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
,
586 WebKitNetworkRequest
*request
, WebKitWebNavigationAction
*na
,
587 WebKitWebPolicyDecision
*pd
, struct tab
*t
)
592 errx(1, "webview_npd_cb");
594 DNPRINTF(XT_D_NAV
, "webview_npd_cb: %s\n",
595 webkit_network_request_get_uri(request
));
598 uri
= (char *)webkit_network_request_get_uri(request
);
599 create_new_tab(uri
, 0);
601 webkit_web_policy_decision_ignore(pd
);
603 return (TRUE
); /* we made the decission */
610 webview_event_cb(GtkWidget
*w
, GdkEventButton
*e
, struct tab
*t
)
612 /* we can not eat the event without throwing gtk off so defer it */
614 /* catch ctrl click */
615 if (e
->type
== GDK_BUTTON_RELEASE
&&
616 CLEAN(e
->state
) == GDK_CONTROL_MASK
)
621 return (XT_CB_PASSTHROUGH
);
625 webview_mimetype_cb(WebKitWebView
*wv
, WebKitWebFrame
*frame
,
626 WebKitNetworkRequest
*request
, char *mime_type
,
627 WebKitWebPolicyDecision
*decision
, struct tab
*t
)
630 errx(1, "webview_mimetype_cb");
632 DNPRINTF(XT_D_DOWNLOAD
, "webview_mimetype_cb: tab %d mime %s\n",
633 t
->tab_id
, mime_type
);
635 if (webkit_web_view_can_show_mime_type(wv
, mime_type
) == FALSE
) {
636 webkit_web_policy_decision_download(decision
);
644 webview_download_cb(WebKitWebView
*wv
, WebKitDownload
*download
, struct tab
*t
)
646 const gchar
*filename
;
650 if (download
== NULL
|| t
== NULL
)
651 errx(1, "webview_download_cb: invalid pointers");
653 filename
= webkit_download_get_suggested_filename(download
);
654 if (filename
== NULL
)
655 return (FALSE
); /* abort download */
657 pwd
= getpwuid(getuid());
659 errx(1, "webview_download_cb: invalid user %d", getuid());
661 if (asprintf(&uri
, "file://%s/%s", pwd
->pw_dir
, filename
) == -1)
662 err(1, "aprintf uri");
664 DNPRINTF(XT_D_DOWNLOAD
, "webview_download_cb: tab %d filename %s "
666 t
->tab_id
, filename
, uri
);
668 webkit_download_set_destination_uri(download
, uri
);
673 webkit_download_start(download
);
675 return (TRUE
); /* start download */
679 webview_hover_cb(WebKitWebView
*wv
, gchar
*title
, gchar
*uri
, struct tab
*t
)
681 DNPRINTF(XT_D_KEY
, "webview_hover_cb: %s %s\n", title
, uri
);
684 errx(1, "webview_hover_cb");
691 t
->hover
= strdup(uri
);
692 } else if (t
->hover
) {
699 webview_keypress_cb(GtkWidget
*w
, GdkEventKey
*e
, struct tab
*t
)
703 /* don't use w directly; use t->whatever instead */
706 errx(1, "webview_keypress_cb");
708 DNPRINTF(XT_D_KEY
, "webview_keypress_cb: keyval 0x%x mask 0x%x t %p\n",
709 e
->keyval
, e
->state
, t
);
711 for (i
= 0; i
< LENGTH(keys
); i
++)
712 if (e
->keyval
== keys
[i
].key
&& CLEAN(e
->state
) ==
714 keys
[i
].func(t
, &keys
[i
].arg
);
715 return (XT_CB_HANDLED
);
718 return (XT_CB_PASSTHROUGH
);
722 cmd_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
724 int rv
= XT_CB_HANDLED
;
725 const gchar
*c
= gtk_entry_get_text(w
);
728 errx(1, "cmd_keypress_cb");
730 DNPRINTF(XT_D_CMD
, "cmd_keypress_cb: keyval 0x%x mask 0x%x t %p\n",
731 e
->keyval
, e
->state
, t
);
735 e
->keyval
= GDK_Escape
;
736 else if (c
[0] != ':')
737 e
->keyval
= GDK_Escape
;
745 gtk_widget_hide(t
->cmd
);
746 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
750 rv
= XT_CB_PASSTHROUGH
;
756 cmd_focusout_cb(GtkWidget
*w
, GdkEventFocus
*e
, struct tab
*t
)
759 errx(1, "cmd_focusout_cb");
761 DNPRINTF(XT_D_CMD
, "cmd_focusout_cb: tab %d focus_wv %d\n",
762 t
->tab_id
, t
->focus_wv
);
764 /* abort command when losing focus */
765 gtk_widget_hide(t
->cmd
);
767 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
769 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
771 return (XT_CB_PASSTHROUGH
);
775 cmd_activate_cb(GtkEntry
*entry
, struct tab
*t
)
779 const gchar
*c
= gtk_entry_get_text(entry
);
782 errx(1, "cmd_activate_cb");
784 DNPRINTF(XT_D_CMD
, "cmd_activate_cb: tab %d %s\n", t
->tab_id
, c
);
789 else if (c
[0] != ':')
795 for (i
= 0; i
< LENGTH(cmds
); i
++)
796 if (cmds
[i
].params
) {
797 if (!strncmp(s
, cmds
[i
].cmd
, strlen(cmds
[i
].cmd
))) {
798 cmds
[i
].arg
.s
= strdup(s
);
799 cmds
[i
].func(t
, &cmds
[i
].arg
);
802 if (!strcmp(s
, cmds
[i
].cmd
))
803 cmds
[i
].func(t
, &cmds
[i
].arg
);
807 gtk_widget_hide(t
->cmd
);
811 create_browser(struct tab
*t
)
816 errx(1, "create_browser");
818 t
->sb_h
= GTK_SCROLLBAR(gtk_hscrollbar_new(NULL
));
819 t
->sb_v
= GTK_SCROLLBAR(gtk_vscrollbar_new(NULL
));
820 t
->adjust_h
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_h
));
821 t
->adjust_v
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_v
));
823 w
= gtk_scrolled_window_new(t
->adjust_h
, t
->adjust_v
);
824 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(w
),
825 GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
827 t
->wv
= WEBKIT_WEB_VIEW(webkit_web_view_new());
828 gtk_container_add(GTK_CONTAINER(w
), GTK_WIDGET(t
->wv
));
830 g_signal_connect(t
->wv
, "notify::load-status",
831 G_CALLBACK(notify_load_status_cb
), t
);
841 w
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
842 gtk_window_set_default_size(GTK_WINDOW(w
), 800, 600);
843 gtk_widget_set_name(w
, "xxxterm");
844 gtk_window_set_wmclass(GTK_WINDOW(w
), "xxxterm", "XXXTerm");
850 create_toolbar(struct tab
*t
)
852 GtkWidget
*toolbar
= gtk_toolbar_new();
855 #if GTK_CHECK_VERSION(2,15,0)
856 gtk_orientable_set_orientation(GTK_ORIENTABLE(toolbar
),
857 GTK_ORIENTATION_HORIZONTAL
);
859 gtk_toolbar_set_orientation(GTK_TOOLBAR(toolbar
),
860 GTK_ORIENTATION_HORIZONTAL
);
862 gtk_toolbar_set_style(GTK_TOOLBAR(toolbar
), GTK_TOOLBAR_BOTH_HORIZ
);
864 i
= gtk_tool_item_new();
865 gtk_tool_item_set_expand(i
, TRUE
);
866 t
->uri_entry
= gtk_entry_new();
867 gtk_container_add(GTK_CONTAINER(i
), t
->uri_entry
);
868 g_signal_connect(G_OBJECT(t
->uri_entry
), "activate",
869 G_CALLBACK(activate_uri_entry_cb
), t
);
870 gtk_toolbar_insert(GTK_TOOLBAR(toolbar
), i
, -1);
876 delete_tab(struct tab
*t
)
878 DNPRINTF(XT_D_TAB
, "delete_tab: %p\n", t
);
883 TAILQ_REMOVE(&tabs
, t
, entry
);
884 if (TAILQ_EMPTY(&tabs
))
885 create_new_tab(NULL
, 1);
887 gtk_widget_destroy(t
->vbox
);
892 create_new_tab(char *title
, int focus
)
898 DNPRINTF(XT_D_TAB
, "create_new_tab: title %s focus %d\n", title
, focus
);
900 if (tabless
&& !TAILQ_EMPTY(&tabs
)) {
901 DNPRINTF(XT_D_TAB
, "create_new_tab: new tab rejected\n");
905 t
= g_malloc0(sizeof *t
);
906 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
909 title
= "(untitled)";
912 if (valid_url_type(title
)) {
913 newuri
= guess_url_type(title
);
918 t
->vbox
= gtk_vbox_new(FALSE
, 0);
921 t
->label
= gtk_label_new(title
);
922 gtk_widget_set_size_request(t
->label
, 100, -1);
925 t
->toolbar
= create_toolbar(t
);
926 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->toolbar
, FALSE
, FALSE
, 0);
929 t
->browser_win
= create_browser(t
);
930 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->browser_win
, TRUE
, TRUE
, 0);
933 t
->cmd
= gtk_entry_new();
934 gtk_entry_set_inner_border(GTK_ENTRY(t
->cmd
), NULL
);
935 gtk_entry_set_has_frame(GTK_ENTRY(t
->cmd
), FALSE
);
936 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->cmd
, FALSE
, FALSE
, 0);
938 /* and show it all */
939 gtk_widget_show_all(t
->vbox
);
940 t
->tab_id
= gtk_notebook_append_page(notebook
, t
->vbox
,
943 g_object_connect((GObject
*)t
->cmd
,
944 "signal::key-press-event", (GCallback
)cmd_keypress_cb
, t
,
945 "signal::focus-out-event", (GCallback
)cmd_focusout_cb
, t
,
946 "signal::activate", (GCallback
)cmd_activate_cb
, t
,
949 g_object_connect((GObject
*)t
->wv
,
950 "signal-after::key-press-event", (GCallback
)webview_keypress_cb
, t
,
951 /* "signal::hovering-over-link", (GCallback)webview_hover_cb, t, */
952 "signal::download-requested", (GCallback
)webview_download_cb
, t
,
953 "signal::mime-type-policy-decision-requested", (GCallback
)webview_mimetype_cb
, t
,
954 "signal::navigation-policy-decision-requested", (GCallback
)webview_npd_cb
, t
,
955 "signal::event", (GCallback
)webview_event_cb
, t
,
958 /* hijack the unused keys as if we were the browser */
959 g_object_connect((GObject
*)t
->toolbar
,
960 "signal-after::key-press-event", (GCallback
)webview_keypress_cb
, t
,
963 g_signal_connect(G_OBJECT(t
->uri_entry
), "focus",
964 G_CALLBACK(focus_uri_entry_cb
), t
);
967 gtk_widget_hide(t
->cmd
);
969 gtk_widget_hide(t
->toolbar
);
972 gtk_notebook_set_current_page(notebook
, t
->tab_id
);
973 DNPRINTF(XT_D_TAB
, "create_new_tab: going to tab: %d\n",
978 webkit_web_view_load_uri(t
->wv
, title
);
980 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
987 notebook_switchpage_cb(GtkNotebook
*nb
, GtkNotebookPage
*nbp
, guint pn
,
992 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: tab: %d\n", pn
);
994 TAILQ_FOREACH(t
, &tabs
, entry
) {
995 if (t
->tab_id
== pn
) {
996 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: going to "
998 gtk_widget_hide(t
->cmd
);
1008 vbox
= gtk_vbox_new(FALSE
, 0);
1009 notebook
= GTK_NOTEBOOK(gtk_notebook_new());
1011 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(notebook
), FALSE
);
1013 gtk_box_pack_start(GTK_BOX(vbox
), GTK_WIDGET(notebook
), TRUE
, TRUE
, 0);
1015 g_object_connect((GObject
*)notebook
,
1016 "signal::switch-page", (GCallback
)notebook_switchpage_cb
, NULL
,
1019 main_window
= create_window();
1020 gtk_container_add(GTK_CONTAINER(main_window
), vbox
);
1021 gtk_widget_show_all(main_window
);
1028 "%s [-STVt] url ...\n", __progname
);
1033 main(int argc
, char *argv
[])
1037 while ((c
= getopt(argc
, argv
, "STVt")) != -1) {
1046 errx(0 , "Version: %s", version
);
1062 gtk_init(&argc
, &argv
);
1063 if (!g_thread_supported())
1064 g_thread_init(NULL
);
1069 create_new_tab(argv
[0], focus
);
1076 create_new_tab("http://www.peereboom.us", 1);