Removed code dealing with old hintmode.
[vimprobable2.git] / main.c
blob691588fc7cb2336ca3f0d6c601a8dc8fefdeb93c
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 < 2)
453 return;
454 text = (char*)gtk_entry_get_text(entry);
455 if (text[0] == ':') {
456 success = process_line((text + 1));
457 } else if ((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
473 return;
474 if (!echo_active)
475 gtk_entry_set_text(entry, "");
476 gtk_widget_grab_focus(GTK_WIDGET(webview));
479 gboolean
480 inputbox_keypress_cb(GtkEntry *entry, GdkEventKey *event) {
481 Arg a;
482 char count_buf[BUFFERSIZE];
484 switch (event->keyval) {
485 case GDK_bracketleft:
486 case GDK_Escape:
487 if (!IS_ESCAPE(event)) break;
488 a.i = HideCompletion;
489 complete(&a);
490 a.i = ModeNormal;
491 return set(&a);
492 break;
493 case GDK_Tab:
494 a.i = DirectionNext;
495 return complete(&a);
496 break;
497 case GDK_Up:
498 a.i = DirectionPrev;
499 return commandhistoryfetch(&a);
500 break;
501 case GDK_Down:
502 a.i = DirectionNext;
503 return commandhistoryfetch(&a);
504 break;
505 case GDK_ISO_Left_Tab:
506 a.i = DirectionPrev;
507 return complete(&a);
508 break;
511 if (followTarget[0] && ((event->keyval >= GDK_1 && event->keyval <= GDK_9)
512 || (event->keyval >= GDK_KP_1 && event->keyval <= GDK_KP_9)
513 || ((event->keyval == GDK_0 || event->keyval == GDK_KP_0) && count))) {
514 /* allow a zero as non-first number */
515 if (event->keyval >= GDK_KP_0 && event->keyval <= GDK_KP_9)
516 count = (count ? count * 10 : 0) + (event->keyval - GDK_KP_0);
517 else
518 count = (count ? count * 10 : 0) + (event->keyval - GDK_0);
519 a.i = Silent;
520 a.s = g_strdup_printf("vimprobable_update_hints(%d)", count);
521 script(&a);
522 update_state();
523 return TRUE;
526 return FALSE;
529 gboolean
530 notify_event_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data) {
531 int i;
532 if (mode == ModeNormal && event->type == GDK_BUTTON_RELEASE) {
533 /* handle mouse click events */
534 for (i = 0; i < LENGTH(mouse); i++) {
535 if (mouse[i].mask == CLEAN(event->button.state)
536 && (mouse[i].modkey == current_modkey
537 || (!mouse[i].modkey && !current_modkey)
538 || mouse[i].modkey == GDK_VoidSymbol) /* wildcard */
539 && mouse[i].button == event->button.button
540 && mouse[i].func) {
541 if (mouse[i].func(&mouse[i].arg)) {
542 current_modkey = count = 0;
543 update_state();
544 return TRUE;
549 return FALSE;
552 static gboolean inputbox_keyrelease_cb(GtkEntry *entry, GdkEventKey *event) {
553 Arg a;
554 guint16 length = gtk_entry_get_text_length(entry);
556 if (!length) {
557 a.i = HideCompletion;
558 complete(&a);
559 a.i = ModeNormal;
560 return set(&a);
562 return FALSE;
565 static gboolean inputbox_changed_cb(GtkEditable *entry, gpointer user_data) {
566 Arg a;
567 char *text = (char*)gtk_entry_get_text(GTK_ENTRY(entry));
568 guint16 length = gtk_entry_get_text_length(GTK_ENTRY(entry));
569 gboolean forward = FALSE;
571 /* Update incremental search if the user changes the search text.
573 * Note: gtk_widget_is_focus() is a poor way to check if the change comes
574 * from the user. But if the entry is focused and the text is set
575 * through gtk_entry_set_text() in some asyncrounous operation,
576 * I would consider that a bug.
579 if (gtk_widget_is_focus(GTK_WIDGET(entry)) && length > 1 && ((forward = text[0] == '/') || text[0] == '?')) {
580 webkit_web_view_unmark_text_matches(webview);
581 webkit_web_view_search_text(webview, &text[1], searchoptions & CaseSensitive, forward, searchoptions & Wrapping);
582 return TRUE;
583 } else if (gtk_widget_is_focus(GTK_WIDGET(entry)) && length >= 1 &&
584 (text[0] == '`' || text[0] == '~')) {
585 a.i = Silent;
586 a.s = g_strdup("vimprobable_cleanup()");
587 script(&a);
589 a.i = Silent;
590 a.s = g_strconcat("vimprobable_show_hints('", text + 1, "')", NULL);
591 script(&a);
593 return TRUE;
594 } else if (length == 0 && followTarget[0]) {
595 memset(followTarget, 0, 8);
596 a.i = Silent;
597 a.s = g_strdup("vimprobable_clear()");
598 script(&a);
599 count = 0;
600 update_state();
603 return FALSE;
606 /* funcs here */
608 void fill_suggline(char * suggline, const char * command, const char *fill_with) {
609 memset(suggline, 0, 512);
610 strncpy(suggline, command, 512);
611 strncat(suggline, " ", 1);
612 strncat(suggline, fill_with, 512 - strlen(suggline) - 1);
615 GtkWidget * fill_eventbox(const char * completion_line) {
616 GtkBox * row;
617 GtkWidget *row_eventbox, *el;
618 GdkColor color;
619 char * markup;
621 row = GTK_BOX(gtk_hbox_new(FALSE, 0));
622 row_eventbox = gtk_event_box_new();
623 gdk_color_parse(completionbgcolor[0], &color);
624 gtk_widget_modify_bg(row_eventbox, GTK_STATE_NORMAL, &color);
625 el = gtk_label_new(NULL);
626 markup = g_strconcat("<span font=\"", completionfont[0], "\" color=\"", completioncolor[0], "\">",
627 g_markup_escape_text(completion_line, strlen(completion_line)), "</span>", NULL);
628 gtk_label_set_markup(GTK_LABEL(el), markup);
629 g_free(markup);
630 gtk_misc_set_alignment(GTK_MISC(el), 0, 0);
631 gtk_box_pack_start(row, el, TRUE, TRUE, 2);
632 gtk_container_add(GTK_CONTAINER(row_eventbox), GTK_WIDGET(row));
633 return row_eventbox;
636 gboolean
637 complete(const Arg *arg) {
638 char *str, *p, *s, *markup, *entry, *searchfor, command[32] = "", suggline[512] = "", **suggurls;
639 size_t listlen, len, cmdlen;
640 int i, spacepos;
641 Listelement *elementlist = NULL, *elementpointer;
642 gboolean highlight = FALSE;
643 GtkBox *row;
644 GtkWidget *row_eventbox, *el;
645 GtkBox *_table;
646 GdkColor color;
647 static GtkWidget *table, *top_border;
648 static char *prefix;
649 static char **suggestions;
650 static GtkWidget **widgets;
651 static int n = 0, m, current = -1;
653 str = (char*)gtk_entry_get_text(GTK_ENTRY(inputbox));
654 len = strlen(str);
656 /* Get the length of the list of commands for completion. We need this to
657 * malloc/realloc correctly.
659 listlen = LENGTH(commands);
661 if ((len == 0 || str[0] != ':') && arg->i != HideCompletion)
662 return TRUE;
663 if (prefix) {
664 if (arg->i != HideCompletion && widgets && current != -1 && !strcmp(&str[1], suggestions[current])) {
665 gdk_color_parse(completionbgcolor[0], &color);
666 gtk_widget_modify_bg(widgets[current], GTK_STATE_NORMAL, &color);
667 current = (n + current + (arg->i == DirectionPrev ? -1 : 1)) % n;
668 if ((arg->i == DirectionNext && current == 0)
669 || (arg->i == DirectionPrev && current == n - 1))
670 current = -1;
671 } else {
672 free(widgets);
673 free(suggestions);
674 free(prefix);
675 gtk_widget_destroy(GTK_WIDGET(table));
676 gtk_widget_destroy(GTK_WIDGET(top_border));
677 table = NULL;
678 widgets = NULL;
679 suggestions = NULL;
680 prefix = NULL;
681 n = 0;
682 current = -1;
683 if (arg->i == HideCompletion)
684 return TRUE;
686 } else if (arg->i == HideCompletion)
687 return TRUE;
688 if (!widgets) {
689 prefix = g_strdup_printf(str);
690 widgets = malloc(sizeof(GtkWidget*) * listlen);
691 suggestions = malloc(sizeof(char*) * listlen);
692 top_border = gtk_event_box_new();
693 gtk_widget_set_size_request(GTK_WIDGET(top_border), 0, 1);
694 gdk_color_parse(completioncolor[2], &color);
695 gtk_widget_modify_bg(top_border, GTK_STATE_NORMAL, &color);
696 table = gtk_event_box_new();
697 gdk_color_parse(completionbgcolor[0], &color);
698 _table = GTK_BOX(gtk_vbox_new(FALSE, 0));
699 highlight = len > 1;
700 if (strchr(str, ' ') == NULL) {
701 /* command completion */
702 listlen = LENGTH(commands);
703 for (i = 0; i < listlen; i++) {
704 if (commands[i].cmd == NULL)
705 break;
706 cmdlen = strlen(commands[i].cmd);
707 if (!highlight || (n < MAX_LIST_SIZE && len - 1 <= cmdlen && !strncmp(&str[1], commands[i].cmd, len - 1))) {
708 p = s = malloc(sizeof(char*) * (highlight ? sizeof(COMPLETION_TAG_OPEN) + sizeof(COMPLETION_TAG_CLOSE) - 1 : 1) + cmdlen);
709 if (highlight) {
710 memcpy(p, COMPLETION_TAG_OPEN, sizeof(COMPLETION_TAG_OPEN) - 1);
711 memcpy((p += sizeof(COMPLETION_TAG_OPEN) - 1), &str[1], len - 1);
712 memcpy((p += len - 1), COMPLETION_TAG_CLOSE, sizeof(COMPLETION_TAG_CLOSE) - 1);
713 p += sizeof(COMPLETION_TAG_CLOSE) - 1;
715 memcpy(p, &commands[i].cmd[len - 1], cmdlen - len + 2);
716 row = GTK_BOX(gtk_hbox_new(FALSE, 0));
717 row_eventbox = gtk_event_box_new();
718 gtk_widget_modify_bg(row_eventbox, GTK_STATE_NORMAL, &color);
719 el = gtk_label_new(NULL);
720 markup = g_strconcat("<span font=\"", completionfont[0], "\" color=\"", completioncolor[0], "\">", s, "</span>", NULL);
721 free(s);
722 gtk_label_set_markup(GTK_LABEL(el), markup);
723 g_free(markup);
724 gtk_misc_set_alignment(GTK_MISC(el), 0, 0);
725 gtk_box_pack_start(row, el, TRUE, TRUE, 2);
726 gtk_container_add(GTK_CONTAINER(row_eventbox), GTK_WIDGET(row));
727 gtk_box_pack_start(_table, GTK_WIDGET(row_eventbox), FALSE, FALSE, 0);
728 suggestions[n] = commands[i].cmd;
729 widgets[n++] = row_eventbox;
732 } else {
733 entry = (char *)malloc(512 * sizeof(char));
734 if (entry == NULL) {
735 return FALSE;
737 memset(entry, 0, 512);
738 suggurls = malloc(sizeof(char*) * listlen);
739 if (suggurls == NULL) {
740 return FALSE;
742 spacepos = strcspn(str, " ");
743 searchfor = (str + spacepos + 1);
744 strncpy(command, (str + 1), spacepos - 1);
745 if (strlen(command) == 3 && strncmp(command, "set", 3) == 0) {
746 /* browser settings */
747 listlen = LENGTH(browsersettings);
748 for (i = 0; i < listlen; i++) {
749 if (n < MAX_LIST_SIZE && strstr(browsersettings[i].name, searchfor) != NULL) {
750 /* match */
751 fill_suggline(suggline, command, browsersettings[i].name);
752 suggurls[n] = (char *)malloc(sizeof(char) * 512 + 1);
753 strncpy(suggurls[n], suggline, 512);
754 suggestions[n] = suggurls[n];
755 row_eventbox = fill_eventbox(suggline);
756 gtk_box_pack_start(_table, GTK_WIDGET(row_eventbox), FALSE, FALSE, 0);
757 widgets[n++] = row_eventbox;
761 } else if (strlen(command) == 2 && strncmp(command, "qt", 2) == 0) {
762 /* completion on tags */
763 spacepos = strcspn(str, " ");
764 searchfor = (str + spacepos + 1);
765 elementlist = complete_list(searchfor, 1, elementlist);
766 } else {
767 /* URL completion: bookmarks */
768 elementlist = complete_list(searchfor, 0, elementlist);
769 m = count_list(elementlist);
770 if (m < MAX_LIST_SIZE) {
771 /* URL completion: history */
772 elementlist = complete_list(searchfor, 2, elementlist);
775 elementpointer = elementlist;
776 while (elementpointer != NULL) {
777 fill_suggline(suggline, command, elementpointer->element);
778 suggurls[n] = (char *)malloc(sizeof(char) * 512 + 1);
779 strncpy(suggurls[n], suggline, 512);
780 suggestions[n] = suggurls[n];
781 row_eventbox = fill_eventbox(suggline);
782 gtk_box_pack_start(_table, GTK_WIDGET(row_eventbox), FALSE, FALSE, 0);
783 widgets[n++] = row_eventbox;
784 elementpointer = elementpointer->next;
785 if (n >= MAX_LIST_SIZE)
786 break;
788 free_list(elementlist);
789 if (suggurls != NULL) {
790 free(suggurls);
791 suggurls = NULL;
793 if (entry != NULL) {
794 free(entry);
795 entry = NULL;
798 /* TA: FIXME - this needs rethinking entirely. */
800 GtkWidget **widgets_temp = realloc(widgets, sizeof(*widgets) * n);
801 if (widgets_temp == NULL && widgets == NULL) {
802 fprintf(stderr, "Couldn't realloc() widgets\n");
803 exit(1);
805 widgets = widgets_temp;
806 char **suggestions_temp = realloc(suggestions, sizeof(*suggestions) * n);
807 if (suggestions_temp == NULL && suggestions == NULL) {
808 fprintf(stderr, "Couldn't realloc() suggestions\n");
809 exit(1);
811 suggestions = suggestions_temp;
813 if (!n) {
814 gdk_color_parse(completionbgcolor[1], &color);
815 gtk_widget_modify_bg(table, GTK_STATE_NORMAL, &color);
816 el = gtk_label_new(NULL);
817 gtk_misc_set_alignment(GTK_MISC(el), 0, 0);
818 markup = g_strconcat("<span font=\"", completionfont[1], "\" color=\"", completioncolor[1], "\">No Completions</span>", NULL);
819 gtk_label_set_markup(GTK_LABEL(el), markup);
820 g_free(markup);
821 gtk_box_pack_start(_table, GTK_WIDGET(el), FALSE, FALSE, 0);
823 gtk_box_pack_start(box, GTK_WIDGET(top_border), FALSE, FALSE, 0);
824 gtk_container_add(GTK_CONTAINER(table), GTK_WIDGET(_table));
825 gtk_box_pack_start(box, GTK_WIDGET(table), FALSE, FALSE, 0);
826 gtk_widget_show_all(GTK_WIDGET(window));
827 if (!n)
828 return TRUE;
829 current = arg->i == DirectionPrev ? n - 1 : 0;
831 if (current != -1) {
832 gdk_color_parse(completionbgcolor[2], &color);
833 gtk_widget_modify_bg(GTK_WIDGET(widgets[current]), GTK_STATE_NORMAL, &color);
834 s = g_strconcat(":", suggestions[current], NULL);
835 gtk_entry_set_text(GTK_ENTRY(inputbox), s);
836 g_free(s);
837 } else
838 gtk_entry_set_text(GTK_ENTRY(inputbox), prefix);
839 gtk_editable_set_position(GTK_EDITABLE(inputbox), -1);
840 return TRUE;
843 gboolean
844 descend(const Arg *arg) {
845 char *source = (char*)webkit_web_view_get_uri(webview), *p = &source[0], *new;
846 int i, len;
847 count = count ? count : 1;
849 if (!source)
850 return TRUE;
851 if (arg->i == Rootdir) {
852 for (i = 0; i < 3; i++) /* get to the third slash */
853 if (!(p = strchr(++p, '/')))
854 return TRUE; /* if we cannot find it quit */
855 } else {
856 len = strlen(source);
857 if (!len) /* if string is empty quit */
858 return TRUE;
859 p = source + len; /* start at the end */
860 if (*(p - 1) == '/') /* /\/$/ is not an additional level */
861 ++count;
862 for (i = 0; i < count; i++)
863 while(*(p--) != '/' || *p == '/') /* count /\/+/ as one slash */
864 if (p == source) /* if we reach the first char pointer quit */
865 return TRUE;
866 ++p; /* since we do p-- in the while, we are pointing at
867 the char before the slash, so +1 */
869 len = p - source + 1; /* new length = end - start + 1 */
870 new = malloc(len + 1);
871 memcpy(new, source, len);
872 new[len] = '\0';
873 webkit_web_view_load_uri(webview, new);
874 free(new);
875 return TRUE;
878 gboolean
879 echo(const Arg *arg) {
880 int index = !arg->s ? 0 : arg->i & (~NoAutoHide);
882 if (index < Info || index > Error)
883 return TRUE;
885 set_widget_font_and_color(inputbox, urlboxfont[index], urlboxbgcolor[index], urlboxcolor[index]);
886 gtk_entry_set_text(GTK_ENTRY(inputbox), !arg->s ? "" : arg->s);
888 return TRUE;
891 gboolean
892 input(const Arg *arg) {
893 int pos = 0;
894 count = 0;
895 const char *url;
896 int index = Info;
897 Arg a;
899 /* if inputbox hidden, show it again */
900 if (!gtk_widget_get_visible(inputbox))
901 gtk_widget_set_visible(inputbox, TRUE);
903 update_state();
905 /* Set the colour and font back to the default, so that we don't still
906 * maintain a red colour from a warning from an end of search indicator,
907 * etc.
909 set_widget_font_and_color(inputbox, urlboxfont[index], urlboxbgcolor[index], urlboxcolor[index]);
911 if (arg->s[0] == '`' || arg->s[0] == '~') {
912 memset(followTarget, 0, 0);
913 strncpy(followTarget, arg->s[0] == '`' ? "current" : "new", 8);
914 a.i = Silent;
915 a.s = g_strdup("vimprobable_show_hints()");
916 script(&a);
919 /* to avoid things like :open URL :open URL2 or :open :open URL */
920 gtk_entry_set_text(GTK_ENTRY(inputbox), "");
921 gtk_editable_insert_text(GTK_EDITABLE(inputbox), arg->s, -1, &pos);
922 if (arg->i & InsertCurrentURL && (url = webkit_web_view_get_uri(webview)))
923 gtk_editable_insert_text(GTK_EDITABLE(inputbox), url, -1, &pos);
924 gtk_widget_grab_focus(inputbox);
925 gtk_editable_set_position(GTK_EDITABLE(inputbox), -1);
927 return TRUE;
930 gboolean
931 navigate(const Arg *arg) {
932 if (arg->i & NavigationForwardBack)
933 webkit_web_view_go_back_or_forward(webview, (arg->i == NavigationBack ? -1 : 1) * (count ? count : 1));
934 else if (arg->i & NavigationReloadActions)
935 (arg->i == NavigationReload ? webkit_web_view_reload : webkit_web_view_reload_bypass_cache)(webview);
936 else
937 webkit_web_view_stop_loading(webview);
938 return TRUE;
941 gboolean
942 number(const Arg *arg) {
943 const char *source = webkit_web_view_get_uri(webview);
944 char *uri, *p, *new;
945 int number, diff = (count ? count : 1) * (arg->i == Increment ? 1 : -1);
947 if (!source)
948 return TRUE;
949 uri = g_strdup_printf(source); /* copy string */
950 p =& uri[0];
951 while(*p != '\0') /* goto the end of the string */
952 ++p;
953 --p;
954 while(*p >= '0' && *p <= '9') /* go back until non number char is reached */
955 --p;
956 if (*(++p) == '\0') { /* if no numbers were found abort */
957 free(uri);
958 return TRUE;
960 number = atoi(p) + diff; /* apply diff on number */
961 *p = '\0';
962 new = g_strdup_printf("%s%d", uri, number); /* create new uri */
963 webkit_web_view_load_uri(webview, new);
964 g_free(new);
965 free(uri);
966 return TRUE;
969 gboolean
970 open_arg(const Arg *arg) {
971 char *argv[6];
972 char *s = arg->s, *p = NULL, *new;
973 Arg a = { .i = NavigationReload };
974 int len;
975 char *search_uri, *search_term;
977 if (embed) {
978 argv[0] = *args;
979 argv[1] = "-e";
980 argv[2] = winid;
981 argv[3] = arg->s;
982 argv[4] = NULL;
983 } else {
984 argv[0] = *args;
985 argv[1] = arg->s;
986 argv[2] = NULL;
989 if (!arg->s)
990 navigate(&a);
991 else if (arg->i == TargetCurrent) {
992 while(*s == ' ') /* strip leading whitespace */
993 ++s;
994 p = (s + strlen(s) - 1);
995 while(*p == ' ') /* strip trailing whitespace */
996 --p;
997 *(p + 1) = '\0';
998 len = strlen(s);
999 new = NULL, p = strchr(s, ' ');
1000 if (p) { /* check for search engines */
1001 *p = '\0';
1002 search_uri = find_uri_for_searchengine(s);
1003 if (search_uri != NULL) {
1004 search_term = soup_uri_encode(p+1, "&");
1005 new = g_strdup_printf(search_uri, search_term);
1006 g_free(search_term);
1008 *p = ' ';
1010 if (!new) {
1011 if (len > 3 && strstr(s, "://")) { /* valid url? */
1012 p = new = g_malloc(len + 1);
1013 while(*s != '\0') { /* strip whitespaces */
1014 if (*s != ' ')
1015 *(p++) = *s;
1016 ++s;
1018 *p = '\0';
1019 } else if (strcspn(s, "/") == 0 || strcspn(s, "./") == 0) { /* prepend "file://" */
1020 new = g_malloc(sizeof("file://") + len);
1021 strcpy(new, "file://");
1022 memcpy(&new[sizeof("file://") - 1], s, len + 1);
1023 } else if (p || !strchr(s, '.')) { /* whitespaces or no dot? */
1024 search_uri = find_uri_for_searchengine(defaultsearch);
1025 if (search_uri != NULL) {
1026 search_term = soup_uri_encode(s, "&");
1027 new = g_strdup_printf(search_uri, search_term);
1028 g_free(search_term);
1030 } else { /* prepend "http://" */
1031 new = g_malloc(sizeof("http://") + len);
1032 strcpy(new, "http://");
1033 memcpy(&new[sizeof("http://") - 1], s, len + 1);
1036 webkit_web_view_load_uri(webview, new);
1037 g_free(new);
1038 } else
1039 g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL);
1040 return TRUE;
1043 gboolean
1044 open_remembered(const Arg *arg)
1046 Arg a = {arg->i, rememberedURI};
1048 if (strcmp(rememberedURI, "")) {
1049 open_arg(&a);
1051 return TRUE;
1054 gboolean
1055 yank(const Arg *arg) {
1056 const char *url, *feedback;
1058 if (arg->i & SourceURL) {
1059 url = webkit_web_view_get_uri(webview);
1060 if (!url)
1061 return TRUE;
1062 feedback = g_strconcat("Yanked ", url, NULL);
1063 give_feedback(feedback);
1064 if (arg->i & ClipboardPrimary)
1065 gtk_clipboard_set_text(clipboards[0], url, -1);
1066 if (arg->i & ClipboardGTK)
1067 gtk_clipboard_set_text(clipboards[1], url, -1);
1068 } else
1069 webkit_web_view_copy_clipboard(webview);
1070 return TRUE;
1073 gboolean
1074 paste(const Arg *arg) {
1075 Arg a = { .i = arg->i & TargetNew, .s = NULL };
1077 /* If we're over a link, open it in a new target. */
1078 if (strlen(rememberedURI) > 0) {
1079 Arg new_target = { .i = TargetNew, .s = arg->s };
1080 open_arg(&new_target);
1081 return TRUE;
1084 if (arg->i & ClipboardPrimary)
1085 a.s = gtk_clipboard_wait_for_text(clipboards[0]);
1086 if (!a.s && arg->i & ClipboardGTK)
1087 a.s = gtk_clipboard_wait_for_text(clipboards[1]);
1088 if (a.s)
1089 open_arg(&a);
1090 return TRUE;
1093 gboolean
1094 quit(const Arg *arg) {
1095 FILE *f;
1096 const char *filename;
1097 const char *uri = webkit_web_view_get_uri(webview);
1098 if (uri != NULL) {
1099 /* write last URL into status file for recreation with "u" */
1100 filename = g_strdup_printf(CLOSED_URL_FILENAME);
1101 f = fopen(filename, "w");
1102 if (f != NULL) {
1103 fprintf(f, "%s", uri);
1104 fclose(f);
1107 gtk_main_quit();
1108 return TRUE;
1111 gboolean
1112 revive(const Arg *arg) {
1113 FILE *f;
1114 const char *filename;
1115 char buffer[512] = "";
1116 Arg a = { .i = TargetNew, .s = NULL };
1117 /* get the URL of the window which has been closed last */
1118 filename = g_strdup_printf(CLOSED_URL_FILENAME);
1119 f = fopen(filename, "r");
1120 if (f != NULL) {
1121 fgets(buffer, 512, f);
1122 fclose(f);
1124 if (strlen(buffer) > 0) {
1125 a.s = buffer;
1126 open_arg(&a);
1127 return TRUE;
1129 return FALSE;
1132 static
1133 gboolean print_frame(const Arg *arg)
1135 WebKitWebFrame *frame = webkit_web_view_get_main_frame(webview);
1136 webkit_web_frame_print (frame);
1137 return TRUE;
1140 gboolean
1141 search(const Arg *arg) {
1142 count = count ? count : 1;
1143 gboolean success, direction = arg->i & DirectionPrev;
1144 Arg a;
1146 if (arg->s) {
1147 free(search_handle);
1148 search_handle = g_strdup_printf(arg->s);
1150 if (!search_handle)
1151 return TRUE;
1152 if (arg->i & DirectionAbsolute)
1153 search_direction = direction;
1154 else
1155 direction ^= search_direction;
1156 do {
1157 success = webkit_web_view_search_text(webview, search_handle, arg->i & CaseSensitive, direction, FALSE);
1158 if (!success) {
1159 if (arg->i & Wrapping) {
1160 success = webkit_web_view_search_text(webview, search_handle, arg->i & CaseSensitive, direction, TRUE);
1161 if (success) {
1162 a.i = Warning;
1163 a.s = g_strdup_printf("search hit %s, continuing at %s",
1164 direction ? "BOTTOM" : "TOP",
1165 direction ? "TOP" : "BOTTOM");
1166 echo(&a);
1167 g_free(a.s);
1168 } else
1169 break;
1170 } else
1171 break;
1173 } while(--count);
1174 if (!success) {
1175 a.i = Error;
1176 a.s = g_strdup_printf("Pattern not found: %s", search_handle);
1177 echo(&a);
1178 g_free(a.s);
1180 return TRUE;
1183 gboolean
1184 set(const Arg *arg) {
1185 Arg a = { .i = Info | NoAutoHide };
1187 switch (arg->i) {
1188 case ModeNormal:
1189 if (search_handle) {
1190 search_handle = NULL;
1191 webkit_web_view_unmark_text_matches(webview);
1193 gtk_entry_set_text(GTK_ENTRY(inputbox), "");
1194 gtk_widget_grab_focus(GTK_WIDGET(webview));
1195 break;
1196 case ModePassThrough:
1197 a.s = g_strdup("-- PASS THROUGH --");
1198 echo(&a);
1199 g_free(a.s);
1200 break;
1201 case ModeSendKey:
1202 a.s = g_strdup("-- PASS TROUGH (next) --");
1203 echo(&a);
1204 g_free(a.s);
1205 break;
1206 case ModeInsert: /* should not be called manually but automatically */
1207 a.s = g_strdup("-- INSERT --");
1208 echo(&a);
1209 g_free(a.s);
1210 break;
1211 default:
1212 return TRUE;
1214 mode = arg->i;
1215 return TRUE;
1218 gchar*
1219 jsapi_ref_to_string(JSContextRef context, JSValueRef ref) {
1220 JSStringRef string_ref;
1221 gchar *string;
1222 size_t length;
1224 string_ref = JSValueToStringCopy(context, ref, NULL);
1225 length = JSStringGetMaximumUTF8CStringSize(string_ref);
1226 string = g_new(gchar, length);
1227 JSStringGetUTF8CString(string_ref, string, length);
1228 JSStringRelease(string_ref);
1229 return string;
1232 void
1233 jsapi_evaluate_script(const gchar *script, gchar **value, gchar **message) {
1234 WebKitWebFrame *frame = webkit_web_view_get_main_frame(webview);
1235 JSGlobalContextRef context = webkit_web_frame_get_global_context(frame);
1236 JSStringRef str;
1237 JSValueRef val, exception;
1239 str = JSStringCreateWithUTF8CString(script);
1240 val = JSEvaluateScript(context, str, JSContextGetGlobalObject(context), NULL, 0, &exception);
1241 JSStringRelease(str);
1242 if (!val)
1243 *message = jsapi_ref_to_string(context, exception);
1244 else
1245 *value = jsapi_ref_to_string(context, val);
1248 gboolean
1249 quickmark(const Arg *a) {
1250 int i, b;
1251 b = atoi(a->s);
1252 char *fn = g_strdup_printf(QUICKMARK_FILE);
1253 FILE *fp;
1254 fp = fopen(fn, "r");
1255 char buf[100];
1257 if (fp != NULL && b < 10) {
1258 for( i=0; i < b; ++i ) {
1259 if (feof(fp)) {
1260 break;
1262 fgets(buf, 100, fp);
1264 char *ptr = strrchr(buf, '\n');
1265 *ptr = '\0';
1266 Arg x = { .s = buf };
1267 if (strlen(buf))
1268 return open_arg(&x);
1269 else {
1270 x.i = Error;
1271 x.s = g_strdup_printf("Quickmark %d not defined", b);
1272 echo(&x);
1273 g_free(x.s);
1274 return false;
1276 } else { return false; }
1279 gboolean
1280 script(const Arg *arg) {
1281 gchar *value = NULL, *message = NULL;
1282 Arg a;
1284 if (!arg->s) {
1285 set_error("Missing argument.");
1286 return FALSE;
1288 jsapi_evaluate_script(arg->s, &value, &message);
1289 if (message) {
1290 set_error(message);
1291 if (arg->s)
1292 g_free(arg->s);
1293 return FALSE;
1295 if (arg->i != Silent && value) {
1296 a.i = arg->i;
1297 a.s = g_strdup(value);
1298 echo(&a);
1299 g_free(a.s);
1301 if (value) {
1302 if (strncmp(value, "fire;", 5) == 0) {
1303 count = 0;
1304 a.s = g_strconcat("vimprobable_fire(", (value + 5), ")", NULL);
1305 a.i = Silent;
1306 script(&a);
1307 } else if (strncmp(value, "open;", 5) == 0) {
1308 count = 0;
1309 a.i = ModeNormal;
1310 set(&a);
1311 if (strncmp(followTarget, "new", 3) == 0)
1312 a.i = TargetNew;
1313 else
1314 a.i = TargetCurrent;
1315 memset(followTarget, 0, 8);
1316 a.s = (value + 5);
1317 open_arg(&a);
1320 if (arg->s)
1321 g_free(arg->s);
1322 g_free(value);
1323 return TRUE;
1326 gboolean
1327 scroll(const Arg *arg) {
1328 GtkAdjustment *adjust = (arg->i & OrientationHoriz) ? adjust_h : adjust_v;
1329 int max = gtk_adjustment_get_upper(adjust) - gtk_adjustment_get_page_size(adjust);
1330 float val = gtk_adjustment_get_value(adjust) / max * 100;
1331 int direction = (arg->i & (1 << 2)) ? 1 : -1;
1333 if ((direction == 1 && val < 100) || (direction == -1 && val > 0)) {
1334 if (arg->i & ScrollMove)
1335 gtk_adjustment_set_value(adjust, gtk_adjustment_get_value(adjust) +
1336 direction * /* direction */
1337 ((arg->i & UnitLine || (arg->i & UnitBuffer && count)) ? (scrollstep * (count ? count : 1)) : (
1338 arg->i & UnitBuffer ? gtk_adjustment_get_page_size(adjust) / 2 :
1339 (count ? count : 1) * (gtk_adjustment_get_page_size(adjust) -
1340 (gtk_adjustment_get_page_size(adjust) > pagingkeep ? pagingkeep : 0)))));
1341 else
1342 gtk_adjustment_set_value(adjust,
1343 ((direction == 1) ? gtk_adjustment_get_upper : gtk_adjustment_get_lower)(adjust));
1344 update_state();
1346 return TRUE;
1349 gboolean
1350 zoom(const Arg *arg) {
1351 webkit_web_view_set_full_content_zoom(webview, (arg->i & ZoomFullContent) > 0);
1352 webkit_web_view_set_zoom_level(webview, (arg->i & ZoomOut) ?
1353 webkit_web_view_get_zoom_level(webview) +
1354 (((float)(count ? count : 1)) * (arg->i & (1 << 1) ? 1.0 : -1.0) * zoomstep) :
1355 (count ? (float)count / 100.0 : 1.0));
1356 return TRUE;
1359 gboolean
1360 fake_key_event(const Arg *a) {
1361 if(!embed) {
1362 return FALSE;
1364 Arg err;
1365 err.i = Error;
1366 Display *xdpy;
1367 if ( (xdpy = XOpenDisplay(NULL)) == NULL ) {
1368 err.s = g_strdup("Couldn't find the XDisplay.");
1369 echo(&err);
1370 g_free(err.s);
1371 return FALSE;
1374 XKeyEvent xk;
1375 xk.display = xdpy;
1376 xk.subwindow = None;
1377 xk.time = CurrentTime;
1378 xk.same_screen = True;
1379 xk.x = xk.y = xk.x_root = xk.y_root = 1;
1380 xk.window = embed;
1381 xk.state = a->i;
1383 if( ! a->s ) {
1384 err.s = g_strdup("Zero pointer as argument! Check your config.h");
1385 echo(&err);
1386 g_free(err.s);
1387 return FALSE;
1390 KeySym keysym;
1391 if( (keysym = XStringToKeysym(a->s)) == NoSymbol ) {
1392 err.s = g_strdup_printf("Couldn't translate %s to keysym", a->s );
1393 echo(&err);
1394 g_free(err.s);
1395 return FALSE;
1398 if( (xk.keycode = XKeysymToKeycode(xdpy, keysym)) == NoSymbol ) {
1399 err.s = g_strdup("Couldn't translate keysym to keycode");
1400 echo(&err);
1401 g_free(err.s);
1402 return FALSE;
1405 xk.type = KeyPress;
1406 if( !XSendEvent(xdpy, embed, True, KeyPressMask, (XEvent *)&xk) ) {
1407 err.s = g_strdup("XSendEvent failed");
1408 echo(&err);
1409 g_free(err.s);
1410 return FALSE;
1412 XFlush(xdpy);
1414 return TRUE;
1418 gboolean
1419 commandhistoryfetch(const Arg *arg) {
1420 if (arg->i == DirectionPrev) {
1421 commandpointer--;
1422 if (commandpointer < 0)
1423 commandpointer = maxcommands - 1;
1424 } else {
1425 commandpointer++;
1426 if (commandpointer == COMMANDHISTSIZE || commandpointer == maxcommands)
1427 commandpointer = 0;
1430 if (commandpointer < 0)
1431 return FALSE;
1433 gtk_entry_set_text(GTK_ENTRY(inputbox), commandhistory[commandpointer ]);
1434 gtk_editable_set_position(GTK_EDITABLE(inputbox), -1);
1435 return TRUE;
1439 gboolean
1440 bookmark(const Arg *arg) {
1441 FILE *f;
1442 const char *filename;
1443 const char *uri = webkit_web_view_get_uri(webview);
1444 const char *title = webkit_web_view_get_title(webview);
1445 filename = g_strdup_printf(BOOKMARKS_STORAGE_FILENAME);
1446 f = fopen(filename, "a");
1447 if (uri == NULL || strlen(uri) == 0) {
1448 set_error("No URI found to bookmark.");
1449 return FALSE;
1451 if (f != NULL) {
1452 fprintf(f, "%s", uri);
1453 if (title != NULL) {
1454 fprintf(f, "%s", " ");
1455 fprintf(f, "%s", title);
1457 if (arg->s && strlen(arg->s)) {
1458 build_taglist(arg, f);
1460 fprintf(f, "%s", "\n");
1461 fclose(f);
1462 give_feedback( "Bookmark saved" );
1463 return TRUE;
1464 } else {
1465 set_error("Bookmarks file not found.");
1466 return FALSE;
1470 gboolean
1471 history() {
1472 FILE *f;
1473 const char *filename;
1474 const char *uri = webkit_web_view_get_uri(webview);
1475 const char *title = webkit_web_view_get_title(webview);
1476 char *entry, buffer[512], *new;
1477 int n, i = 0;
1478 gboolean finished = FALSE;
1479 if (uri != NULL) {
1480 if (title != NULL) {
1481 entry = malloc((strlen(uri) + strlen(title) + 2) * sizeof(char));
1482 memset(entry, 0, strlen(uri) + strlen(title) + 2);
1483 } else {
1484 entry = malloc((strlen(uri) + 1) * sizeof(char));
1485 memset(entry, 0, strlen(uri) + 1);
1487 if (entry != NULL) {
1488 strncpy(entry, uri, strlen(uri));
1489 if (title != NULL) {
1490 strncat(entry, " ", 1);
1491 strncat(entry, title, strlen(title));
1493 n = strlen(entry);
1494 filename = g_strdup_printf(HISTORY_STORAGE_FILENAME);
1495 f = fopen(filename, "r");
1496 if (f != NULL) {
1497 new = (char *)malloc(HISTORY_MAX_ENTRIES * 512 * sizeof(char) + 1);
1498 if (new != NULL) {
1499 memset(new, 0, HISTORY_MAX_ENTRIES * 512 * sizeof(char) + 1);
1500 /* newest entries go on top */
1501 strncpy(new, entry, strlen(entry));
1502 strncat(new, "\n", 1);
1503 /* retain at most HISTORY_MAX_ENTIRES - 1 old entries */
1504 while (finished != TRUE) {
1505 if ((char *)NULL == fgets(buffer, 512, f)) {
1506 /* check if end of file was reached / error occured */
1507 if (!feof(f)) {
1508 break;
1510 /* end of file reached */
1511 finished = TRUE;
1512 continue;
1514 /* compare line (-1 because of newline character) */
1515 if (n != strlen(buffer) - 1 || strncmp(entry, buffer, n) != 0) {
1516 /* if the URI is already in history; we put it on top and skip it here */
1517 strncat(new, buffer, 512);
1518 i++;
1520 if ((i + 1) >= HISTORY_MAX_ENTRIES) {
1521 break;
1524 fclose(f);
1526 f = fopen(filename, "w");
1527 if (f != NULL) {
1528 fprintf(f, "%s", new);
1529 fclose(f);
1531 if (new != NULL) {
1532 free(new);
1533 new = NULL;
1537 if (entry != NULL) {
1538 free(entry);
1539 entry = NULL;
1542 return TRUE;
1545 static gboolean
1546 view_source(const Arg * arg) {
1547 gboolean current_mode = webkit_web_view_get_view_source_mode(webview);
1548 webkit_web_view_set_view_source_mode(webview, !current_mode);
1549 webkit_web_view_reload(webview);
1550 return TRUE;
1553 static gboolean
1554 focus_input(const Arg *arg) {
1555 static Arg a;
1557 a.s = g_strconcat("vimprobable_focus_input()", NULL);
1558 a.i = Silent;
1559 script(&a);
1560 update_state();
1561 return TRUE;
1564 static gboolean
1565 browser_settings(const Arg *arg) {
1566 char line[255];
1567 if (!arg->s) {
1568 set_error("Missing argument.");
1569 return FALSE;
1571 strncpy(line, arg->s, 254);
1572 if (process_set_line(line))
1573 return TRUE;
1574 else {
1575 set_error("Invalid setting.");
1576 return FALSE;
1580 char *
1581 search_word(int whichword) {
1582 int k = 0;
1583 static char word[240];
1584 char *c = my_pair.line;
1586 while (isspace(*c) && *c)
1587 c++;
1589 switch (whichword) {
1590 case 0:
1591 while (*c && !isspace (*c) && *c != '=' && k < 240) {
1592 word[k++] = *c;
1593 c++;
1595 word[k] = '\0';
1596 strncpy(my_pair.what, word, 20);
1597 break;
1598 case 1:
1599 while (*c && k < 240) {
1600 word[k++] = *c;
1601 c++;
1603 word[k] = '\0';
1604 strncpy(my_pair.value, word, 240);
1605 break;
1608 return c;
1611 static gboolean
1612 process_set_line(char *line) {
1613 char *c;
1614 int listlen, i;
1615 gboolean boolval;
1616 WebKitWebSettings *settings;
1618 settings = webkit_web_view_get_settings(webview);
1619 my_pair.line = line;
1620 c = search_word(0);
1621 if (!strlen(my_pair.what))
1622 return FALSE;
1624 while (isspace(*c) && *c)
1625 c++;
1627 if (*c == ':' || *c == '=')
1628 c++;
1630 my_pair.line = c;
1631 c = search_word(1);
1633 listlen = LENGTH(browsersettings);
1634 for (i = 0; i < listlen; i++) {
1635 if (strlen(browsersettings[i].name) == strlen(my_pair.what) && strncmp(browsersettings[i].name, my_pair.what, strlen(my_pair.what)) == 0) {
1636 /* mandatory argument not provided */
1637 if (strlen(my_pair.value) == 0)
1638 return FALSE;
1639 /* process qmark? */
1640 if (strlen(my_pair.what) == 5 && strncmp("qmark", my_pair.what, 5) == 0) {
1641 return (process_save_qmark(my_pair.value, webview));
1643 /* interpret boolean values */
1644 if (browsersettings[i].boolval) {
1645 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) {
1646 boolval = TRUE;
1647 } 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) {
1648 boolval = FALSE;
1649 } else {
1650 return FALSE;
1652 } else if (browsersettings[i].colourval) {
1653 /* interpret as hexadecimal colour */
1654 if (!parse_colour(my_pair.value)) {
1655 return FALSE;
1658 if (browsersettings[i].var != NULL) {
1659 /* write value into internal variable */
1660 /*if (browsersettings[i].intval) {
1661 browsersettings[i].var = atoi(my_pair.value);
1662 } else {*/
1663 strncpy(browsersettings[i].var, my_pair.value, MAX_SETTING_SIZE);
1664 if (strlen(my_pair.value) > MAX_SETTING_SIZE - 1) {
1665 /* in this case, \0 will not have been copied */
1666 browsersettings[i].var[MAX_SETTING_SIZE - 1] = '\0';
1667 /* in case this string is also used for a webkit setting, make sure it's consistent */
1668 my_pair.value[MAX_SETTING_SIZE - 1] = '\0';
1669 give_feedback("String too long; automatically truncated!");
1671 /*}*/
1673 if (strlen(browsersettings[i].webkit) > 0) {
1674 /* activate appropriate webkit setting */
1675 if (browsersettings[i].boolval) {
1676 g_object_set((GObject*)settings, browsersettings[i].webkit, boolval, NULL);
1677 } else if (browsersettings[i].intval) {
1678 g_object_set((GObject*)settings, browsersettings[i].webkit, atoi(my_pair.value), NULL);
1679 } else {
1680 g_object_set((GObject*)settings, browsersettings[i].webkit, my_pair.value, NULL);
1682 webkit_web_view_set_settings(webview, settings);
1685 /* process acceptlanguage*/
1686 if (strlen(my_pair.what) == 14 && strncmp("acceptlanguage", my_pair.what, 14) == 0) {
1687 g_object_set(G_OBJECT(session), "accept-language", acceptlanguage, NULL);
1690 /* toggle proxy usage? */
1691 if (strlen(my_pair.what) == 5 && strncmp("proxy", my_pair.what, 5) == 0) {
1692 toggle_proxy(boolval);
1695 /* Toggle scrollbars. */
1696 if (strlen(my_pair.what) == 10 && strncmp("scrollbars", my_pair.what, 10) == 0)
1697 toggle_scrollbars(boolval);
1699 /* Toggle widgets */
1700 if (strlen(my_pair.what) == 9 && strncmp("statusbar", my_pair.what, 9) == 0)
1701 gtk_widget_set_visible(GTK_WIDGET(statusbar), boolval);
1702 if (strlen(my_pair.what) == 8 && strncmp("inputbox", my_pair.what, 8) == 0)
1703 gtk_widget_set_visible(inputbox, boolval);
1705 /* case sensitivity of completion */
1706 if (strlen(my_pair.what) == 14 && strncmp("completioncase", my_pair.what, 14) == 0)
1707 complete_case_sensitive = boolval;
1709 /* reload page? */
1710 if (browsersettings[i].reload)
1711 webkit_web_view_reload(webview);
1712 return TRUE;
1715 return FALSE;
1718 gboolean
1719 process_line(char *line) {
1720 char *c = line;
1721 int i;
1722 size_t len, length = strlen(line);
1723 gboolean found = FALSE, success = FALSE;
1724 Arg a;
1726 while (isspace(*c))
1727 c++;
1728 /* Ignore blank lines. */
1729 if (c[0] == '\0')
1730 return TRUE;
1731 for (i = 0; i < LENGTH(commands); i++) {
1732 if (commands[i].cmd == NULL)
1733 break;
1734 len = strlen(commands[i].cmd);
1735 if (length >= len && !strncmp(c, commands[i].cmd, len) && (c[len] == ' ' || !c[len])) {
1736 found = TRUE;
1737 a.i = commands[i].arg.i;
1738 a.s = length > len + 1 ? &c[len + 1] : commands[i].arg.s;
1739 success = commands[i].func(&a);
1740 break;
1743 save_command_history(c);
1744 if (!found) {
1745 a.i = Error;
1746 a.s = g_strdup_printf("Not a browser command: %s", c);
1747 echo(&a);
1748 } else if (!success) {
1749 a.i = Error;
1750 if (error_msg != NULL) {
1751 a.s = g_strdup_printf("%s", error_msg);
1752 g_free(error_msg);
1753 error_msg = NULL;
1754 } else {
1755 a.s = g_strdup_printf("Unknown error. Please file a bug report!");
1757 echo(&a);
1758 g_free(a.s);
1760 return success;
1763 static gboolean
1764 search_tag(const Arg * a) {
1765 FILE *f;
1766 const char *filename;
1767 const char *tag = a->s;
1768 char s[BUFFERSIZE], foundtag[40], url[BUFFERSIZE];
1769 int t, i, intag, k;
1771 if (!tag) {
1772 /* The user must give us something to load up. */
1773 set_error("Bookmark tag required with this option.");
1774 return FALSE;
1777 if (strlen(tag) > MAXTAGSIZE) {
1778 set_error("Tag too long.");
1779 return FALSE;
1782 filename = g_strdup_printf(BOOKMARKS_STORAGE_FILENAME);
1783 f = fopen(filename, "r");
1784 if (f == NULL) {
1785 set_error("Couldn't open bookmarks file.");
1786 return FALSE;
1788 while (fgets(s, BUFFERSIZE-1, f)) {
1789 intag = 0;
1790 t = strlen(s) - 1;
1791 while (isspace(s[t]))
1792 t--;
1793 if (s[t] != ']') continue;
1794 while (t > 0) {
1795 if (s[t] == ']') {
1796 if (!intag)
1797 intag = t;
1798 else
1799 intag = 0;
1800 } else {
1801 if (s[t] == '[') {
1802 if (intag) {
1803 i = 0;
1804 k = t + 1;
1805 while (k < intag)
1806 foundtag[i++] = s[k++];
1807 foundtag[i] = '\0';
1808 /* foundtag now contains the tag */
1809 if (strlen(foundtag) < MAXTAGSIZE && strcmp(tag, foundtag) == 0) {
1810 i = 0;
1811 while (isspace(s[i])) i++;
1812 k = 0;
1813 while (s[i] && !isspace(s[i])) url[k++] = s[i++];
1814 url[k] = '\0';
1815 Arg x = { .i = TargetNew, .s = url };
1816 open_arg(&x);
1819 intag = 0;
1822 t--;
1825 return TRUE;
1828 void
1829 toggle_proxy(gboolean onoff) {
1830 SoupURI *proxy_uri;
1831 char *filename, *new;
1832 int len;
1834 if (onoff == FALSE) {
1835 g_object_set(session, "proxy-uri", NULL, NULL);
1836 give_feedback("Proxy deactivated");
1837 } else {
1838 filename = (char *)g_getenv("http_proxy");
1840 /* Fallthrough to checking HTTP_PROXY as well, since this can also be
1841 * defined.
1843 if (filename == NULL)
1844 filename = (char *)g_getenv("HTTP_PROXY");
1846 if (filename != NULL && 0 < (len = strlen(filename))) {
1847 if (strstr(filename, "://") == NULL) {
1848 /* prepend http:// */
1849 new = g_malloc(sizeof("http://") + len);
1850 strcpy(new, "http://");
1851 memcpy(&new[sizeof("http://") - 1], filename, len + 1);
1852 proxy_uri = soup_uri_new(new);
1853 } else {
1854 proxy_uri = soup_uri_new(filename);
1856 g_object_set(session, "proxy-uri", proxy_uri, NULL);
1857 give_feedback("Proxy activated");
1862 void
1863 toggle_scrollbars(gboolean onoff) {
1864 if (onoff == TRUE) {
1865 adjust_h = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(viewport));
1866 adjust_v = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(viewport));
1868 else {
1869 adjust_v = gtk_range_get_adjustment(GTK_RANGE(scroll_v));
1870 adjust_h = gtk_range_get_adjustment(GTK_RANGE(scroll_h));
1872 gtk_widget_set_scroll_adjustments (GTK_WIDGET(webview), adjust_h, adjust_v);
1874 return;
1877 void
1878 update_url(const char *uri) {
1879 gboolean ssl = g_str_has_prefix(uri, "https://");
1880 GdkColor color;
1881 #ifdef ENABLE_HISTORY_INDICATOR
1882 char before[] = " [";
1883 char after[] = "]";
1884 gboolean back = webkit_web_view_can_go_back(webview);
1885 gboolean fwd = webkit_web_view_can_go_forward(webview);
1887 if (!back && !fwd)
1888 before[0] = after[0] = '\0';
1889 #endif
1890 gtk_label_set_markup(GTK_LABEL(status_url), g_markup_printf_escaped(
1891 #ifdef ENABLE_HISTORY_INDICATOR
1892 "<span font=\"%s\">%s%s%s%s%s</span>", statusfont, uri,
1893 before, back ? "+" : "", fwd ? "-" : "", after
1894 #else
1895 "<span font=\"%s\">%s</span>", statusfont, uri
1896 #endif
1898 gdk_color_parse(ssl ? sslbgcolor : statusbgcolor, &color);
1899 gtk_widget_modify_bg(eventbox, GTK_STATE_NORMAL, &color);
1900 gdk_color_parse(ssl ? sslcolor : statuscolor, &color);
1901 gtk_widget_modify_fg(GTK_WIDGET(status_url), GTK_STATE_NORMAL, &color);
1902 gtk_widget_modify_fg(GTK_WIDGET(status_state), GTK_STATE_NORMAL, &color);
1905 void
1906 update_state() {
1907 char *markup;
1908 int download_count = g_list_length(activeDownloads);
1909 GString *status = g_string_new("");
1911 /* construct the status line */
1913 /* count, modkey and input buffer */
1914 g_string_append_printf(status, "%.0d", count);
1915 if (current_modkey) g_string_append_c(status, current_modkey);
1917 /* the number of active downloads */
1918 if (activeDownloads) {
1919 g_string_append_printf(status, " %d active %s", download_count,
1920 (download_count == 1) ? "download" : "downloads");
1923 #ifdef ENABLE_WGET_PROGRESS_BAR
1924 /* the progressbar */
1926 int progress = -1;
1927 char progressbar[progressbartick + 1];
1929 if (activeDownloads) {
1930 progress = 0;
1931 GList *ptr;
1933 for (ptr = activeDownloads; ptr; ptr = g_list_next(ptr)) {
1934 progress += 100 * webkit_download_get_progress(ptr->data);
1937 progress /= download_count;
1939 } else if (webkit_web_view_get_load_status(webview) != WEBKIT_LOAD_FINISHED
1940 && webkit_web_view_get_load_status(webview) != WEBKIT_LOAD_FAILED) {
1942 progress = webkit_web_view_get_progress(webview) * 100;
1945 if (progress >= 0) {
1946 ascii_bar(progressbartick, progress * progressbartick / 100, progressbar);
1947 g_string_append_printf(status, " %c%s%c",
1948 progressborderleft, progressbar, progressborderright);
1951 #endif
1953 /* and the current scroll position */
1955 int max = gtk_adjustment_get_upper(adjust_v) - gtk_adjustment_get_page_size(adjust_v);
1956 int val = (int)(gtk_adjustment_get_value(adjust_v) / max * 100);
1958 if (max == 0)
1959 g_string_append(status, " All");
1960 else if (val == 0)
1961 g_string_append(status, " Top");
1962 else if (val == 100)
1963 g_string_append(status, " Bot");
1964 else
1965 g_string_append_printf(status, " %d%%", val);
1969 markup = g_markup_printf_escaped("<span font=\"%s\">%s</span>", statusfont, status->str);
1970 gtk_label_set_markup(GTK_LABEL(status_state), markup);
1972 g_string_free(status, TRUE);
1975 void
1976 setup_modkeys() {
1977 unsigned int i;
1978 modkeys = calloc(LENGTH(keys) + 1, sizeof(char));
1979 char *ptr = modkeys;
1981 for (i = 0; i < LENGTH(keys); i++)
1982 if (keys[i].modkey && !strchr(modkeys, keys[i].modkey))
1983 *(ptr++) = keys[i].modkey;
1984 modkeys = realloc(modkeys, &ptr[0] - &modkeys[0] + 1);
1987 void
1988 setup_gui() {
1989 scroll_h = GTK_SCROLLBAR(gtk_hscrollbar_new(NULL));
1990 scroll_v = GTK_SCROLLBAR(gtk_vscrollbar_new(NULL));
1991 adjust_h = gtk_range_get_adjustment(GTK_RANGE(scroll_h));
1992 adjust_v = gtk_range_get_adjustment(GTK_RANGE(scroll_v));
1993 if (embed) {
1994 window = GTK_WINDOW(gtk_plug_new(embed));
1995 } else {
1996 window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
1997 gtk_window_set_wmclass(GTK_WINDOW(window), "vimprobable2", "Vimprobable2");
1999 gtk_window_set_default_size(GTK_WINDOW(window), 640, 480);
2000 box = GTK_BOX(gtk_vbox_new(FALSE, 0));
2001 inputbox = gtk_entry_new();
2002 webview = (WebKitWebView*)webkit_web_view_new();
2003 statusbar = GTK_BOX(gtk_hbox_new(FALSE, 0));
2004 eventbox = gtk_event_box_new();
2005 status_url = gtk_label_new(NULL);
2006 status_state = gtk_label_new(NULL);
2007 GdkColor bg;
2008 PangoFontDescription *font;
2009 GdkGeometry hints = { 1, 1 };
2010 inspector = webkit_web_view_get_inspector(WEBKIT_WEB_VIEW(webview));
2012 clipboards[0] = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
2013 clipboards[1] = gtk_clipboard_get(GDK_NONE);
2014 setup_settings();
2015 gdk_color_parse(statusbgcolor, &bg);
2016 gtk_widget_modify_bg(eventbox, GTK_STATE_NORMAL, &bg);
2017 gtk_widget_set_name(GTK_WIDGET(window), "Vimprobable2");
2018 gtk_window_set_geometry_hints(window, NULL, &hints, GDK_HINT_MIN_SIZE);
2020 #ifdef DISABLE_SCROLLBAR
2021 viewport = gtk_scrolled_window_new(NULL, NULL);
2022 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(viewport), GTK_POLICY_NEVER, GTK_POLICY_NEVER);
2023 #else
2024 /* Ensure we still see scrollbars. */
2025 GtkWidget *viewport = gtk_scrolled_window_new(adjust_h, adjust_v);
2026 #endif
2028 setup_signals();
2029 gtk_container_add(GTK_CONTAINER(viewport), GTK_WIDGET(webview));
2031 /* Ensure we set the scroll adjustments now, so that if we're not drawing
2032 * titlebars, we can still scroll.
2034 gtk_widget_set_scroll_adjustments(GTK_WIDGET(webview), adjust_h, adjust_v);
2036 font = pango_font_description_from_string(urlboxfont[0]);
2037 gtk_widget_modify_font(GTK_WIDGET(inputbox), font);
2038 pango_font_description_free(font);
2039 gtk_entry_set_inner_border(GTK_ENTRY(inputbox), NULL);
2040 gtk_misc_set_alignment(GTK_MISC(status_url), 0.0, 0.0);
2041 gtk_misc_set_alignment(GTK_MISC(status_state), 1.0, 0.0);
2042 gtk_box_pack_start(statusbar, status_url, TRUE, TRUE, 2);
2043 gtk_box_pack_start(statusbar, status_state, FALSE, FALSE, 2);
2044 gtk_container_add(GTK_CONTAINER(eventbox), GTK_WIDGET(statusbar));
2045 gtk_box_pack_start(box, viewport, TRUE, TRUE, 0);
2046 gtk_box_pack_start(box, eventbox, FALSE, FALSE, 0);
2047 gtk_entry_set_has_frame(GTK_ENTRY(inputbox), FALSE);
2048 gtk_box_pack_end(box, inputbox, FALSE, FALSE, 0);
2049 gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(box));
2050 gtk_widget_grab_focus(GTK_WIDGET(webview));
2051 gtk_widget_show_all(GTK_WIDGET(window));
2054 void
2055 setup_settings() {
2056 WebKitWebSettings *settings = (WebKitWebSettings*)webkit_web_settings_new();
2057 SoupURI *proxy_uri;
2058 char *filename, *new;
2059 int len;
2061 session = webkit_get_default_session();
2062 g_object_set(G_OBJECT(settings), "default-font-size", DEFAULT_FONT_SIZE, NULL);
2063 g_object_set(G_OBJECT(settings), "enable-scripts", enablePlugins, NULL);
2064 g_object_set(G_OBJECT(settings), "enable-plugins", enablePlugins, NULL);
2065 g_object_set(G_OBJECT(settings), "enable-java-applet", enableJava, NULL);
2066 g_object_set(G_OBJECT(settings), "enable-page-cache", enablePagecache, NULL);
2067 filename = g_strdup_printf(USER_STYLESHEET);
2068 filename = g_strdup_printf("file://%s", filename);
2069 g_object_set(G_OBJECT(settings), "user-stylesheet-uri", filename, NULL);
2070 g_object_set(G_OBJECT(settings), "user-agent", useragent, NULL);
2071 g_object_get(G_OBJECT(settings), "zoom-step", &zoomstep, NULL);
2072 webkit_web_view_set_settings(webview, settings);
2074 /* proxy */
2075 if (use_proxy == TRUE) {
2076 filename = (char *)g_getenv("http_proxy");
2077 if (filename != NULL && 0 < (len = strlen(filename))) {
2078 if (strstr(filename, "://") == NULL) {
2079 /* prepend http:// */
2080 new = g_malloc(sizeof("http://") + len);
2081 strcpy(new, "http://");
2082 memcpy(&new[sizeof("http://") - 1], filename, len + 1);
2083 proxy_uri = soup_uri_new(new);
2084 } else {
2085 proxy_uri = soup_uri_new(filename);
2087 g_object_set(session, "proxy-uri", proxy_uri, NULL);
2092 void
2093 setup_signals() {
2094 #ifdef ENABLE_COOKIE_SUPPORT
2095 /* Headers. */
2096 g_signal_connect_after(G_OBJECT(session), "request-started", G_CALLBACK(new_generic_request), NULL);
2097 #endif
2098 /* Accept-language header */
2099 g_object_set(G_OBJECT(session), "accept-language", acceptlanguage, NULL);
2100 /* window */
2101 g_object_connect(G_OBJECT(window),
2102 "signal::destroy", G_CALLBACK(window_destroyed_cb), NULL,
2103 NULL);
2104 /* webview */
2105 g_object_connect(G_OBJECT(webview),
2106 "signal::title-changed", G_CALLBACK(webview_title_changed_cb), NULL,
2107 "signal::load-progress-changed", G_CALLBACK(webview_progress_changed_cb), NULL,
2108 "signal::load-committed", G_CALLBACK(webview_load_committed_cb), NULL,
2109 "signal::load-finished", G_CALLBACK(webview_load_finished_cb), NULL,
2110 "signal::navigation-policy-decision-requested", G_CALLBACK(webview_navigation_cb), NULL,
2111 "signal::new-window-policy-decision-requested", G_CALLBACK(webview_new_window_cb), NULL,
2112 "signal::mime-type-policy-decision-requested", G_CALLBACK(webview_mimetype_cb), NULL,
2113 "signal::download-requested", G_CALLBACK(webview_download_cb), NULL,
2114 "signal::key-press-event", G_CALLBACK(webview_keypress_cb), NULL,
2115 "signal::hovering-over-link", G_CALLBACK(webview_hoverlink_cb), NULL,
2116 "signal::console-message", G_CALLBACK(webview_console_cb), NULL,
2117 "signal::create-web-view", G_CALLBACK(webview_open_in_new_window_cb), NULL,
2118 "signal::event", G_CALLBACK(notify_event_cb), NULL,
2119 NULL);
2120 /* webview adjustment */
2121 g_object_connect(G_OBJECT(adjust_v),
2122 "signal::value-changed", G_CALLBACK(webview_scroll_cb), NULL,
2123 NULL);
2124 /* inputbox */
2125 g_object_connect(G_OBJECT(inputbox),
2126 "signal::activate", G_CALLBACK(inputbox_activate_cb), NULL,
2127 "signal::key-press-event", G_CALLBACK(inputbox_keypress_cb), NULL,
2128 "signal::key-release-event", G_CALLBACK(inputbox_keyrelease_cb), NULL,
2129 #ifdef ENABLE_INCREMENTAL_SEARCH
2130 "signal::changed", G_CALLBACK(inputbox_changed_cb), NULL,
2131 #endif
2132 NULL);
2133 /* inspector */
2134 g_signal_connect(G_OBJECT(inspector),
2135 "inspect-web-view", G_CALLBACK(inspector_inspect_web_view_cb), NULL);
2138 #ifdef ENABLE_COOKIE_SUPPORT
2139 void
2140 setup_cookies()
2142 if (file_cookie_jar)
2143 g_object_unref(file_cookie_jar);
2145 if (session_cookie_jar)
2146 g_object_unref(session_cookie_jar);
2148 session_cookie_jar = soup_cookie_jar_new();
2150 cookie_store = g_strdup_printf(COOKIES_STORAGE_FILENAME);
2152 load_all_cookies();
2154 g_signal_connect(G_OBJECT(file_cookie_jar), "changed",
2155 G_CALLBACK(update_cookie_jar), NULL);
2157 return;
2160 /* TA: XXX - we should be using this callback for any header-requests we
2161 * receive (hence the name "new_generic_request" -- but for now, its use
2162 * is limited to handling cookies.
2164 void
2165 new_generic_request(SoupSession *session, SoupMessage *soup_msg, gpointer unused) {
2166 SoupMessageHeaders *soup_msg_h;
2167 SoupURI *uri;
2168 const char *cookie_str;
2170 soup_msg_h = soup_msg->request_headers;
2171 soup_message_headers_remove(soup_msg_h, "Cookie");
2172 uri = soup_message_get_uri(soup_msg);
2173 if( (cookie_str = get_cookies(uri)) )
2174 soup_message_headers_append(soup_msg_h, "Cookie", cookie_str);
2176 g_signal_connect_after(G_OBJECT(soup_msg), "got-headers", G_CALLBACK(handle_cookie_request), NULL);
2178 return;
2181 const char *
2182 get_cookies(SoupURI *soup_uri) {
2183 const char *cookie_str;
2185 cookie_str = soup_cookie_jar_get_cookies(file_cookie_jar, soup_uri, TRUE);
2187 return cookie_str;
2190 void
2191 handle_cookie_request(SoupMessage *soup_msg, gpointer unused)
2193 GSList *resp_cookie = NULL;
2194 SoupCookie *cookie;
2196 for(resp_cookie = soup_cookies_from_response(soup_msg);
2197 resp_cookie;
2198 resp_cookie = g_slist_next(resp_cookie))
2200 SoupDate *soup_date;
2201 cookie = soup_cookie_copy((SoupCookie *)resp_cookie->data);
2203 if (cookie_timeout && cookie->expires == NULL) {
2204 soup_date = soup_date_new_from_time_t(time(NULL) + cookie_timeout * 10);
2205 soup_cookie_set_expires(cookie, soup_date);
2207 soup_cookie_jar_add_cookie(file_cookie_jar, cookie);
2210 return;
2213 void
2214 update_cookie_jar(SoupCookieJar *jar, SoupCookie *old, SoupCookie *new)
2216 if (!new) {
2217 /* Nothing to do. */
2218 return;
2221 SoupCookie *copy;
2222 copy = soup_cookie_copy(new);
2224 soup_cookie_jar_add_cookie(session_cookie_jar, copy);
2226 return;
2229 void
2230 load_all_cookies(void)
2232 file_cookie_jar = soup_cookie_jar_text_new(cookie_store, COOKIES_STORAGE_READONLY);
2234 /* Put them back in the session store. */
2235 GSList *cookies_from_file = soup_cookie_jar_all_cookies(file_cookie_jar);
2237 for (; cookies_from_file;
2238 cookies_from_file = cookies_from_file->next)
2240 soup_cookie_jar_add_cookie(session_cookie_jar, cookies_from_file->data);
2243 soup_cookies_free(cookies_from_file);
2245 return;
2248 #endif
2250 void
2251 mop_up(void) {
2252 /* Free up any nasty globals before exiting. */
2253 #ifdef ENABLE_COOKIE_SUPPORT
2254 if (cookie_store)
2255 g_free(cookie_store);
2256 #endif
2257 return;
2261 main(int argc, char *argv[]) {
2262 static Arg a;
2263 static char url[256] = "";
2264 static gboolean ver = false;
2265 static gboolean configfile_exists = FALSE;
2266 static const char *cfile = NULL;
2267 char *searchengines_file;
2268 static GOptionEntry opts[] = {
2269 { "version", 'v', 0, G_OPTION_ARG_NONE, &ver, "print version", NULL },
2270 { "embed", 'e', 0, G_OPTION_ARG_STRING, &winid, "embedded", NULL },
2271 { "configfile", 'c', 0, G_OPTION_ARG_STRING, &cfile, "config file", NULL },
2272 { NULL }
2274 static GError *err;
2275 args = argv;
2277 /* command line argument: version */
2278 if (!gtk_init_with_args(&argc, &argv, "[<uri>]", opts, NULL, &err)) {
2279 g_printerr("can't init gtk: %s\n", err->message);
2280 g_error_free(err);
2281 return EXIT_FAILURE;
2284 if (ver) {
2285 printf("%s\n", INTERNAL_VERSION);
2286 return EXIT_SUCCESS;
2289 if (cfile)
2290 configfile = g_strdup_printf(cfile);
2291 else
2292 configfile = g_strdup_printf(RCFILE);
2294 if (!g_thread_supported())
2295 g_thread_init(NULL);
2297 if (winid)
2298 embed = atoi(winid);
2300 setup_modkeys();
2301 make_keyslist();
2302 setup_gui();
2303 #ifdef ENABLE_COOKIE_SUPPORT
2304 setup_cookies();
2305 #endif
2307 /* Check if the specified file exists. */
2308 /* And only warn the user, if they explicitly asked for a config on the
2309 * command line.
2311 if (!(access(configfile, F_OK) == 0) && cfile) {
2312 char *feedback_str;
2314 feedback_str = g_strdup_printf("Config file '%s' doesn't exist", cfile);
2315 give_feedback(feedback_str);
2316 } else if ((access(configfile, F_OK) == 0))
2317 configfile_exists = true;
2319 /* read config file */
2320 /* But only report errors if we failed, and the file existed. */
2321 if (!read_rcfile(configfile) && configfile_exists) {
2322 a.i = Error;
2323 a.s = g_strdup_printf("Error in config file '%s'", configfile);
2324 echo(&a);
2325 g_free(a.s);
2326 g_free(configfile);
2329 make_searchengines_list(searchengines, LENGTH(searchengines));
2331 /* read searchengines. */
2332 searchengines_file = g_strdup_printf(SEARCHENGINES_STORAGE_FILENAME);
2333 switch (read_searchengines(searchengines_file)) {
2334 case SYNTAX_ERROR:
2335 a.i = Error;
2336 a.s = g_strdup_printf("Syntax error in searchengines file '%s'", searchengines_file);
2337 echo(&a);
2338 break;
2339 case READING_FAILED:
2340 a.i = Error;
2341 a.s = g_strdup_printf("Could not read searchengines file '%s'", searchengines_file);
2342 echo(&a);
2343 break;
2344 default:
2345 break;
2347 g_free(searchengines_file);
2349 /* command line argument: URL */
2350 if (argc > 1) {
2351 strncpy(url, argv[argc - 1], 255);
2352 } else {
2353 strncpy(url, startpage, 255);
2356 a.i = TargetCurrent;
2357 a.s = url;
2358 open_arg(&a);
2359 gtk_main();
2361 mop_up();
2363 return EXIT_SUCCESS;