2 (c) 2009 by Leon Winter
3 (c) 2009-2012 by Hannes Schueller
4 (c) 2009-2010 by Matto Fransen
5 (c) 2010-2011 by Hans-Peter Deifel
6 (c) 2010-2011 by Thomas Adam
8 (c) 2011 by Daniel Carl
9 (c) 2012 by Matthew Carter
15 #include "vimprobable.h"
16 #include "utilities.h"
17 #include "callbacks.h"
18 #include "javascript.h"
20 /* the CLEAN_MOD_*_MASK defines have all the bits set that will be stripped from the modifier bit field */
21 #define CLEAN_MOD_NUMLOCK_MASK (GDK_MOD2_MASK)
22 #define CLEAN_MOD_BUTTON_MASK (GDK_BUTTON1_MASK|GDK_BUTTON2_MASK|GDK_BUTTON3_MASK|GDK_BUTTON4_MASK|GDK_BUTTON5_MASK)
24 /* remove unused bits, numlock symbol and buttons from keymask */
25 #define CLEAN(mask) (mask & (GDK_MODIFIER_MASK) & ~(CLEAN_MOD_NUMLOCK_MASK) & ~(CLEAN_MOD_BUTTON_MASK))
27 #define IS_ESCAPE(event) (IS_ESCAPE_KEY(CLEAN(event->state), event->keyval))
28 #define IS_ESCAPE_KEY(s, k) ((s == 0 && k == GDK_Escape) || \
29 (s == GDK_CONTROL_MASK && k == GDK_bracketleft))
32 static void inputbox_activate_cb(GtkEntry
*entry
, gpointer user_data
);
33 static gboolean
inputbox_keypress_cb(GtkEntry
*entry
, GdkEventKey
*event
);
34 static gboolean
inputbox_keyrelease_cb(GtkEntry
*entry
, GdkEventKey
*event
);
35 static gboolean
inputbox_changed_cb(GtkEditable
*entry
, gpointer user_data
);
36 static WebKitWebView
* inspector_inspect_web_view_cb(gpointer inspector
, WebKitWebView
* web_view
);
37 static gboolean
notify_event_cb(GtkWidget
*widget
, GdkEvent
*event
, gpointer user_data
);
38 static gboolean
webview_console_cb(WebKitWebView
*webview
, char *message
, int line
, char *source
, gpointer user_data
);
39 static gboolean
webview_download_cb(WebKitWebView
*webview
, WebKitDownload
*download
, gpointer user_data
);
40 static void webview_hoverlink_cb(WebKitWebView
*webview
, char *title
, char *link
, gpointer data
);
41 static gboolean
webview_keypress_cb(WebKitWebView
*webview
, GdkEventKey
*event
);
42 static void webview_load_committed_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, gpointer user_data
);
43 static void webview_load_finished_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, gpointer user_data
);
44 static gboolean
webview_mimetype_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, WebKitNetworkRequest
*request
,
45 char *mime_type
, WebKitWebPolicyDecision
*decision
, gpointer user_data
);
46 static gboolean
webview_new_window_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, WebKitNetworkRequest
*request
,
47 WebKitWebNavigationAction
*action
, WebKitWebPolicyDecision
*decision
, gpointer user_data
);
48 static gboolean
webview_open_in_new_window_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, gpointer user_data
);
49 static void webview_progress_changed_cb(WebKitWebView
*webview
, int progress
, gpointer user_data
);
50 static void webview_title_changed_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, char *title
, gpointer user_data
);
51 static void window_destroyed_cb(GtkWidget
*window
, gpointer func_data
);
52 static gboolean
blank_cb(void);
55 static gboolean
bookmark(const Arg
*arg
);
56 static gboolean
browser_settings(const Arg
*arg
);
57 static gboolean
commandhistoryfetch(const Arg
*arg
);
58 static gboolean
complete(const Arg
*arg
);
59 static gboolean
descend(const Arg
*arg
);
60 gboolean
echo(const Arg
*arg
);
61 static gboolean
focus_input(const Arg
*arg
);
62 static gboolean
input(const Arg
*arg
);
63 static gboolean
navigate(const Arg
*arg
);
64 static gboolean
number(const Arg
*arg
);
65 static gboolean
open_arg(const Arg
*arg
);
66 static gboolean
open_remembered(const Arg
*arg
);
67 static gboolean
paste(const Arg
*arg
);
68 static gboolean
quickmark(const Arg
*arg
);
69 static gboolean
quit(const Arg
*arg
);
70 static gboolean
revive(const Arg
*arg
);
71 static gboolean
print_frame(const Arg
*arg
);
72 static gboolean
search(const Arg
*arg
);
73 static gboolean
set(const Arg
*arg
);
74 static gboolean
script(const Arg
*arg
);
75 static gboolean
scroll(const Arg
*arg
);
76 static gboolean
search_tag(const Arg
*arg
);
77 static gboolean
yank(const Arg
*arg
);
78 static gboolean
view_source(const Arg
* arg
);
79 static gboolean
zoom(const Arg
*arg
);
80 static gboolean
fake_key_event(const Arg
*arg
);
82 static void update_url(const char *uri
);
83 static void setup_modkeys(void);
84 static void setup_gui(void);
85 static void setup_settings(void);
86 static void setup_signals(void);
87 static void ascii_bar(int total
, int state
, char *string
);
88 static gchar
*jsapi_ref_to_string(JSContextRef context
, JSValueRef ref
);
89 static void jsapi_evaluate_script(const gchar
*script
, gchar
**value
, gchar
**message
);
90 static void download_progress(WebKitDownload
*d
, GParamSpec
*pspec
);
91 static void set_widget_font_and_color(GtkWidget
*widget
, const char *font_str
,
92 const char *bg_color_str
, const char *fg_color_str
);
94 static gboolean
history(void);
95 static gboolean
process_set_line(char *line
);
96 void save_command_history(char *line
);
97 void toggle_proxy(gboolean onoff
);
98 void toggle_scrollbars(gboolean onoff
);
100 gboolean
process_keypress(GdkEventKey
*event
);
101 void fill_suggline(char * suggline
, const char * command
, const char *fill_with
);
102 GtkWidget
* fill_eventbox(const char * completion_line
);
103 static void mop_up(void);
108 static GtkWindow
*window
;
109 static GtkWidget
*viewport
;
111 static GtkScrollbar
*scroll_h
;
112 static GtkScrollbar
*scroll_v
;
113 static GtkAdjustment
*adjust_h
;
114 static GtkAdjustment
*adjust_v
;
115 static GtkWidget
*inputbox
;
116 static GtkWidget
*eventbox
;
117 static GtkBox
*statusbar
;
118 static GtkWidget
*status_url
;
119 static GtkWidget
*status_state
;
120 static WebKitWebView
*webview
;
121 static SoupSession
*session
;
122 static GtkClipboard
*clipboards
[2];
123 static GdkKeymap
*keymap
;
126 static unsigned int mode
= ModeNormal
;
127 static unsigned int count
= 0;
128 static float zoomstep
;
130 static char current_modkey
;
131 static char *search_handle
;
132 static gboolean search_direction
;
133 static gboolean echo_active
= TRUE
;
134 WebKitWebInspector
*inspector
;
136 static GdkNativeWindow embed
= 0;
137 static char *configfile
= NULL
;
138 static char *winid
= NULL
;
140 static char rememberedURI
[1024] = "";
141 static char followTarget
[8] = "";
142 char *error_msg
= NULL
;
143 char *config_base
= NULL
;
144 static gboolean manual_focus
= FALSE
;
146 GList
*activeDownloads
;
151 GList
*commandhistory
= NULL
;
152 int commandpointer
= 0;
154 KeyList
*keylistroot
= NULL
;
156 /* Cookie support. */
157 #ifdef ENABLE_COOKIE_SUPPORT
158 static SoupCookieJar
*session_cookie_jar
= NULL
;
159 static SoupCookieJar
*file_cookie_jar
= NULL
;
160 static time_t cookie_timeout
= 4800;
161 static char *cookie_store
;
162 static void setup_cookies(void);
163 static char *get_cookies(SoupURI
*soup_uri
);
164 static void load_all_cookies(void);
165 static void new_generic_request(SoupSession
*soup_ses
, SoupMessage
*soup_msg
, gpointer unused
);
166 static void update_cookie_jar(SoupCookieJar
*jar
, SoupCookie
*old
, SoupCookie
*new);
167 static void handle_cookie_request(SoupMessage
*soup_msg
, gpointer unused
);
171 window_destroyed_cb(GtkWidget
*window
, gpointer func_data
) {
176 webview_title_changed_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, char *title
, gpointer user_data
) {
177 gtk_window_set_title(window
, title
);
181 webview_progress_changed_cb(WebKitWebView
*webview
, int progress
, gpointer user_data
) {
182 #ifdef ENABLE_GTK_PROGRESS_BAR
183 gtk_entry_set_progress_fraction(GTK_ENTRY(inputbox
), progress
== 100 ? 0 : (double)progress
/100);
188 #ifdef ENABLE_WGET_PROGRESS_BAR
190 ascii_bar(int total
, int state
, char *string
) {
193 for (i
= 0; i
< state
; i
++)
194 string
[i
] = progressbartickchar
;
195 string
[i
++] = progressbarcurrent
;
196 for (; i
< total
; i
++)
197 string
[i
] = progressbarspacer
;
203 webview_load_committed_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, gpointer user_data
) {
204 Arg a
= { .i
= Silent
, .s
= g_strdup(JS_SETUP_HINTS
) };
205 const char *uri
= webkit_web_view_get_uri(webview
);
211 if (mode
== ModeInsert
|| mode
== ModeHints
) {
212 Arg a
= { .i
= ModeNormal
};
215 manual_focus
= FALSE
;
219 webview_load_finished_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, gpointer user_data
) {
220 WebKitWebSettings
*settings
= webkit_web_view_get_settings(webview
);
223 g_object_get(settings
, "enable-scripts", &scripts
, NULL
);
224 if (escape_input_on_load
&& scripts
&& !manual_focus
&& !gtk_widget_is_focus(inputbox
)) {
225 Arg a
= { .i
= Silent
, .s
= g_strdup("hints.clearFocus();") };
232 if (HISTORY_MAX_ENTRIES
> 0)
238 webview_open_in_new_window_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, gpointer user_data
) {
239 Arg a
= { .i
= TargetNew
, .s
= (char*)webkit_web_view_get_uri(webview
) };
240 if (strlen(rememberedURI
) > 0) {
248 webview_new_window_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, WebKitNetworkRequest
*request
,
249 WebKitWebNavigationAction
*action
, WebKitWebPolicyDecision
*decision
, gpointer user_data
) {
250 Arg a
= { .i
= TargetNew
, .s
= (char*)webkit_network_request_get_uri(request
) };
252 webkit_web_policy_decision_ignore(decision
);
257 webview_mimetype_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, WebKitNetworkRequest
*request
,
258 char *mime_type
, WebKitWebPolicyDecision
*decision
, gpointer user_data
) {
259 if (webkit_web_view_can_show_mime_type(webview
, mime_type
) == FALSE
) {
260 webkit_web_policy_decision_download(decision
);
267 static WebKitWebView
*
268 inspector_inspect_web_view_cb(gpointer inspector
, WebKitWebView
* web_view
) {
269 gchar
* inspector_title
;
270 GtkWidget
* inspector_window
;
271 GtkWidget
* inspector_view
;
273 /* just enough code to show the inspector - no signal handling etc. */
274 inspector_title
= g_strdup_printf("Inspect page - %s - Vimprobable2", webkit_web_view_get_uri(web_view
));
276 inspector_window
= gtk_plug_new(embed
);
278 inspector_window
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
279 gtk_window_set_wmclass(window
, "vimprobable2", "Vimprobable2");
281 gtk_window_set_title(GTK_WINDOW(inspector_window
), inspector_title
);
282 g_free(inspector_title
);
283 inspector_view
= webkit_web_view_new();
284 gtk_container_add(GTK_CONTAINER(inspector_window
), inspector_view
);
285 gtk_widget_show_all(inspector_window
);
286 return WEBKIT_WEB_VIEW(inspector_view
);
290 webview_download_cb(WebKitWebView
*webview
, WebKitDownload
*download
, gpointer user_data
) {
291 const gchar
*filename
;
295 WebKitDownloadStatus status
;
297 filename
= webkit_download_get_suggested_filename(download
);
298 if (filename
== NULL
|| strlen(filename
) == 0) {
299 filename
= "vimprobable_download";
301 path
= g_build_filename(g_strdup_printf(DOWNLOADS_PATH
), filename
, NULL
);
302 uri
= g_strconcat("file://", path
, NULL
);
303 webkit_download_set_destination_uri(download
, uri
);
305 size
= (uint32_t)webkit_download_get_total_size(download
);
308 a
.s
= g_strdup_printf("Download %s started (expected size: %u bytes)...", filename
, size
);
310 a
.s
= g_strdup_printf("Download %s started (unknown size)...", filename
);
313 activeDownloads
= g_list_prepend(activeDownloads
, download
);
314 g_signal_connect(download
, "notify::progress", G_CALLBACK(download_progress
), NULL
);
315 g_signal_connect(download
, "notify::status", G_CALLBACK(download_progress
), NULL
);
316 status
= webkit_download_get_status(download
);
317 if (status
== WEBKIT_DOWNLOAD_STATUS_CREATED
)
318 webkit_download_start(download
);
329 download_progress(WebKitDownload
*d
, GParamSpec
*pspec
) {
331 WebKitDownloadStatus status
= webkit_download_get_status(d
);
333 if (status
!= WEBKIT_DOWNLOAD_STATUS_STARTED
&& status
!= WEBKIT_DOWNLOAD_STATUS_CREATED
) {
334 if (status
!= WEBKIT_DOWNLOAD_STATUS_FINISHED
) {
336 a
.s
= g_strdup_printf("Error while downloading %s", webkit_download_get_suggested_filename(d
));
340 a
.s
= g_strdup_printf("Download %s finished", webkit_download_get_suggested_filename(d
));
344 activeDownloads
= g_list_remove(activeDownloads
, d
);
351 process_keypress(GdkEventKey
*event
) {
354 GdkModifierType irrelevant
;
356 /* Get a mask of modifiers that shouldn't be considered for this event.
357 * E.g.: It shouldn't matter whether ';' is shifted or not. */
358 gdk_keymap_translate_keyboard_state(keymap
, event
->hardware_keycode
,
359 event
->state
, event
->group
, &keyval
, NULL
, NULL
, &irrelevant
);
361 current
= keylistroot
;
363 while (current
!= NULL
) {
364 if (current
->Element
.mask
== (CLEAN(event
->state
) & ~irrelevant
)
365 && (current
->Element
.modkey
== current_modkey
366 || (!current
->Element
.modkey
&& !current_modkey
)
367 || current
->Element
.modkey
== GDK_VoidSymbol
) /* wildcard */
368 && current
->Element
.key
== keyval
369 && current
->Element
.func
)
370 if (current
->Element
.func(¤t
->Element
.arg
)) {
371 current_modkey
= count
= 0;
375 current
= current
->next
;
381 webview_keypress_cb(WebKitWebView
*webview
, GdkEventKey
*event
) {
382 Arg a
= { .i
= ModeNormal
, .s
= NULL
};
384 GdkModifierType irrelevant
;
386 /* Get a mask of modifiers that shouldn't be considered for this event.
387 * E.g.: It shouldn't matter whether ';' is shifted or not. */
388 gdk_keymap_translate_keyboard_state(keymap
, event
->hardware_keycode
,
389 event
->state
, event
->group
, &keyval
, NULL
, NULL
, &irrelevant
);
393 if ((CLEAN(event
->state
) & ~irrelevant
) == 0) {
394 if (IS_ESCAPE(event
)) {
399 } else if (current_modkey
== 0 && ((event
->keyval
>= GDK_1
&& event
->keyval
<= GDK_9
)
400 || (event
->keyval
== GDK_0
&& count
))) {
401 count
= (count
? count
* 10 : 0) + (event
->keyval
- GDK_0
);
404 } else if (strchr(modkeys
, event
->keyval
) && current_modkey
!= event
->keyval
) {
405 current_modkey
= event
->keyval
;
411 if (process_keypress(event
) == TRUE
) return TRUE
;
415 if (IS_ESCAPE(event
)) {
417 a
.s
= g_strdup("hints.clearFocus();");
423 case ModePassThrough
:
424 if (IS_ESCAPE(event
)) {
439 set_widget_font_and_color(GtkWidget
*widget
, const char *font_str
, const char *bg_color_str
,
440 const char *fg_color_str
) {
443 PangoFontDescription
*font
;
445 font
= pango_font_description_from_string(font_str
);
446 gtk_widget_modify_font(widget
, font
);
447 pango_font_description_free(font
);
450 gdk_color_parse(fg_color_str
, &fg_color
);
452 gdk_color_parse(bg_color_str
, &bg_color
);
454 gtk_widget_modify_text(widget
, GTK_STATE_NORMAL
, fg_color_str
? &fg_color
: NULL
);
455 gtk_widget_modify_base(widget
, GTK_STATE_NORMAL
, bg_color_str
? &bg_color
: NULL
);
461 webview_hoverlink_cb(WebKitWebView
*webview
, char *title
, char *link
, gpointer data
) {
462 const char *uri
= webkit_web_view_get_uri(webview
);
465 memset(rememberedURI
, 0, 1024);
467 markup
= g_markup_printf_escaped("<span font=\"%s\">Link: %s</span>", statusfont
, link
);
468 gtk_label_set_markup(GTK_LABEL(status_url
), markup
);
469 strncpy(rememberedURI
, link
, 1024);
476 webview_console_cb(WebKitWebView
*webview
, char *message
, int line
, char *source
, gpointer user_data
) {
479 /* Don't change internal mode if the browser doesn't have focus to prevent inconsistent states */
480 if (gtk_window_has_toplevel_focus(window
)) {
481 if (!strcmp(message
, "hintmode_off") || !strcmp(message
, "insertmode_off")) {
484 } else if (!strcmp(message
, "insertmode_on")) {
493 inputbox_activate_cb(GtkEntry
*entry
, gpointer user_data
) {
495 guint16 length
= gtk_entry_get_text_length(entry
);
497 gboolean success
= FALSE
, forward
= FALSE
;
499 a
.i
= HideCompletion
;
503 text
= (char*)gtk_entry_get_text(entry
);
504 if (length
> 1 && text
[0] == ':') {
505 success
= process_line((text
+ 1));
506 } else if (length
> 1 && ((forward
= text
[0] == '/') || text
[0] == '?')) {
507 webkit_web_view_unmark_text_matches(webview
);
508 #ifdef ENABLE_MATCH_HIGHLITING
509 webkit_web_view_mark_text_matches(webview
, &text
[1], FALSE
, 0);
510 webkit_web_view_set_highlight_text_matches(webview
, TRUE
);
513 #ifndef ENABLE_INCREMENTAL_SEARCH
515 a
.i
= searchoptions
| (forward
? DirectionForward
: DirectionBackwards
);
518 search_direction
= forward
;
519 search_handle
= g_strdup(&text
[1]);
521 } else if (text
[0] == '.' || text
[0] == ',' || text
[0] == ';') {
523 a
.s
= g_strdup_printf("hints.fire();");
530 gtk_entry_set_text(entry
, "");
531 gtk_widget_grab_focus(GTK_WIDGET(webview
));
535 inputbox_keypress_cb(GtkEntry
*entry
, GdkEventKey
*event
) {
539 if (mode
== ModeHints
) {
540 if (event
->keyval
== GDK_Tab
) {
542 a
.s
= g_strdup_printf("hints.focusNextHint();");
548 if (event
->keyval
== GDK_ISO_Left_Tab
) {
550 a
.s
= g_strdup_printf("hints.focusPreviousHint();");
556 if (event
->keyval
== GDK_Return
) {
558 a
.s
= g_strdup_printf("hints.fire();");
565 switch (event
->keyval
) {
566 case GDK_bracketleft
:
568 if (!IS_ESCAPE(event
)) break;
569 a
.i
= HideCompletion
;
581 return commandhistoryfetch(&a
);
585 return commandhistoryfetch(&a
);
587 case GDK_ISO_Left_Tab
:
593 if (mode
== ModeHints
) {
594 if ((CLEAN(event
->state
) & GDK_SHIFT_MASK
) &&
595 (CLEAN(event
->state
) & GDK_CONTROL_MASK
) &&
596 (event
->keyval
== GDK_BackSpace
)) {
599 a
.s
= g_strdup_printf("hints.updateHints(%d);", count
);
606 numval
= g_unichar_digit_value((gunichar
) gdk_keyval_to_unicode(event
->keyval
));
607 if ((numval
>= 1 && numval
<= 9) || (numval
== 0 && count
)) {
608 /* allow a zero as non-first number */
609 count
= (count
? count
* 10 : 0) + numval
;
611 a
.s
= g_strdup_printf("hints.updateHints(%d);", count
);
623 notify_event_cb(GtkWidget
*widget
, GdkEvent
*event
, gpointer user_data
) {
625 WebKitHitTestResult
*result
;
626 WebKitHitTestResultContext context
;
627 if (mode
== ModeNormal
&& event
->type
== GDK_BUTTON_RELEASE
) {
628 /* handle mouse click events */
629 for (i
= 0; i
< LENGTH(mouse
); i
++) {
630 if (mouse
[i
].mask
== CLEAN(event
->button
.state
)
631 && (mouse
[i
].modkey
== current_modkey
632 || (!mouse
[i
].modkey
&& !current_modkey
)
633 || mouse
[i
].modkey
== GDK_VoidSymbol
) /* wildcard */
634 && mouse
[i
].button
== event
->button
.button
636 if (mouse
[i
].func(&mouse
[i
].arg
)) {
637 current_modkey
= count
= 0;
643 result
= webkit_web_view_get_hit_test_result(WEBKIT_WEB_VIEW(widget
), (GdkEventButton
*)event
);
644 g_object_get(result
, "context", &context
, NULL
);
645 if (context
& WEBKIT_HIT_TEST_RESULT_CONTEXT_EDITABLE
) {
646 Arg a
= { .i
= ModeInsert
};
650 } else if (mode
== ModeInsert
&& event
->type
== GDK_BUTTON_RELEASE
) {
651 result
= webkit_web_view_get_hit_test_result(WEBKIT_WEB_VIEW(widget
), (GdkEventButton
*)event
);
652 g_object_get(result
, "context", &context
, NULL
);
653 if (!(context
& WEBKIT_HIT_TEST_RESULT_CONTEXT_EDITABLE
)) {
654 Arg a
= { .i
= ModeNormal
};
658 gchar
*value
= NULL
, *message
= NULL
;
659 jsapi_evaluate_script("window.getSelection().focusNode", &value
, &message
);
660 if (value
&& !strcmp(value
, "[object HTMLFormElement]")) {
661 Arg a
= { .i
= ModeInsert
, .s
= NULL
};
671 static gboolean
inputbox_keyrelease_cb(GtkEntry
*entry
, GdkEventKey
*event
) {
673 guint16 length
= gtk_entry_get_text_length(entry
);
676 a
.i
= HideCompletion
;
684 static gboolean
inputbox_changed_cb(GtkEditable
*entry
, gpointer user_data
) {
686 char *text
= (char*)gtk_entry_get_text(GTK_ENTRY(entry
));
687 guint16 length
= gtk_entry_get_text_length(GTK_ENTRY(entry
));
688 gboolean forward
= FALSE
;
690 /* Update incremental search if the user changes the search text.
692 * Note: gtk_widget_is_focus() is a poor way to check if the change comes
693 * from the user. But if the entry is focused and the text is set
694 * through gtk_entry_set_text() in some asyncrounous operation,
695 * I would consider that a bug.
698 if (gtk_widget_is_focus(GTK_WIDGET(entry
)) && length
> 1 && ((forward
= text
[0] == '/') || text
[0] == '?')) {
699 webkit_web_view_unmark_text_matches(webview
);
700 webkit_web_view_search_text(webview
, &text
[1], searchoptions
& CaseSensitive
, forward
, searchoptions
& Wrapping
);
702 } else if (gtk_widget_is_focus(GTK_WIDGET(entry
)) && length
>= 1 &&
703 (text
[0] == '.' || text
[0] == ',' || text
[0] == ';')) {
707 a
.s
= g_strconcat("hints.createHints('", text
+ 1, "', 'f');", NULL
);
711 a
.s
= g_strconcat("hints.createHints('", text
+ 1, "', 'F');", NULL
);
718 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 's');", NULL
);
721 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 'y');", NULL
);
724 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 'f');", NULL
);
727 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 'F');", NULL
);
729 case 'O': case 'T': case 'W':
730 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 'O');", NULL
);
733 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 'i');", NULL
);
736 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 'I');", NULL
);
748 } else if (length
== 0 && followTarget
[0]) {
751 a
.s
= g_strdup("hints.clearHints();");
763 void fill_suggline(char * suggline
, const char * command
, const char *fill_with
) {
764 memset(suggline
, 0, 512);
765 strncpy(suggline
, command
, 512);
766 strncat(suggline
, " ", 1);
767 strncat(suggline
, fill_with
, 512 - strlen(suggline
) - 1);
770 GtkWidget
* fill_eventbox(const char * completion_line
) {
772 GtkWidget
*row_eventbox
, *el
;
774 char *markup
, *markup_tmp
;
776 row
= GTK_BOX(gtk_hbox_new(FALSE
, 0));
777 row_eventbox
= gtk_event_box_new();
778 gdk_color_parse(completionbgcolor
[0], &color
);
779 gtk_widget_modify_bg(row_eventbox
, GTK_STATE_NORMAL
, &color
);
780 el
= gtk_label_new(NULL
);
781 markup_tmp
= g_markup_escape_text(completion_line
, strlen(completion_line
));
782 markup
= g_strconcat("<span font=\"", completionfont
[0], "\" color=\"", completioncolor
[0], "\">",
783 markup_tmp
, "</span>", NULL
);
784 gtk_label_set_markup(GTK_LABEL(el
), markup
);
787 gtk_misc_set_alignment(GTK_MISC(el
), 0, 0);
788 gtk_box_pack_start(row
, el
, TRUE
, TRUE
, 2);
789 gtk_container_add(GTK_CONTAINER(row_eventbox
), GTK_WIDGET(row
));
794 complete(const Arg
*arg
) {
795 char *str
, *p
, *s
, *markup
, *entry
, *searchfor
, command
[32] = "", suggline
[512] = "", **suggurls
;
796 size_t listlen
, len
, cmdlen
;
798 Listelement
*elementlist
= NULL
, *elementpointer
;
799 gboolean highlight
= FALSE
;
801 GtkWidget
*row_eventbox
, *el
;
804 static GtkWidget
*table
, *top_border
;
806 static char **suggestions
;
807 static GtkWidget
**widgets
;
808 static int n
= 0, m
, current
= -1;
810 str
= (char*)gtk_entry_get_text(GTK_ENTRY(inputbox
));
813 /* Get the length of the list of commands for completion. We need this to
814 * malloc/realloc correctly.
816 listlen
= LENGTH(commands
);
818 if ((len
== 0 || str
[0] != ':') && arg
->i
!= HideCompletion
)
821 if (arg
->i
!= HideCompletion
&& widgets
&& current
!= -1 && !strcmp(&str
[1], suggestions
[current
])) {
822 gdk_color_parse(completionbgcolor
[0], &color
);
823 gtk_widget_modify_bg(widgets
[current
], GTK_STATE_NORMAL
, &color
);
824 current
= (n
+ current
+ (arg
->i
== DirectionPrev
? -1 : 1)) % n
;
825 if ((arg
->i
== DirectionNext
&& current
== 0)
826 || (arg
->i
== DirectionPrev
&& current
== n
- 1))
832 gtk_widget_destroy(GTK_WIDGET(table
));
833 gtk_widget_destroy(GTK_WIDGET(top_border
));
840 if (arg
->i
== HideCompletion
)
843 } else if (arg
->i
== HideCompletion
)
846 prefix
= g_strdup(str
);
847 widgets
= malloc(sizeof(GtkWidget
*) * listlen
);
848 suggestions
= malloc(sizeof(char*) * listlen
);
849 top_border
= gtk_event_box_new();
850 gtk_widget_set_size_request(GTK_WIDGET(top_border
), 0, 1);
851 gdk_color_parse(completioncolor
[2], &color
);
852 gtk_widget_modify_bg(top_border
, GTK_STATE_NORMAL
, &color
);
853 table
= gtk_event_box_new();
854 gdk_color_parse(completionbgcolor
[0], &color
);
855 _table
= GTK_BOX(gtk_vbox_new(FALSE
, 0));
857 if (strchr(str
, ' ') == NULL
) {
858 /* command completion */
859 listlen
= LENGTH(commands
);
860 for (i
= 0; i
< listlen
; i
++) {
861 if (commands
[i
].cmd
== NULL
)
863 cmdlen
= strlen(commands
[i
].cmd
);
864 if (!highlight
|| (n
< MAX_LIST_SIZE
&& len
- 1 <= cmdlen
&& !strncmp(&str
[1], commands
[i
].cmd
, len
- 1))) {
865 p
= s
= malloc(sizeof(char*) * (highlight
? sizeof(COMPLETION_TAG_OPEN
) + sizeof(COMPLETION_TAG_CLOSE
) - 1 : 1) + cmdlen
);
867 memcpy(p
, COMPLETION_TAG_OPEN
, sizeof(COMPLETION_TAG_OPEN
) - 1);
868 memcpy((p
+= sizeof(COMPLETION_TAG_OPEN
) - 1), &str
[1], len
- 1);
869 memcpy((p
+= len
- 1), COMPLETION_TAG_CLOSE
, sizeof(COMPLETION_TAG_CLOSE
) - 1);
870 p
+= sizeof(COMPLETION_TAG_CLOSE
) - 1;
872 memcpy(p
, &commands
[i
].cmd
[len
- 1], cmdlen
- len
+ 2);
873 row
= GTK_BOX(gtk_hbox_new(FALSE
, 0));
874 row_eventbox
= gtk_event_box_new();
875 gtk_widget_modify_bg(row_eventbox
, GTK_STATE_NORMAL
, &color
);
876 el
= gtk_label_new(NULL
);
877 markup
= g_strconcat("<span font=\"", completionfont
[0], "\" color=\"", completioncolor
[0], "\">", s
, "</span>", NULL
);
879 gtk_label_set_markup(GTK_LABEL(el
), markup
);
881 gtk_misc_set_alignment(GTK_MISC(el
), 0, 0);
882 gtk_box_pack_start(row
, el
, TRUE
, TRUE
, 2);
883 gtk_container_add(GTK_CONTAINER(row_eventbox
), GTK_WIDGET(row
));
884 gtk_box_pack_start(_table
, GTK_WIDGET(row_eventbox
), FALSE
, FALSE
, 0);
885 suggestions
[n
] = commands
[i
].cmd
;
886 widgets
[n
++] = row_eventbox
;
890 entry
= (char *)malloc(512 * sizeof(char));
894 memset(entry
, 0, 512);
895 suggurls
= malloc(sizeof(char*) * listlen
);
896 if (suggurls
== NULL
) {
899 spacepos
= strcspn(str
, " ");
900 searchfor
= (str
+ spacepos
+ 1);
901 strncpy(command
, (str
+ 1), spacepos
- 1);
902 if (strlen(command
) == 3 && strncmp(command
, "set", 3) == 0) {
903 /* browser settings */
904 listlen
= LENGTH(browsersettings
);
905 for (i
= 0; i
< listlen
; i
++) {
906 if (n
< MAX_LIST_SIZE
&& strstr(browsersettings
[i
].name
, searchfor
) != NULL
) {
908 fill_suggline(suggline
, command
, browsersettings
[i
].name
);
909 /* FIXME(HP): This memory is never freed */
910 suggurls
[n
] = (char *)malloc(sizeof(char) * 512 + 1);
911 strncpy(suggurls
[n
], suggline
, 512);
912 suggestions
[n
] = suggurls
[n
];
913 row_eventbox
= fill_eventbox(suggline
);
914 gtk_box_pack_start(_table
, GTK_WIDGET(row_eventbox
), FALSE
, FALSE
, 0);
915 widgets
[n
++] = row_eventbox
;
919 } else if (strlen(command
) == 2 && strncmp(command
, "qt", 2) == 0) {
920 /* completion on tags */
921 spacepos
= strcspn(str
, " ");
922 searchfor
= (str
+ spacepos
+ 1);
923 elementlist
= complete_list(searchfor
, 1, elementlist
);
925 /* URL completion: bookmarks */
926 elementlist
= complete_list(searchfor
, 0, elementlist
);
927 m
= count_list(elementlist
);
928 if (m
< MAX_LIST_SIZE
) {
929 /* URL completion: history */
930 elementlist
= complete_list(searchfor
, 2, elementlist
);
933 elementpointer
= elementlist
;
934 while (elementpointer
!= NULL
) {
935 fill_suggline(suggline
, command
, elementpointer
->element
);
936 /* FIXME(HP): This memory is never freed */
937 suggurls
[n
] = (char *)malloc(sizeof(char) * 512 + 1);
938 strncpy(suggurls
[n
], suggline
, 512);
939 suggestions
[n
] = suggurls
[n
];
940 row_eventbox
= fill_eventbox(suggline
);
941 gtk_box_pack_start(_table
, GTK_WIDGET(row_eventbox
), FALSE
, FALSE
, 0);
942 widgets
[n
++] = row_eventbox
;
943 elementpointer
= elementpointer
->next
;
944 if (n
>= MAX_LIST_SIZE
)
947 free_list(elementlist
);
948 if (suggurls
!= NULL
) {
957 /* TA: FIXME - this needs rethinking entirely. */
959 GtkWidget
**widgets_temp
= realloc(widgets
, sizeof(*widgets
) * n
);
960 if (widgets_temp
== NULL
&& widgets
== NULL
) {
961 fprintf(stderr
, "Couldn't realloc() widgets\n");
964 widgets
= widgets_temp
;
965 char **suggestions_temp
= realloc(suggestions
, sizeof(*suggestions
) * n
);
966 if (suggestions_temp
== NULL
&& suggestions
== NULL
) {
967 fprintf(stderr
, "Couldn't realloc() suggestions\n");
970 suggestions
= suggestions_temp
;
973 gdk_color_parse(completionbgcolor
[1], &color
);
974 gtk_widget_modify_bg(table
, GTK_STATE_NORMAL
, &color
);
975 el
= gtk_label_new(NULL
);
976 gtk_misc_set_alignment(GTK_MISC(el
), 0, 0);
977 markup
= g_strconcat("<span font=\"", completionfont
[1], "\" color=\"", completioncolor
[1], "\">No Completions</span>", NULL
);
978 gtk_label_set_markup(GTK_LABEL(el
), markup
);
980 gtk_box_pack_start(_table
, GTK_WIDGET(el
), FALSE
, FALSE
, 0);
982 gtk_box_pack_start(box
, GTK_WIDGET(top_border
), FALSE
, FALSE
, 0);
983 gtk_container_add(GTK_CONTAINER(table
), GTK_WIDGET(_table
));
984 gtk_box_pack_start(box
, GTK_WIDGET(table
), FALSE
, FALSE
, 0);
985 gtk_widget_show_all(GTK_WIDGET(window
));
988 current
= arg
->i
== DirectionPrev
? n
- 1 : 0;
991 gdk_color_parse(completionbgcolor
[2], &color
);
992 gtk_widget_modify_bg(GTK_WIDGET(widgets
[current
]), GTK_STATE_NORMAL
, &color
);
993 s
= g_strconcat(":", suggestions
[current
], NULL
);
994 gtk_entry_set_text(GTK_ENTRY(inputbox
), s
);
997 gtk_entry_set_text(GTK_ENTRY(inputbox
), prefix
);
998 gtk_editable_set_position(GTK_EDITABLE(inputbox
), -1);
1003 descend(const Arg
*arg
) {
1004 char *source
= (char*)webkit_web_view_get_uri(webview
), *p
= &source
[0], *new;
1006 count
= count
? count
: 1;
1010 if (arg
->i
== Rootdir
) {
1011 for (i
= 0; i
< 3; i
++) /* get to the third slash */
1012 if (!(p
= strchr(++p
, '/')))
1013 return TRUE
; /* if we cannot find it quit */
1015 len
= strlen(source
);
1016 if (!len
) /* if string is empty quit */
1018 p
= source
+ len
; /* start at the end */
1019 if (*(p
- 1) == '/') /* /\/$/ is not an additional level */
1021 for (i
= 0; i
< count
; i
++)
1022 while(*(p
--) != '/' || *p
== '/') /* count /\/+/ as one slash */
1023 if (p
== source
) /* if we reach the first char pointer quit */
1025 ++p
; /* since we do p-- in the while, we are pointing at
1026 the char before the slash, so +1 */
1028 len
= p
- source
+ 1; /* new length = end - start + 1 */
1029 new = malloc(len
+ 1);
1030 memcpy(new, source
, len
);
1032 webkit_web_view_load_uri(webview
, new);
1038 echo(const Arg
*arg
) {
1039 int index
= !arg
->s
? 0 : arg
->i
& (~NoAutoHide
);
1041 if (index
< Info
|| index
> Error
)
1044 if (!gtk_widget_is_focus(GTK_WIDGET(inputbox
))) {
1045 set_widget_font_and_color(inputbox
, urlboxfont
[index
], urlboxbgcolor
[index
], urlboxcolor
[index
]);
1046 gtk_entry_set_text(GTK_ENTRY(inputbox
), !arg
->s
? "" : arg
->s
);
1053 input(const Arg
*arg
) {
1060 /* if inputbox hidden, show it again */
1061 if (!gtk_widget_get_visible(inputbox
))
1062 gtk_widget_set_visible(inputbox
, TRUE
);
1066 /* Set the colour and font back to the default, so that we don't still
1067 * maintain a red colour from a warning from an end of search indicator,
1070 set_widget_font_and_color(inputbox
, urlboxfont
[index
], urlboxbgcolor
[index
], urlboxcolor
[index
]);
1072 /* to avoid things like :open URL :open URL2 or :open :open URL */
1073 gtk_entry_set_text(GTK_ENTRY(inputbox
), "");
1074 gtk_editable_insert_text(GTK_EDITABLE(inputbox
), arg
->s
, -1, &pos
);
1075 if (arg
->i
& InsertCurrentURL
&& (url
= webkit_web_view_get_uri(webview
)))
1076 gtk_editable_insert_text(GTK_EDITABLE(inputbox
), url
, -1, &pos
);
1078 gtk_widget_grab_focus(inputbox
);
1079 gtk_editable_set_position(GTK_EDITABLE(inputbox
), -1);
1081 if (arg
->s
[0] == '.' || arg
->s
[0] == ',' || arg
->s
[0] == ';') {
1083 memset(followTarget
, 0, 8);
1084 strncpy(followTarget
, "current", 8);
1086 switch (arg
->s
[0]) {
1088 a
.s
= g_strdup("hints.createHints('', 'f');");
1092 a
.s
= g_strdup("hints.createHints('', 'F');");
1098 switch (arg
->s
[1]) {
1100 a
.s
= g_strdup("hints.createHints('', 's');");
1103 a
.s
= g_strdup("hints.createHints('', 'y');");
1106 a
.s
= g_strdup("hints.createHints('', 'f');");
1109 a
.s
= g_strdup("hints.createHints('', 'F');");
1111 case 'O': case 'T': case 'W':
1112 a
.s
= g_strdup("hints.createHints('', 'O');");
1115 a
.s
= g_strdup("hints.createHints('', 'i');");
1118 a
.s
= g_strdup("hints.createHints('', 'I');");
1135 navigate(const Arg
*arg
) {
1136 if (arg
->i
& NavigationForwardBack
)
1137 webkit_web_view_go_back_or_forward(webview
, (arg
->i
== NavigationBack
? -1 : 1) * (count
? count
: 1));
1138 else if (arg
->i
& NavigationReloadActions
)
1139 (arg
->i
== NavigationReload
? webkit_web_view_reload
: webkit_web_view_reload_bypass_cache
)(webview
);
1141 webkit_web_view_stop_loading(webview
);
1146 number(const Arg
*arg
) {
1147 const char *source
= webkit_web_view_get_uri(webview
);
1148 char *uri
, *p
, *new;
1149 int number
, diff
= (count
? count
: 1) * (arg
->i
== Increment
? 1 : -1);
1153 uri
= g_strdup(source
); /* copy string */
1155 while(*p
!= '\0') /* goto the end of the string */
1158 while(*p
>= '0' && *p
<= '9') /* go back until non number char is reached */
1160 if (*(++p
) == '\0') { /* if no numbers were found abort */
1164 number
= atoi(p
) + diff
; /* apply diff on number */
1166 new = g_strdup_printf("%s%d", uri
, number
); /* create new uri */
1167 webkit_web_view_load_uri(webview
, new);
1174 open_arg(const Arg
*arg
) {
1176 char *s
= arg
->s
, *p
= NULL
, *new;
1177 Arg a
= { .i
= NavigationReload
};
1179 char *search_uri
, *search_term
;
1195 else if (arg
->i
== TargetCurrent
) {
1196 while(*s
== ' ') /* strip leading whitespace */
1198 p
= (s
+ strlen(s
) - 1);
1199 while(*p
== ' ') /* strip trailing whitespace */
1204 /* check for external handlers */
1205 if (open_handler(s
))
1207 /* check for search engines */
1209 if (p
) { /* check for search engines */
1211 search_uri
= find_uri_for_searchengine(s
);
1212 if (search_uri
!= NULL
) {
1213 search_term
= soup_uri_encode(p
+1, "&");
1214 new = g_strdup_printf(search_uri
, search_term
);
1215 g_free(search_term
);
1220 if (len
> 3 && strstr(s
, "://")) { /* valid url? */
1221 p
= new = g_malloc(len
+ 1);
1222 while(*s
!= '\0') { /* strip whitespaces */
1228 } else if (strcspn(s
, "/") == 0 || strcspn(s
, "./") == 0) { /* prepend "file://" */
1229 new = g_malloc(sizeof("file://") + len
);
1230 strcpy(new, "file://");
1231 memcpy(&new[sizeof("file://") - 1], s
, len
+ 1);
1232 } else if (p
|| !strchr(s
, '.')) { /* whitespaces or no dot? */
1233 search_uri
= find_uri_for_searchengine(defaultsearch
);
1234 if (search_uri
!= NULL
) {
1235 search_term
= soup_uri_encode(s
, "&");
1236 new = g_strdup_printf(search_uri
, search_term
);
1237 g_free(search_term
);
1239 } else { /* prepend "http://" */
1240 new = g_malloc(sizeof("http://") + len
);
1241 strcpy(new, "http://");
1242 memcpy(&new[sizeof("http://") - 1], s
, len
+ 1);
1245 webkit_web_view_load_uri(webview
, new);
1248 g_spawn_async(NULL
, argv
, NULL
, G_SPAWN_SEARCH_PATH
, NULL
, NULL
, NULL
, NULL
);
1253 open_remembered(const Arg
*arg
)
1255 Arg a
= {arg
->i
, rememberedURI
};
1257 if (strcmp(rememberedURI
, "")) {
1264 yank(const Arg
*arg
) {
1265 const char *url
, *feedback
, *content
;
1267 if (arg
->i
& SourceSelection
) {
1268 webkit_web_view_copy_clipboard(webview
);
1269 if (arg
->i
& ClipboardPrimary
)
1270 content
= gtk_clipboard_wait_for_text(clipboards
[0]);
1271 if (!content
&& arg
->i
& ClipboardGTK
)
1272 content
= gtk_clipboard_wait_for_text(clipboards
[1]);
1274 feedback
= g_strconcat("Yanked ", content
, NULL
);
1275 g_free((gpointer
*)content
);
1276 give_feedback(feedback
);
1277 g_free((gpointer
*)feedback
);
1280 if (arg
->i
& SourceURL
) {
1281 url
= webkit_web_view_get_uri(webview
);
1287 feedback
= g_strconcat("Yanked ", url
, NULL
);
1288 give_feedback(feedback
);
1289 if (arg
->i
& ClipboardPrimary
)
1290 gtk_clipboard_set_text(clipboards
[0], url
, -1);
1291 if (arg
->i
& ClipboardGTK
)
1292 gtk_clipboard_set_text(clipboards
[1], url
, -1);
1298 paste(const Arg
*arg
) {
1299 Arg a
= { .i
= arg
->i
& TargetNew
, .s
= NULL
};
1301 /* If we're over a link, open it in a new target. */
1302 if (strlen(rememberedURI
) > 0) {
1303 Arg new_target
= { .i
= TargetNew
, .s
= arg
->s
};
1304 open_arg(&new_target
);
1308 if (arg
->i
& ClipboardPrimary
)
1309 a
.s
= gtk_clipboard_wait_for_text(clipboards
[0]);
1310 if (!a
.s
&& arg
->i
& ClipboardGTK
)
1311 a
.s
= gtk_clipboard_wait_for_text(clipboards
[1]);
1320 quit(const Arg
*arg
) {
1322 const char *filename
;
1323 const char *uri
= webkit_web_view_get_uri(webview
);
1325 /* write last URL into status file for recreation with "u" */
1326 filename
= g_strdup_printf(CLOSED_URL_FILENAME
);
1327 f
= fopen(filename
, "w");
1328 g_free((gpointer
*)filename
);
1330 fprintf(f
, "%s", uri
);
1339 revive(const Arg
*arg
) {
1341 const char *filename
;
1342 char buffer
[512] = "";
1343 Arg a
= { .i
= TargetNew
, .s
= NULL
};
1344 /* get the URL of the window which has been closed last */
1345 filename
= g_strdup_printf(CLOSED_URL_FILENAME
);
1346 f
= fopen(filename
, "r");
1347 g_free((gpointer
*)filename
);
1349 fgets(buffer
, 512, f
);
1352 if (strlen(buffer
) > 0) {
1361 gboolean
print_frame(const Arg
*arg
)
1363 WebKitWebFrame
*frame
= webkit_web_view_get_main_frame(webview
);
1364 webkit_web_frame_print (frame
);
1369 search(const Arg
*arg
) {
1370 count
= count
? count
: 1;
1371 gboolean success
, direction
= arg
->i
& DirectionPrev
;
1375 free(search_handle
);
1376 search_handle
= g_strdup(arg
->s
);
1380 if (arg
->i
& DirectionAbsolute
)
1381 search_direction
= direction
;
1383 direction
^= search_direction
;
1385 success
= webkit_web_view_search_text(webview
, search_handle
, arg
->i
& CaseSensitive
, direction
, FALSE
);
1387 if (arg
->i
& Wrapping
) {
1388 success
= webkit_web_view_search_text(webview
, search_handle
, arg
->i
& CaseSensitive
, direction
, TRUE
);
1391 a
.s
= g_strdup_printf("search hit %s, continuing at %s",
1392 direction
? "BOTTOM" : "TOP",
1393 direction
? "TOP" : "BOTTOM");
1404 a
.s
= g_strdup_printf("Pattern not found: %s", search_handle
);
1412 set(const Arg
*arg
) {
1413 Arg a
= { .i
= Info
| NoAutoHide
};
1417 if (search_handle
) {
1418 search_handle
= NULL
;
1419 webkit_web_view_unmark_text_matches(webview
);
1421 gtk_entry_set_text(GTK_ENTRY(inputbox
), "");
1422 gtk_widget_grab_focus(GTK_WIDGET(webview
));
1424 case ModePassThrough
:
1425 a
.s
= g_strdup("-- PASS THROUGH --");
1430 a
.s
= g_strdup("-- PASS TROUGH (next) --");
1434 case ModeInsert
: /* should not be called manually but automatically */
1435 a
.s
= g_strdup("-- INSERT --");
1447 jsapi_ref_to_string(JSContextRef context
, JSValueRef ref
) {
1448 JSStringRef string_ref
;
1452 string_ref
= JSValueToStringCopy(context
, ref
, NULL
);
1453 length
= JSStringGetMaximumUTF8CStringSize(string_ref
);
1454 string
= g_new(gchar
, length
);
1455 JSStringGetUTF8CString(string_ref
, string
, length
);
1456 JSStringRelease(string_ref
);
1461 jsapi_evaluate_script(const gchar
*script
, gchar
**value
, gchar
**message
) {
1462 WebKitWebFrame
*frame
= webkit_web_view_get_main_frame(webview
);
1463 JSGlobalContextRef context
= webkit_web_frame_get_global_context(frame
);
1465 JSValueRef val
, exception
;
1467 str
= JSStringCreateWithUTF8CString(script
);
1468 val
= JSEvaluateScript(context
, str
, JSContextGetGlobalObject(context
), NULL
, 0, &exception
);
1469 JSStringRelease(str
);
1471 *message
= jsapi_ref_to_string(context
, exception
);
1473 *value
= jsapi_ref_to_string(context
, val
);
1477 quickmark(const Arg
*a
) {
1480 char *fn
= g_strdup_printf(QUICKMARK_FILE
);
1482 fp
= fopen(fn
, "r");
1487 if (fp
!= NULL
&& b
< 10) {
1488 for( i
=0; i
< b
; ++i
) {
1492 fgets(buf
, 100, fp
);
1494 char *ptr
= strrchr(buf
, '\n');
1496 Arg x
= { .s
= buf
};
1498 return open_arg(&x
);
1501 x
.s
= g_strdup_printf("Quickmark %d not defined", b
);
1506 } else { return false; }
1510 script(const Arg
*arg
) {
1511 gchar
*value
= NULL
, *message
= NULL
;
1512 char text
[1024] = "";
1514 WebKitNetworkRequest
*request
;
1515 WebKitDownload
*download
;
1518 set_error("Missing argument.");
1521 jsapi_evaluate_script(arg
->s
, &value
, &message
);
1528 if (arg
->i
!= Silent
&& value
) {
1530 a
.s
= g_strdup(value
);
1534 /* switch mode according to scripts return value */
1536 if (strncmp(value
, "done;", 5) == 0) {
1539 } else if (strncmp(value
, "insert;", 7) == 0) {
1542 manual_focus
= TRUE
;
1543 } else if (strncmp(value
, "save;", 5) == 0) {
1544 /* forced download */
1547 request
= webkit_network_request_new((value
+ 5));
1548 download
= webkit_download_new(request
);
1549 webview_download_cb(webview
, download
, (gpointer
*)NULL
);
1550 } else if (strncmp(value
, "yank;", 5) == 0) {
1551 /* yank link URL to clipboard */
1554 a
.i
= ClipboardPrimary
| ClipboardGTK
;
1557 } else if (strncmp(value
, "colon;", 6) == 0) {
1558 /* use link URL for colon command */
1559 strncpy(text
, (char *)gtk_entry_get_text(GTK_ENTRY(inputbox
)), 1023);
1564 a
.s
= g_strconcat(":open ", (value
+ 6), NULL
);
1567 a
.s
= g_strconcat(":tabopen ", (value
+ 6), NULL
);
1574 } else if (strncmp(value
, "error;", 6) == 0) {
1584 scroll(const Arg
*arg
) {
1585 GtkAdjustment
*adjust
= (arg
->i
& OrientationHoriz
) ? adjust_h
: adjust_v
;
1586 int max
= gtk_adjustment_get_upper(adjust
) - gtk_adjustment_get_page_size(adjust
);
1587 float val
= gtk_adjustment_get_value(adjust
) / max
* 100;
1588 int direction
= (arg
->i
& (1 << 2)) ? 1 : -1;
1590 if ((direction
== 1 && val
< 100) || (direction
== -1 && val
> 0)) {
1591 if (arg
->i
& ScrollMove
)
1592 gtk_adjustment_set_value(adjust
, gtk_adjustment_get_value(adjust
) +
1593 direction
* /* direction */
1594 ((arg
->i
& UnitLine
|| (arg
->i
& UnitBuffer
&& count
)) ? (scrollstep
* (count
? count
: 1)) : (
1595 arg
->i
& UnitBuffer
? gtk_adjustment_get_page_size(adjust
) / 2 :
1596 (count
? count
: 1) * (gtk_adjustment_get_page_size(adjust
) -
1597 (gtk_adjustment_get_page_size(adjust
) > pagingkeep
? pagingkeep
: 0)))));
1599 gtk_adjustment_set_value(adjust
,
1600 ((direction
== 1) ? gtk_adjustment_get_upper
: gtk_adjustment_get_lower
)(adjust
));
1607 zoom(const Arg
*arg
) {
1608 webkit_web_view_set_full_content_zoom(webview
, (arg
->i
& ZoomFullContent
) > 0);
1609 webkit_web_view_set_zoom_level(webview
, (arg
->i
& ZoomOut
) ?
1610 webkit_web_view_get_zoom_level(webview
) +
1611 (((float)(count
? count
: 1)) * (arg
->i
& (1 << 1) ? 1.0 : -1.0) * zoomstep
) :
1612 (count
? (float)count
/ 100.0 : 1.0));
1617 fake_key_event(const Arg
*a
) {
1624 if ( (xdpy
= XOpenDisplay(NULL
)) == NULL
) {
1625 err
.s
= g_strdup("Couldn't find the XDisplay.");
1633 xk
.subwindow
= None
;
1634 xk
.time
= CurrentTime
;
1635 xk
.same_screen
= True
;
1636 xk
.x
= xk
.y
= xk
.x_root
= xk
.y_root
= 1;
1641 err
.s
= g_strdup("Zero pointer as argument! Check your config.h");
1648 if( (keysym
= XStringToKeysym(a
->s
)) == NoSymbol
) {
1649 err
.s
= g_strdup_printf("Couldn't translate %s to keysym", a
->s
);
1655 if( (xk
.keycode
= XKeysymToKeycode(xdpy
, keysym
)) == NoSymbol
) {
1656 err
.s
= g_strdup("Couldn't translate keysym to keycode");
1663 if( !XSendEvent(xdpy
, embed
, True
, KeyPressMask
, (XEvent
*)&xk
) ) {
1664 err
.s
= g_strdup("XSendEvent failed");
1676 commandhistoryfetch(const Arg
*arg
) {
1677 const int length
= g_list_length(commandhistory
);
1680 if (arg
->i
== DirectionPrev
) {
1681 commandpointer
= (length
+ commandpointer
- 1) % length
;
1683 commandpointer
= (length
+ commandpointer
+ 1) % length
;
1686 const char* command
= (char *)g_list_nth_data(commandhistory
, commandpointer
);
1687 gtk_entry_set_text(GTK_ENTRY(inputbox
), g_strconcat(":", command
, NULL
));
1688 gtk_editable_set_position(GTK_EDITABLE(inputbox
), -1);
1696 bookmark(const Arg
*arg
) {
1698 const char *filename
;
1699 const char *uri
= webkit_web_view_get_uri(webview
);
1700 const char *title
= webkit_web_view_get_title(webview
);
1701 filename
= g_strdup_printf(BOOKMARKS_STORAGE_FILENAME
);
1702 f
= fopen(filename
, "a");
1703 g_free((gpointer
*)filename
);
1704 if (uri
== NULL
|| strlen(uri
) == 0) {
1705 set_error("No URI found to bookmark.");
1709 fprintf(f
, "%s", uri
);
1710 if (title
!= NULL
) {
1711 fprintf(f
, "%s", " ");
1712 fprintf(f
, "%s", title
);
1714 if (arg
->s
&& strlen(arg
->s
)) {
1715 build_taglist(arg
, f
);
1717 fprintf(f
, "%s", "\n");
1719 give_feedback( "Bookmark saved" );
1722 set_error("Bookmarks file not found.");
1730 const char *filename
;
1731 const char *uri
= webkit_web_view_get_uri(webview
);
1732 const char *title
= webkit_web_view_get_title(webview
);
1733 char *entry
, buffer
[512], *new;
1735 gboolean finished
= FALSE
;
1737 if (title
!= NULL
) {
1738 entry
= malloc((strlen(uri
) + strlen(title
) + 2) * sizeof(char));
1739 memset(entry
, 0, strlen(uri
) + strlen(title
) + 2);
1741 entry
= malloc((strlen(uri
) + 1) * sizeof(char));
1742 memset(entry
, 0, strlen(uri
) + 1);
1744 if (entry
!= NULL
) {
1745 strncpy(entry
, uri
, strlen(uri
));
1746 if (title
!= NULL
) {
1747 strncat(entry
, " ", 1);
1748 strncat(entry
, title
, strlen(title
));
1751 filename
= g_strdup_printf(HISTORY_STORAGE_FILENAME
);
1752 f
= fopen(filename
, "r");
1754 new = (char *)malloc(HISTORY_MAX_ENTRIES
* 512 * sizeof(char) + 1);
1756 memset(new, 0, HISTORY_MAX_ENTRIES
* 512 * sizeof(char) + 1);
1757 /* newest entries go on top */
1758 strncpy(new, entry
, strlen(entry
));
1759 strncat(new, "\n", 1);
1760 /* retain at most HISTORY_MAX_ENTIRES - 1 old entries */
1761 while (finished
!= TRUE
) {
1762 if ((char *)NULL
== fgets(buffer
, 512, f
)) {
1763 /* check if end of file was reached / error occured */
1767 /* end of file reached */
1771 /* compare line (-1 because of newline character) */
1772 if (n
!= strlen(buffer
) - 1 || strncmp(entry
, buffer
, n
) != 0) {
1773 /* if the URI is already in history; we put it on top and skip it here */
1774 strncat(new, buffer
, 512);
1777 if ((i
+ 1) >= HISTORY_MAX_ENTRIES
) {
1783 f
= fopen(filename
, "w");
1784 g_free((gpointer
*)filename
);
1786 fprintf(f
, "%s", new);
1795 if (entry
!= NULL
) {
1804 view_source(const Arg
* arg
) {
1805 gboolean current_mode
= webkit_web_view_get_view_source_mode(webview
);
1806 webkit_web_view_set_view_source_mode(webview
, !current_mode
);
1807 webkit_web_view_reload(webview
);
1812 focus_input(const Arg
*arg
) {
1815 a
.s
= g_strdup("hints.focusInput();");
1820 manual_focus
= TRUE
;
1825 browser_settings(const Arg
*arg
) {
1828 set_error("Missing argument.");
1831 strncpy(line
, arg
->s
, 254);
1832 if (process_set_line(line
))
1835 set_error("Invalid setting.");
1841 search_word(int whichword
) {
1843 static char word
[240];
1844 char *c
= my_pair
.line
;
1846 while (isspace(*c
) && *c
)
1849 switch (whichword
) {
1851 while (*c
&& !isspace (*c
) && *c
!= '=' && k
< 240) {
1856 strncpy(my_pair
.what
, word
, 20);
1859 while (*c
&& k
< 240) {
1864 strncpy(my_pair
.value
, word
, 240);
1872 process_set_line(char *line
) {
1876 WebKitWebSettings
*settings
;
1878 settings
= webkit_web_view_get_settings(webview
);
1879 my_pair
.line
= line
;
1881 if (!strlen(my_pair
.what
))
1884 while (isspace(*c
) && *c
)
1887 if (*c
== ':' || *c
== '=')
1893 listlen
= LENGTH(browsersettings
);
1894 for (i
= 0; i
< listlen
; i
++) {
1895 if (strlen(browsersettings
[i
].name
) == strlen(my_pair
.what
) && strncmp(browsersettings
[i
].name
, my_pair
.what
, strlen(my_pair
.what
)) == 0) {
1896 /* mandatory argument not provided */
1897 if (strlen(my_pair
.value
) == 0)
1899 /* process qmark? */
1900 if (strlen(my_pair
.what
) == 5 && strncmp("qmark", my_pair
.what
, 5) == 0) {
1901 return (process_save_qmark(my_pair
.value
, webview
));
1903 /* interpret boolean values */
1904 if (browsersettings
[i
].boolval
) {
1905 if (strncmp(my_pair
.value
, "on", 2) == 0 || strncmp(my_pair
.value
, "true", 4) == 0 || strncmp(my_pair
.value
, "ON", 2) == 0 || strncmp(my_pair
.value
, "TRUE", 4) == 0) {
1907 } else if (strncmp(my_pair
.value
, "off", 3) == 0 || strncmp(my_pair
.value
, "false", 5) == 0 || strncmp(my_pair
.value
, "OFF", 3) == 0 || strncmp(my_pair
.value
, "FALSE", 5) == 0) {
1912 } else if (browsersettings
[i
].colourval
) {
1913 /* interpret as hexadecimal colour */
1914 if (!parse_colour(my_pair
.value
)) {
1918 if (browsersettings
[i
].var
!= NULL
) {
1919 strncpy(browsersettings
[i
].var
, my_pair
.value
, MAX_SETTING_SIZE
);
1920 if (strlen(my_pair
.value
) > MAX_SETTING_SIZE
- 1) {
1921 /* in this case, \0 will not have been copied */
1922 browsersettings
[i
].var
[MAX_SETTING_SIZE
- 1] = '\0';
1923 /* in case this string is also used for a webkit setting, make sure it's consistent */
1924 my_pair
.value
[MAX_SETTING_SIZE
- 1] = '\0';
1925 give_feedback("String too long; automatically truncated!");
1928 if (strlen(browsersettings
[i
].webkit
) > 0) {
1929 /* activate appropriate webkit setting */
1930 if (browsersettings
[i
].boolval
) {
1931 g_object_set((GObject
*)settings
, browsersettings
[i
].webkit
, boolval
, NULL
);
1932 } else if (browsersettings
[i
].intval
) {
1933 g_object_set((GObject
*)settings
, browsersettings
[i
].webkit
, atoi(my_pair
.value
), NULL
);
1935 g_object_set((GObject
*)settings
, browsersettings
[i
].webkit
, my_pair
.value
, NULL
);
1937 webkit_web_view_set_settings(webview
, settings
);
1940 if (strlen(my_pair
.what
) == 14) {
1941 if (strncmp("acceptlanguage", my_pair
.what
, 14) == 0) {
1942 g_object_set(G_OBJECT(session
), "accept-language", acceptlanguage
, NULL
);
1943 } else if (strncmp("completioncase", my_pair
.what
, 14) == 0) {
1944 complete_case_sensitive
= boolval
;
1946 } else if (strlen(my_pair
.what
) == 5 && strncmp("proxy", my_pair
.what
, 5) == 0) {
1947 toggle_proxy(boolval
);
1948 } else if (strlen(my_pair
.what
) == 10 && strncmp("scrollbars", my_pair
.what
, 10) == 0) {
1949 toggle_scrollbars(boolval
);
1950 } else if (strlen(my_pair
.what
) == 9 && strncmp("statusbar", my_pair
.what
, 9) == 0) {
1951 gtk_widget_set_visible(GTK_WIDGET(statusbar
), boolval
);
1952 } else if (strlen(my_pair
.what
) == 8 && strncmp("inputbox", my_pair
.what
, 8) == 0) {
1953 gtk_widget_set_visible(inputbox
, boolval
);
1954 } else if (strlen(my_pair
.what
) == 11 && strncmp("escapeinput", my_pair
.what
, 11) == 0) {
1955 escape_input_on_load
= boolval
;
1958 /* SSL certificate checking */
1959 if (strlen(my_pair
.what
) == 9 && strncmp("strictssl", my_pair
.what
, 9) == 0) {
1962 g_object_set(G_OBJECT(session
), "ssl-strict", TRUE
, NULL
);
1965 g_object_set(G_OBJECT(session
), "ssl-strict", FALSE
, NULL
);
1968 if (strlen(my_pair
.what
) == 8 && strncmp("cabundle", my_pair
.what
, 8) == 0) {
1969 g_object_set(G_OBJECT(session
), SOUP_SESSION_SSL_CA_FILE
, ca_bundle
, NULL
);
1973 if (browsersettings
[i
].reload
)
1974 webkit_web_view_reload(webview
);
1982 process_line(char *line
) {
1983 char *c
= line
, *command_hist
;
1985 size_t len
, length
= strlen(line
);
1986 gboolean found
= FALSE
, success
= FALSE
;
1991 /* Ignore blank lines. */
1995 command_hist
= g_strdup(c
);
1996 for (i
= 0; i
< LENGTH(commands
); i
++) {
1997 if (commands
[i
].cmd
== NULL
)
1999 len
= strlen(commands
[i
].cmd
);
2000 if (length
>= len
&& !strncmp(c
, commands
[i
].cmd
, len
) && (c
[len
] == ' ' || !c
[len
])) {
2002 a
.i
= commands
[i
].arg
.i
;
2003 a
.s
= g_strdup(length
> len
+ 1 ? &c
[len
+ 1] : commands
[i
].arg
.s
);
2004 success
= commands
[i
].func(&a
);
2010 save_command_history(command_hist
);
2011 g_free(command_hist
);
2015 a
.s
= g_strdup_printf("Not a browser command: %s", c
);
2018 } else if (!success
) {
2020 if (error_msg
!= NULL
) {
2021 a
.s
= g_strdup_printf("%s", error_msg
);
2025 a
.s
= g_strdup_printf("Unknown error. Please file a bug report!");
2034 search_tag(const Arg
* a
) {
2036 const char *filename
;
2037 const char *tag
= a
->s
;
2038 char s
[BUFFERSIZE
], foundtag
[40], url
[BUFFERSIZE
];
2042 /* The user must give us something to load up. */
2043 set_error("Bookmark tag required with this option.");
2047 if (strlen(tag
) > MAXTAGSIZE
) {
2048 set_error("Tag too long.");
2052 filename
= g_strdup_printf(BOOKMARKS_STORAGE_FILENAME
);
2053 f
= fopen(filename
, "r");
2054 g_free((gpointer
*)filename
);
2056 set_error("Couldn't open bookmarks file.");
2059 while (fgets(s
, BUFFERSIZE
-1, f
)) {
2062 while (isspace(s
[t
]))
2064 if (s
[t
] != ']') continue;
2077 foundtag
[i
++] = s
[k
++];
2079 /* foundtag now contains the tag */
2080 if (strlen(foundtag
) < MAXTAGSIZE
&& strcmp(tag
, foundtag
) == 0) {
2082 while (isspace(s
[i
])) i
++;
2084 while (s
[i
] && !isspace(s
[i
])) url
[k
++] = s
[i
++];
2086 Arg x
= { .i
= TargetNew
, .s
= url
};
2100 toggle_proxy(gboolean onoff
) {
2102 char *filename
, *new;
2104 if (onoff
== FALSE
) {
2105 g_object_set(session
, "proxy-uri", NULL
, NULL
);
2107 filename
= (char *)g_getenv("http_proxy");
2109 /* Fallthrough to checking HTTP_PROXY as well, since this can also be
2112 if (filename
== NULL
)
2113 filename
= (char *)g_getenv("HTTP_PROXY");
2115 if (filename
!= NULL
&& 0 < strlen(filename
)) {
2116 new = g_strrstr(filename
, "http://") ? g_strdup(filename
) : g_strdup_printf("http://%s", filename
);
2117 proxy_uri
= soup_uri_new(new);
2119 g_object_set(session
, "proxy-uri", proxy_uri
, NULL
);
2121 soup_uri_free(proxy_uri
);
2128 toggle_scrollbars(gboolean onoff
) {
2129 if (onoff
== TRUE
) {
2130 adjust_h
= gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(viewport
));
2131 adjust_v
= gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(viewport
));
2132 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(viewport
), GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
2134 adjust_v
= gtk_range_get_adjustment(GTK_RANGE(scroll_v
));
2135 adjust_h
= gtk_range_get_adjustment(GTK_RANGE(scroll_h
));
2136 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(viewport
), GTK_POLICY_NEVER
, GTK_POLICY_NEVER
);
2138 gtk_widget_set_scroll_adjustments (GTK_WIDGET(webview
), adjust_h
, adjust_v
);
2144 update_url(const char *uri
) {
2145 gboolean ssl
= g_str_has_prefix(uri
, "https://");
2147 WebKitWebFrame
*frame
;
2148 WebKitWebDataSource
*src
;
2149 WebKitNetworkRequest
*request
;
2152 char *sslactivecolor
;
2154 #ifdef ENABLE_HISTORY_INDICATOR
2155 char before
[] = " [";
2157 gboolean back
= webkit_web_view_can_go_back(webview
);
2158 gboolean fwd
= webkit_web_view_can_go_forward(webview
);
2161 before
[0] = after
[0] = '\0';
2163 markup
= g_markup_printf_escaped(
2164 #ifdef ENABLE_HISTORY_INDICATOR
2165 "<span font=\"%s\">%s%s%s%s%s</span>", statusfont
, uri
,
2166 before
, back
? "+" : "", fwd
? "-" : "", after
2168 "<span font=\"%s\">%s</span>", statusfont
, uri
2171 gtk_label_set_markup(GTK_LABEL(status_url
), markup
);
2174 frame
= webkit_web_view_get_main_frame(webview
);
2175 src
= webkit_web_frame_get_data_source(frame
);
2176 request
= webkit_web_data_source_get_request(src
);
2177 msg
= webkit_network_request_get_message(request
);
2178 ssl_ok
= soup_message_get_flags(msg
) & SOUP_MESSAGE_CERTIFICATE_TRUSTED
;
2180 sslactivecolor
= sslbgcolor
;
2182 sslactivecolor
= sslinvalidbgcolor
;
2184 gdk_color_parse(ssl
? sslactivecolor
: statusbgcolor
, &color
);
2185 gtk_widget_modify_bg(eventbox
, GTK_STATE_NORMAL
, &color
);
2186 gdk_color_parse(ssl
? sslcolor
: statuscolor
, &color
);
2187 gtk_widget_modify_fg(GTK_WIDGET(status_url
), GTK_STATE_NORMAL
, &color
);
2188 gtk_widget_modify_fg(GTK_WIDGET(status_state
), GTK_STATE_NORMAL
, &color
);
2194 int download_count
= g_list_length(activeDownloads
);
2195 GString
*status
= g_string_new("");
2197 /* construct the status line */
2199 /* count, modkey and input buffer */
2200 g_string_append_printf(status
, "%.0d", count
);
2201 if (current_modkey
) g_string_append_c(status
, current_modkey
);
2203 /* the number of active downloads */
2204 if (activeDownloads
) {
2205 g_string_append_printf(status
, " %d active %s", download_count
,
2206 (download_count
== 1) ? "download" : "downloads");
2209 #ifdef ENABLE_WGET_PROGRESS_BAR
2210 /* the progressbar */
2213 char progressbar
[progressbartick
+ 1];
2215 if (activeDownloads
) {
2219 for (ptr
= activeDownloads
; ptr
; ptr
= g_list_next(ptr
)) {
2220 progress
+= 100 * webkit_download_get_progress(ptr
->data
);
2223 progress
/= download_count
;
2225 } else if (webkit_web_view_get_load_status(webview
) != WEBKIT_LOAD_FINISHED
2226 && webkit_web_view_get_load_status(webview
) != WEBKIT_LOAD_FAILED
) {
2228 progress
= webkit_web_view_get_progress(webview
) * 100;
2231 if (progress
>= 0) {
2232 ascii_bar(progressbartick
, progress
* progressbartick
/ 100, progressbar
);
2233 g_string_append_printf(status
, " %c%s%c",
2234 progressborderleft
, progressbar
, progressborderright
);
2239 /* and the current scroll position */
2241 int max
= gtk_adjustment_get_upper(adjust_v
) - gtk_adjustment_get_page_size(adjust_v
);
2242 int val
= (int)(gtk_adjustment_get_value(adjust_v
) / max
* 100);
2245 g_string_append(status
, " All");
2247 g_string_append(status
, " Top");
2248 else if (val
== 100)
2249 g_string_append(status
, " Bot");
2251 g_string_append_printf(status
, " %d%%", val
);
2255 markup
= g_markup_printf_escaped("<span font=\"%s\">%s</span>", statusfont
, status
->str
);
2256 gtk_label_set_markup(GTK_LABEL(status_state
), markup
);
2259 g_string_free(status
, TRUE
);
2265 modkeys
= calloc(LENGTH(keys
) + 1, sizeof(char));
2266 char *ptr
= modkeys
;
2268 for (i
= 0; i
< LENGTH(keys
); i
++)
2269 if (keys
[i
].modkey
&& !strchr(modkeys
, keys
[i
].modkey
))
2270 *(ptr
++) = keys
[i
].modkey
;
2271 modkeys
= realloc(modkeys
, &ptr
[0] - &modkeys
[0] + 1);
2276 scroll_h
= GTK_SCROLLBAR(gtk_hscrollbar_new(NULL
));
2277 scroll_v
= GTK_SCROLLBAR(gtk_vscrollbar_new(NULL
));
2278 adjust_h
= gtk_range_get_adjustment(GTK_RANGE(scroll_h
));
2279 adjust_v
= gtk_range_get_adjustment(GTK_RANGE(scroll_v
));
2281 window
= GTK_WINDOW(gtk_plug_new(embed
));
2283 window
= GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL
));
2284 gtk_window_set_wmclass(GTK_WINDOW(window
), "vimprobable2", "Vimprobable2");
2286 gtk_window_set_default_size(GTK_WINDOW(window
), 640, 480);
2287 box
= GTK_BOX(gtk_vbox_new(FALSE
, 0));
2288 inputbox
= gtk_entry_new();
2289 webview
= (WebKitWebView
*)webkit_web_view_new();
2290 statusbar
= GTK_BOX(gtk_hbox_new(FALSE
, 0));
2291 eventbox
= gtk_event_box_new();
2292 status_url
= gtk_label_new(NULL
);
2293 status_state
= gtk_label_new(NULL
);
2295 PangoFontDescription
*font
;
2296 GdkGeometry hints
= { 1, 1 };
2297 inspector
= webkit_web_view_get_inspector(WEBKIT_WEB_VIEW(webview
));
2299 clipboards
[0] = gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2300 clipboards
[1] = gtk_clipboard_get(GDK_NONE
);
2302 gdk_color_parse(statusbgcolor
, &bg
);
2303 gtk_widget_modify_bg(eventbox
, GTK_STATE_NORMAL
, &bg
);
2304 gtk_widget_set_name(GTK_WIDGET(window
), "Vimprobable2");
2305 gtk_window_set_geometry_hints(window
, NULL
, &hints
, GDK_HINT_MIN_SIZE
);
2307 keymap
= gdk_keymap_get_default();
2309 #ifdef DISABLE_SCROLLBAR
2310 viewport
= gtk_scrolled_window_new(NULL
, NULL
);
2311 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(viewport
), GTK_POLICY_NEVER
, GTK_POLICY_NEVER
);
2313 /* Ensure we still see scrollbars. */
2314 GtkWidget
*viewport
= gtk_scrolled_window_new(adjust_h
, adjust_v
);
2318 gtk_container_add(GTK_CONTAINER(viewport
), GTK_WIDGET(webview
));
2320 /* Ensure we set the scroll adjustments now, so that if we're not drawing
2321 * titlebars, we can still scroll.
2323 gtk_widget_set_scroll_adjustments(GTK_WIDGET(webview
), adjust_h
, adjust_v
);
2325 font
= pango_font_description_from_string(urlboxfont
[0]);
2326 gtk_widget_modify_font(GTK_WIDGET(inputbox
), font
);
2327 pango_font_description_free(font
);
2328 gtk_entry_set_inner_border(GTK_ENTRY(inputbox
), NULL
);
2329 gtk_misc_set_alignment(GTK_MISC(status_url
), 0.0, 0.0);
2330 gtk_misc_set_alignment(GTK_MISC(status_state
), 1.0, 0.0);
2331 gtk_box_pack_start(statusbar
, status_url
, TRUE
, TRUE
, 2);
2332 gtk_box_pack_start(statusbar
, status_state
, FALSE
, FALSE
, 2);
2333 gtk_container_add(GTK_CONTAINER(eventbox
), GTK_WIDGET(statusbar
));
2334 gtk_box_pack_start(box
, viewport
, TRUE
, TRUE
, 0);
2335 gtk_box_pack_start(box
, eventbox
, FALSE
, FALSE
, 0);
2336 gtk_entry_set_has_frame(GTK_ENTRY(inputbox
), FALSE
);
2337 gtk_box_pack_end(box
, inputbox
, FALSE
, FALSE
, 0);
2338 gtk_container_add(GTK_CONTAINER(window
), GTK_WIDGET(box
));
2339 gtk_widget_grab_focus(GTK_WIDGET(webview
));
2340 gtk_widget_show_all(GTK_WIDGET(window
));
2341 set_widget_font_and_color(inputbox
, urlboxfont
[0], urlboxbgcolor
[0], urlboxcolor
[0]);
2342 g_object_set(gtk_widget_get_settings(inputbox
), "gtk-entry-select-on-focus", FALSE
, NULL
);
2347 WebKitWebSettings
*settings
= (WebKitWebSettings
*)webkit_web_settings_new();
2348 char *filename
, *file_url
;
2350 session
= webkit_get_default_session();
2351 g_object_set(G_OBJECT(session
), "ssl-ca-file", ca_bundle
, NULL
);
2352 g_object_set(G_OBJECT(session
), "ssl-strict", strict_ssl
, NULL
);
2353 g_object_set(G_OBJECT(settings
), "default-font-size", DEFAULT_FONT_SIZE
, NULL
);
2354 g_object_set(G_OBJECT(settings
), "enable-scripts", enablePlugins
, NULL
);
2355 g_object_set(G_OBJECT(settings
), "enable-plugins", enablePlugins
, NULL
);
2356 g_object_set(G_OBJECT(settings
), "enable-java-applet", enableJava
, NULL
);
2357 g_object_set(G_OBJECT(settings
), "enable-page-cache", enablePagecache
, NULL
);
2358 filename
= g_strdup_printf(USER_STYLESHEET
);
2359 file_url
= g_strdup_printf("file://%s", filename
);
2360 g_object_set(G_OBJECT(settings
), "user-stylesheet-uri", file_url
, NULL
);
2363 g_object_set(G_OBJECT(settings
), "user-agent", useragent
, NULL
);
2364 g_object_get(G_OBJECT(settings
), "zoom-step", &zoomstep
, NULL
);
2365 webkit_web_view_set_settings(webview
, settings
);
2368 toggle_proxy(use_proxy
);
2373 WebKitWebFrame
*frame
= webkit_web_view_get_main_frame(webview
);
2374 #ifdef ENABLE_COOKIE_SUPPORT
2376 g_signal_connect_after(G_OBJECT(session
), "request-started", G_CALLBACK(new_generic_request
), NULL
);
2378 /* Accept-language header */
2379 g_object_set(G_OBJECT(session
), "accept-language", acceptlanguage
, NULL
);
2381 g_object_connect(G_OBJECT(window
),
2382 "signal::destroy", G_CALLBACK(window_destroyed_cb
), NULL
,
2385 g_signal_connect(G_OBJECT(frame
),
2386 "scrollbars-policy-changed", G_CALLBACK(blank_cb
), NULL
);
2388 g_object_connect(G_OBJECT(webview
),
2389 "signal::title-changed", G_CALLBACK(webview_title_changed_cb
), NULL
,
2390 "signal::load-progress-changed", G_CALLBACK(webview_progress_changed_cb
), NULL
,
2391 "signal::load-committed", G_CALLBACK(webview_load_committed_cb
), NULL
,
2392 "signal::load-finished", G_CALLBACK(webview_load_finished_cb
), NULL
,
2393 "signal::navigation-policy-decision-requested", G_CALLBACK(webview_navigation_cb
), NULL
,
2394 "signal::new-window-policy-decision-requested", G_CALLBACK(webview_new_window_cb
), NULL
,
2395 "signal::mime-type-policy-decision-requested", G_CALLBACK(webview_mimetype_cb
), NULL
,
2396 "signal::download-requested", G_CALLBACK(webview_download_cb
), NULL
,
2397 "signal::key-press-event", G_CALLBACK(webview_keypress_cb
), NULL
,
2398 "signal::hovering-over-link", G_CALLBACK(webview_hoverlink_cb
), NULL
,
2399 "signal::console-message", G_CALLBACK(webview_console_cb
), NULL
,
2400 "signal::create-web-view", G_CALLBACK(webview_open_in_new_window_cb
), NULL
,
2401 "signal::event", G_CALLBACK(notify_event_cb
), NULL
,
2403 /* webview adjustment */
2404 g_object_connect(G_OBJECT(adjust_v
),
2405 "signal::value-changed", G_CALLBACK(webview_scroll_cb
), NULL
,
2408 g_object_connect(G_OBJECT(inputbox
),
2409 "signal::activate", G_CALLBACK(inputbox_activate_cb
), NULL
,
2410 "signal::key-press-event", G_CALLBACK(inputbox_keypress_cb
), NULL
,
2411 "signal::key-release-event", G_CALLBACK(inputbox_keyrelease_cb
), NULL
,
2412 #ifdef ENABLE_INCREMENTAL_SEARCH
2413 "signal::changed", G_CALLBACK(inputbox_changed_cb
), NULL
,
2417 g_signal_connect(G_OBJECT(inspector
),
2418 "inspect-web-view", G_CALLBACK(inspector_inspect_web_view_cb
), NULL
);
2421 #ifdef ENABLE_COOKIE_SUPPORT
2425 if (file_cookie_jar
)
2426 g_object_unref(file_cookie_jar
);
2428 if (session_cookie_jar
)
2429 g_object_unref(session_cookie_jar
);
2431 session_cookie_jar
= soup_cookie_jar_new();
2433 cookie_store
= g_strdup_printf(COOKIES_STORAGE_FILENAME
);
2437 g_signal_connect(G_OBJECT(file_cookie_jar
), "changed",
2438 G_CALLBACK(update_cookie_jar
), NULL
);
2443 /* TA: XXX - we should be using this callback for any header-requests we
2444 * receive (hence the name "new_generic_request" -- but for now, its use
2445 * is limited to handling cookies.
2448 new_generic_request(SoupSession
*session
, SoupMessage
*soup_msg
, gpointer unused
)
2450 SoupMessageHeaders
*soup_msg_h
;
2454 soup_msg_h
= soup_msg
->request_headers
;
2455 soup_message_headers_remove(soup_msg_h
, "Cookie");
2456 uri
= soup_message_get_uri(soup_msg
);
2457 if ((cookie_str
= get_cookies(uri
))) {
2458 soup_message_headers_append(soup_msg_h
, "Cookie", cookie_str
);
2462 g_signal_connect_after(G_OBJECT(soup_msg
), "got-headers", G_CALLBACK(handle_cookie_request
), NULL
);
2468 get_cookies(SoupURI
*soup_uri
) {
2471 cookie_str
= soup_cookie_jar_get_cookies(file_cookie_jar
, soup_uri
, TRUE
);
2477 handle_cookie_request(SoupMessage
*soup_msg
, gpointer unused
)
2479 GSList
*resp_cookie
= NULL
, *cookie_list
;
2482 cookie_list
= soup_cookies_from_response(soup_msg
);
2483 for(resp_cookie
= cookie_list
; resp_cookie
; resp_cookie
= g_slist_next(resp_cookie
))
2485 SoupDate
*soup_date
;
2486 cookie
= soup_cookie_copy((SoupCookie
*)resp_cookie
->data
);
2488 if (cookie_timeout
&& cookie
->expires
== NULL
) {
2489 soup_date
= soup_date_new_from_time_t(time(NULL
) + cookie_timeout
* 10);
2490 soup_cookie_set_expires(cookie
, soup_date
);
2491 soup_date_free(soup_date
);
2493 soup_cookie_jar_add_cookie(file_cookie_jar
, cookie
);
2496 soup_cookies_free(cookie_list
);
2502 update_cookie_jar(SoupCookieJar
*jar
, SoupCookie
*old
, SoupCookie
*new)
2505 /* Nothing to do. */
2510 copy
= soup_cookie_copy(new);
2512 soup_cookie_jar_add_cookie(session_cookie_jar
, copy
);
2518 load_all_cookies(void)
2520 GSList
*cookie_list
;
2521 file_cookie_jar
= soup_cookie_jar_text_new(cookie_store
, COOKIES_STORAGE_READONLY
);
2523 /* Put them back in the session store. */
2524 GSList
*cookies_from_file
= soup_cookie_jar_all_cookies(file_cookie_jar
);
2525 cookie_list
= cookies_from_file
;
2527 for (; cookies_from_file
;
2528 cookies_from_file
= cookies_from_file
->next
)
2530 soup_cookie_jar_add_cookie(session_cookie_jar
, cookies_from_file
->data
);
2533 soup_cookies_free(cookies_from_file
);
2534 g_slist_free(cookie_list
);
2543 /* Free up any nasty globals before exiting. */
2544 #ifdef ENABLE_COOKIE_SUPPORT
2546 g_free(cookie_store
);
2552 main(int argc
, char *argv
[]) {
2554 static char url
[256] = "";
2555 static gboolean ver
= false;
2556 static gboolean configfile_exists
= FALSE
;
2557 static const char *cfile
= NULL
;
2558 static GOptionEntry opts
[] = {
2559 { "version", 'v', 0, G_OPTION_ARG_NONE
, &ver
, "print version", NULL
},
2560 { "embed", 'e', 0, G_OPTION_ARG_STRING
, &winid
, "embedded", NULL
},
2561 { "configfile", 'c', 0, G_OPTION_ARG_STRING
, &cfile
, "config file", NULL
},
2567 /* command line argument: version */
2568 if (!gtk_init_with_args(&argc
, &argv
, "[<uri>]", opts
, NULL
, &err
)) {
2569 g_printerr("can't init gtk: %s\n", err
->message
);
2571 return EXIT_FAILURE
;
2575 printf("%s\n", INTERNAL_VERSION
);
2576 return EXIT_SUCCESS
;
2579 if( getenv("XDG_CONFIG_HOME") )
2580 config_base
= g_strdup_printf("%s", getenv("XDG_CONFIG_HOME"));
2582 config_base
= g_strdup_printf("%s/.config/", getenv("HOME"));
2585 configfile
= g_strdup(cfile
);
2587 configfile
= g_strdup_printf(RCFILE
);
2589 if (!g_thread_supported())
2590 g_thread_init(NULL
);
2593 embed
= atoi(winid
);
2598 #ifdef ENABLE_COOKIE_SUPPORT
2602 make_searchengines_list(searchengines
, LENGTH(searchengines
));
2603 make_uri_handlers_list(uri_handlers
, LENGTH(uri_handlers
));
2605 /* Check if the specified file exists. */
2606 /* And only warn the user, if they explicitly asked for a config on the
2609 if (!(access(configfile
, F_OK
) == 0) && cfile
) {
2612 feedback_str
= g_strdup_printf("Config file '%s' doesn't exist", cfile
);
2613 give_feedback(feedback_str
);
2614 g_free(feedback_str
);
2615 } else if ((access(configfile
, F_OK
) == 0))
2616 configfile_exists
= true;
2618 /* read config file */
2619 /* But only report errors if we failed, and the file existed. */
2620 if ((SUCCESS
!= read_rcfile(configfile
)) && configfile_exists
) {
2622 a
.s
= g_strdup_printf("Error in config file '%s'", configfile
);
2628 /* command line argument: URL */
2630 strncpy(url
, argv
[argc
- 1], 255);
2632 strncpy(url
, startpage
, 255);
2635 a
.i
= TargetCurrent
;
2642 return EXIT_SUCCESS
;