1 // -*- mode:C++ ; compile-command: "g++ -DHAVE_CONFIG_H -I. -I.. -I../include -I../../giac/include -g -c Editeur.cc" -*-
2 #include "Editeur.h"
3 #include "Input.h"
4 #include "Tableur.h"
5 /*
6  *  Copyright (C) 2000,2014 B. Parisse, Institut Fourier, 38402 St Martin d'Heres
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 3 of the License, or
11  *  (at your option) any later version.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program. If not, see <http://www.gnu.org/licenses/>.
20  */
21 
22 
23 #ifdef HAVE_LIBFLTK
24 #include <FL/fl_ask.H>
25 #include <FL/fl_ask.H>
26 #include <FL/Fl_Return_Button.H>
27 #include <FL/Fl_Tooltip.H>
28 #include <fstream>
29 #include "vector.h"
30 #include <algorithm>
31 #include <fcntl.h>
32 #include <cmath>
33 #include <time.h> // for nanosleep
34 #include <stdio.h>
35 #include <dirent.h>
36 #include <sys/stat.h> // auto-recovery function
37 #include "History.h"
38 #include "Print.h"
39 #include "Equation.h"
40 #ifdef HAVE_UNISTD_H
41 #include <unistd.h>
42 #endif
43 
44 using namespace std;
45 using namespace giac;
46 
47 #ifndef NO_NAMESPACE_XCAS
48 namespace xcas {
49 #endif // ndef NO_NAMESPACE_XCAS
50 
51   int alt_ctrl=0;
52   void (*alt_ctrl_cb)(int)=0;
53 
54   std::vector<std::string> Xcas_Text_Editor::history;
55   int Xcas_Text_Editor::count=0;
56 
57   // Highlighting, borrowed from FLTK1.2/test/editor.cxx
58   // Syntax highlighting stuff...
59 
60   Fl_Text_Display::Style_Table_Entry styletable_init[] = {	// Style table
61     { FL_BLACK,      FL_COURIER,        14 }, // A - Plain
62     { FL_DARK_GREEN, FL_COURIER_ITALIC, 14 }, // B - Line comments
63     { FL_DARK_GREEN, FL_COURIER_ITALIC, 14 }, // C - Block comments
64     { FL_CYAN,       FL_COURIER,        14 }, // D - Strings
65     { FL_DARK_RED,   FL_COURIER,        14 }, // E - Directives
66     { FL_DARK_RED,   FL_COURIER_BOLD,   14 }, // F - Types
67     { FL_BLUE,       FL_COURIER_BOLD,   14 }  // G - Keywords
68   };
69   int styletable_n=sizeof(styletable_init) / sizeof(styletable_init[0]);
70   const char         *code_keywords[] = {   // List of known giac keywords...
71     "BEGIN",
72     "BREAK",
73     "CATCH",
74     "CONTINUE",
75     "Cycle",
76     "DO",
77     "Dialog",
78     "ELIF",
79     "ELSE",
80     "END",
81     "Else",
82     "ElseIf",
83     "EndDlog",
84     "EndFor",
85     "EndFunc",
86     "EndIf",
87     "EndLoop",
88     "EndPrgm",
89     "EndTry",
90     "EndWhile",
91     "Exit",
92     "FOR",
93     "FROM",
94     "For",
95     "Func",
96     "Goto",
97     "IF",
98     "If",
99     "LOCAL",
100     "Lbl",
101     "Local"
102     "Loop",
103     "Prgm",
104     "REPEAT",
105     "Return",
106     "STEP",
107     "THEN",
108     "TO",
109     "TRY",
110     "Then",
111     "Try",
112     "WHILE",
113     "While",
114     "alors",
115     "and",
116     "break",
117     "by",
118     "case",
119     "catch",
120     "continue",
121     "de",
122     "def",
123     "default",
124     "do",
125     "downto",
126     "elif",
127     "else",
128     "end",
129     "end_case",
130     "end_for",
131     "end_if",
132     "end_proc",
133     "end_while",
134     "et",
135     "except",
136     "faire",
137     "false",
138     "ffaire",
139     "ffonction",
140     "fi",
141     "fonction",
142     "for",
143     "fpour",
144     "from",
145     "fsi",
146     "ftantque",
147     "function",
148     "global",
149     "goto",
150     "if",
151     "jusqu_a",
152     "jusqua",
153     "jusque",
154     "label",
155     "local",
156     "non",
157     "not",
158     "od",
159     "operator",
160     "or",
161     "ou",
162     "pas",
163     "pour",
164     "proc",
165     "repeat",
166     "repeter",
167     "retourne",
168     "return",
169     "si",
170     "sinon",
171     "step",
172     "switch",
173     "tantque",
174     "then",
175     "throw",
176     "to",
177     "true",
178     "try",
179     "until",
180     "var",
181     "while",
182     "xor"
183   };
184 
185 //
186 // 'compare_keywords()' - Compare two keywords...
187 //
188 
189   int
compare_keywords(const void * a,const void * b)190   compare_keywords(const void *a,
191 		   const void *b) {
192     return (strcmp(*((const char **)a), *((const char **)b)));
193 }
194 
alpha_order(const string & S1,const string & S2)195   bool alpha_order(const string & S1,const string & S2){
196     string s1 =S1;
197     string s2 =S2;
198     for (unsigned i=0;i<s1.size();++i)
199       s1[i]=tolower(s1[i]);
200     for (unsigned i=0;i<s2.size();++i)
201       s2[i]=tolower(s2[i]);
202     if (s1!=s2)
203       return s1<s2;
204     return S1< S2;
205   }
206 
207 //
208 // 'style_parse()' - Parse text and produce style data.
209 //
210 
211   void
style_parse(const char * text,char * style,int length)212   style_parse(const char *text,
213 	      char       *style,
214 	      int        length) {
215     char	     current;
216     int	     col;
217     int	     last;
218     char	     buf[255],
219       *bufptr;
220     const char *temp;
221 
222     for (current = *style, col = 0, last = 0; length > 0; length --, text ++) {
223       if (current == 'B' || current>='E') current = 'A';
224       if (current == 'A') {
225 	// Check for directives, comments, strings, and keywords...
226 	if (col == 0 && *text == '#' && !python_color) {
227 	  // Set style to directive
228 	  current = 'E';
229 	} else if (strncmp(text, "//", 2) == 0 || (python_color &&strncmp(text,"#",1)==0)) {
230 	  current = 'B';
231 	  for (; length > 0 && *text != '\n'; length --, text ++) *style++ = 'B';
232 
233 	  if (length == 0) break;
234 	} else if (strncmp(text, "/*", 2) == 0) {
235 	  current = 'C';
236 	} else if (strncmp(text, "\\\"", 2) == 0) {
237 	  // Quoted quote...
238 	  *style++ = current;
239 	  *style++ = current;
240 	  text ++;
241 	  length --;
242 	  col += 2;
243 	  continue;
244 	} else if (*text == '\"') {
245 	  current = 'D';
246 	} else if (!last && isalphan(*text)) {
247 	  // Might be a keyword...
248 	  for (temp = text, bufptr = buf;
249 	       isalphan(*temp) && bufptr < (buf + sizeof(buf) - 1);
250 	       *bufptr++ = *temp++);
251 
252 	  if (!isalphan(*temp)) {
253 	    *bufptr = '\0';
254 
255 	    bufptr = buf;
256 
257 	    if (bsearch(&bufptr, code_keywords,
258 			       sizeof(code_keywords) / sizeof(code_keywords[0]),
259 			       sizeof(code_keywords[0]), compare_keywords)) {
260 	      current='A';
261 	      while (text < temp) {
262 		*style++ = 'G';
263 		text ++;
264 		length --;
265 		col ++;
266 	      }
267 
268 	      text --;
269 	      length ++;
270 	      last = 1;
271 	      continue;
272 	    } else if (giac::vector_completions_ptr() && binary_search(giac::vector_completions_ptr()->begin(),giac::vector_completions_ptr()->end(),bufptr,alpha_order)) {
273 	      current='A';
274 	      while (text < temp) {
275 		*style++ = 'F';
276 		text ++;
277 		length --;
278 		col ++;
279 	      }
280 
281 	      text --;
282 	      length ++;
283 	      last = 1;
284 	      continue;
285 	    }
286 	  }
287 	}
288       } else if (current == 'C' && strncmp(text, "*/", 2) == 0) {
289 	// Close a C comment...
290 	*style++ = current;
291 	*style++ = current;
292 	text ++;
293 	length --;
294 	current = 'A';
295 	col += 2;
296 	continue;
297       } else if (current == 'D') {
298 	// Continuing in string...
299 	if (strncmp(text, "\\\"", 2) == 0) {
300 	  // Quoted end quote...
301 	  *style++ = current;
302 	  *style++ = current;
303 	  text ++;
304 	  length --;
305 	  col += 2;
306 	  continue;
307 	} else if (*text == '\"') {
308 	  // End quote...
309 	  *style++ = current;
310 	  col ++;
311 	  current = 'A';
312 	  continue;
313 	}
314       } else if ( (current=='F' || current=='G') && !isalphan(*text)){
315 	current='A';
316       }
317       // Copy style info...
318       if (current == 'A' && (*text == '{' || *text == '}')) *style++ = 'G';
319       else *style++ = current;
320       col ++;
321 
322       last = isalphan(*text) || *text == '.';
323 
324       if (*text == '\n') {
325 	// Reset column and possibly reset the style
326 	col = 0;
327 	if (current == 'B' || current >= 'E') current = 'A';
328       }
329     }
330   }
331 
332 
333 //
334 // 'style_update()' - Update the style buffer...
335 //
336 
337   void
style_update(int pos,int nInserted,int nDeleted,int,const char *,void * cbArg)338   style_update(int        pos,		// I - Position of update
339 	       int        nInserted,	// I - Number of inserted chars
340 	       int        nDeleted,	// I - Number of deleted chars
341 	       int        /*nRestyled*/,	// I - Number of restyled chars
342 	       const char * /*deletedText*/,// I - Text that was deleted
343 	       void       *cbArg
344 	       ) {	// I - Callback data
345     int	start,				// Start of text
346       end;				// End of text
347     char	last,				// Last style on line
348       *style,				// Style data
349       *text;				// Text data
350 
351     Fl_Text_Buffer * stylebuf= ((Xcas_Text_Editor *) cbArg)->stylebuf;
352     // If this is just a selection change, just unselect the style buffer...
353     if (nInserted == 0 && nDeleted == 0) {
354       stylebuf->unselect();
355       return;
356     }
357 
358     // Track changes in the text buffer...
359     if (nInserted > 0) {
360       // Insert characters into the style buffer...
361       style = new char[nInserted + 1];
362       memset(style, 'A', nInserted);
363       style[nInserted] = '\0';
364 
365       stylebuf->replace(pos, pos + nDeleted, style);
366       delete[] style;
367     } else {
368       // Just delete characters in the style buffer...
369       stylebuf->remove(pos, pos + nDeleted);
370     }
371 
372     // Select the area that was just updated to avoid unnecessary
373     // callbacks...
374     stylebuf->select(pos, pos + nInserted - nDeleted);
375 
376     // Re-parse the changed region; we do this by parsing from the
377     // beginning of the line of the changed region to the end of
378     // the line of the changed region...  Then we check the last
379     // style character and keep updating if we have a multi-line
380     // comment character...
381     Fl_Text_Buffer * textbuf=((Fl_Text_Editor *)cbArg)->buffer();
382     start = textbuf->line_start(pos);
383     end   = textbuf->line_end(pos + nInserted);
384     text  = textbuf->text_range(start, end);
385     style = stylebuf->text_range(start, end);
386     last  = start==end?0:style[end - start - 1];
387 
388     //  printf("start = %d, end = %d, text = \"%s\", style = \"%s\"...\n",
389     //         start, end, text, style);
390 
391     style_parse(text, style, end - start);
392 
393     //  printf("new style = \"%s\"...\n", style);
394 
395     stylebuf->replace(start, end, style);
396     ((Fl_Text_Editor *)cbArg)->redisplay_range(start, end);
397 
398     if (start==end || last != style[end - start - 1]) {
399       // The last character on the line changed styles, so reparse the
400       // remainder of the buffer...
401       free(text);
402       free(style);
403 
404       end   = textbuf->length();
405       text  = textbuf->text_range(start, end);
406       style = stylebuf->text_range(start, end);
407 
408       style_parse(text, style, end - start);
409 
410       stylebuf->replace(start, end, style);
411       ((Fl_Text_Editor *)cbArg)->redisplay_range(start, end);
412     }
413 
414     free(text);
415     free(style);
416   }
417 
418   //
419   // 'style_unfinished_cb()' - Update unfinished styles.
420   //
421 
422   void
style_unfinished_cb(int,void *)423   style_unfinished_cb(int, void*) {
424   }
425 
426 
427   //
428   // 'style_init()' - Initialize the style buffer...
429   //
430 
style_init(Xcas_Text_Editor * textbuf)431   void  style_init(Xcas_Text_Editor * textbuf  ) {
432   }
433 
434   bool editor_changed=false;
435 
Editor_changed_cb(int,int nInserted,int nDeleted,int,const char *,void * v)436   void Editor_changed_cb(int, int nInserted, int nDeleted,int, const char*, void* v) {
437     if ((nInserted || nDeleted))
438       editor_changed = true;
439   }
440 
do_find_editor(Fl_Widget * widget)441   Fl_Text_Editor * do_find_editor(Fl_Widget * widget){
442     if (!widget)
443       return 0;
444     Fl_Group * gr = widget->parent();
445     if (!gr)
446       return 0;
447     Editeur * e=dynamic_cast<Editeur *>(gr);
448     if (e)
449       return e->editor;
450     else
451       return 0;
452   }
453 
find_editor(Fl_Widget * widget)454   Fl_Text_Editor * find_editor(Fl_Widget * widget){
455     Fl_Text_Editor * ed=do_find_editor(widget);
456     if (ed)
457       return ed;
458     ed=do_find_editor(Fl::focus());
459     if (!ed)
460       fl_alert("%s",gettext("No program editor found. Please click in or add one"));
461     return ed;
462   }
463 
cb_Editeur(Fl_Menu_ * m,void *)464   static void cb_Editeur(Fl_Menu_* m , void*) {
465     Fl_Text_Editor * e = find_editor(m);
466     if (e){
467     }
468   }
469 
470   static void cb_Editeur_Save(Fl_Widget * m , void*);
471 
editeur_load(Fl_Text_Editor * e)472   std::string editeur_load(Fl_Text_Editor * e){
473     if (e){
474       char * newfile = load_file_chooser("Insert program", ("*."+dynamic_cast<Editeur *>(e->parent())->extension).c_str(), "",0,false);
475       if ( file_not_available(newfile) )
476 	return "";
477       e->buffer()->insertfile(newfile,e->insert_position());
478       return newfile;
479     }
480     return "";
481   }
482 
cb_Editeur_Load(Fl_Widget * m,void *)483   static void cb_Editeur_Load(Fl_Widget * m , void*) {
484     Fl_Text_Editor * e = find_editor(m);
485     if (e){
486       if (e->changed()){
487 	int i=fl_ask("%s","Buffer changed. Save?");
488 	if (i)
489 	  cb_Editeur_Save(m,0);
490       }
491       char * newfile = load_file_chooser("Insert program", ("*."+dynamic_cast<Editeur *>(e->parent())->extension).c_str(), "",0,false);
492       if ( file_not_available(newfile) )
493 	return;
494       e->buffer()->loadfile(newfile);
495       e->label(newfile);
496       if (Editeur * ed=dynamic_cast<Editeur *>(m->parent())){
497 	ed->output->value(remove_path(e->label()).c_str());
498 	ed->output->redraw();
499       }
500     }
501   }
502 
cb_Editeur_Insert(Fl_Menu_ * m,void *)503   static void cb_Editeur_Insert(Fl_Menu_* m , void*) {
504     Fl_Text_Editor * e = find_editor(m);
505     if (e)
506       editeur_load(e);
507   }
508 
editeur_insert(Fl_Text_Editor * e,const std::string & newfile,int mode)509   void editeur_insert(Fl_Text_Editor * e,const std::string & newfile,int mode){
510     ifstream in(newfile.c_str());
511     char ch;
512     string tmp;
513     const context * contextptr = get_context(e);
514     while (in){
515       in.get(ch);
516       if (in.eof())
517 	break;
518       tmp += ch;
519     }
520     if (mode<0 || ((mode&0xff)==xcas_mode(contextptr) && (mode>=256)==python_compat(contextptr))){
521       e->insert(tmp.c_str());
522       return;
523     }
524     int save_maple_mode=xcas_mode(contextptr);
525     int save_python=python_compat(contextptr);
526     xcas_mode(contextptr)=(mode&0xff);
527     python_compat((mode>=256?1:0),contextptr);
528     gen g;
529     try {
530       g=gen(tmp,contextptr);
531     }
532     catch (std::runtime_error & err){
533       cerr << err.what() << '\n';
534     }
535     xcas_mode(contextptr)=save_maple_mode;
536     python_compat(save_python,contextptr);
537     if (g.type==_VECT && g.subtype==_SEQ__VECT && xcas_mode(contextptr) !=3 ){
538       const_iterateur it=g._VECTptr->begin(),itend=g._VECTptr->end();
539       for (;it!=itend;++it){
540 	e->insert(it->print(contextptr).c_str());
541 	e->insert(";\n");
542       }
543     }
544     else
545       e->insert(g.print(contextptr).c_str());
546   }
547 
editeur_translate(Fl_Text_Editor * e,int mode)548   void editeur_translate(Fl_Text_Editor * e,int mode){
549     if (!Xcas_help_output)
550       return;
551     static string res;
552     gen g;
553     context * contextptr = get_context(e);
554     try {
555       char * ch=e->buffer()->text();
556       string res(ch);
557       free(ch);
558       g=gen(res,contextptr);
559     }
560     catch (std::runtime_error & err){
561       cerr << err.what() << '\n';
562       return;
563     }
564     int save_maple_mode=xcas_mode(contextptr);
565     xcas_mode(contextptr)=mode;
566     res="";
567     if (g.type==_VECT && g.subtype==_SEQ__VECT && xcas_mode(contextptr) !=3 ){
568       const_iterateur it=g._VECTptr->begin(),itend=g._VECTptr->end();
569       for (;it!=itend;++it){
570 	res +=  it->print_universal(contextptr) ;
571 	res += ";\n";
572       }
573     }
574     else
575       res = g.print_universal(contextptr) + ((xcas_mode(contextptr)==3)?"\n":";\n");
576     xcas_mode(contextptr)=save_maple_mode;
577     Xcas_help_output->value(res.c_str());
578     Xcas_help_output->position(0,res.size());
579     // Xcas_help_output->copy();
580     Fl::copy(Xcas_help_output->Fl_Output::value(),res.size(),1);
581     Fl::selection(*Xcas_help_output,Xcas_help_output->Fl_Output::value(),res.size());
582   }
583 
editeur_export(Fl_Text_Editor * e,const std::string & newfile,int mode)584   void editeur_export(Fl_Text_Editor * e,const std::string & newfile,int mode){
585     context * contextptr = get_context(e);
586     gen g;
587     try {
588       char * ch=e->buffer()->text();
589       string res(ch);
590       free(ch);
591       g=gen(res,contextptr);
592     }
593     catch (std::runtime_error & err){
594       cerr << err.what() << '\n';
595       return;
596     }
597     if (is_file_available(newfile.c_str())){
598       int i=fl_ask("%s",gettext("File exists. Overwrite?"));
599       if (!i)
600 	return;
601     }
602     int save_maple_mode=xcas_mode(contextptr);
603     xcas_mode(contextptr)=(mode & 0xff);
604     int save_python=python_compat(contextptr);
605     python_compat((mode>=256?1:0),contextptr);
606     ofstream of(newfile.c_str());
607     if (g.type==_VECT && g.subtype==_SEQ__VECT && xcas_mode(contextptr) !=3 ){
608       const_iterateur it=g._VECTptr->begin(),itend=g._VECTptr->end();
609       for (;it!=itend;++it){
610 	of << it->print_universal(contextptr) ;
611 	of << ";\n";
612       }
613     }
614     else
615       of << g.print_universal(contextptr) << ((xcas_mode(contextptr)==3)?"\n":";\n");
616     python_compat(save_python,contextptr);
617     xcas_mode(contextptr)=save_maple_mode;
618   }
619 
cb_editeur_insert(Fl_Menu_ * m,const string & extension,int mode)620   void cb_editeur_insert(Fl_Menu_ * m,const string & extension,int mode){
621     Fl_Text_Editor * e = find_editor(m);
622     if (e){
623       char * newfile = load_file_chooser("Insert program",("*"+extension).c_str(), ("session"+extension).c_str(),0,false);
624       if ( file_not_available(newfile) )
625 	return;
626       editeur_insert(e,newfile,mode);
627     }
628   }
629 
cb_Editeur_Insert_File(Fl_Menu_ * m,void *)630   static void cb_Editeur_Insert_File(Fl_Menu_* m , void*) {
631     cb_editeur_insert(m,"",-1);
632   }
633 
cb_Editeur_Insert_Xcas(Fl_Menu_ * m,void *)634   static void cb_Editeur_Insert_Xcas(Fl_Menu_* m , void*) {
635     cb_editeur_insert(m,".cxx",0);
636   }
637 
cb_Editeur_Insert_Python(Fl_Menu_ * m,void *)638   static void cb_Editeur_Insert_Python(Fl_Menu_* m , void*) {
639     cb_editeur_insert(m,".py",256);
640   }
641 
cb_Editeur_Insert_Maple(Fl_Menu_ * m,void *)642   static void cb_Editeur_Insert_Maple(Fl_Menu_* m , void*) {
643     cb_editeur_insert(m,".map",1);
644   }
645 
cb_Editeur_Insert_Mupad(Fl_Menu_ * m,void *)646   static void cb_Editeur_Insert_Mupad(Fl_Menu_* m , void*) {
647     cb_editeur_insert(m,".mu",2);
648   }
649 
cb_Editeur_Insert_Ti(Fl_Menu_ * m,void *)650   static void cb_Editeur_Insert_Ti(Fl_Menu_* m , void*) {
651     cb_editeur_insert(m,".ti",3);
652   }
653 
cb_Editeur_Export_Maple(Fl_Menu_ * m,void *)654   static void cb_Editeur_Export_Maple(Fl_Menu_* m , void*) {
655     Fl_Text_Editor * e = find_editor(m);
656     if (e){
657       char * newfile = file_chooser("Export program", "*.map", "session.map");
658       editeur_export(e,newfile,1);
659     }
660   }
661 
cb_Editeur_Export_Xcas(Fl_Menu_ * m,void *)662   static void cb_Editeur_Export_Xcas(Fl_Menu_* m , void*) {
663     Fl_Text_Editor * e = find_editor(m);
664     if (e){
665       char * newfile = file_chooser("Export program", "*.cxx", "session.cxx");
666       editeur_export(e,newfile,0);
667     }
668   }
669 
cb_Editeur_Export_Python(Fl_Menu_ * m,void *)670   static void cb_Editeur_Export_Python(Fl_Menu_* m , void*) {
671     Fl_Text_Editor * e = find_editor(m);
672     if (e){
673       char * newfile = file_chooser("Export program", "*.py", "session.py");
674       editeur_export(e,newfile,256);
675     }
676   }
677 
cb_Editeur_Export_Mupad(Fl_Menu_ * m,void *)678   static void cb_Editeur_Export_Mupad(Fl_Menu_* m , void*) {
679     Fl_Text_Editor * e = find_editor(m);
680     if (e){
681       char * newfile = file_chooser("Export program", "*.mu", "session.mu");
682       editeur_export(e,newfile,2);
683     }
684   }
685 
cb_Editeur_Export_Ti(Fl_Menu_ * m,void *)686   static void cb_Editeur_Export_Ti(Fl_Menu_* m , void*) {
687     Fl_Text_Editor * e = find_editor(m);
688     if (e){
689       char * newfile = file_chooser("Export program", "*.ti", "session.ti");
690       editeur_export(e,newfile,3);
691     }
692   }
693 
cb_Editeur_Translate_Maple(Fl_Menu_ * m,void *)694   static void cb_Editeur_Translate_Maple(Fl_Menu_* m , void*) {
695     Fl_Text_Editor * e = find_editor(m);
696     if (e){
697       editeur_translate(e,1);
698     }
699   }
700 
cb_Editeur_Translate_Python(Fl_Menu_ * m,void *)701   static void cb_Editeur_Translate_Python(Fl_Menu_* m , void*) {
702     Fl_Text_Editor * e = find_editor(m);
703     if (e){
704       editeur_translate(e,256); // not working
705     }
706   }
707 
cb_Editeur_Translate_Xcas(Fl_Menu_ * m,void *)708   static void cb_Editeur_Translate_Xcas(Fl_Menu_* m , void*) {
709     Fl_Text_Editor * e = find_editor(m);
710     if (e){
711       editeur_translate(e,0);
712     }
713   }
714 
cb_Editeur_Translate_Mupad(Fl_Menu_ * m,void *)715   static void cb_Editeur_Translate_Mupad(Fl_Menu_* m , void*) {
716     Fl_Text_Editor * e = find_editor(m);
717     if (e){
718       editeur_translate(e,2);
719     }
720   }
721 
cb_Editeur_Translate_Ti(Fl_Menu_ * m,void *)722   static void cb_Editeur_Translate_Ti(Fl_Menu_* m , void*) {
723     Fl_Text_Editor * e = find_editor(m);
724     if (e){
725       editeur_translate(e,3);
726     }
727   }
728 
cb_Editeur_Save_as(Fl_Widget * m,void *)729   static void cb_Editeur_Save_as(Fl_Widget * m , void*) {
730     static int program_counter=0;
731     Fl_Text_Editor * e = find_editor(m);
732     if (e){
733       static string tmp;
734       string extension;
735       if (Editeur * ed = dynamic_cast<Editeur *>(m->parent()))
736 	extension=ed->extension;
737       for (;;){
738 	char * newfile ;
739 	if (extension.empty())
740 	  newfile = file_chooser("Store program", "*", ("session"+print_INT_(program_counter)).c_str());
741 	else
742 	  newfile = file_chooser("Store program", ("*."+extension).c_str(), ("session"+print_INT_(program_counter)+"."+extension).c_str());
743 	if ( (!newfile) || (!*newfile))
744 	  return;
745 	tmp=newfile;
746 	if (!extension.empty())
747 	  tmp=remove_extension(tmp.substr(0,1000).c_str())+"."+extension;
748 	if (access(tmp.c_str(),R_OK))
749 	  break;
750 	int i=fl_ask("%s",(tmp+gettext(": file exists. Overwrite?")).c_str());
751 	if (i==1)
752 	  break;
753       }
754       e->label(tmp.c_str());
755       if (Editeur * ed=dynamic_cast<Editeur *>(m->parent())){
756 	ed->output->value(remove_path(e->label()).c_str());
757 	ed->output->redraw();
758       }
759       cb_Editeur_Save(m,0);
760     }
761   }
762 
cb_Editeur_Save(Fl_Widget * m,void *)763   static void cb_Editeur_Save(Fl_Widget * m , void*) {
764     Fl_Text_Editor * e = find_editor(m);
765     if (e // && e->changed()
766 	){
767       if (e->label() && e->label()[0]){
768 	e->buffer()->savefile(e->label());
769 	e->clear_changed();
770       }
771       else
772 	cb_Editeur_Save_as(m,0);
773     }
774   }
775 
776   Fl_Widget * Save_Focus_Button::widget=0;
handle(int event)777   int Save_Focus_Button::handle(int event){
778     if (event==FL_FOCUS)
779       return 0;
780     if (event==FL_MOVE){ // search if focus is one of the parent of the button
781       Fl_Widget * w1 =Fl::focus();
782       Fl_Widget * w2 = this;
783       while (w2){
784 	if (w1==w2)
785 	  break;
786 	w2=w2->parent();
787       }
788       if (w1 && !w2 && widget!=w1){
789 	// it's not a parent, save it
790 	widget=w1;
791       }
792     }
793     return Fl_Button::handle(event);
794   }
795 
handle(int event)796   int No_Focus_Button::handle(int event){
797     switch (event) {
798     case FL_ENTER:
799     case FL_LEAVE:
800       return 1;
801     case FL_RELEASE:
802       do_callback();
803     case FL_PUSH: case FL_DRAG:
804       return 1;
805     case FL_SHORTCUT:
806       if (!(shortcut() ?
807 	    Fl::test_shortcut(shortcut()) : test_shortcut())) return 0;
808       if (type() == FL_RADIO_BUTTON && !value()) {
809 	setonly();
810 	if (when() & FL_WHEN_CHANGED) do_callback();
811       } else if (type() == FL_TOGGLE_BUTTON) {
812 	value(!value());
813 	if (when() & FL_WHEN_CHANGED) do_callback();
814       }
815       if (when() & FL_WHEN_RELEASE) do_callback(); else set_changed();
816       return 1;
817     default:
818       return 0;
819     }
820   }
821 
in_Xcas_input_1arg(Fl_Widget * widget,const std::string & chaine,bool eval)822   void in_Xcas_input_1arg(Fl_Widget * widget,const std::string & chaine ,bool eval){
823     if (!widget)
824       return;
825     Fl::focus(widget);
826     const context * contextptr = get_context(widget);
827     if ( Equation * eqwptr=dynamic_cast<Equation *> (widget) ){
828       vecteur position;
829       gen * act=Equation_selected(eqwptr->data,eqwptr->attr,eqwptr->w(),position,1,contextptr);
830       if (act){
831 	eqwptr->handle_text(chaine,act);
832 	return;
833       }
834       gen f("'"+string(chaine)+"'",contextptr);
835       if (eval || f.type!=_FUNC)
836 	eqwptr->eval_function(f);
837       else {
838 	// make_new_help(chaine);
839 	gen g=eqwptr->get_selection();
840 	if (g.type!=_VECT){
841 	  if (f==at_limit)
842 	    g=gen(makevecteur(g,vx_var,0),_SEQ__VECT);
843 	  if (f==at_integrate || f==at_derive)
844 	    g=gen(makevecteur(g,vx_var),_SEQ__VECT);
845 	  if (f==at_sum)
846 	    g=gen(makevecteur(g,k__IDNT_e,1,n__IDNT_e),_SEQ__VECT);
847 	}
848 	eqwptr->replace_selection(symbolic(*f._FUNCptr,g));
849       }
850       return;
851     }
852     bool par= chaine.empty() || chaine[0]!='_';
853     string s(chaine);
854     if (Multiline_Input_tab * the_input =dynamic_cast<Multiline_Input_tab *>(widget)){
855       if (par)
856 	s = chaine+"(";
857       the_input->insert_replace(s,false);
858       return;
859     }
860     if (Fl_Input * the_input =dynamic_cast<Fl_Input *>(widget)){
861       string t(the_input->value());
862       size_t sel1=the_input->position(),sel2=the_input->mark();
863       if (sel1>sel2){
864 	size_t tmp=sel1;
865 	sel1=sel2;
866 	sel2=tmp;
867       }
868       string t_before=t.substr(0,sel1);
869       string t_selected=t.substr(sel1,sel2-sel1);
870       string t_after=t.substr(sel2,t.size()-sel2);
871       if (par)
872 	s += '(';
873       s = t_before+s;
874       s += t_selected;
875       s += t_after;
876       the_input->value( s.c_str());
877       size_t pos1=t_before.size();
878       size_t pos2=s.size()-t_after.size();
879       if (sel1==sel2)
880 	the_input->position(pos2-1,pos2-1);
881       else
882 	the_input->position(pos1,pos2);
883       return;
884     }
885     if (Editeur * ed=dynamic_cast<Editeur *>(widget)){
886       widget=ed->editor;
887     }
888     if (Xcas_Text_Editor * ed=dynamic_cast<Xcas_Text_Editor *>(widget)){
889       int i=ed->insert_position();
890       ed->buffer()->insert(i,(chaine+'(').c_str());
891       ed->insert_position(i+chaine.size()+1);
892       ed->set_tooltip();
893       if (History_Pack * hp=get_history_pack(ed))
894 	hp->modified(false);
895     }
896   }
897 
Xcas_input_1arg(Save_Focus_Button * m,void *)898   void Xcas_input_1arg(Save_Focus_Button * m , void*) {
899     in_Xcas_input_1arg(m->widget,m->label());
900   }
901 
Xcas_input_arg(Save_Focus_Button * m,const std::string & chaine)902   void Xcas_input_arg(Save_Focus_Button * m , const std::string & chaine) {
903     in_Xcas_input_1arg(m->widget,chaine);
904   }
905 
Xcas_input_1arg(No_Focus_Button * m,void *)906   void Xcas_input_1arg(No_Focus_Button * m , void*) {
907     in_Xcas_input_1arg(Fl::focus(),m->label());
908   }
909 
Xcas_input_arg(No_Focus_Button * m,const std::string & chaine)910   void Xcas_input_arg(No_Focus_Button * m , const std::string & chaine) {
911     in_Xcas_input_1arg(Fl::focus(),chaine);
912   }
913 
in_Xcas_input_char(Fl_Widget * widget,const std::string & chaine,char keysym)914   void in_Xcas_input_char(Fl_Widget * widget,const std::string & chaine,char keysym){
915     if (alt_ctrl_cb && alt_ctrl)
916       alt_ctrl_cb(alt_ctrl);
917     if (!widget)
918       return;
919     static string s;
920     if (chaine.empty())
921       s=" ";
922     else
923       s=chaine;
924     if (alt_ctrl & 2)
925       s[0]=s[0] & 0x1f;
926     if (alt_ctrl & 4)
927       s[0]=s[0] | 0x80;
928     Fl::e_text= (char *) s.c_str();
929     Fl::e_length=s.size();
930     if (alt_ctrl & 2)
931       Fl::e_keysym=keysym & 0x1f;
932     else {
933       if (alt_ctrl & 4)
934 	Fl::e_keysym=keysym | 0x80;
935       Fl::e_keysym=keysym;
936     }
937     alt_ctrl=0;
938     fl_handle(widget);
939   }
940 
Xcas_input_char(Save_Focus_Button * m,void *)941   void Xcas_input_char(Save_Focus_Button * m , void*) {
942     in_Xcas_input_char(m->widget,m->label(),m->label()[0]);
943   }
944 
Xcas_input_label(No_Focus_Button * m,void *)945   void Xcas_input_label(No_Focus_Button * m , void*) {
946     in_Xcas_input_char(Fl::focus(),m->label(),m->label()[0]);
947   }
948 
Xcas_input_char(No_Focus_Button * m,void *)949   void Xcas_input_char(No_Focus_Button * m , void*) {
950     std::string s(m->label());
951     if (m->labelfont()==FL_SYMBOL && s.size()==1){
952       switch (s[0]){
953       case 'a':
954 	s="alpha";
955 	break;
956       case 'b':
957 	s="beta";
958 	break;
959       case 'c':
960 	s="chi";
961 	break;
962       case 'd':
963 	s="delta";
964 	break;
965       case 'e':
966 	s="epsilon";
967 	break;
968       case 'f':
969 	s="phi";
970 	break;
971       case 'g':
972 	s="gamma";
973 	break;
974       case 'h':
975 	s="eta";
976 	break;
977       case 'i':
978 	s="iota";
979 	break;
980       case 'j':
981 	s="varphi";
982 	break;
983       case 'k':
984 	s="kappa";
985 	break;
986       case 'l':
987 	s="lambda";
988 	break;
989       case 'm':
990 	s="mu";
991 	break;
992       case 'n':
993 	s="nu";
994 	break;
995       case 'p':
996 	s="pi";
997 	break;
998       case 'q':
999 	s="theta";
1000 	break;
1001       case 'r':
1002 	s="rho";
1003 	break;
1004       case 's':
1005 	s="sigma";
1006 	break;
1007       case 't':
1008 	s="tau";
1009 	break;
1010       case 'u':
1011 	s="upsilon";
1012 	break;
1013       case 'v':
1014 	s="omega";
1015 	break;
1016       case 'x':
1017 	s="xi";
1018 	break;
1019       case 'y':
1020 	s="psi";
1021 	break;
1022       case 'z':
1023 	s="zeta";
1024 	break;
1025       case 'C':
1026 	s="Chi";
1027 	break;
1028       case 'D':
1029 	s="Delta";
1030 	break;
1031       case 'F':
1032 	s="Phi";
1033 	break;
1034       case 'G':
1035 	s="Gamma";
1036 	break;
1037       case 'L':
1038 	s="Lambda";
1039 	break;
1040       case 'P':
1041 	s="Pi";
1042 	break;
1043       case 'Q':
1044 	s="Theta";
1045 	break;
1046       case 'S':
1047 	s="Sigma";
1048 	break;
1049       case 'W':
1050 	s="Omega";
1051 	break;
1052       case 'X':
1053 	s="Xi";
1054 	break;
1055       case 'Y':
1056 	s="Psi";
1057 	break;
1058       }
1059     }
1060     in_Xcas_input_char(Fl::focus(),s,m->label()[0]);
1061   }
1062 
Xcas_binary_op(Save_Focus_Button * m,void *)1063   void Xcas_binary_op(Save_Focus_Button * m , void*) {
1064     Xcas_input_char(m,0);
1065   }
1066 
Xcas_binary_op(No_Focus_Button * m,void *)1067   void Xcas_binary_op(No_Focus_Button * m , void*) {
1068     Xcas_input_char(m,0);
1069   }
1070 
Xcas_input_0arg(Save_Focus_Button * m,const std::string & chaine)1071   void Xcas_input_0arg(Save_Focus_Button * m , const std::string & chaine) {
1072     Fl_Widget * widget = m->widget;
1073     if (!widget)
1074       return;
1075     string s;
1076     if (chaine.empty())
1077       s=" ";
1078     else
1079       s=chaine;
1080     Fl::e_text= (char *) s.c_str();
1081     Fl::e_length=s.size();
1082     Fl::e_keysym=s[0];
1083     fl_handle(widget);
1084   }
1085 
Xcas_input_0arg(No_Focus_Button *,const std::string & chaine)1086   void Xcas_input_0arg(No_Focus_Button * , const std::string & chaine) {
1087     static string s;
1088     if (chaine.empty())
1089       s=" ";
1090     else
1091       s=chaine;
1092     Fl_Widget * wid = Fl::focus();
1093     const context * contextptr = get_context(wid);
1094     if (Equation * eq=dynamic_cast<Equation * >(wid)){
1095       vecteur position;
1096       gen * act=Equation_selected(eq->data,eq->attr,eq->w(),position,1,contextptr);
1097       if (act){
1098 	eq->handle_text(s,act);
1099 	return;
1100       }
1101       if (s.size()>1){
1102 	eq->parse_desactivate();
1103 	gen f(s,contextptr);
1104 	eq->replace_selection(f);
1105 	return;
1106       }
1107     }
1108     else {
1109       Fl::e_text= (char *) s.c_str();
1110       Fl::e_length=s.size();
1111       Fl::e_keysym=s[0];
1112       fl_handle(wid);
1113     }
1114   }
1115 
Xcas_input_0arg(const std::string & chaine)1116   void Xcas_input_0arg(const std::string & chaine) {
1117     Xcas_input_0arg((No_Focus_Button *)0,chaine);
1118   }
1119 
cb_Editeur_Exec(Save_Focus_Button * m,void *)1120   static void cb_Editeur_Exec(Save_Focus_Button * m , void*) {
1121     Fl_Widget * widget = m->widget;
1122     Fl_Text_Editor * Program_editor = find_editor(m);
1123     if (Program_editor){
1124       const context * contextptr = get_context(Program_editor);
1125       if (!Program_editor || !widget)
1126 	return ;
1127       if ( !dynamic_cast<Fl_Input *>(widget)){
1128 	m->window()->show();
1129 	Fl::focus(Program_editor);
1130 	return ;
1131       }
1132       Editeur * ed = dynamic_cast<Editeur *>(Program_editor->parent());
1133       int r= Program_editor->insert_position();
1134       char * ch=Program_editor->buffer()->line_text(r);
1135       string s=ch;
1136       free(ch);
1137       // Scan for a line ending with //
1138       int ss=s.size();
1139       for (int i=ss-2;i>=0;--i){
1140 	if (s[i]=='/' && s[i+1]=='/')
1141 	  s=s.substr(0,i);
1142       }
1143       ss=s.size();
1144       bool comment=s.empty(),nextline=true;
1145       for (int i=0;i<ss;++i){
1146 	if (s[i]!=' ')
1147 	  break;
1148 	if (i==ss-1)
1149 	  comment=true;
1150       }
1151       if (!comment){
1152 	gen g;
1153 	bool close=lexer_close_parenthesis(contextptr);
1154 	lexer_close_parenthesis(false,contextptr);
1155 	try {
1156 	  if (calc_mode(contextptr)==38)
1157 	    calc_mode(contextptr)=-38;
1158 	  g=gen(s,contextptr);
1159 	}
1160 	catch (std::runtime_error & e){
1161 	  cerr << e.what() << '\n';
1162 	}
1163 	lexer_close_parenthesis(close,contextptr);
1164 	if (giac::first_error_line(contextptr)){
1165 	  if (ed && ed->log){
1166 	    ed->log->value((gettext("Parse error line ")+print_INT_(giac::first_error_line(contextptr))+ gettext(" column ")+print_INT_(giac::lexer_column_number(contextptr))+gettext(" at ") +giac::error_token_name(contextptr)).c_str());
1167 	    ed->log->redraw();
1168 	    Fl_Group * gr=ed->log->parent();
1169 	    if (gr->children()>2){
1170 	      int ah = gr->child(2)->h();
1171 	      Fl_Widget * wid=gr->child(2);
1172 	      gr->remove(wid);
1173 	      delete wid;
1174 	      gr->Fl_Widget::resize(gr->x(),gr->y(),gr->w(),gr->h()-ah);
1175 	      History_Pack * hp=get_history_pack(gr);
1176 	      if (hp)
1177 		hp->redraw();
1178 	    }
1179 	  }
1180 	  nextline=false;
1181 	}
1182       }
1183       if (nextline) {
1184 	int next=Program_editor->buffer()->line_end(r)+1;
1185 	int nextend=Program_editor->buffer()->line_end(next);
1186 	Program_editor->insert_position(next);
1187 	Program_editor->show_insert_position();
1188 	Program_editor->buffer()->select(next,nextend);
1189 	Program_editor->redraw();
1190 	if (!comment){
1191 	  Fl::e_text= (char *) s.c_str();
1192 	  Fl::e_length=s.size();
1193 	  widget->window()->show();
1194 	  fl_handle(widget);
1195 	  widget->do_callback();
1196 	  widget=Fl::focus();
1197 	}
1198 	m->window()->show();
1199 	Fl::focus(Program_editor);
1200 	Fl::flush();
1201 	usleep(500000);
1202 	widget->window()->show();
1203 	Fl::focus(widget);
1204       }
1205     }
1206   }
1207 
cb_Editeur_Preview(Fl_Menu_ * m,void *)1208   static void cb_Editeur_Preview(Fl_Menu_* m , void*) {
1209     Fl_Text_Editor * e = find_editor(m);
1210     if (e)
1211       widget_ps_print(e,e->label(),false);
1212   }
1213 
cb_Editeur_Print(Fl_Menu_ * m,void *)1214   static void cb_Editeur_Print(Fl_Menu_* m , void*) {
1215     Fl_Text_Editor * e = find_editor(m);
1216     if (e)
1217       widget_print(e);
1218   }
1219 
eval()1220   bool Editeur::eval(){
1221     log = find_log_output(parent());
1222     const context * contextptr = get_context(this);
1223     static string logs;
1224     if (log){
1225       logs=gettext("Syntax compatibility mode: ")+print_program_syntax(xcas_mode(contextptr))+"\n";
1226     }
1227     gen g;
1228     bool close=lexer_close_parenthesis(contextptr);
1229     lexer_close_parenthesis(false,contextptr);
1230     try {
1231       char * ch=editor->buffer()->text();
1232       string res(ch);
1233       free(ch);
1234       if (calc_mode(contextptr)==38)
1235 	calc_mode(contextptr)=-38;
1236       g=gen(res,contextptr);
1237     }
1238     catch (std::runtime_error & er){
1239       cerr << er.what() << '\n';
1240     }
1241     lexer_close_parenthesis(close,contextptr);
1242     if (giac::first_error_line(contextptr)){
1243       int pos1=editor->buffer()->skip_lines(0,giac::first_error_line(contextptr)-1);
1244       // int pos2=editor->buffer()->skip_lines(pos1,1);
1245       int pos2=pos1+giac::lexer_column_number(contextptr)-1;
1246       pos1=giacmax(pos2-giac::error_token_name(contextptr).size(),0);
1247       editor->buffer()->select(pos1,pos2);
1248       editor->show_insert_position();
1249       editor->redraw();
1250       if (log){
1251 	logs+=gettext("Parse error line ")+print_INT_(giac::first_error_line(contextptr))+ gettext( " column ")+print_INT_(giac::lexer_column_number(contextptr))+gettext(" at ") +giac::error_token_name(contextptr);
1252 	log->value(logs.c_str());
1253 	Fl_Group * gr=log->parent();
1254 	if (gr->children()>2){
1255 	  int ah = gr->child(2)->h();
1256 	  Fl_Widget * wid=gr->child(2);
1257 	  gr->remove(wid);
1258 	  delete wid;
1259 	  gr->Fl_Widget::resize(gr->x(),gr->y(),gr->w(),gr->h()-ah);
1260 	}
1261 	// if (Parse_error_output)
1262 	//  Parse_error_output->value(logs.c_str());
1263 	output_resize_parent(log);
1264       }
1265       return false;
1266     }
1267     else {
1268       if (log){
1269 	string locals=check_local_assign(g,contextptr);
1270 	if (locals.empty())
1271 	  logs = gettext("Success!");
1272 	else {
1273 	  logs+=string(gettext("Success but ... "))+locals;
1274 	}
1275 	log->value(logs.c_str());
1276 	// if (Parse_error_output)
1277 	//  Parse_error_output->value(logs.c_str());
1278 	output_resize_parent(log);
1279       }
1280       // NOTE: do_callback clears the logs
1281       // protecteval(g,2,contextptr);
1282       do_callback();
1283       return true;
1284       /* Exec g
1285       context * contextptr = 0;
1286       if (History_Pack * p =get_history_pack(this)){
1287       contextptr = p->contextptr;
1288 	// focus to next
1289 	p->_sel_begin=-1;
1290 	int pos=p->set_sel_begin(this)+1;
1291 	p->focus(pos);
1292       }
1293       if (giac::is_context_busy(contextptr) && log)
1294 	log->value((log->value()+string("\nCan not evaluate, system busy")).c_str());
1295       else
1296 	thread_eval(g,eval_level(contextptr),contextptr);
1297       */
1298     }
1299   }
1300 
cb_Editeur_Test(Fl_Widget * m,void *)1301   static void cb_Editeur_Test(Fl_Widget* m , void*) {
1302     Fl_Text_Editor * e = find_editor(m);
1303     if (e){
1304       Fl::focus(e);
1305       Editeur * ed = dynamic_cast<Editeur *>(m->parent());
1306       if (ed){
1307 	ed->eval();
1308       }
1309     }
1310   }
1311 
cb_Editeur_Gotoline(Fl_Widget * m,void *)1312   static void cb_Editeur_Gotoline(Fl_Widget* m , void*) {
1313     Fl_Text_Editor * e = find_editor(m);
1314     if (e){
1315       Fl::focus(e);
1316       Fl_Value_Input * in = dynamic_cast<Fl_Value_Input *>(m);
1317       if (in){
1318 	double l=in->value();
1319 	if (l<1)
1320 	  l=1;
1321 	int newpos=e->buffer()->skip_lines(0,int(l)-1);
1322 	e->insert_position(newpos);
1323       }
1324     }
1325   }
1326 
cb_Editeur_Indent_line(Fl_Widget * m,void *)1327   static void cb_Editeur_Indent_line(Fl_Widget* m , void*) {
1328     Fl_Text_Editor * e = find_editor(m);
1329     if (e){
1330       Fl::focus(e);
1331       Editeur * ed = dynamic_cast<Editeur *>(m->parent());
1332       if (ed){
1333 	ed->editor->indent(e->insert_position());
1334       }
1335     }
1336   }
1337 
cb_Editeur_Indent_all(Fl_Widget * m,void *)1338   static void cb_Editeur_Indent_all(Fl_Widget* m , void*) {
1339     Fl_Text_Editor * e = find_editor(m);
1340     if (e){
1341       Fl::focus(e);
1342       Editeur * ed = dynamic_cast<Editeur *>(m->parent());
1343       if (ed){
1344 	ed->editor->indent();
1345       }
1346     }
1347   }
1348 
cb_Editeur_Next(Fl_Widget * m,void *)1349   static void cb_Editeur_Next(Fl_Widget * m , void*) {
1350     Editeur * e = dynamic_cast<Editeur *>(m->parent());
1351     if (e && e->editor){
1352       Fl_Text_Editor * ed=e->editor;
1353       int pos = ed->insert_position();
1354       int found = ed->buffer()->search_forward(pos, e->search.c_str(), &pos);
1355       if (found) {
1356 	// Found a match; select and update the position...
1357 	ed->buffer()->select(pos, pos+e->search.size());
1358 	ed->insert_position(pos+e->search.size());
1359 	ed->show_insert_position();
1360 	ed->redraw();
1361       }
1362       else {
1363 	fl_alert("%s","No more occurrences of '%s' found!", e->search.c_str());
1364 	ed->insert_position(0);
1365       }
1366       Fl::focus(ed);
1367     }
1368   }
1369 
cb_Editeur_Extension(Fl_Menu_ * m,void *)1370   static void cb_Editeur_Extension(Fl_Menu_* m , void*) {
1371     Editeur * e=dynamic_cast<Editeur *>(m->parent());
1372     if (e){
1373       const char * res=fl_input("New extension",e->extension.c_str());
1374       if (res)
1375 	e->extension=res;
1376     }
1377   }
1378 
cb_Editeur_Extend(Fl_Widget * m,void *)1379   void cb_Editeur_Extend(Fl_Widget * m , void*) {
1380     Editeur * e=dynamic_cast<Editeur *>(m->parent());
1381     if (e->h()<=e->window()->h()-100){
1382       increase_size(e->editor,100);
1383       e->editor->redraw();
1384     }
1385   }
1386 
cb_Editeur_Shrink(Fl_Widget * m,void *)1387   void cb_Editeur_Shrink(Fl_Widget * m , void*) {
1388     Editeur * e=dynamic_cast<Editeur *>(m->parent());
1389     if (e->h()>200){
1390       increase_size(e->editor,-100);
1391       e->editor->redraw();
1392     }
1393   }
1394 
logo_eval_callback(const giac::gen & evaled_g,void * param)1395   void logo_eval_callback(const giac::gen & evaled_g,void * param){
1396     Fl_Widget * wid=static_cast<Fl_Widget *>(param);
1397     if (!wid || !wid->parent())
1398       return;
1399     wid->parent()->redraw();
1400   }
1401 
cb_Editeur_Exec_All(Fl_Widget * m,void *)1402   void cb_Editeur_Exec_All(Fl_Widget * m , void*) {
1403     Editeur * e=dynamic_cast<Editeur *>(m->parent());
1404     context * contextptr = get_context(e);
1405     if (e){
1406       if (Logo * l=dynamic_cast<Logo *>(e->parent())){
1407 	char * ch = e->editor->buffer()->text();
1408 	string s=ch;
1409 	free(ch);
1410 	gen g;
1411 	bool close=lexer_close_parenthesis(contextptr);
1412 	lexer_close_parenthesis(false,contextptr);
1413 	try {
1414 	  if (calc_mode(contextptr)==38)
1415 	    calc_mode(contextptr)=-38;
1416 	  g=gen(s,contextptr);
1417 	}
1418 	catch (std::runtime_error & e){ cerr << e.what() << '\n'; }
1419 	lexer_close_parenthesis(close,contextptr);
1420 	if (giac::first_error_line(contextptr)){
1421 	  int pos1=e->editor->buffer()->skip_lines(0,giac::first_error_line(contextptr)-1);
1422 	  // int pos2=e->editor->buffer()->skip_lines(pos1,1);
1423 	  int pos2=pos1+giac::lexer_column_number(contextptr)-1;
1424 	  pos1=giacmax(pos2-giac::error_token_name(contextptr).size(),0);
1425 	  e->editor->buffer()->select(pos1,pos2);
1426 	  e->editor->show_insert_position();
1427 	  e->editor->redraw();
1428 	  fl_alert("%s",(gettext("Parse error line ")+print_INT_(giac::first_error_line(contextptr))+ gettext(" column ")+print_INT_(giac::lexer_column_number(contextptr)) + gettext(" at ") +giac::error_token_name(contextptr)).c_str());
1429 	}
1430 	else {
1431 	  if (!is_context_busy(contextptr)){
1432 #if 1
1433 	    make_thread(g,eval_level(contextptr),logo_eval_callback,e,contextptr);
1434 	    // protecteval(g,eval_level(contextptr),contextptr);
1435 #else // crashes sometimes, perhaps something non reentrant
1436 	      thread_eval(g,eval_level(contextptr),contextptr);
1437 #endif
1438 	  }
1439 	  l->t->redraw();
1440 	  e->editor->insert_position(s.size());
1441 	}
1442 	return;
1443       }
1444       // Not a logo, try to run it inside focus
1445       fl_message("%s",gettext("Should be used inside a Logo level"));
1446       return;
1447       Fl_Widget * wid=Fl::focus();
1448       History_Pack * hp=get_history_pack(wid);
1449       if (hp){
1450 	hp->set_sel_begin(wid);
1451 	int n=hp->_sel_begin;
1452 	int r=0;
1453 	int taille=e->editor->buffer()->length();
1454 	for (;r<taille;++n){
1455 	  char * ch=e->editor->buffer()->line_text(r);
1456 	  r=e->editor->buffer()->line_end(r)+1;
1457 	  Fl_Multiline_Input * widget=dynamic_cast<Fl_Multiline_Input *>(new_question_multiline_input(hp->w()-hp->_printlevel_w,hp->labelsize()+4));
1458 	  if (!widget) break;
1459 	  widget->value(ch);
1460 	  widget->set_changed();
1461 	  free(ch);
1462 	  hp->add_entry(n,widget);
1463 	  widget->do_callback();
1464 	}
1465       }
1466     }
1467   }
1468 
cb_Editeur_Search(Fl_Widget * m,void *)1469   static void cb_Editeur_Search(Fl_Widget* m , void*) {
1470     static Fl_Window * w = 0;
1471     static Fl_Input * i1=0, * i2=0; // i1=search, i2=replace
1472     static Fl_Button * button0 = 0 ; // cancel
1473     static Fl_Button * button1 =0; // next
1474     static Fl_Button * button2 =0; // replace + next
1475     static Fl_Button * button3 =0; // replace all
1476     Fl_Text_Editor * ed = find_editor(m);
1477     if (!ed) return;
1478     Editeur * e =dynamic_cast<Editeur *>(m->parent());
1479     if (ed && e){
1480       int dx=400,dy=100;
1481       if (!w){
1482 	Fl_Group::current(0);
1483 	w=new Fl_Window(dx,dy);
1484 	i1=new Fl_Input(dx/6,2,dx/3-2,dy/3-4,gettext("Search"));
1485 	i1->tooltip(gettext("Word to search"));
1486 	i1->when(FL_WHEN_ENTER_KEY|FL_WHEN_NOT_CHANGED);
1487 	i2=new Fl_Input((2*dx)/3,2,dx/3-2,dy/3-4,"Replace");
1488 	i2->tooltip(gettext("Replace by"));
1489 	i2->when(FL_WHEN_ENTER_KEY|FL_WHEN_NOT_CHANGED);
1490 	button0 = new Fl_Button(2,2+(2*dy/3),dx/2-4,dy/3-4);
1491 	button0->shortcut(0xff0d);
1492 	button0->label(gettext("Cancel"));
1493 	button1 = new Fl_Button(dx/2+2,2+(2*dy)/3,dx/2-4,dy/3-4);
1494 	button1->shortcut(0xff1b);
1495 	button1->label(gettext("Next"));
1496 	button1->when(FL_WHEN_RELEASE);
1497 	button2 = new Fl_Button(2,2+(dy)/3,dx/2-4,dy/3-4);
1498 	button2->shortcut(0xff1b);
1499 	button2->label(gettext("Replace"));
1500 	button2->when(FL_WHEN_RELEASE);
1501 	button3 = new Fl_Button(dx/2+2,2+(dy)/3,dx/2-4,dy/3-4);
1502 	button3->shortcut(0xff1b);
1503 	button3->label(gettext("Replace all"));
1504 	button3->when(FL_WHEN_RELEASE);
1505 	w->end();
1506 	w->resizable(w);
1507       }
1508       i1->value(e->search.c_str());
1509       History_Pack * hp=get_history_pack(ed);
1510       w->label("Search/Replace");
1511       w->set_modal();
1512       w->show();
1513       w->hotspot(w);
1514       Fl::focus(i1);
1515       autosave_disabled=true;
1516       for (;;) {
1517 	Fl_Widget *o = Fl::readqueue();
1518 	if (!o) Fl::wait();
1519 	else {
1520 	  if (o == button0) break;
1521 	  if (o == w) break;
1522 	  if (o==button3){ // Replace all
1523 	    e->search=i1->value();
1524 	    const char *find = e->search.c_str();
1525 	    const char *replace = i2->value();
1526 	    int i=1;
1527 	    if (!replace[0])
1528 	      i=fl_ask("%s","Really replace by nothing?");
1529 	    if (i && find[0] != 0){
1530 	      ed->previous_word();
1531 	      int pos = ed->insert_position();
1532 	      while (ed->buffer()->search_forward(pos, find, &pos)) {
1533 		ed->buffer()->select(pos, pos+strlen(find));
1534 		ed->buffer()->remove_selection();
1535 		ed->buffer()->insert(pos, replace);
1536 		ed->redraw();
1537 		pos += strlen(replace);
1538 		ed->insert_position(pos);
1539 	      }
1540 	      if (hp)
1541 		hp->modified(false);
1542 	      break;
1543 	    }
1544 	  } // end button3
1545 	  if (o==button2 || o==i2){ // Replace+next
1546 	    if (ed->buffer()->selection_text()==e->search){
1547 	      e->search=i1->value();
1548 	      const char *find = e->search.c_str();
1549 	      const char *replace = i2->value();
1550 	      if (find[0] != 0){
1551 		if (ed->insert_position()>0)
1552 		  ed->previous_word();
1553 		int pos = ed->insert_position();
1554 		if (ed->buffer()->search_forward(pos, find, &pos)) {
1555 		  ed->buffer()->select(pos, pos+strlen(find));
1556 		  ed->buffer()->remove_selection();
1557 		  ed->buffer()->insert(pos, replace);
1558 		  if (hp)
1559 		    hp->modified(false);
1560 		  ed->redraw();
1561 		  pos += strlen(replace);
1562 		  ed->insert_position(pos);
1563 		}
1564 	      }
1565 	    } // end I1!=I2
1566 	  } // end button2
1567 	  if (o==i1 || o==i2 || o==button1 || o==button2){ // Find next
1568 	    e->search=i1->value();
1569 	    if (e->search.empty())
1570 	      break;
1571 	    int pos = ed->insert_position();
1572 	    int found = ed->buffer()->search_forward(pos, e->search.c_str(), &pos);
1573 	    if (found) {
1574 	      // Found a match; select and update the position...
1575 	      ed->buffer()->select(pos, pos+e->search.size());
1576 	      ed->insert_position(pos+e->search.size());
1577 	      ed->show_insert_position();
1578 	      ed->redraw();
1579 	    }
1580 	    else {
1581 	      fl_alert("%s","No occurrences of '%s' found!", e->search.c_str());
1582 	      ed->insert_position(0);
1583 	      ed->show_insert_position();
1584 	    }
1585 	    Fl::focus(ed);
1586 	  } // end button1
1587 	  if (o==i1){
1588 	    ed->window()->show();
1589 	    Fl::focus(ed);
1590 	    break;
1591 	  }
1592 	} // end else of if Fl::wait()
1593       } // end for (;;)
1594       autosave_disabled=false;
1595       w->hide();
1596       Fl::focus(ed);
1597     }
1598   }
1599 
1600 
cb_dialog_test(Fl_Text_Editor * ed)1601   static void cb_dialog_test(Fl_Text_Editor * ed){
1602     int dx=240,dy=300,l=14;
1603     if (ed->window()){
1604       dx=int(0.5*ed->window()->w());
1605       dy=int(0.3*ed->window()->h());
1606       l=ed->window()->labelsize();
1607     }
1608     static Fl_Window * w = 0;
1609     static Fl_Return_Button * button0 = 0 ; // ok
1610     static Fl_Button * button1 =0; // cancel, quit
1611     static Fl_Input * cond=0; // condition
1612     static Fl_Input * ifclause=0; // if
1613     static Fl_Input * elseclause=0; // else
1614     if (!w){
1615       Fl_Group::current(0);
1616       w=new Fl_Window(dx,dy);
1617       int dh=dy/5;
1618       int dw=dx/2;
1619       int y_=2;
1620       cond=new Fl_Input(dw/2,y_,3*dw/2-2,dh-2,gettext("if"));
1621       cond->tooltip(gettext("Enter test e.g. x<=0"));
1622       cond->value("x>1");
1623       y_ += dh;
1624       ifclause=new Fl_Input(dw/2,y_,3*dw/2-2,dh-2,gettext("then"));
1625       ifclause->tooltip(gettext("Enter instruction(s) executed when test returns true, e.g. x:=-x;"));
1626       ifclause->value("x:=1-x;");
1627       y_ += dh;
1628       elseclause=new Fl_Input(dw/2,y_,3*dw/2-2,dh-2,gettext("else"));
1629       elseclause->tooltip(gettext("Enter instruction(s) executed when test returns false, leave empty if none."));
1630       elseclause->value("");
1631       y_ += dh;
1632       y_ += dh;
1633       button0 = new Fl_Return_Button(2,y_,dx/5-4,dh-4);
1634       button0->label(gettext("OK"));
1635       button0->shortcut(0xff0d);
1636       button1 = new Fl_Button(dx/5+2,y_,dx/5-4,dh-4);
1637       button1->label(gettext("Cancel"));
1638       button1->shortcut(0xff1b);
1639     }
1640     w->label((gettext("New test")));
1641     change_group_fontsize(w,l);
1642     w->set_modal();
1643     w->show();
1644     w->hotspot(w);
1645     Fl::focus(cond);
1646     autosave_disabled=true;
1647     context * contextptr=get_context(ed);
1648     int r=-2;
1649     for (;;) {
1650       if (r==-2){
1651 	// re-init fields value for function?
1652       }
1653       r=-1;
1654       Fl_Widget *o = Fl::readqueue();
1655       if (!o) Fl::wait();
1656       else {
1657 	if (o == button0) r = 0; // apply and quit
1658 	if (o == button1) r = 1; // cancel changes, quit
1659 	if (r==0){
1660 	  gen g(cond->value(),contextptr);
1661 	  if (g.type!=_SYMB && g.type!=_IDNT)
1662 	    fl_message("%s",gettext("Invalid condition"));
1663 	  g=gen(ifclause->value(),contextptr);
1664 	  if (is_undef(g))
1665 	    fl_message("%s",gettext("Invalid if clause"));
1666 	}
1667 	if (o == w) r=1;
1668 	if (r==0 || r==1)
1669 	  break;
1670       }
1671     }
1672     autosave_disabled=false;
1673     w->hide();
1674     if (r==0){
1675       giac::context * contextptr = get_context(ed);
1676       bool python=python_compat(contextptr);
1677       int i=ed->insert_position();
1678       string s=python?"if ":"si ";
1679       s += cond->value();
1680       s += python?":\n":" alors ";
1681       s += ifclause->value();
1682       gen g(elseclause->value(),contextptr);
1683       bool ifonly=is_undef(g);
1684       if (!ifonly){
1685 	s += python?"\nelse:\n":" sinon ";
1686 	s += elseclause->value();
1687       }
1688       if (python) s+="\n"; else s += " fsi;\n";
1689       ed->buffer()->insert(i,s.c_str());
1690       int delta=s.size();
1691       if (Xcas_Text_Editor * xed=dynamic_cast<Xcas_Text_Editor *>(ed)){
1692 	int j=ed->buffer()->line_end(i)+1;
1693 	delta += xed->indent(j)-j;
1694 	if (python){
1695 	  j=ed->buffer()->line_end(j)+1;
1696 	  delta += xed->indent(j)-j;
1697 	  //j=ed->buffer()->line_end(j)+1;
1698 	  //delta += xed->indent(j)-j;
1699 	  if (!ifonly){
1700 	    j=ed->buffer()->line_end(j)+1;
1701 	    delta += xed->indent(j)-j;
1702 	    j=ed->buffer()->line_end(j)+1;
1703 	    delta += xed->indent(j)-j;
1704 	  }
1705 	  ed->insert_position(i+delta);
1706 	  Fl_Text_Editor::kf_backspace(0,ed);
1707 	  xed->dedent();
1708 	} else ed->insert_position(i+delta);
1709       }
1710       else
1711 	ed->insert_position(i+delta);
1712     }
1713   }
1714 
cb_dialog_loop(Fl_Text_Editor * ed)1715   static void cb_dialog_loop(Fl_Text_Editor * ed){
1716     int dx=240,dy=300,l=14;
1717     if (ed->window()){
1718       dx=int(0.5*ed->window()->w());
1719       dy=int(0.3*ed->window()->h());
1720       l=ed->window()->labelsize();
1721     }
1722     static Fl_Window * w = 0;
1723     static Fl_Return_Button * button0 = 0 ; // ok
1724     static Fl_Button * button1 =0; // cancel, quit
1725     static Fl_Check_Button * pour=0, * tantque=0;
1726     static Fl_Input * count=0; // for variable
1727     static Fl_Input * start=0;
1728     static Fl_Input * stop=0;
1729     static Fl_Input * step=0;
1730     static Fl_Input * loop=0;
1731     static Fl_Input * cond=0;
1732     if (!w){
1733       Fl_Group::current(0);
1734       w=new Fl_Window(dx,dy);
1735       int dh=dy/5;
1736       int dw=dx/2;
1737       int y_=2;
1738       pour=new Fl_Check_Button(dw/2,y_,dw/2-2,dh-2,gettext("for"));
1739       pour->tooltip(gettext("Check this button for a defined loop"));
1740       pour->value(1);
1741       tantque=new Fl_Check_Button(3*dw/2,y_,dw/2-2,dh-2,gettext("while"));
1742       tantque->tooltip(gettext("Check this button for a undefined loop"));
1743       tantque->value(0);
1744       y_ += dh;
1745       count=new Fl_Input(dw/4,y_,dw/4-2,dh-2,gettext("Var"));
1746       count->tooltip(gettext("Enter counter name, e.g. k"));
1747       count->value("k");
1748       start=new Fl_Input(3*dw/4,y_,dw/4-2,dh-2,gettext("Start"));
1749       start->tooltip(gettext("First value of counter in the loop"));
1750       start->value("1");
1751       stop=new Fl_Input(5*dw/4,y_,dw/4-2,dh-2,gettext("Stop"));
1752       stop->tooltip(gettext("Last value of counter in the loop"));
1753       stop->value("10");
1754       step=new Fl_Input(7*dw/4,y_,dw/4-2,dh-2,gettext("Step"));
1755       step->tooltip(gettext("Increment the counter value at each new execution of the loop"));
1756       step->value("");
1757       cond=new Fl_Input(dw,y_,dw-2,dh-2,gettext("Condition"));
1758       cond->tooltip(gettext("Enter condition for continuation of loop"));
1759       cond->value("k>=0");
1760       cond->hide();
1761       y_ += dh;
1762       loop=new Fl_Input(dw/3,y_,2*dw-dw/3-2,dh-2,gettext("Loop"));
1763       loop->tooltip(gettext("Enter instruction(s) to be executed, for example print(y); print(y*y);"));
1764       loop->value("print(k);");
1765       y_ += dh;
1766       y_ += dh;
1767       button0 = new Fl_Return_Button(2,y_,dx/5-4,dh-4);
1768       button0->label(gettext("OK"));
1769       button0->shortcut(0xff0d);
1770       button1 = new Fl_Button(dx/5+2,y_,dx/5-4,dh-4);
1771       button1->label(gettext("Cancel"));
1772       button1->shortcut(0xff1b);
1773     }
1774     w->label((gettext("New loop")));
1775     change_group_fontsize(w,l);
1776     w->set_modal();
1777     w->show();
1778     autosave_disabled=true;
1779     w->hotspot(w);
1780     Fl::focus(loop);
1781     context * contextptr=get_context(ed);
1782     int r=-2;
1783     for (;;) {
1784       if (r==-2){
1785 	// re-init fields value for function?
1786       }
1787       r=-1;
1788       Fl_Widget *o = Fl::readqueue();
1789       if (!o) Fl::wait();
1790       else {
1791 	if (o == button0) r = 0; // apply and quit
1792 	if (o == button1) r = 1; // cancel changes, quit
1793 	if (r==0 && pour->value()){
1794 	  gen g(count->value(),contextptr);
1795 	  if (g.type!=_IDNT)
1796 	    fl_message("%s",gettext("Invalid counter"));
1797 	}
1798 	if (o==tantque)
1799 	  pour->value(1-tantque->value());
1800 	if (o==pour)
1801 	  tantque->value(1-pour->value());
1802 	if (tantque->value()==1){
1803 	  count->hide(); start->hide(); stop->hide(); step->hide(); cond->show();
1804 	}
1805 	else {
1806 	  count->show(); start->show(); stop->show(); step->show(); cond->hide();
1807 	}
1808 	if (o == w) r=1;
1809 	if (r==0 || r==1)
1810 	  break;
1811       }
1812     }
1813     autosave_disabled=false;
1814     w->hide();
1815     if (r==0){
1816       giac::context * contextptr = get_context(ed);
1817       bool python=python_compat(contextptr);
1818       int i=ed->insert_position();
1819       string s;
1820       if (tantque->value()){
1821 	if (python)
1822 	  s = "while ";
1823 	else
1824 	  s = "tantque ";
1825 	s += cond->value();
1826 	if (python)
1827 	  s += ":\n";
1828 	else
1829 	  s += " faire\n";
1830 	s +=  loop->value();
1831 	if (python) s+="\n\n"; else s += "\nftantque;\n";
1832       }
1833       else {
1834 	if (python)
1835 	  s="for ";
1836 	else
1837 	  s="pour ";
1838 	s += count->value();
1839 	if (python){
1840 	  s += " in range(";
1841 	  s += start->value();
1842 	  s += ",";
1843 	  s += stop->value();
1844 	}
1845 	else {
1846 	  s += " de ";
1847 	  s += start->value();
1848 	  s += " jusque ";
1849 	  s += stop->value()+(python?1:0);
1850 	}
1851 	gen g(step->value(),contextptr);
1852 	if (!is_undef(g)){
1853 	  if (python){
1854 	    s += ',';
1855 	    s += step->value();
1856 	  }
1857 	  else {
1858 	    s += " pas ";
1859 	    s += step->value();
1860 	  }
1861 	}
1862 	if (python)
1863 	  s+="):\n";
1864 	else
1865 	  s += " faire\n";
1866 	s += loop->value();
1867 	if (python)
1868 	  s += "\n";
1869 	else
1870 	  s += "\nfpour;\n";
1871       }
1872       ed->buffer()->insert(i,s.c_str());
1873       int delta=s.size();
1874       if (Xcas_Text_Editor * xed=dynamic_cast<Xcas_Text_Editor *>(ed)){
1875 	int j=ed->buffer()->line_end(i)+1;
1876 	delta += xed->indent(j)-j;
1877 	j=ed->buffer()->line_end(j)+1;
1878 	delta += xed->indent(j)-j;
1879 	if (!python){
1880 	  j=ed->buffer()->line_end(j)+1;
1881 	  delta += xed->indent(j)-j;
1882 	}
1883 	ed->insert_position(i+delta);
1884 	if (python){
1885 	  Fl_Text_Editor::kf_backspace(0,ed);
1886 	  xed->dedent();
1887 	}
1888       }
1889       else
1890 	ed->insert_position(i+delta);
1891     }
1892   }
1893 
cb_choose_func(Fl_Text_Editor * ed)1894   void cb_choose_func(Fl_Text_Editor * ed){
1895     giac::context * contextptr = get_context(ed);
1896     bool python=python_compat(contextptr);
1897     int dx=240,dy=300,l=14;
1898     if (ed->window()){
1899       dx=int(0.5*ed->window()->w());
1900       dy=int(0.3*ed->window()->h());
1901       l=ed->window()->labelsize();
1902     }
1903     static Fl_Window * w = 0;
1904     static Fl_Return_Button * button0 = 0 ; // ok
1905     static Fl_Button * button1 =0; // cancel, quit
1906     static Fl_Input * args=0; // arguments
1907     static Fl_Input * name=0; // name
1908     static Fl_Input * locs=0; // local variables
1909     static Fl_Input * ret=0; // return value
1910     if (!w){
1911       Fl_Group::current(0);
1912       w=new Fl_Window(dx,dy);
1913       int dh=dy/5;
1914       int dw=dx/2;
1915       int y_=2;
1916       name=new Fl_Input(dw,y_,dw-2,dh-2,gettext("Name"));
1917       name->tooltip(gettext("Enter function name, e.g. f or myfunc"));
1918       name->value("f");
1919       y_ += dh;
1920       args=new Fl_Input(dw,y_,dw-2,dh-2,gettext("Arguments"));
1921       args->tooltip(gettext("Enter function argument(s), e.g. x or a,b,c. Leave empty if no argument"));
1922       args->value("x");
1923       y_ += dh;
1924       locs=new Fl_Input(dw,y_,dw-2,dh-2,gettext("Locals"));
1925       locs->tooltip(gettext("Enter local variable(s), e.g. k or n1,n2,n3. Leave empty if no local variables. Local symbolic variables should be purged after declaration."));
1926       locs->value("k");
1927       y_ += dh;
1928       ret=new Fl_Input(dw,y_,dw-2,dh-2,gettext("Return value"));
1929       ret->tooltip(gettext("Enter return value, e.g. x+1 or [a,b]. Leave empty if no local variables"));
1930       ret->value("x*x");
1931       y_ += dh;
1932       button0 = new Fl_Return_Button(2,y_,dx/5-4,dh-4);
1933       button0->label(gettext("OK"));
1934       button0->shortcut(0xff0d);
1935       button1 = new Fl_Button(dx/5+2,y_,dx/5-4,dh-4);
1936       button1->label(gettext("Cancel"));
1937       button1->shortcut(0xff1b);
1938     }
1939     w->label((gettext("New function")));
1940     change_group_fontsize(w,l);
1941     w->set_modal();
1942     if (python) locs->hide(); else locs->show();
1943     w->show();
1944     autosave_disabled=true;
1945     w->hotspot(w);
1946     Fl::focus(name);
1947     int lang=language(contextptr);
1948     int r=-2;
1949     for (;;) {
1950       if (r==-2){
1951 	// re-init fields value for function?
1952       }
1953       r=-1;
1954       Fl_Widget *o = Fl::readqueue();
1955       if (!o) Fl::wait();
1956       else {
1957 	if (o == button0) r = 0; // apply and quit
1958 	if (o == button1) r = 1; // cancel changes, quit
1959 	if (r==0){
1960 	  gen g(name->value(),contextptr);
1961 	  if (g.type!=_IDNT)
1962 	    fl_message("%s",gettext("Invalid functionname"));
1963 	}
1964 	if (o == w) r=1;
1965 	if (r==0 || r==1)
1966 	  break;
1967       }
1968     }
1969     autosave_disabled=false;
1970     w->hide();
1971     bool syntaxefr=lang==1; // activer apres les concours 2017
1972     if (r==0){
1973       int i=0,addi=0; // i=ed->insert_position(),addi=0;
1974       string s;
1975       switch (xcas_mode(contextptr)){
1976       case 0:
1977 	if (python)
1978 	  s += "def ";
1979 	else {
1980 	  if (syntaxefr)
1981 	    s+="fonction ";
1982 	}
1983 	s+=name->value();
1984 	s+='(';
1985 	s+=args->value();
1986 	s+=")";
1987 	if (python)
1988 	  s += ":";
1989 	else {
1990 	  if (!syntaxefr)
1991 	    s+=":={";
1992 	}
1993 	s+="\n";
1994 	if (!python && strlen(locs->value())){
1995 	  s+=python?"    # local ":"  local ";
1996 	  s+=locs->value();
1997 	  if (python)
1998 	    s+="\n    ";
1999 	  else
2000 	    s+=";\n  ";
2001 	}
2002 	addi=s.size();
2003 	s += "\n";
2004 	if (strlen(ret->value())){
2005 	  if (lang==1 && !python)
2006 	    s+="  retourne ";
2007 	  else
2008 	    s+=python?"    return ":"  return ";
2009 	  s+=ret->value();
2010 	  if (!python) s+=";";
2011 	}
2012 	s += "\n";
2013 	if (python)
2014 	  ;
2015 	else {
2016 	  if (syntaxefr)
2017 	    s+="ffonction";
2018 	  else
2019 	    s+="}";
2020 	  s+=":;\n\n";
2021 	}
2022 	ed->buffer()->insert(i,s.c_str());
2023 	ed->insert_position(i+addi);
2024 	break;
2025       case 1:
2026 	s+=name->value();
2027 	s+=":=proc(";
2028 	s+=args->value();
2029 	s+=")\n";
2030 	if (strlen(locs->value())){
2031 	  s+="  local ";
2032 	  s+=locs->value();
2033 	  s+=";\n  ";
2034 	}
2035 	addi=s.size();
2036 	s += "\n";
2037 	if (strlen(ret->value())){
2038 	  s+="  return ";
2039 	  s+=ret->value();
2040 	  s+=";";
2041 	}
2042 	s += "\nend:\n\n";
2043 	ed->buffer()->insert(i,s.c_str());
2044 	ed->insert_position(i+addi);
2045 	break;
2046       case 2:
2047 	s+=name->value();
2048 	s+=":=proc(";
2049 	s+=args->value();
2050 	s+=")\n";
2051 	if (strlen(locs->value())){
2052 	  s+="  local ";
2053 	  s+=locs->value();
2054 	  s+=";\n  ";
2055 	}
2056 	addi=s.size();
2057 	s += "\n";
2058 	if (strlen(ret->value())){
2059 	  s+="  return ";
2060 	  s+=ret->value();
2061 	  s+=";";
2062 	}
2063 	s += "\nend_proc:\n\n";
2064 	ed->buffer()->insert(i,s.c_str());
2065 	ed->insert_position(i+addi);
2066 	break;
2067       case 3:
2068 	s+=':';
2069 	s+=name->value();
2070 	s+='(';
2071 	s+=args->value();
2072 	s+=")\n:Func\n";
2073 	if (strlen(locs->value())){
2074 	  s+="\n:Local ";
2075 	  s+=locs->value();
2076 	  s+="\n:";
2077 	}
2078 	addi=s.size();
2079 	s += "\n:";
2080 	if (strlen(ret->value())){
2081 	  s+="\n:Return ";
2082 	  s+=ret->value();
2083 	}
2084 	s += "\n:EndFunc\n\n";
2085 	ed->buffer()->insert(i,s.c_str());
2086 	ed->insert_position(i+addi);
2087 	break;
2088       }
2089     }
2090   }
2091 
cb_prg_func(Fl_Menu_ * m,void *)2092  static void cb_prg_func(Fl_Menu_* m , void*) {
2093     Fl_Text_Editor * ed = do_find_editor(m);
2094     if (ed){
2095       cb_choose_func(ed);
2096       Fl::focus(ed);
2097     }
2098     else
2099       Xcas_input_0arg("f():={ local ; ; return ;}");
2100   }
2101 
cb_prg_si(Fl_Menu_ * m,void *)2102  static void cb_prg_si(Fl_Menu_* m , void*) {
2103     Fl_Text_Editor * ed = do_find_editor(m);
2104     if (ed){
2105       cb_dialog_test(ed);
2106       Fl::focus(ed);
2107     }
2108     else
2109       Xcas_input_0arg("si alors sinon fsi;");
2110   }
2111 
cb_prg_pour(Fl_Menu_ * m,void *)2112  static void cb_prg_pour(Fl_Menu_* m , void*) {
2113     Fl_Text_Editor * ed = do_find_editor(m);
2114     if (ed){
2115       cb_dialog_loop(ed);
2116       Fl::focus(ed);
2117     }
2118     else
2119       Xcas_input_0arg("pour de jusque faire fpour;");
2120   }
2121 
cb_prg_local(Fl_Menu_ * m,void *)2122   static void cb_prg_local(Fl_Menu_* m , void*) {
2123     Fl_Text_Editor * ed = do_find_editor(m);
2124     if (ed){
2125       giac::context * contextptr = get_context(ed);
2126       int i=ed->insert_position();
2127       ed->buffer()->insert(i,xcas_mode(contextptr)==3?"\n:Local ":"local ;\n  ");
2128       ed->insert_position(i+6);
2129     }
2130     else
2131       Xcas_input_0arg("local ;");
2132   }
2133 
cb_prg_return(Fl_Menu_ * m,void *)2134   static void cb_prg_return(Fl_Menu_* m , void*) {
2135     Fl_Text_Editor * ed = do_find_editor(m);
2136     if (ed){
2137       giac::context * contextptr = get_context(ed);
2138       int i=ed->insert_position();
2139       ed->buffer()->insert(i,xcas_mode(contextptr)==3?"\n:Return ":"return ;");
2140       ed->insert_position(i+7);
2141     }
2142     else
2143       Xcas_input_0arg("return ;");
2144   }
2145 
cb_prg_ifthenelse(Fl_Menu_ * m,void *)2146   static void cb_prg_ifthenelse(Fl_Menu_* m , void*) {
2147     Fl_Text_Editor * ed = do_find_editor(m);
2148     if (ed){
2149       giac::context * contextptr = get_context(ed);
2150       int i=ed->insert_position();
2151       switch (xcas_mode(contextptr)){
2152       case 0:
2153 	ed->buffer()->insert(i,"if () { ;} else { ;}\n  ");
2154 	ed->insert_position(i+4);
2155 	break;
2156       case 1:
2157 	ed->buffer()->insert(i,"if  then ; else ; fi;\n  ");
2158 	ed->insert_position(i+3);
2159 	break;
2160       case 2:
2161 	ed->buffer()->insert(i,"if  then ; else ; end_if;\n  ");
2162 	ed->insert_position(i+3);
2163 	break;
2164       case 3:
2165 	ed->buffer()->insert(i,"\n:If Then\n:Else \n:EndIf;\n");
2166 	ed->insert_position(i+4);
2167 	break;
2168       }
2169     }
2170     else {
2171       giac::context * contextptr = get_context(Xcas_input_focus);
2172       switch (xcas_mode(contextptr)){
2173       case 0:
2174 	Xcas_input_0arg("if () { ; } else { ; }");
2175 	break;
2176       case 1:
2177 	Xcas_input_0arg("if  then ; else ; fi;");
2178 	break;
2179       case 2:
2180 	Xcas_input_0arg("if  then ; else ; end_if;");
2181 	break;
2182       case 3:
2183 	Xcas_input_0arg("\n:If Then\n:Else \n:EndIf;\n");
2184 	break;
2185       }
2186     }
2187   }
2188 
cb_prg_sialorssinon(Fl_Menu_ * m,void *)2189   static void cb_prg_sialorssinon(Fl_Menu_* m , void*) {
2190     Fl_Text_Editor * ed = do_find_editor(m);
2191     if (ed){
2192       giac::context * contextptr = get_context(ed);
2193       int i=ed->insert_position();
2194       ed->buffer()->insert(i,"si  alors ; sinon ; fsi;\n  ");
2195       ed->insert_position(i+3);
2196     }
2197     else
2198       Xcas_input_0arg("si  alors ; sinon  ; fsi");
2199   }
2200 
cb_prg_pour_(Fl_Menu_ * m,void *)2201   static void cb_prg_pour_(Fl_Menu_* m , void*) {
2202     Fl_Text_Editor * ed = do_find_editor(m);
2203     if (ed){
2204       giac::context * contextptr = get_context(ed);
2205       int i=ed->insert_position();
2206       ed->buffer()->insert(i,"pour  de  jusque  faire\n    ;\n  fpour;\n  ");
2207       ed->insert_position(i+5);
2208     }
2209     else
2210       Xcas_input_0arg("pour  de  jusque  faire ; fpour;");
2211   }
2212 
cb_prg_tantque(Fl_Menu_ * m,void *)2213   static void cb_prg_tantque(Fl_Menu_* m , void*) {
2214     Fl_Text_Editor * ed = do_find_editor(m);
2215     if (ed){
2216       giac::context * contextptr = get_context(ed);
2217       int i=ed->insert_position();
2218       ed->buffer()->insert(i,"tantque  faire\n    ;\n  ftantque;\n  ");
2219       ed->insert_position(i+8);
2220     }
2221     else
2222       Xcas_input_0arg("tantque  faire ; ftantque;");
2223   }
2224 
cb_prg_repeter(Fl_Menu_ * m,void *)2225   static void cb_prg_repeter(Fl_Menu_* m , void*) {
2226     Fl_Text_Editor * ed = do_find_editor(m);
2227     if (ed){
2228       giac::context * contextptr = get_context(ed);
2229       int i=ed->insert_position();
2230       ed->buffer()->insert(i,"repeter\n    ;\n  jusqu_a ;\n");
2231       ed->insert_position(i+12);
2232     }
2233     else
2234       Xcas_input_0arg("repeter jusqu_a ;");
2235   }
2236 
cb_prg_ifthen(Fl_Menu_ * m,void *)2237   static void cb_prg_ifthen(Fl_Menu_* m , void*) {
2238     Fl_Text_Editor * ed = do_find_editor(m);
2239     if (ed){
2240       giac::context * contextptr = get_context(ed);
2241       int i=ed->insert_position();
2242       switch (xcas_mode(contextptr)){
2243       case 0:
2244 	ed->buffer()->insert(i,"if () { ;}\n");
2245 	ed->insert_position(i+5);
2246 	break;
2247       case 1:
2248 	ed->buffer()->insert(i,"if  then ; fi;\n");
2249 	ed->insert_position(i+4);
2250 	break;
2251       case 2:
2252 	ed->buffer()->insert(i,"if  then ; end_if;\n");
2253 	ed->insert_position(i+4);
2254 	break;
2255       case 3:
2256 	ed->buffer()->insert(i,"\n:If Then\n:EndIf\n");
2257 	ed->insert_position(i+4);
2258 	break;
2259       }
2260     }
2261     else {
2262       giac::context * contextptr = get_context(Xcas_input_focus);
2263       switch (xcas_mode(contextptr)){
2264       case 0:
2265 	Xcas_input_0arg("if () { ;}");
2266 	break;
2267       case 1:
2268 	Xcas_input_0arg("if then ; fi;");
2269 	break;
2270       case 2:
2271 	Xcas_input_0arg("if then ; end_if;");
2272 	break;
2273       case 3:
2274 	Xcas_input_0arg("\n:If Then\n:EndIf\n");
2275 	break;
2276       }
2277     }
2278   }
2279 
cb_prg_print(Fl_Menu_ * m,void *)2280   static void cb_prg_print(Fl_Menu_* m , void*) {
2281     Fl_Text_Editor * ed = do_find_editor(m);
2282     if (ed){
2283       int i=ed->insert_position();
2284       ed->buffer()->insert(i,gettext("print();"));
2285       ed->insert_position(i+strlen(gettext("print();"))-2);
2286     }
2287     else
2288       Xcas_input_0arg(gettext("print();"));
2289   }
2290 
cb_prg_input(Fl_Menu_ * m,void *)2291   static void cb_prg_input(Fl_Menu_* m , void*) {
2292     Fl_Text_Editor * ed = do_find_editor(m);
2293     if (ed){
2294       int i=ed->insert_position();
2295       ed->buffer()->insert(i,gettext("input();"));
2296       ed->insert_position(i+strlen(gettext("input();"))-2);
2297     }
2298     else
2299       Xcas_input_0arg(gettext("input();"));
2300   }
2301 
cb_prg_switch(Fl_Menu_ * m,void *)2302   static void cb_prg_switch(Fl_Menu_* m , void*) {
2303     Fl_Text_Editor * ed = do_find_editor(m);
2304     if (ed){
2305       int i=ed->insert_position();
2306       ed->buffer()->insert(i,"switch(){\n    case : break;\n  }\n  ");
2307       ed->insert_position(i+8);
2308     }
2309     else
2310       Xcas_input_0arg("switch () {case : break;}");
2311   }
2312 
cb_prg_trycatch(Fl_Menu_ * m,void *)2313   static void cb_prg_trycatch(Fl_Menu_* m , void*) {
2314     Fl_Text_Editor * ed = do_find_editor(m);
2315     if (ed){
2316       giac::context * contextptr = get_context(ed);
2317       int i=ed->insert_position();
2318       ed->buffer()->insert(i,xcas_mode(contextptr)==3?"\n:Try\n\n:Else\n\n:EndTry":"\ntry {\n ;\n}\ncatch(){\n ;}\n");
2319       ed->insert_position(i+7);
2320     }
2321     else {
2322       giac::context * contextptr = get_context(Xcas_input_focus);
2323       Xcas_input_0arg(xcas_mode(contextptr)==3?"\n:Try\n\n:Else\n\n:EndTry":"try { ;}catch(){ ;}");
2324     }
2325   }
2326 
cb_prg_for(Fl_Menu_ * m,void *)2327   static void cb_prg_for(Fl_Menu_* m , void*) {
2328     Fl_Text_Editor * ed = do_find_editor(m);
2329     if (ed){
2330       giac::context * contextptr = get_context(ed);
2331       int i=ed->insert_position();
2332       switch (xcas_mode(contextptr)){
2333       case 0:
2334 	ed->buffer()->insert(i,"for(;;){\n    ;\n  }\n  ");
2335 	ed->insert_position(i+4);
2336 	break;
2337       case 1:
2338 	ed->buffer()->insert(i,"for  from to do\n    ;\n  od;\n  ");
2339 	ed->insert_position(i+4);
2340 	break;
2341       case 2:
2342 	ed->buffer()->insert(i,"for  from to do\n    ;\n  end_for;\n  ");
2343 	ed->insert_position(i+4);
2344 	break;
2345       case 3:
2346 	ed->buffer()->insert(i,"\n:For ,,\n\n:EndFor\n");
2347 	ed->insert_position(i+5);
2348 	break;
2349       }
2350     }
2351     else {
2352       giac::context * contextptr = get_context(Xcas_input_focus);
2353       switch (xcas_mode(contextptr)){
2354       case 0:
2355 	Xcas_input_0arg("for( ; ; ){ ;}");
2356 	break;
2357       case 1:
2358 	Xcas_input_0arg("for  from to do ; od;");
2359 	break;
2360       case 2:
2361 	Xcas_input_0arg("for  from to do ; end_for;");
2362 	break;
2363       case 3:
2364 	Xcas_input_0arg("\n:For ,,\n:\n:EndFor;\n");
2365 	break;
2366       }
2367     }
2368   }
2369 
cb_prg_while(Fl_Menu_ * m,void *)2370   static void cb_prg_while(Fl_Menu_* m , void*) {
2371     Fl_Text_Editor * ed = do_find_editor(m);
2372     if (ed){
2373       giac::context * contextptr = get_context(ed);
2374       int i=ed->insert_position();
2375       switch (xcas_mode(contextptr)){
2376       case 0:
2377 	ed->buffer()->insert(i,"while(){\n    ;\n  }\n  ");
2378 	ed->insert_position(i+6);
2379 	break;
2380       case 1:
2381 	ed->buffer()->insert(i,"while  do\n    ;\n  od;\n  ");
2382 	ed->insert_position(i+6);
2383 	break;
2384       case 2:
2385 	ed->buffer()->insert(i,"while  do\n    ;\n  end_while;\n  ");
2386 	ed->insert_position(i+6);
2387 	break;
2388       case 3:
2389 	ed->buffer()->insert(i,"\n:While \n:\n:EndWhile\n");
2390 	ed->insert_position(i+7);
2391 	break;
2392       }
2393     }
2394     else {
2395       giac::context * contextptr = get_context(Xcas_input_focus);
2396       switch (xcas_mode(contextptr)){
2397       case 0:
2398 	Xcas_input_0arg("while( ){ ;}");
2399 	break;
2400       case 1:
2401 	Xcas_input_0arg("while  do ; od;");
2402 	break;
2403       case 2:
2404 	Xcas_input_0arg("while  do ; end_while;");
2405 	break;
2406       case 3:
2407 	Xcas_input_0arg("\n:While \n:\n:EndWhile;\n");
2408 	break;
2409       }
2410     }
2411   }
2412 
cb_prg_break(Fl_Menu_ * m,void *)2413   static void cb_prg_break(Fl_Menu_* m , void*) {
2414     Fl_Text_Editor * ed = do_find_editor(m);
2415     if (ed){
2416       giac::context * contextptr = get_context(ed);
2417       int i=ed->insert_position();
2418       ed->buffer()->insert(i,xcas_mode(contextptr)==3?":Stop \n:":"break;\n  ");
2419       ed->insert_position(i+8);
2420     }
2421     else {
2422       giac::context * contextptr = get_context(Xcas_input_focus);
2423       Xcas_input_0arg(xcas_mode(contextptr)==3?":Stop \n":"break;");
2424     }
2425   }
2426 
cb_prg_continue(Fl_Menu_ * m,void *)2427   static void cb_prg_continue(Fl_Menu_* m , void*) {
2428     Fl_Text_Editor * ed = do_find_editor(m);
2429     if (ed){
2430       giac::context * contextptr = get_context(ed);
2431       int i=ed->insert_position();
2432       ed->buffer()->insert(i,xcas_mode(contextptr)==3?":Cycle  \n":"continue;\n  ");
2433       ed->insert_position(i+11);
2434     }
2435     else {
2436       giac::context * contextptr = get_context(Xcas_input_focus);
2437       Xcas_input_0arg(xcas_mode(contextptr)==3?":Cycle  \n":"continue;");
2438     }
2439   }
2440 
2441   Fl_Menu_Item Editeur_menu[] = {
2442     {gettext("Prog"), 0,  0, 0, 64, 0, 0, 14, 56},
2443     {gettext("Load"), 0,  (Fl_Callback*)cb_Editeur_Load, 0, 0, 0, 0, 14, 56},
2444     {gettext("Insert"), 0,  0, 0, 64, 0, 0, 14, 56},
2445     {gettext("File"), 0,  (Fl_Callback*)cb_Editeur_Insert_File, 0, 0, 0, 0, 14, 56},
2446     {gettext("Xcas text"), 0,  (Fl_Callback*)cb_Editeur_Insert_Xcas, 0, 0, 0, 0, 14, 56},
2447     {gettext("Xcas Python text"), 0,  (Fl_Callback*)cb_Editeur_Insert_Python, 0, 0, 0, 0, 14, 56},
2448     {gettext("Maple text"), 0,  (Fl_Callback*)cb_Editeur_Insert_Maple, 0, 0, 0, 0, 14, 56},
2449     {gettext("Mupad text"), 0,  (Fl_Callback*)cb_Editeur_Insert_Mupad, 0, 0, 0, 0, 14, 56},
2450     {gettext("TI text"), 0,  (Fl_Callback*)cb_Editeur_Insert_Ti, 0, 0, 0, 0, 14, 56},
2451     {0},
2452     {gettext("Save"), 0,  (Fl_Callback*)cb_Editeur_Save, 0, 0, 0, 0, 14, 56},
2453     {gettext("Save as"), 0,  (Fl_Callback*)cb_Editeur_Save_as, 0, 0, 0, 0, 14, 56},
2454     {gettext("File extension"), 0,  (Fl_Callback*)cb_Editeur_Extension, 0, 0, 0, 0, 14, 56},
2455     {gettext("Export"), 0,  0, 0, 64, 0, 0, 14, 56},
2456     {gettext("Xcas text"), 0,  (Fl_Callback*)cb_Editeur_Export_Xcas, 0, 0, 0, 0, 14, 56},
2457     {gettext("Xcas-python text"), 0,  (Fl_Callback*)cb_Editeur_Export_Python, 0, 0, 0, 0, 14, 56},
2458     {gettext("Maple text"), 0,  (Fl_Callback*)cb_Editeur_Export_Maple, 0, 0, 0, 0, 14, 56},
2459     {gettext("Mupad text"), 0,  (Fl_Callback*)cb_Editeur_Export_Mupad, 0, 0, 0, 0, 14, 56},
2460     {gettext("TI text"), 0,  (Fl_Callback*)cb_Editeur_Export_Ti, 0, 0, 0, 0, 14, 56},
2461     {0},
2462     {gettext("Translate"), 0,  0, 0, 64, 0, 0, 14, 56},
2463     {gettext("Xcas"), 0,  (Fl_Callback*)cb_Editeur_Translate_Xcas, 0, 0, 0, 0, 14, 56},
2464     {gettext("Maple"), 0,  (Fl_Callback*)cb_Editeur_Translate_Maple, 0, 0, 0, 0, 14, 56},
2465     {gettext("Mupad"), 0,  (Fl_Callback*)cb_Editeur_Translate_Mupad, 0, 0, 0, 0, 14, 56},
2466     {gettext("TI"), 0,  (Fl_Callback*)cb_Editeur_Translate_Ti, 0, 0, 0, 0, 14, 56},
2467     {0},
2468     {gettext("Export/Print"), 0,  0, 0, 64, 0, 0, 14, 56},
2469     //    {gettext("latex preview"), 0,  (Fl_Callback*)cb_Tableur_LaTeX_Preview, 0, 0, 0, 0, 14, 56},
2470     //    {gettext("latex printer"), 0,  (Fl_Callback*)cb_Tableur_LaTeX_Print, 0, 0, 0, 0, 14, 56},
2471     {gettext("Preview"), 0,  (Fl_Callback*)cb_Editeur_Preview, 0, 0, 0, 0, 14, 56},
2472     {gettext("to printer"), 0,  (Fl_Callback*)cb_Editeur_Print, 0, 0, 0, 0, 14, 56},
2473     {0}, // end Print
2474     {0}, // end File
2475     {gettext("Edit"), 0,  0, 0, 64, 0, 0, 14, 56},
2476     {gettext("Paste"), 0,  (Fl_Callback *) cb_Paste, 0, 0, 0, 0, 14, 56},
2477     {gettext("Search (Ctrl-F)"), 0,  (Fl_Callback *) cb_Editeur_Search, 0, 0, 0, 0, 14, 56},
2478     {gettext("Indent line (Esc)"), 0,  (Fl_Callback *) cb_Editeur_Indent_line, 0, 0, 0, 0, 14, 56},
2479     {gettext("Indent all"), 0,  (Fl_Callback *) cb_Editeur_Indent_all, 0, 0, 0, 0, 14, 56},
2480     {gettext("Parse"), 0,  (Fl_Callback *) cb_Editeur_Test, 0, 0, 0, 0, 14, 56},
2481     {gettext("Exec all"), 0xffc4,  (Fl_Callback *) cb_Editeur_Exec_All, 0, 0, 0, 0, 14, 56},
2482     {gettext("Extend editor"), 0xffc2,  (Fl_Callback *) cb_Editeur_Extend, 0, 0, 0, 0, 14, 56},
2483     {gettext("Shrink editor"), 0xffc3,  (Fl_Callback *) cb_Editeur_Shrink, 0, 0, 0, 0, 14, 56},
2484     {0}, // end Edit
2485     {gettext("Add"), 0,  0, 0, 64, 0, 0, 14, 56},
2486     {gettext("Func"), 0,  0, 0, 64, 0, 0, 14, 56},
2487     {gettext("new function"), 0,  (Fl_Callback *) cb_prg_func, 0, 0, 0, 0, 14, 56},
2488     {gettext("local"), 0,  (Fl_Callback *) cb_prg_local, 0, 0, 0, 0, 14, 56},
2489     {gettext("return"), 0,  (Fl_Callback *) cb_prg_return, 0, 0, 0, 0, 14, 56},
2490     {0}, // end Func
2491     {gettext("IO"), 0,  0, 0, 64, 0, 0, 14, 56},
2492     {gettext("print"), 0,  (Fl_Callback *) cb_prg_print, 0, 0, 0, 0, 14, 56},
2493     {gettext("input"), 0,  (Fl_Callback *) cb_prg_input, 0, 0, 0, 0, 14, 56},
2494     {0}, // end IO
2495     {gettext("Test"), 0,  0, 0, 64, 0, 0, 14, 56},
2496     {gettext("new test"), 0,  (Fl_Callback *) cb_prg_si, 0, 0, 0, 0, 14, 56},
2497     {gettext("si alors sinon"), 0,  (Fl_Callback *) cb_prg_sialorssinon, 0, 0, 0, 0, 14, 56},
2498     {gettext("if"), 0,  (Fl_Callback *) cb_prg_ifthen, 0, 0, 0, 0, 14, 56},
2499     {gettext("if else"), 0,  (Fl_Callback *) cb_prg_ifthenelse, 0, 0, 0, 0, 14, 56},
2500     {gettext("switch"), 0,  (Fl_Callback *) cb_prg_switch, 0, 0, 0, 0, 14, 56},
2501     {gettext("try catch"), 0,  (Fl_Callback *) cb_prg_trycatch, 0, 0, 0, 0, 14, 56},
2502     {0}, // end Test
2503     {gettext("Loop"), 0,  0, 0, 64, 0, 0, 14, 56},
2504     {gettext("new loop"), 0,  (Fl_Callback *) cb_prg_pour, 0, 0, 0, 0, 14, 56},
2505     {gettext("pour"), 0,  (Fl_Callback *) cb_prg_pour_, 0, 0, 0, 0, 14, 56},
2506     {gettext("tantque"), 0,  (Fl_Callback *) cb_prg_tantque, 0, 0, 0, 0, 14, 56},
2507     {gettext("repeter jusqu_a"), 0,  (Fl_Callback *) cb_prg_repeter, 0, 0, 0, 0, 14, 56},
2508     {gettext("for "), 0,  (Fl_Callback *) cb_prg_for, 0, 0, 0, 0, 14, 56},
2509     {gettext("while "), 0,  (Fl_Callback *) cb_prg_while, 0, 0, 0, 0, 14, 56},
2510     {gettext("break"), 0,  (Fl_Callback *) cb_prg_break, 0, 0, 0, 0, 14, 56},
2511     {gettext("continue"), 0,  (Fl_Callback *) cb_prg_continue, 0, 0, 0, 0, 14, 56},
2512     {0}, // end Loop
2513     {0}, // end Prg
2514     {0} // end menu
2515   };
2516 
clear_gchanged()2517   void Xcas_Text_Editor::clear_gchanged(){
2518     gchanged=false;
2519   }
2520 
set_gchanged()2521   void Xcas_Text_Editor::set_gchanged(){
2522     gchanged=true;
2523   }
2524 
Xcas_Text_Editor(int X,int Y,int W,int H,Fl_Text_Buffer * b,const char * l)2525   Xcas_Text_Editor::Xcas_Text_Editor(int X, int Y, int W, int H, Fl_Text_Buffer *b,const char* l ):Fl_Text_Editor(X,Y,W,H,l){
2526     styletable=vector<Fl_Text_Display::Style_Table_Entry>(styletable_init,styletable_init+styletable_n);
2527     tableur=0;
2528     gchanged=true;
2529     labeltype(FL_NO_LABEL);
2530     color(FL_WHITE);
2531     buffer(b);
2532     char *style = new char[buffer()->length()+1];
2533     char *text = buffer()->text();
2534 
2535 
2536     memset(style, 'A', buffer()->length());
2537     style[buffer()->length()] = '\0';
2538 
2539     stylebuf = new Fl_Text_Buffer(buffer()->length());
2540 
2541     style_parse(text, style, buffer()->length());
2542 
2543     stylebuf->text(style);
2544     delete[] style;
2545     free(text);
2546     highlight_data(stylebuf, &styletable.front(),styletable.size(),'A', style_unfinished_cb, 0);
2547 
2548    }
2549 
Editeur(int x,int y,int w,int h,const char * l)2550   Editeur::Editeur(int x,int y,int w,int h,const char * l):Fl_Group(x,y,w,h,l),contextptr(0){
2551     bool logo=false;
2552     if (parent()){
2553       labelsize(parent()->labelsize());
2554       logo=dynamic_cast<Logo *>(parent());
2555       contextptr=get_context(parent());
2556     }
2557     Fl_Group::current(this);
2558     int L=(3*labelsize())/2;
2559     Fl_Text_Buffer * b = new Fl_Text_Buffer;
2560     editor=new Xcas_Text_Editor(x,y+L,w,h-L,b,l);
2561     editor->Fl_Text_Display::textsize(labelsize());
2562     editor->labelsize(labelsize());
2563     log = 0;
2564     if (logo){
2565       menubar = new Fl_Menu_Bar(x,y,w/2,L);
2566     }
2567     else {
2568       menubar = new Fl_Menu_Bar(x,y,w/4,L);
2569     }
2570     int s= Editeur_menu->size();
2571     Fl_Menu_Item * menuitem = new Fl_Menu_Item[s];
2572     for (int i=0;i<s;++i)
2573       *(menuitem+i)=*(Editeur_menu+i);
2574     menubar->menu (menuitem);
2575     change_menu_fontsize(menuitem,3,labelsize()); // 3=#submenus
2576     linenumber=0; nxt_button=0; exec_button=0; func_button=0; si_button=0; pour_button=0;
2577     if (!logo){
2578       linenumber = new Fl_Value_Input(x+w/3-w/12,y,w/12,L);
2579       linenumber->tooltip(gettext("Line number"));
2580       linenumber->callback((Fl_Callback *)cb_Editeur_Gotoline);
2581       linenumber->when(FL_WHEN_ENTER_KEY|FL_WHEN_NOT_CHANGED);
2582       nxt_button = new Fl_Button(x+w/3,y,w/12,L);
2583       nxt_button->labelsize(labelsize());
2584       nxt_button->label(gettext("nxt"));
2585       nxt_button->tooltip(gettext("Find next occurence (defined by Edit->Search)"));
2586       nxt_button->callback((Fl_Callback *) cb_Editeur_Next);
2587       if (parent()==window()){
2588 	exec_button = new Save_Focus_Button(x+w/3-w/12,y,w/12,L);
2589 	exec_button->callback((Fl_Callback *)cb_Editeur_Exec);
2590 	exec_button->labelsize(labelsize());
2591 	exec_button->label(gettext("exe"));
2592 	exec_button->tooltip(gettext("Exec line in current widget"));
2593       }
2594       else {
2595 	func_button = new Fl_Button(nxt_button->x()+nxt_button->w(),y,w/12,L);
2596 	func_button->labelsize(labelsize());
2597 	func_button->label(gettext("Func"));
2598 	func_button->tooltip(gettext("Assistant for function creation"));
2599 	func_button->callback((Fl_Callback *) cb_prg_func);
2600 	si_button = new Fl_Button(func_button->x()+func_button->w(),y,w/12,L);
2601 	si_button->labelsize(labelsize());
2602 	si_button->label(gettext("Test"));
2603 	si_button->tooltip(gettext("Assistant for test creation"));
2604 	si_button->callback((Fl_Callback *) cb_prg_si);
2605 	pour_button = new Fl_Button(si_button->x()+si_button->w(),y,w/12,L);
2606 	pour_button->labelsize(labelsize());
2607 	pour_button->label(gettext("Loop"));
2608 	pour_button->tooltip(gettext("Assistant for loop creation"));
2609 	pour_button->callback((Fl_Callback *) cb_prg_pour);
2610       }
2611     }
2612     button = new Fl_Button(x+w/2+w/6,y,w/12,L);
2613     button->labelsize(labelsize());
2614     // button->label(logo?"OK":"OK (F9)");
2615     button->label("OK");
2616     button->labelcolor(FL_DARK_GREEN);
2617     button->tooltip(gettext("Parse current program"));
2618     button->callback((Fl_Callback *) logo?cb_Editeur_Exec_All:cb_Editeur_Test);
2619     button->shortcut(0xffc6); // FIXME: quick fix, otherwise Esc leaves xcas
2620     save_button = new Fl_Button(x+w/2+w/4,y,w/12,L);
2621     save_button->labelsize(labelsize());
2622     save_button->label("Save");
2623     save_button->tooltip(gettext("Save current program"));
2624     save_button->callback((Fl_Callback *) cb_Editeur_Save);
2625     output = new Fl_Output(x+w/2+w/4+save_button->w(),y,w-w/2-w/4-save_button->w(),L);
2626     output->labelsize(labelsize());
2627     end();
2628 
2629     b->add_modify_callback(style_update, editor);
2630     b->add_modify_callback(Editor_changed_cb, editor);
2631     resizable(editor);
2632     switch (xcas_mode(contextptr)){
2633     case 0:
2634       if (python_compat(contextptr))
2635 	extension="py";
2636       else
2637 	extension="cxx";
2638       break;
2639     case 1:
2640       extension="map";
2641       break;
2642     case 2:
2643       extension="mu";
2644       break;
2645     case 3:
2646       extension="ti";
2647       break;
2648     }
2649     parent_redraw(this);
2650   }
2651 
position(int & i,int & j) const2652   void Editeur::position(int & i,int & j) const {
2653     if (editor->buffer()->selected())
2654       editor->buffer()->selection_position(&i,&j);
2655     else {
2656       i=editor->insert_position();
2657       j=i;
2658     }
2659   }
2660 
value() const2661   std::string Editeur::value() const {
2662     char * ch=editor->buffer()->text();
2663     string res=ch;
2664     free(ch);
2665     return res;
2666   }
2667 
value() const2668   std::string Xcas_Text_Editor::value() const {
2669     char * ch=buffer()->text();
2670     string res=ch;
2671     free(ch);
2672     return res;
2673   }
2674 
insert_replace(const string & s,bool select)2675   void Xcas_Text_Editor::insert_replace(const string & s,bool select){
2676     if (buffer()->selected()){
2677       int b,e;
2678       buffer()->selection_position(&b,&e);
2679       buffer()->remove_selection();
2680       buffer()->insert(b,s.c_str());
2681       buffer()->select(b,b+s.size());
2682     }
2683     else {
2684       int b=insert_position();
2685       buffer()->insert(b,s.c_str());
2686       buffer()->select(b,b+s.size());
2687     }
2688     set_gchanged();
2689   }
2690 
mark() const2691   int Xcas_Text_Editor::mark() const {
2692     int b,e;
2693     buffer()->selection_position(&b,&e);
2694     return e;
2695   }
2696 
match()2697   void Xcas_Text_Editor::match(){
2698     static bool recursive=false;
2699     if (buffer()->selected())
2700       return;
2701     int pos=insert_position();
2702     int lastkey=Fl::event_key();
2703     if (lastkey!='(' && lastkey!='[' && lastkey!='{' && lastkey!='}' && lastkey!=']' && lastkey!=')' && lastkey!='0' && lastkey!='9' && lastkey!='\'' && lastkey != '=' && lastkey !=FL_Left && lastkey !=FL_Right)
2704       return;
2705     if (recursive)
2706       return;
2707     recursive=true;
2708     // check if cursor is on [, (, ), ]
2709     int p0=pos;
2710     char * ch=buffer()->text();
2711     string c(ch);
2712     free(ch);
2713     int pmax=c.size();
2714     bool closing=false,opening=false;
2715     if (pos<pmax)
2716       opening= c[pos]=='(' || c[pos]=='[' || c[pos]=='{';
2717     if (!opening && pos){
2718       closing = c[pos-1]==')' || c[pos-1]==']' || c[pos-1]=='}';
2719       if (closing)
2720 	--p0;
2721     }
2722     if (opening || closing){
2723       bool ok=giac::matchpos(c,p0);
2724       if (!ok){
2725 	if (closing)
2726 	  p0=pos-1;
2727 	else
2728 	  p0=pos+1;
2729       }
2730       else {
2731 	if (opening && pos<pmax)
2732 	  p0=p0+1;
2733       }
2734       Fl::flush();
2735       usleep(100000);
2736       if (true || !Fl::get_key(lastkey)){
2737 	Fl::check();
2738 	buffer()->select(pos,p0);
2739 	unsigned s=selection_color();
2740 	if (ok){
2741 	  if (opening)
2742 	    selection_color(FL_GREEN);
2743 	  else
2744 	    selection_color(fl_color_cube(0,FL_NUM_GREEN-1,2));
2745 	}
2746 	else
2747 	  selection_color(FL_RED);
2748 	damage(damage() | FL_DAMAGE_ALL);
2749 	redraw();
2750 	Fl::flush();
2751 	usleep(70000);
2752 	if (!Fl::ready()){
2753 	  for (int i=0;i<giac::PARENTHESIS_NWAIT;++i){
2754 	    usleep(50000);
2755 	    if (Fl::ready())
2756 	      break;
2757 	  }
2758 	}
2759 	selection_color(s);
2760 	buffer()->select(pos,pos);
2761 	damage(damage() | FL_DAMAGE_ALL);
2762 	redraw();
2763 	Fl::flush();
2764       }
2765     }
2766     recursive=false;
2767   }
2768 
draw()2769   void Xcas_Text_Editor::draw(){
2770     int clip_x,clip_y,clip_w,clip_h;
2771     fl_clip_box(x(),y(),w(),h(),clip_x,clip_y,clip_w,clip_h);
2772     fl_push_clip(clip_x,clip_y,clip_w,clip_h);
2773     Fl_Text_Editor::draw();
2774     fl_pop_clip();
2775   }
2776 
2777   const string motscleftab[] = {
2778     "{",
2779     "Else",
2780     "ElseIf",
2781     "EndDlog",
2782     "EndFor",
2783     "EndFunc",
2784     "EndIf",
2785     "EndLoop",
2786     "EndPrgm",
2787     "EndTry",
2788     "EndWhile",
2789     "Exit",
2790     "Func",
2791     "Then",
2792     "alors",
2793     "and",
2794     "break",
2795     "by",
2796     "case",
2797     "catch",
2798     "continue",
2799     "de",
2800     "default",
2801     "do",
2802     "downto",
2803     "elif",
2804     "else",
2805     "end",
2806     "end_case",
2807     "end_for",
2808     "end_if",
2809     "end_proc",
2810     "end_while",
2811     "et",
2812     "faire",
2813     "ffaire",
2814     "fi",
2815     "fpour",
2816     "from",
2817     "fsi",
2818     "ftantque",
2819     "jusqu_a",
2820     "jusqua",
2821     "jusque",
2822     "non",
2823     "not",
2824     "od",
2825     "or",
2826     "ou",
2827     "pas",
2828     "sinon",
2829     "step",
2830     "then",
2831     "to",
2832     "until",
2833     "xor"
2834   };
2835 
2836   const std::vector<string> motsclef(motscleftab,motscleftab+sizeof(motscleftab)/sizeof(motscleftab[0]));
2837 
2838   // indent current line at position pos, return new current position
indent(int pos)2839   int Xcas_Text_Editor::indent(int pos){
2840     giac::context * contextptr = get_context(this);
2841     bool python=python_compat(contextptr);
2842     int indentunit=2;
2843     if (python) indentunit=4;
2844     int debut_ligne=buffer()->line_start(pos),indent=0;
2845     // Tab pressed -> indent current line
2846     char Lastchar;
2847     if (debut_ligne){
2848       char * ch_ = buffer()->line_text(pos-1),*ch=ch_;
2849       bool empty_line=true;
2850       int position=buffer()->line_start(debut_ligne-2);
2851       indent = python?0:2;
2852       while (1){
2853 	free(ch_);
2854 	ch_ = buffer()->line_text(position);
2855 	ch=ch_;
2856 	int save_indent=indent;
2857 	// Count spaces in ch
2858 	for (;*ch;++ch,++indent){
2859 	  if ( (python && *ch=='#') || (!python && *ch=='/' && *(ch+1)=='/') )
2860 	    break;
2861 	  if (*ch!=' '){
2862 	    empty_line=false;
2863 	    break;
2864 	  }
2865 	}
2866 	if (position<2 || !empty_line)
2867 	  break;
2868 	// line was empty, restore indent and go one line above
2869 	indent = save_indent;
2870 	position=buffer()->line_start(position-2);
2871       }
2872       if (empty_line)
2873 	indent = 0;
2874       else {
2875 	while (*ch==' ')
2876 	  ++ch;
2877 	int firstchar=*ch,lastchar = *ch,prevlast=0;
2878 	if (!python && lastchar ==';' && !*(ch+1))
2879 	  indent -= indentunit;
2880 	// Add spaces for each open (, open {, open [,
2881 	// remove spaces for ] } )
2882 	for (;*ch;++ch){
2883 	  switch (*ch){
2884 	  case '(': case '[': case '{':
2885 	    indent += indentunit;
2886 	    break;
2887 	  case ')': case ']': case '}':
2888 	    indent -=indentunit;
2889 	    break;
2890 	  }
2891 	  if (*ch=='/' && *(ch+1)=='/')
2892 	    break;
2893 	  if (*ch!=' '){
2894 	    prevlast=lastchar;
2895 	    lastchar=*ch;
2896 	  }
2897 	}
2898 	Lastchar=lastchar;
2899 	if (python){
2900 	  if (lastchar==':')
2901 	    indent += indentunit;
2902 	}
2903 	else {
2904 	  // Last non space should be { or ;
2905 	  if ( (lastchar=='{' && firstchar!='}') || (lastchar=='}' && firstchar!='}') || (lastchar==';' && prevlast!='}' ) )
2906 	    indent -=indentunit;
2907 	}
2908 	free(ch_);
2909       }
2910     }
2911     // Now indent line
2912     char * ch_ = buffer()->line_text(pos), *ch=ch_;
2913     int delta=0;
2914     for (;*ch;++ch,--delta){
2915       if (*ch!=' ')
2916 	break;
2917     }
2918     if (*ch=='}')
2919       indent -= indentunit;
2920     string mot;
2921     mot += *ch;
2922     for(char * ch1=ch+1;*ch1;++ch1){
2923       if (!isalpha(*ch1))
2924 	break;
2925       mot += *ch1;
2926     }
2927     if (Lastchar!='}' && (equalposcomp(motsclef,mot)))
2928       indent -=indentunit;
2929     indent=max(indent,0);
2930     delta += indent;
2931     string s(indent,' ');
2932     s += ch;
2933     free(ch_);
2934     int fin=buffer()->line_end(pos);
2935     buffer()->remove(debut_ligne,fin);
2936     buffer()->insert(debut_ligne,s.c_str());
2937     insert_position(pos+delta);
2938     redraw();
2939     return pos+delta;
2940   }
2941 
indent()2942   void Xcas_Text_Editor::indent(){
2943     int pos=0;
2944     for (;pos<buffer()->length();){
2945       pos=indent(pos);
2946       pos=buffer()->line_end(pos)+1;
2947     }
2948   }
2949 
value(const char * s,bool select)2950   void Xcas_Text_Editor::value(const char * s,bool select){
2951     if (buffer()->length()>0)
2952       buffer()->remove(0,buffer()->length());
2953     buffer()->insert(0,s);
2954     insert_position(0);
2955     if (select)
2956       buffer()->select(0,strlen(s));
2957     else
2958       buffer()->select(0,0);
2959     set_gchanged();
2960   }
2961 
position(int b,int e)2962   void Xcas_Text_Editor::position(int b,int e){
2963     insert_position(b);
2964     buffer()->select(b,e);
2965   }
2966 
resize_nl_before(unsigned nl)2967   void Xcas_Text_Editor::resize_nl_before(unsigned nl){
2968     if (tableur)
2969       return;
2970     /*
2971     Fl_Widget * wid=this;
2972     while (wid){
2973       if (dynamic_cast<Figure *>(wid))
2974 	break;
2975       wid=wid->parent();
2976     }
2977     */
2978     string s = value();
2979     unsigned i=0,l=s.size();
2980     for (;i<l;++i){
2981       if (s[i]=='\n')
2982 	++nl;
2983     }
2984     // increase_size(this,((nl>1 ||wid)?22:9)+nl*(labelsize()+3)-h());
2985     double fs = 1.05*fl_height(textfont(), textsize());
2986     int newsize=(nl>1?23:10)+int(nl*fs+.5);
2987 #ifdef WIN32
2988     newsize += 2;
2989 #endif
2990     if (nl==1){
2991       fl_font(FL_HELVETICA,labelsize());
2992       double taille=1.4*fl_width(s.c_str());
2993       // cerr << ch << " " << taille << " " << labelsize() << '\n';
2994       if (taille>w()) // make enough room for scrollbar
2995 	increase_size(this,25+labelsize()-h());
2996       else
2997 	increase_size(this,newsize-h());
2998     }
2999     else
3000       increase_size(this,newsize-h());
3001     // check_scrollbarsize();
3002     show_insert_position();
3003   }
3004 
set_tooltip()3005   void Xcas_Text_Editor::set_tooltip(){
3006     static string toolt;
3007     History_Pack * hp=get_history_pack(this);
3008     int pos=insert_position();
3009     int wbeg=buffer()->line_start(pos);
3010     string s(buffer()->text_range(wbeg,pos));
3011     s=motclef(s);
3012     unsigned k=0; // quick check : is s a number?
3013     for (;k<s.size();++k){
3014       if (s[k]!='.' && s[k]!='e' && s[k]!='-' && (s[k]<'0' || s[k]>'9') )
3015 	break;
3016     }
3017     if (s.size()>1 && k<s.size()){
3018       const aide & help=helpon(s,*giac::vector_aide_ptr(),giac::language(hp?hp->contextptr:0),giac::vector_aide_ptr()->size());
3019       toolt=writehelp(help,giac::language(hp?hp->contextptr:0));
3020       toolt += '\n';
3021       const char * ch=gettext("No help");
3022       if (toolt.size()>20 && toolt.substr(0,strlen(ch))!=ch)
3023 	toolt += gettext("Press F1 to copy examples or for interactive help");
3024       else {
3025 	if (s.size()<4){
3026 	  tooltip("");
3027 	  return;
3028 	}
3029 	toolt += gettext("Press F1 to open help index at ");
3030 	toolt += s;
3031       }
3032       tooltip(toolt.c_str());
3033       int hh=height(toolt.c_str(),Fl_Tooltip::size());
3034       Fl_Tooltip::enter_area(this,0,-hh,0,0,toolt.c_str());
3035     }
3036     else
3037       tooltip("");
3038   }
3039 
g()3040   gen Xcas_Text_Editor::g() {
3041     if (!gchanged)
3042       return _g;
3043     giac::context * contextptr=get_context(this);
3044     _g=giac::gen(value(),contextptr);
3045     clear_gchanged();
3046     return _g;
3047   }
3048 
completion()3049   int Xcas_Text_Editor::completion(){
3050     Editeur * ed =dynamic_cast<Editeur *>(parent());
3051     History_Pack * hp=get_history_pack(this);
3052     int pos=insert_position();
3053     if (pos)
3054       --pos;
3055 #ifdef FL_DEVICE
3056     char car=buffer()->character(pos);
3057 #else
3058     char car=buffer()->char_at(pos);
3059 #endif
3060     if (car=='\n'){
3061       ++pos;
3062 #ifdef FL_DEVICE
3063       car=buffer()->character(pos);
3064 #else
3065       car=buffer()->char_at(pos);
3066 #endif
3067     }
3068     bool remove1=false;
3069     if (pos && car=='('){
3070       --pos;
3071       remove1=true;
3072 #ifdef FL_DEVICE
3073       car=buffer()->character(pos);
3074 #else
3075       car=buffer()->char_at(pos);
3076 #endif
3077     }
3078     if (1 || isalphan(car)){
3079       int wbeg=buffer()->word_start(pos);
3080       int wend=buffer()->word_end(pos);
3081       if (remove1) ++wend;
3082       pos=wend;
3083       char * cha=buffer()->text_range(wbeg,wend);
3084       string s(cha),ans;
3085       free(cha);
3086       int remove;
3087       if (int ii=handle_tab(s,*giac::vector_completions_ptr(),window()->w(),window()->h()/3,remove,ans,/* immediate out not allowed*/false)){
3088 	window()->show();
3089 	Fl::focus(this);
3090 	handle(FL_FOCUS);
3091 	pos=wend-remove;
3092 	buffer()->remove(pos,wend);
3093 	if (ii==1){
3094 	  buffer()->insert(pos,(ans+"(").c_str());
3095 	  insert_position(pos+ans.size()+1);
3096 	}
3097 	else {
3098 	  buffer()->insert(pos,ans.c_str());
3099 	  insert_position(pos+ans.size());
3100 	}
3101 	set_gchanged();
3102 	if (hp)
3103 	  hp->modified(false);
3104 	set_tooltip();
3105 	if (parent())
3106 	  parent_redraw(parent());
3107       }
3108       return 1;
3109     }
3110     return 0;
3111   }
3112 
set_g(const gen & g)3113   void Xcas_Text_Editor::set_g(const gen & g){
3114     const giac::context * contextptr = get_context(this);
3115     value(g.print(contextptr).c_str());
3116     redraw();
3117     clear_gchanged();
3118     _g=g;
3119   }
3120 
check_scrollbarsize()3121   void Xcas_Text_Editor::check_scrollbarsize(){
3122     if (h()<25+labelsize()){
3123       char * ch=buffer()->text(),*ptr=ch;
3124       if (ch){
3125 	for (;*ptr;++ptr){
3126 	  if (*ptr=='\n')
3127 	    break;
3128 	}
3129 	if (!*ptr){
3130 	  fl_font(FL_HELVETICA,labelsize());
3131 	  double taille=1.4*fl_width(ch);
3132 	  // cerr << ch << " " << taille << " " << labelsize() << '\n';
3133 	  if (taille>w()){ // make enough room for scrollbar
3134 	    increase_size(this,25+labelsize()-h());
3135 	  }
3136 	}
3137 	free(ch);
3138       }
3139     }
3140   }
3141 
dedent()3142   void Xcas_Text_Editor::dedent(){
3143     // delete several characters
3144     int pos=insert_position();
3145     int debut_ligne=buffer()->line_start(pos),i;
3146     char * ch=buffer()->line_text(debut_ligne);
3147     string s(ch); free(ch);
3148     // skip spaces ahead
3149     int lpos=pos-debut_ligne;
3150     for (;lpos<s.size();++lpos){
3151       if (s[lpos]!=' ')
3152 	break;
3153     }
3154     if (lpos<s.size()){
3155       insert_position(lpos+debut_ligne);
3156       pos=insert_position();
3157     }
3158     int curind=pos-debut_ligne;
3159     for (i=0;i<curind;++i){
3160       if (s[i]!=' ')
3161 	break;
3162     }
3163     if (curind && i==curind){
3164       while (debut_ligne>1){
3165 	// search position in previous line indentations
3166 	pos=debut_ligne-1;
3167 	debut_ligne=buffer()->line_start(pos);
3168 	ch=buffer()->line_text(debut_ligne);
3169 	string s(ch); free(ch);
3170 	for (i=0;i<curind;++i){
3171 	  if (s[i]!=' ')
3172 	    break;
3173 	}
3174 	if (i<curind){
3175 	  // std::cerr << "fixme del" << '\n';
3176 	  for (;curind>i;--curind)
3177 	    Fl_Text_Editor::kf_backspace(0,this);
3178 	  break;
3179 	}
3180       }
3181     }
3182   }
3183 
handle(int event)3184   int Xcas_Text_Editor::handle(int event){
3185     if (event==FL_UNFOCUS)
3186       return 1;
3187     if (event==FL_FOCUS || event==FL_PUSH)
3188       Xcas_input_focus=this;
3189     if (event==FL_PUSH && tableur)
3190       tableur->editing=true;
3191     Editeur * ed =dynamic_cast<Editeur *>(parent());
3192     if (event==FL_MOUSEWHEEL){
3193       if (!Fl::event_inside(this))
3194 	return 0;
3195       int n=buffer()->count_lines(0,buffer()->length())+1;
3196       if (n*(labelsize()+1)<=h()+10)
3197 	return 0;
3198       int l=buffer()->count_lines(0,insert_position())+1;
3199       if (Fl::e_dy<0){
3200 	for (;l>n-h()/(2*labelsize());--l)
3201 	  move_up();
3202 	if (l<=1)
3203 	  return 0;
3204 	move_up();
3205       }
3206       else {
3207 	for (;l<h()/(2*labelsize());++l)
3208 	  move_down();
3209 	if (l>=n)
3210 	  return 0;
3211 	move_down();
3212       }
3213       show_cursor();
3214       show_insert_position();
3215     }
3216     if (ed){
3217       if (ed->linenumber){
3218 	int n=buffer()->count_lines(0,insert_position())+1;
3219 	ed->linenumber->value(n);
3220       }
3221     }
3222     if (event==FL_MOUSEWHEEL)
3223       return 1;// Fl_Text_Editor::handle(event);
3224     giac::context * contextptr = get_context(this);
3225     History_Pack * hp=get_history_pack(this);
3226     if (h()<25+labelsize() && !ed && !tableur)
3227       check_scrollbarsize();
3228     if (event==FL_KEYBOARD){
3229       Xcas_input_focus=this;
3230       // FIXME: add 2 ([]) and 12 ({})
3231       if (Fl::event_text() && Fl::event_text()[0]==6){
3232 	cb_Editeur_Search(this,0);
3233 	return 1;
3234       }
3235       if (Fl::event_text() && Fl::event_text()[0]==14){
3236 	cb_Editeur_Next(this,0);
3237 	return 1;
3238       }
3239       if (!tableur && Fl::event_key()==FL_F+5){
3240 	if (h()<window()->h()){
3241 	  increase_size(this,100);
3242 	  redraw();
3243 	  return 1;
3244 	}
3245       }
3246       if (!tableur && Fl::event_key()==FL_F+6){
3247 	if (h()>200)
3248 	  increase_size(this,-100);
3249 	return 1;
3250       }
3251       int change_focus=0;
3252       if (!ed && Fl::event_text() && (Fl::event_text()[0]=='\r') && !Fl::event_state(FL_SHIFT | FL_CTRL | FL_ALT) ){
3253 	if (tableur){
3254 	  do_callback();
3255 	  return 1;
3256 	}
3257 	else {
3258 	  if (value().empty())
3259 	    change_focus=1;
3260 	  else {
3261 	    history.push_back(value());
3262 	    count=history.size();
3263 	    int pos=-1;
3264 	    History_Pack * hp=get_history_pack(this,pos);
3265 	    if (hp)
3266 	      hp->update_pos=pos;
3267 	    History_Pack_cb_eval(this,0);
3268 	    return 1;
3269 	  }
3270 	}
3271       }
3272       if (Fl::event_key()==FL_F+1 || (!ed && Fl::event_text() && (Fl::event_text()[0]==9)) ){
3273 	if (completion())
3274 	  return 1;
3275       }
3276       if (Fl::event_text() && (Fl::event_text()[0]==9 || Fl::event_key()==FL_Escape)){
3277 	if (tableur){
3278 	  if (tableur->editing){
3279 	    tableur->editing=false;
3280 	    Fl::focus(tableur);
3281 	  }
3282 	  else {
3283 	    Fl::selection(*this,value().c_str(),value().size());
3284 	    // Fl::selection(*this,value(),strlen(value()));
3285 	    value("");
3286 	  }
3287 	  set_gchanged();
3288 	  if (hp)
3289 	    hp->modified(false);
3290 	  return 1;
3291 	}
3292 	else {
3293 	  int pos=insert_position();
3294 	  indent(pos);
3295 	  return 1;
3296 	}
3297       } // end if (...) tabulation test
3298       int key=Fl::event_key();
3299       if (!tableur && buffer()->line_start(insert_position())==0 &&
3300 	  ((key==FL_Up && !Fl::event_state(FL_SHIFT |FL_CTRL | FL_ALT))
3301 	   || key==FL_Page_Up)
3302 	  )
3303 	change_focus=-1;
3304       if (!tableur && buffer()->line_end(insert_position())==buffer()->length() &&
3305 	  ( (key==FL_Down  && !Fl::event_state(FL_SHIFT |FL_CTRL | FL_ALT))
3306 	    || key==FL_Page_Down)
3307 	  )
3308 	change_focus=1;
3309       if (change_focus && hp){
3310 	int pos=hp->focus(this);
3311 	if (pos+change_focus>=0 && pos+change_focus<hp->children()){
3312 	  hp->focus(pos+change_focus,true);
3313 	  return 1;
3314 	}
3315       }
3316     } // end if event==FL_KEYBOARD
3317     if (event==FL_KEYBOARD && !ed && !tableur){
3318       if (Fl::event_text() && (Fl::event_text()[0]=='\n' || Fl::event_text()[0]=='\r')){
3319 	resize_nl_before(2);
3320       }
3321     }
3322     if (event==FL_KEYBOARD && !ed && Fl::event_state(FL_CTRL | FL_ALT) && (Fl::event_key()==FL_Up || Fl::event_key()==FL_Down)){
3323       // not handled by the input, use history
3324       int s=history.size();
3325       Fl::focus(this);
3326       int start, end;
3327       if (count==s && buffer()->selection_position(&start,&end) && start>=0 && end>start){
3328 	char * ch=buffer()->text_range(start,end);
3329 	string S(ch);
3330 	free(ch);
3331 	if (history.empty() || S!=history.back()){
3332 	  history.push_back(S);
3333 	  ++s;
3334 	}
3335       }
3336       if (Fl::event_key()==FL_Up){
3337 	--count;
3338 	if (count<0)
3339 	  count=0;
3340       }
3341       else {
3342 	++count;
3343 	if (count>=s)
3344 	  count=max(0,s-1);
3345       }
3346       if (count<s){
3347 	if (buffer()->selection_position(&start,&end) && start>=0 && end>start)
3348 	  buffer()->remove(start,end);
3349 	else
3350 	  start=insert_position();
3351 	buffer()->insert(start,history[count].c_str());
3352 	end = start + history[count].size();
3353 	buffer()->select(start,end);
3354 	set_gchanged();
3355 	if (hp)
3356 	  hp->modified(false);
3357       }
3358       return 1;
3359     }
3360     char before_ch=buffer()->text()[giacmax(insert_position()-1,0)];
3361     int res=Fl_Text_Editor::handle(event);
3362     if (!ed && event==FL_PASTE)
3363       resize_nl_before(1);
3364     if (event==FL_KEYBOARD){
3365 #if 1 //ndef __APPLE__
3366       if (Fl::event_text() && Fl::event_text()[0]>32){
3367 	count=history.size();
3368 	set_gchanged();
3369 	if (hp)
3370 	  hp->modified(false);
3371 	if (Fl::event_text()[0]==')')
3372 	  tooltip("");
3373 	else
3374 	  set_tooltip();
3375       }
3376 #endif
3377       if (!ed && !tableur && Fl::event_key()==FL_BackSpace){
3378 	resize_nl_before(1);
3379 	if (hp)
3380 	  hp->modified(true);
3381       }
3382       if (!tableur && Fl::event_key()==FL_BackSpace && python_compat(contextptr) && !isalnum(before_ch)){
3383 	dedent();
3384       }
3385       if (tableur && tableur->editing && Fl::event_key()==FL_BackSpace && value().empty()){
3386 	tableur->editing=false;
3387 	Fl::focus(tableur);
3388 	return 1;
3389       }
3390       if (ed && !tableur && Fl::event_text() && (Fl::event_text()[0]=='\n' || Fl::event_text()[0]=='\r')){
3391 	indent(insert_position());
3392       }
3393       else
3394 	match();
3395     }
3396     return res;
3397   }
3398 
3399 #ifndef NO_NAMESPACE_XCAS
3400 } // namespace giac
3401 #endif // ndef NO_NAMESPACE_XCAS
3402 
3403 #endif // HAVE_LIBFLTK
3404