Define cookie-related function protoypes.
[vimprobable2.git] / main.c
blob76f97a47a70faaa3665c9daf55295794ade79534
2 /*
3 (c) 2009 by Leon Winter
4 (c) 2009, 2010 by Hannes Schueller
5 (c) 2009, 2010 by Matto Fransen
6 see LICENSE file
7 */
9 #include <X11/Xlib.h>
10 #include "includes.h"
11 #include "vimprobable.h"
12 #include "utilities.h"
13 #include "callbacks.h"
14 #include "javascript.h"
16 /* remove numlock symbol from keymask */
17 #define CLEAN(mask) (mask & ~(GDK_MOD2_MASK) & ~(GDK_BUTTON1_MASK) & ~(GDK_BUTTON2_MASK) & ~(GDK_BUTTON3_MASK) & ~(GDK_BUTTON4_MASK) & ~(GDK_BUTTON5_MASK))
19 /* callbacks here */
20 static void inputbox_activate_cb(GtkEntry *entry, gpointer user_data);
21 static gboolean inputbox_keypress_cb(GtkEntry *entry, GdkEventKey *event);
22 static gboolean inputbox_keyrelease_cb(GtkEntry *entry, GdkEventKey *event);
23 static gboolean inputbox_changed_cb(GtkEditable *entry, gpointer user_data);
24 static WebKitWebView* inspector_inspect_web_view_cb(gpointer inspector, WebKitWebView* web_view);
25 static gboolean notify_event_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data);
26 static gboolean webview_console_cb(WebKitWebView *webview, char *message, int line, char *source, gpointer user_data);
27 static gboolean webview_download_cb(WebKitWebView *webview, WebKitDownload *download, gpointer user_data);
28 static void webview_hoverlink_cb(WebKitWebView *webview, char *title, char *link, gpointer data);
29 static gboolean webview_keypress_cb(WebKitWebView *webview, GdkEventKey *event);
30 static void webview_load_committed_cb(WebKitWebView *webview, WebKitWebFrame *frame, gpointer user_data);
31 static void webview_load_finished_cb(WebKitWebView *webview, WebKitWebFrame *frame, gpointer user_data);
32 static gboolean webview_mimetype_cb(WebKitWebView *webview, WebKitWebFrame *frame, WebKitNetworkRequest *request,
33 char *mime_type, WebKitWebPolicyDecision *decision, gpointer user_data);
34 static gboolean webview_new_window_cb(WebKitWebView *webview, WebKitWebFrame *frame, WebKitNetworkRequest *request,
35 WebKitWebNavigationAction *action, WebKitWebPolicyDecision *decision, gpointer user_data);
36 static gboolean webview_open_in_new_window_cb(WebKitWebView *webview, WebKitWebFrame *frame, gpointer user_data);
37 static void webview_progress_changed_cb(WebKitWebView *webview, int progress, gpointer user_data);
38 static void webview_title_changed_cb(WebKitWebView *webview, WebKitWebFrame *frame, char *title, gpointer user_data);
39 static void window_destroyed_cb(GtkWidget *window, gpointer func_data);
41 /* functions */
42 static gboolean bookmark(const Arg *arg);
43 static gboolean browser_settings(const Arg *arg);
44 static gboolean commandhistoryfetch(const Arg *arg);
45 static gboolean complete(const Arg *arg);
46 static gboolean descend(const Arg *arg);
47 gboolean echo(const Arg *arg);
48 static gboolean focus_input(const Arg *arg);
49 static gboolean input(const Arg *arg);
50 static gboolean navigate(const Arg *arg);
51 static gboolean number(const Arg *arg);
52 static gboolean open_arg(const Arg *arg);
53 static gboolean paste(const Arg *arg);
54 static gboolean quickmark(const Arg *arg);
55 static gboolean quit(const Arg *arg);
56 static gboolean revive(const Arg *arg);
57 static gboolean print_frame(const Arg *arg);
58 static gboolean search(const Arg *arg);
59 static gboolean set(const Arg *arg);
60 static gboolean script(const Arg *arg);
61 static gboolean scroll(const Arg *arg);
62 static gboolean search_tag(const Arg *arg);
63 static gboolean yank(const Arg *arg);
64 static gboolean view_source(const Arg * arg);
65 static gboolean zoom(const Arg *arg);
66 static gboolean fake_key_event(const Arg *arg);
68 static void update_url(const char *uri);
69 static void setup_modkeys(void);
70 static void setup_gui(void);
71 static void setup_settings(void);
72 static void setup_signals(void);
73 static void ascii_bar(int total, int state, char *string);
74 static gchar *jsapi_ref_to_string(JSContextRef context, JSValueRef ref);
75 static void jsapi_evaluate_script(const gchar *script, gchar **value, gchar **message);
76 static void download_progress(WebKitDownload *d, GParamSpec *pspec);
77 static void set_widget_font_and_color(GtkWidget *widget, const char *font_str,
78 const char *bg_color_str, const char *fg_color_str);
80 static gboolean history(void);
81 static gboolean process_set_line(char *line);
82 void save_command_history(char *line);
83 void toggle_proxy(gboolean onoff);
84 void toggle_scrollbars(gboolean onoff);
86 gboolean process_keypress(GdkEventKey *event);
87 void fill_suggline(char * suggline, const char * command, const char *fill_with);
88 GtkWidget * fill_eventbox(const char * completion_line);
91 #include "main.h"
93 /* variables */
94 static GtkWindow *window;
95 static GtkWidget *viewport;
96 static GtkBox *box;
97 static GtkScrollbar *scroll_h;
98 static GtkScrollbar *scroll_v;
99 static GtkAdjustment *adjust_h;
100 static GtkAdjustment *adjust_v;
101 static GtkWidget *inputbox;
102 static GtkWidget *eventbox;
103 static GtkWidget *status_url;
104 static GtkWidget *status_state;
105 static WebKitWebView *webview;
106 static SoupSession *session;
107 static GtkClipboard *clipboards[2];
109 static char **args;
110 static unsigned int mode = ModeNormal;
111 static unsigned int count = 0;
112 static float zoomstep;
113 static char *modkeys;
114 static char current_modkey;
115 static char *search_handle;
116 static gboolean search_direction;
117 static gboolean echo_active = TRUE;
118 WebKitWebInspector *inspector;
120 static GdkNativeWindow embed = 0;
121 static char *configfile = NULL;
122 static char *winid = NULL;
124 static char rememberedURI[128] = "";
125 static char inputKey[5];
126 static char inputBuffer[65] = "";
127 static char chars[65] = "0000000000000000000000000000000000000000000000000000000000000000\n";
128 static char followTarget[8] = "";
129 char *error_msg = NULL;
131 GList *activeDownloads;
133 #include "config.h"
134 #include "keymap.h"
136 char commandhistory[COMMANDHISTSIZE][255];
137 int lastcommand = 0;
138 int maxcommands = 0;
139 int commandpointer = 0;
140 KeyList *keylistroot = NULL;
142 /* Cookie-related information.
144 * Note that this cannot be surrounded by #ifdef blocks for
145 * ENABLE_COOKIE_SUPPORT
147 static time_t cookie_timeout = 4800;
148 static char *cookie_store;
149 static void handle_cookie_request(SoupMessage *soup_msg, gpointer unused);
150 static const char *get_cookies(SoupURI *soup_uri);
151 static void set_single_cookie(SoupCookie *cookie);
152 static void new_generic_request(SoupSession *soup_ses, SoupMessage *soup_msg, gpointer unused);
154 /* callbacks */
155 void
156 window_destroyed_cb(GtkWidget *window, gpointer func_data) {
157 quit(NULL);
160 void
161 webview_title_changed_cb(WebKitWebView *webview, WebKitWebFrame *frame, char *title, gpointer user_data) {
162 gtk_window_set_title(window, title);
165 void
166 webview_progress_changed_cb(WebKitWebView *webview, int progress, gpointer user_data) {
167 #ifdef ENABLE_GTK_PROGRESS_BAR
168 gtk_entry_set_progress_fraction(GTK_ENTRY(inputbox), progress == 100 ? 0 : (double)progress/100);
169 #endif
170 update_state();
173 #ifdef ENABLE_WGET_PROGRESS_BAR
174 void
175 ascii_bar(int total, int state, char *string) {
176 int i;
178 for (i = 0; i < state; i++)
179 string[i] = progressbartickchar;
180 string[i++] = progressbarcurrent;
181 for (; i < total; i++)
182 string[i] = progressbarspacer;
183 string[i] = '\0';
185 #endif
187 void
188 webview_load_committed_cb(WebKitWebView *webview, WebKitWebFrame *frame, gpointer user_data) {
189 Arg a = { .i = Silent, .s = JS_SETUP_HINTS };
190 const char *uri = webkit_web_view_get_uri(webview);
192 update_url(uri);
193 script(&a);
196 void
197 webview_load_finished_cb(WebKitWebView *webview, WebKitWebFrame *frame, gpointer user_data) {
198 Arg a = { .i = Silent, .s = JS_SETUP_INPUT_FOCUS };
200 if (HISTORY_MAX_ENTRIES > 0)
201 history();
202 update_state();
203 script(&a);
206 static gboolean
207 webview_open_in_new_window_cb(WebKitWebView *webview, WebKitWebFrame *frame, gpointer user_data) {
208 Arg a = { .i = TargetNew, .s = (char*)webkit_web_view_get_uri(webview) };
209 if (strlen(rememberedURI) > 0) {
210 a.s = rememberedURI;
212 open_arg(&a);
213 return FALSE;
216 gboolean
217 webview_new_window_cb(WebKitWebView *webview, WebKitWebFrame *frame, WebKitNetworkRequest *request,
218 WebKitWebNavigationAction *action, WebKitWebPolicyDecision *decision, gpointer user_data) {
219 Arg a = { .i = TargetNew, .s = (char*)webkit_network_request_get_uri(request) };
220 open_arg(&a);
221 webkit_web_policy_decision_ignore(decision);
222 return TRUE;
225 gboolean
226 webview_mimetype_cb(WebKitWebView *webview, WebKitWebFrame *frame, WebKitNetworkRequest *request,
227 char *mime_type, WebKitWebPolicyDecision *decision, gpointer user_data) {
228 if (webkit_web_view_can_show_mime_type(webview, mime_type) == FALSE) {
229 webkit_web_policy_decision_download(decision);
230 return TRUE;
231 } else {
232 return FALSE;
236 static WebKitWebView*
237 inspector_inspect_web_view_cb(gpointer inspector, WebKitWebView* web_view) {
238 gchar* inspector_title;
239 GtkWidget* inspector_window;
240 GtkWidget* inspector_view;
242 /* just enough code to show the inspector - no signal handling etc. */
243 inspector_title = g_strdup_printf("Inspect page - %s - Vimprobable2", webkit_web_view_get_uri(web_view));
244 if (embed) {
245 inspector_window = gtk_plug_new(embed);
246 } else {
247 inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
248 gtk_window_set_wmclass(window, "vimprobable2", "Vimprobable2");
250 gtk_window_set_title(GTK_WINDOW(inspector_window), inspector_title);
251 g_free(inspector_title);
252 inspector_view = webkit_web_view_new();
253 gtk_container_add(GTK_CONTAINER(inspector_window), inspector_view);
254 gtk_widget_show_all(inspector_window);
255 return WEBKIT_WEB_VIEW(inspector_view);
258 gboolean
259 webview_download_cb(WebKitWebView *webview, WebKitDownload *download, gpointer user_data) {
260 const gchar *filename;
261 gchar *uri, *path;
262 uint32_t size;
263 Arg a;
265 filename = webkit_download_get_suggested_filename(download);
266 if (filename == NULL || strlen(filename) == 0) {
267 filename = "vimprobable_download";
269 path = g_build_filename(g_strdup_printf(DOWNLOADS_PATH), filename, NULL);
270 uri = g_strconcat("file://", path, NULL);
271 webkit_download_set_destination_uri(download, uri);
272 g_free(uri);
273 size = (uint32_t)webkit_download_get_total_size(download);
274 a.i = Info;
275 if (size > 0)
276 a.s = g_strdup_printf("Download %s started (expected size: %u bytes)...", filename, size);
277 else
278 a.s = g_strdup_printf("Download %s started (unknown size)...", filename);
279 echo(&a);
280 activeDownloads = g_list_prepend(activeDownloads, download);
281 g_signal_connect(download, "notify::progress", G_CALLBACK(download_progress), NULL);
282 g_signal_connect(download, "notify::status", G_CALLBACK(download_progress), NULL);
283 update_state();
284 return TRUE;
287 void
288 download_progress(WebKitDownload *d, GParamSpec *pspec) {
289 Arg a;
290 WebKitDownloadStatus status = webkit_download_get_status(d);
292 if (status != WEBKIT_DOWNLOAD_STATUS_STARTED && status != WEBKIT_DOWNLOAD_STATUS_CREATED) {
293 if (status != WEBKIT_DOWNLOAD_STATUS_FINISHED) {
294 a.i = Error;
295 a.s = g_strdup_printf("Error while downloading %s", webkit_download_get_suggested_filename(d));
296 echo(&a);
297 } else {
298 a.i = Info;
299 a.s = g_strdup_printf("Download %s finished", webkit_download_get_suggested_filename(d));
300 echo(&a);
302 activeDownloads = g_list_remove(activeDownloads, d);
304 update_state();
308 gboolean
309 process_keypress(GdkEventKey *event) {
310 KeyList *current;
312 current = keylistroot;
313 while (current != NULL) {
314 if (current->Element.mask == CLEAN(event->state)
315 && (current->Element.modkey == current_modkey
316 || (!current->Element.modkey && !current_modkey)
317 || current->Element.modkey == GDK_VoidSymbol ) /* wildcard */
318 && current->Element.key == event->keyval
319 && current->Element.func)
320 if (current->Element.func(&current->Element.arg)) {
321 current_modkey = count = 0;
322 update_state();
323 return TRUE;
325 current = current->next;
327 return FALSE;
330 gboolean
331 webview_keypress_cb(WebKitWebView *webview, GdkEventKey *event) {
332 Arg a = { .i = ModeNormal, .s = NULL };
334 switch (mode) {
335 case ModeNormal:
336 if (CLEAN(event->state) == 0) {
337 memset(inputBuffer, 0, 65);
338 if (event->keyval == GDK_Escape) {
339 a.i = Info;
340 a.s = "";
341 echo(&a);
342 } else if (current_modkey == 0 && ((event->keyval >= GDK_1 && event->keyval <= GDK_9)
343 || (event->keyval == GDK_0 && count))) {
344 count = (count ? count * 10 : 0) + (event->keyval - GDK_0);
345 update_state();
346 return TRUE;
347 } else if (strchr(modkeys, event->keyval) && current_modkey != event->keyval) {
348 current_modkey = event->keyval;
349 update_state();
350 return TRUE;
353 /* keybindings */
354 if (process_keypress(event) == TRUE) return TRUE;
356 break;
357 case ModeInsert:
358 if (CLEAN(event->state) == 0 && event->keyval == GDK_Escape) {
359 a.i = Silent;
360 a.s = "vimprobable_clearfocus()";
361 script(&a);
362 a.i = ModeNormal;
363 return set(&a);
365 case ModePassThrough:
366 if (CLEAN(event->state) == 0 && event->keyval == GDK_Escape) {
367 echo(&a);
368 set(&a);
369 return TRUE;
371 break;
372 case ModeSendKey:
373 echo(&a);
374 set(&a);
375 break;
376 case ModeHints:
377 if (CLEAN(event->state) == 0 && event->keyval == GDK_Escape) {
378 a.i = Silent;
379 a.s = "vimprobable_clear()";
380 script(&a);
381 a.i = ModeNormal;
382 count = 0;
383 strncpy(chars, "0000000000000000000000000000000000000000000000000000000000000000\0", 65);
384 return set(&a);
385 } else if (CLEAN(event->state) == 0 && ((event->keyval >= GDK_1 && event->keyval <= GDK_9)
386 || (event->keyval >= GDK_KP_1 && event->keyval <= GDK_KP_9)
387 || ((event->keyval == GDK_0 || event->keyval == GDK_KP_0) && count))) {
388 /* allow a zero as non-first number */
389 if (event->keyval >= GDK_KP_1 && event->keyval <= GDK_KP_9)
390 count = (count ? count * 10 : 0) + (event->keyval - GDK_KP_0);
391 else
392 count = (count ? count * 10 : 0) + (event->keyval - GDK_0);
393 memset(inputBuffer, 0, 65);
394 sprintf(inputBuffer, "%d", count);
395 a.s = g_strconcat("vimprobable_update_hints(", inputBuffer, ")", NULL);
396 a.i = Silent;
397 memset(inputBuffer, 0, 65);
398 strncpy(chars, "0000000000000000000000000000000000000000000000000000000000000000\0", 65);
399 script(&a);
400 update_state();
401 return TRUE;
402 } else if ((CLEAN(event->state) == 0 && (event->keyval >= GDK_a && event->keyval <= GDK_z))
403 || (CLEAN(event->state) == GDK_SHIFT_MASK && (event->keyval >= GDK_A && event->keyval <= GDK_Z))
404 || ((CLEAN(event->state) == 0 || CLEAN(event->state) == GDK_SHIFT_MASK) && (event->keyval >= GDK_space && event->keyval <= GDK_slash))
405 || ((CLEAN(event->state) == 0 || CLEAN(event->state) == GDK_SHIFT_MASK) && (event->keyval >= GDK_colon && event->keyval <= GDK_at))
406 || ((CLEAN(event->state) == 0 || CLEAN(event->state) == GDK_SHIFT_MASK) && (event->keyval >= GDK_braceleft && event->keyval <= GDK_umacron))
407 || ((CLEAN(event->state) == 0 || CLEAN(event->state) == GDK_SHIFT_MASK) && (event->keyval >= GDK_Babovedot && event->keyval <= GDK_ycircumflex))
408 || ((CLEAN(event->state) == 0 || CLEAN(event->state) == GDK_SHIFT_MASK) && (event->keyval >= GDK_OE && event->keyval <= GDK_Ydiaeresis))
409 || ((CLEAN(event->state) == 0 || CLEAN(event->state) == GDK_SHIFT_MASK) && (event->keyval >= GDK_overline && event->keyval <= GDK_semivoicedsound))
410 || ((CLEAN(event->state) == 0 || CLEAN(event->state) == GDK_SHIFT_MASK) && (event->keyval >= GDK_Farsi_0 && event->keyval <= GDK_Arabic_9))
411 || ((CLEAN(event->state) == 0 || CLEAN(event->state) == GDK_SHIFT_MASK) && (event->keyval >= GDK_Arabic_semicolon && event->keyval <= GDK_Arabic_sukun))
412 || ((CLEAN(event->state) == 0 || CLEAN(event->state) == GDK_SHIFT_MASK) && (event->keyval >= GDK_Arabic_madda_above && event->keyval <= GDK_Arabic_heh_goal))
413 || ((CLEAN(event->state) == 0 || CLEAN(event->state) == GDK_SHIFT_MASK) && (event->keyval >= GDK_Cyrillic_GHE_bar && event->keyval <= GDK_Cyrillic_u_macron))
414 || ((CLEAN(event->state) == 0 || CLEAN(event->state) == GDK_SHIFT_MASK) && (event->keyval >= GDK_Serbian_dje && event->keyval <= GDK_Korean_Won))
415 || ((CLEAN(event->state) == 0 || CLEAN(event->state) == GDK_SHIFT_MASK) && (event->keyval >= GDK_Armenian_ligature_ew && event->keyval <= GDK_braille_dots_12345678))) {
416 /* update hints by link text */
417 if (strlen(inputBuffer) < 65) {
418 memset(inputKey, 0, 5);
419 /* support multibyte characters */
420 sprintf(inputKey, "%C", event->keyval);
421 strncat(inputBuffer, inputKey, 64 - strlen(inputBuffer));
422 /* remember the number of bytes of each character */
423 for (count = 0; count < 64; count++) {
424 if (strncmp((chars + count), "0", 1) == 0) {
425 sprintf(inputKey, "%d", (int)strlen(inputKey));
426 strncpy((chars + count), inputKey, 1);
427 break;
430 memset(inputKey, 0, 5);
431 count = 0;
432 a.i = Silent;
433 a.s = "vimprobable_cleanup()";
434 script(&a);
435 a.s = g_strconcat("vimprobable_show_hints('", inputBuffer, "')", NULL);
436 a.i = Silent;
437 script(&a);
438 update_state();
440 return TRUE;
441 } else if (CLEAN(event->state) == 0 && event->keyval == GDK_Return && count) {
442 memset(inputBuffer, 0, 65);
443 sprintf(inputBuffer, "%d", count);
444 a.s = g_strconcat("vimprobable_fire(", inputBuffer, ")", NULL);
445 a.i = Silent;
446 script(&a);
447 memset(inputBuffer, 0, 65);
448 count = 0;
449 strncpy(chars, "0000000000000000000000000000000000000000000000000000000000000000\0", 65);
450 update_state();
451 return TRUE;
452 } else if (CLEAN(event->state) == 0 && event->keyval == GDK_BackSpace) {
453 if (count > 9) {
454 count /= 10;
455 memset(inputBuffer, 0, 65);
456 sprintf(inputBuffer, "%d", count);
457 a.s = g_strconcat("vimprobable_update_hints(", inputBuffer, ")", NULL);
458 a.i = Silent;
459 memset(inputBuffer, 0, 65);
460 script(&a);
461 update_state();
462 } else if (count > 0) {
463 count = 0;
464 memset(inputBuffer, 0, 65);
465 a.i = Silent;
466 a.s = "vimprobable_cleanup()";
467 script(&a);
468 a.s = g_strconcat("vimprobable_show_hints()", NULL);
469 a.i = Silent;
470 script(&a);
471 update_state();
472 } else if (strlen(inputBuffer) > 0) {
473 a.i = Silent;
474 a.s = "vimprobable_cleanup()";
475 script(&a);
476 /* check how many bytes the last character uses */
477 for (count = 0; count < 64; count++) {
478 if (strncmp((chars + count), "0", 1) == 0) {
479 break;
482 memset(inputKey, 0, 5);
483 strncpy(inputKey, (chars + count - 1), 1);
484 strncpy((chars + count - 1), "0", 1);
485 count = atoi(inputKey);
486 /* remove the appropriate number of bytes from the string */
487 strncpy((inputBuffer + strlen(inputBuffer) - count), "\0", 1);
488 count = 0;
489 a.s = g_strconcat("vimprobable_show_hints('", inputBuffer, "')", NULL);
490 a.i = Silent;
491 script(&a);
492 update_state();
494 return TRUE;
496 break;
498 return FALSE;
501 void
502 set_widget_font_and_color(GtkWidget *widget, const char *font_str, const char *bg_color_str,
503 const char *fg_color_str) {
504 GdkColor fg_color;
505 GdkColor bg_color;
506 PangoFontDescription *font;
508 font = pango_font_description_from_string(font_str);
509 gtk_widget_modify_font(widget, font);
510 pango_font_description_free(font);
512 if (fg_color_str)
513 gdk_color_parse(fg_color_str, &fg_color);
514 if (bg_color_str)
515 gdk_color_parse(bg_color_str, &bg_color);
517 gtk_widget_modify_text(widget, GTK_STATE_NORMAL, fg_color_str ? &fg_color : NULL);
518 gtk_widget_modify_base(widget, GTK_STATE_NORMAL, bg_color_str ? &bg_color : NULL);
520 return;
523 void
524 webview_hoverlink_cb(WebKitWebView *webview, char *title, char *link, gpointer data) {
525 const char *uri = webkit_web_view_get_uri(webview);
527 memset(rememberedURI, 0, 128);
528 if (link) {
529 gtk_label_set_markup(GTK_LABEL(status_url), g_markup_printf_escaped("<span font=\"%s\">Link: %s</span>", statusfont, link));
530 strncpy(rememberedURI, link, 128);
531 } else
532 update_url(uri);
535 gboolean
536 webview_console_cb(WebKitWebView *webview, char *message, int line, char *source, gpointer user_data) {
537 Arg a;
539 /* Don't change internal mode if the browser doesn't have focus to prevent inconsistent states */
540 if (gtk_window_has_toplevel_focus(window)) {
541 if (!strcmp(message, "hintmode_off") || !strcmp(message, "insertmode_off")) {
542 a.i = ModeNormal;
543 return set(&a);
544 } else if (!strcmp(message, "insertmode_on")) {
545 a.i = ModeInsert;
546 return set(&a);
549 return FALSE;
552 void
553 inputbox_activate_cb(GtkEntry *entry, gpointer user_data) {
554 char *text;
555 guint16 length = gtk_entry_get_text_length(entry);
556 Arg a;
557 int i;
558 size_t len;
559 gboolean success = FALSE, forward = FALSE, found = FALSE;
561 a.i = HideCompletion;
562 complete(&a);
563 if (length < 2)
564 return;
565 text = (char*)gtk_entry_get_text(entry);
566 if (text[0] == ':') {
567 for (i = 0; i < LENGTH(commands); i++) {
568 len = strlen(commands[i].cmd);
569 if (length >= len && !strncmp(&text[1], commands[i].cmd, len) && (text[len + 1] == ' ' || !text[len + 1])) {
570 found = TRUE;
571 a.i = commands[i].arg.i;
572 a.s = length > len + 2 ? &text[len + 2] : commands[i].arg.s;
573 success = commands[i].func(&a);
574 break;
578 save_command_history(text);
580 if (!found) {
581 a.i = Error;
582 a.s = g_strdup_printf("Not a browser command: %s", &text[1]);
583 echo(&a);
584 g_free(a.s);
585 } else if (!success) {
586 a.i = Error;
587 if (error_msg != NULL) {
588 a.s = g_strdup_printf("%s", error_msg);
589 g_free(error_msg);
590 error_msg = NULL;
591 } else {
592 a.s = g_strdup_printf("Unknown error. Please file a bug report!");
594 echo(&a);
595 g_free(a.s);
597 } else if ((forward = text[0] == '/') || text[0] == '?') {
598 webkit_web_view_unmark_text_matches(webview);
599 #ifdef ENABLE_MATCH_HIGHLITING
600 webkit_web_view_mark_text_matches(webview, &text[1], FALSE, 0);
601 webkit_web_view_set_highlight_text_matches(webview, TRUE);
602 #endif
603 count = 0;
604 #ifndef ENABLE_INCREMENTAL_SEARCH
605 a.s =& text[1];
606 a.i = searchoptions | (forward ? DirectionForward : DirectionBackwards);
607 search(&a);
608 #else
609 search_direction = forward;
610 search_handle = g_strdup(&text[1]);
611 #endif
612 } else
613 return;
614 if (!echo_active)
615 gtk_entry_set_text(entry, "");
616 gtk_widget_grab_focus(GTK_WIDGET(webview));
619 gboolean
620 inputbox_keypress_cb(GtkEntry *entry, GdkEventKey *event) {
621 Arg a;
623 switch (event->keyval) {
624 case GDK_Escape:
625 a.i = HideCompletion;
626 complete(&a);
627 a.i = ModeNormal;
628 return set(&a);
629 break;
630 case GDK_Tab:
631 a.i = DirectionNext;
632 return complete(&a);
633 break;
634 case GDK_Up:
635 a.i = DirectionPrev;
636 return commandhistoryfetch(&a);
637 break;
638 case GDK_Down:
639 a.i = DirectionNext;
640 return commandhistoryfetch(&a);
641 break;
642 case GDK_ISO_Left_Tab:
643 a.i = DirectionPrev;
644 return complete(&a);
645 break;
647 return FALSE;
650 gboolean
651 notify_event_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data) {
652 int i;
653 if (mode == ModeNormal && event->type == GDK_BUTTON_RELEASE) {
654 /* handle mouse click events */
655 for (i = 0; i < LENGTH(mouse); i++) {
656 if (mouse[i].mask == CLEAN(event->button.state)
657 && (mouse[i].modkey == current_modkey
658 || (!mouse[i].modkey && !current_modkey)
659 || mouse[i].modkey == GDK_VoidSymbol) /* wildcard */
660 && mouse[i].button == event->button.button
661 && mouse[i].func) {
662 if (mouse[i].func(&mouse[i].arg)) {
663 current_modkey = count = 0;
664 update_state();
665 return TRUE;
670 return FALSE;
673 static gboolean inputbox_keyrelease_cb(GtkEntry *entry, GdkEventKey *event) {
674 Arg a;
675 guint16 length = gtk_entry_get_text_length(entry);
677 if (!length) {
678 a.i = HideCompletion;
679 complete(&a);
680 a.i = ModeNormal;
681 return set(&a);
683 return FALSE;
686 static gboolean inputbox_changed_cb(GtkEditable *entry, gpointer user_data) {
687 char *text = (char*)gtk_entry_get_text(GTK_ENTRY(entry));
688 guint16 length = gtk_entry_get_text_length(GTK_ENTRY(entry));
689 gboolean forward = FALSE;
691 /* Update incremental search if the user changes the search text.
693 * Note: gtk_widget_is_focus() is a poor way to check if the change comes
694 * from the user. But if the entry is focused and the text is set
695 * through gtk_entry_set_text() in some asyncrounous operation,
696 * I would consider that a bug.
699 if (gtk_widget_is_focus(GTK_WIDGET(entry)) && length > 1 && ((forward = text[0] == '/') || text[0] == '?')) {
700 webkit_web_view_unmark_text_matches(webview);
701 webkit_web_view_search_text(webview, &text[1], searchoptions & CaseSensitive, forward, searchoptions & Wrapping);
702 return TRUE;
705 return FALSE;
708 /* funcs here */
710 void fill_suggline(char * suggline, const char * command, const char *fill_with) {
711 memset(suggline, 0, 512);
712 strncpy(suggline, command, 512);
713 strncat(suggline, " ", 1);
714 strncat(suggline, fill_with, 512 - strlen(suggline) - 1);
717 GtkWidget * fill_eventbox(const char * completion_line) {
718 GtkBox * row;
719 GtkWidget *row_eventbox, *el;
720 GdkColor color;
721 char * markup;
723 row = GTK_BOX(gtk_hbox_new(FALSE, 0));
724 row_eventbox = gtk_event_box_new();
725 gdk_color_parse(completionbgcolor[0], &color);
726 gtk_widget_modify_bg(row_eventbox, GTK_STATE_NORMAL, &color);
727 el = gtk_label_new(NULL);
728 markup = g_strconcat("<span font=\"", completionfont[0], "\" color=\"", completioncolor[0], "\">",
729 g_markup_escape_text(completion_line, strlen(completion_line)), "</span>", NULL);
730 gtk_label_set_markup(GTK_LABEL(el), markup);
731 g_free(markup);
732 gtk_misc_set_alignment(GTK_MISC(el), 0, 0);
733 gtk_box_pack_start(row, el, TRUE, TRUE, 2);
734 gtk_container_add(GTK_CONTAINER(row_eventbox), GTK_WIDGET(row));
735 return row_eventbox;
738 gboolean
739 complete(const Arg *arg) {
740 FILE *f;
741 const char *filename;
742 char *str, *s, *p, *markup, *entry, *searchfor, command[32] = "", suggline[512] = "", *url, **suggurls;
743 size_t listlen, len, cmdlen;
744 int i, spacepos;
745 gboolean highlight = FALSE, finished = FALSE;
746 GtkBox *row;
747 GtkWidget *row_eventbox, *el;
748 GtkBox *_table;
749 GdkColor color;
750 static GtkWidget *table, **widgets, *top_border;
751 static char **suggestions, *prefix;
752 static int n = 0, current = -1;
754 str = (char*)gtk_entry_get_text(GTK_ENTRY(inputbox));
755 len = strlen(str);
756 if ((len == 0 || str[0] != ':') && arg->i != HideCompletion)
757 return TRUE;
758 if (prefix) {
759 if (arg->i != HideCompletion && widgets && current != -1 && !strcmp(&str[1], suggestions[current])) {
760 gdk_color_parse(completionbgcolor[0], &color);
761 gtk_widget_modify_bg(widgets[current], GTK_STATE_NORMAL, &color);
762 current = (n + current + (arg->i == DirectionPrev ? -1 : 1)) % n;
763 if ((arg->i == DirectionNext && current == 0)
764 || (arg->i == DirectionPrev && current == n - 1))
765 current = -1;
766 } else {
767 free(widgets);
768 free(suggestions);
769 free(prefix);
770 gtk_widget_destroy(GTK_WIDGET(table));
771 gtk_widget_destroy(GTK_WIDGET(top_border));
772 table = NULL;
773 widgets = NULL;
774 suggestions = NULL;
775 prefix = NULL;
776 n = 0;
777 current = -1;
778 if (arg->i == HideCompletion)
779 return TRUE;
781 } else if (arg->i == HideCompletion)
782 return TRUE;
783 if (!widgets) {
784 prefix = g_strdup_printf(str);
785 listlen = LENGTH(commands);
786 widgets = malloc(sizeof(GtkWidget*) * listlen);
787 suggestions = malloc(sizeof(char*) * listlen);
788 top_border = gtk_event_box_new();
789 gtk_widget_set_size_request(GTK_WIDGET(top_border), 0, 1);
790 gdk_color_parse(completioncolor[2], &color);
791 gtk_widget_modify_bg(top_border, GTK_STATE_NORMAL, &color);
792 table = gtk_event_box_new();
793 gdk_color_parse(completionbgcolor[0], &color);
794 _table = GTK_BOX(gtk_vbox_new(FALSE, 0));
795 highlight = len > 1;
796 if (strchr(str, ' ') == NULL) {
797 for (i = 0; i < listlen; i++) {
798 cmdlen = strlen(commands[i].cmd);
799 if (!highlight || (len - 1 <= cmdlen && !strncmp(&str[1], commands[i].cmd, len - 1))) {
800 p = s = malloc(sizeof(char*) * (highlight ? sizeof(COMPLETION_TAG_OPEN) + sizeof(COMPLETION_TAG_CLOSE) - 1 : 1) + cmdlen);
801 if (highlight) {
802 memcpy(p, COMPLETION_TAG_OPEN, sizeof(COMPLETION_TAG_OPEN) - 1);
803 memcpy((p += sizeof(COMPLETION_TAG_OPEN) - 1), &str[1], len - 1);
804 memcpy((p += len - 1), COMPLETION_TAG_CLOSE, sizeof(COMPLETION_TAG_CLOSE) - 1);
805 p += sizeof(COMPLETION_TAG_CLOSE) - 1;
807 memcpy(p, &commands[i].cmd[len - 1], cmdlen - len + 2);
808 row = GTK_BOX(gtk_hbox_new(FALSE, 0));
809 row_eventbox = gtk_event_box_new();
810 gtk_widget_modify_bg(row_eventbox, GTK_STATE_NORMAL, &color);
811 el = gtk_label_new(NULL);
812 markup = g_strconcat("<span font=\"", completionfont[0], "\" color=\"", completioncolor[0], "\">", s, "</span>", NULL);
813 free(s);
814 gtk_label_set_markup(GTK_LABEL(el), markup);
815 g_free(markup);
816 gtk_misc_set_alignment(GTK_MISC(el), 0, 0);
817 gtk_box_pack_start(row, el, TRUE, TRUE, 2);
818 gtk_container_add(GTK_CONTAINER(row_eventbox), GTK_WIDGET(row));
819 gtk_box_pack_start(_table, GTK_WIDGET(row_eventbox), FALSE, FALSE, 0);
820 suggestions[n] = commands[i].cmd;
821 widgets[n++] = row_eventbox;
824 } else {
825 entry = (char *)malloc(512 * sizeof(char));
826 if (entry != NULL) {
827 memset(entry, 0, 512);
828 suggurls = malloc(sizeof(char*) * listlen);
829 if (suggurls != NULL) {
830 spacepos = strcspn(str, " ");
831 searchfor = (str + spacepos + 1);
832 strncpy(command, (str + 1), spacepos - 1);
833 if (strlen(command) == 3 && strncmp(command, "set", 3) == 0) {
834 /* browser settings */
835 listlen = LENGTH(browsersettings);
836 for (i = 0; i < listlen; i++) {
837 if (strstr(browsersettings[i].name, searchfor) != NULL) {
838 /* match */
839 fill_suggline(suggline, command, browsersettings[i].name);
840 suggurls[n] = (char *)malloc(sizeof(char) * 512 + 1);
841 strncpy(suggurls[n], suggline, 512);
842 suggestions[n] = suggurls[n];
843 row_eventbox = fill_eventbox(suggline);
844 gtk_box_pack_start(_table, GTK_WIDGET(row_eventbox), FALSE, FALSE, 0);
845 widgets[n++] = row_eventbox;
849 } else {
850 /* URL completion using the current command */
851 filename = g_strdup_printf(BOOKMARKS_STORAGE_FILENAME);
852 f = fopen(filename, "r");
853 if (f != NULL) {
854 while (finished != TRUE) {
855 if ((char *)NULL == fgets(entry, 512, f)) {
856 /* check if end of file was reached / error occured */
857 if (!feof(f)) {
858 break;
860 /* end of file reached */
861 finished = TRUE;
862 continue;
864 if (strstr(entry, searchfor) != NULL) {
865 /* found in bookmarks */
866 if (strchr(entry, ' ') != NULL) {
867 url = strtok(entry, " ");
868 } else {
869 url = strtok(entry, "\n");
871 fill_suggline(suggline, command, url);
872 suggurls[n] = (char *)malloc(sizeof(char) * 512 + 1);
873 strncpy(suggurls[n], suggline, 512);
874 suggestions[n] = suggurls[n];
875 row_eventbox = fill_eventbox(suggline);
876 gtk_box_pack_start(_table, GTK_WIDGET(row_eventbox), FALSE, FALSE, 0);
877 widgets[n++] = row_eventbox;
879 if (n >= listlen) {
880 break;
883 fclose(f);
884 /* history */
885 if (n < listlen) {
886 filename = g_strdup_printf(HISTORY_STORAGE_FILENAME);
887 f = fopen(filename, "r");
888 if (f != NULL) {
889 finished = FALSE;
890 while (finished != TRUE) {
891 if ((char *)NULL == fgets(entry, 512, f)) {
892 /* check if end of file was reached / error occured */
893 if (!feof(f)) {
894 break;
896 /* end of file reached */
897 finished = TRUE;
898 continue;
900 if (strstr(entry, searchfor) != NULL) {
901 /* found in history */
902 if (strchr(entry, ' ') != NULL) {
903 url = strtok(entry, " ");
904 } else {
905 url = strtok(entry, "\n");
907 fill_suggline(suggline, command, url);
908 suggurls[n] = (char *)malloc(sizeof(char) * 512 + 1);
909 strncpy(suggurls[n], suggline, 512);
910 suggestions[n] = suggurls[n];
911 row_eventbox = fill_eventbox(suggline);
912 gtk_box_pack_start(_table, GTK_WIDGET(row_eventbox), FALSE, FALSE, 0);
913 widgets[n++] = row_eventbox;
915 if (n >= listlen) {
916 break;
919 fclose(f);
924 if (suggurls != NULL) {
925 free(suggurls);
926 suggurls = NULL;
929 if (entry != NULL) {
930 free(entry);
931 entry = NULL;
935 widgets = realloc(widgets, sizeof(GtkWidget*) * n);
936 suggestions = realloc(suggestions, sizeof(char*) * n);
937 if (!n) {
938 gdk_color_parse(completionbgcolor[1], &color);
939 gtk_widget_modify_bg(table, GTK_STATE_NORMAL, &color);
940 el = gtk_label_new(NULL);
941 gtk_misc_set_alignment(GTK_MISC(el), 0, 0);
942 markup = g_strconcat("<span font=\"", completionfont[1], "\" color=\"", completioncolor[1], "\">No Completions</span>", NULL);
943 gtk_label_set_markup(GTK_LABEL(el), markup);
944 g_free(markup);
945 gtk_box_pack_start(_table, GTK_WIDGET(el), FALSE, FALSE, 0);
947 gtk_box_pack_start(box, GTK_WIDGET(top_border), FALSE, FALSE, 0);
948 gtk_container_add(GTK_CONTAINER(table), GTK_WIDGET(_table));
949 gtk_box_pack_start(box, GTK_WIDGET(table), FALSE, FALSE, 0);
950 gtk_widget_show_all(GTK_WIDGET(window));
951 if (!n)
952 return TRUE;
953 current = arg->i == DirectionPrev ? n - 1 : 0;
955 if (current != -1) {
956 gdk_color_parse(completionbgcolor[2], &color);
957 gtk_widget_modify_bg(GTK_WIDGET(widgets[current]), GTK_STATE_NORMAL, &color);
958 s = g_strconcat(":", suggestions[current], NULL);
959 gtk_entry_set_text(GTK_ENTRY(inputbox), s);
960 g_free(s);
961 } else
962 gtk_entry_set_text(GTK_ENTRY(inputbox), prefix);
963 gtk_editable_set_position(GTK_EDITABLE(inputbox), -1);
964 return TRUE;
967 gboolean
968 descend(const Arg *arg) {
969 char *source = (char*)webkit_web_view_get_uri(webview), *p = &source[0], *new;
970 int i, len;
971 count = count ? count : 1;
973 if (!source)
974 return TRUE;
975 if (arg->i == Rootdir) {
976 for (i = 0; i < 3; i++) /* get to the third slash */
977 if (!(p = strchr(++p, '/')))
978 return TRUE; /* if we cannot find it quit */
979 } else {
980 len = strlen(source);
981 if (!len) /* if string is empty quit */
982 return TRUE;
983 p = source + len; /* start at the end */
984 if (*(p - 1) == '/') /* /\/$/ is not an additional level */
985 ++count;
986 for (i = 0; i < count; i++)
987 while(*(p--) != '/' || *p == '/') /* count /\/+/ as one slash */
988 if (p == source) /* if we reach the first char pointer quit */
989 return TRUE;
990 ++p; /* since we do p-- in the while, we are pointing at
991 the char before the slash, so +1 */
993 len = p - source + 1; /* new length = end - start + 1 */
994 new = malloc(len + 1);
995 memcpy(new, source, len);
996 new[len] = '\0';
997 webkit_web_view_load_uri(webview, new);
998 free(new);
999 return TRUE;
1002 gboolean
1003 echo(const Arg *arg) {
1004 int index = !arg->s ? 0 : arg->i & (~NoAutoHide);
1006 if (index < Info || index > Error)
1007 return TRUE;
1009 set_widget_font_and_color(inputbox, urlboxfont[index], urlboxbgcolor[index], urlboxcolor[index]);
1010 gtk_entry_set_text(GTK_ENTRY(inputbox), !arg->s ? "" : arg->s);
1012 return TRUE;
1015 gboolean
1016 input(const Arg *arg) {
1017 int pos = 0;
1018 count = 0;
1019 const char *url;
1020 int index = Info;
1022 update_state();
1024 /* Set the colour and font back to the default, so that we don't still
1025 * maintain a red colour from a warning from an end of search indicator,
1026 * etc.
1028 set_widget_font_and_color(inputbox, urlboxfont[index], urlboxbgcolor[index], urlboxcolor[index]);
1030 /* to avoid things like :open URL :open URL2 or :open :open URL */
1031 gtk_entry_set_text(GTK_ENTRY(inputbox), "");
1032 gtk_editable_insert_text(GTK_EDITABLE(inputbox), arg->s, -1, &pos);
1033 if (arg->i & InsertCurrentURL && (url = webkit_web_view_get_uri(webview)))
1034 gtk_editable_insert_text(GTK_EDITABLE(inputbox), url, -1, &pos);
1035 gtk_widget_grab_focus(inputbox);
1036 gtk_editable_set_position(GTK_EDITABLE(inputbox), -1);
1037 return TRUE;
1040 gboolean
1041 navigate(const Arg *arg) {
1042 if (arg->i & NavigationForwardBack)
1043 webkit_web_view_go_back_or_forward(webview, (arg->i == NavigationBack ? -1 : 1) * (count ? count : 1));
1044 else if (arg->i & NavigationReloadActions)
1045 (arg->i == NavigationReload ? webkit_web_view_reload : webkit_web_view_reload_bypass_cache)(webview);
1046 else
1047 webkit_web_view_stop_loading(webview);
1048 return TRUE;
1051 gboolean
1052 number(const Arg *arg) {
1053 const char *source = webkit_web_view_get_uri(webview);
1054 char *uri, *p, *new;
1055 int number, diff = (count ? count : 1) * (arg->i == Increment ? 1 : -1);
1057 if (!source)
1058 return TRUE;
1059 uri = g_strdup_printf(source); /* copy string */
1060 p =& uri[0];
1061 while(*p != '\0') /* goto the end of the string */
1062 ++p;
1063 --p;
1064 while(*p >= '0' && *p <= '9') /* go back until non number char is reached */
1065 --p;
1066 if (*(++p) == '\0') { /* if no numbers were found abort */
1067 free(uri);
1068 return TRUE;
1070 number = atoi(p) + diff; /* apply diff on number */
1071 *p = '\0';
1072 new = g_strdup_printf("%s%d", uri, number); /* create new uri */
1073 webkit_web_view_load_uri(webview, new);
1074 g_free(new);
1075 free(uri);
1076 return TRUE;
1079 gboolean
1080 open_arg(const Arg *arg) {
1081 char *argv[6];
1082 char *s = arg->s, *p, *new;
1083 Arg a = { .i = NavigationReload };
1084 int len, i;
1086 if (embed) {
1087 argv[0] = *args;
1088 argv[1] = "-e";
1089 argv[2] = winid;
1090 argv[3] = arg->s;
1091 argv[4] = NULL;
1092 } else {
1093 argv[0] = *args;
1094 argv[1] = arg->s;
1095 argv[2] = NULL;
1098 if (!arg->s)
1099 navigate(&a);
1100 else if (arg->i == TargetCurrent) {
1101 len = strlen(arg->s);
1102 new = NULL, p = strchr(arg->s, ' ');
1103 if (p) /* check for search engines */
1104 for (i = 0; i < LENGTH(searchengines); i++)
1105 if (!strncmp(arg->s, searchengines[i].handle, p - arg->s)) {
1106 p = soup_uri_encode(++p, "&");
1107 new = g_strdup_printf(searchengines[i].uri, p);
1108 g_free(p);
1109 break;
1111 if (!new) {
1112 if (len > 3 && strstr(arg->s, "://")) { /* valid url? */
1113 p = new = g_malloc(len + 1);
1114 while(*s != '\0') { /* strip whitespaces */
1115 if (*s != ' ')
1116 *(p++) = *s;
1117 ++s;
1119 *p = '\0';
1120 } else if (strcspn(arg->s, "/") == 0 || strcspn(arg->s, "./") == 0) { /* prepend "file://" */
1121 new = g_malloc(sizeof("file://") + len);
1122 strcpy(new, "file://");
1123 memcpy(&new[sizeof("file://") - 1], arg->s, len + 1);
1124 } else if (p || !strchr(arg->s, '.')) { /* whitespaces or no dot? */
1125 p = soup_uri_encode(arg->s, "&");
1126 new = g_strdup_printf(defsearch->uri, p);
1127 g_free(p);
1128 } else { /* prepend "http://" */
1129 new = g_malloc(sizeof("http://") + len);
1130 strcpy(new, "http://");
1131 memcpy(&new[sizeof("http://") - 1], arg->s, len + 1);
1134 webkit_web_view_load_uri(webview, new);
1135 g_free(new);
1136 } else
1137 g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL);
1138 return TRUE;
1141 gboolean
1142 yank(const Arg *arg) {
1143 const char *url, *feedback;
1145 if (arg->i & SourceURL) {
1146 url = webkit_web_view_get_uri(webview);
1147 if (!url)
1148 return TRUE;
1149 feedback = g_strconcat("Yanked ", url, NULL);
1150 give_feedback(feedback);
1151 if (arg->i & ClipboardPrimary)
1152 gtk_clipboard_set_text(clipboards[0], url, -1);
1153 if (arg->i & ClipboardGTK)
1154 gtk_clipboard_set_text(clipboards[1], url, -1);
1155 } else
1156 webkit_web_view_copy_clipboard(webview);
1157 return TRUE;
1160 gboolean
1161 paste(const Arg *arg) {
1162 Arg a = { .i = arg->i & TargetNew, .s = NULL };
1164 /* If we're over a link, open it in a new target. */
1165 if (strlen(rememberedURI) > 0) {
1166 Arg new_target = { .i = TargetNew, .s = arg->s };
1167 open_arg(&new_target);
1168 return TRUE;
1171 if (arg->i & ClipboardPrimary)
1172 a.s = gtk_clipboard_wait_for_text(clipboards[0]);
1173 if (!a.s && arg->i & ClipboardGTK)
1174 a.s = gtk_clipboard_wait_for_text(clipboards[1]);
1175 if (a.s)
1176 open_arg(&a);
1177 return TRUE;
1180 gboolean
1181 quit(const Arg *arg) {
1182 FILE *f;
1183 const char *filename;
1184 const char *uri = webkit_web_view_get_uri(webview);
1185 if (uri != NULL) {
1186 /* write last URL into status file for recreation with "u" */
1187 filename = g_strdup_printf(CLOSED_URL_FILENAME);
1188 f = fopen(filename, "w");
1189 if (f != NULL) {
1190 fprintf(f, "%s", uri);
1191 fclose(f);
1194 gtk_main_quit();
1195 return TRUE;
1198 gboolean
1199 revive(const Arg *arg) {
1200 FILE *f;
1201 const char *filename;
1202 char buffer[512] = "";
1203 Arg a = { .i = TargetNew, .s = NULL };
1204 /* get the URL of the window which has been closed last */
1205 filename = g_strdup_printf(CLOSED_URL_FILENAME);
1206 f = fopen(filename, "r");
1207 if (f != NULL) {
1208 fgets(buffer, 512, f);
1209 fclose(f);
1211 if (strlen(buffer) > 0) {
1212 a.s = buffer;
1213 open_arg(&a);
1214 return TRUE;
1216 return FALSE;
1219 static
1220 gboolean print_frame(const Arg *arg)
1222 WebKitWebFrame *frame = webkit_web_view_get_main_frame(webview);
1223 webkit_web_frame_print (frame);
1224 return TRUE;
1227 gboolean
1228 search(const Arg *arg) {
1229 count = count ? count : 1;
1230 gboolean success, direction = arg->i & DirectionPrev;
1231 Arg a;
1233 if (arg->s) {
1234 free(search_handle);
1235 search_handle = g_strdup_printf(arg->s);
1237 if (!search_handle)
1238 return TRUE;
1239 if (arg->i & DirectionAbsolute)
1240 search_direction = direction;
1241 else
1242 direction ^= search_direction;
1243 do {
1244 success = webkit_web_view_search_text(webview, search_handle, arg->i & CaseSensitive, direction, FALSE);
1245 if (!success) {
1246 if (arg->i & Wrapping) {
1247 success = webkit_web_view_search_text(webview, search_handle, arg->i & CaseSensitive, direction, TRUE);
1248 if (success) {
1249 a.i = Warning;
1250 a.s = g_strdup_printf("search hit %s, continuing at %s",
1251 direction ? "BOTTOM" : "TOP",
1252 direction ? "TOP" : "BOTTOM");
1253 echo(&a);
1254 g_free(a.s);
1255 } else
1256 break;
1257 } else
1258 break;
1260 } while(--count);
1261 if (!success) {
1262 a.i = Error;
1263 a.s = g_strdup_printf("Pattern not found: %s", search_handle);
1264 echo(&a);
1265 g_free(a.s);
1267 return TRUE;
1270 gboolean
1271 set(const Arg *arg) {
1272 Arg a = { .i = Info | NoAutoHide };
1274 switch (arg->i) {
1275 case ModeNormal:
1276 if (search_handle) {
1277 search_handle = NULL;
1278 webkit_web_view_unmark_text_matches(webview);
1280 gtk_entry_set_text(GTK_ENTRY(inputbox), "");
1281 gtk_widget_grab_focus(GTK_WIDGET(webview));
1282 break;
1283 case ModePassThrough:
1284 a.s = "-- PASS THROUGH --";
1285 echo(&a);
1286 break;
1287 case ModeSendKey:
1288 a.s = "-- PASS TROUGH (next) --";
1289 echo(&a);
1290 break;
1291 case ModeInsert: /* should not be called manually but automatically */
1292 a.s = "-- INSERT --";
1293 echo(&a);
1294 break;
1295 case ModeHints:
1296 memset(followTarget, 0, 8);
1297 strncpy(followTarget, arg->s, 8);
1298 a.i = Silent;
1299 a.s = "vimprobable_show_hints()";
1300 script(&a);
1301 break;
1302 default:
1303 return TRUE;
1305 mode = arg->i;
1306 return TRUE;
1309 gchar*
1310 jsapi_ref_to_string(JSContextRef context, JSValueRef ref) {
1311 JSStringRef string_ref;
1312 gchar *string;
1313 size_t length;
1315 string_ref = JSValueToStringCopy(context, ref, NULL);
1316 length = JSStringGetMaximumUTF8CStringSize(string_ref);
1317 string = g_new(gchar, length);
1318 JSStringGetUTF8CString(string_ref, string, length);
1319 JSStringRelease(string_ref);
1320 return string;
1323 void
1324 jsapi_evaluate_script(const gchar *script, gchar **value, gchar **message) {
1325 WebKitWebFrame *frame = webkit_web_view_get_main_frame(webview);
1326 JSGlobalContextRef context = webkit_web_frame_get_global_context(frame);
1327 JSStringRef str;
1328 JSValueRef val, exception;
1330 str = JSStringCreateWithUTF8CString(script);
1331 val = JSEvaluateScript(context, str, JSContextGetGlobalObject(context), NULL, 0, &exception);
1332 JSStringRelease(str);
1333 if (!val)
1334 *message = jsapi_ref_to_string(context, exception);
1335 else
1336 *value = jsapi_ref_to_string(context, val);
1339 gboolean
1340 quickmark(const Arg *a) {
1341 int i, b;
1342 b = atoi(a->s);
1343 char *fn = g_strdup_printf(QUICKMARK_FILE);
1344 FILE *fp;
1345 fp = fopen(fn, "r");
1346 char buf[100];
1348 if (fp != NULL && b < 10) {
1349 for( i=0; i < b; ++i ) {
1350 if (feof(fp)) {
1351 break;
1353 fgets(buf, 100, fp);
1355 char *ptr = strrchr(buf, '\n');
1356 *ptr = '\0';
1357 Arg x = { .s = buf };
1358 if ( strlen(buf)) return open_arg(&x);
1359 else
1361 x.i = Error;
1362 x.s = g_strdup_printf("Quickmark %d not defined", b);
1363 echo(&x);
1364 g_free(x.s);
1365 return false;
1368 else { return false; }
1371 gboolean
1372 script(const Arg *arg) {
1373 gchar *value = NULL, *message = NULL;
1374 Arg a;
1376 if (!arg->s) {
1377 set_error("Missing argument.");
1378 return FALSE;
1380 jsapi_evaluate_script(arg->s, &value, &message);
1381 if (message) {
1382 set_error(message);
1383 return FALSE;
1385 if (arg->i != Silent && value) {
1386 a.i = arg->i;
1387 a.s = value;
1388 echo(&a);
1390 if (value) {
1391 if (strncmp(value, "fire;", 5) == 0) {
1392 count = 0;
1393 strncpy(chars, "0000000000000000000000000000000000000000000000000000000000000000", 64);
1394 memset(inputBuffer, 0, 65);
1395 a.s = g_strconcat("vimprobable_fire(", (value + 5), ")", NULL);
1396 a.i = Silent;
1397 script(&a);
1398 } else if (strncmp(value, "open;", 5) == 0) {
1399 count = 0;
1400 strncpy(chars, "0000000000000000000000000000000000000000000000000000000000000000", 64);
1401 memset(inputBuffer, 0, 65);
1402 a.i = ModeNormal;
1403 set(&a);
1404 if (strncmp(followTarget, "new", 3) == 0)
1405 a.i = TargetNew;
1406 else
1407 a.i = TargetCurrent;
1408 memset(followTarget, 0, 8);
1409 a.s = (value + 5);
1410 open_arg(&a);
1413 g_free(value);
1414 return TRUE;
1417 gboolean
1418 scroll(const Arg *arg) {
1419 GtkAdjustment *adjust = (arg->i & OrientationHoriz) ? adjust_h : adjust_v;
1420 int max = gtk_adjustment_get_upper(adjust) - gtk_adjustment_get_page_size(adjust);
1421 float val = gtk_adjustment_get_value(adjust) / max * 100;
1422 int direction = (arg->i & (1 << 2)) ? 1 : -1;
1424 if ((direction == 1 && val < 100) || (direction == -1 && val > 0)) {
1425 if (arg->i & ScrollMove)
1426 gtk_adjustment_set_value(adjust, gtk_adjustment_get_value(adjust) +
1427 direction * /* direction */
1428 ((arg->i & UnitLine || (arg->i & UnitBuffer && count)) ? (scrollstep * (count ? count : 1)) : (
1429 arg->i & UnitBuffer ? gtk_adjustment_get_page_size(adjust) / 2 :
1430 (count ? count : 1) * (gtk_adjustment_get_page_size(adjust) -
1431 (gtk_adjustment_get_page_size(adjust) > pagingkeep ? pagingkeep : 0)))));
1432 else
1433 gtk_adjustment_set_value(adjust,
1434 ((direction == 1) ? gtk_adjustment_get_upper : gtk_adjustment_get_lower)(adjust));
1435 update_state();
1437 return TRUE;
1440 gboolean
1441 zoom(const Arg *arg) {
1442 webkit_web_view_set_full_content_zoom(webview, (arg->i & ZoomFullContent) > 0);
1443 webkit_web_view_set_zoom_level(webview, (arg->i & ZoomOut) ?
1444 webkit_web_view_get_zoom_level(webview) +
1445 (((float)(count ? count : 1)) * (arg->i & (1 << 1) ? 1.0 : -1.0) * zoomstep) :
1446 (count ? (float)count / 100.0 : 1.0));
1447 return TRUE;
1450 gboolean
1451 fake_key_event(const Arg *a) {
1452 if(!embed) {
1453 return FALSE;
1455 Arg err;
1456 err.i = Error;
1457 Display *xdpy;
1458 if ( (xdpy = XOpenDisplay(NULL)) == NULL ) {
1459 err.s = "Couldn't find the XDisplay.";
1460 echo(&err);
1461 return FALSE;
1464 XKeyEvent xk;
1465 xk.display = xdpy;
1466 xk.subwindow = None;
1467 xk.time = CurrentTime;
1468 xk.same_screen = True;
1469 xk.x = xk.y = xk.x_root = xk.y_root = 1;
1470 xk.window = embed;
1471 xk.state = a->i;
1473 if( ! a->s ) {
1474 err.s = "Zero pointer as argument! Check your config.h";
1475 echo(&err);
1476 return FALSE;
1479 KeySym keysym;
1480 if( (keysym = XStringToKeysym(a->s)) == NoSymbol ) {
1481 err.s = g_strdup_printf("Couldn't translate %s to keysym", a->s );
1482 echo(&err);
1483 return FALSE;
1486 if( (xk.keycode = XKeysymToKeycode(xdpy, keysym)) == NoSymbol ) {
1487 err.s = "Couldn't translate keysym to keycode";
1488 echo(&err);
1489 return FALSE;
1492 xk.type = KeyPress;
1493 if( !XSendEvent(xdpy, embed, True, KeyPressMask, (XEvent *)&xk) ) {
1494 err.s = "XSendEvent failed";
1495 echo(&err);
1496 return FALSE;
1498 XFlush(xdpy);
1500 return TRUE;
1504 gboolean
1505 commandhistoryfetch(const Arg *arg) {
1506 if (arg->i == DirectionPrev) {
1507 commandpointer--;
1508 if (commandpointer < 0)
1509 commandpointer = maxcommands - 1;
1510 } else {
1511 commandpointer++;
1512 if (commandpointer == COMMANDHISTSIZE || commandpointer == maxcommands)
1513 commandpointer = 0;
1516 if (commandpointer < 0)
1517 return FALSE;
1519 gtk_entry_set_text(GTK_ENTRY(inputbox), commandhistory[commandpointer ]);
1520 gtk_editable_set_position(GTK_EDITABLE(inputbox), -1);
1521 return TRUE;
1525 gboolean
1526 bookmark(const Arg *arg) {
1527 FILE *f;
1528 const char *filename;
1529 const char *uri = webkit_web_view_get_uri(webview);
1530 const char *title = webkit_web_view_get_title(webview);
1531 filename = g_strdup_printf(BOOKMARKS_STORAGE_FILENAME);
1532 f = fopen(filename, "a");
1533 if (uri == NULL || strlen(uri) == 0) {
1534 set_error("No URI found to bookmark.");
1535 return FALSE;
1537 if (f != NULL) {
1538 fprintf(f, "%s", uri);
1539 if (title != NULL) {
1540 fprintf(f, "%s", " ");
1541 fprintf(f, "%s", title);
1543 if (arg->s && strlen(arg->s)) {
1544 build_taglist(arg, f);
1546 fprintf(f, "%s", "\n");
1547 fclose(f);
1548 give_feedback( "Bookmark saved" );
1549 return TRUE;
1550 } else {
1551 set_error("Bookmarks file not found.");
1552 return FALSE;
1556 gboolean
1557 history() {
1558 FILE *f;
1559 const char *filename;
1560 const char *uri = webkit_web_view_get_uri(webview);
1561 const char *title = webkit_web_view_get_title(webview);
1562 char *entry, buffer[512], *new;
1563 int n, i = 0;
1564 gboolean finished = FALSE;
1565 if (uri != NULL) {
1566 if (title != NULL) {
1567 entry = malloc((strlen(uri) + strlen(title) + 2) * sizeof(char));
1568 memset(entry, 0, strlen(uri) + strlen(title) + 2);
1569 } else {
1570 entry = malloc((strlen(uri) + 1) * sizeof(char));
1571 memset(entry, 0, strlen(uri) + 1);
1573 if (entry != NULL) {
1574 strncpy(entry, uri, strlen(uri));
1575 if (title != NULL) {
1576 strncat(entry, " ", 1);
1577 strncat(entry, title, strlen(title));
1579 n = strlen(entry);
1580 filename = g_strdup_printf(HISTORY_STORAGE_FILENAME);
1581 f = fopen(filename, "r");
1582 if (f != NULL) {
1583 new = (char *)malloc(HISTORY_MAX_ENTRIES * 512 * sizeof(char) + 1);
1584 if (new != NULL) {
1585 memset(new, 0, HISTORY_MAX_ENTRIES * 512 * sizeof(char) + 1);
1586 /* newest entries go on top */
1587 strncpy(new, entry, strlen(entry));
1588 strncat(new, "\n", 1);
1589 /* retain at most HISTORY_MAX_ENTIRES - 1 old entries */
1590 while (finished != TRUE) {
1591 if ((char *)NULL == fgets(buffer, 512, f)) {
1592 /* check if end of file was reached / error occured */
1593 if (!feof(f)) {
1594 break;
1596 /* end of file reached */
1597 finished = TRUE;
1598 continue;
1600 /* compare line (-1 because of newline character) */
1601 if (n != strlen(buffer) - 1 || strncmp(entry, buffer, n) != 0) {
1602 /* if the URI is already in history; we put it on top and skip it here */
1603 strncat(new, buffer, 512);
1604 i++;
1606 if ((i + 1) >= HISTORY_MAX_ENTRIES) {
1607 break;
1610 fclose(f);
1612 f = fopen(filename, "w");
1613 if (f != NULL) {
1614 fprintf(f, "%s", new);
1615 fclose(f);
1617 if (new != NULL) {
1618 free(new);
1619 new = NULL;
1623 if (entry != NULL) {
1624 free(entry);
1625 entry = NULL;
1628 return TRUE;
1631 static gboolean
1632 view_source(const Arg * arg) {
1633 gboolean current_mode = webkit_web_view_get_view_source_mode(webview);
1634 webkit_web_view_set_view_source_mode(webview, !current_mode);
1635 webkit_web_view_reload(webview);
1636 return TRUE;
1639 static gboolean
1640 focus_input(const Arg *arg) {
1641 static Arg a;
1643 a.s = g_strconcat("vimprobable_focus_input()", NULL);
1644 a.i = Silent;
1645 script(&a);
1646 update_state();
1647 return TRUE;
1650 static gboolean
1651 browser_settings(const Arg *arg) {
1652 char line[255];
1653 if (!arg->s) {
1654 set_error("Missing argument.");
1655 return FALSE;
1657 strncpy(line, arg->s, 254);
1658 if (process_set_line(line))
1659 return TRUE;
1660 else {
1661 set_error("Invalid setting.");
1662 return FALSE;
1666 char *
1667 search_word(int whichword) {
1668 int k = 0;
1669 static char word[240];
1670 char *c = my_pair.line;
1672 while (isspace(*c) && *c)
1673 c++;
1675 switch (whichword) {
1676 case 0:
1677 while (*c && !isspace (*c) && *c != '=' && k < 240) {
1678 word[k++] = *c;
1679 c++;
1681 word[k] = '\0';
1682 strncpy(my_pair.what, word, 20);
1683 break;
1684 case 1:
1685 while (*c && k < 240) {
1686 word[k++] = *c;
1687 c++;
1689 word[k] = '\0';
1690 strncpy(my_pair.value, word, 240);
1691 break;
1694 return c;
1697 static gboolean
1698 process_set_line(char *line) {
1699 char *c;
1700 int listlen, i;
1701 gboolean boolval;
1702 WebKitWebSettings *settings;
1704 settings = webkit_web_view_get_settings(webview);
1705 my_pair.line = line;
1706 c = search_word(0);
1707 if (!strlen(my_pair.what))
1708 return FALSE;
1710 while (isspace(*c) && *c)
1711 c++;
1713 if (*c == ':' || *c == '=')
1714 c++;
1716 my_pair.line = c;
1717 c = search_word(1);
1719 listlen = LENGTH(browsersettings);
1720 for (i = 0; i < listlen; i++) {
1721 if (strlen(browsersettings[i].name) == strlen(my_pair.what) && strncmp(browsersettings[i].name, my_pair.what, strlen(my_pair.what)) == 0) {
1722 /* mandatory argument not provided */
1723 if (strlen(my_pair.value) == 0)
1724 return FALSE;
1725 /* process qmark? */
1726 if (strlen(my_pair.what) == 5 && strncmp("qmark", my_pair.what, 5) == 0) {
1727 return (process_save_qmark(my_pair.value, webview));
1729 /* interpret boolean values */
1730 if (browsersettings[i].boolval) {
1731 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) {
1732 boolval = TRUE;
1733 } 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) {
1734 boolval = FALSE;
1735 } else {
1736 return FALSE;
1738 } else if (browsersettings[i].colourval) {
1739 /* interpret as hexadecimal colour */
1740 if (!parse_colour(my_pair.value)) {
1741 return FALSE;
1744 if (browsersettings[i].var != NULL) {
1745 /* write value into internal variable */
1746 /*if (browsersettings[i].intval) {
1747 browsersettings[i].var = atoi(my_pair.value);
1748 } else {*/
1749 strncpy(browsersettings[i].var, my_pair.value, strlen(my_pair.value) + 1);
1750 /*}*/
1752 if (strlen(browsersettings[i].webkit) > 0) {
1753 /* activate appropriate webkit setting */
1754 if (browsersettings[i].boolval) {
1755 g_object_set((GObject*)settings, browsersettings[i].webkit, boolval, NULL);
1756 } else if (browsersettings[i].intval) {
1757 g_object_set((GObject*)settings, browsersettings[i].webkit, atoi(my_pair.value), NULL);
1758 } else {
1759 g_object_set((GObject*)settings, browsersettings[i].webkit, my_pair.value, NULL);
1761 webkit_web_view_set_settings(webview, settings);
1763 /* toggle proxy usage? */
1764 if (strlen(my_pair.what) == 5 && strncmp("proxy", my_pair.what, 5) == 0) {
1765 toggle_proxy(boolval);
1768 /* Toggle scrollbars. */
1769 if (strlen(my_pair.what) == 10 && strncmp("scrollbars", my_pair.what, 10) == 0)
1770 toggle_scrollbars(boolval);
1772 /* reload page? */
1773 if (browsersettings[i].reload)
1774 webkit_web_view_reload(webview);
1775 return TRUE;
1778 return FALSE;
1781 gboolean
1782 process_line(char *line) {
1783 char *c = line;
1785 while (isspace(*c))
1786 c++;
1787 /* Ignore blank lines. */
1788 if (c[0] == '\0')
1789 return TRUE;
1790 if (strncmp(c, "map", 3) == 0) {
1791 c += 4;
1792 return process_map_line(c);
1793 } else if (strncmp(c, "set", 3) == 0) {
1794 c += 4;
1795 return process_set_line(c);
1797 return FALSE;
1800 static gboolean
1801 search_tag(const Arg * a) {
1802 FILE *f;
1803 const char *filename;
1804 const char *tag = a->s;
1805 char s[BUFFERSIZE], foundtag[40], url[BUFFERSIZE];
1806 int t, i, intag, k;
1808 if (!tag) {
1809 /* The user must give us something to load up. */
1810 set_error("Bookmark tag required with this option.");
1811 return FALSE;
1814 if (strlen(tag) > MAXTAGSIZE) {
1815 set_error("Tag too long.");
1816 return FALSE;
1819 filename = g_strdup_printf(BOOKMARKS_STORAGE_FILENAME);
1820 f = fopen(filename, "r");
1821 if (f == NULL) {
1822 set_error("Couldn't open bookmarks file.");
1823 return FALSE;
1825 while (fgets(s, BUFFERSIZE-1, f)) {
1826 intag = 0;
1827 t = strlen(s) - 1;
1828 while (isspace(s[t]))
1829 t--;
1830 if (s[t] != ']') continue;
1831 while (t > 0) {
1832 if (s[t] == ']') {
1833 if (!intag)
1834 intag = t;
1835 else
1836 intag = 0;
1837 } else {
1838 if (s[t] == '[') {
1839 if (intag) {
1840 i = 0;
1841 k = t + 1;
1842 while (k < intag)
1843 foundtag[i++] = s[k++];
1844 foundtag[i] = '\0';
1845 /* foundtag now contains the tag */
1846 if (strlen(foundtag) < MAXTAGSIZE && strcmp(tag, foundtag) == 0) {
1847 i = 0;
1848 while (isspace(s[i])) i++;
1849 k = 0;
1850 while (s[i] && !isspace(s[i])) url[k++] = s[i++];
1851 url[k] = '\0';
1852 Arg x = { .i = TargetNew, .s = url };
1853 open_arg(&x);
1856 intag = 0;
1859 t--;
1862 return TRUE;
1865 void
1866 toggle_proxy(gboolean onoff) {
1867 SoupURI *proxy_uri;
1868 char *filename, *new;
1869 int len;
1871 if (onoff == FALSE) {
1872 g_object_set(session, "proxy-uri", NULL, NULL);
1873 give_feedback("Proxy deactivated");
1874 } else {
1875 filename = (char *)g_getenv("http_proxy");
1877 /* Fallthrough to checking HTTP_PROXY as well, since this can also be
1878 * defined.
1880 if (filename == NULL)
1881 filename = (char *)g_getenv("HTTP_PROXY");
1883 if (filename != NULL && 0 < (len = strlen(filename))) {
1884 if (strstr(filename, "://") == NULL) {
1885 /* prepend http:// */
1886 new = g_malloc(sizeof("http://") + len);
1887 strcpy(new, "http://");
1888 memcpy(&new[sizeof("http://") - 1], filename, len + 1);
1889 proxy_uri = soup_uri_new(new);
1890 } else {
1891 proxy_uri = soup_uri_new(filename);
1893 g_object_set(session, "proxy-uri", proxy_uri, NULL);
1894 give_feedback("Proxy activated");
1899 void
1900 toggle_scrollbars(gboolean onoff) {
1901 if (onoff == TRUE) {
1902 adjust_h = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(viewport));
1903 adjust_v = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(viewport));
1905 else {
1906 adjust_v = gtk_range_get_adjustment(GTK_RANGE(scroll_v));
1907 adjust_h = gtk_range_get_adjustment(GTK_RANGE(scroll_h));
1909 gtk_widget_set_scroll_adjustments (GTK_WIDGET(webview), adjust_h, adjust_v);
1911 return;
1914 void
1915 update_url(const char *uri) {
1916 gboolean ssl = g_str_has_prefix(uri, "https://");
1917 GdkColor color;
1918 #ifdef ENABLE_HISTORY_INDICATOR
1919 char before[] = " [";
1920 char after[] = "]";
1921 gboolean back = webkit_web_view_can_go_back(webview);
1922 gboolean fwd = webkit_web_view_can_go_forward(webview);
1924 if (!back && !fwd)
1925 before[0] = after[0] = '\0';
1926 #endif
1927 gtk_label_set_markup((GtkLabel*)status_url, g_markup_printf_escaped(
1928 #ifdef ENABLE_HISTORY_INDICATOR
1929 "<span font=\"%s\">%s%s%s%s%s</span>", statusfont, uri,
1930 before, back ? "+" : "", fwd ? "-" : "", after
1931 #else
1932 "<span font=\"%s\">%s</span>", statusfont, uri
1933 #endif
1935 gdk_color_parse(ssl ? sslbgcolor : statusbgcolor, &color);
1936 gtk_widget_modify_bg(eventbox, GTK_STATE_NORMAL, &color);
1937 gdk_color_parse(ssl ? sslcolor : statuscolor, &color);
1938 gtk_widget_modify_fg(GTK_WIDGET(status_url), GTK_STATE_NORMAL, &color);
1939 gtk_widget_modify_fg(GTK_WIDGET(status_state), GTK_STATE_NORMAL, &color);
1942 void
1943 update_state() {
1944 char *markup;
1945 int download_count = g_list_length(activeDownloads);
1946 GString *status = g_string_new("");
1948 /* construct the status line */
1950 /* count, modkey and input buffer */
1951 g_string_append_printf(status, "%.0d", count);
1952 if (current_modkey) g_string_append_c(status, current_modkey);
1953 if (inputBuffer[0]) g_string_append_printf(status, " %s", inputBuffer);
1955 /* the number of active downloads */
1956 if (activeDownloads) {
1957 g_string_append_printf(status, " %d active %s", download_count,
1958 (download_count == 1) ? "download" : "downloads");
1961 #ifdef ENABLE_WGET_PROGRESS_BAR
1962 /* the progressbar */
1964 int progress = -1;
1965 char progressbar[progressbartick + 1];
1967 if (activeDownloads) {
1968 progress = 0;
1969 GList *ptr;
1971 for (ptr = activeDownloads; ptr; ptr = g_list_next(ptr)) {
1972 progress += 100 * webkit_download_get_progress(ptr->data);
1975 progress /= download_count;
1977 } else if (webkit_web_view_get_load_status(webview) != WEBKIT_LOAD_FINISHED
1978 && webkit_web_view_get_load_status(webview) != WEBKIT_LOAD_FAILED) {
1980 progress = webkit_web_view_get_progress(webview) * 100;
1983 if (progress >= 0) {
1984 ascii_bar(progressbartick, progress * progressbartick / 100, progressbar);
1985 g_string_append_printf(status, " %c%s%c",
1986 progressborderleft, progressbar, progressborderright);
1989 #endif
1991 /* and the current scroll position */
1993 int max = gtk_adjustment_get_upper(adjust_v) - gtk_adjustment_get_page_size(adjust_v);
1994 int val = (int)(gtk_adjustment_get_value(adjust_v) / max * 100);
1996 if (max == 0)
1997 g_string_append(status, " All");
1998 else if (val == 0)
1999 g_string_append(status, " Top");
2000 else if (val == 100)
2001 g_string_append(status, " Bot");
2002 else
2003 g_string_append_printf(status, " %d%%", val);
2007 markup = g_markup_printf_escaped("<span font=\"%s\">%s</span>", statusfont, status->str);
2008 gtk_label_set_markup(GTK_LABEL(status_state), markup);
2010 g_string_free(status, TRUE);
2013 void
2014 setup_modkeys() {
2015 unsigned int i;
2016 modkeys = calloc(LENGTH(keys) + 1, sizeof(char));
2017 char *ptr = modkeys;
2019 for (i = 0; i < LENGTH(keys); i++)
2020 if (keys[i].modkey && !strchr(modkeys, keys[i].modkey))
2021 *(ptr++) = keys[i].modkey;
2022 modkeys = realloc(modkeys, &ptr[0] - &modkeys[0] + 1);
2025 void
2026 setup_gui() {
2027 scroll_h = GTK_SCROLLBAR(gtk_hscrollbar_new(NULL));
2028 scroll_v = GTK_SCROLLBAR(gtk_vscrollbar_new(NULL));
2029 adjust_h = gtk_range_get_adjustment(GTK_RANGE(scroll_h));
2030 adjust_v = gtk_range_get_adjustment(GTK_RANGE(scroll_v));
2031 if (embed) {
2032 window = (GtkWindow *)gtk_plug_new(embed);
2033 } else {
2034 window = (GtkWindow *)gtk_window_new(GTK_WINDOW_TOPLEVEL);
2035 gtk_window_set_wmclass(GTK_WINDOW(window), "vimprobable2", "Vimprobable2");
2037 gtk_window_set_default_size(GTK_WINDOW(window), 640, 480);
2038 box = GTK_BOX(gtk_vbox_new(FALSE, 0));
2039 inputbox = gtk_entry_new();
2040 webview = (WebKitWebView*)webkit_web_view_new();
2041 GtkBox *statusbar = GTK_BOX(gtk_hbox_new(FALSE, 0));
2042 eventbox = gtk_event_box_new();
2043 status_url = gtk_label_new(NULL);
2044 status_state = gtk_label_new(NULL);
2045 GdkColor bg;
2046 PangoFontDescription *font;
2047 GdkGeometry hints = { 1, 1 };
2048 inspector = webkit_web_view_get_inspector(WEBKIT_WEB_VIEW(webview));
2050 clipboards[0] = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
2051 clipboards[1] = gtk_clipboard_get(GDK_NONE);
2052 setup_settings();
2053 gdk_color_parse(statusbgcolor, &bg);
2054 gtk_widget_modify_bg(eventbox, GTK_STATE_NORMAL, &bg);
2055 gtk_widget_set_name(GTK_WIDGET(window), "Vimprobable2");
2056 gtk_window_set_geometry_hints(window, NULL, &hints, GDK_HINT_MIN_SIZE);
2058 #ifdef DISABLE_SCROLLBAR
2059 viewport = gtk_scrolled_window_new(NULL, NULL);
2060 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(viewport), GTK_POLICY_NEVER, GTK_POLICY_NEVER);
2061 #else
2062 /* Ensure we still see scrollbars. */
2063 GtkWidget *viewport = gtk_scrolled_window_new(adjust_h, adjust_v);
2064 #endif
2066 setup_signals();
2067 gtk_container_add(GTK_CONTAINER(viewport), GTK_WIDGET(webview));
2069 /* Ensure we set the scroll adjustments now, so that if we're not drawing
2070 * titlebars, we can still scroll.
2072 gtk_widget_set_scroll_adjustments(GTK_WIDGET(webview), adjust_h, adjust_v);
2074 font = pango_font_description_from_string(urlboxfont[0]);
2075 gtk_widget_modify_font(GTK_WIDGET(inputbox), font);
2076 pango_font_description_free(font);
2077 gtk_entry_set_inner_border(GTK_ENTRY(inputbox), NULL);
2078 gtk_misc_set_alignment(GTK_MISC(status_url), 0.0, 0.0);
2079 gtk_misc_set_alignment(GTK_MISC(status_state), 1.0, 0.0);
2080 gtk_box_pack_start(statusbar, status_url, TRUE, TRUE, 2);
2081 gtk_box_pack_start(statusbar, status_state, FALSE, FALSE, 2);
2082 gtk_container_add(GTK_CONTAINER(eventbox), GTK_WIDGET(statusbar));
2083 gtk_box_pack_start(box, viewport, TRUE, TRUE, 0);
2084 gtk_box_pack_start(box, eventbox, FALSE, FALSE, 0);
2085 gtk_entry_set_has_frame(GTK_ENTRY(inputbox), FALSE);
2086 gtk_box_pack_end(box, inputbox, FALSE, FALSE, 0);
2087 gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(box));
2088 gtk_widget_grab_focus(GTK_WIDGET(webview));
2089 gtk_widget_show_all(GTK_WIDGET(window));
2092 void
2093 setup_settings() {
2094 WebKitWebSettings *settings = (WebKitWebSettings*)webkit_web_settings_new();
2095 SoupURI *proxy_uri;
2096 char *filename, *new;
2097 int len;
2098 #ifdef ENABLE_COOKIE_SUPPORT
2099 SoupCookieJar *cookiejar;
2100 #endif
2101 session = webkit_get_default_session();
2102 g_object_set((GObject*)settings, "default-font-size", DEFAULT_FONT_SIZE, NULL);
2103 g_object_set((GObject*)settings, "enable-scripts", enablePlugins, NULL);
2104 g_object_set((GObject*)settings, "enable-plugins", enablePlugins, NULL);
2105 g_object_set((GObject*)settings, "enable-java-applet", enableJava, NULL);
2106 filename = g_strdup_printf(USER_STYLESHEET);
2107 filename = g_strdup_printf("file://%s", filename);
2108 g_object_set((GObject*)settings, "user-stylesheet-uri", filename, NULL);
2109 g_object_set((GObject*)settings, "user-agent", useragent, NULL);
2110 g_object_get((GObject*)settings, "zoom-step", &zoomstep, NULL);
2111 webkit_web_view_set_settings(webview, settings);
2112 #ifdef ENABLE_COOKIE_SUPPORT
2113 filename = g_strdup_printf(COOKIES_STORAGE_FILENAME);
2114 cookiejar = soup_cookie_jar_text_new(filename, COOKIES_STORAGE_READONLY);
2115 g_free(filename);
2116 soup_session_add_feature(session, (SoupSessionFeature*)cookiejar);
2117 #endif
2118 /* proxy */
2119 if (use_proxy == TRUE) {
2120 filename = (char *)g_getenv("http_proxy");
2121 if (filename != NULL && 0 < (len = strlen(filename))) {
2122 if (strstr(filename, "://") == NULL) {
2123 /* prepend http:// */
2124 new = g_malloc(sizeof("http://") + len);
2125 strcpy(new, "http://");
2126 memcpy(&new[sizeof("http://") - 1], filename, len + 1);
2127 proxy_uri = soup_uri_new(new);
2128 } else {
2129 proxy_uri = soup_uri_new(filename);
2131 g_object_set(session, "proxy-uri", proxy_uri, NULL);
2136 void
2137 setup_signals() {
2138 /* window */
2139 g_object_connect((GObject*)window,
2140 "signal::destroy", (GCallback)window_destroyed_cb, NULL,
2141 NULL);
2142 /* webview */
2143 g_object_connect((GObject*)webview,
2144 "signal::title-changed", (GCallback)webview_title_changed_cb, NULL,
2145 "signal::load-progress-changed", (GCallback)webview_progress_changed_cb, NULL,
2146 "signal::load-committed", (GCallback)webview_load_committed_cb, NULL,
2147 "signal::load-finished", (GCallback)webview_load_finished_cb, NULL,
2148 "signal::navigation-policy-decision-requested", (GCallback)webview_navigation_cb, NULL,
2149 "signal::new-window-policy-decision-requested", (GCallback)webview_new_window_cb, NULL,
2150 "signal::mime-type-policy-decision-requested", (GCallback)webview_mimetype_cb, NULL,
2151 "signal::download-requested", (GCallback)webview_download_cb, NULL,
2152 "signal::key-press-event", (GCallback)webview_keypress_cb, NULL,
2153 "signal::hovering-over-link", (GCallback)webview_hoverlink_cb, NULL,
2154 "signal::console-message", (GCallback)webview_console_cb, NULL,
2155 "signal::create-web-view", (GCallback)webview_open_in_new_window_cb, NULL,
2156 "signal::event", (GCallback)notify_event_cb, NULL,
2157 NULL);
2158 /* webview adjustment */
2159 g_object_connect((GObject*)adjust_v,
2160 "signal::value-changed", (GCallback)webview_scroll_cb, NULL,
2161 NULL);
2162 /* inputbox */
2163 g_object_connect((GObject*)inputbox,
2164 "signal::activate", (GCallback)inputbox_activate_cb, NULL,
2165 "signal::key-press-event", (GCallback)inputbox_keypress_cb, NULL,
2166 "signal::key-release-event", (GCallback)inputbox_keyrelease_cb, NULL,
2167 #ifdef ENABLE_INCREMENTAL_SEARCH
2168 "signal::changed", (GCallback)inputbox_changed_cb, NULL,
2169 #endif
2170 NULL);
2171 /* inspector */
2172 g_signal_connect((GObject*)inspector,
2173 "inspect-web-view", (GCallback)inspector_inspect_web_view_cb, NULL);
2177 main(int argc, char *argv[]) {
2178 static Arg a;
2179 static char url[256] = "";
2180 static gboolean ver = false;
2181 static const char *cfile = NULL;
2182 static GOptionEntry opts[] = {
2183 { "version", 'v', 0, G_OPTION_ARG_NONE, &ver, "print version", NULL },
2184 { "embed", 'e', 0, G_OPTION_ARG_STRING, &winid, "embedded", NULL },
2185 { "configfile", 'c', 0, G_OPTION_ARG_STRING, &cfile, "config file", NULL },
2186 { NULL }
2188 static GError *err;
2189 args = argv;
2191 /* command line argument: version */
2192 if (!gtk_init_with_args(&argc, &argv, "[<uri>]", opts, NULL, &err)) {
2193 g_printerr("can't init gtk: %s\n", err->message);
2194 g_error_free(err);
2195 return EXIT_FAILURE;
2198 if (ver) {
2199 printf("%s\n", useragent);
2200 return EXIT_SUCCESS;
2203 if (cfile)
2204 configfile = g_strdup_printf(cfile);
2205 else
2206 configfile = g_strdup_printf(RCFILE);
2208 if (!g_thread_supported())
2209 g_thread_init(NULL);
2211 if (winid)
2212 embed = atoi(winid);
2214 setup_modkeys();
2215 make_keyslist();
2216 setup_gui();
2218 /* read config file */
2219 if (!read_rcfile(configfile)) {
2220 free(configfile);
2221 a.i = Error;
2222 a.s = g_strdup_printf("Error in config file");
2223 echo(&a);
2224 g_free(a.s);
2227 /* command line argument: URL */
2228 if (argc > 1) {
2229 strncpy(url, argv[argc - 1], 255);
2230 } else {
2231 strncpy(url, startpage, 255);
2234 a.i = TargetCurrent;
2235 a.s = url;
2236 open_arg(&a);
2237 gtk_main();
2239 return EXIT_SUCCESS;