fixing compiler warnings concerning non-strict (proto-)typing
[vimprobable2.git] / main.c
blob9520ca66235fba26a4ae303abf9546428c3d1db7
2 /*
3 (c) 2009 by Leon Winter
4 (c) 2009-2011 by Hannes Schueller
5 (c) 2009-2010 by Matto Fransen
6 (c) 2010-2011 by Hans-Peter Deifel
7 (c) 2010-2011 by Thomas Adam
8 (c) 2011 by Albert Kim
9 (c) 2011 by Daniel Carl
10 see LICENSE file
13 #include <X11/Xlib.h>
14 #include "includes.h"
15 #include "vimprobable.h"
16 #include "utilities.h"
17 #include "callbacks.h"
18 #include "javascript.h"
20 /* the CLEAN_MOD_*_MASK defines have all the bits set that will be stripped from the modifier bit field */
21 #define CLEAN_MOD_NUMLOCK_MASK (GDK_MOD2_MASK)
22 #define CLEAN_MOD_BUTTON_MASK (GDK_BUTTON1_MASK|GDK_BUTTON2_MASK|GDK_BUTTON3_MASK|GDK_BUTTON4_MASK|GDK_BUTTON5_MASK)
24 /* remove unused bits, numlock symbol and buttons from keymask */
25 #define CLEAN(mask) (mask & (GDK_MODIFIER_MASK) & ~(CLEAN_MOD_NUMLOCK_MASK) & ~(CLEAN_MOD_BUTTON_MASK))
27 #define IS_ESCAPE(event) (IS_ESCAPE_KEY(CLEAN(event->state), event->keyval))
28 #define IS_ESCAPE_KEY(s, k) ((s == 0 && k == GDK_Escape) || \
29 (s == GDK_CONTROL_MASK && k == GDK_bracketleft))
31 /* callbacks here */
32 static void inputbox_activate_cb(GtkEntry *entry, gpointer user_data);
33 static gboolean inputbox_keypress_cb(GtkEntry *entry, GdkEventKey *event);
34 static gboolean inputbox_keyrelease_cb(GtkEntry *entry, GdkEventKey *event);
35 static gboolean inputbox_changed_cb(GtkEditable *entry, gpointer user_data);
36 static WebKitWebView* inspector_inspect_web_view_cb(gpointer inspector, WebKitWebView* web_view);
37 static gboolean notify_event_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data);
38 static gboolean webview_console_cb(WebKitWebView *webview, char *message, int line, char *source, gpointer user_data);
39 static gboolean webview_download_cb(WebKitWebView *webview, WebKitDownload *download, gpointer user_data);
40 static void webview_hoverlink_cb(WebKitWebView *webview, char *title, char *link, gpointer data);
41 static gboolean webview_keypress_cb(WebKitWebView *webview, GdkEventKey *event);
42 static void webview_load_committed_cb(WebKitWebView *webview, WebKitWebFrame *frame, gpointer user_data);
43 static void webview_load_finished_cb(WebKitWebView *webview, WebKitWebFrame *frame, gpointer user_data);
44 static gboolean webview_mimetype_cb(WebKitWebView *webview, WebKitWebFrame *frame, WebKitNetworkRequest *request,
45 char *mime_type, WebKitWebPolicyDecision *decision, gpointer user_data);
46 static gboolean webview_new_window_cb(WebKitWebView *webview, WebKitWebFrame *frame, WebKitNetworkRequest *request,
47 WebKitWebNavigationAction *action, WebKitWebPolicyDecision *decision, gpointer user_data);
48 static gboolean webview_open_in_new_window_cb(WebKitWebView *webview, WebKitWebFrame *frame, gpointer user_data);
49 static void webview_progress_changed_cb(WebKitWebView *webview, int progress, gpointer user_data);
50 static void webview_title_changed_cb(WebKitWebView *webview, WebKitWebFrame *frame, char *title, gpointer user_data);
51 static void window_destroyed_cb(GtkWidget *window, gpointer func_data);
52 static gboolean blank_cb(void);
54 /* functions */
55 static gboolean bookmark(const Arg *arg);
56 static gboolean browser_settings(const Arg *arg);
57 static gboolean commandhistoryfetch(const Arg *arg);
58 static gboolean complete(const Arg *arg);
59 static gboolean descend(const Arg *arg);
60 gboolean echo(const Arg *arg);
61 static gboolean focus_input(const Arg *arg);
62 static gboolean input(const Arg *arg);
63 static gboolean navigate(const Arg *arg);
64 static gboolean number(const Arg *arg);
65 static gboolean open_arg(const Arg *arg);
66 static gboolean open_remembered(const Arg *arg);
67 static gboolean paste(const Arg *arg);
68 static gboolean quickmark(const Arg *arg);
69 static gboolean quit(const Arg *arg);
70 static gboolean revive(const Arg *arg);
71 static gboolean print_frame(const Arg *arg);
72 static gboolean search(const Arg *arg);
73 static gboolean set(const Arg *arg);
74 static gboolean script(const Arg *arg);
75 static gboolean scroll(const Arg *arg);
76 static gboolean search_tag(const Arg *arg);
77 static gboolean yank(const Arg *arg);
78 static gboolean view_source(const Arg * arg);
79 static gboolean zoom(const Arg *arg);
80 static gboolean fake_key_event(const Arg *arg);
82 static void update_url(const char *uri);
83 static void setup_modkeys(void);
84 static void setup_gui(void);
85 static void setup_settings(void);
86 static void setup_signals(void);
87 static void ascii_bar(int total, int state, char *string);
88 static gchar *jsapi_ref_to_string(JSContextRef context, JSValueRef ref);
89 static void jsapi_evaluate_script(const gchar *script, gchar **value, gchar **message);
90 static void download_progress(WebKitDownload *d, GParamSpec *pspec);
91 static void set_widget_font_and_color(GtkWidget *widget, const char *font_str,
92 const char *bg_color_str, const char *fg_color_str);
94 static gboolean history(void);
95 static gboolean process_set_line(char *line);
96 void save_command_history(char *line);
97 void toggle_proxy(gboolean onoff);
98 void toggle_scrollbars(gboolean onoff);
100 gboolean process_keypress(GdkEventKey *event);
101 void fill_suggline(char * suggline, const char * command, const char *fill_with);
102 GtkWidget * fill_eventbox(const char * completion_line);
103 static void mop_up(void);
105 #include "main.h"
107 /* variables */
108 static GtkWindow *window;
109 static GtkWidget *viewport;
110 static GtkBox *box;
111 static GtkScrollbar *scroll_h;
112 static GtkScrollbar *scroll_v;
113 static GtkAdjustment *adjust_h;
114 static GtkAdjustment *adjust_v;
115 static GtkWidget *inputbox;
116 static GtkWidget *eventbox;
117 static GtkBox *statusbar;
118 static GtkWidget *status_url;
119 static GtkWidget *status_state;
120 static WebKitWebView *webview;
121 static SoupSession *session;
122 static GtkClipboard *clipboards[2];
124 static char **args;
125 static unsigned int mode = ModeNormal;
126 static unsigned int count = 0;
127 static float zoomstep;
128 static char *modkeys;
129 static char current_modkey;
130 static char *search_handle;
131 static gboolean search_direction;
132 static gboolean echo_active = TRUE;
133 WebKitWebInspector *inspector;
135 static GdkNativeWindow embed = 0;
136 static char *configfile = NULL;
137 static char *winid = NULL;
139 static char rememberedURI[1024] = "";
140 static char followTarget[8] = "";
141 char *error_msg = NULL;
142 char *config_base = NULL;
144 GList *activeDownloads;
146 #include "config.h"
147 #include "keymap.h"
149 char commandhistory[COMMANDHISTSIZE][255];
150 int lastcommand = 0;
151 int maxcommands = 0;
152 int commandpointer = 0;
153 KeyList *keylistroot = NULL;
155 /* Cookie support. */
156 #ifdef ENABLE_COOKIE_SUPPORT
157 static SoupCookieJar *session_cookie_jar = NULL;
158 static SoupCookieJar *file_cookie_jar = NULL;
159 static time_t cookie_timeout = 4800;
160 static char *cookie_store;
161 static void setup_cookies(void);
162 static const char *get_cookies(SoupURI *soup_uri);
163 static void load_all_cookies(void);
164 static void new_generic_request(SoupSession *soup_ses, SoupMessage *soup_msg, gpointer unused);
165 static void update_cookie_jar(SoupCookieJar *jar, SoupCookie *old, SoupCookie *new);
166 static void handle_cookie_request(SoupMessage *soup_msg, gpointer unused);
167 #endif
168 /* callbacks */
169 void
170 window_destroyed_cb(GtkWidget *window, gpointer func_data) {
171 quit(NULL);
174 void
175 webview_title_changed_cb(WebKitWebView *webview, WebKitWebFrame *frame, char *title, gpointer user_data) {
176 gtk_window_set_title(window, title);
179 void
180 webview_progress_changed_cb(WebKitWebView *webview, int progress, gpointer user_data) {
181 #ifdef ENABLE_GTK_PROGRESS_BAR
182 gtk_entry_set_progress_fraction(GTK_ENTRY(inputbox), progress == 100 ? 0 : (double)progress/100);
183 #endif
184 update_state();
187 #ifdef ENABLE_WGET_PROGRESS_BAR
188 void
189 ascii_bar(int total, int state, char *string) {
190 int i;
192 for (i = 0; i < state; i++)
193 string[i] = progressbartickchar;
194 string[i++] = progressbarcurrent;
195 for (; i < total; i++)
196 string[i] = progressbarspacer;
197 string[i] = '\0';
199 #endif
201 void
202 webview_load_committed_cb(WebKitWebView *webview, WebKitWebFrame *frame, gpointer user_data) {
203 Arg a = { .i = Silent, .s = g_strdup(JS_SETUP_HINTS) };
204 const char *uri = webkit_web_view_get_uri(webview);
206 update_url(uri);
207 script(&a);
210 void
211 webview_load_finished_cb(WebKitWebView *webview, WebKitWebFrame *frame, gpointer user_data) {
212 Arg a = { .i = Silent, .s = g_strdup(JS_SETUP_INPUT_FOCUS) };
214 if (HISTORY_MAX_ENTRIES > 0)
215 history();
216 update_state();
217 script(&a);
220 static gboolean
221 webview_open_in_new_window_cb(WebKitWebView *webview, WebKitWebFrame *frame, gpointer user_data) {
222 Arg a = { .i = TargetNew, .s = (char*)webkit_web_view_get_uri(webview) };
223 if (strlen(rememberedURI) > 0) {
224 a.s = rememberedURI;
226 open_arg(&a);
227 return FALSE;
230 gboolean
231 webview_new_window_cb(WebKitWebView *webview, WebKitWebFrame *frame, WebKitNetworkRequest *request,
232 WebKitWebNavigationAction *action, WebKitWebPolicyDecision *decision, gpointer user_data) {
233 Arg a = { .i = TargetNew, .s = (char*)webkit_network_request_get_uri(request) };
234 open_arg(&a);
235 webkit_web_policy_decision_ignore(decision);
236 return TRUE;
239 gboolean
240 webview_mimetype_cb(WebKitWebView *webview, WebKitWebFrame *frame, WebKitNetworkRequest *request,
241 char *mime_type, WebKitWebPolicyDecision *decision, gpointer user_data) {
242 if (webkit_web_view_can_show_mime_type(webview, mime_type) == FALSE) {
243 webkit_web_policy_decision_download(decision);
244 return TRUE;
245 } else {
246 return FALSE;
250 static WebKitWebView*
251 inspector_inspect_web_view_cb(gpointer inspector, WebKitWebView* web_view) {
252 gchar* inspector_title;
253 GtkWidget* inspector_window;
254 GtkWidget* inspector_view;
256 /* just enough code to show the inspector - no signal handling etc. */
257 inspector_title = g_strdup_printf("Inspect page - %s - Vimprobable2", webkit_web_view_get_uri(web_view));
258 if (embed) {
259 inspector_window = gtk_plug_new(embed);
260 } else {
261 inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
262 gtk_window_set_wmclass(window, "vimprobable2", "Vimprobable2");
264 gtk_window_set_title(GTK_WINDOW(inspector_window), inspector_title);
265 g_free(inspector_title);
266 inspector_view = webkit_web_view_new();
267 gtk_container_add(GTK_CONTAINER(inspector_window), inspector_view);
268 gtk_widget_show_all(inspector_window);
269 return WEBKIT_WEB_VIEW(inspector_view);
272 gboolean
273 webview_download_cb(WebKitWebView *webview, WebKitDownload *download, gpointer user_data) {
274 const gchar *filename;
275 gchar *uri, *path;
276 uint32_t size;
277 Arg a;
279 filename = webkit_download_get_suggested_filename(download);
280 if (filename == NULL || strlen(filename) == 0) {
281 filename = "vimprobable_download";
283 path = g_build_filename(g_strdup_printf(DOWNLOADS_PATH), filename, NULL);
284 uri = g_strconcat("file://", path, NULL);
285 webkit_download_set_destination_uri(download, uri);
286 g_free(uri);
287 size = (uint32_t)webkit_download_get_total_size(download);
288 a.i = Info;
289 if (size > 0)
290 a.s = g_strdup_printf("Download %s started (expected size: %u bytes)...", filename, size);
291 else
292 a.s = g_strdup_printf("Download %s started (unknown size)...", filename);
293 echo(&a);
294 g_free(a.s);
295 activeDownloads = g_list_prepend(activeDownloads, download);
296 g_signal_connect(download, "notify::progress", G_CALLBACK(download_progress), NULL);
297 g_signal_connect(download, "notify::status", G_CALLBACK(download_progress), NULL);
298 update_state();
299 return TRUE;
302 gboolean
303 blank_cb(void) {
304 return TRUE;
307 void
308 download_progress(WebKitDownload *d, GParamSpec *pspec) {
309 Arg a;
310 WebKitDownloadStatus status = webkit_download_get_status(d);
312 if (status != WEBKIT_DOWNLOAD_STATUS_STARTED && status != WEBKIT_DOWNLOAD_STATUS_CREATED) {
313 if (status != WEBKIT_DOWNLOAD_STATUS_FINISHED) {
314 a.i = Error;
315 a.s = g_strdup_printf("Error while downloading %s", webkit_download_get_suggested_filename(d));
316 echo(&a);
317 } else {
318 a.i = Info;
319 a.s = g_strdup_printf("Download %s finished", webkit_download_get_suggested_filename(d));
320 echo(&a);
322 g_free(a.s);
323 activeDownloads = g_list_remove(activeDownloads, d);
325 update_state();
329 gboolean
330 process_keypress(GdkEventKey *event) {
331 KeyList *current;
333 current = keylistroot;
334 while (current != NULL) {
335 if (current->Element.mask == CLEAN(event->state)
336 && (current->Element.modkey == current_modkey
337 || (!current->Element.modkey && !current_modkey)
338 || current->Element.modkey == GDK_VoidSymbol ) /* wildcard */
339 && current->Element.key == event->keyval
340 && current->Element.func)
341 if (current->Element.func(&current->Element.arg)) {
342 current_modkey = count = 0;
343 update_state();
344 return TRUE;
346 current = current->next;
348 return FALSE;
351 gboolean
352 webview_keypress_cb(WebKitWebView *webview, GdkEventKey *event) {
353 Arg a = { .i = ModeNormal, .s = NULL };
355 switch (mode) {
356 case ModeNormal:
357 if (CLEAN(event->state) == 0) {
358 if (IS_ESCAPE(event)) {
359 a.i = Info;
360 a.s = g_strdup("");
361 echo(&a);
362 g_free(a.s);
363 } else if (current_modkey == 0 && ((event->keyval >= GDK_1 && event->keyval <= GDK_9)
364 || (event->keyval == GDK_0 && count))) {
365 count = (count ? count * 10 : 0) + (event->keyval - GDK_0);
366 update_state();
367 return TRUE;
368 } else if (strchr(modkeys, event->keyval) && current_modkey != event->keyval) {
369 current_modkey = event->keyval;
370 update_state();
371 return TRUE;
374 /* keybindings */
375 if (process_keypress(event) == TRUE) return TRUE;
377 break;
378 case ModeInsert:
379 if (IS_ESCAPE(event)) {
380 a.i = Silent;
381 a.s = g_strdup("hints.clearFocus();");
382 script(&a);
383 a.i = ModeNormal;
384 return set(&a);
386 case ModePassThrough:
387 if (IS_ESCAPE(event)) {
388 echo(&a);
389 set(&a);
390 return TRUE;
392 break;
393 case ModeSendKey:
394 echo(&a);
395 set(&a);
396 break;
398 return FALSE;
401 void
402 set_widget_font_and_color(GtkWidget *widget, const char *font_str, const char *bg_color_str,
403 const char *fg_color_str) {
404 GdkColor fg_color;
405 GdkColor bg_color;
406 PangoFontDescription *font;
408 font = pango_font_description_from_string(font_str);
409 gtk_widget_modify_font(widget, font);
410 pango_font_description_free(font);
412 if (fg_color_str)
413 gdk_color_parse(fg_color_str, &fg_color);
414 if (bg_color_str)
415 gdk_color_parse(bg_color_str, &bg_color);
417 gtk_widget_modify_text(widget, GTK_STATE_NORMAL, fg_color_str ? &fg_color : NULL);
418 gtk_widget_modify_base(widget, GTK_STATE_NORMAL, bg_color_str ? &bg_color : NULL);
420 return;
423 void
424 webview_hoverlink_cb(WebKitWebView *webview, char *title, char *link, gpointer data) {
425 const char *uri = webkit_web_view_get_uri(webview);
427 memset(rememberedURI, 0, 1024);
428 if (link) {
429 gtk_label_set_markup(GTK_LABEL(status_url), g_markup_printf_escaped("<span font=\"%s\">Link: %s</span>", statusfont, link));
430 strncpy(rememberedURI, link, 1024);
431 } else
432 update_url(uri);
435 gboolean
436 webview_console_cb(WebKitWebView *webview, char *message, int line, char *source, gpointer user_data) {
437 Arg a;
439 /* Don't change internal mode if the browser doesn't have focus to prevent inconsistent states */
440 if (gtk_window_has_toplevel_focus(window)) {
441 if (!strcmp(message, "hintmode_off") || !strcmp(message, "insertmode_off")) {
442 a.i = ModeNormal;
443 return set(&a);
444 } else if (!strcmp(message, "insertmode_on")) {
445 a.i = ModeInsert;
446 return set(&a);
449 return FALSE;
452 void
453 inputbox_activate_cb(GtkEntry *entry, gpointer user_data) {
454 char *text;
455 guint16 length = gtk_entry_get_text_length(entry);
456 Arg a;
457 gboolean success = FALSE, forward = FALSE;
459 a.i = HideCompletion;
460 complete(&a);
461 if (length == 0)
462 return;
463 text = (char*)gtk_entry_get_text(entry);
464 if (length > 1 && text[0] == ':') {
465 success = process_line((text + 1));
466 } else if (length > 1 && ((forward = text[0] == '/') || text[0] == '?')) {
467 webkit_web_view_unmark_text_matches(webview);
468 #ifdef ENABLE_MATCH_HIGHLITING
469 webkit_web_view_mark_text_matches(webview, &text[1], FALSE, 0);
470 webkit_web_view_set_highlight_text_matches(webview, TRUE);
471 #endif
472 count = 0;
473 #ifndef ENABLE_INCREMENTAL_SEARCH
474 a.s =& text[1];
475 a.i = searchoptions | (forward ? DirectionForward : DirectionBackwards);
476 search(&a);
477 #else
478 search_direction = forward;
479 search_handle = g_strdup(&text[1]);
480 #endif
481 } else if (text[0] == '.' || text[0] == ',') {
482 a.i = Silent;
483 a.s = g_strdup_printf("hints.fire();");
484 script(&a);
485 update_state();
486 } else
487 return;
488 if (!echo_active)
489 gtk_entry_set_text(entry, "");
490 gtk_widget_grab_focus(GTK_WIDGET(webview));
493 gboolean
494 inputbox_keypress_cb(GtkEntry *entry, GdkEventKey *event) {
495 Arg a;
496 int numval;
498 if (mode == ModeHints) {
499 if (event->keyval == GDK_Tab) {
500 a.i = Silent;
501 a.s = g_strdup_printf("hints.focusNextHint();");
502 script(&a);
503 update_state();
504 return TRUE;
506 if (event->keyval == GDK_ISO_Left_Tab) {
507 a.i = Silent;
508 a.s = g_strdup_printf("hints.focusPreviousHint();");
509 script(&a);
510 update_state();
511 return TRUE;
513 if (event->keyval == GDK_Return) {
514 a.i = Silent;
515 a.s = g_strdup_printf("hints.fire();");
516 script(&a);
517 update_state();
518 return TRUE;
521 switch (event->keyval) {
522 case GDK_bracketleft:
523 case GDK_Escape:
524 if (!IS_ESCAPE(event)) break;
525 a.i = HideCompletion;
526 complete(&a);
527 a.i = ModeNormal;
528 return set(&a);
529 break;
530 case GDK_Tab:
531 a.i = DirectionNext;
532 return complete(&a);
533 break;
534 case GDK_Up:
535 a.i = DirectionPrev;
536 return commandhistoryfetch(&a);
537 break;
538 case GDK_Down:
539 a.i = DirectionNext;
540 return commandhistoryfetch(&a);
541 break;
542 case GDK_ISO_Left_Tab:
543 a.i = DirectionPrev;
544 return complete(&a);
545 break;
548 if (mode == ModeHints) {
549 if ((CLEAN(event->state) & GDK_SHIFT_MASK) &&
550 (CLEAN(event->state) & GDK_CONTROL_MASK) &&
551 (event->keyval == GDK_BackSpace)) {
552 count /= 10;
553 a.i = Silent;
554 a.s = g_strdup_printf("hints.updateHints(%d);", count);
555 script(&a);
556 update_state();
557 return TRUE;
560 numval = g_unichar_digit_value((gunichar) gdk_keyval_to_unicode(event->keyval));
561 if ((numval >= 1 && numval <= 9) || (numval == 0 && count)) {
562 /* allow a zero as non-first number */
563 count = (count ? count * 10 : 0) + numval;
564 a.i = Silent;
565 a.s = g_strdup_printf("hints.updateHints(%d);", count);
566 script(&a);
567 update_state();
568 return TRUE;
572 return FALSE;
575 gboolean
576 notify_event_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data) {
577 int i;
578 if (mode == ModeNormal && event->type == GDK_BUTTON_RELEASE) {
579 /* handle mouse click events */
580 for (i = 0; i < LENGTH(mouse); i++) {
581 if (mouse[i].mask == CLEAN(event->button.state)
582 && (mouse[i].modkey == current_modkey
583 || (!mouse[i].modkey && !current_modkey)
584 || mouse[i].modkey == GDK_VoidSymbol) /* wildcard */
585 && mouse[i].button == event->button.button
586 && mouse[i].func) {
587 if (mouse[i].func(&mouse[i].arg)) {
588 current_modkey = count = 0;
589 update_state();
590 return TRUE;
595 return FALSE;
598 static gboolean inputbox_keyrelease_cb(GtkEntry *entry, GdkEventKey *event) {
599 Arg a;
600 guint16 length = gtk_entry_get_text_length(entry);
602 if (!length) {
603 a.i = HideCompletion;
604 complete(&a);
605 a.i = ModeNormal;
606 return set(&a);
608 return FALSE;
611 static gboolean inputbox_changed_cb(GtkEditable *entry, gpointer user_data) {
612 Arg a;
613 char *text = (char*)gtk_entry_get_text(GTK_ENTRY(entry));
614 guint16 length = gtk_entry_get_text_length(GTK_ENTRY(entry));
615 gboolean forward = FALSE;
617 /* Update incremental search if the user changes the search text.
619 * Note: gtk_widget_is_focus() is a poor way to check if the change comes
620 * from the user. But if the entry is focused and the text is set
621 * through gtk_entry_set_text() in some asyncrounous operation,
622 * I would consider that a bug.
625 if (gtk_widget_is_focus(GTK_WIDGET(entry)) && length > 1 && ((forward = text[0] == '/') || text[0] == '?')) {
626 webkit_web_view_unmark_text_matches(webview);
627 webkit_web_view_search_text(webview, &text[1], searchoptions & CaseSensitive, forward, searchoptions & Wrapping);
628 return TRUE;
629 } else if (gtk_widget_is_focus(GTK_WIDGET(entry)) && length >= 1 &&
630 (text[0] == '.' || text[0] == ',')) {
631 a.i = Silent;
632 switch (text[0]) {
633 case '.':
634 a.s = g_strconcat("hints.createHints('", text + 1, "', 'f');", NULL);
635 break;
637 case ',':
638 a.s = g_strconcat("hints.createHints('", text + 1, "', 'F');", NULL);
639 break;
641 count = 0;
642 script(&a);
644 return TRUE;
645 } else if (length == 0 && followTarget[0]) {
646 mode = ModeNormal;
647 a.i = Silent;
648 a.s = g_strdup("hints.clearHints();");
649 script(&a);
650 count = 0;
651 update_state();
654 return FALSE;
657 /* funcs here */
659 void fill_suggline(char * suggline, const char * command, const char *fill_with) {
660 memset(suggline, 0, 512);
661 strncpy(suggline, command, 512);
662 strncat(suggline, " ", 1);
663 strncat(suggline, fill_with, 512 - strlen(suggline) - 1);
666 GtkWidget * fill_eventbox(const char * completion_line) {
667 GtkBox * row;
668 GtkWidget *row_eventbox, *el;
669 GdkColor color;
670 char * markup;
672 row = GTK_BOX(gtk_hbox_new(FALSE, 0));
673 row_eventbox = gtk_event_box_new();
674 gdk_color_parse(completionbgcolor[0], &color);
675 gtk_widget_modify_bg(row_eventbox, GTK_STATE_NORMAL, &color);
676 el = gtk_label_new(NULL);
677 markup = g_strconcat("<span font=\"", completionfont[0], "\" color=\"", completioncolor[0], "\">",
678 g_markup_escape_text(completion_line, strlen(completion_line)), "</span>", NULL);
679 gtk_label_set_markup(GTK_LABEL(el), markup);
680 g_free(markup);
681 gtk_misc_set_alignment(GTK_MISC(el), 0, 0);
682 gtk_box_pack_start(row, el, TRUE, TRUE, 2);
683 gtk_container_add(GTK_CONTAINER(row_eventbox), GTK_WIDGET(row));
684 return row_eventbox;
687 gboolean
688 complete(const Arg *arg) {
689 char *str, *p, *s, *markup, *entry, *searchfor, command[32] = "", suggline[512] = "", **suggurls;
690 size_t listlen, len, cmdlen;
691 int i, spacepos;
692 Listelement *elementlist = NULL, *elementpointer;
693 gboolean highlight = FALSE;
694 GtkBox *row;
695 GtkWidget *row_eventbox, *el;
696 GtkBox *_table;
697 GdkColor color;
698 static GtkWidget *table, *top_border;
699 static char *prefix;
700 static char **suggestions;
701 static GtkWidget **widgets;
702 static int n = 0, m, current = -1;
704 str = (char*)gtk_entry_get_text(GTK_ENTRY(inputbox));
705 len = strlen(str);
707 /* Get the length of the list of commands for completion. We need this to
708 * malloc/realloc correctly.
710 listlen = LENGTH(commands);
712 if ((len == 0 || str[0] != ':') && arg->i != HideCompletion)
713 return TRUE;
714 if (prefix) {
715 if (arg->i != HideCompletion && widgets && current != -1 && !strcmp(&str[1], suggestions[current])) {
716 gdk_color_parse(completionbgcolor[0], &color);
717 gtk_widget_modify_bg(widgets[current], GTK_STATE_NORMAL, &color);
718 current = (n + current + (arg->i == DirectionPrev ? -1 : 1)) % n;
719 if ((arg->i == DirectionNext && current == 0)
720 || (arg->i == DirectionPrev && current == n - 1))
721 current = -1;
722 } else {
723 free(widgets);
724 free(suggestions);
725 free(prefix);
726 gtk_widget_destroy(GTK_WIDGET(table));
727 gtk_widget_destroy(GTK_WIDGET(top_border));
728 table = NULL;
729 widgets = NULL;
730 suggestions = NULL;
731 prefix = NULL;
732 n = 0;
733 current = -1;
734 if (arg->i == HideCompletion)
735 return TRUE;
737 } else if (arg->i == HideCompletion)
738 return TRUE;
739 if (!widgets) {
740 prefix = g_strdup_printf(str);
741 widgets = malloc(sizeof(GtkWidget*) * listlen);
742 suggestions = malloc(sizeof(char*) * listlen);
743 top_border = gtk_event_box_new();
744 gtk_widget_set_size_request(GTK_WIDGET(top_border), 0, 1);
745 gdk_color_parse(completioncolor[2], &color);
746 gtk_widget_modify_bg(top_border, GTK_STATE_NORMAL, &color);
747 table = gtk_event_box_new();
748 gdk_color_parse(completionbgcolor[0], &color);
749 _table = GTK_BOX(gtk_vbox_new(FALSE, 0));
750 highlight = len > 1;
751 if (strchr(str, ' ') == NULL) {
752 /* command completion */
753 listlen = LENGTH(commands);
754 for (i = 0; i < listlen; i++) {
755 if (commands[i].cmd == NULL)
756 break;
757 cmdlen = strlen(commands[i].cmd);
758 if (!highlight || (n < MAX_LIST_SIZE && len - 1 <= cmdlen && !strncmp(&str[1], commands[i].cmd, len - 1))) {
759 p = s = malloc(sizeof(char*) * (highlight ? sizeof(COMPLETION_TAG_OPEN) + sizeof(COMPLETION_TAG_CLOSE) - 1 : 1) + cmdlen);
760 if (highlight) {
761 memcpy(p, COMPLETION_TAG_OPEN, sizeof(COMPLETION_TAG_OPEN) - 1);
762 memcpy((p += sizeof(COMPLETION_TAG_OPEN) - 1), &str[1], len - 1);
763 memcpy((p += len - 1), COMPLETION_TAG_CLOSE, sizeof(COMPLETION_TAG_CLOSE) - 1);
764 p += sizeof(COMPLETION_TAG_CLOSE) - 1;
766 memcpy(p, &commands[i].cmd[len - 1], cmdlen - len + 2);
767 row = GTK_BOX(gtk_hbox_new(FALSE, 0));
768 row_eventbox = gtk_event_box_new();
769 gtk_widget_modify_bg(row_eventbox, GTK_STATE_NORMAL, &color);
770 el = gtk_label_new(NULL);
771 markup = g_strconcat("<span font=\"", completionfont[0], "\" color=\"", completioncolor[0], "\">", s, "</span>", NULL);
772 free(s);
773 gtk_label_set_markup(GTK_LABEL(el), markup);
774 g_free(markup);
775 gtk_misc_set_alignment(GTK_MISC(el), 0, 0);
776 gtk_box_pack_start(row, el, TRUE, TRUE, 2);
777 gtk_container_add(GTK_CONTAINER(row_eventbox), GTK_WIDGET(row));
778 gtk_box_pack_start(_table, GTK_WIDGET(row_eventbox), FALSE, FALSE, 0);
779 suggestions[n] = commands[i].cmd;
780 widgets[n++] = row_eventbox;
783 } else {
784 entry = (char *)malloc(512 * sizeof(char));
785 if (entry == NULL) {
786 return FALSE;
788 memset(entry, 0, 512);
789 suggurls = malloc(sizeof(char*) * listlen);
790 if (suggurls == NULL) {
791 return FALSE;
793 spacepos = strcspn(str, " ");
794 searchfor = (str + spacepos + 1);
795 strncpy(command, (str + 1), spacepos - 1);
796 if (strlen(command) == 3 && strncmp(command, "set", 3) == 0) {
797 /* browser settings */
798 listlen = LENGTH(browsersettings);
799 for (i = 0; i < listlen; i++) {
800 if (n < MAX_LIST_SIZE && strstr(browsersettings[i].name, searchfor) != NULL) {
801 /* match */
802 fill_suggline(suggline, command, browsersettings[i].name);
803 suggurls[n] = (char *)malloc(sizeof(char) * 512 + 1);
804 strncpy(suggurls[n], suggline, 512);
805 suggestions[n] = suggurls[n];
806 row_eventbox = fill_eventbox(suggline);
807 gtk_box_pack_start(_table, GTK_WIDGET(row_eventbox), FALSE, FALSE, 0);
808 widgets[n++] = row_eventbox;
812 } else if (strlen(command) == 2 && strncmp(command, "qt", 2) == 0) {
813 /* completion on tags */
814 spacepos = strcspn(str, " ");
815 searchfor = (str + spacepos + 1);
816 elementlist = complete_list(searchfor, 1, elementlist);
817 } else {
818 /* URL completion: bookmarks */
819 elementlist = complete_list(searchfor, 0, elementlist);
820 m = count_list(elementlist);
821 if (m < MAX_LIST_SIZE) {
822 /* URL completion: history */
823 elementlist = complete_list(searchfor, 2, elementlist);
826 elementpointer = elementlist;
827 while (elementpointer != NULL) {
828 fill_suggline(suggline, command, elementpointer->element);
829 suggurls[n] = (char *)malloc(sizeof(char) * 512 + 1);
830 strncpy(suggurls[n], suggline, 512);
831 suggestions[n] = suggurls[n];
832 row_eventbox = fill_eventbox(suggline);
833 gtk_box_pack_start(_table, GTK_WIDGET(row_eventbox), FALSE, FALSE, 0);
834 widgets[n++] = row_eventbox;
835 elementpointer = elementpointer->next;
836 if (n >= MAX_LIST_SIZE)
837 break;
839 free_list(elementlist);
840 if (suggurls != NULL) {
841 free(suggurls);
842 suggurls = NULL;
844 if (entry != NULL) {
845 free(entry);
846 entry = NULL;
849 /* TA: FIXME - this needs rethinking entirely. */
851 GtkWidget **widgets_temp = realloc(widgets, sizeof(*widgets) * n);
852 if (widgets_temp == NULL && widgets == NULL) {
853 fprintf(stderr, "Couldn't realloc() widgets\n");
854 exit(1);
856 widgets = widgets_temp;
857 char **suggestions_temp = realloc(suggestions, sizeof(*suggestions) * n);
858 if (suggestions_temp == NULL && suggestions == NULL) {
859 fprintf(stderr, "Couldn't realloc() suggestions\n");
860 exit(1);
862 suggestions = suggestions_temp;
864 if (!n) {
865 gdk_color_parse(completionbgcolor[1], &color);
866 gtk_widget_modify_bg(table, GTK_STATE_NORMAL, &color);
867 el = gtk_label_new(NULL);
868 gtk_misc_set_alignment(GTK_MISC(el), 0, 0);
869 markup = g_strconcat("<span font=\"", completionfont[1], "\" color=\"", completioncolor[1], "\">No Completions</span>", NULL);
870 gtk_label_set_markup(GTK_LABEL(el), markup);
871 g_free(markup);
872 gtk_box_pack_start(_table, GTK_WIDGET(el), FALSE, FALSE, 0);
874 gtk_box_pack_start(box, GTK_WIDGET(top_border), FALSE, FALSE, 0);
875 gtk_container_add(GTK_CONTAINER(table), GTK_WIDGET(_table));
876 gtk_box_pack_start(box, GTK_WIDGET(table), FALSE, FALSE, 0);
877 gtk_widget_show_all(GTK_WIDGET(window));
878 if (!n)
879 return TRUE;
880 current = arg->i == DirectionPrev ? n - 1 : 0;
882 if (current != -1) {
883 gdk_color_parse(completionbgcolor[2], &color);
884 gtk_widget_modify_bg(GTK_WIDGET(widgets[current]), GTK_STATE_NORMAL, &color);
885 s = g_strconcat(":", suggestions[current], NULL);
886 gtk_entry_set_text(GTK_ENTRY(inputbox), s);
887 g_free(s);
888 } else
889 gtk_entry_set_text(GTK_ENTRY(inputbox), prefix);
890 gtk_editable_set_position(GTK_EDITABLE(inputbox), -1);
891 return TRUE;
894 gboolean
895 descend(const Arg *arg) {
896 char *source = (char*)webkit_web_view_get_uri(webview), *p = &source[0], *new;
897 int i, len;
898 count = count ? count : 1;
900 if (!source)
901 return TRUE;
902 if (arg->i == Rootdir) {
903 for (i = 0; i < 3; i++) /* get to the third slash */
904 if (!(p = strchr(++p, '/')))
905 return TRUE; /* if we cannot find it quit */
906 } else {
907 len = strlen(source);
908 if (!len) /* if string is empty quit */
909 return TRUE;
910 p = source + len; /* start at the end */
911 if (*(p - 1) == '/') /* /\/$/ is not an additional level */
912 ++count;
913 for (i = 0; i < count; i++)
914 while(*(p--) != '/' || *p == '/') /* count /\/+/ as one slash */
915 if (p == source) /* if we reach the first char pointer quit */
916 return TRUE;
917 ++p; /* since we do p-- in the while, we are pointing at
918 the char before the slash, so +1 */
920 len = p - source + 1; /* new length = end - start + 1 */
921 new = malloc(len + 1);
922 memcpy(new, source, len);
923 new[len] = '\0';
924 webkit_web_view_load_uri(webview, new);
925 free(new);
926 return TRUE;
929 gboolean
930 echo(const Arg *arg) {
931 int index = !arg->s ? 0 : arg->i & (~NoAutoHide);
933 if (index < Info || index > Error)
934 return TRUE;
936 set_widget_font_and_color(inputbox, urlboxfont[index], urlboxbgcolor[index], urlboxcolor[index]);
937 gtk_entry_set_text(GTK_ENTRY(inputbox), !arg->s ? "" : arg->s);
939 return TRUE;
942 gboolean
943 input(const Arg *arg) {
944 int pos = 0;
945 count = 0;
946 const char *url;
947 int index = Info;
948 Arg a;
950 /* if inputbox hidden, show it again */
951 if (!gtk_widget_get_visible(inputbox))
952 gtk_widget_set_visible(inputbox, TRUE);
954 update_state();
956 /* Set the colour and font back to the default, so that we don't still
957 * maintain a red colour from a warning from an end of search indicator,
958 * etc.
960 set_widget_font_and_color(inputbox, urlboxfont[index], urlboxbgcolor[index], urlboxcolor[index]);
962 /* to avoid things like :open URL :open URL2 or :open :open URL */
963 gtk_entry_set_text(GTK_ENTRY(inputbox), "");
964 gtk_editable_insert_text(GTK_EDITABLE(inputbox), arg->s, -1, &pos);
965 if (arg->i & InsertCurrentURL && (url = webkit_web_view_get_uri(webview)))
966 gtk_editable_insert_text(GTK_EDITABLE(inputbox), url, -1, &pos);
967 gtk_widget_grab_focus(inputbox);
968 gtk_editable_set_position(GTK_EDITABLE(inputbox), -1);
970 if (arg->s[0] == '.' || arg->s[0] == ',') {
971 mode = ModeHints;
972 memset(followTarget, 0, 8);
973 strncpy(followTarget, "current", 8);
974 a.i = Silent;
975 switch (arg->s[0]) {
976 case '.':
977 a.s = g_strdup("hints.createHints('', 'f');");
978 break;
980 case ',':
981 a.s = g_strdup("hints.createHints('', 'F');");
982 break;
984 count = 0;
985 script(&a);
988 return TRUE;
991 gboolean
992 navigate(const Arg *arg) {
993 if (arg->i & NavigationForwardBack)
994 webkit_web_view_go_back_or_forward(webview, (arg->i == NavigationBack ? -1 : 1) * (count ? count : 1));
995 else if (arg->i & NavigationReloadActions)
996 (arg->i == NavigationReload ? webkit_web_view_reload : webkit_web_view_reload_bypass_cache)(webview);
997 else
998 webkit_web_view_stop_loading(webview);
999 return TRUE;
1002 gboolean
1003 number(const Arg *arg) {
1004 const char *source = webkit_web_view_get_uri(webview);
1005 char *uri, *p, *new;
1006 int number, diff = (count ? count : 1) * (arg->i == Increment ? 1 : -1);
1008 if (!source)
1009 return TRUE;
1010 uri = g_strdup_printf(source); /* copy string */
1011 p =& uri[0];
1012 while(*p != '\0') /* goto the end of the string */
1013 ++p;
1014 --p;
1015 while(*p >= '0' && *p <= '9') /* go back until non number char is reached */
1016 --p;
1017 if (*(++p) == '\0') { /* if no numbers were found abort */
1018 free(uri);
1019 return TRUE;
1021 number = atoi(p) + diff; /* apply diff on number */
1022 *p = '\0';
1023 new = g_strdup_printf("%s%d", uri, number); /* create new uri */
1024 webkit_web_view_load_uri(webview, new);
1025 g_free(new);
1026 free(uri);
1027 return TRUE;
1030 gboolean
1031 open_arg(const Arg *arg) {
1032 char *argv[6];
1033 char *s = arg->s, *p = NULL, *new;
1034 Arg a = { .i = NavigationReload };
1035 int len;
1036 char *search_uri, *search_term;
1038 if (embed) {
1039 argv[0] = *args;
1040 argv[1] = "-e";
1041 argv[2] = winid;
1042 argv[3] = arg->s;
1043 argv[4] = NULL;
1044 } else {
1045 argv[0] = *args;
1046 argv[1] = arg->s;
1047 argv[2] = NULL;
1050 if (!arg->s)
1051 navigate(&a);
1052 else if (arg->i == TargetCurrent) {
1053 while(*s == ' ') /* strip leading whitespace */
1054 ++s;
1055 p = (s + strlen(s) - 1);
1056 while(*p == ' ') /* strip trailing whitespace */
1057 --p;
1058 *(p + 1) = '\0';
1059 len = strlen(s);
1060 new = NULL, p = strchr(s, ' ');
1061 if (p) { /* check for search engines */
1062 *p = '\0';
1063 search_uri = find_uri_for_searchengine(s);
1064 if (search_uri != NULL) {
1065 search_term = soup_uri_encode(p+1, "&");
1066 new = g_strdup_printf(search_uri, search_term);
1067 g_free(search_term);
1069 *p = ' ';
1071 if (!new) {
1072 if (len > 3 && strstr(s, "://")) { /* valid url? */
1073 p = new = g_malloc(len + 1);
1074 while(*s != '\0') { /* strip whitespaces */
1075 if (*s != ' ')
1076 *(p++) = *s;
1077 ++s;
1079 *p = '\0';
1080 } else if (strcspn(s, "/") == 0 || strcspn(s, "./") == 0) { /* prepend "file://" */
1081 new = g_malloc(sizeof("file://") + len);
1082 strcpy(new, "file://");
1083 memcpy(&new[sizeof("file://") - 1], s, len + 1);
1084 } else if (p || !strchr(s, '.')) { /* whitespaces or no dot? */
1085 search_uri = find_uri_for_searchengine(defaultsearch);
1086 if (search_uri != NULL) {
1087 search_term = soup_uri_encode(s, "&");
1088 new = g_strdup_printf(search_uri, search_term);
1089 g_free(search_term);
1091 } else { /* prepend "http://" */
1092 new = g_malloc(sizeof("http://") + len);
1093 strcpy(new, "http://");
1094 memcpy(&new[sizeof("http://") - 1], s, len + 1);
1097 webkit_web_view_load_uri(webview, new);
1098 g_free(new);
1099 } else
1100 g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL);
1101 return TRUE;
1104 gboolean
1105 open_remembered(const Arg *arg)
1107 Arg a = {arg->i, rememberedURI};
1109 if (strcmp(rememberedURI, "")) {
1110 open_arg(&a);
1112 return TRUE;
1115 gboolean
1116 yank(const Arg *arg) {
1117 const char *url, *feedback, *content;
1119 if (arg->i & SourceURL) {
1120 url = webkit_web_view_get_uri(webview);
1121 if (!url)
1122 return TRUE;
1123 feedback = g_strconcat("Yanked ", url, NULL);
1124 give_feedback(feedback);
1125 if (arg->i & ClipboardPrimary)
1126 gtk_clipboard_set_text(clipboards[0], url, -1);
1127 if (arg->i & ClipboardGTK)
1128 gtk_clipboard_set_text(clipboards[1], url, -1);
1129 } else {
1130 webkit_web_view_copy_clipboard(webview);
1131 if (arg->i & ClipboardPrimary)
1132 content = gtk_clipboard_wait_for_text(clipboards[0]);
1133 if (!content && arg->i & ClipboardGTK)
1134 content = gtk_clipboard_wait_for_text(clipboards[1]);
1135 if (content) {
1136 feedback = g_strconcat("Yanked ", content, NULL);
1137 g_free((gpointer *)content);
1138 give_feedback(feedback);
1141 return TRUE;
1144 gboolean
1145 paste(const Arg *arg) {
1146 Arg a = { .i = arg->i & TargetNew, .s = NULL };
1148 /* If we're over a link, open it in a new target. */
1149 if (strlen(rememberedURI) > 0) {
1150 Arg new_target = { .i = TargetNew, .s = arg->s };
1151 open_arg(&new_target);
1152 return TRUE;
1155 if (arg->i & ClipboardPrimary)
1156 a.s = gtk_clipboard_wait_for_text(clipboards[0]);
1157 if (!a.s && arg->i & ClipboardGTK)
1158 a.s = gtk_clipboard_wait_for_text(clipboards[1]);
1159 if (a.s)
1160 open_arg(&a);
1161 return TRUE;
1164 gboolean
1165 quit(const Arg *arg) {
1166 FILE *f;
1167 const char *filename;
1168 const char *uri = webkit_web_view_get_uri(webview);
1169 if (uri != NULL) {
1170 /* write last URL into status file for recreation with "u" */
1171 filename = g_strdup_printf(CLOSED_URL_FILENAME);
1172 f = fopen(filename, "w");
1173 if (f != NULL) {
1174 fprintf(f, "%s", uri);
1175 fclose(f);
1178 gtk_main_quit();
1179 return TRUE;
1182 gboolean
1183 revive(const Arg *arg) {
1184 FILE *f;
1185 const char *filename;
1186 char buffer[512] = "";
1187 Arg a = { .i = TargetNew, .s = NULL };
1188 /* get the URL of the window which has been closed last */
1189 filename = g_strdup_printf(CLOSED_URL_FILENAME);
1190 f = fopen(filename, "r");
1191 if (f != NULL) {
1192 fgets(buffer, 512, f);
1193 fclose(f);
1195 if (strlen(buffer) > 0) {
1196 a.s = buffer;
1197 open_arg(&a);
1198 return TRUE;
1200 return FALSE;
1203 static
1204 gboolean print_frame(const Arg *arg)
1206 WebKitWebFrame *frame = webkit_web_view_get_main_frame(webview);
1207 webkit_web_frame_print (frame);
1208 return TRUE;
1211 gboolean
1212 search(const Arg *arg) {
1213 count = count ? count : 1;
1214 gboolean success, direction = arg->i & DirectionPrev;
1215 Arg a;
1217 if (arg->s) {
1218 free(search_handle);
1219 search_handle = g_strdup_printf(arg->s);
1221 if (!search_handle)
1222 return TRUE;
1223 if (arg->i & DirectionAbsolute)
1224 search_direction = direction;
1225 else
1226 direction ^= search_direction;
1227 do {
1228 success = webkit_web_view_search_text(webview, search_handle, arg->i & CaseSensitive, direction, FALSE);
1229 if (!success) {
1230 if (arg->i & Wrapping) {
1231 success = webkit_web_view_search_text(webview, search_handle, arg->i & CaseSensitive, direction, TRUE);
1232 if (success) {
1233 a.i = Warning;
1234 a.s = g_strdup_printf("search hit %s, continuing at %s",
1235 direction ? "BOTTOM" : "TOP",
1236 direction ? "TOP" : "BOTTOM");
1237 echo(&a);
1238 g_free(a.s);
1239 } else
1240 break;
1241 } else
1242 break;
1244 } while(--count);
1245 if (!success) {
1246 a.i = Error;
1247 a.s = g_strdup_printf("Pattern not found: %s", search_handle);
1248 echo(&a);
1249 g_free(a.s);
1251 return TRUE;
1254 gboolean
1255 set(const Arg *arg) {
1256 Arg a = { .i = Info | NoAutoHide };
1258 switch (arg->i) {
1259 case ModeNormal:
1260 if (search_handle) {
1261 search_handle = NULL;
1262 webkit_web_view_unmark_text_matches(webview);
1264 gtk_entry_set_text(GTK_ENTRY(inputbox), "");
1265 gtk_widget_grab_focus(GTK_WIDGET(webview));
1266 break;
1267 case ModePassThrough:
1268 a.s = g_strdup("-- PASS THROUGH --");
1269 echo(&a);
1270 g_free(a.s);
1271 break;
1272 case ModeSendKey:
1273 a.s = g_strdup("-- PASS TROUGH (next) --");
1274 echo(&a);
1275 g_free(a.s);
1276 break;
1277 case ModeInsert: /* should not be called manually but automatically */
1278 a.s = g_strdup("-- INSERT --");
1279 echo(&a);
1280 g_free(a.s);
1281 break;
1282 default:
1283 return TRUE;
1285 mode = arg->i;
1286 return TRUE;
1289 gchar*
1290 jsapi_ref_to_string(JSContextRef context, JSValueRef ref) {
1291 JSStringRef string_ref;
1292 gchar *string;
1293 size_t length;
1295 string_ref = JSValueToStringCopy(context, ref, NULL);
1296 length = JSStringGetMaximumUTF8CStringSize(string_ref);
1297 string = g_new(gchar, length);
1298 JSStringGetUTF8CString(string_ref, string, length);
1299 JSStringRelease(string_ref);
1300 return string;
1303 void
1304 jsapi_evaluate_script(const gchar *script, gchar **value, gchar **message) {
1305 WebKitWebFrame *frame = webkit_web_view_get_main_frame(webview);
1306 JSGlobalContextRef context = webkit_web_frame_get_global_context(frame);
1307 JSStringRef str;
1308 JSValueRef val, exception;
1310 str = JSStringCreateWithUTF8CString(script);
1311 val = JSEvaluateScript(context, str, JSContextGetGlobalObject(context), NULL, 0, &exception);
1312 JSStringRelease(str);
1313 if (!val)
1314 *message = jsapi_ref_to_string(context, exception);
1315 else
1316 *value = jsapi_ref_to_string(context, val);
1319 gboolean
1320 quickmark(const Arg *a) {
1321 int i, b;
1322 b = atoi(a->s);
1323 char *fn = g_strdup_printf(QUICKMARK_FILE);
1324 FILE *fp;
1325 fp = fopen(fn, "r");
1326 g_free(fn);
1327 fn = NULL;
1328 char buf[100];
1330 if (fp != NULL && b < 10) {
1331 for( i=0; i < b; ++i ) {
1332 if (feof(fp)) {
1333 break;
1335 fgets(buf, 100, fp);
1337 char *ptr = strrchr(buf, '\n');
1338 *ptr = '\0';
1339 Arg x = { .s = buf };
1340 if (strlen(buf))
1341 return open_arg(&x);
1342 else {
1343 x.i = Error;
1344 x.s = g_strdup_printf("Quickmark %d not defined", b);
1345 echo(&x);
1346 g_free(x.s);
1347 return false;
1349 } else { return false; }
1352 gboolean
1353 script(const Arg *arg) {
1354 gchar *value = NULL, *message = NULL;
1355 Arg a;
1357 if (!arg->s) {
1358 set_error("Missing argument.");
1359 return FALSE;
1361 jsapi_evaluate_script(arg->s, &value, &message);
1362 if (message) {
1363 set_error(message);
1364 if (arg->s)
1365 g_free(arg->s);
1366 return FALSE;
1368 if (arg->i != Silent && value) {
1369 a.i = arg->i;
1370 a.s = g_strdup(value);
1371 echo(&a);
1372 g_free(a.s);
1374 /* switch mode according to scripts return value */
1375 if (value) {
1376 if (strncmp(value, "done;", 5) == 0) {
1377 a.i = ModeNormal;
1378 set(&a);
1379 } else if (strncmp(value, "insert;", 7) == 0) {
1380 a.i = ModeInsert;
1381 set(&a);
1384 if (arg->s)
1385 g_free(arg->s);
1386 g_free(value);
1387 return TRUE;
1390 gboolean
1391 scroll(const Arg *arg) {
1392 GtkAdjustment *adjust = (arg->i & OrientationHoriz) ? adjust_h : adjust_v;
1393 int max = gtk_adjustment_get_upper(adjust) - gtk_adjustment_get_page_size(adjust);
1394 float val = gtk_adjustment_get_value(adjust) / max * 100;
1395 int direction = (arg->i & (1 << 2)) ? 1 : -1;
1397 if ((direction == 1 && val < 100) || (direction == -1 && val > 0)) {
1398 if (arg->i & ScrollMove)
1399 gtk_adjustment_set_value(adjust, gtk_adjustment_get_value(adjust) +
1400 direction * /* direction */
1401 ((arg->i & UnitLine || (arg->i & UnitBuffer && count)) ? (scrollstep * (count ? count : 1)) : (
1402 arg->i & UnitBuffer ? gtk_adjustment_get_page_size(adjust) / 2 :
1403 (count ? count : 1) * (gtk_adjustment_get_page_size(adjust) -
1404 (gtk_adjustment_get_page_size(adjust) > pagingkeep ? pagingkeep : 0)))));
1405 else
1406 gtk_adjustment_set_value(adjust,
1407 ((direction == 1) ? gtk_adjustment_get_upper : gtk_adjustment_get_lower)(adjust));
1408 update_state();
1410 return TRUE;
1413 gboolean
1414 zoom(const Arg *arg) {
1415 webkit_web_view_set_full_content_zoom(webview, (arg->i & ZoomFullContent) > 0);
1416 webkit_web_view_set_zoom_level(webview, (arg->i & ZoomOut) ?
1417 webkit_web_view_get_zoom_level(webview) +
1418 (((float)(count ? count : 1)) * (arg->i & (1 << 1) ? 1.0 : -1.0) * zoomstep) :
1419 (count ? (float)count / 100.0 : 1.0));
1420 return TRUE;
1423 gboolean
1424 fake_key_event(const Arg *a) {
1425 if(!embed) {
1426 return FALSE;
1428 Arg err;
1429 err.i = Error;
1430 Display *xdpy;
1431 if ( (xdpy = XOpenDisplay(NULL)) == NULL ) {
1432 err.s = g_strdup("Couldn't find the XDisplay.");
1433 echo(&err);
1434 g_free(err.s);
1435 return FALSE;
1438 XKeyEvent xk;
1439 xk.display = xdpy;
1440 xk.subwindow = None;
1441 xk.time = CurrentTime;
1442 xk.same_screen = True;
1443 xk.x = xk.y = xk.x_root = xk.y_root = 1;
1444 xk.window = embed;
1445 xk.state = a->i;
1447 if( ! a->s ) {
1448 err.s = g_strdup("Zero pointer as argument! Check your config.h");
1449 echo(&err);
1450 g_free(err.s);
1451 return FALSE;
1454 KeySym keysym;
1455 if( (keysym = XStringToKeysym(a->s)) == NoSymbol ) {
1456 err.s = g_strdup_printf("Couldn't translate %s to keysym", a->s );
1457 echo(&err);
1458 g_free(err.s);
1459 return FALSE;
1462 if( (xk.keycode = XKeysymToKeycode(xdpy, keysym)) == NoSymbol ) {
1463 err.s = g_strdup("Couldn't translate keysym to keycode");
1464 echo(&err);
1465 g_free(err.s);
1466 return FALSE;
1469 xk.type = KeyPress;
1470 if( !XSendEvent(xdpy, embed, True, KeyPressMask, (XEvent *)&xk) ) {
1471 err.s = g_strdup("XSendEvent failed");
1472 echo(&err);
1473 g_free(err.s);
1474 return FALSE;
1476 XFlush(xdpy);
1478 return TRUE;
1482 gboolean
1483 commandhistoryfetch(const Arg *arg) {
1484 if (arg->i == DirectionPrev) {
1485 commandpointer--;
1486 if (commandpointer < 0)
1487 commandpointer = maxcommands - 1;
1488 } else {
1489 commandpointer++;
1490 if (commandpointer == COMMANDHISTSIZE || commandpointer == maxcommands)
1491 commandpointer = 0;
1494 if (commandpointer < 0)
1495 return FALSE;
1497 gtk_entry_set_text(GTK_ENTRY(inputbox), commandhistory[commandpointer ]);
1498 gtk_editable_set_position(GTK_EDITABLE(inputbox), -1);
1499 return TRUE;
1503 gboolean
1504 bookmark(const Arg *arg) {
1505 FILE *f;
1506 const char *filename;
1507 const char *uri = webkit_web_view_get_uri(webview);
1508 const char *title = webkit_web_view_get_title(webview);
1509 filename = g_strdup_printf(BOOKMARKS_STORAGE_FILENAME);
1510 f = fopen(filename, "a");
1511 if (uri == NULL || strlen(uri) == 0) {
1512 set_error("No URI found to bookmark.");
1513 return FALSE;
1515 if (f != NULL) {
1516 fprintf(f, "%s", uri);
1517 if (title != NULL) {
1518 fprintf(f, "%s", " ");
1519 fprintf(f, "%s", title);
1521 if (arg->s && strlen(arg->s)) {
1522 build_taglist(arg, f);
1524 fprintf(f, "%s", "\n");
1525 fclose(f);
1526 give_feedback( "Bookmark saved" );
1527 return TRUE;
1528 } else {
1529 set_error("Bookmarks file not found.");
1530 return FALSE;
1534 gboolean
1535 history() {
1536 FILE *f;
1537 const char *filename;
1538 const char *uri = webkit_web_view_get_uri(webview);
1539 const char *title = webkit_web_view_get_title(webview);
1540 char *entry, buffer[512], *new;
1541 int n, i = 0;
1542 gboolean finished = FALSE;
1543 if (uri != NULL) {
1544 if (title != NULL) {
1545 entry = malloc((strlen(uri) + strlen(title) + 2) * sizeof(char));
1546 memset(entry, 0, strlen(uri) + strlen(title) + 2);
1547 } else {
1548 entry = malloc((strlen(uri) + 1) * sizeof(char));
1549 memset(entry, 0, strlen(uri) + 1);
1551 if (entry != NULL) {
1552 strncpy(entry, uri, strlen(uri));
1553 if (title != NULL) {
1554 strncat(entry, " ", 1);
1555 strncat(entry, title, strlen(title));
1557 n = strlen(entry);
1558 filename = g_strdup_printf(HISTORY_STORAGE_FILENAME);
1559 f = fopen(filename, "r");
1560 if (f != NULL) {
1561 new = (char *)malloc(HISTORY_MAX_ENTRIES * 512 * sizeof(char) + 1);
1562 if (new != NULL) {
1563 memset(new, 0, HISTORY_MAX_ENTRIES * 512 * sizeof(char) + 1);
1564 /* newest entries go on top */
1565 strncpy(new, entry, strlen(entry));
1566 strncat(new, "\n", 1);
1567 /* retain at most HISTORY_MAX_ENTIRES - 1 old entries */
1568 while (finished != TRUE) {
1569 if ((char *)NULL == fgets(buffer, 512, f)) {
1570 /* check if end of file was reached / error occured */
1571 if (!feof(f)) {
1572 break;
1574 /* end of file reached */
1575 finished = TRUE;
1576 continue;
1578 /* compare line (-1 because of newline character) */
1579 if (n != strlen(buffer) - 1 || strncmp(entry, buffer, n) != 0) {
1580 /* if the URI is already in history; we put it on top and skip it here */
1581 strncat(new, buffer, 512);
1582 i++;
1584 if ((i + 1) >= HISTORY_MAX_ENTRIES) {
1585 break;
1588 fclose(f);
1590 f = fopen(filename, "w");
1591 if (f != NULL) {
1592 fprintf(f, "%s", new);
1593 fclose(f);
1595 if (new != NULL) {
1596 free(new);
1597 new = NULL;
1601 if (entry != NULL) {
1602 free(entry);
1603 entry = NULL;
1606 return TRUE;
1609 static gboolean
1610 view_source(const Arg * arg) {
1611 gboolean current_mode = webkit_web_view_get_view_source_mode(webview);
1612 webkit_web_view_set_view_source_mode(webview, !current_mode);
1613 webkit_web_view_reload(webview);
1614 return TRUE;
1617 static gboolean
1618 focus_input(const Arg *arg) {
1619 static Arg a;
1621 a.s = g_strdup("hints.focusInput();");
1622 a.i = Silent;
1623 script(&a);
1624 update_state();
1625 return TRUE;
1628 static gboolean
1629 browser_settings(const Arg *arg) {
1630 char line[255];
1631 if (!arg->s) {
1632 set_error("Missing argument.");
1633 return FALSE;
1635 strncpy(line, arg->s, 254);
1636 if (process_set_line(line))
1637 return TRUE;
1638 else {
1639 set_error("Invalid setting.");
1640 return FALSE;
1644 char *
1645 search_word(int whichword) {
1646 int k = 0;
1647 static char word[240];
1648 char *c = my_pair.line;
1650 while (isspace(*c) && *c)
1651 c++;
1653 switch (whichword) {
1654 case 0:
1655 while (*c && !isspace (*c) && *c != '=' && k < 240) {
1656 word[k++] = *c;
1657 c++;
1659 word[k] = '\0';
1660 strncpy(my_pair.what, word, 20);
1661 break;
1662 case 1:
1663 while (*c && k < 240) {
1664 word[k++] = *c;
1665 c++;
1667 word[k] = '\0';
1668 strncpy(my_pair.value, word, 240);
1669 break;
1672 return c;
1675 static gboolean
1676 process_set_line(char *line) {
1677 char *c;
1678 int listlen, i;
1679 gboolean boolval;
1680 WebKitWebSettings *settings;
1682 settings = webkit_web_view_get_settings(webview);
1683 my_pair.line = line;
1684 c = search_word(0);
1685 if (!strlen(my_pair.what))
1686 return FALSE;
1688 while (isspace(*c) && *c)
1689 c++;
1691 if (*c == ':' || *c == '=')
1692 c++;
1694 my_pair.line = c;
1695 c = search_word(1);
1697 listlen = LENGTH(browsersettings);
1698 for (i = 0; i < listlen; i++) {
1699 if (strlen(browsersettings[i].name) == strlen(my_pair.what) && strncmp(browsersettings[i].name, my_pair.what, strlen(my_pair.what)) == 0) {
1700 /* mandatory argument not provided */
1701 if (strlen(my_pair.value) == 0)
1702 return FALSE;
1703 /* process qmark? */
1704 if (strlen(my_pair.what) == 5 && strncmp("qmark", my_pair.what, 5) == 0) {
1705 return (process_save_qmark(my_pair.value, webview));
1707 /* interpret boolean values */
1708 if (browsersettings[i].boolval) {
1709 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) {
1710 boolval = TRUE;
1711 } 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) {
1712 boolval = FALSE;
1713 } else {
1714 return FALSE;
1716 } else if (browsersettings[i].colourval) {
1717 /* interpret as hexadecimal colour */
1718 if (!parse_colour(my_pair.value)) {
1719 return FALSE;
1722 if (browsersettings[i].var != NULL) {
1723 /* write value into internal variable */
1724 /*if (browsersettings[i].intval) {
1725 browsersettings[i].var = atoi(my_pair.value);
1726 } else {*/
1727 strncpy(browsersettings[i].var, my_pair.value, MAX_SETTING_SIZE);
1728 if (strlen(my_pair.value) > MAX_SETTING_SIZE - 1) {
1729 /* in this case, \0 will not have been copied */
1730 browsersettings[i].var[MAX_SETTING_SIZE - 1] = '\0';
1731 /* in case this string is also used for a webkit setting, make sure it's consistent */
1732 my_pair.value[MAX_SETTING_SIZE - 1] = '\0';
1733 give_feedback("String too long; automatically truncated!");
1735 /*}*/
1737 if (strlen(browsersettings[i].webkit) > 0) {
1738 /* activate appropriate webkit setting */
1739 if (browsersettings[i].boolval) {
1740 g_object_set((GObject*)settings, browsersettings[i].webkit, boolval, NULL);
1741 } else if (browsersettings[i].intval) {
1742 g_object_set((GObject*)settings, browsersettings[i].webkit, atoi(my_pair.value), NULL);
1743 } else {
1744 g_object_set((GObject*)settings, browsersettings[i].webkit, my_pair.value, NULL);
1746 webkit_web_view_set_settings(webview, settings);
1749 /* process acceptlanguage*/
1750 if (strlen(my_pair.what) == 14 && strncmp("acceptlanguage", my_pair.what, 14) == 0) {
1751 g_object_set(G_OBJECT(session), "accept-language", acceptlanguage, NULL);
1754 /* toggle proxy usage? */
1755 if (strlen(my_pair.what) == 5 && strncmp("proxy", my_pair.what, 5) == 0) {
1756 toggle_proxy(boolval);
1759 /* Toggle scrollbars. */
1760 if (strlen(my_pair.what) == 10 && strncmp("scrollbars", my_pair.what, 10) == 0)
1761 toggle_scrollbars(boolval);
1763 /* Toggle widgets */
1764 if (strlen(my_pair.what) == 9 && strncmp("statusbar", my_pair.what, 9) == 0)
1765 gtk_widget_set_visible(GTK_WIDGET(statusbar), boolval);
1766 if (strlen(my_pair.what) == 8 && strncmp("inputbox", my_pair.what, 8) == 0)
1767 gtk_widget_set_visible(inputbox, boolval);
1769 /* case sensitivity of completion */
1770 if (strlen(my_pair.what) == 14 && strncmp("completioncase", my_pair.what, 14) == 0)
1771 complete_case_sensitive = boolval;
1773 /* reload page? */
1774 if (browsersettings[i].reload)
1775 webkit_web_view_reload(webview);
1776 return TRUE;
1779 return FALSE;
1782 gboolean
1783 process_line(char *line) {
1784 char *c = line;
1785 int i;
1786 size_t len, length = strlen(line);
1787 gboolean found = FALSE, success = FALSE;
1788 Arg a;
1790 while (isspace(*c))
1791 c++;
1792 /* Ignore blank lines. */
1793 if (c[0] == '\0')
1794 return TRUE;
1795 for (i = 0; i < LENGTH(commands); i++) {
1796 if (commands[i].cmd == NULL)
1797 break;
1798 len = strlen(commands[i].cmd);
1799 if (length >= len && !strncmp(c, commands[i].cmd, len) && (c[len] == ' ' || !c[len])) {
1800 found = TRUE;
1801 a.i = commands[i].arg.i;
1802 a.s = length > len + 1 ? &c[len + 1] : commands[i].arg.s;
1803 success = commands[i].func(&a);
1804 break;
1807 save_command_history(c);
1808 if (!found) {
1809 a.i = Error;
1810 a.s = g_strdup_printf("Not a browser command: %s", c);
1811 echo(&a);
1812 } else if (!success) {
1813 a.i = Error;
1814 if (error_msg != NULL) {
1815 a.s = g_strdup_printf("%s", error_msg);
1816 g_free(error_msg);
1817 error_msg = NULL;
1818 } else {
1819 a.s = g_strdup_printf("Unknown error. Please file a bug report!");
1821 echo(&a);
1822 g_free(a.s);
1824 return success;
1827 static gboolean
1828 search_tag(const Arg * a) {
1829 FILE *f;
1830 const char *filename;
1831 const char *tag = a->s;
1832 char s[BUFFERSIZE], foundtag[40], url[BUFFERSIZE];
1833 int t, i, intag, k;
1835 if (!tag) {
1836 /* The user must give us something to load up. */
1837 set_error("Bookmark tag required with this option.");
1838 return FALSE;
1841 if (strlen(tag) > MAXTAGSIZE) {
1842 set_error("Tag too long.");
1843 return FALSE;
1846 filename = g_strdup_printf(BOOKMARKS_STORAGE_FILENAME);
1847 f = fopen(filename, "r");
1848 if (f == NULL) {
1849 set_error("Couldn't open bookmarks file.");
1850 return FALSE;
1852 while (fgets(s, BUFFERSIZE-1, f)) {
1853 intag = 0;
1854 t = strlen(s) - 1;
1855 while (isspace(s[t]))
1856 t--;
1857 if (s[t] != ']') continue;
1858 while (t > 0) {
1859 if (s[t] == ']') {
1860 if (!intag)
1861 intag = t;
1862 else
1863 intag = 0;
1864 } else {
1865 if (s[t] == '[') {
1866 if (intag) {
1867 i = 0;
1868 k = t + 1;
1869 while (k < intag)
1870 foundtag[i++] = s[k++];
1871 foundtag[i] = '\0';
1872 /* foundtag now contains the tag */
1873 if (strlen(foundtag) < MAXTAGSIZE && strcmp(tag, foundtag) == 0) {
1874 i = 0;
1875 while (isspace(s[i])) i++;
1876 k = 0;
1877 while (s[i] && !isspace(s[i])) url[k++] = s[i++];
1878 url[k] = '\0';
1879 Arg x = { .i = TargetNew, .s = url };
1880 open_arg(&x);
1883 intag = 0;
1886 t--;
1889 return TRUE;
1892 void
1893 toggle_proxy(gboolean onoff) {
1894 SoupURI *proxy_uri;
1895 char *filename, *new;
1896 int len;
1898 if (onoff == FALSE) {
1899 g_object_set(session, "proxy-uri", NULL, NULL);
1900 give_feedback("Proxy deactivated");
1901 } else {
1902 filename = (char *)g_getenv("http_proxy");
1904 /* Fallthrough to checking HTTP_PROXY as well, since this can also be
1905 * defined.
1907 if (filename == NULL)
1908 filename = (char *)g_getenv("HTTP_PROXY");
1910 if (filename != NULL && 0 < (len = strlen(filename))) {
1911 if (strstr(filename, "://") == NULL) {
1912 /* prepend http:// */
1913 new = g_malloc(sizeof("http://") + len);
1914 strcpy(new, "http://");
1915 memcpy(&new[sizeof("http://") - 1], filename, len + 1);
1916 proxy_uri = soup_uri_new(new);
1917 } else {
1918 proxy_uri = soup_uri_new(filename);
1920 g_object_set(session, "proxy-uri", proxy_uri, NULL);
1921 give_feedback("Proxy activated");
1926 void
1927 toggle_scrollbars(gboolean onoff) {
1928 if (onoff == TRUE) {
1929 adjust_h = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(viewport));
1930 adjust_v = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(viewport));
1931 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(viewport), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1932 } else {
1933 adjust_v = gtk_range_get_adjustment(GTK_RANGE(scroll_v));
1934 adjust_h = gtk_range_get_adjustment(GTK_RANGE(scroll_h));
1935 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(viewport), GTK_POLICY_NEVER, GTK_POLICY_NEVER);
1937 gtk_widget_set_scroll_adjustments (GTK_WIDGET(webview), adjust_h, adjust_v);
1939 return;
1942 void
1943 update_url(const char *uri) {
1944 gboolean ssl = g_str_has_prefix(uri, "https://");
1945 GdkColor color;
1946 #ifdef ENABLE_HISTORY_INDICATOR
1947 char before[] = " [";
1948 char after[] = "]";
1949 gboolean back = webkit_web_view_can_go_back(webview);
1950 gboolean fwd = webkit_web_view_can_go_forward(webview);
1952 if (!back && !fwd)
1953 before[0] = after[0] = '\0';
1954 #endif
1955 gtk_label_set_markup(GTK_LABEL(status_url), g_markup_printf_escaped(
1956 #ifdef ENABLE_HISTORY_INDICATOR
1957 "<span font=\"%s\">%s%s%s%s%s</span>", statusfont, uri,
1958 before, back ? "+" : "", fwd ? "-" : "", after
1959 #else
1960 "<span font=\"%s\">%s</span>", statusfont, uri
1961 #endif
1963 gdk_color_parse(ssl ? sslbgcolor : statusbgcolor, &color);
1964 gtk_widget_modify_bg(eventbox, GTK_STATE_NORMAL, &color);
1965 gdk_color_parse(ssl ? sslcolor : statuscolor, &color);
1966 gtk_widget_modify_fg(GTK_WIDGET(status_url), GTK_STATE_NORMAL, &color);
1967 gtk_widget_modify_fg(GTK_WIDGET(status_state), GTK_STATE_NORMAL, &color);
1970 void
1971 update_state() {
1972 char *markup;
1973 int download_count = g_list_length(activeDownloads);
1974 GString *status = g_string_new("");
1976 /* construct the status line */
1978 /* count, modkey and input buffer */
1979 g_string_append_printf(status, "%.0d", count);
1980 if (current_modkey) g_string_append_c(status, current_modkey);
1982 /* the number of active downloads */
1983 if (activeDownloads) {
1984 g_string_append_printf(status, " %d active %s", download_count,
1985 (download_count == 1) ? "download" : "downloads");
1988 #ifdef ENABLE_WGET_PROGRESS_BAR
1989 /* the progressbar */
1991 int progress = -1;
1992 char progressbar[progressbartick + 1];
1994 if (activeDownloads) {
1995 progress = 0;
1996 GList *ptr;
1998 for (ptr = activeDownloads; ptr; ptr = g_list_next(ptr)) {
1999 progress += 100 * webkit_download_get_progress(ptr->data);
2002 progress /= download_count;
2004 } else if (webkit_web_view_get_load_status(webview) != WEBKIT_LOAD_FINISHED
2005 && webkit_web_view_get_load_status(webview) != WEBKIT_LOAD_FAILED) {
2007 progress = webkit_web_view_get_progress(webview) * 100;
2010 if (progress >= 0) {
2011 ascii_bar(progressbartick, progress * progressbartick / 100, progressbar);
2012 g_string_append_printf(status, " %c%s%c",
2013 progressborderleft, progressbar, progressborderright);
2016 #endif
2018 /* and the current scroll position */
2020 int max = gtk_adjustment_get_upper(adjust_v) - gtk_adjustment_get_page_size(adjust_v);
2021 int val = (int)(gtk_adjustment_get_value(adjust_v) / max * 100);
2023 if (max == 0)
2024 g_string_append(status, " All");
2025 else if (val == 0)
2026 g_string_append(status, " Top");
2027 else if (val == 100)
2028 g_string_append(status, " Bot");
2029 else
2030 g_string_append_printf(status, " %d%%", val);
2034 markup = g_markup_printf_escaped("<span font=\"%s\">%s</span>", statusfont, status->str);
2035 gtk_label_set_markup(GTK_LABEL(status_state), markup);
2037 g_string_free(status, TRUE);
2040 void
2041 setup_modkeys() {
2042 unsigned int i;
2043 modkeys = calloc(LENGTH(keys) + 1, sizeof(char));
2044 char *ptr = modkeys;
2046 for (i = 0; i < LENGTH(keys); i++)
2047 if (keys[i].modkey && !strchr(modkeys, keys[i].modkey))
2048 *(ptr++) = keys[i].modkey;
2049 modkeys = realloc(modkeys, &ptr[0] - &modkeys[0] + 1);
2052 void
2053 setup_gui() {
2054 scroll_h = GTK_SCROLLBAR(gtk_hscrollbar_new(NULL));
2055 scroll_v = GTK_SCROLLBAR(gtk_vscrollbar_new(NULL));
2056 adjust_h = gtk_range_get_adjustment(GTK_RANGE(scroll_h));
2057 adjust_v = gtk_range_get_adjustment(GTK_RANGE(scroll_v));
2058 if (embed) {
2059 window = GTK_WINDOW(gtk_plug_new(embed));
2060 } else {
2061 window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
2062 gtk_window_set_wmclass(GTK_WINDOW(window), "vimprobable2", "Vimprobable2");
2064 gtk_window_set_default_size(GTK_WINDOW(window), 640, 480);
2065 box = GTK_BOX(gtk_vbox_new(FALSE, 0));
2066 inputbox = gtk_entry_new();
2067 webview = (WebKitWebView*)webkit_web_view_new();
2068 statusbar = GTK_BOX(gtk_hbox_new(FALSE, 0));
2069 eventbox = gtk_event_box_new();
2070 status_url = gtk_label_new(NULL);
2071 status_state = gtk_label_new(NULL);
2072 GdkColor bg;
2073 PangoFontDescription *font;
2074 GdkGeometry hints = { 1, 1 };
2075 inspector = webkit_web_view_get_inspector(WEBKIT_WEB_VIEW(webview));
2077 clipboards[0] = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
2078 clipboards[1] = gtk_clipboard_get(GDK_NONE);
2079 setup_settings();
2080 gdk_color_parse(statusbgcolor, &bg);
2081 gtk_widget_modify_bg(eventbox, GTK_STATE_NORMAL, &bg);
2082 gtk_widget_set_name(GTK_WIDGET(window), "Vimprobable2");
2083 gtk_window_set_geometry_hints(window, NULL, &hints, GDK_HINT_MIN_SIZE);
2085 #ifdef DISABLE_SCROLLBAR
2086 viewport = gtk_scrolled_window_new(NULL, NULL);
2087 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(viewport), GTK_POLICY_NEVER, GTK_POLICY_NEVER);
2088 #else
2089 /* Ensure we still see scrollbars. */
2090 GtkWidget *viewport = gtk_scrolled_window_new(adjust_h, adjust_v);
2091 #endif
2093 setup_signals();
2094 gtk_container_add(GTK_CONTAINER(viewport), GTK_WIDGET(webview));
2096 /* Ensure we set the scroll adjustments now, so that if we're not drawing
2097 * titlebars, we can still scroll.
2099 gtk_widget_set_scroll_adjustments(GTK_WIDGET(webview), adjust_h, adjust_v);
2101 font = pango_font_description_from_string(urlboxfont[0]);
2102 gtk_widget_modify_font(GTK_WIDGET(inputbox), font);
2103 pango_font_description_free(font);
2104 gtk_entry_set_inner_border(GTK_ENTRY(inputbox), NULL);
2105 gtk_misc_set_alignment(GTK_MISC(status_url), 0.0, 0.0);
2106 gtk_misc_set_alignment(GTK_MISC(status_state), 1.0, 0.0);
2107 gtk_box_pack_start(statusbar, status_url, TRUE, TRUE, 2);
2108 gtk_box_pack_start(statusbar, status_state, FALSE, FALSE, 2);
2109 gtk_container_add(GTK_CONTAINER(eventbox), GTK_WIDGET(statusbar));
2110 gtk_box_pack_start(box, viewport, TRUE, TRUE, 0);
2111 gtk_box_pack_start(box, eventbox, FALSE, FALSE, 0);
2112 gtk_entry_set_has_frame(GTK_ENTRY(inputbox), FALSE);
2113 gtk_box_pack_end(box, inputbox, FALSE, FALSE, 0);
2114 gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(box));
2115 gtk_widget_grab_focus(GTK_WIDGET(webview));
2116 gtk_widget_show_all(GTK_WIDGET(window));
2119 void
2120 setup_settings() {
2121 WebKitWebSettings *settings = (WebKitWebSettings*)webkit_web_settings_new();
2122 SoupURI *proxy_uri;
2123 char *filename, *new;
2124 int len;
2126 session = webkit_get_default_session();
2127 g_object_set(G_OBJECT(settings), "default-font-size", DEFAULT_FONT_SIZE, NULL);
2128 g_object_set(G_OBJECT(settings), "enable-scripts", enablePlugins, NULL);
2129 g_object_set(G_OBJECT(settings), "enable-plugins", enablePlugins, NULL);
2130 g_object_set(G_OBJECT(settings), "enable-java-applet", enableJava, NULL);
2131 g_object_set(G_OBJECT(settings), "enable-page-cache", enablePagecache, NULL);
2132 filename = g_strdup_printf(USER_STYLESHEET);
2133 filename = g_strdup_printf("file://%s", filename);
2134 g_object_set(G_OBJECT(settings), "user-stylesheet-uri", filename, NULL);
2135 g_object_set(G_OBJECT(settings), "user-agent", useragent, NULL);
2136 g_object_get(G_OBJECT(settings), "zoom-step", &zoomstep, NULL);
2137 webkit_web_view_set_settings(webview, settings);
2139 /* proxy */
2140 if (use_proxy == TRUE) {
2141 filename = (char *)g_getenv("http_proxy");
2142 if (filename != NULL && 0 < (len = strlen(filename))) {
2143 if (strstr(filename, "://") == NULL) {
2144 /* prepend http:// */
2145 new = g_malloc(sizeof("http://") + len);
2146 strcpy(new, "http://");
2147 memcpy(&new[sizeof("http://") - 1], filename, len + 1);
2148 proxy_uri = soup_uri_new(new);
2149 } else {
2150 proxy_uri = soup_uri_new(filename);
2152 g_object_set(session, "proxy-uri", proxy_uri, NULL);
2157 void
2158 setup_signals() {
2159 WebKitWebFrame *frame = webkit_web_view_get_main_frame(webview);
2160 #ifdef ENABLE_COOKIE_SUPPORT
2161 /* Headers. */
2162 g_signal_connect_after(G_OBJECT(session), "request-started", G_CALLBACK(new_generic_request), NULL);
2163 #endif
2164 /* Accept-language header */
2165 g_object_set(G_OBJECT(session), "accept-language", acceptlanguage, NULL);
2166 /* window */
2167 g_object_connect(G_OBJECT(window),
2168 "signal::destroy", G_CALLBACK(window_destroyed_cb), NULL,
2169 NULL);
2170 /* frame */
2171 g_signal_connect(G_OBJECT(frame),
2172 "scrollbars-policy-changed", G_CALLBACK(blank_cb), NULL);
2173 /* webview */
2174 g_object_connect(G_OBJECT(webview),
2175 "signal::title-changed", G_CALLBACK(webview_title_changed_cb), NULL,
2176 "signal::load-progress-changed", G_CALLBACK(webview_progress_changed_cb), NULL,
2177 "signal::load-committed", G_CALLBACK(webview_load_committed_cb), NULL,
2178 "signal::load-finished", G_CALLBACK(webview_load_finished_cb), NULL,
2179 "signal::navigation-policy-decision-requested", G_CALLBACK(webview_navigation_cb), NULL,
2180 "signal::new-window-policy-decision-requested", G_CALLBACK(webview_new_window_cb), NULL,
2181 "signal::mime-type-policy-decision-requested", G_CALLBACK(webview_mimetype_cb), NULL,
2182 "signal::download-requested", G_CALLBACK(webview_download_cb), NULL,
2183 "signal::key-press-event", G_CALLBACK(webview_keypress_cb), NULL,
2184 "signal::hovering-over-link", G_CALLBACK(webview_hoverlink_cb), NULL,
2185 "signal::console-message", G_CALLBACK(webview_console_cb), NULL,
2186 "signal::create-web-view", G_CALLBACK(webview_open_in_new_window_cb), NULL,
2187 "signal::event", G_CALLBACK(notify_event_cb), NULL,
2188 NULL);
2189 /* webview adjustment */
2190 g_object_connect(G_OBJECT(adjust_v),
2191 "signal::value-changed", G_CALLBACK(webview_scroll_cb), NULL,
2192 NULL);
2193 /* inputbox */
2194 g_object_connect(G_OBJECT(inputbox),
2195 "signal::activate", G_CALLBACK(inputbox_activate_cb), NULL,
2196 "signal::key-press-event", G_CALLBACK(inputbox_keypress_cb), NULL,
2197 "signal::key-release-event", G_CALLBACK(inputbox_keyrelease_cb), NULL,
2198 #ifdef ENABLE_INCREMENTAL_SEARCH
2199 "signal::changed", G_CALLBACK(inputbox_changed_cb), NULL,
2200 #endif
2201 NULL);
2202 /* inspector */
2203 g_signal_connect(G_OBJECT(inspector),
2204 "inspect-web-view", G_CALLBACK(inspector_inspect_web_view_cb), NULL);
2207 #ifdef ENABLE_COOKIE_SUPPORT
2208 void
2209 setup_cookies()
2211 if (file_cookie_jar)
2212 g_object_unref(file_cookie_jar);
2214 if (session_cookie_jar)
2215 g_object_unref(session_cookie_jar);
2217 session_cookie_jar = soup_cookie_jar_new();
2219 cookie_store = g_strdup_printf(COOKIES_STORAGE_FILENAME);
2221 load_all_cookies();
2223 g_signal_connect(G_OBJECT(file_cookie_jar), "changed",
2224 G_CALLBACK(update_cookie_jar), NULL);
2226 return;
2229 /* TA: XXX - we should be using this callback for any header-requests we
2230 * receive (hence the name "new_generic_request" -- but for now, its use
2231 * is limited to handling cookies.
2233 void
2234 new_generic_request(SoupSession *session, SoupMessage *soup_msg, gpointer unused) {
2235 SoupMessageHeaders *soup_msg_h;
2236 SoupURI *uri;
2237 const char *cookie_str;
2239 soup_msg_h = soup_msg->request_headers;
2240 soup_message_headers_remove(soup_msg_h, "Cookie");
2241 uri = soup_message_get_uri(soup_msg);
2242 if( (cookie_str = get_cookies(uri)) )
2243 soup_message_headers_append(soup_msg_h, "Cookie", cookie_str);
2245 g_signal_connect_after(G_OBJECT(soup_msg), "got-headers", G_CALLBACK(handle_cookie_request), NULL);
2247 return;
2250 const char *
2251 get_cookies(SoupURI *soup_uri) {
2252 const char *cookie_str;
2254 cookie_str = soup_cookie_jar_get_cookies(file_cookie_jar, soup_uri, TRUE);
2256 return cookie_str;
2259 void
2260 handle_cookie_request(SoupMessage *soup_msg, gpointer unused)
2262 GSList *resp_cookie = NULL;
2263 SoupCookie *cookie;
2265 for(resp_cookie = soup_cookies_from_response(soup_msg);
2266 resp_cookie;
2267 resp_cookie = g_slist_next(resp_cookie))
2269 SoupDate *soup_date;
2270 cookie = soup_cookie_copy((SoupCookie *)resp_cookie->data);
2272 if (cookie_timeout && cookie->expires == NULL) {
2273 soup_date = soup_date_new_from_time_t(time(NULL) + cookie_timeout * 10);
2274 soup_cookie_set_expires(cookie, soup_date);
2276 soup_cookie_jar_add_cookie(file_cookie_jar, cookie);
2279 return;
2282 void
2283 update_cookie_jar(SoupCookieJar *jar, SoupCookie *old, SoupCookie *new)
2285 if (!new) {
2286 /* Nothing to do. */
2287 return;
2290 SoupCookie *copy;
2291 copy = soup_cookie_copy(new);
2293 soup_cookie_jar_add_cookie(session_cookie_jar, copy);
2295 return;
2298 void
2299 load_all_cookies(void)
2301 file_cookie_jar = soup_cookie_jar_text_new(cookie_store, COOKIES_STORAGE_READONLY);
2303 /* Put them back in the session store. */
2304 GSList *cookies_from_file = soup_cookie_jar_all_cookies(file_cookie_jar);
2306 for (; cookies_from_file;
2307 cookies_from_file = cookies_from_file->next)
2309 soup_cookie_jar_add_cookie(session_cookie_jar, cookies_from_file->data);
2312 soup_cookies_free(cookies_from_file);
2314 return;
2317 #endif
2319 void
2320 mop_up(void) {
2321 /* Free up any nasty globals before exiting. */
2322 #ifdef ENABLE_COOKIE_SUPPORT
2323 if (cookie_store)
2324 g_free(cookie_store);
2325 #endif
2326 return;
2330 main(int argc, char *argv[]) {
2331 static Arg a;
2332 static char url[256] = "";
2333 static gboolean ver = false;
2334 static gboolean configfile_exists = FALSE;
2335 static const char *cfile = NULL;
2336 char *searchengines_file;
2337 static GOptionEntry opts[] = {
2338 { "version", 'v', 0, G_OPTION_ARG_NONE, &ver, "print version", NULL },
2339 { "embed", 'e', 0, G_OPTION_ARG_STRING, &winid, "embedded", NULL },
2340 { "configfile", 'c', 0, G_OPTION_ARG_STRING, &cfile, "config file", NULL },
2341 { NULL }
2343 static GError *err;
2344 args = argv;
2346 /* command line argument: version */
2347 if (!gtk_init_with_args(&argc, &argv, "[<uri>]", opts, NULL, &err)) {
2348 g_printerr("can't init gtk: %s\n", err->message);
2349 g_error_free(err);
2350 return EXIT_FAILURE;
2353 if (ver) {
2354 printf("%s\n", INTERNAL_VERSION);
2355 return EXIT_SUCCESS;
2358 if( getenv("XDG_CONFIG_HOME") )
2359 config_base = g_strdup_printf("%s", getenv("XDG_CONFIG_HOME"));
2360 else
2361 config_base = g_strdup_printf("%s/.config/", getenv("HOME"));
2363 if (cfile)
2364 configfile = g_strdup_printf(cfile);
2365 else
2366 configfile = g_strdup_printf(RCFILE);
2368 if (!g_thread_supported())
2369 g_thread_init(NULL);
2371 if (winid)
2372 embed = atoi(winid);
2374 setup_modkeys();
2375 make_keyslist();
2376 setup_gui();
2377 #ifdef ENABLE_COOKIE_SUPPORT
2378 setup_cookies();
2379 #endif
2381 /* Check if the specified file exists. */
2382 /* And only warn the user, if they explicitly asked for a config on the
2383 * command line.
2385 if (!(access(configfile, F_OK) == 0) && cfile) {
2386 char *feedback_str;
2388 feedback_str = g_strdup_printf("Config file '%s' doesn't exist", cfile);
2389 give_feedback(feedback_str);
2390 } else if ((access(configfile, F_OK) == 0))
2391 configfile_exists = true;
2393 /* read config file */
2394 /* But only report errors if we failed, and the file existed. */
2395 if (!read_rcfile(configfile) && configfile_exists) {
2396 a.i = Error;
2397 a.s = g_strdup_printf("Error in config file '%s'", configfile);
2398 echo(&a);
2399 g_free(a.s);
2400 g_free(configfile);
2403 make_searchengines_list(searchengines, LENGTH(searchengines));
2405 /* read searchengines. */
2406 searchengines_file = g_strdup_printf(SEARCHENGINES_STORAGE_FILENAME);
2407 switch (read_searchengines(searchengines_file)) {
2408 case SYNTAX_ERROR:
2409 a.i = Error;
2410 a.s = g_strdup_printf("Syntax error in searchengines file '%s'", searchengines_file);
2411 echo(&a);
2412 break;
2413 case READING_FAILED:
2414 a.i = Error;
2415 a.s = g_strdup_printf("Could not read searchengines file '%s'", searchengines_file);
2416 echo(&a);
2417 break;
2418 default:
2419 break;
2421 g_free(searchengines_file);
2423 /* command line argument: URL */
2424 if (argc > 1) {
2425 strncpy(url, argv[argc - 1], 255);
2426 } else {
2427 strncpy(url, startpage, 255);
2430 a.i = TargetCurrent;
2431 a.s = url;
2432 open_arg(&a);
2433 gtk_main();
2435 mop_up();
2437 return EXIT_SUCCESS;