updating version number
[vimprobable2.git] / main.c
blob87be23941084f6c9be0ddd5e538c1c45310edb6b
1 /*
2 (c) 2009 by Leon Winter
3 (c) 2009 by Hannes Schueller
4 see LICENSE file
5 */
7 #include <stdlib.h>
8 #include <string.h>
9 #include <gtk/gtk.h>
10 #include <gdk/gdkkeysyms.h>
11 #include <webkit/webkit.h>
12 #include <libsoup/soup.h>
13 #include <JavaScriptCore/JavaScript.h>
14 #include "vimprobable.h"
16 /* inline js fill be filled by js-merge-helper.pl */
17 #define JS_SETUP
19 /* remove numlock symbol from keymask */
20 #define CLEAN(mask) (mask & ~(GDK_MOD2_MASK) & ~(GDK_BUTTON1_MASK) & ~(GDK_BUTTON2_MASK) & ~(GDK_BUTTON3_MASK) & ~(GDK_BUTTON4_MASK) & ~(GDK_BUTTON5_MASK))
22 /* callbacks here */
23 static void window_destroyed_cb(GtkWidget *window, gpointer func_data);
24 static void webview_title_changed_cb(WebKitWebView *webview, WebKitWebFrame *frame, char *title, gpointer user_data);
25 static void webview_load_committed_cb(WebKitWebView *webview, WebKitWebFrame *frame, gpointer user_data);
26 static void webview_title_changed_cb(WebKitWebView *webview, WebKitWebFrame *frame, char *title, gpointer user_data);
27 static void webview_progress_changed_cb(WebKitWebView *webview, int progress, gpointer user_data);
28 static void webview_load_committed_cb(WebKitWebView *webview, WebKitWebFrame *frame, gpointer user_data);
29 static void webview_load_finished_cb(WebKitWebView *webview, WebKitWebFrame *frame, gpointer user_data);
30 static gboolean webview_navigation_cb(WebKitWebView *webview, WebKitWebFrame *frame, WebKitNetworkRequest *request,
31 WebKitWebPolicyDecision *decision, gpointer user_data);
32 static gboolean webview_open_in_new_window_cb(WebKitWebView *webview, WebKitWebFrame *frame, gpointer user_data);
33 static gboolean webview_new_window_cb(WebKitWebView *webview, WebKitWebFrame *frame, WebKitNetworkRequest *request,
34 WebKitWebNavigationAction *action, WebKitWebPolicyDecision *decision, gpointer user_data);
35 static gboolean webview_mimetype_cb(WebKitWebView *webview, WebKitWebFrame *frame, WebKitNetworkRequest *request,
36 char *mime_type, WebKitWebPolicyDecision *decision, gpointer user_data);
37 static gboolean webview_download_cb(WebKitWebView *webview, WebKitDownload *download, gpointer user_data);
38 static gboolean webview_keypress_cb(WebKitWebView *webview, GdkEventKey *event);
39 static void webview_hoverlink_cb(WebKitWebView *webview, char *title, char *link, gpointer data);
40 static gboolean webview_console_cb(WebKitWebView *webview, char *message, int line, char *source, gpointer user_data);
41 static void webview_scroll_cb(GtkAdjustment *adjustment, gpointer user_data);
42 static void inputbox_activate_cb(GtkEntry *entry, gpointer user_data);
43 static gboolean inputbox_keypress_cb(GtkEntry *entry, GdkEventKey *event);
44 static gboolean inputbox_keyrelease_cb(GtkEntry *entry, GdkEventKey *event);
45 static gboolean notify_event_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data);
47 /* functions */
48 static gboolean complete(const Arg *arg);
49 static gboolean descend(const Arg *arg);
50 static gboolean echo(const Arg *arg);
51 static gboolean input(const Arg *arg);
52 static gboolean navigate(const Arg *arg);
53 static gboolean number(const Arg *arg);
54 static gboolean open(const Arg *arg);
55 static gboolean paste(const Arg *arg);
56 static gboolean quit(const Arg *arg);
57 static gboolean search(const Arg *arg);
58 static gboolean set(const Arg *arg);
59 static gboolean script(const Arg *arg);
60 static gboolean scroll(const Arg *arg);
61 static gboolean yank(const Arg *arg);
62 static gboolean zoom(const Arg *arg);
63 static void update_url();
64 static void update_state();
65 static void setup_modkeys();
66 static void setup_gui();
67 static void setup_settings();
68 static void setup_signals();
69 static void ascii_bar(int total, int state, char *string);
70 static gchar *jsapi_ref_to_string(JSContextRef context, JSValueRef ref);
71 static void jsapi_evaluate_script(const gchar *script, gchar **value, gchar **message);
72 static gboolean toggle_plugins();
73 static gboolean toggle_images();
74 static gboolean bookmark();
75 static void history();
77 /* variables */
78 static GtkWidget *window;
79 static GtkBox *box;
80 static GtkAdjustment *adjust_h;
81 static GtkAdjustment *adjust_v;
82 static GtkWidget *inputbox;
83 static GtkWidget *eventbox;
84 static GtkWidget *status_url;
85 static GtkWidget *status_state;
86 static WebKitWebView *webview;
87 static SoupSession *session;
88 static GtkClipboard *clipboards[2];
90 static char **args;
91 static unsigned int mode = ModeNormal;
92 static unsigned int count = 0;
93 static float zoomstep;
94 static char scroll_state[4] = "\0";
95 static char *modkeys;
96 static char current_modkey;
97 static char *search_handle;
98 static gboolean search_direction;
99 static gboolean echo_active = FALSE;
101 char rememberedURI[128] = "";
102 char inputKey[2];
103 char inputBuffer[5] = "";
105 #include "config.h"
107 /* callbacks */
108 void
109 window_destroyed_cb(GtkWidget *window, gpointer func_data) {
110 quit(NULL);
113 void
114 webview_title_changed_cb(WebKitWebView *webview, WebKitWebFrame *frame, char *title, gpointer user_data) {
115 gtk_window_set_title(GTK_WINDOW(window), title);
118 void
119 webview_progress_changed_cb(WebKitWebView *webview, int progress, gpointer user_data) {
120 #ifdef ENABLE_GTK_PROGRESS_BAR
121 gtk_entry_set_progress_fraction(GTK_ENTRY(inputbox), progress == 100 ? 0 : (double)progress/100);
122 #endif
123 update_state();
126 #ifdef ENABLE_WGET_PROGRESS_BAR
127 void
128 ascii_bar(int total, int state, char *string) {
129 int i;
131 for(i = 0; i < state; i++)
132 string[i] = progressbartickchar;
133 string[i++] = progressbarcurrent;
134 for(; i < total; i++)
135 string[i] = progressbarspacer;
136 string[i] = '\0';
138 #endif
140 void
141 webview_load_committed_cb(WebKitWebView *webview, WebKitWebFrame *frame, gpointer user_data) {
142 const char *uri = webkit_web_view_get_uri(webview);
144 update_url(uri);
147 void
148 webview_load_finished_cb(WebKitWebView *webview, WebKitWebFrame *frame, gpointer user_data) {
149 Arg a = { .i = Silent, .s = JS_SETUP };
151 if (HISTORY_MAX_ENTRIES > 0)
152 history();
153 update_state();
154 script(&a);
157 gboolean
158 webview_navigation_cb(WebKitWebView *webview, WebKitWebFrame *frame, WebKitNetworkRequest *request,
159 WebKitWebPolicyDecision *decision, gpointer user_data) {
160 return FALSE;
163 static gboolean
164 webview_open_in_new_window_cb(WebKitWebView *webview, WebKitWebFrame *frame, gpointer user_data) {
165 Arg a = { .i = TargetNew, .s = (char*)webkit_web_view_get_uri(webview) };
166 if (strlen(rememberedURI) > 0) {
167 a.s = rememberedURI;
169 open(&a);
170 return FALSE;
173 gboolean
174 webview_new_window_cb(WebKitWebView *webview, WebKitWebFrame *frame, WebKitNetworkRequest *request,
175 WebKitWebNavigationAction *action, WebKitWebPolicyDecision *decision, gpointer user_data) {
176 Arg a = { .i = TargetNew, .s = (char*)webkit_network_request_get_uri(request) };
177 open(&a);
178 webkit_web_policy_decision_ignore(decision);
179 return TRUE;
182 gboolean
183 webview_mimetype_cb(WebKitWebView *webview, WebKitWebFrame *frame, WebKitNetworkRequest *request,
184 char *mime_type, WebKitWebPolicyDecision *decision, gpointer user_data) {
185 if (webkit_web_view_can_show_mime_type(webview, mime_type) == FALSE) {
186 webkit_web_policy_decision_download(decision);
187 WebKitDownload *download = webkit_download_new(request);
188 return webview_download_cb(webview, download, user_data);
189 } else {
190 return FALSE;
194 gboolean
195 webview_download_cb(WebKitWebView *webview, WebKitDownload *download, gpointer user_data) {
196 const gchar *filename;
197 gchar *uri, *path, *html;
198 filename = webkit_download_get_suggested_filename(download);
199 if (filename == NULL || strlen(filename) == 0) {
200 filename = "vimprobable_download";
202 path = g_build_filename(g_strdup_printf(DOWNLOADS_PATH), filename, NULL);
203 uri = g_strconcat("file://", path, NULL);
204 webkit_download_set_destination_uri(download, uri);
205 g_free(uri);
206 html = g_strdup_printf("Download <b>%s</b>...", filename);
207 webkit_web_view_load_html_string(webview, html, webkit_download_get_uri(download));
208 update_state();
209 g_free(html);
210 return TRUE;
213 gboolean
214 webview_keypress_cb(WebKitWebView *webview, GdkEventKey *event) {
215 unsigned int i;
216 Arg a = { .i = ModeNormal, .s = NULL };
218 switch (mode) {
219 case ModeNormal:
220 if(CLEAN(event->state) == 0) {
221 memset(inputBuffer, 0, 5);
222 if(current_modkey == 0 && ((event->keyval >= GDK_1 && event->keyval <= GDK_9)
223 || (event->keyval == GDK_0 && count))) {
224 count = (count ? count * 10 : 0) + (event->keyval - GDK_0);
225 update_state();
226 return TRUE;
227 } else if(strchr(modkeys, event->keyval) && current_modkey != event->keyval) {
228 current_modkey = event->keyval;
229 update_state();
230 return TRUE;
233 /* keybindings */
234 for(i = 0; i < LENGTH(keys); i++)
235 if(keys[i].mask == CLEAN(event->state)
236 && (keys[i].modkey == current_modkey
237 || (!keys[i].modkey && !current_modkey)
238 || keys[i].modkey == GDK_VoidSymbol) /* wildcard */
239 && keys[i].key == event->keyval
240 && keys[i].func)
241 if(keys[i].func(&keys[i].arg)) {
242 current_modkey = count = 0;
243 update_state();
244 return TRUE;
246 break;
247 case ModeInsert:
248 if(CLEAN(event->state) == 0 && event->keyval == GDK_Escape) {
249 a.i = Silent;
250 a.s = "clearfocus()";
251 script(&a);
252 a.i = ModeNormal;
253 return set(&a);
255 case ModePassThrough:
256 if(CLEAN(event->state) == 0 && event->keyval == GDK_Escape) {
257 echo(&a);
258 set(&a);
259 return TRUE;
261 break;
262 case ModeSendKey:
263 echo(&a);
264 set(&a);
265 break;
266 case ModeHints:
267 if(CLEAN(event->state) == 0 && event->keyval == GDK_Escape) {
268 a.i = Silent;
269 a.s = "clear()";
270 script(&a);
271 a.i = ModeNormal;
272 count = 0;
273 return set(&a);
274 } else if (CLEAN(event->state) == 0 && ((event->keyval >= GDK_1 && event->keyval <= GDK_9)
275 || (event->keyval == GDK_0 && count))) {
276 /* allow a zero as non-first number */
277 count = (count ? count * 10 : 0) + (event->keyval - GDK_0);
278 if (strlen(inputBuffer) <= 4) {
279 memset(inputKey, 0, 2);
280 sprintf(inputKey, "%d", (event->keyval - GDK_0));
281 strncat(inputBuffer, inputKey, 1);
282 a.s = g_strconcat("update_hints(", inputBuffer, ")", NULL);
283 a.i = Silent;
284 script(&a);
285 update_state();
286 } else {
287 /* overflow */
288 a.s = "clear()";
289 a.i = Silent;
290 script(&a);
291 a.i = ModeNormal;
292 count = 0;
293 update_state();
295 } else if (CLEAN(event->state) == 0 && event->keyval == GDK_Return) {
296 a.s = g_strconcat("fire(", inputBuffer, ")", NULL);
297 a.i = Silent;
298 script(&a);
299 memset(inputBuffer, 0, 5);
300 count = 0;
301 update_state();
302 } else if (CLEAN(event->state) == 0 && event->keyval == GDK_BackSpace) {
303 if (strlen(inputBuffer) > 0) {
304 strncpy((inputBuffer + strlen(inputBuffer) - 1), "\0", 1);
305 a.s = g_strconcat("update_hints(", inputBuffer, ")", NULL);
306 a.i = Silent;
307 count = ((count >= 10) ? count/10 : 0);
308 script(&a);
309 update_state();
312 break;
314 return FALSE;
317 void
318 webview_hoverlink_cb(WebKitWebView *webview, char *title, char *link, gpointer data) {
319 const char *uri = webkit_web_view_get_uri(webview);
321 memset(rememberedURI, 0, 128);
322 if(link) {
323 gtk_label_set_markup(GTK_LABEL(status_url), g_markup_printf_escaped("<span font=\"%s\">Link: %s</span>", statusfont, link));
324 strncpy(rememberedURI, link, 128);
325 } else
326 update_url(uri);
329 gboolean
330 webview_console_cb(WebKitWebView *webview, char *message, int line, char *source, gpointer user_data) {
331 Arg a;
333 if(!strcmp(message, "hintmode_off") || !strcmp(message, "insertmode_off")) {
334 a.i = ModeNormal;
335 return set(&a);
336 } else if(!strcmp(message, "insertmode_on")) {
337 a.i = ModeInsert;
338 return set(&a);
340 return FALSE;
343 void
344 webview_scroll_cb(GtkAdjustment *adjustment, gpointer user_data) {
345 update_state();
348 void
349 inputbox_activate_cb(GtkEntry *entry, gpointer user_data) {
350 char *text;
351 guint16 length = gtk_entry_get_text_length(entry);
352 Arg a;
353 int i;
354 size_t len;
355 gboolean success = FALSE, forward = FALSE;
357 a.i = HideCompletion;
358 complete(&a);
359 if(length < 2)
360 return;
361 text = (char*)gtk_entry_get_text(entry);
362 if(text[0] == ':') {
363 for(i = 0; i < LENGTH(commands); i++) {
364 len = strlen(commands[i].cmd);
365 if(length >= len && !strncmp(&text[1], commands[i].cmd, len) && (text[len + 1] == ' ' || !text[len + 1])) {
366 a.i = commands[i].arg.i;
367 a.s = length > len + 2 ? &text[len + 2] : commands[i].arg.s;
368 if((success = commands[i].func(&a)))
369 break;
372 if(!success) {
373 a.i = Error;
374 a.s = g_strdup_printf("Not a browser command: %s", &text[1]);
375 echo(&a);
376 g_free(a.s);
378 } else if((forward = text[0] == '/') || text[0] == '?') {
379 webkit_web_view_unmark_text_matches(webview);
380 #ifdef ENABLE_MATCH_HIGHLITING
381 webkit_web_view_mark_text_matches(webview, &text[1], FALSE, 0);
382 webkit_web_view_set_highlight_text_matches(webview, TRUE);
383 #endif
384 count = 0;
385 a.s =& text[1];
386 a.i = searchoptions | (forward ? DirectionForward : DirectionBackwards);
387 search(&a);
388 } else
389 return;
390 if(!echo_active)
391 gtk_entry_set_text(entry, "");
392 gtk_widget_grab_focus(GTK_WIDGET(webview));
395 gboolean
396 inputbox_keypress_cb(GtkEntry *entry, GdkEventKey *event) {
397 Arg a;
399 if(event->keyval == GDK_Escape) {
400 a.i = HideCompletion;
401 complete(&a);
402 a.i = ModeNormal;
403 return set(&a);
404 } else if(event->keyval == GDK_Tab) {
405 a.i = DirectionNext;
406 return complete(&a);
407 } else if(event->keyval == GDK_ISO_Left_Tab) {
408 a.i = DirectionPrev;
409 return complete(&a);
411 return FALSE;
414 gboolean
415 notify_event_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data) {
416 int i;
417 if (mode == ModeNormal && event->type == GDK_BUTTON_RELEASE) {
418 /* handle mouse click events */
419 for (i = 0; i < LENGTH(mouse); i++) {
420 if (mouse[i].mask == CLEAN(event->button.state)
421 && (mouse[i].modkey == current_modkey
422 || (!mouse[i].modkey && !current_modkey)
423 || mouse[i].modkey == GDK_VoidSymbol) /* wildcard */
424 && mouse[i].button == event->button.button
425 && mouse[i].func) {
426 if (mouse[i].func(&mouse[i].arg)) {
427 current_modkey = count = 0;
428 update_state();
429 return TRUE;
434 return FALSE;
437 static gboolean inputbox_keyrelease_cb(GtkEntry *entry, GdkEventKey *event) {
438 Arg a;
439 guint16 length = gtk_entry_get_text_length(entry);
440 #ifdef ENABLE_INCREMENTAL_SEARCH
441 char *text = (char*)gtk_entry_get_text(entry);
442 gboolean forward = FALSE;
443 #endif
444 if(!length) {
445 a.i = HideCompletion;
446 complete(&a);
447 a.i = ModeNormal;
448 return set(&a);
450 #ifdef ENABLE_INCREMENTAL_SEARCH
451 else if(length > 1 && ((forward = text[0] == '/') || text[0] == '?')) {
452 webkit_web_view_unmark_text_matches(webview);
453 webkit_web_view_search_text(webview, &text[1], searchoptions & CaseSensitive, forward, searchoptions & Wrapping);
455 #endif
456 return FALSE;
459 /* funcs here */
460 gboolean
461 complete(const Arg *arg) {
462 FILE *f;
463 const char *filename;
464 char *str, *s, *p, *markup, *entry, *searchfor, command[32] = "", suggline[512] = "", *url, **suggurls;
465 size_t listlen, len, cmdlen;
466 int i, spacepos;
467 gboolean highlight = FALSE, finished = FALSE;
468 GtkBox *row;
469 GtkWidget *row_eventbox, *el;
470 GtkBox *_table;
471 GdkColor color;
472 static GtkWidget *table, **widgets, *top_border;
473 static char **suggestions, *prefix;
474 static int n = 0, current = -1;
476 str = (char*)gtk_entry_get_text(GTK_ENTRY(inputbox));
477 len = strlen(str);
478 if((len == 0 || str[0] != ':') && arg->i != HideCompletion)
479 return TRUE;
480 if(prefix) {
481 if(arg->i != HideCompletion && widgets && current != -1 && !strcmp(&str[1], suggestions[current])) {
482 gdk_color_parse(completionbgcolor[0], &color);
483 gtk_widget_modify_bg(widgets[current], GTK_STATE_NORMAL, &color);
484 current = (n + current + (arg->i == DirectionPrev ? -1 : 1)) % n;
485 if((arg->i == DirectionNext && current == 0)
486 || (arg->i == DirectionPrev && current == n - 1))
487 current = -1;
488 } else {
489 free(widgets);
490 free(suggestions);
491 free(prefix);
492 gtk_widget_destroy(GTK_WIDGET(table));
493 gtk_widget_destroy(GTK_WIDGET(top_border));
494 table = NULL;
495 widgets = NULL;
496 suggestions = NULL;
497 prefix = NULL;
498 n = 0;
499 current = -1;
500 if(arg->i == HideCompletion)
501 return TRUE;
503 } else if(arg->i == HideCompletion)
504 return TRUE;
505 if(!widgets) {
506 prefix = strdup(str);
507 listlen = LENGTH(commands);
508 widgets = malloc(sizeof(GtkWidget*) * listlen);
509 suggestions = malloc(sizeof(char*) * listlen);
510 top_border = gtk_event_box_new();
511 gtk_widget_set_size_request(GTK_WIDGET(top_border), 0, 1);
512 gdk_color_parse(completioncolor[2], &color);
513 gtk_widget_modify_bg(top_border, GTK_STATE_NORMAL, &color);
514 table = gtk_event_box_new();
515 gdk_color_parse(completionbgcolor[0], &color);
516 _table = GTK_BOX(gtk_vbox_new(FALSE, 0));
517 highlight = len > 1;
518 if (strchr(str, ' ') == NULL) {
519 for(i = 0; i < listlen; i++) {
520 cmdlen = strlen(commands[i].cmd);
521 if(!highlight || (len - 1 <= cmdlen && !strncmp(&str[1], commands[i].cmd, len - 1))) {
522 p = s = malloc(sizeof(char*) * (highlight ? sizeof(COMPLETION_TAG_OPEN) + sizeof(COMPLETION_TAG_CLOSE) - 1 : 1) + cmdlen);
523 if(highlight) {
524 memcpy(p, COMPLETION_TAG_OPEN, sizeof(COMPLETION_TAG_OPEN) - 1);
525 memcpy((p += sizeof(COMPLETION_TAG_OPEN) - 1), &str[1], len - 1);
526 memcpy((p += len - 1), COMPLETION_TAG_CLOSE, sizeof(COMPLETION_TAG_CLOSE) - 1);
527 p += sizeof(COMPLETION_TAG_CLOSE) - 1;
529 memcpy(p, &commands[i].cmd[len - 1], cmdlen - len + 2);
530 row = GTK_BOX(gtk_hbox_new(FALSE, 0));
531 row_eventbox = gtk_event_box_new();
532 gtk_widget_modify_bg(row_eventbox, GTK_STATE_NORMAL, &color);
533 el = gtk_label_new(NULL);
534 markup = g_strconcat("<span font=\"", completionfont[0], "\" color=\"", completioncolor[0], "\">", s, "</span>", NULL);
535 free(s);
536 gtk_label_set_markup(GTK_LABEL(el), markup);
537 g_free(markup);
538 gtk_misc_set_alignment(GTK_MISC(el), 0, 0);
539 gtk_box_pack_start(row, el, TRUE, TRUE, 2);
540 gtk_container_add(GTK_CONTAINER(row_eventbox), GTK_WIDGET(row));
541 gtk_box_pack_start(_table, GTK_WIDGET(row_eventbox), FALSE, FALSE, 0);
542 suggestions[n] = commands[i].cmd;
543 widgets[n++] = row_eventbox;
546 } else {
547 /* URL completion using the current command */
548 entry = (char *)malloc(512 * sizeof(char));
549 if (entry != NULL) {
550 memset(entry, 0, 512);
551 suggurls = malloc(sizeof(char*) * listlen);
552 if (suggurls != NULL) {
553 spacepos = strcspn(str, " ");
554 searchfor = (str + spacepos + 1);
555 strncpy(command, (str + 1), spacepos - 1);
556 filename = g_strdup_printf(BOOKMARKS_STORAGE_FILENAME);
557 f = fopen(filename, "r");
558 if (f != NULL) {
559 while (finished != TRUE) {
560 if ((char *)NULL == fgets(entry, 512, f)) {
561 /* check if end of file was reached / error occured */
562 if (!feof(f)) {
563 break;
565 /* end of file reached */
566 finished = TRUE;
567 continue;
569 if (strstr(entry, searchfor) != NULL) {
570 /* found in bookmarks */
571 memset(suggline, 0, 512);
572 strncpy(suggline, command, 512);
573 strncat(suggline, " ", 1);
574 url = strtok(entry, " ");
575 strncat(suggline, url, 512 - strlen(suggline) - 1);
576 suggurls[n] = (char *)malloc(sizeof(char) * 512 + 1);
577 strncpy(suggurls[n], suggline, 512);
578 suggestions[n] = suggurls[n];
579 row = GTK_BOX(gtk_hbox_new(FALSE, 0));
580 row_eventbox = gtk_event_box_new();
581 gtk_widget_modify_bg(row_eventbox, GTK_STATE_NORMAL, &color);
582 el = gtk_label_new(NULL);
583 markup = g_strconcat("<span font=\"", completionfont[0], "\" color=\"", completioncolor[0], "\">", g_markup_escape_text(suggline, strlen(suggline)), "</span>", NULL);
584 gtk_label_set_markup(GTK_LABEL(el), markup);
585 g_free(markup);
586 gtk_misc_set_alignment(GTK_MISC(el), 0, 0);
587 gtk_box_pack_start(row, el, TRUE, TRUE, 2);
588 gtk_container_add(GTK_CONTAINER(row_eventbox), GTK_WIDGET(row));
589 gtk_box_pack_start(_table, GTK_WIDGET(row_eventbox), FALSE, FALSE, 0);
590 widgets[n++] = row_eventbox;
592 if (n >= listlen) {
593 break;
596 fclose(f);
597 /* history */
598 if (n < listlen) {
599 filename = g_strdup_printf(HISTORY_STORAGE_FILENAME);
600 f = fopen(filename, "r");
601 if (f != NULL) {
602 finished = FALSE;
603 while (finished != TRUE) {
604 if ((char *)NULL == fgets(entry, 512, f)) {
605 /* check if end of file was reached / error occured */
606 if (!feof(f)) {
607 break;
609 /* end of file reached */
610 finished = TRUE;
611 continue;
613 if (strstr(entry, searchfor) != NULL) {
614 /* found in history */
615 memset(suggline, 0, 512);
616 strncpy(suggline, command, 512);
617 strncat(suggline, " ", 1);
618 url = strtok(entry, " ");
619 strncat(suggline, url, 512 - strlen(suggline) - 1);
620 suggurls[n] = (char *)malloc(sizeof(char) * 512 + 1);
621 strncpy(suggurls[n], suggline, 512);
622 suggestions[n] = suggurls[n];
623 row = GTK_BOX(gtk_hbox_new(FALSE, 0));
624 row_eventbox = gtk_event_box_new();
625 gtk_widget_modify_bg(row_eventbox, GTK_STATE_NORMAL, &color);
626 el = gtk_label_new(NULL);
627 markup = g_strconcat("<span font=\"", completionfont[0], "\" color=\"", completioncolor[0], "\">", g_markup_escape_text(suggline, strlen(suggline)), "</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 gtk_box_pack_start(_table, GTK_WIDGET(row_eventbox), FALSE, FALSE, 0);
634 widgets[n++] = row_eventbox;
636 if (n >= listlen) {
637 break;
640 fclose(f);
644 if (suggurls != NULL) {
645 free(suggurls);
646 suggurls = NULL;
649 if (entry != NULL) {
650 free(entry);
651 entry = NULL;
655 widgets = realloc(widgets, sizeof(GtkWidget*) * n);
656 suggestions = realloc(suggestions, sizeof(char*) * n);
657 if(!n) {
658 gdk_color_parse(completionbgcolor[1], &color);
659 gtk_widget_modify_bg(table, GTK_STATE_NORMAL, &color);
660 el = gtk_label_new(NULL);
661 gtk_misc_set_alignment(GTK_MISC(el), 0, 0);
662 markup = g_strconcat("<span font=\"", completionfont[1], "\" color=\"", completioncolor[1], "\">No Completions</span>", NULL);
663 gtk_label_set_markup(GTK_LABEL(el), markup);
664 g_free(markup);
665 gtk_box_pack_start(_table, GTK_WIDGET(el), FALSE, FALSE, 0);
667 gtk_box_pack_start(box, GTK_WIDGET(top_border), FALSE, FALSE, 0);
668 gtk_container_add(GTK_CONTAINER(table), GTK_WIDGET(_table));
669 gtk_box_pack_start(box, GTK_WIDGET(table), FALSE, FALSE, 0);
670 gtk_widget_show_all(window);
671 if(!n)
672 return TRUE;
673 current = arg->i == DirectionPrev ? n - 1 : 0;
675 if(current != -1) {
676 gdk_color_parse(completionbgcolor[2], &color);
677 gtk_widget_modify_bg(GTK_WIDGET(widgets[current]), GTK_STATE_NORMAL, &color);
678 s = g_strconcat(":", suggestions[current], NULL);
679 gtk_entry_set_text(GTK_ENTRY(inputbox), s);
680 g_free(s);
681 } else
682 gtk_entry_set_text(GTK_ENTRY(inputbox), prefix);
683 gtk_editable_set_position(GTK_EDITABLE(inputbox), -1);
684 return TRUE;
687 gboolean
688 descend(const Arg *arg) {
689 char *source = (char*)webkit_web_view_get_uri(webview), *p = &source[0], *new;
690 int i, len;
691 count = count ? count : 1;
693 if(!source)
694 return TRUE;
695 if(arg->i == Rootdir) {
696 for(i = 0; i < 3; i++) /* get to the third slash */
697 if(!(p = strchr(++p, '/')))
698 return TRUE; /* if we cannot find it quit */
699 } else {
700 len = strlen(source);
701 if(!len) /* if string is empty quit */
702 return TRUE;
703 p = source + len; /* start at the end */
704 if(*(p - 1) == '/') /* /\/$/ is not an additional level */
705 ++count;
706 for(i = 0; i < count; i++)
707 while(*(p--) != '/' || *p == '/') /* count /\/+/ as one slash */
708 if(p == source) /* if we reach the first char pointer quit */
709 return TRUE;
710 ++p; /* since we do p-- in the while, we are pointing at
711 the char before the slash, so +1 */
713 len = p - source + 1; /* new length = end - start + 1 */
714 new = malloc(len);
715 memcpy(new, source, len);
716 new[len] = '\0';
717 webkit_web_view_load_uri(webview, new);
718 free(new);
719 return TRUE;
722 gboolean
723 echo(const Arg *arg) {
724 PangoFontDescription *font;
725 GdkColor color;
726 int index = !arg->s ? 0 : arg->i & (~NoAutoHide);
728 if(index < Info || index > Error)
729 return TRUE;
730 font = pango_font_description_from_string(urlboxfont[index]);
731 gtk_widget_modify_font(inputbox, font);
732 pango_font_description_free(font);
733 if(urlboxcolor[index])
734 gdk_color_parse(urlboxcolor[index], &color);
735 gtk_widget_modify_text(inputbox, GTK_STATE_NORMAL, urlboxcolor[index] ? &color : NULL);
736 if(urlboxbgcolor[index])
737 gdk_color_parse(urlboxbgcolor[index], &color);
738 gtk_widget_modify_base(inputbox, GTK_STATE_NORMAL, urlboxbgcolor[index] ? &color : NULL);
739 gtk_entry_set_text(GTK_ENTRY(inputbox), !arg->s ? "" : arg->s);
740 return TRUE;
743 gboolean
744 input(const Arg *arg) {
745 int pos = 0;
746 count = 0;
747 const char *url;
749 update_state();
750 /* to avoid things like :open URL :open URL2 or :open :open URL */
751 gtk_entry_set_text(GTK_ENTRY(inputbox), "");
752 gtk_editable_insert_text(GTK_EDITABLE(inputbox), arg->s, -1, &pos);
753 if(arg->i & InsertCurrentURL && (url = webkit_web_view_get_uri(webview)))
754 gtk_editable_insert_text(GTK_EDITABLE(inputbox), url, -1, &pos);
755 gtk_widget_grab_focus(inputbox);
756 gtk_editable_set_position(GTK_EDITABLE(inputbox), -1);
757 return TRUE;
760 gboolean
761 navigate(const Arg *arg) {
762 if(arg->i & NavigationForwardBack)
763 webkit_web_view_go_back_or_forward(webview, (arg->i == NavigationBack ? -1 : 1) * (count ? count : 1));
764 else if(arg->i & NavigationReloadActions)
765 (arg->i == NavigationReload ? webkit_web_view_reload : webkit_web_view_reload_bypass_cache)(webview);
766 else
767 webkit_web_view_stop_loading(webview);
768 return TRUE;
771 gboolean
772 number(const Arg *arg) {
773 const char *source = webkit_web_view_get_uri(webview);
774 char *uri, *p, *new;
775 int number, diff = (count ? count : 1) * (arg->i == Increment ? 1 : -1);
777 if(!source)
778 return TRUE;
779 uri = strdup(source); /* copy string */
780 p =& uri[0];
781 while(*p != '\0') /* goto the end of the string */
782 ++p;
783 --p;
784 while(*p >= '0' && *p <= '9') /* go back until non number char is reached */
785 --p;
786 if(*(++p) == '\0') { /* if no numbers were found abort */
787 free(uri);
788 return TRUE;
790 number = atoi(p) + diff; /* apply diff on number */
791 *p = '\0';
792 new = g_strdup_printf("%s%d", uri, number); /* create new uri */
793 webkit_web_view_load_uri(webview, new);
794 g_free(new);
795 free(uri);
796 return TRUE;
799 gboolean
800 open(const Arg *arg) {
801 char *argv[] = { *args, arg->s, NULL };
802 char *s = arg->s, *p, *new;
803 Arg a = { .i = NavigationReload };
804 int len, i;
806 if(!arg->s)
807 navigate(&a);
808 else if(arg->i == TargetCurrent) {
809 len = strlen(arg->s);
810 new = NULL, p = strchr(arg->s, ' ');
811 if(p) /* check for search engines */
812 for(i = 0; i < LENGTH(searchengines); i++)
813 if(!strncmp(arg->s, searchengines[i].handle, p - arg->s)) {
814 p = soup_uri_encode(++p, "&");
815 new = g_strdup_printf(searchengines[i].uri, p);
816 g_free(p);
817 break;
819 if(!new) {
820 if(len > 3 && strstr(arg->s, "://")) { /* valid url? */
821 p = new = g_malloc(len + 1);
822 while(*s != '\0') { /* strip whitespaces */
823 if(*s != ' ')
824 *(p++) = *s;
825 ++s;
827 *p = '\0';
828 } else if (strcspn(arg->s, "/") == 0) { /* prepend "file://" */
829 new = g_malloc(sizeof("file://") + len);
830 strcpy(new, "file://");
831 memcpy(&new[sizeof("file://") - 1], arg->s, len + 1);
832 } else if(p || !strchr(arg->s, '.')) { /* whitespaces or no dot? */
833 p = soup_uri_encode(arg->s, "&");
834 new = g_strdup_printf(defsearch->uri, p);
835 g_free(p);
836 } else { /* prepend "http://" */
837 new = g_malloc(sizeof("http://") + len);
838 strcpy(new, "http://");
839 memcpy(&new[sizeof("http://") - 1], arg->s, len + 1);
842 webkit_web_view_load_uri(webview, new);
843 g_free(new);
844 } else
845 g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL);
846 return TRUE;
849 gboolean
850 yank(const Arg *arg) {
851 const char *url;
852 Arg a = { .i = Info };
854 if(arg->i & SourceURL) {
855 url = webkit_web_view_get_uri(webview);
856 if(!url)
857 return TRUE;
858 a.s = g_strdup_printf("Yanked %s", url);
859 echo(&a);
860 g_free(a.s);
861 if(arg->i & ClipboardPrimary)
862 gtk_clipboard_set_text(clipboards[0], url, -1);
863 if(arg->i & ClipboardGTK)
864 gtk_clipboard_set_text(clipboards[1], url, -1);
865 } else
866 webkit_web_view_copy_clipboard(webview);
867 return TRUE;
870 gboolean
871 paste(const Arg *arg) {
872 Arg a = { .i = arg->i & TargetNew, .s = NULL };
874 if(arg->i & ClipboardPrimary)
875 a.s = gtk_clipboard_wait_for_text(clipboards[0]);
876 if(!a.s && arg->i & ClipboardGTK)
877 a.s = gtk_clipboard_wait_for_text(clipboards[1]);
878 if(a.s)
879 open(&a);
880 return TRUE;
883 gboolean
884 quit(const Arg *arg) {
885 gtk_main_quit();
886 return TRUE;
889 gboolean
890 search(const Arg *arg) {
891 count = count ? count : 1;
892 gboolean success, direction = arg->i & DirectionPrev;
893 Arg a;
895 if(arg->s) {
896 free(search_handle);
897 search_handle = strdup(arg->s);
899 if(!search_handle)
900 return TRUE;
901 if(arg->i & DirectionAbsolute)
902 search_direction = direction;
903 else
904 direction ^= search_direction;
905 do {
906 success = webkit_web_view_search_text(webview, search_handle, arg->i & CaseSensitive, direction, FALSE);
907 if(!success) {
908 if(arg->i & Wrapping) {
909 success = webkit_web_view_search_text(webview, search_handle, arg->i & CaseSensitive, direction, TRUE);
910 if(success) {
911 a.i = Warning;
912 a.s = g_strdup_printf("search hit %s, continuing at %s",
913 direction ? "BOTTOM" : "TOP",
914 direction ? "TOP" : "BOTTOM");
915 echo(&a);
916 g_free(a.s);
917 } else
918 break;
919 } else
920 break;
922 } while(--count);
923 if(!success) {
924 a.i = Error;
925 a.s = g_strdup_printf("Pattern not found: %s", search_handle);
926 echo(&a);
927 g_free(a.s);
929 return TRUE;
932 gboolean
933 set(const Arg *arg) {
934 Arg a = { .i = Info | NoAutoHide };
936 switch (arg->i) {
937 case ModeNormal:
938 if(search_handle) {
939 search_handle = NULL;
940 webkit_web_view_unmark_text_matches(webview);
942 gtk_entry_set_text(GTK_ENTRY(inputbox), "");
943 gtk_widget_grab_focus(GTK_WIDGET(webview));
944 break;
945 case ModePassThrough:
946 a.s = "-- PASS THROUGH --";
947 echo(&a);
948 break;
949 case ModeSendKey:
950 a.s = "-- PASS TROUGH (next) --";
951 echo(&a);
952 break;
953 case ModeInsert: /* should not be called manually but automatically */
954 a.s = "-- INSERT --";
955 echo(&a);
956 break;
957 case ModeHints:
958 a.i = Silent;
959 a.s = "show_hints()";
960 script(&a);
961 break;
962 default:
963 return TRUE;
965 mode = arg->i;
966 return TRUE;
969 gchar*
970 jsapi_ref_to_string(JSContextRef context, JSValueRef ref) {
971 JSStringRef string_ref;
972 gchar *string;
973 size_t length;
975 string_ref = JSValueToStringCopy(context, ref, NULL);
976 length = JSStringGetMaximumUTF8CStringSize(string_ref);
977 string = g_new(gchar, length);
978 JSStringGetUTF8CString(string_ref, string, length);
979 JSStringRelease(string_ref);
980 return string;
983 void
984 jsapi_evaluate_script(const gchar *script, gchar **value, gchar **message) {
985 WebKitWebFrame *frame = webkit_web_view_get_main_frame(webview);
986 JSGlobalContextRef context = webkit_web_frame_get_global_context(frame);
987 JSStringRef str;
988 JSValueRef val, exception;
990 str = JSStringCreateWithUTF8CString(script);
991 val = JSEvaluateScript(context, str, JSContextGetGlobalObject(context), NULL, 0, &exception);
992 JSStringRelease(str);
993 if(!val)
994 *message = jsapi_ref_to_string(context, exception);
995 else
996 *value = jsapi_ref_to_string(context, val);
999 gboolean
1000 script(const Arg *arg) {
1001 gchar *value = NULL, *message = NULL;
1002 Arg a;
1004 if(!arg->s)
1005 return TRUE;
1006 jsapi_evaluate_script(arg->s, &value, &message);
1007 if(message) {
1008 a.i = Error;
1009 a.s = message;
1010 echo(&a);
1011 g_free(message);
1013 if(arg->i != Silent && value) {
1014 a.i = arg->i;
1015 a.s = value;
1016 echo(&a);
1018 if (value) {
1019 if (strlen(value) == 5 && strncmp(value, "fired", 5) == 0) {
1020 count = 0;
1023 g_free(value);
1024 return TRUE;
1027 gboolean
1028 scroll(const Arg *arg) {
1029 GtkAdjustment *adjust = (arg->i & OrientationHoriz) ? adjust_h : adjust_v;
1031 if(arg->i & ScrollMove)
1032 gtk_adjustment_set_value(adjust, gtk_adjustment_get_value(adjust) +
1033 (arg->i & (1 << 2) ? 1 : -1) * /* direction */
1034 ((arg->i & UnitLine || (arg->i & UnitBuffer && count)) ? (scrollstep * (count ? count : 1)) : (
1035 arg->i & UnitBuffer ? gtk_adjustment_get_page_size(adjust) / 2 :
1036 (count ? count : 1) * (gtk_adjustment_get_page_size(adjust) -
1037 (gtk_adjustment_get_page_size(adjust) > pagingkeep ? pagingkeep : 0)))));
1038 else
1039 gtk_adjustment_set_value(adjust,
1040 ((arg->i & (1 << 2)) ? gtk_adjustment_get_upper : gtk_adjustment_get_lower)(adjust));
1041 update_state();
1042 return TRUE;
1045 gboolean
1046 zoom(const Arg *arg) {
1047 webkit_web_view_set_full_content_zoom(webview, (arg->i & ZoomFullContent) > 0);
1048 webkit_web_view_set_zoom_level(webview, (arg->i & ZoomOut) ?
1049 webkit_web_view_get_zoom_level(webview) +
1050 (((float)(count ? count : 1)) * (arg->i & (1 << 1) ? 1.0 : -1.0) * zoomstep) :
1051 (count ? (float)count / 100.0 : 1.0));
1052 return TRUE;
1055 gboolean
1056 toggle_plugins() {
1057 static gboolean plugins;
1058 WebKitWebSettings *settings;
1059 settings = webkit_web_view_get_settings(webview);
1060 plugins = !plugins;
1061 g_object_set((GObject*)settings, "enable-plugins", plugins, NULL);
1062 g_object_set((GObject*)settings, "enable-scripts", plugins, NULL);
1063 webkit_web_view_set_settings(webview, settings);
1064 webkit_web_view_reload(webview);
1065 return TRUE;
1068 gboolean
1069 toggle_images() {
1070 static gboolean images;
1071 WebKitWebSettings *settings;
1072 settings = webkit_web_view_get_settings(webview);
1073 images = !images;
1074 g_object_set((GObject*)settings, "auto-load-images", images, NULL);
1075 webkit_web_view_set_settings(webview, settings);
1076 webkit_web_view_reload(webview);
1077 return TRUE;
1080 gboolean
1081 bookmark() {
1082 FILE *f;
1083 const char *filename;
1084 const char *uri = webkit_web_view_get_uri(webview);
1085 const char *title = webkit_web_view_get_title(webview);
1086 filename = g_strdup_printf(BOOKMARKS_STORAGE_FILENAME);
1087 f = fopen(filename, "a");
1088 if (f != NULL && uri != NULL) {
1089 fprintf(f, "%s", uri);
1090 if (title != NULL) {
1091 fprintf(f, "%s", " ");
1092 fprintf(f, "%s", title);
1094 fprintf(f, "%s", "\n");
1095 fclose(f);
1096 return TRUE;
1097 } else {
1098 return FALSE;
1102 void history() {
1103 FILE *f;
1104 const char *filename;
1105 const char *uri = webkit_web_view_get_uri(webview);
1106 const char *title = webkit_web_view_get_title(webview);
1107 char *entry, buffer[512], *new;
1108 int n, i = 0;
1109 gboolean finished = FALSE;
1110 if (entry != NULL) {
1111 if (uri != NULL) {
1112 if (title != NULL) {
1113 entry = malloc((strlen(uri) + strlen(title) + 2) * sizeof(char));
1114 memset(entry, 0, strlen(uri) + strlen(title) + 2);
1115 } else {
1116 entry = malloc((strlen(uri) + 1) * sizeof(char));
1117 memset(entry, 0, strlen(uri) + 1);
1119 strncpy(entry, uri, strlen(uri));
1120 if (title != NULL) {
1121 strncat(entry, " ", 1);
1122 strncat(entry, title, strlen(title));
1124 n = strlen(entry);
1125 filename = g_strdup_printf(HISTORY_STORAGE_FILENAME);
1126 f = fopen(filename, "r");
1127 if (f != NULL) {
1128 new = (char *)malloc(HISTORY_MAX_ENTRIES * 512 * sizeof(char) + 1);
1129 if (new != NULL) {
1130 memset(new, 0, HISTORY_MAX_ENTRIES * 512 * sizeof(char) + 1);
1131 /* newest entries go on top */
1132 strncpy(new, entry, strlen(entry));
1133 strncat(new, "\n", 1);
1134 /* retain at most HISTORY_MAX_ENTIRES - 1 old entries */
1135 while (finished != TRUE) {
1136 if ((char *)NULL == fgets(buffer, 512, f)) {
1137 /* check if end of file was reached / error occured */
1138 if (!feof(f)) {
1139 break;
1141 /* end of file reached */
1142 finished = TRUE;
1143 continue;
1145 /* compare line (-1 because of newline character) */
1146 if (n != strlen(buffer) - 1 || strncmp(entry, buffer, n) != 0) {
1147 /* if the URI is already in history; we put it on top and skip it here */
1148 strncat(new, buffer, 512);
1149 i++;
1151 if (i >= HISTORY_MAX_ENTRIES) {
1152 break;
1155 fclose(f);
1157 f = fopen(filename, "w");
1158 if (f != NULL) {
1159 fprintf(f, "%s", new);
1160 fclose(f);
1162 if (new != NULL) {
1163 free(new);
1164 new = NULL;
1168 if (entry != NULL) {
1169 free(entry);
1170 entry = NULL;
1175 void
1176 update_url(const char *uri) {
1177 gboolean ssl = g_str_has_prefix(uri, "https://");
1178 GdkColor color;
1179 #ifdef ENABLE_HISTORY_INDICATOR
1180 char before[] = " [";
1181 char after[] = "]";
1182 gboolean back = webkit_web_view_can_go_back(webview);
1183 gboolean fwd = webkit_web_view_can_go_forward(webview);
1185 if(!back && !fwd)
1186 before[0] = after[0] = '\0';
1187 #endif
1188 gtk_label_set_markup((GtkLabel*)status_url, g_markup_printf_escaped(
1189 #ifdef ENABLE_HISTORY_INDICATOR
1190 "<span font=\"%s\">%s%s%s%s%s</span>", statusfont, uri,
1191 before, back ? "+" : "", fwd ? "-" : "", after
1192 #else
1193 "<span font=\"%s\">%s</span>", statusfont, uri
1194 #endif
1196 gdk_color_parse(ssl ? sslbgcolor : statusbgcolor, &color);
1197 gtk_widget_modify_bg(eventbox, GTK_STATE_NORMAL, &color);
1198 gdk_color_parse(ssl ? sslcolor : statuscolor, &color);
1199 gtk_widget_modify_fg(GTK_WIDGET(status_url), GTK_STATE_NORMAL, &color);
1200 gtk_widget_modify_fg(GTK_WIDGET(status_state), GTK_STATE_NORMAL, &color);
1203 void
1204 update_state() {
1205 int max = gtk_adjustment_get_upper(adjust_v) - gtk_adjustment_get_page_size(adjust_v);
1206 int val = (int)(gtk_adjustment_get_value(adjust_v) / max * 100);
1207 char *markup;
1208 #ifdef ENABLE_WGET_PROGRESS_BAR
1209 double progress;
1210 char progressbar[progressbartick + 1];
1212 g_object_get((GObject*)webview, "progress", &progress, NULL);
1213 #endif
1215 if(max == 0)
1216 sprintf(&scroll_state[0], "All");
1217 else if(val == 0)
1218 sprintf(&scroll_state[0], "Top");
1219 else if(val == 100)
1220 sprintf(&scroll_state[0], "Bot");
1221 else
1222 sprintf(&scroll_state[0], "%d%%", val);
1223 #ifdef ENABLE_WGET_PROGRESS_BAR
1224 if(webkit_web_view_get_load_status(webview) != WEBKIT_LOAD_FINISHED) {
1225 ascii_bar(progressbartick, (int)(progress * progressbartick / 100), (char*)progressbar);
1226 markup = (char*)g_markup_printf_escaped("<span font=\"%s\">%.0d%c %c%s%c %s</span>",
1227 statusfont, count, current_modkey, progressborderleft, progressbar, progressborderright, scroll_state);
1228 } else
1229 #endif
1230 markup = (char*)g_markup_printf_escaped("<span font=\"%s\">%.0d%c %s</span>", statusfont, count, current_modkey, scroll_state);
1231 gtk_label_set_markup(GTK_LABEL(status_state), markup);
1234 void
1235 setup_modkeys() {
1236 unsigned int i;
1237 modkeys = calloc(LENGTH(keys) + 1, sizeof(char));
1238 char *ptr = modkeys;
1240 for(i = 0; i < LENGTH(keys); i++)
1241 if(keys[i].modkey && !strchr(modkeys, keys[i].modkey))
1242 *(ptr++) = keys[i].modkey;
1243 modkeys = realloc(modkeys, &ptr[0] - &modkeys[0] + 1);
1246 void
1247 setup_gui() {
1248 GtkScrollbar *scroll_h = GTK_SCROLLBAR(gtk_hscrollbar_new(NULL));
1249 GtkScrollbar *scroll_v = GTK_SCROLLBAR(gtk_vscrollbar_new(NULL));
1250 adjust_h = gtk_range_get_adjustment(GTK_RANGE(scroll_h));
1251 adjust_v = gtk_range_get_adjustment(GTK_RANGE(scroll_v));
1252 window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1253 box = GTK_BOX(gtk_vbox_new(FALSE, 0));
1254 inputbox = gtk_entry_new();
1255 GtkWidget *viewport = gtk_scrolled_window_new(adjust_h, adjust_v);
1256 webview = (WebKitWebView*)webkit_web_view_new();
1257 GtkBox *statusbar = GTK_BOX(gtk_hbox_new(FALSE, 0));
1258 eventbox = gtk_event_box_new();
1259 status_url = gtk_label_new(NULL);
1260 status_state = gtk_label_new(NULL);
1261 GdkColor bg;
1262 PangoFontDescription *font;
1263 GdkGeometry hints = { 1, 1 };
1265 clipboards[0] = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
1266 clipboards[1] = gtk_clipboard_get(GDK_NONE);
1267 setup_settings();
1268 gdk_color_parse(statusbgcolor, &bg);
1269 gtk_widget_modify_bg(eventbox, GTK_STATE_NORMAL, &bg);
1270 gtk_widget_set_name(window, "Vimprobable");
1271 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL, &hints, GDK_HINT_MIN_SIZE);
1272 #ifdef DISABLE_SCROLLBAR
1273 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(viewport), GTK_POLICY_NEVER, GTK_POLICY_NEVER);
1274 #endif
1275 setup_signals();
1276 gtk_container_add(GTK_CONTAINER(viewport), GTK_WIDGET(webview));
1277 font = pango_font_description_from_string(urlboxfont[0]);
1278 gtk_widget_modify_font(GTK_WIDGET(inputbox), font);
1279 pango_font_description_free(font);
1280 gtk_entry_set_inner_border(GTK_ENTRY(inputbox), NULL);
1281 gtk_misc_set_alignment(GTK_MISC(status_url), 0.0, 0.0);
1282 gtk_misc_set_alignment(GTK_MISC(status_state), 1.0, 0.0);
1283 gtk_box_pack_start(statusbar, status_url, TRUE, TRUE, 2);
1284 gtk_box_pack_start(statusbar, status_state, FALSE, FALSE, 2);
1285 gtk_container_add(GTK_CONTAINER(eventbox), GTK_WIDGET(statusbar));
1286 gtk_box_pack_start(box, viewport, TRUE, TRUE, 0);
1287 gtk_box_pack_start(box, eventbox, FALSE, FALSE, 0);
1288 gtk_entry_set_has_frame(GTK_ENTRY(inputbox), FALSE);
1289 gtk_box_pack_end(box, inputbox, FALSE, FALSE, 0);
1290 gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(box));
1291 gtk_widget_grab_focus(GTK_WIDGET(webview));
1292 gtk_widget_show_all(window);
1295 void
1296 setup_settings() {
1297 WebKitWebSettings *settings = (WebKitWebSettings*)webkit_web_settings_new();
1298 SoupURI *proxy_uri;
1299 char *filename, *new;
1300 int len;
1301 #ifdef ENABLE_COOKIE_SUPPORT
1302 SoupCookieJar *cookiejar;
1303 #endif
1304 session = webkit_get_default_session();
1305 g_object_set((GObject*)settings, "default-font-size", DEFAULT_FONT_SIZE, NULL);
1306 g_object_set((GObject*)settings, "enable-scripts", enablePlugins, NULL);
1307 g_object_set((GObject*)settings, "enable-plugins", enablePlugins, NULL);
1308 filename = g_strdup_printf(USER_STYLES_FILENAME);
1309 filename = g_strdup_printf("file://%s", filename);
1310 g_object_set((GObject*)settings, "user-stylesheet-uri", filename, NULL);
1311 g_object_set((GObject*)settings, "user-agent", USER_AGENT, NULL);
1312 g_object_get((GObject*)settings, "zoom-step", &zoomstep, NULL);
1313 webkit_web_view_set_settings(webview, settings);
1314 #ifdef ENABLE_COOKIE_SUPPORT
1315 filename = g_strdup_printf(COOKIES_STORAGE_FILENAME);
1316 cookiejar = soup_cookie_jar_text_new(filename, COOKIES_STORAGE_READONLY);
1317 g_free(filename);
1318 soup_session_add_feature(session, (SoupSessionFeature*)cookiejar);
1319 #endif
1320 /* proxy */
1321 filename = (char *)g_getenv("http_proxy");
1322 if (filename != NULL && 0 < (len = strlen(filename))) {
1323 if(strstr(filename, "://") == NULL) {
1324 /* prepend http:// */
1325 new = g_malloc(sizeof("http://") + len);
1326 strcpy(new, "http://");
1327 memcpy(&new[sizeof("http://") - 1], filename, len + 1);
1328 proxy_uri = soup_uri_new(new);
1329 } else {
1330 proxy_uri = soup_uri_new(filename);
1332 g_object_set(session, "proxy-uri", proxy_uri, NULL);
1336 void
1337 setup_signals() {
1338 /* window */
1339 g_object_connect((GObject*)window,
1340 "signal::destroy", (GCallback)window_destroyed_cb, NULL,
1341 NULL);
1342 /* webview */
1343 g_object_connect((GObject*)webview,
1344 "signal::title-changed", (GCallback)webview_title_changed_cb, NULL,
1345 "signal::load-progress-changed", (GCallback)webview_progress_changed_cb, NULL,
1346 "signal::load-committed", (GCallback)webview_load_committed_cb, NULL,
1347 "signal::load-finished", (GCallback)webview_load_finished_cb, NULL,
1348 "signal::navigation-policy-decision-requested", (GCallback)webview_navigation_cb, NULL,
1349 "signal::new-window-policy-decision-requested", (GCallback)webview_new_window_cb, NULL,
1350 "signal::mime-type-policy-decision-requested", (GCallback)webview_mimetype_cb, NULL,
1351 "signal::download-requested", (GCallback)webview_download_cb, NULL,
1352 "signal::key-press-event", (GCallback)webview_keypress_cb, NULL,
1353 "signal::hovering-over-link", (GCallback)webview_hoverlink_cb, NULL,
1354 "signal::console-message", (GCallback)webview_console_cb, NULL,
1355 "signal::create-web-view", (GCallback)webview_open_in_new_window_cb, NULL,
1356 "signal::event", (GCallback)notify_event_cb, NULL,
1357 NULL);
1358 /* webview adjustment */
1359 g_object_connect((GObject*)adjust_v,
1360 "signal::value-changed", (GCallback)webview_scroll_cb, NULL,
1361 NULL);
1362 /* inputbox */
1363 g_object_connect((GObject*)inputbox,
1364 "signal::activate", (GCallback)inputbox_activate_cb, NULL,
1365 "signal::key-press-event", (GCallback)inputbox_keypress_cb, NULL,
1366 "signal::key-release-event", (GCallback)inputbox_keyrelease_cb, NULL,
1367 NULL);
1371 main(int argc, char *argv[]) {
1372 Arg a;
1373 args = argv;
1375 gtk_init(&argc, &argv);
1376 if(!g_thread_supported())
1377 g_thread_init(NULL);
1378 setup_modkeys();
1379 setup_gui();
1380 a.i = TargetCurrent;
1381 a.s = argc > 1 ? argv[1] : startpage;
1382 open(&a);
1383 gtk_main();
1385 return EXIT_SUCCESS;