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 /* datafiles.c : for gretl */
21
22 #define COLL_DEBUG 0
23 #define GFN_DEBUG 0
24
25 #include "gretl.h"
26 #include "datafiles.h"
27 #include "database.h"
28 #include "filelists.h"
29 #include "gretl_www.h"
30 #include "menustate.h"
31 #include "fnsave.h"
32 #include "fncall.h"
33 #include "treeutils.h"
34 #include "selector.h"
35 #include "toolbar.h"
36 #include "winstack.h"
37 #include "fileselect.h"
38 #include "dlgutils.h"
39 #include "textbuf.h"
40
41 #include "gretl_xml.h"
42 #include "gretl_func.h"
43 #include "addons_utils.h"
44
45 #include <sys/stat.h>
46 #include <unistd.h>
47 #include <errno.h>
48
49 static GtkWidget *files_vbox (windata_t *vwin);
50 static GtkWidget *files_notebook (windata_t *vwin, int role);
51 static int populate_notebook_filelists (windata_t *vwin,
52 GtkWidget *notebook,
53 int role);
54 static gint populate_gfn_list (windata_t *vwin);
55
56 typedef struct _file_collection file_collection;
57
58 struct _file_collection {
59 char *path;
60 char *descfile;
61 char *title;
62 int type;
63 GtkWidget *listbox;
64 };
65
66 enum {
67 COLL_DATA,
68 COLL_PS,
69 COLL_MAX
70 };
71
72 enum {
73 PKG_ATTR_RES = 1 << 0,
74 PKG_ATTR_DOC = 1 << 1
75 };
76
77 #define GFN_DIRNAME_COL 5
78 #define GFN_FLAGS_COL 6
79
80 #define REMOTE_ACTION(c) (c == REMOTE_DB || \
81 c == REMOTE_FUNC_FILES || \
82 c == REMOTE_DATA_PKGS || \
83 c == REMOTE_ADDONS || \
84 c == DBNOMICS_DB || \
85 c == DBNOMICS_SERIES)
86
87 #define DBNOMICS_ACTION(c) (c == DBNOMICS_DB || c == DBNOMICS_SERIES)
88
89 static GList *collections[COLL_MAX];
90
role_to_index(int role)91 static int role_to_index (int role)
92 {
93 if (role == TEXTBOOK_DATA) {
94 return COLL_DATA;
95 } else if (role == PS_FILES) {
96 return COLL_PS;
97 } else {
98 return -1;
99 }
100 }
101
collections_for_role(int role)102 static GList *collections_for_role (int role)
103 {
104 int i = role_to_index(role);
105
106 if (i >= 0 && collections[i] != NULL) {
107 return g_list_first(collections[i]);
108 } else {
109 return NULL;
110 }
111 }
112
113 static void
114 read_fn_files_in_dir (GDir *dir, const char *path,
115 GtkListStore *store,
116 GtkTreeIter *iter,
117 int *nfn);
118
full_path(char * s1,const char * s2)119 static char *full_path (char *s1, const char *s2)
120 {
121 static char fpath[FILENAME_MAX];
122 int n = strlen(s1);
123
124 if (s1[n-1] == '.') {
125 s1[n-1] = '\0';
126 n--;
127 }
128
129 if (IS_SLASH(s1[n-1])) {
130 sprintf(fpath, "%s%s", s1, s2);
131 } else {
132 sprintf(fpath, "%s%c%s", s1, SLASH, s2);
133 }
134
135 #if COLL_DEBUG > 1
136 fprintf(stderr, "full_path: got '%s' from '%s' + '%s'\n",
137 fpath, s1, s2);
138 #endif
139
140 return fpath;
141 }
142
143 /* check for a few known, older, file collections whose
144 descriptions files do not conform to the now-standard
145 pattern
146 */
147
is_oldstyle_collection(file_collection * coll,int * err)148 static int is_oldstyle_collection (file_collection *coll, int *err)
149 {
150 const file_collection std_data[] = {
151 { "wooldridge", "jw_descriptions", "Wooldridge", COLL_DATA, NULL },
152 { "gujarati", "dg_descriptions", "Gujarati", COLL_DATA, NULL },
153 { "pwt56", "descriptions", "PWT 56", COLL_DATA, NULL }
154 };
155 const file_collection std_ps = {
156 "pwt56", "ps_descriptions", "PWT 56", COLL_PS, NULL
157 };
158 int i;
159
160 for (i=0; i<3; i++) {
161 if (strstr(coll->path, std_data[i].path) &&
162 !strcmp(coll->descfile, std_data[i].descfile)) {
163 coll->title = gretl_strdup(std_data[i].title);
164 if (coll->title == NULL) {
165 *err = E_ALLOC;
166 } else {
167 coll->type = COLL_DATA;
168 }
169 return 1;
170 }
171 }
172
173 if (strstr(coll->path, std_ps.path) &&
174 !strcmp(coll->descfile, std_ps.descfile)) {
175 coll->title = gretl_strdup(std_ps.title);
176 if (coll->title == NULL) {
177 *err = E_ALLOC;
178 } else {
179 coll->type = COLL_PS;
180 }
181 return 1;
182 }
183
184 return 0;
185 }
186
187 /* return non-zero only on fatal error */
188
get_title_from_descfile(file_collection * coll)189 static int get_title_from_descfile (file_collection *coll)
190 {
191 char line[64], title[24];
192 char *test;
193 FILE *fp;
194 int err = 0;
195
196 test = full_path(coll->path, coll->descfile);
197 fp = gretl_fopen(test, "rb");
198
199 if (fp != NULL && fgets(line, sizeof line, fp) != NULL) {
200 gretl_strstrip(line);
201 if (sscanf(line, "# %23[^:]", title) == 1) {
202 coll->title = gretl_strdup(title);
203 if (coll->title == NULL) {
204 err = E_ALLOC;
205 }
206 }
207 }
208
209 if (fp != NULL) {
210 fclose(fp);
211 }
212
213 return err;
214 }
215
free_file_collection(file_collection * coll)216 static void free_file_collection (file_collection *coll)
217 {
218 free(coll->path);
219 free(coll->descfile);
220 free(coll->title);
221 free(coll);
222 }
223
file_collection_new(const char * path,const char * descfile,int * err)224 static file_collection *file_collection_new (const char *path,
225 const char *descfile,
226 int *err)
227 {
228 file_collection *coll = malloc(sizeof *coll);
229
230 if (coll == NULL) {
231 *err = E_ALLOC;
232 return NULL;
233 }
234
235 coll->type = -1;
236 coll->title = NULL;
237 coll->path = gretl_strdup(path);
238 coll->descfile = gretl_strdup(descfile);
239
240 if (coll->path == NULL || coll->descfile == NULL) {
241 *err = E_ALLOC;
242 } else {
243 int os = is_oldstyle_collection(coll, err);
244
245 if (!*err && !os) {
246 if (strstr(coll->descfile, "ps_")) {
247 coll->type = COLL_PS;
248 } else {
249 coll->type = COLL_DATA;
250 }
251 *err = get_title_from_descfile(coll);
252 }
253 }
254
255 if (*err || coll->title == NULL) {
256 free_file_collection(coll);
257 coll = NULL;
258 }
259
260 return coll;
261 }
262
compare_colls(const void * a,const void * b)263 static int compare_colls (const void *a, const void *b)
264 {
265 const file_collection *ca = a;
266 const file_collection *cb = b;
267
268 if (!strcmp(ca->title, "Gretl")) {
269 return -1;
270 } else if (!strcmp(cb->title, "Gretl")) {
271 return 1;
272 } else {
273 return strcmp(ca->title, cb->title);
274 }
275 }
276
push_collection(file_collection * collection)277 static void push_collection (file_collection *collection)
278 {
279 int i = collection->type;
280
281 collections[i] = g_list_append(collections[i], collection);
282 }
283
destroy_file_collections(void)284 void destroy_file_collections (void)
285 {
286 GList *L;
287 int i;
288
289 for (i=0; i<COLL_MAX; i++) {
290 if (collections[i] != NULL) {
291 L = g_list_first(collections[i]);
292 while (L) {
293 free_file_collection(L->data);
294 L = L->next;
295 }
296 g_list_free(collections[i]);
297 collections[i] = NULL;
298 }
299 }
300 }
301
sort_files_stack(int role)302 static void sort_files_stack (int role)
303 {
304 int i = role_to_index(role);
305
306 if (i >= 0 && collections[i] != NULL) {
307 collections[i] = g_list_sort(collections[i], compare_colls);
308 }
309 }
310
311 /* Returns the number of file collections found and pushed;
312 writes non-zero to @err if something show-stopping
313 occurs
314 */
315
get_file_collections_from_dir(const char * path,GDir * dir,int * err)316 static int get_file_collections_from_dir (const char *path, GDir *dir,
317 int *err)
318 {
319 file_collection *coll;
320 const gchar *dname;
321 int n = 0;
322
323 while (!*err && (dname = g_dir_read_name(dir))) {
324 /* we're looking for a filename that ends with "descriptions" */
325 if (strstr(dname, "descriptions")) {
326 size_t len = strlen(dname);
327
328 #if COLL_DEBUG
329 fprintf(stderr, " %s: looking at '%s'\n", path, dname);
330 #endif
331 if (!strcmp(dname + len - 12, "descriptions")) {
332 coll = file_collection_new(path, dname, err);
333 if (coll != NULL) {
334 push_collection(coll);
335 n++;
336 }
337 }
338 }
339 }
340
341 return n;
342 }
343
dont_go_there(const char * s)344 static int dont_go_there (const char *s)
345 {
346 int ret = 0;
347
348 if (!strcmp(s, "..") || strstr(s, ".inp") || strstr(s, ".gdt") ||
349 strstr(s, ".gretl") || strstr(s, ".hdr")) {
350 ret = 1;
351 }
352
353 return ret;
354 }
355
356 /* Returns the number of collections found; @err is set to
357 non-zero only if something show-stopping occurs
358 */
359
seek_file_collections(const char * basedir,SearchType stype,int * err)360 static int seek_file_collections (const char *basedir,
361 SearchType stype,
362 int *err)
363 {
364 gchar *path = NULL;
365 GDir *topdir;
366 const char *dname;
367 int n_coll = 0;
368
369 #if COLL_DEBUG
370 fprintf(stderr, "*** seek_file_collections: basedir='%s', type=%d\n",
371 basedir, stype);
372 #endif
373
374 if (stype == DATA_SEARCH) {
375 path = g_strdup_printf("%sdata", basedir);
376 } else if (stype == SCRIPT_SEARCH) {
377 path = g_strdup_printf("%sscripts", basedir);
378 } else {
379 /* USER_SEARCH */
380 path = g_strdup(basedir);
381 trim_slash(path);
382 }
383
384 topdir = gretl_opendir(path);
385 if (topdir == NULL) {
386 g_free(path);
387 return 0;
388 }
389
390 #if COLL_DEBUG
391 fprintf(stderr, "*** seek_file_collections: path='%s'\n", path);
392 #endif
393
394 while (!*err && (dname = g_dir_read_name(topdir))) {
395 if (!dont_go_there(dname)) {
396 gchar *subpath;
397 GDir *subdir;
398
399 #if COLL_DEBUG > 1
400 fprintf(stderr, " dname = '%s'\n", dname);
401 #endif
402 subpath = g_build_path("/", path, dname, NULL);
403 subdir = gretl_opendir(subpath);
404 if (subdir != NULL) {
405 #if COLL_DEBUG
406 fprintf(stderr, " trying in subdir '%s'\n", subpath);
407 #endif
408 n_coll += get_file_collections_from_dir(subpath, subdir, err);
409 #if COLL_DEBUG
410 if (*err) {
411 fprintf(stderr, " result: err = %d\n", *err);
412 }
413 #endif
414 g_dir_close(subdir);
415 }
416 g_free(subpath);
417 }
418 }
419
420 g_dir_close(topdir);
421 g_free(path);
422
423 #if COLL_DEBUG
424 fprintf(stderr, "*** found %d collections\n", n_coll);
425 #endif
426
427 return n_coll;
428 }
429
430 #if COLL_DEBUG
431
print_collection(const file_collection * coll)432 static void print_collection (const file_collection *coll)
433 {
434 fprintf(stderr, "path = '%s'\n", coll->path);
435 fprintf(stderr, "descfile = '%s'\n", coll->descfile);
436 if (coll->title != NULL && *coll->title != '\0') {
437 fprintf(stderr, "title = '%s'\n", coll->title);
438 }
439 }
440
print_collections(int role)441 static void print_collections (int role)
442 {
443 GList *L;
444 int i;
445
446 if (role == TEXTBOOK_DATA) {
447 fputs("\n*** Data collections:\n", stderr);
448 } else {
449 fputs("\n*** Script collections:\n", stderr);
450 }
451
452 for (i=0; i<COLL_MAX; i++) {
453 L = g_list_first(collections[i]);
454 while (L) {
455 print_collection(L->data);
456 L = L->next;
457 }
458 }
459 }
460
461 #endif /* COLL_DEBUG */
462
build_file_collections(int role)463 static int build_file_collections (int role)
464 {
465 static int built;
466 static int err;
467
468 if (!built && !err) {
469 const char *wd;
470 int derr[3] = {0};
471 int serr[3] = {0};
472 int uerr[2] = {0};
473 int nd = 0;
474 int ns = 0;
475 int nu = 0;
476 int i;
477
478 gretl_error_clear();
479
480 nd += seek_file_collections(gretl_home(), DATA_SEARCH, &derr[0]);
481 ns += seek_file_collections(gretl_home(), SCRIPT_SEARCH, &serr[0]);
482 #ifdef OS_OSX
483 nd += seek_file_collections(gretl_app_support_dir(), DATA_SEARCH, &derr[1]);
484 ns += seek_file_collections(gretl_app_support_dir(), SCRIPT_SEARCH, &serr[1]);
485 #else
486 nd += seek_file_collections(gretl_dotdir(), DATA_SEARCH, &derr[1]);
487 ns += seek_file_collections(gretl_dotdir(), SCRIPT_SEARCH, &serr[1]);
488 #endif
489
490 nu += seek_file_collections(gretl_workdir(), USER_SEARCH, &uerr[0]);
491 wd = maybe_get_default_workdir();
492 if (wd != NULL) {
493 nu += seek_file_collections(wd, USER_SEARCH, &uerr[1]);
494 nd += seek_file_collections(wd, DATA_SEARCH, &derr[2]);
495 ns += seek_file_collections(wd, SCRIPT_SEARCH, &serr[2]);
496 }
497
498 for (i=0; i<3; i++) {
499 /* help to diagnose any errors? */
500 if (derr[i]) {
501 fprintf(stderr, "data seek %d gave error %d\n", i+1, derr[i]);
502 }
503 if (serr[i]) {
504 fprintf(stderr, "script seek %d gave error %d\n", i+1, serr[i]);
505 }
506 if (i < 2 && uerr[i]) {
507 fprintf(stderr, "user file seek %d gave error %d\n", i+1, uerr[i]);
508 }
509 }
510 if (role == TEXTBOOK_DATA) {
511 if (nd == 0) {
512 gretl_errmsg_ensure("file_collections: no data files found");
513 errbox(gretl_errmsg_get());
514 err = 1;
515 }
516 } else if (role == PS_FILES) {
517 if (ns == 0) {
518 gretl_errmsg_ensure("file_collections: no script files found");
519 errbox(gretl_errmsg_get());
520 err = 1;
521 }
522 }
523 if (!err) {
524 if (nd > 0) {
525 sort_files_stack(TEXTBOOK_DATA);
526 }
527 if (ns > 0) {
528 sort_files_stack(PS_FILES);
529 }
530 }
531 built = 1;
532 }
533
534 #if COLL_DEBUG
535 print_collections(TEXTBOOK_DATA);
536 print_collections(PS_FILES);
537 #endif
538
539 return err;
540 }
541
strip_extension(char * s)542 char *strip_extension (char *s)
543 {
544 char *p = strstr(s, ".tar.gz");
545
546 if (p != NULL) {
547 char *q = strstr(s, "_data");
548
549 if (q != NULL) {
550 *q = '\0';
551 } else {
552 *p = '\0';
553 }
554 } else {
555 p = strrchr(s, '.');
556
557 if (p != NULL &&
558 (!strcmp(p, ".gdt") || !strcmp(p, ".inp") ||
559 !strcmp(p, ".bin") || !strcmp(p, ".gfn") ||
560 !strcmp(p, ".bn7") || !strcmp(p, ".zip"))) {
561 *p = '\0';
562 }
563 }
564
565 return s;
566 }
567
validate_desc_strings(const char * s1,const char * s2,const char * s3)568 static int validate_desc_strings (const char *s1,
569 const char *s2,
570 const char *s3)
571 {
572 int err = 0;
573
574 if (!g_utf8_validate(s1, -1, NULL)) {
575 err = E_DATA;
576 } else if (!g_utf8_validate(s2, -1, NULL)) {
577 err = E_DATA;
578 } else if (s3 != NULL && !g_utf8_validate(s3, -1, NULL)) {
579 err = E_DATA;
580 }
581
582 return err;
583 }
584
read_file_descriptions(windata_t * win,gpointer p)585 static int read_file_descriptions (windata_t *win, gpointer p)
586 {
587 file_collection *collection = (file_collection *) p;
588 GtkListStore *store;
589 GtkTreeSelection *selection;
590 GtkTreeIter iter;
591 char line[MAXLEN];
592 char *index;
593 FILE *fp;
594 int datacols = 0;
595 int err = 0;
596
597 index = full_path(collection->path, collection->descfile);
598
599 fp = gretl_fopen(index, "r");
600 if (fp == NULL) {
601 return E_FOPEN;
602 }
603
604 store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(win->listbox)));
605 gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter);
606
607 while (fgets(line, sizeof line, fp) && !err) {
608 char fname[24], descrip[80], data[64];
609 int nf;
610
611 if (*line == '#') continue;
612
613 nf = sscanf(line, " \"%23[^\"]\",\"%79[^\"]\",\"%63[^\"]\"",
614 fname, descrip, data);
615
616 if (nf == 3) {
617 err = validate_desc_strings(fname, descrip, data);
618 if (!err) {
619 datacols = 3;
620 gtk_list_store_append(store, &iter);
621 gtk_list_store_set(store, &iter,
622 0, strip_extension(fname),
623 1, descrip,
624 2, data, -1);
625 }
626 } else if (nf == 2) {
627 err = validate_desc_strings(fname, descrip, NULL);
628 if (!err) {
629 datacols = 2;
630 gtk_list_store_append(store, &iter);
631 gtk_list_store_set(store, &iter,
632 0, strip_extension(fname),
633 1, descrip, -1);
634 }
635 } else {
636 ; /* ?? */
637 }
638 }
639
640 fclose(fp);
641
642 if (!err && datacols == 2) {
643 /* these windows can have either 2 or 3 columns */
644 GtkTreeViewColumn *col;
645
646 col = gtk_tree_view_get_column(GTK_TREE_VIEW(win->listbox), 2);
647 if (col != NULL) {
648 gtk_tree_view_column_set_visible(col, FALSE);
649 }
650 }
651
652 if (!err) {
653 /* select the first row */
654 gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter);
655 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(win->listbox));
656 gtk_tree_selection_select_iter(selection, &iter);
657 }
658
659 return err;
660 }
661
show_datafile_info(GtkWidget * w,gpointer data)662 static void show_datafile_info (GtkWidget *w, gpointer data)
663 {
664 char fullname[MAXLEN];
665 windata_t *vwin = (windata_t *) data;
666 file_collection *collection;
667 char *descrip;
668 gchar *filename;
669 int err = 0;
670
671 tree_view_get_string(GTK_TREE_VIEW(vwin->listbox), vwin->active_var,
672 0, &filename);
673 collection = g_object_get_data(G_OBJECT(vwin->listbox), "collection");
674 gretl_build_path(fullname, collection->path, filename, NULL);
675 strcat(fullname, ".gdt");
676 g_free(filename);
677
678 #if 0
679 fprintf(stderr, "info: active=%d, fullname='%s'\n", vwin->active_var,
680 fullname);
681 fprintf(stderr, "collection path='%s'\n", collection->path);
682 #endif
683
684 descrip = gretl_get_gdt_description(fullname, &err);
685
686 if (err) {
687 gui_errmsg(err);
688 } else {
689 gchar *title = g_strdup_printf("gretl: %s", _("data info"));
690 PRN *prn;
691
692 prn = gretl_print_new_with_buffer(descrip);
693 view_buffer(prn, 80, 320, title, INFO, NULL);
694 g_free(title);
695 }
696 }
697
browser_open_data(GtkWidget * w,gpointer data)698 void browser_open_data (GtkWidget *w, gpointer data)
699 {
700 windata_t *vwin = (windata_t *) data;
701 file_collection *collection;
702 char tmp[MAXLEN];
703 gchar *filename;
704
705 tree_view_get_string(GTK_TREE_VIEW(vwin->listbox), vwin->active_var,
706 0, &filename);
707 collection = g_object_get_data(G_OBJECT(vwin->listbox), "collection");
708 gretl_build_path(tmp, collection->path, filename, NULL);
709 strcat(tmp, ".gdt");
710 set_tryfile(tmp);
711 g_free(filename);
712
713 set_datapage(collection->title);
714
715 verify_open_data(vwin, OPEN_DATA);
716 }
717
browser_open_ps(GtkWidget * w,gpointer data)718 void browser_open_ps (GtkWidget *w, gpointer data)
719 {
720 windata_t *vwin = (windata_t *) data;
721 file_collection *collection;
722 gchar *filename;
723
724 tree_view_get_string(GTK_TREE_VIEW(vwin->listbox), vwin->active_var,
725 0, &filename);
726 collection = g_object_get_data(G_OBJECT(vwin->listbox), "collection");
727 gretl_build_path(scriptfile, collection->path, filename, NULL);
728 strcat(scriptfile, ".inp");
729 g_free(filename);
730
731 /* close the calling window */
732 gtk_widget_destroy(GTK_WIDGET(vwin->main));
733
734 set_scriptpage(collection->title);
735
736 view_script(scriptfile, 0, VIEW_SCRIPT);
737 }
738
enter_opens_file(GtkWidget * w,GdkEventKey * key,windata_t * vwin)739 static gint enter_opens_file (GtkWidget *w, GdkEventKey *key,
740 windata_t *vwin)
741 {
742 if (key->keyval == GDK_Return || key->keyval == GDK_o) {
743 if (vwin->role == TEXTBOOK_DATA) {
744 browser_open_data(w, vwin);
745 } else if (vwin->role == PS_FILES) {
746 browser_open_ps(w, vwin);
747 }
748 return TRUE;
749 } else {
750 return FALSE;
751 }
752 }
753
browser_delete_current_row(windata_t * vwin)754 static void browser_delete_current_row (windata_t *vwin)
755 {
756 GtkTreeModel *mod;
757 GtkTreeIter iter;
758 int i;
759
760 mod = gtk_tree_view_get_model(GTK_TREE_VIEW(vwin->listbox));
761 if (!gtk_tree_model_get_iter_first(mod, &iter)) {
762 return;
763 }
764
765 for (i=0; ; i++) {
766 if (i == vwin->active_var) {
767 gtk_list_store_remove(GTK_LIST_STORE(mod), &iter);
768 break;
769 } else if (!gtk_tree_model_iter_next(mod, &iter)) {
770 break;
771 }
772 }
773 }
774
browser_delete_row_by_content(windata_t * vwin,int colnum1,const char * test1,int colnum2,const char * test2)775 static void browser_delete_row_by_content (windata_t *vwin,
776 int colnum1,
777 const char *test1,
778 int colnum2,
779 const char *test2)
780 {
781 GtkTreeModel *mod;
782 GtkTreeIter iter;
783 gchar *content;
784 int done = 0;
785
786 mod = gtk_tree_view_get_model(GTK_TREE_VIEW(vwin->listbox));
787 if (!gtk_tree_model_get_iter_first(mod, &iter)) {
788 return;
789 }
790
791 while (1) {
792 gtk_tree_model_get(mod, &iter, colnum1, &content, -1);
793 if (content != NULL && !strcmp(content, test1)) {
794 if (test2 == NULL) {
795 done = 1;
796 } else {
797 g_free(content);
798 gtk_tree_model_get(mod, &iter, colnum2, &content, -1);
799 if (content != NULL && !strcmp(content, test2)) {
800 done = 1;
801 }
802 }
803 if (done) {
804 gtk_list_store_remove(GTK_LIST_STORE(mod), &iter);
805 }
806 }
807 g_free(content);
808 if (done || !gtk_tree_model_iter_next(mod, &iter)) {
809 break;
810 }
811 }
812 }
813
gui_delete_fn_pkg(const char * pkgname,const char * fname,windata_t * vwin)814 static int gui_delete_fn_pkg (const char *pkgname, const char *fname,
815 windata_t *vwin)
816 {
817 gchar *msg = NULL;
818 int delete_ok = 0;
819 int delete = 0;
820 int loaded = 0;
821 int unload = 0;
822 int resp, err = 0;
823
824 if (package_being_edited(pkgname, NULL)) {
825 warnbox_printf(_("%s: please close this object's window first"),
826 pkgname);
827 return 0;
828 }
829
830 /* see if the package is loaded in memory */
831 if (get_function_package_by_name(pkgname) != NULL) {
832 loaded = 1;
833 }
834
835 /* and see if the user is able to delete the package */
836 if (gretl_write_access((char *) fname) == 0) {
837 delete_ok = 1;
838 }
839
840 if (!loaded && !delete_ok) {
841 infobox_printf(_("Package %s is not loaded, and you do "
842 "not have permission to delete it."),
843 pkgname);
844 return 0;
845 }
846
847 if (loaded && delete_ok) {
848 const char *opts[] = {
849 N_("Unload member functions only"),
850 N_("Unload and delete package file"),
851 };
852
853 msg = g_strdup_printf(_("Function package %s"), fname);
854 resp = radio_dialog(NULL, msg, opts, 2, 0, 0, vwin_toplevel(vwin));
855 g_free(msg);
856 if (resp < 0) {
857 /* canceled */
858 return 0;
859 } else if (resp == 1) {
860 delete = 1;
861 } else {
862 unload = 1;
863 }
864 } else if (loaded) {
865 msg = g_strdup_printf(_("Unload package %s?"), pkgname);
866 resp = yes_no_dialog(NULL, msg, vwin_toplevel(vwin));
867 g_free(msg);
868 if (resp == GRETL_NO) {
869 return 0;
870 } else {
871 unload = 1;
872 }
873 } else if (delete_ok) {
874 msg = g_strdup_printf(_("Really delete %s?"), pkgname);
875 resp = yes_no_dialog(NULL, msg, vwin_toplevel(vwin));
876 g_free(msg);
877 if (resp == GRETL_NO) {
878 return 0;
879 } else {
880 delete = 1;
881 }
882 }
883
884 if (unload && !delete) {
885 /* just unload the package from memory */
886 function_package_unload_full_by_filename(fname);
887 } else if (delete) {
888 /* remove entry from registry, if present */
889 gui_function_pkg_unregister(pkgname);
890 /* unload the package from memory */
891 if (loaded) {
892 function_package_unload_full_by_filename(fname);
893 }
894 /* trash the package file(s) */
895 err = delete_function_package(fname);
896 if (err) {
897 gui_errmsg(err);
898 } else {
899 /* remove package from GUI listing */
900 browser_delete_current_row(vwin);
901 }
902 }
903
904 return err;
905 }
906
get_info_width(const char * buf)907 static int get_info_width (const char *buf)
908 {
909 char line[1024];
910 int n, width = 66;
911
912 bufgets_init(buf);
913
914 while (bufgets(line, sizeof line, buf)) {
915 n = strlen(line);
916 if (n > width && n <= HELP_WIDTH) {
917 width = n;
918 }
919 }
920
921 bufgets_finalize(buf);
922
923 return width + 1;
924 }
925
display_function_package_data(const char * pkgname,const char * path,int role)926 windata_t *display_function_package_data (const char *pkgname,
927 const char *path,
928 int role)
929 {
930 windata_t *vwin = NULL;
931 PRN *prn = NULL;
932 int err = 0;
933
934 if (bufopen(&prn)) {
935 return NULL;
936 }
937
938 if (role == VIEW_PKG_INFO) {
939 err = print_function_package_info(path, 1, prn);
940 } else if (role == VIEW_PKG_SAMPLE) {
941 err = print_function_package_sample(path, tabwidth, prn);
942 } else {
943 err = print_function_package_code(path, tabwidth, prn);
944 }
945
946 if (err) {
947 gretl_print_destroy(prn);
948 gui_errmsg(err);
949 } else {
950 gchar *title;
951
952 if (role == VIEW_PKG_SAMPLE) {
953 title = g_strdup_printf("gretl: %s sample", pkgname);
954 } else {
955 title = g_strdup_printf("gretl: %s", pkgname);
956 }
957
958 if (role == VIEW_PKG_INFO) {
959 char *buf = gretl_print_steal_buffer(prn);
960 int width = get_info_width(buf);
961
962 vwin = view_formatted_text_buffer(title, buf, width, 350, role);
963 free(buf);
964 gretl_print_destroy(prn);
965 } else {
966 vwin = view_buffer(prn, 78, 350, title, role, NULL);
967 }
968 strcpy(vwin->fname, path);
969 if (strstr(path, "dltmp")) {
970 set_window_delete_filename(vwin);
971 }
972 g_free(title);
973 }
974
975 return vwin;
976 }
977
978 /* on adding files to gfn view window: ensure that the GTK
979 selection stays in sync with the "active_var" ID
980 */
981
fix_selected_row(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer data)982 static void fix_selected_row (GtkTreeModel *model,
983 GtkTreePath *path,
984 GtkTreeIter *iter,
985 gpointer data)
986 {
987 gint idx = gtk_tree_path_get_indices(path)[0];
988 windata_t *vwin = data;
989
990 vwin->active_var = idx;
991 }
992
993 /* callback from the file selector where the user has chosen
994 a directory at which to point the gfn browser
995 */
996
set_alternate_gfn_dir(windata_t * vwin,char * path)997 void set_alternate_gfn_dir (windata_t *vwin, char *path)
998 {
999 GDir *dir;
1000 int replace = 1;
1001 int nfn = 0;
1002
1003 #if GFN_DEBUG
1004 fprintf(stderr, "set_alternate_gfn_dir: '%s'\n", path);
1005 #endif
1006
1007 dir = gretl_opendir(path);
1008 if (dir == NULL) {
1009 /* should never happen, but... */
1010 return;
1011 }
1012
1013 /* first pass: just count gfn files in @path */
1014 read_fn_files_in_dir(dir, path, NULL, NULL, &nfn);
1015
1016 if (nfn == 0) {
1017 warnbox(_("No function files were found"));
1018 replace = 0;
1019 } else {
1020 /* give the user a chance to back out */
1021 gchar *msg;
1022
1023 msg = g_strdup_printf(_("Found %d function file(s).\n"
1024 "Replace the current listing?"),
1025 nfn);
1026 if (yes_no_dialog("gretl", msg, vwin->main) != GRETL_YES) {
1027 replace = 0;
1028 }
1029 g_free(msg);
1030 }
1031
1032 if (replace) {
1033 /* OK: now rewind, clear, and repopulate the listing using
1034 the @path selected by the user
1035 */
1036 GtkListStore *store;
1037 GtkTreeSelection *sel;
1038 GtkTreeIter iter;
1039 gulong sigid;
1040 int nfn0 = nfn;
1041
1042 store = GTK_LIST_STORE(gtk_tree_view_get_model
1043 (GTK_TREE_VIEW(vwin->listbox)));
1044 g_dir_rewind(dir);
1045 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(vwin->listbox));
1046 sigid = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(sel), "menu_check"));
1047 g_signal_handler_block(sel, sigid);
1048 gtk_list_store_clear(store);
1049 g_signal_handler_unblock(sel, sigid);
1050 nfn = 0;
1051 gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter);
1052 read_fn_files_in_dir(dir, path, store, &iter, &nfn);
1053
1054 if (nfn > 0) {
1055 gtk_tree_selection_selected_foreach(sel, fix_selected_row, vwin);
1056 widget_set_int(vwin->listbox, "altdir", 1);
1057 presort_treelist(vwin);
1058 listbox_select_first(vwin);
1059 } else {
1060 /* can't happen? */
1061 warnbox(_("No function files were found"));
1062 }
1063
1064 if (nfn > 0 && nfn < nfn0) {
1065 gchar *msg;
1066
1067 msg = g_strdup_printf("Ignored %d duplicated file(s)", nfn0 - nfn);
1068 msgbox(msg, GTK_MESSAGE_WARNING, vwin->main);
1069 g_free(msg);
1070 }
1071 }
1072
1073 g_dir_close(dir);
1074 }
1075
gfn_browser_get_alt_path(void)1076 gchar *gfn_browser_get_alt_path (void)
1077 {
1078 windata_t *vwin = get_browser_for_role(FUNC_FILES, NULL);
1079 gchar *path = NULL;
1080
1081 if (vwin != NULL && widget_get_int(vwin->listbox, "altdir")) {
1082 tree_view_get_string(GTK_TREE_VIEW(vwin->listbox), 0,
1083 GFN_DIRNAME_COL, &path);
1084 }
1085
1086 return path;
1087 }
1088
query_remove_gfn_from_registry(const char * pkgname,windata_t * vwin)1089 static void query_remove_gfn_from_registry (const char *pkgname,
1090 windata_t *vwin)
1091 {
1092 gchar *msg;
1093 int resp;
1094
1095 msg = g_strdup_printf(_("Really remove %s from menu?"), pkgname);
1096 resp = yes_no_dialog(NULL, msg, vwin->main);
1097
1098 if (resp == GRETL_YES) {
1099 gui_function_pkg_unregister(pkgname);
1100 }
1101
1102 g_free(msg);
1103 }
1104
browser_functions_handler(windata_t * vwin,int task)1105 static void browser_functions_handler (windata_t *vwin, int task)
1106 {
1107 char path[FILENAME_MAX];
1108 gchar *pkgname = NULL;
1109 gchar *dir = NULL;
1110 int dircol = 0;
1111
1112 if (vwin->role == DBNOMICS_TOP && task == VIEW_PKG_DOC) {
1113 /* special case: not coming from gfn window */
1114 gchar *docpath = g_build_filename(gretl_home(), "functions",
1115 "dbnomics", "dbnomics.pdf",
1116 NULL);
1117 gretl_show_pdf(docpath, NULL);
1118 g_free(docpath);
1119 return;
1120 }
1121
1122 if (vwin->role == FUNC_FILES) {
1123 dircol = GFN_DIRNAME_COL;
1124 } else if (vwin->role != REMOTE_FUNC_FILES &&
1125 vwin->role != PKG_REGISTRY) {
1126 dummy_call();
1127 return;
1128 }
1129
1130 tree_view_get_string(GTK_TREE_VIEW(vwin->listbox), vwin->active_var,
1131 0, &pkgname);
1132
1133 if (dircol != 0) {
1134 tree_view_get_string(GTK_TREE_VIEW(vwin->listbox), vwin->active_var,
1135 dircol, &dir);
1136 if (task == VIEW_PKG_RESOURCES) {
1137 gretl_build_path(path, dir, "examples", NULL);
1138 } else if (task == VIEW_PKG_DOC) {
1139 gretl_build_path(path, dir, pkgname, NULL);
1140 strcat(path, ".pdf");
1141 } else {
1142 gretl_build_path(path, dir, pkgname, NULL);
1143 strcat(path, ".gfn");
1144 }
1145 } else {
1146 strcpy(path, pkgname);
1147 }
1148
1149 if (vwin->role == FUNC_FILES && task != DELETE_FN_PKG) {
1150 /* try to ensure we don't get a stale version */
1151 char test[FILENAME_MAX];
1152 const char *loaded = NULL;
1153 gchar *version = NULL;
1154
1155 gretl_build_path(test, dir, pkgname, NULL);
1156 strcat(test, ".gfn");
1157 if (function_package_is_loaded(test, &loaded)) {
1158 tree_view_get_string(GTK_TREE_VIEW(vwin->listbox), vwin->active_var,
1159 1, &version);
1160 if (loaded != NULL && version != NULL &&
1161 strcmp(version, loaded)) {
1162 function_package_unload_by_filename(test);
1163 }
1164 }
1165 g_free(version);
1166 }
1167
1168 #if GFN_DEBUG
1169 fprintf(stderr, "browser_functions_handler: active=%d, pkgname='%s'\n"
1170 "path='%s'\n", vwin->active_var, pkgname, path);
1171 #endif
1172
1173 if (task == DELETE_FN_PKG) {
1174 gui_delete_fn_pkg(pkgname, path, vwin);
1175 } else if (task == VIEW_FN_PKG_INFO) {
1176 display_function_package_data(pkgname, path, VIEW_PKG_INFO);
1177 } else if (task == VIEW_FN_PKG_SAMPLE) {
1178 display_function_package_data(pkgname, path, VIEW_PKG_SAMPLE);
1179 } else if (task == VIEW_FN_PKG_CODE) {
1180 display_function_package_data(pkgname, path, VIEW_PKG_CODE);
1181 } else if (task == MENU_ADD_FN_PKG) {
1182 gui_function_pkg_query_register(path, vwin->main);
1183 } else if (task == MENU_REMOVE_FN_PKG) {
1184 query_remove_gfn_from_registry(pkgname, vwin);
1185 } else if (task == VIEW_PKG_RESOURCES) {
1186 file_selector_with_startdir(OPEN_ANY, path, vwin_toplevel(vwin));
1187 } else if (task == VIEW_PKG_DOC) {
1188 gretl_show_pdf(path, NULL);
1189 } else if (task == CALL_FN_PKG) {
1190 /* note: this is the double-click default for the local
1191 function package browser; can also be invoked via
1192 context menu and toolbar button
1193 */
1194 open_function_package(pkgname, path, vwin);
1195 }
1196
1197 g_free(pkgname);
1198 g_free(dir);
1199 }
1200
show_addon_info(GtkWidget * w,gpointer data)1201 static void show_addon_info (GtkWidget *w, gpointer data)
1202 {
1203 windata_t *vwin = (windata_t *) data;
1204 gchar *pkgname = NULL;
1205 gchar *descrip = NULL;
1206 gchar *status = NULL;
1207 int v = vwin->active_var;
1208
1209 tree_view_get_string(GTK_TREE_VIEW(vwin->listbox), v, 0, &pkgname);
1210 tree_view_get_string(GTK_TREE_VIEW(vwin->listbox), v, 4, &descrip);
1211 tree_view_get_string(GTK_TREE_VIEW(vwin->listbox), v, 3, &status);
1212
1213 if (pkgname == NULL || descrip == NULL) {
1214 gui_errmsg(E_DATA);
1215 } else {
1216 gchar *localver = NULL;
1217 int done = 0;
1218
1219 if (status != NULL) {
1220 char *path = gretl_addon_get_path(pkgname);
1221 char *ver = NULL, *date = NULL;
1222
1223 if (path != NULL && !strcmp(status, _("Not up to date"))) {
1224 ver = get_addon_version(path, &date);
1225 if (ver != NULL && data != NULL) {
1226 localver = g_strdup_printf("Installed version is %s (%s)",
1227 ver, date);
1228 }
1229 free(ver);
1230 free(date);
1231 } else if (path != NULL && !strcmp(status, _("Up to date")) &&
1232 display_function_package_data(pkgname, path, VIEW_PKG_INFO)) {
1233 done = 1;
1234 }
1235 free(path);
1236 }
1237
1238 if (localver != NULL) {
1239 infobox_printf("%s:\n%s\n%s", pkgname, descrip, localver);
1240 g_free(localver);
1241 } else if (!done) {
1242 infobox_printf("%s:\n%s", pkgname, descrip);
1243 }
1244 }
1245
1246 g_free(pkgname);
1247 g_free(descrip);
1248 g_free(status);
1249 }
1250
install_addon_callback(GtkWidget * w,gpointer data)1251 static void install_addon_callback (GtkWidget *w, gpointer data)
1252 {
1253 windata_t *vwin = (windata_t *) data;
1254 gchar *pkgname = NULL;
1255 int v = vwin->active_var;
1256
1257 tree_view_get_string(GTK_TREE_VIEW(vwin->listbox), v, 0, &pkgname);
1258
1259 if (pkgname == NULL) {
1260 gui_errmsg(E_DATA);
1261 } else {
1262 char *local_path = NULL;
1263 int err = download_addon(pkgname, &local_path);
1264
1265 if (!err) {
1266 list_store_set_string(GTK_TREE_VIEW(vwin->listbox), v, 3,
1267 _("Up to date"));
1268 /* if there was an old version of the addon loaded,
1269 we need to unload it now so as to get correct
1270 information in response to a subsequent call to
1271 "check for addons"
1272 */
1273 function_package_unload_full_by_filename(local_path);
1274 free(local_path);
1275 }
1276
1277 g_free(pkgname);
1278 }
1279 }
1280
1281 /* this function is public because it's called from
1282 doubleclick_action() in callbacks.c
1283 */
1284
browser_call_func(GtkWidget * w,gpointer data)1285 void browser_call_func (GtkWidget *w, gpointer data)
1286 {
1287 windata_t *vwin = (windata_t *) data;
1288
1289 browser_functions_handler(vwin, CALL_FN_PKG);
1290 }
1291
show_function_info(GtkWidget * w,gpointer data)1292 static void show_function_info (GtkWidget *w, gpointer data)
1293 {
1294 windata_t *vwin = (windata_t *) data;
1295
1296 browser_functions_handler(vwin, VIEW_FN_PKG_INFO);
1297 }
1298
show_function_code(GtkWidget * w,gpointer data)1299 static void show_function_code (GtkWidget *w, gpointer data)
1300 {
1301 windata_t *vwin = (windata_t *) data;
1302
1303 browser_functions_handler(vwin, VIEW_FN_PKG_CODE);
1304 }
1305
show_function_sample(GtkWidget * w,gpointer data)1306 static void show_function_sample (GtkWidget *w, gpointer data)
1307 {
1308 windata_t *vwin = (windata_t *) data;
1309
1310 browser_functions_handler(vwin, VIEW_FN_PKG_SAMPLE);
1311 }
1312
show_package_resources(GtkWidget * w,gpointer data)1313 static void show_package_resources (GtkWidget *w, gpointer data)
1314 {
1315 windata_t *vwin = (windata_t *) data;
1316
1317 browser_functions_handler(vwin, VIEW_PKG_RESOURCES);
1318 }
1319
show_package_doc(GtkWidget * w,gpointer data)1320 static void show_package_doc (GtkWidget *w, gpointer data)
1321 {
1322 windata_t *vwin = (windata_t *) data;
1323
1324 browser_functions_handler(vwin, VIEW_PKG_DOC);
1325 }
1326
browser_del_func(GtkWidget * w,gpointer data)1327 static void browser_del_func (GtkWidget *w, gpointer data)
1328 {
1329 windata_t *vwin = (windata_t *) data;
1330
1331 browser_functions_handler(vwin, DELETE_FN_PKG);
1332 }
1333
add_func_to_menu(GtkWidget * w,gpointer data)1334 static void add_func_to_menu (GtkWidget *w, gpointer data)
1335 {
1336 windata_t *vwin = (windata_t *) data;
1337
1338 browser_functions_handler(vwin, MENU_ADD_FN_PKG);
1339 }
1340
gfn_registry_remove(GtkWidget * w,gpointer data)1341 static void gfn_registry_remove (GtkWidget *w, gpointer data)
1342 {
1343 windata_t *vwin = (windata_t *) data;
1344
1345 browser_functions_handler(vwin, MENU_REMOVE_FN_PKG);
1346 }
1347
get_local_viewer(int remote_role)1348 windata_t *get_local_viewer (int remote_role)
1349 {
1350 windata_t *vwin = NULL;
1351
1352 if (remote_role == REMOTE_DB) {
1353 vwin = get_browser_for_role(NATIVE_DB, NULL);
1354 } else if (remote_role == REMOTE_FUNC_FILES) {
1355 vwin = get_browser_for_role(FUNC_FILES, NULL);
1356 }
1357
1358 return vwin;
1359 }
1360
start_new_function_package(GtkWidget * w,gpointer data)1361 void start_new_function_package (GtkWidget *w, gpointer data)
1362 {
1363 windata_t *vwin = (windata_t *) data;
1364
1365 functions_selection_wrapper(vwin_toplevel(vwin));
1366 }
1367
build_datafiles_popup(windata_t * vwin)1368 static void build_datafiles_popup (windata_t *vwin)
1369 {
1370 if (vwin->popup == NULL) {
1371 vwin->popup = gtk_menu_new();
1372 add_popup_item(_("Info"), vwin->popup,
1373 G_CALLBACK(show_datafile_info),
1374 vwin);
1375 add_popup_item(_("Open"), vwin->popup,
1376 G_CALLBACK(browser_open_data),
1377 vwin);
1378 }
1379 }
1380
get_menu_add_ok(windata_t * vwin)1381 static int get_menu_add_ok (windata_t *vwin)
1382 {
1383 gchar *pkgname = NULL;
1384 gchar *dirname = NULL;
1385 int ret = 0;
1386
1387 tree_view_get_string(GTK_TREE_VIEW(vwin->listbox), vwin->active_var,
1388 0, &pkgname);
1389 if (pkgname != NULL && !strcmp(pkgname, "ridge")) {
1390 return 0;
1391 }
1392 tree_view_get_string(GTK_TREE_VIEW(vwin->listbox), vwin->active_var,
1393 GFN_DIRNAME_COL, &dirname);
1394
1395 if (pkgname != NULL && dirname != NULL) {
1396 char path[FILENAME_MAX];
1397
1398 gretl_build_path(path, dirname, pkgname, NULL);
1399 strcat(path, ".gfn");
1400 ret = package_is_available_for_menu(pkgname, path);
1401 }
1402
1403 g_free(pkgname);
1404 g_free(dirname);
1405
1406 return ret;
1407 }
1408
check_extra_buttons_state(GtkTreeSelection * sel,windata_t * vwin)1409 static void check_extra_buttons_state (GtkTreeSelection *sel, windata_t *vwin)
1410 {
1411 GtkWidget *button;
1412 gint flags = 0;
1413
1414 button = g_object_get_data(G_OBJECT(vwin->mbar), "add-button");
1415 if (button != NULL) {
1416 gtk_widget_set_sensitive(button, get_menu_add_ok(vwin));
1417 }
1418
1419 /* Get flags from last, hidden int column, to determine
1420 whether we can offer links to a package's "examples"
1421 directory and/or its documentation in PDF format.
1422 */
1423 tree_view_get_int(GTK_TREE_VIEW(vwin->listbox),
1424 vwin->active_var, GFN_FLAGS_COL, &flags);
1425
1426 button = g_object_get_data(G_OBJECT(vwin->mbar), "res-button");
1427 if (button != NULL) {
1428 gtk_widget_set_sensitive(button, flags & PKG_ATTR_RES);
1429 }
1430
1431 button = g_object_get_data(G_OBJECT(vwin->mbar), "doc-button");
1432 if (button != NULL) {
1433 gtk_widget_set_sensitive(button, flags & PKG_ATTR_DOC);
1434 }
1435 }
1436
connect_menu_adjust_signal(windata_t * vwin)1437 static void connect_menu_adjust_signal (windata_t *vwin)
1438 {
1439 GtkTreeSelection *sel;
1440 gulong id;
1441
1442 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(vwin->listbox));
1443 id = g_signal_connect(G_OBJECT(sel), "changed",
1444 G_CALLBACK(check_extra_buttons_state),
1445 vwin);
1446 g_object_set_data(G_OBJECT(sel), "menu_check", GINT_TO_POINTER(id));
1447 }
1448
build_funcfiles_popup(windata_t * vwin)1449 static void build_funcfiles_popup (windata_t *vwin)
1450 {
1451 vwin->popup = gtk_menu_new();
1452
1453 if (vwin->role == FUNC_FILES) {
1454 /* local function files: full menu */
1455 int add_ok = 0;
1456 int res_ok = 0;
1457 int doc_ok = 0;
1458 GtkWidget *b;
1459
1460 b = g_object_get_data(G_OBJECT(vwin->mbar), "add-button");
1461 if (b != NULL && gtk_widget_is_sensitive(b)) {
1462 add_ok = 1;
1463 }
1464
1465 b = g_object_get_data(G_OBJECT(vwin->mbar), "res-button");
1466 if (b != NULL && gtk_widget_is_sensitive(b)) {
1467 res_ok = 1;
1468 }
1469
1470 b = g_object_get_data(G_OBJECT(vwin->mbar), "doc-button");
1471 if (b != NULL && gtk_widget_is_sensitive(b)) {
1472 doc_ok = 1;
1473 }
1474
1475 add_popup_item(_("Info"), vwin->popup,
1476 G_CALLBACK(show_function_info),
1477 vwin);
1478 add_popup_item(_("Sample script"), vwin->popup,
1479 G_CALLBACK(show_function_sample),
1480 vwin);
1481 add_popup_item(_("View code"), vwin->popup,
1482 G_CALLBACK(show_function_code),
1483 vwin);
1484 add_popup_item(_("Execute"), vwin->popup,
1485 G_CALLBACK(browser_call_func),
1486 vwin);
1487 if (res_ok) {
1488 add_popup_item(_("Resources..."), vwin->popup,
1489 G_CALLBACK(show_package_resources),
1490 vwin);
1491 }
1492 if (add_ok) {
1493 add_popup_item(_("Add to menu"), vwin->popup,
1494 G_CALLBACK(add_func_to_menu),
1495 vwin);
1496 }
1497 if (doc_ok) {
1498 add_popup_item(_("Help"), vwin->popup,
1499 G_CALLBACK(show_package_doc),
1500 vwin);
1501 }
1502 add_popup_item(_("Unload/delete..."), vwin->popup,
1503 G_CALLBACK(browser_del_func),
1504 vwin);
1505 } else if (vwin->role == REMOTE_FUNC_FILES) {
1506 /* files on server: limited menu */
1507 add_popup_item(_("Info"), vwin->popup,
1508 G_CALLBACK(pkg_info_from_server),
1509 vwin);
1510 add_popup_item(_("Install"), vwin->popup,
1511 G_CALLBACK(install_file_from_server),
1512 vwin);
1513 } else if (vwin->role == REMOTE_ADDONS) {
1514 add_popup_item(_("Info"), vwin->popup,
1515 G_CALLBACK(show_addon_info),
1516 vwin);
1517 add_popup_item(_("Install"), vwin->popup,
1518 G_CALLBACK(install_addon_callback),
1519 vwin);
1520 } else if (vwin->role == PKG_REGISTRY) {
1521 add_popup_item(_("Remove"), vwin->popup,
1522 G_CALLBACK(gfn_registry_remove),
1523 vwin);
1524 }
1525 }
1526
1527 static gboolean
funcfiles_popup_handler(GtkWidget * w,GdkEventButton * event,gpointer data)1528 funcfiles_popup_handler (GtkWidget *w, GdkEventButton *event, gpointer data)
1529 {
1530 if (right_click(event)) {
1531 windata_t *vwin = (windata_t *) data;
1532
1533 if (vwin->popup != NULL) {
1534 gtk_widget_destroy(vwin->popup);
1535 vwin->popup = NULL;
1536 }
1537
1538 build_funcfiles_popup(vwin);
1539
1540 if (vwin->popup != NULL) {
1541 gtk_menu_popup(GTK_MENU(vwin->popup), NULL, NULL, NULL, NULL,
1542 event->button, event->time);
1543 g_signal_connect(G_OBJECT(vwin->popup), "destroy",
1544 G_CALLBACK(gtk_widget_destroyed),
1545 &vwin->popup);
1546 }
1547
1548 return TRUE;
1549 }
1550
1551 return FALSE;
1552 }
1553
build_db_popup(windata_t * vwin)1554 static void build_db_popup (windata_t *vwin)
1555 {
1556 if (vwin->popup == NULL) {
1557 vwin->popup = gtk_menu_new();
1558 if (vwin->role == NATIVE_DB) {
1559 add_popup_item(_("List series"), vwin->popup,
1560 G_CALLBACK(open_db_index),
1561 vwin);
1562 add_popup_item(_("Find..."), vwin->popup,
1563 G_CALLBACK(listbox_find),
1564 vwin);
1565 } else {
1566 add_popup_item(_("List series"), vwin->popup,
1567 G_CALLBACK(open_remote_db_index),
1568 vwin);
1569 add_popup_item(_("Install"), vwin->popup,
1570 G_CALLBACK(install_file_from_server),
1571 vwin);
1572 add_popup_item(_("Find..."), vwin->popup,
1573 G_CALLBACK(listbox_find),
1574 vwin);
1575 }
1576 }
1577 }
1578
build_data_pkg_popup(windata_t * vwin)1579 static void build_data_pkg_popup (windata_t *vwin)
1580 {
1581 if (vwin->popup == NULL) {
1582 vwin->popup = gtk_menu_new();
1583 add_popup_item(_("Install"), vwin->popup,
1584 G_CALLBACK(install_file_from_server),
1585 vwin);
1586 }
1587 }
1588
show_server_dbs(GtkWidget * w,gpointer p)1589 static void show_server_dbs (GtkWidget *w, gpointer p)
1590 {
1591 display_files(REMOTE_DB, NULL);
1592 }
1593
show_local_dbs(GtkWidget * w,gpointer p)1594 static void show_local_dbs (GtkWidget *w, gpointer p)
1595 {
1596 display_files(NATIVE_DB, NULL);
1597 }
1598
show_server_funcs(GtkWidget * w,gpointer p)1599 static void show_server_funcs (GtkWidget *w, gpointer p)
1600 {
1601 display_files(REMOTE_FUNC_FILES, NULL);
1602 }
1603
show_server_data_pkgs(GtkWidget * w,gpointer p)1604 static void show_server_data_pkgs (GtkWidget *w, gpointer p)
1605 {
1606 display_files(REMOTE_DATA_PKGS, NULL);
1607 }
1608
show_local_funcs(GtkWidget * w,gpointer p)1609 static void show_local_funcs (GtkWidget *w, gpointer p)
1610 {
1611 display_files(FUNC_FILES, NULL);
1612 }
1613
show_gfn_registry(GtkWidget * w,windata_t * vwin)1614 static void show_gfn_registry (GtkWidget *w, windata_t *vwin)
1615 {
1616 display_files(PKG_REGISTRY, NULL);
1617 }
1618
1619 /* Respond when the user has clicked the Directory button
1620 in the function package browser. What exactly we do
1621 here depends on whether the browser is currently in
1622 its default mode (viewing installed packages) or if
1623 it is redirected -- which is flagged by a non-zero
1624 value for "altdir" on the browser's listbox.
1625 */
1626
alt_funcs_dir(GtkWidget * w,windata_t * vwin)1627 static void alt_funcs_dir (GtkWidget *w, windata_t *vwin)
1628 {
1629 if (widget_get_int(vwin->listbox, "altdir")) {
1630 const char *opts[] = {
1631 N_("Choose another directory"),
1632 N_("Revert to installed packages")
1633 };
1634 int resp;
1635
1636 resp = radio_dialog(NULL, NULL, opts, 2, 0, 0, vwin->main);
1637
1638 if (resp == GRETL_CANCEL) {
1639 return;
1640 } else if (resp == 1) {
1641 /* revert to installed gfns */
1642 widget_set_int(vwin->listbox, "altdir", 0);
1643 populate_gfn_list(vwin);
1644 listbox_select_first(vwin);
1645 return;
1646 }
1647 }
1648
1649 /* If not canceled or reverted to default, put up a
1650 dialog to let the user select a directory: the
1651 callback from that is set_alternate_gfn_dir().
1652 */
1653
1654 file_selector_with_parent(SET_FDIR, FSEL_DATA_VWIN, vwin,
1655 vwin->main);
1656 }
1657
alt_db_dir(GtkWidget * w,windata_t * vwin)1658 static void alt_db_dir (GtkWidget *w, windata_t *vwin)
1659 {
1660 file_selector_with_parent(SET_DBDIR, FSEL_DATA_VWIN, vwin,
1661 vwin->main);
1662 }
1663
get_dbn_menu(windata_t * vwin)1664 static GtkWidget *get_dbn_menu (windata_t *vwin)
1665 {
1666 GtkWidget *menu = gtk_menu_new();
1667 GtkAction *action;
1668 GtkWidget *item;
1669
1670 action = gtk_action_new("DBNbrowse", _("Browse..."), NULL, NULL);
1671 g_signal_connect(G_OBJECT(action), "activate",
1672 G_CALLBACK(show_files), vwin);
1673 item = gtk_action_create_menu_item(action);
1674 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
1675
1676 action = gtk_action_new("DBNseries", _("Specific series..."), NULL, NULL);
1677 g_signal_connect(G_OBJECT(action), "activate",
1678 G_CALLBACK(dbnomics_specific_series), vwin);
1679 item = gtk_action_create_menu_item(action);
1680 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
1681
1682 /* don't leak: record pointer to menu so it can
1683 be destroyed when the window is closed */
1684 vwin_record_toolbar_popup(vwin, menu);
1685
1686 return menu;
1687 }
1688
1689 enum {
1690 BTN_INFO = 1,
1691 BTN_CODE,
1692 BTN_INDX,
1693 BTN_INST,
1694 BTN_EXEC,
1695 BTN_ADD,
1696 BTN_DEL,
1697 BTN_WWW,
1698 BTN_HOME,
1699 BTN_OPEN,
1700 BTN_DIR,
1701 BTN_RES,
1702 BTN_DOC,
1703 BTN_REG,
1704 BTN_DBN,
1705 BTN_PROP
1706 };
1707
1708 static GretlToolItem files_items[] = {
1709 { N_("Open"), GTK_STOCK_OPEN, NULL, BTN_OPEN },
1710 { N_("Select directory"), GTK_STOCK_DIRECTORY, NULL, BTN_DIR },
1711 { N_("Info"), GTK_STOCK_INFO, NULL, BTN_INFO },
1712 { N_("Details"), GTK_STOCK_PROPERTIES, G_CALLBACK(open_dbnomics_series), BTN_PROP },
1713 { N_("Sample script"), GTK_STOCK_JUSTIFY_LEFT, G_CALLBACK(show_function_sample), BTN_CODE },
1714 { N_("View code"), GTK_STOCK_PROPERTIES, G_CALLBACK(show_function_code), BTN_CODE },
1715 { N_("Execute"), GTK_STOCK_EXECUTE, G_CALLBACK(browser_call_func), BTN_EXEC },
1716 { N_("List series"), GTK_STOCK_INDEX, NULL, BTN_INDX },
1717 { N_("Install"), GTK_STOCK_SAVE, NULL, BTN_INST },
1718 { N_("Resources..."), GTK_STOCK_OPEN, G_CALLBACK(show_package_resources), BTN_RES },
1719 { N_("Add to menu"), GTK_STOCK_ADD, G_CALLBACK(add_func_to_menu), BTN_ADD },
1720 { N_("Package registry"), GTK_STOCK_PREFERENCES, G_CALLBACK(show_gfn_registry), BTN_REG },
1721 { N_("Help"), GRETL_STOCK_PDF, G_CALLBACK(show_package_doc), BTN_DOC },
1722 { N_("Unload/delete..."), GTK_STOCK_DELETE, G_CALLBACK(browser_del_func), BTN_DEL },
1723 { N_("Look on server"), GTK_STOCK_NETWORK, NULL, BTN_WWW },
1724 { N_("Local machine"), GTK_STOCK_HOME, NULL, BTN_HOME },
1725 { "DB.NOMICS", GRETL_STOCK_DBN, NULL, BTN_DBN }
1726 };
1727
1728 static GretlToolItem pager_items[] = {
1729 { N_("First"), GTK_STOCK_GOTO_FIRST, G_CALLBACK(dbnomics_pager_call), 1 },
1730 { N_("Previous"), GTK_STOCK_GO_BACK, G_CALLBACK(dbnomics_pager_call), 2 },
1731 { N_("Next"), GTK_STOCK_GO_FORWARD, G_CALLBACK(dbnomics_pager_call), 3 },
1732 { N_("Last"), GTK_STOCK_GOTO_LAST, G_CALLBACK(dbnomics_pager_call), 4 }
1733 };
1734
1735 static int n_files_items = G_N_ELEMENTS(files_items);
1736
1737 #define common_item(f) (f == 0)
1738
1739 #define local_funcs_item(f) (f == BTN_DEL || f == BTN_CODE || \
1740 f == BTN_RES || f == BTN_DOC || \
1741 f == BTN_REG)
1742
files_item_get_callback(GretlToolItem * item,int role)1743 static int files_item_get_callback (GretlToolItem *item, int role)
1744 {
1745 if (common_item(item->flag)) {
1746 return 1;
1747 } else if (item->flag == BTN_DEL) {
1748 if (role == PKG_REGISTRY) {
1749 item->func = G_CALLBACK(gfn_registry_remove);
1750 item->tip = N_("Remove from menu");
1751 return 1;
1752 } else if (role == FUNC_FILES) {
1753 item->func = G_CALLBACK(browser_del_func);
1754 item->tip = N_("Unload/delete...");
1755 return 1;
1756 }
1757 } else if (local_funcs_item(item->flag)) {
1758 if (item->flag == BTN_DOC && role == DBNOMICS_TOP) {
1759 return 1;
1760 } else {
1761 return (role == FUNC_FILES);
1762 }
1763 } else if (item->flag == BTN_INST) {
1764 if (role == REMOTE_ADDONS) {
1765 item->func = G_CALLBACK(install_addon_callback);
1766 return 1;
1767 } else {
1768 item->func = G_CALLBACK(install_file_from_server);
1769 return (role == REMOTE_DB ||
1770 role == REMOTE_FUNC_FILES ||
1771 role == REMOTE_DATA_PKGS);
1772 }
1773 } else if (item->flag == BTN_EXEC || item->flag == BTN_ADD) {
1774 return (role == FUNC_FILES);
1775 } else if (item->flag == BTN_PROP) {
1776 return (role == DBNOMICS_SERIES);
1777 }
1778
1779 item->func = NULL;
1780
1781 if (item->flag == BTN_OPEN) {
1782 /* open: only data files and scripts */
1783 if (role == TEXTBOOK_DATA) {
1784 item->func = G_CALLBACK(browser_open_data);
1785 } else if (role == PS_FILES) {
1786 item->func = G_CALLBACK(browser_open_ps);
1787 }
1788 } else if (item->flag == BTN_INFO) {
1789 if (role == TEXTBOOK_DATA) {
1790 item->func = G_CALLBACK(show_datafile_info);
1791 } else if (role == FUNC_FILES) {
1792 item->func = G_CALLBACK(show_function_info);
1793 } else if (role == REMOTE_FUNC_FILES) {
1794 item->func = G_CALLBACK(pkg_info_from_server);
1795 } else if (role == REMOTE_ADDONS) {
1796 item->func = G_CALLBACK(show_addon_info);
1797 } else if (role == DBNOMICS_DB) {
1798 item->func = G_CALLBACK(show_dbnomics_dimensions);
1799 }
1800 } else if (item->flag == BTN_INDX) {
1801 /* index: databases only */
1802 item->tip = N_("List series");
1803 if (role == NATIVE_DB) {
1804 item->func = G_CALLBACK(open_db_index);
1805 } else if (role == REMOTE_DB) {
1806 item->func = G_CALLBACK(open_remote_db_index);
1807 } else if (role == DBNOMICS_TOP) {
1808 item->func = G_CALLBACK(open_dbnomics_provider);
1809 item->tip = N_("List datasets");
1810 } else if (role == DBNOMICS_DB) {
1811 item->func = G_CALLBACK(open_dbnomics_dataset);
1812 }
1813 } else if (item->flag == BTN_WWW) {
1814 if (role == FUNC_FILES) {
1815 item->func = G_CALLBACK(show_server_funcs);
1816 } else if (role == NATIVE_DB) {
1817 item->func = G_CALLBACK(show_server_dbs);
1818 } else if (role == TEXTBOOK_DATA) {
1819 item->func = G_CALLBACK(show_server_data_pkgs);
1820 }
1821 } else if (item->flag == BTN_HOME) {
1822 /* home: show only for on-server items */
1823 if (role == REMOTE_FUNC_FILES) {
1824 item->func = G_CALLBACK(show_local_funcs);
1825 } else if (role == REMOTE_DB) {
1826 item->func = G_CALLBACK(show_local_dbs);
1827 }
1828 } else if (item->flag == BTN_DIR) {
1829 if (role == FUNC_FILES) {
1830 item->func = G_CALLBACK(alt_funcs_dir);
1831 } else if (role == NATIVE_DB) {
1832 item->func = G_CALLBACK(alt_db_dir);
1833 }
1834 } else if (item->flag == BTN_DBN) {
1835 if (role == NATIVE_DB) {
1836 item->func = G_CALLBACK(dummy_call);
1837 }
1838 }
1839
1840 return (item->func != NULL);
1841 }
1842
filter_remote_funcs(GtkButton * b,windata_t * vwin)1843 static void filter_remote_funcs (GtkButton *b, windata_t *vwin)
1844 {
1845 GtkWidget *combo;
1846 int filter, old_filter;
1847 gchar *s;
1848
1849 combo = g_object_get_data(G_OBJECT(vwin->main), "filter-combo");
1850 old_filter = widget_get_int(combo, "filter");
1851
1852 if (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)) == 0) {
1853 filter = 0;
1854 } else {
1855 s = combo_box_get_active_text(combo);
1856 filter = atoi(s + 1); /* "C<number>" */
1857 g_free(s);
1858 }
1859
1860 if (filter != old_filter) {
1861 populate_remote_func_list(vwin, filter);
1862 widget_set_int(combo, "filter", filter);
1863 listbox_select_first(vwin);
1864 }
1865 }
1866
maybe_add_gfn_filter(windata_t * vwin,GtkWidget * hbox)1867 static void maybe_add_gfn_filter (windata_t *vwin,
1868 GtkWidget *hbox)
1869 {
1870 char *getbuf = NULL;
1871 int err;
1872
1873 err = list_remote_function_categories(&getbuf, OPT_NONE);
1874 if (!err && (getbuf == NULL || *getbuf != 'C')) {
1875 free(getbuf);
1876 err = 1;
1877 }
1878
1879 #if 0
1880 fprintf(stderr, "getbuf: '%s'\n", getbuf);
1881 #endif
1882
1883 if (!err) {
1884 GtkWidget *combo, *button;
1885 char line[128];
1886 gchar *label;
1887
1888 bufgets_init(getbuf);
1889
1890 combo = gtk_combo_box_text_new();
1891 g_object_set_data(G_OBJECT(vwin->main), "filter-combo", combo);
1892 widget_set_int(combo, "filter", 0);
1893
1894 combo_box_append_text(combo, _("All packages"));
1895 while (bufgets(line, sizeof line, getbuf)) {
1896 combo_box_append_text(combo, tailstrip(line));
1897 }
1898 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
1899
1900 bufgets_finalize(getbuf);
1901 free(getbuf);
1902
1903 gtk_box_pack_start(GTK_BOX(hbox), combo, FALSE, FALSE, 5);
1904 label = g_strdup_printf(" %s ", _("filter"));
1905 button = gtk_button_new_with_label(label);
1906 g_free(label);
1907 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 5);
1908 g_signal_connect(G_OBJECT(button), "clicked",
1909 G_CALLBACK(filter_remote_funcs), vwin);
1910
1911 }
1912 }
1913
add_dbnomics_menu_button(GretlToolItem * item,windata_t * vwin)1914 static void add_dbnomics_menu_button (GretlToolItem *item,
1915 windata_t *vwin)
1916 {
1917 GtkWidget *menu = get_dbn_menu(vwin);
1918
1919 vwin_toolbar_insert(item, NULL, menu, vwin, -1);
1920 }
1921
make_files_toolbar(windata_t * vwin)1922 static void make_files_toolbar (windata_t *vwin)
1923 {
1924 GtkWidget *hbox, *button;
1925 GretlToolItem *item;
1926 int i;
1927
1928 hbox = gtk_hbox_new(FALSE, 0);
1929 gtk_box_pack_start(GTK_BOX(vwin->vbox), hbox, FALSE, FALSE, 0);
1930
1931 vwin->mbar = gretl_toolbar_new(NULL);
1932
1933 for (i=0; i<n_files_items; i++) {
1934 item = &files_items[i];
1935 if (item->flag == BTN_DBN) {
1936 if (vwin->role == NATIVE_DB) {
1937 add_dbnomics_menu_button(item, vwin);
1938 }
1939 continue;
1940 }
1941 if (files_item_get_callback(item, vwin->role)) {
1942 button = gretl_toolbar_insert(vwin->mbar, item, item->func, vwin, -1);
1943 if (item->flag == BTN_ADD) {
1944 g_object_set_data(G_OBJECT(vwin->mbar), "add-button", button);
1945 gtk_widget_set_sensitive(button, FALSE);
1946 } else if (item->flag == BTN_RES) {
1947 g_object_set_data(G_OBJECT(vwin->mbar), "res-button", button);
1948 gtk_widget_set_sensitive(button, FALSE);
1949 } else if (item->flag == BTN_DOC && vwin->role != DBNOMICS_TOP) {
1950 /* condition on availablity of PDF documentation */
1951 g_object_set_data(G_OBJECT(vwin->mbar), "doc-button", button);
1952 gtk_widget_set_sensitive(button, FALSE);
1953 }
1954 }
1955 }
1956
1957 if (DBNOMICS_ACTION(vwin->role)) {
1958 const char *ids[] = {
1959 "first-button", "prev-button", "next-button", "last-button"
1960 };
1961 for (i=0; i<4; i++) {
1962 item = &pager_items[i];
1963 button = gretl_toolbar_insert(vwin->mbar, item, item->func, vwin, -1);
1964 g_object_set_data(G_OBJECT(vwin->mbar), ids[i], button);
1965 widget_set_int(button, "action", item->flag);
1966 }
1967 }
1968
1969 gtk_box_pack_start(GTK_BOX(hbox), vwin->mbar, FALSE, FALSE, 0);
1970
1971 if (vwin->role == REMOTE_FUNC_FILES) {
1972 maybe_add_gfn_filter(vwin, hbox);
1973 }
1974
1975 vwin_add_winlist(vwin);
1976 vwin_add_finder(vwin);
1977 gtk_widget_show_all(hbox);
1978 }
1979
files_title(int code)1980 static gchar *files_title (int code)
1981 {
1982 static char hname[48];
1983 gchar *ret = NULL;
1984
1985 if (*hname == '\0') {
1986 const gchar *s = g_get_host_name();
1987
1988 if (s != NULL && strlen(s) < 48) {
1989 strcpy(hname, s);
1990 } else {
1991 strcpy(hname, _("local machine"));
1992 }
1993 }
1994
1995 if (code == NATIVE_DB) {
1996 ret = g_strdup_printf(_("gretl: databases on %s"), hname);
1997 } else {
1998 ret = g_strdup_printf(_("gretl: function packages on %s"), hname);
1999 }
2000
2001 return ret;
2002 }
2003
2004 /* handle drag of pointer from remote database window
2005 to local one, or remote function package window to
2006 local one
2007 */
2008
2009 static void
remote_window_handle_drag(GtkWidget * widget,GdkDragContext * context,gint x,gint y,GtkSelectionData * data,guint info,guint time,gpointer p)2010 remote_window_handle_drag (GtkWidget *widget,
2011 GdkDragContext *context,
2012 gint x,
2013 gint y,
2014 GtkSelectionData *data,
2015 guint info,
2016 guint time,
2017 gpointer p)
2018 {
2019 if (data != NULL &&
2020 (info == GRETL_REMOTE_DB_PTR ||
2021 info == GRETL_REMOTE_FNPKG_PTR)) {
2022 drag_file_from_server(info);
2023 }
2024 }
2025
set_up_viewer_drag_target(windata_t * vwin)2026 static void set_up_viewer_drag_target (windata_t *vwin)
2027 {
2028 GCallback callback;
2029 int i;
2030
2031 if (vwin->role == NATIVE_DB) {
2032 i = GRETL_REMOTE_DB_PTR;
2033 callback = G_CALLBACK(remote_window_handle_drag);
2034 } else if (vwin->role == FUNC_FILES) {
2035 i = GRETL_REMOTE_FNPKG_PTR;
2036 callback = G_CALLBACK(remote_window_handle_drag);
2037 } else {
2038 return;
2039 }
2040
2041 gtk_drag_dest_set(vwin->listbox,
2042 GTK_DEST_DEFAULT_ALL,
2043 &gretl_drag_targets[i], 1,
2044 GDK_ACTION_COPY);
2045
2046 g_signal_connect(G_OBJECT(vwin->listbox), "drag-data-received",
2047 callback, NULL);
2048 }
2049
listbox_select_first(windata_t * vwin)2050 void listbox_select_first (windata_t *vwin)
2051 {
2052 GtkTreeView *view = GTK_TREE_VIEW(vwin->listbox);
2053 GtkTreeModel *model;
2054 GtkTreeSelection *selection;
2055 GtkTreeIter iter;
2056
2057 model = gtk_tree_view_get_model(view);
2058 gtk_tree_model_get_iter_first(model, &iter);
2059 selection = gtk_tree_view_get_selection(view);
2060 gtk_tree_selection_select_iter(selection, &iter);
2061 gtk_widget_grab_focus(vwin->listbox);
2062 }
2063
2064 #define notebook_needed(r) (r==TEXTBOOK_DATA || r==PS_FILES)
2065
display_files(int role,const gchar * path)2066 void display_files (int role, const gchar *path)
2067 {
2068 GtkWidget *filebox;
2069 windata_t *vwin;
2070 gchar *title = NULL;
2071 gchar *mypath = NULL;
2072 int err = 0;
2073
2074 vwin = get_browser_for_role(role, path);
2075 if (vwin != NULL) {
2076 gtk_window_present(GTK_WINDOW(vwin->main));
2077 return;
2078 }
2079
2080 if (role == PKG_REGISTRY && n_user_handled_packages() == 0) {
2081 infobox(_("The gui package registry is empty"));
2082 return;
2083 }
2084
2085 if (role == FUNC_FILES || role == NATIVE_DB) {
2086 title = files_title(role);
2087 } else if (role == PS_FILES) {
2088 title = g_strdup(_("gretl: example scripts"));
2089 } else if (role == TEXTBOOK_DATA) {
2090 title = g_strdup(_("gretl: data files"));
2091 } else if (role == REMOTE_DB) {
2092 title = g_strdup(_("gretl: databases on server"));
2093 } else if (role == DBNOMICS_TOP) {
2094 title = g_strdup(_("gretl: DB.NOMICS providers"));
2095 } else if (role == REMOTE_FUNC_FILES) {
2096 title = g_strdup(_("gretl: function packages on server"));
2097 } else if (role == REMOTE_DATA_PKGS) {
2098 title = g_strdup(_("gretl: data packages on server"));
2099 } else if (role == REMOTE_ADDONS) {
2100 title = g_strdup(_("gretl: addons"));
2101 } else if (role == PKG_REGISTRY) {
2102 title = g_strdup(_("gretl: packages on menus"));
2103 } else if (role == DBNOMICS_DB) {
2104 mypath = g_strdup(path);
2105 title = g_strdup_printf("gretl: %s datasets", path);
2106 } else if (role == DBNOMICS_SERIES) {
2107 mypath = g_strdup(path);
2108 title = g_strdup_printf("gretl: %s", path);
2109 }
2110
2111 vwin = gretl_browser_new(role, title);
2112 g_free(title);
2113
2114 if (role == REMOTE_DB) {
2115 gtk_window_set_default_size(GTK_WINDOW(vwin->main), 640, 480);
2116 }
2117
2118 /* vertical box to hold file-listing widget and other elements */
2119 vwin->vbox = gtk_vbox_new(FALSE, 1);
2120 gtk_box_set_spacing(GTK_BOX(vwin->vbox), 4);
2121 gtk_container_set_border_width(GTK_CONTAINER(vwin->vbox), 4);
2122 gtk_container_add(GTK_CONTAINER(vwin->main), vwin->vbox);
2123
2124 make_files_toolbar(vwin);
2125
2126 if (notebook_needed(role)) {
2127 /* we'll need more than one tab */
2128 filebox = files_notebook(vwin, role);
2129 } else {
2130 /* no tabs needed */
2131 filebox = files_vbox(vwin);
2132 }
2133
2134 if (filebox == NULL) {
2135 gtk_widget_destroy(vwin->main);
2136 return;
2137 }
2138
2139 gtk_box_pack_start(GTK_BOX(vwin->vbox), filebox, TRUE, TRUE, 0);
2140
2141 if (role == TEXTBOOK_DATA) {
2142 file_collection *collection;
2143 GList *L = collections_for_role(role);
2144
2145 build_datafiles_popup(vwin);
2146 while (L) {
2147 collection = L->data;
2148 g_signal_connect(G_OBJECT(collection->listbox), "button-press-event",
2149 G_CALLBACK(popup_menu_handler),
2150 vwin->popup);
2151 L = L->next;
2152 }
2153 } else if (role == FUNC_FILES || role == REMOTE_FUNC_FILES ||
2154 role == REMOTE_ADDONS || role == PKG_REGISTRY) {
2155 g_signal_connect(G_OBJECT(vwin->listbox), "button-press-event",
2156 G_CALLBACK(funcfiles_popup_handler),
2157 vwin);
2158 if (role == FUNC_FILES) {
2159 connect_menu_adjust_signal(vwin);
2160 }
2161 } else if (role == NATIVE_DB || role == REMOTE_DB) {
2162 build_db_popup(vwin);
2163 g_signal_connect(G_OBJECT(vwin->listbox), "button-press-event",
2164 G_CALLBACK(popup_menu_handler),
2165 vwin->popup);
2166 } else if (role == REMOTE_DATA_PKGS) {
2167 build_data_pkg_popup(vwin);
2168 g_signal_connect(G_OBJECT(vwin->listbox), "button-press-event",
2169 G_CALLBACK(popup_menu_handler),
2170 vwin->popup);
2171 }
2172
2173 if (REMOTE_ACTION(role)) {
2174 GtkWidget *hbox;
2175
2176 hbox = gtk_hbox_new(FALSE, 0);
2177 gtk_box_pack_start(GTK_BOX(vwin->vbox), hbox, FALSE, FALSE, 0);
2178 if (DBNOMICS_ACTION(role)) {
2179 vwin->status = gtk_label_new("");
2180 } else {
2181 vwin->status = gtk_label_new(_("Network status: OK"));
2182 }
2183 gtk_label_set_justify(GTK_LABEL(vwin->status), GTK_JUSTIFY_LEFT);
2184 gtk_box_pack_start(GTK_BOX(hbox), vwin->status, FALSE, FALSE, 0);
2185 }
2186
2187 /* put stuff into list box(es) */
2188 if (notebook_needed(role)) {
2189 err = populate_notebook_filelists(vwin, filebox, role);
2190 } else if (role == FUNC_FILES) {
2191 err = populate_filelist(vwin, NULL);
2192 } else if (role == NATIVE_DB) {
2193 gint w, h, ndb = 0;
2194
2195 err = populate_filelist(vwin, &ndb);
2196 if (!err && ndb > 12) {
2197 gtk_widget_get_size_request(filebox, &w, &h);
2198 h += 100;
2199 gtk_widget_set_size_request(filebox, w, h);
2200 }
2201 } else {
2202 err = populate_filelist(vwin, mypath);
2203 }
2204
2205 if (err) {
2206 gtk_widget_destroy(vwin->main);
2207 } else {
2208 gtk_widget_show_all(vwin->main);
2209 gtk_widget_grab_focus(vwin->listbox);
2210 if (role == NATIVE_DB || role == FUNC_FILES) {
2211 set_up_viewer_drag_target(vwin);
2212 }
2213 }
2214
2215 if (err) {
2216 return;
2217 }
2218
2219 if (role != TEXTBOOK_DATA && role != PS_FILES) {
2220 listbox_select_first(vwin);
2221 }
2222 }
2223
display_files_code(const gchar * s)2224 static int display_files_code (const gchar *s)
2225 {
2226 if (!strcmp(s, "DisplayDataFiles"))
2227 return TEXTBOOK_DATA;
2228 if (!strcmp(s, "DisplayScripts"))
2229 return PS_FILES;
2230 if (!strcmp(s, "NativeDB"))
2231 return NATIVE_DB;
2232 if (!strcmp(s, "RemoteDB"))
2233 return REMOTE_DB;
2234 if (!strcmp(s, "LocalGfn"))
2235 return FUNC_FILES;
2236 if (!strcmp(s, "RemoteGfn"))
2237 return REMOTE_FUNC_FILES;
2238 if (!strcmp(s, "SFAddons"))
2239 return REMOTE_ADDONS;
2240 if (!strcmp(s, "DBNbrowse"))
2241 return DBNOMICS_TOP;
2242
2243 return 0;
2244 }
2245
2246 /* make a browser window to display a set of files: textbook
2247 data files, example (formerly: practice) scripts, databases...
2248 */
2249
show_files(GtkAction * action,gpointer p)2250 void show_files (GtkAction *action, gpointer p)
2251 {
2252 int code = display_files_code(gtk_action_get_name(action));
2253
2254 display_files(code, NULL);
2255 }
2256
show_native_dbs(void)2257 void show_native_dbs (void)
2258 {
2259 display_files(NATIVE_DB, NULL);
2260 }
2261
2262 /* functions pertaining to gfn function packages */
2263
populate_gfn_registry_list(windata_t * vwin)2264 static int populate_gfn_registry_list (windata_t *vwin)
2265 {
2266 GtkListStore *store;
2267 GtkTreeIter iter;
2268 char *name;
2269 char *path;
2270 char *label;
2271 int modelwin;
2272 int i, n;
2273
2274 store = GTK_LIST_STORE(gtk_tree_view_get_model
2275 (GTK_TREE_VIEW(vwin->listbox)));
2276 gtk_list_store_clear(store);
2277 gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter);
2278
2279 n = n_registered_packages();
2280
2281 for (i=0; i<n; i++) {
2282 get_registered_pkg_info(i, &name, &path, &label, &modelwin);
2283 if (name != NULL && path != NULL) {
2284 gchar *fullpath, *upath, *s = path;
2285
2286 if (!strncmp(s, "/menubar/", 9)) {
2287 s += 9;
2288 }
2289 upath = user_friendly_menu_path(s, modelwin);
2290 if (upath != NULL) {
2291 fullpath = g_strdup_printf("%s/%s", upath, label);
2292 } else {
2293 fullpath = g_strdup_printf("%s/%s", s, label);
2294 }
2295 gtk_list_store_append(store, &iter);
2296 gtk_list_store_set(store, &iter,
2297 0, name,
2298 1, modelwin ? "Model" : "Main",
2299 2, fullpath,
2300 -1);
2301 g_free(upath);
2302 g_free(fullpath);
2303 }
2304 }
2305
2306 return 0;
2307 }
2308
get_func_info(const char * path,char ** pdesc,char ** pver,char ** pdate,char ** pauthor,int * pdfdoc)2309 static int get_func_info (const char *path, char **pdesc,
2310 char **pver, char **pdate,
2311 char **pauthor, int *pdfdoc)
2312 {
2313 int err;
2314
2315 err = get_function_file_header(path, pdesc, pver, pdate,
2316 pauthor, pdfdoc);
2317 if (err) {
2318 gui_errmsg(err);
2319 }
2320
2321 return err;
2322 }
2323
real_duplicate(const char * fname,const char * version,const char * dirname,GtkTreeModel * model,GtkTreeIter * iter)2324 static int real_duplicate (const char *fname,
2325 const char *version,
2326 const char *dirname,
2327 GtkTreeModel *model,
2328 GtkTreeIter *iter)
2329 {
2330 gchar *dupdir = NULL;
2331
2332 gtk_tree_model_get(model, iter, GFN_DIRNAME_COL, &dupdir, -1);
2333
2334 if (!strcmp(dirname, dupdir)) {
2335 /* it's actually the same file */
2336 return 0;
2337 }
2338
2339 fprintf(stderr, "duplicated function package: %s %s\n",
2340 fname, version);
2341 fprintf(stderr, " %s [first instance found]\n %s [duplicate]\n\n",
2342 dupdir, dirname);
2343 g_free(dupdir);
2344
2345 return 1;
2346 }
2347
fn_file_is_duplicate(const char * fname,const char * version,const char * dirname,GtkListStore * store,int imax)2348 static int fn_file_is_duplicate (const char *fname,
2349 const char *version,
2350 const char *dirname,
2351 GtkListStore *store,
2352 int imax)
2353 {
2354 GtkTreeModel *model = GTK_TREE_MODEL(store);
2355 GtkTreeIter iter;
2356 int ret = 0;
2357
2358 /* search from the top of @model to position @imax for
2359 a row that matches on package name and version
2360 */
2361
2362 if (imax > 0 && gtk_tree_model_get_iter_first(model, &iter)) {
2363 gchar *fname_i;
2364 gchar *version_i;
2365 int i, n;
2366
2367 n = strlen(fname) - 4;
2368
2369 for (i=0; i<imax; i++) {
2370 gtk_tree_model_get(model, &iter,
2371 0, &fname_i,
2372 1, &version_i,
2373 -1);
2374 if (strncmp(fname, fname_i, n) == 0 &&
2375 strcmp(version, version_i) == 0 &&
2376 real_duplicate(fname, version, dirname, model, &iter)) {
2377 ret = 1;
2378 }
2379 g_free(fname_i);
2380 g_free(version_i);
2381 if (ret || !gtk_tree_model_iter_next(model, &iter)) {
2382 break;
2383 }
2384 }
2385 }
2386
2387 return ret;
2388 }
2389
is_functions_dir(const char * path)2390 static int is_functions_dir (const char *path)
2391 {
2392 int n = strlen(path) - 9;
2393
2394 return n > 0 && !strcmp(path + n, "functions");
2395 }
2396
2397 /* For a function package that lives in its own directory,
2398 see if it has an "examples" subdir.
2399 */
2400
have_examples(const char * dirname)2401 static int have_examples (const char *dirname)
2402 {
2403 struct stat sbuf;
2404 gchar *test;
2405 int ret = 0;
2406
2407 test = g_strdup_printf("%s%cexamples", dirname, SLASH);
2408
2409 if (stat(test, &sbuf) == 0 &&
2410 (sbuf.st_mode & S_IFDIR)) {
2411 ret = 1;
2412 }
2413
2414 g_free(test);
2415
2416 return ret;
2417 }
2418
maybe_ellipsize_string(char * s,int maxlen)2419 char *maybe_ellipsize_string (char *s, int maxlen)
2420 {
2421 size_t n = g_utf8_strlen(s, -1);
2422
2423 if (n > maxlen) {
2424 gretl_utf8_truncate(s, maxlen - 3);
2425 strcat(s, "...");
2426 }
2427
2428 return s;
2429 }
2430
2431 /* note: @summary is not const because it may get truncated */
2432
browser_insert_gfn_info(const char * pkgname,const char * version,const char * date,const char * author,char * summary,const char * dirname,int uses_subdir,int pdfdoc,GtkListStore * store,GtkTreeIter * iter)2433 static void browser_insert_gfn_info (const char *pkgname,
2434 const char *version,
2435 const char *date,
2436 const char *author,
2437 char *summary,
2438 const char *dirname,
2439 int uses_subdir,
2440 int pdfdoc,
2441 GtkListStore *store,
2442 GtkTreeIter *iter)
2443 {
2444 char *tmp = NULL;
2445 gint flags = 0;
2446
2447 if (uses_subdir) {
2448 if (have_examples(dirname)) {
2449 flags |= PKG_ATTR_RES;
2450 }
2451 if (pdfdoc) {
2452 flags |= PKG_ATTR_DOC;
2453 }
2454 }
2455
2456 if (g_utf8_strlen(author, -1) > 26) {
2457 tmp = gretl_strdup(author);
2458 maybe_ellipsize_string(tmp, 26);
2459 }
2460 maybe_ellipsize_string(summary, 68);
2461
2462 gtk_list_store_set(store, iter,
2463 0, pkgname,
2464 1, version,
2465 2, date,
2466 3, tmp ? tmp : author,
2467 4, summary,
2468 5, dirname,
2469 6, flags,
2470 -1);
2471 free(tmp);
2472 }
2473
check_loaded_gfn(const char * pkgname,const char * fullname)2474 static void check_loaded_gfn (const char *pkgname,
2475 const char *fullname)
2476 {
2477 const char *path;
2478
2479 path = get_function_package_path_by_name(pkgname);
2480
2481 if (path != NULL && strcmp(path, fullname)) {
2482 /* Watch out, there's a package of the same name already
2483 loaded from a different location.
2484 */
2485 fnpkg *pkg = get_function_package_by_filename(path, NULL);
2486 GtkWidget *editor = function_package_get_editor(pkg);
2487
2488 if (editor != NULL) {
2489 /* warn, don't unload the package being edited */
2490 gchar *msg;
2491
2492 msg = g_strdup_printf("%s: package in new browser window may\n"
2493 "conflict with package editor", pkgname);
2494 gtk_window_present(GTK_WINDOW(editor));
2495 msgbox(msg, GTK_MESSAGE_WARNING, editor);
2496 g_free(msg);
2497 } else {
2498 function_package_unload_full_by_filename(path);
2499 }
2500 }
2501 }
2502
2503 /* Run various checks on @fullname: if all is OK, return 1; if
2504 something is amiss, return 0.
2505 */
2506
ok_gfn_path(const char * fullname,const char * shortname,const char * dirname,GtkListStore * store,GtkTreeIter * iter,int imax,int subdir)2507 static int ok_gfn_path (const char *fullname,
2508 const char *shortname,
2509 const char *dirname,
2510 GtkListStore *store,
2511 GtkTreeIter *iter,
2512 int imax,
2513 int subdir)
2514 {
2515 char *descrip = NULL;
2516 char *version = NULL;
2517 char *date = NULL;
2518 char *author = NULL;
2519 int pdfdoc = 0;
2520 int is_dup = 0;
2521 int err, ok = 0;
2522
2523 /* Note that even if this is a dry run with @store = NULL,
2524 it may be worth performing the next action as a sanity
2525 check on the purported gfn.
2526 */
2527 err = get_func_info(fullname, &descrip, &version, &date,
2528 &author, &pdfdoc);
2529
2530 if (!err && store != NULL) {
2531 is_dup = fn_file_is_duplicate(shortname, version,
2532 dirname, store, imax);
2533 }
2534
2535 #if GFN_DEBUG > 1
2536 fprintf(stderr, "%s: %s: dups_ok=%d, is_dup=%d\n", dirname,
2537 shortname, dups_ok, is_dup);
2538 #endif
2539
2540 if (!err && !is_dup) {
2541 if (store != NULL && iter != NULL) {
2542 /* actually enter the file into the browser */
2543 gchar *pkgname = g_strndup(shortname, strlen(shortname) - 4);
2544
2545 if (gfn_is_loaded(shortname)) {
2546 check_loaded_gfn(pkgname, fullname);
2547 }
2548
2549 gtk_list_store_append(store, iter);
2550 browser_insert_gfn_info(pkgname,
2551 version,
2552 date,
2553 author,
2554 descrip,
2555 dirname,
2556 subdir,
2557 pdfdoc,
2558 store,
2559 iter);
2560
2561 g_free(pkgname);
2562 }
2563 ok = 1;
2564 }
2565
2566 free(descrip);
2567 free(version);
2568 free(date);
2569 free(author);
2570
2571 return ok;
2572 }
2573
2574 /* Read (or simply just count) the .gfn files in a given directory.
2575 The signal to count rather than read is that the @store argument
2576 is NULL.
2577 */
2578
2579 static void
read_fn_files_in_dir(GDir * dir,const char * path,GtkListStore * store,GtkTreeIter * iter,int * nfn)2580 read_fn_files_in_dir (GDir *dir, const char *path,
2581 GtkListStore *store,
2582 GtkTreeIter *iter,
2583 int *nfn)
2584 {
2585 const gchar *basename;
2586 char fullname[MAXLEN];
2587 int imax = *nfn;
2588
2589 /* Look first for a gfn file in its own subdir, as
2590 in functions/foo/foo.gfn. That way if a package
2591 has been updated to zipfile status and there's
2592 also an older "plain gfn" version lying around
2593 we should get the newer one.
2594 */
2595
2596 if (is_functions_dir(path)) {
2597 while ((basename = g_dir_read_name(dir)) != NULL) {
2598 if (!strcmp(basename, ".") ||
2599 !strcmp(basename, "..")) {
2600 continue;
2601 }
2602 gretl_build_path(fullname, path, basename, NULL);
2603 if (gretl_isdir(fullname)) {
2604 /* construct functions/foo/foo.gfn */
2605 gchar *realbase, *realpath;
2606
2607 strcat(fullname, SLASHSTR);
2608 strcat(fullname, basename);
2609 strcat(fullname, ".gfn");
2610 if (gretl_file_exists(fullname)) {
2611 realbase = g_strdup_printf("%s.gfn", basename);
2612 realpath = g_strdup_printf("%s%c%s", path, SLASH, basename);
2613 *nfn += ok_gfn_path(fullname, realbase, realpath,
2614 store, iter, imax, 1);
2615 g_free(realbase);
2616 g_free(realpath);
2617 } else {
2618 gretl_error_clear();
2619 }
2620 }
2621 }
2622
2623 imax = *nfn;
2624 g_dir_rewind(dir);
2625 }
2626
2627 /* then look for "plain gfn" files */
2628
2629 while ((basename = g_dir_read_name(dir)) != NULL) {
2630 if (!strcmp(basename, ".") ||
2631 !strcmp(basename, "..")) {
2632 continue;
2633 }
2634 if (has_suffix(basename, ".gfn")) {
2635 gretl_build_path(fullname, path, basename, NULL);
2636 *nfn += ok_gfn_path(fullname, basename, path,
2637 store, iter, imax, 0);
2638 }
2639 }
2640 }
2641
2642 #if GFN_DEBUG
2643
show_dirs_list(char ** S,int n,const char * msg)2644 static void show_dirs_list (char **S, int n, const char *msg)
2645 {
2646 int i;
2647
2648 fprintf(stderr, "*** dirs list: %s\n", msg);
2649 for (i=0; i<n; i++) {
2650 fprintf(stderr, " %d: '%s'\n", i, S[i]);
2651 }
2652 }
2653
2654 #endif
2655
2656 /* Populate browser displaying gfn files installed on local machine:
2657 this is always called with a "clean slate": either we're showing a
2658 new browser window, or we're recreating the default listing after
2659 the user has pointed the browser at another directory (in which
2660 case the prior listing has been cleared out by the time we get
2661 here).
2662 */
2663
populate_gfn_list(windata_t * vwin)2664 static gint populate_gfn_list (windata_t *vwin)
2665 {
2666 GtkListStore *store;
2667 GtkTreeIter iter;
2668 char **dnames = NULL;
2669 int i, n_dirs = 0;
2670 int nfn = 0;
2671 int err = 0;
2672
2673 store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vwin->listbox)));
2674 gtk_list_store_clear(store);
2675 gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter);
2676
2677 /* compile an array of names of directories to search */
2678 dnames = get_plausible_search_dirs(FUNCS_SEARCH, &n_dirs);
2679
2680 #if GFN_DEBUG
2681 show_dirs_list(dnames, n_dirs, "FUNCS_SEARCH");
2682 #endif
2683
2684 for (i=0; i<n_dirs; i++) {
2685 GDir *dir = gretl_opendir(dnames[i]);
2686
2687 if (dir != NULL) {
2688 read_fn_files_in_dir(dir, dnames[i], store, &iter, &nfn);
2689 g_dir_close(dir);
2690 }
2691 }
2692
2693 /* we're done with the directory names */
2694 strings_array_free(dnames, n_dirs);
2695
2696 if (nfn == 0) {
2697 /* we didn't find any gfn files */
2698 warnbox(_("No gretl function packages were found on this computer.\n"
2699 "Please try /File/Functions packages/On server"));
2700 err = 1;
2701 } else {
2702 presort_treelist(vwin);
2703 }
2704
2705 return err;
2706 }
2707
gfn_paths_match(const char * p0,const char * p1,const char * pkgname)2708 static int gfn_paths_match (const char *p0, const char *p1,
2709 const char *pkgname)
2710 {
2711 int ret = 0;
2712
2713 if (!strcmp(p0, p1)) {
2714 ret = 1;
2715 } else {
2716 /* allow for the possibility that @p1 has had a
2717 package-specific subdir appended, relative to @p0
2718 */
2719 size_t n = strlen(p0);
2720
2721 if (strlen(p1) > n && !strncmp(p1, p0, n) &&
2722 IS_SLASH(p1[n])) {
2723 ret = !strcmp(p1 + n + 1, pkgname);
2724 }
2725 }
2726
2727 return ret;
2728 }
2729
update_gfn_browser(const char * pkgname,const char * version,const char * date,const char * author,const char * descrip,const char * fname,int uses_subdir,int pdfdoc,windata_t * vwin)2730 static void update_gfn_browser (const char *pkgname,
2731 const char *version,
2732 const char *date,
2733 const char *author,
2734 const char *descrip,
2735 const char *fname,
2736 int uses_subdir,
2737 int pdfdoc,
2738 windata_t *vwin)
2739 {
2740 GtkTreeModel *model;
2741 GtkTreeIter iter;
2742 gchar *p, *dirname;
2743 gchar *summary;
2744 gchar *oldname, *oldver, *olddir;
2745 int dirmatch = 0;
2746 int done = 0;
2747
2748 model = gtk_tree_view_get_model(GTK_TREE_VIEW(vwin->listbox));
2749 if (!gtk_tree_model_get_iter_first(model, &iter)) {
2750 return;
2751 }
2752
2753 dirname = g_strdup(fname);
2754 p = strrslash(dirname);
2755 if (p != NULL) {
2756 *p = '\0';
2757 }
2758
2759 /* in case of truncation */
2760 summary = g_strdup(descrip);
2761
2762 while (1) {
2763 gtk_tree_model_get(model, &iter, 0, &oldname, 1, &oldver,
2764 GFN_DIRNAME_COL, &olddir, -1);
2765 if (!strcmp(oldname, pkgname) && !strcmp(oldver, version)) {
2766 /* Found a match for package name and version: so update
2767 the browser entry and record that we're done.
2768 */
2769 fprintf(stderr, "gfn update: updating %s %s\n", pkgname, version);
2770 browser_insert_gfn_info(pkgname, version, date, author, summary,
2771 dirname, uses_subdir, pdfdoc,
2772 GTK_LIST_STORE(model), &iter);
2773 done = 1;
2774 } else if (!dirmatch) {
2775 dirmatch = gfn_paths_match(olddir, dirname, pkgname);
2776 }
2777 g_free(oldname); g_free(oldver); g_free(olddir);
2778 if (done || !gtk_tree_model_iter_next(model, &iter)) {
2779 break;
2780 }
2781 }
2782
2783 if (!done && dirmatch) {
2784 /* We didn't find an entry that matched by pkgname and
2785 version, but we did determine that the browser was
2786 pointing at a directory in which the package in
2787 question would be found, if it were re-read. So it
2788 seems we should append the package (and re-sort the
2789 package list).
2790 */
2791 fprintf(stderr, "gfn update: appending %s %s\n", pkgname, version);
2792 gtk_list_store_append(GTK_LIST_STORE(model), &iter);
2793 browser_insert_gfn_info(pkgname, version, date, author, summary,
2794 dirname, uses_subdir, pdfdoc,
2795 GTK_LIST_STORE(model), &iter);
2796 presort_treelist(vwin);
2797 }
2798
2799 g_free(dirname);
2800 g_free(summary);
2801 }
2802
2803 /* Update function package status, if needed, either after
2804 a call to save a function package, or after deleting a
2805 package by CLI means; the latter case is flagged by
2806 NULL values for @version and @descrip.
2807 */
2808
maybe_update_gfn_browser(const char * pkgname,const char * version,const char * date,const char * author,const char * descrip,const char * fname,int uses_subdir,int pdfdoc)2809 void maybe_update_gfn_browser (const char *pkgname,
2810 const char *version,
2811 const char *date,
2812 const char *author,
2813 const char *descrip,
2814 const char *fname,
2815 int uses_subdir,
2816 int pdfdoc)
2817 {
2818 windata_t *vwin = get_browser_for_role(FUNC_FILES, NULL);
2819 int del = (version == NULL && descrip == NULL);
2820
2821 if (vwin != NULL && vwin->listbox != NULL) {
2822 if (del) {
2823 browser_delete_row_by_content(vwin, 0, pkgname,
2824 GFN_DIRNAME_COL, fname);
2825 } else {
2826 update_gfn_browser(pkgname, version, date, author, descrip,
2827 fname, uses_subdir, pdfdoc, vwin);
2828 }
2829 }
2830 }
2831
maybe_update_pkg_registry_window(const char * pkgname,int code)2832 void maybe_update_pkg_registry_window (const char *pkgname,
2833 int code)
2834 {
2835 windata_t *vwin = get_browser_for_role(PKG_REGISTRY, NULL);
2836
2837 if (vwin != NULL && vwin->listbox != NULL) {
2838 if (code == MENU_ADD_FN_PKG) {
2839 populate_gfn_registry_list(vwin);
2840 } else if (code == DELETE_FN_PKG) {
2841 browser_delete_row_by_content(vwin, 0, pkgname,
2842 0, NULL);
2843 }
2844 }
2845 }
2846
populate_filelist(windata_t * vwin,gpointer p)2847 gint populate_filelist (windata_t *vwin, gpointer p)
2848 {
2849 if (vwin->role == NATIVE_DB) {
2850 return populate_dbfilelist(vwin, p);
2851 } else if (vwin->role == REMOTE_DB) {
2852 return populate_remote_db_list(vwin);
2853 } else if (vwin->role == DBNOMICS_TOP) {
2854 return populate_dbnomics_provider_list(vwin);
2855 } else if (vwin->role == DBNOMICS_DB) {
2856 return populate_dbnomics_dataset_list(vwin, p);
2857 } else if (vwin->role == DBNOMICS_SERIES) {
2858 return populate_dbnomics_series_list(vwin, p);
2859 } else if (vwin->role == REMOTE_FUNC_FILES) {
2860 return populate_remote_func_list(vwin, 0);
2861 } else if (vwin->role == REMOTE_DATA_PKGS) {
2862 return populate_remote_data_pkg_list(vwin);
2863 } else if (vwin->role == FUNC_FILES) {
2864 return populate_gfn_list(vwin);
2865 } else if (vwin->role == REMOTE_ADDONS) {
2866 return populate_remote_addons_list(vwin);
2867 } else if (vwin->role == PKG_REGISTRY) {
2868 return populate_gfn_registry_list(vwin);
2869 } else {
2870 return read_file_descriptions(vwin, p);
2871 }
2872 }
2873
files_vbox(windata_t * vwin)2874 static GtkWidget *files_vbox (windata_t *vwin)
2875 {
2876 const char *data_titles[] = {
2877 N_("File"),
2878 N_("Summary"),
2879 N_("Type")
2880 };
2881 const char *remote_data_titles[] = {
2882 N_("File"),
2883 N_("Source"),
2884 N_("Date")
2885 };
2886 const char *ps_titles[] = {
2887 N_("Script"),
2888 N_("Topic"),
2889 N_("Data")
2890 };
2891 const char *db_titles[] = {
2892 N_("Database"),
2893 N_("Source")
2894 };
2895 const char *remote_db_titles[] = {
2896 N_("Database"),
2897 N_("Source"),
2898 N_("Local status")
2899 };
2900 const char *dbnomics_top_titles[] = {
2901 N_("Code"),
2902 N_("Name")
2903 };
2904 const char *dbnomics_db_titles[] = {
2905 N_("Code"),
2906 N_("Content")
2907 };
2908 const char *dbnomics_series_titles[] = {
2909 N_("Code"),
2910 N_("Description")
2911 };
2912 const char *func_titles[] = {
2913 N_("Package"),
2914 N_("Version"),
2915 N_("Date"),
2916 N_("Author"),
2917 N_("Summary")
2918 };
2919 const char *remote_func_titles[] = {
2920 N_("Package"),
2921 N_("Version"),
2922 N_("Date"),
2923 N_("Author"),
2924 N_("Summary"),
2925 N_("Local status")
2926 };
2927 const char *addons_titles[] = {
2928 N_("Package"),
2929 N_("Version"),
2930 N_("Date"),
2931 N_("Local status")
2932 };
2933 const char *registry_titles[] = {
2934 N_("Package"),
2935 N_("Window"),
2936 N_("Menu")
2937 };
2938
2939 GType types_2[] = {
2940 G_TYPE_STRING,
2941 G_TYPE_STRING
2942 };
2943 GType types_3[] = {
2944 G_TYPE_STRING,
2945 G_TYPE_STRING,
2946 G_TYPE_STRING
2947 };
2948 GType func_types[] = {
2949 G_TYPE_STRING,
2950 G_TYPE_STRING,
2951 G_TYPE_STRING,
2952 G_TYPE_STRING,
2953 G_TYPE_STRING,
2954 G_TYPE_STRING, /* hidden string: directory */
2955 G_TYPE_INT /* hidden flags: has examples dir? doc? */
2956 };
2957 GType remote_func_types[] = {
2958 G_TYPE_STRING,
2959 G_TYPE_STRING,
2960 G_TYPE_STRING,
2961 G_TYPE_STRING,
2962 G_TYPE_STRING,
2963 G_TYPE_STRING,
2964 G_TYPE_BOOLEAN, /* hidden boolean: zipfile? */
2965 G_TYPE_STRING /* hidden string: dependencies */
2966 };
2967 GType addons_types[] = {
2968 G_TYPE_STRING,
2969 G_TYPE_STRING,
2970 G_TYPE_STRING,
2971 G_TYPE_STRING,
2972 G_TYPE_STRING /* hidden string: description */
2973 };
2974 const char **titles = data_titles;
2975 GType *types = types_2;
2976 int full_width = 580, file_height = 300;
2977 int hidden_cols = 0;
2978 int use_tree = 0;
2979 GtkWidget *vbox;
2980 int cols = 2;
2981
2982 switch (vwin->role) {
2983 case TEXTBOOK_DATA:
2984 titles = data_titles;
2985 cols = 3;
2986 full_width = 600;
2987 break;
2988 case NATIVE_DB:
2989 titles = db_titles;
2990 cols = 3;
2991 hidden_cols = 1;
2992 break;
2993 case REMOTE_DB:
2994 titles = remote_db_titles;
2995 cols = 3;
2996 use_tree = 1;
2997 break;
2998 case DBNOMICS_TOP:
2999 titles = dbnomics_top_titles;
3000 full_width = 650;
3001 break;
3002 case DBNOMICS_DB:
3003 titles = dbnomics_db_titles;
3004 full_width = 650;
3005 break;
3006 case DBNOMICS_SERIES:
3007 titles = dbnomics_series_titles;
3008 full_width = 600;
3009 break;
3010 case REMOTE_DATA_PKGS:
3011 titles = remote_data_titles;
3012 cols = 3;
3013 full_width = 600;
3014 break;
3015 case PS_FILES:
3016 titles = ps_titles;
3017 cols = 3;
3018 full_width = 560;
3019 break;
3020 case FUNC_FILES:
3021 titles = func_titles;
3022 types = func_types;
3023 cols = G_N_ELEMENTS(func_types);
3024 hidden_cols = 2;
3025 full_width = 760;
3026 file_height = 320;
3027 break;
3028 case REMOTE_FUNC_FILES:
3029 titles = remote_func_titles;
3030 types = remote_func_types;
3031 cols = G_N_ELEMENTS(remote_func_types);
3032 hidden_cols = 2;
3033 full_width = 760;
3034 file_height = 340;
3035 break;
3036 case REMOTE_ADDONS:
3037 titles = addons_titles;
3038 types = addons_types;
3039 cols = G_N_ELEMENTS(addons_types);
3040 hidden_cols = 1;
3041 full_width = 400;
3042 break;
3043 case PKG_REGISTRY:
3044 titles = registry_titles;
3045 cols = 3;
3046 full_width = 600;
3047 break;
3048 default:
3049 break;
3050 }
3051
3052 if (cols == 3) {
3053 types = types_3;
3054 }
3055
3056 full_width *= gui_scale;
3057 file_height *= gui_scale;
3058
3059 vbox = gtk_vbox_new(FALSE, 0);
3060 gtk_widget_set_size_request(vbox, full_width, file_height);
3061 /* note: the following packs and attaches vwin->listbox */
3062 vwin_add_list_box(vwin, GTK_BOX(vbox), cols, hidden_cols,
3063 types, titles, use_tree);
3064 gtk_widget_show(vbox);
3065
3066 return vbox;
3067 }
3068
switch_files_page(GtkNotebook * notebook,GtkWidget * page,guint pgnum,windata_t * vwin)3069 static void switch_files_page (GtkNotebook *notebook,
3070 GtkWidget *page,
3071 guint pgnum,
3072 windata_t *vwin)
3073 {
3074 GtkWidget *tab = gtk_notebook_get_nth_page(notebook, pgnum);
3075
3076 vwin->listbox = g_object_get_data(G_OBJECT(tab), "listbox");
3077 }
3078
3079 /* below: construct a set of notebook pages for either data file
3080 collections (Ramanathan, Wooldridge, etc.) or example scripts.
3081 The function creates the pages but does not yet fill them out.
3082 */
3083
files_notebook(windata_t * vwin,int role)3084 static GtkWidget *files_notebook (windata_t *vwin, int role)
3085 {
3086 GList *L = NULL;
3087 file_collection *collection;
3088 GtkWidget *notebook;
3089 GtkWidget *page;
3090 GtkWidget *label;
3091 int err = 0;
3092
3093 /* assemble the info we'll need */
3094 err = build_file_collections(role);
3095 if (err) {
3096 fprintf(stderr, "files_notebook: build_files_collections failed\n");
3097 return NULL;
3098 }
3099
3100 L = collections_for_role(role);
3101 if (L == NULL) {
3102 return NULL;
3103 }
3104
3105 notebook = gtk_notebook_new();
3106 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), TRUE);
3107
3108 while (L) {
3109 collection = L->data;
3110 page = files_vbox(vwin);
3111 label = gtk_label_new(collection->title);
3112 gtk_widget_show(label);
3113 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label);
3114 collection->listbox = vwin->listbox;
3115 g_object_set_data(G_OBJECT(collection->listbox), "collection",
3116 collection);
3117 g_object_set_data(G_OBJECT(page), "listbox", collection->listbox);
3118 g_signal_connect(G_OBJECT(collection->listbox), "key-press-event",
3119 G_CALLBACK(enter_opens_file), vwin);
3120 L = L->next;
3121 }
3122
3123 g_signal_connect(G_OBJECT(notebook), "switch-page",
3124 G_CALLBACK(switch_files_page),
3125 vwin);
3126 if (gtk_notebook_get_n_pages(GTK_NOTEBOOK(notebook)) > 5) {
3127 gtk_notebook_popup_enable(GTK_NOTEBOOK(notebook));
3128 }
3129
3130 gtk_widget_show(notebook);
3131
3132 return notebook;
3133 }
3134
3135 /* below: fill out a set of notebook pages (for data files
3136 or script files), entering the details into the page,
3137 then select the page to display
3138 */
3139
populate_notebook_filelists(windata_t * vwin,GtkWidget * notebook,int role)3140 static int populate_notebook_filelists (windata_t *vwin,
3141 GtkWidget *notebook,
3142 int role)
3143 {
3144 file_collection *collection;
3145 file_collection *selected = NULL;
3146 const char *page = NULL;
3147 GList *L = NULL;
3148 int found = 0;
3149 int pgnum = 0;
3150
3151 L = collections_for_role(role);
3152 if (L == NULL) {
3153 return 1;
3154 }
3155
3156 if (role == TEXTBOOK_DATA) {
3157 page = get_datapage();
3158 } else if (role == PS_FILES) {
3159 page = get_scriptpage();
3160 }
3161
3162 selected = L->data;
3163
3164 while (L) {
3165 collection = L->data;
3166 vwin->listbox = collection->listbox;
3167 populate_filelist(vwin, collection);
3168 if (page != NULL && !strcmp(collection->title, page)) {
3169 selected = collection;
3170 pgnum = found;
3171 }
3172 found++;
3173 L = L->next;
3174 }
3175
3176 if (found == 0) {
3177 /* didn't find anything to show */
3178 fprintf(stderr, "populate_notebook_filelists: found = 0!\n");
3179 return 1;
3180 }
3181
3182 vwin->listbox = selected->listbox;
3183 gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), pgnum);
3184 gtk_widget_grab_focus(vwin->listbox);
3185
3186 return 0;
3187 }
3188