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
14 #include <sys/types.h>
19 #include "vimprobable.h"
20 #include "utilities.h"
21 #include "callbacks.h"
22 #include "javascript.h"
24 /* the CLEAN_MOD_*_MASK defines have all the bits set that will be stripped from the modifier bit field */
25 #define CLEAN_MOD_NUMLOCK_MASK (GDK_MOD2_MASK)
26 #define CLEAN_MOD_BUTTON_MASK (GDK_BUTTON1_MASK|GDK_BUTTON2_MASK|GDK_BUTTON3_MASK|GDK_BUTTON4_MASK|GDK_BUTTON5_MASK)
28 /* remove unused bits, numlock symbol and buttons from keymask */
29 #define CLEAN(mask) (mask & (GDK_MODIFIER_MASK) & ~(CLEAN_MOD_NUMLOCK_MASK) & ~(CLEAN_MOD_BUTTON_MASK))
31 #define IS_ESCAPE(event) (IS_ESCAPE_KEY(CLEAN(event->state), event->keyval))
32 #define IS_ESCAPE_KEY(s, k) ((s == 0 && k == GDK_Escape) || \
33 (s == GDK_CONTROL_MASK && k == GDK_bracketleft))
36 static void inputbox_activate_cb(GtkEntry
*entry
, gpointer user_data
);
37 static gboolean
inputbox_keypress_cb(GtkEntry
*entry
, GdkEventKey
*event
);
38 static gboolean
inputbox_keyrelease_cb(GtkEntry
*entry
, GdkEventKey
*event
);
39 static gboolean
inputbox_changed_cb(GtkEditable
*entry
, gpointer user_data
);
40 static WebKitWebView
* inspector_inspect_web_view_cb(gpointer inspector
, WebKitWebView
* web_view
);
41 static gboolean
notify_event_cb(GtkWidget
*widget
, GdkEvent
*event
, gpointer user_data
);
42 static gboolean
webview_console_cb(WebKitWebView
*webview
, char *message
, int line
, char *source
, gpointer user_data
);
43 static gboolean
webview_download_cb(WebKitWebView
*webview
, WebKitDownload
*download
, gpointer user_data
);
44 static void webview_hoverlink_cb(WebKitWebView
*webview
, char *title
, char *link
, gpointer data
);
45 static gboolean
webview_keypress_cb(WebKitWebView
*webview
, GdkEventKey
*event
);
46 static void webview_load_committed_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, gpointer user_data
);
47 static void webview_load_finished_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, gpointer user_data
);
48 static gboolean
webview_mimetype_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, WebKitNetworkRequest
*request
,
49 char *mime_type
, WebKitWebPolicyDecision
*decision
, gpointer user_data
);
50 static void webview_open_js_window_cb(WebKitWebView
* temp_view
, GParamSpec param_spec
);
51 static gboolean
webview_new_window_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, WebKitNetworkRequest
*request
,
52 WebKitWebNavigationAction
*action
, WebKitWebPolicyDecision
*decision
, gpointer user_data
);
53 static WebKitWebView
* webview_open_in_new_window_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, gpointer user_data
);
54 static void webview_progress_changed_cb(WebKitWebView
*webview
, int progress
, gpointer user_data
);
55 static void webview_title_changed_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, char *title
, gpointer user_data
);
56 static void window_destroyed_cb(GtkWidget
*window
, gpointer func_data
);
57 static gboolean
blank_cb(void);
60 static gboolean
bookmark(const Arg
*arg
);
61 static gboolean
browser_settings(const Arg
*arg
);
62 static gboolean
commandhistoryfetch(const Arg
*arg
);
63 static gboolean
complete(const Arg
*arg
);
64 static gboolean
descend(const Arg
*arg
);
65 gboolean
echo(const Arg
*arg
);
66 static gboolean
focus_input(const Arg
*arg
);
67 static gboolean
open_editor(const Arg
*arg
);
68 void _resume_from_editor(GPid child_pid
, int status
, gpointer data
);
69 static gboolean
input(const Arg
*arg
);
70 static gboolean
open_inspector(const Arg
* arg
);
71 static gboolean
navigate(const Arg
*arg
);
72 static gboolean
number(const Arg
*arg
);
73 static gboolean
open_arg(const Arg
*arg
);
74 static gboolean
open_remembered(const Arg
*arg
);
75 static gboolean
paste(const Arg
*arg
);
76 static gboolean
quickmark(const Arg
*arg
);
77 static gboolean
quit(const Arg
*arg
);
78 static gboolean
revive(const Arg
*arg
);
79 static gboolean
print_frame(const Arg
*arg
);
80 static gboolean
search(const Arg
*arg
);
81 static gboolean
set(const Arg
*arg
);
82 static gboolean
script(const Arg
*arg
);
83 static gboolean
scroll(const Arg
*arg
);
84 static gboolean
search_tag(const Arg
*arg
);
85 static gboolean
yank(const Arg
*arg
);
86 static gboolean
view_source(const Arg
* arg
);
87 static gboolean
zoom(const Arg
*arg
);
88 static gboolean
fake_key_event(const Arg
*arg
);
90 static void update_url(const char *uri
);
91 static void setup_modkeys(void);
92 static void setup_gui(void);
93 static void setup_settings(void);
94 static void setup_signals(void);
95 static void ascii_bar(int total
, int state
, char *string
);
96 static gchar
*jsapi_ref_to_string(JSContextRef context
, JSValueRef ref
);
97 static void jsapi_evaluate_script(const gchar
*script
, gchar
**value
, gchar
**message
);
98 static void download_progress(WebKitDownload
*d
, GParamSpec
*pspec
);
99 static void set_widget_font_and_color(GtkWidget
*widget
, const char *font_str
,
100 const char *bg_color_str
, const char *fg_color_str
);
102 static gboolean
history(void);
103 static gboolean
process_set_line(char *line
);
104 void save_command_history(char *line
);
105 void toggle_proxy(gboolean onoff
);
106 void toggle_scrollbars(gboolean onoff
);
107 void set_default_winsize(const char * const size
);
109 gboolean
process_keypress(GdkEventKey
*event
);
110 void fill_suggline(char * suggline
, const char * command
, const char *fill_with
);
111 GtkWidget
* fill_eventbox(const char * completion_line
);
112 static void mop_up(void);
117 static GtkWindow
*window
;
118 static GtkWidget
*viewport
;
120 static GtkScrollbar
*scroll_h
;
121 static GtkScrollbar
*scroll_v
;
122 static GtkAdjustment
*adjust_h
;
123 static GtkAdjustment
*adjust_v
;
124 static GtkWidget
*inputbox
;
125 static GtkWidget
*eventbox
;
126 static GtkBox
*statusbar
;
127 static GtkWidget
*status_url
;
128 static GtkWidget
*status_state
;
129 static WebKitWebView
*webview
;
130 static SoupSession
*session
;
131 static GtkClipboard
*clipboards
[2];
132 static GdkKeymap
*keymap
;
135 static unsigned int mode
= ModeNormal
;
136 static unsigned int count
= 0;
137 static float zoomstep
;
139 static char current_modkey
;
140 static char *search_handle
;
141 static gboolean search_direction
;
142 static gboolean echo_active
= TRUE
;
143 static WebKitWebInspector
*inspector
;
145 static GdkNativeWindow embed
= 0;
146 static char *configfile
= NULL
;
147 static char *winid
= NULL
;
149 static char rememberedURI
[1024] = "";
150 static char followTarget
[8] = "";
151 char *error_msg
= NULL
;
152 char *config_base
= NULL
;
153 static gboolean manual_focus
= FALSE
;
155 GList
*activeDownloads
;
160 GList
*commandhistory
= NULL
;
161 int commandpointer
= 0;
163 KeyList
*keylistroot
= NULL
;
165 /* Cookie support. */
166 #ifdef ENABLE_COOKIE_SUPPORT
167 static SoupCookieJar
*session_cookie_jar
= NULL
;
168 static SoupCookieJar
*file_cookie_jar
= NULL
;
169 static time_t cookie_timeout
= 4800;
170 static char *cookie_store
;
171 static void setup_cookies(void);
172 static char *get_cookies(SoupURI
*soup_uri
);
173 static void load_all_cookies(void);
174 static void new_generic_request(SoupSession
*soup_ses
, SoupMessage
*soup_msg
, gpointer unused
);
175 static void update_cookie_jar(SoupCookieJar
*jar
, SoupCookie
*old
, SoupCookie
*new);
176 static void handle_cookie_request(SoupMessage
*soup_msg
, gpointer unused
);
180 window_destroyed_cb(GtkWidget
*window
, gpointer func_data
) {
185 webview_title_changed_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, char *title
, gpointer user_data
) {
186 gtk_window_set_title(window
, title
);
190 webview_progress_changed_cb(WebKitWebView
*webview
, int progress
, gpointer user_data
) {
191 #ifdef ENABLE_GTK_PROGRESS_BAR
192 gtk_entry_set_progress_fraction(GTK_ENTRY(inputbox
), progress
== 100 ? 0 : (double)progress
/100);
197 #ifdef ENABLE_WGET_PROGRESS_BAR
199 ascii_bar(int total
, int state
, char *string
) {
202 for (i
= 0; i
< state
; i
++)
203 string
[i
] = progressbartickchar
;
204 string
[i
++] = progressbarcurrent
;
205 for (; i
< total
; i
++)
206 string
[i
] = progressbarspacer
;
212 webview_load_committed_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, gpointer user_data
) {
213 Arg a
= { .i
= Silent
, .s
= g_strdup(JS_SETUP_HINTS
) };
214 const char *uri
= webkit_web_view_get_uri(webview
);
220 if (mode
== ModeInsert
|| mode
== ModeHints
) {
221 Arg a
= { .i
= ModeNormal
};
224 manual_focus
= FALSE
;
228 webview_load_finished_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, gpointer user_data
) {
229 WebKitWebSettings
*settings
= webkit_web_view_get_settings(webview
);
232 g_object_get(settings
, "enable-scripts", &scripts
, NULL
);
233 if (escape_input_on_load
&& scripts
&& !manual_focus
&& !gtk_widget_is_focus(inputbox
)) {
234 Arg a
= { .i
= Silent
, .s
= g_strdup("hints.clearFocus();") };
241 if (HISTORY_MAX_ENTRIES
> 0)
247 webview_open_js_window_cb(WebKitWebView
* temp_view
, GParamSpec param_spec
) {
248 /* retrieve the URI of the temporary webview */
249 Arg a
= { .i
= TargetNew
, .s
= (char*)webkit_web_view_get_uri(temp_view
) };
251 webkit_web_view_stop_loading(temp_view
);
252 gtk_widget_destroy(GTK_WIDGET(temp_view
));
253 /* open the requested window */
257 static WebKitWebView
*
258 webview_open_in_new_window_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, gpointer user_data
) {
259 if (rememberedURI
!= NULL
&& strlen(rememberedURI
) > 0) {
260 if (strncmp(rememberedURI
, "javascript:", 11) != 0) {
261 Arg a
= { .i
= TargetNew
, .s
= rememberedURI
};
266 /* create a temporary webview to execute the script in */
267 WebKitWebView
*temp_view
= WEBKIT_WEB_VIEW(webkit_web_view_new());
268 /* wait until the new webview receives its new URI */
269 g_object_connect(temp_view
, "signal::notify::uri", G_CALLBACK(webview_open_js_window_cb
), NULL
, NULL
);
274 webview_new_window_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, WebKitNetworkRequest
*request
,
275 WebKitWebNavigationAction
*action
, WebKitWebPolicyDecision
*decision
, gpointer user_data
) {
276 Arg a
= { .i
= TargetNew
, .s
= (char*)webkit_network_request_get_uri(request
) };
278 webkit_web_policy_decision_ignore(decision
);
283 webview_mimetype_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, WebKitNetworkRequest
*request
,
284 char *mime_type
, WebKitWebPolicyDecision
*decision
, gpointer user_data
) {
285 if (webkit_web_view_can_show_mime_type(webview
, mime_type
) == FALSE
) {
286 webkit_web_policy_decision_download(decision
);
293 static WebKitWebView
*
294 inspector_inspect_web_view_cb(gpointer inspector
, WebKitWebView
* web_view
) {
295 gchar
* inspector_title
;
296 GtkWidget
* inspector_window
;
297 GtkWidget
* inspector_view
;
299 /* just enough code to show the inspector - no signal handling etc. */
300 inspector_title
= g_strdup_printf("Inspect page - %s - Vimprobable2", webkit_web_view_get_uri(web_view
));
302 inspector_window
= gtk_plug_new(embed
);
304 inspector_window
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
305 gtk_window_set_wmclass(window
, "vimprobable2", "Vimprobable2");
307 gtk_window_set_title(GTK_WINDOW(inspector_window
), inspector_title
);
308 g_free(inspector_title
);
309 inspector_view
= webkit_web_view_new();
310 gtk_container_add(GTK_CONTAINER(inspector_window
), inspector_view
);
311 gtk_widget_show_all(inspector_window
);
312 return WEBKIT_WEB_VIEW(inspector_view
);
316 webview_download_cb(WebKitWebView
*webview
, WebKitDownload
*download
, gpointer user_data
) {
317 const gchar
*filename
;
320 WebKitDownloadStatus status
;
322 filename
= webkit_download_get_suggested_filename(download
);
323 if (filename
== NULL
|| strlen(filename
) == 0) {
324 filename
= "vimprobable_download";
326 path
= g_build_filename(g_strdup_printf(DOWNLOADS_PATH
), filename
, NULL
);
327 uri
= g_strconcat("file://", path
, NULL
);
328 webkit_download_set_destination_uri(download
, uri
);
330 size
= (uint32_t)webkit_download_get_total_size(download
);
332 echo_message(Info
, "Download %s started (expected size: %u bytes)...", filename
, size
);
334 echo_message(Info
, "Download %s started (unknown size)...", filename
);
335 activeDownloads
= g_list_prepend(activeDownloads
, download
);
336 g_signal_connect(download
, "notify::progress", G_CALLBACK(download_progress
), NULL
);
337 g_signal_connect(download
, "notify::status", G_CALLBACK(download_progress
), NULL
);
338 status
= webkit_download_get_status(download
);
339 if (status
== WEBKIT_DOWNLOAD_STATUS_CREATED
)
340 webkit_download_start(download
);
351 download_progress(WebKitDownload
*d
, GParamSpec
*pspec
) {
352 WebKitDownloadStatus status
= webkit_download_get_status(d
);
354 if (status
!= WEBKIT_DOWNLOAD_STATUS_STARTED
&& status
!= WEBKIT_DOWNLOAD_STATUS_CREATED
) {
355 if (status
!= WEBKIT_DOWNLOAD_STATUS_FINISHED
) {
356 echo_message(Error
, "Error while downloading %s", webkit_download_get_suggested_filename(d
));
358 echo_message(Info
, "Download %s finished", webkit_download_get_suggested_filename(d
));
360 activeDownloads
= g_list_remove(activeDownloads
, d
);
367 process_keypress(GdkEventKey
*event
) {
370 GdkModifierType irrelevant
;
372 /* Get a mask of modifiers that shouldn't be considered for this event.
373 * E.g.: It shouldn't matter whether ';' is shifted or not. */
374 gdk_keymap_translate_keyboard_state(keymap
, event
->hardware_keycode
,
375 event
->state
, event
->group
, &keyval
, NULL
, NULL
, &irrelevant
);
377 current
= keylistroot
;
379 while (current
!= NULL
) {
380 if (current
->Element
.mask
== (CLEAN(event
->state
) & ~irrelevant
)
381 && (current
->Element
.modkey
== current_modkey
382 || (!current
->Element
.modkey
&& !current_modkey
)
383 || current
->Element
.modkey
== GDK_VoidSymbol
) /* wildcard */
384 && current
->Element
.key
== keyval
385 && current
->Element
.func
)
386 if (current
->Element
.func(¤t
->Element
.arg
)) {
387 current_modkey
= count
= 0;
391 current
= current
->next
;
397 webview_keypress_cb(WebKitWebView
*webview
, GdkEventKey
*event
) {
398 Arg a
= { .i
= ModeNormal
, .s
= NULL
};
400 GdkModifierType irrelevant
;
402 /* Get a mask of modifiers that shouldn't be considered for this event.
403 * E.g.: It shouldn't matter whether ';' is shifted or not. */
404 gdk_keymap_translate_keyboard_state(keymap
, event
->hardware_keycode
,
405 event
->state
, event
->group
, &keyval
, NULL
, NULL
, &irrelevant
);
409 if ((CLEAN(event
->state
) & ~irrelevant
) == 0) {
410 if (IS_ESCAPE(event
)) {
411 echo_message(Info
, "");
413 } else if (current_modkey
== 0 && ((event
->keyval
>= GDK_1
&& event
->keyval
<= GDK_9
)
414 || (event
->keyval
== GDK_0
&& count
))) {
415 count
= (count
? count
* 10 : 0) + (event
->keyval
- GDK_0
);
418 } else if (strchr(modkeys
, event
->keyval
) && current_modkey
!= event
->keyval
) {
419 current_modkey
= event
->keyval
;
425 if (process_keypress(event
) == TRUE
) return TRUE
;
429 if (IS_ESCAPE(event
)) {
431 a
.s
= g_strdup("hints.clearFocus();");
436 } else if (CLEAN(event
->state
) & GDK_CONTROL_MASK
) {
437 /* keybindings of non-printable characters */
438 if (process_keypress(event
) == TRUE
) return TRUE
;
440 case ModePassThrough
:
441 if (IS_ESCAPE(event
)) {
442 echo_message(Info
, "");
448 echo_message(Info
, "");
456 set_widget_font_and_color(GtkWidget
*widget
, const char *font_str
, const char *bg_color_str
,
457 const char *fg_color_str
) {
460 PangoFontDescription
*font
;
462 font
= pango_font_description_from_string(font_str
);
463 gtk_widget_modify_font(widget
, font
);
464 pango_font_description_free(font
);
467 gdk_color_parse(fg_color_str
, &fg_color
);
469 gdk_color_parse(bg_color_str
, &bg_color
);
471 gtk_widget_modify_text(widget
, GTK_STATE_NORMAL
, fg_color_str
? &fg_color
: NULL
);
472 gtk_widget_modify_base(widget
, GTK_STATE_NORMAL
, bg_color_str
? &bg_color
: NULL
);
478 webview_hoverlink_cb(WebKitWebView
*webview
, char *title
, char *link
, gpointer data
) {
479 const char *uri
= webkit_web_view_get_uri(webview
);
482 memset(rememberedURI
, 0, 1024);
484 markup
= g_markup_printf_escaped("<span font=\"%s\">Link: %s</span>", statusfont
, link
);
485 gtk_label_set_markup(GTK_LABEL(status_url
), markup
);
486 strncpy(rememberedURI
, link
, 1024);
493 webview_console_cb(WebKitWebView
*webview
, char *message
, int line
, char *source
, gpointer user_data
) {
496 /* Don't change internal mode if the browser doesn't have focus to prevent inconsistent states */
497 if (gtk_window_has_toplevel_focus(window
)) {
498 if (!strcmp(message
, "hintmode_off") || !strcmp(message
, "insertmode_off")) {
501 } else if (!strcmp(message
, "insertmode_on")) {
510 inputbox_activate_cb(GtkEntry
*entry
, gpointer user_data
) {
512 guint16 length
= gtk_entry_get_text_length(entry
);
514 gboolean success
= FALSE
, forward
= FALSE
;
516 a
.i
= HideCompletion
;
520 text
= (char*)gtk_entry_get_text(entry
);
521 if (length
> 1 && text
[0] == ':') {
522 success
= process_line((text
+ 1));
523 } else if (length
> 1 && ((forward
= text
[0] == '/') || text
[0] == '?')) {
524 webkit_web_view_unmark_text_matches(webview
);
525 #ifdef ENABLE_MATCH_HIGHLITING
526 webkit_web_view_mark_text_matches(webview
, &text
[1], FALSE
, 0);
527 webkit_web_view_set_highlight_text_matches(webview
, TRUE
);
530 #ifndef ENABLE_INCREMENTAL_SEARCH
532 a
.i
= searchoptions
| (forward
? DirectionForward
: DirectionBackwards
);
535 search_direction
= forward
;
536 search_handle
= g_strdup(&text
[1]);
538 } else if (text
[0] == '.' || text
[0] == ',' || text
[0] == ';') {
540 a
.s
= g_strdup_printf("hints.fire();");
547 gtk_entry_set_text(entry
, "");
548 gtk_widget_grab_focus(GTK_WIDGET(webview
));
552 inputbox_keypress_cb(GtkEntry
*entry
, GdkEventKey
*event
) {
556 if (mode
== ModeHints
) {
557 if (event
->keyval
== GDK_Tab
) {
559 a
.s
= g_strdup_printf("hints.focusNextHint();");
565 if (event
->keyval
== GDK_ISO_Left_Tab
) {
567 a
.s
= g_strdup_printf("hints.focusPreviousHint();");
573 if (event
->keyval
== GDK_Return
) {
575 a
.s
= g_strdup_printf("hints.fire();");
582 switch (event
->keyval
) {
583 case GDK_bracketleft
:
585 if (!IS_ESCAPE(event
)) break;
586 a
.i
= HideCompletion
;
598 return commandhistoryfetch(&a
);
602 return commandhistoryfetch(&a
);
604 case GDK_ISO_Left_Tab
:
610 if (mode
== ModeHints
) {
611 if ((CLEAN(event
->state
) & GDK_SHIFT_MASK
) &&
612 (CLEAN(event
->state
) & GDK_CONTROL_MASK
) &&
613 (event
->keyval
== GDK_BackSpace
)) {
616 a
.s
= g_strdup_printf("hints.updateHints(%d);", count
);
623 numval
= g_unichar_digit_value((gunichar
) gdk_keyval_to_unicode(event
->keyval
));
624 if ((numval
>= 1 && numval
<= 9) || (numval
== 0 && count
)) {
625 /* allow a zero as non-first number */
626 count
= (count
? count
* 10 : 0) + numval
;
628 a
.s
= g_strdup_printf("hints.updateHints(%d);", count
);
640 notify_event_cb(GtkWidget
*widget
, GdkEvent
*event
, gpointer user_data
) {
642 WebKitHitTestResult
*result
;
643 WebKitHitTestResultContext context
;
644 if (mode
== ModeNormal
&& event
->type
== GDK_BUTTON_RELEASE
) {
645 /* handle mouse click events */
646 for (i
= 0; i
< LENGTH(mouse
); i
++) {
647 if (mouse
[i
].mask
== CLEAN(event
->button
.state
)
648 && (mouse
[i
].modkey
== current_modkey
649 || (!mouse
[i
].modkey
&& !current_modkey
)
650 || mouse
[i
].modkey
== GDK_VoidSymbol
) /* wildcard */
651 && mouse
[i
].button
== event
->button
.button
653 if (mouse
[i
].func(&mouse
[i
].arg
)) {
654 current_modkey
= count
= 0;
660 result
= webkit_web_view_get_hit_test_result(WEBKIT_WEB_VIEW(widget
), (GdkEventButton
*)event
);
661 g_object_get(result
, "context", &context
, NULL
);
662 if (context
& WEBKIT_HIT_TEST_RESULT_CONTEXT_EDITABLE
) {
663 Arg a
= { .i
= ModeInsert
};
667 } else if (mode
== ModeInsert
&& event
->type
== GDK_BUTTON_RELEASE
) {
668 result
= webkit_web_view_get_hit_test_result(WEBKIT_WEB_VIEW(widget
), (GdkEventButton
*)event
);
669 g_object_get(result
, "context", &context
, NULL
);
670 if (!(context
& WEBKIT_HIT_TEST_RESULT_CONTEXT_EDITABLE
)) {
671 Arg a
= { .i
= ModeNormal
};
675 gchar
*value
= NULL
, *message
= NULL
;
676 jsapi_evaluate_script("window.getSelection().focusNode", &value
, &message
);
677 if (value
&& !strcmp(value
, "[object HTMLFormElement]")) {
678 Arg a
= { .i
= ModeInsert
, .s
= NULL
};
688 static gboolean
inputbox_keyrelease_cb(GtkEntry
*entry
, GdkEventKey
*event
) {
690 guint16 length
= gtk_entry_get_text_length(entry
);
693 a
.i
= HideCompletion
;
701 static gboolean
inputbox_changed_cb(GtkEditable
*entry
, gpointer user_data
) {
703 char *text
= (char*)gtk_entry_get_text(GTK_ENTRY(entry
));
704 guint16 length
= gtk_entry_get_text_length(GTK_ENTRY(entry
));
705 gboolean forward
= FALSE
;
707 /* Update incremental search if the user changes the search text.
709 * Note: gtk_widget_is_focus() is a poor way to check if the change comes
710 * from the user. But if the entry is focused and the text is set
711 * through gtk_entry_set_text() in some asyncrounous operation,
712 * I would consider that a bug.
715 if (gtk_widget_is_focus(GTK_WIDGET(entry
)) && length
> 1 && ((forward
= text
[0] == '/') || text
[0] == '?')) {
716 webkit_web_view_unmark_text_matches(webview
);
717 webkit_web_view_search_text(webview
, &text
[1], searchoptions
& CaseSensitive
, forward
, searchoptions
& Wrapping
);
719 } else if (gtk_widget_is_focus(GTK_WIDGET(entry
)) && length
>= 1 &&
720 (text
[0] == '.' || text
[0] == ',' || text
[0] == ';')) {
724 a
.s
= g_strconcat("hints.createHints('", text
+ 1, "', 'f');", NULL
);
728 a
.s
= g_strconcat("hints.createHints('", text
+ 1, "', 'F');", NULL
);
735 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 's');", NULL
);
738 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 'y');", NULL
);
741 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 'f');", NULL
);
744 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 'F');", NULL
);
746 case 'O': case 'T': case 'W':
747 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 'O');", NULL
);
750 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 'i');", NULL
);
753 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 'I');", NULL
);
765 } else if (length
== 0 && followTarget
[0]) {
768 a
.s
= g_strdup("hints.clearHints();");
780 void fill_suggline(char * suggline
, const char * command
, const char *fill_with
) {
781 memset(suggline
, 0, 512);
782 strncpy(suggline
, command
, 512);
783 strncat(suggline
, " ", 1);
784 strncat(suggline
, fill_with
, 512 - strlen(suggline
) - 1);
787 GtkWidget
* fill_eventbox(const char * completion_line
) {
789 GtkWidget
*row_eventbox
, *el
;
791 char *markup
, *markup_tmp
;
793 row
= GTK_BOX(gtk_hbox_new(FALSE
, 0));
794 row_eventbox
= gtk_event_box_new();
795 gdk_color_parse(completionbgcolor
[0], &color
);
796 gtk_widget_modify_bg(row_eventbox
, GTK_STATE_NORMAL
, &color
);
797 el
= gtk_label_new(NULL
);
798 markup_tmp
= g_markup_escape_text(completion_line
, strlen(completion_line
));
799 markup
= g_strconcat("<span font=\"", completionfont
[0], "\" color=\"", completioncolor
[0], "\">",
800 markup_tmp
, "</span>", NULL
);
801 gtk_label_set_markup(GTK_LABEL(el
), markup
);
804 gtk_misc_set_alignment(GTK_MISC(el
), 0, 0);
805 gtk_box_pack_start(row
, el
, TRUE
, TRUE
, 2);
806 gtk_container_add(GTK_CONTAINER(row_eventbox
), GTK_WIDGET(row
));
811 complete(const Arg
*arg
) {
812 char *str
, *p
, *s
, *markup
, *entry
, *searchfor
, command
[32] = "", suggline
[512] = "", **suggurls
;
813 size_t listlen
, len
, cmdlen
;
815 Listelement
*elementlist
= NULL
, *elementpointer
;
816 gboolean highlight
= FALSE
;
818 GtkWidget
*row_eventbox
, *el
;
821 static GtkWidget
*table
, *top_border
;
823 static char **suggestions
;
824 static GtkWidget
**widgets
;
825 static int n
= 0, m
, current
= -1;
827 str
= (char*)gtk_entry_get_text(GTK_ENTRY(inputbox
));
830 /* Get the length of the list of commands for completion. We need this to
831 * malloc/realloc correctly.
833 listlen
= LENGTH(commands
);
835 if ((len
== 0 || str
[0] != ':') && arg
->i
!= HideCompletion
)
838 if (arg
->i
!= HideCompletion
&& widgets
&& current
!= -1 && !strcmp(&str
[1], suggestions
[current
])) {
839 gdk_color_parse(completionbgcolor
[0], &color
);
840 gtk_widget_modify_bg(widgets
[current
], GTK_STATE_NORMAL
, &color
);
841 current
= (n
+ current
+ (arg
->i
== DirectionPrev
? -1 : 1)) % n
;
842 if ((arg
->i
== DirectionNext
&& current
== 0)
843 || (arg
->i
== DirectionPrev
&& current
== n
- 1))
849 gtk_widget_destroy(GTK_WIDGET(table
));
850 gtk_widget_destroy(GTK_WIDGET(top_border
));
857 if (arg
->i
== HideCompletion
)
860 } else if (arg
->i
== HideCompletion
)
863 prefix
= g_strdup(str
);
864 widgets
= malloc(sizeof(GtkWidget
*) * listlen
);
865 suggestions
= malloc(sizeof(char*) * listlen
);
866 top_border
= gtk_event_box_new();
867 gtk_widget_set_size_request(GTK_WIDGET(top_border
), 0, 1);
868 gdk_color_parse(completioncolor
[2], &color
);
869 gtk_widget_modify_bg(top_border
, GTK_STATE_NORMAL
, &color
);
870 table
= gtk_event_box_new();
871 gdk_color_parse(completionbgcolor
[0], &color
);
872 _table
= GTK_BOX(gtk_vbox_new(FALSE
, 0));
874 if (strchr(str
, ' ') == NULL
) {
875 /* command completion */
876 listlen
= LENGTH(commands
);
877 for (i
= 0; i
< listlen
; i
++) {
878 if (commands
[i
].cmd
== NULL
)
880 cmdlen
= strlen(commands
[i
].cmd
);
881 if (!highlight
|| (n
< MAX_LIST_SIZE
&& len
- 1 <= cmdlen
&& !strncmp(&str
[1], commands
[i
].cmd
, len
- 1))) {
882 p
= s
= malloc(sizeof(char*) * (highlight
? sizeof(COMPLETION_TAG_OPEN
) + sizeof(COMPLETION_TAG_CLOSE
) - 1 : 1) + cmdlen
);
884 memcpy(p
, COMPLETION_TAG_OPEN
, sizeof(COMPLETION_TAG_OPEN
) - 1);
885 memcpy((p
+= sizeof(COMPLETION_TAG_OPEN
) - 1), &str
[1], len
- 1);
886 memcpy((p
+= len
- 1), COMPLETION_TAG_CLOSE
, sizeof(COMPLETION_TAG_CLOSE
) - 1);
887 p
+= sizeof(COMPLETION_TAG_CLOSE
) - 1;
889 memcpy(p
, &commands
[i
].cmd
[len
- 1], cmdlen
- len
+ 2);
890 row
= GTK_BOX(gtk_hbox_new(FALSE
, 0));
891 row_eventbox
= gtk_event_box_new();
892 gtk_widget_modify_bg(row_eventbox
, GTK_STATE_NORMAL
, &color
);
893 el
= gtk_label_new(NULL
);
894 markup
= g_strconcat("<span font=\"", completionfont
[0], "\" color=\"", completioncolor
[0], "\">", s
, "</span>", NULL
);
896 gtk_label_set_markup(GTK_LABEL(el
), markup
);
898 gtk_misc_set_alignment(GTK_MISC(el
), 0, 0);
899 gtk_box_pack_start(row
, el
, TRUE
, TRUE
, 2);
900 gtk_container_add(GTK_CONTAINER(row_eventbox
), GTK_WIDGET(row
));
901 gtk_box_pack_start(_table
, GTK_WIDGET(row_eventbox
), FALSE
, FALSE
, 0);
902 suggestions
[n
] = commands
[i
].cmd
;
903 widgets
[n
++] = row_eventbox
;
907 entry
= (char *)malloc(512 * sizeof(char));
911 memset(entry
, 0, 512);
912 suggurls
= malloc(sizeof(char*) * listlen
);
913 if (suggurls
== NULL
) {
916 spacepos
= strcspn(str
, " ");
917 searchfor
= (str
+ spacepos
+ 1);
918 strncpy(command
, (str
+ 1), spacepos
- 1);
919 if (strlen(command
) == 3 && strncmp(command
, "set", 3) == 0) {
920 /* browser settings */
921 listlen
= LENGTH(browsersettings
);
922 for (i
= 0; i
< listlen
; i
++) {
923 if (n
< MAX_LIST_SIZE
&& strstr(browsersettings
[i
].name
, searchfor
) != NULL
) {
925 fill_suggline(suggline
, command
, browsersettings
[i
].name
);
926 /* FIXME(HP): This memory is never freed */
927 suggurls
[n
] = (char *)malloc(sizeof(char) * 512 + 1);
928 strncpy(suggurls
[n
], suggline
, 512);
929 suggestions
[n
] = suggurls
[n
];
930 row_eventbox
= fill_eventbox(suggline
);
931 gtk_box_pack_start(_table
, GTK_WIDGET(row_eventbox
), FALSE
, FALSE
, 0);
932 widgets
[n
++] = row_eventbox
;
936 } else if (strlen(command
) == 2 && strncmp(command
, "qt", 2) == 0) {
937 /* completion on tags */
938 spacepos
= strcspn(str
, " ");
939 searchfor
= (str
+ spacepos
+ 1);
940 elementlist
= complete_list(searchfor
, 1, elementlist
);
942 /* URL completion: bookmarks */
943 elementlist
= complete_list(searchfor
, 0, elementlist
);
944 m
= count_list(elementlist
);
945 if (m
< MAX_LIST_SIZE
) {
946 /* URL completion: history */
947 elementlist
= complete_list(searchfor
, 2, elementlist
);
950 elementpointer
= elementlist
;
951 while (elementpointer
!= NULL
) {
952 fill_suggline(suggline
, command
, elementpointer
->element
);
953 /* FIXME(HP): This memory is never freed */
954 suggurls
[n
] = (char *)malloc(sizeof(char) * 512 + 1);
955 strncpy(suggurls
[n
], suggline
, 512);
956 suggestions
[n
] = suggurls
[n
];
957 row_eventbox
= fill_eventbox(suggline
);
958 gtk_box_pack_start(_table
, GTK_WIDGET(row_eventbox
), FALSE
, FALSE
, 0);
959 widgets
[n
++] = row_eventbox
;
960 elementpointer
= elementpointer
->next
;
961 if (n
>= MAX_LIST_SIZE
)
964 free_list(elementlist
);
965 if (suggurls
!= NULL
) {
974 /* TA: FIXME - this needs rethinking entirely. */
976 GtkWidget
**widgets_temp
= realloc(widgets
, sizeof(*widgets
) * n
);
977 if (widgets_temp
== NULL
&& widgets
== NULL
) {
978 fprintf(stderr
, "Couldn't realloc() widgets\n");
981 widgets
= widgets_temp
;
982 char **suggestions_temp
= realloc(suggestions
, sizeof(*suggestions
) * n
);
983 if (suggestions_temp
== NULL
&& suggestions
== NULL
) {
984 fprintf(stderr
, "Couldn't realloc() suggestions\n");
987 suggestions
= suggestions_temp
;
990 gdk_color_parse(completionbgcolor
[1], &color
);
991 gtk_widget_modify_bg(table
, GTK_STATE_NORMAL
, &color
);
992 el
= gtk_label_new(NULL
);
993 gtk_misc_set_alignment(GTK_MISC(el
), 0, 0);
994 markup
= g_strconcat("<span font=\"", completionfont
[1], "\" color=\"", completioncolor
[1], "\">No Completions</span>", NULL
);
995 gtk_label_set_markup(GTK_LABEL(el
), markup
);
997 gtk_box_pack_start(_table
, GTK_WIDGET(el
), FALSE
, FALSE
, 0);
999 gtk_box_pack_start(box
, GTK_WIDGET(top_border
), FALSE
, FALSE
, 0);
1000 gtk_container_add(GTK_CONTAINER(table
), GTK_WIDGET(_table
));
1001 gtk_box_pack_start(box
, GTK_WIDGET(table
), FALSE
, FALSE
, 0);
1002 gtk_widget_show_all(GTK_WIDGET(window
));
1005 current
= arg
->i
== DirectionPrev
? n
- 1 : 0;
1007 if (current
!= -1) {
1008 gdk_color_parse(completionbgcolor
[2], &color
);
1009 gtk_widget_modify_bg(GTK_WIDGET(widgets
[current
]), GTK_STATE_NORMAL
, &color
);
1010 s
= g_strconcat(":", suggestions
[current
], NULL
);
1011 gtk_entry_set_text(GTK_ENTRY(inputbox
), s
);
1014 gtk_entry_set_text(GTK_ENTRY(inputbox
), prefix
);
1015 gtk_editable_set_position(GTK_EDITABLE(inputbox
), -1);
1020 descend(const Arg
*arg
) {
1021 char *source
= (char*)webkit_web_view_get_uri(webview
), *p
= &source
[0], *new;
1023 count
= count
? count
: 1;
1027 if (arg
->i
== Rootdir
) {
1028 for (i
= 0; i
< 3; i
++) /* get to the third slash */
1029 if (!(p
= strchr(++p
, '/')))
1030 return TRUE
; /* if we cannot find it quit */
1032 len
= strlen(source
);
1033 if (!len
) /* if string is empty quit */
1035 p
= source
+ len
; /* start at the end */
1036 if (*(p
- 1) == '/') /* /\/$/ is not an additional level */
1038 for (i
= 0; i
< count
; i
++)
1039 while(*(p
--) != '/' || *p
== '/') /* count /\/+/ as one slash */
1040 if (p
== source
) /* if we reach the first char pointer quit */
1042 ++p
; /* since we do p-- in the while, we are pointing at
1043 the char before the slash, so +1 */
1045 len
= p
- source
+ 1; /* new length = end - start + 1 */
1046 new = malloc(len
+ 1);
1047 memcpy(new, source
, len
);
1049 webkit_web_view_load_uri(webview
, new);
1055 echo(const Arg
*arg
) {
1056 int index
= !arg
->s
? 0 : arg
->i
& (~NoAutoHide
);
1058 if (index
< Info
|| index
> Error
)
1061 if (!gtk_widget_is_focus(GTK_WIDGET(inputbox
))) {
1062 set_widget_font_and_color(inputbox
, urlboxfont
[index
], urlboxbgcolor
[index
], urlboxcolor
[index
]);
1063 gtk_entry_set_text(GTK_ENTRY(inputbox
), !arg
->s
? "" : arg
->s
);
1070 open_inspector(const Arg
* arg
) {
1071 gboolean inspect_enabled
;
1072 WebKitWebSettings
*settings
;
1075 settings
= webkit_web_view_get_settings(webview
);
1076 g_object_get(G_OBJECT(settings
), "enable-developer-extras", &inspect_enabled
, NULL
);
1077 if (inspect_enabled
) {
1078 webkit_web_inspector_show(inspector
);
1081 a
.s
= g_strdup("Webinspector is not enabled");
1088 input(const Arg
*arg
) {
1095 /* if inputbox hidden, show it again */
1096 if (!gtk_widget_get_visible(inputbox
))
1097 gtk_widget_set_visible(inputbox
, TRUE
);
1101 /* Set the colour and font back to the default, so that we don't still
1102 * maintain a red colour from a warning from an end of search indicator,
1105 set_widget_font_and_color(inputbox
, urlboxfont
[index
], urlboxbgcolor
[index
], urlboxcolor
[index
]);
1107 /* to avoid things like :open URL :open URL2 or :open :open URL */
1108 gtk_entry_set_text(GTK_ENTRY(inputbox
), "");
1109 gtk_editable_insert_text(GTK_EDITABLE(inputbox
), arg
->s
, -1, &pos
);
1110 if (arg
->i
& InsertCurrentURL
&& (url
= webkit_web_view_get_uri(webview
)))
1111 gtk_editable_insert_text(GTK_EDITABLE(inputbox
), url
, -1, &pos
);
1113 gtk_widget_grab_focus(inputbox
);
1114 gtk_editable_set_position(GTK_EDITABLE(inputbox
), -1);
1116 if (arg
->s
[0] == '.' || arg
->s
[0] == ',' || arg
->s
[0] == ';') {
1118 memset(followTarget
, 0, 8);
1119 strncpy(followTarget
, "current", 8);
1121 switch (arg
->s
[0]) {
1123 a
.s
= g_strdup("hints.createHints('', 'f');");
1127 a
.s
= g_strdup("hints.createHints('', 'F');");
1133 switch (arg
->s
[1]) {
1135 a
.s
= g_strdup("hints.createHints('', 's');");
1138 a
.s
= g_strdup("hints.createHints('', 'y');");
1141 a
.s
= g_strdup("hints.createHints('', 'f');");
1144 a
.s
= g_strdup("hints.createHints('', 'F');");
1146 case 'O': case 'T': case 'W':
1147 a
.s
= g_strdup("hints.createHints('', 'O');");
1150 a
.s
= g_strdup("hints.createHints('', 'i');");
1153 a
.s
= g_strdup("hints.createHints('', 'I');");
1170 navigate(const Arg
*arg
) {
1171 if (arg
->i
& NavigationForwardBack
)
1172 webkit_web_view_go_back_or_forward(webview
, (arg
->i
== NavigationBack
? -1 : 1) * (count
? count
: 1));
1173 else if (arg
->i
& NavigationReloadActions
)
1174 (arg
->i
== NavigationReload
? webkit_web_view_reload
: webkit_web_view_reload_bypass_cache
)(webview
);
1176 webkit_web_view_stop_loading(webview
);
1181 number(const Arg
*arg
) {
1182 const char *source
= webkit_web_view_get_uri(webview
);
1183 char *uri
, *p
, *new;
1184 int number
, diff
= (count
? count
: 1) * (arg
->i
== Increment
? 1 : -1);
1188 uri
= g_strdup(source
); /* copy string */
1190 while(*p
!= '\0') /* goto the end of the string */
1193 while(*p
>= '0' && *p
<= '9') /* go back until non number char is reached */
1195 if (*(++p
) == '\0') { /* if no numbers were found abort */
1199 number
= atoi(p
) + diff
; /* apply diff on number */
1201 new = g_strdup_printf("%s%d", uri
, number
); /* create new uri */
1202 webkit_web_view_load_uri(webview
, new);
1209 open_arg(const Arg
*arg
) {
1211 char *s
= arg
->s
, *p
= NULL
, *new;
1212 Arg a
= { .i
= NavigationReload
};
1214 char *search_uri
, *search_term
;
1230 else if (arg
->i
== TargetCurrent
) {
1231 while(*s
== ' ') /* strip leading whitespace */
1233 p
= (s
+ strlen(s
) - 1);
1234 while(*p
== ' ') /* strip trailing whitespace */
1239 /* check for external handlers */
1240 if (open_handler(s
))
1242 /* check for search engines */
1244 if (p
) { /* check for search engines */
1246 search_uri
= find_uri_for_searchengine(s
);
1247 if (search_uri
!= NULL
) {
1248 search_term
= soup_uri_encode(p
+1, "&");
1249 new = g_strdup_printf(search_uri
, search_term
);
1250 g_free(search_term
);
1255 if (len
> 3 && strstr(s
, "://")) { /* valid url? */
1256 p
= new = g_malloc(len
+ 1);
1257 while(*s
!= '\0') { /* strip whitespaces */
1263 } else if (strcspn(s
, "/") == 0 || strcspn(s
, "./") == 0) { /* prepend "file://" */
1264 new = g_malloc(sizeof("file://") + len
);
1265 strcpy(new, "file://");
1266 memcpy(&new[sizeof("file://") - 1], s
, len
+ 1);
1267 } else if (p
|| !strchr(s
, '.')) { /* whitespaces or no dot? */
1268 search_uri
= find_uri_for_searchengine(defaultsearch
);
1269 if (search_uri
!= NULL
) {
1270 search_term
= soup_uri_encode(s
, "&");
1271 new = g_strdup_printf(search_uri
, search_term
);
1272 g_free(search_term
);
1274 } else { /* prepend "http://" */
1275 new = g_malloc(sizeof("http://") + len
);
1276 strcpy(new, "http://");
1277 memcpy(&new[sizeof("http://") - 1], s
, len
+ 1);
1280 webkit_web_view_load_uri(webview
, new);
1283 g_spawn_async(NULL
, argv
, NULL
, G_SPAWN_SEARCH_PATH
, NULL
, NULL
, NULL
, NULL
);
1288 open_remembered(const Arg
*arg
)
1290 Arg a
= {arg
->i
, rememberedURI
};
1292 if (strcmp(rememberedURI
, "")) {
1299 yank(const Arg
*arg
) {
1300 const char *url
, *content
;
1302 if (arg
->i
& SourceSelection
) {
1303 webkit_web_view_copy_clipboard(webview
);
1304 if (arg
->i
& ClipboardPrimary
)
1305 content
= gtk_clipboard_wait_for_text(clipboards
[0]);
1306 if (!content
&& arg
->i
& ClipboardGTK
)
1307 content
= gtk_clipboard_wait_for_text(clipboards
[1]);
1309 echo_message(Info
, "Yanked %s", content
);
1310 g_free((gpointer
*)content
);
1313 if (arg
->i
& SourceURL
) {
1314 url
= webkit_web_view_get_uri(webview
);
1321 echo_message(Info
, "Yanked %s", url
);
1322 if (arg
->i
& ClipboardPrimary
)
1323 gtk_clipboard_set_text(clipboards
[0], url
, -1);
1324 if (arg
->i
& ClipboardGTK
)
1325 gtk_clipboard_set_text(clipboards
[1], url
, -1);
1331 paste(const Arg
*arg
) {
1332 Arg a
= { .i
= arg
->i
& TargetNew
, .s
= NULL
};
1334 /* If we're over a link, open it in a new target. */
1335 if (strlen(rememberedURI
) > 0) {
1336 Arg new_target
= { .i
= TargetNew
, .s
= arg
->s
};
1337 open_arg(&new_target
);
1341 if (arg
->i
& ClipboardPrimary
)
1342 a
.s
= gtk_clipboard_wait_for_text(clipboards
[0]);
1343 if (!a
.s
&& arg
->i
& ClipboardGTK
)
1344 a
.s
= gtk_clipboard_wait_for_text(clipboards
[1]);
1353 quit(const Arg
*arg
) {
1355 const char *filename
;
1356 const char *uri
= webkit_web_view_get_uri(webview
);
1358 /* write last URL into status file for recreation with "u" */
1359 filename
= g_strdup_printf(CLOSED_URL_FILENAME
);
1360 f
= fopen(filename
, "w");
1361 g_free((gpointer
*)filename
);
1363 fprintf(f
, "%s", uri
);
1372 revive(const Arg
*arg
) {
1374 const char *filename
;
1375 char buffer
[512] = "";
1376 Arg a
= { .i
= TargetNew
, .s
= NULL
};
1377 /* get the URL of the window which has been closed last */
1378 filename
= g_strdup_printf(CLOSED_URL_FILENAME
);
1379 f
= fopen(filename
, "r");
1380 g_free((gpointer
*)filename
);
1382 fgets(buffer
, 512, f
);
1385 if (strlen(buffer
) > 0) {
1394 gboolean
print_frame(const Arg
*arg
)
1396 WebKitWebFrame
*frame
= webkit_web_view_get_main_frame(webview
);
1397 webkit_web_frame_print (frame
);
1402 search(const Arg
*arg
) {
1403 count
= count
? count
: 1;
1404 gboolean success
, direction
= arg
->i
& DirectionPrev
;
1407 free(search_handle
);
1408 search_handle
= g_strdup(arg
->s
);
1412 if (arg
->i
& DirectionAbsolute
)
1413 search_direction
= direction
;
1415 direction
^= search_direction
;
1417 success
= webkit_web_view_search_text(webview
, search_handle
, arg
->i
& CaseSensitive
, direction
, FALSE
);
1419 if (arg
->i
& Wrapping
) {
1420 success
= webkit_web_view_search_text(webview
, search_handle
, arg
->i
& CaseSensitive
, direction
, TRUE
);
1422 echo_message(Warning
, "search hit %s, continuing at %s",
1423 direction
? "BOTTOM" : "TOP",
1424 direction
? "TOP" : "BOTTOM");
1432 echo_message(Error
, "Pattern not found: %s", search_handle
);
1438 set(const Arg
*arg
) {
1441 if (search_handle
) {
1442 search_handle
= NULL
;
1443 webkit_web_view_unmark_text_matches(webview
);
1445 gtk_entry_set_text(GTK_ENTRY(inputbox
), "");
1446 gtk_widget_grab_focus(GTK_WIDGET(webview
));
1448 case ModePassThrough
:
1449 echo_message(Info
| NoAutoHide
, "-- PASS THROUGH --");
1452 echo_message(Info
| NoAutoHide
, "-- PASS TROUGH (next) --");
1454 case ModeInsert
: /* should not be called manually but automatically */
1455 echo_message(Info
| NoAutoHide
, "-- INSERT --");
1465 jsapi_ref_to_string(JSContextRef context
, JSValueRef ref
) {
1466 JSStringRef string_ref
;
1470 string_ref
= JSValueToStringCopy(context
, ref
, NULL
);
1471 length
= JSStringGetMaximumUTF8CStringSize(string_ref
);
1472 string
= g_new(gchar
, length
);
1473 JSStringGetUTF8CString(string_ref
, string
, length
);
1474 JSStringRelease(string_ref
);
1479 jsapi_evaluate_script(const gchar
*script
, gchar
**value
, gchar
**message
) {
1480 WebKitWebFrame
*frame
= webkit_web_view_get_main_frame(webview
);
1481 JSGlobalContextRef context
= webkit_web_frame_get_global_context(frame
);
1483 JSValueRef val
, exception
;
1485 str
= JSStringCreateWithUTF8CString(script
);
1486 val
= JSEvaluateScript(context
, str
, JSContextGetGlobalObject(context
), NULL
, 0, &exception
);
1487 JSStringRelease(str
);
1489 *message
= jsapi_ref_to_string(context
, exception
);
1491 *value
= jsapi_ref_to_string(context
, val
);
1495 quickmark(const Arg
*a
) {
1498 char *fn
= g_strdup_printf(QUICKMARK_FILE
);
1500 fp
= fopen(fn
, "r");
1505 if (fp
!= NULL
&& b
< 10) {
1506 for( i
=0; i
< b
; ++i
) {
1510 fgets(buf
, 100, fp
);
1512 char *ptr
= strrchr(buf
, '\n');
1515 Arg x
= { .s
= buf
};
1516 return open_arg(&x
);
1518 echo_message(Error
, "Quickmark %d not defined", b
);
1521 } else { return false; }
1525 script(const Arg
*arg
) {
1526 gchar
*value
= NULL
, *message
= NULL
;
1527 char text
[1024] = "";
1529 WebKitNetworkRequest
*request
;
1530 WebKitDownload
*download
;
1533 set_error("Missing argument.");
1536 jsapi_evaluate_script(arg
->s
, &value
, &message
);
1543 if (arg
->i
!= Silent
&& value
) {
1544 echo_message(arg
->i
, value
);
1546 /* switch mode according to scripts return value */
1548 if (strncmp(value
, "done;", 5) == 0) {
1551 } else if (strncmp(value
, "insert;", 7) == 0) {
1554 manual_focus
= TRUE
;
1555 } else if (strncmp(value
, "save;", 5) == 0) {
1556 /* forced download */
1559 request
= webkit_network_request_new((value
+ 5));
1560 download
= webkit_download_new(request
);
1561 webview_download_cb(webview
, download
, (gpointer
*)NULL
);
1562 } else if (strncmp(value
, "yank;", 5) == 0) {
1563 /* yank link URL to clipboard */
1566 a
.i
= ClipboardPrimary
| ClipboardGTK
;
1569 } else if (strncmp(value
, "colon;", 6) == 0) {
1570 /* use link URL for colon command */
1571 strncpy(text
, (char *)gtk_entry_get_text(GTK_ENTRY(inputbox
)), 1023);
1576 a
.s
= g_strconcat(":open ", (value
+ 6), NULL
);
1579 a
.s
= g_strconcat(":tabopen ", (value
+ 6), NULL
);
1586 } else if (strncmp(value
, "error;", 6) == 0) {
1596 scroll(const Arg
*arg
) {
1597 GtkAdjustment
*adjust
= (arg
->i
& OrientationHoriz
) ? adjust_h
: adjust_v
;
1598 int max
= gtk_adjustment_get_upper(adjust
) - gtk_adjustment_get_page_size(adjust
);
1599 float val
= gtk_adjustment_get_value(adjust
) / max
* 100;
1600 int direction
= (arg
->i
& (1 << 2)) ? 1 : -1;
1602 if ((direction
== 1 && val
< 100) || (direction
== -1 && val
> 0)) {
1603 if (arg
->i
& ScrollMove
)
1604 gtk_adjustment_set_value(adjust
, gtk_adjustment_get_value(adjust
) +
1605 direction
* /* direction */
1606 ((arg
->i
& UnitLine
|| (arg
->i
& UnitBuffer
&& count
)) ? (scrollstep
* (count
? count
: 1)) : (
1607 arg
->i
& UnitBuffer
? gtk_adjustment_get_page_size(adjust
) / 2 :
1608 (count
? count
: 1) * (gtk_adjustment_get_page_size(adjust
) -
1609 (gtk_adjustment_get_page_size(adjust
) > pagingkeep
? pagingkeep
: 0)))));
1611 gtk_adjustment_set_value(adjust
,
1612 ((direction
== 1) ? gtk_adjustment_get_upper
: gtk_adjustment_get_lower
)(adjust
));
1619 zoom(const Arg
*arg
) {
1620 webkit_web_view_set_full_content_zoom(webview
, (arg
->i
& ZoomFullContent
) > 0);
1621 webkit_web_view_set_zoom_level(webview
, (arg
->i
& ZoomOut
) ?
1622 webkit_web_view_get_zoom_level(webview
) +
1623 (((float)(count
? count
: 1)) * (arg
->i
& (1 << 1) ? 1.0 : -1.0) * zoomstep
) :
1624 (count
? (float)count
/ 100.0 : 1.0));
1629 fake_key_event(const Arg
*a
) {
1634 if ( (xdpy
= XOpenDisplay(NULL
)) == NULL
) {
1635 echo_message(Error
, "Couldn't find the XDisplay.");
1641 xk
.subwindow
= None
;
1642 xk
.time
= CurrentTime
;
1643 xk
.same_screen
= True
;
1644 xk
.x
= xk
.y
= xk
.x_root
= xk
.y_root
= 1;
1649 echo_message(Error
, "Zero pointer as argument! Check your config.h");
1654 if( (keysym
= XStringToKeysym(a
->s
)) == NoSymbol
) {
1655 echo_message(Error
, "Couldn't translate %s to keysym", a
->s
);
1659 if( (xk
.keycode
= XKeysymToKeycode(xdpy
, keysym
)) == NoSymbol
) {
1660 echo_message(Error
, "Couldn't translate keysym to keycode");
1665 if( !XSendEvent(xdpy
, embed
, True
, KeyPressMask
, (XEvent
*)&xk
) ) {
1666 echo_message(Error
, "XSendEvent failed");
1675 commandhistoryfetch(const Arg
*arg
) {
1676 const int length
= g_list_length(commandhistory
);
1679 if (arg
->i
== DirectionPrev
) {
1680 commandpointer
= (length
+ commandpointer
- 1) % length
;
1682 commandpointer
= (length
+ commandpointer
+ 1) % length
;
1685 const char* command
= (char *)g_list_nth_data(commandhistory
, commandpointer
);
1686 gtk_entry_set_text(GTK_ENTRY(inputbox
), g_strconcat(":", command
, NULL
));
1687 gtk_editable_set_position(GTK_EDITABLE(inputbox
), -1);
1695 bookmark(const Arg
*arg
) {
1697 const char *filename
;
1698 const char *uri
= webkit_web_view_get_uri(webview
);
1699 const char *title
= webkit_web_view_get_title(webview
);
1700 filename
= g_strdup_printf(BOOKMARKS_STORAGE_FILENAME
);
1701 f
= fopen(filename
, "a");
1702 g_free((gpointer
*)filename
);
1703 if (uri
== NULL
|| strlen(uri
) == 0) {
1704 set_error("No URI found to bookmark.");
1708 fprintf(f
, "%s", uri
);
1709 if (title
!= NULL
) {
1710 fprintf(f
, "%s", " ");
1711 fprintf(f
, "%s", title
);
1713 if (arg
->s
&& strlen(arg
->s
)) {
1714 build_taglist(arg
, f
);
1716 fprintf(f
, "%s", "\n");
1718 echo_message(Info
, "Bookmark saved");
1721 set_error("Bookmarks file not found.");
1729 const char *filename
;
1730 const char *uri
= webkit_web_view_get_uri(webview
);
1731 const char *title
= webkit_web_view_get_title(webview
);
1732 char *entry
, buffer
[512], *new;
1734 gboolean finished
= FALSE
;
1736 if (title
!= NULL
) {
1737 entry
= malloc((strlen(uri
) + strlen(title
) + 2) * sizeof(char));
1738 memset(entry
, 0, strlen(uri
) + strlen(title
) + 2);
1740 entry
= malloc((strlen(uri
) + 1) * sizeof(char));
1741 memset(entry
, 0, strlen(uri
) + 1);
1743 if (entry
!= NULL
) {
1744 strncpy(entry
, uri
, strlen(uri
));
1745 if (title
!= NULL
) {
1746 strncat(entry
, " ", 1);
1747 strncat(entry
, title
, strlen(title
));
1750 filename
= g_strdup_printf(HISTORY_STORAGE_FILENAME
);
1751 f
= fopen(filename
, "r");
1753 new = (char *)malloc(HISTORY_MAX_ENTRIES
* 512 * sizeof(char) + 1);
1755 memset(new, 0, HISTORY_MAX_ENTRIES
* 512 * sizeof(char) + 1);
1756 /* newest entries go on top */
1757 strncpy(new, entry
, strlen(entry
));
1758 strncat(new, "\n", 1);
1759 /* retain at most HISTORY_MAX_ENTIRES - 1 old entries */
1760 while (finished
!= TRUE
) {
1761 if ((char *)NULL
== fgets(buffer
, 512, f
)) {
1762 /* check if end of file was reached / error occured */
1766 /* end of file reached */
1770 /* compare line (-1 because of newline character) */
1771 if (n
!= strlen(buffer
) - 1 || strncmp(entry
, buffer
, n
) != 0) {
1772 /* if the URI is already in history; we put it on top and skip it here */
1773 strncat(new, buffer
, 512);
1776 if ((i
+ 1) >= HISTORY_MAX_ENTRIES
) {
1782 f
= fopen(filename
, "w");
1783 g_free((gpointer
*)filename
);
1785 fprintf(f
, "%s", new);
1794 if (entry
!= NULL
) {
1803 view_source(const Arg
* arg
) {
1804 gboolean current_mode
= webkit_web_view_get_view_source_mode(webview
);
1805 webkit_web_view_set_view_source_mode(webview
, !current_mode
);
1806 webkit_web_view_reload(webview
);
1810 /* open an external editor defined by the protocol handler for
1811 vimprobableedit on a text box or similar */
1813 open_editor(const Arg
*arg
) {
1817 gchar
*value
= NULL
, *message
= NULL
, *tag
= NULL
, *edit_url
= NULL
;
1818 gchar
*temp_file_name
= g_strdup_printf("%s/vimprobableeditXXXXXX",
1820 int temp_file_handle
= -1;
1822 /* check if active element is suitable for text editing */
1823 jsapi_evaluate_script("document.activeElement.tagName", &value
, &message
);
1826 tag
= g_strdup(value
);
1827 if (strcmp(tag
, "INPUT") == 0) {
1828 /* extra check: type == text */
1829 jsapi_evaluate_script("document.activeElement.type", &value
, &message
);
1830 if (strcmp(value
, "text") != 0) {
1835 } else if (strcmp(tag
, "TEXTAREA") != 0) {
1840 jsapi_evaluate_script("document.activeElement.value", &value
, &message
);
1841 text
= g_strdup(value
);
1848 /* write text into temporary file */
1849 temp_file_handle
= mkstemp(temp_file_name
);
1850 if (temp_file_handle
== -1) {
1851 message
= g_strdup_printf("Could not create temporary file: %s",
1853 echo_message(Error
, message
);
1859 if (write(temp_file_handle
, text
, strlen(text
)) != strlen(text
)) {
1860 message
= g_strdup_printf("Short write to temporary file: %s",
1862 echo_message(Error
, message
);
1868 close(temp_file_handle
);
1872 edit_url
= g_strdup_printf("vimprobableedit:%s", temp_file_name
);
1873 success
= open_handler_pid(edit_url
, &child_pid
);
1876 echo_message(Error
, "External editor open failed (no handler for"
1877 " vimprobableedit protocol?)");
1878 unlink(temp_file_name
);
1884 /* mark the active text box as "under processing" */
1885 jsapi_evaluate_script(
1886 "document.activeElement.disabled = true;"
1887 "document.activeElement.originalBackground = "
1888 " document.activeElement.style.background;"
1889 "document.activeElement.style.background = '#aaaaaa';"
1892 g_child_watch_add(child_pid
, _resume_from_editor
, temp_file_name
);
1894 /* temp_file_name is freed in _resume_from_editor */
1902 /* pick up from where open_editor left the work to the glib event loop.
1904 This is called when the external editor exits.
1906 The data argument points to allocated memory containing the temporary file
1909 _resume_from_editor(GPid child_pid
, int child_status
, gpointer data
) {
1911 GString
*set_value_js
= g_string_new(
1912 "document.activeElement.value = \"");
1913 g_spawn_close_pid(child_pid
);
1914 gchar
*value
= NULL
, *message
= NULL
;
1915 gchar
*temp_file_name
= data
;
1916 gchar buffer
[BUF_SIZE
] = "";
1917 gchar
*buf_ptr
= buffer
;
1920 jsapi_evaluate_script(
1921 "document.activeElement.disabled = true;"
1922 "document.activeElement.style.background = '#aaaaaa';"
1926 echo_message(Error
, "External editor returned with non-zero status,"
1927 " discarding edits.");
1931 /* re-read the new contents of the file and put it into the HTML element */
1932 if (!access(temp_file_name
, R_OK
) == 0) {
1933 message
= g_strdup_printf("Could not access temporary file: %s",
1937 fp
= fopen(temp_file_name
, "r");
1939 /* this would be too weird to even emit an error message */
1942 jsapi_evaluate_script("document.activeElement.value = '';",
1945 while (EOF
!= (char_read
= fgetc(fp
))) {
1946 if (char_read
== '\n') {
1949 } else if (char_read
== '"') {
1953 *buf_ptr
++ = char_read
;
1955 /* ship out as the buffer when space gets tight. This has
1956 fuzz to save on thinking, plus we have enough space for the
1957 trailing "; in any case. */
1958 if (buf_ptr
-buffer
>=BUF_SIZE
-10) {
1960 g_string_append(set_value_js
, buffer
);
1967 g_string_append(set_value_js
, buffer
);
1970 jsapi_evaluate_script(set_value_js
->str
, &value
, &message
);
1972 /* Fall through, error and normal exit are identical */
1974 jsapi_evaluate_script(
1975 "document.activeElement.disabled = false;"
1976 "document.activeElement.style.background ="
1977 " document.activeElement.originalBackground;"
1980 g_string_free(set_value_js
, TRUE
);
1981 unlink(temp_file_name
);
1982 g_free(temp_file_name
);
1988 focus_input(const Arg
*arg
) {
1991 a
.s
= g_strdup("hints.focusInput();");
1996 manual_focus
= TRUE
;
2001 browser_settings(const Arg
*arg
) {
2004 set_error("Missing argument.");
2007 strncpy(line
, arg
->s
, 254);
2008 if (process_set_line(line
))
2011 set_error("Invalid setting.");
2017 search_word(int whichword
) {
2019 static char word
[240];
2020 char *c
= my_pair
.line
;
2022 while (isspace(*c
) && *c
)
2025 switch (whichword
) {
2027 while (*c
&& !isspace (*c
) && *c
!= '=' && k
< 240) {
2032 strncpy(my_pair
.what
, word
, 20);
2035 while (*c
&& k
< 240) {
2040 strncpy(my_pair
.value
, word
, 240);
2048 process_set_line(char *line
) {
2052 WebKitWebSettings
*settings
;
2054 settings
= webkit_web_view_get_settings(webview
);
2055 my_pair
.line
= line
;
2057 if (!strlen(my_pair
.what
))
2060 while (isspace(*c
) && *c
)
2063 if (*c
== ':' || *c
== '=')
2069 listlen
= LENGTH(browsersettings
);
2070 for (i
= 0; i
< listlen
; i
++) {
2071 if (strlen(browsersettings
[i
].name
) == strlen(my_pair
.what
) && strncmp(browsersettings
[i
].name
, my_pair
.what
, strlen(my_pair
.what
)) == 0) {
2072 /* mandatory argument not provided */
2073 if (strlen(my_pair
.value
) == 0)
2075 /* process qmark? */
2076 if (strlen(my_pair
.what
) == 5 && strncmp("qmark", my_pair
.what
, 5) == 0) {
2077 return (process_save_qmark(my_pair
.value
, webview
));
2079 /* interpret boolean values */
2080 if (browsersettings
[i
].boolval
) {
2081 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) {
2083 } 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) {
2088 } else if (browsersettings
[i
].colourval
) {
2089 /* interpret as hexadecimal colour */
2090 if (!parse_colour(my_pair
.value
)) {
2094 if (browsersettings
[i
].var
!= NULL
) {
2095 strncpy(browsersettings
[i
].var
, my_pair
.value
, MAX_SETTING_SIZE
);
2096 if (strlen(my_pair
.value
) > MAX_SETTING_SIZE
- 1) {
2097 /* in this case, \0 will not have been copied */
2098 browsersettings
[i
].var
[MAX_SETTING_SIZE
- 1] = '\0';
2099 /* in case this string is also used for a webkit setting, make sure it's consistent */
2100 my_pair
.value
[MAX_SETTING_SIZE
- 1] = '\0';
2101 echo_message(Info
, "String too long; automatically truncated!");
2104 if (strlen(browsersettings
[i
].webkit
) > 0) {
2105 /* activate appropriate webkit setting */
2106 if (browsersettings
[i
].boolval
) {
2107 g_object_set((GObject
*)settings
, browsersettings
[i
].webkit
, boolval
, NULL
);
2108 } else if (browsersettings
[i
].intval
) {
2109 g_object_set((GObject
*)settings
, browsersettings
[i
].webkit
, atoi(my_pair
.value
), NULL
);
2111 g_object_set((GObject
*)settings
, browsersettings
[i
].webkit
, my_pair
.value
, NULL
);
2113 webkit_web_view_set_settings(webview
, settings
);
2116 if (strlen(my_pair
.what
) == 14) {
2117 if (strncmp("acceptlanguage", my_pair
.what
, 14) == 0) {
2118 g_object_set(G_OBJECT(session
), "accept-language", acceptlanguage
, NULL
);
2119 } else if (strncmp("completioncase", my_pair
.what
, 14) == 0) {
2120 complete_case_sensitive
= boolval
;
2122 } else if (strlen(my_pair
.what
) == 5 && strncmp("proxy", my_pair
.what
, 5) == 0) {
2123 toggle_proxy(boolval
);
2124 } else if (strlen(my_pair
.what
) == 10 && strncmp("scrollbars", my_pair
.what
, 10) == 0) {
2125 toggle_scrollbars(boolval
);
2126 } else if (strlen(my_pair
.what
) == 9 && strncmp("statusbar", my_pair
.what
, 9) == 0) {
2127 gtk_widget_set_visible(GTK_WIDGET(statusbar
), boolval
);
2128 } else if (strlen(my_pair
.what
) == 8 && strncmp("inputbox", my_pair
.what
, 8) == 0) {
2129 gtk_widget_set_visible(inputbox
, boolval
);
2130 } else if (strlen(my_pair
.what
) == 11 && strncmp("escapeinput", my_pair
.what
, 11) == 0) {
2131 escape_input_on_load
= boolval
;
2134 /* SSL certificate checking */
2135 if (strlen(my_pair
.what
) == 9 && strncmp("strictssl", my_pair
.what
, 9) == 0) {
2138 g_object_set(G_OBJECT(session
), "ssl-strict", TRUE
, NULL
);
2141 g_object_set(G_OBJECT(session
), "ssl-strict", FALSE
, NULL
);
2144 if (strlen(my_pair
.what
) == 8 && strncmp("cabundle", my_pair
.what
, 8) == 0) {
2145 g_object_set(G_OBJECT(session
), SOUP_SESSION_SSL_CA_FILE
, ca_bundle
, NULL
);
2147 if (strlen(my_pair
.what
) == 10 && strncmp("windowsize", my_pair
.what
, 10) == 0) {
2148 set_default_winsize(my_pair
.value
);
2152 if (browsersettings
[i
].reload
)
2153 webkit_web_view_reload(webview
);
2161 process_line(char *line
) {
2162 char *c
= line
, *command_hist
;
2164 size_t len
, length
= strlen(line
);
2165 gboolean found
= FALSE
, success
= FALSE
;
2170 /* Ignore blank lines. */
2174 command_hist
= g_strdup(c
);
2175 for (i
= 0; i
< LENGTH(commands
); i
++) {
2176 if (commands
[i
].cmd
== NULL
)
2178 len
= strlen(commands
[i
].cmd
);
2179 if (length
>= len
&& !strncmp(c
, commands
[i
].cmd
, len
) && (c
[len
] == ' ' || !c
[len
])) {
2181 a
.i
= commands
[i
].arg
.i
;
2182 a
.s
= g_strdup(length
> len
+ 1 ? &c
[len
+ 1] : commands
[i
].arg
.s
);
2183 success
= commands
[i
].func(&a
);
2189 save_command_history(command_hist
);
2190 g_free(command_hist
);
2193 echo_message(Error
, "Not a browser command: %s", c
);
2194 } else if (!success
) {
2195 if (error_msg
!= NULL
) {
2196 echo_message(Error
, error_msg
);
2200 echo_message(Error
, "Unknown error. Please file a bug report!");
2207 search_tag(const Arg
* a
) {
2209 const char *filename
;
2210 const char *tag
= a
->s
;
2211 char s
[BUFFERSIZE
], foundtag
[40], url
[BUFFERSIZE
];
2215 /* The user must give us something to load up. */
2216 set_error("Bookmark tag required with this option.");
2220 if (strlen(tag
) > MAXTAGSIZE
) {
2221 set_error("Tag too long.");
2225 filename
= g_strdup_printf(BOOKMARKS_STORAGE_FILENAME
);
2226 f
= fopen(filename
, "r");
2227 g_free((gpointer
*)filename
);
2229 set_error("Couldn't open bookmarks file.");
2232 while (fgets(s
, BUFFERSIZE
-1, f
)) {
2235 while (isspace(s
[t
]))
2237 if (s
[t
] != ']') continue;
2250 foundtag
[i
++] = s
[k
++];
2252 /* foundtag now contains the tag */
2253 if (strlen(foundtag
) < MAXTAGSIZE
&& strcmp(tag
, foundtag
) == 0) {
2255 while (isspace(s
[i
])) i
++;
2257 while (s
[i
] && !isspace(s
[i
])) url
[k
++] = s
[i
++];
2259 Arg x
= { .i
= TargetNew
, .s
= url
};
2273 toggle_proxy(gboolean onoff
) {
2275 char *filename
, *new;
2277 if (onoff
== FALSE
) {
2278 g_object_set(session
, "proxy-uri", NULL
, NULL
);
2280 filename
= (char *)g_getenv("http_proxy");
2282 /* Fallthrough to checking HTTP_PROXY as well, since this can also be
2285 if (filename
== NULL
)
2286 filename
= (char *)g_getenv("HTTP_PROXY");
2288 if (filename
!= NULL
&& 0 < strlen(filename
)) {
2289 new = g_strrstr(filename
, "http://") ? g_strdup(filename
) : g_strdup_printf("http://%s", filename
);
2290 proxy_uri
= soup_uri_new(new);
2292 g_object_set(session
, "proxy-uri", proxy_uri
, NULL
);
2294 soup_uri_free(proxy_uri
);
2301 toggle_scrollbars(gboolean onoff
) {
2302 if (onoff
== TRUE
) {
2303 adjust_h
= gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(viewport
));
2304 adjust_v
= gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(viewport
));
2305 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(viewport
), GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
2307 adjust_v
= gtk_range_get_adjustment(GTK_RANGE(scroll_v
));
2308 adjust_h
= gtk_range_get_adjustment(GTK_RANGE(scroll_h
));
2309 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(viewport
), GTK_POLICY_NEVER
, GTK_POLICY_NEVER
);
2311 gtk_widget_set_scroll_adjustments (GTK_WIDGET(webview
), adjust_h
, adjust_v
);
2316 void set_default_winsize(const char * const size
) {
2318 int x
= 640, y
= 480;
2320 x
= strtol(size
, &p
, 10);
2321 if (errno
== ERANGE
|| x
<= 0) {
2326 if (p
== size
|| strlen(size
) == p
- size
)
2329 y
= strtol(p
+ 1, NULL
, 10);
2330 if (errno
== ERANGE
|| y
<= 0)
2334 gtk_window_resize(GTK_WINDOW(window
), x
, y
);
2338 update_url(const char *uri
) {
2339 gboolean ssl
= g_str_has_prefix(uri
, "https://");
2341 WebKitWebFrame
*frame
;
2342 WebKitWebDataSource
*src
;
2343 WebKitNetworkRequest
*request
;
2346 char *sslactivecolor
;
2348 #ifdef ENABLE_HISTORY_INDICATOR
2349 char before
[] = " [";
2351 gboolean back
= webkit_web_view_can_go_back(webview
);
2352 gboolean fwd
= webkit_web_view_can_go_forward(webview
);
2355 before
[0] = after
[0] = '\0';
2357 markup
= g_markup_printf_escaped(
2358 #ifdef ENABLE_HISTORY_INDICATOR
2359 "<span font=\"%s\">%s%s%s%s%s</span>", statusfont
, uri
,
2360 before
, back
? "+" : "", fwd
? "-" : "", after
2362 "<span font=\"%s\">%s</span>", statusfont
, uri
2365 gtk_label_set_markup(GTK_LABEL(status_url
), markup
);
2368 frame
= webkit_web_view_get_main_frame(webview
);
2369 src
= webkit_web_frame_get_data_source(frame
);
2370 request
= webkit_web_data_source_get_request(src
);
2371 msg
= webkit_network_request_get_message(request
);
2372 ssl_ok
= soup_message_get_flags(msg
) & SOUP_MESSAGE_CERTIFICATE_TRUSTED
;
2374 sslactivecolor
= sslbgcolor
;
2376 sslactivecolor
= sslinvalidbgcolor
;
2378 gdk_color_parse(ssl
? sslactivecolor
: statusbgcolor
, &color
);
2379 gtk_widget_modify_bg(eventbox
, GTK_STATE_NORMAL
, &color
);
2380 gdk_color_parse(ssl
? sslcolor
: statuscolor
, &color
);
2381 gtk_widget_modify_fg(GTK_WIDGET(status_url
), GTK_STATE_NORMAL
, &color
);
2382 gtk_widget_modify_fg(GTK_WIDGET(status_state
), GTK_STATE_NORMAL
, &color
);
2388 int download_count
= g_list_length(activeDownloads
);
2389 GString
*status
= g_string_new("");
2391 /* construct the status line */
2393 /* count, modkey and input buffer */
2394 g_string_append_printf(status
, "%.0d", count
);
2395 if (current_modkey
) g_string_append_c(status
, current_modkey
);
2397 /* the number of active downloads */
2398 if (activeDownloads
) {
2399 g_string_append_printf(status
, " %d active %s", download_count
,
2400 (download_count
== 1) ? "download" : "downloads");
2403 #ifdef ENABLE_WGET_PROGRESS_BAR
2404 /* the progressbar */
2407 char progressbar
[progressbartick
+ 1];
2409 if (activeDownloads
) {
2413 for (ptr
= activeDownloads
; ptr
; ptr
= g_list_next(ptr
)) {
2414 progress
+= 100 * webkit_download_get_progress(ptr
->data
);
2417 progress
/= download_count
;
2419 } else if (webkit_web_view_get_load_status(webview
) != WEBKIT_LOAD_FINISHED
2420 && webkit_web_view_get_load_status(webview
) != WEBKIT_LOAD_FAILED
) {
2422 progress
= webkit_web_view_get_progress(webview
) * 100;
2425 if (progress
>= 0) {
2426 ascii_bar(progressbartick
, progress
* progressbartick
/ 100, progressbar
);
2427 g_string_append_printf(status
, " %c%s%c",
2428 progressborderleft
, progressbar
, progressborderright
);
2433 /* and the current scroll position */
2435 int max
= gtk_adjustment_get_upper(adjust_v
) - gtk_adjustment_get_page_size(adjust_v
);
2436 int val
= (int)(gtk_adjustment_get_value(adjust_v
) / max
* 100);
2439 g_string_append(status
, " All");
2441 g_string_append(status
, " Top");
2442 else if (val
== 100)
2443 g_string_append(status
, " Bot");
2445 g_string_append_printf(status
, " %d%%", val
);
2449 markup
= g_markup_printf_escaped("<span font=\"%s\">%s</span>", statusfont
, status
->str
);
2450 gtk_label_set_markup(GTK_LABEL(status_state
), markup
);
2453 g_string_free(status
, TRUE
);
2459 modkeys
= calloc(LENGTH(keys
) + 1, sizeof(char));
2460 char *ptr
= modkeys
;
2462 for (i
= 0; i
< LENGTH(keys
); i
++)
2463 if (keys
[i
].modkey
&& !strchr(modkeys
, keys
[i
].modkey
))
2464 *(ptr
++) = keys
[i
].modkey
;
2465 modkeys
= realloc(modkeys
, &ptr
[0] - &modkeys
[0] + 1);
2470 scroll_h
= GTK_SCROLLBAR(gtk_hscrollbar_new(NULL
));
2471 scroll_v
= GTK_SCROLLBAR(gtk_vscrollbar_new(NULL
));
2472 adjust_h
= gtk_range_get_adjustment(GTK_RANGE(scroll_h
));
2473 adjust_v
= gtk_range_get_adjustment(GTK_RANGE(scroll_v
));
2475 window
= GTK_WINDOW(gtk_plug_new(embed
));
2477 window
= GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL
));
2478 gtk_window_set_wmclass(GTK_WINDOW(window
), "vimprobable2", "Vimprobable2");
2480 gtk_window_set_default_size(GTK_WINDOW(window
), 640, 480);
2481 box
= GTK_BOX(gtk_vbox_new(FALSE
, 0));
2482 inputbox
= gtk_entry_new();
2483 webview
= (WebKitWebView
*)webkit_web_view_new();
2484 statusbar
= GTK_BOX(gtk_hbox_new(FALSE
, 0));
2485 eventbox
= gtk_event_box_new();
2486 status_url
= gtk_label_new(NULL
);
2487 status_state
= gtk_label_new(NULL
);
2489 PangoFontDescription
*font
;
2490 GdkGeometry hints
= { 1, 1 };
2491 inspector
= webkit_web_view_get_inspector(WEBKIT_WEB_VIEW(webview
));
2493 clipboards
[0] = gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2494 clipboards
[1] = gtk_clipboard_get(GDK_NONE
);
2496 gdk_color_parse(statusbgcolor
, &bg
);
2497 gtk_widget_modify_bg(eventbox
, GTK_STATE_NORMAL
, &bg
);
2498 gtk_widget_set_name(GTK_WIDGET(window
), "Vimprobable2");
2499 gtk_window_set_geometry_hints(window
, NULL
, &hints
, GDK_HINT_MIN_SIZE
);
2501 keymap
= gdk_keymap_get_default();
2503 #ifdef DISABLE_SCROLLBAR
2504 viewport
= gtk_scrolled_window_new(NULL
, NULL
);
2505 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(viewport
), GTK_POLICY_NEVER
, GTK_POLICY_NEVER
);
2507 /* Ensure we still see scrollbars. */
2508 GtkWidget
*viewport
= gtk_scrolled_window_new(adjust_h
, adjust_v
);
2512 gtk_container_add(GTK_CONTAINER(viewport
), GTK_WIDGET(webview
));
2514 /* Ensure we set the scroll adjustments now, so that if we're not drawing
2515 * titlebars, we can still scroll.
2517 gtk_widget_set_scroll_adjustments(GTK_WIDGET(webview
), adjust_h
, adjust_v
);
2519 font
= pango_font_description_from_string(urlboxfont
[0]);
2520 gtk_widget_modify_font(GTK_WIDGET(inputbox
), font
);
2521 pango_font_description_free(font
);
2522 gtk_entry_set_inner_border(GTK_ENTRY(inputbox
), NULL
);
2523 gtk_misc_set_alignment(GTK_MISC(status_url
), 0.0, 0.0);
2524 gtk_misc_set_alignment(GTK_MISC(status_state
), 1.0, 0.0);
2525 gtk_box_pack_start(statusbar
, status_url
, TRUE
, TRUE
, 2);
2526 gtk_box_pack_start(statusbar
, status_state
, FALSE
, FALSE
, 2);
2527 gtk_container_add(GTK_CONTAINER(eventbox
), GTK_WIDGET(statusbar
));
2528 gtk_box_pack_start(box
, viewport
, TRUE
, TRUE
, 0);
2529 gtk_box_pack_start(box
, eventbox
, FALSE
, FALSE
, 0);
2530 gtk_entry_set_has_frame(GTK_ENTRY(inputbox
), FALSE
);
2531 gtk_box_pack_end(box
, inputbox
, FALSE
, FALSE
, 0);
2532 gtk_container_add(GTK_CONTAINER(window
), GTK_WIDGET(box
));
2533 gtk_widget_grab_focus(GTK_WIDGET(webview
));
2534 gtk_widget_show_all(GTK_WIDGET(window
));
2535 set_widget_font_and_color(inputbox
, urlboxfont
[0], urlboxbgcolor
[0], urlboxcolor
[0]);
2536 g_object_set(gtk_widget_get_settings(inputbox
), "gtk-entry-select-on-focus", FALSE
, NULL
);
2541 WebKitWebSettings
*settings
= (WebKitWebSettings
*)webkit_web_settings_new();
2542 char *filename
, *file_url
;
2544 session
= webkit_get_default_session();
2545 g_object_set(G_OBJECT(session
), "ssl-ca-file", ca_bundle
, NULL
);
2546 g_object_set(G_OBJECT(session
), "ssl-strict", strict_ssl
, NULL
);
2547 g_object_set(G_OBJECT(settings
), "default-font-size", DEFAULT_FONT_SIZE
, NULL
);
2548 g_object_set(G_OBJECT(settings
), "enable-scripts", enablePlugins
, NULL
);
2549 g_object_set(G_OBJECT(settings
), "enable-plugins", enablePlugins
, NULL
);
2550 g_object_set(G_OBJECT(settings
), "enable-java-applet", enableJava
, NULL
);
2551 g_object_set(G_OBJECT(settings
), "enable-page-cache", enablePagecache
, NULL
);
2552 filename
= g_strdup_printf(USER_STYLESHEET
);
2553 file_url
= g_strdup_printf("file://%s", filename
);
2554 g_object_set(G_OBJECT(settings
), "user-stylesheet-uri", file_url
, NULL
);
2557 g_object_set(G_OBJECT(settings
), "user-agent", useragent
, NULL
);
2558 g_object_get(G_OBJECT(settings
), "zoom-step", &zoomstep
, NULL
);
2559 webkit_web_view_set_settings(webview
, settings
);
2562 toggle_proxy(use_proxy
);
2567 WebKitWebFrame
*frame
= webkit_web_view_get_main_frame(webview
);
2568 #ifdef ENABLE_COOKIE_SUPPORT
2570 g_signal_connect_after(G_OBJECT(session
), "request-started", G_CALLBACK(new_generic_request
), NULL
);
2572 /* Accept-language header */
2573 g_object_set(G_OBJECT(session
), "accept-language", acceptlanguage
, NULL
);
2575 g_object_connect(G_OBJECT(window
),
2576 "signal::destroy", G_CALLBACK(window_destroyed_cb
), NULL
,
2579 g_signal_connect(G_OBJECT(frame
),
2580 "scrollbars-policy-changed", G_CALLBACK(blank_cb
), NULL
);
2582 g_object_connect(G_OBJECT(webview
),
2583 "signal::title-changed", G_CALLBACK(webview_title_changed_cb
), NULL
,
2584 "signal::load-progress-changed", G_CALLBACK(webview_progress_changed_cb
), NULL
,
2585 "signal::load-committed", G_CALLBACK(webview_load_committed_cb
), NULL
,
2586 "signal::load-finished", G_CALLBACK(webview_load_finished_cb
), NULL
,
2587 "signal::navigation-policy-decision-requested", G_CALLBACK(webview_navigation_cb
), NULL
,
2588 "signal::new-window-policy-decision-requested", G_CALLBACK(webview_new_window_cb
), NULL
,
2589 "signal::mime-type-policy-decision-requested", G_CALLBACK(webview_mimetype_cb
), NULL
,
2590 "signal::download-requested", G_CALLBACK(webview_download_cb
), NULL
,
2591 "signal::key-press-event", G_CALLBACK(webview_keypress_cb
), NULL
,
2592 "signal::hovering-over-link", G_CALLBACK(webview_hoverlink_cb
), NULL
,
2593 "signal::console-message", G_CALLBACK(webview_console_cb
), NULL
,
2594 "signal::create-web-view", G_CALLBACK(webview_open_in_new_window_cb
), NULL
,
2595 "signal::event", G_CALLBACK(notify_event_cb
), NULL
,
2597 /* webview adjustment */
2598 g_object_connect(G_OBJECT(adjust_v
),
2599 "signal::value-changed", G_CALLBACK(webview_scroll_cb
), NULL
,
2602 g_object_connect(G_OBJECT(inputbox
),
2603 "signal::activate", G_CALLBACK(inputbox_activate_cb
), NULL
,
2604 "signal::key-press-event", G_CALLBACK(inputbox_keypress_cb
), NULL
,
2605 "signal::key-release-event", G_CALLBACK(inputbox_keyrelease_cb
), NULL
,
2606 #ifdef ENABLE_INCREMENTAL_SEARCH
2607 "signal::changed", G_CALLBACK(inputbox_changed_cb
), NULL
,
2611 g_signal_connect(G_OBJECT(inspector
),
2612 "inspect-web-view", G_CALLBACK(inspector_inspect_web_view_cb
), NULL
);
2615 #ifdef ENABLE_COOKIE_SUPPORT
2619 if (file_cookie_jar
)
2620 g_object_unref(file_cookie_jar
);
2622 if (session_cookie_jar
)
2623 g_object_unref(session_cookie_jar
);
2625 session_cookie_jar
= soup_cookie_jar_new();
2627 cookie_store
= g_strdup_printf(COOKIES_STORAGE_FILENAME
);
2631 g_signal_connect(G_OBJECT(file_cookie_jar
), "changed",
2632 G_CALLBACK(update_cookie_jar
), NULL
);
2637 /* TA: XXX - we should be using this callback for any header-requests we
2638 * receive (hence the name "new_generic_request" -- but for now, its use
2639 * is limited to handling cookies.
2642 new_generic_request(SoupSession
*session
, SoupMessage
*soup_msg
, gpointer unused
)
2644 SoupMessageHeaders
*soup_msg_h
;
2648 soup_msg_h
= soup_msg
->request_headers
;
2649 soup_message_headers_remove(soup_msg_h
, "Cookie");
2650 uri
= soup_message_get_uri(soup_msg
);
2651 if ((cookie_str
= get_cookies(uri
))) {
2652 soup_message_headers_append(soup_msg_h
, "Cookie", cookie_str
);
2656 g_signal_connect_after(G_OBJECT(soup_msg
), "got-headers", G_CALLBACK(handle_cookie_request
), NULL
);
2662 get_cookies(SoupURI
*soup_uri
) {
2665 cookie_str
= soup_cookie_jar_get_cookies(file_cookie_jar
, soup_uri
, TRUE
);
2671 handle_cookie_request(SoupMessage
*soup_msg
, gpointer unused
)
2673 GSList
*resp_cookie
= NULL
, *cookie_list
;
2676 cookie_list
= soup_cookies_from_response(soup_msg
);
2677 for(resp_cookie
= cookie_list
; resp_cookie
; resp_cookie
= g_slist_next(resp_cookie
))
2679 SoupDate
*soup_date
;
2680 cookie
= soup_cookie_copy((SoupCookie
*)resp_cookie
->data
);
2682 if (cookie_timeout
&& cookie
->expires
== NULL
) {
2683 soup_date
= soup_date_new_from_time_t(time(NULL
) + cookie_timeout
* 10);
2684 soup_cookie_set_expires(cookie
, soup_date
);
2685 soup_date_free(soup_date
);
2687 soup_cookie_jar_add_cookie(file_cookie_jar
, cookie
);
2690 soup_cookies_free(cookie_list
);
2696 update_cookie_jar(SoupCookieJar
*jar
, SoupCookie
*old
, SoupCookie
*new)
2699 /* Nothing to do. */
2704 copy
= soup_cookie_copy(new);
2706 soup_cookie_jar_add_cookie(session_cookie_jar
, copy
);
2712 load_all_cookies(void)
2714 GSList
*cookie_list
;
2715 file_cookie_jar
= soup_cookie_jar_text_new(cookie_store
, COOKIES_STORAGE_READONLY
);
2717 /* Put them back in the session store. */
2718 GSList
*cookies_from_file
= soup_cookie_jar_all_cookies(file_cookie_jar
);
2719 cookie_list
= cookies_from_file
;
2721 for (; cookies_from_file
;
2722 cookies_from_file
= cookies_from_file
->next
)
2724 soup_cookie_jar_add_cookie(session_cookie_jar
, cookies_from_file
->data
);
2727 soup_cookies_free(cookies_from_file
);
2728 g_slist_free(cookie_list
);
2737 /* Free up any nasty globals before exiting. */
2738 #ifdef ENABLE_COOKIE_SUPPORT
2740 g_free(cookie_store
);
2746 main(int argc
, char *argv
[]) {
2748 static char url
[256] = "";
2749 static gboolean ver
= false;
2750 static gboolean configfile_exists
= FALSE
;
2751 static const char *cfile
= NULL
;
2752 static GOptionEntry opts
[] = {
2753 { "version", 'v', 0, G_OPTION_ARG_NONE
, &ver
, "print version", NULL
},
2754 { "embed", 'e', 0, G_OPTION_ARG_STRING
, &winid
, "embedded", NULL
},
2755 { "configfile", 'c', 0, G_OPTION_ARG_STRING
, &cfile
, "config file", NULL
},
2761 /* command line argument: version */
2762 if (!gtk_init_with_args(&argc
, &argv
, "[<uri>]", opts
, NULL
, &err
)) {
2763 g_printerr("can't init gtk: %s\n", err
->message
);
2765 return EXIT_FAILURE
;
2769 printf("%s\n", INTERNAL_VERSION
);
2770 return EXIT_SUCCESS
;
2773 if (getenv("TMPDIR")) {
2774 strncpy(temp_dir
, getenv("TMPDIR"), MAX_SETTING_SIZE
);
2775 temp_dir
[MAX_SETTING_SIZE
-1] = 0;
2778 if( getenv("XDG_CONFIG_HOME") )
2779 config_base
= g_strdup_printf("%s", getenv("XDG_CONFIG_HOME"));
2781 config_base
= g_strdup_printf("%s/.config/", getenv("HOME"));
2784 configfile
= g_strdup(cfile
);
2786 configfile
= g_strdup_printf(RCFILE
);
2788 if (!g_thread_supported())
2789 g_thread_init(NULL
);
2792 if (strncmp(winid
, "0x", 2) == 0) {
2793 embed
= strtol(winid
, NULL
, 16);
2795 embed
= atoi(winid
);
2802 #ifdef ENABLE_COOKIE_SUPPORT
2806 make_searchengines_list(searchengines
, LENGTH(searchengines
));
2807 make_uri_handlers_list(uri_handlers
, LENGTH(uri_handlers
));
2809 /* Check if the specified file exists. */
2810 /* And only warn the user, if they explicitly asked for a config on the
2813 if (!(access(configfile
, F_OK
) == 0) && cfile
) {
2814 echo_message(Info
, "Config file '%s' doesn't exist", cfile
);
2815 } else if ((access(configfile
, F_OK
) == 0))
2816 configfile_exists
= true;
2818 /* read config file */
2819 /* But only report errors if we failed, and the file existed. */
2820 if ((SUCCESS
!= read_rcfile(configfile
)) && configfile_exists
) {
2821 echo_message(Error
, "Error in config file '%s'", configfile
);
2825 /* command line argument: URL */
2827 strncpy(url
, argv
[argc
- 1], 255);
2829 strncpy(url
, startpage
, 255);
2832 a
.i
= TargetCurrent
;
2839 return EXIT_SUCCESS
;