Clean graph and frame selectors then the active graph has changed
[grace.git] / src / graceapp.c
blob43c106412e9e518b5629c5e6a5dd19055c707076
1 /*
2 * Grace - GRaphing, Advanced Computation and Exploration of data
3 *
4 * Home page: http://plasma-gate.weizmann.ac.il/Grace/
5 *
6 * Copyright (c) 2001-2006 Grace Development Team
7 *
8 * Maintained by Evgeny Stambulchik
9 *
11 * All Rights Reserved
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 #include <config.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <string.h>
34 #ifdef HAVE_CUPS
35 # include <cups/cups.h>
36 #endif
38 #include "defines.h"
39 #include "graceapp.h"
40 #include "utils.h"
41 #include "core_utils.h"
42 #include "xprotos.h"
44 GUI *gui_new(GraceApp *gapp)
46 GUI *gui;
48 gui = xmalloc(sizeof(GUI));
49 if (!gui) {
50 return NULL;
52 memset(gui, 0, sizeof(GUI));
54 gui->P = gapp;
56 gui->zoom = 1.0;
58 gui->inwin = FALSE;
59 gui->invert = TRUE;
60 gui->focus_policy = FOCUS_CLICK;
61 gui->draw_focus_flag = TRUE;
63 gui->crosshair_cursor = FALSE;
65 gui->noask = FALSE;
67 gui->instant_update = FALSE;
68 gui->toolbar = TRUE;
69 gui->statusbar = TRUE;
70 gui->locbar = TRUE;
72 gui->install_cmap = CMAP_INSTALL_AUTO;
73 gui->private_cmap = FALSE;
75 #if defined WITH_XMHTML
76 gui->force_external_viewer = FALSE;
77 #else
78 gui->force_external_viewer = TRUE;
79 #endif
81 return gui;
84 void gui_free(GUI *gui)
86 if (gui) {
87 xfree(gui->xstuff);
88 xfree(gui);
93 RunTime *runtime_new(GraceApp *gapp)
95 RunTime *rt;
96 char *s;
98 rt = xmalloc(sizeof(RunTime));
99 if (!rt) {
100 return NULL;
102 memset(rt, 0, sizeof(RunTime));
104 rt->P = gapp;
106 /* allocatables */
107 rt->print_dests = NULL;
108 rt->print_cmd = NULL;
109 rt->gapp_editor = NULL;
110 rt->help_viewer = NULL;
111 rt->workingdir = NULL;
113 #ifdef HAVE_CUPS
114 rt->use_cups = TRUE;
115 #else
116 rt->use_cups = FALSE;
117 #endif
119 /* print command */
120 if ((s = getenv("GRACE_PRINT_CMD")) == NULL) {
121 s = bi_print_cmd();
123 rt->print_cmd = copy_string(NULL, s);
124 if (rt->use_cups) {
125 rt->ptofile = FALSE;
126 } else
127 /* if no print command defined, print to file by default */
128 if (string_is_empty(rt->print_cmd)) {
129 rt->ptofile = TRUE;
130 } else {
131 rt->ptofile = FALSE;
134 /* editor */
135 if ((s = getenv("GRACE_EDITOR")) == NULL) {
136 s = bi_editor();
138 rt->gapp_editor = copy_string(NULL, s);
140 /* html viewer */
141 if ((s = getenv("GRACE_HELPVIEWER")) == NULL) {
142 s = bi_helpviewer();
144 rt->help_viewer = copy_string(NULL, s);
145 if (!strstr(rt->help_viewer, "%s")) {
146 rt->help_viewer = concat_strings(rt->help_viewer, " %s");
149 /* working directory */
150 rt->workingdir = xmalloc(GR_MAXPATHLEN);
151 if (!getcwd(rt->workingdir, GR_MAXPATHLEN - 1)) {
152 runtime_free(rt);
153 return NULL;
155 if (rt->workingdir[strlen(rt->workingdir)-1] != '/') {
156 rt->workingdir = concat_strings(rt->workingdir, "/");
159 if (!rt->print_cmd ||
160 !rt->gapp_editor ||
161 !rt->help_viewer ||
162 !rt->workingdir) {
163 runtime_free(rt);
164 return NULL;
167 if (gapp_init_print(rt) != RETURN_SUCCESS) {
168 runtime_free(rt);
169 return NULL;
172 rt->print_file[0] = '\0';
174 rt->tdevice = 0;
175 rt->hdevice = 0;
177 rt->date_hint = FMT_nohint;
179 rt->timer_delay = 200;
181 rt->autoscale_onread = AUTOSCALE_XY;
184 rt->scrollper = 0.05;
185 rt->shexper = 0.05;
187 rt->resfp = NULL;
189 rt->emergency_save = FALSE;
190 rt->interrupts = 0;
192 return rt;
195 void runtime_free(RunTime *rt)
197 int i;
199 if (!rt) {
200 return;
203 for (i = 0; i < rt->num_print_dests; i++) {
204 PrintDest *pd = &rt->print_dests[i];
205 int j;
206 xfree(pd->name);
207 xfree(pd->inst);
208 xfree(pd->printer);
209 for (j = 0; j < pd->nogroups; j++) {
210 PrintOptGroup *og = &pd->ogroups[j];
211 int k;
212 xfree(og->name);
213 xfree(og->text);
214 for (k = 0; k < og->nopts; k++) {
215 PrintOption *po = &og->opts[k];
216 xfree(po->name);
217 xfree(po->text);
218 dict_free(po->choices);
220 xfree(og->opts);
222 xfree(pd->ogroups);
224 xfree(rt->print_dests);
226 xfree(rt->print_cmd);
227 xfree(rt->gapp_editor);
228 xfree(rt->help_viewer);
229 xfree(rt->workingdir);
231 if (rt->resfp) {
232 gapp_close(rt->resfp);
235 xfree(rt);
238 static void eval_proc(GVarType type, GVarData vardata, void *udata)
240 unsigned int i;
241 DArray *da;
242 char buf[64];
244 switch (type) {
245 case GVarNil:
246 stufftext("(nil)\n");
247 break;
248 case GVarNum:
249 sprintf(buf, "%g\n", vardata.num);
250 stufftext(buf);
251 break;
252 case GVarBool:
253 stufftext(vardata.boolval ? "true":"false");
254 stufftext("\n");
255 break;
256 case GVarArr:
257 da = vardata.arr;
258 stufftext("{");
259 for (i = 0; da && i < da->size; i++) {
260 sprintf(buf, " %g ", da->x[i]);
261 stufftext(buf);
263 stufftext("}\n");
264 break;
265 case GVarStr:
266 stufftext(vardata.str);
267 stufftext("\n");
268 break;
269 default:
270 errmsg("unknown data type");
271 break;
275 GraceApp *gapp_new(void)
277 GraceApp *gapp;
279 gapp = xmalloc(sizeof(GraceApp));
280 if (!gapp) {
281 return NULL;
283 memset(gapp, 0, sizeof(GraceApp));
285 if (grace_init() != RETURN_SUCCESS) {
286 gapp_free(gapp);
287 return NULL;
290 gapp->grace = grace_new(bi_home());
291 if (!gapp->grace) {
292 gapp_free(gapp);
293 return NULL;
295 graal_set_eval_proc(grace_get_graal(gapp->grace), eval_proc);
297 grace_set_udata(gapp->grace, gapp);
299 gapp->rt = runtime_new(gapp);
300 if (!gapp->rt) {
301 gapp_free(gapp);
302 return NULL;
305 gapp->gui = gui_new(gapp);
306 if (!gapp->gui) {
307 gapp_free(gapp);
308 return NULL;
311 gapp->pc = container_new(grace_get_qfactory(gapp->grace), AMEM_MODEL_SIMPLE);
312 if (!gapp->pc) {
313 gapp_free(gapp);
314 return NULL;
317 return gapp;
320 void gapp_free(GraceApp *gapp)
322 unsigned int i;
324 if (!gapp) {
325 return;
328 for (i = 0; i < gapp->gpcount; i++) {
329 gproject_free(gapp->gplist[i]);
331 xfree(gapp->gplist);
333 quark_free(gapp->pc);
334 gui_free(gapp->gui);
335 runtime_free(gapp->rt);
336 grace_free(gapp->grace);
338 xfree(gapp);
341 GraceApp *gapp_from_quark(const Quark *q)
343 GraceApp *gapp = NULL;
344 if (q) {
345 Grace *grace = grace_from_quark(q);
346 gapp = grace_get_udata(grace);
349 return gapp;
352 RunTime *rt_from_quark(const Quark *q)
354 GraceApp *gapp = gapp_from_quark(q);
355 if (gapp) {
356 return gapp->rt;
357 } else {
358 return NULL;
362 GUI *gui_from_quark(const Quark *q)
364 GraceApp *gapp = gapp_from_quark(q);
365 if (gapp) {
366 return gapp->gui;
367 } else {
368 return NULL;
372 GProject *gproject_from_quark(const Quark *q)
374 unsigned int i;
375 GraceApp *gapp;
376 GProject *gp;
377 Quark *project = get_parent_project(q);
379 if (!project) {
380 return NULL;
383 gapp = gapp_from_quark(project);
385 for (i = 0; i < gapp->gpcount; i++) {
386 gp = gapp->gplist[i];
387 if (gproject_get_top(gp) == project) {
388 return gp;
392 return NULL;
395 int gapp_add_gproject(GraceApp *gapp, GProject *gp)
397 void *p;
399 if (!gapp || !gp) {
400 return RETURN_FAILURE;
403 p = xrealloc(gapp->gplist, (gapp->gpcount + 1)*sizeof(GProject));
404 if (!p) {
405 return RETURN_FAILURE;
408 gapp->gplist = p;
409 gapp->gplist[gapp->gpcount] = gp;
410 gapp->gpcount++;
412 return RETURN_SUCCESS;
415 int gapp_delete_gproject(GraceApp *gapp, GProject *gp)
417 unsigned int i, j = 0;
418 GProject **p;
419 GProject *gpr;
421 if (!gapp || !gp) {
422 return RETURN_FAILURE;
425 p = xmalloc((gapp->gpcount - 1)*sizeof(GProject));
426 if (!p) {
427 return RETURN_FAILURE;
430 for (i = 0; i < gapp->gpcount; i++) {
431 gpr = gapp->gplist[i];
433 if (gpr != gp) {
434 p[j] = gpr;
435 j++;
439 xfree(gapp->gplist);
441 gapp->gplist = p;
442 gapp->gpcount--;
444 if (gapp->gp == gp) {
445 gapp->gp = NULL;
448 gproject_free(gp);
450 return RETURN_SUCCESS;
453 int gapp_set_active_gproject(GraceApp *gapp, GProject *gp)
455 if (!gapp || !gp) {
456 return RETURN_FAILURE;
459 if (gapp->gp) {
460 quark_set_active2(gproject_get_top(gapp->gp), FALSE);
462 quark_set_active2(gproject_get_top(gp), TRUE);
464 gapp->gp = gp;
465 /* reset graal ? */
467 /* Set dimensions of all devices */
468 grace_sync_canvas_devices(gp);
470 /* Reset set autocolorization index */
471 gapp->rt->setcolor = 0;
473 /* Request update of color selectors */
474 gapp->gui->need_colorsel_update = TRUE;
476 /* Request update of font selectors */
477 gapp->gui->need_fontsel_update = TRUE;
479 clean_graph_selectors(NULL, QUARK_ETYPE_DELETE, NULL);
480 clean_frame_selectors(NULL, QUARK_ETYPE_DELETE, NULL);
482 return RETURN_SUCCESS;
485 int gapp_set_gproject_id(GraceApp *gapp, GProject *gp, int id)
487 Quark *q = gproject_get_top(gp);
489 return quark_move2(q, quark_parent_get(q), id);
492 int gapp_get_gproject_id(GraceApp *gapp, GProject *gp)
494 Quark *q = gproject_get_top(gp);
496 return quark_get_id(q);
500 * flag to indicate destination of hardcopy output,
501 * ptofile = 0 means print to printer, otherwise print to file
503 void set_ptofile(GraceApp *gapp, int flag)
505 gapp->rt->ptofile = flag;
508 int get_ptofile(const GraceApp *gapp)
510 return gapp->rt->ptofile;
514 * set the current print device
516 int set_printer(GraceApp *gapp, int device)
518 Canvas *canvas = grace_get_canvas(gapp->grace);
519 Device_entry *d = get_device_props(canvas, device);
520 if (!d || d->type == DEVICE_TERM) {
521 return RETURN_FAILURE;
522 } else {
523 gapp->rt->hdevice = device;
524 if (d->type != DEVICE_PRINT) {
525 set_ptofile(gapp, TRUE);
527 return RETURN_SUCCESS;
531 int set_printer_by_name(GraceApp *gapp, const char *dname)
533 Canvas *canvas = grace_get_canvas(gapp->grace);
534 int device;
536 device = get_device_by_name(canvas, dname);
538 return set_printer(gapp, device);
541 int set_page_dimensions(GraceApp *gapp, int wpp, int hpp, int rescale)
543 if (wpp <= 0 || hpp <= 0 || !gapp || !gapp->gp) {
544 return RETURN_FAILURE;
545 } else {
546 int wpp_old, hpp_old;
547 Project *pr = project_get_data(gproject_get_top(gapp->gp));
548 if (!pr) {
549 return RETURN_FAILURE;
552 wpp_old = pr->page_wpp;
553 hpp_old = pr->page_hpp;
555 pr->page_wpp = wpp;
556 pr->page_hpp = hpp;
557 if (rescale) {
558 if (hpp*wpp_old - wpp*hpp_old != 0) {
559 /* aspect ratio changed */
560 double ext_x, ext_y;
561 double old_aspectr, new_aspectr;
563 old_aspectr = (double) wpp_old/hpp_old;
564 new_aspectr = (double) wpp/hpp;
565 if (old_aspectr >= 1.0 && new_aspectr >= 1.0) {
566 ext_x = new_aspectr/old_aspectr;
567 ext_y = 1.0;
568 } else if (old_aspectr <= 1.0 && new_aspectr <= 1.0) {
569 ext_x = 1.0;
570 ext_y = old_aspectr/new_aspectr;
571 } else if (old_aspectr >= 1.0 && new_aspectr <= 1.0) {
572 ext_x = 1.0/old_aspectr;
573 ext_y = 1.0/new_aspectr;
574 } else {
575 ext_x = new_aspectr;
576 ext_y = old_aspectr;
579 rescale_viewport(gproject_get_top(gapp->gp), ext_x, ext_y);
583 grace_sync_canvas_devices(gapp->gp);
585 return RETURN_SUCCESS;
589 #ifdef HAVE_CUPS
590 static void parse_group(PrintDest *pd, ppd_group_t *group)
592 int i, j; /* Looping vars */
593 ppd_option_t *option; /* Current option */
594 ppd_choice_t *choice; /* Current choice */
595 ppd_group_t *subgroup; /* Current subgroup */
596 PrintOptGroup *og;
597 PrintOption *po;
599 pd->ogroups = xrealloc(pd->ogroups, (pd->nogroups + 1)*sizeof(PrintOptGroup));
600 if (!pd->ogroups) {
601 return;
604 og = &pd->ogroups[pd->nogroups];
605 pd->nogroups++;
607 og->name = copy_string(NULL, group->name);
608 og->text = copy_string(NULL, group->text);
610 og->opts = xcalloc(group->num_options, sizeof(PrintOption));
611 if (!og->opts) {
612 return;
614 og->nopts = 0;
616 for (i = 0, option = group->options, po = og->opts; i < group->num_options; i++, option++) {
617 po->name = copy_string(NULL, option->keyword);
618 po->text = copy_string(NULL, option->text);
620 po->choices = dict_new();
621 po->selected = -1;
622 dict_resize(po->choices, option->num_choices);
623 for (j = 0, choice = option->choices; j < option->num_choices; j++, choice++) {
624 DictEntry de;
626 de.key = j;
627 de.name = choice->choice;
628 de.descr = choice->text;
629 dict_entry_copy(&po->choices->entries[j], &de);
631 if (choice->marked) {
632 po->selected = de.key;
633 dict_entry_copy(&po->choices->defaults, &de);
636 if (po->selected != -1) {
637 og->nopts++;
638 po++;
639 pd->nopts++;
640 } else {
641 xfree(po->name);
642 xfree(po->text);
643 dict_free(po->choices);
647 for (i = 0, subgroup = group->subgroups; i < group->num_subgroups; i++, subgroup++) {
648 parse_group(pd, subgroup);
651 #endif
653 int gapp_init_print(RunTime *rt)
655 #ifdef HAVE_CUPS
656 int i;
657 cups_dest_t *dests;
659 rt->print_dest = 0;
661 rt->num_print_dests = cupsGetDests(&dests);
662 if (!rt->num_print_dests) {
663 /* no CUPS printers defined or CUPS not running */
664 rt->use_cups = FALSE;
665 } else {
666 rt->print_dests = xcalloc(rt->num_print_dests, sizeof(PrintDest));
667 if (rt->print_dests == NULL) {
668 return RETURN_FAILURE;
672 for (i = 0; i < rt->num_print_dests; i++) {
673 cups_dest_t *dest = &dests[i];
674 PrintDest *pd = &rt->print_dests[i];
676 char *printer;
677 int j;
678 const char *filename; /* PPD filename */
679 ppd_file_t *ppd; /* PPD data */
680 ppd_group_t *group; /* Current group */
682 printer = copy_string(NULL, dest->name);
683 if (dest->instance) {
684 printer = concat_strings(printer, "/");
685 printer = concat_strings(printer, dest->instance);
688 pd->name = copy_string(NULL, dest->name);
689 pd->inst = copy_string(NULL, dest->instance);
690 pd->printer = printer;
692 if (dest->is_default) {
693 rt->print_dest = i;
696 if ((filename = cupsGetPPD(dest->name)) == NULL) {
697 continue;
700 if ((ppd = ppdOpenFile(filename)) == NULL) {
701 remove(filename);
702 continue;
705 ppdMarkDefaults(ppd);
706 cupsMarkOptions(ppd, dest->num_options, dest->options);
708 for (j = 0, group = ppd->groups; j < ppd->num_groups; j++, group++) {
709 parse_group(pd, group);
712 ppdClose(ppd);
713 remove(filename);
715 cupsFreeDests(rt->num_print_dests, dests);
716 #else
717 rt->print_dest = 0;
718 rt->num_print_dests = 0;
719 #endif
721 return RETURN_SUCCESS;
724 int gapp_print(const GraceApp *gapp, const char *fname)
726 char tbuf[128];
727 int retval = RETURN_SUCCESS;
728 #ifdef HAVE_CUPS
729 if (gapp->rt->use_cups) {
730 PrintDest *pd = &gapp->rt->print_dests[gapp->rt->print_dest];
731 int i, j;
732 int jobid;
733 int num_options = 0;
734 cups_option_t *options = NULL;
736 for (i = 0; i < pd->nogroups; i++) {
737 PrintOptGroup *og = &pd->ogroups[i];
739 for (j = 0; j < og->nopts; j++) {
740 PrintOption *po = &og->opts[j];
741 char *value;
743 if (po->selected != po->choices->defaults.key) {
744 dict_get_name_by_key(po->choices, po->selected, &value);
745 num_options = cupsAddOption(po->name, value, num_options, &options);
750 jobid = cupsPrintFile(pd->name, fname, "Grace", num_options, options);
751 if (jobid == 0) {
752 errmsg(ippErrorString(cupsLastError()));
753 retval = RETURN_FAILURE;
755 } else
756 #endif
758 sprintf(tbuf, "%s %s", get_print_cmd(gapp), fname);
759 system_wrap(tbuf);
762 return retval;
765 #define VP_EPSILON 0.001
768 * If writing to a file, check to see if it exists
770 void do_hardcopy(const GProject *gp)
772 Quark *project = gproject_get_top(gp);
773 GraceApp *gapp = gapp_from_quark(project);
774 RunTime *rt;
775 Canvas *canvas;
776 char fname[GR_MAXPATHLEN];
777 view v;
778 double vx, vy;
779 int truncated_out, res;
780 FILE *prstream;
782 if (!gapp) {
783 return;
786 rt = gapp->rt;
787 canvas = grace_get_canvas(gapp->grace);
789 if (get_ptofile(gapp)) {
790 if (string_is_empty(rt->print_file)) {
791 Device_entry *dev = get_device_props(canvas, rt->hdevice);
792 sprintf(rt->print_file, "%s.%s",
793 QIDSTR(project), dev->fext);
795 strcpy(fname, rt->print_file);
796 prstream = gapp_openw(gapp, fname);
797 } else {
798 strcpy(fname, "gappXXXXXX");
799 prstream = gapp_tmpfile(fname);
802 if (prstream == NULL) {
803 return;
806 canvas_set_prstream(canvas, prstream);
808 select_device(canvas, rt->hdevice);
810 res = gproject_render(gp);
812 gapp_close(prstream);
814 if (res != RETURN_SUCCESS) {
815 return;
818 get_bbox(canvas, BBOX_TYPE_GLOB, &v);
819 project_get_viewport(project, &vx, &vy);
820 if (v.xv1 < 0.0 - VP_EPSILON || v.xv2 > vx + VP_EPSILON ||
821 v.yv1 < 0.0 - VP_EPSILON || v.yv2 > vy + VP_EPSILON) {
822 truncated_out = TRUE;
823 } else {
824 truncated_out = FALSE;
827 if (get_ptofile(gapp) == FALSE) {
828 if (truncated_out == FALSE ||
829 yesno("Printout is truncated. Continue?", NULL, NULL, NULL)) {
830 gapp_print(gapp, fname);
831 #ifndef PRINT_CMD_UNLINKS
832 remove(fname);
833 #endif
835 } else {
836 if (truncated_out == TRUE) {
837 errmsg("Output is truncated - tune device dimensions");
842 int gui_is_page_free(const GUI *gui)
844 return gui->page_free;
847 void gui_set_page_free(GUI *gui, int onoff)
849 if (gui->page_free == onoff) {
850 return;
853 if (gui->inwin) {
854 errmsg("Can not change layout after initialization of GUI");
855 return;
856 } else {
857 gui->page_free = onoff;
861 void gui_set_barebones(GUI *gui)
863 gui->locbar = FALSE;
864 gui->toolbar = FALSE;
865 gui->statusbar = FALSE;