1 /*
2 * gretl -- Gnu Regression, Econometrics and Time-series Library
3 * Copyright (C) 2001 Allin Cottrell and Riccardo "Jack" Lucchetti
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 */
19
20 #include "gretl.h"
21 #include "version.h"
22 #include "gretl_xml.h"
23 #include "libset.h"
24 #include "dlgutils.h"
25 #include "datafiles.h"
26 #include "textbuf.h"
27 #include "fileselect.h"
28 #include "filelists.h"
29 #include "gretl_www.h"
30 #include "gretl_zip.h"
31 #include "winstack.h"
32 #include "menustate.h"
33 #include "selector.h"
34 #include "textutil.h"
35 #include "ssheet.h"
36 #include "fncall.h"
37 #include "fnsave.h"
38
39 #include <libxml/xmlmemory.h>
40 #include <libxml/parser.h>
41
42 #ifdef G_OS_WIN32
43 # include "gretlwin32.h"
44 #endif
45
46 #include "gretl_func.h"
47 #include "gretl_typemap.h"
48
49 #define PKG_DEBUG 0
50 #define N_ENTRIES 5
51 #define N_FILE_ENTRIES 4
52 #define N_DEP_ENTRIES 4
53 #define N_SPECIALS (UFUN_ROLE_MAX - 1)
54 #define HELP_HEIGHT 400
55
56 enum {
57 NO_WINDOW,
58 MAIN_WINDOW,
59 MODEL_WINDOW
60 };
61
62 enum {
63 APPEND_SAMPLE = 1 << 0, /* write functions as .inp: append sample? */
64 WRITE_SAMPFILE = 1 << 1, /* write .spec file: also write sample script? */
65 WRITE_HELPFILE = 1 << 2, /* write .spec file: also write help file? */
66 WRITE_GUI_HELP = 1 << 3 /* write .spec file: also write gui help? */
67 } PkgSaveFlags;
68
69 typedef struct function_info_ function_info;
70 typedef struct login_info_ login_info;
71
72 /* package-editing dialog and associated info */
73
74 struct function_info_ {
75 GtkWidget *dlg; /* editing dialog box */
76 GtkWidget *entries[N_ENTRIES]; /* author, etc. */
77 GtkWidget *file_entries[N_FILE_ENTRIES]; /* data files */
78 GtkWidget *dep_entries[N_DEP_ENTRIES]; /* dependencies */
79 GtkWidget *prov_check; /* "provider" selected? */
80 GtkWidget *codesel; /* code-editing selector */
81 GtkWidget *popup; /* popup menu */
82 GtkWidget *extra; /* extra properties child dialog */
83 GtkWidget *maintree; /* main menu selection tree */
84 GtkWidget *modeltree; /* model menu selection tree */
85 GtkWidget *currtree; /* currently displayed menu treeview */
86 GtkWidget *alttree; /* currently undisplayed menu treeview */
87 GtkWidget *treewin; /* scrolled window to hold menu trees */
88 GtkWidget *mreq_combo; /* model requirement selector */
89 GtkWidget *data_button; /* data access request button */
90 GtkWidget *specdlg; /* pkg spec save dialog */
91 GtkWidget *validate; /* "Validate" gfn button */
92 GtkWidget *tagsel[2]; /* tag selector combos */
93 windata_t *samplewin; /* window for editing sample script */
94 windata_t *helpwin; /* window for editing regular help text */
95 windata_t *gui_helpwin; /* window for editing GUI-specific help text */
96 GtkUIManager *ui; /* for dialog File menu */
97 GList *codewins; /* list of windows editing function code */
98 fnpkg *pkg; /* pointer to package being edited */
99 gchar *ininame; /* initial name for new package (temporary) */
100 gchar *fname; /* package filename */
101 gchar *author; /* package author */
102 gchar *email; /* author's email address */
103 gchar *version; /* package version number */
104 gchar *date; /* package last-revised date */
105 gchar *pkgdesc; /* package description */
106 gchar *tags; /* package tag(s) */
107 gchar *sample; /* sample script for package */
108 gchar *help; /* package help text */
109 gchar *gui_help; /* GUI-specific help text */
110 gchar *sample_fname; /* filename: sample script */
111 gchar *help_fname; /* filename: help text */
112 gchar *gui_help_fname; /* filename: GUI-specific help */
113 gchar *pdfname; /* name of PDF help file */
114 char **pubnames; /* names of public functions */
115 char **privnames; /* names of private functions */
116 char **specials; /* names of special functions */
117 char **datafiles; /* names of included data files */
118 char **depends; /* names of dependencies */
119 int n_pub; /* number of public functions */
120 int n_priv; /* number of private functions */
121 int n_files; /* number of included data files */
122 int n_depends; /* number of dependencies */
123 gchar *provider; /* name of "provider" package */
124 gboolean uses_subdir; /* the package has its own subdir (0/1) */
125 gboolean data_access; /* the package wants access to full data range */
126 gboolean pdfdoc; /* the package has PDF documentation */
127 gchar *menupath; /* path for menu attachment, if any */
128 gchar *menulabel; /* label for menu attachment, if any */
129 int menuwin; /* code for none/main/model window */
130 char *active; /* name of 'active' function */
131 DataReq dreq; /* data requirement of package */
132 GretlCmdIndex mreq; /* model requirement of package */
133 int minver; /* minimum gretl version, package */
134 gboolean modified; /* anything changed in package? */
135 int save_flags; /* see PkgSaveFlags */
136 unsigned char gui_attrs[N_SPECIALS]; /* attribute flags for special funcs */
137 };
138
139 /* info relating to login to server for upload */
140
141 struct login_info_ {
142 GtkWidget *dlg;
143 GtkWidget *login_entry;
144 GtkWidget *pass_entry;
145 char *login;
146 char *pass;
147 int canceled;
148 };
149
150 static int validate_package_file (const char *fname,
151 int verbose);
152 static void finfo_set_menuwin (function_info *finfo);
153 static gint query_save_package (GtkWidget *w, GdkEvent *event,
154 function_info *finfo);
155 static int finfo_save (function_info *finfo);
156 static void gfn_to_script_callback (function_info *finfo);
157 static void gfn_to_spec_callback (function_info *finfo);
158 static void do_pkg_upload (function_info *finfo);
159 static void edit_code_callback (GtkWidget *w, function_info *finfo);
160 static int check_package_filename (const char *fname,
161 int fullpath,
162 GtkWidget *parent);
163 static void regular_help_text_callback (GtkButton *b,
164 function_info *finfo);
165 static void edit_sample_callback (GtkWidget *w, function_info *finfo);
166 static const char *finfo_pkgname (function_info *finfo);
167
finfo_new(void)168 function_info *finfo_new (void)
169 {
170 function_info *finfo;
171
172 finfo = mymalloc(sizeof *finfo);
173 if (finfo == NULL) {
174 return NULL;
175 }
176
177 finfo->specials = strings_array_new(N_SPECIALS);
178 if (finfo->specials == NULL) {
179 free(finfo);
180 return NULL;
181 }
182
183 memset(finfo->gui_attrs, 0, N_SPECIALS);
184
185 finfo->pkg = NULL;
186 finfo->fname = NULL;
187 finfo->ininame = NULL;
188 finfo->author = NULL;
189 finfo->email = NULL;
190 finfo->version = NULL;
191 finfo->date = NULL;
192 finfo->pkgdesc = NULL;
193 finfo->tags = NULL;
194 finfo->sample = NULL;
195 finfo->menupath = NULL;
196 finfo->menulabel = NULL;
197 finfo->menuwin = 0;
198
199 finfo->currtree = NULL;
200 finfo->alttree = NULL;
201 finfo->treewin = NULL;
202 finfo->ui = NULL;
203
204 finfo->modified = FALSE;
205 finfo->save_flags = WRITE_SAMPFILE |
206 WRITE_HELPFILE | WRITE_GUI_HELP;
207
208 finfo->active = NULL;
209 finfo->samplewin = NULL;
210 finfo->helpwin = NULL;
211 finfo->gui_helpwin = NULL;
212 finfo->codewins = NULL;
213 finfo->codesel = NULL;
214 finfo->popup = NULL;
215 finfo->extra = NULL;
216 finfo->specdlg = NULL;
217
218 finfo->tagsel[0] = NULL;
219 finfo->tagsel[1] = NULL;
220
221 finfo->help = NULL;
222 finfo->gui_help = NULL;
223
224 finfo->sample_fname = NULL;
225 finfo->help_fname = NULL;
226 finfo->gui_help_fname = NULL;
227 finfo->pdfname = NULL;
228
229 finfo->pubnames = NULL;
230 finfo->privnames = NULL;
231 finfo->datafiles = NULL;
232 finfo->depends = NULL;
233
234 finfo->n_pub = 0;
235 finfo->n_priv = 0;
236 finfo->n_files = 0;
237 finfo->n_depends = 0;
238 finfo->provider = NULL;
239
240 finfo->dreq = 0;
241 finfo->minver = 10900;
242 finfo->uses_subdir = 0;
243 finfo->data_access = 0;
244 finfo->pdfdoc = 0;
245
246 return finfo;
247 }
248
funname_from_filename(const char * fname)249 static const char *funname_from_filename (const char *fname)
250 {
251 const char *p = strrchr(fname, '.');
252
253 return p + 1;
254 }
255
filename_from_funname(char * fname,const char * funname)256 static char *filename_from_funname (char *fname,
257 const char *funname)
258 {
259 gretl_build_path(fname, gretl_dotdir(), "pkgedit", NULL);
260 strcat(fname, ".");
261 strcat(fname, funname);
262 return fname;
263 }
264
destroy_code_window(windata_t * vwin,gpointer p)265 static void destroy_code_window (windata_t *vwin, gpointer p)
266 {
267 gtk_widget_destroy(vwin->main);
268 }
269
finfo_free(function_info * finfo)270 static void finfo_free (function_info *finfo)
271 {
272 g_free(finfo->fname);
273 g_free(finfo->author);
274 g_free(finfo->email);
275 g_free(finfo->version);
276 g_free(finfo->date);
277 g_free(finfo->pkgdesc);
278 g_free(finfo->tags);
279 g_free(finfo->sample);
280 g_free(finfo->help);
281 g_free(finfo->gui_help);
282
283 g_free(finfo->ininame);
284 g_free(finfo->sample_fname);
285 g_free(finfo->help_fname);
286 g_free(finfo->gui_help_fname);
287 g_free(finfo->pdfname);
288
289 g_free(finfo->menupath);
290 g_free(finfo->menulabel);
291
292 if (finfo->pubnames != NULL) {
293 strings_array_free(finfo->pubnames, finfo->n_pub);
294 }
295
296 if (finfo->privnames != NULL) {
297 strings_array_free(finfo->privnames, finfo->n_priv);
298 }
299
300 if (finfo->specials != NULL) {
301 strings_array_free(finfo->specials, N_SPECIALS);
302 }
303
304 if (finfo->datafiles != NULL) {
305 strings_array_free(finfo->datafiles, finfo->n_files);
306 }
307
308 if (finfo->depends != NULL) {
309 strings_array_free(finfo->depends, finfo->n_depends);
310 }
311
312 if (finfo->provider != NULL) {
313 g_free(finfo->provider);
314 }
315
316 if (finfo->samplewin != NULL) {
317 gtk_widget_destroy(finfo->samplewin->main);
318 }
319
320 if (finfo->helpwin != NULL) {
321 gtk_widget_destroy(finfo->helpwin->main);
322 }
323
324 if (finfo->gui_helpwin != NULL) {
325 gtk_widget_destroy(finfo->gui_helpwin->main);
326 }
327
328 if (finfo->codewins != NULL) {
329 g_list_foreach(finfo->codewins, (GFunc) destroy_code_window, NULL);
330 g_list_free(finfo->codewins);
331 }
332
333 if (finfo->ui != NULL) {
334 g_object_unref(finfo->ui);
335 }
336
337 if (finfo->popup != NULL) {
338 gtk_widget_destroy(finfo->popup);
339 }
340
341 free(finfo);
342 }
343
pkg_save_action(GtkAction * action,function_info * finfo)344 static void pkg_save_action (GtkAction *action, function_info *finfo)
345 {
346 const gchar *s = gtk_action_get_name(action);
347
348 if (!strcmp(s, "Save")) {
349 finfo_save(finfo);
350 } else if (!strcmp(s, "SaveZip")) {
351 file_selector_with_parent(SAVE_GFN_ZIP, FSEL_DATA_MISC,
352 finfo, finfo->dlg);
353 } else if (!strcmp(s, "WriteInp")) {
354 gfn_to_script_callback(finfo);
355 } else if (!strcmp(s, "WriteSpec")) {
356 gfn_to_spec_callback(finfo);
357 } else if (!strcmp(s, "Upload")) {
358 do_pkg_upload(finfo);
359 }
360 }
361
362 const gchar *pkgsave_ui =
363 "<ui>"
364 " <popup>"
365 " <menuitem action='Save'/>"
366 " <menuitem action='SaveZip'/>"
367 " <menuitem action='WriteInp'/>"
368 " <menuitem action='WriteSpec'/>"
369 " <menuitem action='Upload'/>"
370 " </popup>"
371 "</ui>";
372
373 static GtkActionEntry pkgsave_items[] = {
374 { "Save", NULL, N_("_Save gfn"), NULL, NULL, G_CALLBACK(pkg_save_action) },
375 { "SaveZip", NULL, N_("Save _zip file..."), NULL, NULL, G_CALLBACK(pkg_save_action) },
376 { "WriteInp", NULL, N_("Save as _script..."), NULL, NULL, G_CALLBACK(pkg_save_action) },
377 { "WriteSpec", NULL, N_("_Write spec file..."), NULL, NULL, G_CALLBACK(pkg_save_action) },
378 { "Upload", NULL, N_("_Upload to server..."), NULL, NULL, G_CALLBACK(pkg_save_action) },
379 };
380
save_popup_pos(GtkMenu * menu,gint * x,gint * y,gboolean * push_in,gpointer data)381 static void save_popup_pos (GtkMenu *menu,
382 gint *x,
383 gint *y,
384 gboolean *push_in,
385 gpointer data)
386 {
387 GtkWidget *button = data;
388 gint wx, wy, tx, ty;
389
390 gdk_window_get_origin(gtk_widget_get_window(button), &wx, &wy);
391 gtk_widget_translate_coordinates(button, gtk_widget_get_toplevel(button),
392 0, 0, &tx, &ty);
393 *x = wx + tx - 80;
394 *y = wy + ty - 128;
395 *push_in = TRUE;
396 }
397
pkg_save_popup(GtkWidget * button,function_info * finfo)398 static void pkg_save_popup (GtkWidget *button,
399 function_info *finfo)
400 {
401 GtkWidget *menu;
402 gboolean cond;
403
404 if (finfo->ui == NULL) {
405 GtkActionGroup *actions;
406
407 finfo->ui = gtk_ui_manager_new();
408 actions = gtk_action_group_new("PkgActions");
409 gtk_action_group_set_translation_domain(actions, "gretl");
410 gtk_action_group_add_actions(actions, pkgsave_items,
411 G_N_ELEMENTS(pkgsave_items),
412 finfo);
413 gtk_ui_manager_add_ui_from_string(finfo->ui, pkgsave_ui, -1, NULL);
414 gtk_ui_manager_insert_action_group(finfo->ui, actions, 0);
415 g_object_unref(actions);
416 }
417
418 /* set menu item sensitivities */
419 flip(finfo->ui, "/popup/Save", finfo->modified);
420 cond = finfo->fname != NULL;
421 flip(finfo->ui, "/popup/Upload", cond);
422 cond = finfo->pdfdoc || finfo->datafiles != NULL;
423 flip(finfo->ui, "/popup/SaveZip", cond && !finfo->modified);
424
425 menu = gtk_ui_manager_get_widget(finfo->ui, "/popup");
426
427 gtk_menu_popup(GTK_MENU(menu), NULL, NULL,
428 save_popup_pos,
429 button, 0,
430 gtk_get_current_event_time());
431 }
432
finfo_set_modified(function_info * finfo,gboolean s)433 static void finfo_set_modified (function_info *finfo, gboolean s)
434 {
435 if (s != finfo->modified) {
436 gchar *tmp;
437
438 finfo->modified = s;
439 if (s) {
440 tmp = g_strdup_printf("gretl: %s *", finfo_pkgname(finfo));
441 } else {
442 tmp = g_strdup_printf("gretl: %s", finfo_pkgname(finfo));
443 }
444 gtk_window_set_title(GTK_WINDOW(finfo->dlg), tmp);
445 g_free(tmp);
446 }
447 }
448
login_init_or_free(login_info * linfo,int freeit)449 static void login_init_or_free (login_info *linfo, int freeit)
450 {
451 static gchar *login;
452 static gchar *pass;
453
454 if (freeit) {
455 if (!linfo->canceled) {
456 g_free(login);
457 g_free(pass);
458 login = g_strdup(linfo->login);
459 pass = g_strdup(linfo->pass);
460 }
461 g_free(linfo->login);
462 g_free(linfo->pass);
463 } else {
464 linfo->login = (login == NULL)? NULL : g_strdup(login);
465 linfo->pass = (pass == NULL)? NULL : g_strdup(pass);
466 linfo->canceled = 1;
467 }
468 }
469
login_init(login_info * linfo)470 static void login_init (login_info *linfo)
471 {
472 login_init_or_free(linfo, 0);
473 }
474
linfo_free(login_info * linfo)475 static void linfo_free (login_info *linfo)
476 {
477 login_init_or_free(linfo, 1);
478 }
479
login_finalize(GtkWidget * w,login_info * linfo)480 static void login_finalize (GtkWidget *w, login_info *linfo)
481 {
482 linfo->login = entry_box_get_trimmed_text(linfo->login_entry);
483 if (linfo->login == NULL) {
484 gtk_widget_grab_focus(linfo->login_entry);
485 return;
486 }
487
488 linfo->pass = entry_box_get_trimmed_text(linfo->pass_entry);
489 if (linfo->pass == NULL) {
490 gtk_widget_grab_focus(linfo->pass_entry);
491 g_free(linfo->login);
492 return;
493 }
494
495 gtk_widget_destroy(linfo->dlg);
496 }
497
finfo_pkgname(function_info * finfo)498 static const char *finfo_pkgname (function_info *finfo)
499 {
500 if (finfo->pkg != NULL) {
501 return function_package_get_name(finfo->pkg);
502 } else if (finfo->ininame != NULL) {
503 return finfo->ininame;
504 } else {
505 /* "can't happen" */
506 return "untitled";
507 }
508 }
509
510 /* Used by the File, Save dialog when saving a package,
511 or saving packaged functions as a script, or writing
512 a .spec file based on a package.
513 */
514
get_default_package_name(char * fname,gpointer p,int mode)515 void get_default_package_name (char *fname, gpointer p, int mode)
516 {
517 function_info *finfo = (function_info *) p;
518 const char *pkgname = finfo_pkgname(finfo);
519
520 *fname = '\0';
521
522 if (mode == SELECT_PDF) {
523 if (finfo->pdfname != NULL) {
524 strcpy(fname, finfo->pdfname);
525 } else if (finfo->fname != NULL) {
526 switch_ext(fname, finfo->fname, "pdf");
527 }
528 if (*fname != '\0') {
529 /* should be an existing file, or scrub it */
530 if (!gretl_file_exists(fname)) {
531 *fname = '\0';
532 }
533 }
534 } else {
535 strcpy(fname, pkgname);
536 if (mode == SAVE_FUNCTIONS_AS) {
537 strcat(fname, ".inp");
538 } else if (mode == SAVE_GFN_SPEC) {
539 strcat(fname, ".spec");
540 } else if (mode == SAVE_GFN_ZIP) {
541 strcat(fname, ".zip");
542 } else {
543 strcat(fname, ".gfn");
544 }
545 }
546 }
547
548 /* fairly minimal check here! */
549
check_email_string(const char * s)550 static int check_email_string (const char *s)
551 {
552 int err = 0;
553
554 if (strchr(s, ' ') != NULL) {
555 /* no spaces allowed */
556 err = 1;
557 } else if (strchr(s, '@') == NULL) {
558 /* must include "at"-sign */
559 err = 1;
560 }
561
562 return err;
563 }
564
565 /* Check the user-supplied version string for the package: should be
566 something like "1" or "1.02"
567 */
568
check_version_string(const char * s)569 static int check_version_string (const char *s)
570 {
571 int dotcount = 0;
572 int err = 0;
573
574 /* must start and end with a digit */
575 if (!isdigit(*s) || (*s && !isdigit(s[strlen(s) - 1]))) {
576 err = 1;
577 }
578
579 while (*s && !err) {
580 if (!isdigit(*s) && *s != '.') {
581 /* only dots and digits allowed */
582 err = 1;
583 } else if (*s == '.' && ++dotcount > 1) {
584 /* max of one dot exceeded */
585 err = 1;
586 }
587 s++;
588 }
589
590 return err;
591 }
592
pkg_path_is_toplevel(function_info * finfo,const char * pkgname)593 static int pkg_path_is_toplevel (function_info *finfo,
594 const char *pkgname)
595 {
596 gchar *test;
597 int ret;
598
599 /* look for pattern "functions/mypkg.gfn" */
600 test = g_strdup_printf("functions%c%s.gfn", SLASH, pkgname);
601 ret = strstr(finfo->fname, test) != NULL;
602 g_free(test);
603
604 return ret;
605 }
606
set_gfn_save_opt(GtkWidget * w,int * opt)607 static void set_gfn_save_opt (GtkWidget *w, int *opt)
608 {
609 *opt = widget_get_int(w, "action");
610 }
611
save_gfn_ok(GtkButton * button,GtkWidget * dialog)612 static void save_gfn_ok (GtkButton *button, GtkWidget *dialog)
613 {
614 gtk_widget_destroy(dialog);
615 }
616
save_gfn_cancel(GtkButton * button,int * retval)617 static void save_gfn_cancel (GtkButton *button, int *retval)
618 {
619 GtkWidget *dialog;
620
621 dialog = g_object_get_data(G_OBJECT(button), "dialog");
622 *retval = GRETL_CANCEL;
623 gtk_widget_destroy(dialog);
624 }
625
save_gfn_delete(GtkWidget * w,GdkEvent * event,int * retval)626 static void save_gfn_delete (GtkWidget *w, GdkEvent *event, int *retval)
627 {
628 *retval = GRETL_CANCEL;
629 }
630
save_gfn_dialog(function_info * finfo)631 static int save_gfn_dialog (function_info *finfo)
632 {
633 const char *opts[] = {
634 N_("Save the file to its standard \"installed\" location"),
635 N_("Save it to a location of your own choosing")
636 };
637 GtkWidget *dialog;
638 GtkWidget *vbox, *hbox, *label;
639 GtkWidget *button = NULL;
640 GSList *group = NULL;
641 int i, ret = 0;
642
643 if (maybe_raise_dialog()) {
644 return ret;
645 }
646
647 dialog = gretl_dialog_new(NULL, finfo->dlg, GRETL_DLG_BLOCK);
648 g_signal_connect(G_OBJECT(dialog), "delete-event",
649 G_CALLBACK(save_gfn_delete), &ret);
650 vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
651
652 hbox = gtk_hbox_new(FALSE, 5);
653 gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 5);
654 gtk_widget_show(hbox);
655 label = gtk_label_new(_("Save gfn file"));
656 gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 5);
657
658 for (i=0; i<2; i++) {
659 button = gtk_radio_button_new_with_label(group, _(opts[i]));
660 gtk_box_pack_start(GTK_BOX(vbox), button, TRUE, TRUE, 0);
661 g_object_set_data(G_OBJECT(button), "action", GINT_TO_POINTER(i));
662 g_signal_connect(G_OBJECT(button), "clicked",
663 G_CALLBACK(set_gfn_save_opt), &ret);
664 if (i == 0) {
665 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (button), TRUE);
666 }
667 group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(button));
668 }
669
670 hbox = gtk_dialog_get_action_area(GTK_DIALOG(dialog));
671
672 /* "Cancel" */
673 button = cancel_button(hbox);
674 gtk_widget_set_can_default(button, FALSE);
675 g_object_set_data(G_OBJECT(button), "dialog", dialog);
676 g_signal_connect(G_OBJECT(button), "clicked",
677 G_CALLBACK(save_gfn_cancel), &ret);
678
679 /* "OK" */
680 button = ok_button(hbox);
681 g_signal_connect(G_OBJECT(button), "clicked",
682 G_CALLBACK(save_gfn_ok), dialog);
683 gtk_widget_grab_default(button);
684
685 gtk_widget_show_all(dialog);
686
687 return ret;
688 }
689
overwrite_gfn_check(const char * fname,GtkWidget * parent,int * notified)690 static int overwrite_gfn_check (const char *fname,
691 GtkWidget *parent,
692 int *notified)
693 {
694 int resp = GRETL_YES;
695
696 if (gretl_file_exists(fname)) {
697 gchar *msg;
698
699 msg = g_strdup_printf("%s\n\n%s\n%s", fname,
700 _("A file of this name already exists."),
701 _("OK to overwrite it?"));
702 resp = yes_no_dialog(NULL, msg, parent);
703 g_free(msg);
704 if (notified != NULL) {
705 *notified = 1;
706 }
707 }
708
709 return resp;
710 }
711
712 /* In saving a new package, the user has chosen to write to
713 the "install" location. We need to figure out the correct
714 path (possibly creating a package-specific directory) then
715 save the gfn file. We should give the user some feedback
716 whether or not this is successful.
717 */
718
install_gfn(function_info * finfo)719 static int install_gfn (function_info *finfo)
720 {
721 char savepath[FILENAME_MAX];
722 gchar *msg;
723 int notified = 0;
724 int err = 0;
725
726 get_default_dir_for_action(savepath, SAVE_FUNCTIONS);
727
728 if (finfo->uses_subdir) {
729 strcat(savepath, finfo_pkgname(finfo));
730 err = gretl_mkdir(savepath);
731 if (err) {
732 gui_errmsg(err);
733 } else {
734 slash_terminate(savepath);
735 }
736 }
737
738 if (!err) {
739 int resp;
740
741 strcat(savepath, finfo_pkgname(finfo));
742 strcat(savepath, ".gfn");
743 resp = overwrite_gfn_check(savepath, finfo->dlg,
744 ¬ified);
745 if (resp != GRETL_YES) {
746 return 0;
747 }
748 err = save_function_package(savepath, finfo);
749 }
750
751 if (!err && !notified) {
752 msg = g_strdup_printf(_("Wrote gfn file as\n%s"), savepath);
753 msgbox(msg, GTK_MESSAGE_INFO, finfo->dlg);
754 g_free(msg);
755 }
756
757 return err;
758 }
759
760 /* Callback from "Save", when editing a function package. We first
761 assemble and check the relevant info then if the package is new and
762 has not been saved yet (which is flagged by finfo->fname being
763 NULL) we offer a file selector, otherwise we go ahead and save
764 using the package's recorded filename.
765 */
766
finfo_save(function_info * finfo)767 static int finfo_save (function_info *finfo)
768 {
769 const char *missing = "???";
770 char **fields[] = {
771 &finfo->author,
772 &finfo->email,
773 &finfo->version,
774 &finfo->date,
775 &finfo->pkgdesc
776 };
777 int i, err = 0;
778
779 for (i=0; i<N_ENTRIES && !err; i++) {
780 g_free(*fields[i]);
781 *fields[i] = entry_box_get_trimmed_text(finfo->entries[i]);
782 if (*fields[i] == NULL || !strcmp(*fields[i], missing)) {
783 warnbox(_("Please complete all fields"));
784 gtk_entry_set_text(GTK_ENTRY(finfo->entries[i]), missing);
785 gtk_editable_select_region(GTK_EDITABLE(finfo->entries[i]), 0, -1);
786 gtk_widget_grab_focus(finfo->entries[i]);
787 return 1;
788 }
789 }
790
791 if (!finfo->pdfdoc) {
792 int fixit = 0;
793
794 if (finfo->help == NULL || *finfo->help == '\0') {
795 warnbox(_("Please add some help text for this package"));
796 fixit = 1;
797 } else if (strstr(finfo->help, "pdfdoc:") != NULL) {
798 warnbox(_("Please delete the \"pdfdoc:\" line from the help text"));
799 fixit = 1;
800 }
801 if (fixit) {
802 regular_help_text_callback(NULL, finfo);
803 return 1;
804 }
805 }
806
807 if (finfo->sample == NULL) {
808 warnbox(_("Please add a sample script for this package"));
809 edit_sample_callback(NULL, finfo);
810 return 1;
811 }
812
813 if (check_email_string(finfo->email)) {
814 errbox(_("Please supply a valid email address"));
815 return 1;
816 } else {
817 set_author_mail(finfo->email);
818 }
819
820 if (check_version_string(finfo->version)) {
821 errbox(_("Invalid version string: use numbers and '.' only"));
822 return 1;
823 }
824
825 if (finfo->tags == NULL) {
826 warnbox(_("Please select a tag (or two) for this package"));
827 gtk_widget_grab_focus(finfo->tagsel[0]);
828 return 1;
829 }
830
831 if (!finfo->uses_subdir) {
832 if (finfo->n_files > 0 || finfo->pdfdoc) {
833 finfo->uses_subdir = 1;
834 }
835 } else if (finfo->n_files == 0 && !finfo->pdfdoc) {
836 finfo->uses_subdir = 0;
837 }
838
839 if (finfo->fname == NULL) {
840 /* a new save */
841 int resp = save_gfn_dialog(finfo);
842
843 if (resp == 0) {
844 /* "install" the gfn file */
845 err = install_gfn(finfo);
846 }
847 if (resp < 1) {
848 /* cancel or "install" */
849 return err;
850 }
851 }
852
853 if (finfo->fname == NULL) {
854 /* note: the callback from the file selector is
855 save_function_package()
856 */
857 file_selector_with_parent(SAVE_FUNCTIONS, FSEL_DATA_MISC,
858 finfo, finfo->dlg);
859 } else {
860 err = save_function_package(finfo->fname, finfo);
861 }
862
863 return err;
864 }
865
finfo_destroy(GtkWidget * w,function_info * finfo)866 static void finfo_destroy (GtkWidget *w, function_info *finfo)
867 {
868 if (finfo != NULL && finfo->pkg != NULL) {
869 function_package_set_editor(finfo->pkg, NULL);
870 }
871
872 finfo_free(finfo);
873 }
874
update_active_func(GtkComboBox * menu,function_info * finfo)875 static gboolean update_active_func (GtkComboBox *menu,
876 function_info *finfo)
877 {
878 int i = 0;
879
880 if (menu != NULL) {
881 i = gtk_combo_box_get_active(menu);
882 if (i < 0) {
883 i = 0;
884 }
885 }
886
887 if (i < finfo->n_pub) {
888 finfo->active = finfo->pubnames[i];
889 } else {
890 finfo->active = finfo->privnames[i-finfo->n_pub];
891 }
892
893 return FALSE;
894 }
895
896 /* Given a line "function ..." get the function name, with
897 some error checking. The @s we are given here is at an
898 offset of 9 bytes into the line, skipping "function ".
899 */
900
extract_funcname(const char * s,const char * origname)901 static int extract_funcname (const char *s, const char *origname)
902 {
903 char newname[FN_NAMELEN];
904 char word[FN_NAMELEN];
905 int n, type, err = 0;
906
907 s += strspn(s, " ");
908 n = strcspn(s, " (");
909
910 if (n == 0 || n > FN_NAMELEN - 1) {
911 return E_DATA;
912 }
913
914 *newname = *word = '\0';
915 strncat(word, s, n);
916
917 if (!strcmp(word, "void")) {
918 type = GRETL_TYPE_VOID;
919 } else {
920 type = gretl_type_from_string(word);
921 }
922
923 if (!ok_function_return_type(type)) {
924 gretl_errmsg_sprintf("%s: bad or missing return type", origname);
925 err = E_DATA;
926 } else {
927 s += n;
928 s += strspn(s, " ");
929 n = strcspn(s, " (");
930 if (n == 0 || n > FN_NAMELEN - 1) {
931 err = E_DATA;
932 } else {
933 strncat(newname, s, n);
934 if (strcmp(newname, origname)) {
935 gretl_errmsg_set(_("You can't change the name of a function here"));
936 err = E_DATA;
937 }
938 }
939 }
940
941 return err;
942 }
943
pretest_funcname(char * buf,const char * origname)944 static int pretest_funcname (char *buf, const char *origname)
945 {
946 char *s, line[MAXLINE];
947 int err = 0;
948
949 bufgets_init(buf);
950
951 while (bufgets(line, sizeof line, buf) && !err) {
952 s = line + strspn(line, " \t");
953 if (!strncmp(s, "function ", 9)) {
954 err = extract_funcname(s + 9, origname);
955 break;
956 }
957 }
958
959 bufgets_finalize(buf);
960
961 return err;
962 }
963
964 /* callback used when editing a function in the context of the package
965 editor: save window-content to file and pass this to gretl_func to
966 revise the function definition.
967 */
968
update_func_code(windata_t * vwin)969 int update_func_code (windata_t *vwin)
970 {
971 gchar *text = textview_get_text(vwin->text);
972 function_info *finfo = vwin->data;
973 const char *funname;
974 int err;
975
976 funname = funname_from_filename(vwin->fname);
977 err = pretest_funcname(text, funname);
978
979 if (!err) {
980 int save_batch = gretl_in_batch_mode();
981
982 set_current_function_package(finfo->pkg);
983 err = execute_script(NULL, text, NULL, INCLUDE_EXEC, NULL);
984 set_current_function_package(NULL);
985 gretl_set_batch_mode(save_batch);
986 }
987
988 g_free(text);
989
990 if (err) {
991 gui_errmsg(err);
992 } else {
993 mark_vwin_content_saved(vwin);
994 finfo_set_modified(finfo, TRUE);
995 }
996
997 return err;
998 }
999
finfo_remove_codewin(GtkWidget * w,function_info * finfo)1000 static void finfo_remove_codewin (GtkWidget *w, function_info *finfo)
1001 {
1002 gpointer p = g_object_get_data(G_OBJECT(w), "vwin");
1003
1004 finfo->codewins = g_list_remove(finfo->codewins, p);
1005 }
1006
funcname_limit(gunichar c,gpointer p)1007 static gboolean funcname_limit (gunichar c, gpointer p)
1008 {
1009 return (!isalpha(c) && c != '_');
1010 }
1011
catch_codewin_key(GtkWidget * w,GdkEventKey * event,function_info * finfo)1012 static gint catch_codewin_key (GtkWidget *w, GdkEventKey *event,
1013 function_info *finfo)
1014 {
1015 if (finfo->n_pub + finfo->n_priv < 2) {
1016 /* we don't have multiple functions */
1017 return FALSE;
1018 }
1019
1020 /* implement Alt-dot in a given function editing window to
1021 traverse to another window editing a different function
1022 */
1023
1024 if ((event->state & GDK_MOD1_MASK) && event->keyval == GDK_period) {
1025 /* Alt + dot */
1026 windata_t *vwin = g_object_get_data(G_OBJECT(w), "vwin");
1027 GtkTextView *view = GTK_TEXT_VIEW(vwin->text);
1028 GtkTextBuffer *buf = gtk_text_view_get_buffer(view);
1029 GtkTextMark *mark = gtk_text_buffer_get_insert(buf);
1030 GtkTextIter iter, istart, iend;
1031 gchar *word = NULL;
1032
1033 gtk_text_buffer_get_iter_at_mark(buf, &iter, mark);
1034 istart = iend = iter;
1035 if (gtk_text_iter_backward_find_char(&istart, funcname_limit, NULL, NULL) &&
1036 gtk_text_iter_forward_char(&istart) &&
1037 gtk_text_iter_forward_find_char(&iend, funcname_limit, NULL, NULL)) {
1038 word = gtk_text_buffer_get_text(buf, &istart, &iend, FALSE);
1039 }
1040
1041 if (word != NULL) {
1042 /* we got a "word": is it the name of a function in
1043 this package? */
1044 char *active = NULL;
1045 int i;
1046
1047 for (i=0; i<finfo->n_pub && !active; i++) {
1048 if (!strcmp(word, finfo->pubnames[i])) {
1049 active = finfo->pubnames[i];
1050 }
1051 }
1052 for (i=0; i<finfo->n_priv && !active; i++) {
1053 if (!strcmp(word, finfo->privnames[i])) {
1054 active = finfo->privnames[i];
1055 }
1056 }
1057 if (active != NULL) {
1058 finfo->active = active;
1059 edit_code_callback(NULL, finfo);
1060 }
1061 g_free(word);
1062 }
1063
1064 return TRUE;
1065 }
1066
1067 return FALSE;
1068 }
1069
finfo_add_codewin(function_info * finfo,windata_t * vwin)1070 static void finfo_add_codewin (function_info *finfo, windata_t *vwin)
1071 {
1072 finfo->codewins = g_list_append(finfo->codewins, vwin);
1073
1074 g_object_set_data(G_OBJECT(vwin->main), "vwin", vwin);
1075 g_signal_connect(G_OBJECT(vwin->main), "key-press-event",
1076 G_CALLBACK(catch_codewin_key), finfo);
1077 g_signal_connect(G_OBJECT(vwin->main), "destroy",
1078 G_CALLBACK(finfo_remove_codewin),
1079 finfo);
1080 }
1081
get_codewin_by_filename(const char * fname,function_info * finfo)1082 static windata_t *get_codewin_by_filename (const char *fname,
1083 function_info *finfo)
1084 {
1085 GList *list = finfo->codewins;
1086 windata_t *vwin;
1087
1088 while (list) {
1089 vwin = list->data;
1090 if (vwin != NULL && !strcmp(fname, vwin->fname)) {
1091 return vwin;
1092 }
1093 list = g_list_next(list);
1094 }
1095
1096 return NULL;
1097 }
1098
1099 /* editing a public interface or private function belonging
1100 to a package: callback from "Edit function code" button.
1101 */
1102
edit_code_callback(GtkWidget * w,function_info * finfo)1103 static void edit_code_callback (GtkWidget *w, function_info *finfo)
1104 {
1105 char *funname = finfo->active;
1106 char fname[FILENAME_MAX];
1107 ufunc *fun;
1108 windata_t *vwin;
1109 PRN *prn = NULL;
1110
1111 if (funname == NULL) {
1112 return;
1113 }
1114
1115 filename_from_funname(fname, funname);
1116
1117 vwin = get_codewin_by_filename(fname, finfo);
1118 if (vwin != NULL) {
1119 gtk_window_present(GTK_WINDOW(vwin->main));
1120 return;
1121 }
1122
1123 fun = get_function_from_package(funname, finfo->pkg);
1124 if (fun == NULL) {
1125 /* the package may not be saved yet */
1126 fun = get_user_function_by_name(funname);
1127 if (fun == NULL) {
1128 errbox_printf(_("Can't find the function '%s'"), funname);
1129 }
1130 }
1131
1132 if (bufopen(&prn)) {
1133 return;
1134 }
1135
1136 gretl_function_print_code(fun, tabwidth, prn);
1137
1138 vwin = view_buffer(prn, SCRIPT_WIDTH, SCRIPT_HEIGHT,
1139 finfo->active, EDIT_PKG_CODE, finfo);
1140
1141 if (vwin != NULL) {
1142 strcpy(vwin->fname, fname);
1143 finfo_add_codewin(finfo, vwin);
1144 set_window_delete_filename(vwin);
1145 }
1146 }
1147
1148 /* used by callback from Exec in sample script editor window */
1149
package_sample_get_script(windata_t * vwin)1150 gchar *package_sample_get_script (windata_t *vwin)
1151 {
1152 function_info *finfo = vwin->data;
1153 gchar *buf = textview_get_text(vwin->text);
1154 const char *pkgname;
1155 gchar *ret;
1156 char *p, line[MAXLINE];
1157 gsize retsize;
1158 int n, done = 0;
1159
1160 if (buf == NULL || *buf == '\0' || finfo->pkg == NULL) {
1161 return buf;
1162 }
1163
1164 pkgname = function_package_get_name(finfo->pkg);
1165
1166 /* allow for adding "# ", and possibly appending a newline */
1167 retsize = strlen(buf) + 5;
1168 ret = g_malloc(retsize);
1169 *ret = '\0';
1170
1171 /* We need to comment out "include <self>.gfn" if such a line is
1172 included in the sample script: the package is already in memory
1173 and re-loading it now may be disruptive.
1174 */
1175
1176 bufgets_init(buf);
1177
1178 while (bufgets(line, sizeof line, buf)) {
1179 if (!done) {
1180 p = line + strspn(line, " ");
1181 if (!strncmp(p, "include ", 8)) {
1182 p += 8;
1183 p += strspn(p, " ");
1184 n = gretl_namechar_spn(p);
1185 if (!strncmp(p, pkgname, n)) {
1186 g_strlcat(ret, "# ", retsize);
1187 done = 1;
1188 }
1189 }
1190 }
1191 g_strlcat(ret, line, retsize);
1192 }
1193
1194 bufgets_finalize(buf);
1195 g_free(buf);
1196
1197 n = strlen(ret);
1198 if (ret[n-1] != '\n') {
1199 g_strlcat(ret, "\n", retsize);
1200 }
1201
1202 return ret;
1203 }
1204
1205 /* callback from Save in sample script editor window */
1206
update_sample_script(windata_t * vwin)1207 void update_sample_script (windata_t *vwin)
1208 {
1209 function_info *finfo;
1210
1211 finfo = g_object_get_data(G_OBJECT(vwin->main), "finfo");
1212
1213 if (finfo != NULL) {
1214 gchar *text = textview_get_text(vwin->text);
1215
1216 free(finfo->sample);
1217 if (text == NULL || string_is_blank(text)) {
1218 finfo->sample = NULL;
1219 } else {
1220 finfo->sample = gretl_strdup(text);
1221 }
1222 g_free(text);
1223 mark_vwin_content_saved(vwin);
1224 finfo_set_modified(finfo, TRUE);
1225 }
1226 }
1227
1228 /* callback from Save in help text editor window */
1229
update_gfn_help_text(windata_t * vwin)1230 void update_gfn_help_text (windata_t *vwin)
1231 {
1232 function_info *finfo;
1233
1234 finfo = g_object_get_data(G_OBJECT(vwin->main), "finfo");
1235
1236 if (finfo != NULL) {
1237 gchar *text = textview_get_wrapped_text(vwin->text);
1238
1239 if (vwin->role == EDIT_PKG_GHLP) {
1240 g_free(finfo->gui_help);
1241 if (text == NULL || string_is_blank(text)) {
1242 finfo->gui_help = NULL;
1243 } else {
1244 finfo->gui_help = text;
1245 text = NULL;
1246 }
1247 } else {
1248 g_free(finfo->help);
1249 if (text == NULL || string_is_blank(text)) {
1250 finfo->help = NULL;
1251 } else {
1252 finfo->help = text;
1253 text = NULL;
1254 }
1255 }
1256
1257 g_free(text);
1258 mark_vwin_content_saved(vwin);
1259 finfo_set_modified(finfo, TRUE);
1260 }
1261 }
1262
nullify_sample_window(GtkWidget * w,function_info * finfo)1263 static void nullify_sample_window (GtkWidget *w, function_info *finfo)
1264 {
1265 finfo->samplewin = NULL;
1266 }
1267
nullify_helpwin(GtkWidget * w,function_info * finfo)1268 static void nullify_helpwin (GtkWidget *w, function_info *finfo)
1269 {
1270 finfo->helpwin = NULL;
1271 }
1272
nullify_gui_helpwin(GtkWidget * w,function_info * finfo)1273 static void nullify_gui_helpwin (GtkWidget *w, function_info *finfo)
1274 {
1275 finfo->gui_helpwin = NULL;
1276 }
1277
1278 /* edit the sample script for a package: callback from
1279 "Edit sample script" button in packager
1280 */
1281
edit_sample_callback(GtkWidget * w,function_info * finfo)1282 static void edit_sample_callback (GtkWidget *w, function_info *finfo)
1283 {
1284 const char *pkgname = finfo_pkgname(finfo);
1285 gchar *title;
1286 PRN *prn = NULL;
1287
1288 if (finfo->samplewin != NULL) {
1289 gtk_window_present(GTK_WINDOW(finfo->samplewin->main));
1290 return;
1291 }
1292
1293 if (bufopen(&prn)) {
1294 return;
1295 }
1296
1297 title = g_strdup_printf("%s-sample", pkgname);
1298
1299 if (finfo->sample == NULL) {
1300 pprintf(prn, "include %s.gfn\n", pkgname);
1301 } else {
1302 pputs(prn, finfo->sample);
1303 pputc(prn, '\n');
1304 }
1305
1306 finfo->samplewin = view_buffer(prn, 78, 350, title,
1307 EDIT_PKG_SAMPLE, finfo);
1308 if (finfo->sample == NULL) {
1309 cursor_to_end(finfo->samplewin);
1310 }
1311
1312 g_object_set_data(G_OBJECT(finfo->samplewin->main), "finfo",
1313 finfo);
1314 g_signal_connect(G_OBJECT(finfo->samplewin->main), "destroy",
1315 G_CALLBACK(nullify_sample_window), finfo);
1316
1317 g_free(title);
1318 }
1319
1320 /* Callback to launch dialog for adding or removing functions.
1321 We need to be careful here: if the package's "extra
1322 properties" dialog is open, its content is liable to be
1323 out-dated by changes in the public and/or private
1324 function lists. Since it would be very complicated and
1325 error-prone to adjust this content on the fly, we'll
1326 insist that the user closes the extra props dialog
1327 first.
1328 */
1329
add_remove_callback(GtkWidget * w,function_info * finfo)1330 static void add_remove_callback (GtkWidget *w, function_info *finfo)
1331 {
1332 if (finfo->extra != NULL) {
1333 const char *msg = N_("Before adding or removing functions, please close\n"
1334 "the \"extra properties\" dialog (after applying any\n"
1335 "changes you wish to keep).");
1336
1337 gtk_window_present(GTK_WINDOW(finfo->extra));
1338 msgbox(_(msg), GTK_MESSAGE_INFO, finfo->extra);
1339 return;
1340 }
1341
1342 add_remove_functions_dialog(finfo->pubnames, finfo->n_pub,
1343 finfo->privnames, finfo->n_priv,
1344 finfo->pkg, finfo);
1345 }
1346
gfn_to_script_callback(function_info * finfo)1347 static void gfn_to_script_callback (function_info *finfo)
1348 {
1349 gint resp;
1350
1351 if (finfo->n_pub + finfo->n_priv == 0) {
1352 warnbox("No code to save");
1353 return;
1354 }
1355
1356 if (finfo->sample != NULL) {
1357 resp = yes_no_cancel_dialog("gretl",
1358 _("Saving packaged functions as script:\n"
1359 "include the sample script?"),
1360 finfo->dlg);
1361
1362 if (canceled(resp)) {
1363 return;
1364 }
1365
1366 if (resp == GRETL_YES) {
1367 finfo->save_flags |= APPEND_SAMPLE;
1368 } else {
1369 finfo->save_flags &= ~APPEND_SAMPLE;
1370 }
1371 }
1372
1373 file_selector_with_parent(SAVE_FUNCTIONS_AS, FSEL_DATA_MISC,
1374 finfo, finfo->dlg);
1375 }
1376
1377 struct spec_info {
1378 GtkWidget *dialog;
1379 GtkWidget *checks[3];
1380 GtkWidget *entries[3];
1381 int *flags;
1382 function_info *finfo;
1383 int retval;
1384 };
1385
reset_finfo_filename(function_info * finfo,int i,gchar * src)1386 static void reset_finfo_filename (function_info *finfo, int i, gchar *src)
1387 {
1388 if (i == 0) {
1389 g_free(finfo->sample_fname);
1390 finfo->sample_fname = src;
1391 } else if (i == 1) {
1392 g_free(finfo->help_fname);
1393 finfo->help_fname = src;
1394 } else {
1395 g_free(finfo->gui_help_fname);
1396 finfo->gui_help_fname = src;
1397 }
1398 }
1399
spec_save_ok(GtkWidget * button,gpointer data)1400 static void spec_save_ok (GtkWidget *button, gpointer data)
1401 {
1402 struct spec_info *sinfo = data;
1403 function_info *finfo = sinfo->finfo;
1404 gchar *fname;
1405 int i, flag;
1406
1407 for (i=0; i<3; i++) {
1408 if (sinfo->checks[i] != NULL) {
1409 flag = sinfo->flags[i];
1410 finfo->save_flags &= ~flag;
1411 if (button_is_active(sinfo->checks[i])) {
1412 fname = entry_box_get_trimmed_text(sinfo->entries[i]);
1413 if (fname != NULL) {
1414 finfo->save_flags |= flag;
1415 reset_finfo_filename(finfo, i, fname);
1416 }
1417 }
1418 }
1419 }
1420
1421 sinfo->retval = 0;
1422 gtk_widget_destroy(sinfo->dialog);
1423 }
1424
get_pkg_text_filename(function_info * finfo,const char * pkgname,const char ** ids,int i)1425 static gchar *get_pkg_text_filename (function_info *finfo,
1426 const char *pkgname,
1427 const char **ids,
1428 int i)
1429 {
1430 const char *s;
1431 gchar *fname = NULL;
1432
1433 s = function_package_get_string(finfo->pkg, ids[i]);
1434
1435 if (s != NULL) {
1436 fname = g_strdup(s);
1437 } else if (i == 0) {
1438 fname = g_strdup_printf("%s_sample.inp", pkgname);
1439 } else if (i == 1) {
1440 fname = g_strdup_printf("%s_help.txt", pkgname);
1441 } else {
1442 fname = g_strdup_printf("%s_gui_help.txt", pkgname);
1443 }
1444
1445 return fname;
1446 }
1447
sensitize_auxname_entry(GtkToggleButton * button,GtkWidget * w)1448 static void sensitize_auxname_entry (GtkToggleButton *button,
1449 GtkWidget *w)
1450 {
1451 gboolean s = gtk_toggle_button_get_active(button);
1452
1453 gtk_widget_set_sensitive(w, s);
1454 }
1455
nullify_spec_dialog(GtkWidget * w,function_info * finfo)1456 static void nullify_spec_dialog (GtkWidget *w, function_info *finfo)
1457 {
1458 finfo->specdlg = NULL;
1459 }
1460
gfn_spec_save_dialog(function_info * finfo,const char ** texts)1461 static int gfn_spec_save_dialog (function_info *finfo,
1462 const char **texts)
1463 {
1464 const gchar *msgs[] = {
1465 N_("Save sample script as"),
1466 N_("Save help text as"),
1467 N_("Save GUI help as")
1468 };
1469 const char *ids[] = {
1470 "sample-fname",
1471 "help-fname",
1472 "gui-help-fname"
1473 };
1474 int flags[] = {
1475 WRITE_SAMPFILE,
1476 WRITE_HELPFILE,
1477 WRITE_GUI_HELP
1478 };
1479 struct spec_info sinfo;
1480 GtkWidget *dialog, *entry;
1481 GtkWidget *vbox, *hbox, *w;
1482 GtkWidget *table;
1483 const char *pkgname;
1484 gchar *tmp;
1485 int i, j, n = 0;
1486
1487 sinfo.retval = GRETL_CANCEL;
1488 sinfo.finfo = finfo;
1489 sinfo.flags = flags;
1490
1491 finfo->specdlg = sinfo.dialog = dialog =
1492 gretl_dialog_new(NULL, finfo->dlg, GRETL_DLG_BLOCK);
1493 g_signal_connect(G_OBJECT(dialog), "destroy",
1494 G_CALLBACK(nullify_spec_dialog), finfo);
1495
1496 vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1497
1498 pkgname = finfo_pkgname(finfo);
1499
1500 hbox = gtk_hbox_new(FALSE, 5);
1501 gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 5);
1502 tmp = g_strdup_printf(_("Saving %s.spec: also save ancillary file(s)?"),
1503 pkgname);
1504 w = gtk_label_new(tmp);
1505 gtk_box_pack_start(GTK_BOX(hbox), w, TRUE, TRUE, 5);
1506
1507 for (i=0; i<3; i++) {
1508 n += (texts[i] != NULL);
1509 }
1510
1511 table = gtk_table_new(n, 2, FALSE);
1512 gtk_table_set_row_spacings(GTK_TABLE(table), 5);
1513 gtk_table_set_col_spacings(GTK_TABLE(table), 5);
1514 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 5);
1515
1516 j = 0;
1517 for (i=0; i<3; i++) {
1518 if (texts[i] == NULL) {
1519 sinfo.checks[i] = sinfo.entries[i] = NULL;
1520 continue;
1521 }
1522 tmp = get_pkg_text_filename(finfo, pkgname, ids, i);
1523 w = sinfo.checks[i] = gtk_check_button_new_with_label(_(msgs[i]));
1524 if (finfo->save_flags & flags[i]) {
1525 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), TRUE);
1526 }
1527 gtk_table_attach_defaults(GTK_TABLE(table), w, 0, 1, j, j+1);
1528 entry = sinfo.entries[i] = gtk_entry_new();
1529 gtk_entry_set_max_length(GTK_ENTRY(entry), 64);
1530 gtk_entry_set_width_chars(GTK_ENTRY(entry), 28);
1531 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1532 g_signal_connect(G_OBJECT(w), "toggled",
1533 G_CALLBACK(sensitize_auxname_entry),
1534 entry);
1535 gtk_table_attach_defaults(GTK_TABLE(table), entry, 1, 2, j, j+1);
1536 g_free(tmp);
1537 j++;
1538 }
1539
1540 hbox = gtk_dialog_get_action_area(GTK_DIALOG(dialog));
1541
1542 cancel_delete_button(hbox, dialog);
1543 w = ok_button(hbox);
1544 g_signal_connect(G_OBJECT(w), "clicked",
1545 G_CALLBACK(spec_save_ok), &sinfo);
1546 gtk_widget_grab_default(w);
1547
1548 gtk_widget_show_all(dialog);
1549
1550 return sinfo.retval;
1551 }
1552
1553 /* callback from file selector on saving package spec or
1554 zip file: the default location should match that of the
1555 gfn file
1556 */
1557
get_gfn_dir(char * dirname,gpointer p)1558 void get_gfn_dir (char *dirname, gpointer p)
1559 {
1560 function_info *finfo = (function_info *) p;
1561 char *s = NULL;
1562
1563 *dirname = '\0';
1564
1565 if (finfo->fname != NULL) {
1566 strcpy(dirname, finfo->fname);
1567 s = strrslash(dirname);
1568 if (s != NULL) {
1569 *s = '\0';
1570 } else {
1571 *dirname = '\0';
1572 }
1573 }
1574 }
1575
gfn_to_spec_callback(function_info * finfo)1576 static void gfn_to_spec_callback (function_info *finfo)
1577 {
1578 const char *texts[] = {
1579 finfo->sample,
1580 finfo->help,
1581 finfo->gui_help
1582 };
1583 int resp = 0;
1584
1585 if (finfo->specdlg != NULL) {
1586 gtk_window_present(GTK_WINDOW(finfo->specdlg));
1587 return;
1588 }
1589
1590 if (finfo->pkg == NULL) {
1591 warnbox(_("Please save your package first"));
1592 return;
1593 }
1594
1595 if (texts[0] == NULL) {
1596 texts[0] = function_package_get_string(finfo->pkg, "sample-script");
1597 }
1598 if (texts[1] != NULL && finfo->pdfdoc) {
1599 texts[1] = NULL;
1600 }
1601 if (texts[2] == NULL) {
1602 texts[2] = function_package_get_string(finfo->pkg, "gui-help");
1603 }
1604
1605 if (texts[0] != NULL || texts[1] != NULL || texts[2] != NULL) {
1606 resp = gfn_spec_save_dialog(finfo, texts);
1607 }
1608
1609 if (!canceled(resp)) {
1610 file_selector_with_parent(SAVE_GFN_SPEC, FSEL_DATA_MISC,
1611 finfo, finfo->dlg);
1612 }
1613 }
1614
label_hbox(GtkWidget * w,const char * txt)1615 static GtkWidget *label_hbox (GtkWidget *w, const char *txt)
1616 {
1617 GtkWidget *hbox, *label;
1618
1619 hbox = gtk_hbox_new(FALSE, 5);
1620 gtk_box_pack_start(GTK_BOX(w), hbox, FALSE, FALSE, 10);
1621
1622 label = gtk_label_new(txt);
1623 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 10);
1624 gtk_widget_show(label);
1625
1626 return hbox;
1627 }
1628
1629 enum {
1630 REGULAR_BUTTON,
1631 CHECK_BUTTON
1632 };
1633
get_function_names(const int * list,int * err)1634 static char **get_function_names (const int *list, int *err)
1635 {
1636 char **names = NULL;
1637 const char *funname;
1638 int i, n = 0;
1639
1640 for (i=1; i<=list[0] && !*err; i++) {
1641 funname = user_function_name_by_index(list[i]);
1642 if (funname == NULL) {
1643 *err = E_DATA;
1644 } else {
1645 *err = strings_array_add(&names, &n, funname);
1646 }
1647 }
1648
1649 if (*err) {
1650 strings_array_free(names, n);
1651 names = NULL;
1652 }
1653
1654 return names;
1655 }
1656
finfo_reset_function_names(function_info * finfo,char ** pubnames,int npub,char ** privnames,int npriv,int * changed)1657 static int finfo_reset_function_names (function_info *finfo,
1658 char **pubnames, int npub,
1659 char **privnames, int npriv,
1660 int *changed)
1661 {
1662 *changed = 0;
1663
1664 if (npub != finfo->n_pub || npriv != finfo->n_priv) {
1665 /* we know that something has changed */
1666 *changed = 1;
1667 } else {
1668 /* we'll have to check the arrays for any changes */
1669 *changed = strings_array_cmp(pubnames, finfo->pubnames, npub);
1670 if (*changed == 0 && npriv > 0) {
1671 *changed = strings_array_cmp(privnames, finfo->privnames,
1672 npriv);
1673 }
1674 }
1675
1676 if (*changed == 0) {
1677 /* trash the new function-name arrays */
1678 strings_array_free(pubnames, npub);
1679 strings_array_free(privnames, npriv);
1680 } else {
1681 /* replace the old function-name arrays */
1682 strings_array_free(finfo->pubnames, finfo->n_pub);
1683 finfo->pubnames = pubnames;
1684 finfo->n_pub = npub;
1685 strings_array_free(finfo->privnames, finfo->n_priv);
1686 finfo->privnames = privnames;
1687 finfo->n_priv = npriv;
1688 finfo->active = finfo->pubnames[0];
1689 }
1690
1691 return 0;
1692 }
1693
finfo_set_function_names(function_info * finfo,const int * publist,const int * privlist)1694 static int finfo_set_function_names (function_info *finfo,
1695 const int *publist,
1696 const int *privlist)
1697 {
1698 int npriv = (privlist == NULL)? 0 : privlist[0];
1699 int err = 0;
1700
1701 finfo->pubnames = get_function_names(publist, &err);
1702 if (!err) {
1703 finfo->n_pub = publist[0];
1704 }
1705
1706 if (!err && npriv > 0) {
1707 finfo->privnames = get_function_names(privlist, &err);
1708 if (!err) {
1709 finfo->n_priv = npriv;
1710 }
1711 }
1712
1713 return err;
1714 }
1715
func_selector_set_strings(function_info * finfo,GtkWidget * ifmenu)1716 static void func_selector_set_strings (function_info *finfo,
1717 GtkWidget *ifmenu)
1718 {
1719 int i, n = 0;
1720
1721 for (i=0; i<finfo->n_pub; i++) {
1722 combo_box_append_text(ifmenu, finfo->pubnames[i]);
1723 n++;
1724 }
1725
1726 for (i=0; i<finfo->n_priv; i++) {
1727 gchar *s = g_strdup_printf("%s (%s)", finfo->privnames[i], _("private"));
1728
1729 combo_box_append_text(ifmenu, s);
1730 g_free(s);
1731 n++;
1732 }
1733
1734 gtk_combo_box_set_active(GTK_COMBO_BOX(ifmenu), 0);
1735 gtk_widget_set_sensitive(ifmenu, n > 1);
1736 }
1737
active_func_selector(function_info * finfo)1738 static GtkWidget *active_func_selector (function_info *finfo)
1739 {
1740 GtkWidget *ifmenu = gtk_combo_box_text_new();
1741
1742 func_selector_set_strings(finfo, ifmenu);
1743 gtk_widget_show_all(ifmenu);
1744
1745 return ifmenu;
1746 }
1747
dreq_select(GtkComboBox * menu,function_info * finfo)1748 static void dreq_select (GtkComboBox *menu, function_info *finfo)
1749 {
1750 finfo->dreq = gtk_combo_box_get_active(menu);
1751 finfo_set_modified(finfo, TRUE);
1752 }
1753
add_data_requirement_menu(GtkWidget * tbl,int i,function_info * finfo)1754 static void add_data_requirement_menu (GtkWidget *tbl, int i,
1755 function_info *finfo)
1756 {
1757 const char *datareq[] = {
1758 N_("No special requirement"),
1759 N_("Time-series data"),
1760 N_("Quarterly or monthly data"),
1761 N_("Panel data"),
1762 N_("No dataset needed")
1763 };
1764 GtkWidget *datamenu, *tmp;
1765 int j;
1766
1767 tmp = gtk_label_new(_("Data requirement"));
1768 gtk_misc_set_alignment(GTK_MISC(tmp), 1.0, 0.5);
1769 gtk_table_attach_defaults(GTK_TABLE(tbl), tmp, 0, 1, i, i+1);
1770 gtk_widget_show(tmp);
1771
1772 datamenu = gtk_combo_box_text_new();
1773 for (j=0; j<=FN_NODATA_OK; j++) {
1774 combo_box_append_text(datamenu, _(datareq[j]));
1775 }
1776 gtk_combo_box_set_active(GTK_COMBO_BOX(datamenu), finfo->dreq);
1777
1778 tmp = gtk_hbox_new(FALSE, 0);
1779 gtk_box_pack_start(GTK_BOX(tmp), datamenu, FALSE, FALSE, 0);
1780 gtk_table_attach_defaults(GTK_TABLE(tbl), tmp, 1, 2, i, i+1);
1781 gtk_widget_show_all(tmp);
1782
1783 g_signal_connect(G_OBJECT(datamenu), "changed",
1784 G_CALLBACK(dreq_select), finfo);
1785 }
1786
pdf_toggled_callback(GtkToggleButton * button,function_info * finfo)1787 static void pdf_toggled_callback (GtkToggleButton *button,
1788 function_info *finfo)
1789 {
1790 int prev = finfo->pdfdoc;
1791
1792 finfo->pdfdoc = button_is_active(button);
1793 if (finfo->pdfdoc != prev) {
1794 finfo_set_modified(finfo, TRUE);
1795 }
1796
1797 if (!finfo->pdfdoc) {
1798 g_free(finfo->pdfname);
1799 finfo->pdfname = NULL;
1800 }
1801 }
1802
1803 /* We have the name of the PDF file that the user selected
1804 when switching to PDF help via the GUI, recorded in
1805 finfo->pdfname. Now the user is trying to build a
1806 zipfile, so we need to determine if the PDF is already
1807 in place or has to be copied from somewhere else,
1808 then do the copying if need be.
1809
1810 "Already in place" means that the basename of the PDF is
1811 the same as the package name, and the file is either in
1812 the same directory as the gfn or in a subdirectory named
1813 "doc".
1814 */
1815
maybe_copy_pdf_file(function_info * finfo)1816 static int maybe_copy_pdf_file (function_info *finfo)
1817 {
1818 char *p, targ[FILENAME_MAX];
1819 int copy = 1;
1820 int err = 0;
1821
1822 switch_ext(targ, finfo->fname, "pdf");
1823
1824 if (!strcmp(targ, finfo->pdfname)) {
1825 copy = 0;
1826 } else if ((p = strrslash(targ)) != NULL) {
1827 gchar *tmp = g_strdup(p + 1);
1828
1829 p++;
1830 *p = '\0';
1831 strcat(p, "doc");
1832 strcat(p, SLASHSTR);
1833 strcat(p, tmp);
1834 g_free(tmp);
1835 if (!strcmp(targ, finfo->pdfname)) {
1836 copy = 0;
1837 }
1838 } else {
1839 sprintf(targ, "doc%c%s", SLASH, finfo->fname);
1840 switch_ext(targ, targ, "pdf");
1841 if (!strcmp(targ, finfo->pdfname)) {
1842 copy = 0;
1843 }
1844 }
1845
1846 if (copy) {
1847 switch_ext(targ, finfo->fname, "pdf");
1848 err = gretl_copy_file(finfo->pdfname, targ);
1849 if (!err) {
1850 /* this variable has done its work */
1851 g_free(finfo->pdfname);
1852 finfo->pdfname = NULL;
1853 }
1854 }
1855
1856 return err;
1857 }
1858
pdf_press_callback(GtkWidget * button,GdkEvent * event,function_info * finfo)1859 static gboolean pdf_press_callback (GtkWidget *button,
1860 GdkEvent *event,
1861 function_info *finfo)
1862 {
1863 int resp = GRETL_YES;
1864 gboolean ret = TRUE; /* block */
1865
1866 if (finfo->help != NULL && strlen(finfo->help) > 128) {
1867 /* Seems like we may have a usable plain text help
1868 buffer in place -- so warn the user.
1869 */
1870 const gchar *msg1, *msg2, *query;
1871 gchar *text;
1872
1873 msg1 = N_("Switching to PDF help means that you must supply\n"
1874 "a PDF file containing help text for your package.\n\n");
1875
1876 msg2 = N_("It also means that any existing plain text help\n"
1877 "will be lost when the package is saved.\n\n");
1878
1879 query = N_("Switch to PDF help now?");
1880
1881 text = g_strconcat(_(msg1), _(msg2), _(query), NULL);
1882 resp = yes_no_dialog(NULL, text, finfo->dlg);
1883 g_free(text);
1884 }
1885
1886 if (resp == GRETL_YES) {
1887 g_free(finfo->pdfname);
1888 finfo->pdfname = NULL;
1889 file_selector_with_parent(SELECT_PDF, FSEL_DATA_MISC,
1890 finfo, finfo->dlg);
1891 if (finfo->pdfname != NULL) {
1892 /* otherwise the user canceled */
1893 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
1894 ret = FALSE;
1895 }
1896 }
1897
1898 return ret;
1899 }
1900
get_gfn_pdf_dir(char * dirname,gpointer p)1901 void get_gfn_pdf_dir (char *dirname, gpointer p)
1902 {
1903 function_info *finfo = (function_info *) p;
1904 char *s = NULL;
1905
1906 *dirname = '\0';
1907
1908 if (finfo->pdfname != NULL) {
1909 strcpy(dirname, finfo->pdfname);
1910 s = strrslash(dirname);
1911 if (s != NULL) {
1912 *s = '\0';
1913 } else {
1914 *dirname = '\0';
1915 }
1916 } else if (finfo->fname != NULL) {
1917 strcpy(dirname, finfo->fname);
1918 s = strrslash(dirname);
1919 if (s != NULL) {
1920 *s = '\0';
1921 } else {
1922 *dirname = '\0';
1923 }
1924 }
1925 }
1926
1927 /* We get here only if the package already has PDF doc selected
1928 (otherwise the button whose callback this is is disabled).
1929 If finfo->pdfname is non-NULL that means that we haven't
1930 yet built a zipfile, so we should probably preserve that
1931 filename (or at least, directory) as the default when we
1932 open the file selector. But if finfo->pdfname is NULL we'll
1933 show the gfn directory by default. See above, get_gfn_pdf_dir.
1934 */
1935
select_pdf_callback(GtkButton * b,function_info * finfo)1936 static void select_pdf_callback (GtkButton *b, function_info *finfo)
1937 {
1938 file_selector_with_parent(SELECT_PDF, FSEL_DATA_MISC,
1939 finfo, finfo->dlg);
1940 }
1941
add_help_radios(GtkWidget * tbl,int i,function_info * finfo)1942 static void add_help_radios (GtkWidget *tbl, int i,
1943 function_info *finfo)
1944 {
1945 GtkWidget *w, *rb, *htab;
1946 GSList *group = NULL;
1947
1948 w = gtk_label_new(_("Help text"));
1949 gtk_misc_set_alignment(GTK_MISC(w), 0.0, 0.5);
1950 gtk_table_attach_defaults(GTK_TABLE(tbl), w, i, i+1, 0, 1);
1951 gtk_widget_show_all(w);
1952
1953 htab = gtk_table_new(2, 2, TRUE);
1954 gtk_table_set_row_spacings(GTK_TABLE(htab), 4);
1955 gtk_table_set_col_spacings(GTK_TABLE(htab), 2);
1956
1957 rb = gtk_radio_button_new_with_label(group, _("Plain text"));
1958 gtk_table_attach_defaults(GTK_TABLE(htab), rb, 0, 1, 0, 1);
1959 w = gtk_button_new_from_stock(GTK_STOCK_EDIT);
1960 g_signal_connect(G_OBJECT(w), "clicked",
1961 G_CALLBACK(regular_help_text_callback), finfo);
1962 gtk_table_attach_defaults(GTK_TABLE(htab), w, 1, 2, 0, 1);
1963 sensitize_conditional_on(w, rb);
1964
1965 group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(rb));
1966 rb = gtk_radio_button_new_with_label(group, _("PDF file"));
1967 gtk_table_attach_defaults(GTK_TABLE(htab), rb, 0, 1, 1, 2);
1968 g_signal_connect(G_OBJECT(rb), "button-press-event",
1969 G_CALLBACK(pdf_press_callback), finfo);
1970 g_signal_connect(G_OBJECT(rb), "toggled",
1971 G_CALLBACK(pdf_toggled_callback), finfo);
1972 w = gtk_button_new_with_label(_("Select"));
1973 g_signal_connect(G_OBJECT(w), "clicked",
1974 G_CALLBACK(select_pdf_callback), finfo);
1975 gtk_table_attach_defaults(GTK_TABLE(htab), w, 1, 2, 1, 2);
1976 sensitize_conditional_on(w, rb);
1977
1978 if (finfo->pdfdoc) {
1979 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(rb), TRUE);
1980 }
1981
1982 gtk_table_attach_defaults(GTK_TABLE(tbl), htab, i, i+1, 1, 3);
1983 gtk_widget_show_all(htab);
1984 }
1985
1986 enum {
1987 OLD_TO_NEW,
1988 NEW_TO_OLD,
1989 FOR_DISPLAY
1990 };
1991
translate_program_version(int v,int trans)1992 static int translate_program_version (int v, int trans)
1993 {
1994 int vtrans[17][2] = {
1995 {10904, 20110},
1996 {10905, 20111},
1997 {10906, 20112},
1998 {10907, 20113},
1999 {10908, 20120},
2000 {10909, 20121},
2001 {10910, 20122},
2002 {10911, 20123},
2003 {10912, 20130},
2004 {10913, 20131},
2005 {10914, 20132},
2006 {10990, 20140},
2007 {10991, 20141},
2008 {10992, 20142},
2009 {11000, 20150},
2010 {11001, 20151},
2011 {11002, 20152}
2012 };
2013 int i;
2014
2015 if (trans == OLD_TO_NEW) {
2016 for (i=0; i<17; i++) {
2017 if (v == vtrans[i][0]) {
2018 return vtrans[i][1];
2019 }
2020 }
2021 if (v < vtrans[0][0]) {
2022 return vtrans[0][1];
2023 }
2024 } else {
2025 /* new to old, or "for display" */
2026 for (i=0; i<17; i++) {
2027 if (v == vtrans[i][1]) {
2028 return vtrans[i][0];
2029 } else if (i < 16 && v < vtrans[i+1][1]) {
2030 return vtrans[i][0];
2031 }
2032 }
2033 if (trans == NEW_TO_OLD && v < vtrans[0][1]) {
2034 return vtrans[0][0];
2035 }
2036 }
2037
2038 return trans == FOR_DISPLAY ? 0 : 20151;
2039 }
2040
set_oldver_label(GtkWidget * label,int minver)2041 static void set_oldver_label (GtkWidget *label, int minver)
2042 {
2043 int oldv = translate_program_version(minver, FOR_DISPLAY);
2044
2045 if (oldv == 0) {
2046 gtk_label_set_text(GTK_LABEL(label), "");
2047 } else {
2048 char vstr[12];
2049
2050 vstr[0] = '(';
2051 gretl_version_string(vstr + 1, oldv);
2052 strcat(vstr, ")");
2053 gtk_label_set_text(GTK_LABEL(label), vstr);
2054 }
2055 }
2056
adjust_minver(GtkSpinButton * b,function_info * finfo)2057 static void adjust_minver (GtkSpinButton *b, function_info *finfo)
2058 {
2059 GtkWidget *label;
2060
2061 finfo->minver = gtk_spin_button_get_value_as_int(b);
2062 finfo_set_modified(finfo, TRUE);
2063
2064 label = g_object_get_data(G_OBJECT(b), "old-label");
2065 if (label != NULL) {
2066 set_oldver_label(label, finfo->minver);
2067 }
2068 }
2069
letter_to_int(char c)2070 static int letter_to_int (char c)
2071 {
2072 const char *s = "abcdefghij";
2073 int i = 0;
2074
2075 while (*s) {
2076 if (c == *s) {
2077 return i;
2078 }
2079 s++;
2080 i++;
2081 }
2082
2083 return 0;
2084 }
2085
int_to_letter(int i)2086 static char int_to_letter (int i)
2087 {
2088 const char *s = "abcdefghij";
2089
2090 if (i >= 0 && i < 10) {
2091 return s[i];
2092 }
2093
2094 return 'a';
2095 }
2096
version_input(GtkSpinButton * spin,gdouble * new_val,gpointer p)2097 static gint version_input (GtkSpinButton *spin,
2098 gdouble *new_val,
2099 gpointer p)
2100 {
2101 const gchar *s = gtk_entry_get_text(GTK_ENTRY(spin));
2102
2103 *new_val = 10 * atoi(s) + letter_to_int(s[4]);
2104
2105 return TRUE;
2106 }
2107
version_output(GtkSpinButton * spin,gpointer p)2108 static gboolean version_output (GtkSpinButton *spin, gpointer p)
2109 {
2110 int n = gtk_spin_button_get_value_as_int(spin);
2111 int r = n - 10*(n/10);
2112 char buf[6] = {0};
2113
2114 sprintf(buf, "%d", n);
2115 buf[4] = int_to_letter(r);
2116 gtk_entry_set_text(GTK_ENTRY(spin), buf);
2117
2118 return TRUE;
2119 }
2120
add_minver_selector(GtkWidget * tbl,int i,function_info * finfo)2121 static void add_minver_selector (GtkWidget *tbl, int i,
2122 function_info *finfo)
2123 {
2124 GtkWidget *label, *spin, *hbox;
2125 int minminver = 20110; /* gretl 1.9.4, new-style */
2126 int maxminver;
2127 int lwidth;
2128
2129 /* max version requirement: the highest possible release
2130 in the build year */
2131 maxminver = 10 * atoi(GRETL_VERSION) + 9;
2132
2133 if (finfo->minver < 20000) {
2134 /* update an old-style "minver" value */
2135 finfo->minver =
2136 translate_program_version(finfo->minver, OLD_TO_NEW);
2137 }
2138
2139 /* fix out-of-bounds minver */
2140 if (finfo->minver < minminver) {
2141 finfo->minver = minminver;
2142 } else if (finfo->minver > maxminver) {
2143 finfo->minver = maxminver;
2144 }
2145
2146 label = gtk_label_new(_("Minimum gretl version"));
2147 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
2148 gtk_table_attach_defaults(GTK_TABLE(tbl), label, i, i+1, 0, 1);
2149 gtk_widget_show(label);
2150
2151 /* to align things below */
2152 hbox = gtk_hbox_new(FALSE, 0);
2153
2154 /* new-style version spinner */
2155 spin = gtk_spin_button_new_with_range(minminver, maxminver, 1);
2156 gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin), finfo->minver);
2157 gtk_spin_button_set_digits(GTK_SPIN_BUTTON(spin), 5);
2158 gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(spin), FALSE);
2159 g_signal_connect(G_OBJECT(spin), "value-changed",
2160 G_CALLBACK(adjust_minver), finfo);
2161 g_signal_connect(G_OBJECT(spin), "input",
2162 G_CALLBACK(version_input), NULL);
2163 g_signal_connect(G_OBJECT(spin), "output",
2164 G_CALLBACK(version_output), NULL);
2165 gtk_entry_set_width_chars(GTK_ENTRY(spin), 5);
2166 #if GTK_MAJOR_VERSION == 3
2167 /* remedy required for gtk3 */
2168 gtk_entry_set_max_width_chars(GTK_ENTRY(spin), 5);
2169 #endif
2170 gtk_box_pack_start(GTK_BOX(hbox), spin, FALSE, FALSE, 2);
2171
2172 /* translation to old-style version? */
2173 label = gtk_label_new(NULL);
2174 lwidth = get_string_width(" (1.9.12) ");
2175 gtk_widget_set_size_request(label, lwidth, -1);
2176 g_object_set_data(G_OBJECT(spin), "old-label", label);
2177 set_oldver_label(label, finfo->minver);
2178 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 5);
2179
2180 gtk_table_attach_defaults(GTK_TABLE(tbl), hbox, i, i+1, 1, 2);
2181 gtk_widget_show_all(hbox);
2182
2183 /* placeholder to prevent the above from slipping down */
2184 label = gtk_label_new("");
2185 gtk_table_attach_defaults(GTK_TABLE(tbl), label, i, i+1, 2, 3);
2186 gtk_widget_show_all(label);
2187 }
2188
2189 struct jel_lookup {
2190 int code;
2191 const char *label;
2192 };
2193
2194 struct jel_lookup tag_lookups[] = {
2195 { 10, "Econometric and Statistical Methods: General" },
2196 { 11, "Bayesian Analysis: General" },
2197 { 12, "Hypothesis Testing: General" },
2198 { 13, "Estimation: General" },
2199 { 14, "Semiparametric and Nonparametric Methods" },
2200 { 15, "Statistical Simulation Methods: General" },
2201 { 20, "Single Equation Models: General" },
2202 { 21, "Cross-Sectional Models" },
2203 { 22, "Univariate Time-Series Models" },
2204 { 23, "Univariate Panel Data Models" },
2205 { 24, "Truncated, Censored and Threshold Models" },
2206 { 25, "Discrete and Qualitative Choice Models" },
2207 { 26, "Instrumental Variables (IV) Estimation" },
2208 { 30, "Multivariate Models: General" },
2209 { 31, "Multivariate Cross-sectional Models" },
2210 { 32, "Multivariate Time-Series Models" },
2211 { 33, "Multivariate Panel Data Models" },
2212 { 34, "Multivariate: Truncated and Censored" },
2213 { 35, "Multivariate: Discrete and Qualitative" },
2214 { 36, "Multivariate: IV Estimation" },
2215 { 38, "Classification Methods" },
2216 { 40, "Econometric Methods: Special Topics" },
2217 { 41, "Duration Models" },
2218 { 51, "Model Construction and Estimation" },
2219 { 52, "Model Evaluation, Validation, and Selection" },
2220 { 53, "Forecasting, Prediction and Simulation Methods" },
2221 { 54, "Quantitative Policy Modeling" },
2222 { 58, "Financial Econometrics" },
2223 { 81, "Data Access" },
2224 { 88, "Other Computer Software" },
2225 { 0, NULL }
2226 };
2227
2228 /* As a fallback if we couldn't get the canonical listing of tags
2229 from the server, use the inline info above to construct the
2230 listing: we hope it's in sync with that on the server!
2231 */
2232
make_local_tags_buf(void)2233 static char *make_local_tags_buf (void)
2234 {
2235 char *s = NULL;
2236 size_t len = 0;
2237 int i;
2238
2239 for (i=0; tag_lookups[i].code > 0; i++) {
2240 len += strlen(tag_lookups[i].label) + 8;
2241 }
2242
2243 s = calloc(len, 1);
2244
2245 if (s != NULL) {
2246 char s0[6];
2247
2248 for (i=0; tag_lookups[i].code > 0; i++) {
2249 sprintf(s0, "C%02d: ", tag_lookups[i].code);
2250 strcat(s, s0);
2251 strcat(s, tag_lookups[i].label);
2252 strcat(s, "\n");
2253 }
2254 }
2255
2256 return s;
2257 }
2258
tagsel_callback(GtkComboBox * combo,function_info * finfo)2259 static void tagsel_callback (GtkComboBox *combo,
2260 function_info *finfo)
2261 {
2262 char code[6], newtags[32];
2263 int t1, t2;
2264 gchar *s;
2265
2266 if (GTK_WIDGET(combo) == finfo->tagsel[0]) {
2267 t1 = gtk_combo_box_get_active(combo);
2268 t2 = gtk_combo_box_get_active(GTK_COMBO_BOX(finfo->tagsel[1]));
2269 } else {
2270 t1 = gtk_combo_box_get_active(GTK_COMBO_BOX(finfo->tagsel[0]));
2271 t2 = gtk_combo_box_get_active(combo);
2272 }
2273
2274 /* make Tag 2 selectable only if we have a Tag 1 */
2275 gtk_widget_set_sensitive(finfo->tagsel[1], t1 > 0);
2276
2277 if (t1 == 0 || (t2 > 0 && t2 == t1)) {
2278 /* interdict setting the same tag twice */
2279 gtk_combo_box_set_active(GTK_COMBO_BOX(finfo->tagsel[1]), 0);
2280 }
2281
2282 *code = *newtags = '\0';
2283
2284 if (t1 > 0) {
2285 s = combo_box_get_active_text(finfo->tagsel[0]);
2286 sscanf(s, "%4[^: ]", code);
2287 strcat(newtags, code);
2288 g_free(s);
2289 }
2290 if (t2 > 0) {
2291 s = combo_box_get_active_text(finfo->tagsel[1]);
2292 sscanf(s, "%4[^: ]", code);
2293 if (*newtags != '\0') {
2294 strcat(newtags, " ");
2295 }
2296 strcat(newtags, code);
2297 g_free(s);
2298 }
2299
2300 if (*newtags == '\0') {
2301 /* no tags selected in dialog */
2302 if (finfo->tags != NULL) {
2303 g_free(finfo->tags);
2304 finfo->tags = NULL;
2305 finfo_set_modified(finfo, TRUE);
2306 }
2307 } else if (finfo->tags == NULL || strcmp(newtags, finfo->tags)) {
2308 /* update from non-empty @newtags */
2309 g_free(finfo->tags);
2310 finfo->tags = g_strdup(newtags);
2311 finfo_set_modified(finfo, TRUE);
2312 }
2313 }
2314
add_tag_selectors(GtkWidget * tbl,int i,function_info * finfo)2315 static void add_tag_selectors (GtkWidget *tbl, int i,
2316 function_info *finfo)
2317 {
2318 GtkWidget *tmp, *hbox, *combo;
2319 char line[128];
2320 char *getbuf = NULL;
2321 char **S = NULL;
2322 int n_tags = 0;
2323 int j, err;
2324
2325 err = list_remote_function_categories(&getbuf, OPT_A);
2326
2327 if (err || getbuf == NULL || *getbuf != 'C') {
2328 free(getbuf);
2329 getbuf = NULL;
2330 }
2331
2332 if (getbuf == NULL) {
2333 fprintf(stderr, "add_tag_selectors: couldn't get tags list from server\n");
2334 getbuf = make_local_tags_buf();
2335 if (getbuf == NULL) {
2336 return;
2337 }
2338 }
2339
2340 if (finfo->tags != NULL) {
2341 S = gretl_string_split(finfo->tags, &n_tags, NULL);
2342 }
2343
2344 bufgets_init(getbuf);
2345
2346 for (j=0; j<2; j++) {
2347 int active = 0;
2348 int k = 0;
2349
2350 if (j == 0) {
2351 tmp = gtk_label_new(_("Tag"));
2352 } else {
2353 tmp = gtk_label_new(_("Tag 2 (optional)"));
2354 }
2355 gtk_misc_set_alignment(GTK_MISC(tmp), 1.0, 0.5);
2356 gtk_table_attach_defaults(GTK_TABLE(tbl), tmp, 0, 1, i, i+1);
2357 gtk_widget_show(tmp);
2358
2359 finfo->tagsel[j] = combo = gtk_combo_box_text_new();
2360 combo_box_append_text(combo, _("none"));
2361 while (bufgets(line, sizeof line, getbuf)) {
2362 k++;
2363 combo_box_append_text(combo, tailstrip(line));
2364 if (n_tags > j && !strncmp(line, S[j], strlen(S[j]))) {
2365 /* this code is pre-selected */
2366 active = k;
2367 }
2368 }
2369 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), active);
2370 g_signal_connect(G_OBJECT(combo), "changed",
2371 G_CALLBACK(tagsel_callback), finfo);
2372 if (j > 0 && n_tags == 0) {
2373 gtk_widget_set_sensitive(combo, FALSE);
2374 }
2375
2376 hbox = gtk_hbox_new(FALSE, 0);
2377 gtk_box_pack_start(GTK_BOX(hbox), combo, FALSE, FALSE, 2);
2378 gtk_table_attach_defaults(GTK_TABLE(tbl), hbox, 1, 2, i, i+1);
2379 gtk_widget_show_all(hbox);
2380
2381 if (j == 0) {
2382 buf_rewind(getbuf);
2383 i++;
2384 }
2385 }
2386
2387 bufgets_finalize(getbuf);
2388
2389 if (S != NULL) {
2390 strings_array_free(S, n_tags);
2391 }
2392 }
2393
pkg_changed(gpointer p,function_info * finfo)2394 static void pkg_changed (gpointer p, function_info *finfo)
2395 {
2396 finfo_set_modified(finfo, TRUE);
2397 }
2398
get_user_string(void)2399 static const gchar *get_user_string (void)
2400 {
2401 const gchar *name;
2402
2403 name = g_get_real_name();
2404 if (name == NULL) {
2405 name = g_get_user_name();
2406 }
2407
2408 return name;
2409 }
2410
insert_today(GtkWidget * w,GtkWidget * entry)2411 static void insert_today (GtkWidget *w, GtkWidget *entry)
2412 {
2413 gtk_entry_set_text(GTK_ENTRY(entry), print_today());
2414 }
2415
today_popup(GtkWidget * entry,GdkEventButton * event,GtkWidget ** popup)2416 static gint today_popup (GtkWidget *entry, GdkEventButton *event,
2417 GtkWidget **popup)
2418 {
2419 if (*popup == NULL) {
2420 GtkWidget *menu = gtk_menu_new();
2421 GtkWidget *item;
2422
2423 item = gtk_menu_item_new_with_label(_("Insert today's date"));
2424 g_signal_connect(G_OBJECT(item), "activate",
2425 G_CALLBACK(insert_today), entry);
2426 gtk_widget_show(item);
2427 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
2428 *popup = menu;
2429 }
2430
2431 gtk_menu_popup(GTK_MENU(*popup), NULL, NULL, NULL, NULL,
2432 event->button, event->time);
2433
2434 return TRUE;
2435 }
2436
query_save_package(GtkWidget * w,GdkEvent * event,function_info * finfo)2437 static gint query_save_package (GtkWidget *w, GdkEvent *event,
2438 function_info *finfo)
2439 {
2440 if (finfo->modified) {
2441 int resp =
2442 yes_no_cancel_dialog("gretl", _("Save changes?"), w);
2443
2444 if (resp == GRETL_CANCEL) {
2445 return TRUE;
2446 } else if (resp == GRETL_YES) {
2447 return finfo_save(finfo);
2448 }
2449 }
2450
2451 return FALSE;
2452 }
2453
check_pkg_callback(GtkWidget * widget,function_info * finfo)2454 static void check_pkg_callback (GtkWidget *widget, function_info *finfo)
2455 {
2456 validate_package_file(finfo->fname, 1);
2457 }
2458
make_menu_attachment_tree(function_info * finfo,GtkTreePath ** ppath,int modelwin)2459 static GtkTreeStore *make_menu_attachment_tree (function_info *finfo,
2460 GtkTreePath **ppath,
2461 int modelwin)
2462 {
2463 const char *main_items =
2464 "0 Tools\n"
2465 "0 Data\n"
2466 "0 View\n"
2467 "1 GraphVars\n"
2468 "2 MultiPlots\n"
2469 "0 Add\n"
2470 "0 Sample\n"
2471 "0 Variable\n"
2472 "1 URTests\n"
2473 "1 Filter\n"
2474 "0 Model\n"
2475 "1 ivreg\n"
2476 "1 LinearModels\n"
2477 "1 LimdepModels\n"
2478 "2 logit\n"
2479 "2 probit\n"
2480 "1 TSModels\n"
2481 "2 AR-GLS\n"
2482 "1 TSMulti\n"
2483 "1 PanelModels\n"
2484 "1 RobustModels\n";
2485 const char *model_items =
2486 "0 Edit\n"
2487 "0 Tests\n"
2488 "0 Save\n"
2489 "0 Graphs\n"
2490 "0 Analysis\n";
2491 const char *leaders[] = {
2492 "MAINWIN",
2493 "MODELWIN"
2494 };
2495 const char *leader;
2496 GtkTreeStore *store;
2497 GtkTreeIter iter;
2498 GtkTreeIter parents[2];
2499 GtkTreeIter *iterp;
2500 gchar *path, *ustr;
2501 const char *s;
2502 char words[3][16];
2503 char *word;
2504 int level;
2505
2506 store = gtk_tree_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
2507
2508 leader = modelwin ? leaders[1] : leaders[0];
2509 s = modelwin ? model_items : main_items;
2510
2511 while (*s) {
2512 level = atoi(s);
2513 word = words[level];
2514 s += 2;
2515 sscanf(s, "%s", word);
2516 ustr = user_friendly_menu_path(word, modelwin);
2517 if (level == 0) {
2518 path = g_strdup_printf("%s/%s", leader, words[0]);
2519 gtk_tree_store_append(store, &parents[0], NULL);
2520 iterp = &parents[0];
2521 } else if (level == 1) {
2522 path = g_strdup_printf("%s/%s/%s", leader, words[0],
2523 words[1]);
2524 gtk_tree_store_append(store, &parents[1], &parents[0]);
2525 iterp = &parents[1];
2526 } else {
2527 path = g_strdup_printf("%s/%s/%s/%s", leader, words[0],
2528 words[1], words[2]);
2529 gtk_tree_store_append(store, &iter, &parents[1]);
2530 iterp = &iter;
2531 }
2532 gtk_tree_store_set(store, iterp, 0, ustr, 1, path, -1);
2533 if (finfo->menupath != NULL && !strcmp(path, finfo->menupath)) {
2534 /* record the path of pre-selected menu entry */
2535 *ppath = gtk_tree_model_get_path(GTK_TREE_MODEL(store), iterp);
2536 }
2537 g_free(path);
2538 g_free(ustr);
2539 s += strlen(word) + 1;
2540 }
2541
2542 return store;
2543 }
2544
add_menu_navigator(GtkWidget * holder,function_info * finfo,int modelwin)2545 static GtkWidget *add_menu_navigator (GtkWidget *holder,
2546 function_info *finfo,
2547 int modelwin)
2548 {
2549 GtkTreeStore *store;
2550 GtkWidget *view;
2551 GtkCellRenderer *renderer;
2552 GtkTreeViewColumn *column;
2553 GtkTreeSelection *select;
2554 GtkTreePath *path = NULL;
2555
2556 store = make_menu_attachment_tree(finfo, &path, modelwin);
2557 if (store == NULL) {
2558 return NULL;
2559 }
2560
2561 view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
2562 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), FALSE);
2563 g_object_set(view, "enable-tree-lines", TRUE, NULL);
2564 g_object_ref(G_OBJECT(view));
2565
2566 renderer = gtk_cell_renderer_text_new();
2567 column = gtk_tree_view_column_new_with_attributes("",
2568 renderer,
2569 "text", 0,
2570 NULL);
2571 gtk_tree_view_append_column(GTK_TREE_VIEW(view), column);
2572
2573 select = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
2574 gtk_tree_selection_set_mode(select, GTK_SELECTION_SINGLE);
2575
2576 if (path != NULL) {
2577 gtk_tree_view_expand_to_path(GTK_TREE_VIEW(view), path);
2578 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(view), path,
2579 NULL, FALSE, 0, 0);
2580 gtk_tree_selection_select_path(select, path);
2581 gtk_tree_path_free(path);
2582 }
2583
2584 if (finfo->treewin == NULL) {
2585 GtkWidget *sw;
2586
2587 sw = finfo->treewin = gtk_scrolled_window_new(NULL, NULL);
2588 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
2589 GTK_POLICY_AUTOMATIC,
2590 GTK_POLICY_AUTOMATIC);
2591 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
2592 GTK_SHADOW_IN);
2593 gtk_container_add(GTK_CONTAINER(holder), sw);
2594 gtk_widget_set_size_request(sw, 150, 200);
2595 }
2596
2597 if ((modelwin == 0 && finfo->menuwin != MODEL_WINDOW) ||
2598 (modelwin == 1 && finfo->menuwin == MODEL_WINDOW)) {
2599 gtk_container_add(GTK_CONTAINER(finfo->treewin), view);
2600 finfo->currtree = view;
2601 } else {
2602 finfo->alttree = view;
2603 }
2604
2605 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(view));
2606
2607 return view;
2608 }
2609
2610 static GtkWidget *
model_requirement_selector(GtkWidget * holder,function_info * finfo)2611 model_requirement_selector (GtkWidget *holder,
2612 function_info *finfo)
2613 {
2614 GtkWidget *hbox, *label;
2615 GtkWidget *combo;
2616 GretlCmdIndex ci;
2617 int deflt = 0;
2618 int j = 0;
2619
2620 hbox = gtk_hbox_new(FALSE, 5);
2621 label = gtk_label_new(_("Model requirement"));
2622 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 5);
2623
2624 combo = gtk_combo_box_text_new();
2625 g_object_set_data(G_OBJECT(combo), "label", label);
2626 combo_box_append_text(combo, _("Any model"));
2627
2628 for (ci=1; ci<NC; ci++) {
2629 if (MODEL_COMMAND(ci) || EQN_SYSTEM_COMMAND(ci)) {
2630 j++;
2631 combo_box_append_text(combo, gretl_command_word(ci));
2632 if (finfo->mreq == ci) {
2633 deflt = j;
2634 }
2635 }
2636 }
2637
2638 gtk_box_pack_start(GTK_BOX(hbox), combo, FALSE, FALSE, 5);
2639 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), deflt);
2640 gtk_box_pack_start(GTK_BOX(holder), hbox, FALSE, FALSE, 5);
2641
2642 gtk_widget_set_sensitive(label, finfo->menuwin == MODEL_WINDOW);
2643 gtk_widget_set_sensitive(combo, finfo->menuwin == MODEL_WINDOW);
2644
2645 return combo;
2646 }
2647
2648 static GtkWidget *
access_request_button(GtkWidget * holder,function_info * finfo)2649 access_request_button (GtkWidget *holder,
2650 function_info *finfo)
2651 {
2652 GtkWidget *hbox, *button;
2653
2654 hbox = gtk_hbox_new(FALSE, 5);
2655 button = gtk_check_button_new_with_label(_("request access to out-of-sample data"));
2656 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 5);
2657 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), finfo->data_access);
2658 // g_signal_connect(button, "toggled", access_button_callback, finfo);
2659 gtk_box_pack_start(GTK_BOX(holder), hbox, FALSE, FALSE, 5);
2660
2661 return button;
2662 }
2663
switch_menu_view(GtkComboBox * combo,function_info * finfo)2664 static void switch_menu_view (GtkComboBox *combo,
2665 function_info *finfo)
2666 {
2667 int w = gtk_combo_box_get_active(combo);
2668 GtkWidget *sw = finfo->treewin;
2669
2670 if (w == NO_WINDOW) {
2671 if (finfo->currtree != NULL) {
2672 gtk_widget_set_sensitive(finfo->currtree, FALSE);
2673 }
2674 } else if (w == MAIN_WINDOW) {
2675 if (finfo->currtree == finfo->modeltree) {
2676 gtk_container_remove(GTK_CONTAINER(sw), finfo->modeltree);
2677 finfo->alttree = finfo->modeltree;
2678 gtk_widget_show(finfo->maintree);
2679 gtk_container_add(GTK_CONTAINER(sw), finfo->maintree);
2680 finfo->currtree = finfo->maintree;
2681 }
2682 gtk_widget_set_sensitive(finfo->currtree, TRUE);
2683 } else if (w == MODEL_WINDOW) {
2684 if (finfo->currtree == finfo->maintree) {
2685 gtk_container_remove(GTK_CONTAINER(sw), finfo->maintree);
2686 finfo->alttree = finfo->maintree;
2687 gtk_widget_show(finfo->modeltree);
2688 gtk_container_add(GTK_CONTAINER(sw), finfo->modeltree);
2689 finfo->currtree = finfo->modeltree;
2690 }
2691 gtk_widget_set_sensitive(finfo->currtree, TRUE);
2692 }
2693
2694 if (finfo->mreq_combo != NULL) {
2695 GtkWidget *l = g_object_get_data(G_OBJECT(finfo->mreq_combo),
2696 "label");
2697
2698 gtk_widget_set_sensitive(finfo->mreq_combo,
2699 w == MODEL_WINDOW);
2700 gtk_widget_set_sensitive(l, w == MODEL_WINDOW);
2701 }
2702 }
2703
add_data_files_entries(GtkWidget * holder,function_info * finfo)2704 static void add_data_files_entries (GtkWidget *holder,
2705 function_info *finfo)
2706 {
2707 const char *msg = N_("You may add or delete names of data"
2708 "files to be included in the package.");
2709 GtkWidget *w, *hbox, *entry;
2710 int i;
2711
2712 w = gtk_label_new(_(msg));
2713 gtk_label_set_line_wrap(GTK_LABEL(w), TRUE);
2714 hbox = gtk_hbox_new(FALSE, 5);
2715 gtk_box_pack_start(GTK_BOX(hbox), w, FALSE, FALSE, 5);
2716 gtk_box_pack_start(GTK_BOX(holder), hbox, FALSE, FALSE, 5);
2717
2718 for (i=0; i<N_FILE_ENTRIES; i++) {
2719 hbox = gtk_hbox_new(FALSE, 5);
2720 finfo->file_entries[i] = entry = gtk_entry_new();
2721 gtk_entry_set_width_chars(GTK_ENTRY(entry), 32);
2722 if (i < finfo->n_files) {
2723 gtk_entry_set_text(GTK_ENTRY(entry), finfo->datafiles[i]);
2724 }
2725 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2726 gtk_box_pack_start(GTK_BOX(holder), hbox, FALSE, FALSE, 5);
2727 }
2728 }
2729
set_prov_check_state(GtkWidget * b,function_info * finfo)2730 static void set_prov_check_state (GtkWidget *b, function_info *finfo)
2731 {
2732 gboolean s = FALSE;
2733
2734 if (finfo->provider != NULL && finfo->n_depends > 0) {
2735 if (!strcmp(finfo->provider, finfo->depends[0])) {
2736 s = TRUE;
2737 }
2738 } else if (finfo->n_depends == 0) {
2739 gtk_widget_set_sensitive(b, FALSE);
2740 }
2741
2742 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(b), s);
2743 }
2744
adjust_prov_check(GtkEditable * w,GtkWidget * b)2745 static void adjust_prov_check (GtkEditable *w, GtkWidget *b)
2746 {
2747 const gchar *s = gtk_entry_get_text(GTK_ENTRY(w));
2748
2749 if (s == NULL || string_is_blank(s)) {
2750 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(b), FALSE);
2751 gtk_widget_set_sensitive(b, FALSE);
2752 } else {
2753 gtk_widget_set_sensitive(b, TRUE);
2754 }
2755 }
2756
add_dependency_entries(GtkWidget * holder,function_info * finfo)2757 static void add_dependency_entries (GtkWidget *holder,
2758 function_info *finfo)
2759 {
2760 const char *msg = N_("You may add or delete names of packages "
2761 "to be recorded as dependencies.\nLeave off the "
2762 ".gfn or .zip suffix.");
2763 GtkWidget *w, *hbox, *entry;
2764 int i;
2765
2766 w = gtk_label_new(_(msg));
2767 gtk_label_set_line_wrap(GTK_LABEL(w), TRUE);
2768 hbox = gtk_hbox_new(FALSE, 5);
2769 gtk_box_pack_start(GTK_BOX(hbox), w, FALSE, FALSE, 5);
2770 gtk_box_pack_start(GTK_BOX(holder), hbox, FALSE, FALSE, 5);
2771
2772 for (i=0; i<N_DEP_ENTRIES; i++) {
2773 hbox = gtk_hbox_new(FALSE, 5);
2774 finfo->dep_entries[i] = entry = gtk_entry_new();
2775 gtk_entry_set_width_chars(GTK_ENTRY(entry), 32);
2776 if (i < finfo->n_depends) {
2777 gtk_entry_set_text(GTK_ENTRY(entry), finfo->depends[i]);
2778 }
2779 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2780 if (i == 0) {
2781 finfo->prov_check = gtk_check_button_new_with_label(_("provider?"));
2782 set_prov_check_state(finfo->prov_check, finfo);
2783 gtk_box_pack_start(GTK_BOX(hbox), finfo->prov_check, FALSE, FALSE, 5);
2784 g_signal_connect(G_OBJECT(entry), "changed",
2785 G_CALLBACK(adjust_prov_check), finfo->prov_check);
2786 }
2787 gtk_box_pack_start(GTK_BOX(holder), hbox, FALSE, FALSE, 5);
2788 }
2789 }
2790
gui_help_text_callback(GtkButton * b,function_info * finfo)2791 static void gui_help_text_callback (GtkButton *b, function_info *finfo)
2792 {
2793 const char *pkgname;
2794 gchar *title;
2795 PRN *prn = NULL;
2796
2797 if (finfo->gui_helpwin != NULL) {
2798 gtk_window_present(GTK_WINDOW(finfo->gui_helpwin->main));
2799 return;
2800 }
2801
2802 if (finfo->gui_help == NULL) {
2803 const char *msg =
2804 N_("This package has no GUI-specific help text at present.\n"
2805 "Would you like to add some?");
2806
2807 if (yes_no_dialog(NULL, _(msg), finfo->extra) != GRETL_YES) {
2808 return;
2809 }
2810 }
2811
2812 if (bufopen(&prn)) {
2813 return;
2814 }
2815
2816 pkgname = finfo_pkgname(finfo);
2817 title = g_strdup_printf("%s gui-help", pkgname);
2818
2819 if (finfo->gui_help != NULL) {
2820 pputs(prn, finfo->gui_help);
2821 pputc(prn, '\n');
2822 }
2823
2824 finfo->gui_helpwin = view_buffer(prn, HELP_WIDTH, HELP_HEIGHT, title,
2825 EDIT_PKG_GHLP, finfo);
2826 g_object_set_data(G_OBJECT(finfo->gui_helpwin->main), "finfo",
2827 finfo);
2828 g_signal_connect(G_OBJECT(finfo->gui_helpwin->main), "destroy",
2829 G_CALLBACK(nullify_gui_helpwin), finfo);
2830 g_free(title);
2831 }
2832
2833 /* callback for editing plain-text package help */
2834
regular_help_text_callback(GtkButton * b,function_info * finfo)2835 static void regular_help_text_callback (GtkButton *b, function_info *finfo)
2836 {
2837 const char *pkgname = finfo_pkgname(finfo);
2838 gchar *title;
2839 PRN *prn = NULL;
2840
2841 if (finfo->helpwin != NULL) {
2842 gtk_window_present(GTK_WINDOW(finfo->helpwin->main));
2843 return;
2844 }
2845
2846 if (bufopen(&prn)) {
2847 return;
2848 }
2849
2850 title = g_strdup_printf("%s help", pkgname);
2851
2852 if (finfo->help != NULL) {
2853 pputs(prn, finfo->help);
2854 pputc(prn, '\n');
2855 }
2856
2857 finfo->helpwin = view_buffer(prn, HELP_WIDTH, HELP_HEIGHT, title,
2858 EDIT_PKG_HELP, finfo);
2859 g_object_set_data(G_OBJECT(finfo->helpwin->main), "finfo",
2860 finfo);
2861 g_signal_connect(G_OBJECT(finfo->helpwin->main), "destroy",
2862 G_CALLBACK(nullify_helpwin), finfo);
2863 g_free(title);
2864 }
2865
add_menu_attach_top(GtkWidget * holder,function_info * finfo)2866 static void add_menu_attach_top (GtkWidget *holder,
2867 function_info *finfo)
2868 {
2869 GtkWidget *w, *hbox, *entry;
2870 GtkWidget *combo;
2871
2872 /* menu label entry */
2873 hbox = gtk_hbox_new(FALSE, 5);
2874 w = gtk_label_new(_("Label"));
2875 gtk_box_pack_start(GTK_BOX(hbox), w, FALSE, FALSE, 5);
2876 entry = gtk_entry_new();
2877 gtk_entry_set_max_length(GTK_ENTRY(entry), 36);
2878 gtk_entry_set_width_chars(GTK_ENTRY(entry), 32);
2879 if (finfo->menulabel != NULL) {
2880 gtk_entry_set_text(GTK_ENTRY(entry), finfo->menulabel);
2881 }
2882 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2883 g_object_set_data(G_OBJECT(finfo->extra), "label-entry", entry);
2884 gtk_box_pack_start(GTK_BOX(holder), hbox, FALSE, FALSE, 5);
2885
2886 /* menu attachment combo */
2887 hbox = gtk_hbox_new(FALSE, 5);
2888 w = gtk_label_new(_("Window"));
2889 gtk_box_pack_start(GTK_BOX(hbox), w, FALSE, FALSE, 5);
2890 combo = gtk_combo_box_text_new();
2891 combo_box_append_text(combo, _("none"));
2892 combo_box_append_text(combo, _("main window"));
2893 combo_box_append_text(combo, _("model window"));
2894 gtk_box_pack_start(GTK_BOX(hbox), combo, FALSE, FALSE, 5);
2895 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), finfo->menuwin);
2896 g_signal_connect(G_OBJECT(combo), "changed",
2897 G_CALLBACK(switch_menu_view), finfo);
2898
2899 /* gui-help button */
2900 w = gtk_button_new_with_label(_("GUI help text"));
2901 g_signal_connect(G_OBJECT(w), "clicked",
2902 G_CALLBACK(gui_help_text_callback), finfo);
2903 gtk_box_pack_end(GTK_BOX(hbox), w, FALSE, FALSE, 5);
2904
2905 /* complete the packing */
2906 gtk_box_pack_start(GTK_BOX(holder), hbox, FALSE, FALSE, 5);
2907 }
2908
get_model_req_ci(function_info * finfo)2909 static GretlCmdIndex get_model_req_ci (function_info *finfo)
2910 {
2911 GtkWidget *combo = finfo->mreq_combo;
2912 GretlCmdIndex ci = 0;
2913
2914 if (combo != NULL && gtk_widget_is_sensitive(combo)) {
2915 gchar *s = combo_box_get_active_text(combo);
2916
2917 ci = gretl_command_number(s);
2918 g_free(s);
2919 }
2920
2921 return ci;
2922 }
2923
2924 /* pertaining to the "extra properties" dialog: check for
2925 any changes in relation to menu attachment
2926 */
2927
process_menu_attachment(function_info * finfo,gboolean make_changes,int * focus_label)2928 static int process_menu_attachment (function_info *finfo,
2929 gboolean make_changes,
2930 int *focus_label)
2931 {
2932 GtkTreeSelection *selection;
2933 GtkTreeModel *model;
2934 GtkTreeIter iter;
2935 GtkWidget *view, *entry;
2936 gchar *label;
2937 int changed = 0;
2938
2939 view = finfo->currtree;
2940
2941 entry = g_object_get_data(G_OBJECT(finfo->extra), "label-entry");
2942 label = entry_box_get_trimmed_text(entry);
2943
2944 if (label == NULL || *label == '\0') {
2945 if (finfo->menulabel != NULL) {
2946 if (make_changes) {
2947 g_free(finfo->menulabel);
2948 finfo->menulabel = NULL;
2949 }
2950 changed = 1;
2951 }
2952 } else if (finfo->menulabel == NULL || strcmp(finfo->menulabel, label)) {
2953 if (make_changes) {
2954 g_free(finfo->menulabel);
2955 finfo->menulabel = label;
2956 label = NULL;
2957 }
2958 changed = 1;
2959 }
2960
2961 g_free(label);
2962
2963 if (!gtk_widget_is_sensitive(view)) {
2964 /* no menu attachment at present */
2965 if (finfo->menupath != NULL) {
2966 if (make_changes) {
2967 g_free(finfo->menupath);
2968 finfo->menupath = NULL;
2969 }
2970 changed = 1;
2971 }
2972 finfo->menuwin = NO_WINDOW;
2973 goto finish;
2974 }
2975
2976 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
2977
2978 if (!gtk_tree_selection_get_selected(selection, &model, &iter)) {
2979 if (finfo->menupath != NULL) {
2980 if (make_changes) {
2981 g_free(finfo->menupath);
2982 finfo->menupath = NULL;
2983 }
2984 changed = 1;
2985 }
2986 } else {
2987 gchar *newpath = NULL;
2988
2989 gtk_tree_model_get(model, &iter, 1, &newpath, -1);
2990 if (finfo->menupath == NULL || strcmp(finfo->menupath, newpath)) {
2991 if (make_changes) {
2992 g_free(finfo->menupath);
2993 finfo->menupath = newpath;
2994 newpath = NULL;
2995 }
2996 changed = 1;
2997 }
2998 g_free(newpath);
2999 }
3000
3001 finfo_set_menuwin(finfo);
3002
3003 if (finfo->menuwin == MODEL_WINDOW) {
3004 /* model-requirement is relevant */
3005 GretlCmdIndex ci = get_model_req_ci(finfo);
3006
3007 if (ci != finfo->mreq) {
3008 if (make_changes) {
3009 finfo->mreq = ci;
3010 }
3011 changed = 1;
3012 }
3013 } else {
3014 /* model-requirement is otiose */
3015 if (finfo->mreq > 0) {
3016 if (make_changes) {
3017 finfo->mreq = 0;
3018 }
3019 changed = 1;
3020 }
3021 }
3022
3023 if (finfo->data_button != NULL) {
3024 gboolean req = button_is_active(finfo->data_button);
3025
3026 if (req != finfo->data_access) {
3027 if (make_changes) {
3028 finfo->data_access = req;
3029 }
3030 changed = 1;
3031 }
3032 }
3033
3034 finish:
3035
3036 if (make_changes) {
3037 if (finfo->menupath != NULL && *finfo->menupath != '\0' &&
3038 (finfo->menulabel == NULL || *finfo->menulabel == '\0')) {
3039 warnbox(_("To create a menu attachment, you must supply a label."));
3040 *focus_label = 1;
3041 }
3042 }
3043
3044 return changed;
3045 }
3046
want_no_print_toggle(int role)3047 static int want_no_print_toggle (int role)
3048 {
3049 return role != UFUN_GUI_PRECHECK &&
3050 role != UFUN_BUNDLE_PRINT;
3051 }
3052
3053 /* pertaining to the "extra properties" dialog: check for
3054 any changes in relation to the special functions table
3055 */
3056
process_special_functions(function_info * finfo,gboolean make_changes)3057 static int process_special_functions (function_info *finfo,
3058 gboolean make_changes)
3059 {
3060 GtkWidget **c_array;
3061 const char *oldfun;
3062 gchar *newfun;
3063 int n_changed = 0;
3064 int i, err = 0;
3065
3066 c_array = g_object_get_data(G_OBJECT(finfo->extra), "combo-array");
3067
3068 /* For each special function slot, check to see if the
3069 currently selected function differs from what was
3070 present originally, and if so update the record in
3071 @finfo->specials. The changes are not yet saved to
3072 the function package itself.
3073 */
3074
3075 for (i=0; i<N_SPECIALS && !err; i++) {
3076 int role = i + 1;
3077
3078 if (gtk_widget_is_sensitive(c_array[i])) {
3079 int fn_changed = 0, attr_changed = 0;
3080 int newnull = 0, oldnull = 0;
3081 unsigned char attr = 0;
3082 GtkWidget *cb;
3083
3084 /* retrieve and check the selected name */
3085 newfun = combo_box_get_active_text(GTK_COMBO_BOX(c_array[i]));
3086 newnull = (newfun == NULL || *newfun == '\0' ||
3087 !strcmp(newfun, "none"));
3088
3089 /* retrieve and check what was there before */
3090 oldfun = finfo->specials[i];
3091 oldnull = (oldfun == NULL || *oldfun == '\0' ||
3092 !strcmp(oldfun, "none"));
3093
3094 if (want_no_print_toggle(role)) {
3095 /* retrieve the no-print attribute? */
3096 cb = g_object_get_data(G_OBJECT(c_array[i]), "np-toggle");
3097 if (cb != NULL && button_is_active(cb)) {
3098 attr |= UFUN_NOPRINT;
3099 }
3100 }
3101
3102 if (role == UFUN_GUI_MAIN) {
3103 /* retrieve the menu-only attribute? */
3104 cb = g_object_get_data(G_OBJECT(c_array[i]), "mo-toggle");
3105 if (cb != NULL && button_is_active(cb)) {
3106 attr |= UFUN_MENU_ONLY;
3107 }
3108 }
3109
3110 if (oldnull && !newnull) {
3111 fn_changed = 1;
3112 } else if (!oldnull && newnull) {
3113 fn_changed = 1;
3114 } else if (!oldnull && !newnull) {
3115 fn_changed = strcmp(newfun, oldfun);
3116 }
3117
3118 if (attr != finfo->gui_attrs[i]) {
3119 attr_changed = 1;
3120 }
3121
3122 if (make_changes) {
3123 if (fn_changed) {
3124 free(finfo->specials[i]);
3125 finfo->specials[i] = gretl_strdup(newfun);
3126 }
3127 if (attr_changed) {
3128 finfo->gui_attrs[i] = attr;
3129 }
3130 }
3131
3132 if (fn_changed || attr_changed) {
3133 n_changed++;
3134 }
3135
3136 g_free(newfun);
3137 }
3138 }
3139
3140 return n_changed;
3141 }
3142
3143 #define must_be_private(r) (r == UFUN_GUI_PRECHECK)
3144 #define must_be_public(r) (r != UFUN_GUI_PRECHECK && r != UFUN_LIST_MAKER)
3145
3146 /* After adding or deleting functions, check that any
3147 selected "specials" are still valid: the selected
3148 funtion has not been removed from the package, nor
3149 has its public/private status been changed such as
3150 to disqualify it from playing the given role. If a
3151 selection has been invalidated, null it out.
3152 */
3153
verify_selected_specials(function_info * finfo)3154 static void verify_selected_specials (function_info *finfo)
3155 {
3156 const char *seek;
3157 int i, j, found, role;
3158
3159 for (i=0; i<N_SPECIALS; i++) {
3160 role = i + 1;
3161 if (finfo->specials[i] != NULL) {
3162 /* a selection was made */
3163 seek = finfo->specials[i];
3164 found = 0;
3165 if (!must_be_private(role)) {
3166 /* try the public interface list */
3167 for (j=0; j<finfo->n_pub && !found; j++) {
3168 if (!strcmp(seek, finfo->pubnames[j])) {
3169 found = 1;
3170 }
3171 }
3172 }
3173 if (!found && !must_be_public(role)) {
3174 /* try the private interface list */
3175 for (j=0; j<finfo->n_priv && !found; j++) {
3176 if (!strcmp(seek, finfo->privnames[j])) {
3177 found = 1;
3178 }
3179 }
3180 }
3181 if (!found) {
3182 /* gone bad */
3183 free(finfo->specials[i]);
3184 finfo->specials[i] = NULL;
3185 }
3186 }
3187 }
3188 }
3189
data_file_check_existence(function_info * finfo,const char * fname)3190 static int data_file_check_existence (function_info *finfo,
3191 const char *fname)
3192 {
3193 char *p, test[FILENAME_MAX];
3194
3195 strcpy(test, finfo->fname);
3196 p = strrslash(test);
3197 if (p != NULL) {
3198 *p = '\0';
3199 strcat(p, fname);
3200 } else {
3201 strcpy(test, fname);
3202 }
3203
3204 if (!gretl_file_exists(test)) {
3205 gchar *msg;
3206
3207 msg = g_strdup_printf(_("Couldn't find %s"), test);
3208 msgbox(msg, GTK_MESSAGE_WARNING, finfo->extra);
3209 g_free(msg);
3210 return 1;
3211 }
3212
3213 return 0;
3214 }
3215
3216 /* pertaining to the "extra properties" dialog: check for
3217 any changes in relation to included data files
3218 */
3219
process_data_file_names(function_info * finfo,gboolean make_changes)3220 static int process_data_file_names (function_info *finfo,
3221 gboolean make_changes)
3222 {
3223 gchar *fname;
3224 int i, nf = 0;
3225 int changed = 0;
3226
3227 for (i=0; i<N_FILE_ENTRIES; i++) {
3228 fname = entry_box_get_trimmed_text(finfo->file_entries[i]);
3229 if (fname != NULL) {
3230 nf++;
3231 if (i < finfo->n_files &&
3232 strcmp(fname, finfo->datafiles[i])) {
3233 changed = 1;
3234 }
3235 }
3236 g_free(fname);
3237 }
3238
3239 if (!changed && nf != finfo->n_files) {
3240 /* added or deleted */
3241 changed = 1;
3242 }
3243
3244 if (changed && make_changes) {
3245 strings_array_free(finfo->datafiles, finfo->n_files);
3246 if (nf == 0) {
3247 finfo->datafiles = NULL;
3248 finfo->n_files = 0;
3249 } else {
3250 finfo->datafiles = strings_array_new(nf);
3251 if (finfo->datafiles != NULL) {
3252 finfo->n_files = nf;
3253 }
3254 }
3255
3256 if (finfo->datafiles != NULL) {
3257 int j = 0, err = 0;
3258
3259 for (i=0; i<N_FILE_ENTRIES; i++) {
3260 fname = entry_box_get_trimmed_text(finfo->file_entries[i]);
3261 if (fname != NULL) {
3262 if (make_changes && err == 0) {
3263 err = data_file_check_existence(finfo, fname);
3264 }
3265 finfo->datafiles[j++] = gretl_strdup(fname);
3266 }
3267 g_free(fname);
3268 }
3269 }
3270 }
3271
3272 return changed;
3273 }
3274
3275 /* pertaining to the "extra properties" dialog: check for
3276 any changes in relation to dependencies
3277 */
3278
process_dependency_names(function_info * finfo,gboolean make_changes)3279 static int process_dependency_names (function_info *finfo,
3280 gboolean make_changes)
3281 {
3282 gchar *dname;
3283 int i, nd = 0;
3284 int changed = 0;
3285
3286 for (i=0; i<N_DEP_ENTRIES; i++) {
3287 dname = entry_box_get_trimmed_text(finfo->dep_entries[i]);
3288 if (dname != NULL) {
3289 nd++;
3290 if (i < finfo->n_depends &&
3291 strcmp(dname, finfo->depends[i])) {
3292 changed = 1;
3293 }
3294 }
3295 g_free(dname);
3296 }
3297
3298 if (!changed && nd != finfo->n_depends) {
3299 /* added or deleted */
3300 changed = 1;
3301 }
3302
3303 if (changed && make_changes) {
3304 strings_array_free(finfo->depends, finfo->n_depends);
3305 if (nd == 0) {
3306 finfo->depends = NULL;
3307 finfo->n_depends = 0;
3308 } else {
3309 finfo->depends = strings_array_new(nd);
3310 if (finfo->depends != NULL) {
3311 finfo->n_depends = nd;
3312 }
3313 }
3314
3315 if (finfo->depends != NULL) {
3316 int j = 0;
3317
3318 for (i=0; i<N_DEP_ENTRIES; i++) {
3319 dname = entry_box_get_trimmed_text(finfo->dep_entries[i]);
3320 if (dname != NULL) {
3321 finfo->depends[j++] = gretl_strdup(dname);
3322 }
3323 g_free(dname);
3324 }
3325 }
3326 }
3327
3328 return changed;
3329 }
3330
process_provider_name(function_info * finfo,gboolean make_changes)3331 static int process_provider_name (function_info *finfo,
3332 gboolean make_changes)
3333 {
3334 gchar *sname = NULL;
3335 gboolean checked;
3336 int changed = 0;
3337
3338 checked =
3339 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(finfo->prov_check));
3340
3341 if (checked) {
3342 sname = entry_box_get_trimmed_text(finfo->dep_entries[0]);
3343 }
3344
3345 if (sname != NULL && *sname != '\0') {
3346 if (finfo->provider == NULL) {
3347 /* no prior provider */
3348 changed = 1;
3349 } else if (strcmp(sname, finfo->provider)) {
3350 /* different prior provider */
3351 changed = 1;
3352 }
3353 } else if (finfo->provider != NULL) {
3354 /* prior provider was removed */
3355 changed = 1;
3356 }
3357
3358 if (changed && make_changes) {
3359 g_free(finfo->provider);
3360 if (sname != NULL && *sname != '\0') {
3361 finfo->provider = g_strdup(sname);
3362 } else {
3363 finfo->provider = NULL;
3364 }
3365 }
3366
3367 return changed;
3368 }
3369
process_extra_properties(function_info * finfo,gboolean make_changes)3370 static int process_extra_properties (function_info *finfo,
3371 gboolean make_changes)
3372 {
3373 int focus_label = 0;
3374 int changed = 0;
3375
3376 changed += process_special_functions(finfo, make_changes);
3377
3378 changed += process_menu_attachment(finfo, make_changes, &focus_label);
3379 if (focus_label) {
3380 GtkWidget *w = g_object_get_data(G_OBJECT(finfo->extra), "label-entry");
3381
3382 gtk_widget_grab_focus(w);
3383 }
3384
3385 changed += process_data_file_names(finfo, make_changes);
3386 changed += process_dependency_names(finfo, make_changes);
3387 changed += process_provider_name(finfo, make_changes);
3388
3389 if (changed && make_changes) {
3390 finfo_set_modified(finfo, TRUE);
3391 }
3392
3393 return changed;
3394 }
3395
extra_properties_apply(GtkWidget * w,function_info * finfo)3396 static void extra_properties_apply (GtkWidget *w, function_info *finfo)
3397 {
3398 process_extra_properties(finfo, TRUE);
3399 }
3400
extra_properties_close(GtkWidget * w,function_info * finfo)3401 static void extra_properties_close (GtkWidget *w, function_info *finfo)
3402 {
3403 int changed = process_extra_properties(finfo, FALSE);
3404
3405 if (changed) {
3406 int resp = yes_no_cancel_dialog(NULL, _("Apply changes?"),
3407 finfo->extra);
3408
3409 if (resp == GRETL_CANCEL) {
3410 return;
3411 } else if (resp == GRETL_YES) {
3412 process_extra_properties(finfo, TRUE);
3413 }
3414 }
3415
3416 gtk_widget_destroy(finfo->extra);
3417 }
3418
query_save_extra_props(GtkWidget * w,GdkEvent * event,function_info * finfo)3419 static gint query_save_extra_props (GtkWidget *w, GdkEvent *event,
3420 function_info *finfo)
3421 {
3422 int changed = process_extra_properties(finfo, FALSE);
3423
3424 if (changed) {
3425 int resp = yes_no_cancel_dialog(NULL, _("Apply changes?"),
3426 finfo->extra);
3427
3428 if (resp == GRETL_CANCEL) {
3429 return TRUE;
3430 } else if (resp == GRETL_YES) {
3431 process_extra_properties(finfo, TRUE);
3432 }
3433 }
3434
3435 return FALSE;
3436 }
3437
sensitize_attr_toggles(GObject * obj,gboolean s)3438 static void sensitize_attr_toggles (GObject *obj, gboolean s)
3439 {
3440 GtkWidget *cb;
3441
3442 cb = g_object_get_data(obj, "np-toggle");
3443 if (cb != NULL) {
3444 gtk_widget_set_sensitive(cb, s);
3445 }
3446
3447 cb = g_object_get_data(obj, "mo-toggle");
3448 if (cb != NULL) {
3449 gtk_widget_set_sensitive(cb, s);
3450 }
3451 }
3452
3453 /* Prevent the user from assigning a given function to more
3454 then one special role: when a selection is changed, if
3455 the given function is already selected for a different
3456 role, deselect it in that role.
3457 */
3458
special_changed_callback(GtkComboBox * this,function_info * finfo)3459 static void special_changed_callback (GtkComboBox *this,
3460 function_info *finfo)
3461 {
3462 GtkWidget **c_array;
3463 GtkWidget *other;
3464 gchar *s0, *si;
3465 int i, dup = 0;
3466
3467 if (gtk_combo_box_get_active(this) == 0) {
3468 /* selected "none" */
3469 sensitize_attr_toggles(G_OBJECT(this), FALSE);
3470 return;
3471 }
3472
3473 sensitize_attr_toggles(G_OBJECT(this), TRUE);
3474 s0 = combo_box_get_active_text(this);
3475 c_array = g_object_get_data(G_OBJECT(finfo->extra), "combo-array");
3476
3477 for (i=0; i<N_SPECIALS && !dup; i++) {
3478 other = c_array[i];
3479 if (other != GTK_WIDGET(this)) {
3480 si = combo_box_get_active_text(GTK_COMBO_BOX(other));
3481 if (!strcmp(si, s0)) {
3482 /* switch to "none" */
3483 gtk_combo_box_set_active(GTK_COMBO_BOX(other), 0);
3484 sensitize_attr_toggles(G_OBJECT(other), FALSE);
3485 dup = 1;
3486 }
3487 g_free(si);
3488 }
3489 }
3490
3491 g_free(s0);
3492 }
3493
finfo_extra_help(GtkWidget * w,function_info * finfo)3494 static void finfo_extra_help (GtkWidget *w, function_info *finfo)
3495 {
3496 GtkWidget *notebook;
3497 gint page;
3498
3499 notebook = g_object_get_data(G_OBJECT(finfo->extra), "book");
3500 page = gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook));
3501
3502 if (page == 0) {
3503 show_gui_help(GUI_FUNCS);
3504 } else if (page == 1) {
3505 show_gui_help(MENU_ATTACH);
3506 } else if (page == 2) {
3507 show_gui_help(PKG_FILES);
3508 } else {
3509 show_gui_help(PKG_DEPS);
3510 }
3511 }
3512
unref_trees(GtkWidget * w,function_info * finfo)3513 static void unref_trees (GtkWidget *w, function_info *finfo)
3514 {
3515 g_object_unref(G_OBJECT(finfo->maintree));
3516 g_object_unref(G_OBJECT(finfo->modeltree));
3517 }
3518
3519 /* The following function supports three "notebook" tabs. The first
3520 allows the user to select functions in the package for the various
3521 "special" package roles (e.g. gui-main, bundle-print). The second
3522 allows for selection of a menu attachment point and GUI label. The
3523 third allows for specification of additional data to be included in
3524 the package.
3525 */
3526
extra_properties_dialog(GtkWidget * w,function_info * finfo)3527 static void extra_properties_dialog (GtkWidget *w, function_info *finfo)
3528 {
3529 GtkWidget *dlg, *combo, *table;
3530 GtkWidget *tmp, *vbox, *hbox;
3531 GtkWidget **combo_array;
3532 GtkWidget *notebook;
3533 const char *key;
3534 const char *special;
3535 int tabcols = 4;
3536 int nfuns, i, j;
3537
3538 if (finfo->extra != NULL) {
3539 gtk_window_present(GTK_WINDOW(finfo->extra));
3540 return;
3541 }
3542
3543 if (finfo->pkg == NULL) {
3544 warnbox(_("Please save your package first"));
3545 return;
3546 }
3547
3548 finfo->maintree = finfo->modeltree = NULL;
3549 finfo->currtree = finfo->alttree = NULL;
3550 finfo->treewin = NULL;
3551 finfo->mreq_combo = NULL;
3552 finfo->data_button = NULL;
3553
3554 dlg = gretl_dialog_new(_("gretl: extra properties"), finfo->dlg,
3555 GRETL_DLG_BLOCK | GRETL_DLG_RESIZE);
3556 finfo->extra = dlg;
3557 g_signal_connect(G_OBJECT(dlg), "delete-event",
3558 G_CALLBACK(query_save_extra_props), finfo);
3559 g_signal_connect(G_OBJECT(dlg), "destroy",
3560 G_CALLBACK(gtk_widget_destroyed), &finfo->extra);
3561 g_signal_connect(G_OBJECT(dlg), "destroy",
3562 G_CALLBACK(unref_trees), finfo);
3563 vbox = gtk_dialog_get_content_area(GTK_DIALOG(dlg));
3564
3565 notebook = gtk_notebook_new();
3566 gtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 0);
3567 g_object_set_data(G_OBJECT(dlg), "book", notebook);
3568
3569 vbox = gtk_vbox_new(FALSE, 0);
3570 gtk_container_set_border_width(GTK_CONTAINER(vbox), 5);
3571 tmp = gtk_label_new(_("Special functions"));
3572 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox, tmp);
3573
3574 table = gtk_table_new(N_SPECIALS, tabcols, FALSE);
3575 gtk_table_set_row_spacings(GTK_TABLE(table), 5);
3576 gtk_table_set_col_spacings(GTK_TABLE(table), 5);
3577
3578 nfuns = finfo->n_priv + finfo->n_pub;
3579 combo_array = g_malloc(N_SPECIALS * sizeof *combo_array);
3580 g_object_set_data_full(G_OBJECT(dlg), "combo-array",
3581 combo_array, g_free);
3582
3583 /* For each "special" function role, test the functions
3584 in finfo->pkg to see if they qualify as candidates for
3585 that role; if so, add them to the combo selector.
3586 */
3587
3588 for (i=0; i<N_SPECIALS; i++) {
3589 const char *funname = NULL;
3590 int n_cands = 0;
3591 int selected = 0;
3592 int role = i + 1;
3593
3594 key = package_role_get_key(role);
3595 special = finfo->specials[i];
3596 combo = gtk_combo_box_text_new();
3597 combo_box_append_text(combo, "none");
3598
3599 for (j=0; j<nfuns; j++) {
3600 if (j < finfo->n_priv && !must_be_public(role)) {
3601 funname = finfo->privnames[j];
3602 } else if (j >= finfo->n_priv && !must_be_private(role)) {
3603 funname = finfo->pubnames[j - finfo->n_priv];
3604 } else {
3605 continue;
3606 }
3607 if (function_ok_for_package_role(funname, role)) {
3608 combo_box_append_text(combo, funname);
3609 if (special != NULL && !selected && !strcmp(special, funname)) {
3610 selected = n_cands + 1;
3611 }
3612 n_cands++;
3613 }
3614 }
3615
3616 tmp = gtk_label_new(key);
3617 gtk_misc_set_alignment(GTK_MISC(tmp), 1, 0.5);
3618 gtk_table_attach_defaults(GTK_TABLE(table), tmp, 0, 1, i, i+1);
3619 gtk_table_attach_defaults(GTK_TABLE(table), combo, 1, 2, i, i+1);
3620 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), selected);
3621 if (n_cands == 0) {
3622 gtk_widget_set_sensitive(combo, FALSE);
3623 } else {
3624 g_signal_connect(G_OBJECT(combo), "changed",
3625 G_CALLBACK(special_changed_callback),
3626 finfo);
3627 }
3628 combo_array[i] = combo;
3629
3630 if (want_no_print_toggle(role)) {
3631 GtkWidget *cb = gtk_check_button_new_with_label("no-print");
3632
3633 gtk_table_attach_defaults(GTK_TABLE(table), cb, 2, 3, i, i+1);
3634 g_object_set_data(G_OBJECT(combo), "np-toggle", cb);
3635 gtk_widget_set_sensitive(cb, selected > 0);
3636 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cb),
3637 finfo->gui_attrs[i] & UFUN_NOPRINT);
3638 }
3639
3640 if (role == UFUN_GUI_MAIN) {
3641 GtkWidget *cb = gtk_check_button_new_with_label("menu-only");
3642
3643 gtk_table_attach_defaults(GTK_TABLE(table), cb, 3, 4, i, i+1);
3644 g_object_set_data(G_OBJECT(combo), "mo-toggle", cb);
3645 gtk_widget_set_sensitive(cb, selected > 0);
3646 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cb),
3647 finfo->gui_attrs[i] & UFUN_MENU_ONLY);
3648 }
3649 }
3650
3651 hbox = gtk_hbox_new(FALSE, 5);
3652 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
3653 gtk_box_pack_start(GTK_BOX(hbox), table, FALSE, FALSE, 5);
3654
3655 /* the menu attachment page */
3656
3657 vbox = gtk_vbox_new(FALSE, 0);
3658 gtk_container_set_border_width(GTK_CONTAINER(vbox), 5);
3659 tmp = gtk_label_new(_("Menu attachment"));
3660 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox, tmp);
3661
3662 add_menu_attach_top(vbox, finfo);
3663 finfo->maintree = add_menu_navigator(vbox, finfo, 0);
3664 finfo->modeltree = add_menu_navigator(vbox, finfo, 1);
3665
3666 finfo->mreq_combo = model_requirement_selector(vbox, finfo);
3667 finfo->data_button = access_request_button(vbox, finfo);
3668
3669 gtk_widget_set_sensitive(finfo->currtree, finfo->menuwin != NO_WINDOW);
3670
3671 /* the data files page */
3672
3673 vbox = gtk_vbox_new(FALSE, 0);
3674 gtk_container_set_border_width(GTK_CONTAINER(vbox), 5);
3675 tmp = gtk_label_new(_("Data files"));
3676 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox, tmp);
3677 add_data_files_entries(vbox, finfo);
3678
3679 /* the dependencies page */
3680
3681 vbox = gtk_vbox_new(FALSE, 0);
3682 gtk_container_set_border_width(GTK_CONTAINER(vbox), 5);
3683 tmp = gtk_label_new(_("Dependencies"));
3684 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox, tmp);
3685 add_dependency_entries(vbox, finfo);
3686
3687 /* the common buttons area */
3688
3689 hbox = gtk_dialog_get_action_area(GTK_DIALOG(dlg));
3690
3691 /* Apply button */
3692 tmp = apply_button(hbox);
3693 g_signal_connect(G_OBJECT(tmp), "clicked",
3694 G_CALLBACK(extra_properties_apply), finfo);
3695 gtk_widget_grab_default(tmp);
3696
3697 /* Close button */
3698 tmp = close_button(hbox);
3699 g_signal_connect(G_OBJECT(tmp), "clicked",
3700 G_CALLBACK(extra_properties_close), finfo);
3701
3702 /* Help button */
3703 tmp = gtk_button_new_from_stock(GTK_STOCK_HELP);
3704 gtk_container_add(GTK_CONTAINER(hbox), tmp);
3705 gtk_button_box_set_child_secondary(GTK_BUTTON_BOX(hbox),
3706 tmp, TRUE);
3707 g_signal_connect(G_OBJECT(tmp), "clicked",
3708 G_CALLBACK(finfo_extra_help),
3709 finfo);
3710
3711 gtk_widget_show_all(dlg);
3712 }
3713
package_editor_exit_check(GtkWidget * w)3714 int package_editor_exit_check (GtkWidget *w)
3715 {
3716 function_info *finfo;
3717
3718 finfo = g_object_get_data(G_OBJECT(w), "finfo");
3719
3720 if (finfo != NULL && finfo->modified) {
3721 gtk_window_present(GTK_WINDOW(w));
3722 return query_save_package(w, NULL, finfo);
3723 }
3724
3725 return FALSE;
3726 }
3727
3728 /* return non-zero if @w is the window of an editor working
3729 on @pkgname
3730 */
3731
query_package_editor(GtkWidget * w,const char * pkgname)3732 int query_package_editor (GtkWidget *w, const char *pkgname)
3733 {
3734 function_info *finfo;
3735
3736 finfo = g_object_get_data(G_OBJECT(w), "finfo");
3737
3738 if (finfo != NULL && finfo->pkg != NULL) {
3739 const char *myname = function_package_get_name(finfo->pkg);
3740
3741 return strcmp(pkgname, myname) == 0;
3742 }
3743
3744 return FALSE;
3745 }
3746
package_editor_get_pkg(GtkWidget * w)3747 void *package_editor_get_pkg (GtkWidget *w)
3748 {
3749 function_info *finfo;
3750
3751 finfo = g_object_get_data(G_OBJECT(w), "finfo");
3752
3753 if (finfo != NULL && finfo->pkg != NULL) {
3754 return (void *) finfo->pkg;
3755 }
3756
3757 return NULL;
3758 }
3759
delete_dlg_callback(GtkWidget * button,function_info * finfo)3760 static void delete_dlg_callback (GtkWidget *button, function_info *finfo)
3761 {
3762 gint resp = 0;
3763
3764 if (finfo->modified) {
3765 resp = query_save_package(finfo->dlg, NULL, finfo);
3766 }
3767
3768 if (!resp) {
3769 gtk_widget_destroy(finfo->dlg);
3770 }
3771 }
3772
3773 /* Dialog for editing a function package. The user can get here
3774 in either of two ways: after selecting functions to put into a
3775 newly created package, or upon selecting an existing package
3776 for editing.
3777 */
3778
finfo_dialog(function_info * finfo)3779 static void finfo_dialog (function_info *finfo)
3780 {
3781 GtkWidget *button, *label;
3782 GtkWidget *tbl, *vbox, *hbox;
3783 const char *entry_labels[] = {
3784 N_("Author"),
3785 N_("Email"),
3786 N_("Version"),
3787 N_("Date (YYYY-MM-DD)"),
3788 N_("Package description")
3789 };
3790 char *entry_texts[] = {
3791 finfo->author,
3792 finfo->email,
3793 finfo->version,
3794 finfo->date,
3795 finfo->pkgdesc
3796 };
3797 gchar *tmp, *title;
3798 int focused = 0;
3799 int rows = N_ENTRIES + 2;
3800 int i;
3801
3802 finfo->dlg = gretl_gtk_window();
3803 gtk_window_set_default_size(GTK_WINDOW(finfo->dlg), 600, -1);
3804
3805 title = g_strdup_printf("gretl: %s", finfo_pkgname(finfo));
3806 gtk_window_set_title(GTK_WINDOW(finfo->dlg), title);
3807 g_free(title);
3808
3809 if (finfo->pkg != NULL) {
3810 function_package_set_editor(finfo->pkg, finfo->dlg);
3811 }
3812
3813 g_object_set_data(G_OBJECT(finfo->dlg), "finfo", finfo);
3814 gtk_widget_set_name(finfo->dlg, "pkg-editor");
3815 g_signal_connect(G_OBJECT(finfo->dlg), "delete-event",
3816 G_CALLBACK(query_save_package), finfo);
3817 g_signal_connect(G_OBJECT(finfo->dlg), "destroy",
3818 G_CALLBACK(finfo_destroy), finfo);
3819
3820 vbox = gtk_vbox_new(FALSE, 5);
3821 gtk_container_set_border_width(GTK_CONTAINER(vbox), 5);
3822 gtk_container_add(GTK_CONTAINER(finfo->dlg), vbox);
3823
3824 tbl = gtk_table_new(rows, 2, FALSE);
3825 gtk_table_set_col_spacings(GTK_TABLE(tbl), 5);
3826 gtk_table_set_row_spacings(GTK_TABLE(tbl), 4);
3827 gtk_box_pack_start(GTK_BOX(vbox), tbl, FALSE, FALSE, 5);
3828
3829 for (i=0; i<N_ENTRIES; i++) {
3830 GtkWidget *entry;
3831
3832 label = gtk_label_new(_(entry_labels[i]));
3833 gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
3834 gtk_table_attach_defaults(GTK_TABLE(tbl), label, 0, 1, i, i+1);
3835
3836 entry = gtk_entry_new();
3837 gtk_entry_set_width_chars(GTK_ENTRY(entry), 40);
3838 gtk_table_attach_defaults(GTK_TABLE(tbl), entry, 1, 2, i, i+1);
3839
3840 finfo->entries[i] = entry;
3841
3842 if (entry_texts[i] != NULL) {
3843 gtk_entry_set_text(GTK_ENTRY(entry), entry_texts[i]);
3844 if (i == 3) {
3845 g_signal_connect(G_OBJECT(entry), "button-press-event",
3846 G_CALLBACK(today_popup), &finfo->popup);
3847 }
3848 } else if (i == 0) {
3849 /* author */
3850 const gchar *s = get_user_string();
3851
3852 if (s != NULL) {
3853 gtk_entry_set_text(GTK_ENTRY(entry), s);
3854 }
3855 } else if (i == 1) {
3856 /* email */
3857 gtk_entry_set_text(GTK_ENTRY(entry), get_author_mail());
3858 } else if (i == 2) {
3859 /* version */
3860 gtk_entry_set_text(GTK_ENTRY(entry), "1.0");
3861 } else if (i == 3) {
3862 /* date */
3863 gtk_entry_set_text(GTK_ENTRY(entry), print_today());
3864 }
3865
3866 if (i == 0 && entry_texts[i] == NULL) {
3867 /* no author's name */
3868 gtk_widget_grab_focus(entry);
3869 focused = 1;
3870 } else if (i == 1 && !focused &&
3871 (entry_texts[i] == NULL || *entry_texts[i] == '\0')) {
3872 /* no email address */
3873 gtk_widget_grab_focus(entry);
3874 focused = 1;
3875 } else if (i == 2 && !focused) {
3876 /* version number */
3877 gtk_widget_grab_focus(entry);
3878 }
3879
3880 g_signal_connect(GTK_EDITABLE(entry), "changed",
3881 G_CALLBACK(pkg_changed), finfo);
3882 }
3883
3884 add_tag_selectors(tbl, i, finfo);
3885 i += 2;
3886 add_data_requirement_menu(tbl, i, finfo);
3887
3888 /* table for min version and help doc controls */
3889 hbox = gtk_hbox_new(FALSE, 0);
3890 tbl = gtk_table_new(3, 2, FALSE);
3891 gtk_table_set_row_spacings(GTK_TABLE(tbl), 4);
3892 gtk_table_set_col_spacings(GTK_TABLE(tbl), 64);
3893 gtk_box_pack_start(GTK_BOX(hbox), tbl, TRUE, FALSE, 0);
3894 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 8);
3895
3896 add_minver_selector(tbl, 0, finfo);
3897 add_help_radios(tbl, 1, finfo);
3898
3899 /* table for buttons arrayed at foot of window */
3900 hbox = gtk_hbox_new(FALSE, 0);
3901 tbl = gtk_table_new(2, 4, FALSE);
3902 gtk_table_set_row_spacings(GTK_TABLE(tbl), 4);
3903 gtk_table_set_col_spacings(GTK_TABLE(tbl), 4);
3904 gtk_table_set_col_spacing(GTK_TABLE(tbl), 2, 32);
3905 gtk_box_pack_start(GTK_BOX(hbox), tbl, TRUE, FALSE, 0);
3906 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 2);
3907
3908 /* first button row */
3909
3910 /* 1: edit code button */
3911 button = gtk_button_new_with_label(_("Edit function code"));
3912 gtk_table_attach_defaults(GTK_TABLE(tbl), button, 0, 1, 0, 1);
3913 g_signal_connect(G_OBJECT(button), "clicked",
3914 G_CALLBACK(edit_code_callback), finfo);
3915
3916 /* 2: interface selector */
3917 finfo->codesel = active_func_selector(finfo);
3918 gtk_table_attach_defaults(GTK_TABLE(tbl), finfo->codesel, 1, 2, 0, 1);
3919 g_signal_connect(G_OBJECT(finfo->codesel), "changed",
3920 G_CALLBACK(update_active_func), finfo);
3921
3922 update_active_func(NULL, finfo);
3923
3924 /* 3: extra package properties button */
3925 button = gtk_button_new_with_label(_("Extra properties"));
3926 gtk_table_attach_defaults(GTK_TABLE(tbl), button, 2, 3, 0, 1);
3927 g_signal_connect(G_OBJECT(button), "clicked",
3928 G_CALLBACK(extra_properties_dialog), finfo);
3929
3930 /* 4: save-menu button */
3931 tmp = g_strdup_printf(" %s ", _("Save..."));
3932 button = gtk_button_new_with_label(tmp);
3933 g_free(tmp);
3934 gtk_table_attach_defaults(GTK_TABLE(tbl), button, 3, 4, 0, 1);
3935 g_signal_connect(G_OBJECT(button), "clicked",
3936 G_CALLBACK(pkg_save_popup), finfo);
3937
3938 /* second button row */
3939
3940 /* 1: edit sample script button */
3941 button = gtk_button_new_with_label(_("Edit sample script"));
3942 gtk_table_attach_defaults(GTK_TABLE(tbl), button, 0, 1, 1, 2);
3943 g_signal_connect(G_OBJECT(button), "clicked",
3944 G_CALLBACK(edit_sample_callback), finfo);
3945
3946 /* 2: add/remove functions button */
3947 button = gtk_button_new_with_label(_("Add/remove functions"));
3948 gtk_table_attach_defaults(GTK_TABLE(tbl), button, 1, 2, 1, 2);
3949 g_signal_connect(G_OBJECT(button), "clicked",
3950 G_CALLBACK(add_remove_callback), finfo);
3951
3952 /* 3: validate button */
3953 button = gtk_button_new_with_label(_("Validate"));
3954 gtk_table_attach_defaults(GTK_TABLE(tbl), button, 2, 3, 1, 2);
3955 g_signal_connect(G_OBJECT(button), "clicked",
3956 G_CALLBACK(check_pkg_callback), finfo);
3957 gtk_widget_set_sensitive(button, finfo->fname != NULL);
3958 finfo->validate = button;
3959
3960 /* 4: close button */
3961 button = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
3962 gtk_table_attach_defaults(GTK_TABLE(tbl), button, 3, 4, 1, 2);
3963 g_signal_connect(G_OBJECT(button), "clicked",
3964 G_CALLBACK(delete_dlg_callback), finfo);
3965
3966 finfo_set_modified(finfo, finfo->fname == NULL);
3967
3968 window_list_add(finfo->dlg, SAVE_FUNCTIONS);
3969 gtk_widget_show_all(finfo->dlg);
3970 }
3971
web_get_login(GtkWidget * w,gpointer p)3972 static void web_get_login (GtkWidget *w, gpointer p)
3973 {
3974 browser_open("http://gretl.ecn.wfu.edu/cgi-bin/apply/");
3975 }
3976
login_dialog(login_info * linfo,GtkWidget * parent)3977 static void login_dialog (login_info *linfo, GtkWidget *parent)
3978 {
3979 const gchar *msg = N_("Upload package: This means that the package will\n"
3980 "be uploaded to the gretl server for approval.\n"
3981 "You should do this only if you are the author of\n"
3982 "this package and either the package is not already\n"
3983 "on the server or you have made changes since the\n"
3984 "last upload.");
3985 GtkWidget *button, *label;
3986 GtkWidget *tbl, *vbox, *hbox;
3987 int i;
3988
3989 login_init(linfo);
3990
3991 linfo->dlg = gretl_dialog_new(_("gretl: upload"), parent, GRETL_DLG_BLOCK);
3992 vbox = gtk_dialog_get_content_area(GTK_DIALOG(linfo->dlg));
3993
3994 label_hbox(vbox, _(msg));
3995
3996 hbox = gtk_hbox_new(FALSE, 5);
3997 tbl = gtk_table_new(2, 2, FALSE);
3998 gtk_box_pack_start(GTK_BOX(hbox), tbl, FALSE, FALSE, 5);
3999 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 10);
4000
4001 for (i=0; i<2; i++) {
4002 char *src = (i == 0)? linfo->login : linfo->pass;
4003 GtkWidget *entry;
4004
4005 label = gtk_label_new((i == 0)? _("Login") : _("Password"));
4006 gtk_table_attach(GTK_TABLE(tbl), label, 0, 1, i, i+1,
4007 GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL,
4008 5, 5);
4009
4010 entry = gtk_entry_new();
4011 gtk_entry_set_width_chars(GTK_ENTRY(entry), 34);
4012 gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
4013 gtk_table_attach_defaults(GTK_TABLE(tbl), entry, 1, 2, i, i+1);
4014 if (src != NULL) {
4015 gtk_entry_set_text(GTK_ENTRY(entry), src);
4016 }
4017
4018 if (i == 0) {
4019 linfo->login_entry = entry;
4020 } else {
4021 gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE);
4022 linfo->pass_entry = entry;
4023 }
4024 }
4025
4026 label_hbox(vbox,
4027 _("If you don't have a login to the gretl server\n"
4028 "please see http://gretl.ecn.wfu.edu/cgi-bin/apply/.\n"
4029 "The 'Website' button below should open this page\n"
4030 "in your web browser."));
4031
4032 /* control button area */
4033 hbox = gtk_dialog_get_action_area(GTK_DIALOG(linfo->dlg));
4034
4035 /* Cancel */
4036 button = cancel_button(hbox);
4037 g_signal_connect(G_OBJECT(button), "clicked",
4038 G_CALLBACK(delete_widget), linfo->dlg);
4039
4040 /* OK */
4041 button = ok_validate_button(hbox, &linfo->canceled, NULL);
4042 g_signal_connect(G_OBJECT(button), "clicked",
4043 G_CALLBACK(login_finalize), linfo);
4044 gtk_widget_grab_default(button);
4045
4046 /* Website */
4047 button = gtk_button_new_with_label("Website");
4048 gtk_widget_set_can_default(button, TRUE);
4049 gtk_container_add(GTK_CONTAINER(hbox), button);
4050 gtk_button_box_set_child_secondary(GTK_BUTTON_BOX(hbox),
4051 button, TRUE);
4052 g_signal_connect(G_OBJECT(button), "clicked",
4053 G_CALLBACK(web_get_login), NULL);
4054
4055 gtk_widget_show_all(linfo->dlg);
4056 }
4057
4058 /* Check the package file against gretlfunc.dtd. We call
4059 this automatically before uploading a package file to
4060 the server. The user can also choose to run the test by
4061 clicking the "Validate" button in the package editor;
4062 in that case we set the @verbose flag.
4063 */
4064
validate_package_file(const char * fname,int verbose)4065 static int validate_package_file (const char *fname, int verbose)
4066 {
4067 const char *gretldir = gretl_home();
4068 char dtdname[FILENAME_MAX];
4069 xmlDocPtr doc;
4070 xmlDtdPtr dtd;
4071 int err = 0;
4072
4073 err = gretl_xml_open_doc_root(fname, NULL, &doc, NULL);
4074 if (err) {
4075 gui_errmsg(err);
4076 return 1;
4077 }
4078
4079 sprintf(dtdname, "%sfunctions%cgretlfunc.dtd", gretldir, SLASH);
4080 dtd = xmlParseDTD(NULL, (const xmlChar *) dtdname);
4081
4082 if (dtd == NULL) {
4083 if (verbose) {
4084 errbox("Couldn't open DTD to check package");
4085 } else {
4086 fprintf(stderr, "Couldn't open DTD to check package\n");
4087 }
4088 } else {
4089 const char *pkgname = path_last_element(fname);
4090 xmlValidCtxtPtr cvp = xmlNewValidCtxt();
4091 PRN *prn = NULL;
4092 int xerr = 0;
4093
4094 if (cvp == NULL) {
4095 xerr = 1;
4096 if (verbose) nomem();
4097 } else {
4098 xerr = bufopen(&prn);
4099 }
4100
4101 if (xerr) {
4102 xmlFreeDtd(dtd);
4103 xmlFreeDoc(doc);
4104 return 0;
4105 }
4106
4107 cvp->userData = (void *) prn;
4108 cvp->error = (xmlValidityErrorFunc) pprintf2;
4109 cvp->warning = (xmlValidityWarningFunc) pprintf2;
4110
4111 if (!xmlValidateDtd(cvp, doc, dtd)) {
4112 const char *buf = gretl_print_get_buffer(prn);
4113
4114 errbox(buf);
4115 err = 1;
4116 } else if (verbose) {
4117 infobox_printf(_("%s: validated against DTD OK"), pkgname);
4118 } else {
4119 fprintf(stderr, "%s: validated against DTD OK\n", pkgname);
4120 }
4121
4122 gretl_print_destroy(prn);
4123 xmlFreeValidCtxt(cvp);
4124 xmlFreeDtd(dtd);
4125 }
4126
4127 xmlFreeDoc(doc);
4128
4129 return err;
4130 }
4131
4132 /* Collect pkg.gfn plus additional package files (PDF doc and/or data
4133 files) into a temporary dir under the user's dotdir, and make a zip
4134 archive. If @dest is non-NULL, that's the name of the zipfile to
4135 build; otherwise if @pzipname is non-NULL the zipfile will be named
4136 automatically based on the package name, and this name will be
4137 "returned" in @pzipname.
4138 */
4139
gui_pkg_make_zipfile(function_info * finfo,gchar ** pzipname,const char * dest)4140 static int gui_pkg_make_zipfile (function_info *finfo,
4141 gchar **pzipname,
4142 const char *dest)
4143 {
4144 windata_t *vwin;
4145 PRN *prn = NULL;
4146 int err = 0;
4147
4148 if (pzipname == NULL && dest == NULL) {
4149 /* we need one or the other */
4150 return E_DATA;
4151 }
4152
4153 if (finfo->pdfname != NULL) {
4154 err = maybe_copy_pdf_file(finfo);
4155 if (err) {
4156 return err;
4157 }
4158 }
4159
4160 /* open printer for recording */
4161 bufopen(&prn);
4162
4163 err = package_make_zipfile(finfo->fname,
4164 finfo->pdfdoc,
4165 finfo->datafiles,
4166 finfo->n_files,
4167 pzipname, dest,
4168 OPT_G, prn);
4169
4170 /* show details of operation */
4171 vwin = view_buffer(prn, 78, 300, _("build zip file"), BUILD_PKG, NULL);
4172 gtk_window_set_transient_for(GTK_WINDOW(vwin->main),
4173 GTK_WINDOW(finfo->dlg));
4174 gtk_window_set_destroy_with_parent(GTK_WINDOW(vwin->main), TRUE);
4175
4176 return err;
4177 }
4178
do_pkg_upload(function_info * finfo)4179 static void do_pkg_upload (function_info *finfo)
4180 {
4181 const char *fname;
4182 gchar *buf = NULL;
4183 char *retbuf = NULL;
4184 gchar *zipname = NULL;
4185 GdkWindow *cwin = NULL;
4186 login_info linfo;
4187 gsize buflen;
4188 int error_printed = 0;
4189 int err;
4190
4191 err = validate_package_file(finfo->fname, 0);
4192 if (err) {
4193 return;
4194 }
4195
4196 if (finfo->pdfdoc || finfo->datafiles != NULL) {
4197 err = gui_pkg_make_zipfile(finfo, &zipname, NULL);
4198 if (err) {
4199 /* the error message will have been handled above */
4200 return;
4201 }
4202 }
4203
4204 fname = zipname != NULL ? zipname : finfo->fname;
4205
4206 login_dialog(&linfo, finfo->dlg);
4207
4208 if (linfo.canceled) {
4209 linfo_free(&linfo);
4210 g_free(zipname);
4211 return;
4212 }
4213
4214 /* call for the "watch" cursor */
4215 set_wait_cursor(&cwin);
4216
4217 err = gretl_file_get_contents(fname, &buf, &buflen);
4218
4219 if (err) {
4220 error_printed = 1;
4221 } else {
4222 err = upload_function_package(linfo.login, linfo.pass,
4223 path_last_element(fname),
4224 buf, buflen, &retbuf);
4225 fprintf(stderr, "upload_function_package: err = %d\n", err);
4226 }
4227
4228 /* restore default cursor */
4229 unset_wait_cursor(cwin);
4230
4231 if (err) {
4232 if (!error_printed) {
4233 gui_errmsg(err);
4234 }
4235 } else if (retbuf != NULL && *retbuf != '\0') {
4236 infobox(retbuf);
4237 }
4238
4239 if (zipname != NULL) {
4240 /* delete the upload zipfile */
4241 gretl_remove(zipname);
4242 g_free(zipname);
4243 }
4244
4245 g_free(buf);
4246 free(retbuf);
4247
4248 linfo_free(&linfo);
4249 }
4250
upload_precheck_gfn(const char * fname,gchar ** zname)4251 static int upload_precheck_gfn (const char *fname,
4252 gchar **zname)
4253 {
4254 char **datafiles = NULL;
4255 int pdfdoc = 0;
4256 int n_files = 0;
4257 int err;
4258
4259 err = validate_package_file(fname, 0);
4260 if (err) {
4261 return err;
4262 }
4263
4264 if (package_needs_zipping(fname, &pdfdoc, &datafiles, &n_files)) {
4265 int resp;
4266
4267 resp = yes_no_dialog(NULL,
4268 _("This package must be uploaded as a zip file.\n"
4269 "Try to create the zip file now?"),
4270 NULL);
4271 if (resp == GRETL_YES) {
4272 PRN *prn = NULL;
4273
4274 if (bufopen(&prn)) {
4275 err = 1;
4276 } else {
4277 err = package_make_zipfile(fname, pdfdoc,
4278 datafiles, n_files,
4279 zname, NULL,
4280 OPT_G, prn);
4281 /* show result on error */
4282 if (err) {
4283 view_buffer(prn, 78, 300, _("build zip file"),
4284 BUILD_PKG, NULL);
4285 } else {
4286 gretl_print_destroy(prn);
4287 }
4288 }
4289 } else {
4290 /* not really an error, but canceled */
4291 err = 1;
4292 }
4293
4294 strings_array_free(datafiles, n_files);
4295 }
4296
4297 return err;
4298 }
4299
4300 /* callback from file selector, so @fname will be a full path */
4301
upload_specified_package(const char * fname)4302 void upload_specified_package (const char *fname)
4303 {
4304 const char *realname;
4305 gchar *zname = NULL;
4306 gchar *buf = NULL;
4307 char *retbuf = NULL;
4308 login_info linfo;
4309 GdkDisplay *disp;
4310 GdkCursor *cursor;
4311 GdkWindow *w1;
4312 gint x, y;
4313 gsize buflen;
4314 int error_printed = 0;
4315 int err;
4316
4317 if (has_suffix(fname, ".gfn")) {
4318 err = upload_precheck_gfn(fname, &zname);
4319 if (err) {
4320 return;
4321 }
4322 }
4323
4324 login_dialog(&linfo, mdata->main);
4325
4326 if (linfo.canceled) {
4327 if (zname != NULL) {
4328 gretl_remove(zname);
4329 g_free(zname);
4330 }
4331 linfo_free(&linfo);
4332 return;
4333 }
4334
4335 realname = zname != NULL ? zname : fname;
4336
4337 /* set waiting cursor */
4338 disp = gdk_display_get_default();
4339 w1 = gdk_display_get_window_at_pointer(disp, &x, &y);
4340 if (w1 != NULL) {
4341 cursor = gdk_cursor_new(GDK_WATCH);
4342 if (cursor != NULL) {
4343 gdk_window_set_cursor(w1, cursor);
4344 gdk_display_sync(disp);
4345 gdk_cursor_unref(cursor);
4346 }
4347 }
4348
4349 err = gretl_file_get_contents(realname, &buf, &buflen);
4350
4351 if (err) {
4352 error_printed = 1;
4353 } else {
4354 err = upload_function_package(linfo.login, linfo.pass,
4355 path_last_element(realname),
4356 buf, buflen, &retbuf);
4357 fprintf(stderr, "upload_function_package: err = %d\n", err);
4358 }
4359
4360 /* reset default cursor */
4361 if (w1 != NULL) {
4362 gdk_window_set_cursor(w1, NULL);
4363 }
4364
4365 if (err) {
4366 if (!error_printed) {
4367 gui_errmsg(err);
4368 }
4369 } else if (retbuf != NULL && *retbuf != '\0') {
4370 infobox(retbuf);
4371 }
4372
4373 if (zname != NULL) {
4374 /* we created an on-the-fly temporary zipfile */
4375 gretl_remove(zname);
4376 g_free(zname);
4377 }
4378
4379 g_free(buf);
4380 free(retbuf);
4381
4382 linfo_free(&linfo);
4383 }
4384
4385 /* the basename of a function package file must meet some sanity
4386 requirements */
4387
check_package_filename(const char * fname,int fullpath,GtkWidget * parent)4388 static int check_package_filename (const char *fname,
4389 int fullpath,
4390 GtkWidget *parent)
4391 {
4392 const char *p = fname;
4393 int n, err = 0;
4394
4395 if (fullpath) {
4396 p = path_last_slash_const(fname);
4397 if (p == NULL) {
4398 p = fname;
4399 } else {
4400 p++;
4401 }
4402 }
4403
4404 if (fullpath && !has_suffix(p, ".gfn")) {
4405 /* must have the right suffix */
4406 err = 1;
4407 } else {
4408 n = strlen(p) - (fullpath ? 4 : 0);
4409 if (n >= FN_NAMELEN) {
4410 /* too long */
4411 err = 1;
4412 } else if (gretl_namechar_spn(p) != n) {
4413 /* contains funny stuff */
4414 err = 1;
4415 }
4416 }
4417
4418 if (err) {
4419 if (fullpath) {
4420 msgbox(_("Invalid package filename: the name must start with a letter,\n"
4421 "must be less than 32 characters in length, must include only\n"
4422 "ASCII letters, numbers and '_', and must end with \".gfn\"."),
4423 GTK_MESSAGE_ERROR, parent);
4424 } else {
4425 msgbox(_("Invalid package name: the name must start with a letter,\n"
4426 "must be less than 32 characters in length, and must include\n"
4427 "only ASCII letters, numbers and '_'."),
4428 GTK_MESSAGE_ERROR, parent);
4429 }
4430 }
4431
4432 return err;
4433 }
4434
pkg_save_special_functions(function_info * finfo)4435 static int pkg_save_special_functions (function_info *finfo)
4436 {
4437 const char *key;
4438 int i, role, err = 0;
4439
4440 for (i=0; i<N_SPECIALS && !err; i++) {
4441 role = i + 1;
4442 key = package_role_get_key(role);
4443 err = function_set_package_role(finfo->specials[i],
4444 finfo->pkg,
4445 key,
4446 NULL);
4447 if (!err && role == UFUN_GUI_MAIN) {
4448 /* ensure that the gui-main for a model-window
4449 package is set as menu-only */
4450 if (finfo->menupath != NULL &&
4451 strstr(finfo->menupath, "MODELWIN")) {
4452 finfo->gui_attrs[i] |= UFUN_MENU_ONLY;
4453 }
4454 }
4455 }
4456
4457 return err;
4458 }
4459
4460 /* We're saving a previously saved/installed package, and it
4461 (now) ought to be in its own subdir (PDF doc or data files
4462 have been specified). We check to see if the gfn file is
4463 actually just sitting in /some/path/functions.
4464
4465 If so, we try to move it into its own subdir and adjust
4466 everything that depends on its path accordingly.
4467 */
4468
maybe_fix_package_location(function_info * finfo)4469 static int maybe_fix_package_location (function_info *finfo)
4470 {
4471 const char *pkgname;
4472 int err = 0;
4473
4474 pkgname = function_package_get_name(finfo->pkg);
4475
4476 if (pkg_path_is_toplevel(finfo, pkgname)) {
4477 char *p, newpath[FILENAME_MAX];
4478
4479 strcpy(newpath, finfo->fname);
4480 /* trim off pkgname.gfn */
4481 p = strrslash(newpath);
4482 *(p+1) = '\0';
4483 /* append own subdir name */
4484 strcat(newpath, pkgname);
4485 /* make/verify the subdir */
4486 err = gretl_mkdir(newpath);
4487 if (!err) {
4488 /* append pkgname.gfn */
4489 strcat(newpath, SLASHSTR);
4490 strcat(newpath, pkgname);
4491 strcat(newpath, ".gfn");
4492 /* and try moving the file */
4493 err = gretl_rename(finfo->fname, newpath);
4494 }
4495 if (!err) {
4496 /* maybe revise "recent" gfn list */
4497 delete_from_filelist(FILE_LIST_GFN, finfo->fname);
4498 /* update the record in @finfo */
4499 g_free(finfo->fname);
4500 finfo->fname = g_strdup(newpath);
4501 /* and also the in-memory package */
4502 function_package_set_properties(finfo->pkg, "fname",
4503 newpath, NULL);
4504 }
4505
4506 fprintf(stderr, "maybe_fix_package_location: err = %d\n", err);
4507 }
4508
4509 return err;
4510 }
4511
retitle_gfn_dialog(function_info * finfo,const char * pkgname)4512 static void retitle_gfn_dialog (function_info *finfo,
4513 const char *pkgname)
4514 {
4515 gchar *title;
4516
4517 title = g_strdup_printf("gretl: %s", pkgname);
4518 gtk_window_set_title(GTK_WINDOW(finfo->dlg), title);
4519 g_free(title);
4520 }
4521
4522 /* Callback from file selector when saving a function package, or
4523 directly from the package editor if using the package's
4524 existing filename.
4525 */
4526
save_function_package(const char * fname,gpointer p)4527 int save_function_package (const char *fname, gpointer p)
4528 {
4529 function_info *finfo = p;
4530 gchar *pdfstr = NULL;
4531 int err = 0;
4532
4533 if (finfo->fname == NULL) {
4534 /* new save: no filename recorded yet */
4535 err = check_package_filename(fname, 1, finfo->dlg);
4536 if (err) {
4537 return err;
4538 }
4539 finfo->fname = g_strdup(fname);
4540 }
4541
4542 if (finfo->pkg == NULL) {
4543 /* starting from scratch */
4544 finfo->pkg = function_package_new(fname, finfo->pubnames, finfo->n_pub,
4545 finfo->privnames, finfo->n_priv,
4546 &err);
4547 function_package_set_editor(finfo->pkg, finfo->dlg);
4548 } else {
4549 /* revising an existing package */
4550 err = function_package_connect_funcs(finfo->pkg, finfo->pubnames, finfo->n_pub,
4551 finfo->privnames, finfo->n_priv);
4552 if (err) {
4553 fprintf(stderr, "function_package_connect_funcs: err = %d\n", err);
4554 }
4555 }
4556
4557 if (!err) {
4558 /* we need to do this before setting the "gui-attrs" below */
4559 pkg_save_special_functions(finfo);
4560 }
4561
4562 if (!err && finfo->pdfdoc) {
4563 /* make temporary "help" placeholder */
4564 pdfstr = g_strdup_printf("pdfdoc:%s.pdf",
4565 function_package_get_name(finfo->pkg));
4566 }
4567
4568 if (!err) {
4569 err = function_package_set_properties(finfo->pkg,
4570 "author", finfo->author,
4571 "email", finfo->email,
4572 "version", finfo->version,
4573 "date", finfo->date,
4574 "description", finfo->pkgdesc,
4575 "tags", finfo->tags,
4576 "help", pdfstr ? pdfstr : finfo->help,
4577 "sample-script", finfo->sample,
4578 "data-requirement", finfo->dreq,
4579 "min-version", finfo->minver,
4580 "menu-attachment", finfo->menupath,
4581 "label", finfo->menulabel,
4582 "gui-help", finfo->gui_help,
4583 "gui-attrs", finfo->gui_attrs,
4584 "lives-in-subdir", finfo->uses_subdir,
4585 "wants-data-access", finfo->data_access,
4586 "model-requirement", finfo->mreq,
4587 "provider", finfo->provider,
4588 NULL);
4589 if (err) {
4590 fprintf(stderr, "function_package_set_properties: err = %d\n", err);
4591 }
4592 }
4593
4594 if (!err) {
4595 function_package_set_data_files(finfo->pkg,
4596 finfo->datafiles,
4597 finfo->n_files);
4598 function_package_set_depends(finfo->pkg,
4599 finfo->depends,
4600 finfo->n_depends);
4601 }
4602
4603 if (!err && finfo->uses_subdir) {
4604 maybe_fix_package_location(finfo);
4605 }
4606
4607 if (pdfstr != NULL) {
4608 /* free temporary placeholder */
4609 g_free(pdfstr);
4610 }
4611
4612 if (!err) {
4613 err = function_package_write_file(finfo->pkg);
4614 if (err) {
4615 fprintf(stderr, "function_package_write_file: err = %d\n", err);
4616 }
4617 }
4618
4619 if (err) {
4620 gui_errmsg(err);
4621 } else {
4622 const char *pkgname = function_package_get_name(finfo->pkg);
4623
4624 retitle_gfn_dialog(finfo, pkgname);
4625 finfo_set_modified(finfo, FALSE);
4626 gtk_widget_set_sensitive(finfo->validate, TRUE);
4627 maybe_update_gfn_browser(pkgname,
4628 finfo->version,
4629 finfo->date,
4630 finfo->author,
4631 finfo->pkgdesc,
4632 finfo->fname,
4633 finfo->uses_subdir,
4634 finfo->pdfdoc);
4635 mkfilelist(FILE_LIST_GFN, finfo->fname, 0);
4636
4637 /* destroy the temporary pkgname variable */
4638 g_free(finfo->ininame);
4639 finfo->ininame = NULL;
4640
4641 /* revise stored gui package info in accordance with any
4642 changes above, as needed */
4643 gui_function_pkg_revise_status(pkgname,
4644 finfo->fname,
4645 finfo->menulabel,
4646 finfo->menupath,
4647 finfo->uses_subdir,
4648 finfo->dreq,
4649 finfo->mreq);
4650 }
4651
4652 return err;
4653 }
4654
4655 /* callback from file selector when exporting a package in the form
4656 of a regular script */
4657
save_function_package_as_script(const char * fname,gpointer p)4658 int save_function_package_as_script (const char *fname, gpointer p)
4659 {
4660 function_info *finfo = p;
4661 ufunc *fun;
4662 PRN *prn;
4663 int i, err = 0;
4664
4665 prn = gretl_print_new_with_filename(fname, &err);
4666 if (err) {
4667 file_write_errbox(fname);
4668 return err;
4669 }
4670
4671 /* write basic package info */
4672 pprintf(prn, "# author='%s'\n", finfo->author);
4673 if (finfo->email != NULL && *finfo->email != '\0') {
4674 pprintf(prn, "# email='%s'\n", finfo->email);
4675 }
4676 pprintf(prn, "# version='%s'\n", finfo->version);
4677 pprintf(prn, "# date='%s'\n", finfo->date);
4678
4679 /* write private functions, if any */
4680 for (i=0; i<finfo->n_priv; i++) {
4681 fun = get_function_from_package(finfo->privnames[i],
4682 finfo->pkg);
4683 if (fun != NULL) {
4684 pputc(prn, '\n');
4685 gretl_function_print_code(fun, tabwidth, prn);
4686 }
4687 }
4688
4689 /* write public functions */
4690 for (i=0; i<finfo->n_pub; i++) {
4691 fun = get_function_from_package(finfo->pubnames[i],
4692 finfo->pkg);
4693 if (fun != NULL) {
4694 pputc(prn, '\n');
4695 gretl_function_print_code(fun, tabwidth, prn);
4696 }
4697 }
4698
4699 /* append sample script? */
4700 if ((finfo->save_flags & APPEND_SAMPLE) &&
4701 finfo->sample != NULL) {
4702 int n = strlen(finfo->sample);
4703
4704 pputs(prn, "\n# sample function call\n");
4705 pputs(prn, finfo->sample);
4706 if (finfo->sample[n-1] != '\n') {
4707 pputc(prn, '\n');
4708 }
4709 }
4710
4711 gretl_print_destroy(prn);
4712
4713 return 0;
4714 }
4715
maybe_print(PRN * prn,const char * key,const char * arg)4716 static void maybe_print (PRN *prn, const char *key,
4717 const char *arg)
4718 {
4719 if (arg != NULL && *arg != '\0') {
4720 pprintf(prn, "%s = %s\n", key, arg);
4721 } else {
4722 pprintf(prn, "%s = \n", key);
4723 }
4724 }
4725
maybe_write_aux_file(function_info * finfo,const char * fname,const char * id,const gchar * content,PRN * prn)4726 static int maybe_write_aux_file (function_info *finfo,
4727 const char *fname,
4728 const char *id,
4729 const gchar *content,
4730 PRN *prn)
4731 {
4732 int ret = 0;
4733
4734 if (content != NULL && *content != '\0') {
4735 const char *auxname = NULL;
4736 int flag;
4737
4738 if (!strcmp(id, "sample-script")) {
4739 auxname = finfo->sample_fname;
4740 flag = WRITE_SAMPFILE;
4741 } else if (!strcmp(id, "help")) {
4742 auxname = finfo->help_fname;
4743 flag = WRITE_HELPFILE;
4744 } else {
4745 auxname = finfo->gui_help_fname;
4746 flag = WRITE_GUI_HELP;
4747 }
4748
4749 if (auxname != NULL && (finfo->save_flags & flag)) {
4750 /* we'll write out the actual file */
4751 FILE *fp = NULL;
4752
4753 if (path_last_slash_const(fname)) {
4754 /* package fname has directory component */
4755 char *s, tmp[FILENAME_MAX];
4756
4757 strcpy(tmp, fname);
4758 s = strrslash(tmp);
4759 *(s + 1) = '\0';
4760 strcat(tmp, auxname);
4761 fp = gretl_fopen(tmp, "wb"); /* 21017-02-22: was "w" */
4762 } else {
4763 fp = gretl_fopen(auxname, "wb"); /* 21017-02-22: was "w" */
4764 }
4765
4766 if (fp != NULL) {
4767 fputs(content, fp);
4768 fputc('\n', fp);
4769 fclose(fp);
4770 ret = 1;
4771 }
4772 }
4773
4774 if (auxname != NULL) {
4775 /* record filename in spec file */
4776 pputs(prn, auxname);
4777 }
4778 }
4779
4780 return ret;
4781 }
4782
4783 /* Given the in-memory representation of a gfn package, write
4784 out the corresponding .spec file. Also write out to separate
4785 files the package's help text and sample script, if available.
4786 */
4787
save_function_package_spec(const char * fname,gpointer p)4788 int save_function_package_spec (const char *fname, gpointer p)
4789 {
4790 const char *extra_keys[] = {
4791 GUI_MAIN,
4792 "label",
4793 "menu-attachment",
4794 BUNDLE_PRINT,
4795 BUNDLE_PLOT,
4796 BUNDLE_TEST,
4797 BUNDLE_FCAST,
4798 BUNDLE_EXTRA,
4799 GUI_PRECHECK,
4800 LIST_MAKER,
4801 NULL
4802 };
4803 const char *reqstr = NULL;
4804 const char *gui_help;
4805 const char *sample;
4806 gchar *strval;
4807 function_info *finfo = p;
4808 PRN *prn;
4809 char vstr[10];
4810 int nnp = 0, nmo = 0;
4811 int i, len;
4812 int err = 0;
4813
4814 prn = gretl_print_new_with_filename(fname, &err);
4815 if (err) {
4816 file_write_errbox(fname);
4817 return err;
4818 }
4819
4820 maybe_print(prn, "author", finfo->author);
4821 maybe_print(prn, "email", finfo->email);
4822 maybe_print(prn, "version", finfo->version);
4823 maybe_print(prn, "date", finfo->date);
4824 maybe_print(prn, "description", finfo->pkgdesc);
4825 maybe_print(prn, "tags", finfo->tags);
4826
4827 if (finfo->minver > 20000 && finfo->minver < 20151) {
4828 int oldv = translate_program_version(finfo->minver, NEW_TO_OLD);
4829
4830 gretl_version_string(vstr, oldv);
4831 } else {
4832 gretl_version_string(vstr, finfo->minver);
4833 }
4834
4835 pprintf(prn,"min-version = %s\n", vstr);
4836
4837 if (finfo->dreq == FN_NEEDS_TS) {
4838 reqstr = NEEDS_TS;
4839 } else if (finfo->dreq == FN_NEEDS_QM) {
4840 reqstr = NEEDS_QM;
4841 } else if (finfo->dreq == FN_NEEDS_PANEL) {
4842 reqstr = NEEDS_PANEL;
4843 } else if (finfo->dreq == FN_NODATA_OK) {
4844 reqstr = NO_DATA_OK;
4845 }
4846
4847 if (reqstr != NULL) {
4848 pprintf(prn, "data-requirement = %s\n", reqstr);
4849 }
4850
4851 for (i=0; extra_keys[i] != NULL; i++) {
4852 function_package_get_properties(finfo->pkg, extra_keys[i],
4853 &strval, NULL);
4854 if (strval != NULL) {
4855 if (*strval != '\0') {
4856 pprintf(prn, "%s = %s\n", extra_keys[i], strval);
4857 }
4858 g_free(strval);
4859 strval = NULL;
4860 }
4861 }
4862
4863 if (finfo->mreq > 0) {
4864 reqstr = gretl_command_word(finfo->mreq);
4865 if (*reqstr != '\0') {
4866 pprintf(prn, "model-requirement = %s\n", reqstr);
4867 }
4868 }
4869
4870 /* public interface names */
4871 pputs(prn, "public = ");
4872 len = 9;
4873 for (i=0; i<finfo->n_pub; i++) {
4874 const char *s = finfo->pubnames[i];
4875 int n = strlen(s);
4876 ufunc *fun;
4877
4878 len += n;
4879 if (len > 72) {
4880 pputs(prn, "\\\n");
4881 pprintf(prn, " %s ", s);
4882 len = n + 3;
4883 } else {
4884 pprintf(prn, "%s ", s);
4885 len++;
4886 }
4887 fun = get_function_from_package(s, finfo->pkg);
4888 if (user_func_is_noprint(fun)) {
4889 nnp++;
4890 }
4891 if (user_func_is_menu_only(fun)) {
4892 nmo++;
4893 }
4894 }
4895 pputc(prn, '\n');
4896
4897 if (nnp > 0) {
4898 /* no-print interface names */
4899 pputs(prn, "no-print = ");
4900 for (i=0; i<finfo->n_pub; i++) {
4901 const char *s = finfo->pubnames[i];
4902 ufunc *fun = get_function_from_package(s, finfo->pkg);
4903
4904 if (user_func_is_noprint(fun)) {
4905 pprintf(prn, "%s ", s);
4906 }
4907 }
4908 pputc(prn, '\n');
4909 }
4910
4911 if (nmo > 0) {
4912 /* menu-only interface names */
4913 pputs(prn, "menu-only = ");
4914 for (i=0; i<finfo->n_pub; i++) {
4915 const char *s = finfo->pubnames[i];
4916 ufunc *fun = get_function_from_package(s, finfo->pkg);
4917
4918 if (user_func_is_menu_only(fun)) {
4919 pprintf(prn, "%s ", s);
4920 }
4921 }
4922 pputc(prn, '\n');
4923 }
4924
4925 /* write out help text? */
4926 if (finfo->pdfdoc || finfo->help != NULL) {
4927 pputs(prn, "help = ");
4928 if (finfo->pdfdoc) {
4929 pprintf(prn, "%s.pdf\n", function_package_get_name(finfo->pkg));
4930 } else {
4931 maybe_write_aux_file(finfo, fname, "help", finfo->help, prn);
4932 pputc(prn, '\n');
4933 }
4934 }
4935
4936 gui_help = (finfo->gui_help != NULL)? finfo->gui_help :
4937 function_package_get_string(finfo->pkg, "gui-help");
4938
4939 /* write out GUI-specific help text? */
4940 if (gui_help != NULL) {
4941 pputs(prn, "gui-help = ");
4942 maybe_write_aux_file(finfo, fname, "gui-help",
4943 gui_help, prn);
4944 pputc(prn, '\n');
4945 }
4946
4947 sample = (finfo->sample != NULL)? finfo->sample :
4948 function_package_get_string(finfo->pkg, "sample-script");
4949
4950 /* write out sample script? */
4951 pputs(prn, "sample-script = ");
4952 maybe_write_aux_file(finfo, fname, "sample-script",
4953 sample, prn);
4954 pputc(prn, '\n');
4955
4956 /* write out data-files listing? */
4957 if (finfo->datafiles != NULL) {
4958 pputs(prn, "data-files = ");
4959 for (i=0; i<finfo->n_files; i++) {
4960 pputs(prn, finfo->datafiles[i]);
4961 pputc(prn, (i == finfo->n_files - 1)? '\n' : ' ');
4962 }
4963 }
4964
4965 /* write out dependency listing? */
4966 if (finfo->depends != NULL) {
4967 pputs(prn, "depends = ");
4968 for (i=0; i<finfo->n_depends; i++) {
4969 pputs(prn, finfo->depends[i]);
4970 pputc(prn, (i == finfo->n_files - 1)? '\n' : ' ');
4971 }
4972 }
4973
4974 /* write out provider name? */
4975 if (finfo->provider != NULL) {
4976 pprintf(prn, "provider = %s\n", finfo->provider);
4977 }
4978
4979 gretl_print_destroy(prn);
4980
4981 return 0;
4982 }
4983
save_function_package_zipfile(const char * fname,gpointer p)4984 int save_function_package_zipfile (const char *fname, gpointer p)
4985 {
4986 function_info *finfo = p;
4987
4988 gui_pkg_make_zipfile(finfo, NULL, fname);
4989
4990 return 0;
4991 }
4992
set_package_pdfname(const char * fname,gpointer p)4993 int set_package_pdfname (const char *fname, gpointer p)
4994 {
4995 function_info *finfo = p;
4996
4997 g_free(finfo->pdfname);
4998 finfo->pdfname = g_strdup(fname);
4999
5000 return 0;
5001 }
5002
5003 /* Called from function selection dialog: a name has been specified
5004 anda set of functions has been selected -- now we need to add info
5005 on author, version, etc, etc.
5006 */
5007
edit_new_function_package(gchar * pkgname,char ** pubnames,int npub,char ** privnames,int npriv)5008 void edit_new_function_package (gchar *pkgname,
5009 char **pubnames, int npub,
5010 char **privnames, int npriv)
5011 {
5012 function_info *finfo = finfo_new();
5013
5014 if (finfo != NULL) {
5015 finfo->ininame = pkgname;
5016 finfo->pubnames = pubnames;
5017 finfo->n_pub = npub;
5018 finfo->privnames = privnames;
5019 finfo->n_priv = npriv;
5020
5021 /* set up dialog to do the actual editing */
5022 finfo_dialog(finfo);
5023 }
5024 }
5025
5026 /* callback from GUI selector to add/remove functions
5027 when editing a package */
5028
revise_function_package(void * p,char ** pubnames,int npub,char ** privnames,int npriv)5029 void revise_function_package (void *p, char **pubnames, int npub,
5030 char **privnames, int npriv)
5031 {
5032 function_info *finfo = p;
5033 int changed = 0;
5034 int err = 0;
5035
5036 fprintf(stderr, "original: n_pub=%d, n_priv=%d\n",
5037 finfo->n_pub, finfo->n_priv);
5038
5039 err = finfo_reset_function_names(finfo,
5040 pubnames, npub,
5041 privnames, npriv,
5042 &changed);
5043
5044 fprintf(stderr, "revised: n_pub=%d, n_priv=%d (changed=%d)\n",
5045 finfo->n_pub, finfo->n_priv, changed);
5046
5047 if (!err && changed) {
5048 depopulate_combo_box(GTK_COMBO_BOX(finfo->codesel));
5049 func_selector_set_strings(finfo, finfo->codesel);
5050 verify_selected_specials(finfo);
5051 if (finfo->pkg != NULL) {
5052 /* sync with gretl_func.c */
5053 function_package_connect_funcs(finfo->pkg,
5054 finfo->pubnames,
5055 finfo->n_pub,
5056 finfo->privnames,
5057 finfo->n_priv);
5058 }
5059 finfo_set_modified(finfo, TRUE);
5060 }
5061 }
5062
finfo_set_menuwin(function_info * finfo)5063 static void finfo_set_menuwin (function_info *finfo)
5064 {
5065 if (finfo->menupath == NULL) {
5066 finfo->menuwin = NO_WINDOW;
5067 } else if (!strncmp(finfo->menupath, "MAINWIN", 7)) {
5068 finfo->menuwin = MAIN_WINDOW;
5069 } else if (!strncmp(finfo->menupath, "MODELWIN", 8)) {
5070 finfo->menuwin = MODEL_WINDOW;
5071 } else {
5072 finfo->menuwin = NO_WINDOW;
5073 }
5074 }
5075
finfo_set_special_names(function_info * finfo)5076 static int finfo_set_special_names (function_info *finfo)
5077 {
5078 const char *key;
5079 int i, err = 0;
5080
5081 for (i=0; i<N_SPECIALS && !err; i++) {
5082 key = package_role_get_key(i+1);
5083 err = function_package_get_properties(finfo->pkg, key,
5084 &finfo->specials[i],
5085 NULL);
5086 }
5087
5088 return err;
5089 }
5090
finfo_set_data_files(function_info * finfo)5091 static int finfo_set_data_files (function_info *finfo)
5092 {
5093 char **S;
5094 int n = 0;
5095
5096 S = function_package_get_data_files(finfo->pkg, &n);
5097
5098 if (S != NULL) {
5099 finfo->datafiles = S;
5100 finfo->n_files = n;
5101 }
5102
5103 return 0;
5104 }
5105
finfo_set_dependencies(function_info * finfo)5106 static int finfo_set_dependencies (function_info *finfo)
5107 {
5108 char **S;
5109 int n = 0;
5110
5111 S = function_package_get_depends(finfo->pkg, &n);
5112
5113 if (S != NULL) {
5114 finfo->depends = S;
5115 finfo->n_depends = n;
5116 }
5117
5118 return 0;
5119 }
5120
is_pdf_reference(const char * s)5121 static int is_pdf_reference (const char *s)
5122 {
5123 if (s != NULL) {
5124 if (!strncmp(s, "pdfdoc", 6) || has_suffix(s, ".pdf")) {
5125 return 1;
5126 }
5127 }
5128
5129 return 0;
5130 }
5131
5132 #define EDIT_ZIPS 0 /* not yet */
5133
5134 #if EDIT_ZIPS
5135
load_gfn_from_zip(const char * fname,int * err)5136 static fnpkg *load_gfn_from_zip (const char *fname, int *err)
5137 {
5138 fnpkg *pkg = NULL;
5139 char tmpgfn[MAXLEN];
5140 gchar *tmpname, *tmp2;
5141 char *p;
5142
5143 tmpname = g_path_get_basename(fname);
5144 p = strrchr(tmpname, '.');
5145 *p = '\0';
5146 tmp2 = g_strdup(tmpname);
5147 strcat(p, ".gfn");
5148
5149 gretl_build_path(tmpgfn, gretl_dotdir(), tmp2, tmpname, NULL);
5150
5151 #if 0
5152 fprintf(stderr, "from zip: gfn is '%s'\n", tmpgfn);
5153 #endif
5154
5155 *err = gretl_unzip_into(fname, gretl_dotdir());
5156 if (!*err) {
5157 pkg = get_function_package_by_filename(tmpgfn, err);
5158 }
5159
5160 g_free(tmpname);
5161 g_free(tmp2);
5162
5163 return pkg;
5164 }
5165
5166 #endif
5167
edit_function_package(const char * fname)5168 void edit_function_package (const char *fname)
5169 {
5170 GtkWidget *editor;
5171 function_info *finfo = NULL;
5172 int *publist = NULL;
5173 int *privlist = NULL;
5174 fnpkg *pkg;
5175 int err = 0;
5176
5177 #if EDIT_ZIPS
5178 if (has_suffix(fname, ".zip")) {
5179 pkg = load_gfn_from_zip(fname, &err);
5180 } else {
5181 pkg = get_function_package_by_filename(fname, &err);
5182 }
5183 #else
5184 pkg = get_function_package_by_filename(fname, &err);
5185 #endif
5186 if (err) {
5187 gui_errmsg(err);
5188 goto bailout;
5189 }
5190
5191 editor = function_package_get_editor(pkg);
5192 if (editor != NULL) {
5193 /* don't open a second editor for a given package */
5194 gtk_window_present(GTK_WINDOW(editor));
5195 return;
5196 }
5197
5198 finfo = finfo_new();
5199 if (finfo == NULL) {
5200 err = E_ALLOC;
5201 goto bailout;
5202 }
5203
5204 finfo->pkg = pkg;
5205
5206 err = function_package_get_properties(finfo->pkg,
5207 "publist", &publist,
5208 "privlist", &privlist,
5209 "author", &finfo->author,
5210 "email", &finfo->email,
5211 "version", &finfo->version,
5212 "date", &finfo->date,
5213 "description", &finfo->pkgdesc,
5214 "tags", &finfo->tags,
5215 "help", &finfo->help,
5216 "sample-script", &finfo->sample,
5217 "data-requirement", &finfo->dreq,
5218 "min-version", &finfo->minver,
5219 "menu-attachment", &finfo->menupath,
5220 "label", &finfo->menulabel,
5221 "gui-help", &finfo->gui_help,
5222 "lives-in-subdir", &finfo->uses_subdir,
5223 "wants-data-access", &finfo->data_access,
5224 "model-requirement", &finfo->mreq,
5225 "gui-attrs", finfo->gui_attrs,
5226 "provider", &finfo->provider,
5227 NULL);
5228
5229 if (!err && publist == NULL) {
5230 err = E_DATA;
5231 }
5232
5233 if (!err) {
5234 err = finfo_set_function_names(finfo, publist, privlist);
5235 }
5236
5237 if (!err) {
5238 err = finfo_set_special_names(finfo);
5239 }
5240
5241 if (!err) {
5242 finfo_set_menuwin(finfo);
5243 }
5244
5245 if (!err) {
5246 err = finfo_set_data_files(finfo);
5247 }
5248
5249 if (!err) {
5250 err = finfo_set_dependencies(finfo);
5251 }
5252
5253 if (is_pdf_reference(finfo->help)) {
5254 g_free(finfo->help);
5255 finfo->help = NULL;
5256 finfo->pdfdoc = TRUE;
5257 }
5258
5259 #if PKG_DEBUG
5260 printlist(publist, "publist");
5261 printlist(privlist, "privlist");
5262 #endif
5263
5264 free(publist);
5265 free(privlist);
5266
5267 if (err) {
5268 fprintf(stderr, "function_package_get_info: failed on %s\n", fname);
5269 errbox("Couldn't get function package information");
5270 finfo_free(finfo);
5271 goto bailout;
5272 }
5273
5274 finfo->fname = g_strdup(fname);
5275
5276 bailout:
5277
5278 if (err) {
5279 delete_from_filelist(FILE_LIST_GFN, fname);
5280 } else {
5281 /* record opening */
5282 mkfilelist(FILE_LIST_GFN, finfo->fname, 0);
5283 /* and go for it */
5284 finfo_dialog(finfo);
5285 }
5286 }
5287
edit_specified_package(const char * fname)5288 gboolean edit_specified_package (const char *fname)
5289 {
5290 FILE *fp = gretl_fopen(fname, "rb"); /* 2017-02-22: was "r" */
5291 gboolean ret = FALSE;
5292
5293 if (fp == NULL) {
5294 file_read_errbox(fname);
5295 delete_from_filelist(FILE_LIST_GFN, fname);
5296 } else {
5297 fclose(fp);
5298 edit_function_package(fname);
5299 ret = TRUE;
5300 }
5301
5302 return ret;
5303 }
5304
no_user_functions_check(GtkWidget * parent)5305 int no_user_functions_check (GtkWidget *parent)
5306 {
5307 int err = 0;
5308
5309 if (n_free_functions() == 0) {
5310 const gchar *query =
5311 N_("No functions are available for packaging at present.\n"
5312 "Do you want to write a function now?");
5313 int resp;
5314
5315 err = 1;
5316 resp = yes_no_dialog(_("gretl: function packages"),
5317 _(query), parent);
5318 if (resp == GRETL_YES) {
5319 do_new_script(FUNC, NULL);
5320 }
5321 }
5322
5323 return err;
5324 }
5325
5326 /* called from toolbar.c in response to the "build" option from
5327 window editing a .spec file */
5328
build_package_from_spec_file(windata_t * vwin)5329 void build_package_from_spec_file (windata_t *vwin)
5330 {
5331 char inpname[FILENAME_MAX];
5332 char gfnname[FILENAME_MAX];
5333 int resp, err = 0;
5334
5335 switch_ext(inpname, vwin->fname, "inp");
5336 err = gretl_test_fopen(inpname, "rb"); /* 2017-02-22: was "r" */
5337 if (err) {
5338 gchar *msg = g_strdup_printf(_("Couldn't open %s"), inpname);
5339
5340 msgbox(msg, GTK_MESSAGE_ERROR, vwin->main);
5341 g_free(msg);
5342 return;
5343 }
5344
5345 switch_ext(gfnname, vwin->fname, "gfn");
5346 resp = overwrite_gfn_check(gfnname, vwin->main, NULL);
5347
5348 if (resp == GRETL_YES) {
5349 int save_batch = gretl_in_batch_mode();
5350 windata_t *prnwin;
5351 PRN *prn;
5352
5353 if (bufopen(&prn)) {
5354 return;
5355 }
5356
5357 function_package_unload_by_filename(gfnname);
5358
5359 pprintf(prn, "Found script file '%s'\n", inpname);
5360 err = execute_script(inpname, NULL, prn, SCRIPT_EXEC | INCLUDE_EXEC,
5361 vwin->main);
5362 if (!err) {
5363 err = create_and_write_function_package(gfnname, OPT_G, prn);
5364 if (err) {
5365 pputs(prn, "Failed to produce gfn file\n");
5366 } else {
5367 pprintf(prn, "Wrote '%s'\n", gfnname);
5368 }
5369 }
5370 gretl_set_batch_mode(save_batch);
5371 prnwin = view_buffer(prn, 78, 450, _("build gfn file"), BUILD_PKG, NULL);
5372 gtk_window_set_transient_for(GTK_WINDOW(prnwin->main),
5373 GTK_WINDOW(vwin->main));
5374 gtk_window_set_destroy_with_parent(GTK_WINDOW(prnwin->main), TRUE);
5375 }
5376 }
5377