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
navigate(const Arg
*arg
);
71 static gboolean
number(const Arg
*arg
);
72 static gboolean
open_arg(const Arg
*arg
);
73 static gboolean
open_remembered(const Arg
*arg
);
74 static gboolean
paste(const Arg
*arg
);
75 static gboolean
quickmark(const Arg
*arg
);
76 static gboolean
quit(const Arg
*arg
);
77 static gboolean
revive(const Arg
*arg
);
78 static gboolean
print_frame(const Arg
*arg
);
79 static gboolean
search(const Arg
*arg
);
80 static gboolean
set(const Arg
*arg
);
81 static gboolean
script(const Arg
*arg
);
82 static gboolean
scroll(const Arg
*arg
);
83 static gboolean
search_tag(const Arg
*arg
);
84 static gboolean
yank(const Arg
*arg
);
85 static gboolean
view_source(const Arg
* arg
);
86 static gboolean
zoom(const Arg
*arg
);
87 static gboolean
fake_key_event(const Arg
*arg
);
89 static void update_url(const char *uri
);
90 static void setup_modkeys(void);
91 static void setup_gui(void);
92 static void setup_settings(void);
93 static void setup_signals(void);
94 static void ascii_bar(int total
, int state
, char *string
);
95 static gchar
*jsapi_ref_to_string(JSContextRef context
, JSValueRef ref
);
96 static void jsapi_evaluate_script(const gchar
*script
, gchar
**value
, gchar
**message
);
97 static void download_progress(WebKitDownload
*d
, GParamSpec
*pspec
);
98 static void set_widget_font_and_color(GtkWidget
*widget
, const char *font_str
,
99 const char *bg_color_str
, const char *fg_color_str
);
101 static gboolean
history(void);
102 static gboolean
process_set_line(char *line
);
103 void save_command_history(char *line
);
104 void toggle_proxy(gboolean onoff
);
105 void toggle_scrollbars(gboolean onoff
);
106 void set_default_winsize(const char * const size
);
108 gboolean
process_keypress(GdkEventKey
*event
);
109 void fill_suggline(char * suggline
, const char * command
, const char *fill_with
);
110 GtkWidget
* fill_eventbox(const char * completion_line
);
111 static void mop_up(void);
116 static GtkWindow
*window
;
117 static GtkWidget
*viewport
;
119 static GtkScrollbar
*scroll_h
;
120 static GtkScrollbar
*scroll_v
;
121 static GtkAdjustment
*adjust_h
;
122 static GtkAdjustment
*adjust_v
;
123 static GtkWidget
*inputbox
;
124 static GtkWidget
*eventbox
;
125 static GtkBox
*statusbar
;
126 static GtkWidget
*status_url
;
127 static GtkWidget
*status_state
;
128 static WebKitWebView
*webview
;
129 static SoupSession
*session
;
130 static GtkClipboard
*clipboards
[2];
131 static GdkKeymap
*keymap
;
134 static unsigned int mode
= ModeNormal
;
135 static unsigned int count
= 0;
136 static float zoomstep
;
138 static char current_modkey
;
139 static char *search_handle
;
140 static gboolean search_direction
;
141 static gboolean echo_active
= TRUE
;
142 WebKitWebInspector
*inspector
;
144 static GdkNativeWindow embed
= 0;
145 static char *configfile
= NULL
;
146 static char *winid
= NULL
;
148 static char rememberedURI
[1024] = "";
149 static char followTarget
[8] = "";
150 char *error_msg
= NULL
;
151 char *config_base
= NULL
;
152 static gboolean manual_focus
= FALSE
;
154 GList
*activeDownloads
;
159 GList
*commandhistory
= NULL
;
160 int commandpointer
= 0;
162 KeyList
*keylistroot
= NULL
;
164 /* Cookie support. */
165 #ifdef ENABLE_COOKIE_SUPPORT
166 static SoupCookieJar
*session_cookie_jar
= NULL
;
167 static SoupCookieJar
*file_cookie_jar
= NULL
;
168 static time_t cookie_timeout
= 4800;
169 static char *cookie_store
;
170 static void setup_cookies(void);
171 static char *get_cookies(SoupURI
*soup_uri
);
172 static void load_all_cookies(void);
173 static void new_generic_request(SoupSession
*soup_ses
, SoupMessage
*soup_msg
, gpointer unused
);
174 static void update_cookie_jar(SoupCookieJar
*jar
, SoupCookie
*old
, SoupCookie
*new);
175 static void handle_cookie_request(SoupMessage
*soup_msg
, gpointer unused
);
179 window_destroyed_cb(GtkWidget
*window
, gpointer func_data
) {
184 webview_title_changed_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, char *title
, gpointer user_data
) {
185 gtk_window_set_title(window
, title
);
189 webview_progress_changed_cb(WebKitWebView
*webview
, int progress
, gpointer user_data
) {
190 #ifdef ENABLE_GTK_PROGRESS_BAR
191 gtk_entry_set_progress_fraction(GTK_ENTRY(inputbox
), progress
== 100 ? 0 : (double)progress
/100);
196 #ifdef ENABLE_WGET_PROGRESS_BAR
198 ascii_bar(int total
, int state
, char *string
) {
201 for (i
= 0; i
< state
; i
++)
202 string
[i
] = progressbartickchar
;
203 string
[i
++] = progressbarcurrent
;
204 for (; i
< total
; i
++)
205 string
[i
] = progressbarspacer
;
211 webview_load_committed_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, gpointer user_data
) {
212 Arg a
= { .i
= Silent
, .s
= g_strdup(JS_SETUP_HINTS
) };
213 const char *uri
= webkit_web_view_get_uri(webview
);
219 if (mode
== ModeInsert
|| mode
== ModeHints
) {
220 Arg a
= { .i
= ModeNormal
};
223 manual_focus
= FALSE
;
227 webview_load_finished_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, gpointer user_data
) {
228 WebKitWebSettings
*settings
= webkit_web_view_get_settings(webview
);
231 g_object_get(settings
, "enable-scripts", &scripts
, NULL
);
232 if (escape_input_on_load
&& scripts
&& !manual_focus
&& !gtk_widget_is_focus(inputbox
)) {
233 Arg a
= { .i
= Silent
, .s
= g_strdup("hints.clearFocus();") };
240 if (HISTORY_MAX_ENTRIES
> 0)
246 webview_open_js_window_cb(WebKitWebView
* temp_view
, GParamSpec param_spec
) {
247 /* retrieve the URI of the temporary webview */
248 Arg a
= { .i
= TargetNew
, .s
= (char*)webkit_web_view_get_uri(temp_view
) };
250 webkit_web_view_stop_loading(temp_view
);
251 gtk_widget_destroy(GTK_WIDGET(temp_view
));
252 /* open the requested window */
256 static WebKitWebView
*
257 webview_open_in_new_window_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, gpointer user_data
) {
258 /* create a temporary webview to execute the script in */
259 WebKitWebView
*temp_view
= WEBKIT_WEB_VIEW(webkit_web_view_new());
260 /* wait until the new webview receives its new URI */
261 g_object_connect(temp_view
, "signal::notify::uri", G_CALLBACK(webview_open_js_window_cb
), NULL
, NULL
);
266 webview_new_window_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, WebKitNetworkRequest
*request
,
267 WebKitWebNavigationAction
*action
, WebKitWebPolicyDecision
*decision
, gpointer user_data
) {
268 Arg a
= { .i
= TargetNew
, .s
= (char*)webkit_network_request_get_uri(request
) };
270 webkit_web_policy_decision_ignore(decision
);
275 webview_mimetype_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, WebKitNetworkRequest
*request
,
276 char *mime_type
, WebKitWebPolicyDecision
*decision
, gpointer user_data
) {
277 if (webkit_web_view_can_show_mime_type(webview
, mime_type
) == FALSE
) {
278 webkit_web_policy_decision_download(decision
);
285 static WebKitWebView
*
286 inspector_inspect_web_view_cb(gpointer inspector
, WebKitWebView
* web_view
) {
287 gchar
* inspector_title
;
288 GtkWidget
* inspector_window
;
289 GtkWidget
* inspector_view
;
291 /* just enough code to show the inspector - no signal handling etc. */
292 inspector_title
= g_strdup_printf("Inspect page - %s - Vimprobable2", webkit_web_view_get_uri(web_view
));
294 inspector_window
= gtk_plug_new(embed
);
296 inspector_window
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
297 gtk_window_set_wmclass(window
, "vimprobable2", "Vimprobable2");
299 gtk_window_set_title(GTK_WINDOW(inspector_window
), inspector_title
);
300 g_free(inspector_title
);
301 inspector_view
= webkit_web_view_new();
302 gtk_container_add(GTK_CONTAINER(inspector_window
), inspector_view
);
303 gtk_widget_show_all(inspector_window
);
304 return WEBKIT_WEB_VIEW(inspector_view
);
308 webview_download_cb(WebKitWebView
*webview
, WebKitDownload
*download
, gpointer user_data
) {
309 const gchar
*filename
;
313 WebKitDownloadStatus status
;
315 filename
= webkit_download_get_suggested_filename(download
);
316 if (filename
== NULL
|| strlen(filename
) == 0) {
317 filename
= "vimprobable_download";
319 path
= g_build_filename(g_strdup_printf(DOWNLOADS_PATH
), filename
, NULL
);
320 uri
= g_strconcat("file://", path
, NULL
);
321 webkit_download_set_destination_uri(download
, uri
);
323 size
= (uint32_t)webkit_download_get_total_size(download
);
326 a
.s
= g_strdup_printf("Download %s started (expected size: %u bytes)...", filename
, size
);
328 a
.s
= g_strdup_printf("Download %s started (unknown size)...", filename
);
331 activeDownloads
= g_list_prepend(activeDownloads
, download
);
332 g_signal_connect(download
, "notify::progress", G_CALLBACK(download_progress
), NULL
);
333 g_signal_connect(download
, "notify::status", G_CALLBACK(download_progress
), NULL
);
334 status
= webkit_download_get_status(download
);
335 if (status
== WEBKIT_DOWNLOAD_STATUS_CREATED
)
336 webkit_download_start(download
);
347 download_progress(WebKitDownload
*d
, GParamSpec
*pspec
) {
349 WebKitDownloadStatus status
= webkit_download_get_status(d
);
351 if (status
!= WEBKIT_DOWNLOAD_STATUS_STARTED
&& status
!= WEBKIT_DOWNLOAD_STATUS_CREATED
) {
352 if (status
!= WEBKIT_DOWNLOAD_STATUS_FINISHED
) {
354 a
.s
= g_strdup_printf("Error while downloading %s", webkit_download_get_suggested_filename(d
));
358 a
.s
= g_strdup_printf("Download %s finished", webkit_download_get_suggested_filename(d
));
362 activeDownloads
= g_list_remove(activeDownloads
, d
);
369 process_keypress(GdkEventKey
*event
) {
372 GdkModifierType irrelevant
;
374 /* Get a mask of modifiers that shouldn't be considered for this event.
375 * E.g.: It shouldn't matter whether ';' is shifted or not. */
376 gdk_keymap_translate_keyboard_state(keymap
, event
->hardware_keycode
,
377 event
->state
, event
->group
, &keyval
, NULL
, NULL
, &irrelevant
);
379 current
= keylistroot
;
381 while (current
!= NULL
) {
382 if (current
->Element
.mask
== (CLEAN(event
->state
) & ~irrelevant
)
383 && (current
->Element
.modkey
== current_modkey
384 || (!current
->Element
.modkey
&& !current_modkey
)
385 || current
->Element
.modkey
== GDK_VoidSymbol
) /* wildcard */
386 && current
->Element
.key
== keyval
387 && current
->Element
.func
)
388 if (current
->Element
.func(¤t
->Element
.arg
)) {
389 current_modkey
= count
= 0;
393 current
= current
->next
;
399 webview_keypress_cb(WebKitWebView
*webview
, GdkEventKey
*event
) {
400 Arg a
= { .i
= ModeNormal
, .s
= NULL
};
402 GdkModifierType irrelevant
;
404 /* Get a mask of modifiers that shouldn't be considered for this event.
405 * E.g.: It shouldn't matter whether ';' is shifted or not. */
406 gdk_keymap_translate_keyboard_state(keymap
, event
->hardware_keycode
,
407 event
->state
, event
->group
, &keyval
, NULL
, NULL
, &irrelevant
);
411 if ((CLEAN(event
->state
) & ~irrelevant
) == 0) {
412 if (IS_ESCAPE(event
)) {
417 } else if (current_modkey
== 0 && ((event
->keyval
>= GDK_1
&& event
->keyval
<= GDK_9
)
418 || (event
->keyval
== GDK_0
&& count
))) {
419 count
= (count
? count
* 10 : 0) + (event
->keyval
- GDK_0
);
422 } else if (strchr(modkeys
, event
->keyval
) && current_modkey
!= event
->keyval
) {
423 current_modkey
= event
->keyval
;
429 if (process_keypress(event
) == TRUE
) return TRUE
;
433 if (IS_ESCAPE(event
)) {
435 a
.s
= g_strdup("hints.clearFocus();");
440 } else if (CLEAN(event
->state
) & GDK_CONTROL_MASK
) {
441 /* keybindings of non-printable characters */
442 if (process_keypress(event
) == TRUE
) return TRUE
;
444 case ModePassThrough
:
445 if (IS_ESCAPE(event
)) {
460 set_widget_font_and_color(GtkWidget
*widget
, const char *font_str
, const char *bg_color_str
,
461 const char *fg_color_str
) {
464 PangoFontDescription
*font
;
466 font
= pango_font_description_from_string(font_str
);
467 gtk_widget_modify_font(widget
, font
);
468 pango_font_description_free(font
);
471 gdk_color_parse(fg_color_str
, &fg_color
);
473 gdk_color_parse(bg_color_str
, &bg_color
);
475 gtk_widget_modify_text(widget
, GTK_STATE_NORMAL
, fg_color_str
? &fg_color
: NULL
);
476 gtk_widget_modify_base(widget
, GTK_STATE_NORMAL
, bg_color_str
? &bg_color
: NULL
);
482 webview_hoverlink_cb(WebKitWebView
*webview
, char *title
, char *link
, gpointer data
) {
483 const char *uri
= webkit_web_view_get_uri(webview
);
486 memset(rememberedURI
, 0, 1024);
488 markup
= g_markup_printf_escaped("<span font=\"%s\">Link: %s</span>", statusfont
, link
);
489 gtk_label_set_markup(GTK_LABEL(status_url
), markup
);
490 strncpy(rememberedURI
, link
, 1024);
497 webview_console_cb(WebKitWebView
*webview
, char *message
, int line
, char *source
, gpointer user_data
) {
500 /* Don't change internal mode if the browser doesn't have focus to prevent inconsistent states */
501 if (gtk_window_has_toplevel_focus(window
)) {
502 if (!strcmp(message
, "hintmode_off") || !strcmp(message
, "insertmode_off")) {
505 } else if (!strcmp(message
, "insertmode_on")) {
514 inputbox_activate_cb(GtkEntry
*entry
, gpointer user_data
) {
516 guint16 length
= gtk_entry_get_text_length(entry
);
518 gboolean success
= FALSE
, forward
= FALSE
;
520 a
.i
= HideCompletion
;
524 text
= (char*)gtk_entry_get_text(entry
);
525 if (length
> 1 && text
[0] == ':') {
526 success
= process_line((text
+ 1));
527 } else if (length
> 1 && ((forward
= text
[0] == '/') || text
[0] == '?')) {
528 webkit_web_view_unmark_text_matches(webview
);
529 #ifdef ENABLE_MATCH_HIGHLITING
530 webkit_web_view_mark_text_matches(webview
, &text
[1], FALSE
, 0);
531 webkit_web_view_set_highlight_text_matches(webview
, TRUE
);
534 #ifndef ENABLE_INCREMENTAL_SEARCH
536 a
.i
= searchoptions
| (forward
? DirectionForward
: DirectionBackwards
);
539 search_direction
= forward
;
540 search_handle
= g_strdup(&text
[1]);
542 } else if (text
[0] == '.' || text
[0] == ',' || text
[0] == ';') {
544 a
.s
= g_strdup_printf("hints.fire();");
551 gtk_entry_set_text(entry
, "");
552 gtk_widget_grab_focus(GTK_WIDGET(webview
));
556 inputbox_keypress_cb(GtkEntry
*entry
, GdkEventKey
*event
) {
560 if (mode
== ModeHints
) {
561 if (event
->keyval
== GDK_Tab
) {
563 a
.s
= g_strdup_printf("hints.focusNextHint();");
569 if (event
->keyval
== GDK_ISO_Left_Tab
) {
571 a
.s
= g_strdup_printf("hints.focusPreviousHint();");
577 if (event
->keyval
== GDK_Return
) {
579 a
.s
= g_strdup_printf("hints.fire();");
586 switch (event
->keyval
) {
587 case GDK_bracketleft
:
589 if (!IS_ESCAPE(event
)) break;
590 a
.i
= HideCompletion
;
602 return commandhistoryfetch(&a
);
606 return commandhistoryfetch(&a
);
608 case GDK_ISO_Left_Tab
:
614 if (mode
== ModeHints
) {
615 if ((CLEAN(event
->state
) & GDK_SHIFT_MASK
) &&
616 (CLEAN(event
->state
) & GDK_CONTROL_MASK
) &&
617 (event
->keyval
== GDK_BackSpace
)) {
620 a
.s
= g_strdup_printf("hints.updateHints(%d);", count
);
627 numval
= g_unichar_digit_value((gunichar
) gdk_keyval_to_unicode(event
->keyval
));
628 if ((numval
>= 1 && numval
<= 9) || (numval
== 0 && count
)) {
629 /* allow a zero as non-first number */
630 count
= (count
? count
* 10 : 0) + numval
;
632 a
.s
= g_strdup_printf("hints.updateHints(%d);", count
);
644 notify_event_cb(GtkWidget
*widget
, GdkEvent
*event
, gpointer user_data
) {
646 WebKitHitTestResult
*result
;
647 WebKitHitTestResultContext context
;
648 if (mode
== ModeNormal
&& event
->type
== GDK_BUTTON_RELEASE
) {
649 /* handle mouse click events */
650 for (i
= 0; i
< LENGTH(mouse
); i
++) {
651 if (mouse
[i
].mask
== CLEAN(event
->button
.state
)
652 && (mouse
[i
].modkey
== current_modkey
653 || (!mouse
[i
].modkey
&& !current_modkey
)
654 || mouse
[i
].modkey
== GDK_VoidSymbol
) /* wildcard */
655 && mouse
[i
].button
== event
->button
.button
657 if (mouse
[i
].func(&mouse
[i
].arg
)) {
658 current_modkey
= count
= 0;
664 result
= webkit_web_view_get_hit_test_result(WEBKIT_WEB_VIEW(widget
), (GdkEventButton
*)event
);
665 g_object_get(result
, "context", &context
, NULL
);
666 if (context
& WEBKIT_HIT_TEST_RESULT_CONTEXT_EDITABLE
) {
667 Arg a
= { .i
= ModeInsert
};
671 } else if (mode
== ModeInsert
&& event
->type
== GDK_BUTTON_RELEASE
) {
672 result
= webkit_web_view_get_hit_test_result(WEBKIT_WEB_VIEW(widget
), (GdkEventButton
*)event
);
673 g_object_get(result
, "context", &context
, NULL
);
674 if (!(context
& WEBKIT_HIT_TEST_RESULT_CONTEXT_EDITABLE
)) {
675 Arg a
= { .i
= ModeNormal
};
679 gchar
*value
= NULL
, *message
= NULL
;
680 jsapi_evaluate_script("window.getSelection().focusNode", &value
, &message
);
681 if (value
&& !strcmp(value
, "[object HTMLFormElement]")) {
682 Arg a
= { .i
= ModeInsert
, .s
= NULL
};
692 static gboolean
inputbox_keyrelease_cb(GtkEntry
*entry
, GdkEventKey
*event
) {
694 guint16 length
= gtk_entry_get_text_length(entry
);
697 a
.i
= HideCompletion
;
705 static gboolean
inputbox_changed_cb(GtkEditable
*entry
, gpointer user_data
) {
707 char *text
= (char*)gtk_entry_get_text(GTK_ENTRY(entry
));
708 guint16 length
= gtk_entry_get_text_length(GTK_ENTRY(entry
));
709 gboolean forward
= FALSE
;
711 /* Update incremental search if the user changes the search text.
713 * Note: gtk_widget_is_focus() is a poor way to check if the change comes
714 * from the user. But if the entry is focused and the text is set
715 * through gtk_entry_set_text() in some asyncrounous operation,
716 * I would consider that a bug.
719 if (gtk_widget_is_focus(GTK_WIDGET(entry
)) && length
> 1 && ((forward
= text
[0] == '/') || text
[0] == '?')) {
720 webkit_web_view_unmark_text_matches(webview
);
721 webkit_web_view_search_text(webview
, &text
[1], searchoptions
& CaseSensitive
, forward
, searchoptions
& Wrapping
);
723 } else if (gtk_widget_is_focus(GTK_WIDGET(entry
)) && length
>= 1 &&
724 (text
[0] == '.' || text
[0] == ',' || text
[0] == ';')) {
728 a
.s
= g_strconcat("hints.createHints('", text
+ 1, "', 'f');", NULL
);
732 a
.s
= g_strconcat("hints.createHints('", text
+ 1, "', 'F');", NULL
);
739 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 's');", NULL
);
742 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 'y');", NULL
);
745 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 'f');", NULL
);
748 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 'F');", NULL
);
750 case 'O': case 'T': case 'W':
751 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 'O');", NULL
);
754 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 'i');", NULL
);
757 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 'I');", NULL
);
769 } else if (length
== 0 && followTarget
[0]) {
772 a
.s
= g_strdup("hints.clearHints();");
784 void fill_suggline(char * suggline
, const char * command
, const char *fill_with
) {
785 memset(suggline
, 0, 512);
786 strncpy(suggline
, command
, 512);
787 strncat(suggline
, " ", 1);
788 strncat(suggline
, fill_with
, 512 - strlen(suggline
) - 1);
791 GtkWidget
* fill_eventbox(const char * completion_line
) {
793 GtkWidget
*row_eventbox
, *el
;
795 char *markup
, *markup_tmp
;
797 row
= GTK_BOX(gtk_hbox_new(FALSE
, 0));
798 row_eventbox
= gtk_event_box_new();
799 gdk_color_parse(completionbgcolor
[0], &color
);
800 gtk_widget_modify_bg(row_eventbox
, GTK_STATE_NORMAL
, &color
);
801 el
= gtk_label_new(NULL
);
802 markup_tmp
= g_markup_escape_text(completion_line
, strlen(completion_line
));
803 markup
= g_strconcat("<span font=\"", completionfont
[0], "\" color=\"", completioncolor
[0], "\">",
804 markup_tmp
, "</span>", NULL
);
805 gtk_label_set_markup(GTK_LABEL(el
), markup
);
808 gtk_misc_set_alignment(GTK_MISC(el
), 0, 0);
809 gtk_box_pack_start(row
, el
, TRUE
, TRUE
, 2);
810 gtk_container_add(GTK_CONTAINER(row_eventbox
), GTK_WIDGET(row
));
815 complete(const Arg
*arg
) {
816 char *str
, *p
, *s
, *markup
, *entry
, *searchfor
, command
[32] = "", suggline
[512] = "", **suggurls
;
817 size_t listlen
, len
, cmdlen
;
819 Listelement
*elementlist
= NULL
, *elementpointer
;
820 gboolean highlight
= FALSE
;
822 GtkWidget
*row_eventbox
, *el
;
825 static GtkWidget
*table
, *top_border
;
827 static char **suggestions
;
828 static GtkWidget
**widgets
;
829 static int n
= 0, m
, current
= -1;
831 str
= (char*)gtk_entry_get_text(GTK_ENTRY(inputbox
));
834 /* Get the length of the list of commands for completion. We need this to
835 * malloc/realloc correctly.
837 listlen
= LENGTH(commands
);
839 if ((len
== 0 || str
[0] != ':') && arg
->i
!= HideCompletion
)
842 if (arg
->i
!= HideCompletion
&& widgets
&& current
!= -1 && !strcmp(&str
[1], suggestions
[current
])) {
843 gdk_color_parse(completionbgcolor
[0], &color
);
844 gtk_widget_modify_bg(widgets
[current
], GTK_STATE_NORMAL
, &color
);
845 current
= (n
+ current
+ (arg
->i
== DirectionPrev
? -1 : 1)) % n
;
846 if ((arg
->i
== DirectionNext
&& current
== 0)
847 || (arg
->i
== DirectionPrev
&& current
== n
- 1))
853 gtk_widget_destroy(GTK_WIDGET(table
));
854 gtk_widget_destroy(GTK_WIDGET(top_border
));
861 if (arg
->i
== HideCompletion
)
864 } else if (arg
->i
== HideCompletion
)
867 prefix
= g_strdup(str
);
868 widgets
= malloc(sizeof(GtkWidget
*) * listlen
);
869 suggestions
= malloc(sizeof(char*) * listlen
);
870 top_border
= gtk_event_box_new();
871 gtk_widget_set_size_request(GTK_WIDGET(top_border
), 0, 1);
872 gdk_color_parse(completioncolor
[2], &color
);
873 gtk_widget_modify_bg(top_border
, GTK_STATE_NORMAL
, &color
);
874 table
= gtk_event_box_new();
875 gdk_color_parse(completionbgcolor
[0], &color
);
876 _table
= GTK_BOX(gtk_vbox_new(FALSE
, 0));
878 if (strchr(str
, ' ') == NULL
) {
879 /* command completion */
880 listlen
= LENGTH(commands
);
881 for (i
= 0; i
< listlen
; i
++) {
882 if (commands
[i
].cmd
== NULL
)
884 cmdlen
= strlen(commands
[i
].cmd
);
885 if (!highlight
|| (n
< MAX_LIST_SIZE
&& len
- 1 <= cmdlen
&& !strncmp(&str
[1], commands
[i
].cmd
, len
- 1))) {
886 p
= s
= malloc(sizeof(char*) * (highlight
? sizeof(COMPLETION_TAG_OPEN
) + sizeof(COMPLETION_TAG_CLOSE
) - 1 : 1) + cmdlen
);
888 memcpy(p
, COMPLETION_TAG_OPEN
, sizeof(COMPLETION_TAG_OPEN
) - 1);
889 memcpy((p
+= sizeof(COMPLETION_TAG_OPEN
) - 1), &str
[1], len
- 1);
890 memcpy((p
+= len
- 1), COMPLETION_TAG_CLOSE
, sizeof(COMPLETION_TAG_CLOSE
) - 1);
891 p
+= sizeof(COMPLETION_TAG_CLOSE
) - 1;
893 memcpy(p
, &commands
[i
].cmd
[len
- 1], cmdlen
- len
+ 2);
894 row
= GTK_BOX(gtk_hbox_new(FALSE
, 0));
895 row_eventbox
= gtk_event_box_new();
896 gtk_widget_modify_bg(row_eventbox
, GTK_STATE_NORMAL
, &color
);
897 el
= gtk_label_new(NULL
);
898 markup
= g_strconcat("<span font=\"", completionfont
[0], "\" color=\"", completioncolor
[0], "\">", s
, "</span>", NULL
);
900 gtk_label_set_markup(GTK_LABEL(el
), markup
);
902 gtk_misc_set_alignment(GTK_MISC(el
), 0, 0);
903 gtk_box_pack_start(row
, el
, TRUE
, TRUE
, 2);
904 gtk_container_add(GTK_CONTAINER(row_eventbox
), GTK_WIDGET(row
));
905 gtk_box_pack_start(_table
, GTK_WIDGET(row_eventbox
), FALSE
, FALSE
, 0);
906 suggestions
[n
] = commands
[i
].cmd
;
907 widgets
[n
++] = row_eventbox
;
911 entry
= (char *)malloc(512 * sizeof(char));
915 memset(entry
, 0, 512);
916 suggurls
= malloc(sizeof(char*) * listlen
);
917 if (suggurls
== NULL
) {
920 spacepos
= strcspn(str
, " ");
921 searchfor
= (str
+ spacepos
+ 1);
922 strncpy(command
, (str
+ 1), spacepos
- 1);
923 if (strlen(command
) == 3 && strncmp(command
, "set", 3) == 0) {
924 /* browser settings */
925 listlen
= LENGTH(browsersettings
);
926 for (i
= 0; i
< listlen
; i
++) {
927 if (n
< MAX_LIST_SIZE
&& strstr(browsersettings
[i
].name
, searchfor
) != NULL
) {
929 fill_suggline(suggline
, command
, browsersettings
[i
].name
);
930 /* FIXME(HP): This memory is never freed */
931 suggurls
[n
] = (char *)malloc(sizeof(char) * 512 + 1);
932 strncpy(suggurls
[n
], suggline
, 512);
933 suggestions
[n
] = suggurls
[n
];
934 row_eventbox
= fill_eventbox(suggline
);
935 gtk_box_pack_start(_table
, GTK_WIDGET(row_eventbox
), FALSE
, FALSE
, 0);
936 widgets
[n
++] = row_eventbox
;
940 } else if (strlen(command
) == 2 && strncmp(command
, "qt", 2) == 0) {
941 /* completion on tags */
942 spacepos
= strcspn(str
, " ");
943 searchfor
= (str
+ spacepos
+ 1);
944 elementlist
= complete_list(searchfor
, 1, elementlist
);
946 /* URL completion: bookmarks */
947 elementlist
= complete_list(searchfor
, 0, elementlist
);
948 m
= count_list(elementlist
);
949 if (m
< MAX_LIST_SIZE
) {
950 /* URL completion: history */
951 elementlist
= complete_list(searchfor
, 2, elementlist
);
954 elementpointer
= elementlist
;
955 while (elementpointer
!= NULL
) {
956 fill_suggline(suggline
, command
, elementpointer
->element
);
957 /* FIXME(HP): This memory is never freed */
958 suggurls
[n
] = (char *)malloc(sizeof(char) * 512 + 1);
959 strncpy(suggurls
[n
], suggline
, 512);
960 suggestions
[n
] = suggurls
[n
];
961 row_eventbox
= fill_eventbox(suggline
);
962 gtk_box_pack_start(_table
, GTK_WIDGET(row_eventbox
), FALSE
, FALSE
, 0);
963 widgets
[n
++] = row_eventbox
;
964 elementpointer
= elementpointer
->next
;
965 if (n
>= MAX_LIST_SIZE
)
968 free_list(elementlist
);
969 if (suggurls
!= NULL
) {
978 /* TA: FIXME - this needs rethinking entirely. */
980 GtkWidget
**widgets_temp
= realloc(widgets
, sizeof(*widgets
) * n
);
981 if (widgets_temp
== NULL
&& widgets
== NULL
) {
982 fprintf(stderr
, "Couldn't realloc() widgets\n");
985 widgets
= widgets_temp
;
986 char **suggestions_temp
= realloc(suggestions
, sizeof(*suggestions
) * n
);
987 if (suggestions_temp
== NULL
&& suggestions
== NULL
) {
988 fprintf(stderr
, "Couldn't realloc() suggestions\n");
991 suggestions
= suggestions_temp
;
994 gdk_color_parse(completionbgcolor
[1], &color
);
995 gtk_widget_modify_bg(table
, GTK_STATE_NORMAL
, &color
);
996 el
= gtk_label_new(NULL
);
997 gtk_misc_set_alignment(GTK_MISC(el
), 0, 0);
998 markup
= g_strconcat("<span font=\"", completionfont
[1], "\" color=\"", completioncolor
[1], "\">No Completions</span>", NULL
);
999 gtk_label_set_markup(GTK_LABEL(el
), markup
);
1001 gtk_box_pack_start(_table
, GTK_WIDGET(el
), FALSE
, FALSE
, 0);
1003 gtk_box_pack_start(box
, GTK_WIDGET(top_border
), FALSE
, FALSE
, 0);
1004 gtk_container_add(GTK_CONTAINER(table
), GTK_WIDGET(_table
));
1005 gtk_box_pack_start(box
, GTK_WIDGET(table
), FALSE
, FALSE
, 0);
1006 gtk_widget_show_all(GTK_WIDGET(window
));
1009 current
= arg
->i
== DirectionPrev
? n
- 1 : 0;
1011 if (current
!= -1) {
1012 gdk_color_parse(completionbgcolor
[2], &color
);
1013 gtk_widget_modify_bg(GTK_WIDGET(widgets
[current
]), GTK_STATE_NORMAL
, &color
);
1014 s
= g_strconcat(":", suggestions
[current
], NULL
);
1015 gtk_entry_set_text(GTK_ENTRY(inputbox
), s
);
1018 gtk_entry_set_text(GTK_ENTRY(inputbox
), prefix
);
1019 gtk_editable_set_position(GTK_EDITABLE(inputbox
), -1);
1024 descend(const Arg
*arg
) {
1025 char *source
= (char*)webkit_web_view_get_uri(webview
), *p
= &source
[0], *new;
1027 count
= count
? count
: 1;
1031 if (arg
->i
== Rootdir
) {
1032 for (i
= 0; i
< 3; i
++) /* get to the third slash */
1033 if (!(p
= strchr(++p
, '/')))
1034 return TRUE
; /* if we cannot find it quit */
1036 len
= strlen(source
);
1037 if (!len
) /* if string is empty quit */
1039 p
= source
+ len
; /* start at the end */
1040 if (*(p
- 1) == '/') /* /\/$/ is not an additional level */
1042 for (i
= 0; i
< count
; i
++)
1043 while(*(p
--) != '/' || *p
== '/') /* count /\/+/ as one slash */
1044 if (p
== source
) /* if we reach the first char pointer quit */
1046 ++p
; /* since we do p-- in the while, we are pointing at
1047 the char before the slash, so +1 */
1049 len
= p
- source
+ 1; /* new length = end - start + 1 */
1050 new = malloc(len
+ 1);
1051 memcpy(new, source
, len
);
1053 webkit_web_view_load_uri(webview
, new);
1059 echo(const Arg
*arg
) {
1060 int index
= !arg
->s
? 0 : arg
->i
& (~NoAutoHide
);
1062 if (index
< Info
|| index
> Error
)
1065 if (!gtk_widget_is_focus(GTK_WIDGET(inputbox
))) {
1066 set_widget_font_and_color(inputbox
, urlboxfont
[index
], urlboxbgcolor
[index
], urlboxcolor
[index
]);
1067 gtk_entry_set_text(GTK_ENTRY(inputbox
), !arg
->s
? "" : arg
->s
);
1074 input(const Arg
*arg
) {
1081 /* if inputbox hidden, show it again */
1082 if (!gtk_widget_get_visible(inputbox
))
1083 gtk_widget_set_visible(inputbox
, TRUE
);
1087 /* Set the colour and font back to the default, so that we don't still
1088 * maintain a red colour from a warning from an end of search indicator,
1091 set_widget_font_and_color(inputbox
, urlboxfont
[index
], urlboxbgcolor
[index
], urlboxcolor
[index
]);
1093 /* to avoid things like :open URL :open URL2 or :open :open URL */
1094 gtk_entry_set_text(GTK_ENTRY(inputbox
), "");
1095 gtk_editable_insert_text(GTK_EDITABLE(inputbox
), arg
->s
, -1, &pos
);
1096 if (arg
->i
& InsertCurrentURL
&& (url
= webkit_web_view_get_uri(webview
)))
1097 gtk_editable_insert_text(GTK_EDITABLE(inputbox
), url
, -1, &pos
);
1099 gtk_widget_grab_focus(inputbox
);
1100 gtk_editable_set_position(GTK_EDITABLE(inputbox
), -1);
1102 if (arg
->s
[0] == '.' || arg
->s
[0] == ',' || arg
->s
[0] == ';') {
1104 memset(followTarget
, 0, 8);
1105 strncpy(followTarget
, "current", 8);
1107 switch (arg
->s
[0]) {
1109 a
.s
= g_strdup("hints.createHints('', 'f');");
1113 a
.s
= g_strdup("hints.createHints('', 'F');");
1119 switch (arg
->s
[1]) {
1121 a
.s
= g_strdup("hints.createHints('', 's');");
1124 a
.s
= g_strdup("hints.createHints('', 'y');");
1127 a
.s
= g_strdup("hints.createHints('', 'f');");
1130 a
.s
= g_strdup("hints.createHints('', 'F');");
1132 case 'O': case 'T': case 'W':
1133 a
.s
= g_strdup("hints.createHints('', 'O');");
1136 a
.s
= g_strdup("hints.createHints('', 'i');");
1139 a
.s
= g_strdup("hints.createHints('', 'I');");
1156 navigate(const Arg
*arg
) {
1157 if (arg
->i
& NavigationForwardBack
)
1158 webkit_web_view_go_back_or_forward(webview
, (arg
->i
== NavigationBack
? -1 : 1) * (count
? count
: 1));
1159 else if (arg
->i
& NavigationReloadActions
)
1160 (arg
->i
== NavigationReload
? webkit_web_view_reload
: webkit_web_view_reload_bypass_cache
)(webview
);
1162 webkit_web_view_stop_loading(webview
);
1167 number(const Arg
*arg
) {
1168 const char *source
= webkit_web_view_get_uri(webview
);
1169 char *uri
, *p
, *new;
1170 int number
, diff
= (count
? count
: 1) * (arg
->i
== Increment
? 1 : -1);
1174 uri
= g_strdup(source
); /* copy string */
1176 while(*p
!= '\0') /* goto the end of the string */
1179 while(*p
>= '0' && *p
<= '9') /* go back until non number char is reached */
1181 if (*(++p
) == '\0') { /* if no numbers were found abort */
1185 number
= atoi(p
) + diff
; /* apply diff on number */
1187 new = g_strdup_printf("%s%d", uri
, number
); /* create new uri */
1188 webkit_web_view_load_uri(webview
, new);
1195 open_arg(const Arg
*arg
) {
1197 char *s
= arg
->s
, *p
= NULL
, *new;
1198 Arg a
= { .i
= NavigationReload
};
1200 char *search_uri
, *search_term
;
1216 else if (arg
->i
== TargetCurrent
) {
1217 while(*s
== ' ') /* strip leading whitespace */
1219 p
= (s
+ strlen(s
) - 1);
1220 while(*p
== ' ') /* strip trailing whitespace */
1225 /* check for external handlers */
1226 if (open_handler(s
))
1228 /* check for search engines */
1230 if (p
) { /* check for search engines */
1232 search_uri
= find_uri_for_searchengine(s
);
1233 if (search_uri
!= NULL
) {
1234 search_term
= soup_uri_encode(p
+1, "&");
1235 new = g_strdup_printf(search_uri
, search_term
);
1236 g_free(search_term
);
1241 if (len
> 3 && strstr(s
, "://")) { /* valid url? */
1242 p
= new = g_malloc(len
+ 1);
1243 while(*s
!= '\0') { /* strip whitespaces */
1249 } else if (strcspn(s
, "/") == 0 || strcspn(s
, "./") == 0) { /* prepend "file://" */
1250 new = g_malloc(sizeof("file://") + len
);
1251 strcpy(new, "file://");
1252 memcpy(&new[sizeof("file://") - 1], s
, len
+ 1);
1253 } else if (p
|| !strchr(s
, '.')) { /* whitespaces or no dot? */
1254 search_uri
= find_uri_for_searchengine(defaultsearch
);
1255 if (search_uri
!= NULL
) {
1256 search_term
= soup_uri_encode(s
, "&");
1257 new = g_strdup_printf(search_uri
, search_term
);
1258 g_free(search_term
);
1260 } else { /* prepend "http://" */
1261 new = g_malloc(sizeof("http://") + len
);
1262 strcpy(new, "http://");
1263 memcpy(&new[sizeof("http://") - 1], s
, len
+ 1);
1266 webkit_web_view_load_uri(webview
, new);
1269 g_spawn_async(NULL
, argv
, NULL
, G_SPAWN_SEARCH_PATH
, NULL
, NULL
, NULL
, NULL
);
1274 open_remembered(const Arg
*arg
)
1276 Arg a
= {arg
->i
, rememberedURI
};
1278 if (strcmp(rememberedURI
, "")) {
1285 yank(const Arg
*arg
) {
1286 const char *url
, *feedback
, *content
;
1288 if (arg
->i
& SourceSelection
) {
1289 webkit_web_view_copy_clipboard(webview
);
1290 if (arg
->i
& ClipboardPrimary
)
1291 content
= gtk_clipboard_wait_for_text(clipboards
[0]);
1292 if (!content
&& arg
->i
& ClipboardGTK
)
1293 content
= gtk_clipboard_wait_for_text(clipboards
[1]);
1295 feedback
= g_strconcat("Yanked ", content
, NULL
);
1296 g_free((gpointer
*)content
);
1297 give_feedback(feedback
);
1298 g_free((gpointer
*)feedback
);
1301 if (arg
->i
& SourceURL
) {
1302 url
= webkit_web_view_get_uri(webview
);
1308 feedback
= g_strconcat("Yanked ", url
, NULL
);
1309 give_feedback(feedback
);
1310 if (arg
->i
& ClipboardPrimary
)
1311 gtk_clipboard_set_text(clipboards
[0], url
, -1);
1312 if (arg
->i
& ClipboardGTK
)
1313 gtk_clipboard_set_text(clipboards
[1], url
, -1);
1319 paste(const Arg
*arg
) {
1320 Arg a
= { .i
= arg
->i
& TargetNew
, .s
= NULL
};
1322 /* If we're over a link, open it in a new target. */
1323 if (strlen(rememberedURI
) > 0) {
1324 Arg new_target
= { .i
= TargetNew
, .s
= arg
->s
};
1325 open_arg(&new_target
);
1329 if (arg
->i
& ClipboardPrimary
)
1330 a
.s
= gtk_clipboard_wait_for_text(clipboards
[0]);
1331 if (!a
.s
&& arg
->i
& ClipboardGTK
)
1332 a
.s
= gtk_clipboard_wait_for_text(clipboards
[1]);
1341 quit(const Arg
*arg
) {
1343 const char *filename
;
1344 const char *uri
= webkit_web_view_get_uri(webview
);
1346 /* write last URL into status file for recreation with "u" */
1347 filename
= g_strdup_printf(CLOSED_URL_FILENAME
);
1348 f
= fopen(filename
, "w");
1349 g_free((gpointer
*)filename
);
1351 fprintf(f
, "%s", uri
);
1360 revive(const Arg
*arg
) {
1362 const char *filename
;
1363 char buffer
[512] = "";
1364 Arg a
= { .i
= TargetNew
, .s
= NULL
};
1365 /* get the URL of the window which has been closed last */
1366 filename
= g_strdup_printf(CLOSED_URL_FILENAME
);
1367 f
= fopen(filename
, "r");
1368 g_free((gpointer
*)filename
);
1370 fgets(buffer
, 512, f
);
1373 if (strlen(buffer
) > 0) {
1382 gboolean
print_frame(const Arg
*arg
)
1384 WebKitWebFrame
*frame
= webkit_web_view_get_main_frame(webview
);
1385 webkit_web_frame_print (frame
);
1390 search(const Arg
*arg
) {
1391 count
= count
? count
: 1;
1392 gboolean success
, direction
= arg
->i
& DirectionPrev
;
1396 free(search_handle
);
1397 search_handle
= g_strdup(arg
->s
);
1401 if (arg
->i
& DirectionAbsolute
)
1402 search_direction
= direction
;
1404 direction
^= search_direction
;
1406 success
= webkit_web_view_search_text(webview
, search_handle
, arg
->i
& CaseSensitive
, direction
, FALSE
);
1408 if (arg
->i
& Wrapping
) {
1409 success
= webkit_web_view_search_text(webview
, search_handle
, arg
->i
& CaseSensitive
, direction
, TRUE
);
1412 a
.s
= g_strdup_printf("search hit %s, continuing at %s",
1413 direction
? "BOTTOM" : "TOP",
1414 direction
? "TOP" : "BOTTOM");
1425 a
.s
= g_strdup_printf("Pattern not found: %s", search_handle
);
1433 set(const Arg
*arg
) {
1434 Arg a
= { .i
= Info
| NoAutoHide
};
1438 if (search_handle
) {
1439 search_handle
= NULL
;
1440 webkit_web_view_unmark_text_matches(webview
);
1442 gtk_entry_set_text(GTK_ENTRY(inputbox
), "");
1443 gtk_widget_grab_focus(GTK_WIDGET(webview
));
1445 case ModePassThrough
:
1446 a
.s
= g_strdup("-- PASS THROUGH --");
1451 a
.s
= g_strdup("-- PASS TROUGH (next) --");
1455 case ModeInsert
: /* should not be called manually but automatically */
1456 a
.s
= g_strdup("-- INSERT --");
1468 jsapi_ref_to_string(JSContextRef context
, JSValueRef ref
) {
1469 JSStringRef string_ref
;
1473 string_ref
= JSValueToStringCopy(context
, ref
, NULL
);
1474 length
= JSStringGetMaximumUTF8CStringSize(string_ref
);
1475 string
= g_new(gchar
, length
);
1476 JSStringGetUTF8CString(string_ref
, string
, length
);
1477 JSStringRelease(string_ref
);
1482 jsapi_evaluate_script(const gchar
*script
, gchar
**value
, gchar
**message
) {
1483 WebKitWebFrame
*frame
= webkit_web_view_get_main_frame(webview
);
1484 JSGlobalContextRef context
= webkit_web_frame_get_global_context(frame
);
1486 JSValueRef val
, exception
;
1488 str
= JSStringCreateWithUTF8CString(script
);
1489 val
= JSEvaluateScript(context
, str
, JSContextGetGlobalObject(context
), NULL
, 0, &exception
);
1490 JSStringRelease(str
);
1492 *message
= jsapi_ref_to_string(context
, exception
);
1494 *value
= jsapi_ref_to_string(context
, val
);
1498 quickmark(const Arg
*a
) {
1501 char *fn
= g_strdup_printf(QUICKMARK_FILE
);
1503 fp
= fopen(fn
, "r");
1508 if (fp
!= NULL
&& b
< 10) {
1509 for( i
=0; i
< b
; ++i
) {
1513 fgets(buf
, 100, fp
);
1515 char *ptr
= strrchr(buf
, '\n');
1517 Arg x
= { .s
= buf
};
1519 return open_arg(&x
);
1522 x
.s
= g_strdup_printf("Quickmark %d not defined", b
);
1527 } else { return false; }
1531 script(const Arg
*arg
) {
1532 gchar
*value
= NULL
, *message
= NULL
;
1533 char text
[1024] = "";
1535 WebKitNetworkRequest
*request
;
1536 WebKitDownload
*download
;
1539 set_error("Missing argument.");
1542 jsapi_evaluate_script(arg
->s
, &value
, &message
);
1549 if (arg
->i
!= Silent
&& value
) {
1551 a
.s
= g_strdup(value
);
1555 /* switch mode according to scripts return value */
1557 if (strncmp(value
, "done;", 5) == 0) {
1560 } else if (strncmp(value
, "insert;", 7) == 0) {
1563 manual_focus
= TRUE
;
1564 } else if (strncmp(value
, "save;", 5) == 0) {
1565 /* forced download */
1568 request
= webkit_network_request_new((value
+ 5));
1569 download
= webkit_download_new(request
);
1570 webview_download_cb(webview
, download
, (gpointer
*)NULL
);
1571 } else if (strncmp(value
, "yank;", 5) == 0) {
1572 /* yank link URL to clipboard */
1575 a
.i
= ClipboardPrimary
| ClipboardGTK
;
1578 } else if (strncmp(value
, "colon;", 6) == 0) {
1579 /* use link URL for colon command */
1580 strncpy(text
, (char *)gtk_entry_get_text(GTK_ENTRY(inputbox
)), 1023);
1585 a
.s
= g_strconcat(":open ", (value
+ 6), NULL
);
1588 a
.s
= g_strconcat(":tabopen ", (value
+ 6), NULL
);
1595 } else if (strncmp(value
, "error;", 6) == 0) {
1605 scroll(const Arg
*arg
) {
1606 GtkAdjustment
*adjust
= (arg
->i
& OrientationHoriz
) ? adjust_h
: adjust_v
;
1607 int max
= gtk_adjustment_get_upper(adjust
) - gtk_adjustment_get_page_size(adjust
);
1608 float val
= gtk_adjustment_get_value(adjust
) / max
* 100;
1609 int direction
= (arg
->i
& (1 << 2)) ? 1 : -1;
1611 if ((direction
== 1 && val
< 100) || (direction
== -1 && val
> 0)) {
1612 if (arg
->i
& ScrollMove
)
1613 gtk_adjustment_set_value(adjust
, gtk_adjustment_get_value(adjust
) +
1614 direction
* /* direction */
1615 ((arg
->i
& UnitLine
|| (arg
->i
& UnitBuffer
&& count
)) ? (scrollstep
* (count
? count
: 1)) : (
1616 arg
->i
& UnitBuffer
? gtk_adjustment_get_page_size(adjust
) / 2 :
1617 (count
? count
: 1) * (gtk_adjustment_get_page_size(adjust
) -
1618 (gtk_adjustment_get_page_size(adjust
) > pagingkeep
? pagingkeep
: 0)))));
1620 gtk_adjustment_set_value(adjust
,
1621 ((direction
== 1) ? gtk_adjustment_get_upper
: gtk_adjustment_get_lower
)(adjust
));
1628 zoom(const Arg
*arg
) {
1629 webkit_web_view_set_full_content_zoom(webview
, (arg
->i
& ZoomFullContent
) > 0);
1630 webkit_web_view_set_zoom_level(webview
, (arg
->i
& ZoomOut
) ?
1631 webkit_web_view_get_zoom_level(webview
) +
1632 (((float)(count
? count
: 1)) * (arg
->i
& (1 << 1) ? 1.0 : -1.0) * zoomstep
) :
1633 (count
? (float)count
/ 100.0 : 1.0));
1638 fake_key_event(const Arg
*a
) {
1645 if ( (xdpy
= XOpenDisplay(NULL
)) == NULL
) {
1646 err
.s
= g_strdup("Couldn't find the XDisplay.");
1654 xk
.subwindow
= None
;
1655 xk
.time
= CurrentTime
;
1656 xk
.same_screen
= True
;
1657 xk
.x
= xk
.y
= xk
.x_root
= xk
.y_root
= 1;
1662 err
.s
= g_strdup("Zero pointer as argument! Check your config.h");
1669 if( (keysym
= XStringToKeysym(a
->s
)) == NoSymbol
) {
1670 err
.s
= g_strdup_printf("Couldn't translate %s to keysym", a
->s
);
1676 if( (xk
.keycode
= XKeysymToKeycode(xdpy
, keysym
)) == NoSymbol
) {
1677 err
.s
= g_strdup("Couldn't translate keysym to keycode");
1684 if( !XSendEvent(xdpy
, embed
, True
, KeyPressMask
, (XEvent
*)&xk
) ) {
1685 err
.s
= g_strdup("XSendEvent failed");
1697 commandhistoryfetch(const Arg
*arg
) {
1698 const int length
= g_list_length(commandhistory
);
1701 if (arg
->i
== DirectionPrev
) {
1702 commandpointer
= (length
+ commandpointer
- 1) % length
;
1704 commandpointer
= (length
+ commandpointer
+ 1) % length
;
1707 const char* command
= (char *)g_list_nth_data(commandhistory
, commandpointer
);
1708 gtk_entry_set_text(GTK_ENTRY(inputbox
), g_strconcat(":", command
, NULL
));
1709 gtk_editable_set_position(GTK_EDITABLE(inputbox
), -1);
1717 bookmark(const Arg
*arg
) {
1719 const char *filename
;
1720 const char *uri
= webkit_web_view_get_uri(webview
);
1721 const char *title
= webkit_web_view_get_title(webview
);
1722 filename
= g_strdup_printf(BOOKMARKS_STORAGE_FILENAME
);
1723 f
= fopen(filename
, "a");
1724 g_free((gpointer
*)filename
);
1725 if (uri
== NULL
|| strlen(uri
) == 0) {
1726 set_error("No URI found to bookmark.");
1730 fprintf(f
, "%s", uri
);
1731 if (title
!= NULL
) {
1732 fprintf(f
, "%s", " ");
1733 fprintf(f
, "%s", title
);
1735 if (arg
->s
&& strlen(arg
->s
)) {
1736 build_taglist(arg
, f
);
1738 fprintf(f
, "%s", "\n");
1740 give_feedback( "Bookmark saved" );
1743 set_error("Bookmarks file not found.");
1751 const char *filename
;
1752 const char *uri
= webkit_web_view_get_uri(webview
);
1753 const char *title
= webkit_web_view_get_title(webview
);
1754 char *entry
, buffer
[512], *new;
1756 gboolean finished
= FALSE
;
1758 if (title
!= NULL
) {
1759 entry
= malloc((strlen(uri
) + strlen(title
) + 2) * sizeof(char));
1760 memset(entry
, 0, strlen(uri
) + strlen(title
) + 2);
1762 entry
= malloc((strlen(uri
) + 1) * sizeof(char));
1763 memset(entry
, 0, strlen(uri
) + 1);
1765 if (entry
!= NULL
) {
1766 strncpy(entry
, uri
, strlen(uri
));
1767 if (title
!= NULL
) {
1768 strncat(entry
, " ", 1);
1769 strncat(entry
, title
, strlen(title
));
1772 filename
= g_strdup_printf(HISTORY_STORAGE_FILENAME
);
1773 f
= fopen(filename
, "r");
1775 new = (char *)malloc(HISTORY_MAX_ENTRIES
* 512 * sizeof(char) + 1);
1777 memset(new, 0, HISTORY_MAX_ENTRIES
* 512 * sizeof(char) + 1);
1778 /* newest entries go on top */
1779 strncpy(new, entry
, strlen(entry
));
1780 strncat(new, "\n", 1);
1781 /* retain at most HISTORY_MAX_ENTIRES - 1 old entries */
1782 while (finished
!= TRUE
) {
1783 if ((char *)NULL
== fgets(buffer
, 512, f
)) {
1784 /* check if end of file was reached / error occured */
1788 /* end of file reached */
1792 /* compare line (-1 because of newline character) */
1793 if (n
!= strlen(buffer
) - 1 || strncmp(entry
, buffer
, n
) != 0) {
1794 /* if the URI is already in history; we put it on top and skip it here */
1795 strncat(new, buffer
, 512);
1798 if ((i
+ 1) >= HISTORY_MAX_ENTRIES
) {
1804 f
= fopen(filename
, "w");
1805 g_free((gpointer
*)filename
);
1807 fprintf(f
, "%s", new);
1816 if (entry
!= NULL
) {
1825 view_source(const Arg
* arg
) {
1826 gboolean current_mode
= webkit_web_view_get_view_source_mode(webview
);
1827 webkit_web_view_set_view_source_mode(webview
, !current_mode
);
1828 webkit_web_view_reload(webview
);
1832 /* open an external editor defined by the protocol handler for
1833 vimprobableedit on a text box or similar */
1835 open_editor(const Arg
*arg
) {
1839 gchar
*value
= NULL
, *message
= NULL
, *tag
= NULL
, *edit_url
= NULL
;
1840 gchar
*temp_file_name
= g_strdup("/tmp/vimprobableeditXXXXXX");
1841 int temp_file_handle
= -1;
1843 /* check if active element is suitable for text editing */
1844 jsapi_evaluate_script("document.activeElement.tagName", &value
, &message
);
1847 tag
= g_strdup(value
);
1848 if (strcmp(tag
, "INPUT") == 0) {
1849 /* extra check: type == text */
1850 jsapi_evaluate_script("document.activeElement.type", &value
, &message
);
1851 if (strcmp(value
, "text") != 0) {
1856 } else if (strcmp(tag
, "TEXTAREA") != 0) {
1861 jsapi_evaluate_script("document.activeElement.value", &value
, &message
);
1862 text
= g_strdup(value
);
1869 /* write text into temporary file */
1870 temp_file_handle
= mkstemp(temp_file_name
);
1871 if (temp_file_handle
== -1) {
1872 message
= g_strdup_printf("Could not create temporary file: %s",
1874 give_feedback(message
);
1880 if (write(temp_file_handle
, text
, strlen(text
)) != strlen(text
)) {
1881 message
= g_strdup_printf("Short write to temporary file: %s",
1883 give_feedback(message
);
1889 close(temp_file_handle
);
1893 edit_url
= g_strdup_printf("vimprobableedit:%s", temp_file_name
);
1894 success
= open_handler_pid(edit_url
, &child_pid
);
1897 give_feedback("External editor open failed (no handler for"
1898 " vimprobableedit protocol?)");
1899 unlink(temp_file_name
);
1905 /* mark the active text box as "under processing" */
1906 jsapi_evaluate_script(
1907 "document.activeElement.disabled = true;"
1908 "document.activeElement.originalBackground = "
1909 " document.activeElement.style.background;"
1910 "document.activeElement.style.background = '#aaaaaa';"
1913 g_child_watch_add(child_pid
, _resume_from_editor
, temp_file_name
);
1915 /* temp_file_name is freed in _resume_from_editor */
1923 /* pick up from where open_editor left the work to the glib event loop.
1925 This is called when the external editor exits.
1927 The data argument points to allocated memory containing the temporary file
1930 _resume_from_editor(GPid child_pid
, int child_status
, gpointer data
) {
1932 GString
*new_text
= g_string_new("");
1933 g_spawn_close_pid(child_pid
);
1934 gchar
*value
= NULL
, *message
= NULL
;
1935 gchar
*temp_file_name
= data
;
1936 gchar buffer
[255] = "";
1938 jsapi_evaluate_script(
1939 "document.activeElement.disabled = true;"
1940 "document.activeElement.style.background = '#aaaaaa';"
1944 give_feedback("External editor returned with non-zero status,"
1945 " discarding edits.");
1949 /* re-read the new contents of the file and put it into the HTML element */
1950 if (!access(temp_file_name
, R_OK
) == 0) {
1951 message
= g_strdup_printf("Could not access temporary file: %s",
1955 fp
= fopen(temp_file_name
, "r");
1957 /* this would be too weird to even emit an error message */
1960 jsapi_evaluate_script("document.activeElement.value = '';",
1962 new_text
= g_string_append(new_text
, "\"");
1963 while (fgets(buffer
, 254, fp
)) {
1964 if (buffer
[strlen(buffer
)-1] == '\n') {
1965 /* encode line breaks into the string as Javascript does not like actual line breaks */
1966 new_text
= g_string_append_len(
1967 new_text
, buffer
, strlen(buffer
) - 1);
1968 new_text
= g_string_append(new_text
, "\\n");
1970 new_text
= g_string_append(new_text
, buffer
);
1973 new_text
= g_string_append(new_text
, "\"");
1975 /* FIXME: Is the memory returned by g_strconcat actually freed? */
1976 jsapi_evaluate_script(g_strconcat("document.activeElement.value = ",
1977 new_text
->str
, ";", NULL
), &value
, &message
);
1979 /* Fall through, error and normal exit are identical */
1982 g_string_free(new_text
, TRUE
);
1985 jsapi_evaluate_script(
1986 "document.activeElement.disabled = false;"
1987 "document.activeElement.style.background ="
1988 " document.activeElement.originalBackground;"
1991 unlink(temp_file_name
);
1992 g_free(temp_file_name
);
1998 focus_input(const Arg
*arg
) {
2001 a
.s
= g_strdup("hints.focusInput();");
2006 manual_focus
= TRUE
;
2011 browser_settings(const Arg
*arg
) {
2014 set_error("Missing argument.");
2017 strncpy(line
, arg
->s
, 254);
2018 if (process_set_line(line
))
2021 set_error("Invalid setting.");
2027 search_word(int whichword
) {
2029 static char word
[240];
2030 char *c
= my_pair
.line
;
2032 while (isspace(*c
) && *c
)
2035 switch (whichword
) {
2037 while (*c
&& !isspace (*c
) && *c
!= '=' && k
< 240) {
2042 strncpy(my_pair
.what
, word
, 20);
2045 while (*c
&& k
< 240) {
2050 strncpy(my_pair
.value
, word
, 240);
2058 process_set_line(char *line
) {
2062 WebKitWebSettings
*settings
;
2064 settings
= webkit_web_view_get_settings(webview
);
2065 my_pair
.line
= line
;
2067 if (!strlen(my_pair
.what
))
2070 while (isspace(*c
) && *c
)
2073 if (*c
== ':' || *c
== '=')
2079 listlen
= LENGTH(browsersettings
);
2080 for (i
= 0; i
< listlen
; i
++) {
2081 if (strlen(browsersettings
[i
].name
) == strlen(my_pair
.what
) && strncmp(browsersettings
[i
].name
, my_pair
.what
, strlen(my_pair
.what
)) == 0) {
2082 /* mandatory argument not provided */
2083 if (strlen(my_pair
.value
) == 0)
2085 /* process qmark? */
2086 if (strlen(my_pair
.what
) == 5 && strncmp("qmark", my_pair
.what
, 5) == 0) {
2087 return (process_save_qmark(my_pair
.value
, webview
));
2089 /* interpret boolean values */
2090 if (browsersettings
[i
].boolval
) {
2091 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) {
2093 } 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) {
2098 } else if (browsersettings
[i
].colourval
) {
2099 /* interpret as hexadecimal colour */
2100 if (!parse_colour(my_pair
.value
)) {
2104 if (browsersettings
[i
].var
!= NULL
) {
2105 strncpy(browsersettings
[i
].var
, my_pair
.value
, MAX_SETTING_SIZE
);
2106 if (strlen(my_pair
.value
) > MAX_SETTING_SIZE
- 1) {
2107 /* in this case, \0 will not have been copied */
2108 browsersettings
[i
].var
[MAX_SETTING_SIZE
- 1] = '\0';
2109 /* in case this string is also used for a webkit setting, make sure it's consistent */
2110 my_pair
.value
[MAX_SETTING_SIZE
- 1] = '\0';
2111 give_feedback("String too long; automatically truncated!");
2114 if (strlen(browsersettings
[i
].webkit
) > 0) {
2115 /* activate appropriate webkit setting */
2116 if (browsersettings
[i
].boolval
) {
2117 g_object_set((GObject
*)settings
, browsersettings
[i
].webkit
, boolval
, NULL
);
2118 } else if (browsersettings
[i
].intval
) {
2119 g_object_set((GObject
*)settings
, browsersettings
[i
].webkit
, atoi(my_pair
.value
), NULL
);
2121 g_object_set((GObject
*)settings
, browsersettings
[i
].webkit
, my_pair
.value
, NULL
);
2123 webkit_web_view_set_settings(webview
, settings
);
2126 if (strlen(my_pair
.what
) == 14) {
2127 if (strncmp("acceptlanguage", my_pair
.what
, 14) == 0) {
2128 g_object_set(G_OBJECT(session
), "accept-language", acceptlanguage
, NULL
);
2129 } else if (strncmp("completioncase", my_pair
.what
, 14) == 0) {
2130 complete_case_sensitive
= boolval
;
2132 } else if (strlen(my_pair
.what
) == 5 && strncmp("proxy", my_pair
.what
, 5) == 0) {
2133 toggle_proxy(boolval
);
2134 } else if (strlen(my_pair
.what
) == 10 && strncmp("scrollbars", my_pair
.what
, 10) == 0) {
2135 toggle_scrollbars(boolval
);
2136 } else if (strlen(my_pair
.what
) == 9 && strncmp("statusbar", my_pair
.what
, 9) == 0) {
2137 gtk_widget_set_visible(GTK_WIDGET(statusbar
), boolval
);
2138 } else if (strlen(my_pair
.what
) == 8 && strncmp("inputbox", my_pair
.what
, 8) == 0) {
2139 gtk_widget_set_visible(inputbox
, boolval
);
2140 } else if (strlen(my_pair
.what
) == 11 && strncmp("escapeinput", my_pair
.what
, 11) == 0) {
2141 escape_input_on_load
= boolval
;
2144 /* SSL certificate checking */
2145 if (strlen(my_pair
.what
) == 9 && strncmp("strictssl", my_pair
.what
, 9) == 0) {
2148 g_object_set(G_OBJECT(session
), "ssl-strict", TRUE
, NULL
);
2151 g_object_set(G_OBJECT(session
), "ssl-strict", FALSE
, NULL
);
2154 if (strlen(my_pair
.what
) == 8 && strncmp("cabundle", my_pair
.what
, 8) == 0) {
2155 g_object_set(G_OBJECT(session
), SOUP_SESSION_SSL_CA_FILE
, ca_bundle
, NULL
);
2157 if (strlen(my_pair
.what
) == 10 && strncmp("windowsize", my_pair
.what
, 10) == 0) {
2158 set_default_winsize(my_pair
.value
);
2162 if (browsersettings
[i
].reload
)
2163 webkit_web_view_reload(webview
);
2171 process_line(char *line
) {
2172 char *c
= line
, *command_hist
;
2174 size_t len
, length
= strlen(line
);
2175 gboolean found
= FALSE
, success
= FALSE
;
2180 /* Ignore blank lines. */
2184 command_hist
= g_strdup(c
);
2185 for (i
= 0; i
< LENGTH(commands
); i
++) {
2186 if (commands
[i
].cmd
== NULL
)
2188 len
= strlen(commands
[i
].cmd
);
2189 if (length
>= len
&& !strncmp(c
, commands
[i
].cmd
, len
) && (c
[len
] == ' ' || !c
[len
])) {
2191 a
.i
= commands
[i
].arg
.i
;
2192 a
.s
= g_strdup(length
> len
+ 1 ? &c
[len
+ 1] : commands
[i
].arg
.s
);
2193 success
= commands
[i
].func(&a
);
2199 save_command_history(command_hist
);
2200 g_free(command_hist
);
2204 a
.s
= g_strdup_printf("Not a browser command: %s", c
);
2207 } else if (!success
) {
2209 if (error_msg
!= NULL
) {
2210 a
.s
= g_strdup_printf("%s", error_msg
);
2214 a
.s
= g_strdup_printf("Unknown error. Please file a bug report!");
2223 search_tag(const Arg
* a
) {
2225 const char *filename
;
2226 const char *tag
= a
->s
;
2227 char s
[BUFFERSIZE
], foundtag
[40], url
[BUFFERSIZE
];
2231 /* The user must give us something to load up. */
2232 set_error("Bookmark tag required with this option.");
2236 if (strlen(tag
) > MAXTAGSIZE
) {
2237 set_error("Tag too long.");
2241 filename
= g_strdup_printf(BOOKMARKS_STORAGE_FILENAME
);
2242 f
= fopen(filename
, "r");
2243 g_free((gpointer
*)filename
);
2245 set_error("Couldn't open bookmarks file.");
2248 while (fgets(s
, BUFFERSIZE
-1, f
)) {
2251 while (isspace(s
[t
]))
2253 if (s
[t
] != ']') continue;
2266 foundtag
[i
++] = s
[k
++];
2268 /* foundtag now contains the tag */
2269 if (strlen(foundtag
) < MAXTAGSIZE
&& strcmp(tag
, foundtag
) == 0) {
2271 while (isspace(s
[i
])) i
++;
2273 while (s
[i
] && !isspace(s
[i
])) url
[k
++] = s
[i
++];
2275 Arg x
= { .i
= TargetNew
, .s
= url
};
2289 toggle_proxy(gboolean onoff
) {
2291 char *filename
, *new;
2293 if (onoff
== FALSE
) {
2294 g_object_set(session
, "proxy-uri", NULL
, NULL
);
2296 filename
= (char *)g_getenv("http_proxy");
2298 /* Fallthrough to checking HTTP_PROXY as well, since this can also be
2301 if (filename
== NULL
)
2302 filename
= (char *)g_getenv("HTTP_PROXY");
2304 if (filename
!= NULL
&& 0 < strlen(filename
)) {
2305 new = g_strrstr(filename
, "http://") ? g_strdup(filename
) : g_strdup_printf("http://%s", filename
);
2306 proxy_uri
= soup_uri_new(new);
2308 g_object_set(session
, "proxy-uri", proxy_uri
, NULL
);
2310 soup_uri_free(proxy_uri
);
2317 toggle_scrollbars(gboolean onoff
) {
2318 if (onoff
== TRUE
) {
2319 adjust_h
= gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(viewport
));
2320 adjust_v
= gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(viewport
));
2321 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(viewport
), GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
2323 adjust_v
= gtk_range_get_adjustment(GTK_RANGE(scroll_v
));
2324 adjust_h
= gtk_range_get_adjustment(GTK_RANGE(scroll_h
));
2325 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(viewport
), GTK_POLICY_NEVER
, GTK_POLICY_NEVER
);
2327 gtk_widget_set_scroll_adjustments (GTK_WIDGET(webview
), adjust_h
, adjust_v
);
2332 void set_default_winsize(const char * const size
) {
2334 int x
= 640, y
= 480;
2336 x
= strtol(size
, &p
, 10);
2337 if (errno
== ERANGE
|| x
<= 0) {
2342 if (p
== size
|| strlen(size
) == p
- size
)
2345 y
= strtol(p
+ 1, NULL
, 10);
2346 if (errno
== ERANGE
|| y
<= 0)
2350 gtk_window_resize(GTK_WINDOW(window
), x
, y
);
2354 update_url(const char *uri
) {
2355 gboolean ssl
= g_str_has_prefix(uri
, "https://");
2357 WebKitWebFrame
*frame
;
2358 WebKitWebDataSource
*src
;
2359 WebKitNetworkRequest
*request
;
2362 char *sslactivecolor
;
2364 #ifdef ENABLE_HISTORY_INDICATOR
2365 char before
[] = " [";
2367 gboolean back
= webkit_web_view_can_go_back(webview
);
2368 gboolean fwd
= webkit_web_view_can_go_forward(webview
);
2371 before
[0] = after
[0] = '\0';
2373 markup
= g_markup_printf_escaped(
2374 #ifdef ENABLE_HISTORY_INDICATOR
2375 "<span font=\"%s\">%s%s%s%s%s</span>", statusfont
, uri
,
2376 before
, back
? "+" : "", fwd
? "-" : "", after
2378 "<span font=\"%s\">%s</span>", statusfont
, uri
2381 gtk_label_set_markup(GTK_LABEL(status_url
), markup
);
2384 frame
= webkit_web_view_get_main_frame(webview
);
2385 src
= webkit_web_frame_get_data_source(frame
);
2386 request
= webkit_web_data_source_get_request(src
);
2387 msg
= webkit_network_request_get_message(request
);
2388 ssl_ok
= soup_message_get_flags(msg
) & SOUP_MESSAGE_CERTIFICATE_TRUSTED
;
2390 sslactivecolor
= sslbgcolor
;
2392 sslactivecolor
= sslinvalidbgcolor
;
2394 gdk_color_parse(ssl
? sslactivecolor
: statusbgcolor
, &color
);
2395 gtk_widget_modify_bg(eventbox
, GTK_STATE_NORMAL
, &color
);
2396 gdk_color_parse(ssl
? sslcolor
: statuscolor
, &color
);
2397 gtk_widget_modify_fg(GTK_WIDGET(status_url
), GTK_STATE_NORMAL
, &color
);
2398 gtk_widget_modify_fg(GTK_WIDGET(status_state
), GTK_STATE_NORMAL
, &color
);
2404 int download_count
= g_list_length(activeDownloads
);
2405 GString
*status
= g_string_new("");
2407 /* construct the status line */
2409 /* count, modkey and input buffer */
2410 g_string_append_printf(status
, "%.0d", count
);
2411 if (current_modkey
) g_string_append_c(status
, current_modkey
);
2413 /* the number of active downloads */
2414 if (activeDownloads
) {
2415 g_string_append_printf(status
, " %d active %s", download_count
,
2416 (download_count
== 1) ? "download" : "downloads");
2419 #ifdef ENABLE_WGET_PROGRESS_BAR
2420 /* the progressbar */
2423 char progressbar
[progressbartick
+ 1];
2425 if (activeDownloads
) {
2429 for (ptr
= activeDownloads
; ptr
; ptr
= g_list_next(ptr
)) {
2430 progress
+= 100 * webkit_download_get_progress(ptr
->data
);
2433 progress
/= download_count
;
2435 } else if (webkit_web_view_get_load_status(webview
) != WEBKIT_LOAD_FINISHED
2436 && webkit_web_view_get_load_status(webview
) != WEBKIT_LOAD_FAILED
) {
2438 progress
= webkit_web_view_get_progress(webview
) * 100;
2441 if (progress
>= 0) {
2442 ascii_bar(progressbartick
, progress
* progressbartick
/ 100, progressbar
);
2443 g_string_append_printf(status
, " %c%s%c",
2444 progressborderleft
, progressbar
, progressborderright
);
2449 /* and the current scroll position */
2451 int max
= gtk_adjustment_get_upper(adjust_v
) - gtk_adjustment_get_page_size(adjust_v
);
2452 int val
= (int)(gtk_adjustment_get_value(adjust_v
) / max
* 100);
2455 g_string_append(status
, " All");
2457 g_string_append(status
, " Top");
2458 else if (val
== 100)
2459 g_string_append(status
, " Bot");
2461 g_string_append_printf(status
, " %d%%", val
);
2465 markup
= g_markup_printf_escaped("<span font=\"%s\">%s</span>", statusfont
, status
->str
);
2466 gtk_label_set_markup(GTK_LABEL(status_state
), markup
);
2469 g_string_free(status
, TRUE
);
2475 modkeys
= calloc(LENGTH(keys
) + 1, sizeof(char));
2476 char *ptr
= modkeys
;
2478 for (i
= 0; i
< LENGTH(keys
); i
++)
2479 if (keys
[i
].modkey
&& !strchr(modkeys
, keys
[i
].modkey
))
2480 *(ptr
++) = keys
[i
].modkey
;
2481 modkeys
= realloc(modkeys
, &ptr
[0] - &modkeys
[0] + 1);
2486 scroll_h
= GTK_SCROLLBAR(gtk_hscrollbar_new(NULL
));
2487 scroll_v
= GTK_SCROLLBAR(gtk_vscrollbar_new(NULL
));
2488 adjust_h
= gtk_range_get_adjustment(GTK_RANGE(scroll_h
));
2489 adjust_v
= gtk_range_get_adjustment(GTK_RANGE(scroll_v
));
2491 window
= GTK_WINDOW(gtk_plug_new(embed
));
2493 window
= GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL
));
2494 gtk_window_set_wmclass(GTK_WINDOW(window
), "vimprobable2", "Vimprobable2");
2496 gtk_window_set_default_size(GTK_WINDOW(window
), 640, 480);
2497 box
= GTK_BOX(gtk_vbox_new(FALSE
, 0));
2498 inputbox
= gtk_entry_new();
2499 webview
= (WebKitWebView
*)webkit_web_view_new();
2500 statusbar
= GTK_BOX(gtk_hbox_new(FALSE
, 0));
2501 eventbox
= gtk_event_box_new();
2502 status_url
= gtk_label_new(NULL
);
2503 status_state
= gtk_label_new(NULL
);
2505 PangoFontDescription
*font
;
2506 GdkGeometry hints
= { 1, 1 };
2507 inspector
= webkit_web_view_get_inspector(WEBKIT_WEB_VIEW(webview
));
2509 clipboards
[0] = gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2510 clipboards
[1] = gtk_clipboard_get(GDK_NONE
);
2512 gdk_color_parse(statusbgcolor
, &bg
);
2513 gtk_widget_modify_bg(eventbox
, GTK_STATE_NORMAL
, &bg
);
2514 gtk_widget_set_name(GTK_WIDGET(window
), "Vimprobable2");
2515 gtk_window_set_geometry_hints(window
, NULL
, &hints
, GDK_HINT_MIN_SIZE
);
2517 keymap
= gdk_keymap_get_default();
2519 #ifdef DISABLE_SCROLLBAR
2520 viewport
= gtk_scrolled_window_new(NULL
, NULL
);
2521 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(viewport
), GTK_POLICY_NEVER
, GTK_POLICY_NEVER
);
2523 /* Ensure we still see scrollbars. */
2524 GtkWidget
*viewport
= gtk_scrolled_window_new(adjust_h
, adjust_v
);
2528 gtk_container_add(GTK_CONTAINER(viewport
), GTK_WIDGET(webview
));
2530 /* Ensure we set the scroll adjustments now, so that if we're not drawing
2531 * titlebars, we can still scroll.
2533 gtk_widget_set_scroll_adjustments(GTK_WIDGET(webview
), adjust_h
, adjust_v
);
2535 font
= pango_font_description_from_string(urlboxfont
[0]);
2536 gtk_widget_modify_font(GTK_WIDGET(inputbox
), font
);
2537 pango_font_description_free(font
);
2538 gtk_entry_set_inner_border(GTK_ENTRY(inputbox
), NULL
);
2539 gtk_misc_set_alignment(GTK_MISC(status_url
), 0.0, 0.0);
2540 gtk_misc_set_alignment(GTK_MISC(status_state
), 1.0, 0.0);
2541 gtk_box_pack_start(statusbar
, status_url
, TRUE
, TRUE
, 2);
2542 gtk_box_pack_start(statusbar
, status_state
, FALSE
, FALSE
, 2);
2543 gtk_container_add(GTK_CONTAINER(eventbox
), GTK_WIDGET(statusbar
));
2544 gtk_box_pack_start(box
, viewport
, TRUE
, TRUE
, 0);
2545 gtk_box_pack_start(box
, eventbox
, FALSE
, FALSE
, 0);
2546 gtk_entry_set_has_frame(GTK_ENTRY(inputbox
), FALSE
);
2547 gtk_box_pack_end(box
, inputbox
, FALSE
, FALSE
, 0);
2548 gtk_container_add(GTK_CONTAINER(window
), GTK_WIDGET(box
));
2549 gtk_widget_grab_focus(GTK_WIDGET(webview
));
2550 gtk_widget_show_all(GTK_WIDGET(window
));
2551 set_widget_font_and_color(inputbox
, urlboxfont
[0], urlboxbgcolor
[0], urlboxcolor
[0]);
2552 g_object_set(gtk_widget_get_settings(inputbox
), "gtk-entry-select-on-focus", FALSE
, NULL
);
2557 WebKitWebSettings
*settings
= (WebKitWebSettings
*)webkit_web_settings_new();
2558 char *filename
, *file_url
;
2560 session
= webkit_get_default_session();
2561 g_object_set(G_OBJECT(session
), "ssl-ca-file", ca_bundle
, NULL
);
2562 g_object_set(G_OBJECT(session
), "ssl-strict", strict_ssl
, NULL
);
2563 g_object_set(G_OBJECT(settings
), "default-font-size", DEFAULT_FONT_SIZE
, NULL
);
2564 g_object_set(G_OBJECT(settings
), "enable-scripts", enablePlugins
, NULL
);
2565 g_object_set(G_OBJECT(settings
), "enable-plugins", enablePlugins
, NULL
);
2566 g_object_set(G_OBJECT(settings
), "enable-java-applet", enableJava
, NULL
);
2567 g_object_set(G_OBJECT(settings
), "enable-page-cache", enablePagecache
, NULL
);
2568 filename
= g_strdup_printf(USER_STYLESHEET
);
2569 file_url
= g_strdup_printf("file://%s", filename
);
2570 g_object_set(G_OBJECT(settings
), "user-stylesheet-uri", file_url
, NULL
);
2573 g_object_set(G_OBJECT(settings
), "user-agent", useragent
, NULL
);
2574 g_object_get(G_OBJECT(settings
), "zoom-step", &zoomstep
, NULL
);
2575 webkit_web_view_set_settings(webview
, settings
);
2578 toggle_proxy(use_proxy
);
2583 WebKitWebFrame
*frame
= webkit_web_view_get_main_frame(webview
);
2584 #ifdef ENABLE_COOKIE_SUPPORT
2586 g_signal_connect_after(G_OBJECT(session
), "request-started", G_CALLBACK(new_generic_request
), NULL
);
2588 /* Accept-language header */
2589 g_object_set(G_OBJECT(session
), "accept-language", acceptlanguage
, NULL
);
2591 g_object_connect(G_OBJECT(window
),
2592 "signal::destroy", G_CALLBACK(window_destroyed_cb
), NULL
,
2595 g_signal_connect(G_OBJECT(frame
),
2596 "scrollbars-policy-changed", G_CALLBACK(blank_cb
), NULL
);
2598 g_object_connect(G_OBJECT(webview
),
2599 "signal::title-changed", G_CALLBACK(webview_title_changed_cb
), NULL
,
2600 "signal::load-progress-changed", G_CALLBACK(webview_progress_changed_cb
), NULL
,
2601 "signal::load-committed", G_CALLBACK(webview_load_committed_cb
), NULL
,
2602 "signal::load-finished", G_CALLBACK(webview_load_finished_cb
), NULL
,
2603 "signal::navigation-policy-decision-requested", G_CALLBACK(webview_navigation_cb
), NULL
,
2604 "signal::new-window-policy-decision-requested", G_CALLBACK(webview_new_window_cb
), NULL
,
2605 "signal::mime-type-policy-decision-requested", G_CALLBACK(webview_mimetype_cb
), NULL
,
2606 "signal::download-requested", G_CALLBACK(webview_download_cb
), NULL
,
2607 "signal::key-press-event", G_CALLBACK(webview_keypress_cb
), NULL
,
2608 "signal::hovering-over-link", G_CALLBACK(webview_hoverlink_cb
), NULL
,
2609 "signal::console-message", G_CALLBACK(webview_console_cb
), NULL
,
2610 "signal::create-web-view", G_CALLBACK(webview_open_in_new_window_cb
), NULL
,
2611 "signal::event", G_CALLBACK(notify_event_cb
), NULL
,
2613 /* webview adjustment */
2614 g_object_connect(G_OBJECT(adjust_v
),
2615 "signal::value-changed", G_CALLBACK(webview_scroll_cb
), NULL
,
2618 g_object_connect(G_OBJECT(inputbox
),
2619 "signal::activate", G_CALLBACK(inputbox_activate_cb
), NULL
,
2620 "signal::key-press-event", G_CALLBACK(inputbox_keypress_cb
), NULL
,
2621 "signal::key-release-event", G_CALLBACK(inputbox_keyrelease_cb
), NULL
,
2622 #ifdef ENABLE_INCREMENTAL_SEARCH
2623 "signal::changed", G_CALLBACK(inputbox_changed_cb
), NULL
,
2627 g_signal_connect(G_OBJECT(inspector
),
2628 "inspect-web-view", G_CALLBACK(inspector_inspect_web_view_cb
), NULL
);
2631 #ifdef ENABLE_COOKIE_SUPPORT
2635 if (file_cookie_jar
)
2636 g_object_unref(file_cookie_jar
);
2638 if (session_cookie_jar
)
2639 g_object_unref(session_cookie_jar
);
2641 session_cookie_jar
= soup_cookie_jar_new();
2643 cookie_store
= g_strdup_printf(COOKIES_STORAGE_FILENAME
);
2647 g_signal_connect(G_OBJECT(file_cookie_jar
), "changed",
2648 G_CALLBACK(update_cookie_jar
), NULL
);
2653 /* TA: XXX - we should be using this callback for any header-requests we
2654 * receive (hence the name "new_generic_request" -- but for now, its use
2655 * is limited to handling cookies.
2658 new_generic_request(SoupSession
*session
, SoupMessage
*soup_msg
, gpointer unused
)
2660 SoupMessageHeaders
*soup_msg_h
;
2664 soup_msg_h
= soup_msg
->request_headers
;
2665 soup_message_headers_remove(soup_msg_h
, "Cookie");
2666 uri
= soup_message_get_uri(soup_msg
);
2667 if ((cookie_str
= get_cookies(uri
))) {
2668 soup_message_headers_append(soup_msg_h
, "Cookie", cookie_str
);
2672 g_signal_connect_after(G_OBJECT(soup_msg
), "got-headers", G_CALLBACK(handle_cookie_request
), NULL
);
2678 get_cookies(SoupURI
*soup_uri
) {
2681 cookie_str
= soup_cookie_jar_get_cookies(file_cookie_jar
, soup_uri
, TRUE
);
2687 handle_cookie_request(SoupMessage
*soup_msg
, gpointer unused
)
2689 GSList
*resp_cookie
= NULL
, *cookie_list
;
2692 cookie_list
= soup_cookies_from_response(soup_msg
);
2693 for(resp_cookie
= cookie_list
; resp_cookie
; resp_cookie
= g_slist_next(resp_cookie
))
2695 SoupDate
*soup_date
;
2696 cookie
= soup_cookie_copy((SoupCookie
*)resp_cookie
->data
);
2698 if (cookie_timeout
&& cookie
->expires
== NULL
) {
2699 soup_date
= soup_date_new_from_time_t(time(NULL
) + cookie_timeout
* 10);
2700 soup_cookie_set_expires(cookie
, soup_date
);
2701 soup_date_free(soup_date
);
2703 soup_cookie_jar_add_cookie(file_cookie_jar
, cookie
);
2706 soup_cookies_free(cookie_list
);
2712 update_cookie_jar(SoupCookieJar
*jar
, SoupCookie
*old
, SoupCookie
*new)
2715 /* Nothing to do. */
2720 copy
= soup_cookie_copy(new);
2722 soup_cookie_jar_add_cookie(session_cookie_jar
, copy
);
2728 load_all_cookies(void)
2730 GSList
*cookie_list
;
2731 file_cookie_jar
= soup_cookie_jar_text_new(cookie_store
, COOKIES_STORAGE_READONLY
);
2733 /* Put them back in the session store. */
2734 GSList
*cookies_from_file
= soup_cookie_jar_all_cookies(file_cookie_jar
);
2735 cookie_list
= cookies_from_file
;
2737 for (; cookies_from_file
;
2738 cookies_from_file
= cookies_from_file
->next
)
2740 soup_cookie_jar_add_cookie(session_cookie_jar
, cookies_from_file
->data
);
2743 soup_cookies_free(cookies_from_file
);
2744 g_slist_free(cookie_list
);
2753 /* Free up any nasty globals before exiting. */
2754 #ifdef ENABLE_COOKIE_SUPPORT
2756 g_free(cookie_store
);
2762 main(int argc
, char *argv
[]) {
2764 static char url
[256] = "";
2765 static gboolean ver
= false;
2766 static gboolean configfile_exists
= FALSE
;
2767 static const char *cfile
= NULL
;
2768 static GOptionEntry opts
[] = {
2769 { "version", 'v', 0, G_OPTION_ARG_NONE
, &ver
, "print version", NULL
},
2770 { "embed", 'e', 0, G_OPTION_ARG_STRING
, &winid
, "embedded", NULL
},
2771 { "configfile", 'c', 0, G_OPTION_ARG_STRING
, &cfile
, "config file", NULL
},
2777 /* command line argument: version */
2778 if (!gtk_init_with_args(&argc
, &argv
, "[<uri>]", opts
, NULL
, &err
)) {
2779 g_printerr("can't init gtk: %s\n", err
->message
);
2781 return EXIT_FAILURE
;
2785 printf("%s\n", INTERNAL_VERSION
);
2786 return EXIT_SUCCESS
;
2789 if( getenv("XDG_CONFIG_HOME") )
2790 config_base
= g_strdup_printf("%s", getenv("XDG_CONFIG_HOME"));
2792 config_base
= g_strdup_printf("%s/.config/", getenv("HOME"));
2795 configfile
= g_strdup(cfile
);
2797 configfile
= g_strdup_printf(RCFILE
);
2799 if (!g_thread_supported())
2800 g_thread_init(NULL
);
2803 if (strncmp(winid
, "0x", 2) == 0) {
2804 embed
= strtol(winid
, NULL
, 16);
2806 embed
= atoi(winid
);
2813 #ifdef ENABLE_COOKIE_SUPPORT
2817 make_searchengines_list(searchengines
, LENGTH(searchengines
));
2818 make_uri_handlers_list(uri_handlers
, LENGTH(uri_handlers
));
2820 /* Check if the specified file exists. */
2821 /* And only warn the user, if they explicitly asked for a config on the
2824 if (!(access(configfile
, F_OK
) == 0) && cfile
) {
2827 feedback_str
= g_strdup_printf("Config file '%s' doesn't exist", cfile
);
2828 give_feedback(feedback_str
);
2829 g_free(feedback_str
);
2830 } else if ((access(configfile
, F_OK
) == 0))
2831 configfile_exists
= true;
2833 /* read config file */
2834 /* But only report errors if we failed, and the file existed. */
2835 if ((SUCCESS
!= read_rcfile(configfile
)) && configfile_exists
) {
2837 a
.s
= g_strdup_printf("Error in config file '%s'", configfile
);
2843 /* command line argument: URL */
2845 strncpy(url
, argv
[argc
- 1], 255);
2847 strncpy(url
, startpage
, 255);
2850 a
.i
= TargetCurrent
;
2857 return EXIT_SUCCESS
;