// -*- mode:C++ ; compile-command: "g++ -I.. -g -c Eqw.cc" -*-
#include "first.h"
/*
 *  Copyright (C) 2003 B. Parisse, Institut Fourier, 38402 St Martin d'Heres
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
#include "global.h"

#ifdef HAVE_LIBFLTK
#include <FL/Fl.h>
#include <FL/fl_ask.h>
#include <FL/Fl_Output.h>
#include "Eqw.h"
#include "prog.h"
#include "rpn.h"
#include "identificateur.h"
#include "subst.h"
#include "symbolic.h"
#include <fstream>
#include <vector>
#include <algorithm>
#include <fcntl.h>
#include <cmath>
#include <time.h> // for nanosleep
#include "usual.h"
#include "derive.h"
#include "solve.h"
#include "series.h"
#include "intg.h"
#include "misc.h"
#include "sheet.h"
#include "path.h"
#include "ti89.h"
#include "plot.h"
#include "plotfltk.h"
#include "tex.h"
#include "xcas.h"
#include <FL/Fl_Button.h>
#include <FL/Fl_Return_Button.h>
#include <FL/Fl_Browser.h>
#ifndef __APPLE__
#include <FL/Fl_PNG_Image.h>
#endif

using namespace std;

#ifndef NO_NAMESPACE_GIAC
namespace giac {
#endif // ndef NO_NAMESPACE_GIAC

  vector<string> completion_tab;
  bool pretty_input=true;

  int Eqw_binary_search_pos(const eqwdata & e,int x,int y){
    int ss=e.g._STRNGptr->size();
    int debut=0,fin=ss,pos,l=0;
    fl_font(FL_HELVETICA,e.eqw_attributs.fontsize);
    for (;debut+1<fin;){
      pos=(debut+fin)/2;
      l=int(fl_width(e.g._STRNGptr->substr(0,pos).c_str()));
      if (e.x+l<x)
	debut=pos;
      else
	fin=pos;
    }
    int l2=int(fl_width(e.g._STRNGptr->substr(0,fin).c_str()));
    x=x-e.x;
    if (x-l>l2-x)
      return fin;
    else
      return debut;
  }

  bool Eqw_multistring_selection(const_iterateur & it,const_iterateur & itend,bool search_active){
    for (;it!=itend;++it){ // find first selection
      if (it->type==_EQW){
	if (search_active){
	  if (it->_EQWptr->active)
	    break;
	}
	else {
	  if (it->_EQWptr->selected)
	    break;
	}
      }
    }
    if (it==itend)
      return false; // nothing selected
    const_iterateur it_end_sel=it+1;
    for (;it_end_sel!=itend;++it_end_sel){ // find last selection
      if (it_end_sel->type==_EQW){ 
	if (search_active){
	  if (!it_end_sel->_EQWptr->active)
	    break;
	}
	else {
	  if (!it_end_sel->_EQWptr->selected)
	    break;
	}
      }
    }
    itend=it_end_sel-1;
    return true;
  }

  eqwdata Eqw_total_size(const gen & g){
    if (g.type==_EQW)
      return *g._EQWptr;
    if (g.type!=_VECT || g._VECTptr->empty())
      return eqwdata(0,0,0,0,attributs(0,0,0),undef);
    return Eqw_total_size(g._VECTptr->back());
  }

  gen Eqw_translate(const gen & g,int deltax,int deltay){
    if (g.type==_EQW){
      gen res(*g._EQWptr);
      res._EQWptr->x += deltax;
      res._EQWptr->y += deltay;
      res._EQWptr->baseline += deltay;
      return res;
    }
    if (g.type!=_VECT)
      setsizeerr();
    vecteur v=*g._VECTptr;
    iterateur it=v.begin(),itend=v.end();
    for (;it!=itend;++it)
      *it=Eqw_translate(*it,deltax,deltay);
    return gen(v,g.subtype);
  }

  gen Eqw_change_attributs(const gen & g,const attributs & newa){
    if (g.type==_EQW){
      gen res(*g._EQWptr);
      res._EQWptr->eqw_attributs = newa;
      return res;
    }
    if (g.type!=_VECT)
      setsizeerr();
    vecteur v=*g._VECTptr;
    iterateur it=v.begin(),itend=v.end();
    for (;it!=itend;++it)
      *it=Eqw_change_attributs(*it,newa);
    return gen(v,g.subtype);
  }

  vecteur Eqw_subsizes(const gen & arg,const attributs & a,int windowhsize){
    vecteur v;
    if ( (arg.type==_VECT) && ( (arg.subtype==_SEQ__VECT) 
				// || (!ckmatrix(arg)) 
				) ){
      const_iterateur it=arg._VECTptr->begin(),itend=arg._VECTptr->end();
      for (;it!=itend;++it)
	v.push_back(Eqw_compute_size(*it,a,windowhsize));
    }
    else {
      v.push_back(Eqw_compute_size(arg,a,windowhsize));
    }
    return v;
  }

  // vertical merge with same baseline
  // for vertical merge of hp,yp at top (like ^) add fontsize to yp
  // at bottom (like lower bound of int) substract fontsize from yp
  void Eqw_vertical_adjust(int hp,int yp,int & h,int & y){
    int yf=min(y,yp);
    h=max(y+h,yp+hp)-yf;
    y=yf;
  }

  // utility for gen embedded widget memory allocation
  void fltk_fl_widget_delete_function(void * ptr){
    Fl_Widget * w=(Fl_Widget *) ptr;
    /* not attached to any group
    if (w->parent()){
      Fl_Group * wg=dynamic_cast<Fl_Group *>(w->parent());
      if (wg)
	wg->remove(w);
    }
    */
    delete w;
  }

  gen Eqw_compute_symb_size(const gen & g,const attributs & a,int windowhsize){
    if (g.type!=_SYMB)
      return Eqw_compute_size(g,a,windowhsize);
    unary_function_ptr & u=g._SYMBptr->sommet;
    gen arg=g._SYMBptr->feuille;
    if (u==at_multistring){
      gen tmp=_multistring(arg);
      tmp.subtype=1;
      return Eqw_compute_size(tmp,a,windowhsize);
    }
    if (u==at_makevector){
      vecteur v(1,arg);
      if (arg.type==_VECT)
	v=*arg._VECTptr;
      iterateur it=v.begin(),itend=v.end();
      for (;it!=itend;++it){
	if ( (it->type==_SYMB) && (it->_SYMBptr->sommet==at_makevector) )
	  *it=_makevector(it->_SYMBptr->feuille);
      }
      return Eqw_compute_size(v,a,windowhsize);
    }
    if (u==at_makesuite){
      if (arg.type==_VECT)
	return Eqw_compute_size(gen(*arg._VECTptr,_SEQ__VECT),a,windowhsize);
      else
	return Eqw_compute_size(arg,a,windowhsize);
    }
    if (u==at_sqrt)
      return Eqw_compute_size(symbolic(at_pow,makevecteur(arg,plus_one_half)),a,windowhsize);
    if (u==at_division){
      if (arg.type!=_VECT || arg._VECTptr->size()!=2)
	return Eqw_compute_size(arg,a,windowhsize);
      gen tmp;
      tmp.type=_FRAC;
      tmp._FRACptr = new fraction(arg._VECTptr->front(),arg._VECTptr->back());
      tmp.ref_count = new int(1);
      return Eqw_compute_size(tmp,a,windowhsize);
    }
    if (u==at_prod){
      if (arg.type==_VECT && !arg._VECTptr->empty() && arg._VECTptr->back().is_symb_of_sommet(at_inv)) {
	vecteur & uv=*arg._VECTptr;
	int tmps=uv.size(),invbegin;
	gen n,d;
	vecteur den(1,uv.back()._SYMBptr->feuille);
	// group all inv from the end to the beginning for the denominator
	for (invbegin=tmps-2;invbegin>=0;--invbegin){
	  if (!uv[invbegin].is_symb_of_sommet(at_inv))
	    break;
	  den.push_back(uv[invbegin]._SYMBptr->feuille);
	}
	if (den.size()>1)
	  d=symbolic(at_prod,den);
	else
	  d=den.front();
	if (invbegin==-1)
	  n=plus_one;
	else {
	  if (invbegin==0)
	    n=uv.front();
	  else
	    n=symbolic(at_prod,vecteur(uv.begin(),uv.begin()+invbegin+1));
	}
	return Eqw_compute_size(fraction(n,d),a,windowhsize);
      }
      /* Commented since it does not work well with product of matrices and inv
      vecteur num,den;
      prod2frac(g,num,den);
      if (!den.empty()){
	gen n,d;
	if (num.empty())
	  n=plus_one;
	else {
	  if (num.size()==1)
	    n=num.front();
	  else
	    n=symbolic(at_prod,num);
	}
	if (den.size()==1)
	  d=den.front();
	else
	  d=symbolic(at_prod,den);
	return Eqw_compute_size(fraction(n,d),a,windowhsize);
      }
      */
    }
    if (u==at_inv)
      return Eqw_compute_size(fraction(plus_one,arg),a,windowhsize);
    if (u==at_expr && arg.type==_VECT && arg.subtype==_SEQ__VECT && arg._VECTptr->size()==2 && arg._VECTptr->back().type==_INT_){
      gen varg1=Eqw_compute_size(arg._VECTptr->front(),a,windowhsize);
      eqwdata vv(Eqw_total_size(varg1));
      gen varg2=eqwdata(0,0,0,0,a,arg._VECTptr->back());
      vecteur v12(makevecteur(varg1,varg2));
      v12.push_back(eqwdata(vv.dx,vv.dy,0,vv.y,a,at_expr,0));
      return gen(v12,_SEQ__VECT);
    }
    int llp=int(fl_width("("));
    int lrp=int(fl_width(")"));
    int lc=int(fl_width(","));
    int ls=int(fl_width(u.ptr->s.c_str()));
    if (isalpha(u.ptr->s[0]))
      ls += 2;
    // special cases first int, sigma, /, ^
    // and if printed as printsommetasoperator
    // otherwise print with usual functional notation
    int x=0;
    int h=a.fontsize;
    int y=0;
    if ((u==at_integrate) || (u==at_sum) ){ // Int
      int s=1;
      if (arg.type==_VECT)
	s=arg._VECTptr->size();
      else
	arg=vecteur(1,arg);
      // s==1 -> general case
      if ( (s==1) || (s==2) ){ // int f(x) dx and sum f(n) n
	vecteur v(Eqw_subsizes(gen(*arg._VECTptr,_SEQ__VECT),a,windowhsize));
	eqwdata vv(Eqw_total_size(v[0]));
	if (s==1){
	  x=a.fontsize;
	  v[0]=Eqw_translate(v[0],x,0);
	  x += int(fl_width(" dx"));
	}
	if (s==2){
	  if (u==at_integrate){
	    x=a.fontsize;
	    v[0]=Eqw_translate(v[0],x,0);
	    x += vv.dx+int(fl_width(" d"));
	    Eqw_vertical_adjust(vv.dy,vv.y,h,y);
	    vv=Eqw_total_size(v[1]);
	    v[1]=Eqw_translate(v[1],x,0);
	    Eqw_vertical_adjust(vv.dy,vv.y,h,y);
	  }
	  else {
	    Eqw_vertical_adjust(vv.dy,vv.y,h,y);
	    eqwdata v1=Eqw_total_size(v[1]);
	    x=max(a.fontsize,v1.dx)+a.fontsize/3; // var name size
	    v[1]=Eqw_translate(v[1],0,-v1.dy-v1.y);
	    Eqw_vertical_adjust(v1.dy,-v1.dy,h,y);
	    v[0]=Eqw_translate(v[0],x,0);
	    x += vv.dx; // add function size
	  }
	}
	if (u==at_integrate){
	  x += vv.dx;
	  if (h==a.fontsize)
	    h+=2*a.fontsize/3;
	  if (y==0){
	    y=-2*a.fontsize/3;
	    h+=2*a.fontsize/3;
	  }
	}
	v.push_back(eqwdata(x,h,0,y,a,u,0));
	return gen(v,_SEQ__VECT);
      }
      if (s>=3){ // int _a^b f(x) dx
	vecteur & intarg=*arg._VECTptr;
	gen tmp_l,tmp_u,tmp_f,tmp_x;
	attributs aa(a);
	if (a.fontsize>=10)
	  aa.fontsize -= 2;
	tmp_f=Eqw_compute_size(intarg[0],a,windowhsize);
	tmp_x=Eqw_compute_size(intarg[1],a,windowhsize);
	tmp_l=Eqw_compute_size(intarg[2],aa,windowhsize);
	if (s==4)
	  tmp_u=Eqw_compute_size(intarg[3],aa,windowhsize);
	x=a.fontsize;
	eqwdata vv(Eqw_total_size(tmp_l));
	tmp_l=Eqw_translate(tmp_l,x-2,-vv.y-vv.dy);
	vv=Eqw_total_size(tmp_l);
	Eqw_vertical_adjust(vv.dy,vv.y,h,y);
	int lx = vv.dx;
	if (s==4){
	  vv=Eqw_total_size(tmp_u);
	  tmp_u=Eqw_translate(tmp_u,x,a.fontsize-3-vv.y);
	  vv=Eqw_total_size(tmp_u);
	  Eqw_vertical_adjust(vv.dy,vv.y,h,y);
	}
	x += max(lx,vv.dx);
	tmp_f=Eqw_translate(tmp_f,x,0);
	vv=Eqw_total_size(tmp_f);
	Eqw_vertical_adjust(vv.dy,vv.y,h,y);
	if (u==at_integrate){
	  x += vv.dx+int(fl_width(" d"));
	  tmp_x=Eqw_translate(tmp_x,x,0);
	  vv=Eqw_total_size(tmp_x);
	  Eqw_vertical_adjust(vv.dy,vv.y,h,y);
	  x += vv.dx;
	}
	else {
	  x += vv.dx;
	  Eqw_vertical_adjust(vv.dy,vv.y,h,y);
	  vv=Eqw_total_size(tmp_x);
	  x=max(x,vv.dx)+a.fontsize/3;
	  tmp_x=Eqw_translate(tmp_x,0,-vv.dy-vv.y);
	  Eqw_vertical_adjust(vv.dy,-vv.dy,h,y);
	}
	vecteur res(makevecteur(tmp_f,tmp_x,tmp_l));
	if (s==4)
	  res.push_back(tmp_u);
	res.push_back(eqwdata(x,h,0,y,a,u,0));
	return gen(res,_SEQ__VECT);
      }
    }
    if (u==at_limit && arg.type==_VECT){ // limit
      vecteur & limarg=*arg._VECTptr;
      int s=limarg.size();
      if (s>=3){
	gen tmp_l,tmp_f,tmp_x,tmp_dir;
	attributs aa(a);
	if (a.fontsize>=10)
	  aa.fontsize -= 2;
	tmp_f=Eqw_compute_size(limarg[0],a,windowhsize);
	tmp_x=Eqw_compute_size(limarg[1],aa,windowhsize);
	tmp_l=Eqw_compute_size(limarg[2],aa,windowhsize);
	if (s==4)
	  tmp_dir=Eqw_compute_size(limarg[3],aa,windowhsize);;
	eqwdata vf(Eqw_total_size(tmp_f));
	eqwdata vx(Eqw_total_size(tmp_x));
	eqwdata vl(Eqw_total_size(tmp_l));
	eqwdata vdir(Eqw_total_size(tmp_dir));
	int sous=max(vx.dy,vl.dy);
	if (s==4)
	  tmp_f=Eqw_translate(tmp_f,vx.dx+vl.dx+vdir.dx+a.fontsize+4,0);
	else
	  tmp_f=Eqw_translate(tmp_f,vx.dx+vl.dx+a.fontsize+2,0);
	tmp_x=Eqw_translate(tmp_x,0,-sous-vl.y);
	tmp_l=Eqw_translate(tmp_l,vx.dx+a.fontsize+2,-sous-vl.y);
	if (s==4)
	  tmp_dir=Eqw_translate(tmp_dir,vx.dx+vl.dx+a.fontsize+4,-sous-vl.y);
	h=vf.dy;
	y=vf.y;
	vl=Eqw_total_size(tmp_l);
	Eqw_vertical_adjust(vl.dy,vl.y,h,y);
	vecteur res(makevecteur(tmp_f,tmp_x,tmp_l));
	if (s==4){
	  res.push_back(tmp_dir);
	  res.push_back(eqwdata(vf.dx+vx.dx+a.fontsize+4+vl.dx+vdir.dx,h,0,y,a,u,0));
	}
	else
	  res.push_back(eqwdata(vf.dx+vx.dx+a.fontsize+2+vl.dx,h,0,y,a,u,0));
	return gen(res,_SEQ__VECT);
      }
    }
    if ( (u==at_of || u==at_at) && arg.type==_VECT && arg._VECTptr->size()==2 ){
      // user function, function in 1st arg, arguments in 2nd arg
      gen varg1=Eqw_compute_size(arg._VECTptr->front(),a,windowhsize);
      eqwdata vv=Eqw_total_size(varg1);
      Eqw_vertical_adjust(vv.dy,vv.y,h,y);
      gen arg2=arg._VECTptr->back();
      /* if (u==at_at && maple_mode!=0)
	 arg2=arg2+plus_one; */
      gen varg2=Eqw_compute_size(arg2,a,windowhsize);
      varg2=Eqw_translate(varg2,vv.dx+llp,0);
      vv=Eqw_total_size(varg2);
      Eqw_vertical_adjust(vv.dy,vv.y,h,y);
      vecteur res(makevecteur(varg1,varg2));
      res.push_back(eqwdata(vv.dx+vv.x+lrp,h,0,y,a,u,0));
      return gen(res,_SEQ__VECT);
    }
    if (u==at_pow){ 
      // first arg not translated
      gen varg=Eqw_compute_size(arg._VECTptr->front(),a,windowhsize);
      eqwdata vv=Eqw_total_size(varg);
      // 1/2 ->sqrt, otherwise as exponent
      if (arg._VECTptr->back()==plus_one_half){
	varg=Eqw_translate(varg,a.fontsize,0);
	vecteur res(1,varg);
	res.push_back(eqwdata(vv.dx+a.fontsize,vv.dy+4,vv.x,vv.y,a,at_sqrt,0));
	return gen(res,_SEQ__VECT);
      }
      if (vv.g.type==_FUNC)
	x=llp;
      varg=Eqw_translate(varg,x,0);
      Eqw_vertical_adjust(vv.dy,vv.y,h,y);
      vecteur res(1,varg);
      // 2nd arg translated 
      if (vv.g.type==_FUNC)
	x+=vv.dx+lrp;
      else
	x+=vv.dx+1;
      if (a.fontsize>=10){
	attributs aa(a);
	aa.fontsize -= 2;
	varg=Eqw_compute_size(arg._VECTptr->back(),aa,windowhsize);
      }
      else
	varg=Eqw_compute_size(arg._VECTptr->back(),a,windowhsize);
      vv=Eqw_total_size(varg);
      varg=Eqw_translate(varg,x,2*(a.fontsize/3)-vv.y);
      res.push_back(varg);
      vv=Eqw_total_size(varg);
      Eqw_vertical_adjust(vv.dy,vv.y,h,y);
      x += vv.dx;
      res.push_back(eqwdata(x,h,0,y,a,u,0));
      return gen(res,_SEQ__VECT);
    }
    if (u==at_sto){ // A:=B, *it -> B
      gen varg=Eqw_compute_size(arg._VECTptr->back(),a,windowhsize);
      eqwdata vv=Eqw_total_size(varg);
      Eqw_vertical_adjust(vv.dy,vv.y,h,y);
      varg=Eqw_translate(varg,x,0);
      vecteur v(2);
      v[1]=varg;
      x+=vv.dx;
      x+=ls+3;
      // first arg not translated
      varg=Eqw_compute_size(arg._VECTptr->front(),a,windowhsize);
      vv=Eqw_total_size(varg);
      if (need_parenthesis(vv.g))
	x+=llp;
      varg=Eqw_translate(varg,x,0);
      Eqw_vertical_adjust(vv.dy,vv.y,h,y);
      v[0]=varg;
      x += vv.dx;
      if (need_parenthesis(vv.g.type))
	x+=lrp;
      v.push_back(eqwdata(x,h,0,y,a,u,0));
      return gen(v,_SEQ__VECT);
    }
    if (u==at_program && arg._VECTptr->back().type!=_VECT && !arg._VECTptr->back().is_symb_of_sommet(at_local) ){
      gen varg=Eqw_compute_size(arg._VECTptr->front(),a,windowhsize);
      eqwdata vv=Eqw_total_size(varg);
      Eqw_vertical_adjust(vv.dy,vv.y,h,y);
      varg=Eqw_translate(varg,x,0);
      vecteur v(2);
      v[0]=varg;
      x+=vv.dx;
      x+=int(fl_width("->"))+3;
      varg=Eqw_compute_size(arg._VECTptr->back(),a,windowhsize);
      vv=Eqw_total_size(varg);
      varg=Eqw_translate(varg,x,0);
      Eqw_vertical_adjust(vv.dy,vv.y,h,y);
      v[1]=varg;
      x += vv.dx;
      v.push_back(eqwdata(x,h,0,y,a,u,0));
      return gen(v,_SEQ__VECT);      
    }
    bool binaryop= (u.ptr->printsommet==&printsommetasoperator) || equalposcomp(binary_op_tab,u);
    if ( u!=at_sto && u.ptr->printsommet!=NULL && !binaryop ){
      gen tmp=string2gen(g.print(),false);
      return Eqw_compute_size(symbolic(at_expr,gen(makevecteur(tmp,maple_mode),_SEQ__VECT)),a,windowhsize);
    }
     vecteur v;
     if (!binaryop || arg.type!=_VECT)
       v=Eqw_subsizes(arg,a,windowhsize);
     else
       v=Eqw_subsizes(gen(*arg._VECTptr,_SEQ__VECT),a,windowhsize);
    iterateur it=v.begin(),itend=v.end();
    if ( it==itend || (itend-it==1) ){ 
      gen gtmp;
      if (it==itend)
	gtmp=Eqw_compute_size(gen(vecteur(0),_SEQ__VECT),a,windowhsize);
      else
	gtmp=*it;
      // unary op, shift arg position horizontally
      x=ls+llp;
      eqwdata vv=Eqw_total_size(gtmp);
      gen tmp=Eqw_translate(gtmp,x,0);
      x=x+vv.dx+lrp;
      Eqw_vertical_adjust(vv.dy,vv.y,h,y);
      return gen(makevecteur(tmp,eqwdata(x,h,0,y,a,u,0)),_EQW__VECT);
    }
    if (binaryop){ // op (default with par)
      int currenth=h,largeur=0;
      iterateur itprec=v.begin();
      h=0;
      if (u==at_plus){ // op without parenthesis
	llp=0;
	lrp=0;
      }
      for (;;){
	eqwdata vv=Eqw_total_size(*it);
	if (need_parenthesis(vv.g))
	  x+=llp;
	if (u==at_plus && it!=v.begin() &&
	    ( 
	     (it->type==_VECT && it->_VECTptr->back().type==_EQW && it->_VECTptr->back()._EQWptr->g==at_neg) 
	    || 
	     ( it->type==_EQW && is_integer(it->_EQWptr->g) && is_strictly_positive(-it->_EQWptr->g,0) ) 
	     ) 
	    )
	  x -= ls;
	if (x>windowhsize-vv.dx && x>windowhsize/2){
	  largeur=max(x,largeur);
	  x=0;
	  if (need_parenthesis(vv.g))
	    x+=llp;
	  h+=currenth;
	  *it=Eqw_translate(*it,x,0);
	  for (iterateur kt=v.begin();kt!=itprec;++kt)
	    *kt=Eqw_translate(*kt,0,currenth);
	  if (y){
	    for (iterateur kt=itprec;kt!=it;++kt)
	      *kt=Eqw_translate(*kt,0,-y);
	  }
	  itprec=it;
	  currenth=vv.dy;
	  y=vv.y;
	}
	else {
	  *it=Eqw_translate(*it,x,0);
	  vv=Eqw_total_size(*it);
	  Eqw_vertical_adjust(vv.dy,vv.y,currenth,y);
	}
	x+=vv.dx;
	if (need_parenthesis(vv.g))
	  x+=lrp;
	++it;
	if (it==itend){
	  for (iterateur kt=v.begin();kt!=itprec;++kt)
	    *kt=Eqw_translate(*kt,0,currenth+y);
	  h+=currenth;
	  v.push_back(eqwdata(max(x,largeur),h,0,y,a,u,0));
	  // cerr << v << endl;
	  return gen(v,_SEQ__VECT);
	}
	x += ls+3;
      } 
    }
    // normal printing
    x=ls+llp;
    for (;;){
      eqwdata vv=Eqw_total_size(*it);
      *it=Eqw_translate(*it,x,0);
      Eqw_vertical_adjust(vv.dy,vv.y,h,y);
      x+=vv.dx;
      ++it;
      if (it==itend){
	x+=lrp;
	v.push_back(eqwdata(x,h,0,y,a,u,0));
	return gen(v,_SEQ__VECT);
      }
      x+=lc;
    }
  }

  // windowhsize is used for g of type HIST_EQW (history) right justify answers
  // Returns either a eqwdata type object (terminal) or a vector 
  // (of subtype _EQW__VECT or _HIST__VECT)
  gen Eqw_compute_size(const gen & g,const attributs & a,int windowhsize){
    fl_font(FL_HELVETICA,a.fontsize);
    /**************
     * FL_WIDGET  *
     **************/
    if (g.type==_POINTER_) {
      if (g.subtype==_FL_WIDGET_POINTER){
	Fl_Widget * wg=(Fl_Widget *) g._POINTER_val;
	return eqwdata(wg->w(),wg->h(),0,0,a,g);
      }
#ifndef __APPLE__
      if (g.subtype==_FL_IMAGE_POINTER){
	Fl_Image * ptr=(Fl_Image *) g._POINTER_val;
	return eqwdata(ptr->w(),ptr->h(),0,0,a,g);
      }
#endif
    }
    /***************
     *   STRINGS   *
     ***************/
    if (g.type==_STRNG){
      string s;
      s=*g._STRNGptr;
      string cs;
      int ss=s.size();
      /* if (!ss)
	 return eqwdata(10,6,0,0,a,g); */
      int hsize=0,vsize=0;
      bool newline=false;
      vecteur res;
      gen tmps;
      for (int pos=0;pos<ss;++pos){
	char ch=s[pos];
	if (ch=='\n'){
	  newline=true;
	  hsize=max(hsize,int(fl_width((' '+cs).c_str())));
	  tmps=string2gen(cs,false);
	  vsize+=a.fontsize;
	  res.push_back(eqwdata(hsize,a.fontsize,0,-vsize,a,tmps));
	  cs="";
	}
	else
	  cs += ch;
      }
      hsize=max(hsize,int(fl_width((' '+cs).c_str())));
      vsize+=a.fontsize;
      tmps=string2gen(cs,false);
      if (!newline){
	tmps.subtype=g.subtype;
	return eqwdata(hsize,a.fontsize,0,0,a,tmps);
      }
      gen tmp=eqwdata(hsize,a.fontsize,0,-vsize,a,tmps);
      res.push_back(tmp);
      res.push_back(eqwdata(hsize,vsize,0,-vsize,a,at_multistring));
      tmp= gen(res,_EQW__VECT);
      tmp=Eqw_translate(tmp,0,vsize);
      return tmp;
    }
    /*****************
     *   FRACTIONS   *
     *****************/
    if (g.type==_FRAC){
      gen v1=Eqw_compute_size(g._FRACptr->num,a,windowhsize);
      eqwdata vv1=Eqw_total_size(v1);
      gen v2=Eqw_compute_size(g._FRACptr->den,a,windowhsize);
      eqwdata vv2=Eqw_total_size(v2);
      // Center the fraction
      int w1=vv1.dx,w2=vv2.dx;
      int w=max(w1,w2)+2;
      vecteur v(3);
      v[0]=Eqw_translate(v1,(w-w1)/2,4-vv1.y);
      v[1]=Eqw_translate(v2,(w-w2)/2,-vv2.dy-vv2.y);
      v[2]=eqwdata(w,4+vv1.dy+vv2.dy,0,-vv2.dy,a,at_division,0);
      return gen(v,_SEQ__VECT);
    }
    /***************
     *   VECTORS   *
     ***************/
    if ( (g.type==_VECT) && !g._VECTptr->empty() ){
      if (g.subtype==_SPREAD__VECT)
	return Eqw_compute_size(string2gen("spreadsheet",false),a,windowhsize);
      vecteur v;
      const_iterateur it=g._VECTptr->begin(),itend=g._VECTptr->end();
      int x=0,y=0,h=a.fontsize; 
      /***************
       *   HISTORY   *
       ***************/
      if (g.subtype==_HIST__VECT){ 
	int l=windowhsize;
	vecteur vplot;
	for (int i=0;it!=itend;++it,++i){
	  gen tmpg(*it);
	  if (!rpn_mode && it->type==_VECT && !it->_VECTptr->empty()){
	    if (it->_VECTptr->front().type==_STRNG)
	      tmpg=makevecteur(it->_VECTptr->front(),string2gen("",false));
	    gen tmpback=it->_VECTptr->back();
	    if (tmpback.type==_POINTER_ && tmpback.subtype==_FL_WIDGET_POINTER && fl_widget_updatepict_function)
	      tmpback = fl_widget_updatepict_function(tmpback);
	    if (tmpback.is_symb_of_sommet(at_pnt) || (tmpback.type==_VECT && !tmpback._VECTptr->empty() && tmpback._VECTptr->back().is_symb_of_sommet(at_pnt)))
	      vplot.push_back(tmpback);
	    if (tmpback.is_symb_of_sommet(at_erase))
	      vplot.clear();
	    gen itfront=it->_VECTptr->front();
	    if (itfront.is_symb_of_sommet(at_expr)){
	      itfront=itfront._SYMBptr->feuille;
	      int mode=maple_mode;
	      if (itfront.type==_VECT && !itfront._VECTptr->empty()){
		mode=itfront._VECTptr->back().val;
		itfront=itfront._VECTptr->front();
	      }
	      if (itfront.type==_STRNG){
		int save_maple_mode=maple_mode;
		maple_mode=mode;
		try {
		  itfront=gen(*itfront._STRNGptr);
		} catch (std::runtime_error & e){
		}
		maple_mode=save_maple_mode;
	      }
	    }
	    if (itfront.type==_SYMB){
	      unary_function_ptr & u=itfront._SYMBptr->sommet;
	      int i0=equalposcomp(implicittex_plot_sommets,u);
	      gen itback=it->_VECTptr->back();
	      if (u==at_graph3d2tex){
		//		if (itfront._SYMBptr->feuille.type!=_STRNG && itback.type==_INT_&& itback.val>=0)
		//  png_replot(itback.val);
		// does not work inside a worksheet!
		i0=1;
	      }
	      if (itback.type==_VECT && !itback._VECTptr->empty())
		itback=itback._VECTptr->back();
	      if (itback.is_symb_of_sommet(at_pnt) && itback.subtype>=0)
		itback=itback.subtype;
#ifndef __APPLE__
	      if (i0 && itback.type==_INT_ && itback.val>=0){
		Fl_Group::current(0);
		Fl_PNG_Image * ptr=new Fl_PNG_Image((gnuplot_filename+print_INT_(itback.val)+".png").c_str());
		cerr << ptr->w() << "*" << ptr->h() << endl;
		tmpg=makevecteur(it->_VECTptr->front(),gen(ptr,_FL_IMAGE_POINTER));
	      }
	      else {
#endif
		if (!itback.is_symb_of_sommet(at_pnt))
		  i0=0;
		if (i0 || u==at_graph2tex){ // Make a fl_image
		  Fl_Group::current(0);
		  Image * ptr=new Image(Eqw_History_group->x(),Eqw_History_group->y(),min(windowhsize,Eqw_History_group->w())-40,min(windowhsize,Eqw_History_group->h())-30,"");
		  if (u==at_graph2tex)
		    ptr->plot_instructions_ptr=new vecteur(vplot);
		  else
		    ptr->plot_instructions_ptr=new vecteur(1,tmpback);
		  ptr->autoname=0;
		  ptr->embedded=true;
		  if (Xcas_turtle->value())
		    ptr->turtleptr=new std::vector<logo_turtle>(*Picture->turtleptr);
		  /* Eqw_History_group->add(ptr);
		     if (Eqw_history->visible())
		     ptr->show(); */
		  gen newres=gen(ptr,_FL_WIDGET_POINTER);
		  tmpg=makevecteur(it->_VECTptr->front(),newres);
		  // not very clean!! This assumes HIST type is only used
		  // for history. I do it so that the image is not recomputed
		  if (history_out.size()>i)
		    history_out[i]=newres;
		}
		else {
		  i0=equalposcomp(notexprint_plot_sommets,u);
		  bool answer_print=!i0 && u!=at_nodisp;
		  if (!answer_print)
		    tmpg=makevecteur(it->_VECTptr->front(),string2gen("Done",false));
		} // end 2d-graph if/else
#ifndef __APPLE__
	      } // end 3d-graph if/else
#endif
	    } // end ifront.type==_SYMB
	  }
	  if (!pretty_input && tmpg.type==_VECT && tmpg._VECTptr->size()==2){
	    vecteur & tmpv=*tmpg._VECTptr;
	    if (tmpv.front().type!=_STRNG && !tmpv.front().is_symb_of_sommet(at_expr)){
	      string tmps=tmpv.front().print();
	      tmpg=makevecteur(symbolic(at_expr,gen(makevecteur(string2gen(tmps,false),maple_mode),_SEQ__VECT)),tmpv.back());
	    }
	  }
	  tmpg.subtype=_SEQ__VECT;
	  vecteur tmp(Eqw_subsizes(tmpg,a,windowhsize));
	  iterateur jt=tmp.begin(); // this is the question
	  // compute the size of writing the history level i
	  eqwdata w(Eqw_total_size(*jt));
	  if (rpn_mode) // ignore question
	    v.push_back(eqwdata(1,1,x,-y,w.eqw_attributs,string2gen("",false)));
	  else { 
	    y += w.dy + 2;
	    x = int(fl_width((print_INT_(i)+": ").c_str()));
	    if (l<w.dx+x)
	      l=w.dx+x;
	    v.push_back(Eqw_translate(*jt,x,-y-w.y));
	  }
	  jt=tmp.end()-1; // this is the answer
	  *jt=Eqw_change_attributs(*jt,attributs(a.fontsize,a.background,a.text_color+4));
	  w=Eqw_total_size(*jt);
	  y += w.dy + 2; 
	  x = int(fl_width("    "));
	  l=max(l,w.dx+x);
	  int xshift=x;
	  if (w.dx+4<windowhsize-a.fontsize){
	    if (center_history)
	      xshift=(windowhsize-w.dx-4)/2;
	    else
	      xshift=windowhsize-w.dx-4-a.fontsize;
	  }
	  v.push_back(Eqw_translate(*jt,xshift,-y-w.y));
	}
	v.push_back(eqwdata(l,y,0,-y,a,at_makevector,0));
	// cerr << v << endl;
	return Eqw_translate(gen(v,_HIST__VECT),0,y);
      } // END HISTORY
      /***************
       *   MATRICE   *
       ***************/
      if (ckmatrix(g) && g.subtype!=_SEQ__VECT){
	gen mkvect(at_makevector);
	mkvect.subtype=_SEQ__VECT;
	gen mkmat(at_makevector);
	mkmat.subtype=_MATRIX__VECT;
	int nrows,ncols;
	mdims(*g._VECTptr,nrows,ncols);
	if (ncols){
	  vecteur all_sizes;
	  all_sizes.reserve(nrows);
	  vector<int> row_heights(nrows),row_bases(nrows),col_widths(ncols);
	  // vertical gluing
	  for (int i=0;it!=itend;++it,++i){
	    gen tmpg=*it;
	    tmpg.subtype=_SEQ__VECT;
	    vecteur tmp(Eqw_subsizes(tmpg,a,max(windowhsize/ncols-a.fontsize,230)));
	    int h=a.fontsize,y=0;
	    const_iterateur jt=tmp.begin(),jtend=tmp.end();
	    for (int j=0;jt!=jtend;++jt,++j){
	      eqwdata w(Eqw_total_size(*jt));
	      Eqw_vertical_adjust(w.dy,w.y,h,y);
	      col_widths[j]=max(col_widths[j],w.dx);
	    }
	    if (i)
	      row_heights[i]=row_heights[i-1]+h+a.fontsize;
	    else
	      row_heights[i]=h;
	    row_bases[i]=y;
	    all_sizes.push_back(tmp);
	  }
	  // accumulate col widths
	  col_widths.front() +=(3*a.fontsize)/2;
	  vector<int>::iterator iit=col_widths.begin()+1,iitend=col_widths.end();
	  for (;iit!=iitend;++iit)
	    *iit += *(iit-1)+a.fontsize;
	  // translate each cell
	  it=all_sizes.begin();
	  itend=all_sizes.end();
	  int h,y,prev_h=0;
	  for (int i=0;it!=itend;++it,++i){
	    h=row_heights[i];
	    y=row_bases[i];
	    iterateur jt=it->_VECTptr->begin(),jtend=it->_VECTptr->end();
	    for (int j=0;jt!=jtend;++jt,++j){
	      eqwdata w(Eqw_total_size(*jt));
	      if (j)
		*jt=Eqw_translate(*jt,col_widths[j-1]-w.x,-h-y);
	      else
		*jt=Eqw_translate(*jt,-w.x+a.fontsize/2,-h-y);
	    }
	    it->_VECTptr->push_back(eqwdata(col_widths.back(),h-prev_h,0,-h,a,mkvect,0));
	    prev_h=h;
	  }
	  all_sizes.push_back(eqwdata(col_widths.back(),row_heights.back(),0,-row_heights.back(),a,mkmat,-row_heights.back()/2));
	  return Eqw_translate(all_sizes,0,row_heights.back()/2);
	}
      } // end matrices
      /*************************
       *   SEQUENCES/VECTORS   *
       *************************/
      // horizontal gluing
      x += a.fontsize/2;
      int ncols=itend-it;
      //ncols=min(ncols,5);
      for (;it!=itend;++it){
	gen cur_size=Eqw_compute_size(*it,a,max(windowhsize/ncols-a.fontsize,230));
	eqwdata tmp=Eqw_total_size(cur_size);
	v.push_back(Eqw_translate(cur_size,x-tmp.x,0));
	x=x+tmp.dx+a.fontsize/2;
	Eqw_vertical_adjust(tmp.dy,tmp.y,h,y);
      }
      gen mkvect(at_makevector);
      if (g.subtype==_SEQ__VECT)
	mkvect=at_makesuite;
      else
	mkvect.subtype=g.subtype;
      v.push_back(eqwdata(x,h,0,y,a,mkvect,0));
      return gen(v,_EQW__VECT);
    } // end sequences
    if (g.type!=_SYMB){
      int i=int(fl_width(g.print().c_str()));
      gen tmp=eqwdata(i,a.fontsize,0,0,a,g);
      return tmp;
    }
    /**********************
     *  SYMBOLIC HANDLING *
     **********************/
    return Eqw_compute_symb_size(g,a,windowhsize);
    // return Eqw_compute_symb_size(aplatir_fois_plus(g),a,windowhsize);
    // aplatir_fois_plus is a problem for Eqw_replace_selection
    // because it will modify the structure of the data
  }

  string Eqw_extract_string(const string & cs,int begin_sel,int end_sel){
    int css=cs.size();
    if (!css)
      return cs;
    int sel0=min(max(0,begin_sel),css-1),sel1=css;
    if (end_sel>=0)
      sel1=min(end_sel,css);
    if (sel0>sel1)
      std::swap<int>(sel0,sel1);
    return cs.substr(sel0,sel1-sel0);
  }

  void Eqw_draw(const eqwdata & e,int x,int y,int rightx,int lowery,Eqw * eq,int begin_sel,int end_sel){
    if ( (e.dx+e.x-x<0) || (e.x-x>rightx) || (e.y>y) || e.y+e.dy<lowery)
      return; // nothing to draw, out of window
    gen gg=e.g;
    bool selected=e.selected;
    int fontsize=e.eqw_attributs.fontsize;
    int text_color=e.eqw_attributs.text_color;
    int background=e.eqw_attributs.background;
    fl_color(text_color);
    fl_font(FL_HELVETICA,fontsize);
    if (gg.type==_POINTER_) {
      if (gg.subtype==_FL_WIDGET_POINTER){
	Fl_Widget * wg=(Fl_Widget *) gg._POINTER_val;
	Image * img=dynamic_cast<Image *>(wg);
	if (img){
	  img->deltax=e.x-x;
	  img->deltay=y-(e.y+e.dy);
	  wg->draw();
	  return;
	}
      }
#ifndef __APPLE__
      if (gg.subtype==_FL_IMAGE_POINTER){
	Fl_Image * flimg= (Fl_Image *)gg._POINTER_val;
	int hautgauche=y-e.y-e.dy, hautgauchecorrige=max(hautgauche,eq->y());
	int decalvert=hautgauchecorrige-hautgauche;
	if (flimg){
	  flimg->draw(e.x,hautgauchecorrige,e.dx,e.dy-decalvert,x,decalvert);
	  return;
	}
      }
#endif
      // wg->resize(e.x-x,y-e.y-e.dy,e.dx,e.dy);
      // wg->draw(); // automatically done if it belongs to the group
      return;
    }
    if (gg.type==_STRNG){
      string s;
      if (e.active){
	// draw s and the cursor
	s=*gg._STRNGptr;
	int ss=s.size();
	int pos=max(min(eq->active_pos,ss),0);
	if (eq->need_active_parse)
	  s=s.substr(0,pos)+"|"+s.substr(pos,ss-pos);
	else
	  s='"'+s.substr(0,pos)+"|"+s.substr(pos,ss-pos);
      }
      else {
	if (gg.subtype)
	  s='"'+*gg._STRNGptr;
	else
	  s=' '+*gg._STRNGptr;
      }
      string cs;
      int ss=s.size();
      int vsize = fontsize -2;
      for (int pos=0;pos<ss;++pos){
	char ch=s[pos];
	if (ch=='\n'){
	  fl_draw(cs.c_str(),e.x-x,y-e.y+vsize-e.dy);
	  cs="";
	  vsize += fontsize;
	}
	else
	  cs += ch;
      }
      fl_draw(cs.c_str(),e.x-x,y-e.y+vsize-e.dy);
      // If selected take care of begin/end selection
      int css=cs.size(); // must be >=1 since selected hence not active
      if (selected && css){
	int sel0=min(begin_sel+1,css-1),sel1=css;
	if (end_sel>=0)
	  sel1=min(end_sel+1,css);
	if (sel0>sel1)
	  std::swap<int>(sel0,sel1);
	int deltax=int(fl_width(cs.substr(0,sel0).c_str()));
	cs=cs.substr(sel0,sel1-sel0);
	int dx=int(fl_width(cs.c_str()));
	fl_rectf(e.x-x+deltax,y-e.y-e.dy+1,dx,e.dy+1);
	fl_color(background);
	fl_draw(cs.c_str(),e.x-x+deltax,y-e.y+vsize-e.dy);
      }
      return;
    }
    if (selected){
      fl_rectf(e.x-x,y-e.y-e.dy+1,e.dx,e.dy+1);
      fl_color(background);
    }
    string s=gg.print();
    if (gg.type==_IDNT && !s.empty() && s[0]=='_')
      s=s.substr(1,s.size()-1);
    // cerr << s.size() << endl;
    fl_draw(s.c_str(),e.x-x,y-e.y);
    return;
  }

  void Eqw_draw(const gen & g,int x,int y,int rightx,int lowery,Eqw * equat){
    if (g.type==_EQW){ // terminal
      eqwdata & e=*g._EQWptr;
      Eqw_draw(e,x,y,rightx,lowery,equat,equat->begin_sel,equat->end_sel);
    }
    if (g.type!=_VECT)
      return;
    vecteur & v=*g._VECTptr;
    if (v.empty())
      return;
    gen tmp=v.back();
    if (tmp.type!=_EQW){
      cerr << "EQW error:" << v << endl;
      return;
    }
    eqwdata & w=*tmp._EQWptr;
    if ( (w.dx+w.x-x<0) || (w.x-x>rightx) || (w.y>y) || (w.y+w.dy<lowery) )
      return; // nothing to draw, out of window
    /*******************
     * draw the vector *
     *******************/
    // v is the vector, w the master operator eqwdata
    gen oper=w.g; 
    bool selected=w.selected;
    int fontsize=w.eqw_attributs.fontsize;
    int background=w.eqw_attributs.background;
    int text_color=w.eqw_attributs.text_color;
    int x0=w.x;
    int y0=w.y; // lower coordinate of the master vector
    int y1=y0+w.dy; // upper coordinate of the master vector
    if (selected){
      fl_color(text_color);
      fl_rectf(w.x-x,y-w.y-w.dy+1,w.dx,w.dy+1);
    }
    else {
      fl_color(background);
      // fl_rectf(w.x-x,y-w.y-w.dy,w.dx,w.dy-2);
    }
    // draw arguments of v
    const_iterateur it=v.begin(),itend=v.end()-1;
    if (oper==at_multistring){
      const_iterateur it_beg=it,it_end=itend;
      if (Eqw_multistring_selection(it_beg,it_end,false)){
	// begin_sel and end_sel apply respect. to it_beg and it_end sel. lines
	for (;it!=it_beg;++it)
	  Eqw_draw(*it->_EQWptr,x,y,rightx,lowery,equat,-1,-1);
	if (it_beg==it_end){
	  Eqw_draw(*it->_EQWptr,x,y,rightx,lowery,equat,equat->begin_sel,equat->end_sel);
	  ++it;
	}
	else {
	  Eqw_draw(*it->_EQWptr,x,y,rightx,lowery,equat,equat->begin_sel,-1);
	  ++it;
	  for (;it!=it_end;++it)
	    Eqw_draw(*it->_EQWptr,x,y,rightx,lowery,equat,-1,-1);
	  Eqw_draw(*it->_EQWptr,x,y,rightx,lowery,equat,-1,equat->end_sel);
	  ++it;
	}
	for (;it!=itend;++it)
	  Eqw_draw(*it->_EQWptr,x,y,rightx,lowery,equat,-1,-1);
	return;
      }
    }
    if (oper==at_expr && v.size()==3){
      Eqw_draw(*it,x,y,rightx,lowery,equat);
      return;
    }
    for (;it!=itend;++it)
      Eqw_draw(*it,x,y,rightx,lowery,equat);
    if (oper==at_multistring)
      return;
    if (selected)
      fl_color(background);
    else
      fl_color(text_color);      
    fl_font(FL_HELVETICA,fontsize);
    string s;
    if (g.subtype==_HIST__VECT){ // For history, we must write history levels
      it=v.begin();
      int nlevels=(itend-it)/2-1,wlevel;
      int skip=2; // int skip=2-rpn_mode;
      for (int i=0;it!=itend;it+=skip,++i){
	eqwdata tmp=Eqw_total_size(*it);
	fl_font(FL_HELVETICA,tmp.eqw_attributs.fontsize);
	fl_color(FL_BLUE);
	// cerr << tmp << endl;
	int yy;
	// uncommented, seemed previously to be problematic with strings
	if (tmp.hasbaseline)
	  yy=y-tmp.baseline;
	else
	  yy=y-tmp.y-(tmp.dy-tmp.eqw_attributs.fontsize)/2;
	if (yy<0 || yy>y-lowery)	  
	  continue;
	if (rpn_mode)
	  wlevel=nlevels-i;
	else
	  wlevel=i;
	if (wlevel || !rpn_mode)
	  fl_draw((print_INT_(wlevel)+": ").c_str(),-x,yy);
      }
      return; // nothing else to do
    }
    if (oper.type==_FUNC){
      // catch here special cases user function, vect/matr, ^, int, sqrt, etc.
      unary_function_ptr & u=*oper._FUNCptr;
      if (u==at_at){ // draw brackets around 2nd arg
	gen arg2=v[1]; // 2nd arg of at_of, i.e. what's inside the parenth.
	eqwdata varg2=Eqw_total_size(arg2);
	x0=varg2.x;
	y0=varg2.y;
	y1=y0+varg2.dy;
	fontsize=varg2.eqw_attributs.fontsize;
	fl_font(FL_HELVETICA,fontsize);
	if (x0-x<rightx)
	  fl_draw("[",x0-x-int(fl_width("[")),y-varg2.baseline);
	x0 += varg2.dx ;
	if (x0-x<rightx)
	  fl_draw("]",x0-x,y-varg2.baseline);
	return;
      }
      if (u==at_of){ // do we need to draw some parenthesis?
	gen arg2=v[1]; // 2nd arg of at_of, i.e. what's inside the parenth.
	if (arg2.type!=_VECT || arg2._VECTptr->back().type !=_EQW || arg2._VECTptr->back()._EQWptr->g!=at_makesuite){ // Yes (if not _EQW it's a sequence with parent)
	  eqwdata varg2=Eqw_total_size(arg2);
	  x0=varg2.x;
	  y0=varg2.y;
	  y1=y0+varg2.dy;
	  fontsize=varg2.eqw_attributs.fontsize;
	  int pfontsize=max(fontsize,(fontsize+(varg2.baseline-varg2.y))/2);
	  fl_font(FL_HELVETICA,pfontsize); // was fontsize
	  if (x0-x<rightx)
	    fl_draw("(",x0-x-int(fl_width("(")),y-varg2.baseline);
	  x0 += varg2.dx ;
	  if (x0-x<rightx)
	    fl_draw(")",x0-x,y-varg2.baseline);
	}
	return;
      }
      if (u==at_makesuite){
	int pfontsize=max(fontsize,(fontsize+(w.baseline-w.y))/2);
	fl_font(FL_HELVETICA,pfontsize);
	if (x0-x<rightx)
	  fl_draw("(",x0-x-int(fl_width("("))/2,y-w.baseline);
	x0 += w.dx;
	if (x0-x<rightx)
	  fl_draw(")",x0-x-int(fl_width("("))/2,y-w.baseline); 
	// print commas between args
	it=v.begin(),itend=v.end()-2;
	for (;it!=itend;++it){
	  eqwdata varg2=Eqw_total_size(*it);
	  fontsize=varg2.eqw_attributs.fontsize;
	  fl_font(FL_HELVETICA,fontsize);
	  if (varg2.x+varg2.dx-x<rightx)
	    fl_draw(",",varg2.x+varg2.dx-x,y-varg2.baseline);	  
	}
	return;
      }
      if (u==at_makevector){ // draw [] delimiters for vector/matrices
	if (oper.subtype!=_SEQ__VECT){
	  if (x0-x+1<rightx){
	    fl_line(x0-x+1,y-y0+1,x0-x+1,y-y1-1);
	    if (oper.subtype==_MATRIX__VECT)
	      fl_line(x0-x+2,y-y0+1,x0-x+2,y-y1-1);
	    fl_line(x0-x+1,y-y0+1,x0-x+fontsize/4,y-y0+1);
	    fl_line(x0-x+1,y-y1-1,x0-x+fontsize/4,y-y1-1);
	  }
	  x0 += w.dx ;
	  if (x0-x-1<rightx){
	    fl_line(x0-x-1,y-y0+1,x0-x-1,y-y1-1);
	    if (oper.subtype==_MATRIX__VECT)
	      fl_line(x0-x-2,y-y0+1,x0-x-2,y-y1-1);
	    fl_line(x0-x-1,y-y0+1,x0-x-fontsize/4,y-y0+1);
	    fl_line(x0-x-1,y-y1-1,x0-x-fontsize/4,y-y1-1);
	  }
	}
	return;
      }
      int lpsize=int(fl_width("("));
      int rpsize=int(fl_width(")"));
      eqwdata tmp=Eqw_total_size(v.front()); // tmp= 1st arg eqwdata
      if (u==at_sto)
	tmp=Eqw_total_size(v[1]);
      x0=w.x-x;
      y0=y-w.baseline;
      if (u==at_pow){
	if (!need_parenthesis(tmp.g))
	  return;
	if (tmp.x-x-lpsize<rightx)
	  fl_draw("(",tmp.x-x-lpsize,y-tmp.baseline);
	if (tmp.x+tmp.dx-x<rightx)
	  fl_draw(")",tmp.x+tmp.dx-x,y-tmp.baseline);
	return;
      }
      if (u==at_program){
	if (tmp.x+tmp.dx-x<rightx)
	  fl_draw("->",tmp.x+tmp.dx-x,y-tmp.baseline);
	return;
      }
      if (u==at_sum){
	if (x0<rightx){
	  fl_line(x0,y0,x0+(2*fontsize)/3,y0);
	  fl_line(x0,y0-fontsize,x0+(2*fontsize)/3,y0-fontsize);
	  fl_line(x0,y0,x0+fontsize/2,y0-fontsize/2,x0,y0-fontsize);
	}
	return;
      }
      if (u==at_sqrt){
	y0 =1+y-w.y;
	int h=w.dy;
	if (x0<rightx){
	  fl_line(x0+2,y0-h/2,x0+fontsize/2,y0-2,x0+fontsize,y0-h);
	  fl_line(x0+fontsize,y0-h,x0+w.dx-1,y0-h);
	}
	return;
      }
      if (u==at_integrate){
	x0+=2;
	y0+=fontsize/2;
	if (x0<rightx){
	  fl_arc(x0,y0,fontsize/3,fontsize/3,180,360);
	  fl_line(x0+fontsize/3,y0,x0+fontsize/3,y0-2*fontsize+4);
	  fl_arc(x0+fontsize/3,y0-2*fontsize+3,fontsize/3,fontsize/3,0,180);
	}
	if (v.size()!=2){ // if arg has size > 1 draw the d
	  eqwdata ptmp=Eqw_total_size(v[1]);
	  if (ptmp.x-x<rightx)
	    fl_draw(" d",ptmp.x-x-int(fl_width(" d")),y-ptmp.baseline);
	}
	else {
	  eqwdata ptmp=Eqw_total_size(v[0]);
	  if (ptmp.x+ptmp.dx-x<rightx)
	    fl_draw(" dx",ptmp.x+ptmp.dx-x,y-ptmp.baseline);
	}
	return;
      }
      if (u==at_division){
	if (x0<rightx)
	  fl_line(x0+1,y0-2,x0+w.dx-1,y0-2);
	return;
      }
      if (u==at_limit && v.size()>=4){
	if (x0<rightx)
	  fl_draw("lim",w.x-x,y-w.baseline);
	gen arg2=v[1]; // 2nd arg of limit, i.e. the variable
	if (arg2.type==_EQW){ 
	  eqwdata & varg2=*arg2._EQWptr;
	  if (varg2.x+varg2.dx+2-x<rightx)
	    fl_draw("->",varg2.x+varg2.dx+2-x,y-varg2.y);
	}
	if (v.size()>=5){
	  arg2=v[2]; // 3rd arg of lim, the point, draw a comma after if dir.
	  if (arg2.type==_EQW){ 
	    eqwdata & varg2=*arg2._EQWptr;
	    if (varg2.x+varg2.dx-x<rightx)
	      fl_draw(",",varg2.x+varg2.dx-x,y-varg2.baseline);
	  }
	}
	return;
      }
      bool parenthesis=true;
      string opstring(",");
      if (u.ptr->printsommet==&printsommetasoperator || equalposcomp(binary_op_tab,u) )
	opstring=u.ptr->s.c_str();
      else {
	if (u==at_sto)
	  opstring=":=";
	parenthesis=false;
      }
      int yy=y0; // y0 is the lower coordinate of the whole eqwdata
      int opsize=int(fl_width(opstring.c_str()))+3;
      it=v.begin();
      itend=v.end()-1;
      // Reminder: here tmp is the 1st arg eqwdata, w the whole eqwdata
      if ( (itend-it==1) && ( (u==at_neg) 
			      || (u==at_plus) // uncommented for +infinity
			      ) ){ 
	if (u==at_neg &&need_parenthesis(tmp.g)){
	  if (tmp.x-x-lpsize<rightx)
	    fl_draw("(",tmp.x-x-lpsize,y-tmp.baseline);
	  if (tmp.x-x+tmp.dx<rightx)
	    fl_draw(")",tmp.x-x+tmp.dx,y-tmp.baseline);
	}
	if (w.x-x<rightx)
	  fl_draw(u.ptr->s.c_str(),w.x-x,y-w.baseline);
	return;
      }
      // write first open parenthesis
      if (u==at_plus)
	parenthesis=false;
      else {
	if (parenthesis && need_parenthesis(tmp.g)){
	  if (w.x-x<rightx){
	    int pfontsize=max(fontsize,(fontsize+(tmp.baseline-tmp.y))/2);
	    fl_font(FL_HELVETICA,pfontsize);
	    fl_draw("(",w.x-x,y-tmp.baseline);
	    fl_font(FL_HELVETICA,fontsize);
	  }
	}
      }
      for (;;){
	// write close parenthesis at end
	int xx=tmp.dx+tmp.x-x;
	if (parenthesis && need_parenthesis(tmp.g)){
	  if (xx<rightx){
	    int pfontsize=max(fontsize,(fontsize+(tmp.baseline-tmp.y))/2);
	    fl_font(FL_HELVETICA,pfontsize);
	    fl_draw(")",xx,y-tmp.baseline);
	    fl_font(FL_HELVETICA,fontsize);
	  }
	  xx +=rpsize;
	}
	++it;
	if (it==itend){
	  if (u.ptr->printsommet==&printsommetasoperator || u==at_sto || equalposcomp(binary_op_tab,u))
	    return;
	  else
	    break;
	}
	// write operator
	if (u==at_prod)
	  // fl_draw(".",xx+3,y-tmp.y-tmp.dy/2);
	  fl_draw(".",xx+3,y-tmp.baseline-fontsize/3);
	else {
	  gen tmpgen;
	  if (u==at_plus && ( 
			     (it->type==_VECT && it->_VECTptr->back().type==_EQW && it->_VECTptr->back()._EQWptr->g==at_neg) 
			     || 
			     ( it->type==_EQW && is_integer(it->_EQWptr->g) && is_strictly_positive(-it->_EQWptr->g,0) ) 
			     )
	      )
	    ;
	  else {
	    if (xx+1<rightx)
	      // fl_draw(opstring.c_str(),xx+1,y-tmp.y-tmp.dy/2+fontsize/2);
	      fl_draw(opstring.c_str(),xx+1,y-tmp.baseline);
	  }
	}
	// write right parent, update tmp
	tmp=Eqw_total_size(*it);
	if (parenthesis && (need_parenthesis(tmp.g)) ){
	  if (tmp.x-lpsize-x<rightx){
	    int pfontsize=max(fontsize,(fontsize+(tmp.baseline-tmp.y))/2);
	    fl_font(FL_HELVETICA,pfontsize);
	    fl_draw("(",tmp.x-lpsize-x,y-tmp.baseline);
	    fl_font(FL_HELVETICA,fontsize);
	  }
	}
      } // end for (;;)
      s=u.ptr->s+"(";
      if (w.x-x<rightx)
	fl_draw(s.c_str(),w.x-x,y-w.baseline);
      if (w.x+w.dx-x-rpsize<rightx)
	fl_draw(")",w.x+w.dx-x-rpsize,y-w.baseline);
      return;
    }
    s=oper.print();
    if (w.x-x<rightx)
      fl_draw(s.c_str(),w.x-x,y-w.baseline);
  }

  void Eqw::draw(){
    fl_color(FL_WHITE);
    fl_rectf(0, 0, w(), h());
    fl_color(attr.text_color);
    Eqw_draw(data,xleft,ytop,xleft+w(),ytop-h(),this);
  }

  bool Eqw_box_sizes(const gen & g,int & l,int & h,int & x,int & y,attributs & attr,bool & selected,bool search_active=false){
    if (g.type==_EQW){
      eqwdata & w=*g._EQWptr;
      x=w.x;
      y=w.y;
      l=w.dx;
      h=w.dy;
      if (search_active)
	selected=w.active;
      else
	selected=w.selected;
      attr=w.eqw_attributs;
      return true;
    }
    else {
      if (g.type!=_VECT || g._VECTptr->empty() ){
	l=0;
	h=0;
	x=0;
	y=0;
	attr=attributs(0,0,0);
	selected=false;
	return true;
      }
      gen & g1=g._VECTptr->back();
      Eqw_box_sizes(g1,l,h,x,y,attr,selected,search_active);
      return false;
    }
  }

  // Set the scrollbars according to data, xleft, ytop
  void Eqw::setscroll(){
    redraw();
    eqwdata e=Eqw_total_size(data);
    if (e.dx>w()){
      xleft=max(e.x,xleft);
      xleft=min(e.x+e.dx-w(),xleft);
      hscroll->value(xleft-e.x,w(),0,e.dx);
      hscroll->show();
    }
    else {
      xleft = e.x - (w()-e.dx)/2;
      hscroll->value(0,1,0,1);
      hscroll->show();
    }
    if (e.dy>h()){
      ytop=max(e.y+h(),ytop);
      ytop=min(e.y+e.dy,ytop);
      vscroll->value(e.dy+e.y-ytop,h(),0,e.dy);
      vscroll->show();
    }
    else {
      ytop = e.y + (e.dy + h())/2;
      vscroll->value(0,1,0,1);
      vscroll->show();
    }
  }

  void Eqw_cb_scroll(Fl_Widget * w, void*) {
    Eqw_Scrollbar * s=dynamic_cast<Eqw_Scrollbar *>(w);
    if (!s)
      return;
    if (s->vertical){
      eqwdata e=Eqw_total_size(s->eqwptr->data);
      s->eqwptr->ytop=e.dy+e.y-s->value();
    }
    else
      s->eqwptr->xleft=s->value();
    s->eqwptr->setscroll();
    s->eqwptr->redraw();
  }

  // select or deselect part of the current eqution
  // This is done *in place*
  void Eqw_select(gen & g,bool select,bool active_search){
    if (g.type==_EQW){
      eqwdata & e=*g._EQWptr;
      if (active_search)
	e.active=select;
      else
	e.selected=select;
    }
    if (g.type!=_VECT)
      return;
    vecteur & v=*g._VECTptr;
    iterateur it=v.begin(),itend=v.end();
    for (;it!=itend;++it)
      Eqw_select(*it,select,active_search);
  }

  void Eqw::deselect(){
    begin_sel=-1;
    end_sel=-1;
    Eqw_select(data,false);
    redraw();
  }

  void Eqw::select(){
    begin_sel=-1;
    end_sel=-1;
    Eqw_select(data,true);
    adjust_xy_sel();
    redraw();
  }

  void Eqw::fl_select(){
    gen g=get_selection();
    string s=g.print();
    Fl::selection(*this,s.c_str(),s.size());
    if (cb_select)
      cb_select(s.c_str());
  }

  // check if interval xmin..xmax and x1..x2 have a non empty intersection
  bool interval_crossing(int xmin,int xmax,int x1,int x2){
    if (xmax<x1 || xmin>x2)
      return false;
    return true;
  }

  // Select in g the subrectangle xmin,ymin,xmax,ymax
  void Eqw_select_rectangle(gen & g,int x1,int y1,int x2,int y2,Eqw * eq){
    int xmin=min(x1,x2),xmax=max(x1,x2),ymin=min(y1,y2),ymax=max(y1,y2);
    if (g.type==_EQW) {
      Eqw_select(g,true);
      if (g._EQWptr->g.type==_STRNG){
	// compute begin/end_sel
	if (y1<y2){
	  std::swap<int>(y1,y2);
	  std::swap<int>(x1,x2);
	}
	eq->begin_sel=Eqw_binary_search_pos(*g._EQWptr,x1,y1);
	eq->end_sel=Eqw_binary_search_pos(*g._EQWptr,x2,y2);
      }
      return;
    }
    if (g.type!=_VECT)
      return;
    vecteur & v=*g._VECTptr;
    // Find the first element of v containing the whole rectangle
    iterateur it=v.begin(),itend=v.end()-1,last_sel_it;
    for (;it!=itend;++it){
      int x,y,l,h;
      attributs attr(0,0,0);
      bool selected;
      Eqw_box_sizes(*it,l,h,x,y,attr,selected);
      if ( (x<=xmin) && (y<=ymin) && (xmax<=x+l) && (ymax<=y+h) ){
	Eqw_select_rectangle(*it,x1,y1,x2,y2,eq);
	return;
      }
    }
    // None, then we select each element that crosses the rectangle
    bool selectall=true,find_first_sel=false,find_last_sel=false;
    if (itend->type==_EQW && itend->_EQWptr->g==at_multistring){
      if (y1<y2){
	std::swap<int>(y1,y2);
	std::swap<int>(x1,x2);
      }
      find_first_sel=true;
      selectall=false;
    }
    it=v.begin();
    for (;it!=itend;++it){
      int x,y,l,h;
      attributs attr(0,0,0);
      bool selected;
      Eqw_box_sizes(*it,l,h,x,y,attr,selected);
      bool xcross=interval_crossing(xmin,xmax,x,x+l),ycross=interval_crossing(ymin,ymax,y,y+h);
      if ( xcross && ycross ){
	Eqw_select(*it,true);
	last_sel_it=it;
	if (find_first_sel){
	  find_first_sel=false;
	  find_last_sel=true;
	  eq->begin_sel=Eqw_binary_search_pos(*it->_EQWptr,x1,y1);
	}
      }
      else {
	if (ycross && g.subtype==_HIST__VECT){
	  Eqw_select(*it,true);
	  last_sel_it=it;
	}
	else
	  selectall=false;
      }
    }
    if (find_last_sel)
      eq->end_sel=Eqw_binary_search_pos(*last_sel_it->_EQWptr,x2,y2);
    if (selectall) 
      Eqw_select(g,true);
  }

  void Eqw::select_rectangle(int x,int y){
    // x,y are the end of the mouse selection
    Eqw_select_rectangle(data,xsel,ysel,x,y,this);
  }

  // return true if g has some selection inside
  bool Eqw_adjust_xy(const gen & g,int & xleft,int & ytop,int & xright,int & ybottom,bool active_search=false){
    int x,y,w,h;
    attributs f(0,0,0);
    bool selected;
    Eqw_box_sizes(g,w,h,x,y,f,selected,active_search);
    if ( (g.type==_EQW__VECT) || selected ){ // terminal or selected
      xleft=x;
      ybottom=y;
      if (selected){ // g is selected
	ytop=y+h;
	xright=x+w;
	return true;
      }
      else { // no selection
	xright=x;
	ytop=y;
	return false;
      }
    }
    if (g.type!=_VECT)
      return false;
    // last not selected, recurse
    iterateur it=g._VECTptr->begin(),itend=g._VECTptr->end()-1;
    for (;it!=itend;++it){
      if (Eqw_adjust_xy(*it,xleft,ytop,xright,ybottom,active_search))
	return true;
    }
    return false;
  }
 
  void Eqw::adjust_xy(){
    // recalculate xsel, ysel to begin top of selection
    // and xcur, ycur to end bottom of selection
    Eqw_adjust_xy(data,xsel,ysel,xcur,ycur);
  }

  void Eqw::adjust_xy_sel(){ 
    // same + recalc xleft
    Eqw_adjust_xy(data,xsel,ysel,xcur,ycur);
    if ((xcur>xleft+w()) || (xsel<xleft) )
      xleft=max(xsel-w()/2,0);
    if ( ycur < ytop-h() || ysel >ytop )
      ytop=ysel;
    // recalc scrollbars
    setscroll();
  }

  // Find i position of first selected item in it..itend
  bool Eqw_find_multistring_pos(const_iterateur it,const_iterateur itend,int & i,int &nrows,bool active_search=false){
    int t;
    nrows=itend-it;
    for (i=0;it!=itend;++it,++i){ // find row
      if (it->type!=_EQW || it->_EQWptr->g.type!=_STRNG)
	return false;
      if (Eqw_adjust_xy(*it,t,t,t,t,active_search))
	break;
    }
    if (it==itend)
      return false;
    return true;
  }

  // Assumes it points to the beginning of a string, selects position i
  void Eqw_select_multistring_pos(iterateur it,int i){
    iterateur jt=(it+i);
    Eqw_select(*jt,true);
  }

  // find selection position in it..itend 
  // Return true if at least 1 item is selected
  bool Eqw_find_vector_pos(const_iterateur it,const_iterateur itend,int & i,int &nrows){
    int t;
    nrows=itend-it;
    for (i=0;it!=itend;++it,++i){ // find row in i
      if (Eqw_adjust_xy(*it,t,t,t,t))
	break;
    }
    if (it==itend){
      --i;
      return false;
    }
    return true;
  }

  // Find i,j position of first selected item in it..itend
  bool Eqw_find_matrix_pos(const_iterateur it,const_iterateur itend,int & i,int &j,int &nrows,int & ncols,bool active_search=false){
    // (incomplete) check for a matrix: the first element must be a vector
    if (it->type!=_VECT || it->_VECTptr->empty() )
      return false;
    gen & tmp=it->_VECTptr->back();
    if (tmp.type!=_EQW || tmp._EQWptr->g !=at_makevector)
      return false;
    // find selection position and move down
    int t;
    nrows=itend-it;
    int c=it->_VECTptr->size();
    const_iterateur it0=it;
    for (;it0!=itend;++it0){ // more complete matrix check
      if (it0->type!=_VECT || it0->_VECTptr->size()!=c)
	return false;
    }
    for (i=0;it!=itend;++it,++i){ // find row
      if (Eqw_adjust_xy(*it,t,t,t,t,active_search))
	break;
    }
    if (it==itend || it->type!=_VECT )
      return false;
    // find column
    const_iterateur jt=it->_VECTptr->begin(),jtend=it->_VECTptr->end()-1;
    ncols=jtend-jt;
    for (j=0;jt!=jtend;++jt,++j){
      if (Eqw_adjust_xy(*jt,t,t,t,t,active_search))
	break;
    }
    if (jt==jtend)
      return false;
    return true;
  }

  // Assumes it points to the beginning of the matrix, selects position i,j
  void Eqw_select_matrix_pos(iterateur it,int i,int j){
    iterateur jt=(it+i)->_VECTptr->begin()+j;
    Eqw_select(*jt,true);
  }

  // increase selection up (like HP49 eqw Up key)
  // x,y is the cursor position
  void Eqw_select_up(gen & g,int x,int y,int mode){
    if (g.type==_EQW){ // terminal -> select
      g._EQWptr->selected=true;
      return;
    }
    if (g.type!=_VECT || g._VECTptr->empty())
      return;
    vecteur & v=*g._VECTptr;
    iterateur it=v.begin(),itend=v.end()-1;
    if (v.back().type!=_EQW)
      setsizeerr();
    eqwdata & w=*v.back()._EQWptr;
    int i,j,nrows,ncols;
    if ( mode && w.g==at_makevector && Eqw_find_matrix_pos(it,itend,i,j,nrows,ncols) ){
      --i;
      if (i<0)
	i=nrows-1;
      Eqw_select(g,false);
      Eqw_select_matrix_pos(it,i,j);
      return;
    }
    if ( mode && w.g==at_multistring && Eqw_find_multistring_pos(it,itend,i,nrows) ){
      --i;
      if (i<0)
	i=nrows-1;
      Eqw_select(g,false);
      Eqw_select_multistring_pos(it,i);
      return;
    }
    // find a box containing the cursor
    int x0,y0,w0,h0;
    attributs attr(0,0,0);
    bool selected;
    for (;it!=itend;++it){
      Eqw_box_sizes(*it,w0,h0,x0,y0,attr,selected);
      if ( (x0<x) && (x0+w0>=x) && (y0<=y) && (y0+h0>=y) )
	break;
    }
    if (it==itend)
      return;
    // if the box is selected select g else recurse
    if (selected)
      Eqw_select(g,true);
    else
      Eqw_select_up(*it,x,y,mode);
  }

  void Eqw::select_up(int mode){
    begin_sel=-1;
    end_sel=-1;
    // mode is 1 for shifted key
    Eqw_select_up(data,xcur,ycur,mode);
    adjust_xy_sel();
    if (cb_select)
      cb_select(get_selection().print().c_str());
    redraw();
  }

  // decrease selection up (like HP49 eqw Down key)
  void Eqw_select_down(gen & g,int x,int y,int mode, Eqw * eq){
    if (g.type==_EQW){ // terminal -> deselect and activate
      eqwdata & e=*g._EQWptr;
      e.selected=false;
      if (eq->modifiable){
	e.active=true;
	eq->active_pos=0;
	if (e.g.type==_STRNG )
	  eq->need_active_parse=false;
	else {
	  eq->need_active_parse=true;
	  e.g=string2gen(e.g.print(),false);
	  e.g.subtype=1;
	}
      }
      return;
    }
    if (g.type!=_VECT || g._VECTptr->empty() )
      return;
    vecteur & v=*g._VECTptr;
    iterateur it=v.begin(),itend=v.end()-1;
    if (itend->type!=_EQW)
      setsizeerr();
    eqwdata & w=*itend->_EQWptr;
    if (w.selected){ // deselect operator (sommet of the symbolic)
      w.selected=false;
      if (itend-it==1)
	return;
    }
    int i,j,nrows,ncols;
    if ( mode && w.g==at_makevector && Eqw_find_matrix_pos(it,itend,i,j,nrows,ncols) ){
      ++i;
      if (i>=nrows)
	i=0;
      Eqw_select(g,false);
      Eqw_select_matrix_pos(it,i,j);
      return;
    }
    if ( mode && w.g==at_multistring && Eqw_find_multistring_pos(it,itend,i,nrows) ){
      ++i;
      if (i>=nrows)
	i=0;
      Eqw_select(g,false);
      Eqw_select_multistring_pos(it,i);
      return;
    }
    // not selected, find the 1st selected element if any
    int x0,y0,w0,h0;
    attributs attr(0,0,0);
    bool selected;
    for (;it!=itend;++it){
      Eqw_box_sizes(*it,w0,h0,x0,y0,attr,selected);
      if (selected)
	break;
    }
    if (selected){ // deselect all elements except this one
      iterateur it1=v.begin();
      bool des=false;
      int tmp;
      for (;it1!=itend;++it1){
	if (it1!=it){ // if *it1 was selected deselect it
	  if (Eqw_adjust_xy(*it1,tmp,tmp,tmp,tmp)){
	    des=true;
	    Eqw_select(*it1,false);
	  }
	}
      }
      if (!des){ // nothing else was selected, recurse
	Eqw_select_down(*it,x,y,mode,eq);
      }
      return;
    }
    it=v.begin();
    for (;it!=itend;++it){
      Eqw_box_sizes(*it,w0,h0,x0,y0,attr,selected);
      if ( (x0<=x) && (x0+w0>=x) && (y0<=y) && (y0+h0>=y) )
	break;
    }
    if (it!=itend)
      Eqw_select_down(*it,x,y,mode,eq);
  }

  void Eqw::select_down(int mode){
    begin_sel=-1;
    end_sel=-1;
    Eqw_select_down(data,xcur,ycur,mode,this);
    adjust_xy_sel();
    if (cb_select)
      cb_select(get_selection().print().c_str());
    redraw();
  }

  gen Eqw_data2gen(const gen & g){
    if (g.type==_EQW){
      eqwdata & e=*g._EQWptr;
      if ( e.g.type==_STRNG && e.active && e.g.subtype )
	 return *e.g._STRNGptr;
      return e.g;
    }
    if (g.type!=_VECT)
      return g;
    vecteur & v=*g._VECTptr;
    const_iterateur it=v.begin(),itend=v.end()-1;
    vecteur res;
    if (g.subtype==_HIST__VECT){
      vecteur tmp(2);
      for (;it!=itend;++it){
	tmp[0]=Eqw_data2gen(*it);
	++it;
	if (it==itend)
	  break;
	tmp[1]=Eqw_data2gen(*it);
	res.push_back(tmp);
      }
      return gen(res,_HIST__VECT);
    }
    res.reserve(itend-it);
    for (;it!=itend;++it){
      res.push_back(Eqw_data2gen(*it));
    }
    gen f=Eqw_data2gen(*it);
    if (f==at_makesuite)
      return gen(res,_SEQ__VECT);
    if (f==at_makevector)
      return res;
    if (f==at_program && res.size()==2)
      res.insert(res.begin()+1,res.front()*zero);
    gen arg=gen(res,g.subtype);
    if (f.type==_FUNC) {
      if (res.size()==1) 
	return symbolic(*f._FUNCptr,res.front());
      else {
	if (f==at_multistring){
	  gen tmp=_multistring(res);
	  return tmp;
	}
	return symbolic(*f._FUNCptr,arg);
      }
    }
    else
      return f(arg,0);
  }

  gen Eqw_selected2gen(Eqw * eq,const gen & g,bool & selected,bool search_active=false){
    if (g.type==_EQW){
      eqwdata & e=*g._EQWptr;
      if (search_active)
	selected = e.active;
      else
	selected = e.selected;
      if (!selected)
	return Eqw_nullstring();
      if (e.g.type!=_STRNG)
	return e.g;
      if (e.active && e.g.subtype )
	return *e.g._STRNGptr; // parse
      // begin_sel/end_sel
      string res=*e.g._STRNGptr;
      if (!e.active)
	res=Eqw_extract_string(*e.g._STRNGptr,eq->begin_sel,eq->end_sel);
      return string2gen(res,false);
    }
    selected=false;
    if (g.type!=_VECT)
      return g;
    vecteur & v=*g._VECTptr;
    const_iterateur it=v.begin(),itend=v.end()-1;
    if (itend->type==_EQW && itend->_EQWptr->g==at_multistring){
      string res;
      if (!Eqw_multistring_selection(it,itend,search_active))
	return string2gen(res,false);
      selected=true;
      if (it==itend) // selection inside one string
	return Eqw_selected2gen(eq,*it,selected,search_active);
      res=Eqw_extract_string(*it->_EQWptr->g._STRNGptr,eq->begin_sel,-1)+'\n';
      ++it;
      for (;it!=itend;++it)
	res=res+*it->_EQWptr->g._STRNGptr+'\n';
      res=res+Eqw_extract_string(*itend->_EQWptr->g._STRNGptr,-1,eq->end_sel);
      return string2gen(res,false);
    }
    int w0,h0,x0,y0,tmp;
    attributs attr(0,0,0);
    Eqw_box_sizes(*itend,w0,h0,x0,y0,attr,selected,search_active);
    if (selected)
      return Eqw_data2gen(g);
    vecteur res;
    res.reserve(itend-it);
    for (;it!=itend;++it){
      gen tmp=Eqw_selected2gen(eq,*it,selected,search_active);
      if (selected)
	res.push_back(tmp);
    }
    selected=!res.empty();
    if (!selected)
      return zero;
    if (res.size()==1) 
      return res.front();
    gen f=Eqw_data2gen(*it);
    if (f==at_makesuite)
      return gen(res,_SEQ__VECT);
    if (f==at_makevector)
      return res;
    if (f==at_program && res.size()==2)
      res.insert(res.begin()+1,res.front()*zero);
    gen arg=gen(res,g.subtype);
    if (f.type==_FUNC)
      return symbolic(*f._FUNCptr,arg);
    else
      return f(arg,0);
  }

  // return a pointer to the selected object
  // it will modify g if for example a subsum of a sum is selected
  gen * Eqw_selected(gen & g,attributs & attr,int windowhsize,vecteur & position,bool active_search){
    // FIXME begin_sel/end_sel
    int x0,y0,w0,h0,tmp;
    bool selected;
    if (!Eqw_adjust_xy(g,tmp,tmp,tmp,tmp,active_search))
      return 0;
    Eqw_box_sizes(g,w0,h0,x0,y0,attr,selected,active_search);
    if (selected)
      return &g;
    if (g.type!=_VECT)
      return 0;
    vecteur & v=*g._VECTptr;
    iterateur it=v.begin(),itend=v.end()-1;
    for (;it!=itend;++it){ // search first selected item
      Eqw_box_sizes(*it,w0,h0,x0,y0,attr,selected,active_search);
      if (selected)
	break;
    }
    if (it==itend){
      it=v.begin();
      for (;it!=itend;++it){
	if (Eqw_adjust_xy(*it,tmp,tmp,tmp,tmp,active_search)){
	  position.push_back(int(it-v.begin()));
	  return Eqw_selected(*it,attr,windowhsize,position,active_search);
	}
      }
      return 0;
    }
    iterateur itb=it;
    position.push_back(int(it-v.begin()));
    // find the end of the selection
    ++it;
    for (;it!=itend;++it){
      Eqw_box_sizes(*it,w0,h0,x0,y0,attr,selected,active_search);
      if (!selected)
	break;
    }
    --it;
    Eqw_box_sizes(*it,w0,h0,x0,y0,attr,selected,active_search);
    if (it==itb)
      return &*it;
    // arrange v
    if (itb==v.begin() && it+1==itend){
      position.pop_back();
      return &g;
    }
    vecteur subop(itb,it+1);
    subop.push_back(v.back());
    gen temp=Eqw_data2gen(subop);
    eqwdata e=Eqw_total_size(*it);
    *it=Eqw_compute_size(temp,attr,windowhsize);
    *it=Eqw_translate(*it,e.x,e.y);
    int pos=itb-v.begin();
    v.erase(itb,it);
    return &v[pos];
  }

  // make a free copy of g
  gen Eqw_copy(const gen & g){
    if (g.type==_EQW)
      return *g._EQWptr;
    if (g.type!=_VECT)
      return g;
    vecteur & v = *g._VECTptr;
    const_iterateur it=v.begin(),itend=v.end();
    vecteur res;
    res.reserve(itend-it);
    for (;it!=itend;++it)
      res.push_back(Eqw_copy(*it));
    return gen(res,g.subtype);
  }

  // move selection right (like HP49 eqw Right key)
  void Eqw_select_rightleft(gen & g,int windowhsize,bool right,int mode){
    if (g.type!=_VECT)  // we are on a terminal, nothing to do
      return;
    // find 1st object with something selected
    vecteur & v=*g._VECTptr;
    iterateur it=v.begin(),itend=v.end()-1;
    eqwdata & w=*itend->_EQWptr;
    int i,j,nrows,ncols,tmp;
    if ( (mode) && (w.g==at_makevector) && Eqw_find_matrix_pos(it,itend,i,j,nrows,ncols) ){
      if (right)
	++j;
      else
	--j;
      if (j<0)
	j=ncols-1;
      if (j>=ncols)
	j=0;
      Eqw_select(g,false);
      Eqw_select_matrix_pos(it,i,j);
      return;
    }
    for (;it!=itend;++it){
      if (Eqw_adjust_xy(*it,tmp,tmp,tmp,tmp))
	break;
    }
    if (it==itend)
      return;
    iterateur it0=it; // save first selected
    // find first non selected
    if (right || (mode==2) ){
      ++it;
      for (;it!=itend;++it){
	if (!Eqw_adjust_xy(*it,tmp,tmp,tmp,tmp))
	  break;
      }
      if (mode==2 && it0==v.begin() && it==itend)
	return;
      --it;
    }
    // it -> last selected if moving right or exchanging selection right/left
    int x0,y0,w0,h0;
    attributs attr(0,0,0);
    bool selected;
    Eqw_box_sizes(*it,w0,h0,x0,y0,attr,selected);
    if (!selected){
      Eqw_select_rightleft(*it,windowhsize,right,mode);
      return;
    }
    iterateur it1=it;
    if (right){
      ++it;
      if (it==itend)
	it=v.begin();
    }
    else {
      it=it0;
      if (it==v.begin())
	it=itend;
      --it;
    }
    if (mode==2){ // exchange it0->it1 with it
      if (it1==it0){
	gen tmp=*it;
	*it=*it0;
	*it0=tmp;
      }
      else {
	vecteur tmpv(it0,it1+1);
	tmpv.push_back(*itend);
	gen sel=Eqw_data2gen(tmpv);
	sel=Eqw_compute_size(sel,attr,windowhsize);
	Eqw_select(sel,true);
	gen tmp=*it;
	*it=sel;
	*it1=tmp;
	v.erase(it0,it1);
      }
      vecteur position;
      gen g_copy=Eqw_copy(g);
      gen * ptr=Eqw_selected(g,attr,windowhsize,position);
      if (ptr){
	if (ptr->type==_VECT && !ptr->_VECTptr->empty() && ptr->_VECTptr->back().type==_EQW && ptr->_VECTptr->back()._EQWptr->g==at_multistring){
	  g=g_copy;
	  return;
	}
	g=Eqw_compute_size(Eqw_data2gen(g),attr,windowhsize);
	const_iterateur it=position.begin(),itend=position.end();
	ptr=&g;
	for (;it!=itend;++it){
	  ptr=&(*ptr->_VECTptr)[it->val];
	}
	Eqw_select(*ptr,true);
      }
      return;
    }
    else
      Eqw_select(g,false);
    Eqw_select(*it,true);
  }

  void Eqw::select_right(int mode){
    begin_sel=-1;
    end_sel=-1;
    Eqw_select_rightleft(data,w(),true,mode);
    adjust_xy_sel();
    if (cb_select)
      cb_select(get_selection().print().c_str());
    redraw();
  }

  void Eqw::select_left(int mode){
    begin_sel=-1;
    end_sel=-1;
    Eqw_select_rightleft(data,w(),false,mode);
    adjust_xy_sel();
    if (cb_select)
      cb_select(get_selection().print().c_str());
    redraw();
  }

  /* // modify g in place
  void Eqw_eval_function(const gen & f,gen & g,attributs iattr,int windowhsize){
    
    attributs attr(0,0,0);
    vecteur position;
    gen g_copy=Eqw_copy(g);
    gen * ptr=Eqw_selected(g,attr,windowhsize,position);
    if (!ptr) // should begin entry mode here
      return; 
    if (ptr->type==_VECT && !ptr->_VECTptr->empty() && ptr->_VECTptr->back().type==_EQW && ptr->_VECTptr->back()._EQWptr->g==at_multistring){
      g=g_copy;
      return;
    }
    gen tmp=Eqw_data2gen(*ptr);
    tmp=f(eval(tmp));
    * ptr = Eqw_compute_size(tmp,attr,windowhsize);
    tmp=Eqw_data2gen(g);
    g=Eqw_compute_size(tmp,iattr,windowhsize);
    const_iterateur it=position.begin(),itend=position.end();
    ptr=&g;
    for (;it!=itend;++it){
      ptr=&(*ptr->_VECTptr)[it->val];
    }
    Eqw_select(*ptr,true);
  }
  */

  void Eqw::save_data(){
    lastdata=Eqw_copy(data);
    if (max_history_size){
      undo_history.push_back(get_data());
      if (undo_history.size()>2*max_history_size){
	iterateur it=undo_history.begin();
	undo_history.erase(it,it+max_history_size);
      }
    }
  }

  void Eqw::rcl_data(){
    std::swap<gen>(data,lastdata);
    if (!undo_history.empty()){
      undo_history.back()=get_data();
    }
    adjust_xy_sel();
  }

  void Eqw::eval_function(const gen & f){
    bool save_interrupt_button=interrupt_button;
    interrupt_button = true;
    save_data();
    gen g(get_selection());
    try {
      if (f.type==_FUNC && f._FUNCptr->quoted)
	g=f(g,0);
      else
	g=f(eval(g),0);
    } catch (...){
      g=undef;
    }
    replace_selection(g,0);
    interrupt_button=save_interrupt_button;
    adjust_xy_sel();
    redraw();
  }

  gen Eqw::get_selection(){
    bool has_selection;
    return Eqw_selected2gen(this,data,has_selection);    
  }

  gen Eqw::get_data(){
    return Eqw_data2gen(data);
  }

  void Eqw::set_data(const gen & g){
    data=Eqw_compute_size(g,attr,w());
    eqwdata e = Eqw_total_size(data);
    ytop = e.y;
    xleft = e.x;
    setscroll();
  }

  bool Eqw_replace_selection(const gen & f,Eqw * eqwptr,int windowhsize,bool active_search=false){
    gen & g = eqwptr->data;
    attributs attr(0,0,0);
    vecteur position;
    gen save_g=Eqw_copy(g);
    gen * ptr=Eqw_selected(g,attr,windowhsize,position,active_search);
    if (!ptr) {
      g=save_g;
      return false; 
    }
    if (g.type==_VECT && g.subtype==_HIST__VECT && !position.empty()){
      if ( (position.front().val%2) ^ rpn_mode){
	g=save_g;
	return false;
      }
    }
    bool stringrep= !active_search && ptr->type==_EQW && ptr->_EQWptr->g.type==_STRNG && eqwptr->begin_sel>=0 && eqwptr->end_sel>=0 ;
    bool multistringrep= ptr->type==_VECT && !ptr->_VECTptr->empty() && ptr->_VECTptr->back().type==_EQW && ptr->_VECTptr->back()._EQWptr->g==at_multistring;
    if (multistringrep){ // check if the whole multistring is replaced or not
      *ptr=Eqw_nullstring(eqwptr->attr,0);
      multistringrep= in_multistring(eqwptr->data)!=-1;
    }
    if (stringrep || multistringrep){
      g=save_g;
      eqwptr->remove_selection();
      vecteur position;
      gen * act=Eqw_selected(eqwptr->data,eqwptr->attr,eqwptr->w(),position,1);
      string s;
      if (f.type==_STRNG)
	s=*f._STRNGptr;
      else
	s=f.print();
      eqwptr->handle_text(s,act);
      return true;
    }
    if (g.type==_VECT && g.subtype==_HIST__VECT && position.size()>=1 && position.size()<=2){
      int pos=position.front().val; 
      bool doit=pos>=0 && pos<g._VECTptr->size();
      if (doit){
	if (position.size()==2){
	  doit=false;
	  if (is_zero(position.back())){ 
	    gen gtmp=g[pos];
	    if (gtmp.type==_VECT && gtmp._VECTptr->back().type==_EQW && gtmp._VECTptr->back()._EQWptr->g==at_expr)
	      doit=true;
	  }
	}
      }
      if (doit){ 
	g = save_g;
	Eqw_select(g,false,active_search);
	vecteur & v = *g._VECTptr;
	int s=v.size(),newdx;
	eqwdata olde=Eqw_total_size(v[pos]);
	if (position.size()==2){
	  gen vposfront=Eqw_data2gen(v[pos]);
	  gen vposfrontf=vposfront._SYMBptr->feuille;
	  if (vposfrontf.type==_VECT && !vposfrontf._VECTptr->empty()){
	    vecteur vposfrontv=*vposfrontf._VECTptr;
	    vposfrontv[0]=f;
	    vposfrontf=gen(vposfrontv,vposfrontf.subtype);
	  }
	  else
	    vposfrontf=f;
	  v[pos]=Eqw_compute_size(symbolic(at_expr,vposfrontf),olde.eqw_attributs,windowhsize);
	}
	else {
	  v[pos]=Eqw_compute_size(f,olde.eqw_attributs,windowhsize);
	}
	eqwdata newe=Eqw_total_size(v[pos]);
	int deltady=newe.dy-olde.dy;
	if (pos%2){
	  int xshift=int(fl_width("    "));;
	  if (newe.dx+4<windowhsize-eqwptr->attr.fontsize){
	    if (center_history)
	      xshift=(windowhsize-newe.dx-4)/2;
	    else
	      xshift=windowhsize-newe.dx-4-eqwptr->attr.fontsize;
	  }
	  newdx=newe.dx+xshift;
	  v[pos]=Eqw_translate(v[pos],xshift-newe.x,olde.y-newe.y-deltady);
	}
	else {
	  newdx=newe.dx+olde.x;
	  v[pos]=Eqw_translate(v[pos],olde.x-newe.x,olde.y-newe.y-deltady);
	}
	if (position.size()==2)
	  Eqw_select(v[pos]._VECTptr->front(),true,active_search);
	else
	  Eqw_select(v[pos],true,active_search);
	// translate the rest of history
	for (++pos;pos<s;++pos){
	  v[pos]=Eqw_translate(v[pos],0,-deltady);
	}
	--pos;
	if (v[pos].type==_EQW){ // should be true!
	  eqwdata & e = *v[pos]._EQWptr;
	  e.dx=max(e.dx,newdx);
	  eqwptr->xleft=e.dx;
	}
	eqwptr->setscroll();
	return true;
      }
    }
    eqwdata olde=Eqw_total_size(*ptr);
    *ptr=Eqw_compute_size(f,olde.eqw_attributs,windowhsize);
    gen tmp=Eqw_data2gen(g);
    g=Eqw_compute_size(tmp,eqwptr->attr,windowhsize);
    const_iterateur it=position.begin(),itend=position.end();
    ptr=&g;
    for (;it!=itend;++it){
      ptr=&(*ptr->_VECTptr)[it->val];
    }
    Eqw_select(*ptr,true,active_search);
    return true;
  }

  bool Eqw::replace_selection(const gen & f,bool active_search){
    save_data();
    bool res=Eqw_replace_selection(f,this,w(),active_search);
    if (!active_search)
      fl_select();
    adjust_xy_sel();
    redraw();
    return res;
  }

  // deselect and activate
  bool Eqw_deselect_and_activate(gen & g){
    if (g.type==_EQW){
      if (g._EQWptr->selected){
	g._EQWptr->selected=false;
	g._EQWptr->active=true;
	return true;
      }
      else
	return false;
    }
    if (g.type!=_VECT)
      return false;
    vecteur & v = *g._VECTptr;
    iterateur it=v.begin(),itend=v.end()-1;
    if (Eqw_deselect_and_activate(*itend))
      return true;
    for (;it!=itend;++it){
      if (Eqw_deselect_and_activate(*it))
	return true;
    }
    return false;
  }

  void Eqw::deselect_and_activate(){
    begin_sel=-1;
    end_sel=-1;
    active_pos=0;
    Eqw_deselect_and_activate(data);
  }
  
  bool Eqw_desactivate_and_select(gen & g,bool need_active_parse){
    if (g.type==_EQW){
      if (g._EQWptr->active){
	g._EQWptr->active=false;
	g._EQWptr->selected=true;
	if (need_active_parse && g._EQWptr->g.type==_STRNG )
	  g._EQWptr->g=gen(*g._EQWptr->g._STRNGptr);
	return true;
      }
      else
	return false;
    }
    if (g.type!=_VECT)
      return false;
    vecteur & v = *g._VECTptr;
    iterateur it=v.begin(),itend=v.end()-1;
    if (Eqw_desactivate_and_select(*itend,need_active_parse))
      return true;
    for (;it!=itend;++it){
      if (Eqw_desactivate_and_select(*it,need_active_parse))
	return true;
    }
    return false;
  }

  void Eqw::desactivate_and_select(){
    begin_sel=-1;
    end_sel=-1;
    Eqw_desactivate_and_select(data,need_active_parse);
  }
  
  // if nothing selected, select active -> select something
  // Parse selection
  void Eqw::insure_something_selected(){
    bool has_selection=false;
    gen g=Eqw_selected2gen(this,data,has_selection);
    if (!has_selection){
      // Check for something active, desactive and select
      g=Eqw_selected2gen(this,data,has_selection,1);
      if (has_selection){
	desactivate_and_select();
      }
      else {
	select_up(0);
	g=Eqw_selected2gen(this,data,has_selection);
      }
    }
  }

  bool Eqw_parse_desactivate(gen & g,Eqw * eq){
    if (g.type==_EQW){
      eqwdata & e=*g._EQWptr;
      if (!e.active)
	return false;
      e.active=false;
      e.selected=true;
      if (e.g.type==_STRNG && eq->need_active_parse){
	try {
	  gen g1=gen(*e.g._STRNGptr);
	  e.g=g1;
	  eq->replace_selection(g1);
	}
	catch (std::runtime_error & er){
	  cerr << er.what() << endl;
	}
      }
      return true;
    }
    if (g.type!=_VECT)
      return false;
    vecteur & v =*g._VECTptr;
    iterateur it=v.begin(),itend=v.end();
    for (;it!=itend;++it){
      if (Eqw_parse_desactivate(*it,eq))
	return true;
    }
    return false;
  }

  bool Eqw::parse_desactivate(){
    begin_sel=-1;
    end_sel=-1;
    if (!Eqw_parse_desactivate(data,this))
      return false;
    return true;
  }

  // return true if something selected inside or whole selected
  bool Eqw_is_selected(const gen & g,bool inside=false,bool active_search=false){
    if (g.type==_EQW){
      if (active_search)
	return g._EQWptr->active;
      else
	return g._EQWptr->selected;
    }
    if (g.type!=_VECT || g._VECTptr->empty())
      return false;
    if (Eqw_is_selected(g._VECTptr->back(),inside,active_search))
      return true;
    if (!inside)
      return false;
    const_iterateur it=g._VECTptr->begin(),itend=g._VECTptr->end();
    for (;it!=itend;++it){
      if (Eqw_is_selected(*it,inside,active_search))
	return true;
    }
    return false;
  }

  bool Eqw::is_selected(bool inside,bool active_search){
    return Eqw_is_selected(data,inside,active_search);
  }

  gen Eqw_nullstring(){
    gen tmps=string2gen("",false);
    tmps.subtype=1;
    return tmps;
  }

  gen Eqw_nullhistlevel(){
    gen hh1,hh2(string2gen("",false));
    if (pretty_input)
      hh1=Eqw_nullstring();
    else
      hh1=symbolic(at_expr,gen(makevecteur(string2gen(";",false),maple_mode),_SEQ__VECT));
    gen hh=gen(vecteur(1,makevecteur(hh1,hh2)),_HIST__VECT);
    return hh;
  }

  bool is_semi_expr(const gen & g){
    if (!g.is_symb_of_sommet(at_expr))
      return false;
    gen tmp=g._SYMBptr->feuille;
    return tmp.type==_VECT && tmp.subtype==_SEQ__VECT && tmp._VECTptr->front().type==_STRNG && *tmp._VECTptr->front()._STRNGptr==";";
  }

  gen Eqw_nullstring(const attributs & a,int windowhsize){
    gen g=Eqw_compute_size(Eqw_nullstring(),a,windowhsize);
    Eqw_select(g,true,1);
    return g;
  }

  void Eqw_remove_selection(gen & g,int & pos,Eqw * eq){
    if (g.type==_EQW){
      eqwdata & e=*g._EQWptr;
      if (e.g.type==_STRNG){
	eq->need_active_parse=false;
	string s = *e.g._STRNGptr;
	int ss=s.size();
	if (ss){
	  int begin_sel=max(0,min(eq->begin_sel,ss-1));
	  int end_sel=ss;
	  if (eq->end_sel>=0)
	    end_sel=min(eq->end_sel,ss);
	  if (end_sel<begin_sel)
	    std::swap<int>(begin_sel,end_sel);
	  if (end_sel<ss)
	    s = s.substr(0,begin_sel)+s.substr(end_sel,ss-end_sel);
	  else
	    s = s.substr(0,begin_sel);
	  pos = begin_sel;
	}
	else
	  pos=0;
	e.g = string2gen(s,false);
      }
      else {
	eq->need_active_parse=true;
	e.g=string2gen(e.g.print(),false);
	e.g.subtype=1;
	pos=e.g._STRNGptr->size();
      }
      e.active=true;
      e.selected=false;
      return;
    }
    if (g.type!=_VECT)
      return;
    vecteur & v =*g._VECTptr;
    iterateur it=v.begin(),itend=v.end();
    if (Eqw_is_selected(*(itend-1),false)){
      g=Eqw_nullstring(v.back()._EQWptr->eqw_attributs);
      pos=0;
      return;
    }
    for (;it!=itend;++it){
      if (Eqw_is_selected(*it,false))
	break;
    }
    if (it!=itend){
      iterateur it0=it;
      int pos=it0-v.begin();
      for (;it!=itend;++it){
	if (!Eqw_is_selected(*it,false))
	  break;
      }
      if (v.back().type == _EQW && v.back()._EQWptr->g==at_multistring && it0->type==_EQW && it0->_EQWptr->g.type==_STRNG && eq->begin_sel>=0 && eq->end_sel>=0){
	--it; // it0 first selected, it last selected
	int ss=it->_EQWptr->g._STRNGptr->size();
	int begin_sel=min(eq->begin_sel,ss),end_sel=min(eq->end_sel,ss);
	Eqw_select(*it0,false);
	if (it==it0){
	  *it->_EQWptr->g._STRNGptr=it->_EQWptr->g._STRNGptr->substr(0,begin_sel)+it->_EQWptr->g._STRNGptr->substr(end_sel,ss-end_sel);
	  Eqw_select(*it,true,true);
	  eq->active_pos=begin_sel;
	  eq->need_active_parse=false;
	  return ;
	}
	begin_sel=min(eq->begin_sel,it0->_EQWptr->g._STRNGptr->size());
	*it0->_EQWptr->g._STRNGptr=it0->_EQWptr->g._STRNGptr->substr(0,begin_sel)+it->_EQWptr->g._STRNGptr->substr(end_sel,ss-end_sel);
	Eqw_select(*it0,true,true);
	eq->active_pos=begin_sel;
	eq->need_active_parse=false;
	++it0;
	++it;
	iterateur it1=it0;
	int deltay=0;
	int n=it0-v.begin();
	for (;it1!=it;++it1){
	  eqwdata e=Eqw_total_size(*it1);
	  deltay=deltay+e.dy;
	}
	v.erase(it0,it);
	int s=v.size();
	for (int j=n;j<s;++j){
	  v[j]=Eqw_translate(v[j],0,deltay);
	}
	return;
      }
      // Check whether it0 is a string, in that case erase in the string
      if (it0+1==it && it0->type==_EQW && it0->_EQWptr->g.type==_STRNG){
	int ss=it0->_EQWptr->g._STRNGptr->size();
	int begin_sel=max(min(eq->begin_sel,ss),0),end_sel=max(min(eq->end_sel,ss),0);
	Eqw_select(*it0,false);
	*it0->_EQWptr->g._STRNGptr=it0->_EQWptr->g._STRNGptr->substr(0,begin_sel)+it0->_EQWptr->g._STRNGptr->substr(end_sel,ss-end_sel);      
	Eqw_select(*it0,true,true);
	eq->active_pos=begin_sel;
	eq->need_active_parse=false;
	return ;
      }
      // delete from it0 to it
      // NOTE that for history, it must be of even size
      if (g.subtype==_HIST__VECT){
	if ( (it0-v.begin())%2)
	  --it0;
	if ( (it-v.begin())%2)
	  ++it;
      }
      v.erase(it0,it);
      if (v.size()==1){ // only the last item, i.e. operator
	g=Eqw_nullstring(v.back()._EQWptr->eqw_attributs);
	pos=0;
	return;
      }
      if (v.size()==2 
	  // && (v.back()._EQWptr->g==at_plus || v.back()._EQWptr->g==at_prod) 
	  ){
	g=v.front();
	Eqw_select(g,true);
	return;
      }
      /* it=v.begin(),itend=v.end();
	 for (;it!=itend;++it)
	 Eqw_select(*it,true); */
      if (pos>1 && pos<v.size())
	Eqw_select(v[pos-1],true);
      return;
    }
    // Remove 1st selection 
    it=v.begin();
    for (;it!=itend;++it){
      if (Eqw_is_selected(*it,true)){
	Eqw_remove_selection(*it,pos,eq);
	return;
      }
    }
  }

  void Eqw::remove_selection(){
    save_data();
    Eqw_remove_selection(data,active_pos,this);
    begin_sel=-1;
    end_sel=-1;
    redraw();
  }

  void Eqw::replace_down_left_activate(const gen & g){
    if (replace_selection(g)){
      select_down(0);
      select_left(0); 
      deselect_and_activate();
      if (in_multistring(data)==-1)
	need_active_parse=true;
    }
  }

  void handle_backspace(Eqw * eqwptr,int pos,vecteur * vptr){
    string s=*(*vptr)[pos]._EQWptr->g._STRNGptr;
    iterateur it=vptr->begin()+pos;
    vptr->erase(it);
    --pos;
    string s0=*(*vptr)[pos]._EQWptr->g._STRNGptr;
    eqwptr->active_pos=s0.size();
    s=s0+s;
    Eqw_select((*vptr)[pos],true,true);
    eqwptr->replace_selection(string2gen(s,false),1);
  }

  // act = 0 if nothing active or -> the active part
  // return true if the new selected part must be copied in clipboard
  bool Eqw::handle_key(unsigned char c,gen * act){
    if (!c || c>=0x80)
      return false;
    if (!act || need_active_parse){
      if (c=='(' || c==')'){ // if selection replace vector by sequences
	if (!act){
	  insure_something_selected();
	  gen g=get_selection();
	  if (!g.is_symb_of_sommet(at_makevector))
	    return false;
	  replace_selection(symbolic(at_makesuite,g._SYMBptr->feuille));
	  return true;
	}
      }
      if (c=='[' || c==']'){ // if selection replace seq by vector
	if (!act){
	  insure_something_selected();
	  gen g=get_selection();
	  if (!g.is_symb_of_sommet(at_makesuite))
	    return false;
	  replace_selection(symbolic(at_makevector,g._SYMBptr->feuille));
	  return true;
	}
      }
      if (!act && (c=='+' || c=='-' || c=='*' || c=='/' || c=='^' || c==',' || c=='<' || c=='>' || c=='=') ){
	insure_something_selected();
	gen g=get_selection();
	gen tmp;
	if (c==','){
	  if (g.type==_SYMB && g._SYMBptr->feuille.type==_VECT){
	    bool inside=g.is_symb_of_sommet(at_of) || g.is_symb_of_sommet(at_at);
	    int sub;
	    vecteur v;
	    if (inside){
	      if (g._SYMBptr->feuille._VECTptr->back().type==_VECT){
		v=*g._SYMBptr->feuille._VECTptr->back()._VECTptr;
		sub=g._SYMBptr->feuille._VECTptr->back().subtype;
	      }
	      else {
		v=makevecteur(g);
		sub=_SEQ__VECT;
	      }
	    }
	    else
	      v=*g._SYMBptr->feuille._VECTptr;
	    v.push_back(zero);
	    gen tmp;
	    if (inside)
	      tmp=symbolic(g._SYMBptr->sommet,gen(makevecteur(g._SYMBptr->feuille._VECTptr->front(),gen(v,sub)),g._SYMBptr->feuille.subtype));
	    else
	      tmp=symbolic(g._SYMBptr->sommet,gen(v,g._SYMBptr->feuille.subtype));
	    replace_selection(tmp);
	    return false;
	  }
	  if (g.type==_VECT){
	    vecteur v=*g._VECTptr;
	    v.push_back(zero);
	    replace_selection(gen(v,g.subtype));
	    return false;
	  }
	  if (g.type==_SYMB){
	    vecteur v(gen2vecteur(g._SYMBptr->feuille));
	    if (g._SYMBptr->sommet==at_integrate || g._SYMBptr->sommet==at_derive)
	      v.push_back(vx_var);
	    v.push_back(zero);
	    if (g._SYMBptr->sommet==at_integrate)
	      v.push_back(plus_one);
	    replace_selection(symbolic(g._SYMBptr->sommet,gen(v,_SEQ__VECT)));
	    return false;
	  }
	  tmp=at_makesuite;
	} // end if c==','
	else
	  tmp=gen(string("'")+char(c)+string("'"));
	if (tmp.type!=_FUNC)
	  setsizeerr();
	unary_function_ptr f=*tmp._FUNCptr;
	if ( g.is_symb_of_sommet(at_makesuite) && ( (c!='/' && c!='^') || (g._SYMBptr->feuille.type==_VECT && g._SYMBptr->feuille._VECTptr->size()==2) ) ){
	  replace_selection(symbolic(f,g._SYMBptr->feuille));
	  return true;
	}
	else {
	  tmp=gen(makevecteur(g,Eqw_nullstring()),_SEQ__VECT);
	  replace_down_left_activate(symbolic(f,tmp));
	  return false;
	}
      }
    } // end not active or active_parse
    if (act){
      // *act is active and is a terminal of type string
      string s=*act->_EQWptr->g._STRNGptr;
      int ss=s.size();
      active_pos=max(min(active_pos,ss),0);
      vecteur * vptr;
      int pos;
      switch (c){
      case 0x0b:
	if (active_pos>0)
	  --active_pos;
	else {
	  pos=in_multistring(data,vptr);
	  if (pos>0){ // back one line and active_pos maximal
	    Eqw_select(*act,false,true);
	    --pos;
	    Eqw_select((*vptr)[pos],true,true);
	    active_pos=RAND_MAX;
	  }
	}
	return 1;
      case 0x0c:
	if (active_pos<ss)
	  ++active_pos;
	else {
	  pos=in_multistring(data,vptr);
	  if (pos>=0 && pos<vptr->size()-1){ // forward one line and active_pos maximal
	    Eqw_select(*act,false,true);
	    ++pos;
	    Eqw_select((*vptr)[pos],true,true);
	    active_pos=0;
	  }
	}
	return 1;
      case 0x7f:
	if (active_pos>0){
	  s=s.substr(0,active_pos-1)+s.substr(active_pos,ss-active_pos);
	  --active_pos;
	}
	else {
	  pos=in_multistring(data,vptr);
	  if (pos>0){
	    handle_backspace(this,pos,vptr);
	    return 1;
	  }
	  else {
	    if (ss==0){
	      act->_EQWptr->active=false;
	      act->_EQWptr->selected=true;
	      begin_sel=-1;
	      end_sel=-1;
	      remove_selection();
	      return false;
	    }
	  }
	}
	break;
      default:
	s = s.substr(0,active_pos)+char(c)+s.substr(active_pos,ss-active_pos);
	++active_pos;
      }
      replace_selection(string2gen(s,false),1);
      return false;
    }
    // nothing active
    bool has_selection=false;
    gen g=Eqw_selected2gen(this,data,has_selection);
    if (has_selection){
      string tmp(1,c);
      gen tmps=string2gen(tmp,false);
      tmps.subtype=1;
      if (replace_selection(tmps)){
	// if nothing active, activate
	attributs a(0,0,0);
	vecteur position;
	gen * ptr =Eqw_selected(data,a,w(),position,1);
	if (!ptr){
	  deselect_and_activate();
	  active_pos=1;
	}
	if (in_multistring(data)==-1 && g.type!=_STRNG)
	  need_active_parse=true;
      }
      return false;
    }
    // nothing selected
    select_up(0);
    return true;
  }

  /* not used // return true if conversion needed and done
  bool string2vector(const string & s,vector<string> & v){
    int pos=s.find('\n'),newpos;
    int ss=s.size();
    if (pos<0 || pos >= ss)
      return false;
    v.clear();
    v.push_back(s.substr(0,pos));
    for (;pos<ss;){
      newpos=s.find('\n',pos);
      if (newpos<0 || newpos >= ss){
	v.push_back(s.substr(pos,ss-pos));
	return true;
      }
      v.push_back(s.substr(pos,newpos-pos));
      pos=newpos;
    }
  }
  */

  bool Eqw::handle_text(const string & paste_s_orig,gen * act){
    string paste_s(paste_s_orig);
    int ps=paste_s.size();
    if (ps>2 && paste_s[0]=='"' && paste_s[ps-1]=='"'){
      ps = ps-2; 
      paste_s=paste_s.substr(1,ps);
    }
    int nl=paste_s.find('\n');
    bool needmulti=(nl>=0 && nl<ps);
    if (act){ // insert s
      string s;
      if (act->type!=_EQW || act->_EQWptr->g.type!=_STRNG){
	cerr << "Error " << *act << endl;
	return false;
      }
      s=*act->_EQWptr->g._STRNGptr;
      int pos=max(min(active_pos,s.size()),0);
      active_pos=pos;
      int toend=s.size()-pos;
      s=s.substr(0,active_pos)+paste_s+s.substr(active_pos,toend);
      gen g=string2gen(s,false);
      if (!needmulti){
	if (replace_selection(g,1))
	  active_pos = pos+paste_s.size();
	return false;
      }
      vecteur * vptr;
      int n=in_multistring(data,vptr);
      if (n==-1){
	if (need_active_parse)
	  g=symbolic(at_expr,g);
	desactivate_and_select();
	if (replace_selection(g)){
	  if (need_active_parse)
	    select_down(0);
	  select_down(0);
	  select_left(0); 
	  pos = get_selection()._STRNGptr->size()-toend;
	  deselect_and_activate();
	  active_pos = max(pos,0);
	  need_active_parse=false;
	}
	return false;
      }
      // insert in current multistring
      s="";
      const_iterateur it=vptr->begin(),itend=vptr->end()-1;
      for (int i=0;i<n;++i,++it){
	if (it->type==_EQW && it->_EQWptr->g.type==_STRNG)
	  s += *it->_EQWptr->g._STRNGptr + '\n';
      }
      string ss=*act->_EQWptr->g._STRNGptr;
      s += ss.substr(0,active_pos)+paste_s+ss.substr(active_pos,ss.size()-active_pos);
      for (++it;it!=itend;++it){
	if (it->type==_EQW && it->_EQWptr->g.type==_STRNG)
	  s += '\n'+*it->_EQWptr->g._STRNGptr;
      }
      g=string2gen(s,false);
      desactivate_and_select();
      // count newline in paste_s, add to n, activate this line, cursor
      // at end of this line - the value of toend
      for (int i=0;i<ps;++i){
	if (paste_s[i]=='\n')
	  ++n;
      }
      select_up(0);
      vecteur position;
      attributs tmpattr(0,0,0);
      gen * ptr = Eqw_selected(data,tmpattr,w(),position);
      eqwdata olde=Eqw_total_size(*ptr);
      * ptr = Eqw_compute_size(g,olde.eqw_attributs,w());
      Eqw_select(data,false);
      Eqw_select((*ptr->_VECTptr)[n],true);
      g=get_selection();
      if (g.type==_STRNG){
	pos = g._STRNGptr->size();
	deselect_and_activate();
	need_active_parse = false;
	if (replace_selection(g,1))
	  active_pos = max(pos- toend,0) ;
	need_active_parse = false;
      }
      return false;
    }
    // remove selection and replace
    gen g=string2gen(paste_s,false);
    if (needmulti)
      g=symbolic(at_expr,g);
    replace_selection(g);
    return true;
  }

  bool Eqw::handle_text(const string & paste_s_orig){
    vecteur position;
    gen * act=Eqw_selected(data,attr,w(),position,1);
    return handle_text(paste_s_orig,act);
  }

  void cb_eqw_browser(Fl_Browser * b,void *){
  }

  // extern Fl_Output * Help_text;

  // Find a completion of s in v -> ans, return true if user OK
  // dx,dy=size of browser window
  bool handle_tab(const string & s,const vector<string> & v,int dx,int dy,string & ans){
    // search non ascii char in s starting from the end
    int ss=s.size();
    string res;
    for (int i=ss-1;i>=0;--i){
      if (isalpha(s[i]) || s[i]=='_' || (s[i]>='0' && s[i]<='9') )
	;
      else
	break;
      res=s[i]+res;
    }
    ss=res.size();
    Fl_Window * w = new Fl_Window(dx,dy);
    Fl_Return_Button * button0 = new Fl_Return_Button(2,2,dx/2-4,16);
    button0->shortcut(0xff0d);
    button0->label(gettext("OK"));
    Fl_Button * button1 = new Fl_Button(dx/2+2,2,dx/2-4,16);
    button1->shortcut(0xff1b);
    button1->label(gettext("Cancel"));
    Fl_Browser * browser = new Fl_Browser(2,20,dx,dy-22);
    browser->type(2);
    browser->label("Completions");
    browser->callback((Fl_Callback*)cb_eqw_browser);
    vector<string> vres;
    for (int k=0;k<v.size();++k){
      if (v[k].substr(0,ss)==res){
	vres.push_back(v[k]);
	browser->add(v[k].c_str());
      }
    }
    int vs=vres.size(),i=1,r;
    w->end();
    w->resizable(w);
    w->set_modal();
    if (vs){
      browser->value(1);
      w->show();
      Fl::focus(button1);
      for (;;) {
	Fl_Widget *o = Fl::readqueue();
	if (!o) Fl::wait();
	else {
	  if (o == button0) {r = 0; break;}
	  if (o == button1) {r = 1; break;}
	  if (o == w) { r=0; break; }
	  // show help, does not seem to refresh
	  i=browser->value();
	  if (i<=vs && i>0){
	    res=vres[i-1];
	    make_new_help(res.c_str());
	    // Help_text->redraw();
	  }
	}
      }
      w->hide();
      i=browser->value();
    }
    delete browser;
    delete button1;
    delete button0;
    delete w;
    if (r==0 && i<=vs && i>0){
      res=vres[i-1];
      res=res.substr(ss,res.size()-ss);
      ans=res;
      return true;
    }
    else
      return false;
  }


  // activate at x,y, return cursor position or -1, or -3 for fl_widget_pointer
  int Eqw_set_active(int x,int y,gen & g,Eqw * eq,bool inmultistring,gen * & activecellptr){
    activecellptr=0;
    if (g.type==_EQW){
      // x,y must be in the box
      eqwdata & e=*g._EQWptr;
      if (e.x>x || e.x+e.dx<x || e.y>y || e.y+e.dy<y)
	return -1; 
      activecellptr=&e.g;
      if (e.g.type==_POINTER_){
	return -3;
      }
      e.active=true;
      if (e.g.type==_STRNG)
	eq->need_active_parse=!inmultistring && e.g._STRNGptr->empty();
      else {
	eq->need_active_parse=true;
	e.g=string2gen(e.g.print(),false);
	e.g.subtype=1;
      }
      // binary search for active_pos
      return Eqw_binary_search_pos(e,x,y);
    }
    if (g.type!=_VECT)
      return -1;
    vecteur & v =*g._VECTptr;
    if (v.empty())
      return -1;
    iterateur it=v.begin(),itend=v.end()-1;
    if (!inmultistring)
      inmultistring = itend->type==_EQW && itend->_EQWptr->g==at_multistring;
    int pos;
    for (;it!=itend;++it){
      pos=Eqw_set_active(x,y,*it,eq,inmultistring,activecellptr);
      if (pos!=-1)
	return pos;
    }
    if (it->type==_EQW){ // change operator
      eqwdata & e=*it->_EQWptr;
      if (e.x>x || e.x+e.dx<x || e.y>y || e.y+e.dy<y)
	return -1;
      /* commented change of operator since it messes up beginners
      if (e.g.type==_FUNC && !e.g._FUNCptr->ptr->printsommet && e.g!=at_makesuite && e.g!=at_makelist && e.g!=at_makevector ){
	eqwdata f(e);
	f.g=string2gen(e.g._FUNCptr->ptr->s,false);
	int pos=Eqw_binary_search_pos(f,x,y);
	string ans,debut=f.g._STRNGptr->substr(0,pos);
	if (handle_tab(debut,completion_tab,eq->h()/2,eq->w()/2,ans)){
	  e.g=gen("'"+debut+ans+"'");
	  return -2;
	}
      }
      */ 
    }
    return -1;
  }
  gen * Eqw::set_active(int x,int y){
    deselect();
    gen * gptr;
    int pos=Eqw_set_active(x,y,data,this,false,gptr);
    if (pos==-3)
      return gptr;
    if (pos==-2){
      data=Eqw_compute_size(get_data(),attr,w());
      redraw();
      pos=-1;
    }
    if (pos==-1)
      Eqw_select(data,false,true);
    else
      active_pos=pos;
    return 0;
  }

  // Return 0 if the mouse cursor is not on a _POINTER_ object,
  // returns a pointer to the gen of type _POINTER_ otherwise
  gen * Eqw_is_pointer_active(Eqw * eq,const gen & g,int x,int y){
    if (g.type==_EQW){
      // x,y must be in the box
      eqwdata & e=*g._EQWptr;
      if (e.x>x || e.x+e.dx<x || e.y>y || e.y+e.dy<y)
	return 0; 
      if (e.g.type==_POINTER_)
	return &e.g;
      return 0;
    }
    if (g.type!=_VECT)
      return 0;
    vecteur & v =*g._VECTptr;
    if (v.empty())
      return 0;
    iterateur it=v.begin(),itend=v.end()-1;
    gen * pos;
    for (;it!=itend;++it){
      pos=Eqw_is_pointer_active(eq,*it,x,y);
      if (pos)
	return pos;
    }
    return 0;
  }

  // for multistring Up and Down should not select
  void Eqw_multistring_activate(Eqw * eq,int direction,int pos){
    gen save_data=Eqw_copy(eq->data);
    if (direction)
      eq->select_down(1);
    else
      eq->select_up(1);
    if (eq->get_selection().type==_STRNG){
      eq->deselect_and_activate();
      eq->active_pos=max(pos,0);
    }
    else {
      eq->data=save_data;
      eq->adjust_xy_sel();
    }
  }

  // return -1 if not in a multistring, position otherwise
  int in_multistring(const gen & data,vecteur * & vptr){
    if (data.type!=_VECT)
      return -1;
    vecteur & v =*data._VECTptr;
    iterateur it=v.begin(),itend=v.end();
    if (itend==it)
      return -1;
    --itend;
    if (itend->type==_EQW && itend->_EQWptr->g==at_multistring){
      for (;it!=itend;++it){
	if (it->type==_EQW && it->_EQWptr->active){
	  vptr = &v;
	  return it-v.begin();
	}
      }
      return -1;
    }
    for (;it!=itend;++it){
      int i=in_multistring(*it,vptr);
      if (i!=-1)
	return i;
    }
    return -1;
  }

  // return -1 if not in a multistring, position otherwise
  int in_multistring(const gen & data){
    vecteur * vptr;
    return in_multistring(data,vptr);
  }

  void handle_newline(Eqw * eqwptr){
    vecteur * vptr;
    int n=in_multistring(eqwptr->data,vptr);
    if (n!=-1){
      // add a new line here
      string s( *(*vptr)[n]._EQWptr->g._STRNGptr);
      int ss=s.size(),pos=max(min(eqwptr->active_pos,ss),0);
      string s1=s.substr(0,pos);
      string s2=s.substr(pos,ss-pos);
      (*vptr)[n]=Eqw_compute_size(string2gen(s1,false),eqwptr->attr,0);
      iterateur it=vptr->begin()+(n+1);
      vptr->insert(it,Eqw_compute_size(string2gen(s2,false),eqwptr->attr,0));
      Eqw_select((*vptr)[n],false,true); // desactivate line n
      Eqw_select((*vptr)[n+1],true,true); // activate line n+1
      eqwptr->active_pos=0;
      eqwptr->replace_selection(string2gen(s2,false),1);
    }
    else {
      // not in a multistring, transform current in a multistring
      bool parse=eqwptr->need_active_parse;
      eqwptr->need_active_parse=false;
      eqwptr->desactivate_and_select();
      gen g=eqwptr->get_selection();
      if (g.type==_STRNG)       // FIXME: should add newline at active_pos
	g=string2gen(*g._STRNGptr+'\n',false);
      bool res;
      if (parse){
	g=symbolic(at_expr,g);
	res=eqwptr->replace_selection(g);
	eqwptr->select_down(0);
      }
      else {
	res=eqwptr->replace_selection(g);
      }
      if (res){
	eqwptr->select_down(0);
	eqwptr->select_right(0);
	eqwptr->deselect_and_activate();
	eqwptr->need_active_parse=false;
      }
    }
  }

  bool switch_expr(Eqw * eqwptr){
    gen g=eqwptr->get_selection();
    if (g.type==_STRNG){
      eqwptr->replace_selection(symbolic(at_expr,g));
      return true;
    }
    if (g.is_symb_of_sommet(at_expr)){
      eqwptr->replace_selection(g._SYMBptr->feuille);
      return true;
    }
    return false;
  }

  int Eqw::handle(int event){
    int i=in_handle(event);
    if (i==-1)
      return 1;
    if (i)
      Fl::focus(this);
    return i;
  }

  int Eqw::in_handle(int event){
    if (event==FL_FOCUS){
      Fl::focus(this);
      return 1;
    }
    bool is_history=(data.type==_VECT && data.subtype==_HIST__VECT);
    bool complete_level_selected=false;
    eqwdata data_eqwdata=Eqw_total_size(data);
    if (is_history){ // check if a complete level is selected
      const_iterateur it=data._VECTptr->begin(),itend=data._VECTptr->end();
      for (;it!=itend;++it){
	eqwdata e=Eqw_total_size(*it);
	if (e.selected){
	  complete_level_selected=true;
	  break;
	}
      }
    }
    attributs attr;
    vecteur position;
    gen f,* act=Eqw_selected(data,attr,w(),position,1);
    char ch;
    if (act && act->type==_EQW && act->_EQWptr->g.type==_STRNG)
      ;
    else
      act=0;
    if (event==FL_RELEASE && Fl::event_button()== FL_MIDDLE_MOUSE ){
      int x,y;
      x=Fl::event_x()+xleft;
      y=Fl::event_y();
      deselect();
      gen * gptr=set_active(x,ytop-y);
      if (gptr && gptr->type==_POINTER_)
	// should call the handle method of the pointer
	return 1;
      Fl::paste(*this);
      return 1;
    }
    if (event==FL_PASTE) {
      handle_text(Fl::event_text(),act);
      return 1;
    }
    if (event==FL_KEYBOARD){
      /* if (Fl::event_state()== FL_CTRL || Fl::event_state()== FL_ALT)
	 return 0; */
      bool sel_changed=true;
      bool shifton= (Fl::event_state()== FL_SHIFT );
      int save_active_pos=active_pos;
      switch (Fl::event_key()){
      case FL_Left:
	if (act)
	  handle_key(0x0b,act);
	else {
	  if (!parse_desactivate())
	    select_left(!shifton); // it was shift_on
	}
	break;
      case FL_Right:
	if (act)
	  handle_key(0x0c,act);
	else {
	  if (!parse_desactivate())
	    select_right(!shifton);
	}
	break;
      case FL_Up:
	if (shifton && complete_level_selected){
	  select_left(0);
	  select_left(0);
	}
	else {
	  if (!parse_desactivate())
	    select_up(!shifton);
	  else 
	    if (!shifton)
	      Eqw_multistring_activate(this,0,save_active_pos);
	}
	break;
      case FL_Down:
	if (shifton && complete_level_selected){
	  select_right(0);
	  select_right(0);
	}
	else {
	  if (!parse_desactivate())
	    select_down(!shifton);
	  else 
	    if (!shifton)
	      Eqw_multistring_activate(this,1,save_active_pos);
	}
	break;
      case FL_Escape: 
	if (cb_escape)
	  cb_escape(this);
	else
	  parse_desactivate();
	redraw();
	return 1;
      case FL_Shift_L: case FL_Shift_R: 
      case FL_Caps_Lock: 
      case FL_Meta_L: case FL_Meta_R: case FL_Menu: case FL_Num_Lock:
      case FL_Insert: 
	return 1;
      case FL_Control_R: case FL_Control_L: case FL_Alt_L: case FL_Alt_R:
	return 0;
      case FL_BackSpace:
      case FL_Delete: 
	if (is_history && complete_level_selected && cb_backspace){
	  cb_backspace(this);
	  return 1;
	}
	if (modifiable){
	  if (act){
	    handle_key(0x7f,act);
	    return 1;
	  }
	  else {
	    f=get_selection();
	    if (f.type==_SYMB){
	      f=f._SYMBptr->feuille;
	      if (f.type==_VECT && !f.subtype && !ckmatrix(f))
		f.subtype=_SEQ__VECT;
	      replace_selection(f);
	    }
	    else
	      remove_selection();
	  }
	}
	break;
      case FL_Enter: case FL_KP_Enter:
	/*
	if (old_upper_window_state==upper_window_mtrw){
	  manage_upper_window(upper_window_mtrw,false);
	}
	else {	  
	  manage_upper_window(upper_window_hist,false);
	}
	input_value(eqw_data2gen(data).print().c_str());
	*/
	if (shifton)
	  handle_newline(this);
	else {
	  if (cb_enter)
	    cb_enter(this);
	  else
	    parse_desactivate();
	}
	redraw();
	return 1;
      case FL_Home: 
	xleft=data_eqwdata.x;
	sel_changed=false;
	break;
      case FL_End: 
	xleft=data_eqwdata.x+data_eqwdata.dx-w();
	sel_changed=false;
	break;
      case FL_Page_Up:
	ytop -= h();
	sel_changed=false;
	break;
      case FL_Page_Down: 
	ytop += h();
	sel_changed=false;
	break;
      default:
	if (modifiable){
	  ch=Fl::event_text()[0];
	  int es=Fl::event_state();
	  if (es== FL_ALT || es==1572864){ 
	    if (ch=='n' || ch=='f' || ch=='s' || ch=='e' )
	      return 0;
	    if (ch=='v'){
	      gen e;
	      select_user_var(100,200,e,0);
	      if (allow_immediate_eval){
		parse_desactivate();
		eval_function(e);
	      }
	      return 1;
	    }
	  }
	  if (ch>=17 && ch<=19) // Ctrl-Q R S
	    return 0;
	  if (ch==22){ // Ctrl-V
	    Fl::paste(*this);
	    return 1;
	  }
	  if (ch==26){ // Ctrl-Z
	    rcl_data();
	    return 1;
	  }
	  if (ch==9){
	    string s,ans;
	    if (act){
	      s=*act->_EQWptr->g._STRNGptr;
	      int ss=s.size();
	      active_pos=max(min(active_pos,ss),0);
	      s=s.substr(0,active_pos);
	      if (handle_tab(s,completion_tab,h()/2,w()/2,ans)){
		make_new_help((s+ans).c_str());
		handle_text(ans+"()");
	      }
	    }
	    else {
	      insure_something_selected();
	      gen gs=get_selection();
	      s=gs.print();
	      int pos=s.find('(');
	      if (pos>0 && pos<s.size())
		s=s.substr(0,pos);
	      if (gs.type==_SYMB && handle_tab(s,completion_tab,h()/2,w()/2,ans)){
		make_new_help((s+ans).c_str());
		gen gf=gen("'"+s+ans+"'");
		if (gf.type==_FUNC)
		  replace_selection(symbolic(*gf._FUNCptr,gs._SYMBptr->feuille));
	      }
	    }
	    return 1;
	  }
	  if (ch=='"'){
	    if (act &&  in_multistring(data)==-1){
	      // change mode, ! in multistring
	      need_active_parse=!need_active_parse;
	      redraw();
	      return 1;
	    }
	    if (switch_expr(this)){
	      redraw();
	      return 1;
	    }
	  }
	  sel_changed=handle_key(ch,act);
	} // end if (modifiable)
      }
      if (sel_changed){
	fl_select();
	setscroll();
      }
      return 1;
    } // end event==FL_KEYBOARD
    int x,y;
    x=Fl::event_x()+xleft;
    y=Fl::event_y();
    if (event==FL_PUSH ){
      if (Fl::event_button()!= FL_MIDDLE_MOUSE)
	// desactivate active cell if any
	parse_desactivate();
      xsel=x;
      ysel=ytop-y;
      xcur=xsel;
      ycur=ysel;
      if (Fl::event_button()!= FL_MIDDLE_MOUSE)
	deselect();
      redraw();
      gen * gptr=Eqw_is_pointer_active(this,data,x,ytop-y);
      if (gptr && gptr->type==_POINTER_ && gptr->subtype==_FL_WIDGET_POINTER){
	// call the handle method of the pointer
	Fl_Widget * flptr=(Fl_Widget *) gptr->_POINTER_val;
	flptr->handle(event);
	Fl::focus(this);
	return 1;
      }
      return 1;
    }
    if (event==FL_DRAG){
      if (Fl::event_button()!= FL_MIDDLE_MOUSE)
	deselect();
      redraw();
      if ( (absint(xsel-x)<=2) && (absint(ysel-(ytop-y))<2) ){
	redraw();
	return 1;
      }
      gen * gptr=Eqw_is_pointer_active(this,data,x,ytop-y);
      if (gptr && gptr->type==_POINTER_ && gptr->subtype==_FL_WIDGET_POINTER){
	// call the handle method of the pointer
	Fl_Widget * flptr=(Fl_Widget *) gptr->_POINTER_val;
	flptr->handle(event);
	Fl::focus(this);
	return 1;
      }
      if (Fl::event_button()!= FL_MIDDLE_MOUSE)
	select_rectangle(x,ytop-y);
      return 1;
    }
    if (event==FL_RELEASE){
      Fl::focus(this);
      deselect();
      redraw();
      gen * gptr=Eqw_is_pointer_active(this,data,x,ytop-y);
      if (gptr && gptr->type==_POINTER_ && gptr->subtype==_FL_WIDGET_POINTER){
	// call the handle method of the pointer
	Fl_Widget * flptr=(Fl_Widget *) gptr->_POINTER_val;
	flptr->handle(event);
	Fl::focus(this);
	return 1;
      }
      if (is_history && !modifiable && (absint(xsel-x)<=5) && (absint(ysel-(ytop-y))<5) ){
	gen * gptr=set_active(x,ytop-y);
	desactivate_and_select();
	select_rectangle(x,ytop-y);
	// Run modif button callback
	modify_history();
	Fl::focus(input);
	int s=strlen(input->value());
	input->position(s,s);
	return -1;
      }
      if ( (absint(xsel-x)<=2) && (absint(ysel-(ytop-y))<2) ){
	gen * gptr=set_active(x,ytop-y);
	if (Fl::event_clicks()){ // double-click
	  // x=xsel+10;
	  // y=ytop-ysel+4;
	  desactivate_and_select();
	  //gen g=get_selection();
	  select_rectangle(x,ytop-y);
	}
	return 1;
      }
      select_rectangle(x,ytop-y);
      if (Fl::event_button()!= FL_MIDDLE_MOUSE)
	fl_select();
      return 1;
    }
    return 0;
  }

  Eqw::Eqw(int x, int y, int w, int h): Fl_Window(x, y, w, h){
    xleft=0;
    ytop=h/2;
    xcur=0;
    ycur=0;
    begin_sel=-1;
    end_sel=-1;
    cb_escape=0;
    cb_enter=0;
    cb_backspace=0;
    cb_select=0;
    max_history_size=0;
    attr=attributs(14,FL_WHITE,FL_BLACK);
    modifiable=true;
    allow_immediate_eval=true;
    data=Eqw_nullstring(attr);
    lastdata=Eqw_copy(data);
    Fl_Group * gr=dynamic_cast<Fl_Group *>(parent());
    vscroll=new Eqw_Scrollbar(x+w+1,y,14,h,this,true);
    hscroll=new Eqw_Scrollbar(x,y+h+1,w,14,this,false);
    if (gr){
      gr->add(vscroll);
      gr->add(hscroll);
    }
    setscroll();
  }

  Eqw::Eqw(int x, int y, int w, int h, const char* l,const gen & g): Fl_Window(x, y, w, h, l){
    xleft=0;
    ytop=h/2;
    xcur=0;
    ycur=0;
    begin_sel=-1;
    end_sel=-1;
    cb_escape=0;
    cb_enter=0;
    cb_backspace=0;
    cb_select=0;
    max_history_size=0;
    attr=attributs(14,FL_WHITE,FL_BLACK);
    modifiable=true;
    allow_immediate_eval=true;
    data=Eqw_compute_size(g,attr,w);
    lastdata=Eqw_copy(data);
    Fl_Group * gr=dynamic_cast<Fl_Group *>(parent());
    vscroll=new Eqw_Scrollbar(x+w+1,y,14,h,this,true);
    hscroll=new Eqw_Scrollbar(x,y+h+1,w,14,this,false);
    if (gr){
      gr->add(vscroll);
      gr->add(hscroll);
    }
    setscroll();
  }

  Eqw::Eqw(int x, int y, int w, int h, const char* l,const gen & g,attributs myattr) : Fl_Window(x, y, w, h, l){ 
    xleft=0;
    ytop=h/2;
    xcur=0;
    ycur=0;
    begin_sel=-1;
    end_sel=-1;
    cb_escape=0;
    cb_enter=0;
    cb_backspace=0;
    cb_select=0;
    max_history_size=0;
    attr=myattr;
    modifiable=true;
    allow_immediate_eval=true;
    data=Eqw_compute_size(g,attr,w);
    lastdata=Eqw_copy(data);
    Fl_Group * gr=dynamic_cast<Fl_Group *>(parent());
    vscroll=new Eqw_Scrollbar(x+w+1,y,14,h,this,true);
    hscroll=new Eqw_Scrollbar(x,y+h+1,w,14,this,false);
    if (gr){
      gr->add(vscroll);
      gr->add(hscroll);
    }
    setscroll();
  }

  Eqw::~Eqw(){
    Fl_Group * g=dynamic_cast<Fl_Group *>(parent());
    if (g){
      g->remove(hscroll);
      g->remove(vscroll);
    }
    delete vscroll;
    delete hscroll;
  }

  int Input_tab::handle(int event){
#ifdef HAVE_LIBFLVW
    if (equalposcomp(current_upper_window,upper_window_mtrw)==1){
      if( event==FL_PUSH && spread_ptr) {
	spread_ptr->editing=true;
	spread_ptr->edit_row=spread_ptr->row();
	spread_ptr->edit_col=spread_ptr->col();
	// cerr << "Mtrw edit begins" << endl;
      }
    }
#endif
    if (event==FL_KEYBOARD && Fl::event_key()==FL_Enter){
      position(size(), 0);
      hist.push_back(value());
      count=hist.size();
      do_callback();
      return 1;
    }
    if (event==FL_KEYBOARD && (Fl::event_key()==FL_Up || Fl::event_key()==FL_Down)){
      if (Fl_Input::handle(event))
	return 1;
      // not handled by the input, use history
      int s=hist.size();
      Fl::focus(this);
      if (Fl::event_key()==FL_Up){
	--count;
	if (count<0)
	  count=0;
      }
      else {
	++count;
	if (count>=s)
	  count=max(0,s-1);
      }
      if (count<s){
	string v(value());
	int pos1=position(),pos2=mark();
	if (pos1>pos2)
	  std::swap<int>(pos1,pos2);
	value((v.substr(0,pos1)+hist[count]+v.substr(pos2,v.size()-pos2)).c_str());
	position(pos1,pos1+hist[count].size());
      }
      return 1;
    }
    if (event!=FL_KEYBOARD || Fl::event_text()[0]!=9 )
      return Fl_Input::handle(event);
    string s(value()),ans;
    if (position()<s.size())
      s=s.substr(0,position());
    if (handle_tab(s,completion_tab,100,100,ans)){
      make_new_help((s+ans).c_str());
      insert((ans+"()").c_str());
      position(position()-1,position()-1);
    }
    return 1;
  }

  void Input_tab::draw(){
    int pos=position();
    if (pos!=mark()){
      Fl_Input::draw();
      return;
    }
    // check if cursor is on [, (, ), ]
    int p0=pos;
    const char * c=value();
    int pmax=string(c).size();
    bool closing=false,opening=false;
    if (pos<pmax)
      opening= c[pos]=='(' || c[pos]=='[' || c[pos]=='{';
    if (!opening && pos){
      closing = c[pos-1]==')' || c[pos-1]==']' || c[pos-1]=='}';
      if (closing)
	--p0;
    }
    if (!opening && !closing){
      damage(damage() | FL_DAMAGE_ALL);
      Fl_Input::draw();
      return;
    }
    bool ok=matchpos(c,p0);
    if (!ok){
      if (closing)
	p0=pos-1;
      else
	p0=pos+1;
    }
    else {
      if (opening && pos<pmax)
	p0=p0+1;
    }
    mark(p0);
    unsigned s=selection_color();
    if (ok){
      if (opening)
	selection_color(FL_GREEN);
      else
	selection_color(fl_color_cube(0,FL_NUM_GREEN-1,2)); 
    }
    else
      selection_color(FL_RED);
    damage(damage() | FL_DAMAGE_ALL);
    Fl_Input::draw();
    selection_color(s);
    mark(pos);
  }


  // return a variable from the list of vars
  bool select_user_var(int dx,int dy,gen & ans,GIAC_CONTEXT){
    gen res=_VARS(1,contextptr);
    if (res.type!=_VECT)
      return false;
    vecteur v(*res._VECTptr);
    int s=v.size();
    Fl_Window * w = new Fl_Window(dx,dy);
    Fl_Return_Button * button0 = new Fl_Return_Button(2,2,dx/2-4,16);
    button0->shortcut(0xff0d);
    button0->label(gettext("OK"));
    Fl_Button * button1 = new Fl_Button(dx/2+2,2,dx/2-4,16);
    button1->shortcut(0xff1b);
    button1->label(gettext("Cancel"));
    Fl_Browser * browser = new Fl_Browser(2,20,dx,dy-22);
    browser->type(2);
    browser->label("Variables");
    browser->callback((Fl_Callback*)cb_eqw_browser);
    for (int k=0;k<s;++k)
      browser->add(v[k].print().c_str());
    browser->value(1);
    int i=1,r;
    w->end();
    w->resizable(w);
    w->set_modal();
    if (s){
      browser->value(1);
      w->show();
      Fl_Widget * savefocus=Fl::focus();
      Fl::focus(button1);
      for (;;) {
	Fl_Widget *o = Fl::readqueue();
	if (!o) Fl::wait();
	else {
	  if (o == button0) {r = 0; break;}
	  if (o == button1) {r = 1; break;}
	  if (o == w) { r=0; break; }
	}
      }
      Fl::focus(savefocus);
      w->hide();
      i=browser->value();
    }
    delete browser;
    delete button1;
    delete button0;
    delete w;
    if (r==0 && i<=s && i>0){
      ans=v[i-1];
      return true;
    }
    else
      return false;    
  } 


#ifndef NO_NAMESPACE_GIAC
} // namespace giac
#endif // ndef NO_NAMESPACE_GIAC

#endif // HAVE_LIBFLTK
