1 // -*- mode:C++ ; compile-command: "g++-3.4 -I. -I.. -g -c Input.cc -Wall" -*-
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 "Input.h"
30 #include <FL/Fl.H>
31 #include <FL/fl_draw.H>
32 #include <FL/Fl_Window.H>
33 #include <FL/fl_ask.H>
34 #include <FL/Fl_File_Chooser.H>
35 #include <FL/Fl_Hold_Browser.H>
36 #include <FL/Fl_Return_Button.H>
37 #include <FL/Fl_Tooltip.H>
38 #include "History.h"
39 #include "Xcas1.h"
40 #include "Tableur.h"
41 #include "Graph3d.h"
42 #include "Help1.h"
43 #ifndef IN_GIAC
44 #include <giac/plot.h>
45 #include <giac/help.h>
46 #include <giac/global.h>
47 #else
48 #include "plot.h"
49 #include "help.h"
50 #include "global.h"
51 #endif
52 #include <iostream>
53 #include <fstream>
54 #ifdef HAVE_UNISTD_H
55 #include <unistd.h>
56 #endif
57 using namespace std;
58 
59 #ifndef NO_NAMESPACE_XCAS
60 namespace xcas {
61 #endif // ndef NO_NAMESPACE_XCAS
62 
63 #define TAB_ARGS 6
64 
65 #if defined __APPLE__ || defined WIN32
66   bool use_external_browser = true ;
67 #else
68   bool use_external_browser = getenv("BROWSER") ;
69 #endif
70   std::vector<std::string> Multiline_Input_tab::history;
71   int Multiline_Input_tab::count=0;
72 
73   Fl_Help_Dialog * Xcas_help_window = new Fl_Help_Dialog();
74   Fl_Widget * Xcas_input_focus=0;
75 
system_browser(const string & s)76   void system_browser(const string & s){
77     int i=giac::system_no_deprecation(s.c_str());
78     if (i!=0){
79       fl_alert("%s",("Switching to internal browser, error running browser command "+s).c_str());
80       use_external_browser=false;
81     }
82   }
83 
read_aide(const string & progname,int language)84   bool read_aide(const string & progname,int language){
85     string helpfile("aide_cas");
86     int helpitems=0;
87     (*giac::vector_aide_ptr())=giac::readhelp(helpfile.c_str(),helpitems,false);
88     if (!helpitems){
89       if (getenv("XCAS_HELP"))
90 	helpfile=getenv("XCAS_HELP");
91       else
92 	helpfile=giac::giac_aide_dir()+"aide_cas";
93       (*giac::vector_aide_ptr())=giac::readhelp(helpfile.c_str(),helpitems);
94     }
95     if (!helpitems){
96       cerr << "// Unable to open help file "<< helpfile << '\n';
97       return false;
98     }
99     else {
100       cerr << "// Using help file " << helpfile << '\n';
101       giac::xcasroot()=giac::xcasroot_dir((char *) progname.c_str());
102       /* patch for gsview TEMP, but does not work
103       if (!getenv("TEMP")){
104 	if (giac::is_file_available("/tmp"))
105 	  setenv("TEMP","/tmp",1);
106 	else
107 	  setenv("TEMP",giac::xcasroot().c_str(),1);
108       }
109       */
110       cerr << "// root dir " << giac::xcasroot() << '\n';
111       giac::html_help_init((char *) progname.c_str(),language);
112       giac::update_completions();
113       return true;
114     }
115   }
116 
117   // return the last keyword of s
motclef(const std::string & s)118   std::string motclef(const std::string & s){
119     int l=s.size();
120     int i=l-1;
121     for (;i>=0;i--){
122       if (giac::isalphan(s[i]))
123 	break;
124     }
125     l=i+1;
126     for (;i>=0;i--){
127       if (!giac::isalphan(s[i]))
128 	return s.substr(i+1,l-1-i);
129     }
130     return s.substr(0,l);
131   }
132 
findtooltip(const giac::gen & g)133   string findtooltip(const giac::gen & g){
134     string s,s1,s2;
135     static const char * tooltip_tab[]={"Integer","Expression","Variable","Matrix","Function","String","Polynom","Vector","Point","List","List of point","List of reals","List of integers","Sequence of variables","Command",};
136     static const char * tooltip_name[]={"Intg","Expr","Var","Mtrx","Fnc","Str","Poly","Vect","Pnt","Lst","LstPnt","LstReal","LstIntg","SeqVar","Cmd",0};
137     if (g.is_symb_of_sommet(giac::at_ou)){
138       giac::gen & f = g._SYMBptr->feuille;
139       if (f.type==giac::_VECT){
140 	giac::const_iterateur it=f._VECTptr->begin(),itend=f._VECTptr->end();
141 	if (it!=itend){
142 	  for (s=findtooltip(*it),++it;it!=itend;++it)
143 	    s = s + gettext(" or ") + findtooltip(*it);
144 	}
145       }
146       return s;
147     }
148     if (g.type==giac::_VECT && g._VECTptr->size()==1)
149       return findtooltip(g._VECTptr->front())+"(optional)";
150     s = g.print(giac::context0);
151     int l=s.size();
152     int p=s.find('(');
153     if (p>0 && p<l){
154       s1 = s.substr(0,p); s2=s.substr(p,l);
155     }
156     else s1=s;
157     const char ** tooltip_ptr=tooltip_name;
158     for (;*tooltip_ptr;++tooltip_ptr){
159       if (*tooltip_ptr==s1){
160 	return tooltip_tab[tooltip_ptr-tooltip_name]+s2;
161       }
162     }
163     return s;
164   }
165 
split(const std::string & s,Fl_Widget * w)166   std::string split(const std::string & s,Fl_Widget * w){
167     fl_font(w->labelfont(),w->labelsize());
168     string res,ajout;
169     int fin=s.size(),debut=0;
170     int taille=w->w();
171     for (;debut<fin;){
172       int l=fin-debut;
173       ajout=s.substr(debut,l);
174       if (fl_width(ajout.c_str())<=taille)
175 	return res+ajout;
176       for (--l;l>0;--l){
177 	if (s[debut+l]==' '){
178 	  ajout=s.substr(debut,l);
179 	  if (fl_width(ajout.c_str())<=taille){
180 	    res += ajout;
181 	    res += '\n';
182 	    break;
183 	  }
184 	}
185       }
186       if (l==0)
187 	return res+ajout;
188       debut += l+1;
189     }
190     return res;
191   }
192 
update_examples(const string & s,Fl_Browser * examples,Fl_Browser * related,Fl_Browser * syns,Fl_Output * output,Fl_Input ** argtab,int language)193   void update_examples(const string & s,Fl_Browser * examples,Fl_Browser * related,Fl_Browser * syns,Fl_Output * output,Fl_Input ** argtab,int language){
194     help_output(s,language);
195     if (output->label())
196       delete output->label();
197     char * ptr=new char [s.size()+1];
198     strcpy(ptr,s.c_str());
199     output->label(ptr);
200     if (output->parent())
201       output->parent()->redraw();
202     if (examples && output ){
203       giac::aide cur_aide=giac::helpon(s,(*giac::vector_aide_ptr()),language,(*giac::vector_aide_ptr()).size(),false);
204       string result=cur_aide.cmd_name;
205       if (!cur_aide.syntax.empty()){
206 	result=result+"("+cur_aide.syntax+")\n";
207 	giac::gen helpg;
208 	giac::vecteur helpv;
209 	if (!cur_aide.syntax.empty()){
210 	  helpg=giac::gen(cur_aide.syntax,giac::context0);
211 	  if (helpg.type==giac::_VECT)
212 	    helpv=*helpg._VECTptr;
213 	  else
214 	    helpv=giac::vecteur(1,helpg);
215 	}
216 	giac::const_iterateur jt=helpv.begin(),jtend=helpv.end();
217 	Fl_Input ** ptr=argtab, ** argtabend=argtab+TAB_ARGS;
218 	if (jtend-jt>TAB_ARGS)
219 	  jtend=jt+TAB_ARGS;
220 	for (;*ptr && jt!=jtend;++jt,++ptr){
221 	  giac::gen tmp=*jt;
222 	  string tmps=tmp.print(giac::context0);
223 	  if (tmp.type==giac::_VECT)
224 	    (*ptr)->labelcolor(FL_BLUE);
225 	  else
226 	    (*ptr)->labelcolor(FL_BLACK);
227 	  if ((*ptr)->label())
228 	    free((void *) (*ptr)->label());
229 	  char * chartab = (char *) malloc(sizeof(char)*(tmps.size()+1));
230 	  strcpy(chartab,tmps.c_str());
231 	  (*ptr)->label(chartab);
232 	  (*ptr)->show();
233 	  (*ptr)->value("");
234 	  tmps = findtooltip(tmp);
235 	  if ((*ptr)->tooltip())
236 	    free((void *) (*ptr)->tooltip());
237 	  char * chartab2 = (char *) malloc(sizeof(char)*(tmps.size()+1));
238 	  strcpy(chartab2,tmps.c_str());
239 	  (*ptr)->tooltip(chartab2);
240 	}
241 	int L=output->labelsize()+2;
242 	int eh=L*((1+(ptr-argtab))/2);
243 	int outputyh = output->y()+output->h();
244 	examples->resize(examples->x(),outputyh+eh,examples->w(),examples->parent()->h()-outputyh-eh-2);
245 	for (;ptr!=argtabend;++ptr){
246 	  if (*ptr)
247 	    (*ptr)->hide();
248 	}
249       }
250       vector<giac::localized_string>::const_iterator it=cur_aide.blabla.begin(),itend=cur_aide.blabla.end();
251       for (;it!=itend;++it){
252 	if (it->language==language){
253 	  result = split(it->chaine,output) +'\n'+result ;
254 	  break;
255 	}
256       }
257       output->value(result.c_str());
258       examples->clear();
259       examples->add(s.c_str());
260       std::vector<std::string>::const_iterator jt=cur_aide.examples.begin(),jtend=cur_aide.examples.end();
261       for (;jt!=jtend;++jt)
262 	examples->add(jt->c_str());
263       related->clear();
264       syns->clear();
265       std::vector<giac::indexed_string>::const_iterator kt=cur_aide.related.begin(),ktend=cur_aide.related.end();
266       for (;kt!=ktend;++kt){
267 	string tmp=giac::localize(kt->chaine,language);
268 	related->add(tmp.c_str());
269       }
270       std::vector<giac::localized_string>::const_iterator lt=cur_aide.synonymes.begin(),ltend=cur_aide.synonymes.end();
271       for (;lt!=ltend;++lt){
272 	if (lt->chaine!=s)
273 	  syns->add(lt->chaine.c_str());
274       }
275     }
276   }
277 
handle_tab_cb_browser(Fl_Browser * b,void *)278   void handle_tab_cb_browser(Fl_Browser * b,void *){
279     int k=b->value();
280     if (k>=1){
281       string s=b->text(k);
282       // find examples browser
283       Fl_Group * g = b->parent();
284       Fl_Browser * examples=0, * related=0,*syns=0;
285       Fl_Output * output=0;
286       Fl_Input * input=0,*argtab[TAB_ARGS]={0,0,0,0,0,0};
287       if (g){
288 	int n=g->children();
289 	for (int i=0;i<n-1;i++){
290 	  if (i>4 && (output=dynamic_cast<Fl_Output *>(g->child(i)))){
291 	    related=dynamic_cast<Fl_Browser *>(g->child(i-4));
292 	    syns=dynamic_cast<Fl_Browser *>(g->child(i-3));
293 	    examples=dynamic_cast<Fl_Browser *>(g->child(i+7));
294 	    input=dynamic_cast<Fl_Input *>(g->child(i-1));
295 	    for (int k=0;k<TAB_ARGS;k++){
296 	      argtab[k] = dynamic_cast<Fl_Input *>(g->child(i+1+k));
297 	    }
298 	    break;
299 	  }
300 	}
301       }
302       if (output && input && examples && related && syns){
303 	update_examples(s,examples,related,syns,output,argtab,giac::language(giac::context0));
304 	input->value(b->text(k));
305 	if (Fl::event_clicks()){
306 	  g->hide();
307 	}
308       }
309       else
310 	help_output(s,giac::language(giac::context0));
311     }
312   }
313 
browser_html_help(Fl_Browser * b,Fl_Browser * examples,Fl_Browser * related,Fl_Browser * syns,Fl_Output * output,Fl_Input ** argtab,int language)314   void browser_html_help(Fl_Browser * b,Fl_Browser * examples,Fl_Browser * related,Fl_Browser * syns,Fl_Output * output,Fl_Input ** argtab,int language){
315     int k=b->value();
316     if (k>=1){
317       if (Xcas_help_window){
318 	string s=b->text(k);
319 	update_examples(s,examples,related,syns,output,argtab,language);
320 	std::map<std::string,std::string>::const_iterator it=giac::lexer_localization_map().find(s),itend=giac::lexer_localization_map().end();
321 	if (it!=itend)
322 	  s=it->second;
323 	vector<string> v=giac::html_help(giac::html_mtt,s);
324 	if (!v.empty()){
325 	  if (use_external_browser)
326 	    giac::system_browser_command(v.front());
327 	  else {
328 	    Xcas_help_window->load(v.front().c_str());
329 	    if (!xcas::Xcas_help_window->visible())
330 	      xcas::Xcas_help_window->show();
331 	  }
332 	}
333       }
334       b->window()->show();
335       Fl::focus(b);
336     }
337   }
338 
339   Fl_Window * handle_tab_w = 0;
340   // Find a completion of s in v -> ans, return true if user OK
341   // dx,dy=size of browser window
handle_tab(const string & s,const vector<string> & v,int dx,int dy,int & remove,string & ans,bool allow_immediate_out)342   int handle_tab(const string & s,const vector<string> & v,int dx,int dy,int & remove,string & ans,bool allow_immediate_out){
343     static Fl_Hold_Browser * browser = 0;
344     static Fl_Hold_Browser * related = 0;
345     static Fl_Hold_Browser * syns = 0;
346     static Fl_Button * button0 = 0 ;
347     static Fl_Button * button1 =0;
348     static Fl_Button * button2 =0;
349     static Fl_Button * topic_help=0;
350     static Fl_Input * input = 0;
351     static Fl_Multiline_Output * output = 0;
352     static Fl_Hold_Browser * examples = 0;
353     static Fl_Input * argtab[TAB_ARGS]={0,0,0,0,0,0};
354     int L=16;
355     const giac::context * contextptr=giac::context0;
356     if (xcas::Xcas_input_focus && xcas::Xcas_input_focus->window()){
357       dx=4*xcas::Xcas_input_focus->window()->w()/5;
358       dy=4*xcas::Xcas_input_focus->window()->h()/5;
359       L=xcas::Xcas_input_focus->labelsize()+2;
360       contextptr=get_context(xcas::Xcas_input_focus);
361     }
362     else {
363       if (dx>500)
364 	dx=500;
365     }
366     if (dy<300)
367       dy=300;
368     if (dx<240)
369       dx=240;
370     // search non ascii char in s starting from the end
371     int ss=s.size();
372     string res;
373     remove=0;
374     for (int i=ss-1;i>=0;--i,++remove){
375       const char & ch =s[i];
376       if (giac::isalphan(ch) || ch=='&' || ch=='|' || ch=='=' || ch==':' || ch=='@' || ch=='<' || ch=='>' || ch=='+' || ch=='-' || ch=='/' || ch=='*' || ch=='$' || ch=='%')
377 	res=ch+res;
378       else {
379 	if (!res.empty())
380 	  break;
381       }
382     }
383     ss=res.size();
384     if (!handle_tab_w){
385       Fl_Group::current(0);
386       handle_tab_w=new Fl_Window(50,100,dx,dy,gettext("Index"));
387       button0 = new Fl_Button(2,2,dx/3-4,L+2);
388       button0->shortcut(0xff0d);
389       button0->label(gettext("OK"));
390       button0->tooltip(gettext("Click to copy the commandname to the commandline"));
391       button1 = new Fl_Button(dx/3+2,2,dx/3-4,L+2);
392       button1->shortcut(0xff1b);
393       button1->label(gettext("Cancel"));
394       button2 = new Fl_Button(2*dx/3+2,2,dx/3-4,L+2);
395       button2->label(gettext("Details"));
396       button2->tooltip(gettext("Show full HTML help in browser"));
397       browser = new Fl_Hold_Browser(2,2*L+4,dx/2-2,dy/2-(2*L+4));
398       browser->format_char(0);
399       browser->type(2);
400       browser->label(gettext("Index"));
401       browser->align(FL_ALIGN_TOP);
402       browser->callback((Fl_Callback*)handle_tab_cb_browser);
403       // order is important: related,syns, examples,input,output
404       related = new Fl_Hold_Browser(dx/2+2,2*L+4,dx/2-2,dy/4-(L+2));
405       related->label(gettext("Related"));
406       related->format_char(0);
407       related->align(FL_ALIGN_TOP);
408       related->tooltip(gettext("Click for help on related command"));
409       syns = new Fl_Hold_Browser(dx/2+2,related->y()+related->h()+L+2,dx/2-2,dy/4-(2*L+4));
410       syns->format_char(0);
411       syns->label(gettext("Synonyms"));
412       syns->align(FL_ALIGN_TOP);
413       topic_help = new  Fl_Button(0,browser->y()+browser->h(),L,L+4);
414       topic_help->label("?");
415       topic_help->tooltip(gettext("Search this word in HTML help"));
416       input = new Fl_Input(L,browser->y()+browser->h(),dx-L,L+4);
417       input->when(FL_WHEN_CHANGED |FL_WHEN_ENTER_KEY |FL_WHEN_NOT_CHANGED);
418       // input->label("?");
419       input->tooltip(gettext("Show commandnames starting from this text"));
420       output = new Fl_Multiline_Output(2,input->y()+input->h(),dx-4,3*L+9);
421       output->tooltip(gettext("Command short description and syntax"));
422       // arguments
423       int ypos=output->y()+output->h();
424       for (int j=0;j<TAB_ARGS;j++){
425 	argtab[j]= new Fl_Input(dx/4+(j%2)*dx/2,ypos+1+(j/2)*L,dx/4,L-1);
426 	argtab[j]->when(FL_WHEN_ENTER_KEY |FL_WHEN_NOT_CHANGED);
427       }
428       ypos += 3*L;
429       examples = new Fl_Hold_Browser(output->x(),ypos,output->w(),handle_tab_w->h()-output->y()-output->h()-2-3*L);
430       examples->label("Examples");
431       examples->type(2);
432       examples->align(FL_ALIGN_LEFT);
433       examples->tooltip(gettext("Left-click: copy example to commandline, right-click: fill in template with example values"));
434       handle_tab_w->end();
435       handle_tab_w->resizable(handle_tab_w);
436       change_group_fontsize(handle_tab_w,L-2);
437     }
438     else {
439       browser->clear();
440       examples->clear();
441       related->clear();
442     }
443     if (ss)
444       input->value(res.c_str());
445     else {
446       res=input->value();
447       ss=res.size();
448       allow_immediate_out=false;
449     }
450     input->position(ss,ss);
451     vector<string> vres;
452     int vs=v.size(),i=0,r=-1,i_=0;
453     for (int k=0;k<vs;++k){
454       vres.push_back(v[k]);
455       browser->add(v[k].c_str());
456       if (!i && v[k].substr(0,ss)==res){
457 	i=k+1;
458       }
459       if (v[k][0]==res[0]){
460 	i_=k+1;
461       }
462     }
463     if (!i){
464       if (allow_immediate_out)
465 	return 0;
466       else
467 	i=i_;
468       if (!i)
469 	i=1;
470     }
471     handle_tab_w->set_modal();
472     if (vs){
473       browser->value(i);
474       string bt=browser->text(i);
475       update_examples(bt,examples,related,syns,output,argtab,giac::language(contextptr));
476       handle_tab_w->show();
477       handle_tab_w->hotspot(handle_tab_w);
478       Fl::focus(input);
479       for (;;) {
480 	if (!handle_tab_w->shown()){
481 	  r=0; break;
482 	}
483 	Fl_Widget *o = Fl::readqueue();
484 	if (!o) Fl::wait();
485 	else {
486 	  if (o == topic_help){ help_fltk(input->value()); }
487 	  if (o == button0) {r = 0; break;}
488 	  if (o == button1) {r = 1; break;}
489 	  if (o == button2)  browser_html_help(browser,examples,related,syns,output,argtab,giac::language(contextptr));
490 	  int j=0;
491 	  for (;j<TAB_ARGS;j++){
492 	    if (o==argtab[j]){
493 	      cerr << j << '\n';
494 	      Fl::e_keysym=argtab[j]->value()[0];
495 	      break;
496 	    }
497 	  }
498 	  if (j!=TAB_ARGS){ r=0; break; }
499 	  if ( o == examples && examples->value() ) {
500 	    string tmp=examples->text(examples->value());
501 	    if (Fl::event_button()!=3 || (!tmp.empty() && tmp[0]==' ')){
502 	      r=2;
503 	      break;
504 	    }
505 	    giac::gen tmpg(tmp,contextptr);
506 	    if (browser->value()>=1 && tmpg.type==giac::_SYMB && tmpg._SYMBptr->sommet==giac::gen(browser->text(browser->value()),contextptr))
507 	      tmpg=tmpg._SYMBptr->feuille;
508 	    giac::vecteur v;
509 	    if (tmpg.type==giac::_VECT && tmpg.subtype==giac::_SEQ__VECT)
510 	      v=*tmpg._VECTptr;
511 	    else
512 	      v=giac::vecteur(1,tmpg);
513 	    int vs=v.size();
514 	    for (int j=0;j<TAB_ARGS && j<vs;++j){
515 	      if (argtab && argtab[j])
516 		argtab[j]->value(v[j].print(contextptr).c_str());
517 	    }
518 	  }
519 	  if ( o == related && related->value() ) {
520 	    string s=related->text(related->value());
521 	    update_examples(s,examples,related,syns,output,argtab,giac::language(contextptr));
522 	    for (i=0;i<vs;++i){
523 	      if (v[i]==s){
524 		browser->value(i+1);
525 		break;
526 	      }
527 	    }
528 	  }
529 	  if ( o == syns && syns->value() ) {
530 	    string s=syns->text(syns->value());
531 	    update_examples(s,examples,related,syns,output,argtab,giac::language(contextptr));
532 	    for (i=0;i<vs;++i){
533 	      if (v[i]==s){
534 		browser->value(i+1);
535 		break;
536 	      }
537 	    }
538 	  }
539 	  if (o == handle_tab_w) { r=1; break; }
540 	  if (o == input){
541 	    if (Fl::event_key(FL_Enter) || Fl::event_key(FL_KP_Enter)){
542 	      if (Fl::event_state(FL_SHIFT |FL_CTRL | FL_ALT)){
543 		Fl::focus(examples);
544 		browser_html_help(browser,examples,related,syns,output,argtab,giac::language(contextptr));
545 	      }
546 	      else {
547 		r=0;
548 		break;
549 	      }
550 	    }
551 	    else {
552 	      const char * entree=input->value();
553 	      char nentree[256]; nentree[255]=0;
554 	      char nch[256]; nentree[255]=0;
555 	      for (int j=0;j<255;++j){
556 		nentree[j]=tolower(entree[j]);
557 		if (!entree[j]) break;
558 	      }
559 	      for (i=1;i<=vs;++i){
560 		const char * ch=browser->text(i);
561 		for (int j=0;j<255;++j){
562 		  nch[j]=tolower(ch[j]);
563 		  if (!ch[j]) break;
564 		}
565 		if (ch){
566 		  int comp=strcmp(nch,nentree);
567 		  if (!comp)
568 		    comp=strcmp(ch,entree);
569 		  if (comp>=0)
570 		    break;
571 		}
572 	      }
573 	      if (i<=vs){
574 		browser->value(i);
575 		update_examples(browser->text(i),examples,related,syns,output,argtab,giac::language(contextptr));
576 	      }
577 	    }
578 	  }
579 	}
580       }
581       /* does not work properly, since focus might change
582 	 if (foc && foc->window())
583 	foc->window()->show();
584 	else
585       */
586 	handle_tab_w->hide();
587       // Xcas_help_window->hide();
588       i=browser->value();
589     }
590     // delete browser;
591     // delete button1;
592     // delete button0;
593     // delete w;
594     int j=examples->value(); // ,k=related->value();
595     if (r==2 && j<=examples->size() && j>0){
596       ans=examples->text(j);
597       return 2;
598     }
599     if (r==0 && i<=vs && i>0){
600       ans=vres[i-1];
601       string addans;
602       Fl_Input ** ptr=argtab;
603       if (ptr && *ptr && (*ptr)->visible()){
604 	addans = "(";
605 	for (int j=0;*ptr && j<TAB_ARGS;++j){
606 	  string tmp=(*ptr)->value();
607 	  if (tmp.empty())
608 	    continue;
609 	  addans += tmp;
610 	  ++ptr;
611 	  if (!(*ptr)->visible())
612 	    break;
613 	  addans += ",";
614 	}
615 	if (addans[addans.size()-1]==',')
616 	  addans=addans.substr(0,addans.size()-1);
617 	addans += ")";
618       }
619       if (addans.size()>2){
620 	ans += addans;
621 	return 2;
622       }
623       return 1;
624     }
625     else
626       return 0;
627   }
628 
629 
insert_replace(const string & chaine,bool selected)630   void Multiline_Input_tab::insert_replace(const string & chaine,bool selected){
631     size_t pos1=position();
632     size_t pos2=mark();
633     if (pos1>pos2){
634       size_t tmp=pos1;
635       pos1=pos2;
636       pos2=tmp;
637     }
638     string input_s(value()),new_input;
639     size_t l=input_s.size();
640     new_input=input_s.substr(0,pos1)+chaine;
641     if (pos2<l)
642       new_input += input_s.substr(pos2,l-pos2);
643     value(new_input.c_str());
644     pos2=pos1+string(chaine).size();
645     if (selected)
646       position(pos1,pos2);
647     else
648       position(pos2,pos2);
649     set_changed();
650   }
651 
Multiline_default_callback(Fl_Widget * w,void *)652   void Multiline_default_callback(Fl_Widget * w,void *){
653     w->handle(FL_ENTER);
654   }
655 
Multiline_Input_tab(int x,int y,int w,int h,const char * l)656   Multiline_Input_tab::Multiline_Input_tab(int x,int y,int w,int h,const char * l):
657     Fl_Multiline_Input(x, y, w, h, l),handling(false),completion_tab(giac::vector_completions_ptr()),tableur(0),_g(giac::undef) {
658     if (parent()){
659       labelfont(parent()->labelfont());
660       labelsize(parent()->labelsize());
661       textfont(parent()->labelfont());
662       textsize(parent()->labelsize());
663     }
664     Fl_Widget::callback(Multiline_default_callback); parent_redraw(this);
665     when(FL_WHEN_ENTER_KEY|FL_WHEN_NOT_CHANGED);
666     color(FL_WHITE);
667     textfont(FL_TIMES);
668   }
669 
Comment_Multiline_Input(int x,int y,int w,int h,const char * l)670   Comment_Multiline_Input::Comment_Multiline_Input(int x,int y,int w,int h,const char * l):
671     Fl_Multiline_Input(x,y,w,h,l) {
672     if (parent()){
673       labelfont(parent()->labelfont());
674       labelsize(parent()->labelsize());
675       textfont(parent()->labelfont());
676       textsize(parent()->labelsize());
677     }
678     parent_redraw(this);
679     color(FL_WHITE);
680   }
681 
match()682   void Multiline_Input_tab::match(){
683     static bool recursive_call=false;
684     if (mark()!=position())
685       return;
686     int lastkey=Fl::event_key();
687     if (lastkey!='(' && lastkey!='[' && lastkey!='{' && lastkey!='}' && lastkey!=']' && lastkey!=')' && lastkey!='0' && lastkey!='9' && lastkey!='\'' && lastkey != '=' && lastkey !=FL_Left && lastkey !=FL_Right)
688       return;
689     if (recursive_call)
690       return;
691     recursive_call=true;
692     // check if cursor is on [, (, ), ]
693     int pos=position(),p0=pos;
694     const char * c=value();
695     int pmax=string(c).size();
696     bool closing=false,opening=false;
697     if (pos<pmax)
698       opening= c[pos]=='(' || c[pos]=='[' || c[pos]=='{';
699     if (!opening && pos){
700       closing = c[pos-1]==')' || c[pos-1]==']' || c[pos-1]=='}';
701       if (closing)
702 	--p0;
703     }
704     if (opening || closing){
705       Fl::flush();
706       usleep(100000);
707       if (true || !Fl::get_key(lastkey)){
708 	Fl::check();
709 	int pos=position(),p0=pos;
710 	const char * c=value();
711 	int pmax=string(c).size();
712 	bool closing=false,opening=false;
713 	if (pos<pmax)
714 	  opening= c[pos]=='(' || c[pos]=='[' || c[pos]=='{';
715 	if (!opening && pos){
716 	  closing = c[pos-1]==')' || c[pos-1]==']' || c[pos-1]=='}';
717 	  if (closing)
718 	    --p0;
719 	}
720 	if (opening || closing){
721 	  bool ok=giac::matchpos(c,p0);
722 	  if (!ok){
723 	    if (closing)
724 	      p0=pos-1;
725 	    else
726 	      p0=pos+1;
727 	  }
728 	  else {
729 	    if (opening && pos<pmax)
730 	      p0=p0+1;
731 	  }
732 	  mark(p0);
733 	  unsigned s=selection_color();
734 	  if (ok){
735 	    if (opening)
736 	      selection_color(FL_GREEN);
737 	    else
738 	      selection_color(fl_color_cube(0,FL_NUM_GREEN-1,2));
739 	  }
740 	  else
741 	    selection_color(FL_RED);
742 	  damage(damage() | FL_DAMAGE_ALL);
743 	  redraw();
744 	  Fl::flush();
745 	  usleep(70000);
746 	  if (!Fl::ready()){
747 	    for (int i=0;i<giac::PARENTHESIS_NWAIT;++i){
748 	      usleep(50000);
749 	      if (Fl::ready())
750 		break;
751 	    }
752 	  }
753 	  selection_color(s);
754 	  position(pos,pos);
755 	  damage(damage() | FL_DAMAGE_ALL);
756 	  redraw();
757 	  Fl::flush();
758 	}
759       }
760     }
761     recursive_call=false;
762   }
763 
handle(int event)764   int Multiline_Input_tab::handle(int event){
765     // if (handling)      return 0;
766     handling=true;
767     string oldval(value());
768     int res=in_handle(event);
769     History_Pack * g=get_history_pack(this);
770     if (g && value()!=oldval)
771       g->modified(false);
772     handling=false;
773     return res;
774   }
775 
is_text_a_level(const char * ch)776   bool is_text_a_level(const char * ch){
777     unsigned l=strlen(ch),i;
778     char cmp[]="// fltk ";
779     for (i=0;i<l && i<8;++i){
780       if (ch[i]!=cmp[i])
781 	return false;
782     }
783     return true;
784   }
785 
increase_size(Fl_Widget * wid,int L)786   void increase_size(Fl_Widget * wid, int L){
787     if (!wid) return;
788     // CERR << "increase size " << L << '\n';
789     if (L+wid->h()<=wid->labelsize()+4)
790       L=wid->labelsize()+5-wid->h();
791     if (!L) return;
792     int pos;
793     History_Pack * g = get_history_pack(wid,pos);
794     if (g){
795       Fl_Group * gr=wid->parent();
796       // find parents to above history pack
797       std::vector<Fl_Widget *> parents(1,wid);
798       for (;gr && gr!=g;gr=gr->parent()){
799 	parents.push_back(gr);
800       }
801       Fl_Widget  * tmp=0,*tmp2;
802       Fl_Group * tmpg=0;
803       int i=parents.size()-1;
804       for (;i>=0;--i){
805 	tmpg=gr;
806 	tmp=parents[i];
807 	// move children of gr below tmp
808 	int k=tmpg->children();
809 	for (int j=0;j<k;++j){
810 	  tmp2=tmpg->child(j);
811 	  if (tmp2->y()>tmp->y()){
812 	    tmp2->resize(tmp2->x(),tmp2->y()+L,tmp2->w(),tmp2->h());
813 	    tmp2->redraw();
814 	  }
815 	  else {
816 	    if (tmp2==tmp){
817 	      tmp->Fl_Widget::resize(tmp->x(),tmp->y(),tmp->w(),tmp->h()+L);
818 	      tmp->redraw();
819 	      gr=dynamic_cast<Fl_Group *>(tmp);
820 	    }
821 	  }
822 	} // end for j
823       } // end for i
824       // recompute pack
825       g->resize();
826       g->redraw();
827     }
828   }
829 
resize_nl()830   void Multiline_Input_tab::resize_nl(){
831     if (tableur)
832       return;
833     const char * ch = value();
834     unsigned i=0,l=strlen(ch),nl=1;
835     for (;i<l;++i){
836       if (ch[i]=='\n')
837 	++nl;
838     }
839     increase_size(this,6+nl*(labelsize()+2)-h());
840   }
841 
need_nl()842   bool Multiline_Input_tab::need_nl(){
843     int i=position(),i0;
844     const char * ch=value();
845     for (i0=i-1;i0>=0;--i0){
846       if (ch[i0]=='\n')
847 	break;
848     }
849     string s=string(ch).substr(i0+1,i-i0-1);
850     fl_font(textfont(),textsize());
851     int lw=int(1.2*fl_width(s.c_str()));
852     return lw>w()+20;
853   }
854 
height(const char * ch,int labelsize)855   int height(const char * ch,int labelsize){
856     int n=strlen(ch);
857     int h0=labelsize+4,res=n?h0+2:1;
858     int maxh=300;
859     for (int i=0;i<n-1;++i,++ch){
860       if (*ch=='\n'){
861 	res += h0;
862 	if (res>maxh)
863 	  break;
864       }
865     }
866     return res;
867   }
868 
in_handle(int event)869   int Multiline_Input_tab::in_handle(int event){
870     History_Pack * g=get_history_pack(this);
871     if (g && event==FL_MOUSEWHEEL){
872       if (!Fl::event_inside(this))
873 	return 0;
874       if (Fl_Scroll * sc = dynamic_cast<Fl_Scroll *>(g->parent())){
875 	int scy=sc->yposition()+labelsize()*Fl::e_dy;
876 	if (scy<0)
877 	  scy=0;
878 #ifdef _HAVE_FL_UTF8_HDR_
879 	sc->scroll_to(sc->xposition(),scy);
880 #else
881 	sc->position(sc->xposition(),scy);
882 #endif
883 	return 1;
884       }
885     }
886     if (event==FL_FOCUS || event==FL_PUSH || event==FL_KEYBOARD){
887       Xcas_input_focus=this;
888       autosave_disabled=false;
889     }
890     if (event==FL_FOCUS){
891       // Fl::focus(this);
892       // redraw();
893       return Fl_Multiline_Input::handle(event);
894     }
895     if (event==FL_UNFOCUS){
896       return Fl_Multiline_Input::handle(event);
897     }
898     if (event==FL_PUSH && tableur)
899       tableur->editing=true;
900     if (g && event==FL_PASTE){
901       // check that it's not a // fltk ... pasting a full level
902       const char * ch=Fl::event_text();
903       if (ch){
904 	if (is_text_a_level(ch))
905 	  return g->handle(event);
906       }
907       if (Fl_Multiline_Input::handle(event)){
908 	if (!tableur){
909 	  // add space for ch
910 	  unsigned i=0,l=strlen(ch),nl=0;
911 	  for (;i<l;++i){
912 	    if (ch[i]=='\n')
913 	      ++nl;
914 	  }
915 	  increase_size(this,nl*(labelsize()+1));
916 	}
917 	return 1;
918       }
919     }
920     if (event==FL_KEYBOARD) {
921       static string toolt;
922       string str;
923       int key=Fl::event_key();
924       if (g && key==FL_F+9){
925 	History_Fold * hf = get_history_fold(g);
926 	if (!hf) return 0;
927 	hf->eval();
928 	return 1;
929       }
930       if (Fl::event_text()){
931 	int i=Fl::event_text()[0];
932 	switch (i){
933 	case 22: case 25:
934 	  // Ctrl-V or Ctrl-Y paste, no need to check for level text
935 	  // because they paste using an FL_PASTE event
936 	case 3: case 4: case 5: case 21: case 23: case 24: case 26:
937 	  if (Fl_Multiline_Input::handle(event)){
938 	    resize_nl();
939 	    return 1;
940 	  }
941 	  break;
942 	case 1:
943 	  position(0);
944 	  mark(size());
945 	  Fl::selection(*this,value(),strlen(value()));
946 	  return 1;
947 	case 2:
948 	  insert("[]");
949 	  position(position()-1,position()-1);
950 	  return 1;
951 	case 11:
952 	  insert("\n");
953 	  resize_nl();
954 	  return 1;
955 	case 12:
956 	  insert("{}");
957 	  position(position()-1,position()-1);
958 	  return 1;
959 	case ' ': case ',': case '+':
960 	  if (need_nl()){
961 	    Fl_Multiline_Input::handle(event);
962 	    insert("\n");
963 	    resize_nl();
964 	    return 1;
965 	  }
966 	  break;
967 #ifndef __APPLE__
968 	case '(':
969 	  // Fl::belowmouse(this);
970 	  str=motclef(string(value()).substr(0,position()));
971 	  if (!str.empty()){
972 	    toolt=writehelp(helpon(str,*giac::vector_aide_ptr(),giac::language(g?g->contextptr:0),giac::vector_aide_ptr()->size()),giac::language(g?g->contextptr:0));
973 	    tooltip(toolt.c_str());
974 	    int hh=height(toolt.c_str(),Fl_Tooltip::size());
975 	    Fl_Tooltip::enter_area(this,0,-hh,0,0,toolt.c_str());
976 	  }
977 	  break;
978 	case ')':
979 	  tooltip("");
980 	  break;
981 #endif
982 	}
983       }
984       if (key==FL_Enter || key==FL_KP_Enter){
985 	if (Fl::event_shift() || strlen(value())==0){
986 	  insert("\n");
987 	  if (!tableur)
988 	    increase_size(this,labelsize()+1);
989 	  return 1;
990 	}
991 	giac::gen val;
992 	// keep warnings
993 	giac::context * contextptr=g?g->contextptr:0;
994 	ostringstream warnings ;
995 #ifdef WITH_MYOSTREAM
996 	giac::my_ostream * old=giac::logptr(contextptr);
997 	giac::my_ostream newptr(&warnings);
998 	logptr(&newptr,contextptr);
999 #else
1000 	my_ostream * old=giac::logptr(contextptr);
1001 	logptr(&warnings,contextptr);
1002 #endif
1003 	giac::first_error_line(contextptr)=0;
1004 	try {
1005 	  val=warn_equal(giac::gen(value(),contextptr),contextptr);
1006 	}
1007 	catch (...){
1008 	  ;
1009 	}
1010 	string warn0=warnings.str(),curline,debutline;
1011 	giac::logptr(old,contextptr);
1012 	static string warn;
1013 	warn="";
1014 	// Read warn0 lines and skip Parsing and Success in warn
1015 	unsigned warns=warn0.size();
1016 	char ch;
1017 	for (unsigned i=0;i<warns;){
1018 	  ch = warn0[i];
1019 	  ++i;
1020 	  curline += ch;
1021 	  if (ch=='\n' || i==warns){
1022 	    debutline=curline.substr(0,min(size_t(10),curline.size()));
1023 	    if (debutline!=gettext("// Parsing") && debutline!=gettext("// Success"))
1024 	      warn += curline;
1025 	    *old << curline;
1026 	    curline="";
1027 	  }
1028 	}
1029 	// check if there is a parse error at end of input
1030 	// if so then does like a event_shift
1031 	if (giac::first_error_line(contextptr)){
1032 	  string logs;
1033 	  int col=giac::lexer_column_number(contextptr);
1034 	  string token=giac::error_token_name(contextptr);
1035 	  logs = gettext("Syntax error in compatibility mode: ")+giac::print_program_syntax(giac::xcas_mode(contextptr))+"\n";
1036 	  logs += gettext("Parse error line ")+giac::print_INT_(giac::first_error_line(contextptr))+ gettext(" column ")+giac::print_INT_(col) + gettext(" at ") ;
1037 	  if (!token.empty() && token[0]=='%')
1038 	    logs += token.substr(1,token.size()-1);
1039 	  else
1040 	    logs += token;
1041 	  const char * ch=value();
1042 	  unsigned line=0,line_beg=0,line_end=0,taille=strlen(ch),i;
1043 	  for (i=0;i<taille;i++){
1044 	    if (ch[i]=='\n'){
1045 	      ++line;
1046 	      line_end=i;
1047 	      if (line==unsigned(giac::first_error_line(contextptr)))
1048 		break;
1049 	      line_beg=i+1;
1050 	    }
1051 	  }
1052 	  if (line_beg+col>taille){
1053 	    int ans=fl_ask("%s",((logs+'\n')+gettext("To get a newline, use shift-Enter. Reedit?")).c_str());
1054 	    if (ans==1){
1055 	      position(taille,taille);
1056 	      Fl::focus(this);
1057 	      handle(FL_FOCUS);
1058 	      // insert("\n");
1059 	      // if (!tableur) increase_size(this,labelsize()+1);
1060 	      return 1;
1061 	    }
1062 	  }
1063 	  else {
1064 	    // position(line_beg,line_end);
1065 	    int ans=fl_ask("%s",(logs+"\nReedit?").c_str());
1066 	    if (ans){
1067 	      i=line_beg+col-1;
1068 	      position(max(int(i-token.size()),0),i);
1069 	      Fl::focus(this);
1070 	      handle(FL_FOCUS);
1071 	      return 1;
1072 	    }
1073 	  }
1074 	} // if first_error_line
1075 	else {
1076 	  // Correct parse, check for warnings
1077 	  if (warn.empty()){
1078 	    _g=val;
1079 	    clear_changed();
1080 	  }
1081 	  else
1082 	    fl_message("%s",warn.c_str());
1083 	}
1084 	position(size(), 0);
1085 	history.push_back(value());
1086 	count=history.size();
1087 	int pos=-1;
1088 	History_Pack * hp=get_history_pack(this,pos);
1089 	if (hp)
1090 	  hp->update_pos=pos;
1091 	find_fold_autosave_function(true);
1092 	do_callback();
1093 	return 1;
1094       }
1095       if (key==FL_Escape){
1096 	if (tableur && tableur->editing){
1097 	  tableur->editing=false;
1098 	  Fl::focus(tableur);
1099 	}
1100 	else {
1101 	  Fl::selection(*this,value(),strlen(value()));
1102 	  value("");
1103 	}
1104 	return 1;
1105       }
1106       if (key==FL_BackSpace){
1107 	const char * ch = value();
1108 	unsigned l=strlen(ch);
1109 	unsigned p=position();
1110 	unsigned m=mark();
1111 	if (tableur && tableur->editing && !l){
1112 	  tableur->editing=false;
1113 	  Fl::focus(tableur);
1114 	  return 1;
1115 	}
1116 	if (l && p && p==m && p<=l && ch[p-1]=='\n'){
1117 	  if (!tableur)
1118 	    increase_size(this,-labelsize()-1);
1119 	}
1120       }
1121       if (tableur){
1122 	tableur->editing=true;
1123 	tableur->edit_row=tableur->row();
1124 	tableur->edit_col=tableur->col();
1125       }
1126       if (g && !tableur && key==FL_Right && !Fl::event_state(FL_SHIFT | FL_CTRL | FL_ALT)){
1127 	int i=position();
1128 	if (i==size())
1129 	  return 1;
1130       }
1131       if (g && !tableur && key==FL_Left && !Fl::event_state(FL_SHIFT | FL_CTRL | FL_ALT)){
1132 	int i=position();
1133 	if (!i)
1134 	  return 1;
1135       }
1136       if (g && !tableur && (key==FL_Up || key==FL_Page_Up) && !Fl::event_state(FL_SHIFT | FL_CTRL | FL_ALT)){
1137 	// if we are at the top line, go one level up
1138 	int i=position();
1139 	if (i)
1140 	  i=line_start(i);
1141 	if (!i || (key==FL_Page_Up) ){
1142 	  redraw();
1143 	  g->_sel_begin=-1;
1144 	  int pos=g->set_sel_begin(this);
1145 	  g->_sel_begin=-1;
1146 	  --pos;
1147 	  if (pos>=0)
1148 	    g->focus(pos,true);
1149 	  return 1;
1150 	}
1151       }
1152       if (g && !tableur && (key==FL_Down || key==FL_Page_Down) && !Fl::event_state(FL_SHIFT | FL_CTRL | FL_ALT)){
1153 	// if we are at the top line, go one level up
1154 	int i=position();
1155 	i=line_end(i);
1156 	if (i>=size() || (key==FL_Page_Down) ){
1157 	  redraw();
1158 	  g->_sel_begin=-1;
1159 	  int pos=g->set_sel_begin(this)+1;
1160 	  g->_sel_begin=-1;
1161 	  g->focus(pos,true);
1162 	  return 1;
1163 	}
1164       }
1165       if (key==FL_Up || key==FL_Down){
1166 	if (!Fl::event_state(FL_SHIFT |FL_CTRL | FL_ALT) && Fl_Multiline_Input::handle(event)){
1167 	  match();
1168 	  redraw();
1169 	  return 1;
1170 	}
1171 	if (!Fl::event_state(FL_CTRL | FL_ALT))
1172 	  return 0;
1173 	// not handled by the input, use history
1174 	int s=history.size();
1175 	Fl::focus(this);
1176 	if (Fl::event_key()==FL_Up){
1177 	  --count;
1178 	  if (count<0)
1179 	    count=0;
1180 	}
1181 	else {
1182 	  ++count;
1183 	  if (count>=s)
1184 	    count=max(0,s-1);
1185 	}
1186 	if (count<s){
1187 	  string v(value());
1188 	  int pos1=position(),pos2=mark();
1189 	  if (pos1>pos2)
1190 	    giac::swapint(pos1,pos2);
1191 	  value((v.substr(0,pos1)+history[count]+v.substr(pos2,v.size()-pos2)).c_str());
1192 	  position(pos1,pos1+history[count].size());
1193 	}
1194 	set_changed();
1195 	return 1;
1196       }
1197     }
1198     if (!completion_tab || event!=FL_KEYBOARD || (Fl::event_text() && Fl::event_text()[0]!=9 && Fl::event_key()!=FL_F+1)){
1199       const char * ch = value();
1200       unsigned l=strlen(ch);
1201       unsigned p=position();
1202       unsigned m=mark();
1203       bool test_backspace=(l && p && p==m && p<=l && ch[p-1]=='\n') || (p!=m);
1204       int res=Fl_Multiline_Input::handle(event);
1205       if (event==FL_KEYBOARD && res ){
1206 	match();
1207 	redraw();
1208 	if ( test_backspace && Fl::event_key()==FL_BackSpace)
1209 	  resize_nl();
1210       }
1211       return res;
1212     }
1213     string s(value()),ans;
1214     if (position()<int(s.size()))
1215       s=s.substr(0,position());
1216     int delta=s.size();
1217     s=motclef(s);
1218     delta -= s.size();
1219     int remove;
1220     if (int ii=handle_tab(s,*completion_tab,window()->w(),window()->h()/3,remove,ans)){
1221       window()->show();
1222       cut(-remove-delta);
1223       if (ii==1){
1224 	insert((ans+"()").c_str());
1225 	position(size()-1);
1226       }
1227       else {
1228 	insert(ans.c_str());
1229 	position(size());
1230       }
1231       if (parent())
1232 	parent_redraw(parent());
1233     }
1234     Fl::focus(this);
1235     handle(FL_FOCUS);
1236     return 1;
1237   }
1238 
1239   /* void Multiline_Input_tab::draw(){
1240     int pos=position();
1241     int clip_x,clip_y,clip_w,clip_h;
1242     fl_clip_box(x(),y(),w(),h(),clip_x,clip_y,clip_w,clip_h);
1243     if (clip_w==0 || clip_h==0)
1244       return;
1245     // if (pos!=mark()){
1246       Fl_Multiline_Input::draw();
1247       return;
1248       // }
1249       } */
1250 
1251   // geo_print / geoprint
_pnt2string(const giac::gen & g,const giac::context * contextptr)1252   std::string _pnt2string(const giac::gen & g,const giac::context * contextptr){
1253     unsigned ta=taille(g,100);
1254     if (ta>100)
1255       return "Done";
1256     if (g.is_symb_of_sommet(giac::at_pnt) && !is3d(g)){
1257       giac::gen & f=g._SYMBptr->feuille;
1258       if (f.type==giac::_VECT && !f._VECTptr->empty()){
1259 	giac::gen f0=f._VECTptr->front();
1260 	if (f0.is_symb_of_sommet(giac::at_legende)){
1261 	  return g.print(contextptr);
1262 	}
1263 	if (f0.is_symb_of_sommet(giac::at_curve)){
1264 	  giac::gen f1=f[0]._SYMBptr->feuille;
1265 	  if (f1.type==giac::_VECT && !f1._VECTptr->empty() ){
1266 	    giac::gen f1f=f1._VECTptr->front();
1267 	    if (f1f.type==giac::_VECT && f1f._VECTptr->size()>=4){
1268 	      giac::vecteur f1v=*f1f._VECTptr;
1269 	      return "plotparam("+pnt2string(f1v[0],contextptr)+","+f1v[1].print(contextptr)+"="+f1v[2].print(contextptr)+".."+f1v[3].print(contextptr)+")";
1270 	    }
1271 	  }
1272 	}
1273 	if (f0.is_symb_of_sommet(giac::at_cercle) && f0._SYMBptr->feuille.type==giac::_VECT){
1274 	  if (f0._SYMBptr->feuille._VECTptr->size()==3 && ((*f0._SYMBptr->feuille._VECTptr)[2]!=giac::cst_two_pi || (*f0._SYMBptr->feuille._VECTptr)[1]!=0))
1275 	    return f0.print(contextptr);
1276 	  giac::gen centre,rayon;
1277 	  if (!giac::centre_rayon(f0,centre,rayon,true,0))
1278 	    return "cercle_error";
1279 	  if (!complex_mode(contextptr) && (centre.type<giac::_IDNT || centre.type==giac::_FRAC) )
1280 	    return gettext("circle")+string("(point(")+giac::re(centre,contextptr).print(contextptr)+","+giac::im(centre,contextptr).print(contextptr)+"),"+rayon.print(contextptr)+")";
1281 	  else
1282 	    return gettext("circle")+string("(point(")+centre.print(contextptr)+"),"+rayon.print(contextptr)+")";
1283 	}
1284 	if (f0.type==giac::_VECT &&f0.subtype!=giac::_POINT__VECT){
1285 	  std::string s=gettext("polygon")+string("(");
1286 	  giac::const_iterateur it=f0._VECTptr->begin(),itend=f0._VECTptr->end();
1287 	  if ( itend-it==2){
1288 	    switch(f0.subtype){
1289 	    case giac::_LINE__VECT:
1290 	      s=gettext("line")+string("(");
1291 	      break;
1292 	    case giac::_HALFLINE__VECT:
1293 	      s=gettext("half_line")+string("(");
1294 	      break;
1295 	    case giac::_GROUP__VECT:
1296 	      s=gettext("segment")+string("(");
1297 	      break;
1298 	    }
1299 	    if (f0.subtype==giac::_LINE__VECT && it->type!=giac::_VECT){ // 2-d line
1300 	      s += _equation(g,contextptr).print(contextptr) + ")";
1301 	      return s;
1302 	    }
1303 	  }
1304 	  for (;it!=itend;){
1305 	    s += "point(";
1306 	    if (!complex_mode(contextptr) && (it->type<giac::_IDNT || it->type==giac::_FRAC) )
1307 	      s += giac::re(*it,contextptr).print(contextptr)+","+giac::im(*it,contextptr).print(contextptr);
1308 	    else
1309 	      s+=it->print(contextptr);
1310 	    s+=")";
1311 	    ++it;
1312 	    s += it==itend?")":",";
1313 	  }
1314 	  return s;
1315 	}
1316 	if ( (f0.type!=giac::_FRAC && f0.type>=giac::_IDNT) || is3d(g) || complex_mode(contextptr))
1317 	  return "point("+f0.print(contextptr)+")";
1318 	else
1319 	  return "point("+giac::re(f0,contextptr).print(contextptr)+","+giac::im(f0,contextptr).print(contextptr)+")";
1320       }
1321     }
1322     if (g.type==giac::_VECT && !g._VECTptr->empty() && g._VECTptr->back().is_symb_of_sommet(giac::at_pnt)){
1323       std::string s = "[";
1324       giac::const_iterateur it=g._VECTptr->begin(),itend=g._VECTptr->end();
1325       for (;it!=itend;){
1326 	s += pnt2string(*it,contextptr);
1327 	++it;
1328 	s += it==itend?"]":",";
1329       }
1330       return s;
1331     }
1332     return g.print(contextptr);
1333   }
1334 
pnt2string(const giac::gen & g,const giac::context * contextptr)1335   std::string pnt2string(const giac::gen & g,const giac::context * contextptr){
1336     try {
1337       return _pnt2string(g,contextptr);
1338     }
1339     catch (...){
1340       return "conversion error";
1341     }
1342   }
1343 
value(const giac::gen & _g)1344   void Gen_Output::value(const giac::gen & _g){
1345     g=_g;
1346     Fl_Multiline_Output::value(pnt2string(g,get_context(this)).c_str());
1347   }
1348 
value(const char * ch)1349   void Gen_Output::value(const char * ch){
1350     giac::context * contextptr=get_context(this);
1351     g=giac::gen(ch,contextptr);
1352     Fl_Multiline_Output::value(ch);
1353   }
1354 
value() const1355   giac::gen Gen_Output::value() const {
1356     return g;
1357   }
1358 
set_g(const giac::gen & g)1359   void Multiline_Input_tab::set_g(const giac::gen & g) {
1360     const giac::context * contextptr = get_context(this);
1361     value(g.print(contextptr).c_str());
1362     redraw();
1363     clear_changed();
1364     _g=g;
1365   }
1366 
g()1367   giac::gen Multiline_Input_tab::g() {
1368     if (!changed())
1369       return _g;
1370     giac::context * contextptr=get_context(this);
1371     _g=giac::gen(value(),contextptr);
1372     clear_changed();
1373     return _g;
1374   }
1375 
value(const char * ch)1376   void Enlargable_Multiline_Output::value(const char * ch){
1377     Fl_Multiline_Output::value(ch);
1378     resize();
1379   }
1380 
resize()1381   void Enlargable_Multiline_Output::resize(){
1382     // Count number of \n
1383     const char * ch=Fl_Multiline_Output::value();
1384     int n=strlen(ch);
1385     int h0=labelsize(),res=n?h0+4:1;
1386     int j=0,nc=0,nl=0;
1387     string temp="";
1388     fl_font(textfont(),labelsize());
1389     for (int i=0;i<n;++i,++ch){
1390       if (*ch=='\n'){
1391 	res += h0+1;
1392 	nc=max(nc,j);
1393 	j=0;
1394 	nl=max(nl,int(fl_width(temp.c_str())));
1395 	temp ="";
1396       }
1397       else {
1398 	j++;
1399 	temp += *ch;
1400       }
1401     }
1402     nl+=6;
1403     if (parent()){
1404       int w=parent()->w();
1405       int h=parent()->h();
1406       if (res<h-6)
1407 	res=h-6;
1408       if (nl<w-labelsize())
1409 	nl=w-labelsize();
1410       parent()->redraw();
1411     }
1412     Fl_Multiline_Output::resize(x(),y(),nl,res);
1413   }
1414 
handle(int event)1415   int Comment_Multiline_Input::handle(int event){
1416     string oldval(value());
1417     int res=in_handle(event);
1418     History_Pack * g=get_history_pack(this);
1419     if (g && value()!=oldval)
1420       g->modified(false);
1421     return res;
1422   }
1423 
in_handle(int event)1424   int Comment_Multiline_Input::in_handle(int event){
1425     if (event==FL_FOCUS){
1426       Xcas_input_focus=this;
1427       autosave_disabled=false;
1428       // Fl::focus(this);
1429       // redraw();
1430       return Fl_Multiline_Input::handle(event);
1431     }
1432     if (event==FL_UNFOCUS){
1433       return Fl_Multiline_Input::handle(event);
1434     }
1435     if (event==FL_KEYBOARD){
1436       redraw();
1437       int key=Fl::event_key();
1438       if ( (key==FL_Enter || key==FL_KP_Enter)
1439 	   && Fl::event_shift()){
1440 	insert("\n");
1441 	increase_size(this,labelsize()+2);
1442 	return 1;
1443       }
1444       if (key==FL_BackSpace){
1445 	const char * ch = value();
1446 	unsigned l=strlen(ch);
1447 	unsigned p=position();
1448 	unsigned m=mark();
1449 	if (l && p && p==m && p<=l && ch[p-1]=='\n'){
1450 	  increase_size(this,-labelsize()-2);
1451 	}
1452       }
1453       History_Pack * hp = get_history_pack(this);
1454       int change_focus=0;
1455       if (hp && ( (key==FL_Up && !line_start(position())) || key==FL_Page_Up))
1456 	change_focus=-1;
1457       if (hp && ( (key==FL_Down && line_end(position())==size()) || key==FL_Page_Down || key==FL_Enter))
1458 	change_focus=1;
1459       if (key==FL_Enter){
1460 	string s=value();
1461 	s+=' ';
1462 	// search in s for a word with ., check if it's an existing filename or URL
1463 	int ss=s.size();
1464 	for (int i=0;i<ss;++i){
1465 	  if (s[i]==' ')
1466 	    continue;
1467 	  int wordbegin=i;
1468 	  bool haspoint=false;
1469 	  // begin of word
1470 	  for (++i;i<ss;++i){
1471 	    if (s[i]=='.' &&
1472 		i<ss-1 && s[i+1]>='a' && s[i+1]<='z' &&
1473 		i && (isalpha(s[i-1]) || (s[i-1]>='0' && s[i-1]<='9'))
1474 		)
1475 	      haspoint=true;
1476 	    if (s[i]==' ')
1477 	      break;
1478 	  }
1479 	  if (!haspoint)
1480 	    continue;
1481 	  string url;
1482 	  if (i>wordbegin+8 && (s.substr(wordbegin,8)=="https://" || s.substr(wordbegin,7)=="http://" || s.substr(wordbegin,7)=="file://")){
1483 	    url=s.substr(wordbegin,i-wordbegin);
1484 	  }
1485 	  else {
1486 	    if (s[wordbegin]=='@'){
1487 	      url=s.substr(wordbegin+1,i-wordbegin-1);
1488 	    }
1489 	  }
1490 	  if (url.empty())
1491 	    continue;
1492 	  if (giac::is_file_available(url.c_str())){
1493 	    if (url[0]!='/')
1494 	      url=*giac::_pwd(0,0)._STRNGptr+"/"+url;
1495 	  }
1496 	  else {
1497 	    if (url.size()<8 || (url.substr(0,8)!="https://" && url.substr(0,7)!="http://" && url.substr(0,7)!="file://"))
1498 	      url="http://"+url;
1499 	  }
1500 	  giac::system_browser_command(url);
1501 	  // break;
1502 	}
1503       }
1504       if (change_focus && hp){
1505 	hp->_sel_begin=-1;
1506 	int pos=hp->set_sel_begin(this);
1507 	if (pos+change_focus>=0)
1508 	  hp->focus(pos+change_focus,true);
1509 	return 1;
1510       }
1511     }
1512     return Fl_Multiline_Input::handle(event);
1513   }
1514 
1515 #ifndef NO_NAMESPACE_XCAS
1516 } // namespace xcas
1517 #endif // ndef NO_NAMESPACE_XCAS
1518 
1519 
1520 #endif // HAVE_LIBFLTK
1521