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 /* helpfiles.c for gretl */
21
22 #include "gretl.h"
23 #include "textbuf.h"
24 #include "gretl_www.h"
25 #include "treeutils.h"
26 #include "dlgutils.h"
27 #include "menustate.h"
28 #include "toolbar.h"
29 #include "winstack.h"
30 #include "database.h"
31 #include "fncall.h"
32
33 #ifdef G_OS_WIN32
34 # include "gretlwin32.h"
35 #else
36 # include <sys/stat.h>
37 # include <sys/types.h>
38 # include <fcntl.h>
39 # include <unistd.h>
40 # include <dirent.h>
41 #endif
42
43 #define HDEBUG 0
44
45 #define cmdref_role(r) (r == CMD_HELP || r == CMD_HELP_EN)
46 #define funcref_role(r) (r == FUNC_HELP || r == FUNC_HELP_EN)
47
48 static int translated_cmdref;
49 static int translated_fnref;
50
51 static windata_t *real_do_help (int idx, int pos, int role);
52 static void en_help_callback (GtkWidget *w, windata_t *hwin);
53 static void helpwin_set_topic_index (windata_t *hwin, int idx);
54
55 /* searching stuff */
56 static void find_in_text (GtkWidget *button, GtkWidget *dialog);
57 static void find_in_listbox (GtkWidget *button, GtkWidget *dialog);
58 static void find_string_dialog (void (*findfunc)(), windata_t *vwin);
59 static gboolean real_find_in_text (GtkTextView *view, const gchar *s,
60 gboolean sensitive,
61 gboolean from_cursor,
62 gboolean search_all);
63 static gboolean real_find_in_listbox (windata_t *vwin, const gchar *s,
64 gboolean sensitive,
65 gboolean vnames);
66 static void vwin_finder_callback (GtkEntry *entry, windata_t *vwin);
67
68 static GtkWidget *find_dialog = NULL;
69 static GtkWidget *find_entry;
70 static char *needle;
71
72 enum {
73 /* don't collide with the enumeration in toolbar.c */
74 EN_ITEM = 255
75 };
76
77 static GretlToolItem help_tools[] = {
78 { N_("Larger"), GTK_STOCK_ZOOM_IN, G_CALLBACK(text_larger), 0},
79 { N_("Smaller"), GTK_STOCK_ZOOM_OUT, G_CALLBACK(text_smaller), 0},
80 { N_("Show English help"), GRETL_STOCK_EN, G_CALLBACK(en_help_callback), EN_ITEM }
81 };
82
83 static gint n_help_tools = G_N_ELEMENTS(help_tools);
84
85 struct gui_help_item {
86 int code;
87 char *string;
88 };
89
90 /* codes and strings for GUI help items other than
91 regular gretl commands */
92
93 static struct gui_help_item gui_help_items[] = {
94 { 0, "nothing" },
95 { GR_PLOT, "graphing" },
96 { GR_XY, "graphing" },
97 { GR_DUMMY, "factorized" },
98 { GR_XYZ, "controlled" },
99 { BXPLOT, "boxplots" },
100 { GR_BOX, "boxplots" },
101 { GR_FBOX, "boxplots" },
102 { GR_3D, "3-D" },
103 { ONLINE, "online" },
104 { EXPORT, "export" },
105 { SMPLBOOL, "sampling" },
106 { SMPLDUM, "sampling" },
107 { COMPACT, "compact" },
108 { EXPAND, "expand" },
109 { TDISAGG, "tdisagg" },
110 { VSETMISS, "missing" },
111 { GSETMISS, "missing" },
112 { GUI_HELP, "dialog" },
113 { GENR_RANDOM, "genrand" },
114 { SEED_RANDOM, "genseed" },
115 { KERNEL_DENSITY, "density" },
116 { HCCME, "hccme" },
117 { IRF_BOOT, "irfboot" },
118 { HTEST, "gui-htest" },
119 { HTESTNP, "gui-htest-np" },
120 { MODEL_RESTR, "restrict-model" },
121 { SYS_RESTR, "restrict-system" },
122 { VECM_RESTR, "restrict-vecm" },
123 { LAGS_DIALOG, "lags-dialog" },
124 { MINIBUF, "minibuffer" },
125 { VLAGSEL, "VAR-lagselect" },
126 { VAROMIT, "VAR-omit" },
127 { PANEL_MODE, "panel-mode" },
128 { PANEL_WLS, "panel-wls" },
129 { PANEL_B, "panel-between" },
130 { BOOTSTRAP, "bootstrap" },
131 { TRANSPOS, "transpos" },
132 { DATASORT, "datasort" },
133 { WORKDIR, "workdir" },
134 { DFGLS, "dfgls" },
135 { GPT_ADDLINE, "addline" },
136 { GPT_CURVE, "curve" },
137 { SAVE_SESSION, "save-session" },
138 { SAVE_CMD_LOG, "save-script" },
139 { BFGS_CONFIG, "bfgs-config" },
140 { SAVE_LABELS, "save-labels" }, /* FIXME */
141 { OPEN_LABELS, "add-labels" }, /* FIXME */
142 { COUNTMOD, "count-model" },
143 { REGLS, "regls" },
144 { REGLS_ADV, "regls-advanced" },
145 { BWFILTER, "bwfilter" },
146 { POLYWEIGHTS, "polyweights" },
147 { EMAFILTER, "ema-filter" },
148 { X12AHELP, "x12a" },
149 { MAILHELP, "mailer" },
150 { LOESS, "loess" },
151 { NADARWAT, "nadarwat" },
152 { CLUSTER, "cluster" },
153 { GUI_FUNCS, "gui-funcs" },
154 { MENU_ATTACH, "menu-attach" },
155 { REPROBIT, "reprobit" },
156 { DAILY_PURGE, "daily-purge" },
157 { PKG_FILES, "data-files" },
158 { PKG_DEPS, "pkg-depends" },
159 { EDITOR, "script-editor" },
160 { MIDAS_LIST, "MIDAS_list" },
161 { MIDAS_PARM, "MIDAS_parm" },
162 { PKGHELP, "packages" },
163 { DBNHELP, "dbnomics" },
164 { MAPHELP, "maps" },
165 { -1, NULL },
166 };
167
168 /* state the topic headings from the script help files so they
169 can be translated */
170
171 const char *intl_topics[] = {
172 N_("Dataset"),
173 N_("Estimation"),
174 N_("Graphs"),
175 N_("Prediction"),
176 N_("Printing"),
177 N_("Programming"),
178 N_("Statistics"),
179 N_("Tests"),
180 N_("Transformations"),
181 N_("Utilities")
182 };
183
184 /* Handle non-uniqueness of map from 'extra' command words to codes
185 (e.g. both GR_PLOT and GR_XY correspond to "graphing"). We want
186 the first code, to find the right place in the help file
187 when responding to a "context help" request.
188 */
189
gui_ci_to_index(int ci)190 static int gui_ci_to_index (int ci)
191 {
192 if (ci < NC) {
193 /* regular gretl command, no problem */
194 return ci;
195 } else {
196 int i, k, ret = ci;
197
198 for (i=1; gui_help_items[i].code > 0; i++) {
199 if (ci == gui_help_items[i].code) {
200 for (k=i-1; k>0; k--) {
201 /* back up the list so long as the word above
202 is the same as the current one */
203 if (!strcmp(gui_help_items[k].string,
204 gui_help_items[i].string)) {
205 ret = gui_help_items[k].code;
206 } else {
207 break;
208 }
209 }
210 return ret;
211 }
212 }
213 }
214
215 return -1;
216 }
217
218 /* Public because it's wanted in textbuf.c for getting
219 the help indices of certain items that are not actual
220 gretl commands.
221 */
222
extra_command_number(const char * s)223 int extra_command_number (const char *s)
224 {
225 int i;
226
227 for (i=1; gui_help_items[i].code > 0; i++) {
228 if (!strcmp(s, gui_help_items[i].string)) {
229 return gui_help_items[i].code;
230 }
231 }
232
233 return -1;
234 }
235
helpfile_init(void)236 void helpfile_init (void)
237 {
238 translated_cmdref = using_translated_helpfile(GRETL_CMDREF);
239 translated_fnref = using_translated_helpfile(GRETL_FUNCREF);
240 }
241
quoted_help_string(const char * s)242 char *quoted_help_string (const char *s)
243 {
244 const char *p, *q;
245
246 p = strchr(s, '"');
247 q = strrchr(s, '"');
248
249 if (p != NULL && q != NULL && q - p > 1) {
250 p++;
251 return g_strndup(p, q - p);
252 }
253
254 return g_strdup("Missing string");
255 }
256
gui_help_topic_index(const char * word)257 static int gui_help_topic_index (const char *word)
258 {
259 int h = gretl_command_number(word);
260
261 if (h == 0) {
262 h = extra_command_number(word);
263 }
264
265 return h;
266 }
267
268 enum {
269 STRING_COL,
270 POSITION_COL,
271 INDEX_COL,
272 NUM_COLS
273 };
274
help_tree_select_row(GtkTreeSelection * selection,windata_t * hwin)275 static void help_tree_select_row (GtkTreeSelection *selection,
276 windata_t *hwin)
277 {
278 GtkTreeIter iter;
279 GtkTreeModel *model;
280
281 if (!gtk_tree_selection_get_selected(selection, &model, &iter)) {
282 return;
283 }
284
285 if (!gtk_tree_model_iter_has_child(model, &iter)) {
286 int pos, idx;
287
288 gtk_tree_model_get(model, &iter,
289 POSITION_COL, &pos,
290 INDEX_COL, &idx,
291 -1);
292
293 if (idx != hwin->active_var) {
294 /* not already in position */
295 real_do_help(idx, pos, hwin->role);
296 }
297 }
298 }
299
get_section_iter(GtkTreeModel * model,const char * s,GtkTreeIter * iter)300 static int get_section_iter (GtkTreeModel *model, const char *s,
301 GtkTreeIter *iter)
302 {
303 gchar *sect;
304 int found = 0;
305
306 if (!gtk_tree_model_get_iter_first(model, iter)) {
307 return 0;
308 }
309
310 while (!found && gtk_tree_model_iter_next(model, iter)) {
311 if (gtk_tree_model_iter_has_child(model, iter)) {
312 gtk_tree_model_get(model, iter, STRING_COL, §, -1);
313 if (!strcmp(s, sect)) {
314 found = 1;
315 }
316 g_free(sect);
317 }
318 }
319
320 return found;
321 }
322
real_funcs_heading(const char * s)323 static const char *real_funcs_heading (const char *s)
324 {
325 if (!strcmp(s, "access")) {
326 return _("Accessors");
327 } else if (!strcmp(s, "straccess")) {
328 return _("Built-in strings");
329 } else if (!strcmp(s, "data-utils")) {
330 return _("Data utilities");
331 } else if (!strcmp(s, "math")) {
332 return _("Mathematical");
333 } else if (!strcmp(s, "transforms")) {
334 return _("Transformations");
335 } else if (!strcmp(s, "matrix")) {
336 return _("Matrix manipulation");
337 } else if (!strcmp(s, "linalg")) {
338 return _("Linear algebra");
339 } else if (!strcmp(s, "complex")) {
340 return _("Complex numbers");
341 } else if (!strcmp(s, "numerical")) {
342 return _("Numerical methods");
343 } else if (!strcmp(s, "probdist")) {
344 return _("Probability");
345 } else if (!strcmp(s, "panel")) {
346 return _("Panel data");
347 } else if (!strcmp(s, "calendar")) {
348 return _("Calendar");
349 } else if (!strcmp(s, "timeseries")) {
350 return _("Time-series");
351 } else if (!strcmp(s, "stats")) {
352 return _("Statistical");
353 } else if (!strcmp(s, "nonparam")) {
354 return _("Non-parametric");
355 } else if (!strcmp(s, "midas")) {
356 return _("MIDAS");
357 } else if (!strcmp(s, "sspace")) {
358 return _("State space");
359 } else if (!strcmp(s, "programming")) {
360 return _("Programming");
361 } else if (!strcmp(s, "strings")) {
362 return _("Strings");
363 } else if (!strcmp(s, "mpi")) {
364 return _("MPI");
365 } else {
366 return "??";
367 }
368 }
369
make_help_topics_tree(int role)370 static GtkTreeStore *make_help_topics_tree (int role)
371 {
372 const char *fname;
373 GtkTreeStore *store;
374 GtkTreeIter iter, parent;
375 gchar *s, *buf = NULL;
376 char word[32], sect[48];
377 const char *heading;
378 int pos = 0, idx = 0;
379 int err;
380
381 if (role == CMD_HELP) {
382 fname = helpfile_path(GRETL_CMDREF, 0, 0);
383 } else if (role == GUI_HELP) {
384 fname = helpfile_path(GRETL_GUI_HELP, 0, 0);
385 } else if (role == FUNC_HELP) {
386 fname = helpfile_path(GRETL_FUNCREF, 0, 0);
387 } else if (role == CMD_HELP_EN) {
388 fname = helpfile_path(GRETL_CMDREF, 0, 1);
389 } else if (role == GUI_HELP_EN) {
390 fname = helpfile_path(GRETL_GUI_HELP, 0, 1);
391 } else if (role == FUNC_HELP_EN) {
392 fname = helpfile_path(GRETL_FUNCREF, 0, 1);
393 } else {
394 return NULL;
395 }
396
397 err = gretl_file_get_contents(fname, &buf, NULL);
398 if (err) {
399 return NULL;
400 }
401
402 store = gtk_tree_store_new(NUM_COLS, G_TYPE_STRING,
403 G_TYPE_INT, G_TYPE_INT);
404
405 gtk_tree_store_append(store, &iter, NULL);
406 gtk_tree_store_set(store, &iter, STRING_COL, _("Index"),
407 POSITION_COL, 0, -1);
408
409 s = buf;
410
411 #if HDEBUG
412 fprintf(stderr, "*** processing %s ***\n", fname);
413 #endif
414
415 while (*s) {
416 if (*s == '\n' && *(s+1) == '#' &&
417 *(s+2) != '#' && *(s+2) != '\0') {
418 if (sscanf(s+2, "%31s %47s", word, sect) == 2) {
419 if (funcref_role(role)) {
420 heading = real_funcs_heading(sect);
421 } else {
422 heading = _(sect);
423 }
424 if (!get_section_iter(GTK_TREE_MODEL(store), heading, &parent)) {
425 gtk_tree_store_append(store, &parent, NULL);
426 gtk_tree_store_set(store, &parent,
427 STRING_COL, heading,
428 POSITION_COL, 0,
429 INDEX_COL, 0,
430 -1);
431 }
432 gtk_tree_store_append(store, &iter, &parent);
433 if (funcref_role(role)) {
434 ++idx;
435 } else if (role == GUI_HELP || role == GUI_HELP_EN) {
436 idx = gui_help_topic_index(word);
437 } else {
438 idx = gretl_command_number(word);
439 }
440 #if HDEBUG
441 fprintf(stderr, " %s: pos %d, idx, %d\n", word, pos+1, idx);
442 #endif
443 gtk_tree_store_set(store, &iter,
444 STRING_COL, word,
445 POSITION_COL, pos + 1,
446 INDEX_COL, idx,
447 -1);
448 }
449 }
450 s++;
451 pos++;
452 }
453
454 g_free(buf);
455
456 return store;
457 }
458
get_help_topics_tree(int role)459 static GtkTreeStore *get_help_topics_tree (int role)
460 {
461 static GtkTreeStore *cmd_tree;
462 static GtkTreeStore *gui_tree;
463 static GtkTreeStore *func_tree;
464 static GtkTreeStore *en_cmd_tree;
465 static GtkTreeStore *en_gui_tree;
466 static GtkTreeStore *en_func_tree;
467 GtkTreeStore **ptree = NULL;
468
469 if (role == CMD_HELP) {
470 ptree = &cmd_tree;
471 } else if (role == GUI_HELP) {
472 ptree = &gui_tree;
473 } else if (role == FUNC_HELP) {
474 ptree = &func_tree;
475 } else if (role == CMD_HELP_EN) {
476 ptree = &en_cmd_tree;
477 } else if (role == GUI_HELP_EN) {
478 ptree = &en_gui_tree;
479 } else if (role == FUNC_HELP_EN) {
480 ptree = &en_func_tree;
481 } else {
482 return NULL;
483 }
484
485 if (*ptree == NULL) {
486 *ptree = make_help_topics_tree(role);
487 }
488
489 return *ptree;
490 }
491
492 /* add a tree-style navigation pane to the left of the help index or
493 text */
494
add_help_navigator(windata_t * vwin,GtkWidget * hp)495 int add_help_navigator (windata_t *vwin, GtkWidget *hp)
496 {
497 GtkTreeStore *store;
498 GtkWidget *view, *sw;
499 GtkCellRenderer *renderer;
500 GtkTreeViewColumn *column;
501 GtkTreeSelection *select;
502
503 store = get_help_topics_tree(vwin->role);
504 if (store == NULL) {
505 return 1;
506 }
507
508 view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
509 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), FALSE);
510 g_object_set(view, "enable-tree-lines", TRUE, NULL);
511
512 renderer = gtk_cell_renderer_text_new();
513
514 column = gtk_tree_view_column_new_with_attributes("",
515 renderer,
516 "text", 0,
517 NULL);
518 gtk_tree_view_append_column(GTK_TREE_VIEW(view), column);
519
520 select = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
521 gtk_tree_selection_set_mode(select, GTK_SELECTION_SINGLE);
522 g_signal_connect(G_OBJECT(select), "changed",
523 G_CALLBACK(help_tree_select_row),
524 vwin);
525
526 sw = gtk_scrolled_window_new(NULL, NULL);
527 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
528 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
529 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
530 GTK_SHADOW_IN);
531 gtk_container_add(GTK_CONTAINER(sw), view);
532 gtk_paned_pack1(GTK_PANED(hp), sw, FALSE, TRUE);
533 gtk_widget_set_size_request(sw, 150, -1);
534 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(view));
535
536 g_object_set_data(G_OBJECT(vwin->text), "tview", view);
537
538 return 0;
539 }
540
help_attr_from_word(const char * word,int role,int col)541 static int help_attr_from_word (const char *word,
542 int role, int col)
543 {
544 GtkTreeModel *model;
545 GtkTreeIter iter, child;
546 gchar *s;
547 int attr = 0;
548
549 model = GTK_TREE_MODEL(get_help_topics_tree(role));
550
551 if (!model || !gtk_tree_model_get_iter_first(model, &iter)) {
552 return 0;
553 }
554
555 while (gtk_tree_model_iter_next(model, &iter)) {
556 if (gtk_tree_model_iter_children(model, &child, &iter)) {
557 while (1) {
558 gtk_tree_model_get(model, &child, STRING_COL, &s, -1);
559 if (!strcmp(s, word)) {
560 gtk_tree_model_get(model, &child, col, &attr, -1);
561 g_free(s);
562 return attr;
563 }
564 g_free(s);
565 if (!gtk_tree_model_iter_next(model, &child)) {
566 break;
567 }
568 }
569 }
570 }
571
572 return 0;
573 }
574
function_help_index_from_word(const char * word,int role)575 int function_help_index_from_word (const char *word, int role)
576 {
577 return help_attr_from_word(word, role, INDEX_COL);
578 }
579
function_help_pos_from_word(const char * word,int role)580 static int function_help_pos_from_word (const char *word, int role)
581 {
582 return help_attr_from_word(word, role, POSITION_COL);
583 }
584
help_pos_from_index(int idx,int role)585 static int help_pos_from_index (int idx, int role)
586 {
587 GtkTreeModel *model;
588 GtkTreeIter iter, child;
589 int pos, tidx;
590
591 model = GTK_TREE_MODEL(get_help_topics_tree(role));
592
593 if (!model || !gtk_tree_model_get_iter_first(model, &iter)) {
594 return 0;
595 }
596
597 while (gtk_tree_model_iter_next(model, &iter)) {
598 if (gtk_tree_model_iter_children(model, &child, &iter)) {
599 while (1) {
600 gtk_tree_model_get(model, &child, INDEX_COL, &tidx, -1);
601 if (tidx == idx) {
602 gtk_tree_model_get(model, &child, POSITION_COL, &pos, -1);
603 return pos;
604 }
605 if (!gtk_tree_model_iter_next(model, &child)) {
606 break;
607 }
608 }
609 }
610 }
611
612 return 0;
613 }
614
help_index_from_pos(int pos,int role)615 static int help_index_from_pos (int pos, int role)
616 {
617 GtkTreeModel *model;
618 GtkTreeIter iter, child;
619 int idx, tpos;
620
621 model = GTK_TREE_MODEL(get_help_topics_tree(role));
622
623 if (!model || !gtk_tree_model_get_iter_first(model, &iter)) {
624 return 0;
625 }
626
627 while (gtk_tree_model_iter_next(model, &iter)) {
628 if (gtk_tree_model_iter_children(model, &child, &iter)) {
629 while (1) {
630 gtk_tree_model_get(model, &child, POSITION_COL, &tpos, -1);
631 if (tpos == pos) {
632 gtk_tree_model_get(model, &child, INDEX_COL, &idx, -1);
633 return idx;
634 }
635 if (!gtk_tree_model_iter_next(model, &child)) {
636 break;
637 }
638 }
639 }
640 }
641
642 return 0;
643 }
644
help_iter_from_index(int idx,int role,GtkTreeIter * iter,GtkTreeIter * parent)645 static gboolean help_iter_from_index (int idx, int role,
646 GtkTreeIter *iter,
647 GtkTreeIter *parent)
648 {
649 GtkTreeModel *model;
650 int fnum;
651
652 model = GTK_TREE_MODEL(get_help_topics_tree(role));
653
654 if (!model || !gtk_tree_model_get_iter_first(model, parent)) {
655 return 0;
656 }
657
658 while (gtk_tree_model_iter_next(model, parent)) {
659 if (gtk_tree_model_iter_children(model, iter, parent)) {
660 while (1) {
661 gtk_tree_model_get(model, iter, INDEX_COL, &fnum, -1);
662 if (idx == fnum) {
663 return TRUE;
664 }
665 if (!gtk_tree_model_iter_next(model, iter)) {
666 break;
667 }
668 }
669 }
670 }
671
672 return FALSE;
673 }
674
en_help_callback(GtkWidget * w,windata_t * hwin)675 static void en_help_callback (GtkWidget *w, windata_t *hwin)
676 {
677 int idx = hwin->active_var;
678 int pos, role = 0;
679
680 if (hwin->role == CMD_HELP) {
681 role = CMD_HELP_EN;
682 } else if (hwin->role == GUI_HELP) {
683 role = GUI_HELP_EN;
684 } else if (hwin->role == FUNC_HELP) {
685 role = FUNC_HELP_EN;
686 } else {
687 return;
688 }
689
690 pos = help_pos_from_index(idx, role);
691
692 if (pos < 0 && role != GUI_HELP_EN) {
693 /* missed, but we can at least show the index page */
694 pos = 0;
695 }
696
697 real_do_help(idx, pos, role);
698 }
699
700 #if GTK_MAJOR_VERSION == 2
701
normalize_base(GtkWidget * w,gpointer p)702 static void normalize_base (GtkWidget *w, gpointer p)
703 {
704 gtk_widget_modify_base(w, GTK_STATE_SELECTED, NULL);
705 }
706
notify_string_not_found(GtkWidget * entry)707 void notify_string_not_found (GtkWidget *entry)
708 {
709 GdkColor color;
710
711 gdk_color_parse("red", &color);
712 gtk_widget_grab_focus(entry);
713 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
714 gtk_widget_modify_base(entry, GTK_STATE_SELECTED, &color);
715 g_signal_connect(G_OBJECT(entry), "changed",
716 G_CALLBACK(normalize_base), NULL);
717 }
718
719 #else /* GTK 3.0 */
720
normalize_style(GtkWidget * w,gpointer p)721 static void normalize_style (GtkWidget *w, gpointer p)
722 {
723 GtkStyleContext *context;
724
725 if (p == NULL) {
726 context = gtk_widget_get_style_context(w);
727 } else {
728 context = GTK_STYLE_CONTEXT(p);
729 }
730 gtk_style_context_remove_class(context, GTK_STYLE_CLASS_ERROR);
731 }
732
notify_string_not_found(GtkWidget * entry)733 void notify_string_not_found (GtkWidget *entry)
734 {
735 GtkStyleContext *context;
736
737 context = gtk_widget_get_style_context(entry);
738 gtk_style_context_add_class(context,
739 GTK_STYLE_CLASS_ERROR);
740 gtk_widget_grab_focus(entry);
741 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
742 g_signal_connect(G_OBJECT(entry), "changed",
743 G_CALLBACK(normalize_style), context);
744 }
745
746 #endif
747
748 #define help_index_ok(r) (r == CMD_HELP || \
749 r == CMD_HELP_EN || \
750 r == FUNC_HELP || \
751 r == FUNC_HELP_EN)
752
finder_key_handler(GtkEntry * entry,GdkEventKey * event,windata_t * vwin)753 static gboolean finder_key_handler (GtkEntry *entry, GdkEventKey *event,
754 windata_t *vwin)
755 {
756 guint keyval = event->keyval;
757
758 #ifdef OS_OSX
759 if (keyval == GDK_g && cmd_key(event)) {
760 /* Command-G: repeat search */
761 vwin_finder_callback(entry, vwin);
762 return TRUE;
763 }
764 #endif
765
766 if (keyval == GDK_Tab && help_index_ok(vwin->role) &&
767 vwin->active_var == 0) {
768 /* tab-completion in help index mode */
769 const gchar *s = gtk_entry_get_text(entry);
770
771 if (s != NULL && *s != '\0') {
772 const char *comp = NULL;
773
774 if (cmdref_role(vwin->role)) {
775 comp = gretl_command_complete(s);
776 } else if (funcref_role(vwin->role)) {
777 comp = gretl_function_complete(s);
778 }
779
780 if (comp != NULL) {
781 gtk_entry_set_text(entry, comp);
782 gtk_editable_set_position(GTK_EDITABLE(entry), -1);
783 }
784
785 return TRUE;
786 }
787 } else if (keyval == GDK_g && (event->state & GDK_CONTROL_MASK)) {
788 /* Ctrl-G: repeat search */
789 vwin_finder_callback(entry, vwin);
790 return TRUE;
791 }
792
793 return FALSE;
794 }
795
796 #define starts_topic(s) (s[0]=='\n' && s[1]=='#' && s[2]==' ')
797
798 /* apparatus for permitting the user to search across all the
799 "pages" in a help file
800 */
801
maybe_switch_page(const char * s,windata_t * hwin)802 static int maybe_switch_page (const char *s, windata_t *hwin)
803 {
804 const gchar *src, *hbuf;
805 int currpos, newpos = 0;
806 int wrapped = 0;
807 int k, n = strlen(s);
808 int ok = 0;
809
810 /* where are we in the help file right now? */
811 currpos = help_pos_from_index(hwin->active_var, hwin->role);
812 hbuf = (const gchar *) hwin->data;
813 k = currpos;
814 src = hbuf + k;
815
816 /* skip to start of next page */
817 while (*src != '\0') {
818 if (starts_topic(src)) {
819 break;
820 }
821 k++;
822 src++;
823 }
824
825 retry:
826
827 /* see if the search text can be found on a page other than
828 the current one; if so, we'll switch to it */
829
830 while (!ok && *src != '\0') {
831 if (starts_topic(src)) {
832 /* record page position */
833 newpos = k + 1;
834 } else if (wrapped && newpos == currpos) {
835 /* we got back to where we started */
836 break;
837 } else if (newpos != currpos && !strncmp(s, src, n)) {
838 /* found the text on a different page */
839 ok = 1;
840 }
841 k++;
842 src++;
843 }
844
845 if (!ok && !wrapped) {
846 /* start from the top */
847 src = hbuf;
848 newpos = k = 0;
849 wrapped = 1;
850 goto retry;
851 }
852
853 if (ok) {
854 /* text found: move to new position */
855 int idx = help_index_from_pos(newpos, hwin->role);
856 int en = (hwin->role == CMD_HELP_EN || hwin->role == FUNC_HELP_EN);
857
858 set_help_topic_buffer(hwin, newpos, en);
859 helpwin_set_topic_index(hwin, idx);
860 }
861
862 return ok;
863 }
864
all_lower_case(const char * s)865 static int all_lower_case (const char *s)
866 {
867 while (*s) {
868 if (tolower(*s) != *s) {
869 return 0;
870 }
871 s++;
872 }
873
874 return 1;
875 }
876
maybe_go_to_page(windata_t * vwin)877 static int maybe_go_to_page (windata_t *vwin)
878 {
879 gboolean ret = FALSE;
880 int idx, pos = 0;
881
882 if (funcref_role(vwin->role)) {
883 /* looking for a function */
884 idx = function_help_index_from_word(needle, vwin->role);
885 if (idx > 0) {
886 pos = help_pos_from_index(idx, vwin->role);
887 }
888 } else {
889 /* looking for a command */
890 idx = gretl_command_number(needle);
891 if (idx > 0) {
892 pos = help_pos_from_index(idx, vwin->role);
893 }
894 }
895
896 if (pos > 0) {
897 real_do_help(idx, pos, vwin->role);
898 ret = TRUE;
899 }
900
901 return ret;
902 }
903
904 /* respond to Enter key in the 'finder' entry */
905
vwin_finder_callback(GtkEntry * entry,windata_t * vwin)906 static void vwin_finder_callback (GtkEntry *entry, windata_t *vwin)
907 {
908 gboolean search_all = FALSE;
909 gboolean sensitive;
910 gboolean found;
911
912 needle = gtk_editable_get_chars(GTK_EDITABLE(entry), 0, -1);
913 if (needle == NULL || *needle == '\0') {
914 return;
915 }
916
917 sensitive = !all_lower_case(needle);
918
919 if (g_object_get_data(G_OBJECT(entry), "search-all")) {
920 if (vwin->role == DBNOMICS_TOP ||
921 vwin->role == DBNOMICS_DB ||
922 vwin->role == DBNOMICS_SERIES) {
923 dbnomics_search(needle, vwin);
924 return;
925 } else {
926 search_all = TRUE;
927 }
928 }
929
930 if (vwin->text != NULL) {
931 gboolean from_cursor = TRUE;
932
933 found = real_find_in_text(GTK_TEXT_VIEW(vwin->text), needle,
934 sensitive, from_cursor, search_all);
935 } else {
936 found = real_find_in_listbox(vwin, needle, sensitive, 0);
937 }
938
939 if (!found && (vwin->role == CMD_HELP ||
940 vwin->role == CMD_HELP_EN ||
941 vwin->role == FUNC_HELP ||
942 vwin->role == FUNC_HELP_EN)) {
943 found = maybe_go_to_page(vwin);
944 }
945
946 if (!found && search_all) {
947 if (maybe_switch_page(needle, vwin)) {
948 found = real_find_in_text(GTK_TEXT_VIEW(vwin->text), needle,
949 sensitive, TRUE, TRUE);
950 }
951 }
952
953 if (!found) {
954 notify_string_not_found(GTK_WIDGET(entry));
955 }
956 }
957
toggle_search_all_help(GtkComboBox * box,GtkWidget * entry)958 static void toggle_search_all_help (GtkComboBox *box, GtkWidget *entry)
959 {
960 gint i = gtk_combo_box_get_active(box);
961
962 if (i > 0) {
963 g_object_set_data(G_OBJECT(entry), "search-all", GINT_TO_POINTER(1));
964 } else {
965 g_object_steal_data(G_OBJECT(entry), "search-all");
966 }
967 }
968
finder_add_options(GtkWidget * hbox,GtkWidget * entry)969 static void finder_add_options (GtkWidget *hbox, GtkWidget *entry)
970 {
971 GtkWidget *combo = gtk_combo_box_text_new();
972
973 combo_box_append_text(combo, _("this page"));
974 combo_box_append_text(combo, _("all pages"));
975 gtk_box_pack_end(GTK_BOX(hbox), combo, FALSE, FALSE, 5);
976 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
977
978 g_signal_connect(G_OBJECT(combo), "changed",
979 G_CALLBACK(toggle_search_all_help),
980 entry);
981 }
982
toggle_search_this_help(GtkComboBox * box,GtkWidget * entry)983 static void toggle_search_this_help (GtkComboBox *box, GtkWidget *entry)
984 {
985 gint i = gtk_combo_box_get_active(box);
986
987 if (i > 0) {
988 g_object_steal_data(G_OBJECT(entry), "search-all");
989 } else {
990 g_object_set_data(G_OBJECT(entry), "search-all", GINT_TO_POINTER(1));
991 }
992 }
993
finder_add_dbn_options(windata_t * vwin,GtkWidget * hbox,GtkWidget * entry)994 static void finder_add_dbn_options (windata_t *vwin,
995 GtkWidget *hbox,
996 GtkWidget *entry)
997 {
998 GtkWidget *combo = gtk_combo_box_text_new();
999
1000 if (vwin->role == DBNOMICS_DB) {
1001 combo_box_append_text(combo, _("selected dataset"));
1002 } else if (vwin->role == DBNOMICS_SERIES) {
1003 combo_box_append_text(combo, _("this dataset"));
1004 } else {
1005 combo_box_append_text(combo, _("all DB.NOMICS"));
1006 }
1007 combo_box_append_text(combo, _("this window"));
1008 gtk_box_pack_end(GTK_BOX(hbox), combo, FALSE, FALSE, 5);
1009 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
1010
1011 g_object_set_data(G_OBJECT(entry), "search-all", GINT_TO_POINTER(1));
1012 g_signal_connect(G_OBJECT(combo), "changed",
1013 G_CALLBACK(toggle_search_this_help),
1014 entry);
1015 }
1016
finder_icon_press(GtkEntry * entry,GtkEntryIconPosition pos,GdkEvent * event,windata_t * vwin)1017 static void finder_icon_press (GtkEntry *entry,
1018 GtkEntryIconPosition pos,
1019 GdkEvent *event,
1020 windata_t *vwin)
1021 {
1022 vwin_finder_callback(entry, vwin);
1023 }
1024
add_finder_icon(windata_t * vwin,GtkWidget * entry)1025 static void add_finder_icon (windata_t *vwin, GtkWidget *entry)
1026 {
1027 gtk_entry_set_icon_from_stock(GTK_ENTRY(entry),
1028 GTK_ENTRY_ICON_SECONDARY,
1029 GTK_STOCK_FIND);
1030 gtk_entry_set_icon_activatable(GTK_ENTRY(entry),
1031 GTK_ENTRY_ICON_SECONDARY,
1032 TRUE);
1033 g_signal_connect(G_OBJECT(entry), "icon-press",
1034 G_CALLBACK(finder_icon_press),
1035 vwin);
1036 }
1037
1038 /* add a "search box" to the right of a viewer window's toolbar */
1039
vwin_add_finder(windata_t * vwin)1040 void vwin_add_finder (windata_t *vwin)
1041 {
1042 GtkWidget *entry;
1043 GtkWidget *hbox;
1044 int fwidth = 16;
1045
1046 hbox = gtk_widget_get_parent(vwin->mbar);
1047 vwin->finder = entry = gtk_entry_new();
1048
1049 if (vwin->role == CMD_HELP ||
1050 vwin->role == CMD_HELP_EN ||
1051 vwin->role == FUNC_HELP ||
1052 vwin->role == FUNC_HELP_EN) {
1053 finder_add_options(hbox, entry);
1054 } else if (vwin->role == DBNOMICS_TOP ||
1055 vwin->role == DBNOMICS_DB ||
1056 vwin->role == DBNOMICS_SERIES) {
1057 finder_add_dbn_options(vwin, hbox, entry);
1058 fwidth = 28;
1059 }
1060
1061 gtk_entry_set_width_chars(GTK_ENTRY(entry), fwidth);
1062 gtk_box_pack_end(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
1063 add_finder_icon(vwin, entry);
1064
1065 if (vwin->role == DBNOMICS_TOP ||
1066 vwin->role == VIEW_DBSEARCH ||
1067 vwin->role == DBNOMICS_SERIES ||
1068 vwin->role == DBNOMICS_DB) {
1069 maybe_fill_dbn_finder(vwin->finder);
1070 }
1071
1072 g_signal_connect(G_OBJECT(entry), "key-press-event",
1073 G_CALLBACK(finder_key_handler), vwin);
1074 g_signal_connect(G_OBJECT(entry), "activate",
1075 G_CALLBACK(vwin_finder_callback),
1076 vwin);
1077 }
1078
add_footer_close_button(GtkWidget * hbox)1079 static void add_footer_close_button (GtkWidget *hbox)
1080 {
1081 GtkWidget *img = gtk_image_new_from_stock(GRETL_STOCK_CLOSE,
1082 GTK_ICON_SIZE_MENU);
1083 GtkWidget *button = gtk_button_new();
1084
1085 gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
1086 gtk_container_add(GTK_CONTAINER(button), img);
1087 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 5);
1088 g_signal_connect_swapped(button, "clicked",
1089 G_CALLBACK(gtk_widget_hide),
1090 hbox);
1091 gtk_widget_show_all(button);
1092 }
1093
catch_footer_key(GtkWidget * w,GdkEventKey * event,GtkWidget * targ)1094 static gint catch_footer_key (GtkWidget *w, GdkEventKey *event,
1095 GtkWidget *targ)
1096 {
1097 if (event->keyval == GDK_Escape) {
1098 gtk_widget_hide(targ);
1099 return TRUE;
1100 } else {
1101 return FALSE;
1102 }
1103 }
1104
1105 /* Callback from "hide" signal on the footer finder: we
1106 want to turn the focus back onto the associated
1107 text widget
1108 */
1109
vwin_refocus_text(GtkWidget * w,windata_t * vwin)1110 static void vwin_refocus_text (GtkWidget *w, windata_t *vwin)
1111 {
1112 /* in case the prior string was not found, cancel the
1113 error indicator
1114 */
1115 #if GTK_MAJOR_VERSION == 2
1116 normalize_base(vwin->finder, NULL);
1117 #else
1118 normalize_style(vwin->finder, NULL);
1119 #endif
1120
1121 if (vwin_is_editing(vwin)) {
1122 /* let the text area regain focus */
1123 gtk_widget_grab_focus(vwin->text);
1124 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(vwin->text), TRUE);
1125 }
1126 }
1127
vwin_add_footer_finder(windata_t * vwin)1128 static void vwin_add_footer_finder (windata_t *vwin)
1129 {
1130 GtkWidget *hbox, *entry;
1131
1132 hbox = gtk_hbox_new(FALSE, 5);
1133 vwin->finder = entry = gtk_entry_new();
1134 gtk_entry_set_width_chars(GTK_ENTRY(entry), 20);
1135 add_finder_icon(vwin, entry);
1136
1137 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 10);
1138 add_footer_close_button(hbox);
1139 gtk_box_pack_end(GTK_BOX(vwin->vbox), hbox, FALSE, FALSE, 2);
1140
1141 g_signal_connect(G_OBJECT(entry), "key-press-event",
1142 G_CALLBACK(catch_footer_key), hbox);
1143 g_signal_connect(G_OBJECT(entry), "key-press-event",
1144 G_CALLBACK(finder_key_handler), vwin);
1145 g_signal_connect(G_OBJECT(entry), "activate",
1146 G_CALLBACK(vwin_finder_callback),
1147 vwin);
1148 g_signal_connect(G_OBJECT(hbox), "hide",
1149 G_CALLBACK(vwin_refocus_text),
1150 vwin);
1151
1152 gtk_widget_show_all(hbox);
1153 gtk_widget_grab_focus(entry);
1154 }
1155
1156 #define SHOW_FINDER(r) (r != GUI_HELP && r != GUI_HELP_EN)
1157 #define SHOW_EN_BUTTON(r) ((translated_cmdref && (r==CMD_HELP || r==GUI_HELP)) || \
1158 (translated_fnref && r == FUNC_HELP))
1159
set_up_helpview_menu(windata_t * hwin)1160 void set_up_helpview_menu (windata_t *hwin)
1161 {
1162 GretlToolItem *item = NULL;
1163 GtkWidget *hbox;
1164 int i;
1165
1166 hbox = gtk_hbox_new(FALSE, 0);
1167 hwin->mbar = gretl_toolbar_new(NULL);
1168
1169 for (i=0; i<n_help_tools; i++) {
1170 item = &help_tools[i];
1171 if (!SHOW_EN_BUTTON(hwin->role) && item->flag == EN_ITEM) {
1172 continue;
1173 }
1174 gretl_toolbar_insert(hwin->mbar, item, item->func, hwin, -1);
1175 }
1176
1177 gtk_box_pack_start(GTK_BOX(hwin->vbox), hbox, FALSE, FALSE, 0);
1178 gtk_box_pack_start(GTK_BOX(hbox), hwin->mbar, FALSE, FALSE, 0);
1179
1180 vwin_add_winlist(hwin);
1181
1182 if (SHOW_FINDER(hwin->role)) {
1183 vwin_add_finder(hwin);
1184 }
1185
1186 gtk_widget_show_all(hbox);
1187 }
1188
show_gui_help(int helpcode)1189 void show_gui_help (int helpcode)
1190 {
1191 int idx = gui_ci_to_index(helpcode);
1192 int pos, role = GUI_HELP;
1193
1194 /* try for GUI help first */
1195 pos = help_pos_from_index(idx, role);
1196
1197 if (pos <= 0 && translated_cmdref) {
1198 /* English GUI help? */
1199 role = GUI_HELP_EN;
1200 pos = help_pos_from_index(idx, role);
1201 }
1202
1203 if (pos <= 0) {
1204 /* command help? */
1205 role = CMD_HELP;
1206 pos = help_pos_from_index(idx, role);
1207 }
1208
1209 if (pos <= 0 && translated_cmdref) {
1210 /* English command help? */
1211 role = CMD_HELP_EN;
1212 pos = help_pos_from_index(idx, role);
1213 }
1214
1215 if (pos > 0) {
1216 real_do_help(idx, pos, role);
1217 } else {
1218 warnbox(_("Sorry, no help is available"));
1219 }
1220 }
1221
context_help(GtkWidget * widget,gpointer data)1222 static void context_help (GtkWidget *widget, gpointer data)
1223 {
1224 int helpcode = GPOINTER_TO_INT(data);
1225
1226 show_gui_help(helpcode);
1227 }
1228
context_help_button(GtkWidget * hbox,int helpcode)1229 GtkWidget *context_help_button (GtkWidget *hbox, int helpcode)
1230 {
1231 GtkWidget *button;
1232
1233 button = gtk_button_new_from_stock(GTK_STOCK_HELP);
1234 gtk_widget_set_can_default(button, TRUE);
1235 gtk_container_add(GTK_CONTAINER(hbox), button);
1236 gtk_button_box_set_child_secondary(GTK_BUTTON_BOX(hbox),
1237 button, TRUE);
1238
1239 if (helpcode >= 0) {
1240 /* passing helpcode < 0 is a way of reserving
1241 the callback for something special
1242 */
1243 g_signal_connect(G_OBJECT(button), "clicked",
1244 G_CALLBACK(context_help),
1245 GINT_TO_POINTER(helpcode));
1246 }
1247
1248 return button;
1249 }
1250
nullify_hwin(GtkWidget * w,windata_t ** phwin)1251 static gboolean nullify_hwin (GtkWidget *w, windata_t **phwin)
1252 {
1253 *phwin = NULL;
1254 return FALSE;
1255 }
1256
1257 /* sync the tree index view with the currently selected topic, if it's
1258 not already in sync */
1259
helpwin_set_topic_index(windata_t * hwin,int idx)1260 static void helpwin_set_topic_index (windata_t *hwin, int idx)
1261 {
1262 GtkWidget *w =
1263 g_object_get_data(G_OBJECT(hwin->text), "tview");
1264 GtkTreeView *view = GTK_TREE_VIEW(w);
1265
1266 hwin->active_var = idx;
1267
1268 if (view != NULL) {
1269 GtkTreeModel *model = gtk_tree_view_get_model(view);
1270 GtkTreeIter iter, parent;
1271 gboolean ok;
1272
1273 if (idx == 0) {
1274 ok = gtk_tree_model_get_iter_first(model, &iter);
1275 } else {
1276 ok = help_iter_from_index(idx, hwin->role, &iter,
1277 &parent);
1278 }
1279
1280 if (ok) {
1281 GtkTreeSelection *sel;
1282
1283 sel = gtk_tree_view_get_selection(view);
1284 if (!gtk_tree_selection_iter_is_selected(sel, &iter)) {
1285 GtkTreePath *path;
1286
1287 /* gtk_tree_view_collapse_all(view); should we? */
1288 gtk_tree_selection_select_iter(sel, &iter);
1289 path = gtk_tree_model_get_path(model, &iter);
1290 gtk_tree_view_expand_to_path(view, path);
1291 gtk_tree_view_set_cursor(view, path, NULL, FALSE);
1292 gtk_tree_path_free(path);
1293 }
1294 }
1295 }
1296 }
1297
real_do_help(int idx,int pos,int role)1298 static windata_t *real_do_help (int idx, int pos, int role)
1299 {
1300 static windata_t *gui_hwin;
1301 static windata_t *cmd_hwin;
1302 static windata_t *func_hwin;
1303 static windata_t *en_gui_hwin;
1304 static windata_t *en_cmd_hwin;
1305 static windata_t *en_func_hwin;
1306 windata_t *hwin = NULL;
1307 const char *fname = NULL;
1308 int en = 0;
1309
1310 if (pos < 0) {
1311 dummy_call();
1312 return NULL;
1313 }
1314
1315 #if HDEBUG
1316 fprintf(stderr, "real_do_help: idx=%d, pos=%d, role=%d\n",
1317 idx, pos, role);
1318 fprintf(stderr, "gui_hwin = %p\n", (void *) gui_hwin);
1319 fprintf(stderr, "cmd_hwin = %p\n", (void *) cmd_hwin);
1320 fprintf(stderr, "func_hwin = %p\n", (void *) func_hwin);
1321 fprintf(stderr, "en_gui_hwin = %p\n", (void *) en_gui_hwin);
1322 fprintf(stderr, "en_cmd_hwin = %p\n", (void *) en_cmd_hwin);
1323 fprintf(stderr, "en_func_hwin = %p\n", (void *) en_func_hwin);
1324 #endif
1325
1326 if (role == CMD_HELP) {
1327 hwin = cmd_hwin;
1328 fname = helpfile_path(GRETL_CMDREF, 0, 0);
1329 } else if (role == GUI_HELP) {
1330 hwin = gui_hwin;
1331 fname = helpfile_path(GRETL_GUI_HELP, 0, 0);
1332 } else if (role == FUNC_HELP) {
1333 hwin = func_hwin;
1334 fname = helpfile_path(GRETL_FUNCREF, 0, 0);
1335 } else if (role == CMD_HELP_EN) {
1336 hwin = en_cmd_hwin;
1337 fname = helpfile_path(GRETL_CMDREF, 0, 1);
1338 en = 1;
1339 } else if (role == GUI_HELP_EN) {
1340 hwin = en_gui_hwin;
1341 fname = helpfile_path(GRETL_GUI_HELP, 0, 1);
1342 en = 1;
1343 } else if (role == FUNC_HELP_EN) {
1344 hwin = en_func_hwin;
1345 fname = helpfile_path(GRETL_FUNCREF, 0, 1);
1346 en = 1;
1347 }
1348
1349 if (hwin != NULL) {
1350 gtk_window_present(GTK_WINDOW(hwin->main));
1351 } else {
1352 hwin = view_help_file(fname, role);
1353 if (hwin != NULL) {
1354 windata_t **phwin = NULL;
1355
1356 if (role == CMD_HELP) {
1357 cmd_hwin = hwin;
1358 phwin = &cmd_hwin;
1359 } else if (role == GUI_HELP) {
1360 gui_hwin = hwin;
1361 phwin = &gui_hwin;
1362 } else if (role == FUNC_HELP) {
1363 func_hwin = hwin;
1364 phwin = &func_hwin;
1365 } else if (role == CMD_HELP_EN) {
1366 en_cmd_hwin = hwin;
1367 phwin = &en_cmd_hwin;
1368 } else if (role == GUI_HELP_EN) {
1369 en_gui_hwin = hwin;
1370 phwin = &en_gui_hwin;
1371 } else if (role == FUNC_HELP_EN) {
1372 en_func_hwin = hwin;
1373 phwin = &en_func_hwin;
1374 }
1375
1376 g_signal_connect(G_OBJECT(hwin->main), "destroy",
1377 G_CALLBACK(nullify_hwin), phwin);
1378 }
1379 }
1380
1381 #if HDEBUG
1382 fprintf(stderr, "real_do_help: doing set_help_topic_buffer:\n"
1383 " hwin=%p, hcode=%d, pos=%d, role=%d\n",
1384 (void *) hwin, hcode, pos, role);
1385 #endif
1386
1387 if (hwin != NULL) {
1388 int ret = set_help_topic_buffer(hwin, pos, en);
1389
1390 if (ret >= 0) {
1391 helpwin_set_topic_index(hwin, idx);
1392 }
1393 }
1394
1395 return hwin;
1396 }
1397
display_text_help(GtkAction * action)1398 void display_text_help (GtkAction *action)
1399 {
1400 if (action != NULL) {
1401 const char *aname = gtk_action_get_name(action);
1402
1403 if (!strcmp(aname, "TextCmdRef")) {
1404 real_do_help(0, 0, CMD_HELP);
1405 } else if (!strcmp(aname, "FuncRef")) {
1406 real_do_help(0, 0, FUNC_HELP);
1407 } else if (!strcmp(aname, "PkgHelp")) {
1408 show_gui_help(PKGHELP);
1409 }
1410 } else {
1411 /* default: commands help */
1412 real_do_help(0, 0, CMD_HELP);
1413 }
1414 }
1415
1416 /* called from textbuf.c */
1417
command_help_callback(int idx,int en)1418 void command_help_callback (int idx, int en)
1419 {
1420 int role = (en)? CMD_HELP_EN : CMD_HELP;
1421 int pos = 0;
1422
1423 if (idx > NC) {
1424 /* a GUI-special help item */
1425 show_gui_help(idx);
1426 return;
1427 }
1428
1429 if (idx > 0) {
1430 pos = help_pos_from_index(idx, role);
1431 if (pos < 0 && !en && translated_cmdref) {
1432 /* no translated entry: fall back on English */
1433 role = CMD_HELP_EN;
1434 pos = help_pos_from_index(idx, role);
1435 }
1436 }
1437
1438 real_do_help(idx, pos, role);
1439 }
1440
1441 /* called from textbuf.c */
1442
function_help_callback(int idx,int en)1443 void function_help_callback (int idx, int en)
1444 {
1445 int role = (en)? FUNC_HELP_EN : FUNC_HELP;
1446 int pos = help_pos_from_index(idx, role);
1447
1448 real_do_help(idx, pos, role);
1449 }
1450
1451 /* below: must return > 0 to do anything useful */
1452
help_pos_from_string(const char * s,int * idx,int * role)1453 static int help_pos_from_string (const char *s, int *idx, int *role)
1454 {
1455 char word[16];
1456 int pos;
1457
1458 *word = '\0';
1459 strncat(word, s, 15);
1460
1461 *idx = gretl_command_number(word);
1462 pos = help_pos_from_index(*idx, *role);
1463
1464 if (pos <= 0 && translated_cmdref) {
1465 pos = help_pos_from_index(*idx, CMD_HELP_EN);
1466 if (pos > 0) {
1467 *role = CMD_HELP_EN;
1468 }
1469 }
1470
1471 if (pos <= 0) {
1472 /* try for function instead of command? */
1473 pos = function_help_pos_from_word(word, FUNC_HELP);
1474 if (pos > 0) {
1475 *role = FUNC_HELP;
1476 *idx = function_help_index_from_word(word, *role);
1477 } else if (translated_fnref) {
1478 pos = function_help_pos_from_word(word, FUNC_HELP_EN);
1479 if (pos > 0) {
1480 *role = FUNC_HELP_EN;
1481 *idx = function_help_index_from_word(word, *role);
1482 }
1483 }
1484 }
1485
1486 return pos;
1487 }
1488
interactive_script_help(GtkWidget * widget,GdkEventButton * b,windata_t * vwin)1489 gint interactive_script_help (GtkWidget *widget, GdkEventButton *b,
1490 windata_t *vwin)
1491 {
1492 if (!window_help_is_active(vwin)) {
1493 /* command help not activated */
1494 return FALSE;
1495 } else {
1496 gchar *text = NULL;
1497 int pos = -1;
1498 int idx = 0;
1499 int role = CMD_HELP;
1500 GtkTextBuffer *buf;
1501 GtkTextIter iter;
1502
1503 buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(vwin->text));
1504 gtk_text_buffer_get_iter_at_mark(buf, &iter,
1505 gtk_text_buffer_get_insert(buf));
1506
1507 if (gtk_text_iter_inside_word(&iter)) {
1508 GtkTextIter w_start, w_end;
1509
1510 w_start = iter;
1511 w_end = iter;
1512
1513 if (!gtk_text_iter_starts_word(&iter)) {
1514 gtk_text_iter_backward_word_start(&w_start);
1515 }
1516
1517 if (!gtk_text_iter_ends_word(&iter)) {
1518 gtk_text_iter_forward_word_end(&w_end);
1519 }
1520
1521 text = gtk_text_buffer_get_text(buf, &w_start, &w_end, FALSE);
1522
1523 /* dollar accessors */
1524 if (text != NULL) {
1525 GtkTextIter dstart = w_start;
1526 gchar *dtest = NULL;
1527
1528 if (gtk_text_iter_backward_char(&dstart)) {
1529 dtest = gtk_text_buffer_get_text(buf, &dstart,
1530 &w_start, FALSE);
1531 if (*dtest == '$') {
1532 gchar *s = g_strdup_printf("$%s", text);
1533
1534 g_free(text);
1535 text = s;
1536 }
1537 g_free(dtest);
1538 }
1539 }
1540 }
1541
1542 if (text != NULL && *text != '\0') {
1543 pos = help_pos_from_string(text, &idx, &role);
1544 }
1545
1546 unset_window_help_active(vwin);
1547 text_set_cursor(vwin->text, 0);
1548
1549 if (text != NULL && *text != '\0') {
1550 if (pos <= 0) {
1551 warnbox(_("Sorry, help not found"));
1552 } else {
1553 real_do_help(idx, pos, role);
1554 }
1555 }
1556
1557 g_free(text);
1558 }
1559
1560 return FALSE;
1561 }
1562
1563 /* First response to "help <param>" in GUI console, when given with no
1564 option: if we got a command word or function name, pop open a
1565 nicely formatted help window. If this function returns non-zero
1566 we'll fall back on the command-line help function.
1567 */
1568
gui_console_help(const char * param)1569 int gui_console_help (const char *param)
1570 {
1571 int idx = 0, role = CMD_HELP;
1572 int pos, err = 0;
1573
1574 pos = help_pos_from_string(param, &idx, &role);
1575
1576 if (pos <= 0) {
1577 err = 1;
1578 } else {
1579 real_do_help(idx, pos, role);
1580 }
1581
1582 return err;
1583 }
1584
text_find(gpointer unused,gpointer data)1585 void text_find (gpointer unused, gpointer data)
1586 {
1587 windata_t *vwin = (windata_t *) data;
1588
1589 if (vwin->finder != NULL) {
1590 GtkWidget *p = gtk_widget_get_parent(vwin->finder);
1591
1592 if (!gtk_widget_get_visible(p)) {
1593 gtk_widget_show_all(p);
1594 }
1595 gtk_widget_grab_focus(vwin->finder);
1596 gtk_editable_select_region(GTK_EDITABLE(vwin->finder),
1597 0, -1);
1598 } else if (vwin->flags & VWIN_USE_FOOTER) {
1599 vwin_add_footer_finder(vwin);
1600 } else {
1601 find_string_dialog(find_in_text, vwin);
1602 }
1603 }
1604
text_find_again(gpointer unused,gpointer data)1605 void text_find_again (gpointer unused, gpointer data)
1606 {
1607 windata_t *vwin = (windata_t *) data;
1608
1609 if (vwin->finder != NULL) {
1610 if (gtk_widget_get_visible(vwin->finder)) {
1611 g_signal_emit_by_name(G_OBJECT(vwin->finder), "activate", NULL);
1612 }
1613 } else if (find_dialog != NULL) {
1614 if (vwin == g_object_get_data(G_OBJECT(find_dialog), "windat")) {
1615 find_in_text(NULL, find_dialog);
1616 }
1617 }
1618 }
1619
listbox_find(gpointer unused,gpointer data)1620 void listbox_find (gpointer unused, gpointer data)
1621 {
1622 windata_t *vwin = (windata_t *) data;
1623
1624 if (vwin->finder != NULL) {
1625 gtk_widget_grab_focus(vwin->finder);
1626 gtk_editable_select_region(GTK_EDITABLE(vwin->finder),
1627 0, -1);
1628 } else {
1629 find_string_dialog(find_in_listbox, vwin);
1630 }
1631 }
1632
listbox_find_again(gpointer unused,gpointer data)1633 void listbox_find_again (gpointer unused, gpointer data)
1634 {
1635 windata_t *vwin = (windata_t *) data;
1636
1637 if (vwin->finder != NULL) {
1638 g_signal_emit_by_name(G_OBJECT(vwin->finder), "activate", NULL);
1639 } else if (find_dialog != NULL) {
1640 if (vwin == g_object_get_data(G_OBJECT(find_dialog), "windat")) {
1641 find_in_listbox(NULL, find_dialog);
1642 }
1643 }
1644 }
1645
close_find_dialog(GtkWidget * widget,gpointer data)1646 static gint close_find_dialog (GtkWidget *widget, gpointer data)
1647 {
1648 find_dialog = NULL;
1649 return FALSE;
1650 }
1651
string_match_pos(const char * haystack,const char * needle,gboolean sensitive,int start)1652 static int string_match_pos (const char *haystack, const char *needle,
1653 gboolean sensitive, int start)
1654 {
1655 int hlen = strlen(haystack);
1656 int nlen = strlen(needle);
1657 int pos, found;
1658
1659 for (pos = start; pos < hlen; pos++) {
1660 if (sensitive) {
1661 found = !strncmp(&haystack[pos], needle, nlen);
1662 } else {
1663 found = !g_ascii_strncasecmp(&haystack[pos], needle, nlen);
1664 }
1665 if (found) {
1666 return pos;
1667 }
1668 }
1669
1670 return -1;
1671 }
1672
1673 /* search for string @s in text buffer associated with @view */
1674
real_find_in_text(GtkTextView * view,const gchar * s,gboolean sensitive,gboolean from_cursor,gboolean search_all)1675 static gboolean real_find_in_text (GtkTextView *view, const gchar *s,
1676 gboolean sensitive,
1677 gboolean from_cursor,
1678 gboolean search_all)
1679 {
1680 GtkTextBuffer *buf;
1681 GtkTextIter iter, start, end;
1682 GtkTextMark *vis;
1683 int found = 0;
1684 int wrapped = 0;
1685 int n = strlen(s);
1686 gchar *got;
1687
1688 buf = gtk_text_view_get_buffer(view);
1689
1690 text_search_wrap:
1691
1692 if (from_cursor) {
1693 GtkTextIter sel_bound;
1694
1695 gtk_text_buffer_get_iter_at_mark(buf, &iter,
1696 gtk_text_buffer_get_insert(buf));
1697 gtk_text_buffer_get_iter_at_mark(buf, &sel_bound,
1698 gtk_text_buffer_get_selection_bound(buf));
1699 gtk_text_iter_order(&sel_bound, &iter);
1700 } else {
1701 gtk_text_buffer_get_iter_at_offset(buf, &iter, 0);
1702 }
1703
1704 start = end = iter;
1705
1706 if (!gtk_text_iter_forward_chars(&end, n)) {
1707 /* we're already at end of the buffer */
1708 if (from_cursor && !wrapped && !search_all) {
1709 from_cursor = FALSE;
1710 wrapped = 1;
1711 goto text_search_wrap;
1712 } else {
1713 return 0;
1714 }
1715 }
1716
1717 while (!found) {
1718 got = gtk_text_buffer_get_text(buf, &start, &end, FALSE);
1719 if (sensitive) {
1720 found = !strcmp(got, s);
1721 } else {
1722 found = !g_ascii_strcasecmp(got, s);
1723 }
1724 g_free(got);
1725 if (found || !gtk_text_iter_forward_char(&start) ||
1726 !gtk_text_iter_forward_char(&end)) {
1727 break;
1728 }
1729 }
1730
1731 if (found) {
1732 gtk_text_buffer_place_cursor(buf, &start);
1733 gtk_text_buffer_move_mark_by_name(buf, "selection_bound", &end);
1734 vis = gtk_text_buffer_create_mark(buf, "vis", &end, FALSE);
1735 gtk_text_view_scroll_to_mark(view, vis, 0.05, FALSE, 0, 0);
1736 } else if (from_cursor && !wrapped && !search_all) {
1737 /* try wrapping */
1738 from_cursor = FALSE;
1739 wrapped = 1;
1740 goto text_search_wrap;
1741 }
1742
1743 return found;
1744 }
1745
find_in_text(GtkWidget * button,GtkWidget * dialog)1746 static void find_in_text (GtkWidget *button, GtkWidget *dialog)
1747 {
1748 windata_t *vwin = g_object_get_data(G_OBJECT(dialog), "windat");
1749 gboolean found, sensitive;
1750
1751 needle = gtk_editable_get_chars(GTK_EDITABLE(find_entry), 0, -1);
1752 if (needle == NULL || *needle == '\0') {
1753 return;
1754 }
1755
1756 sensitive = !all_lower_case(needle);
1757
1758 found = real_find_in_text(GTK_TEXT_VIEW(vwin->text), needle,
1759 sensitive, TRUE, FALSE);
1760
1761 if (!found) {
1762 notify_string_not_found(find_entry);
1763 }
1764 }
1765
1766 static void
get_tree_model_haystack(GtkTreeModel * mod,GtkTreeIter * iter,int col,char * haystack)1767 get_tree_model_haystack (GtkTreeModel *mod, GtkTreeIter *iter, int col,
1768 char *haystack)
1769 {
1770 gchar *tmp = NULL;
1771
1772 gtk_tree_model_get(mod, iter, col, &tmp, -1);
1773 if (tmp != NULL) {
1774 strcpy(haystack, tmp);
1775 g_free(tmp);
1776 } else {
1777 *haystack = '\0';
1778 }
1779 }
1780
real_find_in_listbox(windata_t * vwin,const gchar * s,gboolean sensitive,gboolean vnames)1781 static gboolean real_find_in_listbox (windata_t *vwin,
1782 const gchar *s,
1783 gboolean sensitive,
1784 gboolean vnames)
1785 {
1786 int search_cols[4] = {0, 0, -1, -1};
1787 int minvar, wrapped = 0;
1788 char haystack[MAXLEN];
1789 char pstr[16];
1790 GtkTreeModel *model = NULL;
1791 GtkTreeIter iter;
1792 gboolean got_iter;
1793 int i, pos = -1;
1794
1795 /* first check that there's something to search */
1796 if (vwin->listbox != NULL) {
1797 model = gtk_tree_view_get_model(GTK_TREE_VIEW(vwin->listbox));
1798 }
1799 if (model == NULL) {
1800 return FALSE;
1801 }
1802
1803 /* if searching in the main gretl window, start on line 1 */
1804 minvar = (vwin == mdata)? 1 : 0;
1805
1806 /* first try to get the current line plus one as starting point */
1807 sprintf(pstr, "%d", vwin->active_var);
1808 got_iter = gtk_tree_model_get_iter_from_string(model, &iter, pstr);
1809 if (got_iter) {
1810 got_iter = gtk_tree_model_iter_next(model, &iter);
1811 }
1812
1813 if (!got_iter) {
1814 /* fallback: start from the top */
1815 got_iter = gtk_tree_model_get_iter_first(model, &iter);
1816 }
1817
1818 if (!got_iter) {
1819 /* failed totally, get out */
1820 return FALSE;
1821 }
1822
1823 if (vnames) {
1824 /* case-sensitive search for series names */
1825 search_cols[0] = 1; /* series name */
1826 search_cols[1] = -1; /* invalid */
1827 } else if (vwin == mdata) {
1828 search_cols[0] = 1; /* series name */
1829 search_cols[1] = 2; /* description */
1830 } else if (vwin->role == FUNC_FILES) {
1831 search_cols[0] = 0; /* package name */
1832 search_cols[1] = 4; /* description */
1833 search_cols[2] = 3; /* author */
1834 } else if (vwin->role == REMOTE_FUNC_FILES) {
1835 search_cols[0] = 0; /* package name */
1836 search_cols[1] = 4; /* description */
1837 search_cols[2] = 3; /* author */
1838 } else {
1839 /* databases, datafiles */
1840 search_cols[0] = 1; /* description */
1841 search_cols[1] = 0; /* filename */
1842 }
1843
1844 search_wrap:
1845
1846 while (pos < 0) {
1847 for (i=0; pos < 0 && search_cols[i] >= 0; i++) {
1848 get_tree_model_haystack(model, &iter, search_cols[i], haystack);
1849 pos = string_match_pos(haystack, needle, sensitive, 0);
1850 }
1851 if (pos >= 0 || !gtk_tree_model_iter_next(model, &iter)) {
1852 break;
1853 }
1854 }
1855
1856 if (pos < 0 && vwin->active_var > minvar && !wrapped) {
1857 /* try wrapping to start */
1858 gtk_tree_model_get_iter_first(model, &iter);
1859 if (minvar > 0 && !gtk_tree_model_iter_next(model, &iter)) {
1860 ; /* do nothing: there's only one line in the box */
1861 } else {
1862 wrapped = 1;
1863 goto search_wrap;
1864 }
1865 }
1866
1867 if (pos >= 0) {
1868 GtkTreePath *path = gtk_tree_model_get_path(model, &iter);
1869
1870 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vwin->listbox),
1871 path, NULL, FALSE);
1872 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(vwin->listbox),
1873 path, NULL, TRUE, 0.5, 0);
1874 vwin->active_var = tree_path_get_row_number(path);
1875 gtk_tree_path_free(path);
1876 }
1877
1878 return (pos >= 0);
1879 }
1880
1881 /* Given @targ, the name of a function package, try to find
1882 it in @vwin's listbox, and if found focus that row.
1883 Return TRUE if found, FALSE otherwise.
1884 */
1885
find_package_in_viewer(windata_t * vwin,const gchar * targ)1886 gboolean find_package_in_viewer (windata_t *vwin,
1887 const gchar *targ)
1888 {
1889 char haystack[MAXLEN];
1890 GtkTreeModel *model;
1891 GtkTreeIter iter;
1892 int pos = -1;
1893
1894 model = gtk_tree_view_get_model(GTK_TREE_VIEW(vwin->listbox));
1895
1896 if (model == NULL || !gtk_tree_model_get_iter_first(model, &iter)) {
1897 return FALSE;
1898 }
1899
1900 while (pos < 0) {
1901 get_tree_model_haystack(model, &iter, 0, haystack);
1902 pos = string_match_pos(haystack, targ, TRUE, 0);
1903 if (pos >= 0 || !gtk_tree_model_iter_next(model, &iter)) {
1904 break;
1905 }
1906 }
1907
1908 if (pos >= 0) {
1909 GtkTreePath *path = gtk_tree_model_get_path(model, &iter);
1910
1911 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(vwin->listbox),
1912 path, NULL, FALSE, 0, 0);
1913 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vwin->listbox),
1914 path, NULL, FALSE);
1915 vwin->active_var = tree_path_get_row_number(path);
1916 gtk_tree_path_free(path);
1917 }
1918
1919 return (pos >= 0);
1920 }
1921
1922 /* used for windows that do not have a built-in search entry,
1923 but which call the function find_string_dialog() */
1924
find_in_listbox(GtkWidget * w,GtkWidget * dialog)1925 static void find_in_listbox (GtkWidget *w, GtkWidget *dialog)
1926 {
1927 windata_t *vwin = g_object_get_data(G_OBJECT(dialog), "windat");
1928 gpointer vp;
1929 gboolean sensitive;
1930 gboolean vnames = FALSE;
1931 gboolean found;
1932
1933 if (needle != NULL) {
1934 g_free(needle);
1935 needle = NULL;
1936 }
1937
1938 needle = gtk_editable_get_chars(GTK_EDITABLE(find_entry), 0, -1);
1939 if (needle == NULL || *needle == '\0') {
1940 return;
1941 }
1942
1943 sensitive = !all_lower_case(needle);
1944
1945 /* are we confining the search to variable names? */
1946 vp = g_object_get_data(G_OBJECT(dialog), "vnames_only");
1947 if (vp != NULL) {
1948 vnames = GPOINTER_TO_INT(vp);
1949 if (vnames) {
1950 /* varname search is advertised as case-sensitive */
1951 sensitive = TRUE;
1952 }
1953 }
1954
1955 found = real_find_in_listbox(vwin, needle, sensitive, vnames);
1956
1957 if (!found) {
1958 notify_string_not_found(find_entry);
1959 }
1960 }
1961
cancel_find(GtkWidget * button,GtkWidget * dialog)1962 static void cancel_find (GtkWidget *button, GtkWidget *dialog)
1963 {
1964 if (find_dialog != NULL) {
1965 gtk_widget_destroy(dialog);
1966 find_dialog = NULL;
1967 }
1968 }
1969
parent_find(GtkWidget * finder,windata_t * caller)1970 static void parent_find (GtkWidget *finder, windata_t *caller)
1971 {
1972 GtkWidget *w = vwin_toplevel(caller);
1973
1974 if (w != NULL) {
1975 gtk_window_set_transient_for(GTK_WINDOW(finder), GTK_WINDOW(w));
1976 gtk_window_set_destroy_with_parent(GTK_WINDOW(finder), TRUE);
1977 }
1978 }
1979
toggle_vname_search(GtkToggleButton * tb,GtkWidget * w)1980 static void toggle_vname_search (GtkToggleButton *tb, GtkWidget *w)
1981 {
1982 if (gtk_toggle_button_get_active(tb)) {
1983 g_object_set_data(G_OBJECT(w), "vnames_only",
1984 GINT_TO_POINTER(1));
1985 } else {
1986 g_object_set_data(G_OBJECT(w), "vnames_only",
1987 GINT_TO_POINTER(0));
1988 }
1989 }
1990
maybe_find_again(GtkWidget * w,GdkEventKey * event,GtkWidget * button)1991 static gint maybe_find_again (GtkWidget *w, GdkEventKey *event,
1992 GtkWidget *button)
1993 {
1994 if ((event->state & GDK_CONTROL_MASK) &&
1995 (event->keyval == GDK_g || event->keyval == GDK_G)) {
1996 g_signal_emit_by_name(G_OBJECT(button), "clicked", NULL);
1997 return TRUE;
1998 } else {
1999 return FALSE;
2000 }
2001 }
2002
find_string_dialog(void (* findfunc)(),windata_t * vwin)2003 static void find_string_dialog (void (*findfunc)(), windata_t *vwin)
2004 {
2005 GtkWidget *parent;
2006 GtkWidget *label;
2007 GtkWidget *button;
2008 GtkWidget *vbox;
2009 GtkWidget *hbox;
2010
2011 if (find_dialog != NULL) {
2012 g_object_set_data(G_OBJECT(find_dialog), "windat", vwin);
2013 parent_find(find_dialog, vwin);
2014 gtk_window_present(GTK_WINDOW(find_dialog));
2015 return;
2016 }
2017
2018 parent = vwin->topmain != NULL ? vwin->topmain : vwin->main;
2019 find_dialog = gretl_dialog_new(_("gretl: find"), parent, 0);
2020 g_object_set_data(G_OBJECT(find_dialog), "windat", vwin);
2021
2022 g_signal_connect(G_OBJECT(find_dialog), "destroy",
2023 G_CALLBACK(close_find_dialog),
2024 find_dialog);
2025
2026 hbox = gtk_hbox_new(FALSE, 5);
2027 label = gtk_label_new(_(" Find what:"));
2028 gtk_widget_show(label);
2029 find_entry = gtk_entry_new();
2030
2031 if (needle != NULL) {
2032 gtk_entry_set_text(GTK_ENTRY(find_entry), needle);
2033 gtk_editable_select_region(GTK_EDITABLE(find_entry), 0, -1);
2034 }
2035
2036 g_signal_connect(G_OBJECT(find_entry), "activate",
2037 G_CALLBACK(findfunc), find_dialog);
2038 gtk_widget_show(find_entry);
2039 gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 5);
2040 gtk_box_pack_start(GTK_BOX(hbox), find_entry, TRUE, TRUE, 5);
2041 gtk_widget_show(hbox);
2042
2043 vbox = gtk_dialog_get_content_area(GTK_DIALOG(find_dialog));
2044
2045 gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 5);
2046
2047 if (vwin == mdata) {
2048 hbox = gtk_hbox_new(FALSE, 5);
2049 button = gtk_check_button_new_with_label(_("Variable names only (case sensitive)"));
2050 g_signal_connect(G_OBJECT(button), "toggled",
2051 G_CALLBACK(toggle_vname_search), find_dialog);
2052 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 5);
2053 gtk_widget_show_all(hbox);
2054 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
2055 }
2056
2057 hbox = gtk_dialog_get_action_area(GTK_DIALOG(find_dialog));
2058
2059 /* Close button */
2060 button = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
2061 gtk_container_add(GTK_CONTAINER(hbox), button);
2062 g_signal_connect(G_OBJECT(button), "clicked",
2063 G_CALLBACK(cancel_find), find_dialog);
2064 gtk_widget_show(button);
2065
2066 /* Find button */
2067 button = gtk_button_new_from_stock(GTK_STOCK_FIND);
2068 gtk_widget_set_can_default(button, TRUE);
2069 gtk_container_add(GTK_CONTAINER(hbox), button);
2070 g_signal_connect(G_OBJECT(button), "clicked",
2071 G_CALLBACK(findfunc), find_dialog);
2072 gtk_widget_grab_default(button);
2073 gtk_widget_show(button);
2074
2075 g_signal_connect(G_OBJECT(find_entry), "key-press-event",
2076 G_CALLBACK(maybe_find_again), button);
2077
2078 gtk_widget_grab_focus(find_entry);
2079 gtk_widget_show(find_dialog);
2080 }
2081
2082 enum {
2083 GRETL_GUIDE = 1,
2084 GRETL_REF,
2085 GNUPLOT_REF,
2086 X12A_REF,
2087 GRETL_KEYS,
2088 HANSL_PRIMER,
2089 PKGBOOK,
2090 GRETL_MPI,
2091 GRETL_SVM,
2092 GRETL_DBN,
2093 GRETL_GEO,
2094 GRETL_LP
2095 };
2096
get_writable_doc_path(char * path,const char * fname)2097 static int get_writable_doc_path (char *path, const char *fname)
2098 {
2099 static int sysdoc_writable = -1;
2100 static int userdoc_writable = -1;
2101 const char *gretldir = gretl_home();
2102 const char *dotdir = gretl_dotdir();
2103 FILE *fp;
2104 int err = 0;
2105
2106 #ifdef G_OS_WIN32
2107 sysdoc_writable = 0;
2108 #endif
2109
2110 if (sysdoc_writable == 1) {
2111 sprintf(path, "%sdoc%c%s", gretldir, SLASH, fname);
2112 return 0;
2113 } else if (userdoc_writable == 1) {
2114 sprintf(path, "%sdoc%c%s", dotdir, SLASH, fname);
2115 return 0;
2116 }
2117
2118 if (sysdoc_writable < 0) {
2119 sysdoc_writable = 0;
2120 sprintf(path, "%sdoc", gretldir);
2121 if (gretl_mkdir(path) == 0) {
2122 strcat(path, SLASHSTR);
2123 strcat(path, fname);
2124 fp = gretl_fopen(path, "w");
2125 if (fp != NULL) {
2126 sysdoc_writable = 1;
2127 fclose(fp);
2128 gretl_remove(path);
2129 }
2130 }
2131 }
2132
2133 if (!sysdoc_writable && userdoc_writable < 0) {
2134 /* can't write to 'sys' dir, user dir not tested yet */
2135 userdoc_writable = 0;
2136 sprintf(path, "%sdoc", dotdir);
2137 if (gretl_mkdir(path) == 0) {
2138 sprintf(path, "%sdoc%c%s", dotdir, SLASH, fname);
2139 fp = gretl_fopen(path, "w");
2140 if (fp != NULL) {
2141 userdoc_writable = 1;
2142 fclose(fp);
2143 gretl_remove(path);
2144 }
2145 }
2146 }
2147
2148 if (!sysdoc_writable && !userdoc_writable) {
2149 err = 1;
2150 }
2151
2152 return err;
2153 }
2154
get_x12a_doc_path(char * path,const char * fname)2155 static int get_x12a_doc_path (char *path, const char *fname)
2156 {
2157 const char *x12a = gretl_x12_arima();
2158 int ret = 0;
2159
2160 *path = '\0';
2161
2162 if (x12a != NULL && *x12a != '\0') {
2163 char *p;
2164
2165 strcpy(path, x12a);
2166 p = strrslash(path);
2167 if (p != NULL) {
2168 sprintf(p + 1, "docs%c%s", SLASH, fname);
2169 ret = 1;
2170 } else {
2171 *path = '\0';
2172 }
2173
2174 #if !defined(G_OS_WIN32) && !defined(OS_OSX)
2175 if (!ret) {
2176 /* using gretl x12a package? */
2177 if (gretl_x12_is_x13()) {
2178 sprintf(path, "/opt/x13as/docs/%s", fname);
2179 } else {
2180 sprintf(path, "/opt/x12arima/docs/%s", fname);
2181 }
2182 ret = 1;
2183 }
2184 #endif
2185 }
2186
2187 return ret;
2188 }
2189
2190 /* Get a language-specific query string, for asking
2191 the user whether a translation is preferred.
2192 */
2193
tr_query(const char * lang)2194 static const char *tr_query (const char *lang)
2195 {
2196 if (!strcmp(lang, "es")) {
2197 return "¿Mostrar traducción al español?";
2198 } else if (!strcmp(lang, "gl")) {
2199 return "Mostrar tradución ao galego?";
2200 } else if (!strcmp(lang, "it")) {
2201 return "Mostra traduzione in italiano?";
2202 } else if (!strcmp(lang, "pt")) {
2203 return "Mostrar portugues tradução?";
2204 } else if (!strcmp(lang, "ru")) {
2205 return "Показать перевод на русский язык?";
2206 } else {
2207 return NULL;
2208 }
2209 }
2210
2211 /* For @code giving the ID number of a doc resource and
2212 @lang identifying a language, return the filename of a
2213 language-specific version of the resource, or NULL if
2214 none is available.
2215 */
2216
have_translation(int code,const char * lang)2217 static const char *have_translation (int code, const char *lang)
2218 {
2219 gchar *ret = NULL;
2220
2221 if (code == HANSL_PRIMER) {
2222 if (!strcmp(lang, "ru")) {
2223 ret = "hansl-primer-ru.pdf";
2224 }
2225 } else if (code == GRETL_REF) {
2226 if (!strcmp(lang, "es")) {
2227 ret = "gretl-ref-es.pdf";
2228 } else if (!strcmp(lang, "gl")) {
2229 ret = "gretl-ref-gl.pdf";
2230 } else if (!strcmp(lang, "it")) {
2231 ret = "gretl-ref-it.pdf";
2232 } else if (!strcmp(lang, "pt")) {
2233 ret = "gretl-ref-pt.pdf";
2234 }
2235 }
2236
2237 return ret;
2238 }
2239
2240 /* Determine if we should show a translation of the
2241 doc resource indentified by @code. If so, return
2242 the required filename; if not, return NULL.
2243 */
2244
show_translation(int code)2245 static const char *show_translation (int code)
2246 {
2247 const char *fname = NULL;
2248 char lang[3] = {0};
2249
2250 #ifdef WIN32
2251 gchar *loc = g_win32_getlocale();
2252
2253 strncat(lang, loc, 2);
2254 if (loc != NULL) {
2255 fname = have_translation(code, lang);
2256 g_free(loc);
2257 }
2258 #elif defined(ENABLE_NLS)
2259 char *loc = setlocale(LC_MESSAGES, NULL);
2260
2261 if (loc != NULL) {
2262 strncat(lang, loc, 2);
2263 fname = have_translation(code, lang);
2264 }
2265 #endif
2266
2267 if (fname != NULL) {
2268 /* We have a translation, but does the user want it? */
2269 const char *msg = tr_query(lang);
2270 int resp = yes_no_dialog(NULL, msg, NULL);
2271
2272 if (resp != GRETL_YES) {
2273 fname = NULL;
2274 }
2275 }
2276
2277 return fname;
2278 }
2279
2280 /* @pref is the documentation preference registered in settings.c:
2281 0 = English, US letter
2282 1 = English, A4
2283 [2 = Translation, if available]
2284 */
2285
find_or_download_pdf(int code,int pref,char * fullpath)2286 static int find_or_download_pdf (int code, int pref, char *fullpath)
2287 {
2288 const char *guide_files[] = {
2289 "gretl-guide.pdf",
2290 "gretl-guide-a4.pdf"
2291 };
2292 const char *ref_files[] = {
2293 "gretl-ref.pdf",
2294 "gretl-ref-a4.pdf",
2295 };
2296 const char *kbd_files[] = {
2297 "gretl-keys.pdf",
2298 "gretl-keys-a4.pdf"
2299 };
2300 const char *primer_files[] = {
2301 "hansl-primer.pdf",
2302 "hansl-primer-a4.pdf",
2303 };
2304 const char *pkgbook_files[] = {
2305 "pkgbook.pdf",
2306 "pkgbook-a4.pdf"
2307 };
2308 const char *gretlMPI_files[] = {
2309 "gretl-mpi.pdf",
2310 "gretl-mpi-a4.pdf"
2311 };
2312 const char *gretlSVM_files[] = {
2313 "gretl-svm.pdf",
2314 "gretl-svm-a4.pdf"
2315 };
2316 const char *gretlLP_files[] = {
2317 "gretl-lpsolve.pdf",
2318 "gretl-lpsolve-a4.pdf"
2319 };
2320 const char *fname = NULL;
2321 int gotit = 0;
2322 int err = 0;
2323
2324 if (pref < 0 || pref > 2) {
2325 /* out of bounds */
2326 pref = 0;
2327 }
2328
2329 if (pref > 0) {
2330 /* Try offering a translation where available: currently only
2331 for the Gretl Reference and Hansl primer (Russian).
2332 */
2333 pref = 1;
2334 if (code == HANSL_PRIMER || code == GRETL_REF) {
2335 fname = show_translation(code);
2336 }
2337 }
2338
2339 #if 0
2340 fprintf(stderr, "HERE code=%d, pref=%d, fname %s\n",
2341 code, pref, fname != NULL ? fname : "TBD");
2342 #endif
2343
2344 if (fname != NULL) {
2345 /* we got a specific translation */
2346 goto next_step;
2347 }
2348
2349 if (code == GRETL_GUIDE) {
2350 fname = guide_files[pref];
2351 } else if (code == GRETL_REF) {
2352 fname = ref_files[pref];
2353 } else if (code == GRETL_KEYS) {
2354 fname = kbd_files[pref];
2355 } else if (code == HANSL_PRIMER) {
2356 fname = primer_files[pref];
2357 } else if (code == PKGBOOK) {
2358 fname = pkgbook_files[pref];
2359 } else if (code == GRETL_MPI) {
2360 fname = gretlMPI_files[pref];
2361 } else if (code == GRETL_SVM) {
2362 fname = gretlSVM_files[pref];
2363 } else if (code == GRETL_LP) {
2364 fname = gretlLP_files[pref];
2365 } else if (code == GNUPLOT_REF) {
2366 fname = "gnuplot.pdf";
2367 } else if (code == X12A_REF) {
2368 fname = gretl_x12_is_x13() ? "docX13AS.pdf" : "x12adocV03.pdf";
2369 } else if (code == GRETL_DBN) {
2370 fname = "dbnomics.pdf";
2371 sprintf(fullpath, "%sfunctions%cdbnomics%c%s",
2372 gretl_home(), SLASH, SLASH, fname);
2373 } else if (code == GRETL_GEO) {
2374 fname = "geoplot.pdf";
2375 sprintf(fullpath, "%sfunctions%cgeoplot%c%s",
2376 gretl_home(), SLASH, SLASH, fname);
2377 } else {
2378 return E_DATA;
2379 }
2380
2381 next_step:
2382
2383 fprintf(stderr, "pdf help: looking for %s\n", fname);
2384
2385 if (code != GRETL_DBN && code != GRETL_GEO) {
2386 /* is the file available in public dir? */
2387 sprintf(fullpath, "%sdoc%c%s", gretl_home(), SLASH, fname);
2388 }
2389
2390 err = gretl_test_fopen(fullpath, "r");
2391 if (!err) {
2392 gotit = 1;
2393 }
2394
2395 if (!gotit && code == X12A_REF) {
2396 get_x12a_doc_path(fullpath, fname);
2397 if (*fullpath != '\0') {
2398 err = gretl_test_fopen(fullpath, "r");
2399 if (!err) {
2400 gotit = 1;
2401 }
2402 }
2403 }
2404
2405 if (!gotit) {
2406 /* try in the user's dotdir? */
2407 if (code == GRETL_DBN) {
2408 sprintf(fullpath, "%sfunctions%cdbnomics%cdbnomics.pdf",
2409 gretl_dotdir(), SLASH, SLASH);
2410 } else if (code == GRETL_GEO) {
2411 sprintf(fullpath, "%sfunctions%cgeojson%cgeoplot.pdf",
2412 gretl_dotdir(), SLASH, SLASH);
2413 } else {
2414 sprintf(fullpath, "%sdoc%c%s", gretl_dotdir(), SLASH, fname);
2415 }
2416 err = gretl_test_fopen(fullpath, "r");
2417 if (!err) {
2418 gotit = 1;
2419 }
2420 }
2421
2422 if (!gotit) {
2423 if (code == GRETL_DBN) {
2424 /* try installing the dbnomics package */
2425 char *dlpath = NULL;
2426
2427 err = download_addon("dbnomics", &dlpath);
2428 if (!err) {
2429 /* .gfn -> .pdf */
2430 switch_ext(fullpath, dlpath, "pdf");
2431 free(dlpath);
2432 }
2433 } else {
2434 /* try downloading the manual file */
2435 err = get_writable_doc_path(fullpath, fname);
2436 if (!err) {
2437 err = retrieve_manfile(fname, fullpath);
2438 }
2439 }
2440 if (err) {
2441 const char *buf = gretl_errmsg_get();
2442
2443 if (*buf) {
2444 errbox(buf);
2445 } else {
2446 errbox(_("Failed to download file"));
2447 }
2448 }
2449 }
2450
2451 return err;
2452 }
2453
get_pdf_path(const char * name,char * fullpath)2454 int get_pdf_path (const char *name, char *fullpath)
2455 {
2456 int code = 0;
2457
2458 if (!strcmp(name, "gretl-lpsolve.pdf")) {
2459 code = GRETL_LP;
2460 } else if (!strcmp(name, "gretl-svm.pdf")) {
2461 code = GRETL_SVM;
2462 }
2463
2464 if (code > 0) {
2465 return find_or_download_pdf(code, 0, fullpath);
2466 } else {
2467 return 1;
2468 }
2469 }
2470
gretl_show_pdf(const char * fname,const char * option)2471 void gretl_show_pdf (const char *fname, const char *option)
2472 {
2473 #if defined(G_OS_WIN32)
2474 if (option != NULL) {
2475 win32_open_pdf(fname, option);
2476 } else {
2477 win32_open_file(fname);
2478 }
2479 #elif defined(OS_OSX)
2480 if (option != NULL) {
2481 osx_open_pdf(fname, option);
2482 } else {
2483 osx_open_file(fname);
2484 }
2485 #else
2486 gretl_fork("viewpdf", fname, option);
2487 #endif
2488 }
2489
display_pdf_help(GtkAction * action)2490 void display_pdf_help (GtkAction *action)
2491 {
2492 char fname[FILENAME_MAX];
2493 int err, code = GRETL_GUIDE;
2494
2495 if (action != NULL) {
2496 const char *aname = gtk_action_get_name(action);
2497
2498 if (!strcmp(aname, "PDFCmdRef")) {
2499 code = GRETL_REF;
2500 } else if (!strcmp(aname, "KbdRef")) {
2501 code = GRETL_KEYS;
2502 } else if (!strcmp(aname, "Primer")) {
2503 code = HANSL_PRIMER;
2504 } else if (!strcmp(aname, "Pkgbook")) {
2505 code = PKGBOOK;
2506 } else if (!strcmp(aname, "gretlMPI")) {
2507 code = GRETL_MPI;
2508 } else if (!strcmp(aname, "gretlSVM")) {
2509 code = GRETL_SVM;
2510 } else if (!strcmp(aname, "gretlDBN")) {
2511 code = GRETL_DBN;
2512 } else if (!strcmp(aname, "GeoplotDoc")) {
2513 code = GRETL_GEO;
2514 } else if (!strcmp(aname, "LpsolveDoc")) {
2515 code = GRETL_LP;
2516 }
2517 }
2518
2519 err = find_or_download_pdf(code, get_manpref(), fname);
2520
2521 if (!err) {
2522 gretl_show_pdf(fname, NULL);
2523 }
2524 }
2525
display_guide_chapter(const char * dest)2526 void display_guide_chapter (const char *dest)
2527 {
2528 char fname[FILENAME_MAX];
2529 int err;
2530
2531 err = find_or_download_pdf(GRETL_GUIDE, get_manpref(), fname);
2532
2533 #ifdef G_OS_WIN32
2534 if (!err) {
2535 gretl_show_pdf(fname, dest);
2536 }
2537 #elif defined(OS_OSX)
2538 if (!err) {
2539 gretl_show_pdf(fname, dest);
2540 }
2541 #else /* Linux */
2542 if (!err) {
2543 gchar *tmp = NULL;
2544
2545 if (strstr(viewpdf, "okular")) {
2546 /* special case: option stuck onto fname */
2547 tmp = g_strdup_printf("%s#%s", fname, dest);
2548 gretl_show_pdf(tmp, NULL);
2549 } else {
2550 if (strstr(viewpdf, "xpdf")) {
2551 tmp = g_strdup_printf("+%s", dest);
2552 } else if (strstr(viewpdf, "evince")) {
2553 tmp = g_strdup_printf("--named-dest=%s", dest);
2554 }
2555 gretl_show_pdf(fname, tmp);
2556 }
2557 g_free(tmp);
2558 }
2559 #endif
2560 }
2561
display_gnuplot_help(void)2562 void display_gnuplot_help (void)
2563 {
2564 char fname[FILENAME_MAX];
2565 int err;
2566
2567 err = find_or_download_pdf(GNUPLOT_REF, 0, fname);
2568
2569 if (!err) {
2570 gretl_show_pdf(fname, NULL);
2571 }
2572 }
2573
display_x12a_help(void)2574 void display_x12a_help (void)
2575 {
2576 char fname[FILENAME_MAX];
2577 int err;
2578
2579 err = find_or_download_pdf(X12A_REF, 0, fname);
2580
2581 if (!err) {
2582 gretl_show_pdf(fname, NULL);
2583 }
2584 }
2585