NetSurf
viewdata.c
Go to the documentation of this file.
1/*
2 * Copyright 2014 Vincent Sanders <vince@netsurf-browser.org>
3 *
4 * This file is part of NetSurf, http://www.netsurf-browser.org/
5 *
6 * NetSurf is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
9 *
10 * NetSurf is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19/**
20 * \file
21 * generic data viewer implementation.
22 *
23 * This viewer can be used for utf-8 encoded chunk of data. Thie data
24 * might be page source or the debugging of dom or box trees. It will
25 * show the data in a tab, window or editor as per user configuration.
26 */
27
28#include <stdlib.h>
29#include <string.h>
30#include <strings.h>
31#define _WITH_GETLINE /* necessary for FreeBSD */
32#include <stdio.h>
33#include <unistd.h>
34#include <gtk/gtk.h>
35
36#include "utils/log.h"
37#include "utils/nsoption.h"
38#include "utils/utf8.h"
39#include "utils/messages.h"
40#include "utils/utils.h"
41#include "utils/file.h"
42#include "utils/filepath.h"
43#include "utils/nsurl.h"
45
46#include "gtk/warn.h"
47#include "gtk/about.h"
48#include "gtk/fetch.h"
49#include "gtk/compat.h"
50#include "gtk/resources.h"
51#include "gtk/viewdata.h"
52
54 char *data;
55 size_t data_len;
56 char *filename;
57
58 GtkBuilder *builder; /**< The gtk builder that built the widgets. */
59 GtkWindow *window; /**< handle to gtk window (builder holds reference) */
60 GtkTextView *gv; /**< handle to gtk text view (builder holds reference) */
61
64};
65
66struct menu_events {
67 const char *widget;
68 GCallback handler;
69};
70
72static char viewdata_zoomlevel = 10;
73
74#define MENUEVENT(x) { #x, G_CALLBACK(nsgtk_on_##x##_activate) }
75#define MENUPROTO(x) static gboolean nsgtk_on_##x##_activate( \
76 GtkMenuItem *widget, gpointer g)
77
78MENUPROTO(viewdata_save_as);
79MENUPROTO(viewdata_print);
80MENUPROTO(viewdata_close);
81MENUPROTO(viewdata_select_all);
82MENUPROTO(viewdata_cut);
83MENUPROTO(viewdata_copy);
84MENUPROTO(viewdata_paste);
85MENUPROTO(viewdata_delete);
86MENUPROTO(viewdata_zoom_in);
87MENUPROTO(viewdata_zoom_out);
88MENUPROTO(viewdata_zoom_normal);
89MENUPROTO(viewdata_about);
90
92 MENUEVENT(viewdata_save_as),
93 MENUEVENT(viewdata_print),
94 MENUEVENT(viewdata_close),
95 MENUEVENT(viewdata_select_all),
96 MENUEVENT(viewdata_cut),
97 MENUEVENT(viewdata_copy),
98 MENUEVENT(viewdata_paste),
99 MENUEVENT(viewdata_delete),
100 MENUEVENT(viewdata_zoom_in),
101 MENUEVENT(viewdata_zoom_out),
102 MENUEVENT(viewdata_zoom_normal),
103 MENUEVENT(viewdata_about),
104 {NULL, NULL}
105};
106
107static void nsgtk_attach_viewdata_menu_handlers(GtkBuilder *xml, gpointer g)
108{
109 struct menu_events *event = viewdata_menu_events;
110
111 while (event->widget != NULL)
112 {
113 GtkWidget *w = GTK_WIDGET(gtk_builder_get_object(xml, event->widget));
114 g_signal_connect(G_OBJECT(w), "activate", event->handler, g);
115 event++;
116 }
117}
118
119static gboolean nsgtk_viewdata_destroy_event(GtkBuilder *window, gpointer g)
120{
121 struct nsgtk_viewdata_ctx *vdctx = (struct nsgtk_viewdata_ctx *)g;
122
123 if (vdctx->next != NULL) {
124 vdctx->next->prev = vdctx->prev;
125 }
126
127 if (vdctx->prev != NULL) {
128 vdctx->prev->next = vdctx->next;
129 } else {
130 nsgtk_viewdata_list = vdctx->next;
131 }
132
133 /* release the data */
134 free(vdctx->data);
135
136 /* free the builder */
137 g_object_unref(G_OBJECT(vdctx->builder));
138
139 /* free the context structure */
140 free(vdctx);
141
142 return FALSE;
143}
144
145static gboolean nsgtk_viewdata_delete_event(GtkWindow * window, gpointer g)
146{
147 return FALSE;
148}
149
150
151
152static void nsgtk_viewdata_file_save(GtkWindow *parent, const char *filename,
153 const char *data, size_t data_size)
154{
155 FILE *f;
156 GtkWidget *notif;
157 GtkWidget *label;
158
159 f = fopen(filename, "w+");
160 if (f != NULL) {
161 fwrite(data, data_size, 1, f);
162 fclose(f);
163 return;
164 }
165
166 /* inform user of faliure */
167 notif = gtk_dialog_new_with_buttons(messages_get("gtkSaveFailedTitle"),
168 parent,
169 GTK_DIALOG_MODAL,
171 GTK_RESPONSE_NONE,
172 NULL);
173
174 g_signal_connect_swapped(notif, "response",
175 G_CALLBACK(gtk_widget_destroy), notif);
176
177 label = gtk_label_new(messages_get("gtkSaveFailed"));
178 gtk_container_add(GTK_CONTAINER(nsgtk_dialog_get_content_area(GTK_DIALOG(notif))), label);
179 gtk_widget_show_all(notif);
180
181}
182
183
184gboolean nsgtk_on_viewdata_save_as_activate(GtkMenuItem *widget, gpointer g)
185{
186 struct nsgtk_viewdata_ctx *nsg = (struct nsgtk_viewdata_ctx *) g;
187 GtkWidget *fc;
188
189 fc = gtk_file_chooser_dialog_new(messages_get("gtkSaveFile"),
190 nsg->window,
191 GTK_FILE_CHOOSER_ACTION_SAVE,
193 GTK_RESPONSE_CANCEL,
195 GTK_RESPONSE_ACCEPT,
196 NULL);
197
198 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(fc), nsg->filename);
199
200 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(fc),
201 TRUE);
202
203 if (gtk_dialog_run(GTK_DIALOG(fc)) == GTK_RESPONSE_ACCEPT) {
204 char *filename;
205 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fc));
207 g_free(filename);
208 }
209
210 gtk_widget_destroy(fc);
211
212 return TRUE;
213}
214
215
216gboolean nsgtk_on_viewdata_print_activate( GtkMenuItem *widget, gpointer g)
217{
218 /* correct printing */
219
220 return TRUE;
221}
222
223gboolean nsgtk_on_viewdata_close_activate( GtkMenuItem *widget, gpointer g)
224{
225 struct nsgtk_viewdata_ctx *nsg = (struct nsgtk_viewdata_ctx *) g;
226
227 gtk_widget_destroy(GTK_WIDGET(nsg->window));
228
229 return TRUE;
230}
231
232
233
234gboolean nsgtk_on_viewdata_select_all_activate (GtkMenuItem *widget, gpointer g)
235{
236 struct nsgtk_viewdata_ctx *nsg = (struct nsgtk_viewdata_ctx *) g;
237 GtkTextBuffer *buf = gtk_text_view_get_buffer(nsg->gv);
238 GtkTextIter start, end;
239
240 gtk_text_buffer_get_bounds(buf, &start, &end);
241
242 gtk_text_buffer_select_range(buf, &start, &end);
243
244 return TRUE;
245}
246
247gboolean nsgtk_on_viewdata_cut_activate(GtkMenuItem *widget, gpointer g)
248{
249 return TRUE;
250}
251
252gboolean nsgtk_on_viewdata_copy_activate(GtkMenuItem *widget, gpointer g)
253{
254 struct nsgtk_viewdata_ctx *nsg = (struct nsgtk_viewdata_ctx *) g;
255 GtkTextBuffer *buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(nsg->gv));
256
257 gtk_text_buffer_copy_clipboard(buf,
258 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
259
260 return TRUE;
261}
262
263gboolean nsgtk_on_viewdata_paste_activate(GtkMenuItem *widget, gpointer g)
264{
265 return TRUE;
266}
267
268gboolean nsgtk_on_viewdata_delete_activate(GtkMenuItem *widget, gpointer g)
269{
270 return TRUE;
271}
272
273static void nsgtk_viewdata_update_zoomlevel(gpointer g)
274{
275 struct nsgtk_viewdata_ctx *nsg;
276 GtkTextBuffer *buf;
277 GtkTextTagTable *tab;
278 GtkTextTag *tag;
279
281 while (nsg) {
282 if (nsg->gv) {
283 buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(nsg->gv));
284
285 tab = gtk_text_buffer_get_tag_table(
286 GTK_TEXT_BUFFER(buf));
287
288 tag = gtk_text_tag_table_lookup(tab, "zoomlevel");
289 if (!tag) {
290 tag = gtk_text_tag_new("zoomlevel");
291 gtk_text_tag_table_add(tab, GTK_TEXT_TAG(tag));
292 }
293
294 gdouble fscale = ((gdouble) viewdata_zoomlevel) / 10;
295
296 g_object_set(GTK_TEXT_TAG(tag), "scale", fscale, NULL);
297
298 GtkTextIter start, end;
299
300 gtk_text_buffer_get_bounds(GTK_TEXT_BUFFER(buf),
301 &start, &end);
302 gtk_text_buffer_remove_all_tags(GTK_TEXT_BUFFER(buf),
303 &start, &end);
304 gtk_text_buffer_apply_tag(GTK_TEXT_BUFFER(buf),
305 GTK_TEXT_TAG(tag), &start, &end);
306 }
307 nsg = nsg->next;
308 }
309}
310
311gboolean nsgtk_on_viewdata_zoom_in_activate(GtkMenuItem *widget, gpointer g)
312{
315
316 return TRUE;
317}
318
319gboolean nsgtk_on_viewdata_zoom_out_activate(GtkMenuItem *widget, gpointer g)
320{
321 if (viewdata_zoomlevel > 1) {
324 }
325
326 return TRUE;
327}
328
329
330gboolean nsgtk_on_viewdata_zoom_normal_activate(GtkMenuItem *widget, gpointer g)
331{
334
335 return TRUE;
336}
337
338gboolean nsgtk_on_viewdata_about_activate(GtkMenuItem *widget, gpointer g)
339{
340 struct nsgtk_viewdata_ctx *nsg = (struct nsgtk_viewdata_ctx *) g;
341
343
344 return TRUE;
345}
346
347/**
348 * View the data in a gtk text window.
349 */
350static nserror
351window_init(const char *title,
352 const char *filename,
353 char *ndata,
354 size_t ndata_len)
355{
356 GtkWindow *window;
357 GtkWidget *cutbutton;
358 GtkWidget *pastebutton;
359 GtkWidget *deletebutton;
360 GtkWidget *printbutton;
361 GtkTextView *dataview;
362 PangoFontDescription *fontdesc;
363 GtkTextBuffer *tb;
364 struct nsgtk_viewdata_ctx *newctx;
365 nserror res;
366
367 newctx = malloc(sizeof(struct nsgtk_viewdata_ctx));
368 if (newctx == NULL) {
369 return NSERROR_NOMEM;
370 }
371
372 res = nsgtk_builder_new_from_resname("viewdata", &newctx->builder);
373 if (res != NSERROR_OK) {
374 NSLOG(netsurf, INFO, "Viewdata UI builder init failed");
375 free(newctx);
376 return res;
377 }
378
379 gtk_builder_connect_signals(newctx->builder, NULL);
380
381 window = GTK_WINDOW(gtk_builder_get_object(newctx->builder,
382 "ViewDataWindow"));
383 if (window == NULL) {
384 NSLOG(netsurf, INFO, "Unable to find window in builder ");
385
386 /* free the builder */
387 g_object_unref(G_OBJECT(newctx->builder));
388
389 /* free the context structure */
390 free(newctx);
391
392 return NSERROR_INIT_FAILED;
393 }
394
395 cutbutton = GTK_WIDGET(gtk_builder_get_object(newctx->builder, "viewdata_cut"));
396 pastebutton = GTK_WIDGET(gtk_builder_get_object(newctx->builder, "viewdata_paste"));
397 deletebutton = GTK_WIDGET(gtk_builder_get_object(newctx->builder, "viewdata_delete"));
398 printbutton = GTK_WIDGET(gtk_builder_get_object(newctx->builder, "viewdata_print"));
399 gtk_widget_set_sensitive(cutbutton, FALSE);
400 gtk_widget_set_sensitive(pastebutton, FALSE);
401 gtk_widget_set_sensitive(deletebutton, FALSE);
402 /* for now */
403 gtk_widget_set_sensitive(printbutton, FALSE);
404
405
406 newctx->filename = strdup(filename);
407
408 newctx->data = ndata;
409 newctx->data_len = ndata_len;
410
411 newctx->window = window;
412
413 newctx->next = nsgtk_viewdata_list;
414 newctx->prev = NULL;
415 if (nsgtk_viewdata_list != NULL) {
416 nsgtk_viewdata_list->prev = newctx;
417 }
418 nsgtk_viewdata_list = newctx;
419
421
422 gtk_window_set_title(window, title);
423
424 g_signal_connect(G_OBJECT(window), "destroy",
426 newctx);
427 g_signal_connect(G_OBJECT(window), "delete-event",
428 G_CALLBACK(nsgtk_viewdata_delete_event),
429 newctx);
430
431 dataview = GTK_TEXT_VIEW(gtk_builder_get_object(newctx->builder,
432 "viewdata_view"));
433
434 fontdesc = pango_font_description_from_string("Monospace 8");
435
436 newctx->gv = dataview;
437 nsgtk_widget_modify_font(GTK_WIDGET(dataview), fontdesc);
438
439 tb = gtk_text_view_get_buffer(dataview);
440 gtk_text_buffer_set_text(tb, newctx->data, -1);
441
442 gtk_widget_show(GTK_WIDGET(window));
443
444 return NSERROR_OK;
445}
446
447/**
448 * open a window to dispaly an existing file.
449 */
450static nserror
451window_init_fname(const char *title,
452 const char *leafname,
453 const char *filename)
454{
455 nserror ret;
456 FILE *f;
457 char *ndata;
458 long tell_len;
459 size_t ndata_len;
460
461 f = fopen(filename, "r");
462 if (f == NULL) {
463 return NSERROR_NOT_FOUND;
464 }
465 if (fseek(f, 0, SEEK_END) != 0) {
466 fclose(f);
467 return NSERROR_BAD_SIZE;
468 }
469
470 tell_len = ftell(f);
471 if (tell_len == -1) {
472 fclose(f);
473 return NSERROR_BAD_SIZE;
474 }
475
476 if (fseek(f, 0, SEEK_SET) != 0) {
477 fclose(f);
478 return NSERROR_BAD_SIZE;
479 }
480
481 ndata = malloc(tell_len);
482
483 ndata_len = fread(ndata, 1, tell_len, f);
484
485 fclose(f);
486
487 /* window init takes ownership of the ndata if there is no error */
488 ret = window_init(title, leafname, ndata, ndata_len);
489 if (ret != NSERROR_OK) {
490 free(ndata);
491 }
492
493 return ret;
494}
495
496/**
497 * open a new tab from an existing file.
498 */
499static nserror
500tab_init_fname(const char *title,
501 const char *leafname,
502 const char *fname)
503{
504 nsurl *url;
505 nserror ret;
506
507 /* Open tab on temporary file */
508 ret = netsurf_path_to_nsurl(fname, &url);
509 if (ret != NSERROR_OK) {
510 return ret;
511 }
512
513 /* open tab on temportary file */
514 ret = browser_window_create(BW_CREATE_TAB | BW_CREATE_HISTORY, url, NULL, NULL, NULL);
515 nsurl_unref(url);
516 if (ret != NSERROR_OK) {
517 return ret;
518 }
519
520 return NSERROR_OK;
521}
522
523/**
524 * create a new tab from data.
525 */
526static nserror
527tab_init(const char *title,
528 const char *leafname,
529 char *ndata,
530 size_t ndata_len)
531{
532 nserror ret;
533 gchar *fname;
534 gint handle;
535 FILE *f;
536
537 handle = g_file_open_tmp("nsgtkdataXXXXXX", &fname, NULL);
538 if ((handle == -1) || (fname == NULL)) {
539 return NSERROR_SAVE_FAILED;
540 }
541 close(handle); /* in case it was binary mode */
542
543 /* save data to temporary file */
544 f = fopen(fname, "w");
545 if (f == NULL) {
546 nsgtk_warning(messages_get("gtkSourceTabError"), 0);
547 g_free(fname);
548 return NSERROR_SAVE_FAILED;
549 }
550 fprintf(f, "%s", ndata);
551 fclose(f);
552
553 ret = tab_init_fname(title, leafname, fname);
554 if (ret == NSERROR_OK) {
555 free(ndata);
556 }
557
558 g_free(fname);
559
560 return ret;
561}
562
563
564/**
565 * Build string vector of search path.
566 *
567 * ${XDG_DATA_HOME:-$HOME/.local/share}:${XDG_DATA_DIRS:-/usr/local/share:/usr/share}
568 *
569 * $XDG_DATA_HOME if empty use $HOME/.local/share
570 *
571 * XDG_DATA_DIRS if empty use /usr/local/share/:/usr/share/
572 *
573 * \return string vector of search pathnames or NULL on error.
574 */
575static char** xdg_data_strvec(void)
576{
577 const char *xdg_data_dirs;
578 const char *xdg_data_home;
579 const char *home_dir;
580 char *xdg_data_path;
581 int xdg_data_size;
582 char **svec;
583
584 xdg_data_dirs = getenv("XDG_DATA_DIRS");
585 if ((xdg_data_dirs == NULL) ||
586 (*xdg_data_dirs == 0) ||
587 (strlen(xdg_data_dirs) > 4096)) {
588 xdg_data_dirs = "/usr/local/share/:/usr/share/";
589 }
590
591 xdg_data_home = getenv("XDG_DATA_HOME");
592 if ((xdg_data_home == NULL) ||
593 (*xdg_data_home == 0) ||
594 (strlen(xdg_data_home) > 4096)) {
595 /* $XDG_DATA_HOME is empty use $HOME/.local/share */
596
597 home_dir = getenv("HOME");
598 if ((home_dir == NULL) ||
599 (*home_dir == 0) ||
600 (strlen(home_dir) > 4096)) {
601 xdg_data_path = strdup(xdg_data_dirs);
602 } else {
603 xdg_data_size = strlen(home_dir) +
604 SLEN("/.local/share:") +
605 strlen(xdg_data_dirs) + 1;
606 xdg_data_path = malloc(xdg_data_size);
607 snprintf(xdg_data_path, xdg_data_size ,
608 "%s/.local/share/:%s",
609 home_dir, xdg_data_dirs);
610 }
611 } else {
612 xdg_data_size = strlen(xdg_data_home) +
613 strlen(xdg_data_dirs) + 2;
614 xdg_data_path = malloc(xdg_data_size);
615 snprintf(xdg_data_path, xdg_data_size , "%s:%s",
616 xdg_data_home, xdg_data_dirs);
617 }
618
619 NSLOG(netsurf, INFO, "%s", xdg_data_path);
620
621 svec = filepath_path_to_strvec(xdg_data_path);
622 free(xdg_data_path);
623
624 return svec;
625}
626
627/**
628 * Search application defaults file for matching mime type.
629 *
630 * create filename form path and applications/defaults.list
631 *
632 * look for [Default Applications]
633 * search lines looking like mime/type=Desktop
634 *
635 * \param path The base path.
636 * \param mimetype The mimetype to search for.
637 * \return The desktop file associated with the mime type or NULL if not found.
638 */
639static char *xdg_get_default_app(const char *path, const char *mimetype)
640{
641 FILE *fp;
642 char *line = NULL;
643 size_t len = 0;
644 ssize_t rd;
645 int fname_len;
646 char *fname;
647 int mimetype_len;
648 char *ret = NULL;
649
650 fname_len = strlen(path) + SLEN("/applications/defaults.list") + 1;
651 fname = malloc(fname_len);
652 snprintf(fname, fname_len, "%s/applications/defaults.list", path);
653
654 NSLOG(netsurf, INFO, "Checking %s", fname);
655
656 fp = fopen(fname, "r");
657 free(fname);
658 if (fp == NULL) {
659 return NULL;
660 }
661
662 mimetype_len = strlen(mimetype);
663 while ((rd = getline(&line, &len, fp)) != -1) {
664 /* line includes line endings if present, remove them */
665 while ((line[rd - 1] == '\n') || (line[rd - 1] == '\r')) {
666 rd--;
667 }
668 line[rd] = 0;
669
670 /* look for mimetype */
671 if ((rd > mimetype_len) &&
672 (line[mimetype_len] == '=') &&
673 (strncmp(line, mimetype, mimetype_len) == 0)) {
674
675 ret = strdup(line + mimetype_len + 1);
676
677 NSLOG(netsurf, INFO,
678 "Found line match for %s length %zu\n",
679 mimetype,
680 rd);
681 NSLOG(netsurf, INFO, "Result %s", ret);
682
683 break;
684 }
685 }
686
687 free(line);
688 fclose(fp);
689
690 return ret;
691}
692
693/**
694 * Search desktop file for an Exec line.
695 *
696 * search path is combined with applications/application.desktop to
697 * create a filename.
698 *
699 * Desktop file format http://standards.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html
700 *
701 * \todo The parsing of the desktop file is badly incomplete and needs
702 * improving. For example the handling of the = delimiter is wrong and
703 * selection from the "Desktop Entry" group is completely absent.
704 *
705 */
706static char *xdg_get_exec_cmd(const char *path, const char *desktop)
707{
708 FILE *fp;
709 char *line = NULL;
710 size_t len = 0;
711 ssize_t rd;
712 int fname_len;
713 char *fname;
714 char *ret = NULL;
715
716 fname_len = strlen(path) + SLEN("/applications/") + strlen(desktop) + 1;
717 fname = malloc(fname_len);
718 snprintf(fname, fname_len, "%s/applications/%s", path, desktop);
719
720 NSLOG(netsurf, INFO, "Checking %s", fname);
721
722 fp = fopen(fname, "r");
723 free(fname);
724 if (fp == NULL) {
725 return NULL;
726 }
727
728 while ((rd = getline(&line, &len, fp)) != -1) {
729 /* line includes line endings if present, remove them */
730 while ((line[rd - 1] == '\n') || (line[rd - 1] == '\r')) {
731 rd--;
732 }
733 line[rd] = 0;
734
735 /* look for mimetype */
736 if ((rd > (ssize_t)SLEN("Exec=")) &&
737 (strncmp(line, "Exec=", SLEN("Exec=")) == 0)) {
738
739 ret = strdup(line + SLEN("Exec="));
740
741 NSLOG(netsurf, INFO, "Found Exec length %zu", rd);
742 NSLOG(netsurf, INFO, "Result %s", ret);
743
744 break;
745 }
746 }
747
748 free(line);
749 fclose(fp);
750
751 return ret;
752}
753
754static char *exec_arg(const char *arg, int len, const char *fname)
755{
756 char *res = NULL;
757
758 if (*arg == '%') {
759 arg++;
760 if ((*arg == 'f') || (*arg == 'F') ||
761 (*arg == 'u') || (*arg == 'U')) {
762 res = strdup(fname);
763 }
764 } else {
765 res = calloc(1, len + 1);
766 if (res != NULL) {
767 memcpy(res, arg, len);
768 }
769 }
770
771 return res;
772}
773
774/**
775 * Build vector for executing app.
776 */
777static char **build_exec_argv(const char *fname, const char *exec_cmd)
778{
779 char **argv;
780 const char *start; /* current arguments start */
781 const char *cur; /* current ptr within exec cmd */
782 int aidx = 0; /* argv index */
783
784 argv = calloc(10, sizeof(char *));
785 if (argv == NULL) {
786 return NULL;
787 }
788
789 cur = exec_cmd;
790 while (*cur != 0) {
791 /* skip whitespace */
792 while ((*cur != 0) && (*cur == ' ')) {
793 cur++;
794 }
795 if (*cur == 0) {
796 break;
797 }
798 start = cur;
799
800 /* find end of element */
801 while ((*cur != 0) && (*cur != ' ')) {
802 cur++;
803 }
804
805 argv[aidx] = exec_arg(start, cur - start, fname);
806 if (argv[aidx] != NULL) {
807 NSLOG(netsurf, INFO, "adding \"%s\"", argv[aidx]);
808 aidx++;
809 }
810 }
811
812 /* if no arguments were found there was nothing to execute */
813 if (aidx == 0) {
814 free(argv);
815 return NULL;
816 }
817
818 return argv;
819}
820
821
822/**
823 * open an editor from an existing file.
824 */
825static nserror
826editor_init_fname(const char *title,
827 const char *leafname,
828 const char *fname)
829{
830 char **xdg_data_vec;
831 int veci;
832 /* desktop file of default app for mimetype */
833 char *def_app_desktop = NULL;
834 char *exec_cmd = NULL;
835 char **argv;
836
837 /* build string vector of search path */
838 xdg_data_vec = xdg_data_strvec();
839
840 /* find user configured app for opening text/plain */
841 veci = 0;
842 while (xdg_data_vec[veci] != NULL) {
843 def_app_desktop = xdg_get_default_app(xdg_data_vec[veci],
844 "text/plain");
845 if (def_app_desktop != NULL) {
846 break;
847 }
848 veci++;
849 }
850
851 if (def_app_desktop == NULL) {
852 /* no default app */
853 filepath_free_strvec(xdg_data_vec);
854 return NSERROR_NOT_FOUND;
855 }
856
857 /* find app to execute */
858 veci = 0;
859 while (xdg_data_vec[veci] != NULL) {
860 exec_cmd = xdg_get_exec_cmd(xdg_data_vec[veci], def_app_desktop);
861 if (exec_cmd != NULL) {
862 break;
863 }
864 veci++;
865 }
866 free(def_app_desktop);
867 filepath_free_strvec(xdg_data_vec);
868
869 if (exec_cmd == NULL) {
870 /* no exec entry */
871 return NSERROR_NOT_FOUND;
872 }
873
874 /* build exec vector */
875 argv = build_exec_argv(fname, exec_cmd);
876 free(exec_cmd);
877
878 /* execute target app on saved data */
879 if (g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL,
880 NULL, NULL) != TRUE) {
881 return NSERROR_NOT_FOUND;
882 }
884
885 return NSERROR_OK;
886}
887
888/**
889 * open an editor with data.
890 */
891static nserror
892editor_init(const char *title,
893 const char *leafname,
894 char *ndata,
895 size_t ndata_len)
896{
897
898 nserror ret;
899 gchar *fname;
900 gint handle;
901 FILE *f;
902
903 handle = g_file_open_tmp("nsgtkdataXXXXXX", &fname, NULL);
904 if ((handle == -1) || (fname == NULL)) {
905 return NSERROR_SAVE_FAILED;
906 }
907 close(handle); /* in case it was binary mode */
908
909 /* save data to temporary file */
910 f = fopen(fname, "w");
911 if (f == NULL) {
912 nsgtk_warning(messages_get("gtkSourceTabError"), 0);
913 g_free(fname);
914 return NSERROR_SAVE_FAILED;
915 }
916 fprintf(f, "%s", ndata);
917 fclose(f);
918
919 ret = editor_init_fname(title, leafname, fname);
920 if (ret == NSERROR_OK) {
921 free(ndata);
922 }
923
924 g_free(fname);
925
926 return ret;
927}
928
929/* exported interface documented in gtk/viewdata.h */
931nsgtk_viewdata(const char *title,
932 const char *filename,
933 char *ndata,
934 size_t ndata_len)
935{
936 nserror ret;
937
938 switch (nsoption_int(developer_view)) {
939 case 0:
940 ret = window_init(title, filename, ndata, ndata_len);
941 break;
942
943 case 1:
944 ret = tab_init(title, filename, ndata, ndata_len);
945 break;
946
947 case 2:
948 ret = editor_init(title, filename, ndata, ndata_len);
949 break;
950
951 default:
953 break;
954 }
955 if (ret != NSERROR_OK) {
956 /* release the data */
957 free(ndata);
958 }
959
960
961 return ret;
962}
963
964/* exported interface documented in gtk/viewdata.h */
966nsgtk_viewfile(const char *title,
967 const char *leafname,
968 const char *filename)
969{
970 nserror ret;
971
972 switch (nsoption_int(developer_view)) {
973 case 0:
974 ret = window_init_fname(title, leafname, filename);
975 break;
976
977 case 1:
978 ret = tab_init_fname(title, leafname, filename);
979 break;
980
981 case 2:
982 ret = editor_init_fname(title, leafname, filename);
983 break;
984
985 default:
987 break;
988 }
989
990 return ret;
991}
Browser window creation and manipulation interface.
nserror browser_window_create(enum browser_window_create_flags flags, struct nsurl *url, struct nsurl *referrer, struct browser_window *existing, struct browser_window **bw)
Create and open a new root browser window with the given page.
@ BW_CREATE_HISTORY
this will form a new history node (don't set for back/reload/etc)
@ BW_CREATE_TAB
New gui_window to be tab in same window as "existing" gui_window.
GtkWidget * nsgtk_dialog_get_content_area(GtkDialog *dialog)
Definition: compat.c:413
void nsgtk_widget_modify_font(GtkWidget *widget, PangoFontDescription *font_desc)
Definition: compat.c:393
Compatibility functions for older GTK versions (interface)
#define NSGTK_STOCK_SAVE
Definition: compat.h:61
#define NSGTK_STOCK_OK
Definition: compat.h:64
#define NSGTK_STOCK_CANCEL
Definition: compat.h:55
wimp_w parent
Definition: dialog.c:88
nserror
Enumeration of error codes.
Definition: errors.h:29
@ NSERROR_SAVE_FAILED
Failed to save data.
Definition: errors.h:36
@ NSERROR_NOT_FOUND
Requested item not found.
Definition: errors.h:34
@ NSERROR_INIT_FAILED
Initialisation failed.
Definition: errors.h:38
@ NSERROR_BAD_PARAMETER
Bad Parameter.
Definition: errors.h:48
@ NSERROR_BAD_SIZE
Bad size.
Definition: errors.h:60
@ NSERROR_NOMEM
Memory exhaustion.
Definition: errors.h:32
@ NSERROR_OK
No error.
Definition: errors.h:30
char ** filepath_path_to_strvec(const char *path)
Convert a colon separated list of path elements into a string vector.
Definition: filepath.c:313
void filepath_free_strvec(char **pathv)
Free a string vector.
Definition: filepath.c:356
Utility routines to obtain paths to file resources.
void nsgtk_about_dialog_init(GtkWindow *parent)
Definition: about.c:95
nserror nsgtk_warning(const char *warning, const char *detail)
Warn the user of an event.
Definition: gui.c:96
#define NSLOG(catname, level, logmsg, args...)
Definition: log.h:116
const char * messages_get(const char *key)
Fast lookup of a message by key from the standard Messages hash.
Definition: messages.c:241
Localised message support (interface).
NetSurf URL handling (interface).
void nsurl_unref(nsurl *url)
Drop a reference to a NetSurf URL object.
struct nsurl nsurl
NetSurf URL object.
Definition: nsurl.h:31
nserror nsgtk_builder_new_from_resname(const char *resname, GtkBuilder **builder_out)
Create gtk builder object for the named ui resource.
Definition: resources.c:526
Interface to gtk builtin resource handling.
Interface to utility string handling.
GCallback handler
Definition: cookies.c:54
const char * widget
struct nsgtk_viewdata_ctx * prev
Definition: viewdata.c:63
struct nsgtk_viewdata_ctx * next
Definition: viewdata.c:62
GtkBuilder * builder
The gtk builder that built the widgets.
Definition: viewdata.c:58
GtkTextView * gv
handle to gtk text view (builder holds reference)
Definition: viewdata.c:60
GtkWindow * window
handle to gtk window (builder holds reference)
Definition: viewdata.c:59
nserror netsurf_path_to_nsurl(const char *path, struct nsurl **url)
Create a nsurl from a path.
Definition: file.c:307
Default operations table for files.
Option reading and saving interface.
#define nsoption_int(OPTION)
Get the value of an integer option.
Definition: nsoption.h:313
UTF-8 manipulation functions (interface).
Interface to a number of general purpose functionality.
#define SLEN(x)
Calculate length of constant C string.
Definition: utils.h:88
gboolean nsgtk_on_viewdata_select_all_activate(GtkMenuItem *widget, gpointer g)
Definition: viewdata.c:234
static gboolean nsgtk_viewdata_destroy_event(GtkBuilder *window, gpointer g)
Definition: viewdata.c:119
static char * xdg_get_exec_cmd(const char *path, const char *desktop)
Search desktop file for an Exec line.
Definition: viewdata.c:706
nserror nsgtk_viewfile(const char *title, const char *leafname, const char *filename)
Display file to a user.
Definition: viewdata.c:966
static nserror tab_init_fname(const char *title, const char *leafname, const char *fname)
open a new tab from an existing file.
Definition: viewdata.c:500
static char ** build_exec_argv(const char *fname, const char *exec_cmd)
Build vector for executing app.
Definition: viewdata.c:777
gboolean nsgtk_on_viewdata_about_activate(GtkMenuItem *widget, gpointer g)
Definition: viewdata.c:338
static char ** xdg_data_strvec(void)
Build string vector of search path.
Definition: viewdata.c:575
static nserror window_init(const char *title, const char *filename, char *ndata, size_t ndata_len)
View the data in a gtk text window.
Definition: viewdata.c:351
static void nsgtk_attach_viewdata_menu_handlers(GtkBuilder *xml, gpointer g)
Definition: viewdata.c:107
static char * xdg_get_default_app(const char *path, const char *mimetype)
Search application defaults file for matching mime type.
Definition: viewdata.c:639
#define MENUEVENT(x)
Definition: viewdata.c:74
static void nsgtk_viewdata_file_save(GtkWindow *parent, const char *filename, const char *data, size_t data_size)
Definition: viewdata.c:152
gboolean nsgtk_on_viewdata_zoom_normal_activate(GtkMenuItem *widget, gpointer g)
Definition: viewdata.c:330
static nserror editor_init(const char *title, const char *leafname, char *ndata, size_t ndata_len)
open an editor with data.
Definition: viewdata.c:892
gboolean nsgtk_on_viewdata_zoom_in_activate(GtkMenuItem *widget, gpointer g)
Definition: viewdata.c:311
#define MENUPROTO(x)
Definition: viewdata.c:75
gboolean nsgtk_on_viewdata_save_as_activate(GtkMenuItem *widget, gpointer g)
Definition: viewdata.c:184
gboolean nsgtk_on_viewdata_copy_activate(GtkMenuItem *widget, gpointer g)
Definition: viewdata.c:252
static nserror editor_init_fname(const char *title, const char *leafname, const char *fname)
open an editor from an existing file.
Definition: viewdata.c:826
static struct nsgtk_viewdata_ctx * nsgtk_viewdata_list
Definition: viewdata.c:71
static gboolean nsgtk_viewdata_delete_event(GtkWindow *window, gpointer g)
Definition: viewdata.c:145
static nserror tab_init(const char *title, const char *leafname, char *ndata, size_t ndata_len)
create a new tab from data.
Definition: viewdata.c:527
gboolean nsgtk_on_viewdata_paste_activate(GtkMenuItem *widget, gpointer g)
Definition: viewdata.c:263
nserror nsgtk_viewdata(const char *title, const char *filename, char *ndata, size_t ndata_len)
Display text to a user.
Definition: viewdata.c:931
static nserror window_init_fname(const char *title, const char *leafname, const char *filename)
open a window to dispaly an existing file.
Definition: viewdata.c:451
static char viewdata_zoomlevel
Definition: viewdata.c:72
gboolean nsgtk_on_viewdata_print_activate(GtkMenuItem *widget, gpointer g)
Definition: viewdata.c:216
gboolean nsgtk_on_viewdata_close_activate(GtkMenuItem *widget, gpointer g)
Definition: viewdata.c:223
gboolean nsgtk_on_viewdata_delete_activate(GtkMenuItem *widget, gpointer g)
Definition: viewdata.c:268
gboolean nsgtk_on_viewdata_zoom_out_activate(GtkMenuItem *widget, gpointer g)
Definition: viewdata.c:319
static void nsgtk_viewdata_update_zoomlevel(gpointer g)
Definition: viewdata.c:273
static struct menu_events viewdata_menu_events[]
Definition: viewdata.c:91
gboolean nsgtk_on_viewdata_cut_activate(GtkMenuItem *widget, gpointer g)
Definition: viewdata.c:247
static char * exec_arg(const char *arg, int len, const char *fname)
Definition: viewdata.c:754
static nserror path(const struct redraw_context *ctx, const plot_style_t *pstyle, const float *p, unsigned int n, const float transform[6])
Plots a path.
Definition: plot.c:821
static nserror line(const struct redraw_context *ctx, const plot_style_t *style, const struct rect *line)
Plots a line.
Definition: plot.c:579