Fixed bug with not being able to press enter to follow single digit hints.
[vimprobable/e.git] / main.c
blob79f4d46e3898a103b6b191cfb906e665e3ed903a
2 /*
3 (c) 2009 by Leon Winter
4 (c) 2009, 2010 by Hannes Schueller
5 (c) 2009, 2010 by Matto Fransen
6 (c) 2010 by Hans-Peter Deifel
7 (c) 2010 by Thomas Adam
8 see LICENSE file
9 */
11 #include <X11/Xlib.h>
12 #include "includes.h"
13 #include "vimprobable.h"
14 #include "utilities.h"
15 #include "callbacks.h"
16 #include "javascript.h"
18 /* the CLEAN_MOD_*_MASK defines have all the bits set that will be stripped from the modifier bit field */
19 #define CLEAN_MOD_NUMLOCK_MASK (GDK_MOD2_MASK)
20 #define CLEAN_MOD_BUTTON_MASK (GDK_BUTTON1_MASK|GDK_BUTTON2_MASK|GDK_BUTTON3_MASK|GDK_BUTTON4_MASK|GDK_BUTTON5_MASK)
22 /* remove unused bits, numlock symbol and buttons from keymask */
23 #define CLEAN(mask) (mask & (GDK_MODIFIER_MASK) & ~(CLEAN_MOD_NUMLOCK_MASK) & ~(CLEAN_MOD_BUTTON_MASK))
25 #define IS_ESCAPE(event) (IS_ESCAPE_KEY(CLEAN(event->state), event->keyval))
26 #define IS_ESCAPE_KEY(s, k) ((s == 0 && k == GDK_Escape) || \
27 (s == GDK_CONTROL_MASK && k == GDK_bracketleft))
29 /* callbacks here */
30 static void inputbox_activate_cb(GtkEntry *entry, gpointer user_data);
31 static gboolean inputbox_keypress_cb(GtkEntry *entry, GdkEventKey *event);
32 static gboolean inputbox_keyrelease_cb(GtkEntry *entry, GdkEventKey *event);
33 static gboolean inputbox_changed_cb(GtkEditable *entry, gpointer user_data);
34 static WebKitWebView* inspector_inspect_web_view_cb(gpointer inspector, WebKitWebView* web_view);
35 static gboolean notify_event_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data);
36 static gboolean webview_console_cb(WebKitWebView *webview, char *message, int line, char *source, gpointer user_data);
37 static gboolean webview_download_cb(WebKitWebView *webview, WebKitDownload *download, gpointer user_data);
38 static void webview_hoverlink_cb(WebKitWebView *webview, char *title, char *link, gpointer data);
39 static gboolean webview_keypress_cb(WebKitWebView *webview, GdkEventKey *event);
40 static void webview_load_committed_cb(WebKitWebView *webview, WebKitWebFrame *frame, gpointer user_data);
41 static void webview_load_finished_cb(WebKitWebView *webview, WebKitWebFrame *frame, gpointer user_data);
42 static gboolean webview_mimetype_cb(WebKitWebView *webview, WebKitWebFrame *frame, WebKitNetworkRequest *request,
43 char *mime_type, WebKitWebPolicyDecision *decision, gpointer user_data);
44 static gboolean webview_new_window_cb(WebKitWebView *webview, WebKitWebFrame *frame, WebKitNetworkRequest *request,
45 WebKitWebNavigationAction *action, WebKitWebPolicyDecision *decision, gpointer user_data);
46 static gboolean webview_open_in_new_window_cb(WebKitWebView *webview, WebKitWebFrame *frame, gpointer user_data);
47 static void webview_progress_changed_cb(WebKitWebView *webview, int progress, gpointer user_data);
48 static void webview_title_changed_cb(WebKitWebView *webview, WebKitWebFrame *frame, char *title, gpointer user_data);
49 static void window_destroyed_cb(GtkWidget *window, gpointer func_data);
51 /* functions */
52 static gboolean bookmark(const Arg *arg);
53 static gboolean browser_settings(const Arg *arg);
54 static gboolean commandhistoryfetch(const Arg *arg);
55 static gboolean complete(const Arg *arg);
56 static gboolean descend(const Arg *arg);
57 gboolean echo(const Arg *arg);
58 static gboolean focus_input(const Arg *arg);
59 static gboolean input(const Arg *arg);
60 static gboolean navigate(const Arg *arg);
61 static gboolean number(const Arg *arg);
62 static gboolean open_arg(const Arg *arg);
63 static gboolean open_remembered(const Arg *arg);
64 static gboolean paste(const Arg *arg);
65 static gboolean quickmark(const Arg *arg);
66 static gboolean quit(const Arg *arg);
67 static gboolean revive(const Arg *arg);
68 static gboolean print_frame(const Arg *arg);
69 static gboolean search(const Arg *arg);
70 static gboolean set(const Arg *arg);
71 static gboolean script(const Arg *arg);
72 static gboolean scroll(const Arg *arg);
73 static gboolean search_tag(const Arg *arg);
74 static gboolean yank(const Arg *arg);
75 static gboolean view_source(const Arg * arg);
76 static gboolean zoom(const Arg *arg);
77 static gboolean fake_key_event(const Arg *arg);
79 static void update_url(const char *uri);
80 static void setup_modkeys(void);
81 static void setup_gui(void);
82 static void setup_settings(void);
83 static void setup_signals(void);
84 static void ascii_bar(int total, int state, char *string);
85 static gchar *jsapi_ref_to_string(JSContextRef context, JSValueRef ref);
86 static void jsapi_evaluate_script(const gchar *script, gchar **value, gchar **message);
87 static void download_progress(WebKitDownload *d, GParamSpec *pspec);
88 static void set_widget_font_and_color(GtkWidget *widget, const char *font_str,
89 const char *bg_color_str, const char *fg_color_str);
91 static gboolean history(void);
92 static gboolean process_set_line(char *line);
93 void save_command_history(char *line);
94 void toggle_proxy(gboolean onoff);
95 void toggle_scrollbars(gboolean onoff);
97 gboolean process_keypress(GdkEventKey *event);
98 void fill_suggline(char * suggline, const char * command, const char *fill_with);
99 GtkWidget * fill_eventbox(const char * completion_line);
100 static void mop_up(void);
102 #include "main.h"
104 /* variables */
105 static GtkWindow *window;
106 static GtkWidget *viewport;
107 static GtkBox *box;
108 static GtkScrollbar *scroll_h;
109 static GtkScrollbar *scroll_v;
110 static GtkAdjustment *adjust_h;
111 static GtkAdjustment *adjust_v;
112 static GtkWidget *inputbox;
113 static GtkWidget *eventbox;
114 static GtkBox *statusbar;
115 static GtkWidget *status_url;
116 static GtkWidget *status_state;
117 static WebKitWebView *webview;
118 static SoupSession *session;
119 static GtkClipboard *clipboards[2];
121 static char **args;
122 static unsigned int mode = ModeNormal;
123 static unsigned int count = 0;
124 static float zoomstep;
125 static char *modkeys;
126 static char current_modkey;
127 static char *search_handle;
128 static gboolean search_direction;
129 static gboolean echo_active = TRUE;
130 WebKitWebInspector *inspector;
132 static GdkNativeWindow embed = 0;
133 static char *configfile = NULL;
134 static char *winid = NULL;
136 static char rememberedURI[1024] = "";
137 static char followTarget[8] = "";
138 char *error_msg = NULL;
140 GList *activeDownloads;
142 #include "config.h"
143 #include "keymap.h"
145 char commandhistory[COMMANDHISTSIZE][255];
146 int lastcommand = 0;
147 int maxcommands = 0;
148 int commandpointer = 0;
149 KeyList *keylistroot = NULL;
151 /* Cookie support. */
152 #ifdef ENABLE_COOKIE_SUPPORT
153 static SoupCookieJar *session_cookie_jar = NULL;
154 static SoupCookieJar *file_cookie_jar = NULL;
155 static time_t cookie_timeout = 4800;
156 static char *cookie_store;
157 static void setup_cookies(void);
158 static const char *get_cookies(SoupURI *soup_uri);
159 static void load_all_cookies(void);
160 static void new_generic_request(SoupSession *soup_ses, SoupMessage *soup_msg, gpointer unused);
161 static void update_cookie_jar(SoupCookieJar *jar, SoupCookie *old, SoupCookie *new);
162 static void handle_cookie_request(SoupMessage *soup_msg, gpointer unused);
163 #endif
164 /* callbacks */
165 void
166 window_destroyed_cb(GtkWidget *window, gpointer func_data) {
167 quit(NULL);
170 void
171 webview_title_changed_cb(WebKitWebView *webview, WebKitWebFrame *frame, char *title, gpointer user_data) {
172 gtk_window_set_title(window, title);
175 void
176 webview_progress_changed_cb(WebKitWebView *webview, int progress, gpointer user_data) {
177 #ifdef ENABLE_GTK_PROGRESS_BAR
178 gtk_entry_set_progress_fraction(GTK_ENTRY(inputbox), progress == 100 ? 0 : (double)progress/100);
179 #endif
180 update_state();
183 #ifdef ENABLE_WGET_PROGRESS_BAR
184 void
185 ascii_bar(int total, int state, char *string) {
186 int i;
188 for (i = 0; i < state; i++)
189 string[i] = progressbartickchar;
190 string[i++] = progressbarcurrent;
191 for (; i < total; i++)
192 string[i] = progressbarspacer;
193 string[i] = '\0';
195 #endif
197 void
198 webview_load_committed_cb(WebKitWebView *webview, WebKitWebFrame *frame, gpointer user_data) {
199 Arg a = { .i = Silent, .s = g_strdup(JS_SETUP_HINTS) };
200 const char *uri = webkit_web_view_get_uri(webview);
202 update_url(uri);
203 script(&a);
206 void
207 webview_load_finished_cb(WebKitWebView *webview, WebKitWebFrame *frame, gpointer user_data) {
208 Arg a = { .i = Silent, .s = g_strdup(JS_SETUP_INPUT_FOCUS) };
210 if (HISTORY_MAX_ENTRIES > 0)
211 history();
212 update_state();
213 script(&a);
216 static gboolean
217 webview_open_in_new_window_cb(WebKitWebView *webview, WebKitWebFrame *frame, gpointer user_data) {
218 Arg a = { .i = TargetNew, .s = (char*)webkit_web_view_get_uri(webview) };
219 if (strlen(rememberedURI) > 0) {
220 a.s = rememberedURI;
222 open_arg(&a);
223 return FALSE;
226 gboolean
227 webview_new_window_cb(WebKitWebView *webview, WebKitWebFrame *frame, WebKitNetworkRequest *request,
228 WebKitWebNavigationAction *action, WebKitWebPolicyDecision *decision, gpointer user_data) {
229 Arg a = { .i = TargetNew, .s = (char*)webkit_network_request_get_uri(request) };
230 open_arg(&a);
231 webkit_web_policy_decision_ignore(decision);
232 return TRUE;
235 gboolean
236 webview_mimetype_cb(WebKitWebView *webview, WebKitWebFrame *frame, WebKitNetworkRequest *request,
237 char *mime_type, WebKitWebPolicyDecision *decision, gpointer user_data) {
238 if (webkit_web_view_can_show_mime_type(webview, mime_type) == FALSE) {
239 webkit_web_policy_decision_download(decision);
240 return TRUE;
241 } else {
242 return FALSE;
246 static WebKitWebView*
247 inspector_inspect_web_view_cb(gpointer inspector, WebKitWebView* web_view) {
248 gchar* inspector_title;
249 GtkWidget* inspector_window;
250 GtkWidget* inspector_view;
252 /* just enough code to show the inspector - no signal handling etc. */
253 inspector_title = g_strdup_printf("Inspect page - %s - Vimprobable2", webkit_web_view_get_uri(web_view));
254 if (embed) {
255 inspector_window = gtk_plug_new(embed);
256 } else {
257 inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
258 gtk_window_set_wmclass(window, "vimprobable2", "Vimprobable2");
260 gtk_window_set_title(GTK_WINDOW(inspector_window), inspector_title);
261 g_free(inspector_title);
262 inspector_view = webkit_web_view_new();
263 gtk_container_add(GTK_CONTAINER(inspector_window), inspector_view);
264 gtk_widget_show_all(inspector_window);
265 return WEBKIT_WEB_VIEW(inspector_view);
268 gboolean
269 webview_download_cb(WebKitWebView *webview, WebKitDownload *download, gpointer user_data) {
270 const gchar *filename;
271 gchar *uri, *path;
272 uint32_t size;
273 Arg a;
275 filename = webkit_download_get_suggested_filename(download);
276 if (filename == NULL || strlen(filename) == 0) {
277 filename = "vimprobable_download";
279 path = g_build_filename(g_strdup_printf(DOWNLOADS_PATH), filename, NULL);
280 uri = g_strconcat("file://", path, NULL);
281 webkit_download_set_destination_uri(download, uri);
282 g_free(uri);
283 size = (uint32_t)webkit_download_get_total_size(download);
284 a.i = Info;
285 if (size > 0)
286 a.s = g_strdup_printf("Download %s started (expected size: %u bytes)...", filename, size);
287 else
288 a.s = g_strdup_printf("Download %s started (unknown size)...", filename);
289 echo(&a);
290 g_free(a.s);
291 activeDownloads = g_list_prepend(activeDownloads, download);
292 g_signal_connect(download, "notify::progress", G_CALLBACK(download_progress), NULL);
293 g_signal_connect(download, "notify::status", G_CALLBACK(download_progress), NULL);
294 update_state();
295 return TRUE;
298 void
299 download_progress(WebKitDownload *d, GParamSpec *pspec) {
300 Arg a;
301 WebKitDownloadStatus status = webkit_download_get_status(d);
303 if (status != WEBKIT_DOWNLOAD_STATUS_STARTED && status != WEBKIT_DOWNLOAD_STATUS_CREATED) {
304 if (status != WEBKIT_DOWNLOAD_STATUS_FINISHED) {
305 a.i = Error;
306 a.s = g_strdup_printf("Error while downloading %s", webkit_download_get_suggested_filename(d));
307 echo(&a);
308 } else {
309 a.i = Info;
310 a.s = g_strdup_printf("Download %s finished", webkit_download_get_suggested_filename(d));
311 echo(&a);
313 g_free(a.s);
314 activeDownloads = g_list_remove(activeDownloads, d);
316 update_state();
320 gboolean
321 process_keypress(GdkEventKey *event) {
322 KeyList *current;
324 current = keylistroot;
325 while (current != NULL) {
326 if (current->Element.mask == CLEAN(event->state)
327 && (current->Element.modkey == current_modkey
328 || (!current->Element.modkey && !current_modkey)
329 || current->Element.modkey == GDK_VoidSymbol ) /* wildcard */
330 && current->Element.key == event->keyval
331 && current->Element.func)
332 if (current->Element.func(&current->Element.arg)) {
333 current_modkey = count = 0;
334 update_state();
335 return TRUE;
337 current = current->next;
339 return FALSE;
342 gboolean
343 webview_keypress_cb(WebKitWebView *webview, GdkEventKey *event) {
344 Arg a = { .i = ModeNormal, .s = NULL };
346 switch (mode) {
347 case ModeNormal:
348 if (CLEAN(event->state) == 0) {
349 if (IS_ESCAPE(event)) {
350 a.i = Info;
351 a.s = g_strdup("");
352 echo(&a);
353 g_free(a.s);
354 } else if (current_modkey == 0 && ((event->keyval >= GDK_1 && event->keyval <= GDK_9)
355 || (event->keyval == GDK_0 && count))) {
356 count = (count ? count * 10 : 0) + (event->keyval - GDK_0);
357 update_state();
358 return TRUE;
359 } else if (strchr(modkeys, event->keyval) && current_modkey != event->keyval) {
360 current_modkey = event->keyval;
361 update_state();
362 return TRUE;
365 /* keybindings */
366 if (process_keypress(event) == TRUE) return TRUE;
368 break;
369 case ModeInsert:
370 if (IS_ESCAPE(event)) {
371 a.i = Silent;
372 a.s = g_strdup("vimprobable_clearfocus()");
373 script(&a);
374 a.i = ModeNormal;
375 return set(&a);
377 case ModePassThrough:
378 if (IS_ESCAPE(event)) {
379 echo(&a);
380 set(&a);
381 return TRUE;
383 break;
384 case ModeSendKey:
385 echo(&a);
386 set(&a);
387 break;
389 return FALSE;
392 void
393 set_widget_font_and_color(GtkWidget *widget, const char *font_str, const char *bg_color_str,
394 const char *fg_color_str) {
395 GdkColor fg_color;
396 GdkColor bg_color;
397 PangoFontDescription *font;
399 font = pango_font_description_from_string(font_str);
400 gtk_widget_modify_font(widget, font);
401 pango_font_description_free(font);
403 if (fg_color_str)
404 gdk_color_parse(fg_color_str, &fg_color);
405 if (bg_color_str)
406 gdk_color_parse(bg_color_str, &bg_color);
408 gtk_widget_modify_text(widget, GTK_STATE_NORMAL, fg_color_str ? &fg_color : NULL);
409 gtk_widget_modify_base(widget, GTK_STATE_NORMAL, bg_color_str ? &bg_color : NULL);
411 return;
414 void
415 webview_hoverlink_cb(WebKitWebView *webview, char *title, char *link, gpointer data) {
416 const char *uri = webkit_web_view_get_uri(webview);
418 memset(rememberedURI, 0, 1024);
419 if (link) {
420 gtk_label_set_markup(GTK_LABEL(status_url), g_markup_printf_escaped("<span font=\"%s\">Link: %s</span>", statusfont, link));
421 strncpy(rememberedURI, link, 1024);
422 } else
423 update_url(uri);
426 gboolean
427 webview_console_cb(WebKitWebView *webview, char *message, int line, char *source, gpointer user_data) {
428 Arg a;
430 /* Don't change internal mode if the browser doesn't have focus to prevent inconsistent states */
431 if (gtk_window_has_toplevel_focus(window)) {
432 if (!strcmp(message, "hintmode_off") || !strcmp(message, "insertmode_off")) {
433 a.i = ModeNormal;
434 return set(&a);
435 } else if (!strcmp(message, "insertmode_on")) {
436 a.i = ModeInsert;
437 return set(&a);
440 return FALSE;
443 void
444 inputbox_activate_cb(GtkEntry *entry, gpointer user_data) {
445 char *text;
446 guint16 length = gtk_entry_get_text_length(entry);
447 Arg a;
448 gboolean success = FALSE, forward = FALSE;
450 a.i = HideCompletion;
451 complete(&a);
452 if (length == 0)
453 return;
454 text = (char*)gtk_entry_get_text(entry);
455 if (length > 1 && text[0] == ':') {
456 success = process_line((text + 1));
457 } else if (length > 1 && ((forward = text[0] == '/') || text[0] == '?')) {
458 webkit_web_view_unmark_text_matches(webview);
459 #ifdef ENABLE_MATCH_HIGHLITING
460 webkit_web_view_mark_text_matches(webview, &text[1], FALSE, 0);
461 webkit_web_view_set_highlight_text_matches(webview, TRUE);
462 #endif
463 count = 0;
464 #ifndef ENABLE_INCREMENTAL_SEARCH
465 a.s =& text[1];
466 a.i = searchoptions | (forward ? DirectionForward : DirectionBackwards);
467 search(&a);
468 #else
469 search_direction = forward;
470 search_handle = g_strdup(&text[1]);
471 #endif
472 } else if (count && (text[0] == '`' || text[0] == '~')) {
473 a.i = Silent;
474 a.s = g_strdup_printf("vimprobable_fire(%d)", count);
475 script(&a);
476 update_state();
477 } else
478 return;
479 if (!echo_active)
480 gtk_entry_set_text(entry, "");
481 gtk_widget_grab_focus(GTK_WIDGET(webview));
484 gboolean
485 inputbox_keypress_cb(GtkEntry *entry, GdkEventKey *event) {
486 Arg a;
487 int numval;
488 char count_buf[BUFFERSIZE];
490 switch (event->keyval) {
491 case GDK_bracketleft:
492 case GDK_Escape:
493 if (!IS_ESCAPE(event)) break;
494 a.i = HideCompletion;
495 complete(&a);
496 a.i = ModeNormal;
497 return set(&a);
498 break;
499 case GDK_Tab:
500 a.i = DirectionNext;
501 return complete(&a);
502 break;
503 case GDK_Up:
504 a.i = DirectionPrev;
505 return commandhistoryfetch(&a);
506 break;
507 case GDK_Down:
508 a.i = DirectionNext;
509 return commandhistoryfetch(&a);
510 break;
511 case GDK_ISO_Left_Tab:
512 a.i = DirectionPrev;
513 return complete(&a);
514 break;
517 numval = g_unichar_digit_value((gunichar) gdk_keyval_to_unicode(event->keyval));
518 if (followTarget[0] && ((numval >= 1 && numval <= 9) || (numval == 0 && count))) {
519 /* allow a zero as non-first number */
520 count = (count ? count * 10 : 0) + numval;
521 a.i = Silent;
522 a.s = g_strdup_printf("vimprobable_update_hints(%d)", count);
523 script(&a);
524 update_state();
525 return TRUE;
528 return FALSE;
531 gboolean
532 notify_event_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data) {
533 int i;
534 if (mode == ModeNormal && event->type == GDK_BUTTON_RELEASE) {
535 /* handle mouse click events */
536 for (i = 0; i < LENGTH(mouse); i++) {
537 if (mouse[i].mask == CLEAN(event->button.state)
538 && (mouse[i].modkey == current_modkey
539 || (!mouse[i].modkey && !current_modkey)
540 || mouse[i].modkey == GDK_VoidSymbol) /* wildcard */
541 && mouse[i].button == event->button.button
542 && mouse[i].func) {
543 if (mouse[i].func(&mouse[i].arg)) {
544 current_modkey = count = 0;
545 update_state();
546 return TRUE;
551 return FALSE;
554 static gboolean inputbox_keyrelease_cb(GtkEntry *entry, GdkEventKey *event) {
555 Arg a;
556 guint16 length = gtk_entry_get_text_length(entry);
558 if (!length) {
559 a.i = HideCompletion;
560 complete(&a);
561 a.i = ModeNormal;
562 return set(&a);
564 return FALSE;
567 static gboolean inputbox_changed_cb(GtkEditable *entry, gpointer user_data) {
568 Arg a;
569 char *text = (char*)gtk_entry_get_text(GTK_ENTRY(entry));
570 guint16 length = gtk_entry_get_text_length(GTK_ENTRY(entry));
571 gboolean forward = FALSE;
573 /* Update incremental search if the user changes the search text.
575 * Note: gtk_widget_is_focus() is a poor way to check if the change comes
576 * from the user. But if the entry is focused and the text is set
577 * through gtk_entry_set_text() in some asyncrounous operation,
578 * I would consider that a bug.
581 if (gtk_widget_is_focus(GTK_WIDGET(entry)) && length > 1 && ((forward = text[0] == '/') || text[0] == '?')) {
582 webkit_web_view_unmark_text_matches(webview);
583 webkit_web_view_search_text(webview, &text[1], searchoptions & CaseSensitive, forward, searchoptions & Wrapping);
584 return TRUE;
585 } else if (gtk_widget_is_focus(GTK_WIDGET(entry)) && length >= 1 &&
586 (text[0] == '`' || text[0] == '~')) {
587 a.i = Silent;
588 a.s = g_strdup("vimprobable_cleanup()");
589 script(&a);
591 a.i = Silent;
592 a.s = g_strconcat("vimprobable_show_hints('", text + 1, "')", NULL);
593 script(&a);
595 return TRUE;
596 } else if (length == 0 && followTarget[0]) {
597 memset(followTarget, 0, 8);
598 a.i = Silent;
599 a.s = g_strdup("vimprobable_clear()");
600 script(&a);
601 count = 0;
602 update_state();
605 return FALSE;
608 /* funcs here */
610 void fill_suggline(char * suggline, const char * command, const char *fill_with) {
611 memset(suggline, 0, 512);
612 strncpy(suggline, command, 512);
613 strncat(suggline, " ", 1);
614 strncat(suggline, fill_with, 512 - strlen(suggline) - 1);
617 GtkWidget * fill_eventbox(const char * completion_line) {
618 GtkBox * row;
619 GtkWidget *row_eventbox, *el;
620 GdkColor color;
621 char * markup;
623 row = GTK_BOX(gtk_hbox_new(FALSE, 0));
624 row_eventbox = gtk_event_box_new();
625 gdk_color_parse(completionbgcolor[0], &color);
626 gtk_widget_modify_bg(row_eventbox, GTK_STATE_NORMAL, &color);
627 el = gtk_label_new(NULL);
628 markup = g_strconcat("<span font=\"", completionfont[0], "\" color=\"", completioncolor[0], "\">",
629 g_markup_escape_text(completion_line, strlen(completion_line)), "</span>", NULL);
630 gtk_label_set_markup(GTK_LABEL(el), markup);
631 g_free(markup);
632 gtk_misc_set_alignment(GTK_MISC(el), 0, 0);
633 gtk_box_pack_start(row, el, TRUE, TRUE, 2);
634 gtk_container_add(GTK_CONTAINER(row_eventbox), GTK_WIDGET(row));
635 return row_eventbox;
638 gboolean
639 complete(const Arg *arg) {
640 char *str, *p, *s, *markup, *entry, *searchfor, command[32] = "", suggline[512] = "", **suggurls;
641 size_t listlen, len, cmdlen;
642 int i, spacepos;
643 Listelement *elementlist = NULL, *elementpointer;
644 gboolean highlight = FALSE;
645 GtkBox *row;
646 GtkWidget *row_eventbox, *el;
647 GtkBox *_table;
648 GdkColor color;
649 static GtkWidget *table, *top_border;
650 static char *prefix;
651 static char **suggestions;
652 static GtkWidget **widgets;
653 static int n = 0, m, current = -1;
655 str = (char*)gtk_entry_get_text(GTK_ENTRY(inputbox));
656 len = strlen(str);
658 /* Get the length of the list of commands for completion. We need this to
659 * malloc/realloc correctly.
661 listlen = LENGTH(commands);
663 if ((len == 0 || str[0] != ':') && arg->i != HideCompletion)
664 return TRUE;
665 if (prefix) {
666 if (arg->i != HideCompletion && widgets && current != -1 && !strcmp(&str[1], suggestions[current])) {
667 gdk_color_parse(completionbgcolor[0], &color);
668 gtk_widget_modify_bg(widgets[current], GTK_STATE_NORMAL, &color);
669 current = (n + current + (arg->i == DirectionPrev ? -1 : 1)) % n;
670 if ((arg->i == DirectionNext && current == 0)
671 || (arg->i == DirectionPrev && current == n - 1))
672 current = -1;
673 } else {
674 free(widgets);
675 free(suggestions);
676 free(prefix);
677 gtk_widget_destroy(GTK_WIDGET(table));
678 gtk_widget_destroy(GTK_WIDGET(top_border));
679 table = NULL;
680 widgets = NULL;
681 suggestions = NULL;
682 prefix = NULL;
683 n = 0;
684 current = -1;
685 if (arg->i == HideCompletion)
686 return TRUE;
688 } else if (arg->i == HideCompletion)
689 return TRUE;
690 if (!widgets) {
691 prefix = g_strdup_printf(str);
692 widgets = malloc(sizeof(GtkWidget*) * listlen);
693 suggestions = malloc(sizeof(char*) * listlen);
694 top_border = gtk_event_box_new();
695 gtk_widget_set_size_request(GTK_WIDGET(top_border), 0, 1);
696 gdk_color_parse(completioncolor[2], &color);
697 gtk_widget_modify_bg(top_border, GTK_STATE_NORMAL, &color);
698 table = gtk_event_box_new();
699 gdk_color_parse(completionbgcolor[0], &color);
700 _table = GTK_BOX(gtk_vbox_new(FALSE, 0));
701 highlight = len > 1;
702 if (strchr(str, ' ') == NULL) {
703 /* command completion */
704 listlen = LENGTH(commands);
705 for (i = 0; i < listlen; i++) {
706 if (commands[i].cmd == NULL)
707 break;
708 cmdlen = strlen(commands[i].cmd);
709 if (!highlight || (n < MAX_LIST_SIZE && len - 1 <= cmdlen && !strncmp(&str[1], commands[i].cmd, len - 1))) {
710 p = s = malloc(sizeof(char*) * (highlight ? sizeof(COMPLETION_TAG_OPEN) + sizeof(COMPLETION_TAG_CLOSE) - 1 : 1) + cmdlen);
711 if (highlight) {
712 memcpy(p, COMPLETION_TAG_OPEN, sizeof(COMPLETION_TAG_OPEN) - 1);
713 memcpy((p += sizeof(COMPLETION_TAG_OPEN) - 1), &str[1], len - 1);
714 memcpy((p += len - 1), COMPLETION_TAG_CLOSE, sizeof(COMPLETION_TAG_CLOSE) - 1);
715 p += sizeof(COMPLETION_TAG_CLOSE) - 1;
717 memcpy(p, &commands[i].cmd[len - 1], cmdlen - len + 2);
718 row = GTK_BOX(gtk_hbox_new(FALSE, 0));
719 row_eventbox = gtk_event_box_new();
720 gtk_widget_modify_bg(row_eventbox, GTK_STATE_NORMAL, &color);
721 el = gtk_label_new(NULL);
722 markup = g_strconcat("<span font=\"", completionfont[0], "\" color=\"", completioncolor[0], "\">", s, "</span>", NULL);
723 free(s);
724 gtk_label_set_markup(GTK_LABEL(el), markup);
725 g_free(markup);
726 gtk_misc_set_alignment(GTK_MISC(el), 0, 0);
727 gtk_box_pack_start(row, el, TRUE, TRUE, 2);
728 gtk_container_add(GTK_CONTAINER(row_eventbox), GTK_WIDGET(row));
729 gtk_box_pack_start(_table, GTK_WIDGET(row_eventbox), FALSE, FALSE, 0);
730 suggestions[n] = commands[i].cmd;
731 widgets[n++] = row_eventbox;
734 } else {
735 entry = (char *)malloc(512 * sizeof(char));
736 if (entry == NULL) {
737 return FALSE;
739 memset(entry, 0, 512);
740 suggurls = malloc(sizeof(char*) * listlen);
741 if (suggurls == NULL) {
742 return FALSE;
744 spacepos = strcspn(str, " ");
745 searchfor = (str + spacepos + 1);
746 strncpy(command, (str + 1), spacepos - 1);
747 if (strlen(command) == 3 && strncmp(command, "set", 3) == 0) {
748 /* browser settings */
749 listlen = LENGTH(browsersettings);
750 for (i = 0; i < listlen; i++) {
751 if (n < MAX_LIST_SIZE && strstr(browsersettings[i].name, searchfor) != NULL) {
752 /* match */
753 fill_suggline(suggline, command, browsersettings[i].name);
754 suggurls[n] = (char *)malloc(sizeof(char) * 512 + 1);
755 strncpy(suggurls[n], suggline, 512);
756 suggestions[n] = suggurls[n];
757 row_eventbox = fill_eventbox(suggline);
758 gtk_box_pack_start(_table, GTK_WIDGET(row_eventbox), FALSE, FALSE, 0);
759 widgets[n++] = row_eventbox;
763 } else if (strlen(command) == 2 && strncmp(command, "qt", 2) == 0) {
764 /* completion on tags */
765 spacepos = strcspn(str, " ");
766 searchfor = (str + spacepos + 1);
767 elementlist = complete_list(searchfor, 1, elementlist);
768 } else {
769 /* URL completion: bookmarks */
770 elementlist = complete_list(searchfor, 0, elementlist);
771 m = count_list(elementlist);
772 if (m < MAX_LIST_SIZE) {
773 /* URL completion: history */
774 elementlist = complete_list(searchfor, 2, elementlist);
777 elementpointer = elementlist;
778 while (elementpointer != NULL) {
779 fill_suggline(suggline, command, elementpointer->element);
780 suggurls[n] = (char *)malloc(sizeof(char) * 512 + 1);
781 strncpy(suggurls[n], suggline, 512);
782 suggestions[n] = suggurls[n];
783 row_eventbox = fill_eventbox(suggline);
784 gtk_box_pack_start(_table, GTK_WIDGET(row_eventbox), FALSE, FALSE, 0);
785 widgets[n++] = row_eventbox;
786 elementpointer = elementpointer->next;
787 if (n >= MAX_LIST_SIZE)
788 break;
790 free_list(elementlist);
791 if (suggurls != NULL) {
792 free(suggurls);
793 suggurls = NULL;
795 if (entry != NULL) {
796 free(entry);
797 entry = NULL;
800 /* TA: FIXME - this needs rethinking entirely. */
802 GtkWidget **widgets_temp = realloc(widgets, sizeof(*widgets) * n);
803 if (widgets_temp == NULL && widgets == NULL) {
804 fprintf(stderr, "Couldn't realloc() widgets\n");
805 exit(1);
807 widgets = widgets_temp;
808 char **suggestions_temp = realloc(suggestions, sizeof(*suggestions) * n);
809 if (suggestions_temp == NULL && suggestions == NULL) {
810 fprintf(stderr, "Couldn't realloc() suggestions\n");
811 exit(1);
813 suggestions = suggestions_temp;
815 if (!n) {
816 gdk_color_parse(completionbgcolor[1], &color);
817 gtk_widget_modify_bg(table, GTK_STATE_NORMAL, &color);
818 el = gtk_label_new(NULL);
819 gtk_misc_set_alignment(GTK_MISC(el), 0, 0);
820 markup = g_strconcat("<span font=\"", completionfont[1], "\" color=\"", completioncolor[1], "\">No Completions</span>", NULL);
821 gtk_label_set_markup(GTK_LABEL(el), markup);
822 g_free(markup);
823 gtk_box_pack_start(_table, GTK_WIDGET(el), FALSE, FALSE, 0);
825 gtk_box_pack_start(box, GTK_WIDGET(top_border), FALSE, FALSE, 0);
826 gtk_container_add(GTK_CONTAINER(table), GTK_WIDGET(_table));
827 gtk_box_pack_start(box, GTK_WIDGET(table), FALSE, FALSE, 0);
828 gtk_widget_show_all(GTK_WIDGET(window));
829 if (!n)
830 return TRUE;
831 current = arg->i == DirectionPrev ? n - 1 : 0;
833 if (current != -1) {
834 gdk_color_parse(completionbgcolor[2], &color);
835 gtk_widget_modify_bg(GTK_WIDGET(widgets[current]), GTK_STATE_NORMAL, &color);
836 s = g_strconcat(":", suggestions[current], NULL);
837 gtk_entry_set_text(GTK_ENTRY(inputbox), s);
838 g_free(s);
839 } else
840 gtk_entry_set_text(GTK_ENTRY(inputbox), prefix);
841 gtk_editable_set_position(GTK_EDITABLE(inputbox), -1);
842 return TRUE;
845 gboolean
846 descend(const Arg *arg) {
847 char *source = (char*)webkit_web_view_get_uri(webview), *p = &source[0], *new;
848 int i, len;
849 count = count ? count : 1;
851 if (!source)
852 return TRUE;
853 if (arg->i == Rootdir) {
854 for (i = 0; i < 3; i++) /* get to the third slash */
855 if (!(p = strchr(++p, '/')))
856 return TRUE; /* if we cannot find it quit */
857 } else {
858 len = strlen(source);
859 if (!len) /* if string is empty quit */
860 return TRUE;
861 p = source + len; /* start at the end */
862 if (*(p - 1) == '/') /* /\/$/ is not an additional level */
863 ++count;
864 for (i = 0; i < count; i++)
865 while(*(p--) != '/' || *p == '/') /* count /\/+/ as one slash */
866 if (p == source) /* if we reach the first char pointer quit */
867 return TRUE;
868 ++p; /* since we do p-- in the while, we are pointing at
869 the char before the slash, so +1 */
871 len = p - source + 1; /* new length = end - start + 1 */
872 new = malloc(len + 1);
873 memcpy(new, source, len);
874 new[len] = '\0';
875 webkit_web_view_load_uri(webview, new);
876 free(new);
877 return TRUE;
880 gboolean
881 echo(const Arg *arg) {
882 int index = !arg->s ? 0 : arg->i & (~NoAutoHide);
884 if (index < Info || index > Error)
885 return TRUE;
887 set_widget_font_and_color(inputbox, urlboxfont[index], urlboxbgcolor[index], urlboxcolor[index]);
888 gtk_entry_set_text(GTK_ENTRY(inputbox), !arg->s ? "" : arg->s);
890 return TRUE;
893 gboolean
894 input(const Arg *arg) {
895 int pos = 0;
896 count = 0;
897 const char *url;
898 int index = Info;
899 Arg a;
901 /* if inputbox hidden, show it again */
902 if (!gtk_widget_get_visible(inputbox))
903 gtk_widget_set_visible(inputbox, TRUE);
905 update_state();
907 /* Set the colour and font back to the default, so that we don't still
908 * maintain a red colour from a warning from an end of search indicator,
909 * etc.
911 set_widget_font_and_color(inputbox, urlboxfont[index], urlboxbgcolor[index], urlboxcolor[index]);
913 if (arg->s[0] == '`' || arg->s[0] == '~') {
914 memset(followTarget, 0, 0);
915 strncpy(followTarget, arg->s[0] == '`' ? "current" : "new", 8);
916 a.i = Silent;
917 a.s = g_strdup("vimprobable_show_hints()");
918 script(&a);
921 /* to avoid things like :open URL :open URL2 or :open :open URL */
922 gtk_entry_set_text(GTK_ENTRY(inputbox), "");
923 gtk_editable_insert_text(GTK_EDITABLE(inputbox), arg->s, -1, &pos);
924 if (arg->i & InsertCurrentURL && (url = webkit_web_view_get_uri(webview)))
925 gtk_editable_insert_text(GTK_EDITABLE(inputbox), url, -1, &pos);
926 gtk_widget_grab_focus(inputbox);
927 gtk_editable_set_position(GTK_EDITABLE(inputbox), -1);
929 return TRUE;
932 gboolean
933 navigate(const Arg *arg) {
934 if (arg->i & NavigationForwardBack)
935 webkit_web_view_go_back_or_forward(webview, (arg->i == NavigationBack ? -1 : 1) * (count ? count : 1));
936 else if (arg->i & NavigationReloadActions)
937 (arg->i == NavigationReload ? webkit_web_view_reload : webkit_web_view_reload_bypass_cache)(webview);
938 else
939 webkit_web_view_stop_loading(webview);
940 return TRUE;
943 gboolean
944 number(const Arg *arg) {
945 const char *source = webkit_web_view_get_uri(webview);
946 char *uri, *p, *new;
947 int number, diff = (count ? count : 1) * (arg->i == Increment ? 1 : -1);
949 if (!source)
950 return TRUE;
951 uri = g_strdup_printf(source); /* copy string */
952 p =& uri[0];
953 while(*p != '\0') /* goto the end of the string */
954 ++p;
955 --p;
956 while(*p >= '0' && *p <= '9') /* go back until non number char is reached */
957 --p;
958 if (*(++p) == '\0') { /* if no numbers were found abort */
959 free(uri);
960 return TRUE;
962 number = atoi(p) + diff; /* apply diff on number */
963 *p = '\0';
964 new = g_strdup_printf("%s%d", uri, number); /* create new uri */
965 webkit_web_view_load_uri(webview, new);
966 g_free(new);
967 free(uri);
968 return TRUE;
971 gboolean
972 open_arg(const Arg *arg) {
973 char *argv[6];
974 char *s = arg->s, *p = NULL, *new;
975 Arg a = { .i = NavigationReload };
976 int len;
977 char *search_uri, *search_term;
979 if (embed) {
980 argv[0] = *args;
981 argv[1] = "-e";
982 argv[2] = winid;
983 argv[3] = arg->s;
984 argv[4] = NULL;
985 } else {
986 argv[0] = *args;
987 argv[1] = arg->s;
988 argv[2] = NULL;
991 if (!arg->s)
992 navigate(&a);
993 else if (arg->i == TargetCurrent) {
994 while(*s == ' ') /* strip leading whitespace */
995 ++s;
996 p = (s + strlen(s) - 1);
997 while(*p == ' ') /* strip trailing whitespace */
998 --p;
999 *(p + 1) = '\0';
1000 len = strlen(s);
1001 new = NULL, p = strchr(s, ' ');
1002 if (p) { /* check for search engines */
1003 *p = '\0';
1004 search_uri = find_uri_for_searchengine(s);
1005 if (search_uri != NULL) {
1006 search_term = soup_uri_encode(p+1, "&");
1007 new = g_strdup_printf(search_uri, search_term);
1008 g_free(search_term);
1010 *p = ' ';
1012 if (!new) {
1013 if (len > 3 && strstr(s, "://")) { /* valid url? */
1014 p = new = g_malloc(len + 1);
1015 while(*s != '\0') { /* strip whitespaces */
1016 if (*s != ' ')
1017 *(p++) = *s;
1018 ++s;
1020 *p = '\0';
1021 } else if (strcspn(s, "/") == 0 || strcspn(s, "./") == 0) { /* prepend "file://" */
1022 new = g_malloc(sizeof("file://") + len);
1023 strcpy(new, "file://");
1024 memcpy(&new[sizeof("file://") - 1], s, len + 1);
1025 } else if (p || !strchr(s, '.')) { /* whitespaces or no dot? */
1026 search_uri = find_uri_for_searchengine(defaultsearch);
1027 if (search_uri != NULL) {
1028 search_term = soup_uri_encode(s, "&");
1029 new = g_strdup_printf(search_uri, search_term);
1030 g_free(search_term);
1032 } else { /* prepend "http://" */
1033 new = g_malloc(sizeof("http://") + len);
1034 strcpy(new, "http://");
1035 memcpy(&new[sizeof("http://") - 1], s, len + 1);
1038 webkit_web_view_load_uri(webview, new);
1039 g_free(new);
1040 } else
1041 g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL);
1042 return TRUE;
1045 gboolean
1046 open_remembered(const Arg *arg)
1048 Arg a = {arg->i, rememberedURI};
1050 if (strcmp(rememberedURI, "")) {
1051 open_arg(&a);
1053 return TRUE;
1056 gboolean
1057 yank(const Arg *arg) {
1058 const char *url, *feedback;
1060 if (arg->i & SourceURL) {
1061 url = webkit_web_view_get_uri(webview);
1062 if (!url)
1063 return TRUE;
1064 feedback = g_strconcat("Yanked ", url, NULL);
1065 give_feedback(feedback);
1066 if (arg->i & ClipboardPrimary)
1067 gtk_clipboard_set_text(clipboards[0], url, -1);
1068 if (arg->i & ClipboardGTK)
1069 gtk_clipboard_set_text(clipboards[1], url, -1);
1070 } else
1071 webkit_web_view_copy_clipboard(webview);
1072 return TRUE;
1075 gboolean
1076 paste(const Arg *arg) {
1077 Arg a = { .i = arg->i & TargetNew, .s = NULL };
1079 /* If we're over a link, open it in a new target. */
1080 if (strlen(rememberedURI) > 0) {
1081 Arg new_target = { .i = TargetNew, .s = arg->s };
1082 open_arg(&new_target);
1083 return TRUE;
1086 if (arg->i & ClipboardPrimary)
1087 a.s = gtk_clipboard_wait_for_text(clipboards[0]);
1088 if (!a.s && arg->i & ClipboardGTK)
1089 a.s = gtk_clipboard_wait_for_text(clipboards[1]);
1090 if (a.s)
1091 open_arg(&a);
1092 return TRUE;
1095 gboolean
1096 quit(const Arg *arg) {
1097 FILE *f;
1098 const char *filename;
1099 const char *uri = webkit_web_view_get_uri(webview);
1100 if (uri != NULL) {
1101 /* write last URL into status file for recreation with "u" */
1102 filename = g_strdup_printf(CLOSED_URL_FILENAME);
1103 f = fopen(filename, "w");
1104 if (f != NULL) {
1105 fprintf(f, "%s", uri);
1106 fclose(f);
1109 gtk_main_quit();
1110 return TRUE;
1113 gboolean
1114 revive(const Arg *arg) {
1115 FILE *f;
1116 const char *filename;
1117 char buffer[512] = "";
1118 Arg a = { .i = TargetNew, .s = NULL };
1119 /* get the URL of the window which has been closed last */
1120 filename = g_strdup_printf(CLOSED_URL_FILENAME);
1121 f = fopen(filename, "r");
1122 if (f != NULL) {
1123 fgets(buffer, 512, f);
1124 fclose(f);
1126 if (strlen(buffer) > 0) {
1127 a.s = buffer;
1128 open_arg(&a);
1129 return TRUE;
1131 return FALSE;
1134 static
1135 gboolean print_frame(const Arg *arg)
1137 WebKitWebFrame *frame = webkit_web_view_get_main_frame(webview);
1138 webkit_web_frame_print (frame);
1139 return TRUE;
1142 gboolean
1143 search(const Arg *arg) {
1144 count = count ? count : 1;
1145 gboolean success, direction = arg->i & DirectionPrev;
1146 Arg a;
1148 if (arg->s) {
1149 free(search_handle);
1150 search_handle = g_strdup_printf(arg->s);
1152 if (!search_handle)
1153 return TRUE;
1154 if (arg->i & DirectionAbsolute)
1155 search_direction = direction;
1156 else
1157 direction ^= search_direction;
1158 do {
1159 success = webkit_web_view_search_text(webview, search_handle, arg->i & CaseSensitive, direction, FALSE);
1160 if (!success) {
1161 if (arg->i & Wrapping) {
1162 success = webkit_web_view_search_text(webview, search_handle, arg->i & CaseSensitive, direction, TRUE);
1163 if (success) {
1164 a.i = Warning;
1165 a.s = g_strdup_printf("search hit %s, continuing at %s",
1166 direction ? "BOTTOM" : "TOP",
1167 direction ? "TOP" : "BOTTOM");
1168 echo(&a);
1169 g_free(a.s);
1170 } else
1171 break;
1172 } else
1173 break;
1175 } while(--count);
1176 if (!success) {
1177 a.i = Error;
1178 a.s = g_strdup_printf("Pattern not found: %s", search_handle);
1179 echo(&a);
1180 g_free(a.s);
1182 return TRUE;
1185 gboolean
1186 set(const Arg *arg) {
1187 Arg a = { .i = Info | NoAutoHide };
1189 switch (arg->i) {
1190 case ModeNormal:
1191 if (search_handle) {
1192 search_handle = NULL;
1193 webkit_web_view_unmark_text_matches(webview);
1195 gtk_entry_set_text(GTK_ENTRY(inputbox), "");
1196 gtk_widget_grab_focus(GTK_WIDGET(webview));
1197 break;
1198 case ModePassThrough:
1199 a.s = g_strdup("-- PASS THROUGH --");
1200 echo(&a);
1201 g_free(a.s);
1202 break;
1203 case ModeSendKey:
1204 a.s = g_strdup("-- PASS TROUGH (next) --");
1205 echo(&a);
1206 g_free(a.s);
1207 break;
1208 case ModeInsert: /* should not be called manually but automatically */
1209 a.s = g_strdup("-- INSERT --");
1210 echo(&a);
1211 g_free(a.s);
1212 break;
1213 default:
1214 return TRUE;
1216 mode = arg->i;
1217 return TRUE;
1220 gchar*
1221 jsapi_ref_to_string(JSContextRef context, JSValueRef ref) {
1222 JSStringRef string_ref;
1223 gchar *string;
1224 size_t length;
1226 string_ref = JSValueToStringCopy(context, ref, NULL);
1227 length = JSStringGetMaximumUTF8CStringSize(string_ref);
1228 string = g_new(gchar, length);
1229 JSStringGetUTF8CString(string_ref, string, length);
1230 JSStringRelease(string_ref);
1231 return string;
1234 void
1235 jsapi_evaluate_script(const gchar *script, gchar **value, gchar **message) {
1236 WebKitWebFrame *frame = webkit_web_view_get_main_frame(webview);
1237 JSGlobalContextRef context = webkit_web_frame_get_global_context(frame);
1238 JSStringRef str;
1239 JSValueRef val, exception;
1241 str = JSStringCreateWithUTF8CString(script);
1242 val = JSEvaluateScript(context, str, JSContextGetGlobalObject(context), NULL, 0, &exception);
1243 JSStringRelease(str);
1244 if (!val)
1245 *message = jsapi_ref_to_string(context, exception);
1246 else
1247 *value = jsapi_ref_to_string(context, val);
1250 gboolean
1251 quickmark(const Arg *a) {
1252 int i, b;
1253 b = atoi(a->s);
1254 char *fn = g_strdup_printf(QUICKMARK_FILE);
1255 FILE *fp;
1256 fp = fopen(fn, "r");
1257 char buf[100];
1259 if (fp != NULL && b < 10) {
1260 for( i=0; i < b; ++i ) {
1261 if (feof(fp)) {
1262 break;
1264 fgets(buf, 100, fp);
1266 char *ptr = strrchr(buf, '\n');
1267 *ptr = '\0';
1268 Arg x = { .s = buf };
1269 if (strlen(buf))
1270 return open_arg(&x);
1271 else {
1272 x.i = Error;
1273 x.s = g_strdup_printf("Quickmark %d not defined", b);
1274 echo(&x);
1275 g_free(x.s);
1276 return false;
1278 } else { return false; }
1281 gboolean
1282 script(const Arg *arg) {
1283 gchar *value = NULL, *message = NULL;
1284 Arg a;
1286 if (!arg->s) {
1287 set_error("Missing argument.");
1288 return FALSE;
1290 jsapi_evaluate_script(arg->s, &value, &message);
1291 if (message) {
1292 set_error(message);
1293 if (arg->s)
1294 g_free(arg->s);
1295 return FALSE;
1297 if (arg->i != Silent && value) {
1298 a.i = arg->i;
1299 a.s = g_strdup(value);
1300 echo(&a);
1301 g_free(a.s);
1303 if (value) {
1304 if (strncmp(value, "fire;", 5) == 0) {
1305 count = 0;
1306 a.s = g_strconcat("vimprobable_fire(", (value + 5), ")", NULL);
1307 a.i = Silent;
1308 script(&a);
1309 } else if (strncmp(value, "open;", 5) == 0) {
1310 count = 0;
1311 a.i = ModeNormal;
1312 set(&a);
1313 if (strncmp(followTarget, "new", 3) == 0)
1314 a.i = TargetNew;
1315 else
1316 a.i = TargetCurrent;
1317 memset(followTarget, 0, 8);
1318 a.s = (value + 5);
1319 open_arg(&a);
1322 if (arg->s)
1323 g_free(arg->s);
1324 g_free(value);
1325 return TRUE;
1328 gboolean
1329 scroll(const Arg *arg) {
1330 GtkAdjustment *adjust = (arg->i & OrientationHoriz) ? adjust_h : adjust_v;
1331 int max = gtk_adjustment_get_upper(adjust) - gtk_adjustment_get_page_size(adjust);
1332 float val = gtk_adjustment_get_value(adjust) / max * 100;
1333 int direction = (arg->i & (1 << 2)) ? 1 : -1;
1335 if ((direction == 1 && val < 100) || (direction == -1 && val > 0)) {
1336 if (arg->i & ScrollMove)
1337 gtk_adjustment_set_value(adjust, gtk_adjustment_get_value(adjust) +
1338 direction * /* direction */
1339 ((arg->i & UnitLine || (arg->i & UnitBuffer && count)) ? (scrollstep * (count ? count : 1)) : (
1340 arg->i & UnitBuffer ? gtk_adjustment_get_page_size(adjust) / 2 :
1341 (count ? count : 1) * (gtk_adjustment_get_page_size(adjust) -
1342 (gtk_adjustment_get_page_size(adjust) > pagingkeep ? pagingkeep : 0)))));
1343 else
1344 gtk_adjustment_set_value(adjust,
1345 ((direction == 1) ? gtk_adjustment_get_upper : gtk_adjustment_get_lower)(adjust));
1346 update_state();
1348 return TRUE;
1351 gboolean
1352 zoom(const Arg *arg) {
1353 webkit_web_view_set_full_content_zoom(webview, (arg->i & ZoomFullContent) > 0);
1354 webkit_web_view_set_zoom_level(webview, (arg->i & ZoomOut) ?
1355 webkit_web_view_get_zoom_level(webview) +
1356 (((float)(count ? count : 1)) * (arg->i & (1 << 1) ? 1.0 : -1.0) * zoomstep) :
1357 (count ? (float)count / 100.0 : 1.0));
1358 return TRUE;
1361 gboolean
1362 fake_key_event(const Arg *a) {
1363 if(!embed) {
1364 return FALSE;
1366 Arg err;
1367 err.i = Error;
1368 Display *xdpy;
1369 if ( (xdpy = XOpenDisplay(NULL)) == NULL ) {
1370 err.s = g_strdup("Couldn't find the XDisplay.");
1371 echo(&err);
1372 g_free(err.s);
1373 return FALSE;
1376 XKeyEvent xk;
1377 xk.display = xdpy;
1378 xk.subwindow = None;
1379 xk.time = CurrentTime;
1380 xk.same_screen = True;
1381 xk.x = xk.y = xk.x_root = xk.y_root = 1;
1382 xk.window = embed;
1383 xk.state = a->i;
1385 if( ! a->s ) {
1386 err.s = g_strdup("Zero pointer as argument! Check your config.h");
1387 echo(&err);
1388 g_free(err.s);
1389 return FALSE;
1392 KeySym keysym;
1393 if( (keysym = XStringToKeysym(a->s)) == NoSymbol ) {
1394 err.s = g_strdup_printf("Couldn't translate %s to keysym", a->s );
1395 echo(&err);
1396 g_free(err.s);
1397 return FALSE;
1400 if( (xk.keycode = XKeysymToKeycode(xdpy, keysym)) == NoSymbol ) {
1401 err.s = g_strdup("Couldn't translate keysym to keycode");
1402 echo(&err);
1403 g_free(err.s);
1404 return FALSE;
1407 xk.type = KeyPress;
1408 if( !XSendEvent(xdpy, embed, True, KeyPressMask, (XEvent *)&xk) ) {
1409 err.s = g_strdup("XSendEvent failed");
1410 echo(&err);
1411 g_free(err.s);
1412 return FALSE;
1414 XFlush(xdpy);
1416 return TRUE;
1420 gboolean
1421 commandhistoryfetch(const Arg *arg) {
1422 if (arg->i == DirectionPrev) {
1423 commandpointer--;
1424 if (commandpointer < 0)
1425 commandpointer = maxcommands - 1;
1426 } else {
1427 commandpointer++;
1428 if (commandpointer == COMMANDHISTSIZE || commandpointer == maxcommands)
1429 commandpointer = 0;
1432 if (commandpointer < 0)
1433 return FALSE;
1435 gtk_entry_set_text(GTK_ENTRY(inputbox), commandhistory[commandpointer ]);
1436 gtk_editable_set_position(GTK_EDITABLE(inputbox), -1);
1437 return TRUE;
1441 gboolean
1442 bookmark(const Arg *arg) {
1443 FILE *f;
1444 const char *filename;
1445 const char *uri = webkit_web_view_get_uri(webview);
1446 const char *title = webkit_web_view_get_title(webview);
1447 filename = g_strdup_printf(BOOKMARKS_STORAGE_FILENAME);
1448 f = fopen(filename, "a");
1449 if (uri == NULL || strlen(uri) == 0) {
1450 set_error("No URI found to bookmark.");
1451 return FALSE;
1453 if (f != NULL) {
1454 fprintf(f, "%s", uri);
1455 if (title != NULL) {
1456 fprintf(f, "%s", " ");
1457 fprintf(f, "%s", title);
1459 if (arg->s && strlen(arg->s)) {
1460 build_taglist(arg, f);
1462 fprintf(f, "%s", "\n");
1463 fclose(f);
1464 give_feedback( "Bookmark saved" );
1465 return TRUE;
1466 } else {
1467 set_error("Bookmarks file not found.");
1468 return FALSE;
1472 gboolean
1473 history() {
1474 FILE *f;
1475 const char *filename;
1476 const char *uri = webkit_web_view_get_uri(webview);
1477 const char *title = webkit_web_view_get_title(webview);
1478 char *entry, buffer[512], *new;
1479 int n, i = 0;
1480 gboolean finished = FALSE;
1481 if (uri != NULL) {
1482 if (title != NULL) {
1483 entry = malloc((strlen(uri) + strlen(title) + 2) * sizeof(char));
1484 memset(entry, 0, strlen(uri) + strlen(title) + 2);
1485 } else {
1486 entry = malloc((strlen(uri) + 1) * sizeof(char));
1487 memset(entry, 0, strlen(uri) + 1);
1489 if (entry != NULL) {
1490 strncpy(entry, uri, strlen(uri));
1491 if (title != NULL) {
1492 strncat(entry, " ", 1);
1493 strncat(entry, title, strlen(title));
1495 n = strlen(entry);
1496 filename = g_strdup_printf(HISTORY_STORAGE_FILENAME);
1497 f = fopen(filename, "r");
1498 if (f != NULL) {
1499 new = (char *)malloc(HISTORY_MAX_ENTRIES * 512 * sizeof(char) + 1);
1500 if (new != NULL) {
1501 memset(new, 0, HISTORY_MAX_ENTRIES * 512 * sizeof(char) + 1);
1502 /* newest entries go on top */
1503 strncpy(new, entry, strlen(entry));
1504 strncat(new, "\n", 1);
1505 /* retain at most HISTORY_MAX_ENTIRES - 1 old entries */
1506 while (finished != TRUE) {
1507 if ((char *)NULL == fgets(buffer, 512, f)) {
1508 /* check if end of file was reached / error occured */
1509 if (!feof(f)) {
1510 break;
1512 /* end of file reached */
1513 finished = TRUE;
1514 continue;
1516 /* compare line (-1 because of newline character) */
1517 if (n != strlen(buffer) - 1 || strncmp(entry, buffer, n) != 0) {
1518 /* if the URI is already in history; we put it on top and skip it here */
1519 strncat(new, buffer, 512);
1520 i++;
1522 if ((i + 1) >= HISTORY_MAX_ENTRIES) {
1523 break;
1526 fclose(f);
1528 f = fopen(filename, "w");
1529 if (f != NULL) {
1530 fprintf(f, "%s", new);
1531 fclose(f);
1533 if (new != NULL) {
1534 free(new);
1535 new = NULL;
1539 if (entry != NULL) {
1540 free(entry);
1541 entry = NULL;
1544 return TRUE;
1547 static gboolean
1548 view_source(const Arg * arg) {
1549 gboolean current_mode = webkit_web_view_get_view_source_mode(webview);
1550 webkit_web_view_set_view_source_mode(webview, !current_mode);
1551 webkit_web_view_reload(webview);
1552 return TRUE;
1555 static gboolean
1556 focus_input(const Arg *arg) {
1557 static Arg a;
1559 a.s = g_strconcat("vimprobable_focus_input()", NULL);
1560 a.i = Silent;
1561 script(&a);
1562 update_state();
1563 return TRUE;
1566 static gboolean
1567 browser_settings(const Arg *arg) {
1568 char line[255];
1569 if (!arg->s) {
1570 set_error("Missing argument.");
1571 return FALSE;
1573 strncpy(line, arg->s, 254);
1574 if (process_set_line(line))
1575 return TRUE;
1576 else {
1577 set_error("Invalid setting.");
1578 return FALSE;
1582 char *
1583 search_word(int whichword) {
1584 int k = 0;
1585 static char word[240];
1586 char *c = my_pair.line;
1588 while (isspace(*c) && *c)
1589 c++;
1591 switch (whichword) {
1592 case 0:
1593 while (*c && !isspace (*c) && *c != '=' && k < 240) {
1594 word[k++] = *c;
1595 c++;
1597 word[k] = '\0';
1598 strncpy(my_pair.what, word, 20);
1599 break;
1600 case 1:
1601 while (*c && k < 240) {
1602 word[k++] = *c;
1603 c++;
1605 word[k] = '\0';
1606 strncpy(my_pair.value, word, 240);
1607 break;
1610 return c;
1613 static gboolean
1614 process_set_line(char *line) {
1615 char *c;
1616 int listlen, i;
1617 gboolean boolval;
1618 WebKitWebSettings *settings;
1620 settings = webkit_web_view_get_settings(webview);
1621 my_pair.line = line;
1622 c = search_word(0);
1623 if (!strlen(my_pair.what))
1624 return FALSE;
1626 while (isspace(*c) && *c)
1627 c++;
1629 if (*c == ':' || *c == '=')
1630 c++;
1632 my_pair.line = c;
1633 c = search_word(1);
1635 listlen = LENGTH(browsersettings);
1636 for (i = 0; i < listlen; i++) {
1637 if (strlen(browsersettings[i].name) == strlen(my_pair.what) && strncmp(browsersettings[i].name, my_pair.what, strlen(my_pair.what)) == 0) {
1638 /* mandatory argument not provided */
1639 if (strlen(my_pair.value) == 0)
1640 return FALSE;
1641 /* process qmark? */
1642 if (strlen(my_pair.what) == 5 && strncmp("qmark", my_pair.what, 5) == 0) {
1643 return (process_save_qmark(my_pair.value, webview));
1645 /* interpret boolean values */
1646 if (browsersettings[i].boolval) {
1647 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) {
1648 boolval = TRUE;
1649 } 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) {
1650 boolval = FALSE;
1651 } else {
1652 return FALSE;
1654 } else if (browsersettings[i].colourval) {
1655 /* interpret as hexadecimal colour */
1656 if (!parse_colour(my_pair.value)) {
1657 return FALSE;
1660 if (browsersettings[i].var != NULL) {
1661 /* write value into internal variable */
1662 /*if (browsersettings[i].intval) {
1663 browsersettings[i].var = atoi(my_pair.value);
1664 } else {*/
1665 strncpy(browsersettings[i].var, my_pair.value, MAX_SETTING_SIZE);
1666 if (strlen(my_pair.value) > MAX_SETTING_SIZE - 1) {
1667 /* in this case, \0 will not have been copied */
1668 browsersettings[i].var[MAX_SETTING_SIZE - 1] = '\0';
1669 /* in case this string is also used for a webkit setting, make sure it's consistent */
1670 my_pair.value[MAX_SETTING_SIZE - 1] = '\0';
1671 give_feedback("String too long; automatically truncated!");
1673 /*}*/
1675 if (strlen(browsersettings[i].webkit) > 0) {
1676 /* activate appropriate webkit setting */
1677 if (browsersettings[i].boolval) {
1678 g_object_set((GObject*)settings, browsersettings[i].webkit, boolval, NULL);
1679 } else if (browsersettings[i].intval) {
1680 g_object_set((GObject*)settings, browsersettings[i].webkit, atoi(my_pair.value), NULL);
1681 } else {
1682 g_object_set((GObject*)settings, browsersettings[i].webkit, my_pair.value, NULL);
1684 webkit_web_view_set_settings(webview, settings);
1687 /* process acceptlanguage*/
1688 if (strlen(my_pair.what) == 14 && strncmp("acceptlanguage", my_pair.what, 14) == 0) {
1689 g_object_set(G_OBJECT(session), "accept-language", acceptlanguage, NULL);
1692 /* toggle proxy usage? */
1693 if (strlen(my_pair.what) == 5 && strncmp("proxy", my_pair.what, 5) == 0) {
1694 toggle_proxy(boolval);
1697 /* Toggle scrollbars. */
1698 if (strlen(my_pair.what) == 10 && strncmp("scrollbars", my_pair.what, 10) == 0)
1699 toggle_scrollbars(boolval);
1701 /* Toggle widgets */
1702 if (strlen(my_pair.what) == 9 && strncmp("statusbar", my_pair.what, 9) == 0)
1703 gtk_widget_set_visible(GTK_WIDGET(statusbar), boolval);
1704 if (strlen(my_pair.what) == 8 && strncmp("inputbox", my_pair.what, 8) == 0)
1705 gtk_widget_set_visible(inputbox, boolval);
1707 /* case sensitivity of completion */
1708 if (strlen(my_pair.what) == 14 && strncmp("completioncase", my_pair.what, 14) == 0)
1709 complete_case_sensitive = boolval;
1711 /* reload page? */
1712 if (browsersettings[i].reload)
1713 webkit_web_view_reload(webview);
1714 return TRUE;
1717 return FALSE;
1720 gboolean
1721 process_line(char *line) {
1722 char *c = line;
1723 int i;
1724 size_t len, length = strlen(line);
1725 gboolean found = FALSE, success = FALSE;
1726 Arg a;
1728 while (isspace(*c))
1729 c++;
1730 /* Ignore blank lines. */
1731 if (c[0] == '\0')
1732 return TRUE;
1733 for (i = 0; i < LENGTH(commands); i++) {
1734 if (commands[i].cmd == NULL)
1735 break;
1736 len = strlen(commands[i].cmd);
1737 if (length >= len && !strncmp(c, commands[i].cmd, len) && (c[len] == ' ' || !c[len])) {
1738 found = TRUE;
1739 a.i = commands[i].arg.i;
1740 a.s = length > len + 1 ? &c[len + 1] : commands[i].arg.s;
1741 success = commands[i].func(&a);
1742 break;
1745 save_command_history(c);
1746 if (!found) {
1747 a.i = Error;
1748 a.s = g_strdup_printf("Not a browser command: %s", c);
1749 echo(&a);
1750 } else if (!success) {
1751 a.i = Error;
1752 if (error_msg != NULL) {
1753 a.s = g_strdup_printf("%s", error_msg);
1754 g_free(error_msg);
1755 error_msg = NULL;
1756 } else {
1757 a.s = g_strdup_printf("Unknown error. Please file a bug report!");
1759 echo(&a);
1760 g_free(a.s);
1762 return success;
1765 static gboolean
1766 search_tag(const Arg * a) {
1767 FILE *f;
1768 const char *filename;
1769 const char *tag = a->s;
1770 char s[BUFFERSIZE], foundtag[40], url[BUFFERSIZE];
1771 int t, i, intag, k;
1773 if (!tag) {
1774 /* The user must give us something to load up. */
1775 set_error("Bookmark tag required with this option.");
1776 return FALSE;
1779 if (strlen(tag) > MAXTAGSIZE) {
1780 set_error("Tag too long.");
1781 return FALSE;
1784 filename = g_strdup_printf(BOOKMARKS_STORAGE_FILENAME);
1785 f = fopen(filename, "r");
1786 if (f == NULL) {
1787 set_error("Couldn't open bookmarks file.");
1788 return FALSE;
1790 while (fgets(s, BUFFERSIZE-1, f)) {
1791 intag = 0;
1792 t = strlen(s) - 1;
1793 while (isspace(s[t]))
1794 t--;
1795 if (s[t] != ']') continue;
1796 while (t > 0) {
1797 if (s[t] == ']') {
1798 if (!intag)
1799 intag = t;
1800 else
1801 intag = 0;
1802 } else {
1803 if (s[t] == '[') {
1804 if (intag) {
1805 i = 0;
1806 k = t + 1;
1807 while (k < intag)
1808 foundtag[i++] = s[k++];
1809 foundtag[i] = '\0';
1810 /* foundtag now contains the tag */
1811 if (strlen(foundtag) < MAXTAGSIZE && strcmp(tag, foundtag) == 0) {
1812 i = 0;
1813 while (isspace(s[i])) i++;
1814 k = 0;
1815 while (s[i] && !isspace(s[i])) url[k++] = s[i++];
1816 url[k] = '\0';
1817 Arg x = { .i = TargetNew, .s = url };
1818 open_arg(&x);
1821 intag = 0;
1824 t--;
1827 return TRUE;
1830 void
1831 toggle_proxy(gboolean onoff) {
1832 SoupURI *proxy_uri;
1833 char *filename, *new;
1834 int len;
1836 if (onoff == FALSE) {
1837 g_object_set(session, "proxy-uri", NULL, NULL);
1838 give_feedback("Proxy deactivated");
1839 } else {
1840 filename = (char *)g_getenv("http_proxy");
1842 /* Fallthrough to checking HTTP_PROXY as well, since this can also be
1843 * defined.
1845 if (filename == NULL)
1846 filename = (char *)g_getenv("HTTP_PROXY");
1848 if (filename != NULL && 0 < (len = strlen(filename))) {
1849 if (strstr(filename, "://") == NULL) {
1850 /* prepend http:// */
1851 new = g_malloc(sizeof("http://") + len);
1852 strcpy(new, "http://");
1853 memcpy(&new[sizeof("http://") - 1], filename, len + 1);
1854 proxy_uri = soup_uri_new(new);
1855 } else {
1856 proxy_uri = soup_uri_new(filename);
1858 g_object_set(session, "proxy-uri", proxy_uri, NULL);
1859 give_feedback("Proxy activated");
1864 void
1865 toggle_scrollbars(gboolean onoff) {
1866 if (onoff == TRUE) {
1867 adjust_h = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(viewport));
1868 adjust_v = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(viewport));
1870 else {
1871 adjust_v = gtk_range_get_adjustment(GTK_RANGE(scroll_v));
1872 adjust_h = gtk_range_get_adjustment(GTK_RANGE(scroll_h));
1874 gtk_widget_set_scroll_adjustments (GTK_WIDGET(webview), adjust_h, adjust_v);
1876 return;
1879 void
1880 update_url(const char *uri) {
1881 gboolean ssl = g_str_has_prefix(uri, "https://");
1882 GdkColor color;
1883 #ifdef ENABLE_HISTORY_INDICATOR
1884 char before[] = " [";
1885 char after[] = "]";
1886 gboolean back = webkit_web_view_can_go_back(webview);
1887 gboolean fwd = webkit_web_view_can_go_forward(webview);
1889 if (!back && !fwd)
1890 before[0] = after[0] = '\0';
1891 #endif
1892 gtk_label_set_markup(GTK_LABEL(status_url), g_markup_printf_escaped(
1893 #ifdef ENABLE_HISTORY_INDICATOR
1894 "<span font=\"%s\">%s%s%s%s%s</span>", statusfont, uri,
1895 before, back ? "+" : "", fwd ? "-" : "", after
1896 #else
1897 "<span font=\"%s\">%s</span>", statusfont, uri
1898 #endif
1900 gdk_color_parse(ssl ? sslbgcolor : statusbgcolor, &color);
1901 gtk_widget_modify_bg(eventbox, GTK_STATE_NORMAL, &color);
1902 gdk_color_parse(ssl ? sslcolor : statuscolor, &color);
1903 gtk_widget_modify_fg(GTK_WIDGET(status_url), GTK_STATE_NORMAL, &color);
1904 gtk_widget_modify_fg(GTK_WIDGET(status_state), GTK_STATE_NORMAL, &color);
1907 void
1908 update_state() {
1909 char *markup;
1910 int download_count = g_list_length(activeDownloads);
1911 GString *status = g_string_new("");
1913 /* construct the status line */
1915 /* count, modkey and input buffer */
1916 g_string_append_printf(status, "%.0d", count);
1917 if (current_modkey) g_string_append_c(status, current_modkey);
1919 /* the number of active downloads */
1920 if (activeDownloads) {
1921 g_string_append_printf(status, " %d active %s", download_count,
1922 (download_count == 1) ? "download" : "downloads");
1925 #ifdef ENABLE_WGET_PROGRESS_BAR
1926 /* the progressbar */
1928 int progress = -1;
1929 char progressbar[progressbartick + 1];
1931 if (activeDownloads) {
1932 progress = 0;
1933 GList *ptr;
1935 for (ptr = activeDownloads; ptr; ptr = g_list_next(ptr)) {
1936 progress += 100 * webkit_download_get_progress(ptr->data);
1939 progress /= download_count;
1941 } else if (webkit_web_view_get_load_status(webview) != WEBKIT_LOAD_FINISHED
1942 && webkit_web_view_get_load_status(webview) != WEBKIT_LOAD_FAILED) {
1944 progress = webkit_web_view_get_progress(webview) * 100;
1947 if (progress >= 0) {
1948 ascii_bar(progressbartick, progress * progressbartick / 100, progressbar);
1949 g_string_append_printf(status, " %c%s%c",
1950 progressborderleft, progressbar, progressborderright);
1953 #endif
1955 /* and the current scroll position */
1957 int max = gtk_adjustment_get_upper(adjust_v) - gtk_adjustment_get_page_size(adjust_v);
1958 int val = (int)(gtk_adjustment_get_value(adjust_v) / max * 100);
1960 if (max == 0)
1961 g_string_append(status, " All");
1962 else if (val == 0)
1963 g_string_append(status, " Top");
1964 else if (val == 100)
1965 g_string_append(status, " Bot");
1966 else
1967 g_string_append_printf(status, " %d%%", val);
1971 markup = g_markup_printf_escaped("<span font=\"%s\">%s</span>", statusfont, status->str);
1972 gtk_label_set_markup(GTK_LABEL(status_state), markup);
1974 g_string_free(status, TRUE);
1977 void
1978 setup_modkeys() {
1979 unsigned int i;
1980 modkeys = calloc(LENGTH(keys) + 1, sizeof(char));
1981 char *ptr = modkeys;
1983 for (i = 0; i < LENGTH(keys); i++)
1984 if (keys[i].modkey && !strchr(modkeys, keys[i].modkey))
1985 *(ptr++) = keys[i].modkey;
1986 modkeys = realloc(modkeys, &ptr[0] - &modkeys[0] + 1);
1989 void
1990 setup_gui() {
1991 scroll_h = GTK_SCROLLBAR(gtk_hscrollbar_new(NULL));
1992 scroll_v = GTK_SCROLLBAR(gtk_vscrollbar_new(NULL));
1993 adjust_h = gtk_range_get_adjustment(GTK_RANGE(scroll_h));
1994 adjust_v = gtk_range_get_adjustment(GTK_RANGE(scroll_v));
1995 if (embed) {
1996 window = GTK_WINDOW(gtk_plug_new(embed));
1997 } else {
1998 window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
1999 gtk_window_set_wmclass(GTK_WINDOW(window), "vimprobable2", "Vimprobable2");
2001 gtk_window_set_default_size(GTK_WINDOW(window), 640, 480);
2002 box = GTK_BOX(gtk_vbox_new(FALSE, 0));
2003 inputbox = gtk_entry_new();
2004 webview = (WebKitWebView*)webkit_web_view_new();
2005 statusbar = GTK_BOX(gtk_hbox_new(FALSE, 0));
2006 eventbox = gtk_event_box_new();
2007 status_url = gtk_label_new(NULL);
2008 status_state = gtk_label_new(NULL);
2009 GdkColor bg;
2010 PangoFontDescription *font;
2011 GdkGeometry hints = { 1, 1 };
2012 inspector = webkit_web_view_get_inspector(WEBKIT_WEB_VIEW(webview));
2014 clipboards[0] = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
2015 clipboards[1] = gtk_clipboard_get(GDK_NONE);
2016 setup_settings();
2017 gdk_color_parse(statusbgcolor, &bg);
2018 gtk_widget_modify_bg(eventbox, GTK_STATE_NORMAL, &bg);
2019 gtk_widget_set_name(GTK_WIDGET(window), "Vimprobable2");
2020 gtk_window_set_geometry_hints(window, NULL, &hints, GDK_HINT_MIN_SIZE);
2022 #ifdef DISABLE_SCROLLBAR
2023 viewport = gtk_scrolled_window_new(NULL, NULL);
2024 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(viewport), GTK_POLICY_NEVER, GTK_POLICY_NEVER);
2025 #else
2026 /* Ensure we still see scrollbars. */
2027 GtkWidget *viewport = gtk_scrolled_window_new(adjust_h, adjust_v);
2028 #endif
2030 setup_signals();
2031 gtk_container_add(GTK_CONTAINER(viewport), GTK_WIDGET(webview));
2033 /* Ensure we set the scroll adjustments now, so that if we're not drawing
2034 * titlebars, we can still scroll.
2036 gtk_widget_set_scroll_adjustments(GTK_WIDGET(webview), adjust_h, adjust_v);
2038 font = pango_font_description_from_string(urlboxfont[0]);
2039 gtk_widget_modify_font(GTK_WIDGET(inputbox), font);
2040 pango_font_description_free(font);
2041 gtk_entry_set_inner_border(GTK_ENTRY(inputbox), NULL);
2042 gtk_misc_set_alignment(GTK_MISC(status_url), 0.0, 0.0);
2043 gtk_misc_set_alignment(GTK_MISC(status_state), 1.0, 0.0);
2044 gtk_box_pack_start(statusbar, status_url, TRUE, TRUE, 2);
2045 gtk_box_pack_start(statusbar, status_state, FALSE, FALSE, 2);
2046 gtk_container_add(GTK_CONTAINER(eventbox), GTK_WIDGET(statusbar));
2047 gtk_box_pack_start(box, viewport, TRUE, TRUE, 0);
2048 gtk_box_pack_start(box, eventbox, FALSE, FALSE, 0);
2049 gtk_entry_set_has_frame(GTK_ENTRY(inputbox), FALSE);
2050 gtk_box_pack_end(box, inputbox, FALSE, FALSE, 0);
2051 gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(box));
2052 gtk_widget_grab_focus(GTK_WIDGET(webview));
2053 gtk_widget_show_all(GTK_WIDGET(window));
2056 void
2057 setup_settings() {
2058 WebKitWebSettings *settings = (WebKitWebSettings*)webkit_web_settings_new();
2059 SoupURI *proxy_uri;
2060 char *filename, *new;
2061 int len;
2063 session = webkit_get_default_session();
2064 g_object_set(G_OBJECT(settings), "default-font-size", DEFAULT_FONT_SIZE, NULL);
2065 g_object_set(G_OBJECT(settings), "enable-scripts", enablePlugins, NULL);
2066 g_object_set(G_OBJECT(settings), "enable-plugins", enablePlugins, NULL);
2067 g_object_set(G_OBJECT(settings), "enable-java-applet", enableJava, NULL);
2068 g_object_set(G_OBJECT(settings), "enable-page-cache", enablePagecache, NULL);
2069 filename = g_strdup_printf(USER_STYLESHEET);
2070 filename = g_strdup_printf("file://%s", filename);
2071 g_object_set(G_OBJECT(settings), "user-stylesheet-uri", filename, NULL);
2072 g_object_set(G_OBJECT(settings), "user-agent", useragent, NULL);
2073 g_object_get(G_OBJECT(settings), "zoom-step", &zoomstep, NULL);
2074 webkit_web_view_set_settings(webview, settings);
2076 /* proxy */
2077 if (use_proxy == TRUE) {
2078 filename = (char *)g_getenv("http_proxy");
2079 if (filename != NULL && 0 < (len = strlen(filename))) {
2080 if (strstr(filename, "://") == NULL) {
2081 /* prepend http:// */
2082 new = g_malloc(sizeof("http://") + len);
2083 strcpy(new, "http://");
2084 memcpy(&new[sizeof("http://") - 1], filename, len + 1);
2085 proxy_uri = soup_uri_new(new);
2086 } else {
2087 proxy_uri = soup_uri_new(filename);
2089 g_object_set(session, "proxy-uri", proxy_uri, NULL);
2094 void
2095 setup_signals() {
2096 #ifdef ENABLE_COOKIE_SUPPORT
2097 /* Headers. */
2098 g_signal_connect_after(G_OBJECT(session), "request-started", G_CALLBACK(new_generic_request), NULL);
2099 #endif
2100 /* Accept-language header */
2101 g_object_set(G_OBJECT(session), "accept-language", acceptlanguage, NULL);
2102 /* window */
2103 g_object_connect(G_OBJECT(window),
2104 "signal::destroy", G_CALLBACK(window_destroyed_cb), NULL,
2105 NULL);
2106 /* webview */
2107 g_object_connect(G_OBJECT(webview),
2108 "signal::title-changed", G_CALLBACK(webview_title_changed_cb), NULL,
2109 "signal::load-progress-changed", G_CALLBACK(webview_progress_changed_cb), NULL,
2110 "signal::load-committed", G_CALLBACK(webview_load_committed_cb), NULL,
2111 "signal::load-finished", G_CALLBACK(webview_load_finished_cb), NULL,
2112 "signal::navigation-policy-decision-requested", G_CALLBACK(webview_navigation_cb), NULL,
2113 "signal::new-window-policy-decision-requested", G_CALLBACK(webview_new_window_cb), NULL,
2114 "signal::mime-type-policy-decision-requested", G_CALLBACK(webview_mimetype_cb), NULL,
2115 "signal::download-requested", G_CALLBACK(webview_download_cb), NULL,
2116 "signal::key-press-event", G_CALLBACK(webview_keypress_cb), NULL,
2117 "signal::hovering-over-link", G_CALLBACK(webview_hoverlink_cb), NULL,
2118 "signal::console-message", G_CALLBACK(webview_console_cb), NULL,
2119 "signal::create-web-view", G_CALLBACK(webview_open_in_new_window_cb), NULL,
2120 "signal::event", G_CALLBACK(notify_event_cb), NULL,
2121 NULL);
2122 /* webview adjustment */
2123 g_object_connect(G_OBJECT(adjust_v),
2124 "signal::value-changed", G_CALLBACK(webview_scroll_cb), NULL,
2125 NULL);
2126 /* inputbox */
2127 g_object_connect(G_OBJECT(inputbox),
2128 "signal::activate", G_CALLBACK(inputbox_activate_cb), NULL,
2129 "signal::key-press-event", G_CALLBACK(inputbox_keypress_cb), NULL,
2130 "signal::key-release-event", G_CALLBACK(inputbox_keyrelease_cb), NULL,
2131 #ifdef ENABLE_INCREMENTAL_SEARCH
2132 "signal::changed", G_CALLBACK(inputbox_changed_cb), NULL,
2133 #endif
2134 NULL);
2135 /* inspector */
2136 g_signal_connect(G_OBJECT(inspector),
2137 "inspect-web-view", G_CALLBACK(inspector_inspect_web_view_cb), NULL);
2140 #ifdef ENABLE_COOKIE_SUPPORT
2141 void
2142 setup_cookies()
2144 if (file_cookie_jar)
2145 g_object_unref(file_cookie_jar);
2147 if (session_cookie_jar)
2148 g_object_unref(session_cookie_jar);
2150 session_cookie_jar = soup_cookie_jar_new();
2152 cookie_store = g_strdup_printf(COOKIES_STORAGE_FILENAME);
2154 load_all_cookies();
2156 g_signal_connect(G_OBJECT(file_cookie_jar), "changed",
2157 G_CALLBACK(update_cookie_jar), NULL);
2159 return;
2162 /* TA: XXX - we should be using this callback for any header-requests we
2163 * receive (hence the name "new_generic_request" -- but for now, its use
2164 * is limited to handling cookies.
2166 void
2167 new_generic_request(SoupSession *session, SoupMessage *soup_msg, gpointer unused) {
2168 SoupMessageHeaders *soup_msg_h;
2169 SoupURI *uri;
2170 const char *cookie_str;
2172 soup_msg_h = soup_msg->request_headers;
2173 soup_message_headers_remove(soup_msg_h, "Cookie");
2174 uri = soup_message_get_uri(soup_msg);
2175 if( (cookie_str = get_cookies(uri)) )
2176 soup_message_headers_append(soup_msg_h, "Cookie", cookie_str);
2178 g_signal_connect_after(G_OBJECT(soup_msg), "got-headers", G_CALLBACK(handle_cookie_request), NULL);
2180 return;
2183 const char *
2184 get_cookies(SoupURI *soup_uri) {
2185 const char *cookie_str;
2187 cookie_str = soup_cookie_jar_get_cookies(file_cookie_jar, soup_uri, TRUE);
2189 return cookie_str;
2192 void
2193 handle_cookie_request(SoupMessage *soup_msg, gpointer unused)
2195 GSList *resp_cookie = NULL;
2196 SoupCookie *cookie;
2198 for(resp_cookie = soup_cookies_from_response(soup_msg);
2199 resp_cookie;
2200 resp_cookie = g_slist_next(resp_cookie))
2202 SoupDate *soup_date;
2203 cookie = soup_cookie_copy((SoupCookie *)resp_cookie->data);
2205 if (cookie_timeout && cookie->expires == NULL) {
2206 soup_date = soup_date_new_from_time_t(time(NULL) + cookie_timeout * 10);
2207 soup_cookie_set_expires(cookie, soup_date);
2209 soup_cookie_jar_add_cookie(file_cookie_jar, cookie);
2212 return;
2215 void
2216 update_cookie_jar(SoupCookieJar *jar, SoupCookie *old, SoupCookie *new)
2218 if (!new) {
2219 /* Nothing to do. */
2220 return;
2223 SoupCookie *copy;
2224 copy = soup_cookie_copy(new);
2226 soup_cookie_jar_add_cookie(session_cookie_jar, copy);
2228 return;
2231 void
2232 load_all_cookies(void)
2234 file_cookie_jar = soup_cookie_jar_text_new(cookie_store, COOKIES_STORAGE_READONLY);
2236 /* Put them back in the session store. */
2237 GSList *cookies_from_file = soup_cookie_jar_all_cookies(file_cookie_jar);
2239 for (; cookies_from_file;
2240 cookies_from_file = cookies_from_file->next)
2242 soup_cookie_jar_add_cookie(session_cookie_jar, cookies_from_file->data);
2245 soup_cookies_free(cookies_from_file);
2247 return;
2250 #endif
2252 void
2253 mop_up(void) {
2254 /* Free up any nasty globals before exiting. */
2255 #ifdef ENABLE_COOKIE_SUPPORT
2256 if (cookie_store)
2257 g_free(cookie_store);
2258 #endif
2259 return;
2263 main(int argc, char *argv[]) {
2264 static Arg a;
2265 static char url[256] = "";
2266 static gboolean ver = false;
2267 static gboolean configfile_exists = FALSE;
2268 static const char *cfile = NULL;
2269 char *searchengines_file;
2270 static GOptionEntry opts[] = {
2271 { "version", 'v', 0, G_OPTION_ARG_NONE, &ver, "print version", NULL },
2272 { "embed", 'e', 0, G_OPTION_ARG_STRING, &winid, "embedded", NULL },
2273 { "configfile", 'c', 0, G_OPTION_ARG_STRING, &cfile, "config file", NULL },
2274 { NULL }
2276 static GError *err;
2277 args = argv;
2279 /* command line argument: version */
2280 if (!gtk_init_with_args(&argc, &argv, "[<uri>]", opts, NULL, &err)) {
2281 g_printerr("can't init gtk: %s\n", err->message);
2282 g_error_free(err);
2283 return EXIT_FAILURE;
2286 if (ver) {
2287 printf("%s\n", INTERNAL_VERSION);
2288 return EXIT_SUCCESS;
2291 if (cfile)
2292 configfile = g_strdup_printf(cfile);
2293 else
2294 configfile = g_strdup_printf(RCFILE);
2296 if (!g_thread_supported())
2297 g_thread_init(NULL);
2299 if (winid)
2300 embed = atoi(winid);
2302 setup_modkeys();
2303 make_keyslist();
2304 setup_gui();
2305 #ifdef ENABLE_COOKIE_SUPPORT
2306 setup_cookies();
2307 #endif
2309 /* Check if the specified file exists. */
2310 /* And only warn the user, if they explicitly asked for a config on the
2311 * command line.
2313 if (!(access(configfile, F_OK) == 0) && cfile) {
2314 char *feedback_str;
2316 feedback_str = g_strdup_printf("Config file '%s' doesn't exist", cfile);
2317 give_feedback(feedback_str);
2318 } else if ((access(configfile, F_OK) == 0))
2319 configfile_exists = true;
2321 /* read config file */
2322 /* But only report errors if we failed, and the file existed. */
2323 if (!read_rcfile(configfile) && configfile_exists) {
2324 a.i = Error;
2325 a.s = g_strdup_printf("Error in config file '%s'", configfile);
2326 echo(&a);
2327 g_free(a.s);
2328 g_free(configfile);
2331 make_searchengines_list(searchengines, LENGTH(searchengines));
2333 /* read searchengines. */
2334 searchengines_file = g_strdup_printf(SEARCHENGINES_STORAGE_FILENAME);
2335 switch (read_searchengines(searchengines_file)) {
2336 case SYNTAX_ERROR:
2337 a.i = Error;
2338 a.s = g_strdup_printf("Syntax error in searchengines file '%s'", searchengines_file);
2339 echo(&a);
2340 break;
2341 case READING_FAILED:
2342 a.i = Error;
2343 a.s = g_strdup_printf("Could not read searchengines file '%s'", searchengines_file);
2344 echo(&a);
2345 break;
2346 default:
2347 break;
2349 g_free(searchengines_file);
2351 /* command line argument: URL */
2352 if (argc > 1) {
2353 strncpy(url, argv[argc - 1], 255);
2354 } else {
2355 strncpy(url, startpage, 255);
2358 a.i = TargetCurrent;
2359 a.s = url;
2360 open_arg(&a);
2361 gtk_main();
2363 mop_up();
2365 return EXIT_SUCCESS;