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 clear_focus(void);
91 static void update_url(const char *uri
);
92 static void setup_modkeys(void);
93 static void setup_gui(void);
94 static void setup_settings(void);
95 static void setup_signals(void);
96 static void ascii_bar(int total
, int state
, char *string
);
97 static gchar
*jsapi_ref_to_string(JSContextRef context
, JSValueRef ref
);
98 static void jsapi_evaluate_script(const gchar
*script
, gchar
**value
, gchar
**message
);
99 static void download_progress(WebKitDownload
*d
, GParamSpec
*pspec
);
100 static void set_widget_font_and_color(GtkWidget
*widget
, const char *font_str
,
101 const char *bg_color_str
, const char *fg_color_str
);
102 static void scripts_run_user_file(void);
104 static gboolean
history(void);
105 static gboolean
process_set_line(char *line
);
106 void save_command_history(char *line
);
107 void toggle_proxy(gboolean onoff
);
108 void toggle_scrollbars(gboolean onoff
);
109 void set_default_winsize(const char * const size
);
111 gboolean
process_keypress(GdkEventKey
*event
);
112 void fill_suggline(char * suggline
, const char * command
, const char *fill_with
);
113 GtkWidget
* fill_eventbox(const char * completion_line
);
114 static void mop_up(void);
119 static GtkWindow
*window
;
120 static GtkWidget
*viewport
;
122 static GtkScrollbar
*scroll_h
;
123 static GtkScrollbar
*scroll_v
;
124 static GtkAdjustment
*adjust_h
;
125 static GtkAdjustment
*adjust_v
;
126 static GtkWidget
*inputbox
;
127 static GtkWidget
*eventbox
;
128 static GtkBox
*statusbar
;
129 static GtkWidget
*status_url
;
130 static GtkWidget
*status_state
;
131 static WebKitWebView
*webview
;
132 static SoupSession
*session
;
133 static GtkClipboard
*clipboards
[2];
134 static GdkKeymap
*keymap
;
137 static unsigned int mode
= ModeNormal
;
138 static unsigned int count
= 0;
139 static float zoomstep
;
141 static char current_modkey
;
142 static char *search_handle
;
143 static gboolean search_direction
;
144 static gboolean echo_active
= TRUE
;
145 static WebKitWebInspector
*inspector
;
147 static GdkNativeWindow embed
= 0;
148 static char *configfile
= NULL
;
149 static char *winid
= NULL
;
151 static char rememberedURI
[1024] = "";
152 static char followTarget
[8] = "";
153 char *error_msg
= NULL
;
154 char *config_base
= NULL
;
155 static gboolean manual_focus
= FALSE
;
157 GList
*activeDownloads
;
162 GList
*commandhistory
= NULL
;
163 int commandpointer
= 0;
165 KeyList
*keylistroot
= NULL
;
167 /* Cookie support. */
168 #ifdef ENABLE_COOKIE_SUPPORT
169 static SoupCookieJar
*session_cookie_jar
= NULL
;
170 static SoupCookieJar
*file_cookie_jar
= NULL
;
171 static time_t cookie_timeout
= 4800;
172 static char *cookie_store
;
173 static void setup_cookies(void);
174 static char *get_cookies(SoupURI
*soup_uri
);
175 static void load_all_cookies(void);
176 static void new_generic_request(SoupSession
*soup_ses
, SoupMessage
*soup_msg
, gpointer unused
);
177 static void update_cookie_jar(SoupCookieJar
*jar
, SoupCookie
*old
, SoupCookie
*new);
178 static void handle_cookie_request(SoupMessage
*soup_msg
, gpointer unused
);
182 window_destroyed_cb(GtkWidget
*window
, gpointer func_data
) {
187 webview_title_changed_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, char *title
, gpointer user_data
) {
188 gtk_window_set_title(window
, title
);
192 webview_progress_changed_cb(WebKitWebView
*webview
, int progress
, gpointer user_data
) {
193 #ifdef ENABLE_GTK_PROGRESS_BAR
194 gtk_entry_set_progress_fraction(GTK_ENTRY(inputbox
), progress
== 100 ? 0 : (double)progress
/100);
199 #ifdef ENABLE_WGET_PROGRESS_BAR
201 ascii_bar(int total
, int state
, char *string
) {
204 for (i
= 0; i
< state
; i
++)
205 string
[i
] = progressbartickchar
;
206 string
[i
++] = progressbarcurrent
;
207 for (; i
< total
; i
++)
208 string
[i
] = progressbarspacer
;
214 webview_load_committed_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, gpointer user_data
) {
215 Arg a
= { .i
= Silent
, .s
= g_strdup(JS_SETUP_HINTS
) };
216 const char *uri
= webkit_web_view_get_uri(webview
);
221 scripts_run_user_file();
223 if (mode
== ModeInsert
|| mode
== ModeHints
) {
224 Arg a
= { .i
= ModeNormal
};
227 manual_focus
= FALSE
;
231 webview_load_finished_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, gpointer user_data
) {
232 WebKitWebSettings
*settings
= webkit_web_view_get_settings(webview
);
235 g_object_get(settings
, "enable-scripts", &scripts
, NULL
);
236 if (escape_input_on_load
&& scripts
&& !manual_focus
&& !gtk_widget_is_focus(inputbox
)) {
239 if (HISTORY_MAX_ENTRIES
> 0)
245 webview_open_js_window_cb(WebKitWebView
* temp_view
, GParamSpec param_spec
) {
246 /* retrieve the URI of the temporary webview */
247 Arg a
= { .i
= TargetNew
, .s
= (char*)webkit_web_view_get_uri(temp_view
) };
249 webkit_web_view_stop_loading(temp_view
);
250 gtk_widget_destroy(GTK_WIDGET(temp_view
));
251 /* open the requested window */
255 static WebKitWebView
*
256 webview_open_in_new_window_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, gpointer user_data
) {
257 if (rememberedURI
!= NULL
&& strlen(rememberedURI
) > 0) {
258 if (strncmp(rememberedURI
, "javascript:", 11) != 0) {
259 Arg a
= { .i
= TargetNew
, .s
= rememberedURI
};
264 /* create a temporary webview to execute the script in */
265 WebKitWebView
*temp_view
= WEBKIT_WEB_VIEW(webkit_web_view_new());
266 /* wait until the new webview receives its new URI */
267 g_object_connect(temp_view
, "signal::notify::uri", G_CALLBACK(webview_open_js_window_cb
), NULL
, NULL
);
272 webview_new_window_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, WebKitNetworkRequest
*request
,
273 WebKitWebNavigationAction
*action
, WebKitWebPolicyDecision
*decision
, gpointer user_data
) {
274 Arg a
= { .i
= TargetNew
, .s
= (char*)webkit_network_request_get_uri(request
) };
276 webkit_web_policy_decision_ignore(decision
);
281 webview_mimetype_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, WebKitNetworkRequest
*request
,
282 char *mime_type
, WebKitWebPolicyDecision
*decision
, gpointer user_data
) {
283 if (webkit_web_view_can_show_mime_type(webview
, mime_type
) == FALSE
) {
284 webkit_web_policy_decision_download(decision
);
291 static WebKitWebView
*
292 inspector_inspect_web_view_cb(gpointer inspector
, WebKitWebView
* web_view
) {
293 gchar
* inspector_title
;
294 GtkWidget
* inspector_window
;
295 GtkWidget
* inspector_view
;
297 /* just enough code to show the inspector - no signal handling etc. */
298 inspector_title
= g_strdup_printf("Inspect page - %s - Vimprobable2", webkit_web_view_get_uri(web_view
));
300 inspector_window
= gtk_plug_new(embed
);
302 inspector_window
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
303 gtk_window_set_wmclass(window
, "vimprobable2", "Vimprobable2");
305 gtk_window_set_title(GTK_WINDOW(inspector_window
), inspector_title
);
306 g_free(inspector_title
);
307 inspector_view
= webkit_web_view_new();
308 gtk_container_add(GTK_CONTAINER(inspector_window
), inspector_view
);
309 gtk_widget_show_all(inspector_window
);
310 return WEBKIT_WEB_VIEW(inspector_view
);
314 webview_download_cb(WebKitWebView
*webview
, WebKitDownload
*download
, gpointer user_data
) {
315 const gchar
*filename
;
318 WebKitDownloadStatus status
;
320 filename
= webkit_download_get_suggested_filename(download
);
321 if (filename
== NULL
|| strlen(filename
) == 0) {
322 filename
= "vimprobable_download";
324 path
= g_build_filename(g_strdup_printf(DOWNLOADS_PATH
), filename
, NULL
);
325 uri
= g_strconcat("file://", path
, NULL
);
326 webkit_download_set_destination_uri(download
, uri
);
328 size
= (uint32_t)webkit_download_get_total_size(download
);
330 echo_message(Info
, "Download %s started (expected size: %u bytes)...", filename
, size
);
332 echo_message(Info
, "Download %s started (unknown size)...", filename
);
333 activeDownloads
= g_list_prepend(activeDownloads
, download
);
334 g_signal_connect(download
, "notify::progress", G_CALLBACK(download_progress
), NULL
);
335 g_signal_connect(download
, "notify::status", G_CALLBACK(download_progress
), NULL
);
336 status
= webkit_download_get_status(download
);
337 if (status
== WEBKIT_DOWNLOAD_STATUS_CREATED
)
338 webkit_download_start(download
);
349 download_progress(WebKitDownload
*d
, GParamSpec
*pspec
) {
350 WebKitDownloadStatus status
= webkit_download_get_status(d
);
352 if (status
!= WEBKIT_DOWNLOAD_STATUS_STARTED
&& status
!= WEBKIT_DOWNLOAD_STATUS_CREATED
) {
353 if (status
!= WEBKIT_DOWNLOAD_STATUS_FINISHED
) {
354 echo_message(Error
, "Error while downloading %s", webkit_download_get_suggested_filename(d
));
356 echo_message(Info
, "Download %s finished", webkit_download_get_suggested_filename(d
));
358 activeDownloads
= g_list_remove(activeDownloads
, d
);
365 process_keypress(GdkEventKey
*event
) {
368 GdkModifierType irrelevant
;
370 /* Get a mask of modifiers that shouldn't be considered for this event.
371 * E.g.: It shouldn't matter whether ';' is shifted or not. */
372 gdk_keymap_translate_keyboard_state(keymap
, event
->hardware_keycode
,
373 event
->state
, event
->group
, &keyval
, NULL
, NULL
, &irrelevant
);
375 current
= keylistroot
;
377 while (current
!= NULL
) {
378 if (current
->Element
.mask
== (CLEAN(event
->state
) & ~irrelevant
)
379 && (current
->Element
.modkey
== current_modkey
380 || (!current
->Element
.modkey
&& !current_modkey
)
381 || current
->Element
.modkey
== GDK_VoidSymbol
) /* wildcard */
382 && current
->Element
.key
== keyval
383 && current
->Element
.func
)
384 if (current
->Element
.func(¤t
->Element
.arg
)) {
385 current_modkey
= count
= 0;
389 current
= current
->next
;
395 webview_keypress_cb(WebKitWebView
*webview
, GdkEventKey
*event
) {
396 Arg a
= { .i
= ModeNormal
, .s
= NULL
};
398 GdkModifierType irrelevant
;
400 /* Get a mask of modifiers that shouldn't be considered for this event.
401 * E.g.: It shouldn't matter whether ';' is shifted or not. */
402 gdk_keymap_translate_keyboard_state(keymap
, event
->hardware_keycode
,
403 event
->state
, event
->group
, &keyval
, NULL
, NULL
, &irrelevant
);
407 if ((CLEAN(event
->state
) & ~irrelevant
) == 0) {
408 if (IS_ESCAPE(event
)) {
409 echo_message(Info
, "");
411 } else if (current_modkey
== 0 && ((event
->keyval
>= GDK_1
&& event
->keyval
<= GDK_9
)
412 || (event
->keyval
== GDK_0
&& count
))) {
413 count
= (count
? count
* 10 : 0) + (event
->keyval
- GDK_0
);
416 } else if (strchr(modkeys
, event
->keyval
) && current_modkey
!= event
->keyval
) {
417 current_modkey
= event
->keyval
;
423 if (process_keypress(event
) == TRUE
) return TRUE
;
427 if (IS_ESCAPE(event
)) {
429 a
.s
= g_strdup("hints.clearFocus();");
434 } else if (CLEAN(event
->state
) & GDK_CONTROL_MASK
) {
435 /* keybindings of non-printable characters */
436 if (process_keypress(event
) == TRUE
) return TRUE
;
438 case ModePassThrough
:
439 if (IS_ESCAPE(event
)) {
440 echo_message(Info
, "");
446 echo_message(Info
, "");
454 set_widget_font_and_color(GtkWidget
*widget
, const char *font_str
, const char *bg_color_str
,
455 const char *fg_color_str
) {
458 PangoFontDescription
*font
;
460 font
= pango_font_description_from_string(font_str
);
461 gtk_widget_modify_font(widget
, font
);
462 pango_font_description_free(font
);
465 gdk_color_parse(fg_color_str
, &fg_color
);
467 gdk_color_parse(bg_color_str
, &bg_color
);
469 gtk_widget_modify_text(widget
, GTK_STATE_NORMAL
, fg_color_str
? &fg_color
: NULL
);
470 gtk_widget_modify_base(widget
, GTK_STATE_NORMAL
, bg_color_str
? &bg_color
: NULL
);
476 webview_hoverlink_cb(WebKitWebView
*webview
, char *title
, char *link
, gpointer data
) {
477 const char *uri
= webkit_web_view_get_uri(webview
);
480 memset(rememberedURI
, 0, 1024);
482 markup
= g_markup_printf_escaped("<span font=\"%s\">Link: %s</span>", statusfont
, link
);
483 gtk_label_set_markup(GTK_LABEL(status_url
), markup
);
484 strncpy(rememberedURI
, link
, 1024);
491 webview_console_cb(WebKitWebView
*webview
, char *message
, int line
, char *source
, gpointer user_data
) {
494 /* Don't change internal mode if the browser doesn't have focus to prevent inconsistent states */
495 if (gtk_window_has_toplevel_focus(window
)) {
496 if (!strcmp(message
, "hintmode_off") || !strcmp(message
, "insertmode_off")) {
499 } else if (!strcmp(message
, "insertmode_on")) {
508 inputbox_activate_cb(GtkEntry
*entry
, gpointer user_data
) {
510 guint16 length
= gtk_entry_get_text_length(entry
);
512 gboolean success
= FALSE
, forward
= FALSE
;
514 a
.i
= HideCompletion
;
518 text
= (char*)gtk_entry_get_text(entry
);
520 /* move focus from inputbox to print potential messages that could not be
521 * printed as long as the inputbox is focused */
522 gtk_widget_grab_focus(GTK_WIDGET(webview
));
524 if (length
> 1 && text
[0] == ':') {
525 success
= process_line((text
+ 1));
526 } else if (length
> 1 && ((forward
= text
[0] == '/') || text
[0] == '?')) {
527 webkit_web_view_unmark_text_matches(webview
);
528 #ifdef ENABLE_MATCH_HIGHLITING
529 webkit_web_view_mark_text_matches(webview
, &text
[1], FALSE
, 0);
530 webkit_web_view_set_highlight_text_matches(webview
, TRUE
);
533 #ifndef ENABLE_INCREMENTAL_SEARCH
535 a
.i
= searchoptions
| (forward
? DirectionForward
: DirectionBackwards
);
538 search_direction
= forward
;
539 search_handle
= g_strdup(&text
[1]);
541 } else if (text
[0] == '.' || text
[0] == ',' || text
[0] == ';') {
543 a
.s
= g_strdup_printf("hints.fire();");
550 gtk_entry_set_text(entry
, "");
551 gtk_widget_grab_focus(GTK_WIDGET(webview
));
555 inputbox_keypress_cb(GtkEntry
*entry
, GdkEventKey
*event
) {
559 if (mode
== ModeHints
) {
560 if (event
->keyval
== GDK_Tab
) {
562 a
.s
= g_strdup_printf("hints.focusNextHint();");
568 if (event
->keyval
== GDK_ISO_Left_Tab
) {
570 a
.s
= g_strdup_printf("hints.focusPreviousHint();");
576 if (event
->keyval
== GDK_Return
) {
578 a
.s
= g_strdup_printf("hints.fire();");
585 switch (event
->keyval
) {
586 case GDK_bracketleft
:
588 if (!IS_ESCAPE(event
)) break;
589 a
.i
= HideCompletion
;
601 return commandhistoryfetch(&a
);
605 return commandhistoryfetch(&a
);
607 case GDK_ISO_Left_Tab
:
613 if (mode
== ModeHints
) {
614 if ((CLEAN(event
->state
) & GDK_SHIFT_MASK
) &&
615 (CLEAN(event
->state
) & GDK_CONTROL_MASK
) &&
616 (event
->keyval
== GDK_BackSpace
)) {
619 a
.s
= g_strdup_printf("hints.updateHints(%d);", count
);
626 numval
= g_unichar_digit_value((gunichar
) gdk_keyval_to_unicode(event
->keyval
));
627 if ((numval
>= 1 && numval
<= 9) || (numval
== 0 && count
)) {
628 /* allow a zero as non-first number */
629 count
= (count
? count
* 10 : 0) + numval
;
631 a
.s
= g_strdup_printf("hints.updateHints(%d);", count
);
643 notify_event_cb(GtkWidget
*widget
, GdkEvent
*event
, gpointer user_data
) {
645 WebKitHitTestResult
*result
;
646 WebKitHitTestResultContext context
;
647 if (mode
== ModeNormal
&& event
->type
== GDK_BUTTON_RELEASE
) {
648 /* handle mouse click events */
649 for (i
= 0; i
< LENGTH(mouse
); i
++) {
650 if (mouse
[i
].mask
== CLEAN(event
->button
.state
)
651 && (mouse
[i
].modkey
== current_modkey
652 || (!mouse
[i
].modkey
&& !current_modkey
)
653 || mouse
[i
].modkey
== GDK_VoidSymbol
) /* wildcard */
654 && mouse
[i
].button
== event
->button
.button
656 if (mouse
[i
].func(&mouse
[i
].arg
)) {
657 current_modkey
= count
= 0;
663 result
= webkit_web_view_get_hit_test_result(WEBKIT_WEB_VIEW(widget
), (GdkEventButton
*)event
);
664 g_object_get(result
, "context", &context
, NULL
);
665 if (context
& WEBKIT_HIT_TEST_RESULT_CONTEXT_EDITABLE
) {
666 Arg a
= { .i
= ModeInsert
};
670 } else if (mode
== ModeInsert
&& event
->type
== GDK_BUTTON_RELEASE
) {
671 result
= webkit_web_view_get_hit_test_result(WEBKIT_WEB_VIEW(widget
), (GdkEventButton
*)event
);
672 g_object_get(result
, "context", &context
, NULL
);
673 if (!(context
& WEBKIT_HIT_TEST_RESULT_CONTEXT_EDITABLE
)) {
674 Arg a
= { .i
= ModeNormal
};
678 gchar
*value
= NULL
, *message
= NULL
;
679 jsapi_evaluate_script("window.getSelection().focusNode", &value
, &message
);
680 if (value
&& !strcmp(value
, "[object HTMLFormElement]")) {
681 Arg a
= { .i
= ModeInsert
, .s
= NULL
};
691 static gboolean
inputbox_keyrelease_cb(GtkEntry
*entry
, GdkEventKey
*event
) {
693 guint16 length
= gtk_entry_get_text_length(entry
);
696 a
.i
= HideCompletion
;
704 static gboolean
inputbox_changed_cb(GtkEditable
*entry
, gpointer user_data
) {
706 char *text
= (char*)gtk_entry_get_text(GTK_ENTRY(entry
));
707 guint16 length
= gtk_entry_get_text_length(GTK_ENTRY(entry
));
708 gboolean forward
= FALSE
;
710 /* Update incremental search if the user changes the search text.
712 * Note: gtk_widget_is_focus() is a poor way to check if the change comes
713 * from the user. But if the entry is focused and the text is set
714 * through gtk_entry_set_text() in some asyncrounous operation,
715 * I would consider that a bug.
718 if (gtk_widget_is_focus(GTK_WIDGET(entry
)) && length
> 1 && ((forward
= text
[0] == '/') || text
[0] == '?')) {
719 webkit_web_view_unmark_text_matches(webview
);
720 webkit_web_view_search_text(webview
, &text
[1], searchoptions
& CaseSensitive
, forward
, searchoptions
& Wrapping
);
722 } else if (gtk_widget_is_focus(GTK_WIDGET(entry
)) && length
>= 1 &&
723 (text
[0] == '.' || text
[0] == ',' || text
[0] == ';')) {
727 a
.s
= g_strconcat("hints.createHints('", text
+ 1, "', 'f');", NULL
);
731 a
.s
= g_strconcat("hints.createHints('", text
+ 1, "', 'F');", NULL
);
738 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 's');", NULL
);
741 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 'y');", NULL
);
744 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 'f');", NULL
);
747 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 'F');", NULL
);
749 case 'O': case 'T': case 'W':
750 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 'O');", NULL
);
753 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 'i');", NULL
);
756 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 'I');", NULL
);
768 } else if (length
== 0 && followTarget
[0]) {
771 a
.s
= g_strdup("hints.clearHints();");
783 void fill_suggline(char * suggline
, const char * command
, const char *fill_with
) {
784 memset(suggline
, 0, 512);
785 strncpy(suggline
, command
, 512);
786 strncat(suggline
, " ", 1);
787 strncat(suggline
, fill_with
, 512 - strlen(suggline
) - 1);
790 GtkWidget
* fill_eventbox(const char * completion_line
) {
792 GtkWidget
*row_eventbox
, *el
;
794 char *markup
, *markup_tmp
;
796 row
= GTK_BOX(gtk_hbox_new(FALSE
, 0));
797 row_eventbox
= gtk_event_box_new();
798 gdk_color_parse(completionbgcolor
[0], &color
);
799 gtk_widget_modify_bg(row_eventbox
, GTK_STATE_NORMAL
, &color
);
800 el
= gtk_label_new(NULL
);
801 markup_tmp
= g_markup_escape_text(completion_line
, strlen(completion_line
));
802 markup
= g_strconcat("<span font=\"", completionfont
[0], "\" color=\"", completioncolor
[0], "\">",
803 markup_tmp
, "</span>", NULL
);
804 gtk_label_set_markup(GTK_LABEL(el
), markup
);
807 gtk_misc_set_alignment(GTK_MISC(el
), 0, 0);
808 gtk_box_pack_start(row
, el
, TRUE
, TRUE
, 2);
809 gtk_container_add(GTK_CONTAINER(row_eventbox
), GTK_WIDGET(row
));
814 complete(const Arg
*arg
) {
815 char *str
, *p
, *s
, *markup
, *entry
, *searchfor
, command
[32] = "", suggline
[512] = "", **suggurls
;
816 size_t listlen
, len
, cmdlen
;
818 Listelement
*elementlist
= NULL
, *elementpointer
;
819 gboolean highlight
= FALSE
;
821 GtkWidget
*row_eventbox
, *el
;
824 static GtkWidget
*table
, *top_border
;
826 static char **suggestions
;
827 static GtkWidget
**widgets
;
828 static int n
= 0, m
, current
= -1;
830 str
= (char*)gtk_entry_get_text(GTK_ENTRY(inputbox
));
833 /* Get the length of the list of commands for completion. We need this to
834 * malloc/realloc correctly.
836 listlen
= LENGTH(commands
);
838 if ((len
== 0 || str
[0] != ':') && arg
->i
!= HideCompletion
)
841 if (arg
->i
!= HideCompletion
&& widgets
&& current
!= -1 && !strcmp(&str
[1], suggestions
[current
])) {
842 gdk_color_parse(completionbgcolor
[0], &color
);
843 gtk_widget_modify_bg(widgets
[current
], GTK_STATE_NORMAL
, &color
);
844 current
= (n
+ current
+ (arg
->i
== DirectionPrev
? -1 : 1)) % n
;
845 if ((arg
->i
== DirectionNext
&& current
== 0)
846 || (arg
->i
== DirectionPrev
&& current
== n
- 1))
852 gtk_widget_destroy(GTK_WIDGET(table
));
853 gtk_widget_destroy(GTK_WIDGET(top_border
));
860 if (arg
->i
== HideCompletion
)
863 } else if (arg
->i
== HideCompletion
)
866 prefix
= g_strdup(str
);
867 widgets
= malloc(sizeof(GtkWidget
*) * listlen
);
868 suggestions
= malloc(sizeof(char*) * listlen
);
869 top_border
= gtk_event_box_new();
870 gtk_widget_set_size_request(GTK_WIDGET(top_border
), 0, 1);
871 gdk_color_parse(completioncolor
[2], &color
);
872 gtk_widget_modify_bg(top_border
, GTK_STATE_NORMAL
, &color
);
873 table
= gtk_event_box_new();
874 gdk_color_parse(completionbgcolor
[0], &color
);
875 _table
= GTK_BOX(gtk_vbox_new(FALSE
, 0));
877 if (strchr(str
, ' ') == NULL
) {
878 /* command completion */
879 listlen
= LENGTH(commands
);
880 for (i
= 0; i
< listlen
; i
++) {
881 if (commands
[i
].cmd
== NULL
)
883 cmdlen
= strlen(commands
[i
].cmd
);
884 if (!highlight
|| (n
< MAX_LIST_SIZE
&& len
- 1 <= cmdlen
&& !strncmp(&str
[1], commands
[i
].cmd
, len
- 1))) {
885 p
= s
= malloc(sizeof(char*) * (highlight
? sizeof(COMPLETION_TAG_OPEN
) + sizeof(COMPLETION_TAG_CLOSE
) - 1 : 1) + cmdlen
);
887 memcpy(p
, COMPLETION_TAG_OPEN
, sizeof(COMPLETION_TAG_OPEN
) - 1);
888 memcpy((p
+= sizeof(COMPLETION_TAG_OPEN
) - 1), &str
[1], len
- 1);
889 memcpy((p
+= len
- 1), COMPLETION_TAG_CLOSE
, sizeof(COMPLETION_TAG_CLOSE
) - 1);
890 p
+= sizeof(COMPLETION_TAG_CLOSE
) - 1;
892 memcpy(p
, &commands
[i
].cmd
[len
- 1], cmdlen
- len
+ 2);
893 row
= GTK_BOX(gtk_hbox_new(FALSE
, 0));
894 row_eventbox
= gtk_event_box_new();
895 gtk_widget_modify_bg(row_eventbox
, GTK_STATE_NORMAL
, &color
);
896 el
= gtk_label_new(NULL
);
897 markup
= g_strconcat("<span font=\"", completionfont
[0], "\" color=\"", completioncolor
[0], "\">", s
, "</span>", NULL
);
899 gtk_label_set_markup(GTK_LABEL(el
), markup
);
901 gtk_misc_set_alignment(GTK_MISC(el
), 0, 0);
902 gtk_box_pack_start(row
, el
, TRUE
, TRUE
, 2);
903 gtk_container_add(GTK_CONTAINER(row_eventbox
), GTK_WIDGET(row
));
904 gtk_box_pack_start(_table
, GTK_WIDGET(row_eventbox
), FALSE
, FALSE
, 0);
905 suggestions
[n
] = commands
[i
].cmd
;
906 widgets
[n
++] = row_eventbox
;
910 entry
= (char *)malloc(512 * sizeof(char));
914 memset(entry
, 0, 512);
915 suggurls
= malloc(sizeof(char*) * listlen
);
916 if (suggurls
== NULL
) {
919 spacepos
= strcspn(str
, " ");
920 searchfor
= (str
+ spacepos
+ 1);
921 strncpy(command
, (str
+ 1), spacepos
- 1);
922 if (strlen(command
) == 3 && strncmp(command
, "set", 3) == 0) {
923 /* browser settings */
924 listlen
= LENGTH(browsersettings
);
925 for (i
= 0; i
< listlen
; i
++) {
926 if (n
< MAX_LIST_SIZE
&& strstr(browsersettings
[i
].name
, searchfor
) != NULL
) {
928 fill_suggline(suggline
, command
, browsersettings
[i
].name
);
929 /* FIXME(HP): This memory is never freed */
930 suggurls
[n
] = (char *)malloc(sizeof(char) * 512 + 1);
931 strncpy(suggurls
[n
], suggline
, 512);
932 suggestions
[n
] = suggurls
[n
];
933 row_eventbox
= fill_eventbox(suggline
);
934 gtk_box_pack_start(_table
, GTK_WIDGET(row_eventbox
), FALSE
, FALSE
, 0);
935 widgets
[n
++] = row_eventbox
;
939 } else if (strlen(command
) == 2 && strncmp(command
, "qt", 2) == 0) {
940 /* completion on tags */
941 spacepos
= strcspn(str
, " ");
942 searchfor
= (str
+ spacepos
+ 1);
943 elementlist
= complete_list(searchfor
, 1, elementlist
);
945 /* URL completion: bookmarks */
946 elementlist
= complete_list(searchfor
, 0, elementlist
);
947 m
= count_list(elementlist
);
948 if (m
< MAX_LIST_SIZE
) {
949 /* URL completion: history */
950 elementlist
= complete_list(searchfor
, 2, elementlist
);
953 elementpointer
= elementlist
;
954 while (elementpointer
!= NULL
) {
955 fill_suggline(suggline
, command
, elementpointer
->element
);
956 /* FIXME(HP): This memory is never freed */
957 suggurls
[n
] = (char *)malloc(sizeof(char) * 512 + 1);
958 strncpy(suggurls
[n
], suggline
, 512);
959 suggestions
[n
] = suggurls
[n
];
960 row_eventbox
= fill_eventbox(suggline
);
961 gtk_box_pack_start(_table
, GTK_WIDGET(row_eventbox
), FALSE
, FALSE
, 0);
962 widgets
[n
++] = row_eventbox
;
963 elementpointer
= elementpointer
->next
;
964 if (n
>= MAX_LIST_SIZE
)
967 free_list(elementlist
);
968 if (suggurls
!= NULL
) {
977 /* TA: FIXME - this needs rethinking entirely. */
979 GtkWidget
**widgets_temp
= realloc(widgets
, sizeof(*widgets
) * n
);
980 if (widgets_temp
== NULL
&& widgets
== NULL
) {
981 fprintf(stderr
, "Couldn't realloc() widgets\n");
984 widgets
= widgets_temp
;
985 char **suggestions_temp
= realloc(suggestions
, sizeof(*suggestions
) * n
);
986 if (suggestions_temp
== NULL
&& suggestions
== NULL
) {
987 fprintf(stderr
, "Couldn't realloc() suggestions\n");
990 suggestions
= suggestions_temp
;
993 gdk_color_parse(completionbgcolor
[1], &color
);
994 gtk_widget_modify_bg(table
, GTK_STATE_NORMAL
, &color
);
995 el
= gtk_label_new(NULL
);
996 gtk_misc_set_alignment(GTK_MISC(el
), 0, 0);
997 markup
= g_strconcat("<span font=\"", completionfont
[1], "\" color=\"", completioncolor
[1], "\">No Completions</span>", NULL
);
998 gtk_label_set_markup(GTK_LABEL(el
), markup
);
1000 gtk_box_pack_start(_table
, GTK_WIDGET(el
), FALSE
, FALSE
, 0);
1002 gtk_box_pack_start(box
, GTK_WIDGET(top_border
), FALSE
, FALSE
, 0);
1003 gtk_container_add(GTK_CONTAINER(table
), GTK_WIDGET(_table
));
1004 gtk_box_pack_start(box
, GTK_WIDGET(table
), FALSE
, FALSE
, 0);
1005 gtk_widget_show_all(GTK_WIDGET(window
));
1008 current
= arg
->i
== DirectionPrev
? n
- 1 : 0;
1010 if (current
!= -1) {
1011 gdk_color_parse(completionbgcolor
[2], &color
);
1012 gtk_widget_modify_bg(GTK_WIDGET(widgets
[current
]), GTK_STATE_NORMAL
, &color
);
1013 s
= g_strconcat(":", suggestions
[current
], NULL
);
1014 gtk_entry_set_text(GTK_ENTRY(inputbox
), s
);
1017 gtk_entry_set_text(GTK_ENTRY(inputbox
), prefix
);
1018 gtk_editable_set_position(GTK_EDITABLE(inputbox
), -1);
1023 descend(const Arg
*arg
) {
1024 char *source
= (char*)webkit_web_view_get_uri(webview
), *p
= &source
[0], *new;
1026 count
= count
? count
: 1;
1030 if (arg
->i
== Rootdir
) {
1031 for (i
= 0; i
< 3; i
++) /* get to the third slash */
1032 if (!(p
= strchr(++p
, '/')))
1033 return TRUE
; /* if we cannot find it quit */
1035 len
= strlen(source
);
1036 if (!len
) /* if string is empty quit */
1038 p
= source
+ len
; /* start at the end */
1039 if (*(p
- 1) == '/') /* /\/$/ is not an additional level */
1041 for (i
= 0; i
< count
; i
++)
1042 while(*(p
--) != '/' || *p
== '/') /* count /\/+/ as one slash */
1043 if (p
== source
) /* if we reach the first char pointer quit */
1045 ++p
; /* since we do p-- in the while, we are pointing at
1046 the char before the slash, so +1 */
1048 len
= p
- source
+ 1; /* new length = end - start + 1 */
1049 new = malloc(len
+ 1);
1050 memcpy(new, source
, len
);
1052 webkit_web_view_load_uri(webview
, new);
1058 echo(const Arg
*arg
) {
1059 int index
= !arg
->s
? 0 : arg
->i
& (~NoAutoHide
);
1061 if (index
< Info
|| index
> Error
)
1064 if (!gtk_widget_is_focus(GTK_WIDGET(inputbox
))) {
1065 set_widget_font_and_color(inputbox
, urlboxfont
[index
], urlboxbgcolor
[index
], urlboxcolor
[index
]);
1066 gtk_entry_set_text(GTK_ENTRY(inputbox
), !arg
->s
? "" : arg
->s
);
1073 open_inspector(const Arg
* arg
) {
1074 gboolean inspect_enabled
;
1075 WebKitWebSettings
*settings
;
1077 settings
= webkit_web_view_get_settings(webview
);
1078 g_object_get(G_OBJECT(settings
), "enable-developer-extras", &inspect_enabled
, NULL
);
1079 if (inspect_enabled
) {
1080 webkit_web_inspector_show(inspector
);
1083 echo_message(Error
, "Webinspector is not enabled");
1089 input(const Arg
*arg
) {
1096 /* if inputbox hidden, show it again */
1097 if (!gtk_widget_get_visible(inputbox
))
1098 gtk_widget_set_visible(inputbox
, TRUE
);
1102 /* Set the colour and font back to the default, so that we don't still
1103 * maintain a red colour from a warning from an end of search indicator,
1106 set_widget_font_and_color(inputbox
, urlboxfont
[index
], urlboxbgcolor
[index
], urlboxcolor
[index
]);
1108 /* to avoid things like :open URL :open URL2 or :open :open URL */
1109 gtk_entry_set_text(GTK_ENTRY(inputbox
), "");
1110 gtk_editable_insert_text(GTK_EDITABLE(inputbox
), arg
->s
, -1, &pos
);
1111 if (arg
->i
& InsertCurrentURL
&& (url
= webkit_web_view_get_uri(webview
)))
1112 gtk_editable_insert_text(GTK_EDITABLE(inputbox
), url
, -1, &pos
);
1114 gtk_widget_grab_focus(inputbox
);
1115 gtk_editable_set_position(GTK_EDITABLE(inputbox
), -1);
1117 if (arg
->s
[0] == '.' || arg
->s
[0] == ',' || arg
->s
[0] == ';') {
1119 memset(followTarget
, 0, 8);
1120 strncpy(followTarget
, "current", 8);
1122 switch (arg
->s
[0]) {
1124 a
.s
= g_strdup("hints.createHints('', 'f');");
1128 a
.s
= g_strdup("hints.createHints('', 'F');");
1134 switch (arg
->s
[1]) {
1136 a
.s
= g_strdup("hints.createHints('', 's');");
1139 a
.s
= g_strdup("hints.createHints('', 'y');");
1142 a
.s
= g_strdup("hints.createHints('', 'f');");
1145 a
.s
= g_strdup("hints.createHints('', 'F');");
1147 case 'O': case 'T': case 'W':
1148 a
.s
= g_strdup("hints.createHints('', 'O');");
1151 a
.s
= g_strdup("hints.createHints('', 'i');");
1154 a
.s
= g_strdup("hints.createHints('', 'I');");
1171 navigate(const Arg
*arg
) {
1172 if (arg
->i
& NavigationForwardBack
)
1173 webkit_web_view_go_back_or_forward(webview
, (arg
->i
== NavigationBack
? -1 : 1) * (count
? count
: 1));
1174 else if (arg
->i
& NavigationReloadActions
)
1175 (arg
->i
== NavigationReload
? webkit_web_view_reload
: webkit_web_view_reload_bypass_cache
)(webview
);
1177 webkit_web_view_stop_loading(webview
);
1182 number(const Arg
*arg
) {
1183 const char *source
= webkit_web_view_get_uri(webview
);
1184 char *uri
, *p
, *new;
1185 int number
, diff
= (count
? count
: 1) * (arg
->i
== Increment
? 1 : -1);
1189 uri
= g_strdup(source
); /* copy string */
1191 while(*p
!= '\0') /* goto the end of the string */
1194 while(*p
>= '0' && *p
<= '9') /* go back until non number char is reached */
1196 if (*(++p
) == '\0') { /* if no numbers were found abort */
1200 number
= atoi(p
) + diff
; /* apply diff on number */
1202 new = g_strdup_printf("%s%d", uri
, number
); /* create new uri */
1203 webkit_web_view_load_uri(webview
, new);
1210 open_arg(const Arg
*arg
) {
1212 char *s
= arg
->s
, *p
= NULL
, *new;
1213 Arg a
= { .i
= NavigationReload
};
1215 char *search_uri
, *search_term
;
1231 else if (arg
->i
== TargetCurrent
) {
1232 while(*s
== ' ') /* strip leading whitespace */
1234 p
= (s
+ strlen(s
) - 1);
1235 while(*p
== ' ') /* strip trailing whitespace */
1240 /* check for external handlers */
1241 if (open_handler(s
))
1243 /* check for search engines */
1245 if (p
) { /* check for search engines */
1247 search_uri
= find_uri_for_searchengine(s
);
1248 if (search_uri
!= NULL
) {
1249 search_term
= soup_uri_encode(p
+1, "&");
1250 new = g_strdup_printf(search_uri
, search_term
);
1251 g_free(search_term
);
1256 if (len
> 3 && strstr(s
, "://")) { /* valid url? */
1257 p
= new = g_malloc(len
+ 1);
1258 while(*s
!= '\0') { /* strip whitespaces */
1264 } else if (strcspn(s
, "/") == 0 || strcspn(s
, "./") == 0) { /* prepend "file://" */
1265 new = g_malloc(sizeof("file://") + len
);
1266 strcpy(new, "file://");
1267 memcpy(&new[sizeof("file://") - 1], s
, len
+ 1);
1268 } else if (p
|| !strchr(s
, '.')) { /* whitespaces or no dot? */
1269 search_uri
= find_uri_for_searchengine(defaultsearch
);
1270 if (search_uri
!= NULL
) {
1271 search_term
= soup_uri_encode(s
, "&");
1272 new = g_strdup_printf(search_uri
, search_term
);
1273 g_free(search_term
);
1275 } else { /* prepend "http://" */
1276 new = g_malloc(sizeof("http://") + len
);
1277 strcpy(new, "http://");
1278 memcpy(&new[sizeof("http://") - 1], s
, len
+ 1);
1281 webkit_web_view_load_uri(webview
, new);
1284 g_spawn_async(NULL
, argv
, NULL
, G_SPAWN_SEARCH_PATH
, NULL
, NULL
, NULL
, NULL
);
1289 open_remembered(const Arg
*arg
)
1291 Arg a
= {arg
->i
, rememberedURI
};
1293 if (strcmp(rememberedURI
, "")) {
1300 yank(const Arg
*arg
) {
1301 const char *url
, *content
;
1303 if (arg
->i
& SourceSelection
) {
1304 webkit_web_view_copy_clipboard(webview
);
1305 if (arg
->i
& ClipboardPrimary
)
1306 content
= gtk_clipboard_wait_for_text(clipboards
[0]);
1307 if (!content
&& arg
->i
& ClipboardGTK
)
1308 content
= gtk_clipboard_wait_for_text(clipboards
[1]);
1310 echo_message(Info
, "Yanked %s", content
);
1311 g_free((gpointer
*)content
);
1314 if (arg
->i
& SourceURL
) {
1315 url
= webkit_web_view_get_uri(webview
);
1322 echo_message(Info
, "Yanked %s", url
);
1323 if (arg
->i
& ClipboardPrimary
)
1324 gtk_clipboard_set_text(clipboards
[0], url
, -1);
1325 if (arg
->i
& ClipboardGTK
)
1326 gtk_clipboard_set_text(clipboards
[1], url
, -1);
1332 paste(const Arg
*arg
) {
1333 Arg a
= { .i
= arg
->i
& TargetNew
, .s
= NULL
};
1335 /* If we're over a link, open it in a new target. */
1336 if (strlen(rememberedURI
) > 0) {
1337 Arg new_target
= { .i
= TargetNew
, .s
= arg
->s
};
1338 open_arg(&new_target
);
1342 if (arg
->i
& ClipboardPrimary
)
1343 a
.s
= gtk_clipboard_wait_for_text(clipboards
[0]);
1344 if (!a
.s
&& arg
->i
& ClipboardGTK
)
1345 a
.s
= gtk_clipboard_wait_for_text(clipboards
[1]);
1354 quit(const Arg
*arg
) {
1356 const char *filename
;
1357 const char *uri
= webkit_web_view_get_uri(webview
);
1359 /* write last URL into status file for recreation with "u" */
1360 filename
= g_strdup_printf(CLOSED_URL_FILENAME
);
1361 f
= fopen(filename
, "w");
1362 g_free((gpointer
*)filename
);
1364 fprintf(f
, "%s", uri
);
1373 revive(const Arg
*arg
) {
1375 const char *filename
;
1376 char buffer
[512] = "";
1377 Arg a
= { .i
= TargetNew
, .s
= NULL
};
1378 /* get the URL of the window which has been closed last */
1379 filename
= g_strdup_printf(CLOSED_URL_FILENAME
);
1380 f
= fopen(filename
, "r");
1381 g_free((gpointer
*)filename
);
1383 fgets(buffer
, 512, f
);
1386 if (strlen(buffer
) > 0) {
1395 gboolean
print_frame(const Arg
*arg
)
1397 WebKitWebFrame
*frame
= webkit_web_view_get_main_frame(webview
);
1398 webkit_web_frame_print (frame
);
1403 search(const Arg
*arg
) {
1404 count
= count
? count
: 1;
1405 gboolean success
, direction
= arg
->i
& DirectionPrev
;
1408 free(search_handle
);
1409 search_handle
= g_strdup(arg
->s
);
1413 if (arg
->i
& DirectionAbsolute
)
1414 search_direction
= direction
;
1416 direction
^= search_direction
;
1418 success
= webkit_web_view_search_text(webview
, search_handle
, arg
->i
& CaseSensitive
, direction
, FALSE
);
1420 if (arg
->i
& Wrapping
) {
1421 success
= webkit_web_view_search_text(webview
, search_handle
, arg
->i
& CaseSensitive
, direction
, TRUE
);
1423 echo_message(Warning
, "search hit %s, continuing at %s",
1424 direction
? "BOTTOM" : "TOP",
1425 direction
? "TOP" : "BOTTOM");
1433 echo_message(Error
, "Pattern not found: %s", search_handle
);
1439 set(const Arg
*arg
) {
1442 if (search_handle
) {
1443 search_handle
= NULL
;
1444 webkit_web_view_unmark_text_matches(webview
);
1446 gtk_entry_set_text(GTK_ENTRY(inputbox
), "");
1447 gtk_widget_grab_focus(GTK_WIDGET(webview
));
1449 case ModePassThrough
:
1450 echo_message(Info
| NoAutoHide
, "-- PASS THROUGH --");
1453 echo_message(Info
| NoAutoHide
, "-- PASS TROUGH (next) --");
1455 case ModeInsert
: /* should not be called manually but automatically */
1456 echo_message(Info
| NoAutoHide
, "-- INSERT --");
1466 jsapi_ref_to_string(JSContextRef context
, JSValueRef ref
) {
1467 JSStringRef string_ref
;
1471 string_ref
= JSValueToStringCopy(context
, ref
, NULL
);
1472 length
= JSStringGetMaximumUTF8CStringSize(string_ref
);
1473 string
= g_new(gchar
, length
);
1474 JSStringGetUTF8CString(string_ref
, string
, length
);
1475 JSStringRelease(string_ref
);
1480 jsapi_evaluate_script(const gchar
*script
, gchar
**value
, gchar
**message
) {
1481 WebKitWebFrame
*frame
= webkit_web_view_get_main_frame(webview
);
1482 JSGlobalContextRef context
= webkit_web_frame_get_global_context(frame
);
1484 JSValueRef val
, exception
;
1486 str
= JSStringCreateWithUTF8CString(script
);
1487 val
= JSEvaluateScript(context
, str
, JSContextGetGlobalObject(context
), NULL
, 0, &exception
);
1488 JSStringRelease(str
);
1490 *message
= jsapi_ref_to_string(context
, exception
);
1492 *value
= jsapi_ref_to_string(context
, val
);
1496 quickmark(const Arg
*a
) {
1499 char *fn
= g_strdup_printf(QUICKMARK_FILE
);
1501 fp
= fopen(fn
, "r");
1506 if (fp
!= NULL
&& b
< 10) {
1507 for( i
=0; i
< b
; ++i
) {
1511 fgets(buf
, 100, fp
);
1513 char *ptr
= strrchr(buf
, '\n');
1516 Arg x
= { .s
= buf
};
1517 return open_arg(&x
);
1519 echo_message(Error
, "Quickmark %d not defined", b
);
1522 } else { return false; }
1526 script(const Arg
*arg
) {
1527 gchar
*value
= NULL
, *message
= NULL
;
1528 char text
[1024] = "";
1530 WebKitNetworkRequest
*request
;
1531 WebKitDownload
*download
;
1534 set_error("Missing argument.");
1537 jsapi_evaluate_script(arg
->s
, &value
, &message
);
1544 if (arg
->i
!= Silent
&& value
) {
1545 echo_message(arg
->i
, value
);
1547 /* switch mode according to scripts return value */
1549 if (strncmp(value
, "done;", 5) == 0) {
1552 } else if (strncmp(value
, "insert;", 7) == 0) {
1555 manual_focus
= TRUE
;
1556 } else if (strncmp(value
, "save;", 5) == 0) {
1557 /* forced download */
1560 request
= webkit_network_request_new((value
+ 5));
1561 download
= webkit_download_new(request
);
1562 webview_download_cb(webview
, download
, (gpointer
*)NULL
);
1563 } else if (strncmp(value
, "yank;", 5) == 0) {
1564 /* yank link URL to clipboard */
1567 a
.i
= ClipboardPrimary
| ClipboardGTK
;
1570 } else if (strncmp(value
, "colon;", 6) == 0) {
1571 /* use link URL for colon command */
1572 strncpy(text
, (char *)gtk_entry_get_text(GTK_ENTRY(inputbox
)), 1023);
1577 a
.s
= g_strconcat(":open ", (value
+ 6), NULL
);
1580 a
.s
= g_strconcat(":tabopen ", (value
+ 6), NULL
);
1587 } else if (strncmp(value
, "error;", 6) == 0) {
1597 scroll(const Arg
*arg
) {
1598 GtkAdjustment
*adjust
= (arg
->i
& OrientationHoriz
) ? adjust_h
: adjust_v
;
1599 int max
= gtk_adjustment_get_upper(adjust
) - gtk_adjustment_get_page_size(adjust
);
1600 float val
= gtk_adjustment_get_value(adjust
) / max
* 100;
1601 int direction
= (arg
->i
& (1 << 2)) ? 1 : -1;
1603 if ((direction
== 1 && val
< 100) || (direction
== -1 && val
> 0)) {
1604 if (arg
->i
& ScrollMove
)
1605 gtk_adjustment_set_value(adjust
, gtk_adjustment_get_value(adjust
) +
1606 direction
* /* direction */
1607 ((arg
->i
& UnitLine
|| (arg
->i
& UnitBuffer
&& count
)) ? (scrollstep
* (count
? count
: 1)) : (
1608 arg
->i
& UnitBuffer
? gtk_adjustment_get_page_size(adjust
) / 2 :
1609 (count
? count
: 1) * (gtk_adjustment_get_page_size(adjust
) -
1610 (gtk_adjustment_get_page_size(adjust
) > pagingkeep
? pagingkeep
: 0)))));
1612 gtk_adjustment_set_value(adjust
,
1613 ((direction
== 1) ? gtk_adjustment_get_upper
: gtk_adjustment_get_lower
)(adjust
));
1620 zoom(const Arg
*arg
) {
1621 webkit_web_view_set_full_content_zoom(webview
, (arg
->i
& ZoomFullContent
) > 0);
1622 webkit_web_view_set_zoom_level(webview
, (arg
->i
& ZoomOut
) ?
1623 webkit_web_view_get_zoom_level(webview
) +
1624 (((float)(count
? count
: 1)) * (arg
->i
& (1 << 1) ? 1.0 : -1.0) * zoomstep
) :
1625 (count
? (float)count
/ 100.0 : 1.0));
1630 fake_key_event(const Arg
*a
) {
1635 if ( (xdpy
= XOpenDisplay(NULL
)) == NULL
) {
1636 echo_message(Error
, "Couldn't find the XDisplay.");
1642 xk
.subwindow
= None
;
1643 xk
.time
= CurrentTime
;
1644 xk
.same_screen
= True
;
1645 xk
.x
= xk
.y
= xk
.x_root
= xk
.y_root
= 1;
1650 echo_message(Error
, "Zero pointer as argument! Check your config.h");
1655 if( (keysym
= XStringToKeysym(a
->s
)) == NoSymbol
) {
1656 echo_message(Error
, "Couldn't translate %s to keysym", a
->s
);
1660 if( (xk
.keycode
= XKeysymToKeycode(xdpy
, keysym
)) == NoSymbol
) {
1661 echo_message(Error
, "Couldn't translate keysym to keycode");
1666 if( !XSendEvent(xdpy
, embed
, True
, KeyPressMask
, (XEvent
*)&xk
) ) {
1667 echo_message(Error
, "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 echo_message(Info
, "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
);
1811 /* open an external editor defined by the protocol handler for
1812 vimprobableedit on a text box or similar */
1814 open_editor(const Arg
*arg
) {
1818 gchar
*value
= NULL
, *message
= NULL
, *tag
= NULL
, *edit_url
= NULL
;
1819 gchar
*temp_file_name
= g_strdup_printf("%s/vimprobableeditXXXXXX",
1821 int temp_file_handle
= -1;
1823 /* check if active element is suitable for text editing */
1824 jsapi_evaluate_script("document.activeElement.tagName", &value
, &message
);
1827 tag
= g_strdup(value
);
1828 if (strcmp(tag
, "INPUT") == 0) {
1829 /* extra check: type == text */
1830 jsapi_evaluate_script("document.activeElement.type", &value
, &message
);
1831 if (strcmp(value
, "text") != 0) {
1836 } else if (strcmp(tag
, "TEXTAREA") != 0) {
1841 jsapi_evaluate_script("document.activeElement.value", &value
, &message
);
1842 text
= g_strdup(value
);
1849 /* write text into temporary file */
1850 temp_file_handle
= mkstemp(temp_file_name
);
1851 if (temp_file_handle
== -1) {
1852 message
= g_strdup_printf("Could not create temporary file: %s",
1854 echo_message(Error
, message
);
1860 if (write(temp_file_handle
, text
, strlen(text
)) != strlen(text
)) {
1861 message
= g_strdup_printf("Short write to temporary file: %s",
1863 echo_message(Error
, message
);
1869 close(temp_file_handle
);
1873 edit_url
= g_strdup_printf("vimprobableedit:%s", temp_file_name
);
1874 success
= open_handler_pid(edit_url
, &child_pid
);
1877 echo_message(Error
, "External editor open failed (no handler for"
1878 " vimprobableedit protocol?)");
1879 unlink(temp_file_name
);
1885 /* mark the active text box as "under processing" */
1886 jsapi_evaluate_script(
1887 "document.activeElement.disabled = true;"
1888 "document.activeElement.originalBackground = "
1889 " document.activeElement.style.background;"
1890 "document.activeElement.style.background = '#aaaaaa';"
1893 g_child_watch_add(child_pid
, _resume_from_editor
, temp_file_name
);
1895 /* temp_file_name is freed in _resume_from_editor */
1903 /* pick up from where open_editor left the work to the glib event loop.
1905 This is called when the external editor exits.
1907 The data argument points to allocated memory containing the temporary file
1910 _resume_from_editor(GPid child_pid
, int child_status
, gpointer data
) {
1912 GString
*set_value_js
= g_string_new(
1913 "document.activeElement.value = \"");
1914 g_spawn_close_pid(child_pid
);
1915 gchar
*value
= NULL
, *message
= NULL
;
1916 gchar
*temp_file_name
= data
;
1917 gchar buffer
[BUF_SIZE
] = "";
1918 gchar
*buf_ptr
= buffer
;
1921 jsapi_evaluate_script(
1922 "document.activeElement.disabled = true;"
1923 "document.activeElement.style.background = '#aaaaaa';"
1927 echo_message(Error
, "External editor returned with non-zero status,"
1928 " discarding edits.");
1932 /* re-read the new contents of the file and put it into the HTML element */
1933 if (!access(temp_file_name
, R_OK
) == 0) {
1934 message
= g_strdup_printf("Could not access temporary file: %s",
1938 fp
= fopen(temp_file_name
, "r");
1940 /* this would be too weird to even emit an error message */
1943 jsapi_evaluate_script("document.activeElement.value = '';",
1946 while (EOF
!= (char_read
= fgetc(fp
))) {
1947 if (char_read
== '\n') {
1950 } else if (char_read
== '"') {
1954 *buf_ptr
++ = char_read
;
1956 /* ship out as the buffer when space gets tight. This has
1957 fuzz to save on thinking, plus we have enough space for the
1958 trailing "; in any case. */
1959 if (buf_ptr
-buffer
>=BUF_SIZE
-10) {
1961 g_string_append(set_value_js
, buffer
);
1968 g_string_append(set_value_js
, buffer
);
1971 jsapi_evaluate_script(set_value_js
->str
, &value
, &message
);
1973 /* Fall through, error and normal exit are identical */
1975 jsapi_evaluate_script(
1976 "document.activeElement.disabled = false;"
1977 "document.activeElement.style.background ="
1978 " document.activeElement.originalBackground;"
1981 g_string_free(set_value_js
, TRUE
);
1982 unlink(temp_file_name
);
1983 g_free(temp_file_name
);
1989 focus_input(const Arg
*arg
) {
1992 a
.s
= g_strdup("hints.focusInput();");
1997 manual_focus
= TRUE
;
2005 a
.s
= g_strdup("hints.clearFocus();");
2015 browser_settings(const Arg
*arg
) {
2018 set_error("Missing argument.");
2021 strncpy(line
, arg
->s
, 254);
2022 if (process_set_line(line
))
2025 set_error("Invalid setting.");
2031 search_word(int whichword
) {
2033 static char word
[240];
2034 char *c
= my_pair
.line
;
2036 while (isspace(*c
) && *c
)
2039 switch (whichword
) {
2041 while (*c
&& !isspace (*c
) && *c
!= '=' && k
< 240) {
2046 strncpy(my_pair
.what
, word
, 20);
2049 while (*c
&& k
< 240) {
2054 strncpy(my_pair
.value
, word
, 240);
2062 process_set_line(char *line
) {
2066 WebKitWebSettings
*settings
;
2068 settings
= webkit_web_view_get_settings(webview
);
2069 my_pair
.line
= line
;
2071 if (!strlen(my_pair
.what
))
2074 while (isspace(*c
) && *c
)
2077 if (*c
== ':' || *c
== '=')
2083 listlen
= LENGTH(browsersettings
);
2084 for (i
= 0; i
< listlen
; i
++) {
2085 if (strlen(browsersettings
[i
].name
) == strlen(my_pair
.what
) && strncmp(browsersettings
[i
].name
, my_pair
.what
, strlen(my_pair
.what
)) == 0) {
2086 /* mandatory argument not provided */
2087 if (strlen(my_pair
.value
) == 0)
2089 /* process qmark? */
2090 if (strlen(my_pair
.what
) == 5 && strncmp("qmark", my_pair
.what
, 5) == 0) {
2091 return (process_save_qmark(my_pair
.value
, webview
));
2093 /* interpret boolean values */
2094 if (browsersettings
[i
].boolval
) {
2095 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) {
2097 } 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) {
2102 } else if (browsersettings
[i
].colourval
) {
2103 /* interpret as hexadecimal colour */
2104 if (!parse_colour(my_pair
.value
)) {
2108 if (browsersettings
[i
].var
!= NULL
) {
2109 strncpy(browsersettings
[i
].var
, my_pair
.value
, MAX_SETTING_SIZE
);
2110 if (strlen(my_pair
.value
) > MAX_SETTING_SIZE
- 1) {
2111 /* in this case, \0 will not have been copied */
2112 browsersettings
[i
].var
[MAX_SETTING_SIZE
- 1] = '\0';
2113 /* in case this string is also used for a webkit setting, make sure it's consistent */
2114 my_pair
.value
[MAX_SETTING_SIZE
- 1] = '\0';
2115 echo_message(Info
, "String too long; automatically truncated!");
2118 if (strlen(browsersettings
[i
].webkit
) > 0) {
2119 /* activate appropriate webkit setting */
2120 if (browsersettings
[i
].boolval
) {
2121 g_object_set((GObject
*)settings
, browsersettings
[i
].webkit
, boolval
, NULL
);
2122 } else if (browsersettings
[i
].intval
) {
2123 g_object_set((GObject
*)settings
, browsersettings
[i
].webkit
, atoi(my_pair
.value
), NULL
);
2125 g_object_set((GObject
*)settings
, browsersettings
[i
].webkit
, my_pair
.value
, NULL
);
2127 webkit_web_view_set_settings(webview
, settings
);
2130 if (strlen(my_pair
.what
) == 14) {
2131 if (strncmp("acceptlanguage", my_pair
.what
, 14) == 0) {
2132 g_object_set(G_OBJECT(session
), "accept-language", acceptlanguage
, NULL
);
2133 } else if (strncmp("completioncase", my_pair
.what
, 14) == 0) {
2134 complete_case_sensitive
= boolval
;
2136 } else if (strlen(my_pair
.what
) == 5 && strncmp("proxy", my_pair
.what
, 5) == 0) {
2137 toggle_proxy(boolval
);
2138 } else if (strlen(my_pair
.what
) == 10 && strncmp("scrollbars", my_pair
.what
, 10) == 0) {
2139 toggle_scrollbars(boolval
);
2140 } else if (strlen(my_pair
.what
) == 9 && strncmp("statusbar", my_pair
.what
, 9) == 0) {
2141 gtk_widget_set_visible(GTK_WIDGET(statusbar
), boolval
);
2142 } else if (strlen(my_pair
.what
) == 8 && strncmp("inputbox", my_pair
.what
, 8) == 0) {
2143 gtk_widget_set_visible(inputbox
, boolval
);
2144 } else if (strlen(my_pair
.what
) == 11 && strncmp("escapeinput", my_pair
.what
, 11) == 0) {
2145 escape_input_on_load
= boolval
;
2148 /* SSL certificate checking */
2149 if (strlen(my_pair
.what
) == 9 && strncmp("strictssl", my_pair
.what
, 9) == 0) {
2152 g_object_set(G_OBJECT(session
), "ssl-strict", TRUE
, NULL
);
2155 g_object_set(G_OBJECT(session
), "ssl-strict", FALSE
, NULL
);
2158 if (strlen(my_pair
.what
) == 8 && strncmp("cabundle", my_pair
.what
, 8) == 0) {
2159 g_object_set(G_OBJECT(session
), SOUP_SESSION_SSL_CA_FILE
, ca_bundle
, NULL
);
2161 if (strlen(my_pair
.what
) == 10 && strncmp("windowsize", my_pair
.what
, 10) == 0) {
2162 set_default_winsize(my_pair
.value
);
2166 if (browsersettings
[i
].reload
)
2167 webkit_web_view_reload(webview
);
2175 process_line(char *line
) {
2176 char *c
= line
, *command_hist
;
2178 size_t len
, length
= strlen(line
);
2179 gboolean found
= FALSE
, success
= FALSE
;
2184 /* Ignore blank lines. */
2188 command_hist
= g_strdup(c
);
2189 for (i
= 0; i
< LENGTH(commands
); i
++) {
2190 if (commands
[i
].cmd
== NULL
)
2192 len
= strlen(commands
[i
].cmd
);
2193 if (length
>= len
&& !strncmp(c
, commands
[i
].cmd
, len
) && (c
[len
] == ' ' || !c
[len
])) {
2195 a
.i
= commands
[i
].arg
.i
;
2196 a
.s
= g_strdup(length
> len
+ 1 ? &c
[len
+ 1] : commands
[i
].arg
.s
);
2197 success
= commands
[i
].func(&a
);
2203 save_command_history(command_hist
);
2204 g_free(command_hist
);
2207 echo_message(Error
, "Not a browser command: %s", c
);
2208 } else if (!success
) {
2209 if (error_msg
!= NULL
) {
2210 echo_message(Error
, error_msg
);
2214 echo_message(Error
, "Unknown error. Please file a bug report!");
2221 search_tag(const Arg
* a
) {
2223 const char *filename
;
2224 const char *tag
= a
->s
;
2225 char s
[BUFFERSIZE
], foundtag
[40], url
[BUFFERSIZE
];
2229 /* The user must give us something to load up. */
2230 set_error("Bookmark tag required with this option.");
2234 if (strlen(tag
) > MAXTAGSIZE
) {
2235 set_error("Tag too long.");
2239 filename
= g_strdup_printf(BOOKMARKS_STORAGE_FILENAME
);
2240 f
= fopen(filename
, "r");
2241 g_free((gpointer
*)filename
);
2243 set_error("Couldn't open bookmarks file.");
2246 while (fgets(s
, BUFFERSIZE
-1, f
)) {
2249 while (isspace(s
[t
]))
2251 if (s
[t
] != ']') continue;
2264 foundtag
[i
++] = s
[k
++];
2266 /* foundtag now contains the tag */
2267 if (strlen(foundtag
) < MAXTAGSIZE
&& strcmp(tag
, foundtag
) == 0) {
2269 while (isspace(s
[i
])) i
++;
2271 while (s
[i
] && !isspace(s
[i
])) url
[k
++] = s
[i
++];
2273 Arg x
= { .i
= TargetNew
, .s
= url
};
2287 toggle_proxy(gboolean onoff
) {
2289 char *filename
, *new;
2291 if (onoff
== FALSE
) {
2292 g_object_set(session
, "proxy-uri", NULL
, NULL
);
2294 filename
= (char *)g_getenv("http_proxy");
2296 /* Fallthrough to checking HTTP_PROXY as well, since this can also be
2299 if (filename
== NULL
)
2300 filename
= (char *)g_getenv("HTTP_PROXY");
2302 if (filename
!= NULL
&& 0 < strlen(filename
)) {
2303 new = g_strrstr(filename
, "http://") ? g_strdup(filename
) : g_strdup_printf("http://%s", filename
);
2304 proxy_uri
= soup_uri_new(new);
2306 g_object_set(session
, "proxy-uri", proxy_uri
, NULL
);
2308 soup_uri_free(proxy_uri
);
2315 toggle_scrollbars(gboolean onoff
) {
2316 if (onoff
== TRUE
) {
2317 adjust_h
= gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(viewport
));
2318 adjust_v
= gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(viewport
));
2319 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(viewport
), GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
2321 adjust_v
= gtk_range_get_adjustment(GTK_RANGE(scroll_v
));
2322 adjust_h
= gtk_range_get_adjustment(GTK_RANGE(scroll_h
));
2323 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(viewport
), GTK_POLICY_NEVER
, GTK_POLICY_NEVER
);
2325 gtk_widget_set_scroll_adjustments (GTK_WIDGET(webview
), adjust_h
, adjust_v
);
2330 void set_default_winsize(const char * const size
) {
2332 int x
= 640, y
= 480;
2334 x
= strtol(size
, &p
, 10);
2335 if (errno
== ERANGE
|| x
<= 0) {
2340 if (p
== size
|| strlen(size
) == p
- size
)
2343 y
= strtol(p
+ 1, NULL
, 10);
2344 if (errno
== ERANGE
|| y
<= 0)
2348 gtk_window_resize(GTK_WINDOW(window
), x
, y
);
2352 update_url(const char *uri
) {
2353 gboolean ssl
= g_str_has_prefix(uri
, "https://");
2355 WebKitWebFrame
*frame
;
2356 WebKitWebDataSource
*src
;
2357 WebKitNetworkRequest
*request
;
2360 char *sslactivecolor
;
2362 #ifdef ENABLE_HISTORY_INDICATOR
2363 char before
[] = " [";
2365 gboolean back
= webkit_web_view_can_go_back(webview
);
2366 gboolean fwd
= webkit_web_view_can_go_forward(webview
);
2369 before
[0] = after
[0] = '\0';
2371 markup
= g_markup_printf_escaped(
2372 #ifdef ENABLE_HISTORY_INDICATOR
2373 "<span font=\"%s\">%s%s%s%s%s</span>", statusfont
, uri
,
2374 before
, back
? "+" : "", fwd
? "-" : "", after
2376 "<span font=\"%s\">%s</span>", statusfont
, uri
2379 gtk_label_set_markup(GTK_LABEL(status_url
), markup
);
2382 frame
= webkit_web_view_get_main_frame(webview
);
2383 src
= webkit_web_frame_get_data_source(frame
);
2384 request
= webkit_web_data_source_get_request(src
);
2385 msg
= webkit_network_request_get_message(request
);
2386 ssl_ok
= soup_message_get_flags(msg
) & SOUP_MESSAGE_CERTIFICATE_TRUSTED
;
2388 sslactivecolor
= sslbgcolor
;
2390 sslactivecolor
= sslinvalidbgcolor
;
2392 gdk_color_parse(ssl
? sslactivecolor
: statusbgcolor
, &color
);
2393 gtk_widget_modify_bg(eventbox
, GTK_STATE_NORMAL
, &color
);
2394 gdk_color_parse(ssl
? sslcolor
: statuscolor
, &color
);
2395 gtk_widget_modify_fg(GTK_WIDGET(status_url
), GTK_STATE_NORMAL
, &color
);
2396 gtk_widget_modify_fg(GTK_WIDGET(status_state
), GTK_STATE_NORMAL
, &color
);
2402 int download_count
= g_list_length(activeDownloads
);
2403 GString
*status
= g_string_new("");
2405 /* construct the status line */
2407 /* count, modkey and input buffer */
2408 g_string_append_printf(status
, "%.0d", count
);
2409 if (current_modkey
) g_string_append_c(status
, current_modkey
);
2411 /* the number of active downloads */
2412 if (activeDownloads
) {
2413 g_string_append_printf(status
, " %d active %s", download_count
,
2414 (download_count
== 1) ? "download" : "downloads");
2417 #ifdef ENABLE_WGET_PROGRESS_BAR
2418 /* the progressbar */
2421 char progressbar
[progressbartick
+ 1];
2423 if (activeDownloads
) {
2427 for (ptr
= activeDownloads
; ptr
; ptr
= g_list_next(ptr
)) {
2428 progress
+= 100 * webkit_download_get_progress(ptr
->data
);
2431 progress
/= download_count
;
2433 } else if (webkit_web_view_get_load_status(webview
) != WEBKIT_LOAD_FINISHED
2434 && webkit_web_view_get_load_status(webview
) != WEBKIT_LOAD_FAILED
) {
2436 progress
= webkit_web_view_get_progress(webview
) * 100;
2439 if (progress
>= 0) {
2440 ascii_bar(progressbartick
, progress
* progressbartick
/ 100, progressbar
);
2441 g_string_append_printf(status
, " %c%s%c",
2442 progressborderleft
, progressbar
, progressborderright
);
2447 /* and the current scroll position */
2449 int max
= gtk_adjustment_get_upper(adjust_v
) - gtk_adjustment_get_page_size(adjust_v
);
2450 int val
= (int)(gtk_adjustment_get_value(adjust_v
) / max
* 100);
2453 g_string_append(status
, " All");
2455 g_string_append(status
, " Top");
2456 else if (val
== 100)
2457 g_string_append(status
, " Bot");
2459 g_string_append_printf(status
, " %d%%", val
);
2463 markup
= g_markup_printf_escaped("<span font=\"%s\">%s</span>", statusfont
, status
->str
);
2464 gtk_label_set_markup(GTK_LABEL(status_state
), markup
);
2467 g_string_free(status
, TRUE
);
2473 modkeys
= calloc(LENGTH(keys
) + 1, sizeof(char));
2474 char *ptr
= modkeys
;
2476 for (i
= 0; i
< LENGTH(keys
); i
++)
2477 if (keys
[i
].modkey
&& !strchr(modkeys
, keys
[i
].modkey
))
2478 *(ptr
++) = keys
[i
].modkey
;
2479 modkeys
= realloc(modkeys
, &ptr
[0] - &modkeys
[0] + 1);
2484 scroll_h
= GTK_SCROLLBAR(gtk_hscrollbar_new(NULL
));
2485 scroll_v
= GTK_SCROLLBAR(gtk_vscrollbar_new(NULL
));
2486 adjust_h
= gtk_range_get_adjustment(GTK_RANGE(scroll_h
));
2487 adjust_v
= gtk_range_get_adjustment(GTK_RANGE(scroll_v
));
2489 window
= GTK_WINDOW(gtk_plug_new(embed
));
2491 window
= GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL
));
2492 gtk_window_set_wmclass(GTK_WINDOW(window
), "vimprobable2", "Vimprobable2");
2494 gtk_window_set_default_size(GTK_WINDOW(window
), 640, 480);
2495 box
= GTK_BOX(gtk_vbox_new(FALSE
, 0));
2496 inputbox
= gtk_entry_new();
2497 webview
= (WebKitWebView
*)webkit_web_view_new();
2498 statusbar
= GTK_BOX(gtk_hbox_new(FALSE
, 0));
2499 eventbox
= gtk_event_box_new();
2500 status_url
= gtk_label_new(NULL
);
2501 status_state
= gtk_label_new(NULL
);
2503 PangoFontDescription
*font
;
2504 GdkGeometry hints
= { 1, 1 };
2505 inspector
= webkit_web_view_get_inspector(WEBKIT_WEB_VIEW(webview
));
2507 clipboards
[0] = gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2508 clipboards
[1] = gtk_clipboard_get(GDK_NONE
);
2510 gdk_color_parse(statusbgcolor
, &bg
);
2511 gtk_widget_modify_bg(eventbox
, GTK_STATE_NORMAL
, &bg
);
2512 gtk_widget_set_name(GTK_WIDGET(window
), "Vimprobable2");
2513 gtk_window_set_geometry_hints(window
, NULL
, &hints
, GDK_HINT_MIN_SIZE
);
2515 keymap
= gdk_keymap_get_default();
2517 #ifdef DISABLE_SCROLLBAR
2518 viewport
= gtk_scrolled_window_new(NULL
, NULL
);
2519 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(viewport
), GTK_POLICY_NEVER
, GTK_POLICY_NEVER
);
2521 /* Ensure we still see scrollbars. */
2522 GtkWidget
*viewport
= gtk_scrolled_window_new(adjust_h
, adjust_v
);
2526 gtk_container_add(GTK_CONTAINER(viewport
), GTK_WIDGET(webview
));
2528 /* Ensure we set the scroll adjustments now, so that if we're not drawing
2529 * titlebars, we can still scroll.
2531 gtk_widget_set_scroll_adjustments(GTK_WIDGET(webview
), adjust_h
, adjust_v
);
2533 font
= pango_font_description_from_string(urlboxfont
[0]);
2534 gtk_widget_modify_font(GTK_WIDGET(inputbox
), font
);
2535 pango_font_description_free(font
);
2536 gtk_entry_set_inner_border(GTK_ENTRY(inputbox
), NULL
);
2537 gtk_misc_set_alignment(GTK_MISC(status_url
), 0.0, 0.0);
2538 gtk_misc_set_alignment(GTK_MISC(status_state
), 1.0, 0.0);
2539 gtk_box_pack_start(statusbar
, status_url
, TRUE
, TRUE
, 2);
2540 gtk_box_pack_start(statusbar
, status_state
, FALSE
, FALSE
, 2);
2541 gtk_container_add(GTK_CONTAINER(eventbox
), GTK_WIDGET(statusbar
));
2542 gtk_box_pack_start(box
, viewport
, TRUE
, TRUE
, 0);
2543 gtk_box_pack_start(box
, eventbox
, FALSE
, FALSE
, 0);
2544 gtk_entry_set_has_frame(GTK_ENTRY(inputbox
), FALSE
);
2545 gtk_box_pack_end(box
, inputbox
, FALSE
, FALSE
, 0);
2546 gtk_container_add(GTK_CONTAINER(window
), GTK_WIDGET(box
));
2547 gtk_widget_grab_focus(GTK_WIDGET(webview
));
2548 gtk_widget_show_all(GTK_WIDGET(window
));
2549 set_widget_font_and_color(inputbox
, urlboxfont
[0], urlboxbgcolor
[0], urlboxcolor
[0]);
2550 g_object_set(gtk_widget_get_settings(inputbox
), "gtk-entry-select-on-focus", FALSE
, NULL
);
2555 WebKitWebSettings
*settings
= (WebKitWebSettings
*)webkit_web_settings_new();
2556 char *filename
, *file_url
;
2558 session
= webkit_get_default_session();
2559 g_object_set(G_OBJECT(session
), "ssl-ca-file", ca_bundle
, NULL
);
2560 g_object_set(G_OBJECT(session
), "ssl-strict", strict_ssl
, NULL
);
2561 g_object_set(G_OBJECT(settings
), "default-font-size", DEFAULT_FONT_SIZE
, NULL
);
2562 g_object_set(G_OBJECT(settings
), "enable-scripts", enablePlugins
, NULL
);
2563 g_object_set(G_OBJECT(settings
), "enable-plugins", enablePlugins
, NULL
);
2564 g_object_set(G_OBJECT(settings
), "enable-java-applet", enableJava
, NULL
);
2565 g_object_set(G_OBJECT(settings
), "enable-page-cache", enablePagecache
, NULL
);
2566 filename
= g_strdup_printf(USER_STYLESHEET
);
2567 file_url
= g_strdup_printf("file://%s", filename
);
2568 g_object_set(G_OBJECT(settings
), "user-stylesheet-uri", file_url
, NULL
);
2571 g_object_set(G_OBJECT(settings
), "user-agent", useragent
, NULL
);
2572 g_object_get(G_OBJECT(settings
), "zoom-step", &zoomstep
, NULL
);
2573 webkit_web_view_set_settings(webview
, settings
);
2576 toggle_proxy(use_proxy
);
2581 WebKitWebFrame
*frame
= webkit_web_view_get_main_frame(webview
);
2582 #ifdef ENABLE_COOKIE_SUPPORT
2584 g_signal_connect_after(G_OBJECT(session
), "request-started", G_CALLBACK(new_generic_request
), NULL
);
2586 /* Accept-language header */
2587 g_object_set(G_OBJECT(session
), "accept-language", acceptlanguage
, NULL
);
2589 g_object_connect(G_OBJECT(window
),
2590 "signal::destroy", G_CALLBACK(window_destroyed_cb
), NULL
,
2593 g_signal_connect(G_OBJECT(frame
),
2594 "scrollbars-policy-changed", G_CALLBACK(blank_cb
), NULL
);
2596 g_object_connect(G_OBJECT(webview
),
2597 "signal::title-changed", G_CALLBACK(webview_title_changed_cb
), NULL
,
2598 "signal::load-progress-changed", G_CALLBACK(webview_progress_changed_cb
), NULL
,
2599 "signal::load-committed", G_CALLBACK(webview_load_committed_cb
), NULL
,
2600 "signal::load-finished", G_CALLBACK(webview_load_finished_cb
), NULL
,
2601 "signal::navigation-policy-decision-requested", G_CALLBACK(webview_navigation_cb
), NULL
,
2602 "signal::new-window-policy-decision-requested", G_CALLBACK(webview_new_window_cb
), NULL
,
2603 "signal::mime-type-policy-decision-requested", G_CALLBACK(webview_mimetype_cb
), NULL
,
2604 "signal::download-requested", G_CALLBACK(webview_download_cb
), NULL
,
2605 "signal::key-press-event", G_CALLBACK(webview_keypress_cb
), NULL
,
2606 "signal::hovering-over-link", G_CALLBACK(webview_hoverlink_cb
), NULL
,
2607 "signal::console-message", G_CALLBACK(webview_console_cb
), NULL
,
2608 "signal::create-web-view", G_CALLBACK(webview_open_in_new_window_cb
), NULL
,
2609 "signal::event", G_CALLBACK(notify_event_cb
), NULL
,
2611 /* webview adjustment */
2612 g_object_connect(G_OBJECT(adjust_v
),
2613 "signal::value-changed", G_CALLBACK(webview_scroll_cb
), NULL
,
2616 g_object_connect(G_OBJECT(inputbox
),
2617 "signal::activate", G_CALLBACK(inputbox_activate_cb
), NULL
,
2618 "signal::key-press-event", G_CALLBACK(inputbox_keypress_cb
), NULL
,
2619 "signal::key-release-event", G_CALLBACK(inputbox_keyrelease_cb
), NULL
,
2620 #ifdef ENABLE_INCREMENTAL_SEARCH
2621 "signal::changed", G_CALLBACK(inputbox_changed_cb
), NULL
,
2625 g_signal_connect(G_OBJECT(inspector
),
2626 "inspect-web-view", G_CALLBACK(inspector_inspect_web_view_cb
), NULL
);
2629 #ifdef ENABLE_USER_SCRIPTFILE
2631 scripts_run_user_file() {
2632 gchar
*js
= NULL
, *user_scriptfile
= NULL
;
2633 GError
*error
= NULL
;
2635 user_scriptfile
= g_strdup_printf(USER_SCRIPTFILE
);
2637 /* run the users script file */
2638 if (g_file_test(user_scriptfile
, G_FILE_TEST_IS_REGULAR
)
2639 && g_file_get_contents(user_scriptfile
, &js
, NULL
, &error
)) {
2641 gchar
*value
= NULL
, *message
= NULL
;
2643 jsapi_evaluate_script(js
, &value
, &message
);
2645 fprintf(stderr
, "%s", message
);
2649 fprintf(stderr
, "Cannot open %s: %s\n", user_scriptfile
, error
? error
->message
: "file not found");
2652 g_free(user_scriptfile
);
2656 #ifdef ENABLE_COOKIE_SUPPORT
2660 if (file_cookie_jar
)
2661 g_object_unref(file_cookie_jar
);
2663 if (session_cookie_jar
)
2664 g_object_unref(session_cookie_jar
);
2666 session_cookie_jar
= soup_cookie_jar_new();
2668 cookie_store
= g_strdup_printf(COOKIES_STORAGE_FILENAME
);
2672 g_signal_connect(G_OBJECT(file_cookie_jar
), "changed",
2673 G_CALLBACK(update_cookie_jar
), NULL
);
2678 /* TA: XXX - we should be using this callback for any header-requests we
2679 * receive (hence the name "new_generic_request" -- but for now, its use
2680 * is limited to handling cookies.
2683 new_generic_request(SoupSession
*session
, SoupMessage
*soup_msg
, gpointer unused
)
2685 SoupMessageHeaders
*soup_msg_h
;
2689 soup_msg_h
= soup_msg
->request_headers
;
2690 soup_message_headers_remove(soup_msg_h
, "Cookie");
2691 uri
= soup_message_get_uri(soup_msg
);
2692 if ((cookie_str
= get_cookies(uri
))) {
2693 soup_message_headers_append(soup_msg_h
, "Cookie", cookie_str
);
2697 g_signal_connect_after(G_OBJECT(soup_msg
), "got-headers", G_CALLBACK(handle_cookie_request
), NULL
);
2703 get_cookies(SoupURI
*soup_uri
) {
2706 cookie_str
= soup_cookie_jar_get_cookies(file_cookie_jar
, soup_uri
, TRUE
);
2712 handle_cookie_request(SoupMessage
*soup_msg
, gpointer unused
)
2714 GSList
*resp_cookie
= NULL
, *cookie_list
;
2717 cookie_list
= soup_cookies_from_response(soup_msg
);
2718 for(resp_cookie
= cookie_list
; resp_cookie
; resp_cookie
= g_slist_next(resp_cookie
))
2720 SoupDate
*soup_date
;
2721 cookie
= soup_cookie_copy((SoupCookie
*)resp_cookie
->data
);
2723 if (cookie_timeout
&& cookie
->expires
== NULL
) {
2724 soup_date
= soup_date_new_from_time_t(time(NULL
) + cookie_timeout
* 10);
2725 soup_cookie_set_expires(cookie
, soup_date
);
2726 soup_date_free(soup_date
);
2728 soup_cookie_jar_add_cookie(file_cookie_jar
, cookie
);
2731 soup_cookies_free(cookie_list
);
2737 update_cookie_jar(SoupCookieJar
*jar
, SoupCookie
*old
, SoupCookie
*new)
2740 /* Nothing to do. */
2745 copy
= soup_cookie_copy(new);
2747 soup_cookie_jar_add_cookie(session_cookie_jar
, copy
);
2753 load_all_cookies(void)
2755 GSList
*cookie_list
;
2756 file_cookie_jar
= soup_cookie_jar_text_new(cookie_store
, COOKIES_STORAGE_READONLY
);
2758 /* Put them back in the session store. */
2759 GSList
*cookies_from_file
= soup_cookie_jar_all_cookies(file_cookie_jar
);
2760 cookie_list
= cookies_from_file
;
2762 for (; cookies_from_file
;
2763 cookies_from_file
= cookies_from_file
->next
)
2765 soup_cookie_jar_add_cookie(session_cookie_jar
, cookies_from_file
->data
);
2768 soup_cookies_free(cookies_from_file
);
2769 g_slist_free(cookie_list
);
2778 /* Free up any nasty globals before exiting. */
2779 #ifdef ENABLE_COOKIE_SUPPORT
2781 g_free(cookie_store
);
2787 main(int argc
, char *argv
[]) {
2789 static char url
[256] = "";
2790 static gboolean ver
= false;
2791 static gboolean configfile_exists
= FALSE
;
2792 static const char *cfile
= NULL
;
2793 static GOptionEntry opts
[] = {
2794 { "version", 'v', 0, G_OPTION_ARG_NONE
, &ver
, "print version", NULL
},
2795 { "embed", 'e', 0, G_OPTION_ARG_STRING
, &winid
, "embedded", NULL
},
2796 { "configfile", 'c', 0, G_OPTION_ARG_STRING
, &cfile
, "config file", NULL
},
2802 /* command line argument: version */
2803 if (!gtk_init_with_args(&argc
, &argv
, "[<uri>]", opts
, NULL
, &err
)) {
2804 g_printerr("can't init gtk: %s\n", err
->message
);
2806 return EXIT_FAILURE
;
2810 printf("%s\n", INTERNAL_VERSION
);
2811 return EXIT_SUCCESS
;
2814 if (getenv("TMPDIR")) {
2815 strncpy(temp_dir
, getenv("TMPDIR"), MAX_SETTING_SIZE
);
2816 temp_dir
[MAX_SETTING_SIZE
-1] = 0;
2819 if( getenv("XDG_CONFIG_HOME") )
2820 config_base
= g_strdup_printf("%s", getenv("XDG_CONFIG_HOME"));
2822 config_base
= g_strdup_printf("%s/.config/", getenv("HOME"));
2825 configfile
= g_strdup(cfile
);
2827 configfile
= g_strdup_printf(RCFILE
);
2829 if (!g_thread_supported())
2830 g_thread_init(NULL
);
2833 if (strncmp(winid
, "0x", 2) == 0) {
2834 embed
= strtol(winid
, NULL
, 16);
2836 embed
= atoi(winid
);
2843 #ifdef ENABLE_COOKIE_SUPPORT
2847 make_searchengines_list(searchengines
, LENGTH(searchengines
));
2848 make_uri_handlers_list(uri_handlers
, LENGTH(uri_handlers
));
2850 /* Check if the specified file exists. */
2851 /* And only warn the user, if they explicitly asked for a config on the
2854 if (!(access(configfile
, F_OK
) == 0) && cfile
) {
2855 echo_message(Info
, "Config file '%s' doesn't exist", cfile
);
2856 } else if ((access(configfile
, F_OK
) == 0))
2857 configfile_exists
= true;
2859 /* read config file */
2860 /* But only report errors if we failed, and the file existed. */
2861 if ((SUCCESS
!= read_rcfile(configfile
)) && configfile_exists
) {
2862 echo_message(Error
, "Error in config file '%s'", configfile
);
2866 /* command line argument: URL */
2868 strncpy(url
, argv
[argc
- 1], 255);
2870 strncpy(url
, startpage
, 255);
2873 a
.i
= TargetCurrent
;
2880 return EXIT_SUCCESS
;