1 /*
2 * gretl -- Gnu Regression, Econometrics and Time-series Library
3 * Copyright (C) 2001 Allin Cottrell and Riccardo "Jack" Lucchetti
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program 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 #include "gretl.h"
21 #include "textbuf.h"
22 #include "toolbar.h"
23 #include "dlgutils.h"
24 #include "winstack.h"
25 #include "tabwin.h"
26 #include "guiprint.h"
27 #include "gui_recode.h"
28 #include "gretl_func.h"
29 #include "addons_utils.h"
30 #include "datafiles.h"
31 #include "database.h"
32 #include "fncall.h"
33
34 #ifdef G_OS_WIN32
35 # include "gretlwin32.h" /* for browser_open() */
36 #endif
37
38 #if GTKSOURCEVIEW_VERSION > 2
39 # define GTK_IS_SOURCE_VIEW GTK_SOURCE_IS_VIEW
40 #else /* using GtkSourceView 2 */
41 # include <gtksourceview/gtksourcelanguagemanager.h>
42 # include <gtksourceview/gtksourceprintcompositor.h>
43 # include <gtksourceview/gtksourcestyleschememanager.h>
44 #endif
45
46 #ifdef HAVE_GTKSV_COMPLETION
47 # include "completions.h"
48 #endif
49
50 #define TABDEBUG 0
51 #define KDEBUG 0
52
53 /* Dummy "page" numbers for use in hyperlinks: these
54 must be greater than the number of gretl commands
55 and built-in functions to avoid collisions.
56 */
57
58 #define GUIDE_PAGE 999
59 #define SCRIPT_PAGE 998
60 #define GFR_PAGE 997
61 #define BIB_PAGE 996
62 #define EXT_PAGE 995
63 #define PDF_PAGE 994
64 #define MNU_PAGE 993
65 #define DBN_PAGE 992
66 #define DBS_PAGE 991
67 #define NEXT_PAGE 990
68
69 enum {
70 PLAIN_TEXT,
71 BLUE_TEXT,
72 RED_TEXT
73 };
74
75 #define gui_help(r) (r == GUI_HELP || r == GUI_HELP_EN)
76 #define function_help(r) (r == FUNC_HELP || r == FUNC_HELP_EN)
77 #define foreign_script_role(r) (r == EDIT_GP || \
78 r == EDIT_R || \
79 r == EDIT_OX || \
80 r == EDIT_OCTAVE || \
81 r == EDIT_PYTHON || \
82 r == EDIT_STATA || \
83 r == EDIT_JULIA || \
84 r == EDIT_DYNARE || \
85 r == EDIT_LPSOLVE)
86
87 /* globals accessed in settings.c */
88 int tabwidth = 4;
89 int smarttab = 1;
90 int script_line_numbers = 0;
91 int script_auto_bracket = 0;
92
93 static gboolean script_electric_enter (windata_t *vwin);
94 static gboolean script_tab_handler (windata_t *vwin, GdkEvent *event);
95 static gboolean
96 script_popup_handler (GtkWidget *w, GdkEventButton *event, gpointer p);
97 static gchar *textview_get_current_line_with_newline (GtkWidget *view);
98 static gboolean
99 insert_text_with_markup (GtkTextBuffer *tbuf, GtkTextIter *iter,
100 const char *s, int role);
101 static void connect_link_signals (windata_t *vwin);
102 static void auto_indent_script (GtkWidget *w, windata_t *vwin);
103
text_set_cursor(GtkWidget * w,GdkCursorType cspec)104 void text_set_cursor (GtkWidget *w, GdkCursorType cspec)
105 {
106 GdkWindow *win = gtk_text_view_get_window(GTK_TEXT_VIEW(w),
107 GTK_TEXT_WINDOW_TEXT);
108
109 if (cspec == 0) {
110 gdk_window_set_cursor(win, NULL);
111 } else {
112 GdkCursor *cursor = gdk_cursor_new(cspec);
113
114 if (cursor != NULL) {
115 gdk_window_set_cursor(win, cursor);
116 gdk_cursor_unref(cursor);
117 }
118 }
119 }
120
cursor_to_top(windata_t * vwin)121 void cursor_to_top (windata_t *vwin)
122 {
123 GtkTextView *view = GTK_TEXT_VIEW(vwin->text);
124 GtkTextBuffer *buf = gtk_text_view_get_buffer(view);
125 GtkTextIter start;
126 GtkTextMark *mark;
127
128 gtk_text_buffer_get_start_iter(buf, &start);
129 gtk_text_buffer_place_cursor(buf, &start);
130 mark = gtk_text_buffer_create_mark(buf, NULL, &start, FALSE);
131 gtk_text_view_scroll_to_mark(view, mark, 0.0, FALSE, 0, 0);
132 gtk_text_buffer_delete_mark(buf, mark);
133 }
134
cursor_to_end(windata_t * vwin)135 void cursor_to_end (windata_t *vwin)
136 {
137 GtkTextView *view = GTK_TEXT_VIEW(vwin->text);
138 GtkTextBuffer *buf = gtk_text_view_get_buffer(view);
139 GtkTextIter end;
140
141 gtk_text_buffer_get_end_iter(buf, &end);
142 gtk_text_buffer_place_cursor(buf, &end);
143 }
144
scroll_to_foot(windata_t * vwin)145 void scroll_to_foot (windata_t *vwin)
146 {
147 GtkTextView *view = GTK_TEXT_VIEW(vwin->text);
148 GtkTextBuffer *buf = gtk_text_view_get_buffer(view);
149 GtkTextIter end;
150 GtkTextMark *mark;
151
152 gtk_text_buffer_get_end_iter(buf, &end);
153 mark = gtk_text_buffer_create_mark(buf, NULL, &end, FALSE);
154 gtk_text_view_scroll_to_mark(view, mark, 0.0, FALSE, 0, 0);
155 gtk_text_buffer_delete_mark(buf, mark);
156 }
157
cursor_to_mark(windata_t * vwin,GtkTextMark * mark)158 void cursor_to_mark (windata_t *vwin, GtkTextMark *mark)
159 {
160 GtkTextView *view = GTK_TEXT_VIEW(vwin->text);
161 GtkTextBuffer *buf = gtk_text_view_get_buffer(view);
162 GtkTextIter iter;
163
164 gtk_text_buffer_get_iter_at_mark(buf, &iter, mark);
165 gtk_text_buffer_place_cursor(buf, &iter);
166 gtk_text_view_scroll_to_mark(view, mark, 0.0, TRUE, 0, 0.1);
167 }
168
get_char_width_and_height(GtkWidget * widget,int * width,int * height)169 static void get_char_width_and_height (GtkWidget *widget,
170 int *width,
171 int *height)
172 {
173 PangoContext *pc;
174 PangoLayout *pl;
175 int w = 0, h = 0;
176
177 pc = gtk_widget_get_pango_context(widget);
178 pango_context_set_font_description(pc, fixed_font);
179 pl = pango_layout_new(pc);
180 pango_layout_set_text(pl, "X", -1);
181 pango_layout_get_pixel_size(pl, &w, &h);
182 g_object_unref(pl);
183
184 if (width != NULL) {
185 *width = w;
186 }
187 if (height != NULL) {
188 *height = h;
189 }
190 }
191
get_char_width(GtkWidget * widget)192 gint get_char_width (GtkWidget *widget)
193 {
194 int width;
195
196 get_char_width_and_height(widget, &width, NULL);
197 return width;
198 }
199
get_char_height(GtkWidget * widget)200 gint get_char_height (GtkWidget *widget)
201 {
202 int height;
203
204 get_char_width_and_height(widget, NULL, &height);
205 return height;
206 }
207
textview_get_text(GtkWidget * view)208 gchar *textview_get_text (GtkWidget *view)
209 {
210 GtkTextBuffer *tbuf;
211 GtkTextIter start, end;
212
213 g_return_val_if_fail(GTK_IS_TEXT_VIEW(view), NULL);
214
215 tbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(view));
216 gtk_text_buffer_get_start_iter(tbuf, &start);
217 gtk_text_buffer_get_end_iter(tbuf, &end);
218
219 return gtk_text_buffer_get_text(tbuf, &start, &end, FALSE);
220 }
221
textview_get_trimmed_text(GtkWidget * view)222 gchar *textview_get_trimmed_text (GtkWidget *view)
223 {
224 g_return_val_if_fail(GTK_IS_TEXT_VIEW(view), NULL);
225
226 return g_strchug(g_strchomp(textview_get_text(view)));
227 }
228
normalize_line(const char * s)229 static gchar *normalize_line (const char *s)
230 {
231 int i, j = 0, n = strlen(s);
232 gchar *ret = g_malloc0(n);
233
234 for (i=0; s[i]; i++) {
235 if (isspace(s[i])) {
236 ret[j++] = ' ';
237 while (isspace(s[i])) i++;
238 }
239 ret[j++] = s[i];
240 }
241
242 return ret;
243 }
244
textview_get_normalized_line(GtkWidget * view)245 gchar *textview_get_normalized_line (GtkWidget *view)
246 {
247 gchar *ret = NULL;
248
249 g_return_val_if_fail(GTK_IS_TEXT_VIEW(view), NULL);
250
251 ret = g_strchug(g_strchomp(textview_get_text(view)));
252 if (strchr(ret, '\n') != NULL || strstr(ret, " ") != NULL) {
253 gchar *alt = normalize_line(ret);
254
255 g_free(ret);
256 ret = alt;
257 }
258
259 return ret;
260 }
261
262 /* Special: handle the case where text has been line-wrapped
263 in an editor window and we want to save the text as
264 wrapped -- that is, forcibly to truncate excessively
265 long lines. We use this for function-package help text.
266 */
267
textview_get_wrapped_text(GtkWidget * view)268 gchar *textview_get_wrapped_text (GtkWidget *view)
269 {
270 GtkTextView *tview;
271 GtkTextBuffer *tbuf;
272 GtkTextIter start, end;
273 GString *str;
274 gchar *line;
275
276 g_return_val_if_fail(GTK_IS_TEXT_VIEW(view), NULL);
277
278 tview = GTK_TEXT_VIEW(view);
279 tbuf = gtk_text_view_get_buffer(tview);
280 gtk_text_buffer_get_start_iter(tbuf, &start);
281 end = start;
282
283 /* first detect and handle the case where no wrapping has
284 occurred */
285 if (!gtk_text_view_forward_display_line(tview, &end)) {
286 gtk_text_buffer_get_end_iter(tbuf, &end);
287 return gtk_text_buffer_get_text(tbuf, &start, &end, FALSE);
288 }
289
290 str = g_string_new(NULL);
291
292 end = start;
293 while (gtk_text_view_forward_display_line(tview, &end)) {
294 line = gtk_text_buffer_get_text(tbuf, &start, &end, FALSE);
295 g_strchomp(line);
296 g_string_append(str, line);
297 g_string_append_c(str, '\n');
298 g_free(line);
299 start = end;
300 }
301
302 if (!gtk_text_iter_is_end(&start)) {
303 /* there's some residual text */
304 gtk_text_buffer_get_end_iter(tbuf, &end);
305 line = gtk_text_buffer_get_text(tbuf, &start, &end, FALSE);
306 g_strchomp(line);
307 g_string_append(str, line);
308 g_string_append_c(str, '\n');
309 g_free(line);
310 }
311
312 return g_string_free(str, FALSE);
313 }
314
textview_get_selection_or_all(GtkWidget * view,gboolean * selection)315 gchar *textview_get_selection_or_all (GtkWidget *view,
316 gboolean *selection)
317 {
318 GtkTextBuffer *tbuf;
319 GtkTextIter start, end;
320
321 g_return_val_if_fail(GTK_IS_TEXT_VIEW(view), NULL);
322
323 tbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(view));
324 if (tbuf == NULL) {
325 return NULL;
326 }
327
328 if (gtk_text_buffer_get_selection_bounds(tbuf, &start, &end)) {
329 *selection = TRUE;
330 } else {
331 *selection = FALSE;
332 gtk_text_buffer_get_start_iter(tbuf, &start);
333 gtk_text_buffer_get_end_iter(tbuf, &end);
334 }
335
336 return gtk_text_buffer_get_text(tbuf, &start, &end, FALSE);
337 }
338
real_textview_set_text(GtkWidget * view,const gchar * text,gboolean select)339 static int real_textview_set_text (GtkWidget *view,
340 const gchar *text,
341 gboolean select)
342 {
343 GtkTextBuffer *tbuf;
344
345 g_return_val_if_fail(GTK_IS_TEXT_VIEW(view), 1);
346
347 tbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(view));
348 g_return_val_if_fail(tbuf != NULL, 1);
349
350 if (text != NULL && select) {
351 GtkTextIter start, end;
352
353 gtk_text_buffer_set_text(tbuf, text, -1);
354 gtk_text_buffer_get_start_iter(tbuf, &start);
355 gtk_text_buffer_get_end_iter(tbuf, &end);
356 gtk_text_buffer_select_range(tbuf, &start, &end);
357 } else if (text != NULL) {
358 gtk_text_buffer_set_text(tbuf, text, -1);
359 } else {
360 gtk_text_buffer_set_text(tbuf, "", -1);
361 }
362
363 return 0;
364 }
365
textview_set_text(GtkWidget * view,const gchar * text)366 int textview_set_text (GtkWidget *view, const gchar *text)
367 {
368 return real_textview_set_text(view, text, FALSE);
369 }
370
textview_set_text_selected(GtkWidget * view,const gchar * text)371 int textview_set_text_selected (GtkWidget *view, const gchar *text)
372 {
373 return real_textview_set_text(view, text, TRUE);
374 }
375
textview_set_cursor_at_line(GtkWidget * view,int line)376 int textview_set_cursor_at_line (GtkWidget *view, int line)
377 {
378 GtkTextBuffer *tbuf;
379 GtkTextIter iter;
380
381 g_return_val_if_fail(GTK_IS_TEXT_VIEW(view), 1);
382
383 tbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(view));
384 g_return_val_if_fail(tbuf != NULL, 1);
385
386 gtk_text_buffer_get_iter_at_line(tbuf, &iter, line);
387 gtk_text_buffer_place_cursor(tbuf, &iter);
388
389 return 0;
390 }
391
viewer_char_count(windata_t * vwin)392 int viewer_char_count (windata_t *vwin)
393 {
394 GtkTextBuffer *tbuf;
395
396 tbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(vwin->text));
397 return gtk_text_buffer_get_char_count(tbuf);
398 }
399
text_paste(GtkWidget * w,windata_t * vwin)400 void text_paste (GtkWidget *w, windata_t *vwin)
401 {
402 gtk_text_buffer_paste_clipboard(gtk_text_view_get_buffer(GTK_TEXT_VIEW(vwin->text)),
403 gtk_clipboard_get(GDK_NONE),
404 NULL, TRUE);
405 }
406
text_redo(GtkWidget * w,windata_t * vwin)407 void text_redo (GtkWidget *w, windata_t *vwin)
408 {
409 if (vwin->sbuf != NULL && gtk_source_buffer_can_redo(vwin->sbuf)) {
410 gtk_source_buffer_redo(vwin->sbuf);
411 } else {
412 warnbox(_("No redo information available"));
413 }
414 }
415
text_undo(GtkWidget * w,windata_t * vwin)416 void text_undo (GtkWidget *w, windata_t *vwin)
417 {
418 if (vwin->sbuf != NULL && gtk_source_buffer_can_undo(vwin->sbuf)) {
419 gtk_source_buffer_undo(vwin->sbuf);
420 } else {
421 warnbox(_("No undo information available"));
422 }
423 }
424
text_can_undo(windata_t * vwin)425 int text_can_undo (windata_t *vwin)
426 {
427 if (vwin->sbuf != NULL) {
428 return gtk_source_buffer_can_undo(vwin->sbuf);
429 } else {
430 return 0;
431 }
432 }
433
source_buffer_load_file(GtkSourceBuffer * sbuf,int role,const char * fname)434 static int source_buffer_load_file (GtkSourceBuffer *sbuf,
435 int role,
436 const char *fname)
437 {
438 GtkTextBuffer *tbuf = GTK_TEXT_BUFFER(sbuf);
439 GtkTextIter iter;
440 gchar *buf = NULL;
441 gsize sz = 0;
442
443 gtk_source_buffer_begin_not_undoable_action(sbuf);
444 gtk_text_buffer_set_text(tbuf, "", -1);
445 gtk_text_buffer_get_iter_at_offset(tbuf, &iter, 0);
446
447 gretl_file_get_contents(fname, &buf, &sz);
448
449 if (buf != NULL) {
450 gchar *trbuf = NULL;
451
452 if (!g_utf8_validate(buf, -1, NULL)) {
453 trbuf = my_locale_to_utf8(buf);
454 if (trbuf != NULL) {
455 gtk_text_buffer_insert(tbuf, &iter, trbuf, -1);
456 g_free(trbuf);
457 }
458 } else {
459 gtk_text_buffer_insert(tbuf, &iter, buf, -1);
460 }
461 g_free(buf);
462 }
463
464 gtk_source_buffer_end_not_undoable_action(sbuf);
465 gtk_text_buffer_set_modified(tbuf, role == EDIT_PKG_SAMPLE);
466
467 /* move cursor to the beginning */
468 gtk_text_buffer_get_start_iter(tbuf, &iter);
469 gtk_text_buffer_place_cursor(tbuf, &iter);
470
471 return 0;
472 }
473
source_buffer_load_buf(GtkSourceBuffer * sbuf,const char * buf)474 static int source_buffer_load_buf (GtkSourceBuffer *sbuf, const char *buf)
475 {
476 GtkTextBuffer *tbuf = GTK_TEXT_BUFFER(sbuf);
477 GtkTextIter iter;
478
479 gtk_source_buffer_begin_not_undoable_action(sbuf);
480 gtk_text_buffer_set_text(tbuf, buf, -1);
481 gtk_source_buffer_end_not_undoable_action(sbuf);
482 gtk_text_buffer_set_modified(tbuf, FALSE);
483
484 /* move cursor to the beginning */
485 gtk_text_buffer_get_start_iter(tbuf, &iter);
486 gtk_text_buffer_place_cursor(tbuf, &iter);
487
488 return 0;
489 }
490
sourceview_apply_language(windata_t * vwin)491 static void sourceview_apply_language (windata_t *vwin)
492 {
493 GtkSourceLanguageManager *lm;
494 GtkSourceLanguage *lang = NULL;
495 const char *id = NULL;
496
497 lm = g_object_get_data(G_OBJECT(vwin->sbuf), "languages-manager");
498
499 if (lm == NULL) {
500 return;
501 }
502
503 if (vwin->role == EDIT_GP) {
504 id = "gnuplot";
505 } else if (vwin->role == EDIT_R) {
506 id = "r";
507 } else if (vwin->role == EDIT_OX) {
508 id = "cpp";
509 } else if (vwin->role == EDIT_OCTAVE) {
510 id = "octave";
511 } else if (vwin->role == EDIT_PYTHON) {
512 id = "python";
513 } else if (vwin->role == EDIT_JULIA) {
514 id = "julia";
515 } else if (vwin->role == EDIT_STATA) {
516 id = "stata";
517 } else if (vwin->role == EDIT_DYNARE) {
518 id = "cpp";
519 } else if (vwin->role == EDIT_LPSOLVE) {
520 id = "lpsolve";
521 } else if (vwin->role == EDIT_SPEC) {
522 id = "gfnspec";
523 } else {
524 id = "gretl";
525 }
526
527 lang = gtk_source_language_manager_get_language(lm, id);
528
529 if (lang == NULL) {
530 const gchar * const *S = gtk_source_language_manager_get_search_path(lm);
531
532 fprintf(stderr, "*** gtksourceview: lang is NULL for id='%s'\n", id);
533 if (S != NULL) {
534 fprintf(stderr, "the gtksourceview search path:\n");
535 while (*S != NULL) {
536 fprintf(stderr, " %s\n", *S);
537 S++;
538 }
539 } else {
540 fprintf(stderr, "the gtksourceview search path is NULL!\n");
541 }
542 } else {
543 gtk_source_buffer_set_language(vwin->sbuf, lang);
544 }
545 }
546
547 #define SV_PRINT_DEBUG 0
548
549 #if SV_PRINT_DEBUG
550
show_print_context(GtkPrintContext * context)551 static void show_print_context (GtkPrintContext *context)
552 {
553 gdouble x = gtk_print_context_get_width(context);
554 gdouble y = gtk_print_context_get_height(context);
555 GtkPageSetup *psu;
556
557 fprintf(stderr, "begin_print: context pixel size: %g x %g\n", x, y);
558 x = gtk_print_context_get_dpi_x(context);
559 y = gtk_print_context_get_dpi_y(context);
560 fprintf(stderr, " context dpi: %g, %g\n", x, y);
561
562 psu = gtk_print_context_get_page_setup(context);
563 if (psu != NULL) {
564 x = gtk_page_setup_get_paper_width(psu, GTK_UNIT_INCH);
565 y = gtk_page_setup_get_paper_width(psu, GTK_UNIT_POINTS);
566 fprintf(stderr, " paper width: %g in, %g points\n", x, y);
567 x = gtk_page_setup_get_paper_height(psu, GTK_UNIT_INCH);
568 y = gtk_page_setup_get_paper_height(psu, GTK_UNIT_POINTS);
569 fprintf(stderr, " paper height: %g in, %g points\n", x, y);
570 }
571 }
572
573 #endif
574
begin_print(GtkPrintOperation * operation,GtkPrintContext * context,gpointer data)575 static void begin_print (GtkPrintOperation *operation,
576 GtkPrintContext *context,
577 gpointer data)
578 {
579 GtkSourcePrintCompositor *comp;
580 int n_pages;
581
582 comp = GTK_SOURCE_PRINT_COMPOSITOR(data);
583
584 #if SV_PRINT_DEBUG
585 GtkPrintSettings *st = gtk_print_operation_get_print_settings(operation);
586 if (st != NULL) {
587 int rx = gtk_print_settings_get_resolution_x(st);
588 int ry = gtk_print_settings_get_resolution_y(st);
589
590 fprintf(stderr, "settings: resolution %d,%d\n", rx, ry);
591 }
592 show_print_context(context);
593 #endif
594
595 while (!gtk_source_print_compositor_paginate(comp, context));
596
597 n_pages = gtk_source_print_compositor_get_n_pages(comp);
598 gtk_print_operation_set_n_pages(operation, n_pages);
599 }
600
draw_page(GtkPrintOperation * operation,GtkPrintContext * context,gint page_nr,gpointer data)601 static void draw_page (GtkPrintOperation *operation,
602 GtkPrintContext *context,
603 gint page_nr,
604 gpointer data)
605 {
606 GtkSourcePrintCompositor *comp =
607 GTK_SOURCE_PRINT_COMPOSITOR(data);
608
609 gtk_source_print_compositor_draw_page(comp, context,
610 page_nr);
611 }
612
sourceview_print(windata_t * vwin)613 void sourceview_print (windata_t *vwin)
614 {
615 GtkSourceView *view = GTK_SOURCE_VIEW(vwin->text);
616 GtkSourcePrintCompositor *comp;
617 GtkPrintOperation *print;
618 GtkPrintOperationResult res;
619 GtkWidget *mainwin;
620 GError *error = NULL;
621
622 comp = gtk_source_print_compositor_new_from_view(view);
623 print = gtk_print_operation_new();
624
625 #ifdef G_OS_WIN32
626 /* the units are wacky if we don't set this */
627 gtk_print_operation_set_unit(print, GTK_UNIT_POINTS);
628 #endif
629
630 gtk_source_print_compositor_set_right_margin(comp, 60, GTK_UNIT_POINTS);
631 gtk_source_print_compositor_set_left_margin(comp, 60, GTK_UNIT_POINTS);
632 gtk_source_print_compositor_set_top_margin(comp, 54, GTK_UNIT_POINTS);
633 gtk_source_print_compositor_set_bottom_margin(comp, 72, GTK_UNIT_POINTS);
634 gtk_source_print_compositor_set_wrap_mode(comp, GTK_WRAP_WORD);
635 gtk_source_print_compositor_set_body_font_name(comp, "Monospace 9");
636
637 g_signal_connect(G_OBJECT(print), "begin_print", G_CALLBACK(begin_print), comp);
638 g_signal_connect(G_OBJECT(print), "draw_page", G_CALLBACK(draw_page), comp);
639
640 mainwin = vwin_toplevel(vwin);
641 res = gtk_print_operation_run(print,
642 GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG,
643 GTK_WINDOW(mainwin),
644 &error);
645
646 if (res == GTK_PRINT_OPERATION_RESULT_ERROR) {
647 GtkWidget *dlg;
648
649 dlg = gtk_message_dialog_new(GTK_WINDOW(mainwin),
650 GTK_DIALOG_DESTROY_WITH_PARENT,
651 GTK_MESSAGE_ERROR,
652 GTK_BUTTONS_CLOSE,
653 "Error printing file:\n%s",
654 error->message);
655 g_signal_connect(G_OBJECT(dlg), "response",
656 G_CALLBACK(gtk_widget_destroy), NULL);
657 gtk_widget_show(dlg);
658 g_error_free(error);
659 } else if (res == GTK_PRINT_OPERATION_RESULT_APPLY) {
660 ; /* OK: maybe save the settings? */
661 }
662
663 if (print != NULL) {
664 g_object_unref(print);
665 }
666 }
667
sourceview_insert_file(windata_t * vwin,const char * fname)668 void sourceview_insert_file (windata_t *vwin, const char *fname)
669 {
670 sourceview_apply_language(vwin);
671 source_buffer_load_file(vwin->sbuf, vwin->role, fname);
672 }
673
sourceview_insert_buffer(windata_t * vwin,const char * buf)674 void sourceview_insert_buffer (windata_t *vwin, const char *buf)
675 {
676 sourceview_apply_language(vwin);
677 source_buffer_load_buf(vwin->sbuf, buf);
678 }
679
set_source_tabs(GtkWidget * w,int cw)680 static void set_source_tabs (GtkWidget *w, int cw)
681 {
682 static PangoTabArray *ta;
683
684 if (ta == NULL) {
685 int tabw = tabwidth * cw;
686 gint i, loc = tabw;
687
688 ta = pango_tab_array_new(10, TRUE);
689 for (i=0; i<10; i++) {
690 pango_tab_array_set_tab(ta, i, PANGO_TAB_LEFT, loc);
691 loc += tabw;
692 }
693 }
694
695 gtk_text_view_set_tabs(GTK_TEXT_VIEW(w), ta);
696 }
697
698 #define tabkey(k) (k == GDK_Tab || \
699 k == GDK_ISO_Left_Tab || \
700 k == GDK_KP_Tab)
701
702 /* Special keystrokes in native script window: Ctrl-Return sends the
703 current line for execution; Ctrl-R sends the whole script for
704 execution (keyboard equivalent of the "execute" button);
705 Ctrl-I does auto-indentation.
706 */
707
script_key_handler(GtkWidget * w,GdkEvent * event,windata_t * vwin)708 static gint script_key_handler (GtkWidget *w,
709 GdkEvent *event,
710 windata_t *vwin)
711 {
712 guint keyval = ((GdkEventKey *) event)->keyval;
713 guint state = ((GdkEventKey *) event)->state;
714 gboolean ret = FALSE;
715
716 #if KDEBUG
717 fprintf(stderr, "HERE script_key_handler (keyval %u, %s)\n",
718 keyval, gdk_keyval_name(keyval));
719 #endif
720
721 if (state & GDK_CONTROL_MASK) {
722 if (keyval == GDK_R) {
723 run_script_silent(w, vwin);
724 ret = TRUE;
725 } else if (keyval == GDK_r) {
726 do_run_script(w, vwin);
727 ret = TRUE;
728 } else if (keyval == GDK_Return) {
729 gchar *str = textview_get_current_line_with_newline(w);
730
731 if (str != NULL) {
732 if (!string_is_blank(str)) {
733 run_script_fragment(vwin, str);
734 }
735 g_free(str);
736 }
737 ret = TRUE;
738 } else if (keyval == GDK_i) {
739 auto_indent_script(w, vwin);
740 ret = TRUE;
741 }
742 } else if (keyval == GDK_F1) {
743 set_window_help_active(vwin);
744 interactive_script_help(NULL, NULL, vwin);
745 ret = TRUE;
746 } else if (editing_hansl(vwin->role)) {
747 if (keyval == GDK_Return) {
748 ret = script_electric_enter(vwin);
749 } else if (tabkey(keyval)) {
750 #if TABDEBUG
751 fprintf(stderr, "*** calling script_tab_handler ***\n");
752 #endif
753 ret = script_tab_handler(vwin, event);
754 } else if (script_auto_bracket && lbracket(keyval)) {
755 ret = script_bracket_handler(vwin, keyval);
756 }
757 }
758
759 return ret;
760 }
761
762 static gint
foreign_script_key_handler(GtkWidget * w,GdkEvent * event,windata_t * vwin)763 foreign_script_key_handler (GtkWidget *w, GdkEvent *event, windata_t *vwin)
764 {
765 guint keyval = ((GdkEventKey *) event)->keyval;
766 gboolean ret = FALSE;
767
768 if (((GdkEventKey *) event)->state & GDK_CONTROL_MASK) {
769 if (keyval == GDK_r) {
770 do_run_script(w, vwin);
771 ret = TRUE;
772 }
773 }
774
775 return ret;
776 }
777
778 #ifdef PKGBUILD
779
780 # ifdef G_OS_WIN32
781
ensure_utf8_path(gchar * path)782 static gchar *ensure_utf8_path (gchar *path)
783 {
784 if (!g_utf8_validate(path, -1, NULL)) {
785 gchar *tmp;
786 gsize bytes;
787
788 tmp = g_locale_to_utf8(path, -1, NULL, &bytes, NULL);
789 if (tmp != NULL) {
790 g_free(path);
791 path = tmp;
792 }
793 }
794
795 return path;
796 }
797
798 # endif
799
800 /* Packages for Windows and OS X: gtksourceview needs to
801 be told where to find its language-specs and style
802 files: these live under gtksourceview inside the package.
803
804 On Windows we need to ensure that the "set_search_path"
805 functions are fed a UTF-8 path, since gtksourceview uses
806 g_open() internally and the Glib filename encoding is
807 always UTF-8 on Windows.
808 */
809
ensure_sourceview_path(GtkSourceLanguageManager * lm)810 static void ensure_sourceview_path (GtkSourceLanguageManager *lm)
811 {
812 static int done;
813
814 if (!done && lm == NULL) {
815 lm = gtk_source_language_manager_get_default();
816 }
817
818 if (!done && lm != NULL) {
819 GtkSourceStyleSchemeManager *mgr;
820 gchar *dirs[2] = {NULL, NULL};
821
822 dirs[0] = g_strdup_printf("%sgtksourceview", gretl_home());
823 # ifdef G_OS_WIN32
824 dirs[0] = ensure_utf8_path(dirs[0]);
825 # endif
826 gtk_source_language_manager_set_search_path(lm, dirs);
827
828 mgr = gtk_source_style_scheme_manager_get_default();
829 gtk_source_style_scheme_manager_set_search_path(mgr, dirs);
830 gtk_source_style_scheme_manager_force_rescan(mgr);
831
832 g_free(dirs[0]);
833 done = 1;
834 }
835 }
836
837 #else /* not PKGBUILD */
838
839 /* gtksourceview needs to be told to search both its own "native"
840 paths and @prefix/share/gretl/gtksourceview for both language
841 file and style files
842 */
843
ensure_sourceview_path(GtkSourceLanguageManager * lm)844 static void ensure_sourceview_path (GtkSourceLanguageManager *lm)
845 {
846 static int done;
847
848 if (!done && lm == NULL) {
849 lm = gtk_source_language_manager_get_default();
850 }
851
852 if (!done && lm != NULL) {
853 GtkSourceStyleSchemeManager *mgr;
854 gchar *dirs[3] = {NULL, NULL, NULL};
855
856 /* languages: we need to set path, can't just append */
857 dirs[0] = g_strdup_printf("%sgtksourceview", gretl_home());
858 #if GTKSOURCEVIEW_VERSION > 3
859 dirs[1] = g_strdup_printf("%s/share/gtksourceview-%d/language-specs",
860 SVPREFIX, GTKSOURCEVIEW_VERSION);
861 #else
862 dirs[1] = g_strdup_printf("%s/share/gtksourceview-%d.0/language-specs",
863 SVPREFIX, GTKSOURCEVIEW_VERSION);
864 #endif
865 gtk_source_language_manager_set_search_path(lm, dirs);
866
867 /* styles: we can just append to the default path */
868 mgr = gtk_source_style_scheme_manager_get_default();
869 gtk_source_style_scheme_manager_append_search_path(mgr, dirs[0]);
870 gtk_source_style_scheme_manager_force_rescan(mgr);
871
872 g_free(dirs[0]);
873 g_free(dirs[1]);
874
875 done = 1;
876 }
877 }
878
879 #endif
880
set_console_output_style(GtkSourceBuffer * sbuf,GtkSourceStyleScheme * scheme)881 static void set_console_output_style (GtkSourceBuffer *sbuf,
882 GtkSourceStyleScheme *scheme)
883 {
884 GtkSourceStyle *style = NULL;
885 GtkTextTag *tag = NULL;
886 GtkTextTagTable *tt;
887 int done = 0;
888
889 tt = gtk_text_buffer_get_tag_table(GTK_TEXT_BUFFER(sbuf));
890 if (tt != NULL) {
891 tag = gtk_text_tag_table_lookup(tt, "output");
892 }
893 if (tag != NULL) {
894 style = gtk_source_style_scheme_get_style(scheme, "text");
895 }
896 if (style != NULL) {
897 gchar *fg = NULL, *bg = NULL;
898
899 g_object_get(style, "foreground", &fg, "background", &bg, NULL);
900 if (fg != NULL && bg != NULL) {
901 g_object_set(tag, "foreground", fg, "background", bg, NULL);
902 done = 1;
903 }
904 g_free(fg);
905 g_free(bg);
906 }
907 if (tag != NULL && !done) {
908 /* fallback */
909 g_object_set(tag, "foreground", "black", "background", "white", NULL);
910 }
911 }
912
set_style_for_buffer(GtkSourceBuffer * sbuf,const char * id,int role)913 static void set_style_for_buffer (GtkSourceBuffer *sbuf,
914 const char *id,
915 int role)
916 {
917 GtkSourceStyleSchemeManager *mgr;
918 GtkSourceStyleScheme *scheme;
919
920 if (id == NULL || *id == '\0') {
921 return;
922 }
923
924 mgr = gtk_source_style_scheme_manager_get_default();
925 scheme = gtk_source_style_scheme_manager_get_scheme(mgr, id);
926 if (scheme != NULL) {
927 gtk_source_buffer_set_style_scheme(sbuf, scheme);
928 if (role == CONSOLE) {
929 set_console_output_style(sbuf, scheme);
930 }
931 }
932 }
933
set_style_for_textview(GtkWidget * text,const char * id)934 void set_style_for_textview (GtkWidget *text, const char *id)
935 {
936 GtkSourceStyleSchemeManager *mgr;
937 GtkSourceStyleScheme *scheme;
938 GtkTextBuffer *tbuf;
939
940 tbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
941 mgr = gtk_source_style_scheme_manager_get_default();
942 scheme = gtk_source_style_scheme_manager_get_scheme(mgr, id);
943 if (scheme != NULL) {
944 gtk_source_buffer_set_style_scheme(GTK_SOURCE_BUFFER(tbuf), scheme);
945 }
946 }
947
948 #define gretl_script_role(r) (r == EDIT_HANSL || \
949 r == VIEW_SCRIPT || \
950 r == EDIT_PKG_CODE || \
951 r == EDIT_PKG_SAMPLE || \
952 r == VIEW_PKG_SAMPLE)
953
create_source(windata_t * vwin,int hsize,int vsize,gboolean editable)954 void create_source (windata_t *vwin, int hsize, int vsize,
955 gboolean editable)
956 {
957 GtkSourceLanguageManager *lm = NULL;
958 GtkSourceBuffer *sbuf;
959 GtkTextView *view;
960 int cw;
961
962 if (textview_use_highlighting(vwin->role)) {
963 lm = gtk_source_language_manager_get_default();
964 ensure_sourceview_path(lm);
965 }
966
967 sbuf = GTK_SOURCE_BUFFER(gtk_source_buffer_new(NULL));
968 if (lm != NULL) {
969 g_object_set_data(G_OBJECT(sbuf), "languages-manager", lm);
970 }
971 gtk_source_buffer_set_highlight_matching_brackets(sbuf, TRUE);
972
973 vwin->text = gtk_source_view_new_with_buffer(sbuf);
974 vwin->sbuf = sbuf;
975
976 #ifdef HAVE_GTKSV_COMPLETION
977 if (editing_hansl(vwin->role) || vwin->role == CONSOLE) {
978 set_sv_completion(vwin);
979 }
980 #endif
981
982 view = GTK_TEXT_VIEW(vwin->text);
983 gtk_text_view_set_wrap_mode(view, GTK_WRAP_NONE);
984 gtk_text_view_set_left_margin(view, 4);
985 gtk_text_view_set_right_margin(view, 4);
986
987 gtk_widget_modify_font(GTK_WIDGET(vwin->text), fixed_font);
988
989 cw = get_char_width(vwin->text);
990 set_source_tabs(vwin->text, cw);
991
992 if (hsize > 0) {
993 hsize *= cw;
994 hsize += 48; /* ?? */
995 }
996
997 if (!(vwin->flags & VWIN_SWALLOW) && hsize > 0 && vsize > 0) {
998 GtkWidget *vmain = vwin_toplevel(vwin);
999
1000 if (window_is_tab(vwin)) {
1001 vsize += 15;
1002 }
1003 if (vsize < 0.62 * hsize) {
1004 /* approx golden ratio */
1005 vsize = 0.62 * hsize;
1006 }
1007 gtk_window_set_default_size(GTK_WINDOW(vmain), hsize, vsize);
1008 }
1009
1010 gtk_text_view_set_editable(view, editable);
1011 gtk_text_view_set_cursor_visible(view, editable);
1012
1013 gtk_source_view_set_show_line_numbers(GTK_SOURCE_VIEW(vwin->text),
1014 script_line_numbers);
1015
1016 if (lm != NULL) {
1017 set_style_for_buffer(sbuf, get_sourceview_style(), vwin->role);
1018 }
1019
1020 if (!(vwin->flags & WVIN_KEY_SIGNAL_SET)) {
1021 g_signal_connect(G_OBJECT(vwin->text), "key-press-event",
1022 G_CALLBACK(catch_viewer_key), vwin);
1023 }
1024
1025 if (gretl_script_role(vwin->role)) {
1026 g_signal_connect(G_OBJECT(vwin->text), "key-press-event",
1027 G_CALLBACK(script_key_handler), vwin);
1028 g_signal_connect(G_OBJECT(vwin->text), "button-press-event",
1029 G_CALLBACK(script_popup_handler),
1030 vwin);
1031 g_signal_connect(G_OBJECT(vwin->text), "button-release-event",
1032 G_CALLBACK(interactive_script_help), vwin);
1033 } else if (foreign_script_role(vwin->role)) {
1034 g_signal_connect(G_OBJECT(vwin->text), "key-press-event",
1035 G_CALLBACK(foreign_script_key_handler), vwin);
1036 g_signal_connect(G_OBJECT(vwin->text), "button-press-event",
1037 G_CALLBACK(script_popup_handler),
1038 vwin);
1039 } else if (vwin->role == VIEW_LOG) {
1040 g_signal_connect(G_OBJECT(vwin->text), "button-release-event",
1041 G_CALLBACK(interactive_script_help), vwin);
1042 }
1043 }
1044
1045 /* Manufacture a little sampler sourceview for use in the
1046 Editor tab of the gretl preferences dialog
1047 */
1048
create_sample_source(const char * style)1049 GtkWidget *create_sample_source (const char *style)
1050 {
1051 GtkSourceLanguageManager *lm;
1052 GtkSourceLanguage *lang;
1053 GtkSourceBuffer *sbuf;
1054 GtkTextView *view;
1055 GtkWidget *text;
1056
1057 const gchar *sample =
1058 "# sample of highlighting style\n"
1059 "open wages.gdt\n"
1060 "series l_wage = log(wage)\n"
1061 "ols l_wage 0 male school exper --robust\n";
1062
1063 lm = gtk_source_language_manager_get_default();
1064 if (lm == NULL) {
1065 return NULL;
1066 }
1067
1068 ensure_sourceview_path(lm);
1069
1070 lang = gtk_source_language_manager_get_language(lm, "gretl");
1071 if (lang == NULL) {
1072 return NULL;
1073 }
1074
1075 sbuf = GTK_SOURCE_BUFFER(gtk_source_buffer_new_with_language(lang));
1076 text = gtk_source_view_new_with_buffer(sbuf);
1077 view = GTK_TEXT_VIEW(text);
1078
1079 gtk_text_view_set_left_margin(view, 4);
1080 gtk_text_view_set_right_margin(view, 4);
1081 gtk_widget_modify_font(text, fixed_font);
1082 gtk_text_view_set_editable(view, FALSE);
1083 gtk_text_view_set_cursor_visible(view, FALSE);
1084
1085 gtk_text_buffer_set_text(GTK_TEXT_BUFFER(sbuf), sample, -1);
1086 gtk_source_buffer_set_highlight_syntax(sbuf, TRUE);
1087 set_style_for_buffer(sbuf, style, 0);
1088
1089 return text;
1090 }
1091
1092 /* callback after changes made in preferences dialog */
1093
update_script_editor_options(windata_t * vwin)1094 void update_script_editor_options (windata_t *vwin)
1095 {
1096 ensure_sourceview_path(NULL);
1097
1098 if (vwin->role != CONSOLE) {
1099 gtk_source_view_set_show_line_numbers(GTK_SOURCE_VIEW(vwin->text),
1100 script_line_numbers);
1101 }
1102 set_style_for_buffer(vwin->sbuf, get_sourceview_style(), vwin->role);
1103
1104 #ifdef HAVE_GTKSV_COMPLETION
1105 if (vwin->role == CONSOLE || editing_hansl(vwin->role)) {
1106 set_sv_completion(vwin);
1107 }
1108 #endif
1109 }
1110
text_change_size(windata_t * vwin,int delta)1111 static int text_change_size (windata_t *vwin, int delta)
1112 {
1113 static PangoFontDescription *hpf;
1114 static int fsize0;
1115 int fsize;
1116
1117 if (hpf == NULL) {
1118 hpf = pango_font_description_copy(fixed_font);
1119 fsize0 = pango_font_description_get_size(hpf) / PANGO_SCALE;
1120 }
1121
1122 if (vwin == NULL) {
1123 return fsize0;
1124 }
1125
1126 fsize = widget_get_int(vwin->text, "fsize");
1127 if (fsize == 0) {
1128 fsize = fsize0;
1129 }
1130
1131 fsize += delta;
1132
1133 pango_font_description_set_size(hpf, fsize * PANGO_SCALE);
1134 gtk_widget_modify_font(vwin->text, hpf);
1135 widget_set_int(vwin->text, "fsize", fsize);
1136
1137 return fsize;
1138 }
1139
text_larger(GtkWidget * w,gpointer data)1140 void text_larger (GtkWidget *w, gpointer data)
1141 {
1142 text_change_size((windata_t *) data, 1);
1143 }
1144
text_smaller(GtkWidget * w,gpointer data)1145 void text_smaller (GtkWidget *w, gpointer data)
1146 {
1147 text_change_size((windata_t *) data, -1);
1148 }
1149
1150 #ifdef OS_OSX
1151 # define helpfont "Geneva"
1152 #else
1153 # define helpfont "sans"
1154 #endif
1155
gretl_tags_new(void)1156 static GtkTextTagTable *gretl_tags_new (void)
1157 {
1158 GtkTextTagTable *table;
1159 GtkTextTag *tag;
1160 int bigsize;
1161 int smallsize;
1162
1163 bigsize = 15 * PANGO_SCALE;
1164 smallsize = 8 * PANGO_SCALE;
1165
1166 table = gtk_text_tag_table_new();
1167
1168 tag = gtk_text_tag_new("bluetext");
1169 g_object_set(tag, "foreground", "blue", NULL);
1170 gtk_text_tag_table_add(table, tag);
1171
1172 tag = gtk_text_tag_new("redtext");
1173 g_object_set(tag, "foreground", "red", NULL);
1174 gtk_text_tag_table_add(table, tag);
1175
1176 tag = gtk_text_tag_new("greentext");
1177 g_object_set(tag, "foreground", "green", NULL);
1178 gtk_text_tag_table_add(table, tag);
1179
1180 tag = gtk_text_tag_new("title");
1181 g_object_set(tag, "justification", GTK_JUSTIFY_CENTER,
1182 "pixels_above_lines", 15,
1183 "family", helpfont,
1184 "size", bigsize, NULL);
1185 gtk_text_tag_table_add(table, tag);
1186
1187 tag = gtk_text_tag_new("heading");
1188 g_object_set(tag, "family", helpfont,
1189 "weight", PANGO_WEIGHT_BOLD,
1190 NULL);
1191 gtk_text_tag_table_add(table, tag);
1192
1193 tag = gtk_text_tag_new("grayhead");
1194 g_object_set(tag, "family", helpfont,
1195 "foreground", "gray",
1196 "pixels_below_lines", 5,
1197 NULL);
1198 gtk_text_tag_table_add(table, tag);
1199
1200 tag = gtk_text_tag_new("italic");
1201 g_object_set(tag, "family", helpfont,
1202 "style", PANGO_STYLE_ITALIC,
1203 NULL);
1204 gtk_text_tag_table_add(table, tag);
1205
1206 tag = gtk_text_tag_new("replaceable");
1207 g_object_set(tag, "family", helpfont,
1208 "style", PANGO_STYLE_ITALIC,
1209 NULL);
1210 gtk_text_tag_table_add(table, tag);
1211
1212 tag = gtk_text_tag_new("superscript");
1213 g_object_set(tag, "family", helpfont,
1214 "style", PANGO_STYLE_NORMAL,
1215 "rise", 4 * PANGO_SCALE,
1216 "size", smallsize,
1217 NULL);
1218 gtk_text_tag_table_add(table, tag);
1219
1220 tag = gtk_text_tag_new("subscript");
1221 g_object_set(tag, "family", helpfont,
1222 "style", PANGO_STYLE_ITALIC,
1223 "rise", -3 * PANGO_SCALE,
1224 "size", smallsize,
1225 NULL);
1226 gtk_text_tag_table_add(table, tag);
1227
1228 tag = gtk_text_tag_new("subscript-numeral");
1229 g_object_set(tag, "family", helpfont,
1230 "style", PANGO_STYLE_NORMAL,
1231 "rise", -3 * PANGO_SCALE,
1232 "size", smallsize,
1233 NULL);
1234 gtk_text_tag_table_add(table, tag);
1235
1236 tag = gtk_text_tag_new("literal");
1237 g_object_set(tag, "family", "monospace", NULL);
1238 gtk_text_tag_table_add(table, tag);
1239
1240 tag = gtk_text_tag_new("optflag");
1241 g_object_set(tag, "family", "monospace",
1242 "foreground", "#396d60", NULL);
1243 gtk_text_tag_table_add(table, tag);
1244
1245 tag = gtk_text_tag_new("text");
1246 g_object_set(tag, "family", helpfont, NULL);
1247 gtk_text_tag_table_add(table, tag);
1248
1249 tag = gtk_text_tag_new("indented");
1250 g_object_set(tag, "left_margin", 16, "indent", -12, NULL);
1251 gtk_text_tag_table_add(table, tag);
1252
1253 tag = gtk_text_tag_new("code");
1254 g_object_set(tag, "family", "monospace",
1255 "paragraph-background", "#e6f3ff",
1256 NULL);
1257 gtk_text_tag_table_add(table, tag);
1258
1259 tag = gtk_text_tag_new("mono");
1260 g_object_set(tag, "family", "monospace", NULL);
1261 gtk_text_tag_table_add(table, tag);
1262
1263 return table;
1264 }
1265
gretl_text_buf_new(void)1266 GtkTextBuffer *gretl_text_buf_new (void)
1267 {
1268 static GtkTextTagTable *tags = NULL;
1269 GtkTextBuffer *tbuf;
1270
1271 if (tags == NULL) {
1272 tags = gretl_tags_new();
1273 }
1274
1275 tbuf = gtk_text_buffer_new(tags);
1276
1277 return tbuf;
1278 }
1279
1280 static void
real_textview_add_colorized(GtkWidget * view,const char * buf,int append,int trim)1281 real_textview_add_colorized (GtkWidget *view, const char *buf,
1282 int append, int trim)
1283 {
1284 GtkTextBuffer *tbuf;
1285 GtkTextIter iter;
1286 int nextcolor, thiscolor = PLAIN_TEXT;
1287 int in_comment = 0;
1288 char readbuf[4096];
1289 int i = 0;
1290
1291 g_return_if_fail(GTK_IS_TEXT_VIEW(view));
1292
1293 tbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(view));
1294
1295 if (append) {
1296 gtk_text_buffer_get_end_iter(tbuf, &iter);
1297 } else {
1298 gtk_text_buffer_get_iter_at_offset(tbuf, &iter, 0);
1299 }
1300
1301 bufgets_init(buf);
1302
1303 while (bufgets(readbuf, sizeof readbuf, buf)) {
1304 if (trim && i++ < 2) {
1305 continue;
1306 }
1307
1308 if (ends_with_backslash(readbuf)) {
1309 nextcolor = thiscolor;
1310 } else {
1311 nextcolor = PLAIN_TEXT;
1312 }
1313
1314 if (*readbuf == '#' || *readbuf == '?' ||
1315 *readbuf == '>' || in_comment) {
1316 thiscolor = BLUE_TEXT;
1317 } else if (!strncmp(readbuf, "/*", 2)) {
1318 in_comment = 1;
1319 thiscolor = nextcolor = BLUE_TEXT;
1320 }
1321
1322 if (strstr(readbuf, "*/")) {
1323 in_comment = 0;
1324 nextcolor = PLAIN_TEXT;
1325 }
1326
1327 if (thiscolor == BLUE_TEXT) {
1328 gtk_text_buffer_insert_with_tags_by_name(tbuf, &iter,
1329 readbuf, -1,
1330 "bluetext", NULL);
1331 } else {
1332 gtk_text_buffer_insert(tbuf, &iter, readbuf, -1);
1333 }
1334
1335 thiscolor = nextcolor;
1336 }
1337
1338 bufgets_finalize(buf);
1339 }
1340
textview_set_text_colorized(GtkWidget * view,const char * buf)1341 void textview_set_text_colorized (GtkWidget *view, const char *buf)
1342 {
1343 real_textview_add_colorized(view, buf, 0, 0);
1344 }
1345
textview_append_text_colorized(GtkWidget * view,const char * buf,int trim)1346 void textview_append_text_colorized (GtkWidget *view, const char *buf, int trim)
1347 {
1348 real_textview_add_colorized(view, buf, 1, trim);
1349 }
1350
textview_set_text_report(GtkWidget * view,const char * buf)1351 void textview_set_text_report (GtkWidget *view, const char *buf)
1352 {
1353 GtkTextBuffer *tbuf;
1354 GtkTextIter iter;
1355 const char *p;
1356
1357 /* plain text except for "<@ok>" represented as "OK" in
1358 green and "<@fail>" as "failed" in red
1359 */
1360
1361 tbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(view));
1362 gtk_text_buffer_get_iter_at_offset(tbuf, &iter, 0);
1363
1364 while ((p = strstr(buf, "<@"))) {
1365 gtk_text_buffer_insert(tbuf, &iter, buf, p - buf);
1366 if (!strncmp(p, "<@ok>", 5)) {
1367 gtk_text_buffer_insert_with_tags_by_name(tbuf, &iter, _("OK"),
1368 -1, "greentext", NULL);
1369 buf = p + 5;
1370 } else if (!strncmp(p, "<@fail>", 7)) {
1371 gtk_text_buffer_insert_with_tags_by_name(tbuf, &iter, _("failed"),
1372 -1, "redtext", NULL);
1373 buf = p + 7;
1374 }
1375 }
1376
1377 gtk_text_buffer_insert(tbuf, &iter, buf, -1);
1378 }
1379
semicolon_pos(const char * s)1380 static const char *semicolon_pos (const char *s)
1381 {
1382 while (*s != '\0' && *s != '"') {
1383 if (*s == ';') {
1384 return s;
1385 }
1386 s++;
1387 }
1388 return NULL;
1389 }
1390
textview_set_text_dbsearch(windata_t * vwin,const char * buf)1391 void textview_set_text_dbsearch (windata_t *vwin, const char *buf)
1392 {
1393 GtkTextBuffer *tbuf;
1394 GtkTextTagTable *tab;
1395 GtkTextIter iter;
1396 GtkTextTag *tag;
1397 gchar *dsname = NULL;
1398 gchar *show = NULL;
1399 int page;
1400 const char *p, *q;
1401
1402 /* plain text except for "<@dbn>" tags for links */
1403
1404 tbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(vwin->text));
1405 tab = gtk_text_buffer_get_tag_table(tbuf);
1406 gtk_text_buffer_get_iter_at_offset(tbuf, &iter, 0);
1407
1408 while ((p = strstr(buf, "<@dbn"))) {
1409 gtk_text_buffer_insert(tbuf, &iter, buf, p - buf);
1410 p += 7;
1411 q = semicolon_pos(p);
1412 if (q != NULL) {
1413 /* should be show;tagname */
1414 page = DBS_PAGE;
1415 show = g_strndup(p, q-p);
1416 p = q + 1;
1417 q = strchr(p, '"');
1418 dsname = g_strndup(p, q-p);
1419 } else {
1420 q = strchr(p, '"');
1421 dsname = g_strndup(p, q-p);
1422 if (!strcmp(dsname, "_NEXT_")) {
1423 page = NEXT_PAGE;
1424 show = g_strdup(_("Next results"));
1425 } else {
1426 page = DBN_PAGE;
1427 show = NULL;
1428 }
1429 }
1430 tag = gtk_text_tag_table_lookup(tab, dsname);
1431 if (tag == NULL) {
1432 tag = gtk_text_buffer_create_tag(tbuf, dsname, "foreground", "blue",
1433 NULL);
1434 g_object_set_data(G_OBJECT(tag), "page", GINT_TO_POINTER(page));
1435 }
1436 gtk_text_buffer_insert_with_tags(tbuf, &iter,
1437 show == NULL ? dsname : show,
1438 -1, tag, NULL);
1439 g_free(dsname);
1440 if (show != NULL) {
1441 g_free(show);
1442 }
1443 buf = q + 2;
1444 }
1445
1446 gtk_text_buffer_insert(tbuf, &iter, buf, -1);
1447
1448 connect_link_signals(vwin);
1449 }
1450
textview_delete_processing_message(GtkWidget * view)1451 void textview_delete_processing_message (GtkWidget *view)
1452 {
1453 GtkTextBuffer *tbuf;
1454 GtkTextMark *m0, *m1;
1455 GtkTextIter i0, i1;
1456
1457 g_return_if_fail(GTK_IS_TEXT_VIEW(view));
1458
1459 tbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(view));
1460 m0 = gtk_text_buffer_get_mark(tbuf, "pstart");
1461 m1 = gtk_text_buffer_get_mark(tbuf, "pstop");
1462 if (m0 != NULL && m1 != NULL) {
1463 gtk_text_buffer_get_iter_at_mark(tbuf, &i0, m0);
1464 gtk_text_buffer_get_iter_at_mark(tbuf, &i1, m1);
1465 gtk_text_buffer_delete(tbuf, &i0, &i1);
1466 gtk_text_buffer_delete_mark(tbuf, m0);
1467 gtk_text_buffer_delete_mark(tbuf, m1);
1468 }
1469 }
1470
textview_add_processing_message(GtkWidget * view)1471 void textview_add_processing_message (GtkWidget *view)
1472 {
1473 const char *msg = N_("processing...\n");
1474 GtkTextBuffer *tbuf;
1475 GtkTextIter iter;
1476 GtkTextMark *mstop;
1477
1478 g_return_if_fail(GTK_IS_TEXT_VIEW(view));
1479
1480 tbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(view));
1481 gtk_text_buffer_get_end_iter(tbuf, &iter);
1482 gtk_text_buffer_create_mark(tbuf, "pstart", &iter, TRUE);
1483 gtk_text_buffer_insert_with_tags_by_name(tbuf, &iter,
1484 _(msg), -1,
1485 "redtext", NULL);
1486 mstop = gtk_text_buffer_create_mark(tbuf, "pstop", &iter, TRUE);
1487 gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(view), mstop, 0.0,
1488 FALSE, 0, 0);
1489 }
1490
textview_append_text(GtkWidget * view,const char * text)1491 void textview_append_text (GtkWidget *view, const char *text)
1492 {
1493 GtkTextBuffer *tbuf;
1494 GtkTextIter iter;
1495
1496 g_return_if_fail(GTK_IS_TEXT_VIEW(view));
1497
1498 tbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(view));
1499 gtk_text_buffer_get_end_iter(tbuf, &iter);
1500 gtk_text_buffer_insert(tbuf, &iter, text, -1);
1501 }
1502
textview_insert_text(GtkWidget * view,const char * text)1503 void textview_insert_text (GtkWidget *view, const char *text)
1504 {
1505 GtkTextBuffer *tbuf;
1506
1507 g_return_if_fail(GTK_IS_TEXT_VIEW(view));
1508
1509 tbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(view));
1510 gtk_text_buffer_insert_at_cursor(tbuf, text, -1);
1511 }
1512
textview_clear_text(GtkWidget * view)1513 void textview_clear_text (GtkWidget *view)
1514 {
1515 GtkTextBuffer *tbuf;
1516
1517 g_return_if_fail(GTK_IS_TEXT_VIEW(view));
1518
1519 tbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(view));
1520 gtk_text_buffer_set_text (tbuf, "", -1);
1521 }
1522
textview_insert_file(windata_t * vwin,const char * fname)1523 void textview_insert_file (windata_t *vwin, const char *fname)
1524 {
1525 FILE *fp;
1526 GtkTextBuffer *tbuf;
1527 GtkTextIter iter;
1528 int thiscolor, nextcolor;
1529 char fline[MAXSTR], *chunk;
1530 int links = 0;
1531 int i = 0;
1532
1533 g_return_if_fail(GTK_IS_TEXT_VIEW(vwin->text));
1534
1535 fp = gretl_fopen(fname, "r");
1536 if (fp == NULL) {
1537 file_read_errbox(fname);
1538 return;
1539 }
1540
1541 thiscolor = nextcolor = PLAIN_TEXT;
1542
1543 tbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(vwin->text));
1544 gtk_text_buffer_get_iter_at_offset(tbuf, &iter, 0);
1545
1546 memset(fline, 0, sizeof fline);
1547
1548 while (fgets(fline, sizeof fline, fp)) {
1549 if (!g_utf8_validate(fline, -1, NULL)) {
1550 if (i == 0) {
1551 chunk = my_locale_to_utf8(fline);
1552 i++;
1553 } else {
1554 chunk = my_locale_to_utf8_next(fline);
1555 }
1556 if (chunk == NULL) {
1557 continue;
1558 }
1559 } else {
1560 chunk = fline;
1561 }
1562
1563 nextcolor = PLAIN_TEXT;
1564
1565 if (vwin->role == VIEW_DOC && strchr(chunk, '<')) {
1566 if (!links) {
1567 links = insert_text_with_markup(tbuf, &iter, chunk, vwin->role);
1568 } else {
1569 insert_text_with_markup(tbuf, &iter, chunk, vwin->role);
1570 }
1571 } else {
1572 if (vwin->role == SCRIPT_OUT && ends_with_backslash(chunk)) {
1573 nextcolor = BLUE_TEXT;
1574 }
1575
1576 if (*chunk == '?') {
1577 thiscolor = (vwin->role == CONSOLE)? RED_TEXT : BLUE_TEXT;
1578 } else if (*chunk == '#') {
1579 thiscolor = BLUE_TEXT;
1580 }
1581
1582 switch (thiscolor) {
1583 case PLAIN_TEXT:
1584 gtk_text_buffer_insert(tbuf, &iter, chunk, -1);
1585 break;
1586 case BLUE_TEXT:
1587 gtk_text_buffer_insert_with_tags_by_name(tbuf, &iter,
1588 chunk, -1,
1589 "bluetext", NULL);
1590 break;
1591 case RED_TEXT:
1592 gtk_text_buffer_insert_with_tags_by_name(tbuf, &iter,
1593 chunk, -1,
1594 "redtext", NULL);
1595 break;
1596 }
1597 }
1598
1599 if (chunk != fline) {
1600 g_free(chunk);
1601 }
1602
1603 thiscolor = nextcolor;
1604 memset(fline, 0, sizeof fline);
1605 }
1606
1607 fclose(fp);
1608
1609 if (links) {
1610 connect_link_signals(vwin);
1611 }
1612 }
1613
get_mnu_string(const char * key)1614 static gchar *get_mnu_string (const char *key)
1615 {
1616 const char *s;
1617 gchar *ret;
1618
1619 if (!strcmp(key, "LocalGfn")) {
1620 s = _("On _local machine...");
1621 } else if (!strcmp(key, "RemoteGfn")) {
1622 s = _("On _server...");
1623 } else if (!strcmp(key, "Pkgbook")) {
1624 s = _("_Function package guide");
1625 } else if (!strcmp(key, "SFAddons")) {
1626 s = _("Check for _addons");
1627 } else if (!strcmp(key, "Registry")) {
1628 s = _("Package registry");
1629 } else if (!strcmp(key, "gretlMPI")) {
1630 s = _("gretl + MPI");
1631 } else if (!strcmp(key, "gretlSVM")) {
1632 s = _("gretl + SVM");
1633 } else if (!strcmp(key, "SetSeed")) {
1634 s = _("_Seed for random numbers");
1635 } else if (!strcmp(key, "gretlDBN")) {
1636 s = _("dbnomics for gretl");
1637 } else if (!strcmp(key, "GeoplotDoc")) {
1638 s = _("Creating maps");
1639 } else if (!strcmp(key, "LpsolveDoc")) {
1640 s = _("Linear Programs");
1641 } else {
1642 s = key;
1643 }
1644
1645 ret = g_strdup(s);
1646
1647 gretl_delchar('_', ret);
1648 gretl_delchar('.', ret);
1649
1650 return ret;
1651 }
1652
1653 #define TAGLEN 128
1654
insert_link(GtkTextBuffer * tbuf,GtkTextIter * iter,const char * text,gint page,const char * indent)1655 static gboolean insert_link (GtkTextBuffer *tbuf, GtkTextIter *iter,
1656 const char *text, gint page,
1657 const char *indent)
1658 {
1659 GtkTextTagTable *tab = gtk_text_buffer_get_tag_table(tbuf);
1660 GtkTextTag *tag;
1661 gchar *show = NULL;
1662 gchar tagname[TAGLEN];
1663
1664 if (page == GUIDE_PAGE) {
1665 char *p = strrchr(text, '#');
1666
1667 if (p != NULL) {
1668 show = g_strndup(text, p - text);
1669 strcpy(tagname, p + 1);
1670 } else {
1671 strcpy(tagname, "tag:guide");
1672 }
1673 } else if (page == MNU_PAGE) {
1674 show = get_mnu_string(text);
1675 strcpy(tagname, text);
1676 } else if (page == SCRIPT_PAGE || page == EXT_PAGE) {
1677 *tagname = '\0';
1678 strncat(tagname, text, TAGLEN-1);
1679 } else if (page == PDF_PAGE) {
1680 const char *p = path_last_slash_const(text);
1681
1682 *tagname = '\0';
1683 strncat(tagname, text, TAGLEN-1);
1684 if (p != NULL) {
1685 show = g_strdup(p + 1);
1686 } else {
1687 show = g_strdup(text); /* OK? */
1688 }
1689 } else if (page == BIB_PAGE) {
1690 char *p = strrchr(text, ';');
1691
1692 if (p != NULL) {
1693 strcpy(tagname, p + 1);
1694 show = g_strndup(text, p - text);
1695 } else {
1696 strcpy(tagname, text);
1697 }
1698 } else {
1699 sprintf(tagname, "tag:p%d", page);
1700 }
1701
1702 tag = gtk_text_tag_table_lookup(tab, tagname);
1703
1704 if (tag == NULL) {
1705 if (page == GUIDE_PAGE || page == BIB_PAGE || page == MNU_PAGE) {
1706 tag = gtk_text_buffer_create_tag(tbuf, tagname, "foreground", "blue",
1707 "family", helpfont, NULL);
1708 } else if (page == SCRIPT_PAGE || page == EXT_PAGE ||
1709 page == PDF_PAGE) {
1710 tag = gtk_text_buffer_create_tag(tbuf, tagname, "foreground", "blue",
1711 "family", "monospace", NULL);
1712 } else if (indent != NULL) {
1713 tag = gtk_text_buffer_create_tag(tbuf, tagname, "foreground", "blue",
1714 "left_margin", 30, NULL);
1715 } else {
1716 tag = gtk_text_buffer_create_tag(tbuf, tagname, "foreground", "blue", NULL);
1717 }
1718 g_object_set_data(G_OBJECT(tag), "page", GINT_TO_POINTER(page));
1719 }
1720
1721 if (show != NULL) {
1722 gtk_text_buffer_insert_with_tags(tbuf, iter, show, -1, tag, NULL);
1723 g_free(show);
1724 } else {
1725 gtk_text_buffer_insert_with_tags(tbuf, iter, text, -1, tag, NULL);
1726 }
1727
1728 return TRUE;
1729 }
1730
insert_xlink(GtkTextBuffer * tbuf,GtkTextIter * iter,const char * text,gint page,const char * indent)1731 static gboolean insert_xlink (GtkTextBuffer *tbuf, GtkTextIter *iter,
1732 const char *text, gint page,
1733 const char *indent)
1734 {
1735 GtkTextTagTable *tab = gtk_text_buffer_get_tag_table(tbuf);
1736 GtkTextTag *tag;
1737 int gfr = 0;
1738 gchar tagname[32];
1739
1740 if (page == GFR_PAGE) {
1741 strcpy(tagname, "tag:gfr");
1742 gfr = 1;
1743 page = 0;
1744 } else {
1745 sprintf(tagname, "xtag:p%d", page);
1746 }
1747
1748 tag = gtk_text_tag_table_lookup(tab, tagname);
1749
1750 if (tag == NULL) {
1751 /* the required tag is not already in the table */
1752 if (gfr) {
1753 tag = gtk_text_buffer_create_tag(tbuf, tagname, "foreground", "blue",
1754 "family", helpfont, NULL);
1755 } else if (indent != NULL) {
1756 tag = gtk_text_buffer_create_tag(tbuf, tagname, "foreground", "blue",
1757 "left_margin", 30, NULL);
1758 } else {
1759 tag = gtk_text_buffer_create_tag(tbuf, tagname, "foreground", "blue", NULL);
1760 }
1761 g_object_set_data(G_OBJECT(tag), "page", GINT_TO_POINTER(page));
1762 g_object_set_data(G_OBJECT(tag), "xref", GINT_TO_POINTER(1));
1763 }
1764
1765 gtk_text_buffer_insert_with_tags(tbuf, iter, text, -1, tag, NULL);
1766
1767 return TRUE;
1768 }
1769
open_script_link(GtkTextTag * tag)1770 static void open_script_link (GtkTextTag *tag)
1771 {
1772 char fullname[MAXLEN] = {0};
1773 gchar *fname = NULL;
1774 int err;
1775
1776 g_object_get(G_OBJECT(tag), "name", &fname, NULL);
1777 err = get_full_filename(fname, fullname, OPT_S);
1778 if (err) {
1779 errbox_printf(_("Couldn't find %s"), fname);
1780 } else {
1781 err = gretl_test_fopen(fullname, "r");
1782 if (err) {
1783 errbox_printf(_("Couldn't read %s"), fullname);
1784 }
1785 }
1786 g_free(fname);
1787
1788 if (!err) {
1789 view_script(fullname, 0, VIEW_SCRIPT);
1790 }
1791 }
1792
make_bibitem_window(const char * buf,GtkWidget * tview)1793 static void make_bibitem_window (const char *buf,
1794 GtkWidget *tview)
1795 {
1796 windata_t *vwin;
1797 GtkWidget *top, *vmain;
1798
1799 vwin = view_formatted_text_buffer(NULL, buf, 64, 100, VIEW_BIBITEM);
1800 vmain = vwin_toplevel(vwin);
1801 top = gtk_widget_get_toplevel(tview);
1802 gtk_window_set_transient_for(GTK_WINDOW(vmain), GTK_WINDOW(top));
1803 gtk_window_set_destroy_with_parent(GTK_WINDOW(vmain), TRUE);
1804 gtk_window_set_position(GTK_WINDOW(vmain),
1805 GTK_WIN_POS_CENTER_ON_PARENT);
1806 gtk_widget_show(vmain);
1807 }
1808
open_bibitem_link(GtkTextTag * tag,GtkWidget * tview)1809 static void open_bibitem_link (GtkTextTag *tag, GtkWidget *tview)
1810 {
1811 const char *gretldir = gretl_home();
1812 gchar *key = NULL;
1813 char fullname[MAXLEN];
1814 FILE *fp;
1815
1816 g_object_get(G_OBJECT(tag), "name", &key, NULL);
1817 sprintf(fullname, "%sgretlhelp.refs", gretldir);
1818 fp = gretl_fopen(fullname, "r");
1819
1820 if (fp != NULL) {
1821 char *buf, line[4096];
1822 gchar *p, *modbuf;
1823 int n = strlen(key);
1824
1825 while (fgets(line, sizeof line, fp)) {
1826 if (!strncmp(line, "<@key=\"", 7)) {
1827 if (!strncmp(line + 7, key, n)) {
1828 p = strchr(line + 7, '>');
1829 if (p != NULL) {
1830 buf = p + 1;
1831 if ((p = strstr(buf, "<@url")) != NULL) {
1832 /* put bibitem URL on new line */
1833 n = p - buf;
1834 modbuf = g_strdup_printf("%.*s\n%s", n, buf, p);
1835 make_bibitem_window(modbuf, tview);
1836 g_free(modbuf);
1837 } else {
1838 make_bibitem_window(buf, tview);
1839 }
1840 }
1841 break;
1842 }
1843 }
1844 }
1845
1846 fclose(fp);
1847 }
1848
1849 g_free(key);
1850 }
1851
object_get_int(gpointer p,const char * key)1852 static int object_get_int (gpointer p, const char *key)
1853 {
1854 return GPOINTER_TO_INT(g_object_get_data(G_OBJECT(p), key));
1855 }
1856
open_external_link(GtkTextTag * tag)1857 static void open_external_link (GtkTextTag *tag)
1858 {
1859 gchar *name = NULL;
1860
1861 g_object_get(G_OBJECT(tag), "name", &name, NULL);
1862
1863 if (name != NULL) {
1864 if (strncmp(name, "http://", 7) &&
1865 strncmp(name, "https://", 8)) {
1866 gchar *url = g_strdup_printf("http://%s", name);
1867
1868 browser_open(url);
1869 g_free(url);
1870 } else {
1871 browser_open(name);
1872 }
1873 g_free(name);
1874 }
1875 }
1876
open_menu_item(GtkTextTag * tag)1877 static void open_menu_item (GtkTextTag *tag)
1878 {
1879 gchar *name = NULL;
1880
1881 g_object_get(G_OBJECT(tag), "name", &name, NULL);
1882
1883 if (name != NULL) {
1884 if (!strcmp(name, "RemoteGfn")) {
1885 display_files(REMOTE_FUNC_FILES, NULL);
1886 } else if (!strcmp(name, "LocalGfn")) {
1887 display_files(FUNC_FILES, NULL);
1888 } else if (!strcmp(name, "SFAddons")) {
1889 display_files(REMOTE_ADDONS, NULL);
1890 } else if (!strcmp(name, "Registry")) {
1891 display_files(PKG_REGISTRY, NULL);
1892 } else if (!strcmp(name, "SetSeed")) {
1893 rand_seed_dialog();
1894 } else {
1895 /* should be a PDF help file */
1896 static GtkAction *action;
1897
1898 if (action == NULL) {
1899 action = gtk_action_new(name, NULL, NULL, NULL);
1900 }
1901 display_pdf_help(action);
1902 }
1903 g_free(name);
1904 }
1905 }
1906
1907 /* opening a series-listing window, coming from a dbnomics
1908 dataset window */
1909
open_dbn_link(GtkTextTag * tag)1910 static void open_dbn_link (GtkTextTag *tag)
1911 {
1912 gchar *name = NULL;
1913
1914 g_object_get(G_OBJECT(tag), "name", &name, NULL);
1915
1916 if (name != NULL) {
1917 display_files(DBNOMICS_SERIES, name);
1918 g_free(name);
1919 }
1920 }
1921
1922 /* opening a specific series info window, coming from a
1923 dbnomics dataset-search window */
1924
open_dbs_link(GtkTextTag * tag)1925 static void open_dbs_link (GtkTextTag *tag)
1926 {
1927 gchar *name = NULL;
1928
1929 g_object_get(G_OBJECT(tag), "name", &name, NULL);
1930
1931 if (name != NULL) {
1932 dbnomics_get_series_call(name);
1933 g_free(name);
1934 }
1935 }
1936
1937 /* opening next "page" pf dbnomics search results */
1938
open_next_link(GtkTextTag * tag,GtkWidget * tview)1939 static void open_next_link (GtkTextTag *tag, GtkWidget *tview)
1940 {
1941 windata_t *vwin;
1942
1943 vwin = g_object_get_data(G_OBJECT(tview), "vwin");
1944 if (vwin != NULL) {
1945 dbnomics_search(NULL, vwin);
1946 }
1947 }
1948
open_pdf_file(GtkTextTag * tag)1949 static void open_pdf_file (GtkTextTag *tag)
1950 {
1951 gchar *name = NULL;
1952
1953 g_object_get(G_OBJECT(tag), "name", &name, NULL);
1954
1955 if (name != NULL) {
1956 int warn = 0;
1957
1958 if (strchr(name, '/') == NULL && strchr(name, '\\') == NULL) {
1959 char *path = get_addon_pdf_path(name);
1960
1961 if (path != NULL) {
1962 gretl_show_pdf(path, NULL);
1963 free(path);
1964 } else {
1965 /* not an addon file */
1966 char fname[FILENAME_MAX] = {0};
1967
1968 warn = get_pdf_path(name, fname);
1969 if (!warn) {
1970 gretl_show_pdf(fname, NULL);
1971 }
1972 }
1973 } else if (gretl_stat(name, NULL) == 0) {
1974 gretl_show_pdf(name, NULL);
1975 } else {
1976 warn = 1;
1977 }
1978 if (warn) {
1979 warnbox_printf(_("Couldn't open %s"), name);
1980 }
1981 g_free(name);
1982 }
1983 }
1984
follow_if_link(GtkWidget * tview,GtkTextIter * iter,gpointer en_ptr)1985 static void follow_if_link (GtkWidget *tview, GtkTextIter *iter,
1986 gpointer en_ptr)
1987 {
1988 GSList *tags = NULL, *tagp = NULL;
1989
1990 tags = gtk_text_iter_get_tags(iter);
1991
1992 for (tagp = tags; tagp != NULL; tagp = tagp->next) {
1993 GtkTextTag *tag = tagp->data;
1994 gint page = object_get_int(tag, "page");
1995 gint xref = object_get_int(tag, "xref");
1996 gint en = GPOINTER_TO_INT(en_ptr);
1997
1998 if (page != 0 || xref != 0) {
1999 if (page == GUIDE_PAGE) {
2000 gchar *name = NULL;
2001
2002 g_object_get(tag, "name", &name, NULL);
2003 if (name != NULL && strstr(name, "chap:")) {
2004 display_guide_chapter(name);
2005 } else {
2006 display_pdf_help(NULL);
2007 }
2008 g_free(name);
2009 } else if (page == SCRIPT_PAGE) {
2010 open_script_link(tag);
2011 } else if (page == BIB_PAGE) {
2012 open_bibitem_link(tag, tview);
2013 } else if (page == EXT_PAGE) {
2014 open_external_link(tag);
2015 } else if (page == PDF_PAGE) {
2016 open_pdf_file(tag);
2017 } else if (page == MNU_PAGE) {
2018 open_menu_item(tag);
2019 } else if (page == DBN_PAGE) {
2020 open_dbn_link(tag);
2021 } else if (page == DBS_PAGE) {
2022 open_dbs_link(tag);
2023 } else if (page == NEXT_PAGE) {
2024 open_next_link(tag, tview);
2025 } else {
2026 int role = object_get_int(tview, "role");
2027
2028 if (function_help(role)) {
2029 if (xref) {
2030 command_help_callback(page, en);
2031 } else {
2032 function_help_callback(page, en);
2033 }
2034 } else {
2035 /* commands help */
2036 if (xref) {
2037 function_help_callback(page, en);
2038 } else {
2039 command_help_callback(page, en);
2040 }
2041 }
2042 }
2043 break;
2044 }
2045 }
2046
2047 if (tags) {
2048 g_slist_free(tags);
2049 }
2050 }
2051
2052 /* Help links can be activated by pressing Enter */
2053
cmdref_key_press(GtkWidget * tview,GdkEventKey * ev,gpointer en_ptr)2054 static gboolean cmdref_key_press (GtkWidget *tview, GdkEventKey *ev,
2055 gpointer en_ptr)
2056 {
2057 GtkTextIter iter;
2058 GtkTextBuffer *tbuf;
2059
2060 switch (ev->keyval) {
2061 case GDK_Return:
2062 case GDK_KP_Enter:
2063 tbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(tview));
2064 gtk_text_buffer_get_iter_at_mark(tbuf, &iter,
2065 gtk_text_buffer_get_insert(tbuf));
2066 follow_if_link(tview, &iter, en_ptr);
2067 break;
2068 default:
2069 break;
2070 }
2071
2072 return FALSE;
2073 }
2074
2075 /* Help links can be activated by clicking */
2076
cmdref_event_after(GtkWidget * w,GdkEvent * ev,gpointer en_ptr)2077 static gboolean cmdref_event_after (GtkWidget *w, GdkEvent *ev,
2078 gpointer en_ptr)
2079 {
2080 GtkTextIter start, end, iter;
2081 GtkTextView *view;
2082 GtkTextBuffer *buffer;
2083 GdkEventButton *event;
2084 gint x, y;
2085
2086 if (ev->type != GDK_BUTTON_RELEASE) {
2087 return FALSE;
2088 }
2089
2090 event = (GdkEventButton *) ev;
2091
2092 if (event->button != 1) {
2093 return FALSE;
2094 }
2095
2096 view = GTK_TEXT_VIEW(w);
2097 buffer = gtk_text_view_get_buffer(view);
2098
2099 /* don't follow a link if the user has selected something */
2100 gtk_text_buffer_get_selection_bounds(buffer, &start, &end);
2101 if (gtk_text_iter_get_offset(&start) != gtk_text_iter_get_offset(&end))
2102 return FALSE;
2103
2104 gtk_text_view_window_to_buffer_coords(view, GTK_TEXT_WINDOW_WIDGET,
2105 event->x, event->y, &x, &y);
2106
2107 gtk_text_view_get_iter_at_location(view, &iter, x, y);
2108
2109 follow_if_link(w, &iter, en_ptr);
2110
2111 return FALSE;
2112 }
2113
2114 static GdkCursor *hand_cursor = NULL;
2115 static GdkCursor *regular_cursor = NULL;
2116
ensure_text_cursors(void)2117 static void ensure_text_cursors (void)
2118 {
2119 if (hand_cursor == NULL) {
2120 hand_cursor = gdk_cursor_new(GDK_HAND2);
2121 }
2122 if (regular_cursor == NULL) {
2123 regular_cursor = gdk_cursor_new(GDK_XTERM);
2124 }
2125 }
2126
2127 static void
set_cursor_if_appropriate(GtkTextView * view,gint x,gint y)2128 set_cursor_if_appropriate (GtkTextView *view, gint x, gint y)
2129 {
2130 static gboolean hovering_over_link = FALSE;
2131 GSList *tags = NULL, *tagp = NULL;
2132 GtkTextIter iter;
2133 gboolean hovering = FALSE;
2134
2135 gtk_text_view_get_iter_at_location(view, &iter, x, y);
2136 tags = gtk_text_iter_get_tags(&iter);
2137
2138 for (tagp = tags; tagp != NULL; tagp = tagp->next) {
2139 GtkTextTag *tag = tagp->data;
2140 gint page = object_get_int(tag, "page");
2141 gint xref = object_get_int(tag, "xref");
2142
2143 if (page != 0 || xref != 0) {
2144 hovering = TRUE;
2145 break;
2146 }
2147 }
2148
2149 if (hovering != hovering_over_link) {
2150 hovering_over_link = hovering;
2151 if (hovering_over_link) {
2152 gdk_window_set_cursor(gtk_text_view_get_window(view, GTK_TEXT_WINDOW_TEXT),
2153 hand_cursor);
2154 } else {
2155 gdk_window_set_cursor(gtk_text_view_get_window(view, GTK_TEXT_WINDOW_TEXT),
2156 regular_cursor);
2157 }
2158 }
2159
2160 if (tags) {
2161 g_slist_free(tags);
2162 }
2163 }
2164
2165 static gboolean
cmdref_motion_notify(GtkWidget * w,GdkEventMotion * event)2166 cmdref_motion_notify (GtkWidget *w, GdkEventMotion *event)
2167 {
2168 GtkTextView *view = GTK_TEXT_VIEW(w);
2169 gint x, y;
2170
2171 gtk_text_view_window_to_buffer_coords(view, GTK_TEXT_WINDOW_WIDGET,
2172 event->x, event->y, &x, &y);
2173 set_cursor_if_appropriate(view, x, y);
2174
2175 return FALSE;
2176 }
2177
2178 static gboolean
cmdref_visibility_notify(GtkWidget * w,GdkEventVisibility * e)2179 cmdref_visibility_notify (GtkWidget *w, GdkEventVisibility *e)
2180 {
2181 GtkTextView *view = GTK_TEXT_VIEW(w);
2182 gint wx, wy, bx, by;
2183
2184 widget_get_pointer_info(w, &wx, &wy, NULL);
2185 gtk_text_view_window_to_buffer_coords(view, GTK_TEXT_WINDOW_WIDGET,
2186 wx, wy, &bx, &by);
2187 set_cursor_if_appropriate(view, bx, by);
2188
2189 return FALSE;
2190 }
2191
connect_link_signals(windata_t * vwin)2192 static void connect_link_signals (windata_t *vwin)
2193 {
2194 ensure_text_cursors();
2195 g_signal_connect(G_OBJECT(vwin->text), "key-press-event",
2196 G_CALLBACK(cmdref_key_press), NULL);
2197 g_signal_connect(G_OBJECT(vwin->text), "event-after",
2198 G_CALLBACK(cmdref_event_after), NULL);
2199 g_signal_connect(G_OBJECT(vwin->text), "motion-notify-event",
2200 G_CALLBACK(cmdref_motion_notify), NULL);
2201 g_signal_connect(G_OBJECT(vwin->text), "visibility-notify-event",
2202 G_CALLBACK(cmdref_visibility_notify), NULL);
2203 }
2204
maybe_connect_help_signals(windata_t * hwin,int en)2205 static void maybe_connect_help_signals (windata_t *hwin, int en)
2206 {
2207 int done = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(hwin->text),
2208 "sigs_connected"));
2209
2210 ensure_text_cursors();
2211
2212 if (!done) {
2213 gpointer en_ptr = GINT_TO_POINTER(en);
2214
2215 g_signal_connect(G_OBJECT(hwin->text), "key-press-event",
2216 G_CALLBACK(cmdref_key_press), en_ptr);
2217 g_signal_connect(G_OBJECT(hwin->text), "event-after",
2218 G_CALLBACK(cmdref_event_after), en_ptr);
2219 g_signal_connect(G_OBJECT(hwin->text), "motion-notify-event",
2220 G_CALLBACK(cmdref_motion_notify), NULL);
2221 g_signal_connect(G_OBJECT(hwin->text), "visibility-notify-event",
2222 G_CALLBACK(cmdref_visibility_notify), NULL);
2223 g_object_set_data(G_OBJECT(hwin->text), "sigs_connected",
2224 GINT_TO_POINTER(1));
2225 }
2226 }
2227
maybe_set_help_tabs(windata_t * hwin)2228 static void maybe_set_help_tabs (windata_t *hwin)
2229 {
2230 int done = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(hwin->text),
2231 "tabs_set"));
2232
2233 if (!done) {
2234 PangoTabArray *tabs;
2235
2236 tabs = pango_tab_array_new(1, TRUE);
2237 pango_tab_array_set_tab(tabs, 0, PANGO_TAB_LEFT, 50);
2238 gtk_text_view_set_tabs(GTK_TEXT_VIEW(hwin->text), tabs);
2239 pango_tab_array_free(tabs);
2240 g_object_set_data(G_OBJECT(hwin->text), "tabs_set", GINT_TO_POINTER(1));
2241 }
2242 }
2243
2244 /* Construct the index page for the gretl command reference.
2245 Note: we assume here that the maximum length of a gretl
2246 command word is 8 characters.
2247 */
2248
cmdref_index_page(windata_t * hwin,GtkTextBuffer * tbuf,int en)2249 static void cmdref_index_page (windata_t *hwin, GtkTextBuffer *tbuf, int en)
2250 {
2251 const char *header = N_("Gretl Command Reference");
2252 const gchar *s = (const gchar *) hwin->data;
2253 GtkTextIter iter;
2254 char word[10];
2255 int llen, llen_max = 6;
2256 int idx, j, n;
2257
2258 gtk_text_buffer_get_iter_at_offset(tbuf, &iter, 0);
2259 gtk_text_buffer_insert_with_tags_by_name(tbuf, &iter,
2260 (en)? header : _(header), -1,
2261 "title", NULL);
2262 gtk_text_buffer_insert(tbuf, &iter, "\n\n", -1);
2263
2264 llen = 0;
2265
2266 while (*s) {
2267 if (*s == '\n' && *(s+1) == '#' && *(s+2) != '\0') {
2268 if (sscanf(s + 2, "%8s", word)) {
2269 idx = gretl_command_number(word);
2270 insert_link(tbuf, &iter, word, idx, NULL);
2271 if (++llen == llen_max) {
2272 gtk_text_buffer_insert(tbuf, &iter, "\n", -1);
2273 llen = 0;
2274 } else {
2275 n = 10 - strlen(word);
2276 for (j=0; j<n; j++) {
2277 gtk_text_buffer_insert(tbuf, &iter, " ", -1);
2278 }
2279 }
2280 }
2281 }
2282 s++;
2283 }
2284
2285 gtk_text_view_set_buffer(GTK_TEXT_VIEW(hwin->text), tbuf);
2286
2287 maybe_connect_help_signals(hwin, en);
2288 maybe_set_help_tabs(hwin);
2289 }
2290
2291 /* construct the index page for the gretl function reference */
2292
funcref_index_page(windata_t * hwin,GtkTextBuffer * tbuf,int en)2293 static void funcref_index_page (windata_t *hwin, GtkTextBuffer *tbuf, int en)
2294 {
2295 const char *header = N_("Gretl Function Reference");
2296 const char *heads[] = {
2297 N_("Accessors"),
2298 N_("Built-in strings"),
2299 N_("Functions proper")
2300 };
2301 const gchar *s = (const gchar *) hwin->data;
2302 gchar *hstr;
2303 GtkTextIter iter;
2304 char word[12];
2305 int llen, llen_max = 5;
2306 int i, j, k, n;
2307
2308 gtk_text_buffer_get_iter_at_offset(tbuf, &iter, 0);
2309 gtk_text_buffer_insert_with_tags_by_name(tbuf, &iter,
2310 (en)? header : _(header), -1,
2311 "title", NULL);
2312 gtk_text_buffer_insert(tbuf, &iter, "\n\n", -1);
2313
2314 i = 1;
2315 k = 0;
2316 llen = 0;
2317
2318 while (*s) {
2319 if (*s == '\n' && s[1] == '#' && s[2] != '\0') {
2320 if (s[2] == '#') {
2321 /* insert section heading */
2322 if (i > 1) {
2323 gtk_text_buffer_insert(tbuf, &iter, "\n\n", -1);
2324 }
2325 hstr = g_strdup_printf("%s\n", en ? heads[k] : _(heads[k]));
2326 gtk_text_buffer_insert_with_tags_by_name(tbuf, &iter,
2327 hstr, -1,
2328 "grayhead", NULL);
2329 g_free(hstr);
2330 llen = 0;
2331 s += 2;
2332 k++;
2333 } else if (sscanf(s + 2, "%10s", word)) {
2334 /* got a function name */
2335 insert_link(tbuf, &iter, word, i, NULL);
2336 if (++llen == llen_max) {
2337 gtk_text_buffer_insert(tbuf, &iter, "\n", -1);
2338 llen = 0;
2339 } else {
2340 n = 12 - strlen(word);
2341 for (j=0; j<n; j++) {
2342 gtk_text_buffer_insert(tbuf, &iter, " ", -1);
2343 }
2344 }
2345 i++;
2346 }
2347 }
2348 s++;
2349 }
2350
2351 gtk_text_view_set_buffer(GTK_TEXT_VIEW(hwin->text), tbuf);
2352
2353 maybe_connect_help_signals(hwin, en);
2354 maybe_set_help_tabs(hwin);
2355 }
2356
2357 /* apparatus to support the 'Back' popup menu item */
2358
push_backpage(GtkWidget * w,int pg)2359 static void push_backpage (GtkWidget *w, int pg)
2360 {
2361 gpointer p = GINT_TO_POINTER(pg);
2362
2363 g_object_set_data(G_OBJECT(w), "backpage", p);
2364 }
2365
pop_backpage(GtkWidget * w)2366 static int pop_backpage (GtkWidget *w)
2367 {
2368 gpointer p = g_object_get_data(G_OBJECT(w), "backpage");
2369
2370 return GPOINTER_TO_INT(p);
2371 }
2372
help_popup_click(GtkWidget * w,gpointer p)2373 static gint help_popup_click (GtkWidget *w, gpointer p)
2374 {
2375 windata_t *hwin = (windata_t *) p;
2376 int action = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(w), "action"));
2377 int en = (hwin->role == CMD_HELP_EN || hwin->role == FUNC_HELP_EN);
2378 int page = 0;
2379
2380 if (action == 2) {
2381 page = pop_backpage(hwin->text);
2382 }
2383
2384 if (function_help(hwin->role)) {
2385 function_help_callback(page, en);
2386 } else {
2387 command_help_callback(page, en);
2388 }
2389
2390 return FALSE;
2391 }
2392
build_help_popup(windata_t * hwin)2393 static GtkWidget *build_help_popup (windata_t *hwin)
2394 {
2395 const char *items[] = {
2396 N_("Index"),
2397 N_("Back")
2398 };
2399 GtkWidget *pmenu = gtk_menu_new();
2400 GtkWidget *item;
2401 int i, imin = 0, imax = 2;
2402
2403 if (hwin->active_var == 0) {
2404 /* don't offer "Index" if we're in the index */
2405 imin = 1;
2406 }
2407
2408 if (pop_backpage(hwin->text) == 0) {
2409 /* don't offer "Back" if we haven't been anywhere */
2410 imax = 1;
2411 }
2412
2413 for (i=imin; i<imax; i++) {
2414 item = gtk_menu_item_new_with_label(_(items[i]));
2415 g_object_set_data(G_OBJECT(item), "action", GINT_TO_POINTER(i+1));
2416 g_signal_connect(G_OBJECT(item), "activate",
2417 G_CALLBACK(help_popup_click),
2418 hwin);
2419 gtk_widget_show(item);
2420 gtk_menu_shell_append(GTK_MENU_SHELL(pmenu), item);
2421 }
2422
2423 return pmenu;
2424 }
2425
2426 gboolean
help_popup_handler(GtkWidget * w,GdkEventButton * event,gpointer p)2427 help_popup_handler (GtkWidget *w, GdkEventButton *event, gpointer p)
2428 {
2429 if (right_click(event)) {
2430 windata_t *hwin = (windata_t *) p;
2431
2432 if (hwin->active_var == 0 && pop_backpage(w) == 0) {
2433 return TRUE;
2434 }
2435
2436 if (hwin->popup) {
2437 gtk_widget_destroy(hwin->popup);
2438 hwin->popup = NULL;
2439 }
2440
2441 hwin->popup = build_help_popup(hwin);
2442
2443 if (hwin->popup != NULL) {
2444 gtk_menu_popup(GTK_MENU(hwin->popup), NULL, NULL, NULL, NULL,
2445 event->button, event->time);
2446 g_signal_connect(G_OBJECT(hwin->popup), "destroy",
2447 G_CALLBACK(gtk_widget_destroyed),
2448 &hwin->popup);
2449 }
2450
2451 return TRUE;
2452 }
2453
2454 return FALSE;
2455 }
2456
reformat_para(char * buf,int maxlen)2457 static void reformat_para (char *buf, int maxlen)
2458 {
2459 char *p = buf;
2460 char *line;
2461 int i, n;
2462
2463 g_strchomp(g_strchug(buf));
2464
2465 /* normalize spaces, removing any existing line breaks */
2466
2467 while (*p) {
2468 if (*p == ' ' || *p == '\n' || *p == '\t') {
2469 *p = ' ';
2470 n = strspn(p + 1, " \t\n");
2471 if (n > 0) {
2472 g_strchug(p + 1);
2473 p += n;
2474 }
2475 }
2476 p++;
2477 }
2478
2479 line = p = buf;
2480 n = 0;
2481
2482 /* insert line breaks to give lines of length up
2483 to @maxlen */
2484
2485 while (*p++) {
2486 n++;
2487 if (n > maxlen) {
2488 /* back up to first available break-point */
2489 for (i=n-1; i>0; i--) {
2490 if (line[i] == ' ') {
2491 line[i] = '\n';
2492 p = line = &line[i+1];
2493 n = 0;
2494 }
2495 if (n == 0) {
2496 break;
2497 }
2498 }
2499 }
2500 }
2501 }
2502
prev_double_nl(GtkTextIter * pos,GtkTextIter * start)2503 static gboolean prev_double_nl (GtkTextIter *pos,
2504 GtkTextIter *start)
2505 {
2506 GtkTextIter cpos = *pos;
2507 int nlcount = 0;
2508 gunichar c;
2509 gboolean ret = 0;
2510
2511 if (gtk_text_iter_get_char(pos) == '\n') {
2512 nlcount = 1;
2513 }
2514
2515 while (gtk_text_iter_backward_char(&cpos)) {
2516 c = gtk_text_iter_get_char(&cpos);
2517 if (c == '\n') {
2518 if (++nlcount == 2) {
2519 *start = cpos;
2520 gtk_text_iter_forward_chars(start, 2);
2521 ret = 1;
2522 break;
2523 }
2524 } else if (!isspace(c)) {
2525 nlcount = 0;
2526 }
2527 }
2528
2529 return ret;
2530 }
2531
next_double_nl(GtkTextIter * pos,GtkTextIter * end)2532 static gboolean next_double_nl (GtkTextIter *pos,
2533 GtkTextIter *end)
2534 {
2535 GtkTextIter cpos = *pos;
2536 int nlcount = 0;
2537 gunichar c;
2538 gboolean ret = 0;
2539
2540 if (gtk_text_iter_get_char(pos) == '\n') {
2541 nlcount = 1;
2542 }
2543
2544 while (gtk_text_iter_forward_char(&cpos)) {
2545 c = gtk_text_iter_get_char(&cpos);
2546 if (c == '\n') {
2547 if (++nlcount == 2) {
2548 *end = cpos;
2549 gtk_text_iter_backward_char(end);
2550 ret = 1;
2551 break;
2552 }
2553 } else if (!isspace(c)) {
2554 nlcount = 0;
2555 }
2556 }
2557
2558 return ret;
2559 }
2560
not_in_para(GtkTextBuffer * buf,GtkTextIter * pos)2561 static int not_in_para (GtkTextBuffer *buf,
2562 GtkTextIter *pos)
2563 {
2564 GtkTextIter cpos = *pos;
2565 int got_text = 0;
2566 gunichar c;
2567
2568 /* We're "not in a paragraph" if the current
2569 cursor position is bracketed by newlines,
2570 with no non-space character intervening.
2571 */
2572
2573 c = gtk_text_iter_get_char(&cpos);
2574
2575 if (!isspace(c)) {
2576 got_text = 1;
2577 } else if (c == '\n') {
2578 /* crawl backwards to newline or text */
2579 while (gtk_text_iter_backward_char(&cpos)) {
2580 c = gtk_text_iter_get_char(&cpos);
2581 if (c == '\n') {
2582 break;
2583 } else if (!isspace(c)) {
2584 got_text = 1;
2585 break;
2586 }
2587 }
2588 } else if (isspace(c)) {
2589 /* crawl forward to newline or text */
2590 while (gtk_text_iter_forward_char(&cpos)) {
2591 c = gtk_text_iter_get_char(&cpos);
2592 if (c == '\n') {
2593 break;
2594 } else if (!isspace(c)) {
2595 got_text = 1;
2596 break;
2597 }
2598 }
2599 if (!got_text) {
2600 /* OK, try backwards */
2601 cpos = *pos;
2602 while (gtk_text_iter_backward_char(&cpos)) {
2603 c = gtk_text_iter_get_char(&cpos);
2604 if (c == '\n') {
2605 break;
2606 } else if (!isspace(c)) {
2607 got_text = 1;
2608 break;
2609 }
2610 }
2611 }
2612 }
2613
2614 return !got_text;
2615 }
2616
textbuf_get_para_limits(GtkTextBuffer * buf,GtkTextIter * pos,GtkTextIter * start,GtkTextIter * end)2617 static gboolean textbuf_get_para_limits (GtkTextBuffer *buf,
2618 GtkTextIter *pos,
2619 GtkTextIter *start,
2620 GtkTextIter *end)
2621 {
2622 if (not_in_para(buf, pos)) {
2623 return FALSE;
2624 }
2625
2626 if (!prev_double_nl(pos, start)) {
2627 gtk_text_buffer_get_start_iter(buf, start);
2628 }
2629
2630 if (!next_double_nl(pos, end)) {
2631 gtk_text_buffer_get_end_iter(buf, end);
2632 }
2633
2634 return TRUE;
2635 }
2636
textview_format_paragraph(GtkWidget * view)2637 void textview_format_paragraph (GtkWidget *view)
2638 {
2639 GtkTextBuffer *buf;
2640 GtkTextIter pos, start, end;
2641 gchar *para = NULL;
2642
2643 buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(view));
2644
2645 /* find where the cursor is */
2646 gtk_text_buffer_get_iter_at_mark(buf, &pos,
2647 gtk_text_buffer_get_insert(buf));
2648
2649 /* find start and end of paragraph, if we're in one */
2650 if (!textbuf_get_para_limits(buf, &pos, &start, &end)) {
2651 return;
2652 }
2653
2654 /* grab the para text */
2655 para = gtk_text_buffer_get_text(buf, &start, &end, FALSE);
2656
2657 if (para != NULL && !string_is_blank(para)) {
2658 reformat_para(para, 72);
2659 gtk_text_buffer_begin_user_action(buf);
2660 gtk_text_buffer_delete(buf, &start, &end);
2661 gtk_text_buffer_insert(buf, &start, para, -1);
2662 gtk_text_buffer_end_user_action(buf);
2663 g_free(para);
2664
2665 }
2666 }
2667
textview_get_current_line(GtkWidget * view,int allow_blank)2668 static gchar *textview_get_current_line (GtkWidget *view, int allow_blank)
2669 {
2670 GtkTextBuffer *buf;
2671 GtkTextIter start, end;
2672 gchar *ret = NULL;
2673
2674 buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(view));
2675 gtk_text_buffer_get_iter_at_mark(buf, &start,
2676 gtk_text_buffer_get_insert(buf));
2677 gtk_text_iter_set_line_offset(&start, 0);
2678 gtk_text_buffer_get_iter_at_mark(buf, &end,
2679 gtk_text_buffer_get_insert(buf));
2680 if (!gtk_text_iter_ends_line(&end)) {
2681 /* N.B. don't skip on to the end of the _next_ line */
2682 gtk_text_iter_forward_to_line_end(&end);
2683 }
2684
2685 ret = gtk_text_buffer_get_text(buf, &start, &end, FALSE);
2686
2687 if (!allow_blank && string_is_blank(ret)) {
2688 g_free(ret);
2689 ret = NULL;
2690 }
2691
2692 return ret;
2693 }
2694
textview_get_current_line_with_newline(GtkWidget * view)2695 static gchar *textview_get_current_line_with_newline (GtkWidget *view)
2696 {
2697 gchar *s = textview_get_current_line(view, 0);
2698
2699 if (s != NULL && *s != '\0' && s[strlen(s)-1] != '\n') {
2700 gchar *tmp = g_strdup_printf("%s\n", s);
2701
2702 g_free(s);
2703 s = tmp;
2704 }
2705
2706 return s;
2707 }
2708
2709 /* Determine whether or not any of the lines in a chunk of text
2710 are indented, via spaces or tabs.
2711 */
2712
text_is_indented(const gchar * s)2713 static int text_is_indented (const gchar *s)
2714 {
2715 int leading = 1;
2716
2717 if (s == NULL) {
2718 return 0;
2719 }
2720
2721 while (*s) {
2722 if (*s == '\n') {
2723 leading = 1;
2724 } else if (*s != ' ' && *s != '\t') {
2725 leading = 0;
2726 }
2727 if (leading && (*s == ' ' || *s == '\t')) {
2728 return 1;
2729 }
2730 s++;
2731 }
2732
2733 return 0;
2734 }
2735
2736 /* Determine whether or not a chunk of text is commented, in the form
2737 of each line beginning with '#' (with possible leading white
2738 space). If some lines are commented and others are not, return -1,
2739 which blocks the comment/uncomment menu items.
2740 */
2741
text_is_commented(const gchar * s)2742 static int text_is_commented (const gchar *s)
2743 {
2744 int gotc = 0, comm = 0;
2745 int lines = 1;
2746
2747 if (s == NULL) {
2748 return -1;
2749 }
2750
2751 while (*s) {
2752 if (!gotc) {
2753 if (*s == '#') {
2754 comm++;
2755 gotc = 1;
2756 } else if (!isspace(*s)) {
2757 gotc = 1;
2758 }
2759 } else if (*s == '\n') {
2760 gotc = 0;
2761 if (*(s+1)) {
2762 lines++;
2763 }
2764 }
2765 s++;
2766 }
2767
2768 if (comm > 0 && comm < lines) {
2769 /* mixed */
2770 comm = -1;
2771 }
2772
2773 return comm;
2774 }
2775
2776 struct textbit {
2777 windata_t *vwin;
2778 GtkTextBuffer *buf;
2779 GtkTextIter start;
2780 GtkTextIter end;
2781 gchar *chunk;
2782 int commented;
2783 int selected;
2784 };
2785
2786 /* either insert or remove '#' comment markers at the start of the
2787 line(s) of a chunk of text
2788 */
2789
comment_or_uncomment_text(GtkWidget * w,gpointer p)2790 static void comment_or_uncomment_text (GtkWidget *w, gpointer p)
2791 {
2792 struct textbit *tb = (struct textbit *) p;
2793 gchar *s;
2794
2795 gtk_text_buffer_delete(tb->buf, &tb->start, &tb->end);
2796
2797 if (tb->selected) {
2798 char line[1024];
2799
2800 bufgets_init(tb->chunk);
2801 while (bufgets(line, sizeof line, tb->chunk)) {
2802 if (tb->commented) {
2803 s = strchr(line, '#');
2804 if (s != NULL) {
2805 s++;
2806 if (*s == ' ') s++;
2807 gtk_text_buffer_insert(tb->buf, &tb->start, s, -1);
2808 }
2809 } else {
2810 gtk_text_buffer_insert(tb->buf, &tb->start, "# ", -1);
2811 gtk_text_buffer_insert(tb->buf, &tb->start, line, -1);
2812 }
2813 }
2814 bufgets_finalize(tb->chunk);
2815 } else {
2816 if (tb->commented) {
2817 s = strchr(tb->chunk, '#');
2818 if (s != NULL) {
2819 s++;
2820 if (*s == ' ') s++;
2821 gtk_text_buffer_insert(tb->buf, &tb->start, s, -1);
2822 }
2823 } else {
2824 gtk_text_buffer_insert(tb->buf, &tb->start, "# ", -1);
2825 gtk_text_buffer_insert(tb->buf, &tb->start, tb->chunk, -1);
2826 }
2827 }
2828 }
2829
2830 enum {
2831 TAB_NEXT,
2832 TAB_PREV
2833 };
2834
spaces_to_tab_stop(const char * s,int targ)2835 static int spaces_to_tab_stop (const char *s, int targ)
2836 {
2837 int ret, n = 0;
2838
2839 while (*s) {
2840 if (*s == ' ') {
2841 n++;
2842 } else if (*s == '\t') {
2843 n += tabwidth;
2844 } else {
2845 break;
2846 }
2847 s++;
2848 }
2849
2850 if (targ == TAB_NEXT) {
2851 ret = tabwidth - (n % tabwidth);
2852 } else {
2853 if (n % tabwidth == 0) {
2854 ret = n - tabwidth;
2855 if (ret < 0) ret = 0;
2856 } else {
2857 ret = (n / tabwidth) * tabwidth;
2858 }
2859 }
2860
2861 return ret;
2862 }
2863
textbuf_get_cmdword(const char * s,char * word)2864 static void textbuf_get_cmdword (const char *s, char *word)
2865 {
2866 if (!strncmp(s, "catch ", 6)) {
2867 s += 6;
2868 }
2869
2870 if (sscanf(s, "%*s <- %8s", word) != 1) {
2871 sscanf(s, "%8s", word);
2872 }
2873 }
2874
2875 #define bare_quote(p,s) (*p == '"' && (p-s==0 || *(p-1) != '\\'))
2876 #define starts_comment(p) (*p == '/' && *(p+1) == '*')
2877 #define ends_comment(p) (*p == '*' && *(p+1) == '/')
2878
check_for_comment(const char * s,int * incomm)2879 static void check_for_comment (const char *s, int *incomm)
2880 {
2881 int commbak = *incomm;
2882 const char *p = s;
2883 int quoted = 0;
2884
2885 while (*p) {
2886 if (!quoted && !*incomm && *p == '#') {
2887 break;
2888 }
2889 if (!*incomm && bare_quote(p, s)) {
2890 quoted = !quoted;
2891 }
2892 if (!quoted) {
2893 if (starts_comment(p)) {
2894 *incomm = 1;
2895 p += 2;
2896 } else if (ends_comment(p)) {
2897 *incomm = 0;
2898 p += 2;
2899 p += strspn(p, " ");
2900 }
2901 }
2902 if (*p) {
2903 p++;
2904 }
2905 }
2906
2907 if (*incomm && commbak) {
2908 /* on the second or subsequent line of a multiline
2909 comment */
2910 *incomm = 2;
2911 }
2912 }
2913
2914 /* determine whether a given line is subject to
2915 continuation (i.e. ends with backslash, comma
2916 or semicolon, other than in a comment)
2917 */
2918
line_broken(const char * s)2919 static int line_broken (const char *s)
2920 {
2921 int ret = 0;
2922
2923 if (*s != '\0') {
2924 int i, n = strlen(s);
2925
2926 for (i=n-1; i>=0; i--) {
2927 if (s[i] == '\\' || s[i] == ',') {
2928 ret = 1;
2929 } else if (!ret && !isspace(s[i])) {
2930 break;
2931 } else if (ret && s[i] == '#') {
2932 ret = 0;
2933 break;
2934 }
2935 }
2936 }
2937
2938 return ret;
2939 }
2940
strip_trailing_whitespace(char * s)2941 static void strip_trailing_whitespace (char *s)
2942 {
2943 int i, n = strlen(s);
2944
2945 for (i=n-1; i>=0; i--) {
2946 if (s[i] == '\n' || s[i] == ' ' || s[i] == '\t') {
2947 s[i] = '\0';
2948 } else {
2949 break;
2950 }
2951 }
2952
2953 strcat(s, "\n");
2954 }
2955
2956 /* determine position of unmatched left parenthesis,
2957 when applicable, if @s starts a function definition
2958 */
2959
left_paren_offset(const char * s)2960 static int left_paren_offset (const char *s)
2961 {
2962 const char *p = strchr(s, '(');
2963
2964 if (p != NULL && strchr(p, ')') == NULL) {
2965 return p - s;
2966 } else {
2967 return 0;
2968 }
2969 }
2970
normalize_indent(GtkTextBuffer * tbuf,const gchar * buf,GtkTextIter * start,GtkTextIter * end)2971 static void normalize_indent (GtkTextBuffer *tbuf,
2972 const gchar *buf,
2973 GtkTextIter *start,
2974 GtkTextIter *end)
2975 {
2976 int this_indent = 0;
2977 int next_indent = 0;
2978 char word[9], line[1024];
2979 char lastline[1024];
2980 const char *ins;
2981 int incomment = 0;
2982 int inforeign = 0;
2983 int lp_pos = 0;
2984 int lp_zero = 0;
2985 int i, nsp;
2986
2987 if (buf == NULL) {
2988 return;
2989 }
2990
2991 gtk_text_buffer_delete(tbuf, start, end);
2992
2993 lastline[0] = '\0';
2994 bufgets_init(buf);
2995
2996 while (bufgets(line, sizeof line, buf)) {
2997 int handled = 0;
2998
2999 strip_trailing_whitespace(line);
3000
3001 if (string_is_blank(line)) {
3002 gtk_text_buffer_insert(tbuf, start, line, -1);
3003 continue;
3004 }
3005 check_for_comment(line, &incomment);
3006 #if 0
3007 if (incomment) {
3008 /* in multiline comment */
3009 gtk_text_buffer_insert(tbuf, start, line, -1);
3010 continue;
3011 }
3012 #endif
3013 ins = line + strspn(line, " \t");
3014 if (!incomment) {
3015 *word = '\0';
3016 textbuf_get_cmdword(ins, word);
3017 if (!strcmp(word, "foreign")) {
3018 inforeign = 1;
3019 } else if (inforeign) {
3020 if (!strncmp(ins, "end foreign", 11)) {
3021 inforeign = 0;
3022 } else {
3023 gtk_text_buffer_insert(tbuf, start, line, -1);
3024 handled = 1;
3025 }
3026 } else {
3027 if (!strcmp(word, "function")) {
3028 lp_pos = left_paren_offset(ins);
3029 } else if (lp_pos > 0 && strchr(ins, ')') != NULL) {
3030 lp_zero = 1;
3031 }
3032 if (!strcmp(word, "outfile")) {
3033 /* handle legacy syntax */
3034 adjust_indent(ins, &this_indent, &next_indent);
3035 } else {
3036 adjust_indent(word, &this_indent, &next_indent);
3037 }
3038 }
3039 }
3040 if (!handled) {
3041 nsp = this_indent * tabwidth;
3042 if (incomment == 2) {
3043 nsp += 3;
3044 } else if (line_broken(lastline)) {
3045 if (lp_pos > 0) {
3046 nsp = lp_pos + 1;
3047 } else {
3048 nsp += 2;
3049 }
3050 }
3051 for (i=0; i<nsp; i++) {
3052 gtk_text_buffer_insert(tbuf, start, " ", -1);
3053 }
3054 gtk_text_buffer_insert(tbuf, start, ins, -1);
3055 }
3056 strcpy(lastline, line);
3057 if (lp_zero) {
3058 lp_zero = lp_pos = 0;
3059 }
3060 }
3061
3062 bufgets_finalize(buf);
3063 }
3064
in_foreign_land(GtkWidget * text_widget)3065 static int in_foreign_land (GtkWidget *text_widget)
3066 {
3067 GtkTextBuffer *tbuf;
3068 GtkTextIter start, end;
3069 gchar *buf;
3070 char *s, line[1024];
3071 int inforeign = 0;
3072
3073 tbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_widget));
3074 gtk_text_buffer_get_start_iter(tbuf, &start);
3075 gtk_text_buffer_get_iter_at_mark(tbuf, &end,
3076 gtk_text_buffer_get_insert(tbuf));
3077 buf = gtk_text_buffer_get_text(tbuf, &start, &end, FALSE);
3078
3079 bufgets_init(buf);
3080
3081 while (bufgets(line, sizeof line, buf)) {
3082 s = line + strspn(line, " \t");
3083 if (!strncmp(s, "foreign ", 8)) {
3084 inforeign = 1;
3085 } else if (!strncmp(s, "end foreign", 11)) {
3086 inforeign = 0;
3087 }
3088 }
3089
3090 bufgets_finalize(buf);
3091 g_free(buf);
3092
3093 return inforeign;
3094 }
3095
auto_indent_script(GtkWidget * w,windata_t * vwin)3096 static void auto_indent_script (GtkWidget *w, windata_t *vwin)
3097 {
3098 GtkAdjustment *adj;
3099 GtkTextBuffer *tbuf;
3100 GtkTextMark *mark;
3101 GtkTextIter here, start, end;
3102 gchar *buf;
3103 gint line, offset;
3104 gdouble pos;
3105
3106 tbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(vwin->text));
3107
3108 /* record scrolling position */
3109 adj = gtk_text_view_get_vadjustment(GTK_TEXT_VIEW(vwin->text));
3110 pos = gtk_adjustment_get_value(adj);
3111
3112 /* record cursor position (line, offset) */
3113 mark = gtk_text_buffer_get_insert(tbuf);
3114 gtk_text_buffer_get_iter_at_mark(tbuf, &here, mark);
3115 line = gtk_text_iter_get_line(&here);
3116 offset = gtk_text_iter_get_line_offset(&here);
3117
3118 /* grab and revise the text */
3119 gtk_text_buffer_get_start_iter(tbuf, &start);
3120 gtk_text_buffer_get_end_iter(tbuf, &end);
3121 buf = gtk_text_buffer_get_text(tbuf, &start, &end, FALSE);
3122 normalize_indent(tbuf, buf, &start, &end);
3123 g_free(buf);
3124
3125 /* restore cursor position */
3126 gtk_text_buffer_get_iter_at_line(tbuf, &here, line);
3127 gtk_text_iter_set_line_offset(&here, offset);
3128 gtk_text_buffer_place_cursor(tbuf, &here);
3129
3130 /* restore scrolling position */
3131 gtk_adjustment_set_value(adj, pos);
3132 gtk_adjustment_value_changed(adj);
3133 }
3134
indent_region(GtkWidget * w,gpointer p)3135 static void indent_region (GtkWidget *w, gpointer p)
3136 {
3137 struct textbit *tb = (struct textbit *) p;
3138
3139 if (smarttab) {
3140 normalize_indent(tb->buf, tb->chunk, &tb->start, &tb->end);
3141 } else {
3142 char line[1024];
3143 int i, n;
3144
3145 gtk_text_buffer_delete(tb->buf, &tb->start, &tb->end);
3146
3147 bufgets_init(tb->chunk);
3148
3149 while (bufgets(line, sizeof line, tb->chunk)) {
3150 n = spaces_to_tab_stop(line, TAB_NEXT);
3151 for (i=0; i<n; i++) {
3152 gtk_text_buffer_insert(tb->buf, &tb->start, " ", -1);
3153 }
3154 gtk_text_buffer_insert(tb->buf, &tb->start, line, -1);
3155 }
3156
3157 bufgets_finalize(tb->chunk);
3158 }
3159 }
3160
indent_hansl(GtkWidget * w,windata_t * vwin)3161 void indent_hansl (GtkWidget *w, windata_t *vwin)
3162 {
3163 auto_indent_script(w, vwin);
3164 }
3165
unindent_region(GtkWidget * w,gpointer p)3166 static void unindent_region (GtkWidget *w, gpointer p)
3167 {
3168 struct textbit *tb = (struct textbit *) p;
3169 char line[1024];
3170 char *ins;
3171 int i, n;
3172
3173 gtk_text_buffer_delete(tb->buf, &tb->start, &tb->end);
3174
3175 bufgets_init(tb->chunk);
3176
3177 while (bufgets(line, sizeof line, tb->chunk)) {
3178 n = spaces_to_tab_stop(line, TAB_PREV);
3179 ins = line + strspn(line, " \t");
3180 for (i=0; i<n; i++) {
3181 gtk_text_buffer_insert(tb->buf, &tb->start, " ", -1);
3182 }
3183 gtk_text_buffer_insert(tb->buf, &tb->start, ins, -1);
3184 }
3185
3186 bufgets_finalize(tb->chunk);
3187 }
3188
exec_script_text(GtkWidget * w,gpointer p)3189 static void exec_script_text (GtkWidget *w, gpointer p)
3190 {
3191 struct textbit *tb = (struct textbit *) p;
3192
3193 run_script_fragment(tb->vwin, tb->chunk);
3194 tb->chunk = NULL; /* will be freed already */
3195 }
3196
3197 enum {
3198 AUTO_SELECT_NONE,
3199 AUTO_SELECT_LINE
3200 };
3201
vwin_get_textbit(windata_t * vwin,int mode)3202 static struct textbit *vwin_get_textbit (windata_t *vwin, int mode)
3203 {
3204 GtkTextBuffer *tbuf;
3205 GtkTextIter start, end;
3206 int selected = 0;
3207 struct textbit *tb;
3208
3209 tbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(vwin->text));
3210 if (gtk_text_buffer_get_selection_bounds(tbuf, &start, &end)) {
3211 selected = 1;
3212 }
3213
3214 if (!selected && mode != AUTO_SELECT_LINE) {
3215 return NULL;
3216 }
3217
3218 tb = malloc(sizeof *tb);
3219 if (tb == NULL) {
3220 return NULL;
3221 }
3222
3223 tb->vwin = vwin;
3224 tb->buf = tbuf;
3225 tb->start = start;
3226 tb->end = end;
3227 tb->selected = selected;
3228 tb->commented = 0;
3229 tb->chunk = NULL;
3230
3231 if (selected) {
3232 int endpos;
3233
3234 gtk_text_iter_set_line_offset(&tb->start, 0);
3235 endpos = gtk_text_iter_get_line_offset(&tb->end);
3236 if (endpos > 0 && !gtk_text_iter_ends_line(&tb->end)) {
3237 gtk_text_iter_forward_to_line_end(&tb->end);
3238 }
3239 tb->chunk = gtk_text_buffer_get_text(tb->buf, &tb->start, &tb->end, FALSE);
3240 } else {
3241 gtk_text_buffer_get_iter_at_mark(tb->buf, &tb->start,
3242 gtk_text_buffer_get_insert(tb->buf));
3243 gtk_text_iter_set_line_offset(&tb->start, 0);
3244 gtk_text_buffer_get_iter_at_mark(tb->buf, &tb->end,
3245 gtk_text_buffer_get_insert(tb->buf));
3246 gtk_text_iter_forward_to_line_end(&tb->end);
3247 tb->chunk = gtk_text_buffer_get_text(tb->buf, &tb->start, &tb->end, FALSE);
3248 }
3249
3250 return tb;
3251 }
3252
count_leading_spaces(const char * s)3253 static int count_leading_spaces (const char *s)
3254 {
3255 int n = 0;
3256
3257 while (*s) {
3258 if (*s == ' ') {
3259 n++;
3260 } else if (*s == '\t') {
3261 n += tabwidth;
3262 } else {
3263 break;
3264 }
3265 s++;
3266 }
3267
3268 return n;
3269 }
3270
3271 /* Given what's presumed to be a start-of-line iter, find how many
3272 leading spaces are on the line, counting tabs as multiple spaces.
3273 */
3274
leading_spaces_at_iter(GtkTextBuffer * tbuf,GtkTextIter * start,const char * word)3275 static int leading_spaces_at_iter (GtkTextBuffer *tbuf,
3276 GtkTextIter *start,
3277 const char *word)
3278 {
3279 GtkTextIter end = *start;
3280 gchar *s;
3281 int n = 0;
3282
3283 gtk_text_iter_forward_to_line_end(&end);
3284 s = gtk_text_buffer_get_text(tbuf, start, &end, FALSE);
3285 if (s != NULL) {
3286 if (!strcmp(word, "function")) {
3287 n = left_paren_offset(s);
3288 if (n > 0) {
3289 n--;
3290 } else {
3291 n = count_leading_spaces(s);
3292 }
3293 } else {
3294 n = count_leading_spaces(s);
3295 }
3296 g_free(s);
3297 }
3298
3299 return n;
3300 }
3301
incremental_leading_spaces(const char * prevword,const char * thisword)3302 static int incremental_leading_spaces (const char *prevword,
3303 const char *thisword)
3304 {
3305 int this_indent = 0;
3306 int next_indent = 0;
3307
3308 if (*prevword != '\0') {
3309 int prev_indent = 0;
3310
3311 adjust_indent(prevword, &this_indent, &next_indent);
3312 #if TABDEBUG > 1
3313 fprintf(stderr, "adjust_indent 1: prevword='%s', this=%d, next=%d\n",
3314 prevword, this_indent, next_indent);
3315 #endif
3316 prev_indent = this_indent;
3317 if (*thisword != '\0') {
3318 adjust_indent(thisword, &this_indent, &next_indent);
3319 #if TABDEBUG > 1
3320 fprintf(stderr, "adjust_indent 2: thisword='%s', this=%d\n",
3321 thisword, this_indent);
3322 #endif
3323 this_indent -= prev_indent;
3324 #if TABDEBUG > 1
3325 fprintf(stderr, "adjust_indent 3: this=%d\n", this_indent);
3326 #endif
3327 } else {
3328 this_indent = next_indent - this_indent;
3329 }
3330 }
3331
3332 #if TABDEBUG > 1
3333 fprintf(stderr, "incremental_leading_spaces: returning %d*%d = %d\n",
3334 this_indent, tabwidth, this_indent * tabwidth);
3335 #endif
3336
3337 return this_indent * tabwidth;
3338 }
3339
line_continues(const gchar * s)3340 static int line_continues (const gchar *s)
3341 {
3342 int i, n = strlen(s);
3343
3344 for (i=n-1; i>=0; i--) {
3345 if (s[i] != ' ' && s[i] != '\t') {
3346 return (s[i] == '\\' || s[i] == ',');
3347 }
3348 }
3349
3350 return 0;
3351 }
3352
get_word_and_cont(const char * s,char * word,int * contd)3353 static int get_word_and_cont (const char *s, char *word, int *contd)
3354 {
3355 /* don't move onto next line */
3356 if (*s != '\n' && *s != '\r') {
3357 s += strspn(s, " \t");
3358 if (sscanf(s, "%*s <- %8s", word) != 1) {
3359 sscanf(s, "%8s", word);
3360 }
3361 if (*word != '#' && contd != NULL) {
3362 *contd = line_continues(s);
3363 }
3364 }
3365
3366 return *word != '\0';
3367 }
3368
line_continues_previous(GtkTextBuffer * tbuf,GtkTextIter iter)3369 static int line_continues_previous (GtkTextBuffer *tbuf,
3370 GtkTextIter iter)
3371 {
3372 GtkTextIter end, prev = iter;
3373 gchar *s;
3374 int ret = 0;
3375
3376 if (gtk_text_iter_backward_line(&prev)) {
3377 end = prev;
3378 if (gtk_text_iter_forward_to_line_end(&end)) {
3379 s = gtk_text_buffer_get_text(tbuf, &prev, &end, FALSE);
3380 if (s != NULL) {
3381 if (*s != '\n' && *s != '\r') {
3382 ret = line_continues(s);
3383 }
3384 g_free(s);
3385 }
3386 }
3387 }
3388
3389 return ret;
3390 }
3391
3392 /* get "command word", max 8 characters: work backwards up script
3393 to find this */
3394
get_previous_line_start_word(char * word,GtkTextBuffer * tbuf,GtkTextIter iter,int * leadspace,int * contd)3395 static char *get_previous_line_start_word (char *word,
3396 GtkTextBuffer *tbuf,
3397 GtkTextIter iter,
3398 int *leadspace,
3399 int *contd)
3400 {
3401 GtkTextIter end, prev = iter;
3402 int *pcont = contd;
3403 int rparen = 0;
3404 int i = 0;
3405 gchar *s;
3406
3407 *word = '\0';
3408
3409 while (*word == '\0' && gtk_text_iter_backward_line(&prev)) {
3410 end = prev;
3411 if (gtk_text_iter_forward_to_line_end(&end)) {
3412 s = gtk_text_buffer_get_text(tbuf, &prev, &end, FALSE);
3413 if (s != NULL) {
3414 if (i == 0 && s[strlen(s)-1] == ')') {
3415 rparen = 1;
3416 }
3417 if (get_word_and_cont(s, word, pcont)) {
3418 pcont = NULL;
3419 }
3420 g_free(s);
3421 }
3422 }
3423
3424 if (line_continues_previous(tbuf, prev)) {
3425 /* back up one line further */
3426 *word = '\0';
3427 }
3428
3429 if (*word != '\0' && leadspace != NULL) {
3430 *leadspace = leading_spaces_at_iter(tbuf, &prev, word);
3431 }
3432 i++;
3433 }
3434
3435 if (rparen && !strcmp(word, "function")) {
3436 /* Revise our judgement: looks like we must be on the
3437 first line following a function signature, so we do
3438 not want the deep indent suitable for a continuation
3439 signature line.
3440 */
3441 *leadspace = 0;
3442 }
3443
3444 return word;
3445 }
3446
3447 /* Is the insertion point at the start of a line, or in a white-space
3448 field to the left of any non-space characters? If so, we'll trying
3449 inserting a "smart" soft tab in response to the Tab key. If not,
3450 and @comp_ok is non-NULL, we'll check whether gtksourceview
3451 completion seems possible at the current insertion point.
3452 */
3453
maybe_insert_smart_tab(windata_t * vwin,int * comp_ok)3454 static int maybe_insert_smart_tab (windata_t *vwin,
3455 int *comp_ok)
3456 {
3457 GtkTextBuffer *tbuf;
3458 GtkTextMark *mark;
3459 GtkTextIter start, end;
3460 int curr_nsp = 0;
3461 int pos, ret = 0;
3462
3463 tbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(vwin->text));
3464
3465 if (gtk_text_buffer_get_selection_bounds(tbuf, &start, &end)) {
3466 /* don't do this if there's a selection in place */
3467 return 0;
3468 }
3469
3470 /* find @pos, the line-index of the insertion point */
3471 mark = gtk_text_buffer_get_insert(tbuf);
3472 gtk_text_buffer_get_iter_at_mark(tbuf, &end, mark);
3473 pos = gtk_text_iter_get_line_offset(&end);
3474
3475 if (pos == 0) {
3476 /* we're at the left margin, OK */
3477 ret = 1;
3478 } else {
3479 gchar *chunk;
3480
3481 start = end;
3482 gtk_text_iter_set_line_offset(&start, 0);
3483 /* grab the text between line start and insertion point */
3484 chunk = gtk_text_buffer_get_text(tbuf, &start, &end, FALSE);
3485 if (strspn(chunk, " \t") == pos) {
3486 /* set @ret if this chunk is just white space */
3487 ret = 1;
3488 } else if (comp_ok != NULL && pos > 1) {
3489 /* follow-up: is the context OK for completion? */
3490 *comp_ok = !isspace(chunk[pos-1]) && !isspace(chunk[pos-2]);
3491 }
3492 g_free(chunk);
3493 }
3494
3495 if (ret) {
3496 /* OK, let's insert a smart tab */
3497 GtkTextIter prev = start;
3498 char *s, thisword[9] = {0};
3499 char prevword[9];
3500 int contd = 0;
3501 int i, nsp = 0;
3502
3503 s = textview_get_current_line(vwin->text, 1);
3504 if (s != NULL) {
3505 #if TABDEBUG > 1
3506 fprintf(stderr, "*** maybe_insert_smart_tab: "
3507 "current line = '%s'\n", s);
3508 #endif
3509 sscanf(s, "%8s", thisword);
3510 curr_nsp = strspn(s, " \t");
3511 g_free(s);
3512 }
3513
3514 get_previous_line_start_word(prevword, tbuf, prev, &nsp, &contd);
3515
3516 if (contd) {
3517 nsp += 2;
3518 } else {
3519 nsp += incremental_leading_spaces(prevword, thisword);
3520 #if TABDEBUG > 1
3521 fprintf(stderr, " leading spaces: nsp + incr = %d, curr_nsp %d\n",
3522 nsp, curr_nsp);
3523 #endif
3524 }
3525
3526 if (curr_nsp > 0) {
3527 end = start;
3528 gtk_text_iter_set_line_offset(&end, curr_nsp);
3529 gtk_text_buffer_delete(tbuf, &start, &end);
3530 }
3531 gtk_text_iter_set_line_offset(&start, 0);
3532 for (i=0; i<nsp; i++) {
3533 gtk_text_buffer_insert(tbuf, &start, " ", -1);
3534 }
3535 }
3536
3537 return ret;
3538 }
3539
3540 #ifdef HAVE_GTKSV_COMPLETION
3541
3542 /* Is the insertion point directly preceded by at least two
3543 non-space characters? If so we have a possible candidate for
3544 completion via gtksourceview. We come here only if "smart
3545 tab" is not in force; otherwise this check is rolled into
3546 the check for inserting a smart tab.
3547
3548 This function is public because it's called from console.c
3549 as well as internally.
3550 */
3551
maybe_try_completion(windata_t * vwin)3552 int maybe_try_completion (windata_t *vwin)
3553 {
3554 GtkTextBuffer *tbuf;
3555 GtkTextMark *mark;
3556 GtkTextIter start, end;
3557 int pos, ret = 0;
3558
3559 tbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(vwin->text));
3560
3561 if (gtk_text_buffer_get_selection_bounds(tbuf, &start, &end)) {
3562 /* don't do this if there's a selection in place? */
3563 return 0;
3564 }
3565
3566 /* find @pos, the line-index of the insertion point */
3567 mark = gtk_text_buffer_get_insert(tbuf);
3568 gtk_text_buffer_get_iter_at_mark(tbuf, &end, mark);
3569 pos = gtk_text_iter_get_line_offset(&end);
3570
3571 if (pos > 1) {
3572 const char *test;
3573 gchar *chunk;
3574
3575 start = end;
3576 gtk_text_iter_set_line_offset(&start, 0);
3577 test = chunk = gtk_text_buffer_get_text(tbuf, &start, &end, FALSE);
3578 if (vwin->role == CONSOLE && !strncmp(chunk, "? ", 2)) {
3579 test += 2;
3580 pos -= 2;
3581 }
3582 ret = !isspace(test[pos-1]) && !isspace(test[pos-2]);
3583 g_free(chunk);
3584 }
3585
3586 return ret;
3587 }
3588
3589 #endif /* HAVE_GTKSV_COMPLETION */
3590
leftchar(guint k)3591 static char leftchar (guint k)
3592 {
3593 return k == GDK_parenleft ? '(' :
3594 k == GDK_bracketleft ? '[' : '{';
3595 }
3596
rightchar(guint k)3597 static char rightchar (guint k)
3598 {
3599 return k == GDK_parenleft ? ')' :
3600 k == GDK_bracketleft ? ']' : '}';
3601 }
3602
3603 /* Is the insertion point at the end of a line? If so, we'll
3604 auto-insert a matching right bracket and move the cursor
3605 back before it.
3606 */
3607
maybe_insert_auto_bracket(windata_t * vwin,guint keyval)3608 static int maybe_insert_auto_bracket (windata_t *vwin,
3609 guint keyval)
3610 {
3611 GtkTextBuffer *tbuf;
3612 GtkTextMark *mark;
3613 GtkTextIter start;
3614 gchar *chunk = NULL;
3615 int ret = 0;
3616
3617 tbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(vwin->text));
3618
3619 if (gtk_text_buffer_get_has_selection(tbuf)) {
3620 return 0;
3621 }
3622
3623 mark = gtk_text_buffer_get_insert(tbuf);
3624 gtk_text_buffer_get_iter_at_mark(tbuf, &start, mark);
3625
3626 if (gtk_text_iter_ends_line(&start)) {
3627 ret = 1;
3628 } else {
3629 GtkTextIter end = start;
3630
3631 gtk_text_iter_forward_to_line_end(&end);
3632 chunk = gtk_text_buffer_get_text(tbuf, &start, &end, FALSE);
3633 if (chunk != NULL) {
3634 ret = strspn(chunk, " \t\n") == strlen(chunk);
3635 g_free(chunk);
3636 }
3637 }
3638
3639 if (ret) {
3640 char s[2] = {0};
3641
3642 s[0] = leftchar(keyval);
3643 gtk_text_buffer_insert(tbuf, &start, s, -1);
3644 s[0] = rightchar(keyval);
3645 gtk_text_buffer_insert(tbuf, &start, s, -1);
3646 gtk_text_iter_backward_char(&start);
3647 gtk_text_buffer_place_cursor(tbuf, &start);
3648 }
3649
3650 return ret;
3651 }
3652
3653 /* On "Enter" in script editing, try to compute the correct indent
3654 level for the current line, and make an adjustment if it's not
3655 already right. We also attempt to place the cursor at the
3656 appropriate indent on the next, new line.
3657 */
3658
script_electric_enter(windata_t * vwin)3659 static gboolean script_electric_enter (windata_t *vwin)
3660 {
3661 char *s = NULL;
3662 int targsp = 0;
3663 gboolean ret = FALSE;
3664
3665 if (!smarttab || in_foreign_land(vwin->text)) {
3666 return FALSE;
3667 }
3668
3669 #if TABDEBUG
3670 fprintf(stderr, "*** script_electric_enter\n");
3671 #endif
3672
3673 s = textview_get_current_line(vwin->text, 0);
3674
3675 if (s != NULL && *s != '\0') {
3676 /* work on the line that starts with @thisword */
3677 GtkTextBuffer *tbuf;
3678 GtkTextMark *mark;
3679 GtkTextIter start, end;
3680 char thisword[9];
3681 char prevword[9];
3682 int i, diff, nsp, incr;
3683 int ipos, k, contd = 0;
3684
3685 tbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(vwin->text));
3686 mark = gtk_text_buffer_get_insert(tbuf);
3687 gtk_text_buffer_get_iter_at_mark(tbuf, &start, mark);
3688 ipos = gtk_text_iter_get_line_offset(&start);
3689 gtk_text_iter_set_line_offset(&start, 0);
3690
3691 *thisword = '\0';
3692 sscanf(s, "%8s", thisword);
3693 nsp = count_leading_spaces(s);
3694 get_previous_line_start_word(prevword, tbuf, start, &targsp, &contd);
3695
3696 #if TABDEBUG
3697 if (contd) {
3698 fprintf(stderr, "prevword='%s', leading space = %d\n",
3699 prevword, targsp);
3700 fprintf(stderr, "got line continuation\n");
3701 } else {
3702 fprintf(stderr, "thisword='%s', leading space = %d\n",
3703 thisword, nsp);
3704 fprintf(stderr, "prevword='%s', leading space = %d\n",
3705 prevword, targsp);
3706 }
3707 #endif
3708
3709 if (contd) {
3710 incr = 2;
3711 } else {
3712 #if TABDEBUG > 1
3713 fprintf(stderr, "getting leading spaces ('%s', '%s')\n",
3714 prevword, thisword);
3715 #endif
3716 incr = incremental_leading_spaces(prevword, thisword);
3717 }
3718
3719 targsp += incr;
3720 if (targsp < 0) {
3721 /* indentation messed up? */
3722 targsp = 0;
3723 }
3724
3725 diff = nsp - targsp;
3726
3727 #if TABDEBUG
3728 fprintf(stderr, "incr = %d: after increment targsp = %d, diff = %d\n",
3729 incr, targsp, diff);
3730 #endif
3731 if (diff > 0) {
3732 end = start;
3733 gtk_text_iter_forward_chars(&end, diff);
3734 gtk_text_buffer_delete(tbuf, &start, &end);
3735 } else if (diff < 0) {
3736 diff = -diff;
3737 for (i=0; i<diff; i++) {
3738 gtk_text_buffer_insert(tbuf, &start, " ", -1);
3739 }
3740 }
3741
3742 /* try to arrange correct indent on the new line? */
3743 if (ipos == 0) {
3744 k = targsp + incremental_leading_spaces(prevword, "");
3745 } else {
3746 k = targsp + incremental_leading_spaces(thisword, "");
3747 }
3748 #if TABDEBUG
3749 fprintf(stderr, "new line indent: k = %d\n", k);
3750 #endif
3751 gtk_text_buffer_begin_user_action(tbuf);
3752 gtk_text_buffer_get_iter_at_mark(tbuf, &start, mark);
3753 gtk_text_buffer_insert(tbuf, &start, "\n", -1);
3754 for (i=0; i<k; i++) {
3755 gtk_text_buffer_insert(tbuf, &start, " ", -1);
3756 }
3757 gtk_text_buffer_place_cursor(tbuf, &start);
3758 gtk_text_buffer_end_user_action(tbuf);
3759 mark = gtk_text_buffer_get_insert(tbuf);
3760 gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(vwin->text), mark);
3761 ret = TRUE;
3762 }
3763
3764 g_free(s);
3765
3766 return ret;
3767 }
3768
3769 /* Handler for the Tab key when editing a gretl script: we
3770 may want to insert a "smart tab", or take Tab as a
3771 request for gtksourceview completion.
3772 */
3773
3774 #ifdef HAVE_GTKSV_COMPLETION
3775
script_tab_handler(windata_t * vwin,GdkEvent * event)3776 static gboolean script_tab_handler (windata_t *vwin, GdkEvent *event)
3777 {
3778 int ucomp;
3779
3780 g_return_val_if_fail(GTK_IS_TEXT_VIEW(vwin->text), FALSE);
3781
3782 if (in_foreign_land(vwin->text)) {
3783 return FALSE;
3784 } else if (((GdkEventKey *) event)->state & GDK_SHIFT_MASK) {
3785 return FALSE;
3786 }
3787
3788 ucomp = (hansl_completion == COMPLETE_USER);
3789
3790 if (smarttab) {
3791 int comp_ok = 0;
3792 int *ptr = ucomp ? &comp_ok : NULL;
3793
3794 if (maybe_insert_smart_tab(vwin, ptr)) {
3795 return TRUE;
3796 } else if (comp_ok) {
3797 call_user_completion(vwin->text);
3798 return TRUE;
3799 }
3800 } else if (ucomp && maybe_try_completion(vwin)) {
3801 call_user_completion(vwin->text);
3802 return TRUE;
3803 }
3804
3805 return FALSE;
3806 }
3807
3808 #else
3809
script_tab_handler(windata_t * vwin,GdkEvent * event)3810 static gboolean script_tab_handler (windata_t *vwin, GdkEvent *event)
3811 {
3812 g_return_val_if_fail(GTK_IS_TEXT_VIEW(vwin->text), FALSE);
3813
3814 if (in_foreign_land(vwin->text)) {
3815 return FALSE;
3816 } else if (((GdkEventKey *) event)->state & GDK_SHIFT_MASK) {
3817 return FALSE;
3818 }
3819
3820 if (smarttab && maybe_insert_smart_tab(vwin, NULL)) {
3821 return TRUE;
3822 }
3823
3824 return FALSE;
3825 }
3826
3827 #endif /* HAVE_GTKSV_COMPLETION or not */
3828
script_bracket_handler(windata_t * vwin,guint keyval)3829 gboolean script_bracket_handler (windata_t *vwin, guint keyval)
3830 {
3831 if (maybe_insert_auto_bracket(vwin, keyval)) {
3832 return TRUE;
3833 } else {
3834 return FALSE;
3835 }
3836 }
3837
3838 /* Return a listing of the available gtksourceview style
3839 ids for use in the gretl preferences dialog.
3840 */
3841
get_sourceview_style_ids(int * n)3842 const char **get_sourceview_style_ids (int *n)
3843 {
3844 GtkSourceStyleSchemeManager *mgr;
3845 const gchar * const *ids = NULL;
3846
3847 ensure_sourceview_path(NULL);
3848
3849 *n = 0;
3850
3851 mgr = gtk_source_style_scheme_manager_get_default();
3852 if (mgr != NULL) {
3853 int i = 0;
3854
3855 ids = gtk_source_style_scheme_manager_get_scheme_ids(mgr);
3856 if (ids != NULL) {
3857 while (ids[i] != NULL) i++;
3858 *n = i;
3859 }
3860 }
3861
3862 return (const char **) ids;
3863 }
3864
get_graph_theme_ids(int * n)3865 const char **get_graph_theme_ids (int *n)
3866 {
3867 static char **S = NULL;
3868 static int n_found;
3869
3870 if (S != NULL) {
3871 *n = n_found;
3872 } else {
3873 gchar *path;
3874 GDir *dir;
3875
3876 *n = 0;
3877 path = g_build_filename(gretl_home(), "data", "gnuplot", NULL);
3878 dir = gretl_opendir(path);
3879
3880 S = strings_array_new(1);
3881 S[0] = gretl_strdup("classic");
3882 *n = 1;
3883
3884 if (dir != NULL) {
3885 const gchar *fname;
3886 gchar *tmp, *p;
3887 int err = 0;
3888
3889 while (!err && (fname = g_dir_read_name(dir))) {
3890 if (!strncmp(fname, "default.", 8) ||
3891 !strncmp(fname, "classic.", 8)) {
3892 continue;
3893 }
3894 if (has_suffix(fname, ".gpsty")) {
3895 tmp = g_strdup(fname);
3896 p = strstr(tmp, ".gpsty");
3897 *p = '\0';
3898 err = strings_array_add(&S, n, tmp);
3899 g_free(tmp);
3900 }
3901 }
3902 g_dir_close(dir);
3903 }
3904 n_found = *n;
3905 g_free(path);
3906 }
3907
3908 return (const char **) S;
3909 }
3910
call_prefs_dialog(GtkWidget * w,windata_t * vwin)3911 static void call_prefs_dialog (GtkWidget *w, windata_t *vwin)
3912 {
3913 preferences_dialog(TAB_EDITOR, NULL, vwin_toplevel(vwin));
3914 }
3915
3916 static GtkWidget *
build_script_popup(windata_t * vwin,struct textbit ** ptb)3917 build_script_popup (windata_t *vwin, struct textbit **ptb)
3918 {
3919 const char *items[] = {
3920 N_("Comment line"),
3921 N_("Uncomment line"),
3922 N_("Comment region"),
3923 N_("Uncomment region"),
3924 };
3925 GtkWidget *pmenu = NULL;
3926 struct textbit *tb = NULL;
3927 GtkWidget *item;
3928
3929 g_return_val_if_fail(GTK_IS_TEXT_VIEW(vwin->text), NULL);
3930
3931 /* "generic" text window menu -- we may add to this */
3932 pmenu = build_text_popup(vwin);
3933
3934 if (foreign_script_role(vwin->role)) {
3935 *ptb = NULL;
3936 goto dock_undock;
3937 }
3938
3939 tb = vwin_get_textbit(vwin, AUTO_SELECT_LINE);
3940 if (tb == NULL) {
3941 *ptb = NULL;
3942 return pmenu;
3943 }
3944
3945 tb->commented = text_is_commented(tb->chunk);
3946
3947 if (tb->commented > 0 && !editing_hansl(vwin->role)) {
3948 g_free(tb->chunk);
3949 free(tb);
3950 *ptb = NULL;
3951 goto dock_undock;
3952 }
3953
3954 *ptb = tb;
3955
3956 if (tb->commented <= 0 && vwin->role != EDIT_PKG_CODE) {
3957 /* we have some uncommented material: allow exec option */
3958 if (tb->selected) {
3959 item = gtk_menu_item_new_with_label(_("Execute region"));
3960 } else {
3961 item = gtk_menu_item_new_with_label(_("Execute line"));
3962 }
3963 g_signal_connect(G_OBJECT(item), "activate",
3964 G_CALLBACK(exec_script_text),
3965 *ptb);
3966 gtk_widget_show(item);
3967 gtk_menu_shell_append(GTK_MENU_SHELL(pmenu), item);
3968 }
3969
3970 if (editing_hansl(vwin->role) && tb->commented >= 0) {
3971 /* material is either all commented or all uncommented:
3972 allow comment/uncomment option
3973 */
3974 int i = (tb->selected && !tb->commented)? 2 :
3975 (tb->selected && tb->commented)? 3 :
3976 (!tb->selected && !tb->commented)? 0 : 1;
3977
3978 item = gtk_menu_item_new_with_label(_(items[i]));
3979 g_signal_connect(G_OBJECT(item), "activate",
3980 G_CALLBACK(comment_or_uncomment_text),
3981 *ptb);
3982 gtk_widget_show(item);
3983 gtk_menu_shell_append(GTK_MENU_SHELL(pmenu), item);
3984 }
3985
3986 if (editing_hansl(vwin->role)) {
3987 if (tb->selected) {
3988 item = gtk_menu_item_new_with_label(smarttab?
3989 _("Auto-indent region") :
3990 _("Indent region"));
3991 g_signal_connect(G_OBJECT(item), "activate",
3992 G_CALLBACK(indent_region),
3993 *ptb);
3994 gtk_widget_show(item);
3995 gtk_menu_shell_append(GTK_MENU_SHELL(pmenu), item);
3996 }
3997
3998 if (!smarttab && tb->selected && text_is_indented(tb->chunk)) {
3999 item = gtk_menu_item_new_with_label(_("Unindent region"));
4000 g_signal_connect(G_OBJECT(item), "activate",
4001 G_CALLBACK(unindent_region),
4002 *ptb);
4003 gtk_widget_show(item);
4004 gtk_menu_shell_append(GTK_MENU_SHELL(pmenu), item);
4005 }
4006
4007 item = gtk_menu_item_new_with_label(_("Auto-indent script (Ctrl+I)"));
4008 g_signal_connect(G_OBJECT(item), "activate",
4009 G_CALLBACK(auto_indent_script),
4010 vwin);
4011 gtk_widget_show(item);
4012 gtk_menu_shell_append(GTK_MENU_SHELL(pmenu), item);
4013 }
4014
4015 dock_undock:
4016
4017 if (GTK_IS_SOURCE_VIEW(vwin->text)) {
4018 item = gtk_menu_item_new_with_label(_("Preferences..."));
4019 g_signal_connect(G_OBJECT(item), "activate",
4020 G_CALLBACK(call_prefs_dialog),
4021 vwin);
4022 gtk_widget_show(item);
4023 gtk_menu_shell_append(GTK_MENU_SHELL(pmenu), item);
4024 }
4025
4026 if (window_is_undockable(vwin)) {
4027 add_undock_popup_item(pmenu, vwin);
4028 } else if (window_is_dockable(vwin)) {
4029 add_dock_popup_item(pmenu, vwin);
4030 }
4031
4032 return pmenu;
4033 }
4034
destroy_textbit(GtkWidget ** pw,struct textbit * tc)4035 static gboolean destroy_textbit (GtkWidget **pw, struct textbit *tc)
4036 {
4037 if (tc != NULL) {
4038 tc->vwin->popup = NULL;
4039 g_free(tc->chunk);
4040 free(tc);
4041 }
4042
4043 return FALSE;
4044 }
4045
4046 static gboolean
script_popup_handler(GtkWidget * w,GdkEventButton * event,gpointer p)4047 script_popup_handler (GtkWidget *w, GdkEventButton *event, gpointer p)
4048 {
4049 if (right_click(event)) {
4050 windata_t *vwin = (windata_t *) p;
4051 struct textbit *tc = NULL;
4052
4053 if (vwin->popup) {
4054 gtk_widget_destroy(vwin->popup);
4055 vwin->popup = NULL;
4056 }
4057
4058 vwin->popup = build_script_popup(vwin, &tc);
4059
4060 if (vwin->popup != NULL) {
4061 gtk_menu_popup(GTK_MENU(vwin->popup), NULL, NULL, NULL, NULL,
4062 event->button, event->time);
4063 g_signal_connect(G_OBJECT(vwin->popup), "destroy",
4064 G_CALLBACK(destroy_textbit),
4065 tc);
4066 }
4067
4068 return TRUE;
4069 }
4070
4071 return FALSE;
4072 }
4073
4074 enum {
4075 INSERT_NONE,
4076 INSERT_REF,
4077 INSERT_XREF,
4078 INSERT_FIG,
4079 INSERT_REPL,
4080 INSERT_LIT,
4081 INSERT_URL,
4082 INSERT_OPT,
4083 INSERT_ITAL,
4084 INSERT_SUP,
4085 INSERT_SUB,
4086 INSERT_TEXT,
4087 INSERT_MATH,
4088 INSERT_GUGLINK,
4089 INSERT_INPLINK,
4090 INSERT_GFRLINK,
4091 INSERT_BIBLINK,
4092 INSERT_ADBLINK,
4093 INSERT_MNULINK,
4094 INSERT_BOLD
4095 };
4096
insert_help_figure(GtkTextBuffer * tbuf,GtkTextIter * iter,const char * fig)4097 static void insert_help_figure (GtkTextBuffer *tbuf, GtkTextIter *iter,
4098 const char *fig)
4099 {
4100 char figfile[FILENAME_MAX];
4101 GdkPixbuf *pixbuf;
4102
4103 sprintf(figfile, "%shelpfigs%c%s.png", gretl_home(),
4104 SLASH, fig);
4105
4106 pixbuf = gdk_pixbuf_new_from_file(figfile, NULL);
4107
4108 if (pixbuf != NULL) {
4109 gtk_text_buffer_insert_pixbuf(tbuf, iter, pixbuf);
4110 g_object_unref(pixbuf);
4111 }
4112 }
4113
insert_math_content(GtkTextBuffer * tbuf,GtkTextIter * iter,const char * s,const char * indent)4114 static void insert_math_content (GtkTextBuffer *tbuf, GtkTextIter *iter,
4115 const char *s, const char *indent)
4116 {
4117 static char minus[4];
4118 gchar ubuf[6];
4119 gunichar c;
4120 int i, n;
4121
4122 if (*minus == '\0') {
4123 /* find the best representation of minus */
4124 PangoFontDescription *pfd;
4125
4126 pfd = pango_font_description_from_string(helpfont);
4127
4128 if (font_has_symbol(pfd, 0x2212)) {
4129 /* preferred: unicode minus */
4130 minus[0] = 0xE2;
4131 minus[1] = 0x88;
4132 minus[2] = 0x92;
4133 } else if (font_has_symbol(pfd, 0x2013)) {
4134 /* fallback: unicode endash */
4135 minus[0] = 0xE2;
4136 minus[1] = 0x80;
4137 minus[2] = 0x93;
4138 } else {
4139 /* otherwise: plain old dash */
4140 minus[0] = '-';
4141 }
4142 pango_font_description_free(pfd);
4143 }
4144
4145 n = g_utf8_strlen(s, -1);
4146
4147 for (i=0; i<n; i++) {
4148 c = g_utf8_get_char(s);
4149 if (*s == '-') {
4150 gtk_text_buffer_insert(tbuf, iter, minus, -1);
4151 } else {
4152 memset(ubuf, 0, sizeof ubuf);
4153 g_unichar_to_utf8(c, ubuf);
4154 if (g_unichar_isalpha(c)) {
4155 gtk_text_buffer_insert_with_tags_by_name(tbuf, iter, ubuf, -1,
4156 "italic", indent, NULL);
4157 } else {
4158 gtk_text_buffer_insert(tbuf, iter, ubuf, -1);
4159 }
4160 }
4161 if (i < n-1) {
4162 s = g_utf8_find_next_char(s, NULL);
4163 }
4164 }
4165 }
4166
insert_tagged_text(GtkTextBuffer * tbuf,GtkTextIter * iter,const char * s,int ins,const char * indent)4167 static void insert_tagged_text (GtkTextBuffer *tbuf, GtkTextIter *iter,
4168 const char *s, int ins, const char *indent)
4169 {
4170 const char *ftag = NULL;
4171
4172 switch (ins) {
4173 case INSERT_ITAL:
4174 case INSERT_MATH: /* FIXME */
4175 ftag = "italic";
4176 break;
4177 case INSERT_REPL:
4178 ftag = "replaceable";
4179 break;
4180 case INSERT_LIT:
4181 ftag = "literal";
4182 break;
4183 case INSERT_OPT:
4184 ftag = "optflag";
4185 break;
4186 case INSERT_SUP:
4187 ftag = "superscript";
4188 break;
4189 case INSERT_SUB:
4190 if (integer_string(s)) {
4191 ftag = "subscript-numeral";
4192 } else {
4193 ftag = "subscript";
4194 }
4195 break;
4196 case INSERT_BOLD:
4197 ftag = "heading";
4198 break;
4199 default:
4200 break;
4201 }
4202
4203 #ifdef G_OS_WIN32
4204 if (ins == INSERT_OPT) {
4205 /* Unicode word joiner not supported? Try zero width
4206 non breaking space instead */
4207 char tmp[32];
4208
4209 strcpy(tmp, s);
4210 tmp[2] = 0xEF;
4211 tmp[3] = 0xBB;
4212 tmp[4] = 0xBF;
4213
4214 gtk_text_buffer_insert_with_tags_by_name(tbuf, iter, tmp, -1,
4215 ftag, indent, NULL);
4216 return;
4217 }
4218 #endif
4219
4220 if (ftag != NULL) {
4221 gtk_text_buffer_insert_with_tags_by_name(tbuf, iter, s, -1,
4222 ftag, indent, NULL);
4223 }
4224 }
4225
get_string_and_instruction(const char * p,int * ins)4226 static gchar *get_string_and_instruction (const char *p, int *ins)
4227 {
4228 gchar *str = NULL;
4229
4230 *ins = INSERT_NONE;
4231
4232 if (!strncmp(p, "ref", 3)) {
4233 *ins = INSERT_REF;
4234 } else if (!strncmp(p, "xrf", 3)) {
4235 *ins = INSERT_XREF;
4236 } else if (!strncmp(p, "fig", 3)) {
4237 *ins = INSERT_FIG;
4238 } else if (!strncmp(p, "itl", 3)) {
4239 *ins = INSERT_ITAL;
4240 } else if (!strncmp(p, "var", 3)) {
4241 *ins = INSERT_REPL;
4242 } else if (!strncmp(p, "lit", 3)) {
4243 *ins = INSERT_LIT;
4244 } else if (!strncmp(p, "url", 3)) {
4245 *ins = INSERT_URL;
4246 } else if (!strncmp(p, "opt", 3)) {
4247 *ins = INSERT_OPT;
4248 } else if (!strncmp(p, "sup", 3)) {
4249 *ins = INSERT_SUP;
4250 } else if (!strncmp(p, "sub", 3)) {
4251 *ins = INSERT_SUB;
4252 } else if (!strncmp(p, "mth", 3)) {
4253 *ins = INSERT_MATH;
4254 } else if (!strncmp(p, "pdf", 3)) {
4255 *ins = INSERT_GUGLINK;
4256 } else if (!strncmp(p, "inp", 3)) {
4257 *ins = INSERT_INPLINK;
4258 } else if (!strncmp(p, "gfr", 3)) {
4259 *ins = INSERT_GFRLINK;
4260 } else if (!strncmp(p, "bib", 3)) {
4261 *ins = INSERT_BIBLINK;
4262 } else if (!strncmp(p, "adb", 3)) {
4263 *ins = INSERT_ADBLINK;
4264 } else if (!strncmp(p, "mnu", 3)) {
4265 *ins = INSERT_MNULINK;
4266 } else if (!strncmp(p, "hd1", 3)) {
4267 *ins = INSERT_BOLD;
4268 }
4269
4270 if (*ins != INSERT_NONE) {
4271 int i;
4272
4273 p += 5; /* skip 'tag="' */
4274 for (i=0; p[i]; i++) {
4275 if (p[i] == '"' && p[i+1] == '>') {
4276 break;
4277 }
4278 }
4279 str = g_strndup(p, i);
4280 }
4281
4282 return str;
4283 }
4284
get_code_skip(const char * s)4285 static int get_code_skip (const char *s)
4286 {
4287 int skip = 5;
4288
4289 while (*s) {
4290 if (*s == '\n') {
4291 skip++;
4292 break;
4293 } else if (isspace(*s)) {
4294 skip++;
4295 }
4296 s++;
4297 }
4298
4299 return skip;
4300 }
4301
command_word_index(char * s)4302 static int command_word_index (char *s)
4303 {
4304 int i = gretl_command_number(s);
4305
4306 if (i == 0) {
4307 i = extra_command_number(s);
4308 if (i < 0) {
4309 i = 0;
4310 } else {
4311 /* e.g. "MIDAS_list" -> "MIDAS list", for
4312 display purposes */
4313 gretl_charsub(s, '_', ' ');
4314 }
4315 }
4316
4317 return i;
4318 }
4319
4320 /* return non-zero if we inserted any hyperlinks */
4321
4322 static gboolean
insert_text_with_markup(GtkTextBuffer * tbuf,GtkTextIter * iter,const char * s,int role)4323 insert_text_with_markup (GtkTextBuffer *tbuf, GtkTextIter *iter,
4324 const char *s, int role)
4325 {
4326 gboolean ret = FALSE;
4327 gchar *targ = NULL;
4328 const char *indent = NULL;
4329 const char *p;
4330 int code = 0;
4331 int mono = 0;
4332 int itarg, ins;
4333
4334 while ((p = strstr(s, "<"))) {
4335 int skip = 0;
4336
4337 if (code) {
4338 gtk_text_buffer_insert_with_tags_by_name(tbuf, iter, s, p - s,
4339 "code", indent, NULL);
4340 } else if (mono) {
4341 gtk_text_buffer_insert_with_tags_by_name(tbuf, iter, s, p - s,
4342 "mono", indent, NULL);
4343 } else {
4344 gtk_text_buffer_insert_with_tags_by_name(tbuf, iter, s, p - s,
4345 "text", indent, NULL);
4346 }
4347
4348 p++;
4349
4350 if (*p == '@') {
4351 /* "atomic" markup */
4352 targ = get_string_and_instruction(p + 1, &ins);
4353 if (ins == INSERT_REF) {
4354 if (function_help(role)) {
4355 /* FIXME? */
4356 itarg = function_help_index_from_word(targ, role);
4357 } else {
4358 itarg = command_word_index(targ);
4359 }
4360 ret = insert_link(tbuf, iter, targ, itarg, indent);
4361 } else if (ins == INSERT_XREF) {
4362 if (function_help(role)) {
4363 itarg = command_word_index(targ);
4364 } else {
4365 itarg = function_help_index_from_word(targ, FUNC_HELP);
4366 }
4367 ret = insert_xlink(tbuf, iter, targ, itarg, indent);
4368 } else if (ins == INSERT_URL) {
4369 ret = insert_link(tbuf, iter, targ, EXT_PAGE, indent);
4370 } else if (ins == INSERT_GUGLINK) {
4371 ret = insert_link(tbuf, iter, targ, GUIDE_PAGE, indent);
4372 } else if (ins == INSERT_INPLINK) {
4373 ret = insert_link(tbuf, iter, targ, SCRIPT_PAGE, indent);
4374 } else if (ins == INSERT_BIBLINK) {
4375 ret = insert_link(tbuf, iter, targ, BIB_PAGE, indent);
4376 } else if (ins == INSERT_GFRLINK) {
4377 ret = insert_xlink(tbuf, iter, targ, GFR_PAGE, indent);
4378 } else if (ins == INSERT_ADBLINK) {
4379 ret = insert_link(tbuf, iter, targ, PDF_PAGE, indent);
4380 } else if (ins == INSERT_MNULINK) {
4381 ret = insert_link(tbuf, iter, targ, MNU_PAGE, indent);
4382 } else if (ins == INSERT_FIG) {
4383 insert_help_figure(tbuf, iter, targ);
4384 } else if (ins == INSERT_MATH) {
4385 insert_math_content(tbuf, iter, targ, indent);
4386 } else if (ins != INSERT_NONE) {
4387 insert_tagged_text(tbuf, iter, targ, ins, indent);
4388 }
4389 skip = 8 + strlen(targ);
4390 } else if (!strncmp(p, "indent", 6)) {
4391 indent = "indented";
4392 skip = 7 + (*(p+7) == '\n');
4393 } else if (!strncmp(p, "/indent", 7)) {
4394 indent = NULL;
4395 skip = 8 + (*(p+8) == '\n');
4396 } else if (!strncmp(p, "code", 4)) {
4397 code = 1;
4398 skip = get_code_skip(p + 5);
4399 } else if (!strncmp(p, "/code", 5)) {
4400 code = 0;
4401 skip = 6 + (*(p+6) == '\n');
4402 } else if (!strncmp(p, "mono", 4)) {
4403 mono = 1;
4404 skip = get_code_skip(p + 5);
4405 } else if (!strncmp(p, "/mono", 5)) {
4406 mono = 0;
4407 skip = 6 + (*(p+6) == '\n');
4408 } else {
4409 /* literal "<" */
4410 gtk_text_buffer_insert(tbuf, iter, "<", 1);
4411 }
4412
4413 if (targ != NULL) {
4414 g_free(targ);
4415 targ = NULL;
4416 }
4417 s = p + skip;
4418 }
4419
4420 if (code) {
4421 gtk_text_buffer_insert_with_tags_by_name(tbuf, iter, s, -1,
4422 "code", indent, NULL);
4423 } else if (mono) {
4424 gtk_text_buffer_insert_with_tags_by_name(tbuf, iter, s, -1,
4425 "mono", indent, NULL);
4426 } else {
4427 gtk_text_buffer_insert_with_tags_by_name(tbuf, iter, s, -1,
4428 "text", indent, NULL);
4429 }
4430
4431 return ret;
4432 }
4433
grab_topic_buffer(const char * s)4434 static char *grab_topic_buffer (const char *s)
4435 {
4436 const char *p = strstr(s, "\n#");
4437 char *buf;
4438
4439 if (p != NULL) {
4440 buf = g_strndup(s, p - s);
4441 } else {
4442 buf = g_strdup(s);
4443 }
4444
4445 return buf;
4446 }
4447
4448 /* Pull the appropriate chunk of help text out of the buffer attached
4449 to the help viewer and display it, if possible. Return >= 0
4450 if we did OK, < 0 on failure.
4451 */
4452
set_help_topic_buffer(windata_t * hwin,int pos,int en)4453 int set_help_topic_buffer (windata_t *hwin, int pos, int en)
4454 {
4455 GtkTextBuffer *textb;
4456 GtkTextIter iter;
4457 char line[256];
4458 gchar *hbuf;
4459 char *buf;
4460
4461 push_backpage(hwin->text, hwin->active_var);
4462
4463 textb = gretl_text_buf_new();
4464
4465 if (pos == 0) {
4466 /* no topic selected */
4467 if (function_help(hwin->role)) {
4468 funcref_index_page(hwin, textb, en);
4469 } else {
4470 cmdref_index_page(hwin, textb, en);
4471 }
4472 cursor_to_top(hwin);
4473 return 0;
4474 }
4475
4476 /* OK, so pos is non-zero */
4477
4478 maybe_connect_help_signals(hwin, en);
4479 maybe_set_help_tabs(hwin);
4480
4481 gtk_text_buffer_get_iter_at_offset(textb, &iter, 0);
4482
4483 hbuf = (gchar *) hwin->data + pos;
4484
4485 bufgets_init(hbuf);
4486 buf = bufgets(line, sizeof line, hbuf);
4487 bufgets_finalize(hbuf);
4488
4489 if (buf == NULL) {
4490 return -1;
4491 }
4492
4493 tailstrip(line);
4494
4495 if (gui_help(hwin->role)) {
4496 /* topic heading: descriptive string */
4497 gchar *p = quoted_help_string(line);
4498
4499 gtk_text_buffer_insert_with_tags_by_name(textb, &iter,
4500 p, -1,
4501 "heading", NULL);
4502 free(p);
4503 } else {
4504 /* topic heading: plain command word */
4505 char hword[12];
4506
4507 sscanf(line + 2, "%11s", hword);
4508 gtk_text_buffer_insert_with_tags_by_name(textb, &iter,
4509 hword, -1,
4510 "redtext", NULL);
4511 }
4512
4513 if (function_help(hwin->role)) {
4514 gtk_text_buffer_insert(textb, &iter, "\n\n", 2);
4515 } else {
4516 gtk_text_buffer_insert(textb, &iter, "\n", 1);
4517 }
4518
4519 buf = grab_topic_buffer(hbuf + strlen(line) + 1);
4520 if (buf == NULL) {
4521 return -1;
4522 }
4523
4524 insert_text_with_markup(textb, &iter, buf, hwin->role);
4525 free(buf);
4526
4527 gtk_text_view_set_buffer(GTK_TEXT_VIEW(hwin->text), textb);
4528 maybe_connect_help_signals(hwin, en);
4529 cursor_to_top(hwin);
4530
4531 return 1;
4532 }
4533
gretl_viewer_set_formatted_buffer(windata_t * vwin,const char * buf)4534 void gretl_viewer_set_formatted_buffer (windata_t *vwin, const char *buf)
4535 {
4536 GtkTextBuffer *textb;
4537 GtkTextIter iter;
4538 gboolean links;
4539
4540 textb = gretl_text_buf_new();
4541 gtk_text_buffer_get_iter_at_offset(textb, &iter, 0);
4542 links = insert_text_with_markup(textb, &iter, buf, FUNC_HELP);
4543
4544 gtk_text_view_set_buffer(GTK_TEXT_VIEW(vwin->text), textb);
4545 cursor_to_top(vwin);
4546
4547 if (links) {
4548 connect_link_signals(vwin);
4549 }
4550 }
4551
get_screen_height(void)4552 int get_screen_height (void)
4553 {
4554 static int screen_height;
4555
4556 if (screen_height == 0) {
4557 GdkScreen *s = gdk_screen_get_default();
4558
4559 if (s != NULL) {
4560 screen_height = gdk_screen_get_height(s);
4561 }
4562 }
4563
4564 return screen_height;
4565 }
4566
set_max_text_width(windata_t * vwin,int width,int height)4567 static void set_max_text_width (windata_t *vwin,
4568 int width,
4569 int height)
4570 {
4571 GdkGeometry hints = {0};
4572
4573 hints.max_width = width;
4574 hints.max_height = height * 3;
4575
4576 gtk_window_set_geometry_hints(GTK_WINDOW(vwin->main),
4577 GTK_WIDGET(vwin->main),
4578 &hints,
4579 GDK_HINT_MAX_SIZE);
4580 }
4581
4582 #define HDEBUG 0
4583 #define HELP_WRAP 1
4584
create_text(windata_t * vwin,int hsize,int vsize,int nlines,gboolean editable)4585 void create_text (windata_t *vwin, int hsize, int vsize,
4586 int nlines, gboolean editable)
4587 {
4588 GtkTextBuffer *tbuf = gretl_text_buf_new();
4589 GtkWidget *w = gtk_text_view_new_with_buffer(tbuf);
4590 int role = vwin->role;
4591
4592 vwin->text = w;
4593
4594 /* in which cases should we do text wrapping? */
4595
4596 if (help_role(role) || role == VIEW_PKG_INFO ||
4597 role == VIEW_BIBITEM || role == VIEW_CODEBOOK ||
4598 #if HELP_WRAP
4599 role == EDIT_PKG_HELP || role == EDIT_PKG_GHLP ||
4600 #endif
4601 role == VIEW_DBNOMICS || role == IMPORT ||
4602 role == VIEW_DBSEARCH) {
4603 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(w), GTK_WRAP_WORD);
4604 } else {
4605 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(w), GTK_WRAP_NONE);
4606 }
4607
4608 if (role == VIEW_DBSEARCH) {
4609 /* make @vwin discoverable via @w */
4610 g_object_set_data(G_OBJECT(w), "vwin", vwin);
4611 }
4612
4613 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(w), 4);
4614 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(w), 4);
4615
4616 gtk_widget_modify_font(GTK_WIDGET(w), fixed_font);
4617
4618 #if HDEBUG
4619 /* the incoming @hsize is expressed in characters */
4620 fprintf(stderr, "create_text: initial hsize = %d\n", hsize);
4621 #endif
4622
4623 if (hsize > 0 || nlines > 0) {
4624 int px, py;
4625
4626 get_char_width_and_height(w, &px, &py);
4627 if (hsize > 0) {
4628 hsize *= px;
4629 hsize += 48;
4630 }
4631 #if HDEBUG
4632 fprintf(stderr, " px = %d, py = %d; hsize now = %d, nlines = %d\n",
4633 px, py, hsize, nlines);
4634 #endif
4635 if (nlines > 0) {
4636 /* Perhaps adjust how tall the window is? */
4637 int v1 = (nlines + 2) * py;
4638 int sv = get_screen_height();
4639
4640 if (v1 > 0.85 * vsize && v1 <= 0.7 * sv) {
4641 vsize = v1;
4642 } else if (v1 > 0.7 * sv) {
4643 vsize = 0.7 * sv;
4644 }
4645 } else if (role != VIEW_BIBITEM && vsize < 0.62 * hsize) {
4646 vsize = 0.62 * hsize;
4647 }
4648 }
4649
4650 if (hsize > 0 && vsize > 0) {
4651 GtkWidget *vmain = vwin_toplevel(vwin);
4652
4653 if (window_is_tab(vwin)) {
4654 vsize += 15;
4655 }
4656 #if HDEBUG
4657 fprintf(stderr, " setting default size (%d, %d)\n", hsize, vsize);
4658 #endif
4659 gtk_window_set_default_size(GTK_WINDOW(vmain), hsize, vsize);
4660 if (role == EDIT_PKG_HELP || role == EDIT_PKG_GHLP) {
4661 /* for editing help files: limit the width of the window
4662 to discourage use of excessively long lines
4663 */
4664 set_max_text_width(vwin, hsize, vsize);
4665 }
4666 }
4667
4668 gtk_text_view_set_editable(GTK_TEXT_VIEW(w), editable);
4669 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(w), editable);
4670 }
4671
gretl_console_tags_new(void)4672 static GtkTextTagTable *gretl_console_tags_new (void)
4673 {
4674 GtkTextTagTable *table;
4675 GtkTextTag *tag;
4676
4677 table = gtk_text_tag_table_new();
4678
4679 tag = gtk_text_tag_new("prompt");
4680 g_object_set(tag, "foreground", "red", NULL);
4681 gtk_text_tag_table_add(table, tag);
4682
4683 tag = gtk_text_tag_new("output");
4684 g_object_set(tag, "foreground", "black",
4685 "weight", PANGO_WEIGHT_NORMAL, NULL);
4686 gtk_text_tag_table_add(table, tag);
4687
4688 return table;
4689 }
4690
create_console(windata_t * vwin,int hsize,int vsize)4691 void create_console (windata_t *vwin, int hsize, int vsize)
4692 {
4693 static GtkTextTagTable *console_tags = NULL;
4694 GtkSourceLanguageManager *lm = NULL;
4695 GtkSourceBuffer *sbuf;
4696 GtkTextView *view;
4697 int cw;
4698
4699 if (console_tags == NULL) {
4700 console_tags = gretl_console_tags_new();
4701 }
4702
4703 /* since 2018-06-09: use syntax highlighting */
4704 lm = gtk_source_language_manager_get_default();
4705 ensure_sourceview_path(lm);
4706
4707 sbuf = gtk_source_buffer_new(console_tags);
4708 gtk_source_buffer_set_highlight_matching_brackets(sbuf, TRUE);
4709 if (lm != NULL) {
4710 g_object_set_data(G_OBJECT(sbuf), "languages-manager", lm);
4711 set_style_for_buffer(sbuf, get_sourceview_style(), CONSOLE);
4712 }
4713
4714 vwin->text = gtk_source_view_new_with_buffer(sbuf);
4715 vwin->sbuf = sbuf;
4716
4717 view = GTK_TEXT_VIEW(vwin->text);
4718 gtk_text_view_set_wrap_mode(view, GTK_WRAP_NONE);
4719 gtk_text_view_set_left_margin(view, 4);
4720 gtk_text_view_set_right_margin(view, 4);
4721
4722 gtk_widget_modify_font(GTK_WIDGET(vwin->text), fixed_font);
4723
4724 cw = get_char_width(vwin->text);
4725 set_source_tabs(vwin->text, cw);
4726
4727 #ifdef HAVE_GTKSV_COMPLETION
4728 if (console_completion) {
4729 /* since 2021-06-11 */
4730 set_sv_completion(vwin);
4731 }
4732 #endif
4733
4734 if (hsize > 0) {
4735 hsize *= cw;
4736 hsize += 48; /* ?? */
4737 }
4738 if (vsize < 0.62 * hsize) {
4739 vsize = 0.62 * hsize;
4740 }
4741
4742 if (vwin->main != vwin->vbox && hsize > 0 && vsize > 0) {
4743 GtkWidget *vmain = vwin_toplevel(vwin);
4744
4745 gtk_window_set_default_size(GTK_WINDOW(vmain), hsize, vsize);
4746 }
4747
4748 sourceview_apply_language(vwin);
4749 gtk_text_view_set_editable(view, TRUE);
4750 gtk_text_view_set_cursor_visible(view, TRUE);
4751 }
4752
text_set_word_wrap(GtkWidget * w,gboolean wrap)4753 void text_set_word_wrap (GtkWidget *w, gboolean wrap)
4754 {
4755 if (wrap) {
4756 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(w), GTK_WRAP_WORD);
4757 } else {
4758 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(w), GTK_WRAP_NONE);
4759 }
4760 }
4761
text_table_setup(GtkWidget * vbox,GtkWidget * w)4762 void text_table_setup (GtkWidget *vbox, GtkWidget *w)
4763 {
4764 GtkWidget *sw;
4765
4766 sw = gtk_scrolled_window_new(NULL, NULL);
4767 gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, FALSE);
4768 g_object_set_data(G_OBJECT(vbox), "sw", sw);
4769
4770 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
4771 GTK_POLICY_AUTOMATIC,
4772 GTK_POLICY_AUTOMATIC);
4773 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
4774 GTK_SHADOW_IN);
4775 gtk_container_add(GTK_CONTAINER(sw), w);
4776 gtk_widget_show(w);
4777 gtk_widget_show(sw);
4778 }
4779
set_pane_text_properties(GtkTextView * w2,GtkTextView * w1)4780 static void set_pane_text_properties (GtkTextView *w2,
4781 GtkTextView *w1)
4782 {
4783 gtk_text_view_set_wrap_mode(w2, 0);
4784 gtk_text_view_set_left_margin(w2, 4);
4785 gtk_text_view_set_right_margin(w2, 4);
4786
4787 gtk_widget_modify_font(GTK_WIDGET(w2), fixed_font);
4788
4789 if (gtk_text_view_get_editable(w1)) {
4790 gtk_text_view_set_editable(w2, TRUE);
4791 gtk_text_view_set_cursor_visible(w2, TRUE);
4792 } else {
4793 gtk_text_view_set_editable(w2, FALSE);
4794 gtk_text_view_set_cursor_visible(w2, FALSE);
4795 }
4796 }
4797
4798 /* divide a text window into two panes */
4799
viewer_split_pane(windata_t * vwin,int vertical)4800 void viewer_split_pane (windata_t *vwin, int vertical)
4801 {
4802 GtkWidget *vbox = vwin->vbox;
4803 GtkWidget *view1 = vwin->text;
4804 GtkWidget *sw, *paned, *view2;
4805 GtkWidget *vmain;
4806 GtkTextBuffer *tbuf;
4807 gint width, height;
4808
4809 sw = g_object_get_data(G_OBJECT(vbox), "sw");
4810
4811 vmain = vwin_toplevel(vwin);
4812 gtk_window_get_size(GTK_WINDOW(vmain), &width, &height);
4813
4814 g_object_ref(sw);
4815 gtk_container_remove(GTK_CONTAINER(vwin->vbox), sw);
4816
4817 if (vertical) {
4818 paned = gtk_hpaned_new();
4819 } else {
4820 paned = gtk_vpaned_new();
4821 }
4822
4823 gtk_container_set_border_width(GTK_CONTAINER(paned), 0);
4824 gtk_container_add(GTK_CONTAINER(vbox), paned);
4825
4826 g_object_set_data(G_OBJECT(vwin->vbox), "paned", paned);
4827 g_object_set_data(G_OBJECT(vwin->vbox), "sw", NULL);
4828
4829 tbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(view1));
4830
4831 if (GTK_IS_SOURCE_VIEW(view1)) {
4832 view2 = gtk_source_view_new_with_buffer(GTK_SOURCE_BUFFER(tbuf));
4833 } else {
4834 view2 = gtk_text_view_new_with_buffer(tbuf);
4835 }
4836
4837 set_pane_text_properties(GTK_TEXT_VIEW(view2),
4838 GTK_TEXT_VIEW(view1));
4839
4840 g_signal_connect(G_OBJECT(view2), "button-press-event",
4841 G_CALLBACK(text_popup_handler), vwin);
4842
4843 gtk_paned_add1(GTK_PANED(paned), sw);
4844 g_object_unref(sw);
4845
4846 sw = gtk_scrolled_window_new(NULL, NULL);
4847 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
4848 GTK_POLICY_AUTOMATIC,
4849 GTK_POLICY_AUTOMATIC);
4850 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
4851 GTK_SHADOW_IN);
4852 gtk_paned_add2(GTK_PANED(paned), sw);
4853 gtk_container_add(GTK_CONTAINER(sw), view2);
4854
4855 if (vertical) {
4856 gtk_paned_set_position(GTK_PANED(paned), width / 2 - 5);
4857 } else {
4858 gtk_paned_set_position(GTK_PANED(paned), height / 2 - 24);
4859 }
4860
4861 gtk_widget_show_all(paned);
4862 }
4863
4864 /* script output window: revert to a single pane */
4865
viewer_close_pane(windata_t * vwin)4866 void viewer_close_pane (windata_t *vwin)
4867 {
4868 GtkWidget *sw, *paned;
4869
4870 paned = g_object_get_data(G_OBJECT(vwin->vbox), "paned");
4871
4872 /* grab the first child and reference it */
4873 sw = gtk_paned_get_child1(GTK_PANED(paned));
4874 g_object_ref(sw);
4875 gtk_container_remove(GTK_CONTAINER(paned), sw);
4876
4877 /* remove the "paned" widget */
4878 gtk_widget_destroy(paned);
4879 g_object_set_data(G_OBJECT(vwin->vbox), "paned", NULL);
4880
4881 /* and repack the scrolled window */
4882 gtk_container_add(GTK_CONTAINER(vwin->vbox), sw);
4883 g_object_set_data(G_OBJECT(vwin->vbox), "sw", sw);
4884 gtk_widget_show(sw);
4885 g_object_unref(sw);
4886 }
4887