1 // -*- mode:C++ ; compile-command: "g++-3.4 -I. -I.. -g -c Xcas1.cc -Wall -DHAVE_CONFIG_H -DIN_GIAC" -*-
2 /*
3  *  Copyright (C) 2005,2014 B. Parisse, Institut Fourier, 38402 St Martin d'Heres
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 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22 #ifndef IN_GIAC
23 #include <giac/first.h>
24 #else
25 #include "first.h"
26 #endif
27 #include <string>
28 #ifdef HAVE_LIBFLTK
29 #include <FL/Fl.H>
30 #include <FL/fl_draw.H>
31 #include <FL/Fl_Window.H>
32 #include <FL/fl_ask.H>
33 #include <FL/Fl_File_Chooser.H>
34 #include <FL/Fl_Value_Input.H>
35 #include <FL/Fl_Counter.H>
36 #include <FL/Fl_Tile.H>
37 #include <FL/Fl_Hold_Browser.H>
38 #endif
39 #include "Xcas1.h"
40 #include "Input.h"
41 #include "Equation.h"
42 #include "Graph.h"
43 #include "Graph3d.h"
44 #include "Tableur.h"
45 #include "Editeur.h"
46 #include "Print.h"
47 #include <iostream>
48 #include <fstream>
49 #ifdef HAVE_SSTREAM
50 #include <sstream>
51 #else
52 #include <strstream>
53 #endif
54 #include <typeinfo>
55 #include <fcntl.h>
56 #include <stdio.h>
57 #include <string.h>
58 #include <dirent.h>
59 #include <sys/stat.h> // auto-recovery function
60 #ifdef HAVE_SYS_TIME_H
61 #include <sys/time.h>
62 #endif
63 #ifdef HAVE_LIBPTHREAD
64 #include <semaphore.h>
65 #endif
66 #ifdef HAVE_PTHREAD_H
67 #include <pthread.h>
68 #endif
69 #ifndef IN_GIAC
70 #include <giac/global.h>
71 #include <giac/misc.h>
72 #include <giac/gen.h>
73 #else
74 #include "global.h"
75 #include "misc.h"
76 #include "gen.h"
77 #endif
78 using namespace std;
79 using namespace giac;
80 
81 #ifndef NO_NAMESPACE_XCAS
82 namespace xcas {
83 #endif // ndef NO_NAMESPACE_XCAS
84 
85 #ifdef HAVE_LIBFLTK
86 
87   bool interrupt_button = true ;
88   bool geo_run=false;
89   bool sheet_run=false;
90   bool recovery_mode=false;
91   int fonts_available=1;
92 
93   void (*initialize_function)()=0;
94 
95   Xcas_config_type Xcas_config;
96   void (* menu2rpn_callback)(Fl_Widget *,void *)=0;
97   Enlargable_Multiline_Output *Xcas_help_output =0 ;
98   xcas::Graph2d *Xcas_DispG=0;
99   xcas::Equation * Xcas_PrintG=0;
100   int show_xcas_dispg=0,redraw_turtle=0;
101   std::string xcas_paused="";
102   int xcas_dispg_entries=0;
103   Fl_Window * Xcas_DispG_Window=0;
104   Fl_Window * Xcas_Main_Window=0;
105   Fl_Button *Xcas_DispG_Cancel=0;
106   Fl_Button *Xcas_Cancel=0;
107   bool file_save_context=true;
108 
xcas_gprintf(unsigned special,const std::string & format,const vecteur & v,GIAC_CONTEXT)109   void xcas_gprintf(unsigned special,const std::string & format,const vecteur & v,GIAC_CONTEXT){
110     if (Xcas_PrintG){
111       Fl::lock();
112       gen g=Xcas_PrintG->get_data();
113       Fl::unlock();
114       vecteur w=makevecteur(string2gen("",false));//makevecteur(string2gen("",false),string2gen("Step by step console",false));
115       if (g.type==_VECT) w=*g._VECTptr;
116       // add format,v at the end of w
117       int posnl=0;
118       unsigned i=0;
119       for (;posnl<int(format.size());){
120 	int nl=int(format.find('\n',posnl));
121 	string curs;
122 	bool finish = nl<0 || nl>=int(format.size());
123 	if (finish)
124 	  curs=format.substr(posnl,format.size()-posnl);
125 	else {
126 	  curs=format.substr(posnl,nl-posnl);
127 	  posnl=nl+1;
128 	}
129 	vecteur cur;
130 	for (;i<v.size() && !curs.empty();++i){
131 	  int p=int(curs.find("%gen"));
132 	  if (p<0 || p>=int(curs.size()))
133 	    break;
134 	  string cursp=curs.substr(0,p);
135 	  if (!cursp.empty())
136 	    cur.push_back(string2gen(cursp,false));
137 	  cur.push_back(v[i]);
138 	  curs=curs.substr(p+4,curs.size()-p-4);
139 	}
140 	if (!curs.empty())
141 	  cur.push_back(string2gen(curs,false));
142 	if (cur.empty())
143 	  continue;
144 	if (cur.size()==1)
145 	  w.push_back(cur.front());
146 	else
147 	  w.push_back(gen(cur,_SEQ__VECT));
148 	if (finish)
149 	  break;
150       }
151       g=gen(w,_HIST__VECT);
152       Fl::lock();
153       Xcas_PrintG->set_data(g);
154       Fl::unlock();
155     }
156   }
157 
158   // debugger variables
159   int xcas_debug_ok,xcas_current_instruction;
160   Fl_Double_Window * Xcas_Debug_Window=0;
161   Fl_Tile* Xcas_debug_tile =0;
162   Fl_Browser* Xcas_source_browser =0;
163   xcas::Multiline_Input_tab* Xcas_debug_input =0;
164   Fl_Output* Xcas_debug_messages =0;
165   Fl_Group* Xcas_debug_buttons =0;
166   Fl_Button* Xcas_sst_button=0,*Xcas_sst_in_button=0,*Xcas_cont_button=0,* Xcas_kill_button =0, * Xcas_break_button=0,* Xcas_rmbrk_button=0,*Xcas_watch_button =0,*Xcas_rmwatch_button=0;
167   Fl_Browser* Xcas_variable_browser=0;
cb_Xcas_source_browser(Fl_Browser *,void * user)168   static void cb_Xcas_source_browser(Fl_Browser*, void* user) {
169     std::string s("breakpoint(");
170     giac::context * contextptr =(giac::context *) user;
171     s += giac::debug_ptr(contextptr)->debug_prog_name->print();
172     s += ',';
173     s += giac::print_INT_(Xcas_source_browser->value());
174     s += ')';
175     Xcas_debug_input->value(s.c_str());
176     Xcas_debug_input->position(s.size()-1,s.size()-1);
177     Fl::focus(Xcas_debug_input);
178     Fl::flush();
179     usleep(200000);
180     Xcas_source_browser->value(xcas_current_instruction);
181   }
182 
cb_Xcas_debug_input(xcas::Multiline_Input_tab *,void *)183   static void cb_Xcas_debug_input(xcas::Multiline_Input_tab*, void*) {
184     xcas_debug_ok=true;
185   }
186 
cb_Xcas_sst_button(Fl_Button *,void *)187   static void cb_Xcas_sst_button(Fl_Button*, void*) {
188     Xcas_debug_input->value("sst");
189     xcas_debug_ok=true;
190   }
191 
cb_Xcas_sst_in_button(Fl_Button *,void *)192   static void cb_Xcas_sst_in_button(Fl_Button*, void*) {
193     Xcas_debug_input->value("sst_in");
194     xcas_debug_ok=true;
195   }
196 
cb_Xcas_cont_button(Fl_Button *,void *)197   static void cb_Xcas_cont_button(Fl_Button*, void*) {
198     Xcas_debug_input->value("cont");
199     xcas_debug_ok=true;
200   }
201 
cb_Xcas_kill_button(Fl_Button *,void *)202   static void cb_Xcas_kill_button(Fl_Button*, void*) {
203     Xcas_debug_input->value("kill");
204     Xcas_debug_input->position(4,4);
205     Fl::focus(Xcas_debug_input);
206   }
207 
cb_Xcas_break_button(Fl_Button *,void * user)208   static void cb_Xcas_break_button(Fl_Button*, void* user) {
209     string s("breakpoint(");
210     giac::context * contextptr =(giac::context *) user;
211     s += giac::debug_ptr(contextptr)->debug_prog_name->print();
212     s += ',';
213     s += giac::print_INT_(Xcas_source_browser->value());
214     s += ')';
215     Xcas_debug_input->value(s.c_str());
216     Xcas_debug_input->position(s.size()-1,s.size()-1);
217     Fl::focus(Xcas_debug_input);
218   }
219 
cb_Xcas_rmbrk_button(Fl_Button *,void * user)220   static void cb_Xcas_rmbrk_button(Fl_Button*, void* user) {
221     string s("rmbreakpoint(");
222     giac::context * contextptr =(giac::context *) user;
223     s += giac::debug_ptr(contextptr)->debug_prog_name->print();
224     s += ',';
225     s += giac::print_INT_(Xcas_source_browser->value());
226     s += ')';
227     Xcas_debug_input->value(s.c_str());
228     Xcas_debug_input->position(s.size()-1,s.size()-1);
229     Fl::focus(Xcas_debug_input);
230   }
231 
cb_Xcas_watch_button(Fl_Button *,void *)232   static void cb_Xcas_watch_button(Fl_Button*, void*) {
233     Xcas_debug_input->value("watch(");
234     Xcas_debug_input->position(6,6);
235     Fl::focus(Xcas_debug_input);
236   }
237 
cb_Xcas_rmwatch_button(Fl_Button *,void *)238   static void cb_Xcas_rmwatch_button(Fl_Button*, void*) {
239     Xcas_debug_input->value("rmwatch(");
240     Xcas_debug_input->position(8,8);
241     Fl::focus(Xcas_debug_input);
242   }
243 
cb_Xcas_variable_browser(Fl_Browser *,void *)244   static void cb_Xcas_variable_browser(Fl_Browser*, void*) {
245     Xcas_debug_input->insert(Xcas_variable_browser->text(Xcas_variable_browser->value()));
246   }
247 
248   // end debugger callbacks and defs
249 
250   int autosave_time = 60 ; /* default save session every 60 seconds */
251   bool autosave_disabled = false;
252 
find_fold_autosave_function(bool warn_user)253   bool find_fold_autosave_function(bool warn_user){
254     History_Fold * hf=0;
255     Fl_Widget * w = xcas::Xcas_input_focus;
256     for (;w;){
257       if ( (hf=dynamic_cast<History_Fold *>(w)) )
258 	break;
259       w=w->parent();
260     }
261     if (hf)
262       return hf->autosave(warn_user);
263     else
264       return false;
265   }
266 
267   bool (*autosave_function)(bool) = find_fold_autosave_function ;
268   void (*idle_function)()=0;
269 
270   unsigned max_debug_printsize=1000;
Xcas_debugguer(int status,giac::context * contextptr)271   void Xcas_debugguer(int status,giac::context * contextptr){
272     if (debug_ptr(contextptr) && debug_ptr(contextptr)->debug_contextptr)
273       contextptr = debug_ptr(contextptr)->debug_contextptr;
274     // 2 -> debug, 3 ->wait click
275     if (status==3){
276       vecteur v(gen2vecteur(Xcas_DispG?Xcas_DispG->waiting_click_value:undef));
277       if (v.empty()){
278 	if (Xcas_DispG_Window) Xcas_DispG_Window->show();
279 	if (Xcas_DispG_Cancel) Xcas_DispG_Cancel->show();
280 	if (Xcas_DispG) Xcas_DispG->waiting_click=true;
281 	for (;;) {
282 	  Fl::wait();
283 	  if (Xcas_DispG_Window && !Xcas_DispG_Window->visible()){
284 	    Xcas_DispG->waiting_click=false;
285 	    Xcas_DispG->waiting_click_value=undef;
286 	  }
287 	  bool wait=Xcas_DispG?Xcas_DispG->waiting_click:false;
288 	  if (!wait)
289 	    break;
290 	}
291 	if (Xcas_DispG) Xcas_DispG->waiting_click=false;
292 	if (Xcas_DispG_Cancel) Xcas_DispG_Cancel->hide();
293 	thread_eval_status(1,contextptr);
294 	return;
295       }
296       v=inputform_pre_analysis(v,contextptr);
297       gen res=makeform(v,contextptr);
298       if (Xcas_DispG)
299 	Xcas_DispG->waiting_click_value=inputform_post_analysis(v,res,contextptr);
300       thread_eval_status(1,contextptr);
301       return;
302     }
303     if (status==2){
304       if (!Xcas_Debug_Window){
305 	int dx,dy;
306 	if (xcas::Xcas_input_focus && xcas::Xcas_input_focus->window()){
307 	  dx=8*(2*xcas::Xcas_input_focus->window()->w()/24);
308 	  dy=11*(2*xcas::Xcas_input_focus->window()->h()/30);
309 	}
310 	else {
311 	  dx=400;
312 	  dy=500;
313 	}
314 	Fl_Double_Window* o = Xcas_Debug_Window = new Fl_Double_Window(dx,dy, gettext("Xcas Debug Window"));
315 	{
316 	  Fl_Tile* o = Xcas_debug_tile = new Fl_Tile(0, 0, dx,dy);
317 	  o->box(FL_FLAT_BOX);
318 	  { Fl_Browser* o = Xcas_source_browser = new Fl_Browser(0,0,dx,4*dy/11);
319 	  o->tooltip(gettext("Show the source of the program"));
320 	  o->type(2);
321 	  o->callback((Fl_Callback*)cb_Xcas_source_browser);
322 	  }
323 	  { xcas::Multiline_Input_tab* o = Xcas_debug_input = new xcas::Multiline_Input_tab(dx/8,4*dy/11, dx-dx/8,dy/11, gettext("eval"));
324 	  o->type(4);
325 	  o->box(FL_DOWN_BOX);
326 	  o->color(FL_BACKGROUND2_COLOR);
327 	  o->selection_color(FL_SELECTION_COLOR);
328 	  o->labeltype(FL_NORMAL_LABEL);
329 	  o->labelfont(0);
330 	  o->labelsize(14);
331 	  o->labelcolor(FL_FOREGROUND_COLOR);
332 	  o->textcolor(1);
333 	  o->callback((Fl_Callback*)cb_Xcas_debug_input);
334 	  o->align(FL_ALIGN_LEFT);
335 	  o->when(FL_WHEN_ENTER_KEY);
336 	  }
337 	  { Fl_Output* o = Xcas_debug_messages = new Fl_Output(0,5*dy/11,dx,2*dy/11);
338 	  o->type(12);
339 	  o->labelcolor(FL_FOREGROUND_COLOR);
340 	  o->textcolor(4);
341 	  }
342 	  { Fl_Group* o = Xcas_debug_buttons = new Fl_Group(0,7*dy/11,dx,dy/11);
343 	  { Fl_Button* o = Xcas_sst_button = new Fl_Button(0,7*dy/11,dx/8,dy/11, gettext("sst F5"));
344 	  o->tooltip(gettext("Execute current line, skip function"));
345 	  o->callback((Fl_Callback*)cb_Xcas_sst_button);
346 	  o->shortcut(0xffc2);
347 	  }
348 	  { Fl_Button* o = Xcas_sst_in_button = new Fl_Button(dx/8,7*dy/11, dx/8, dy/11, gettext("in F6"));
349 	  o->tooltip(gettext("Execute current line, step in function"));
350 	  o->callback((Fl_Callback*)cb_Xcas_sst_in_button);
351 	  o->shortcut(0xffc3);
352 	  }
353 	  { Fl_Button* o = Xcas_cont_button = new Fl_Button(2*dx/8,7*dy/11, dx/8, dy/11, gettext("cont F7"));
354 	  o->tooltip(gettext("Continue execution until next breakpoint"));
355 	  o->callback((Fl_Callback*)cb_Xcas_cont_button);
356 	  o->shortcut(0xffc4);
357 	  }
358 	  { Fl_Button* o = Xcas_kill_button = new Fl_Button(3*dx/8,7*dy/11, dx/8, dy/11, gettext("kill"));
359 	  o->tooltip(gettext("Kill current program"));
360 	  o->callback((Fl_Callback*)cb_Xcas_kill_button);
361 	  }
362 	  { Fl_Button* o = Xcas_break_button = new Fl_Button(4*dx/8,7*dy/11, dx/8, dy/11, gettext("break"));
363 	  o->tooltip(gettext("Add a breakpoint"));
364 	  o->callback((Fl_Callback*)cb_Xcas_break_button);
365 	  }
366 	  { Fl_Button* o = Xcas_rmbrk_button = new Fl_Button(5*dx/8,7*dy/11, dx/8, dy/11, gettext("rmbrk"));
367 	  o->tooltip(gettext("Remove a breakpoint"));
368 	  o->callback((Fl_Callback*)cb_Xcas_rmbrk_button);
369 	  }
370 	  { Fl_Button* o = Xcas_watch_button = new Fl_Button(6*dx/8,7*dy/11,dx/8, dy/11, gettext("watch"));
371 	  o->tooltip(gettext("Add a variable name to the watch"));
372 	  o->callback((Fl_Callback*)cb_Xcas_watch_button);
373 	  }
374 	  { Fl_Button* o = Xcas_rmwatch_button = new Fl_Button(7*dx/8,7*dy/11,dx/8, dy/11, gettext("rmwtch"));
375 	  o->tooltip(gettext("Add a variable name to the watch"));
376 	  o->callback((Fl_Callback*)cb_Xcas_rmwatch_button);
377 	  }
378 	  o->end();
379 	  }
380 	  { Fl_Browser* o = Xcas_variable_browser = new Fl_Browser(0,8*dy/11,dx,3*dy/11);
381 	  o->tooltip(gettext("Show watch variables"));
382 	  o->type(2);
383 	  o->callback((Fl_Callback*)cb_Xcas_variable_browser);
384 	  }
385 	  o->end();
386 	}
387 	o->end();
388 	o->resizable(o);
389       }
390       Xcas_source_browser->user_data( (void *) contextptr);
391       Xcas_break_button->user_data( (void *) contextptr);
392       Xcas_rmbrk_button->user_data( (void *) contextptr);
393       if (Xcas_Main_Window && Xcas_Debug_Window)
394 	change_group_fontsize(Xcas_Debug_Window,Xcas_Main_Window->labelsize());
395       if (Xcas_Debug_Window){
396 	Xcas_Debug_Window->show();
397 	// Xcas_Debug_Window->set_modal();
398 	Xcas_debug_input->when(FL_WHEN_ENTER_KEY|FL_WHEN_NOT_CHANGED);
399       }
400       // Debugging mode
401       // cerr << "Debugging" << '\n';
402       debug_struct * dbgptr=debug_ptr(contextptr);
403       if (dbgptr){
404 	if (dbgptr->debug_info_ptr && dbgptr->debug_info_ptr->type==_VECT){
405 	  vecteur & w =*dbgptr->debug_info_ptr->_VECTptr;
406 	  // w[0]=function, args,
407 	  // w[1]=breakpoints
408 	  // w[2] = instruction evaled or program source
409 	  // w[3]= evaluation result
410 	  // w[4]= current instruction number
411 	  // w[5] = watch vector, w[6] = watch values
412 	  string msg;
413 	  msg += "eval("+w[2].print(contextptr) + ")= " + w[3].print(contextptr) + '\n';
414 	  msg += "Stopped in ";
415 	  if ( w[0].type==_VECT && !w[0]._VECTptr->empty() ){
416 	    if (!dbgptr->debug_prog_name)
417 	      dbgptr->debug_prog_name=new gen;
418 	    if (*dbgptr->debug_prog_name!=w[0]._VECTptr->front()){
419 	      gen prog=w[0]._VECTptr->front();
420 	      *dbgptr->debug_prog_name=prog;
421 	      if (prog.type==_IDNT)
422 		prog=prog.eval(1,contextptr);
423 	      if (prog.is_symb_of_sommet(at_program) && prog.type==_VECT && prog._VECTptr->size()==3 )
424 		prog=prog._VECTptr->back();
425 	      if (prog.type==_SYMB)
426 		prog=prog._SYMBptr->feuille;
427 	      if (prog.type==_VECT && prog._VECTptr->size()==3)
428 		prog=prog._VECTptr->back();
429 	      w[2]=prog;
430 	      Xcas_source_browser->clear();
431 	      vector<string> vs;
432 	      if (w[2].type==_VECT)
433 		debug_print(*w[2]._VECTptr,vs,contextptr);
434 	      else
435 		debug_print(w[2],vs,contextptr);
436 	      vector<string>::iterator it=vs.begin(),itend=vs.end();
437 	      for (;it!=itend;++it){
438 		string & cur=*it; // replace \n by space
439 		for (;;){
440 		  size_t pos=cur.find('\n');
441 		  if (pos<0 || pos>=cur.size())
442 		    break;
443 		  cur[pos]=' ';
444 		}
445 		Xcas_source_browser->add(cur.c_str());
446 	      }
447 	    }
448 	    msg += dbgptr->debug_prog_name->print(contextptr) + "(" + gen(vecteur(w[0]._VECTptr->begin()+1,w[0]._VECTptr->end()),_SEQ__VECT).print(contextptr)+")";
449 	    msg += ",\nbreakpoint "+w[1].print(contextptr);
450 	  } // if (w[0].type==_VECT)
451 	  else
452 	    msg += w[0].print(contextptr);
453 	  msg += ",\nline " + w[4].print(contextptr);
454 	  Xcas_debug_messages->value(msg.c_str());
455 	  gen w_in(w[5]),w_out(w[6]);
456 	  if ( (w_in.type==_VECT) && (w_out.type==_VECT) ){
457 	    int pos=max(Xcas_variable_browser->value(),1);
458 	    Xcas_variable_browser->clear();
459 	    const_iterateur it=w_in._VECTptr->begin(),itend=w_in._VECTptr->end();
460 	    const_iterateur jt=w_out._VECTptr->begin(),jtend=w_out._VECTptr->end();
461 	    for (;(it!=itend)&&(jt!=jtend);++it,++jt){
462 	      string tmps = replace(jt->print(contextptr),'\n',' ');
463 	      if (tmps.size()>max_debug_printsize)
464 		tmps=tmps.substr(0,max_debug_printsize);
465 	      Xcas_variable_browser->add( (it->print(contextptr) + " := " + tmps ).c_str());
466 	    }
467 	    Xcas_variable_browser->value(giacmin(pos,w_in._VECTptr->size()));
468 	  }
469 	  xcas_current_instruction=w[4].val;
470 	  Xcas_source_browser->value(w[4].val);
471 	  /* console mode debugging
472 	     if (dbgptr->debug_refresh){
473 	     if (dbgptr->fast_debug_info_ptr)
474 	     cerr << *dbgptr->fast_debug_info_ptr << '\n';
475 	     }
476 	     else {
477 	     if (dbgptr->debug_info_ptr)
478 	     cerr << *dbgptr->debug_info_ptr << '\n';
479 	       }
480 	  */
481 	}
482       }
483       gen g(at_sst);
484       gen nxt;
485       xcas_debug_ok=false;
486       while (Xcas_Debug_Window->shown() && !xcas_debug_ok){
487 	Fl::wait();
488 	int cs=context_list().size(),ci=0,status=0;
489 	for (;ci<cs;++ci){
490 	  status=check_thread(context_list()[ci]);
491 	}
492       }
493       /* console mode debugging
494 	 char buf[10000];
495 	 cin.getline(buf,10000-1,'\n');
496 	 if (buf[0]){
497 	 try {
498 	 g=gen(buf);
499 	 }
500 	 catch (std::runtime_error & err){
501 	 cerr << err.what();
502 	 }
503 	 }
504       */
505       if (!Xcas_Debug_Window->visible())
506 	g=at_kill;
507       else {
508 	string buf(Xcas_debug_input->value());
509 	if (!buf.empty()){
510 	  try {
511 	    g=gen(buf,contextptr);
512 	  }
513 	  catch (std::runtime_error & err){
514 	    cerr << err.what();
515 	  }
516 	}
517       }
518       *dbgptr->fast_debug_info_ptr = g;
519       thread_eval_status(1,contextptr);
520     }
521   }
522 
Xcas_idle_function(void * dontcheck)523   void Xcas_idle_function(void * dontcheck){
524     static int initialized=-1;
525     static time_t last_save=time(0);
526     int cs=context_list().size(),ci=0,status=0;
527     context * cptr=0;
528     for (;ci<cs;++ci){
529       cptr=context_list()[ci];
530       if (!dontcheck || (void *)cptr!=dontcheck)
531 	status=check_thread(cptr);
532       if (status>1)
533 	break;
534     }
535     if (ci<cs){
536       context * contextptr=context_list()[ci];
537       Xcas_debugguer(status,contextptr);
538     }
539     if (Xcas_Debug_Window) {
540       if (status<2 && Xcas_Debug_Window->shown()){
541 	if (cptr){
542 	  usleep(100000);
543 	  status=check_thread(cptr);
544 	}
545 	if (status<2)
546 	  Xcas_Debug_Window->hide();
547 	else
548 	  return; // moved from below otherwise after debugging STOP button is always on
549       }
550     }
551     bool lock=true; // added because DispG freeze in linux
552     if (Xcas_DispG_Window){
553       if (show_xcas_dispg){
554 	lock=false;
555 	if (show_xcas_dispg & 2){
556 	  if (!Xcas_DispG_Window->visible())
557 	    Xcas_DispG->autoscale();
558 	  if (show_xcas_dispg & 1){
559 	    Xcas_DispG_Window->show();
560 	    Xcas_DispG_Window->iconize();
561 	  }
562 	  else
563 	    Xcas_DispG_Window->show();
564 	}
565 	else
566 	  Xcas_DispG_Window->hide();
567 	show_xcas_dispg=0;
568       }
569     }
570     if (xcas_paused!=""){
571       Xcas_Main_Window->redraw();
572       if (Xcas_DispG) Xcas_DispG->redraw();
573       fl_message("%s",xcas_paused.c_str());
574       xcas_paused="";
575     }
576     ++initialized;
577     if (initialized % 5){
578       if (lock) Fl::unlock();
579 #ifdef WIN32
580       usleep(10000);
581 #else
582       usleep(1000);
583 #endif
584       if (lock) Fl::lock();
585       return;
586     }
587     if (!initialized && initialize_function)
588       initialize_function();
589     if (
590 #ifdef WIN32
591 	!(initialized%20) &&
592 #endif
593 	idle_function
594 	)
595       idle_function();
596     /* autosave */
597     time_t current=time(0);
598     if (!autosave_disabled && autosave_function && double(current-last_save)>autosave_time){
599       if (autosave_function(false))
600 	last_save=current;
601       else
602 	last_save=current-autosave_time/2;
603     }
604     struct timeval cur;
605     struct timezone tz;
606     gettimeofday(&cur,&tz);
607     vector<Graph2d3d *>::const_iterator it=animations.begin(),itend=animations.end();
608     for (;it!=itend;++it){
609       Graph2d3d * gr = *it;
610       if (!gr->paused && gr->animation_dt>0){
611         double dt=cur.tv_sec-gr->animation_last.tv_sec+double(cur.tv_usec-gr->animation_last.tv_usec)/1e6;
612 	if (dt>gr->animation_dt){
613 	  gr->redraw();
614 	}
615       }
616     }
617   }
618 
operator <(const time_string & ts1,const time_string & ts2)619   bool operator < (const time_string & ts1,const time_string & ts2){
620     if (ts1.t!=ts2.t)
621       return ts1.t < ts2.t;
622     else
623       return ts1.s < ts2.s;
624   }
625 
626   // Check for auto-recovery data in directory s
has_autorecover_data(const string & s_orig,vector<time_string> & newest)627   bool has_autorecover_data(const string & s_orig,vector<time_string> & newest){
628     DIR *dp;
629     struct dirent *ep;
630     string s=s_orig;
631     if (!s.empty() && s[s.size()-1]!='/')
632       s += '/';
633     else
634       s += '/';
635     dp = opendir (s.c_str());
636     if (dp != NULL){
637       while ( (ep = readdir (dp)) ){
638 	string cur(ep->d_name);
639 	if (cur.size()>13&& cur.substr(0,10)=="xcas_auto_"){
640 	  string curext=cur.substr(cur.size()-3,3);
641 	  if (curext=="xws"){
642 	    struct stat st;
643 	    if (!stat((s+ep->d_name).c_str(),&st)){
644 	      time_t & t=st.st_mtime; // last modif
645 	      newest.push_back(time_string(t,s+cur));
646 	    }
647 	  }
648 	}
649       }
650       (void) closedir (dp);
651       if (newest.size()==0) // no file
652 	return false;
653       return true;
654     }
655     else
656       return false; // Couldn't open the directory
657   }
658 
stream_copy(std::istream & in,std::ostream & out)659   bool stream_copy(std::istream & in,std::ostream & out) {
660     char c;
661     while (in){
662       if (!out){
663 	return false;
664       }
665       c=in.get();
666       if (in.eof()){
667 	return true;
668       }
669       out << c ;
670     }
671     return false;
672   }
673 
674 
add_user_menu(Fl_Menu_ * m,const string & s,const string & doc_prefix,Fl_Callback * cb)675   bool add_user_menu(Fl_Menu_ *m,const string & s, const string & doc_prefix,Fl_Callback * cb){
676     string menufile;
677     bool menufile_found=false;
678     if (is_file_available(s.c_str())){
679       menufile_found=true;
680       menufile=s;
681     }
682     else {
683       if (getenv("XCAS_HELP")){
684 	menufile=getenv("XCAS_HELP");
685 	int ms=menufile.size();
686 	for (--ms;ms>0;--ms){
687 	  if (menufile[ms]=='/')
688 	    break;
689 	}
690 	if (ms)
691 	  menufile=menufile.substr(0,ms+1)+doc_prefix+s;
692       }
693       else
694 	menufile=giac::giac_aide_dir()+doc_prefix+s;
695       menufile_found=is_file_available(menufile.c_str());
696     }
697     if (!menufile_found){
698       cerr << "// Unable to open menu file "<< menufile << '\n';
699       return false;
700     }
701     cerr << "// Using menu file " << menufile << '\n';
702     // Now reading commandnames from file for menus
703     // Syntax is menu/submenu/item, callback inserts item name
704     ifstream in(menufile.c_str());
705     string lu;
706     int BUFSIZE=10000,endpos;
707     char buf[BUFSIZE+1];
708     while (in){
709       in.getline(buf,BUFSIZE,'\n');
710       endpos=strlen(buf);
711       if (endpos){
712 	if (buf[endpos-1]=='\n')
713 	  buf[endpos-1]=0;
714 	lu=buf;
715       }
716       if (in.eof())
717 	break;
718       m->add(lu.c_str(),0,cb);
719     }
720     return true;
721   }
722 
nextfl_menu(Fl_Menu_Item * & m)723   void nextfl_menu(Fl_Menu_Item * & m){
724     if (m->submenu()){
725       ++m;
726       for (;m->text;)
727 	nextfl_menu(m);
728     }
729     ++m;
730   }
731 
copy_menu(Fl_Menu_ * menu,const string & prefix,Fl_Menu_Item * & m)732   void copy_menu(Fl_Menu_ * menu,const string & prefix,Fl_Menu_Item * & m){
733     for (;m->text;++m){
734       if (m->submenu()){
735 	string prefix2=prefix+m->label();
736 	prefix2 += '/';
737 	++m;
738 	copy_menu(menu,prefix2,m);
739       }
740       else
741 	menu->add((prefix+m->text).c_str(),m->shortcut_,m->callback_,m->user_data_,m->flags);
742     }
743   }
744 
fl_menu2rpn_menu(Fl_Menu_Item * & m)745   vecteur fl_menu2rpn_menu(Fl_Menu_Item * & m){
746     vecteur res;
747     ++m;
748     for (;m->text;++m){
749       if (m->submenu()){
750 	string s=m->label();
751 	vecteur tmp(fl_menu2rpn_menu(m));
752 	if (!tmp.empty())
753 	  res.push_back(makevecteur(string2gen(s,false),tmp));
754       }
755       else {
756 	gen g;
757 	if (m->callback_==menu2rpn_callback){
758 	  find_or_make_symbol(m->label(),g,0,false,context0);
759 	  // g=gen(string("'")+m->label()+string("'"));
760 	  if (g.is_symb_of_sommet(at_quote))
761 	    g=g._SYMBptr->feuille;
762 	  res.push_back(g);
763 	}
764       }
765     }
766     return res;
767   }
768 
change_menu_fontsize(Fl_Menu * & m,int labelfontsize)769   void change_menu_fontsize(Fl_Menu * & m,int labelfontsize){
770     if (!m->text)
771       return;
772     m->labelsize(labelfontsize);
773     if (m->submenu()){
774       ++m;
775       for (;m->text;++m)
776 	change_menu_fontsize(m,labelfontsize);
777     }
778   }
779 
change_menu_fontsize(Fl_Menu_Item * m,int n,int labelfontsize)780   void change_menu_fontsize(Fl_Menu_Item * m,int n,int labelfontsize){
781     for (int i=0;i<n;++i){
782       change_menu_fontsize(m,labelfontsize);
783       ++m;
784     }
785   }
786 
change_equation_fontsize(Equation * eq,int labelfontsize)787   void change_equation_fontsize(Equation * eq,int labelfontsize){
788     if (eq->attr.fontsize==labelfontsize)
789       return;
790     eq->attr.fontsize=labelfontsize;
791     eq->labelsize(labelfontsize);
792     eq->resize(eq->x(),eq->y(),eq->w(),eq->h());
793     eq->set_data(eq->get_data());
794     eq->adjust_widget_size();
795   }
796 
change_group_fontsize(Fl_Widget * w,int labelfontsize)797   void change_group_fontsize(Fl_Widget * w,int labelfontsize){
798     w->labelsize(labelfontsize);
799     w->redraw();
800     if (Equation * eq=dynamic_cast<Equation *>(w)){
801       change_equation_fontsize(eq,labelfontsize);
802       return;
803     }
804     Fl_Group * g = dynamic_cast<Fl_Group *>(w);
805     if (!g)
806       return;
807     Fl_Font police=g->labelfont();
808     Fl_Widget * o;
809     Fl_Widget * const * ptr= g->array();
810     int n=g->children();
811     for (int i=0;i<n;++ptr,++i){
812       o = *ptr;
813       if (o->labelfont()!=FL_SYMBOL)
814 	o->labelfont(police);
815       if (Fl_Menu * m=dynamic_cast<Fl_Menu *>(o)){
816 	change_menu_fontsize(m,labelfontsize);
817 	continue;
818       }
819       if (Fl_Menu_ * mb=dynamic_cast<Fl_Menu_ *>(o)){
820 	Fl_Menu_Item * mm=(Fl_Menu_Item *)mb->menu();
821 	for (;mm->text;++mm)
822 	  change_menu_fontsize(mm,labelfontsize);
823       }
824       if (Fl_Input_ * in=dynamic_cast<Fl_Input_ * >(o)){
825 	in->textsize(labelfontsize);
826 	in->textfont(police);
827       }
828       if (Equation * eq=dynamic_cast<Equation * >(o)){
829 	eq->labelfont(police);
830 	change_equation_fontsize(eq,labelfontsize);
831       }
832       if (Xcas_Text_Editor * xed=dynamic_cast<Xcas_Text_Editor *>(o)){
833 	xed->Fl_Text_Display::textsize(labelfontsize);
834 	xed->Fl_Text_Display::textfont(labelfontsize);
835 	vector<Fl_Text_Display::Style_Table_Entry> & v=xed->styletable;
836 	for (unsigned i=0;i<v.size();++i)
837 	  v[i].size=labelfontsize;
838 	xed->labelsize(labelfontsize);
839 	Editeur * ed =dynamic_cast<Editeur *>(xed->parent());
840 	if (!ed && !xed->tableur)
841 	  xed->resize_nl_before(1);
842 	xed->redraw();
843       }
844       if (Fl_Value_Input * v=dynamic_cast<Fl_Value_Input *>(o))
845 	v->textsize(labelfontsize);
846       if (Fl_Browser * b=dynamic_cast<Fl_Browser *>(o))
847 	b->textsize(labelfontsize);
848       if (Fl_Counter * cc=dynamic_cast<Fl_Counter *>(o))
849 	cc->textsize(labelfontsize);
850       if (Fl_Group * og=dynamic_cast<Fl_Group *>(o))
851 	change_group_fontsize(og,labelfontsize);
852       if (HScroll * os =dynamic_cast<HScroll *>(o)){
853 	// resize childs according to new scrollbar width
854 	os->resize(os->x(),os->y(),os->w(),os->h());
855       }
856       if (Flv_Table_Gen * fl = dynamic_cast<Flv_Table_Gen *>(o)){
857 	fl->global_style.font_size(labelfontsize);
858 	fl->global_style.height(labelfontsize+4);
859 	fl->redraw();
860       }
861       o->labelsize(labelfontsize);
862       if (Graph2d3d * gr=dynamic_cast<Graph2d3d *>(o))
863 	if (gr->parent() && !dynamic_cast<Figure *>(gr->parent()) && !dynamic_cast<Tableur_Group *>(gr->parent()))
864 	  gr->resize_mouse_param_group(gr->legende_size);
865     }
866   }
867 
868   // Cut a string for display in a multiline area of size w using fontsize
cut_help(const string & s,int fontsize,int w)869   string cut_help(const string & s,int fontsize,int w){
870     if (s.empty())
871       return s;
872     int sw;
873     // find 1st cut and add cut_help of the rest of the string to it
874     int begin=0,taille=s.size(),end; // cut is between begin and first \n
875     for (end=0;end<taille;++end){
876       if (s[end]=='\n')
877 	break;
878     }
879     fl_font(FL_HELVETICA,fontsize);
880     sw=int(fl_width(s.substr(0,end).c_str()));
881     if (sw<w){
882       if (end==taille)
883 	return s;
884       return s.substr(0,end+1)+cut_help(s.substr(end+1,taille-end-1),fontsize,w);
885     }
886     for (;end-begin>2;){
887       int pos=(begin+end)/2,i; // find 1st space after middlepoint
888       for (i=pos;i>begin;--i){
889 	if (s[i]==' ')
890 	  break;
891       }
892       if (i==begin){ // no space before, try after
893 	for (i=pos;i<end;++i){
894 	  if (s[i]==' ')
895 	    break;
896 	}
897       }
898       if (i==end){
899 	// No space at all, return 0 -> begin or 0 -> end if begin==0
900 	if (begin)
901 	  pos=begin;
902 	else {
903 	  if (end)
904 	    pos=end;
905 	  else
906 	    return s;
907 	}
908 	if (pos==taille)
909 	  return s;
910 	return s.substr(0,pos)+'\n'+cut_help(s.substr(pos,taille-pos),fontsize,w);
911       }
912       sw=int(fl_width(s.substr(0,i).c_str()));
913       if (sw<w)
914 	begin=i;
915       else
916 	end=i;
917     }
918     return s.substr(0,end+1)+cut_help(s.substr(end+1,taille-end-1),fontsize,w);
919   }
920 
help_output(const std::string & s,int language)921   void help_output(const std::string & s,int language){
922     giac::aide cur_aide=helpon(s,(*giac::vector_aide_ptr()),language,(*giac::vector_aide_ptr()).size(),false);
923     if (Xcas_help_output){
924       string result=cur_aide.cmd_name;
925       if (!cur_aide.syntax.empty())
926 	result=result+"("+cur_aide.syntax+")\n";
927       vector<localized_string>::const_iterator it=cur_aide.blabla.begin(),itend=cur_aide.blabla.end();
928       for (;it!=itend;++it){
929 	if (it->language==language){
930 	  result = it->chaine +'\n'+result ;
931 	  break;
932 	}
933       }
934       std::vector<std::string>::const_iterator jt=cur_aide.examples.begin(),jtend=cur_aide.examples.end();
935       for (;jt!=jtend;++jt)
936 	result = result + *jt + '\n';
937       std::vector<indexed_string>::const_iterator kt=cur_aide.related.begin(),ktend=cur_aide.related.end();
938       for (;kt!=ktend;++kt)
939 	result = result + "-> "+print_INT_(kt->index)+": "+kt->chaine+'\n';
940       Xcas_help_output->value(result.c_str());
941       Xcas_help_output->redraw();
942     }
943   }
944 
browser_help(const giac::gen & g,int language)945   void browser_help(const giac::gen & g,int language){
946     giac::gen f(g);
947     string s;
948     if (f.type==giac::_SYMB)
949       f=f._SYMBptr->sommet;
950     if (f.type==giac::_FUNC)
951       s=f._FUNCptr->ptr()->s;
952     giac::html_vtt=giac::html_help(giac::html_mtt,s);
953     help_output(s,language);
954     if (!giac::html_vtt.empty()){
955       if (use_external_browser)
956 	giac::system_browser_command(giac::html_vtt.front());
957       else {
958 	if (xcas::Xcas_help_window){
959 	  xcas::Xcas_help_window->load(giac::html_vtt.front().c_str());
960 	  if (!xcas::Xcas_help_window->visible())
961 	    xcas::Xcas_help_window->show();
962 	}
963       }
964     }
965   }
966 
check_browser_help(const giac::gen & g,int language)967   void check_browser_help(const giac::gen & g,int language){
968     if (g.is_symb_of_sommet(giac::at_findhelp))
969       browser_help(g._SYMBptr->feuille,language);
970   }
971 
dbgprint(const gen & g,vector<string> & vs)972   void dbgprint(const gen & g,vector<string> & vs){
973     vecteur v(gen2vecteur(g));
974     const_iterateur it=v.begin(),itend=v.end();
975     for (;it!=itend;++it)
976       vs.push_back(it->print(context0));
977   }
978 
fl_wait_0001(context * contextptr)979   void fl_wait_0001(context * contextptr){
980     static int t_sec=0,t_usec=0;
981     int status=thread_eval_status(contextptr);
982     Xcas_debugguer(status,contextptr);
983     // don't flush too often
984     timeval tp;
985     if (!gettimeofday(&tp,0)){
986       if (tp.tv_sec==t_sec && tp.tv_usec < t_usec+50000){
987 	usleep(100);
988 	return;
989       }
990       t_sec=tp.tv_sec;
991       t_usec=tp.tv_usec ;
992     }
993 #ifdef __APPLE__
994     Fl::wait(0.001);
995 #else
996     Fl::wait(0.0001);
997 #endif
998     Xcas_idle_function(0);
999   }
1000 
thread_eval(const giac::gen & g,int level,context * contextptr)1001   giac::gen thread_eval(const giac::gen & g,int level,context * contextptr){
1002     // Remove idle function for wait to work
1003     // cerr << "remove idle " << '\n';
1004     Fl::remove_idle(xcas::Xcas_idle_function,0);
1005     gen res=giac::thread_eval(g,level,contextptr,fl_wait_0001);
1006     if (Xcas_Debug_Window) Xcas_Debug_Window->hide();
1007     fl_wait_0001(contextptr);
1008     // Re-add idle function
1009     Fl::add_idle(xcas::Xcas_idle_function,0);
1010     return res;
1011   }
1012 
in_Xcas_eval(Fl_Widget * w,const giac::gen & evaled_g0,int pretty_output,GIAC_CONTEXT)1013   Fl_Widget * in_Xcas_eval(Fl_Widget * w,const giac::gen & evaled_g0,int pretty_output,GIAC_CONTEXT){
1014     gen evaled_g=evaled_g0;
1015     if (evaled_g.type==_MAP && evaled_g.subtype==1)
1016       evaled_g=giac::maptoarray(*evaled_g._MAPptr,contextptr);
1017     // cleanup
1018     giac::clear_prog_status(contextptr);
1019     giac::cleanup_context(contextptr);
1020     History_Pack * hp = get_history_pack(w);
1021     if (hp) Fl_Group::current(hp);
1022     if (calc_mode(contextptr)==-38)
1023       calc_mode(contextptr)=38;
1024     /*    FIXME: use simplifier only if the user ask for it
1025 	  if (!g.is_symb_of_sommet(at_ifactor))
1026 	  evaled_g=giac::_simplifier(evaled_g,contextptr); */
1027     if (pretty_output){
1028       int anim=giac::animations(evaled_g);
1029       if (evaled_g.is_symb_of_sommet(at_parameter) && evaled_g._SYMBptr->feuille.type==_VECT){
1030 	Gen_Value_Slider * res = parameter2slider(evaled_g,contextptr);
1031 	if (res){
1032 	  res->resize(w->x()+3*w->labelsize(),w->y(),w->w()-3*w->labelsize(),w->labelsize());
1033 	  res->labelsize(w->labelsize());
1034 	  return res;
1035 	}
1036       }
1037       int tt;
1038       if (evaled_g.type == _VECT && (tt=graph_output_type(evaled_g)) ){
1039 	if (tt==4){
1040 	  // Fl_Tile * g = new Fl_Tile(w->x(),w->y(),w->w(),max(130,w->w()/3));
1041 	  // g->labelsize(w->labelsize());
1042 	  Turtle * tu = new Turtle(w->x(),w->y(),w->w(),max(130,w->w()/3));
1043 	  tu->turtleptr=&turtle_stack(contextptr);
1044 	  //g->end();
1045 	  //change_group_fontsize(g,w->labelsize());
1046 	  //return g;
1047 	  return tu;
1048 	}
1049 	Fl_Tile * g = new Fl_Tile(w->x(),w->y(),w->w(),max(130,w->w()/3));
1050 	g->labelsize(w->labelsize());
1051 	Graph2d3d * tmp;
1052 #ifdef HAVE_LIBFLTK_GL
1053 	if (is3d(evaled_g._VECTptr->back())){
1054 #ifdef GRAPH_WINDOW
1055 	  Fl_Window * win=new Fl_Window(w->x(),w->y(),w->w(),g->h());
1056 	  tmp =new Graph3d(0,0,w->w(),g->h(),"",hp);
1057 	  win->end(); win->show();
1058 	  Fl_Group::current(g);
1059 #else
1060 	  tmp =new Graph3d(w->x(),w->y(),w->w(),g->h(),"",hp);
1061 #endif
1062 	  tmp->show();
1063 	}
1064 	else
1065 #endif
1066 	  {
1067 	    tmp=new Graph2d(w->x(),w->y(),w->w(),g->h(),"",hp);
1068 	    if (Xcas_config.ortho)
1069 	      tmp->orthonormalize();
1070 	  }
1071 	tmp->add(evaled_g);
1072 	if (anim)
1073 	  tmp->animation_dt=1./5;
1074 	if (Xcas_config.autoscale)
1075 	  tmp->autoscale();
1076 	tmp->update_infos(evaled_g,contextptr);
1077 	g->end();
1078 	change_group_fontsize(g,w->labelsize());
1079 	return g;
1080       }
1081       if (is_pnt_or_pixon(evaled_g) || anim){
1082 	Fl_Tile * g = new Fl_Tile(w->x(),w->y(),w->w(),max(130,w->w()/3));
1083 	g->labelsize(w->labelsize());
1084 	Graph2d3d * res;
1085 #ifdef HAVE_LIBFLTK_GL
1086 	if (is3d(evaled_g)){
1087 #ifdef GRAPH_WINDOW
1088 	  Fl_Window * win=new Fl_Window(w->x(),w->y(),w->w(),g->h());
1089 	  res = new Graph3d(0,0,w->w(),g->h(),"",hp);
1090 	  win->end(); win->show();
1091 	  g->add(win);
1092 	  Fl_Group::current(g);
1093 #else
1094 	  res = new Graph3d(w->x(),w->y(),w->w(),g->h(),"",hp);
1095 #endif
1096 	  res->show();
1097 	}
1098 	else
1099 #endif
1100 	  {
1101 	    res=new Graph2d(w->x(),w->y(),w->w(),g->h(),"",hp);
1102 	    if (Xcas_config.ortho)
1103 	      res->orthonormalize();
1104 	  }
1105 	res->add(evaled_g);
1106 	if (anim)
1107 	  res->animation_dt=1./5;
1108 	if (Xcas_config.autoscale)
1109 	  res->autoscale();
1110 	res->update_infos(evaled_g,contextptr);
1111 	g->end();
1112 	change_group_fontsize(g,w->labelsize());
1113 	return g;
1114       }
1115       /*
1116       if (evaled_g.type==_DOUBLE_ || evaled_g.type==_INT_){
1117 	Gen_Output * o = new Gen_Output(w->x(),w->y(),w->w(),w->labelsize()+7);
1118 	o->textsize(w->labelsize());
1119 	o->value(evaled_g);
1120 	o->textcolor(FL_BLACK);
1121 	fl_font(FL_HELVETICA,w->labelsize());
1122 	int hss=int(fl_width(o->Fl_Output::value()))+4;
1123 	if (hss<=w->w())
1124 	  return o;
1125 	else
1126 	  delete o;
1127       }
1128       */
1129       unsigned ta=taille(evaled_g,max_prettyprint_equation);
1130       if (ta<max_prettyprint_equation){
1131 	giac::attributs attr(w->labelsize(),Xcas_equation_background_color,Xcas_equation_color);
1132 	giac::gen varg=Equation_compute_size(evaled_g,attr,w->w(),contextptr);
1133 	giac::eqwdata vv(Equation_total_size(varg));
1134 	int maxh=w->window()?(3*w->window()->h())/5:1000;
1135 	int scrollsize=3;
1136 	if (vv.dx>w->w()-2*w->labelsize())
1137 	  scrollsize=w->labelsize()+3;
1138 	int h=min(vv.dy+scrollsize+3,maxh); // =max(min(vv.dy+20,400),60);
1139 	Equation * res = new Equation(w->x(),w->y(),w->w(),h,"",evaled_g,attr,contextptr);
1140 	res->box(FL_FLAT_BOX);
1141 	return res;
1142       }
1143     }
1144     Gen_Output * o = new Gen_Output(w->x(),w->y(),w->w(),w->labelsize()+7);
1145     o->textsize(w->labelsize());
1146     o->value(evaled_g);
1147     o->textcolor(FL_BLUE);
1148     fl_font(FL_HELVETICA,w->labelsize());
1149     int hss=int(fl_width(o->Fl_Output::value()))+4;
1150     if (hss<=w->w())
1151       return o;
1152     o->resize(w->x(),w->y(),hss,w->labelsize()+7);
1153     Fl_Scroll * s = new Fl_Scroll(w->x(),w->y(),w->w(),2*w->labelsize()+9);
1154     s->hscrollbar.resize(s->hscrollbar.x(),w->y()+w->labelsize()+7,s->hscrollbar.w(),w->labelsize());
1155     s->box(FL_FLAT_BOX);
1156     s->end();
1157     s->add(o);
1158     return s;
1159   }
1160 
Xcas_eval_callback(const giac::gen & evaled_g,void * param)1161   void Xcas_eval_callback(const giac::gen & evaled_g,void * param){
1162     Fl_Widget * wid=static_cast<Fl_Widget *>(param);
1163     if (!wid)
1164       return;
1165     int hp_pos;
1166     History_Pack * hp = get_history_pack(wid,hp_pos);
1167     context * contextptr = hp?hp->contextptr:0;
1168     Fl_Group * gr = wid->parent();
1169     if (!hp || !gr)
1170       return;
1171 #ifdef HAVE_LIBPTHREAD
1172     // cerr << "geo2d lock" << '\n';
1173     pthread_mutex_lock(&interactive_mutex);
1174 #endif
1175     bool b=io_graph(contextptr);
1176     io_graph(contextptr)=false;
1177     bool block=block_signal;
1178     block_signal=true;
1179     // int m=gr->children();
1180     // reset output
1181 #ifdef WITH_MYOSTREAM
1182     logptr(&my_cerr,contextptr);
1183 #else
1184     logptr(&std::cerr,contextptr);
1185 #endif
1186     if (!giac::history_out(contextptr).empty() && giac::history_out(contextptr).size()==giac::history_in(contextptr).size())
1187       giac::history_out(contextptr).back()=evaled_g;
1188     else
1189       giac::history_out(contextptr).push_back(evaled_g);
1190     int pos=gr->find(wid);
1191     Fl_Widget * res = in_Xcas_eval(wid,evaled_g,hp->pretty_output,contextptr);
1192     if (Log_Output * lout=find_log_output(gr))
1193       output_resize_parent(lout,false);
1194     if (res){
1195       // resize group/pack here
1196       gr->Fl_Widget::resize(gr->x(),gr->y(),gr->w(),gr->h()+res->h()-wid->h());
1197       res->resize(wid->x(),wid->y(),res->w(),res->h());
1198       int dy=res->h()-wid->h();
1199       gr->remove(wid);
1200       Graph2d3d * widgraph = 0;
1201       if (Fl_Group * widgr = dynamic_cast<Fl_Group *>(wid)){
1202 	if (widgr->children())
1203 	  widgraph=dynamic_cast<Graph2d3d *>(widgr->child(0));
1204       }
1205       Graph2d3d * resgraph = 0;
1206       if (Fl_Group * widgr = dynamic_cast<Fl_Group *>(res)){
1207 	if (widgr->children())
1208 	  resgraph=dynamic_cast<Graph2d3d *>(widgr->child(0));
1209       }
1210       gr->insert(*res,pos);
1211       change_group_fontsize(gr,gr->labelsize());
1212       if (widgraph && resgraph){
1213 	resgraph->copy(*widgraph);
1214 	resgraph->resize(resgraph->x(),resgraph->y(),widgraph->w(),resgraph->h());
1215 	resgraph->resize_mouse_param_group(gr->w()-widgraph->w());
1216       }
1217       // gr->resizable(gr);
1218       hp->resize();
1219       if (Fl_Scroll * s = dynamic_cast<Fl_Scroll *>(hp->parent())){
1220 	int spos=s->yposition();
1221 	if (spos+s->h()>hp->h()){
1222 #ifdef _HAVE_FL_UTF8_HDR_
1223 	  s->scroll_to(0,max(min(hp->h()-s->h(),spos+dy),0));
1224 #else
1225 	  s->position(0,max(min(hp->h()-s->h(),spos+dy),0));
1226 #endif
1227 	}
1228 	s->redraw();
1229       }
1230       Fl_Group * group = parent_skip_scroll(hp);
1231       if (Logo * logo=dynamic_cast<Logo *>(group)){
1232 	logo->redraw();
1233       }
1234       // show DispG?
1235       if (hp->pretty_output && !Xcas_DispG_Window->visible()){
1236 	int entries=Xcas_DispG->plot_instructions.size();
1237 	if (entries>xcas_dispg_entries){
1238 	  if (resgraph){
1239 	    vecteur dispgv=vecteur(Xcas_DispG->plot_instructions.begin()+xcas_dispg_entries,Xcas_DispG->plot_instructions.end()),v1,v2;
1240 	    aplatir(dispgv,v1);
1241 	    aplatir(resgraph->plot_instructions,v2);
1242 	    if (v1!=v2){
1243 	      if (Xcas_DispG_Cancel) Xcas_DispG_Cancel->hide();
1244 	      show_xcas_dispg=3;
1245 	    }
1246 	  }
1247 	  else {
1248 	    if (Xcas_DispG_Cancel) Xcas_DispG_Cancel->hide();
1249 	    show_xcas_dispg=3;
1250 	  }
1251 	}
1252       }
1253       block_signal=block;
1254       io_graph(contextptr)=b;
1255 #ifdef HAVE_LIBPTHREAD
1256       pthread_mutex_unlock(&interactive_mutex);
1257 #endif
1258       // if hp has eval_below, call callback on next widget
1259       if (hp->eval_below){
1260 	hp->next(hp_pos);
1261 	/*
1262 	Fl_Group * hpp = parent_skip_scroll(hp);
1263 	if (hpp){
1264 	  int N=hpp->children();
1265 	  for (int i=0;i<N;++i){
1266 	    Graph2d3d * geo = dynamic_cast<Graph2d3d *>(hpp->child(i));
1267 	    if (geo){
1268 	      geo->handle(FL_FOCUS);
1269 	    }
1270 	  }
1271 	}
1272 	*/
1273       }
1274       else {
1275 	Fl_Group * hpp = parent_skip_scroll(hp);
1276 	if (hpp){
1277 	  int N=hpp->children();
1278 	  for (int i=0;i<N;++i){
1279 	    Graph2d3d * geo = dynamic_cast<Graph2d3d *>(hpp->child(i));
1280 	    if (geo){
1281 	      geo->add(evaled_g);
1282 	      geo->no_handle=false;
1283 	    }
1284 	  }
1285 	}
1286 	hp->focus(hp_pos+1,false);
1287       }
1288     } // if (res)
1289     else { // res==0 no output
1290       if (Fl_Input_ * i=dynamic_cast<Fl_Input_ * >(wid))
1291 	i->value("No_output");
1292       hp->resize();
1293       block_signal=block;
1294       io_graph(contextptr)=b;
1295 #ifdef HAVE_LIBPTHREAD
1296       pthread_mutex_unlock(&interactive_mutex);
1297 #endif
1298     }
1299   }
1300 
Xcas_eval(Fl_Widget * w,const giac::gen & g_)1301   Fl_Widget * Xcas_eval(Fl_Widget * w,const giac::gen & g_){
1302     if (!w)
1303       return 0;
1304     if (debug_infolevel>=5)
1305       cerr << "eval " << g_ << '\n';
1306     Fl_Group * gr=w->parent();
1307     Fl_Group::current(gr);
1308     // Find history_pack above for context from widget
1309     History_Pack * hp = get_history_pack(w);
1310     context * contextptr=hp?hp->contextptr:0;
1311     check_browser_help(g_,giac::language(contextptr));
1312     if (!hp)
1313       return 0;
1314     gen g=add_autosimplify(g_,contextptr);
1315     giac::gen evaled_g;
1316     // if w 2nd brother is a graph2d3d, return a graph2d3d with the same
1317     // config
1318     Fl_Widget * res = 0;
1319     if (gr && gr->children()>=3){
1320       if (Fl_Output * out=dynamic_cast<Fl_Output *>(gr->child(2))){
1321 	if (strcmp(out->value(),gettext("Computing..."))==0){
1322 	  out->value(gettext("Unable to launch thread. Press STOP to interrupt."));
1323 	  return w;
1324 	}
1325       }
1326       if (Fl_Group * grc2=dynamic_cast<Fl_Group * >(gr->child(2))){
1327 	if (grc2->children()){
1328 	  if (Graph2d3d * graph=dynamic_cast<Graph2d3d *>(grc2->child(0))){
1329 	    Fl_Tile * temptile = new Fl_Tile(w->x(),w->y(),w->w(),w->labelsize()+6);
1330 	    Graph2d * tmp = new Graph2d(w->x(),w->y(),w->w(),temptile->h(),"",hp);
1331 	    temptile->end();
1332 	    tmp->copy(*graph);
1333 	    tmp->resize(tmp->x(),tmp->y(),graph->w(),tmp->h());
1334 	    temptile->labelsize(w->labelsize());
1335 	    res=temptile;
1336 	  }
1337 	}
1338       }
1339     }
1340     giac::history_in(contextptr).push_back(g_);
1341     // commented otherwise ans() does not work
1342     // giac::history_out.push_back(g);
1343     Fl_Output * out=0;
1344     bool graphres=res;
1345     out =new Fl_Output(w->x(),w->y(),w->w(),w->labelsize());
1346     out->labelsize(w->labelsize());
1347     if (!res)
1348       res=out;
1349     bool ok=make_thread(g,eval_level(contextptr),Xcas_eval_callback,res,contextptr);
1350     if (graphres){
1351       if (ok){
1352 	gr->remove(out);
1353 	delete out;
1354 	return res;
1355       }
1356       gr->remove(res);
1357       delete res;
1358       res=out;
1359     }
1360     out->value(ok?gettext("Computing..."):gettext("Unable to launch thread. Press STOP to interrupt."));
1361     return res;
1362   }
1363 
Xcas_eval(Fl_Widget * w)1364   Fl_Widget * Xcas_eval(Fl_Widget * w) {
1365     giac::gen g;
1366     int res=parse(w,g);
1367     if (res==1)
1368       return Xcas_eval(w,g);
1369     if (res==-1)
1370       return w;
1371     /* Fl_Output * o = new Fl_Output(w->x(),w->y(),w->w(),w->h());
1372        o->value("Invalid input");
1373        return o; */
1374     return 0;
1375   }
1376 
1377 
print_DOUBLE_(double d)1378   string print_DOUBLE_(double d){
1379     char s[256];
1380 #ifdef IPAQ
1381     sprintf(s,"%.4g",d);
1382 #else
1383     sprintf(s,"%.5g",d);
1384 #endif
1385     return s;
1386   }
1387 
replaces(const string & s,char c1,const string & c2)1388   string replaces(const string & s,char c1,const string & c2){
1389     string res;
1390     int l=s.size();
1391     res.reserve(l);
1392     const char * ch=s.c_str();
1393     for (int i=0;i<l;++i,++ch){
1394       if (*ch==c1) res+=c2; else res += *ch;
1395     }
1396     return res;
1397   }
1398 
seq2vecteur(const vecteur & v)1399   vecteur seq2vecteur(const vecteur & v){
1400     vecteur w(v);
1401     iterateur it=w.begin(),itend=w.end();
1402     for (;it!=itend;++it){
1403       if (it->type==_VECT)
1404 	it->subtype=0;
1405     }
1406     return w;
1407   }
1408 
load_history_fold(int sx,int sy,int sw,int sh,int sl,const char * filename,bool modified)1409   History_Fold * load_history_fold(int sx,int sy,int sw,int sh,int sl,const char * filename,bool modified){
1410     Fl_Group::current(0);
1411     xcas::History_Fold * w = new xcas::History_Fold(sx,sy,sw,sh,1);
1412     w->end();
1413     w->pack->contextptr = giac::clone_context(giac::context0);
1414     w->pack->labelsize(sl);
1415     w->pack->eval=xcas::Xcas_eval;
1416     w->pack->_insert=xcas::Xcas_pack_insert;
1417     w->pack->_select=xcas::Xcas_pack_select;
1418     w->pack->new_url(filename);
1419     w->pack->insert_url(filename,-1);
1420     w->labelfont(w->pack->labelfont());
1421     xcas::change_group_fontsize(w,w->pack->labelsize());
1422     if (!modified)
1423       w->pack->clear_modified();
1424     else {
1425       w->autosave(true);
1426       if (w->pack->url){ delete w->pack->url; w->pack->url=0; }
1427       w->label("Unnamed");
1428     }
1429     return w;
1430   }
1431 
replace_html5(const string & s)1432   std::string replace_html5(const string & s){
1433     string res;
1434     size_t ss=s.size(),i;
1435     for (i=0;i<ss;++i){
1436       char ch=s[i];
1437       if ( (ch>='0' && ch<='9') || (ch>='a' && ch<='z') || (ch>='A' && ch<='Z'))
1438 	res += ch;
1439       else {
1440 	res +='%';
1441 	int t=(ch&0xf0)>>4;
1442 	if (t<10) res += ('0'+t); else res += 'a'+(t-10);
1443 	t=ch&0x0f;
1444 	if (t<10) res += ('0'+t); else res += 'a'+(t-10);
1445       }
1446     }
1447     //std::cerr << s << '\n' << res << '\n';
1448     return res;
1449   }
1450 
widget_html5(const Fl_Widget * o)1451   std::string widget_html5(const Fl_Widget * o){
1452     string res;
1453     const giac::context * contextptr = get_context(o);
1454     // Add here code for specific widgets
1455     if (const Tableur_Group * t = dynamic_cast<const Tableur_Group *>(o)){
1456       Flv_Table_Gen * g=t->table;
1457       matrice m=g->m;
1458       res = replace_html5(gen(m,_SPREAD__VECT).print(contextptr));
1459       return '+'+res+'&';
1460     }
1461     if (const Figure * f = dynamic_cast<const Figure *>(o)){
1462       res = widget_html5(f->geo->hp);
1463       return res;
1464     }
1465     if (const Logo * l=dynamic_cast<const Logo *>(o)){
1466       res = widget_html5(l->hp);
1467       return res;
1468     }
1469     if (const Editeur * ed=dynamic_cast<const Editeur *>(o)){
1470       string s=unlocalize(ed->value());
1471       res = replace_html5(s);
1472       return '+'+res+'&';
1473     }
1474     if (const Xcas_Text_Editor * ed=dynamic_cast<const Xcas_Text_Editor *>(o)){
1475       string s=unlocalize(ed->value());
1476       res = replace_html5(s);
1477       return '+'+res+'&';
1478     }
1479     if (dynamic_cast<const Fl_Output *>(o))
1480       return "";
1481     if (const Fl_Input_ * i=dynamic_cast<const Fl_Input_ *>(o)){
1482       string s=i->value();
1483       if (dynamic_cast<const Comment_Multiline_Input *>(i))
1484 	s = "// "+replaces(s,'\n',"<br>");
1485       if ( dynamic_cast<const Multiline_Input_tab *>(i) )
1486 	s=unlocalize(s);
1487       if (s.empty())
1488 	return s;
1489       res = replace_html5(s);
1490       return '+'+res+'&' ;
1491     }
1492     if (const Fl_Group * g=dynamic_cast<const Fl_Group *>(o)){
1493       int ypos=0;
1494       // call widget_sprint on children
1495       int n=g->children();
1496       for (int i=0;i<n;++i){
1497 	Fl_Widget * wid=g->child(i);
1498 	res += widget_html5(wid);
1499       }
1500       return res;
1501     }
1502     if (const Gen_Value_Slider *g=dynamic_cast<const Gen_Value_Slider *>(o)){
1503       res = string(g->label())+","+print_DOUBLE_(g->value())+","+print_DOUBLE_(g->minimum())+","+print_DOUBLE_(g->maximum())+","+print_DOUBLE_(g->Fl_Valuator::step());
1504       return '*'+res+'&';
1505     }
1506     return res ;
1507   }
1508 
widget_sprint(const Fl_Widget * o)1509   std::string widget_sprint(const Fl_Widget * o){
1510     string res;
1511     const giac::context * contextptr = get_context(o);
1512     res = "// fltk " + string(typeid(*o).name());
1513     int wh=o->h();
1514     if (const History_Fold * hf=dynamic_cast<const History_Fold *>(o)){
1515       if (hf->_folded)
1516 	wh=hf->_horig;
1517     }
1518     res += " " + giac::print_INT_(o->x()) + " " + giac::print_INT_(o->y())+ " " + giac::print_INT_(o->w()) + " " + giac::print_INT_(wh) + " " + giac::print_INT_(o->labelsize()) + " " + giac::print_INT_(o->labelfont()) ;
1519     // Add here code for specific widgets
1520     if (const Flv_Table_Gen * g = dynamic_cast<const Flv_Table_Gen *>(o)){
1521       res += '\n'+print_INT_(g->is_spreadsheet)+" "+print_INT_(g->matrix_fill_cells)+" "+print_INT_(g->spreadsheet_recompute)+" "+print_INT_(g->matrix_symmetry)+ " " +replace(gen(g->m,_SPREAD__VECT).print(contextptr),'\n','�')+'\n';
1522       return res;
1523     }
1524     if (const Tableur_Group * t = dynamic_cast<const Tableur_Group *>(o)){
1525       Flv_Table_Gen * g=t->table;
1526       Graph2d3d * gr = g->graph;
1527       matrice m=g->m;
1528       if (!g->m.empty()){
1529 	gen m0=m.front();
1530 	if (m0.type==_VECT && !m0._VECTptr->empty()){
1531 	  vecteur m0v=*m0._VECTptr;
1532 	  gen m00=m0v.front();
1533 	  if (m00.type==_VECT && m00._VECTptr->size()==3){
1534 	    vecteur rowv,colv,dispv;
1535 	    for (int i=0;i<g->rows();++i)
1536 	      rowv.push_back(g->row_height(i));
1537 	    for (int i=0;i<g->cols();++i)
1538 	      colv.push_back(g->col_width(i));
1539 	    dispv.push_back(t->disposition);
1540 	    if (t->disposition/2){
1541 	      if (t->disposition % 2){ // graph at right
1542 		dispv.push_back(g->w());
1543 		dispv.push_back(g->graph->w());
1544 		dispv.push_back(g->graph->mouse_param_group->w());
1545 	      }
1546 	      else { // graph below
1547 		double dtable=double(g->h())/t->h();
1548 		double dgraph=((1-dtable)*g->graph->w())/g->w();
1549 		dispv.push_back(dtable);
1550 		dispv.push_back(dgraph);
1551 		dispv.push_back(1-dtable-dgraph);
1552 	      }
1553 	    }
1554 	    gen newm002=makevecteur(g->name,
1555 					  makevecteur(gr->window_xmin,gr->window_xmax,gr->window_ymin,gr->window_ymax),
1556 					  gr->npixels,
1557 					  makevecteur(gr->x_tick,gr->y_tick),
1558 				    gr->show_axes,gr->show_names,dispv,makevecteur(rowv,colv),g->init);
1559 	    m00=gen(makevecteur(m00[0],m00[1],newm002),m0v[0].subtype);
1560 	    m0v[0]=m00;
1561 	    m[0]=gen(m0v,m0[0].subtype);
1562 	  }
1563 	}
1564       }
1565       res += '\n'+print_INT_(g->is_spreadsheet)+" "+print_INT_(g->matrix_fill_cells)+" "+print_INT_(g->spreadsheet_recompute)+" "+print_INT_(g->matrix_symmetry)+ " " +replace(gen(m,_SPREAD__VECT).print(contextptr),'\n','�')+'\n';
1566       return res;
1567     }
1568     if (const Figure * f = dynamic_cast<const Figure *>(o)){
1569       res += " landscape="+print_INT_(f->disposition)+" history=";
1570       if (f->disposition & 1){
1571 	double tmp=double(f->geo->h())/f->h();
1572 	res += print_DOUBLE_((1-tmp)*double(f->s->w())/f->w())+ " geo="+print_DOUBLE_(tmp*double(f->geo->w())/f->w())+ " "+" mouse_param="+print_DOUBLE_(tmp*double(f->geo->mouse_param_group->w())/f->w());
1573       }
1574       else
1575 	res += print_DOUBLE_(double(f->s->w())/f->w())+ " geo="+print_DOUBLE_(double(f->geo->w())/f->w())+ " "+" mouse_param="+print_DOUBLE_(double(f->geo->mouse_param_group->w())/f->w());
1576       res += '\n'+widget_sprint(f->geo->hp)+widget_sprint(f->geo)+'\n';
1577       return res;
1578     }
1579     if (const Logo * l=dynamic_cast<const Logo *>(o)){
1580       res += '\n'+widget_sprint(l->hp)+widget_sprint(l->ed);
1581       return res;
1582     }
1583     if (const Editeur * ed=dynamic_cast<const Editeur *>(o)){
1584       string s=unlocalize(ed->value());
1585       res += '\n'+print_INT_(s.size())+" ,\n"+s;
1586       return res;
1587     }
1588     if (const Xcas_Text_Editor * ed=dynamic_cast<const Xcas_Text_Editor *>(o)){
1589       string s=unlocalize(ed->value());
1590       res += '\n'+print_INT_(s.size())+" ,\n"+s;
1591       return res;
1592     }
1593     if (const Gen_Output * i=dynamic_cast<const Gen_Output *>(o)){
1594       res += '\n';
1595       string s=taille(i->value(),100)>100?string("Done"):i->value().print(contextptr);
1596       s=unlocalize(s);
1597       res += replace(s,'\n','�');
1598       // res += '"';
1599       return res + '\n';
1600     }
1601     if (const Fl_Input_ * i=dynamic_cast<const Fl_Input_ *>(o)){
1602       res += '\n';
1603       string s=i->value();
1604       if ( dynamic_cast<const Multiline_Input_tab *>(i) )
1605 	s=unlocalize(s);
1606       res += replace(s,'\n','�');
1607       // res += '"';
1608       return res + '\n';
1609     }
1610     if (const Log_Output * i=dynamic_cast<const Log_Output *>(o)){
1611       res += '\n';
1612       // res += '"';
1613       res += replace(i->value(),'\n','�');
1614       // res += '"';
1615       return res + '\n';
1616     }
1617     if (const Equation * i=dynamic_cast<const Equation *>(o)){
1618       res += " " + giac::print_INT_(i->output_equation);
1619       res += '\n';
1620       // res += '"';
1621       string s=taille(i->get_data(),1000)>1000?string("Done"):i->value();
1622       res += replace(unlocalize(s),'\n','�');
1623       // res += '"';
1624       return res + '\n';
1625     }
1626     if (const Graph2d3d * i=dynamic_cast<const Graph2d3d *>(o)){
1627       res += '\n';
1628       res += print_DOUBLE_(i->window_xmin) + ',' + print_DOUBLE_(i->window_xmax) + ',';
1629       res += print_DOUBLE_(i->window_ymin) + ',' + print_DOUBLE_(i->window_ymax) + ',';
1630       res += replace(giac::gen(giac::merge_pixon(seq2vecteur(i->plot_instructions))).print(contextptr),'\n','�');
1631       res += ','+ print_DOUBLE_(i->window_zmin) + ',' + print_DOUBLE_(i->window_zmax)+','+print_DOUBLE_(i->q.w) +','+print_DOUBLE_(i->q.x)+','+print_DOUBLE_(i->q.y)+','+print_DOUBLE_(i->q.z) + ','+print_DOUBLE_(i->x_tick) + ',' + print_DOUBLE_(i->y_tick)+','+print_INT_(i->show_axes)+','+print_INT_(i->couleur)+','+print_INT_(i->approx)+','+print_DOUBLE_(i->ylegende)+',';
1632       if (i->paused)
1633 	res += "-";
1634       res +=print_DOUBLE_(i->animation_dt)+','+print_INT_(i->show_mouse_on_object)+','+print_INT_(i->display_mode);
1635       res += ",[";
1636       if (dynamic_cast<const Graph3d *>(i)){
1637 	for (int j=0;;){
1638 	  res += "["+ print_DOUBLE_(i->light_x[j]);
1639 	  res += ","+ print_DOUBLE_(i->light_y[j]);
1640 	  res += ","+ print_DOUBLE_(i->light_z[j]);
1641 	  res += ","+ print_DOUBLE_(i->light_w[j]);
1642 	  res += ","+ print_DOUBLE_(i->light_diffuse_r[j]);
1643 	  res += ","+ print_DOUBLE_(i->light_diffuse_g[j]);
1644 	  res += ","+ print_DOUBLE_(i->light_diffuse_b[j]);
1645 	  res += ","+ print_DOUBLE_(i->light_diffuse_a[j]);
1646 	  res += ","+ print_DOUBLE_(i->light_specular_r[j]);
1647 	  res += ","+ print_DOUBLE_(i->light_specular_g[j]);
1648 	  res += ","+ print_DOUBLE_(i->light_specular_b[j]);
1649 	  res += ","+ print_DOUBLE_(i->light_specular_a[j]);
1650 	  res += ","+ print_DOUBLE_(i->light_ambient_r[j]);
1651 	  res += ","+ print_DOUBLE_(i->light_ambient_g[j]);
1652 	  res += ","+ print_DOUBLE_(i->light_ambient_b[j]);
1653 	  res += ","+ print_DOUBLE_(i->light_ambient_a[j]);
1654 	  res += ","+ print_DOUBLE_(i->light_spot_x[j]);
1655 	  res += ","+ print_DOUBLE_(i->light_spot_y[j]);
1656 	  res += ","+ print_DOUBLE_(i->light_spot_z[j]);
1657 	  res += ","+ print_DOUBLE_(i->light_spot_w[j]);
1658 	  res += ","+ print_DOUBLE_(i->light_spot_exponent[j]);
1659 	  res += ","+ print_DOUBLE_(i->light_spot_cutoff[j]);
1660 	  res += ","+ print_DOUBLE_(i->light_0[j]);
1661 	  res += ","+ print_DOUBLE_(i->light_1[j]);
1662 	  res += ","+ print_DOUBLE_(i->light_2[j])+","+print_INT_(i->light_on[j])+"]";
1663 	  ++j;
1664 	  if (j==8)
1665 	    break;
1666 	  res += ",";
1667 	}
1668       } // end i is a graph3d
1669       res += "]";
1670       res += ","+print_INT_(i->ntheta)+","+print_INT_(i->nphi);
1671       res += ","+print_INT_(i->rotanim_type)+","+print_INT_(i->rotanim_danim)+","+print_INT_(i->rotanim_nstep)+","+print_DOUBLE_(i->rotanim_rx)+","+print_DOUBLE_(i->rotanim_ry)+","+print_DOUBLE_(i->rotanim_rz)+","+print_DOUBLE_(i->rotanim_tstep);
1672       res += ","+print_INT_(i->x_axis_color)+","+print_INT_(i->y_axis_color)+","+print_INT_(i->z_axis_color);
1673       return res + '\n';
1674     }
1675     if (const History_Fold * g=dynamic_cast<const History_Fold *>(o)){
1676       res += '\n'+replace(g->input->value(),'\n','�') + '\n';
1677       res += widget_sprint(g->pack);
1678       return res;
1679     }
1680     if (const Fl_Group * g=dynamic_cast<const Fl_Group *>(o)){
1681       int ypos=0;
1682       if (Fl_Scroll * s=(Fl_Scroll *) dynamic_cast<const Fl_Scroll *>(g)){
1683 	ypos=s->yposition();
1684 #ifdef _HAVE_FL_UTF8_HDR_
1685 	s->scroll_to(s->xposition(),0);
1686 #else
1687 	s->position(s->xposition(),0);
1688 #endif
1689       }
1690       // call widget_sprint on children
1691       int n=g->children();
1692       res += "\n[\n";
1693       for (int i=0;i<n;++i){
1694 	Fl_Widget * wid=g->child(i);
1695 	res += widget_sprint(wid);
1696 	if (i!=n-1)
1697 	  res += ",\n";
1698       }
1699       res += "]\n";
1700       if (Fl_Scroll * s=(Fl_Scroll *) dynamic_cast<const Fl_Scroll *>(g)){
1701 #ifdef _HAVE_FL_UTF8_HDR_
1702 	s->scroll_to(s->xposition(),ypos);
1703 #else
1704 	s->position(s->xposition(),ypos);
1705 #endif
1706       }
1707       return res;
1708     }
1709     if (const Gen_Value_Slider *g=dynamic_cast<const Gen_Value_Slider *>(o)){
1710       res += "\n" + giac::print_INT_(g->pos)+" "+print_DOUBLE_(g->minimum())+" "+print_DOUBLE_(g->maximum())+" "+print_DOUBLE_(g->value())+" "+string(g->label())+" "+print_DOUBLE_(g->Fl_Valuator::step())+"\n";
1711       return res;
1712     }
1713     return res + "\n[]\n";
1714   }
1715 
next_line(const string & s,int L,string & line,int & i)1716   void next_line(const string & s,int L,string & line,int & i){
1717     line="";
1718     for (;i<L;++i){
1719       line += (s[i]=='�'?'\n':s[i]);
1720       if (s[i]=='\n'){
1721 	++i;
1722 	break;
1723       }
1724     }
1725     // cerr << i << " " << line << '\n';
1726   }
1727 
next_line_nonl(const string & s,int L,string & line,int & i)1728   void next_line_nonl(const string & s,int L,string & line,int & i){
1729     next_line(s,L,line,i);
1730     int t=line.size();
1731     if (t && line[t-1]=='\n')
1732       line=line.substr(0,t-1);
1733   }
1734 
1735   // Read a group of widget from string s starting a pos i
widget_group_load(Fl_Group * res,const string & s,int L,int & i,GIAC_CONTEXT)1736   Fl_Group * widget_group_load(Fl_Group * res,const string & s,int L,int & i,GIAC_CONTEXT){
1737     Fl_Group::current(res);
1738     for (;Fl_Widget * o=widget_load(s,L,i,contextptr,res->w());){
1739       if (History_Pack * hp=dynamic_cast<History_Pack *>(o))
1740 	hp->contextptr=(giac::context *)contextptr;
1741       if (o->w()>res->w())
1742 	o->resize(o->x(),o->y(),res->w(),o->h());
1743       if (o->parent()!=res)
1744 	res->add(o);
1745       // remove empty group
1746       if (Fl_Group * gr = dynamic_cast<Fl_Group * >(o)){
1747 	if (!gr->children())
1748 	  res->remove(gr);
1749       }
1750       if (i<L-1 && s[i]==',' && s[i+1]=='\n')
1751 	i += 2;
1752     }
1753     res->end();
1754     string line;
1755     next_line(s,L,line,i);
1756     if (line=="]\n")
1757       return res;
1758     else
1759       return 0;
1760   }
1761 
graphic_load(Graph2d3d * res,const std::string & s,int L,string & line,int & i)1762   void graphic_load(Graph2d3d * res,const std::string & s,int L,string & line,int & i){
1763     /*
1764     if (res->mouse_param_group && !dynamic_cast<Figure *>(res->parent())){
1765       int X=res->x();
1766       int Y=res->y();
1767       int W=res->parent()->w();
1768       int H=res->h();
1769       int l=W/4;
1770       if (l>6*res->labelsize())
1771 	l=6*res->labelsize();
1772       res->resize(X,Y,W-l,H);
1773       res->resize_mouse_param_group(l);
1774     }
1775     */
1776     next_line_nonl(s,L,line,i);
1777     if (s[i]=='\n')
1778       ++i;
1779     const giac::context * contextptr = get_context(res);
1780     giac::gen g(line,contextptr);
1781     if (g.type==_VECT && g._VECTptr->size()>=5){
1782       giac::vecteur & v =*g._VECTptr;
1783       res->window_xmin= giac::evalf_double(v[0],1,contextptr)._DOUBLE_val;
1784       res->window_xmax= giac::evalf_double(v[1],1,contextptr)._DOUBLE_val;
1785       res->window_ymin= giac::evalf_double(v[2],1,contextptr)._DOUBLE_val;
1786       res->window_ymax= giac::evalf_double(v[3],1,contextptr)._DOUBLE_val;
1787       gen gg=v[4];
1788       if (gg.type==_VECT)
1789 	res->add(*gg._VECTptr);
1790       else
1791 	res->add(gg);
1792       if (v.size()>=7){
1793 	res->window_zmin= giac::evalf_double(v[5],1,contextptr)._DOUBLE_val;
1794 	res->window_zmax= giac::evalf_double(v[6],1,contextptr)._DOUBLE_val;
1795       }
1796       if (v.size()>=11){
1797 	res->q=quaternion_double(giac::evalf_double(v[7],1,contextptr)._DOUBLE_val,giac::evalf_double(v[8],1,contextptr)._DOUBLE_val,giac::evalf_double(v[9],1,contextptr)._DOUBLE_val,giac::evalf_double(v[10],1,contextptr)._DOUBLE_val);
1798       }
1799       if (v.size()>=13){
1800 	res->x_tick=giac::evalf_double(v[11],1,contextptr)._DOUBLE_val;
1801 	res->y_tick=giac::evalf_double(v[12],1,contextptr)._DOUBLE_val;
1802       }
1803       if (v.size()>=14)
1804 	res->show_axes=v[13].val;
1805       if (v.size()>=16){
1806 	res->couleur=v[14].val;
1807 	res->approx=v[15].val;
1808       }
1809       if (v.size()>=17)
1810 	res->ylegende=evalf_double(v[16],1,contextptr)._DOUBLE_val;
1811       if (v.size()>=18){
1812 	gen tmp=evalf_double(v[17],1,contextptr);
1813 	res->animation_dt=tmp.DOUBLE_val();
1814 	if (std::abs(res->animation_dt<1e-10))
1815 	  res->animation_dt=0;
1816 	if (res->animation_dt<0){
1817 	  res->paused=true;
1818 	  res->animation_dt=-res->animation_dt;
1819 	}
1820       }
1821       if (v.size()>=19)
1822 	res->show_mouse_on_object=int(evalf_double(v[18],1,contextptr)._DOUBLE_val);
1823       if (v.size()>=20)
1824 	res->display_mode=int(evalf_double(v[19],1,contextptr)._DOUBLE_val);
1825       if (v.size()>=21 && v[20].type==_VECT){
1826 	vecteur & vv=*v[20]._VECTptr;
1827 	for (unsigned j=0;j< vv.size() && j<8;++j){
1828 	  gen & tmp=vv[j];
1829 	  if ( (tmp.type==_VECT && tmp._VECTptr->size()>=25) ){
1830 	    vecteur w = *evalf_double(tmp,1,contextptr)._VECTptr;
1831 	    res->light_x[j]=w[0]._DOUBLE_val;
1832 	    res->light_y[j]=w[1]._DOUBLE_val;
1833 	    res->light_z[j]=w[2]._DOUBLE_val;
1834 	    res->light_w[j]=w[3]._DOUBLE_val;
1835 	    res->light_diffuse_r[j]=w[4]._DOUBLE_val;
1836 	    res->light_diffuse_g[j]=w[5]._DOUBLE_val;
1837 	    res->light_diffuse_b[j]=w[6]._DOUBLE_val;
1838 	    res->light_diffuse_a[j]=w[7]._DOUBLE_val;
1839 	    res->light_specular_r[j]=w[8]._DOUBLE_val;
1840 	    res->light_specular_g[j]=w[9]._DOUBLE_val;
1841 	    res->light_specular_b[j]=w[10]._DOUBLE_val;
1842 	    res->light_specular_a[j]=w[11]._DOUBLE_val;
1843 	    res->light_ambient_r[j]=w[12]._DOUBLE_val;
1844 	    res->light_ambient_g[j]=w[13]._DOUBLE_val;
1845 	    res->light_ambient_b[j]=w[14]._DOUBLE_val;
1846 	    res->light_ambient_a[j]=w[15]._DOUBLE_val;
1847 	    res->light_spot_x[j]=w[16]._DOUBLE_val;
1848 	    res->light_spot_y[j]=w[17]._DOUBLE_val;
1849 	    res->light_spot_z[j]=w[18]._DOUBLE_val;
1850 	    res->light_spot_w[j]=w[19]._DOUBLE_val;
1851 	    res->light_spot_exponent[j]=w[20]._DOUBLE_val;
1852 	    res->light_spot_cutoff[j]=w[21]._DOUBLE_val;
1853 	    res->light_0[j]=w[22]._DOUBLE_val;
1854 	    res->light_1[j]=w[23]._DOUBLE_val;
1855 	    res->light_2[j]=w[24]._DOUBLE_val;
1856 	    if (tmp._VECTptr->size()>=26)
1857 	      res->light_on[j]=int(w[25]._DOUBLE_val);
1858 	  }
1859 	}
1860       }
1861       if (v.size()>=23 && v[21].type==_INT_ && v[22].type==_INT_){
1862 	res->ntheta=v[21].val;
1863 	res->nphi=v[22].val;
1864       }
1865       if (v.size()>=26 && v[23].type==_INT_ && v[24].type==_INT_&& v[25].type==_INT_){
1866 	res->rotanim_type=v[23].val;
1867 	res->rotanim_danim=v[24].val;
1868 	res->rotanim_nstep=v[25].val;
1869       }
1870       if (v.size()>=30 && v[26].type==_DOUBLE_ && v[27].type==_DOUBLE_&& v[28].type==_DOUBLE_){
1871 	res->rotanim_rx=v[26]._DOUBLE_val;
1872 	res->rotanim_ry=v[27]._DOUBLE_val;
1873 	res->rotanim_rz=v[28]._DOUBLE_val;
1874 	res->rotanim_tstep=v[29]._DOUBLE_val;
1875       }
1876       if (v.size()>=33 && v[30].type==_INT_ && v[31].type==_INT_&& v[32].type==_INT_){
1877 	res->x_axis_color=v[30].val;
1878 	res->y_axis_color=v[31].val;
1879 	res->z_axis_color=v[32].val;
1880       }
1881     }
1882     if (Geo2d * geo=dynamic_cast<Geo2d *>(res)){
1883       geo->hp=geo_find_history_pack(geo);
1884       if (geo_run && geo->hp)
1885 	geo->hp->update();
1886     }
1887 #ifdef HAVE_LIBFLTK_GL
1888     if (Geo3d * geo=dynamic_cast<Geo3d *>(res)){
1889       geo->hp=geo_find_history_pack(geo);
1890       if (geo_run && geo->hp)
1891 	geo->hp->update();
1892     }
1893 #endif
1894   }
1895 
tableur_load(Flv_Table_Gen * & res,const std::string & s,int x,int y,int w,int h)1896   void tableur_load(Flv_Table_Gen * & res,const std::string & s,int x,int y,int w,int h){
1897     int taille=s.size(),i=0;
1898     for (int j=0;j<4&&i<taille;i++){
1899       j += (s[i]==' ');
1900     }
1901     string s1=s.substr(0,i);
1902     string s2=s.substr(i,taille-i);
1903 #ifdef HAVE_SSTREAM
1904     istringstream in(s1);
1905 #else
1906     istrstream in(s1.c_str());
1907 #endif
1908     int is_spreadsheet,matrix_fill_cells,spreadsheet_recompute,matrix_symmetry;
1909     in >> is_spreadsheet >> matrix_fill_cells >> spreadsheet_recompute >> matrix_symmetry ;
1910     giac::context * contextptr=get_context(res);
1911     gen g(s2,contextptr);
1912     if (!ckmatrix(g,true))
1913       return ;
1914     Tableur_Group * t=dynamic_cast<Tableur_Group *>(res->parent());
1915     spread_ck(*g._VECTptr); // in place modifications of g
1916     // Find name of tableur in g
1917     matrice m=*g._VECTptr;
1918     if (!res)
1919       res = new Flv_Table_Gen(x,y,w,h,"");
1920     else {
1921       int n,c;
1922       mdims(m,n,c);
1923       res->rows(n);
1924       res->cols(c);
1925     }
1926     if (!m.empty()){
1927       gen g0=g._VECTptr->front();
1928       if (g0.type==_VECT && !g0._VECTptr->empty()){
1929 	vecteur m0=*g0._VECTptr;
1930 	gen g00= m0.front();
1931 	if (g00.type==_VECT && g00._VECTptr->size()>=3){
1932 	  vecteur m00=*g00._VECTptr;
1933 	  gen g002 = m00.back();
1934 	  if (g002.type==_IDNT)
1935 	    res->name=g002;
1936 	  if (g002.type==_VECT){
1937 	    Graph2d * gr = res->graph;
1938 	    vecteur & ggv = *g002._VECTptr;
1939 	    int ggs=ggv.size();
1940 	    if (ggs>0 && ggv[0].type==_IDNT)
1941 	      res->name=ggv[0];
1942 	    if (gr && ggs>5){
1943 	      if (ggv[1].type==_VECT && ggv[1]._VECTptr->size()>3){
1944 		vecteur & tmpv=*ggv[1]._VECTptr;
1945 		gr->window_xmin=evalf_double(tmpv[0],1,contextptr)._DOUBLE_val;
1946 		gr->window_xmax=evalf_double(tmpv[1],1,contextptr)._DOUBLE_val;
1947 		gr->window_ymin=evalf_double(tmpv[2],1,contextptr)._DOUBLE_val;
1948 		gr->window_ymax=evalf_double(tmpv[3],1,contextptr)._DOUBLE_val;
1949 	      }
1950 	      if (ggv[2].type==_INT_)
1951 		gr->npixels = ggv[2].val;
1952 	      if (ggv[3].type==_VECT && ggv[3]._VECTptr->size()>1){
1953 		vecteur & tmpv=*ggv[3]._VECTptr;
1954 	      gr->x_tick=evalf_double(tmpv[0],1,contextptr)._DOUBLE_val;
1955 	      gr->y_tick=evalf_double(tmpv[1],1,contextptr)._DOUBLE_val;
1956 	      }
1957 	      if (ggv[4].type==_INT_)
1958 		gr->show_axes = ggv[4].val;
1959 	      if (ggv[5].type==_INT_)
1960 		gr->show_names = ggv[5].val;
1961 	    }
1962 	    if (ggs>6 && t){
1963 	      if (ggv[6].type==_INT_)
1964 		t->disposition=ggv[6].val;
1965 	      if (ggv[6].type==_VECT){
1966 		vecteur dispv=*ggv[6]._VECTptr;
1967 		int dispvs=dispv.size();
1968 		if (dispvs)
1969 		  t->disposition=dispv[0].val;
1970 		if (dispvs>3){
1971 		  double d1=evalf_double(dispv[1],1,contextptr)._DOUBLE_val;
1972 		  double d2=evalf_double(dispv[2],1,contextptr)._DOUBLE_val;
1973 		  double d3=evalf_double(dispv[3],1,contextptr)._DOUBLE_val;
1974 		  t->resize2(d1,d2,d3);
1975 		}
1976 	      }
1977 	    }
1978 	    if (ggs>7 && t){ // read row_height and col_witdh
1979 	      if (ggv[7].type==_VECT && ggv[7]._VECTptr->size()==2){
1980 		gen & rowg=ggv[7]._VECTptr->front();
1981 		if (rowg.type==_VECT){
1982 		  vecteur & rowv = *rowg._VECTptr;
1983 		  int rowvs=giacmin(rowv.size(),t->table->rows());
1984 		  for (int i=0;i<rowvs ;++i){
1985 		    if (rowv[i].type==_INT_)
1986 		      t->table->row_height(rowv[i].val,i);
1987 		  }
1988 		}
1989 		gen & colg=ggv[7]._VECTptr->back();
1990 		if (colg.type==_VECT){
1991 		  vecteur & colv = *colg._VECTptr;
1992 		  int colvs=giacmin(colv.size(),t->table->cols());
1993 		  for (int i=0;i<colvs ;++i){
1994 		    if (colv[i].type==_INT_)
1995 		      t->table->col_width(colv[i].val,i);
1996 		  }
1997 		}
1998 	      }
1999 	    }
2000 	    if (ggs>8 && t){
2001 	      t->table->init=ggv[8];
2002 	    }
2003 	  } // end g002 of type _VECT
2004 	  g002=2;
2005 	  m00.back()=g002;
2006 	  m0.front()=gen(m00,m0.front().subtype);
2007 	  m.front()=gen(m0,m.front().subtype);
2008 	  res->filename = new string(res->name.print(contextptr)+".tab");
2009 	}
2010       }
2011     }
2012     if (res)
2013       res->set_matrix(m,false,false);
2014     res->is_spreadsheet = is_spreadsheet;
2015     res->matrix_fill_cells = matrix_fill_cells;
2016     res->spreadsheet_recompute = spreadsheet_recompute;
2017     res->matrix_symmetry=matrix_symmetry;
2018     res->update_status();
2019     if (sheet_run)
2020       res->spread_eval_interrupt();
2021     res->update_spread_graph();
2022     res->redraw();
2023     if (t)
2024       t->resize2();
2025   }
2026 
xcas_text_editor_load(Xcas_Text_Editor * & res,const std::string & s,int x,int y,int w,int h)2027   void xcas_text_editor_load(Xcas_Text_Editor * & res,const std::string & s,int x,int y,int w,int h){
2028     Fl_Text_Buffer * b = new Fl_Text_Buffer;
2029     res = new Xcas_Text_Editor(x,y,w,h,b);
2030     res->callback(History_Pack_cb_eval,0);
2031     res->textcolor(Xcas_input_color);
2032     res->color(Xcas_input_background_color);
2033     res->buffer()->add_modify_callback(style_update, res);
2034     res->buffer()->insert(0,s.c_str());
2035     res->set_gchanged();
2036     res->end();
2037   }
2038 
editeur_load(Editeur * & res,const std::string & s,int x,int y,int w,int h)2039   void editeur_load(Editeur * & res,const std::string & s,int x,int y,int w,int h){
2040     res = new Editeur(x,y,w,h,"");
2041     res->callback(History_Pack_cb_eval,0);
2042     res->editor->buffer()->insert(0,s.c_str());
2043   }
2044 
split_string(const string & s,char sep,string & before,string & after)2045   bool split_string(const string & s,char sep,string & before,string & after){
2046     int j=s.find('='),ss=s.size();
2047     if (j>0 && j<ss-1){
2048       before=s.substr(0,j);
2049       after=s.substr(j+1,ss-j-1);
2050       return true;
2051     }
2052     else
2053       return false;
2054   }
2055 
2056   // Read a widget from string s starting at position i
2057   // Return 0 on syntax error or incomplete widget otherwise
widget_load(const std::string & s,int L,int & i,GIAC_CONTEXT,int widgetw)2058   Fl_Widget * widget_load(const std::string & s,int L,int & i,GIAC_CONTEXT,int widgetw){
2059     // Get next line
2060     string line;
2061     int i_orig=i;
2062     next_line(s,L,line,i);
2063     // Check for a fltk comment
2064     if (line.size()>8 && line.substr(0,8)=="// fltk "){
2065 #ifdef HAVE_SSTREAM
2066     istringstream is(line);
2067 #else
2068     istrstream is(line.c_str());
2069 #endif
2070       string tmp,attrib;
2071       char ch;
2072       is >> tmp; // get //
2073       is >> tmp; // get fltk
2074       is >> tmp; // get widget type
2075       vector<string> v;
2076       for (;;){
2077 	ch=is.get();
2078 	if (ch=='\n' || !is || is.eof())
2079 	  break;
2080 	if (ch<=' '){
2081 	  if (!attrib.empty()){
2082 	    v.push_back(attrib);
2083 	    attrib="";
2084 	  }
2085 	}
2086 	else
2087 	  attrib += ch;
2088       }
2089       if (!attrib.empty())
2090 	v.push_back(attrib);
2091       int vs=v.size();
2092       int x=(vs>0)?atoi(v[0].c_str()):0;
2093       int y=(vs>1)?atoi(v[1].c_str()):0;
2094       int w=(vs>2)?atoi(v[2].c_str()):10;
2095       bool ortho=false;
2096       if (widgetw){
2097 	ortho=(w!=widgetw);
2098 	w=widgetw;
2099       }
2100       if (w<10)
2101 	w=10;
2102       int h0=(vs>3)?atoi(v[3].c_str()):5;
2103       if (h0<1)
2104 	h0=1;
2105       int h=h0;
2106       if (h<5)
2107 	h=5;
2108       int lsize=(vs>4)?atoi(v[4].c_str()):14;
2109       Fl_Font police=(vs>5)?Fl_Font(atoi(v[5].c_str())):FL_HELVETICA;
2110       if (police>=fonts_available)
2111 	police=FL_HELVETICA;
2112       unsigned pos=tmp.find("History_Fold"),tmps=tmp.size();
2113       if (pos>0 && pos<tmps){
2114 	next_line_nonl(s,L,line,i);
2115 	History_Fold * res= new History_Fold(x,y,w,h);
2116 	res->labelsize(lsize);
2117 	res->labelfont(police);
2118 	res->input->value(line.c_str());
2119 	next_line(s,L,line,i); // should be history_pack...
2120 	next_line(s,L,line,i); // should be [\n
2121 	res->pack->labelfont(police);
2122 	res->pack->eval=xcas::Xcas_eval;
2123 	res->pack->_insert=xcas::Xcas_pack_insert;
2124 	res->pack->_select=xcas::Xcas_pack_select;
2125 	widget_group_load(res->pack,s,L,i,contextptr);
2126 	res->fold();
2127 	res->pack->_resize_above=true;
2128 	res->redraw();
2129 	return res->pack?res:0;
2130       }
2131       pos = tmp.find("History_Pack");
2132       if (pos>0 && pos<tmps){
2133 	History_Pack * res= new History_Pack(x,y,w,h);
2134 	res->labelsize(lsize);
2135 	res->labelfont(police);
2136 	next_line(s,L,line,i); // should be [\n
2137 	res->eval=xcas::Xcas_eval;
2138 	res->_insert=xcas::Xcas_pack_insert;
2139 	res->_select=xcas::Xcas_pack_select;
2140 	widget_group_load(res,s,L,i,contextptr);
2141 	res->redraw();
2142 	// next_line(s,L,line,i);
2143 	return res;
2144       }
2145       pos = tmp.find("Tableur_Group");
2146       if (pos>0 && pos<tmps){
2147 	Tableur_Group * res= new Tableur_Group(x,y,w,h,lsize);
2148 	res->labelfont(police);
2149 	next_line_nonl(s,L,line,i);
2150 	tableur_load(res->table,line,x,y,w,h);
2151 	return res;
2152       }
2153       pos=tmp.find("Turtle");
2154       if (pos>0 && pos<tmps){
2155 	Turtle * tu=new Turtle(x,y,w,h);
2156 	tu->turtleptr=&turtle_stack(context0);
2157 	next_line(s,L,line,i); // skip []
2158 	return tu;
2159       }
2160       pos=tmp.find("Fl_Scrollbar");
2161       if (pos>0 && pos<tmps){
2162 	next_line(s,L,line,i); // skip []
2163 	return 0;
2164       }
2165       pos=tmp.find("Mouse_Position");
2166       if (pos>0 && pos<tmps){
2167 	next_line(s,L,line,i); // skip []
2168 	return new Mouse_Position(x,y,w,h,0);
2169       }
2170       pos=tmp.find("Fl_Button");
2171       unsigned pos2=tmp.find("Fl_Menu_Bar");
2172       if ( (pos>0 && pos<tmps) || (pos2>0 && pos2<tmps) ){
2173 	next_line(s,L,line,i); // skip []
2174 	return new Fl_Button(x,y,w,h);
2175       }
2176       pos=tmp.find("Scroll");
2177       if (pos>0 && pos<tmps){
2178 	next_line(s,L,line,i);
2179 	if (line!="[\n")
2180 	  return 0;
2181 	Fl_Scroll * res= new Fl_Scroll(x,y,w,h);
2182 	res->labelsize(lsize);
2183 	res->labelfont(police);
2184 	res->box(FL_FLAT_BOX);
2185 	widget_group_load(res,s,L,i,contextptr);
2186 	// reorder scrollbars at the end
2187 	int n=res->children();
2188 	if (n && dynamic_cast<Fl_Scrollbar *>(res->child(0)))
2189 	  res->add(res->child(0));
2190 	if (n && dynamic_cast<Fl_Scrollbar *>(res->child(0)))
2191 	  res->add(res->child(0));
2192 	// skip next scrollbar
2193 	next_line(s,L,line,i); // skip Fl_Scrollbar
2194 	next_line(s,L,line,i); // skip []
2195 	next_line(s,L,line,i); // skip ] (matching the [ from Fl_Scroll)
2196 	return res;
2197       }
2198       pos=tmp.find("Fl_Group");
2199       if (pos>0 && pos<tmps){
2200 	next_line(s,L,line,i);
2201 	if (line!="[\n")
2202 	  return 0;
2203 	int i2=i;
2204 	string line2;
2205 	next_line(s,L,line2,i2);
2206 	Fl_Group * res= new Fl_Group(x,y,w,h);
2207 	res->labelsize(lsize);
2208 	res->labelfont(police);
2209 	widget_group_load(res,s,L,i,contextptr);
2210 	tmps=line2.size();
2211 	pos=line2.find("Mouse_Position");
2212 	if (pos>0 && pos <tmps)
2213 	  res->clear(); // remove this group since it's a param_mouse_group
2214 	return res;
2215       }
2216       pos=tmp.find("Fl_Tile");
2217       if (pos>0 && pos<tmps){
2218 	next_line(s,L,line,i);
2219 	if (line!="[\n")
2220 	  return 0;
2221 	int i2=i;
2222 	string line2;
2223 	next_line(s,L,line2,i2);
2224 	Fl_Tile * res= new Fl_Tile(x,y,w,h);
2225 	res->labelsize(lsize);
2226 	res->labelfont(police);
2227 	widget_group_load(res,s,L,i,contextptr);
2228 	tmps=line2.size();
2229 	pos=line2.find("Mouse_Position");
2230 	if (pos>0 && pos <tmps)
2231 	  res->clear(); // remove this group since it's a param_mouse_group
2232 	pos=line2.find("Flv_Table_Gen");
2233 	// remove objects not linked inside Flv_Table_Gen
2234 	if (pos>0 && pos<tmps){
2235 	  int n=res->children();
2236 	  Flv_Table_Gen * g = dynamic_cast<Flv_Table_Gen *>(res->child(0));
2237 	  if (g){
2238 	    for (int i=n-1;i;--i){
2239 	      Fl_Widget * w = res->child(i);
2240 	      if (w==g->_goto || w==g->input || w==g->graph || w==g->graph-> mouse_param_group)
2241 		break;
2242 	      res->remove(w);
2243 	      delete w;
2244 	    }
2245 	  }
2246 	}
2247 	return res;
2248       }
2249       pos=tmp.find("Figure");
2250       if (pos>0 && pos<tmps){
2251 	next_line(s,L,line,i); // should be History_Pack
2252 	next_line(s,L,line,i); // should be [\n
2253 	Figure * fig = new Figure(x,y,w,h,lsize,false);
2254 	fig->geo->no_handle=true;
2255 	fig->geo->hp->remove_entry(0,false);
2256 	fig->geo->hp->eval_below=true;
2257 	fig->geo->hp->pretty_output=false;
2258 	fig->geo->hp->labelfont(police);
2259 	widget_group_load(fig->geo->hp,s,L,i,contextptr);
2260 	next_line(s,L,line,i);
2261 	pos=line.find("3d");// should be Geo2d or Geo3d
2262 	bool dim3= pos>0 && pos<line.size();
2263 #ifdef HAVE_LIBFLTK_GL
2264 	if (dim3){
2265 	  Graph2d3d * wid=fig->geo;
2266 	  Fl_Group * p=wid->parent();
2267 	  p->remove(wid);
2268 	  p->remove(wid->mouse_param_group);
2269 	  Fl_Group::current(p);
2270 	  fig->geo=new Geo3d(wid->x(),wid->y(),2*w/3,h-wid->labelsize(),fig->geo->hp);
2271 	  int pos=p->find(wid);
2272 	  p->insert(*fig->geo,pos);
2273 	  delete wid;
2274 	}
2275 #endif
2276 	graphic_load(fig->geo,s,L,line,i);
2277 	// fig->geo->hide();
2278 	// parse further parameters for the figure
2279 	string before,after;
2280 	double dhp=0.25,dgeo=0.5,dmp=0.25;
2281 	for (int i=5;i<vs;++i){
2282 	  if (split_string(v[i],'=',before,after)){
2283 	    if (before=="landscape" && after=="1"){
2284 	      fig->disposition=1;
2285 	    }
2286 	    if (before=="history") // percentage for History Pack
2287 	      dhp=max(0.14,atof(after.c_str()));
2288 	    if (before=="geo")
2289 	      dgeo=max(0.28,atof(after.c_str()));
2290 	    if (before=="mouse_param")
2291 	      dmp=max(0.14,atof(after.c_str()));
2292 	  }
2293 	}
2294 	fig->resize(fig->x(),fig->y(),fig->w(),fig->h(),dhp,dgeo,dmp);
2295 	if (ortho)
2296 	  fig->geo->orthonormalize();
2297 	fig->geo->hp->resize();
2298 	fig->redraw();
2299 	return fig;
2300       }
2301       pos=tmp.find("Logo");
2302       if (pos>0 && pos<tmps){
2303 	Logo * l = new Logo(x,y,w,h,lsize);
2304 	l->hp->remove_entry(0);
2305 	next_line(s,L,line,i); // should be History_Pack
2306 	next_line(s,L,line,i); // should be [\n
2307 	widget_group_load(l->hp,s,L,i,contextptr);
2308 	l->hp->resize();
2309 	turtle(contextptr).widget = l->t;
2310 	next_line_nonl(s,L,line,i); // should be Editeur
2311 	next_line_nonl(s,L,line,i);
2312 	// read size
2313 #ifdef HAVE_SSTREAM
2314     istringstream is(line);
2315 #else
2316     istrstream is(line.c_str());
2317 #endif
2318 	int taille;
2319 	is >> taille;
2320 	l->ed->editor->buffer()->remove(0,l->ed->editor->buffer()->length());
2321 	l->ed->editor->buffer()->insert(0,s.substr(i,taille).c_str());
2322 	// cb_Editeur_Exec_All(l->ed->editor,0);
2323 	i += taille ;
2324 	return l;
2325       }
2326       pos= tmp.find("Equation");
2327       if (pos>0 && pos<tmps){
2328 	next_line_nonl(s,L,line,i);
2329 	giac::gen g(line,contextptr);
2330 	bool output=true;
2331 	if (vs>6)
2332 	  output=atoi(v[6].c_str());
2333 	giac::attributs attr(lsize,output?Xcas_equation_background_color:Xcas_equation_input_background_color,output?Xcas_equation_color:Xcas_equation_input_color);
2334 	Equation * res = new Equation(x,y,w,h,"",g,attr);
2335 	res->box(FL_FLAT_BOX);
2336 	res->labelsize(lsize);
2337 	res->labelfont(police);
2338 	res->output_equation=output;
2339 	if (!output)
2340 	  res->cb_enter=History_Pack_cb_eval;
2341 	return res;
2342       }
2343       pos = tmp.find("Flv_Table_Gen");
2344       if (pos>0 && pos<tmps){
2345 	Flv_Table_Gen * res=0;
2346 	next_line_nonl(s,L,line,i);
2347 	tableur_load(res,line,x,y,w,h);
2348 	return res;
2349       }
2350       pos = tmp.find("Xcas_Text_Editor");
2351       if (pos>0 && pos<tmps){
2352 	Xcas_Text_Editor * res=0;
2353 	next_line_nonl(s,L,line,i);
2354 	// read size
2355 #ifdef HAVE_SSTREAM
2356 	istringstream is(line);
2357 #else
2358 	istrstream is(line.c_str());
2359 #endif
2360 	int taille;
2361 	is >> taille;
2362 	string tmp=localize(s.substr(i,taille),language(contextptr));
2363 	xcas_text_editor_load(res,tmp,x,y,w,h);
2364 	i += taille ;
2365 	return res;
2366       }
2367       pos = tmp.find("Editeur");
2368       if (pos>0 && pos<tmps){
2369 	Editeur * res=0;
2370 	next_line_nonl(s,L,line,i);
2371 	// read size
2372 #ifdef HAVE_SSTREAM
2373 	istringstream is(line);
2374 #else
2375 	istrstream is(line.c_str());
2376 #endif
2377 	int taille;
2378 	is >> taille;
2379 	string tmp=localize(s.substr(i,taille),language(contextptr));
2380 	editeur_load(res,tmp,x,y,w,h);
2381 	i += taille ;
2382 	return res;
2383       }
2384       pos= tmp.find("Graph2d");
2385       if (pos<=0 || pos>=tmps)
2386 	pos= tmp.find("Graphic");
2387       if (pos>0 && pos<tmps){
2388 	Graph2d * res = new Graph2d(x,y,w,h,0);
2389 	res->labelfont(police);
2390 	graphic_load(res,s,L,line,i);
2391 	return res;
2392       }
2393       pos= tmp.find("Geo2d");
2394       if (pos<=0 || pos>=tmps)
2395 	pos= tmp.find("Geometry");
2396       if (pos>0 && pos<tmps){
2397 	Geo2d * res = new Geo2d(x,y,w,h,0);
2398 	res->labelfont(police);
2399 	graphic_load(res,s,L,line,i);
2400 	return res;
2401       }
2402 #ifdef HAVE_LIBFLTK_GL
2403       pos= tmp.find("Graph3d");
2404       if (pos>0 && pos<tmps){
2405 	Graph3d * res = new Graph3d(x,y,w,h,"",0);
2406 	res->labelfont(police);
2407 	graphic_load(res,s,L,line,i);
2408 	return res;
2409       }
2410       pos= tmp.find("Geo3d");
2411       if (pos>0 && pos<tmps){
2412 	Geo3d * res = new Geo3d(x,y,w,h,0);
2413 	res->labelfont(police);
2414 	graphic_load(res,s,L,line,i);
2415 	return res;
2416       }
2417 #endif
2418       pos=tmp.find("Multiline_Input_tab");
2419       if (pos>0 && pos<tmps){
2420 	Multiline_Input_tab * res = new Multiline_Input_tab(x,y,w,h);
2421 	res->when(FL_WHEN_ENTER_KEY|FL_WHEN_NOT_CHANGED);
2422 	res->callback(xcas::History_Pack_cb_eval,0);
2423 	res->textcolor(Xcas_input_color);
2424 	res->labelfont(police);
2425 	res->textfont(police);
2426 	next_line_nonl(s,L,line,i);
2427 	line=localize(line,language(contextptr));
2428 	res->value(line.c_str());
2429 	res->set_changed();
2430 	return res;
2431       }
2432       pos=tmp.find("Input");
2433       if (pos>0 && pos<tmps){
2434 	Comment_Multiline_Input * res = new Comment_Multiline_Input(x,y,w,h);
2435 	next_line_nonl(s,L,line,i);
2436 	res->value(line.c_str());
2437 	res->labelfont(police);
2438 	res->textfont(police);
2439 	res->callback((Fl_Callback*)Comment_cb_eval);
2440 	res->when(FL_WHEN_ENTER_KEY|FL_WHEN_NOT_CHANGED);
2441 	res->set_changed();
2442 	res->textcolor(Xcas_comment_color);
2443 	return res;
2444       }
2445       pos=tmp.find("Gen_Output");
2446       if (pos>0 && pos<tmps){
2447 	Gen_Output * res = new Gen_Output(x,y,w,h);
2448 	res->textcolor(FL_BLACK);
2449 	res->labelfont(police);
2450 	res->textfont(police);
2451 	next_line_nonl(s,L,line,i);
2452 	if (line.size()>=65536)
2453 	  res->value("Object_too_large");
2454 	else
2455 	  res->value(line.c_str());
2456 	return res;
2457       }
2458       pos=tmp.find("Output");
2459       if (pos>0 && pos<tmps){
2460 	Log_Output * res = new Log_Output(x,y,w,h0);
2461 	res->textcolor(Xcas_log_color);
2462 	res->labelfont(police);
2463 	res->textfont(police);
2464 	next_line_nonl(s,L,line,i);
2465 	res->value(line.c_str());
2466 	return res;
2467       }
2468       pos=tmp.find("Gen_Value_Slider");
2469       if (pos>0 && pos<tmps){
2470 	next_line_nonl(s,L,line,i);
2471 #ifdef HAVE_SSTREAM
2472     istringstream is(line);
2473 #else
2474     istrstream is(line.c_str());
2475 #endif
2476 	double m,M,val,step=0.1;
2477 	int pos;
2478 	string name;
2479 	// is >> name ; // this is not the name but //, name is read below
2480 	try {
2481 	  is >> pos >> m >> M >> val >> name >> step;
2482 	} catch(std::runtime_error & e) {
2483 	}
2484 	Gen_Value_Slider * res=new Gen_Value_Slider(x,y,w-3*lsize,h,pos,m,M,(M-m)/100.,name);
2485 	res->value(val);
2486 	res->step(step);
2487 	res->label(name.c_str());
2488 	res->labelfont(police);
2489 	res->labelsize(lsize);
2490 	return res;
2491       }
2492     } // end if line begins with // fltk
2493     else
2494       i=i_orig; // restore original position
2495     return 0;
2496   }
2497 
Xcas_pack_select(const xcas::History_Pack * pack,int sel_begin,int sel_end)2498   const char * Xcas_pack_select(const xcas::History_Pack * pack,int sel_begin,int sel_end) {
2499     int n=pack->children();
2500     static std::string s;
2501     s="";
2502     if (n){
2503       int s1=sel_begin,s2=sel_end;
2504       if (s1>s2){ s1=sel_end; s2=sel_begin; }
2505       if (s2>=n) s2=n-1;
2506       if (s1<=0) s1=0;
2507       for (int i=s1;i<=s2;i++){
2508 	s += widget_sprint(pack->child(i));
2509 	if (i<s2)
2510 	  s += ",\n";
2511       }
2512     }
2513     return s.c_str();
2514   }
2515 
Xcas_pack_insert(xcas::History_Pack * pack,const char * chaine,int length,int before_position)2516   int Xcas_pack_insert(xcas::History_Pack * pack,const char * chaine,int length,int before_position) {
2517     if (!pack)
2518       return 0;
2519     Fl_Group::current(pack);
2520     context * contextptr = pack->contextptr;
2521     int n=pack->children();
2522     if (!n)
2523       before_position=n;
2524     else
2525       if (before_position<0 || before_position>=n) before_position=n-1;
2526     int l0=strlen(chaine);
2527     if (length>l0)
2528       l0=length;
2529     string s;
2530     for (int i0=0;i0<l0;i0++){
2531       // if (chaine[i0]!=13)
2532 	s += chaine[i0];
2533     }
2534     // cerr << s << '\n';
2535     int L=s.size(),i=0;
2536     // Check for an HTML link
2537     if (L>8 && (s.substr(0,6)=="http:/" || s.substr(0,7)=="https:/" || s.substr(0,6)=="file:/") ){
2538       // find # position, then create normal line for +, slider for *
2539       int pos=s.find('#');
2540       if (pos>0 && pos<L){
2541 	int police=pack->labelfont(),lsize=pack->labelsize(),y=before_position;
2542 	bool finished=false;
2543 	while (!finished){
2544 	  int nextpos=s.find('&',pos+1);
2545 	  if (nextpos > L){
2546 	    nextpos=L;
2547 	    finished=true;
2548 	  }
2549 	  if (nextpos<pos+2)
2550 	    break;
2551 	  string txt=s.substr(pos+2,nextpos-pos-2);
2552 	  txt=html_filter(txt);
2553 	  if (s[pos+1]=='*'){
2554 	    gen g(txt,contextptr);
2555 	    if (g.type==_VECT && g._VECTptr->size()>=5){
2556 	      txt="assume("+g[0].print(contextptr)+"=["+g[1].print(contextptr)+","+g[2].print(contextptr)+","+g[3].print(contextptr)+","+g[4].print(contextptr)+"])";
2557 	    }
2558 	  }
2559 	  else {
2560 	    if (s[pos+1]!='+'){
2561 	      pos=nextpos;
2562 	      continue;
2563 	    }
2564 	    if (s.size()>pos+3 && s[pos+2]=='/' && s[pos+3]=='/'){
2565 	      txt=replace(txt,'\n',char(163)); // should count \n and ajust size
2566 	      txt="// fltk 7Fl_Tile 14 68 845 25 18 0\n[\n// fltk N4xcas23Comment_Multiline_InputE 14 68 845 24 18 0\n"+txt.substr(2,txt.size()-2)+"\n,\n// fltk N4xcas10Log_OutputE 14 93 845 1 18 0\n\n]";
2567 	    }
2568 	    else {
2569 	      int pos=txt.find('\n');
2570 	      if (pos>0 && pos<txt.size()){
2571 		txt="// fltk 7Fl_Tile 14 68 845 254 18 0\n[\n// fltk N4xcas16Xcas_Text_EditorE 14 68 845 253 18 0\n"+print_INT_(txt.size())+" ,\n"+txt+",\n// fltk N4xcas10Log_OutputE 14 321 845 1 18 0\n\n]";
2572 	      }
2573 	    }
2574 	  }
2575 	  Xcas_pack_insert(pack,txt.c_str(),txt.size(),y);
2576 	  ++y;
2577 	  pos=nextpos;
2578 	}
2579 	pack->redraw();
2580 	// next_line(s,L,line,i);
2581 	return 1;
2582       }
2583     }
2584 
2585     Fl_Widget * o;
2586     for (;i<L;++before_position){
2587       if (i+7<L && s.substr(i,7)=="// fltk"){
2588 	if ( (o=widget_load(s,L,i,contextptr,pack->w()-pack->_printlevel_w) )){
2589 	  if (Fl_Group * g=dynamic_cast<Fl_Group *>(o))
2590 	    pack->add_entry(before_position,g);
2591 	  else
2592 	    pack->add_entry(before_position,o);
2593 	  if (i<L-1 && s[i]==',' && s[i+1]=='\n')
2594 	    i += 2;
2595 	  continue;
2596 	}
2597       } // end if (i+7<L)
2598       if (i+11<L && s.substr(i,10)=="// context"){
2599 	i+=11;
2600 	int nchar=0;
2601 	while (i<L && s[i]>='0' && s[i]<='9'){
2602 	  nchar *= 10;
2603 	  nchar += s[i]-'0';
2604 	  ++i;
2605 	}
2606 	if (nchar<=L-i-1){
2607 	  string tmps=s.substr(i+1,nchar);
2608 	  gen replace;
2609 	  if (!recovery_mode)
2610 	    unarchive_session_string(tmps,-1,replace,contextptr);
2611 	  i+=nchar+2;
2612 	  continue;
2613 	}
2614       }
2615       // Now add line in a multiline_input_tab entry
2616       string line;
2617       next_line_nonl(s,L,line,i);
2618       int ls=line.size();
2619       if (ls>2 && line[ls-1]==';' && line[ls-2]==';')
2620 	line = line.substr(0,ls-1);
2621       if (ls>2 && line[0]=='/' && line[1]=='/')
2622 	line = "/*"+line.substr(1,ls-1)+"*/";
2623       if (ls>4 && line[0]=='/' && line[1]=='*' && line[ls-1]=='/' && line[ls-2]=='*'){
2624 	line=line.substr(2,ls-4);
2625 	Comment_Multiline_Input * w = dynamic_cast<Comment_Multiline_Input *>(new_comment_input(max(pack->w()-pack->_printlevel_w,1),pack->labelsize()+10));
2626 	if (w){
2627 	  w->value(line.c_str());
2628 	  w->set_changed();
2629 	  pack->add_entry(before_position,w);
2630 	}
2631       }
2632       else {
2633 #if 0
2634 	Multiline_Input_tab * w = dynamic_cast<Multiline_Input_tab *>(new_question_multiline_input(max(pack->w()-pack->_printlevel_w,1),pack->labelsize()+10));
2635 #else
2636 	// requires call to update() to be commented in History_Pack::handle(int event)
2637 	// for FL_PASTE event
2638 	Xcas_Text_Editor * w = dynamic_cast<Xcas_Text_Editor *>(new_question_editor(max(pack->w()-pack->_printlevel_w,1),pack->labelsize()+14));
2639 #endif
2640 	if (w){
2641 	  w->value(line.c_str());
2642 	  w->set_changed();
2643 	  pack->add_entry(before_position,(Fl_Widget*) w);
2644 	  w->resize_nl_before(1);
2645 	}
2646       }
2647     } // end for
2648     change_group_fontsize(pack,pack->labelsize());
2649     set_context(pack,contextptr);
2650     parent_redraw(pack);
2651     return i;
2652   }
2653 
in_Xcas_fltk_interactive(const giac::gen & g,const giac::context * contextptr)2654   giac::gen in_Xcas_fltk_interactive(const giac::gen & g,const giac::context * contextptr){
2655     if (is_zero(g,contextptr))
2656       return Xcas_DispG?Xcas_DispG->plot_instructions:undef;
2657     if (g.type==_SYMB){
2658       unary_function_ptr & u=g._SYMBptr->sommet;
2659       gen f=g._SYMBptr->feuille;
2660       if (u==at_pnt){
2661 	//	Xcas_DispG_Window->show();
2662 	// Xcas_Main_Window->show();
2663 	redraw_turtle=1;
2664 	if (Xcas_DispG && f.subtype!=_LOGO__VECT)
2665 	  Xcas_DispG->add(g);
2666 	return 1;
2667       }
2668       if (u==at_equal){
2669 	if (Xcas_DispG) Xcas_DispG->add(g);
2670 	return 1;
2671       }
2672       if (u==at_erase){
2673 	// Xcas_DispG_Window->show();
2674 	// Xcas_Main_Window->show();
2675 	if (Xcas_DispG) Xcas_DispG->clear();
2676 	return 1;
2677       }
2678       if (u==at_Pictsize){
2679 	if (Xcas_DispG){
2680 	  // plot_instructionsw=Xcas_DispG->w();
2681 	  // plot_instructionsh=Xcas_DispG->h();
2682 	  gnuplot_xmax=Xcas_DispG->window_xmax;
2683 	  gnuplot_xmin=Xcas_DispG->window_xmin;
2684 	  gnuplot_ymax=Xcas_DispG->window_ymax;
2685 	  gnuplot_ymin=Xcas_DispG->window_ymin;
2686 	  return makevecteur(Xcas_DispG->w(),Xcas_DispG->h());
2687 	}
2688 	return makevecteur(0,0);
2689       }
2690       if (u==at_RclPic && f.type==_VECT){
2691 	// Xcas_DispG_Window->show();
2692 	// Xcas_Main_Window->show();
2693 	if (Xcas_DispG) Xcas_DispG->add(*f._VECTptr);
2694 	return 1;
2695       }
2696       if (u==at_RplcPic && f.type==_VECT){
2697 	// Xcas_DispG_Window->show();
2698 	// Xcas_Main_Window->show();
2699 	if (Xcas_DispG){
2700 	  Xcas_DispG->clear();
2701 	  Xcas_DispG->add(*f._VECTptr);
2702 	}
2703 	return 1;
2704       }
2705       if (u==at_Pause){
2706 	if (f.type==_STRNG)
2707 	  xcas_paused=*f._STRNGptr;
2708 	else
2709 	  xcas_paused="Paused";
2710 	for (int i=0;i<360000;++i){
2711 	  if (xcas_paused.empty())
2712 	    break;
2713 	  usleep(10000);
2714 	}
2715 	return 1;
2716       }
2717       if (u==at_ClrIO){
2718 	if (Xcas_PrintG)
2719 	  Xcas_PrintG->set_data(string2gen("",false));
2720 	* logptr(contextptr) << "";
2721 	return 1;
2722       }
2723       if (u==at_DispG){
2724 	if (Xcas_DispG_Window) show_xcas_dispg=2;
2725 #ifndef WIN32
2726 	if (Xcas_Main_Window) Xcas_Main_Window->show();
2727 #endif
2728 	return 1;
2729       }
2730       if (u==at_DispHome){
2731 	if (Xcas_DispG_Window) show_xcas_dispg=1;
2732 	return 1;
2733       }
2734       /*
2735       if (u==at_Output){
2736 	user_screen_io_x=f[0].val;
2737 	user_screen_io_y=f[1].val;
2738 	f=f[2];
2739       }
2740       if (u==at_Output || u==at_print){
2741 	gen tmp=Eqw_compute_size(f,Eqw_history->attr,Eqw_history->w());
2742 	eqwdata v=Eqw_total_size(tmp);
2743 	if (Eqw_history->data.type!=_VECT && Eqw_history->data.type!=_EQW){
2744 	  Eqw_history->data=tmp;
2745 	  user_screen_io_y=v.y; // top of next writing
2746 	}
2747 	else {
2748 	  eqwdata w=Eqw_total_size(Eqw_history->data);
2749 	  int h=w.dy,y=w.y;
2750 	  tmp=Eqw_translate(tmp,user_screen_io_x,user_screen_io_y-v.y-v.dy);
2751 	  v=Eqw_total_size(tmp);
2752 	  Eqw_vertical_adjust(v.dy,v.y,h,y);
2753 	  user_screen_io_y = y;
2754 	  user_screen_io_x = 0;
2755 	  vecteur res;
2756 	  if (Eqw_history->data.type==_EQW)
2757 	    res.push_back(Eqw_history->data);
2758 	  else
2759 	    res=vecteur(Eqw_history->data._VECTptr->begin(),Eqw_history->data._VECTptr->end()-1);
2760 	  res.push_back(tmp);
2761 	  res.push_back(eqwdata(max(w.dx,v.dx),h,0,y,Eqw_history->attr,at_makevector,0));
2762 	  gen gg=gen(res,_EQW__VECT);
2763 	  Eqw_history->data=gg;
2764 	}
2765 	Eqw_history->setscroll();
2766 	return f;
2767       }
2768       */
2769     }
2770     return 1;
2771   }
2772 
Xcas_fltk_interactive(const giac::gen & g,GIAC_CONTEXT)2773   giac::gen Xcas_fltk_interactive(const giac::gen & g,GIAC_CONTEXT){
2774 #ifdef HAVE_LIBPTHREAD
2775     // cerr << "xcas lock" << g << '\n';
2776     pthread_mutex_lock(&interactive_mutex);
2777 #endif
2778     if (block_signal){
2779       cerr << "blocked " << g << '\n';
2780 #ifdef HAVE_LIBPTHREAD
2781       pthread_mutex_unlock(&interactive_mutex);
2782 #endif
2783       return zero;
2784     }
2785     gen res=in_Xcas_fltk_interactive(g,contextptr);
2786     // FIXME change interactive for context, like input
2787 #ifdef HAVE_LIBPTHREAD
2788     // cerr << "xcas unlock" << '\n';
2789     pthread_mutex_unlock(&interactive_mutex);
2790 #endif
2791     return res;
2792   }
2793 
in_Xcas_fltk_getKey(const giac::gen & g,giac::context * contextptr)2794   giac::gen in_Xcas_fltk_getKey(const giac::gen & g,giac::context * contextptr){
2795     int ch=Fl::event_key();
2796     if (Fl::event_key(ch))
2797       return ch;
2798     else
2799       return -ch;
2800   }
2801 
Xcas_fltk_getKey(const giac::gen & g,GIAC_CONTEXT)2802   giac::gen Xcas_fltk_getKey(const giac::gen & g,GIAC_CONTEXT){
2803     if (block_signal){
2804       return zero;
2805     }
2806     gen res;
2807     if (is_minus_one(g)){
2808       Fl::lock();
2809       res=in_Xcas_fltk_getKey(g,0);
2810       Fl::unlock();
2811     }
2812     else
2813       res=Xcas_fltk_input(makevecteur(at_getKey,g),contextptr);
2814     // FIXME change interactive for context, like input
2815     if (is_inf(res))
2816       setsizeerr();
2817     return res;
2818   }
2819 
2820   vector <Fl_Browser *> vbrowser;
2821   vector<int> gbrowser;
cb_plotfltk_browser(Fl_Browser * b,void *)2822   void cb_plotfltk_browser(Fl_Browser * b,void *){
2823     if (b->value()){
2824       vector <Fl_Browser *>::const_iterator it=vbrowser.begin(),itend=vbrowser.end();
2825       for (;it!=itend;++it){
2826 	if (*it==b)
2827 	  break;
2828       }
2829       if (it!=itend)
2830 	gbrowser[it-vbrowser.begin()]=b->value();
2831     }
2832   }
2833 
nlines(const string & s)2834   static int nlines(const string & s){
2835     int res=1;
2836     int ns=s.size()-1;
2837     for (int i=0;i<ns;++i){
2838       if (s[i]=='\n') ++res;
2839     }
2840     return res;
2841   }
2842 
2843   // Given a vector v describing an input form, return
makeform(const vecteur & v0,GIAC_CONTEXT)2844   gen makeform(const vecteur & v0,GIAC_CONTEXT) {
2845     vecteur v;
2846     aplatir(v0,v);
2847     if (v.size()==1 && v.front().is_symb_of_sommet(at_output)){
2848       fl_message("%s",eval(v.front()._SYMBptr->feuille,contextptr).print(contextptr).c_str());
2849       return plus_one;
2850     }
2851     if (v.size()==1 && v.front().type==_STRNG){
2852       v.push_back(identificateur("_input_"));
2853       // CERR << v << '\n';
2854     }
2855     if (!v.empty() && v.front()==at_getKey){
2856       Fl_Widget * foc=Fl::focus();
2857       static Fl_Window * getkeywin=0;
2858       static Fl_Button * getkeybut = 0;
2859       static Fl_Input * getkeyin = 0;
2860       static Fl_Multiline_Output * getkeyout = 0;
2861       if (!getkeywin){
2862 	Fl_Group::current(0);
2863 	getkeywin=new Fl_Window(50,50,200,200);
2864 	getkeywin->label(gettext("Press a key"));
2865 	getkeyout= new Fl_Multiline_Output(2,24,196,170);
2866 	getkeybut=new Fl_Button(2,2,96,20);
2867 	getkeybut->label(gettext("Cancel"));
2868 	getkeybut->shortcut("^[");
2869 	getkeyin = new Fl_Input(102,2,96,20);
2870 	getkeyin->when(FL_WHEN_CHANGED);
2871 	getkeywin->end();
2872 	getkeywin->resizable(getkeywin);
2873       }
2874       string msg(gettext("Press a key\n"));
2875       int vs=v.size();
2876       for (int i=1;i<vs;++i){
2877 	if (v[i].type==_STRNG)
2878 	  msg += *v[i]._STRNGptr;
2879 	else
2880 	  msg += v[i].print(contextptr);
2881 	if (i==vs-1)
2882 	  break;
2883 	msg += '\n';
2884       }
2885       getkeyout->value(msg.c_str());
2886       getkeyin->value("");
2887       getkeywin->show();
2888       getkeywin->set_modal();
2889       Fl::focus(getkeyin);
2890       if (Xcas_Main_Window){
2891 	Xcas_Main_Window->redraw();
2892       }
2893       Fl::flush();
2894       gen res=undef;
2895       for (;;){
2896 	Fl_Widget *o = Fl::readqueue();
2897 	if (!o) Fl::wait();
2898 	if (o==getkeybut)
2899 	  res=unsigned_inf;
2900 	if (o==getkeybut || o==getkeywin){
2901 	  break;
2902 	}
2903 	if (o==getkeyin){
2904 	  int l=strlen(getkeyin->value());
2905 	  if (l){
2906 	    res=getkeyin->value()[l-1];
2907 	    break;
2908 	  }
2909 	}
2910       }
2911       getkeywin->hide();
2912       Fl::focus(foc);
2913       return res;
2914     }
2915     int initw,inith;
2916     if (Xcas_input_focus && Xcas_input_focus->window()){
2917       initw=(Xcas_input_focus->window()->w()*3)/4;
2918       inith=(Xcas_input_focus->window()->h()*3)/4;
2919     }
2920     else {
2921       initw = 240;
2922       inith = 320;
2923     }
2924     Fl_Group::current(0);
2925     static Fl_Window * Plotfltk_w = new Fl_Window(initw,inith);
2926     int r;
2927     vecteur res;
2928     Fl_Window * w = Plotfltk_w ;
2929     w->end();
2930     int taillew=w->w(),tailleh=w->h();
2931     if (taillew<100 || tailleh<100){
2932       if (taillew<100)
2933 	taillew=100;
2934       if (tailleh<100)
2935 	tailleh=100;
2936       w->resize(w->x(),w->y(),taillew,tailleh);
2937     }
2938     Fl_Group::current(w);
2939     static Fl_Button * button0 = new Fl_Button(2+(2*taillew)/3, 0, taillew/4, 20);
2940     button0->shortcut("^[");
2941     button0->resize(2+(2*taillew)/3, 0, taillew/4, 20);
2942     button0->label(gettext("Cancel"));
2943     static Fl_Button * button1 = new Fl_Return_Button(2+taillew/3, 0, taillew/4, 20);
2944     button1->label(gettext("OK"));
2945     button1->resize(2+taillew/3, 0, taillew/4, 20);
2946     static Fl_Button * button2 = new Fl_Button(2, 0, taillew/4, 20);
2947     button2->label(gettext("STOP"));
2948     button2->resize(2, 0, taillew/4, 20);
2949     // Now parse v
2950     int current_y=22;
2951     vector<Fl_Input *> vinput;
2952     vector<Fl_Output *> voutput;
2953     vector<string *> labels;
2954     /*
2955     vector <Fl_Menu_Button *> vpopup;
2956     vector < vector<Fl_Menu_Item * > > vpopupitem;
2957     */
2958     const_iterateur it=v.begin(),itend=v.end();
2959     bool focused=false;
2960     for (;it!=itend;++it){
2961       if (it->type==_IDNT || it->is_symb_of_sommet(at_of) || it->is_symb_of_sommet(at_at)){
2962 	Fl_Input * o;
2963 	string * l=new string (it->print(contextptr));
2964 	labels.push_back(l);
2965 	int taille=4+int(fl_width(l->c_str()));
2966 	if (taille>taillew/2)
2967 	  taille=taillew/2;
2968 	int yadd=6+nlines(*l)*14;
2969 	o=new Fl_Input(taille,current_y,taillew-taille,yadd,l->c_str());
2970 	vinput.push_back(o);
2971 	if (!focused){
2972 	  focused=true;
2973 	  Fl::focus(o);
2974 	}
2975 	current_y += yadd;
2976 	continue;
2977       }
2978       if ( (it->type==_STRNG) && it+1!=itend && ((it+1)->type==_IDNT || (it+1)->is_symb_of_sommet(at_at) || (it+1)->is_symb_of_sommet(at_of)) ){
2979 	Fl_Input * o;
2980 	string * l=new string (*it->_STRNGptr);
2981 	labels.push_back(l);
2982 	int yadd=6+nlines(*l)*14;
2983 	int taille=4+int(fl_width(l->c_str()));
2984 	if (taille>taillew/2)
2985 	  taille=taillew/2;
2986 	o=new Fl_Input(taille,current_y,taillew-taille,yadd,l->c_str());
2987 	++it;
2988 	vinput.push_back(o);
2989 	if (!focused){
2990 	  focused=true;
2991 	  Fl::focus(o);
2992 	}
2993 	current_y += yadd;
2994 	continue;
2995       }
2996       if (it->type!=_SYMB)
2997 	continue;
2998       unary_function_ptr & u=it->_SYMBptr->sommet;
2999       gen g=it->_SYMBptr->feuille;
3000       if ( (u==at_click) || (u==at_Request) ) {
3001 	vecteur vg(gen2vecteur(g));
3002 	if (vg.empty())
3003 	  continue;
3004 	Fl_Input * o;
3005 	gen tmps=vg[0].eval(eval_level(contextptr),contextptr);
3006 	string * l=new string (tmps.type==_STRNG?*tmps._STRNGptr:tmps.print(contextptr));
3007 	labels.push_back(l);
3008 	int taille=4+int(fl_width(l->c_str()));
3009 	if (taille>taillew/2)
3010 	  taille=taillew/2;
3011 	o=new Fl_Input(taille,current_y,taillew-taille,20,l->c_str());
3012 	if (vg.size()>1&& u!=at_Request)
3013 	  o->value(vg[1].eval(eval_level(contextptr),contextptr).print(contextptr).c_str());
3014 	vinput.push_back(o);
3015 	if (!focused){
3016 	  focused=true;
3017 	  Fl::focus(o);
3018 	}
3019 	current_y +=20;
3020 	continue;
3021       }
3022       if ( (u==at_output) || (u==at_Text) || (u==at_Title) ){
3023 	g=g.eval(eval_level(contextptr),contextptr);
3024 	Fl_Output * o;
3025 	if (u==at_Title){
3026 	  string * l=new string (g.type==_STRNG?*g._STRNGptr:g.print(contextptr));
3027 	  labels.push_back(l);
3028 	  o=new Fl_Output(taillew/2-40,current_y,0,20,l->c_str());
3029 	  o->value("");
3030 	}
3031 	else {
3032 	  o=new Fl_Output(40,current_y,taillew-60,20,"");
3033 	  o->value((g.type==_STRNG?*g._STRNGptr:g.print(contextptr)).c_str());
3034 	}
3035 	voutput.push_back(o);
3036 	current_y += 20;
3037 	continue;
3038       }
3039       /*
3040       if (u==at_popup){
3041 	vecteur vg(gen2vecteur(g));
3042 	if (vg.empty())
3043 	  continue;
3044 	Fl_Menu_Button * o=new Fl_Menu_Button(10,current_y,100,20,vg[0].print(contextptr).c_str());
3045 	vpopup.push_back(o);
3046 	vector<Fl_Menu_Item *> tmpmenu;
3047 	const iterateur tmpit=vg.begin()+1,tmpend=vg.end();
3048 	int y=current_y;
3049 	for (;tmpit!=tmpend;++tmpit){
3050 	  Fl_Menu_Item * o=new Fl_Menu_Item(10,y,100,15,tmpit->print(contextptr).c_str());
3051 	  tmpmenu.push_back(o);
3052 	  y +=15;
3053 	}
3054 	vpopupitem.push_back(tmpmenu);
3055 	o->end();
3056 	current_y +=20;
3057 	continue;
3058       }
3059       */
3060       if ( (u==at_choosebox) || (u==at_DropDown) || (u==at_Popup) ){
3061 	vecteur vg(gen2vecteur(g));
3062 	if (vg.size()==2)
3063 	  vg.insert(vg.begin(),string2gen("",false));
3064 	if (vg.size()!=3)
3065 	  continue;
3066 	int hs=64;
3067 	if (vg[1].type==_VECT)
3068 	  hs=vg[1]._VECTptr->size()*16+5;
3069 	hs=min(hs,160);
3070 	Fl_Browser * o =new Fl_Browser(50, current_y+16, 180, hs);
3071 	current_y += hs+16;
3072         o->type(2);
3073 	string * l=new string (vg[0].type==_STRNG?*vg[0]._STRNGptr:vg[0].print(contextptr));
3074 	labels.push_back(l);
3075         o->label(l->c_str());
3076 	o->align(FL_ALIGN_TOP);
3077         o->callback((Fl_Callback*)cb_plotfltk_browser);
3078 	vbrowser.push_back(o);
3079 	gbrowser.push_back(0);
3080 	vecteur wg(gen2vecteur(vg[1]));
3081 	const_iterateur jt=wg.begin(),jtend=wg.end();
3082 	for (;jt!=jtend;++jt){
3083 	  o->add(jt->print(contextptr).c_str());
3084 	}
3085 	gen g=protecteval(vg[2],eval_level(contextptr),contextptr);
3086 	if ( (g.type==_INT_) && (g.val>0) && (g.val<=int(wg.size())) )
3087 	  o->value(g.val);
3088 	continue;
3089       }
3090     }
3091     Xcas_Main_Window->redraw();
3092     w->Fl_Widget::resize(w->x(),w->y(),taillew,current_y);
3093     w->resizable(w);
3094     w->set_modal();
3095     w->show();
3096     for (;;) {
3097       Fl_Widget *o = Fl::readqueue();
3098       if (!o) Fl::wait();
3099       else {
3100 	if (o == button0) {r = 0; break;}
3101 	if (o == button1) {r = 1; break;}
3102 	if (o == button2) {r = 2; break;}
3103 	if (o == w) { r=0; break; }
3104       }
3105     }
3106     w->hide();
3107     bool request=false;
3108     gen gtmp("ok",contextptr);
3109     res.push_back(symb_sto(r,gtmp));
3110     // store results
3111     it=v.begin();
3112     int ibrowser=0;
3113     vector<Fl_Input *>::const_iterator vinput_it=vinput.begin();
3114     gen resadd,tmp;
3115     for (;it!=itend;++it){
3116       if (it->type==_IDNT || it->is_symb_of_sommet(at_at) || it->is_symb_of_sommet(at_of)){
3117 	if (it+1!=itend && *(it+1)==1)
3118 	  resadd=string2gen((*vinput_it)->value(),false);
3119 	else {
3120 	  try {
3121 	    resadd=gen((*vinput_it)->value(),contextptr);
3122 	  }
3123 	  catch (std::runtime_error & e){
3124 	    resadd=string2gen(e.what(),false);
3125 	  }
3126 	}
3127 	tmp=*it;
3128 	res.push_back(symb_sto(resadd,tmp));
3129 	++vinput_it;
3130       }
3131       if (it->type!=_SYMB)
3132 	continue;
3133       unary_function_ptr & u=it->_SYMBptr->sommet;
3134       gen & g=it->_SYMBptr->feuille;
3135       if ( (u==at_click) || (u==at_Request) ){
3136 	vecteur vg(gen2vecteur(g));
3137 	try {
3138 	  if (vg.size()>3|| u==at_Request){
3139 	    request=true;
3140 	    resadd=string2gen((*vinput_it)->value(),false);
3141 	  }
3142 	  else
3143 	    resadd=gen((*vinput_it)->value(),contextptr);
3144 	}
3145 	catch (std::runtime_error & e){
3146 	  resadd=string2gen(e.what(),false);
3147 	}
3148 	if (u==at_Request && vg.size()>1){
3149 	  res.push_back(symb_sto(resadd,vg[1]));
3150 	  request=true;
3151 	}
3152 	else {
3153 	  if (vg.size()>2)
3154 	    res.push_back(symb_sto(resadd,vg[2]));
3155 	}
3156 	++vinput_it;
3157 	continue;
3158       }
3159       if ( (u==at_choosebox) || (u==at_DropDown) || (u==at_Popup) ){
3160 	vecteur vg(gen2vecteur(g));
3161 	if (vg.size()==2)
3162 	  vg.insert(vg.begin(),zero);
3163 	if (vg.size()!=3)
3164 	  continue;
3165 	res.push_back(symb_sto(gbrowser[ibrowser],vg[2]));
3166 	++ibrowser;
3167 	continue;
3168       }
3169     }
3170     // delete widgets
3171     {
3172       vector<Fl_Input *>::const_iterator jt=vinput.begin(),jtend=vinput.end();
3173       for (;jt!=jtend;++jt){
3174 	w->Fl_Group::remove(*jt);
3175 	delete *jt;
3176       }
3177       vinput.clear();
3178     }
3179     {
3180       vector<Fl_Output *>::const_iterator jt=voutput.begin(),jtend=voutput.end();
3181       for (;jt!=jtend;++jt){
3182 	w->Fl_Group::remove(*jt);
3183 	delete *jt;
3184       }
3185       voutput.clear();
3186     }
3187     {
3188       vector<Fl_Browser *>::const_iterator jt=vbrowser.begin(),jtend=vbrowser.end();
3189       for (;jt!=jtend;++jt){
3190 	w->Fl_Group::remove(*jt);
3191 	delete *jt;
3192       }
3193       vbrowser.clear();
3194     }
3195     {
3196       vector<string *>::const_iterator jt=labels.begin(),jtend=labels.end();
3197       for (;jt!=jtend;++jt){
3198 	delete *jt;
3199       }
3200       labels.clear();
3201     }
3202     /*
3203     {
3204       vector<Fl_Menu_Button *>::const_iterator jt=vpopup.begin(),jtend=vpopup.end();
3205       for (;jt!=jtend;++jt){
3206 	w->Fl_Group::remove(*jt);
3207 	delete *jt;
3208       }
3209     }
3210     {
3211       vector< vector<Fl_Menu_Item *> >::const_iterator jt=vpopupitem.begin(),jtend=vpopupitem.end();
3212       for (;jt!=jtend;++jt){
3213 	vector<Fl_Menu_Item *>::const_iterator jtt=jt->begin(),jttend=jt->end();
3214 	for (;jtt!=jttend;+jtt)
3215 	w->Fl_Group::remove(*jt);
3216 	  delete *jtt;
3217       }
3218     }
3219     */
3220     // delete button1;
3221     // delete button0;
3222     // delete w;
3223     if (r==2)
3224       return 0;
3225     if (request)
3226       return res;
3227     if (r==0)
3228       return undef;
3229     else
3230       return res;
3231   }
3232 
3233   // FIXME: forms should work under win32!!
3234 #if defined WIN32 || defined __APPLE__
Xcas_fltk_input(const giac::gen & arg,const giac::context * contextptr)3235   giac::gen Xcas_fltk_input(const giac::gen & arg,const giac::context * contextptr){
3236     Fl::lock();
3237     if (Xcas_DispG) Xcas_DispG->waiting_click_value=arg;
3238     Fl::unlock();
3239     thread_eval_status(3,contextptr);
3240     for (;;){
3241       if (thread_eval_status(contextptr)==1)
3242 	break;
3243       usleep(10000);
3244     }
3245     Fl::lock();
3246     gen res=Xcas_DispG?Xcas_DispG->waiting_click_value:undef;
3247     Fl::unlock();
3248     return res.eval(eval_level(contextptr),contextptr);
3249     // return res;
3250   }
3251 
Xcas_fltk_inputform(const gen & args,const giac::context * contextptr)3252   gen Xcas_fltk_inputform(const gen & args,const giac::context * contextptr){
3253     return Xcas_fltk_input(args,contextptr);
3254   }
3255 
3256 #else
3257 
Xcas_fltk_inputform(const gen & args,const giac::context * contextptr)3258   gen Xcas_fltk_inputform(const gen & args,const giac::context * contextptr){
3259     if (args.type==_VECT && args._VECTptr->empty())
3260       return Xcas_fltk_inputform(identificateur("tmp_input"),contextptr);
3261     vecteur v(inputform_pre_analysis(args,contextptr));
3262     Fl::lock();
3263     gen res=makeform(v,contextptr);
3264     Fl::unlock();
3265     return inputform_post_analysis(v,res,contextptr);
3266   }
3267 
Xcas_fltk_input(const giac::gen & arg,const giac::context * contextptr)3268   giac::gen Xcas_fltk_input(const giac::gen & arg,const giac::context * contextptr){
3269     vecteur v(inputform_pre_analysis(arg,contextptr));
3270     if (v.empty()){
3271       Fl::lock();
3272       if (Xcas_DispG_Window) show_xcas_dispg=2;
3273       if (Xcas_DispG_Cancel) Xcas_DispG_Cancel->show();
3274       if (Xcas_DispG) Xcas_DispG->waiting_click=true;
3275       Fl::unlock();
3276       for (;;) {
3277 #ifdef HAVE_LIBPTHREAD
3278 	usleep(10000);
3279 #else
3280 	Fl::wait();
3281 #endif // HAVE_LIBPTHREAD
3282 	Fl::lock();
3283 	bool wait= (Xcas_DispG && Xcas_DispG_Window && Xcas_DispG_Window->visible())? Xcas_DispG->waiting_click : false;
3284 	Fl::unlock();
3285 	if (!wait)
3286 	  break;
3287       }
3288       Fl::lock();
3289       if (Xcas_DispG) Xcas_DispG->waiting_click=false;
3290       if (Xcas_DispG_Cancel) Xcas_DispG_Cancel->hide();
3291       gen res=(Xcas_DispG && Xcas_DispG_Window && Xcas_DispG_Window->visible())?Xcas_DispG->waiting_click_value:undef;
3292       Fl::unlock();
3293       return res;
3294     }
3295     else {
3296       Fl::lock();
3297       gen res=makeform(v,contextptr);
3298       Fl::unlock();
3299       return inputform_post_analysis(v,res,contextptr);
3300     }
3301   }
3302 
3303 #endif // WIN32
3304 
3305 #endif // HAVE_LIBFLTK
3306 
3307   // functions for icas or C-library usage
read_file(const giac::gen & g)3308   int read_file(const giac::gen & g){
3309     if (g.is_symb_of_sommet(giac::at_read))
3310       return 1;
3311     if (g.is_symb_of_sommet(giac::at_geo2d))
3312       return 2;
3313     if (g.is_symb_of_sommet(giac::at_geo3d))
3314       return 3;
3315     if (g.is_symb_of_sommet(giac::at_spreadsheet))
3316       return 4;
3317     return 0;
3318   }
3319 
icas_eval_callback(const giac::gen & evaled_g,void * param)3320   void icas_eval_callback(const giac::gen & evaled_g,void * param){
3321     giac::gen * resptr=(giac::gen *) param;
3322     // cerr << "icas_eval_callback " << evaled_g << '\n';
3323     *resptr=evaled_g;
3324   }
3325 
3326   // eval g to gg, if g is a read command, replace g by the file content
3327   // and set reading_file to true
icas_eval(giac::gen & g,giac::gen & gg,int & reading_file,std::string & filename,giac::context * contextptr)3328   void icas_eval(giac::gen & g,giac::gen & gg,int & reading_file,std::string &filename,giac::context * contextptr){
3329     if (debug_infolevel)
3330       CERR << CLOCK() << " icas_eval " << g << '\n';
3331     try {
3332       reading_file=read_file(g);
3333       if (g.type==_SYMB && g._SYMBptr->feuille.type==giac::_STRNG)
3334 	filename=reading_file?*g._SYMBptr->feuille._STRNGptr:"";
3335       if (g.type==_SYMB && reading_file){
3336 	if (g._SYMBptr->feuille.type==giac::_VECT && !g._SYMBptr->feuille._VECTptr->empty()){
3337 	  gg=_read(g._SYMBptr->feuille,contextptr);
3338 	  return;
3339 	}
3340 	else {
3341 	  if (g._SYMBptr->feuille.type!=giac::_STRNG)
3342 	    g=g._SYMBptr->feuille;
3343 	  else
3344 	    g=quote_read(g._SYMBptr->feuille,contextptr);
3345 	}
3346       }
3347       if (g.type==giac::_VECT && !filename.empty()){
3348 	// patch to handle spreadsheet[ correctly
3349 	FILE * inf=fopen(filename.c_str(),"r");
3350 	if (inf){
3351 	  char ch[13];
3352 	  ch[12]=0;
3353 	  if (fread(ch,1,12,inf)==12 && !strcmp(ch,"spreadsheet[")){
3354 	    giac::makespreadsheetmatrice(*g._VECTptr,0);
3355 	    g.subtype=giac::_SPREAD__VECT;
3356 	  }
3357 	  fclose(inf);
3358 	}
3359       }
3360       giac::gen g1=giac::approx_mode(contextptr)?giac::symbolic(giac::at_evalf,g):g;
3361       giac::gen result;
3362       signal(SIGINT,ctrl_c_signal_handler);
3363 #ifdef HAVE_LIBPTHREAD
3364       giac::make_thread(g1,eval_level(contextptr),icas_eval_callback,&result,contextptr);
3365       int status;
3366       while (1){
3367 	// look at other threads
3368 	int cs=context_list().size(),ci=0;
3369 	for (;ci<cs;++ci){
3370 	  context * cptr=context_list()[ci];
3371 	  if (cptr!=contextptr)
3372 	    status=check_thread(cptr);
3373 	}
3374 	// 0 finished, 2/3 debug/wait click
3375 	status=giac::check_thread(contextptr);
3376 #ifdef HAVE_LIBFLTK
3377 	if (xcas::Xcas_Debug_Window && status<2)
3378 	  xcas::Xcas_Debug_Window->hide();
3379 #endif
3380 	if (status<=0){
3381 #ifdef HAVE_LIBFLTK
3382 	  Fl::flush();
3383 #endif
3384 	  break;
3385 	}
3386 #ifdef HAVE_LIBFLTK
3387 	if (status!=1)
3388 	  xcas::Xcas_debugguer(status,contextptr);
3389 #else
3390 	// FIXME Debugguer without FLTK
3391 	if (status!=1)
3392 	  giac::thread_eval_status(1,contextptr);
3393 #endif
3394 	if (ctrl_c){
3395 	  if (giac::is_context_busy(contextptr))
3396 	    giac::kill_thread(true,contextptr);
3397 	  ctrl_c=false; interrupted=false;
3398 	}
3399 	else
3400 	  usleep(1000);
3401       }
3402       gg=result;
3403 #else
3404       gg=eval(g1,eval_level(contextptr),contextptr);
3405 #endif
3406     }
3407     catch(std::runtime_error & e){
3408       if (!contextptr)
3409 	giac::protection_level=0;
3410       gg=giac::string2gen(e.what(),false);
3411     }
3412   }
3413 
3414   int fltk_return_value=-1;
3415 #ifdef HAVE_LIBFLTK
cb_Close(Fl_Button * m,void *)3416   static void cb_Close(Fl_Button * m , void*) {
3417     if (m->window())
3418       m->window()->hide();
3419     fltk_return_value=0;
3420   }
3421 
cb_Cancel(Fl_Button * m,void *)3422   static void cb_Cancel(Fl_Button * m , void*) {
3423     if (m->window())
3424       m->window()->hide();
3425     fltk_return_value=1;
3426   }
3427 
cb_Kill(Fl_Button * m,void *)3428   static void cb_Kill(Fl_Button * m , void*) {
3429     // FIXME user_data()
3430     giac::kill_thread(true,context0);
3431   }
3432 
has_graph3d(Fl_Widget * widget)3433   bool has_graph3d(Fl_Widget * widget){
3434     Fl_Group * g=dynamic_cast<Fl_Group *>(widget);
3435     if (!g)
3436       return false;
3437     int n=g->children();
3438     for (int i=0;i<n;++i){
3439       Fl_Widget * wid = g->child(i);
3440       if (Fl_Group * gr=dynamic_cast<Fl_Group * >(wid)){
3441 	if (has_graph3d(gr))
3442 	  return true;
3443       }
3444       if (Graph3d * gr3=dynamic_cast<Graph3d * >(wid))
3445 	return true;
3446     }
3447     return false;
3448   }
3449 #endif
3450 
quit_idle_function(void * widget)3451   void quit_idle_function(void * widget){
3452     static int t=0;
3453     if (!widget){
3454       t=CLOCK();
3455       return;
3456     }
3457 #ifdef HAVE_LIBFLTK
3458     Fl_Widget * w=(Fl_Widget *)(widget);
3459     if (has_graph3d(w) && (CLOCK()-t<1e5)) return;
3460     w->hide();
3461     fltk_return_value=0;
3462 #endif
3463   }
3464 
3465   // open a FLTK window, that will be printed to filename when closed
3466   // return false if FLTK not avail
fltk_view(const giac::gen & g,giac::gen & ge,const std::string & filename,std::string & figure_filename,int file_type,const giac::context * contextptr)3467   bool fltk_view(const giac::gen & g,giac::gen & ge,const std::string & filename,std::string & figure_filename,int file_type,const giac::context *contextptr){
3468 #ifdef __APPLE__
3469     // return false;
3470 #endif
3471 #ifdef HAVE_LIBFLTK
3472 #if !defined(__APPLE__) && !defined(WIN32)
3473     if (!getenv("DISPLAY"))
3474       return false;
3475 #endif    // FIXME GIAC_CONTEXT
3476     // const giac::context * contextptr = get_context(Fl_Group::current());
3477     bool geometry=!figure_filename.empty();
3478     if (file_type==4 && figure_filename.empty()){
3479       figure_filename="table.tab";
3480       geometry=true;
3481     }
3482     Fl_Window * w=0;
3483     Fl_Return_Button * button0 = 0 ;
3484     Fl_Button * button1 =0,*button2=0;
3485     if (!w){
3486       int dx=800,dy=geometry?500:360;
3487       Fl_Group::current(0);
3488       w=new Fl_Window(dx,dy);
3489       button0 = new Fl_Return_Button(2,dy-25,dx/3-4,20);
3490       button0->shortcut(0xff0d);
3491       button0->label(gettext("OK"));
3492       button0->callback( (Fl_Callback *) cb_Close);
3493       button1 = new Fl_Button(dx/3+2,dy-25,dx/3-4,20);
3494       button1->shortcut(0xff1b);
3495       button1->label(gettext("Cancel"));
3496       button1->callback( (Fl_Callback *) cb_Cancel);
3497       button2 = new Fl_Button(2*dx/3+2,dy-25,dx/3-4,20);
3498       button2->shortcut(0xff1b);
3499       button2->label(gettext("STOP"));
3500       button2->callback( (Fl_Callback *) cb_Kill);
3501       w->end();
3502       w->resizable(w);
3503     }
3504     // xcas::initialize_function=load_autorecover_data;
3505     if (file_type==-1){
3506       quit_idle_function(0);
3507       Fl::add_idle(quit_idle_function,w);
3508     }
3509     else
3510       Fl::add_idle(xcas::Xcas_idle_function,0);
3511     // xcas::idle_function=Xcas_update_mode;
3512     Fl_Group::current(w);
3513     int dx=w->w(),dy=w->h();
3514     Fl_Tile * this_graph_tile=new Fl_Tile(0,0,dx,dy-25);
3515     Fl_Widget * wid =0,*print_wid=0;
3516     if (geometry){
3517       if (file_type==4 || (giac::ckmatrix(ge,true) && ge.subtype==giac::_SPREAD__VECT)){
3518 	xcas::Tableur_Group * t=new xcas::Tableur_Group(0,0,dx,dy-25,20,2);
3519 	wid=t;
3520 	giac::gen tmp=giac::gen(giac::remove_path(giac::remove_extension(figure_filename)),contextptr);
3521 	if (tmp.type==giac::_IDNT)
3522 	  t->table->name=tmp;
3523 	t->table->contextptr=(giac::context *) contextptr;
3524 	if (giac::ckmatrix(ge,true) && ge.subtype==giac::_SPREAD__VECT){
3525 	  t->table->set_matrix(*ge._VECTptr,true,false); // don't reeval!
3526 	  // t->table->paste(*ge._VECTptr,false); // don't reeval!
3527 	}
3528 	if (t->table->filename)
3529 	  delete t->table->filename;
3530 	if ( (t->table->filename = new std::string(figure_filename)) )
3531 	  t->fname->label(t->table->filename->c_str());
3532 	t->table->update_status();
3533       }
3534       else {
3535 	wid=new xcas::Figure(0,0,dx,dy-25,20,(is3d(ge)||file_type==3));
3536 	if (wid)
3537 	  print_wid=((xcas::Figure *)wid)->geo;
3538       }
3539     }
3540     else {
3541       int t=graph_output_type(ge);
3542       if (t==4 || file_type==4){
3543 	xcas::Turtle * tu=new xcas::Turtle(0,0,dx,dy-25);
3544 	tu->turtleptr=&turtle_stack(contextptr);
3545 	print_wid=wid=tu;
3546       }
3547       else {
3548 	if (t==3||file_type==3){
3549 #ifdef HAVE_LIBFLTK_GL
3550 	  print_wid=wid=new xcas::Graph3d(0,0,dx,dy-25,"",0);
3551 #endif
3552 	}
3553 	else {
3554 	  if (t==2 || file_type==2)
3555 	    print_wid=wid=new xcas::Graph2d(0,0,dx,dy-25);
3556 	  else {
3557 	    print_wid=wid=new Fl_Output(0,0,dx,dy-25);
3558 	    ((Fl_Output *) (wid))->value("No suitable widget");
3559 	  }
3560 	}
3561       }
3562     }
3563     if (xcas::Graph2d3d * this_graph = dynamic_cast<xcas::Graph2d3d *>(wid)){
3564       this_graph->add(ge);
3565       this_graph->autoscale();
3566       this_graph->update_infos(ge,contextptr);
3567     }
3568     if (xcas::Figure * fig=dynamic_cast<xcas::Figure *>(wid)){
3569       fig->rename(figure_filename.c_str());
3570       fig->geo->hp->remove_entry(0);
3571       if (!is_undef(ge))
3572 	fig->geo->add(ge);
3573       // put g in figure history_pack
3574       if (g.type!=giac::_VECT){
3575 	if (!is_undef(ge)){
3576 	  xcas::Multiline_Input_tab * mi = dynamic_cast<xcas::Multiline_Input_tab * >(xcas::new_question_multiline_input(100,20));
3577 	  mi->set_g(g);
3578 	  fig->geo->hp->add_entry(-1,mi);
3579 	}
3580       }
3581       else {
3582 	giac::const_iterateur it=g._VECTptr->begin(),itend=g._VECTptr->end();
3583 	for (;it!=itend;++it){
3584 	  xcas::Multiline_Input_tab * mi = dynamic_cast<xcas::Multiline_Input_tab * >(xcas::new_question_multiline_input(100,20));
3585 	  mi->set_g(*it);
3586 	  fig->geo->hp->add_entry(-1,mi);
3587 	}
3588       }
3589       if (fig->geo->hp->children()==0){
3590 	fig->geo->hp->add_entry(-1);
3591 	fig->geo->set_mode(0,0,1);
3592 	fig->geo->approx=true;
3593 	fig->mode->value("point");
3594       }
3595       fig->geo->hp->update();
3596     }
3597     this_graph_tile->end();
3598     w->show();
3599     w->hotspot(w);
3600     fltk_return_value=-1;
3601     Fl::run();
3602     if (file_type==-1)
3603       Fl::remove_idle(quit_idle_function,w);
3604     else
3605       Fl::remove_idle(xcas::Xcas_idle_function,0);
3606     w->show();
3607     if (!fltk_return_value){
3608       if (xcas::Figure * fig=dynamic_cast<xcas::Figure *>(wid)){
3609 	if (fig->geo->hp->_modified && !figure_filename.empty()){
3610 	  int i=fl_ask("Figure modified. Save?");
3611 	  if (i)
3612 	    fig->save_figure_as(figure_filename);
3613 	}
3614       }
3615       if (xcas::Tableur_Group * t=dynamic_cast<xcas::Tableur_Group * >(wid)){
3616 	if (t->table->changed_ && !t->table->filename->empty()){
3617 	  int i=fl_ask("Sheet modified. Save?");
3618 	  if (i){
3619 	    ofstream of(t->table->filename->c_str());
3620 	    if (!of)
3621 	      fl_message("%s","Write error");
3622 	    else
3623 	      of << giac::gen(t->table->m,giac::_SPREAD__VECT) << '\n';
3624 	    of.close();
3625 	  }
3626 	}
3627 	else {
3628 	  ge=t->table->m;
3629 	  ge.subtype=giac::_SPREAD__VECT;
3630 	}
3631       }
3632       // now try to print the widget
3633       if (!print_wid)
3634 	print_wid=wid;
3635       print_wid->resize(0,0,dx,dy);
3636       if (xcas::Figure * fig=dynamic_cast<xcas::Figure *>(wid)){
3637 	fig->geo->orthonormalize();
3638       }
3639       if (!filename.empty()
3640 	  // && !figure_filename.empty()
3641 	  )
3642 	xcas::widget_ps_print(print_wid,filename,true,0,false,true,false); // don't ask user for pixel size
3643     }
3644     w->hide();
3645     Fl::wait(0.001);
3646     w->remove(this_graph_tile);
3647     delete wid;
3648     delete this_graph_tile;
3649     delete button0;
3650     delete button1;
3651     delete button2;
3652     delete w;
3653     return !fltk_return_value;
3654 #else // HAVE_LIBFLTK
3655     return false;
3656 #endif
3657   }
3658 
3659 #ifdef HAVE_LIBFLTK
3660   // Font selection, borrowed from FLTK tests font.cxx
3661   Fl_Window *form=0;
3662 
3663   class FontDisplay : public Fl_Widget {
3664     void draw();
3665   public:
3666     int font, size;
FontDisplay(Fl_Boxtype B,int X,int Y,int W,int H,const char * L=0)3667     FontDisplay(Fl_Boxtype B, int X, int Y, int W, int H, const char* L = 0) :
3668       Fl_Widget(X,Y,W,H,L) {box(B); font = 0; size = 14;}
3669   };
draw()3670   void FontDisplay::draw() {
3671     draw_box();
3672     fl_font((Fl_Font)font, size);
3673     fl_color(FL_BLACK);
3674     fl_draw(label(), x()+3, y()+3, w()-6, h()-6, align());
3675   }
3676 
3677   FontDisplay *textobj=0;
3678 
3679   Fl_Hold_Browser *fontobj=0, *sizeobj=0;
3680 
3681 #define FLTK_FONT_MAX 256
3682   int *sizes[FLTK_FONT_MAX];
3683   int numsizes[FLTK_FONT_MAX];
3684   int pickedsize = 14;
3685 
font_cb(Fl_Widget *,long)3686   void font_cb(Fl_Widget *, long) {
3687     int fn = fontobj->value();
3688     if (!fn) return;
3689     fn--;
3690     textobj->font = fn;
3691     sizeobj->clear();
3692     int n = numsizes[fn];
3693     int *s = sizes[fn];
3694     if (!n) {
3695       // no sizes
3696     } else if (s[0] == 0) {
3697       // many sizes;
3698       int j = 1;
3699       for (int i = 1; i<64 || i<s[n-1]; i++) {
3700 	char buf[20];
3701 	if (j < n && i==s[j]) {sprintf(buf,"@b%d",i); j++;}
3702 	else sprintf(buf,"%d",i);
3703 	sizeobj->add(buf);
3704       }
3705       sizeobj->value(pickedsize);
3706     } else {
3707       // some sizes
3708       int w = 0;
3709       for (int i = 0; i < n; i++) {
3710 	if (s[i]<=pickedsize) w = i;
3711 	char buf[20];
3712 	sprintf(buf,"@b%d",s[i]);
3713 	sizeobj->add(buf);
3714       }
3715       sizeobj->value(w+1);
3716     }
3717     textobj->redraw();
3718   }
3719 
size_cb(Fl_Widget *,long)3720   void size_cb(Fl_Widget *, long) {
3721     int i = sizeobj->value();
3722     if (!i) return;
3723     const char *c = sizeobj->text(i);
3724     while (*c < '0' || *c > '9') c++;
3725     pickedsize = atoi(c);
3726     textobj->size = pickedsize;
3727     textobj->redraw();
3728   }
3729 
get_font(Fl_Font & police,int & taille)3730   bool get_font(Fl_Font & police,int & taille){
3731     static Fl_Return_Button * button0 = 0 ;
3732     static Fl_Button * button1 =0;
3733     static char label[400];
3734     Fl::scheme(NULL);
3735     if (!form){
3736       Fl_Group::current(0);
3737       form = new Fl_Window(550,370);
3738       strcpy(label, gettext("What a nice font"));
3739       int i = strlen(label);
3740       uchar c;
3741       label[i++] = char(10);
3742       for (c = ' '+1; c < 127; c++,i++) {if (!(c&0x1f)) label[i]=' '; else label[i]=c;}
3743       label[i++] = char(10);
3744       for (c = 0xA1; c; c++,i++) {if (!(c&0x1f)) label[i]=' '; else label[i]=c;}
3745       label[i] = 0;
3746       button0 = new Fl_Return_Button(100,10,100,20);
3747       button0->shortcut(0xff0d);
3748       button0->label(gettext("OK"));
3749       button1 = new Fl_Button(300,10,100,20);
3750       button1->shortcut(0xff1b);
3751       button1->label(gettext("Cancel"));
3752 
3753       textobj = new FontDisplay(FL_FRAME_BOX,10,40,530,140,label);
3754       textobj->align(FL_ALIGN_TOP|FL_ALIGN_LEFT|FL_ALIGN_INSIDE|FL_ALIGN_CLIP);
3755       textobj->color(9,47);
3756       fontobj = new Fl_Hold_Browser(10, 190, 390, 170);
3757       fontobj->box(FL_FRAME_BOX);
3758       fontobj->color(53,3);
3759       fontobj->callback(font_cb);
3760       form->resizable(fontobj);
3761       sizeobj = new Fl_Hold_Browser(410, 190, 130, 170);
3762       sizeobj->box(FL_FRAME_BOX);
3763       sizeobj->color(53,3);
3764       sizeobj->callback(size_cb);
3765       form->end();
3766       int k = min(fonts_available,FLTK_FONT_MAX);
3767       for (i = 0; i < k; i++) {
3768 	int t; const char *name = Fl::get_font_name((Fl_Font)i,&t);
3769 	char buffer[128];
3770 	if (t) {
3771 	  char *p = buffer;
3772 	  if (t & FL_BOLD) {*p++ = '@'; *p++ = 'b';}
3773 	  if (t & FL_ITALIC) {*p++ = '@'; *p++ = 'i';}
3774 	  strcpy(p,name);
3775 	  name = buffer;
3776 	}
3777 	fontobj->add(name);
3778 	int *s; int n = Fl::get_font_sizes((Fl_Font)i, s);
3779 	numsizes[i] = n;
3780 	if (n) {
3781 	  sizes[i] = new int[n];
3782 	  for (int j=0; j<n; j++) sizes[i][j] = s[j];
3783 	}
3784       }
3785     }
3786     pickedsize = taille;
3787     textobj->size = pickedsize;
3788     fontobj->value(police+1);
3789     font_cb(fontobj,0);
3790     form->set_modal();
3791     form->show();
3792     form->hotspot(form);
3793     Fl::focus(form);
3794     int r=-1;
3795     for (;;){
3796       Fl_Widget *o = Fl::readqueue();
3797       if (!o) Fl::wait();
3798       else {
3799 	if (o==form){ r=1; break; }
3800 	if (o == button0) {r = 0; break;}
3801 	if (o == button1) {r = 1; break;}
3802       }
3803     }
3804     form->hide();
3805     if (r)
3806       return false;
3807     police=Fl_Font(textobj->font);
3808     taille=textobj->size;
3809     return true;
3810   }
3811 
3812   /*
3813   void test_alert(){
3814     string s;
3815     for (int i=0;i<256;i++)
3816       s+=char(i);
3817     fl_alert("%s",s.c_str());
3818   }
3819   */
3820 
3821 #endif // HAVE_LIBFLTK
3822 
3823 #ifndef NO_NAMESPACE_XCAS
3824 } // namespace xcas
3825 #endif // ndef NO_NAMESPACE_XCAS
3826 
3827