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 "dlgutils.h"
22 #include "varprint.h"
23 #include "textutil.h"
24 #include "textbuf.h"
25 #include "guiprint.h"
26 #include "model_table.h"
27 #include "clipboard.h"
28 #include "fileselect.h"
29 #include "texprint.h"
30 #include "system.h"
31 #include "winstack.h"
32
33 #if GTKSOURCEVIEW_VERSION == 2
34 # include <gtksourceview/gtksourceiter.h>
35 #endif
36
37 struct search_replace {
38 GtkWidget *w; /* the dialog box */
39 GtkWidget *f_entry; /* Find string entry */
40 GtkWidget *r_entry; /* Replace string entry */
41 GtkWidget *r_button; /* Replace button */
42 gchar *find; /* the Find string */
43 gchar *replace; /* the Replace string */
44 GtkTextBuffer *buf;
45 GtkTextView *view;
46 GtkTextMark *mark;
47 GtkTextIter iter;
48 };
49
destroy_replacer(GtkWidget * widget,struct search_replace * s)50 static gboolean destroy_replacer (GtkWidget *widget,
51 struct search_replace *s)
52 {
53 GtkTextMark *mark;
54
55 mark = gtk_text_buffer_get_mark(s->buf, "srmark");
56 if (mark != NULL) {
57 gtk_text_buffer_delete_mark(s->buf, mark);
58 }
59
60 g_free(s->find);
61 g_free(s->replace);
62
63 gtk_main_quit();
64 return FALSE;
65 }
66
67 /* here we simply find (or not) the given string */
68
replace_find_callback(GtkWidget * widget,struct search_replace * s)69 static void replace_find_callback (GtkWidget *widget,
70 struct search_replace *s)
71 {
72 GtkTextIter f_start, f_end;
73 gboolean found;
74
75 g_free(s->find);
76 s->find = gtk_editable_get_chars(GTK_EDITABLE(s->f_entry), 0, -1);
77
78 if (s->find == NULL || *s->find == '\0') {
79 return;
80 }
81
82 gtk_text_buffer_get_iter_at_mark(s->buf, &s->iter, s->mark);
83
84 #if GTKSOURCEVIEW_VERSION == 2
85 found = gtk_source_iter_forward_search(&s->iter,
86 s->find,
87 0,
88 &f_start,
89 &f_end,
90 NULL);
91 #else /* GTK 3 */
92 found = gtk_text_iter_forward_search(&s->iter,
93 s->find,
94 0,
95 &f_start,
96 &f_end,
97 NULL);
98 #endif
99
100 if (found) {
101 gtk_text_buffer_select_range(s->buf, &f_start, &f_end);
102 gtk_text_buffer_move_mark(s->buf, s->mark, &f_end);
103 if (gtk_text_iter_forward_line(&f_end)) {
104 /* go one line further on, if possible */
105 gtk_text_buffer_move_mark(s->buf, s->mark, &f_end);
106 }
107 gtk_text_view_scroll_mark_onscreen(s->view, s->mark);
108 } else {
109 notify_string_not_found(s->f_entry);
110 }
111
112 gtk_widget_set_sensitive(s->r_button, found);
113 }
114
update_search_strings(struct search_replace * s)115 static void update_search_strings (struct search_replace *s)
116 {
117 g_free(s->find);
118 g_free(s->replace);
119
120 s->find = gtk_editable_get_chars(GTK_EDITABLE(s->f_entry), 0, -1);
121 s->replace = gtk_editable_get_chars(GTK_EDITABLE(s->r_entry), 0, -1);
122 }
123
124 /* replace an occurrence of the Find string that has just been
125 found, and selected */
126
replace_single_callback(GtkWidget * button,struct search_replace * s)127 static void replace_single_callback (GtkWidget *button,
128 struct search_replace *s)
129 {
130 GtkTextIter r_start, r_end;
131 gchar *text;
132
133 update_search_strings(s);
134
135 if (s->find == NULL || s->replace == NULL || *s->find == '\0') {
136 return;
137 }
138
139 if (!gtk_text_buffer_get_selection_bounds(s->buf, &r_start, &r_end)) {
140 return;
141 }
142
143 text = gtk_text_buffer_get_text(s->buf, &r_start, &r_end, FALSE);
144
145 if (strcmp(text, s->find) == 0) {
146 gtk_text_buffer_begin_user_action(s->buf);
147 gtk_text_buffer_delete(s->buf, &r_start, &r_end);
148 gtk_text_buffer_insert(s->buf, &r_start, s->replace, -1);
149 gtk_text_buffer_move_mark(s->buf, s->mark, &r_start);
150 gtk_text_buffer_end_user_action(s->buf);
151 }
152
153 gtk_widget_set_sensitive(button, FALSE);
154 g_free(text);
155 }
156
157 /* replace all occurrences of the Find string in the text
158 buffer, or in the current selection if there is one
159 */
160
replace_all_callback(GtkWidget * button,struct search_replace * s)161 static void replace_all_callback (GtkWidget *button,
162 struct search_replace *s)
163 {
164 #if GTKSOURCEVIEW_VERSION == 2
165 GtkSourceSearchFlags search_flags;
166 #else
167 GtkTextSearchFlags search_flags;
168 #endif
169 GtkTextIter start, selstart, end;
170 GtkTextIter r_start, r_end;
171 GtkTextMark *mark;
172 gint init_pos[2];
173 gint replace_len;
174 gboolean selected = FALSE;
175 gboolean found = TRUE;
176 gboolean do_brackets;
177 int count = 0;
178
179 update_search_strings(s);
180
181 if (s->find == NULL || s->replace == NULL || *s->find == '\0') {
182 return;
183 }
184
185 /* record the initial cursor position */
186 mark = gtk_text_buffer_get_insert(s->buf);
187 gtk_text_buffer_get_iter_at_mark(s->buf, &s->iter, mark);
188 init_pos[0] = gtk_text_iter_get_line(&s->iter);
189 init_pos[1] = gtk_text_iter_get_line_index(&s->iter);
190
191 /* if there's a selection in place, respect it, otherwise work
192 on the whole buffer */
193 if (gtk_text_buffer_get_selection_bounds(s->buf, &start, &end)) {
194 selected = TRUE;
195 } else {
196 gtk_text_buffer_get_start_iter(s->buf, &start);
197 gtk_text_buffer_get_end_iter(s->buf, &end);
198 }
199
200 #if GTKSOURCEVIEW_VERSION == 2
201 search_flags = GTK_SOURCE_SEARCH_VISIBLE_ONLY | GTK_SOURCE_SEARCH_TEXT_ONLY;
202 #else
203 search_flags = GTK_TEXT_SEARCH_VISIBLE_ONLY | GTK_TEXT_SEARCH_TEXT_ONLY;
204 #endif
205 replace_len = strlen(s->replace);
206
207 /* avoid spending time matching brackets */
208 do_brackets = gtk_source_buffer_get_highlight_matching_brackets(GTK_SOURCE_BUFFER(s->buf));
209 gtk_source_buffer_set_highlight_matching_brackets(GTK_SOURCE_BUFFER(s->buf), FALSE);
210
211 gtk_text_buffer_begin_user_action(s->buf);
212
213 do {
214 #if GTKSOURCEVIEW_VERSION == 2
215 found = gtk_source_iter_forward_search(&start,
216 s->find,
217 search_flags,
218 &r_start,
219 &r_end,
220 selected ? &end : NULL);
221 #else /* GTK 3 */
222 found = gtk_text_iter_forward_search(&start,
223 s->find,
224 search_flags,
225 &r_start,
226 &r_end,
227 selected ? &end : NULL);
228 #endif
229 if (found) {
230 count++;
231 gtk_text_buffer_delete(s->buf, &r_start, &r_end);
232 gtk_text_buffer_insert(s->buf, &r_start,
233 s->replace,
234 replace_len);
235 start = r_start;
236 if (selected) {
237 gtk_text_buffer_get_selection_bounds(s->buf,
238 &selstart,
239 &end);
240 }
241 }
242 } while (found);
243
244 #if 0
245 fprintf(stderr, "replaced %d occurrences\n", count);
246 #endif
247
248 gtk_text_buffer_end_user_action(s->buf);
249
250 gtk_source_buffer_set_highlight_matching_brackets(GTK_SOURCE_BUFFER(s->buf),
251 do_brackets);
252
253 /* put the cursor back where we found it */
254 gtk_text_buffer_get_start_iter(s->buf, &s->iter);
255 gtk_text_iter_set_line(&s->iter, init_pos[0]);
256 gtk_text_iter_set_line_index(&s->iter, init_pos[1]);
257 gtk_text_buffer_place_cursor(s->buf, &s->iter);
258 gtk_text_buffer_move_mark(s->buf, s->mark, &s->iter);
259 }
260
replace_string_dialog(windata_t * vwin)261 static void replace_string_dialog (windata_t *vwin)
262 {
263 GtkWidget *label, *button;
264 GtkWidget *vbox, *abox;
265 GtkWidget *table;
266 struct search_replace sr_t;
267 struct search_replace *s = &sr_t;
268
269 s->find = s->replace = NULL;
270
271 s->view = GTK_TEXT_VIEW(vwin->text);
272 s->buf = gtk_text_view_get_buffer(s->view);
273 gtk_text_buffer_get_start_iter(s->buf, &s->iter);
274 s->mark = gtk_text_buffer_create_mark(s->buf, "srmark",
275 &s->iter, FALSE);
276
277 s->w = gretl_gtk_dialog();
278 gretl_dialog_set_destruction(s->w, vwin_toplevel(vwin));
279 g_signal_connect(G_OBJECT(s->w), "destroy",
280 G_CALLBACK(destroy_replacer), s);
281
282 gtk_window_set_title(GTK_WINDOW(s->w), _("gretl: replace"));
283 gtk_container_set_border_width(GTK_CONTAINER(s->w), 5);
284
285 table = gtk_table_new(2, 2, FALSE);
286
287 /* 'Find' label and entry */
288 label = gtk_label_new(_("Find:"));
289 s->f_entry = gtk_entry_new();
290 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, 0, 0,
291 5, 5);
292 gtk_table_attach(GTK_TABLE(table), s->f_entry, 1, 2, 0, 1,
293 GTK_EXPAND | GTK_FILL, 0, 5, 5);
294
295 /* 'Replace' label and entry */
296 label = gtk_label_new(_("Replace with:"));
297 s->r_entry = gtk_entry_new();
298 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2, 0, 0,
299 5, 5);
300 gtk_table_attach(GTK_TABLE(table), s->r_entry, 1, 2, 1, 2,
301 GTK_EXPAND | GTK_FILL, 0, 5, 5);
302
303 vbox = gtk_dialog_get_content_area(GTK_DIALOG(s->w));
304 gtk_box_pack_start(GTK_BOX(vbox), table, TRUE, TRUE, 5);
305
306 abox = gtk_dialog_get_action_area(GTK_DIALOG(s->w));
307
308 gtk_box_set_spacing(GTK_BOX(abox), 15);
309 gtk_box_set_homogeneous(GTK_BOX(abox), TRUE);
310 gtk_window_set_position(GTK_WINDOW(s->w), GTK_WIN_POS_MOUSE);
311
312 /* Close button */
313 button = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
314 gtk_widget_set_can_default(button, TRUE);
315 gtk_box_pack_start(GTK_BOX(abox), button, TRUE, TRUE, 0);
316 g_signal_connect(G_OBJECT(button), "clicked",
317 G_CALLBACK(delete_widget), s->w);
318
319 /* Replace All button */
320 button = gtk_button_new_with_mnemonic(_("Replace _All"));
321 gtk_widget_set_can_default(button, TRUE);
322 gtk_box_pack_start(GTK_BOX(abox), button, TRUE, TRUE, 0);
323 g_signal_connect(G_OBJECT(button), "clicked",
324 G_CALLBACK(replace_all_callback), s);
325
326 /* Replace button */
327 button = gtk_button_new_with_mnemonic(_("_Replace"));
328 gtk_widget_set_can_default(button, TRUE);
329 gtk_box_pack_start(GTK_BOX(abox), button, TRUE, TRUE, 0);
330 g_signal_connect(G_OBJECT(button), "clicked",
331 G_CALLBACK(replace_single_callback), s);
332 gtk_widget_set_sensitive(button, FALSE);
333 s->r_button = button;
334
335 /* Find button -- make this the default */
336 button = gtk_button_new_from_stock(GTK_STOCK_FIND);
337 gtk_widget_set_can_default(button, TRUE);
338 gtk_box_pack_start(GTK_BOX(abox), button, TRUE, TRUE, 0);
339 g_signal_connect(G_OBJECT(button), "clicked",
340 G_CALLBACK(replace_find_callback), s);
341 gtk_widget_grab_default(button);
342
343 gtk_widget_grab_focus(s->f_entry);
344 gtk_widget_show_all(s->w);
345
346 /* we need to block so that the search/replace
347 struct (above) doesn't drop out of scope */
348 gtk_main();
349 }
350
text_replace(GtkWidget * w,windata_t * vwin)351 void text_replace (GtkWidget *w, windata_t *vwin)
352 {
353 replace_string_dialog(vwin);
354 }
355
prep_prn_for_file_save(PRN * prn,int fmt)356 static int prep_prn_for_file_save (PRN *prn, int fmt)
357 {
358 const char *orig = gretl_print_get_buffer(prn);
359 char *modbuf;
360 int err;
361
362 err = maybe_post_process_buffer(orig, fmt, W_SAVE, &modbuf);
363
364 if (!err && modbuf != NULL) {
365 gretl_print_replace_buffer(prn, modbuf);
366 }
367
368 return err;
369 }
370
special_text_handler(windata_t * vwin,guint fmt,int what)371 static int special_text_handler (windata_t *vwin, guint fmt, int what)
372 {
373 int cmd = vwin->role;
374 PRN *prn = NULL;
375 int err = 0;
376
377 if (bufopen(&prn)) {
378 return 1;
379 }
380
381 gretl_print_set_format(prn, fmt);
382
383 if (cmd == SUMMARY) {
384 Summary *summ = (Summary *) vwin->data;
385
386 special_print_summary(summ, dataset, prn);
387 } else if (cmd == CORR || cmd == COVAR) {
388 VMatrix *corr = (VMatrix *) vwin->data;
389
390 special_print_vmatrix(corr, dataset, prn);
391 } else if (cmd == AFR) {
392 FITRESID *fr = (FITRESID *) vwin->data;
393
394 special_print_fit_resid(fr, dataset, prn);
395 } else if (cmd == FCAST) {
396 FITRESID *fr = (FITRESID *) vwin->data;
397
398 special_print_forecast(fr, dataset, prn);
399 } else if (cmd == COEFFINT) {
400 CoeffIntervals *cf = (CoeffIntervals *) vwin->data;
401
402 special_print_confints(cf, prn);
403 } else if (cmd == VIEW_MODEL) {
404 MODEL *pmod = (MODEL *) vwin->data;
405
406 if (pmod->errcode) {
407 err = pmod->errcode;
408 } else {
409 int wdigits = widget_get_int(vwin->text, "digits");
410 int dsave = get_gretl_digits();
411
412 if (wdigits > 0 && wdigits != dsave) {
413 set_gretl_digits(wdigits);
414 }
415 if (tex_format(prn)) {
416 err = tex_print_model(pmod, dataset,
417 get_tex_eqn_opt(),
418 prn);
419 } else {
420 /* RTF or CSV */
421 err = printmodel(pmod, dataset, OPT_NONE, prn);
422 }
423 set_gretl_digits(dsave);
424 }
425 } else if (cmd == VAR || cmd == VECM) {
426 GRETL_VAR *var = (GRETL_VAR *) vwin->data;
427
428 err = gretl_VAR_print(var, dataset, OPT_NONE, prn);
429 } else if (cmd == VAR_IRF || cmd == VAR_DECOMP) {
430 windata_t *parent = vwin->gretl_parent;
431
432 if (parent == NULL) {
433 err = E_DATA;
434 } else {
435 GRETL_VAR *var = (GRETL_VAR *) parent->data;
436 /* here active_var records preferred horizon */
437 int h = vwin->active_var;
438
439 if (cmd == VAR_IRF) {
440 gretl_VAR_print_all_impulse_responses(var, dataset, h, prn);
441 } else {
442 gretl_VAR_print_all_fcast_decomps(var, dataset, h, prn);
443 }
444 }
445 } else if (cmd == SYSTEM) {
446 equation_system *sys = (equation_system *) vwin->data;
447
448 err = gretl_system_print(sys, dataset, OPT_NONE, prn);
449 } else if (cmd == VIEW_MODELTABLE) {
450 err = special_print_model_table(prn);
451 }
452
453 if (!err && what == W_SAVE) {
454 err = prep_prn_for_file_save(prn, fmt);
455 }
456
457 if (err) {
458 gui_errmsg(err);
459 } else {
460 if (what == W_PREVIEW) {
461 /* TeX only: there's no RTF preview option */
462 view_latex(prn);
463 } else if (what == W_COPY) {
464 prn_to_clipboard(prn, fmt);
465 } else if (what == W_SAVE) {
466 int action;
467
468 if (fmt & GRETL_FORMAT_TEX) {
469 action = SAVE_TEX;
470 } else if (fmt & GRETL_FORMAT_CSV) {
471 action = EXPORT_CSV;
472 } else {
473 action = SAVE_RTF;
474 }
475
476 file_selector_with_parent(action, FSEL_DATA_PRN,
477 prn, vwin_toplevel(vwin));
478 }
479 }
480
481 gretl_print_destroy(prn);
482
483 return err;
484 }
485
window_tex_callback(GtkWidget * w,windata_t * vwin)486 void window_tex_callback (GtkWidget *w, windata_t *vwin)
487 {
488 const char *opts[] = {
489 N_("View"),
490 N_("Copy"),
491 N_("Save")
492 };
493 int opt;
494
495 if (vwin->role == VAR_IRF || vwin->role == VAR_DECOMP) {
496 if (vwin->gretl_parent == NULL) {
497 warnbox(_("Not available"));
498 gtk_widget_set_sensitive(w, FALSE);
499 return;
500 }
501 }
502
503 opt = radio_dialog("gretl: LaTeX", NULL, opts, 3, 0, 0,
504 vwin_toplevel(vwin));
505
506 if (opt >= 0) {
507 int fmt = GRETL_FORMAT_TEX;
508
509 if (vwin->role == VIEW_MODELTABLE) {
510 fmt |= GRETL_FORMAT_MODELTAB;
511
512 if (model_table_landscape()) {
513 fmt |= GRETL_FORMAT_LANDSCAPE;
514 }
515 }
516
517 special_text_handler(vwin, fmt, opt);
518 }
519 }
520
tex_format_code(GtkAction * action)521 static int tex_format_code (GtkAction *action)
522 {
523 const gchar *s = gtk_action_get_name(action);
524 int fmt = GRETL_FORMAT_TEX;
525
526 if (strstr(s, "Eqn")) {
527 fmt |= GRETL_FORMAT_EQN;
528 }
529
530 return fmt;
531 }
532
model_tex_view(GtkAction * action,gpointer data)533 void model_tex_view (GtkAction *action, gpointer data)
534 {
535 windata_t *vwin = (windata_t *) data;
536 int fmt = tex_format_code(action);
537
538 special_text_handler(vwin, fmt, W_PREVIEW);
539 }
540
model_tex_save(GtkAction * action,gpointer data)541 void model_tex_save (GtkAction *action, gpointer data)
542 {
543 windata_t *vwin = (windata_t *) data;
544 int fmt = tex_format_code(action);
545
546 special_text_handler(vwin, fmt, W_SAVE);
547 }
548
model_tex_copy(GtkAction * action,gpointer data)549 void model_tex_copy (GtkAction *action, gpointer data)
550 {
551 windata_t *vwin = (windata_t *) data;
552 int fmt = tex_format_code(action);
553
554 special_text_handler(vwin, fmt, W_COPY);
555 }
556
text_window_get_copy_buf(windata_t * vwin,int select)557 static gchar *text_window_get_copy_buf (windata_t *vwin, int select)
558 {
559 gchar *cpy = NULL;
560
561 if (select) {
562 GtkTextBuffer *buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(vwin->text));
563 GtkTextIter start, end;
564
565 if (gtk_text_buffer_get_selection_bounds(buf, &start, &end)) {
566 cpy = gtk_text_buffer_get_text(buf, &start, &end, FALSE);
567 }
568 } else {
569 cpy = textview_get_text(vwin->text);
570 }
571
572 return cpy;
573 }
574
multiple_formats_ok(windata_t * vwin)575 int multiple_formats_ok (windata_t *vwin)
576 {
577 int r = vwin->role;
578
579 if (r == SUMMARY || r == VAR_SUMMARY ||
580 r == ALL_SUMMARY || r == AFR ||
581 r == CORR || r == ALL_CORR ||
582 r == FCAST || r == COEFFINT ||
583 r == COVAR || r == VIEW_MODELTABLE ||
584 r == VAR || r == VECM ||
585 r == VAR_IRF || r == VAR_DECOMP) {
586 return 1;
587 } else if (r == VIEW_MODEL) {
588 MODEL *pmod = (MODEL *) vwin->data;
589
590 return !RQ_SPECIAL_MODEL(pmod);
591 } else {
592 return 0;
593 }
594 }
595
make_prn_for_buf(gchar * buf,int fmt,int action,int * err)596 static PRN *make_prn_for_buf (gchar *buf, int fmt, int action,
597 int *err)
598 {
599 char *modbuf = NULL;
600 PRN *prn = NULL;
601
602 if (action == W_SAVE) {
603 *err = maybe_post_process_buffer(buf, fmt, action, &modbuf);
604 }
605
606 if (*err) {
607 g_free(buf);
608 } else {
609 if (modbuf != NULL) {
610 prn = gretl_print_new_with_buffer(modbuf);
611 g_free(buf);
612 } else {
613 prn = gretl_print_new_with_buffer(buf);
614 }
615 if (prn == NULL) {
616 *err = E_ALLOC;
617 }
618 }
619
620 return prn;
621 }
622
623 /* copying text from gretl windows */
624
625 #define SPECIAL_FORMAT(f) ((f & GRETL_FORMAT_TEX) || \
626 (f & GRETL_FORMAT_RTF))
627
window_copy_or_save(windata_t * vwin,guint fmt,int action)628 static void window_copy_or_save (windata_t *vwin, guint fmt, int action)
629 {
630 gchar *buf = NULL;
631
632 if (vwin->role == VIEW_MODEL && fmt == GRETL_FORMAT_CSV) {
633 special_text_handler(vwin, fmt, action);
634 } else if (multiple_formats_ok(vwin) && SPECIAL_FORMAT(fmt)) {
635 special_text_handler(vwin, fmt, action);
636 } else if (fmt == GRETL_FORMAT_CSV || fmt == GRETL_FORMAT_TAB ||
637 fmt == GRETL_FORMAT_RTF) {
638 copy_vars_formatted(vwin, fmt, action);
639 } else if (fmt == GRETL_FORMAT_TXT || fmt == GRETL_FORMAT_RTF_TXT) {
640 buf = text_window_get_copy_buf(vwin, 0);
641 } else if (fmt == GRETL_FORMAT_SELECTION) {
642 buf = text_window_get_copy_buf(vwin, 1);
643 fmt = GRETL_FORMAT_TXT;
644 }
645
646 if (buf != NULL) {
647 /* handle the last two cases above */
648 PRN *prn;
649 int err = 0;
650
651 prn = make_prn_for_buf(buf, fmt, action, &err);
652
653 if (!err) {
654 if (action == W_COPY) {
655 prn_to_clipboard(prn, fmt);
656 } else {
657 /* saving to file */
658 int fcode = (fmt == GRETL_FORMAT_RTF_TXT)?
659 SAVE_RTF : SAVE_OUTPUT;
660
661 file_selector_with_parent(fcode, FSEL_DATA_PRN, prn,
662 vwin_toplevel(vwin));
663 }
664 gretl_print_destroy(prn);
665 }
666 }
667 }
668
window_copy(windata_t * vwin,guint fmt)669 void window_copy (windata_t *vwin, guint fmt)
670 {
671 window_copy_or_save(vwin, fmt, W_COPY);
672 }
673
window_save(windata_t * vwin,guint fmt)674 void window_save (windata_t *vwin, guint fmt)
675 {
676 window_copy_or_save(vwin, fmt, W_SAVE);
677 }
678
679 /* "native" printing from gretl windows */
680
window_print(GtkAction * action,windata_t * vwin)681 void window_print (GtkAction *action, windata_t *vwin)
682 {
683 gchar *buf, *selbuf = NULL;
684 const char *filename = NULL;
685 GtkTextBuffer *tbuf;
686 GtkTextIter start, end;
687
688 tbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(vwin->text));
689 buf = textview_get_text(vwin->text);
690
691 if (gtk_text_buffer_get_selection_bounds(tbuf, &start, &end)) {
692 selbuf = gtk_text_buffer_get_text(tbuf, &start, &end, FALSE);
693 }
694
695 if (vwin->role == EDIT_HANSL ||
696 vwin->role == VIEW_SCRIPT) {
697 const char *p = path_last_slash_const(vwin->fname);
698
699 if (p != NULL) {
700 filename = p + 1;
701 } else {
702 filename = vwin->fname;
703 }
704 }
705
706 print_window_content(buf, selbuf, filename, vwin);
707
708 g_free(buf);
709 g_free(selbuf);
710 }
711
712 #define U_MINUS(u,i) (u[i] == 0xE2 && u[i+1] == 0x88 && u[i+2] == 0x92)
713
714 /* check @s for Unicode minus signs (U+2212), and if any are found do an
715 in-place replacement with ASCII dashes
716 */
717
strip_unicode_minus(char * s)718 char *strip_unicode_minus (char *s)
719 {
720 unsigned char *u = (unsigned char *) s;
721 int i, n = strlen(s);
722 int got_minus = 0;
723
724 for (i=0; i<n-3; i++) {
725 if (U_MINUS(u, i)) {
726 got_minus = 1;
727 break;
728 }
729 }
730
731 if (got_minus) {
732 char *tmp = calloc(n, 1);
733
734 if (tmp != NULL) {
735 int j = 0;
736
737 for (i=0; i<n; i++) {
738 if (i < n - 3 && U_MINUS(u, i)) {
739 tmp[j++] = '-';
740 i += 2;
741 } else {
742 tmp[j++] = u[i];
743 }
744 }
745 strcpy(s, tmp);
746 free(tmp);
747 }
748 }
749
750 return s;
751 }
752
has_unicode_minus(const unsigned char * s)753 int has_unicode_minus (const unsigned char *s)
754 {
755 int i, n = strlen((const char *) s);
756 int has_minus = 0;
757
758 for (i=0; i<n-3; i++) {
759 if (U_MINUS(s, i)) {
760 has_minus = 1;
761 break;
762 }
763 }
764
765 return has_minus;
766 }
767
768 /* print buf to file, trying to ensure it's not messed up */
769
system_print_buf(const gchar * buf,FILE * fp)770 void system_print_buf (const gchar *buf, FILE *fp)
771 {
772 const char *p = buf;
773 int cbak = 0;
774
775 while (*p) {
776 if (*p == '\r') {
777 if (*(p+1) != '\n') {
778 fputc('\n', fp);
779 }
780 } else {
781 fputc(*p, fp);
782 }
783 cbak = *p;
784 p++;
785 }
786
787 /* ensure file ends with newline */
788 if (cbak != '\n') {
789 fputc('\n', fp);
790 }
791 }
792
793 /* Convert a buffer to DOS/Windows text format, optionally
794 adding minimal RTF formatting. This function does not
795 take charge of text re-encoding, it just handles CR + LF
796 endings and (optional) RTF monospaced font spec.
797 */
798
dosify_buffer(const char * buf,int format)799 static char *dosify_buffer (const char *buf, int format)
800 {
801 #ifdef G_OS_WIN32 /* alt font not working with lowriter */
802 const char *rtf_preamble = "{\\rtf1\r\n"
803 "{\\fonttbl{\\f0\\fnil\\fprq1\\fcharset1 Consolas{\\*\\falt Courier New};}}\r\n"
804 "\\f0\\fs18\r\n";
805 #else
806 const char *rtf_preamble = "{\\rtf1\r\n"
807 "{\\fonttbl{\\f0\\fnil\\fprq1\\fcharset1 Courier New;}}\r\n"
808 "\\f0\\fs18\r\n";
809 #endif
810 int extra = 0, nlines = 0;
811 int add_rtf = 0;
812 char *targ, *q;
813 const char *p;
814
815 if (buf == NULL || *buf == '\0') {
816 return NULL;
817 }
818
819 if (format == GRETL_FORMAT_RTF_TXT) {
820 add_rtf = 1;
821 }
822
823 p = buf;
824 while (*p) {
825 if (*p++ == '\n') nlines++;
826 }
827 extra = nlines + 1;
828
829 if (add_rtf) {
830 extra *= 5;
831 extra += strlen(rtf_preamble) + 5;
832 }
833
834 targ = malloc(strlen(buf) + extra);
835 if (targ == NULL) {
836 return NULL;
837 }
838
839 if (add_rtf) {
840 strcpy(targ, rtf_preamble);
841 q = targ + strlen(targ);
842 } else {
843 q = targ;
844 }
845
846 p = buf;
847
848 while (*p) {
849 int pplus = 1;
850 int nl = 0;
851
852 if (*p == '\r' && *(p+1) == '\n') {
853 nl = 1; pplus = 2;
854 } else if (*p == '\n') {
855 nl = 1;
856 }
857
858 if (nl) {
859 if (add_rtf) {
860 *q++ = '\\';
861 *q++ = 'p';
862 *q++ = 'a';
863 *q++ = 'r';
864 }
865 *q++ = '\r';
866 *q++ = '\n';
867 } else {
868 *q++ = *p;
869 }
870
871 p += pplus;
872 }
873
874 *q = '\0';
875
876 if (add_rtf) {
877 strcat(q, "}\r\n");
878 }
879
880 return targ;
881 }
882
883 #ifdef G_OS_WIN32
884
885 #define plain_text(f) (f & (GRETL_FORMAT_TXT | GRETL_FORMAT_CSV | GRETL_FORMAT_TAB))
886
want_bom(int fmt,int action)887 static int want_bom (int fmt, int action)
888 {
889 /* Note sure about this, but for now if we're saving
890 "plain text" to file in UTF-8, we'll leave it in
891 UTF-8 and prepend the UTF-8 BOM.
892 */
893 if (action == W_SAVE && fmt == GRETL_FORMAT_TXT) {
894 return 1;
895 } else {
896 return 0;
897 }
898 }
899
prepend_bom(const char * orig)900 static char *prepend_bom (const char *orig)
901 {
902 char *buf = malloc(strlen(orig) + 4);
903
904 if (buf != NULL) {
905 buf[0] = 0xEF;
906 buf[1] = 0xBB;
907 buf[2] = 0xBF;
908 buf[3] = 0;
909 strcat(buf, orig);
910 }
911
912 return buf;
913 }
914
915 #endif
916
maybe_post_process_buffer(const char * buf,int fmt,int action,char ** modbuf)917 int maybe_post_process_buffer (const char *buf, int fmt,
918 int action, char **modbuf)
919 {
920 int rtf_output = 0;
921 int utf8_coded = 0;
922 char *trbuf = NULL;
923 char *final = NULL;
924 int err = 0;
925
926 if (fmt & (GRETL_FORMAT_RTF | GRETL_FORMAT_RTF_TXT)) {
927 rtf_output = 1;
928 }
929
930 if (utf8_encoded(buf)) {
931 utf8_coded = 1;
932 }
933
934 if (rtf_output) {
935 /* When writing RTF, recode if required and ensure
936 CR + LF.
937 */
938 if (utf8_coded) {
939 trbuf = utf8_to_rtf(buf);
940 if (trbuf == NULL) {
941 err = E_ALLOC;
942 }
943 }
944 if (!err) {
945 if (trbuf != NULL) {
946 final = dosify_buffer(trbuf, fmt);
947 } else {
948 final = dosify_buffer(buf, fmt);
949 }
950 if (final == NULL) {
951 err = E_ALLOC;
952 }
953 }
954 goto finish;
955 }
956
957 #ifdef G_OS_WIN32
958 if (plain_text(fmt)) {
959 if (utf8_coded && want_bom(fmt, action)) {
960 trbuf = prepend_bom(buf);
961 if (trbuf == NULL) {
962 err = E_ALLOC;
963 }
964 }
965 if (!err && action == W_COPY) {
966 if (trbuf != NULL) {
967 final = dosify_buffer(trbuf, fmt);
968 } else {
969 final = dosify_buffer(buf, fmt);
970 }
971 if (final == NULL) {
972 err = E_ALLOC;
973 }
974 } else {
975 final = trbuf;
976 }
977 }
978 #endif
979
980 finish:
981
982 if (trbuf != NULL && trbuf != final) {
983 free(trbuf);
984 }
985
986 *modbuf = final;
987
988 return err;
989 }
990