/* 
 *  File:        math_cursor.C
 *  Purpose:     Interaction for mathed
 *  Author:      Alejandro Aguilar Sierra <asierra@servidor.unam.mx> 
 *  Created:     January 1996
 *  Description: Math interaction for a WYSIWYG math editor.
 *
 *  Dependencies: Xlib, XForms
 *
 *  Copyright: (c) 1996, Alejandro Aguilar Sierra
 *
 *   Version: 0.5beta, Mathed & Lyx project.
 *
 *   You are free to use and modify this code under the terms of
 *   the GNU General Public Licence version 2 or later.
 */

#include "config.h"

#include "forms.h"

#include "math_inset.h"
#include "math_parser.h"
#include "math_cursor.h"

extern void mathed_set_font(short type, int style);
extern int mathed_char_width(short type, int style, byte c);
extern int mathed_string_width(short type, int style, byte* s, int ls);
extern int mathed_string_height(short, int, byte*, int, int&, int&);
extern int mathed_char_height(short, int, byte, int&, int&);

extern GC canvasGC, mathGC, latexGC, cursorGC, mathFrameGC;

inline bool IsAlpha(char c)
{
   return ('A' <= c  && c<='Z' || 'a' <= c  && c<='z');
}

// This was very smaller, I'll change it later 
inline bool IsMacro(short token, int id)
{
   return (token!=LM_TK_FRAC && token!=LM_TK_SQRT && token!=LM_TK_WIDE &&
	   token!=LM_TK_SPACE && token!=LM_TK_DOTS &&  token!=LM_TK_FUNCLIM &&
	   token!=LM_TK_BIGSYM && !(token==LM_TK_SYM && id<255));
}

inline char *strnew(const char* s)
{
   char *s1 = new char[strlen(s)+1];
   return strcpy(s1, s);
}

LyxMathXIter::LyxMathXIter(MathParInset* pp): p(pp) 
{ 
   x = y = 0;
   sx = sw = 0;   
   limits = false;
   s_type = 0;  
   if (p) {
     SetData(p);
     size = (p) ? p->GetStyle(): 0;
   }
}

void LyxMathXIter::SetData(MathParInset *pp)
{
   p = pp;
   x = y = 0;
   array = p->GetData();
   if (!array) {
      array = new LyxArrayBase;
      p->SetData(array);
   }
   size = p->GetStyle();
   Reset();
}

byte* LyxMathXIter::GetString(int& ls)
{  
   static byte s[80];
   byte *sx =  LyxMathIter::GetString(ls);
   if (ls>0) {
      if (MathIsBinary(FCode())) { //FCode()==LM_TC_BOP) {
	 byte *ps = &s[0];
	 for (int i=0; i<ls; i++) {
	    *(ps++) = ' ';
	    *(ps++) = sx[i];
	    *(ps++) = ' ';
	 }
//	 *ps = ' ';
	 ls = 3*ls;
      } else
	strncpy((char*)s, (char*)sx, ls);
      x += mathed_string_width(fcode, size, s, ls);
      return &s[0];
   } 	    
   return NULL;
}

Bool LyxMathXIter::Next()
{  
   if (!OK()) return False;
   int w=0;
   if (IsInset()) {
      LyxMathInset* px = GetInset();
      w = px->Width();
      if (px->GetType()==LM_OT_SCRIPT) {
	 if (w>sw) sw = w;
	 w = 0;
      } else
	sx = (px->GetLimits()) ? w: 0;
   } else {  
      byte c = GetChar();
      if (c>=' ') {
	 if (MathIsBinary(FCode())) { // fcode==LM_TC_BOP) {
	    byte s[4] = "   ";
	    s[1] = c;
	    w = mathed_string_width(fcode,size, s, 3);
	 } else	    
	   w = mathed_char_width(fcode, size, c);
      } else
      if (c==LM_TC_TAB && p) {
	 w = p->GetTab(1);
	 //fprintf(stderr, "WW[%d]", w);
      } else 
	fprintf(stderr, "No hubo w[%d]!", (int)c);   
   }
   if (LyxMathIter::Next()) {
      if (sw>0 && GetChar()!=LM_TC_UP && GetChar()!=LM_TC_DOWN) {
	 w = (sx>sw) ? 0: sw-sx;
	 sx = sw = 0;
      }
      x += w;
      return True;
   } else
     return False;
}


void LyxMathXIter::GoBegin()
{
   Reset();
   x = y = 0;   
   if (p) 
     (void)p->GetTab(-10000);  // This is a dirty trick. I hope you never use
                        //  a matrix with 10,000 columns.  [Alejandro 050596]
}

void LyxMathXIter::GoLast()
{
   while (Next());
}


void LyxMathXIter::Adjust()
{
   int posx = pos;
   GoBegin();
   while (posx>pos) Next();
}

Bool LyxMathXIter::Prev()
{  
   if (LyxMathIter::Prev()) {
      if (IsInset()) {
	 LyxMathInset* px = GetInset();
	 if (px->GetType()!=LM_OT_SCRIPT)
	   x -= px->Width(); 
      } else {  
	 byte c = GetChar();
	 if (c>=' ') {
	    if (MathIsBinary(FCode())) { // FCode()==LM_TC_BOP) {
	       byte s[4] = "   ";
	       s[1] = c;
	       x -= mathed_string_width(fcode,size, s, 3);
	    } else	    
	      x -= mathed_char_width(fcode, size, c);
	 } else 
	   if (c==LM_TC_TAB && p) { 
	      x -= p->GetTab(0);
	      (void)p->GetTab(-1);
	   }
      }
      return True;
   }
   return False;
}


#define MAX_STACK_ITEMS 32

struct MathStackXIter {
   
   int i, imax;
   
   LyxMathXIter *item;
   
   MathStackXIter(int n=MAX_STACK_ITEMS): imax(n) {
      item = new LyxMathXIter[imax];
      i = 0;
   }
   
   ~MathStackXIter() {
      delete[] item;
   }
   
   void push(LyxMathXIter** a) {
      *a = &item[i++];
   }
      
   LyxMathXIter* pop(void) {
      i--;
      return &item[i-1];
   }
      
   LyxMathXIter* Item(int idx) {
      return (idx+1 <= i) ? &item[i-idx-1]: (LyxMathXIter*)NULL;
   }

   void Reset() {
      i = 0;
   }
   
   int Full(void) {
      return (i>=MAX_STACK_ITEMS);
   }
   
   int Empty(void) {
      return (i<=1);
   }
   
   void UpdatePos(int, int);
} mathstk;

void MathStackXIter::UpdatePos(int /*dx*/, int /*dy*/)
{
   fprintf(stderr, "Mathed: Updating pos!\n");
   /*
   int x, y;
   for (int j=0; j<i; j++) {
      item[j].GetOrg(x, y);
      x -= dx; y -= dy;
      item[j].SetOrg(x, y);
   }*/
}

LyxMathCursor::LyxMathCursor(MathParInset *p) // : par(p)
{
   SetPar(p);
   selarray = NULL;  
}


void LyxMathCursor::SetPar(MathParInset *p)
{
   win = 0;
   is_visible = False;
   macro_mode = false;
   selection = false;
   mathstk.Reset();
   mathstk.push(&cursor);
   par = p;
   cursor->SetData(par);
}

void LyxMathCursor::Draw(long unsigned pm, int x, int y)
{
   win = pm;    // win = (mathedCanvas) ? mathedCanvas: pm;
   par->Metrics();
   int w = par->Width()+2, a = par->Ascent()+1, h = par->Height()+1;
   if (par->GetType()>LM_OT_PAR) { a += 4;  h += 8; }
   
   if (!canvasGC) mathed_set_font(LM_TC_VAR, 1);
//   XFillRectangle(fl_display,pm, canvasGC, x, y-a, w, h);
   XDrawRectangle(fl_display,pm, mathFrameGC, x-1, y-a, w, h); 
   par->Draw(pm, x, y);
   cursor->Adjust();
}


void LyxMathCursor::Redraw()
{  
   fprintf(stderr, "Mathed: Redrawing!\n");
   par->Metrics();
   int w = par->Width(), h = par->Height();
   int x, y;
   par->GetXY(x, y);
   mathed_set_font(LM_TC_VAR, 1);
   XFillRectangle(fl_display, win,canvasGC,x, y-par->Ascent(), w, h);
   par->Draw(win, x, y);
}

bool LyxMathCursor::Left(bool sel)
{
   if (macro_mode) {
      MacroModeBack();
      return true;
   }
   if (sel && !selection) SelStart();
   if (!sel && selection) SelClear();
   bool result = cursor->Prev();
   if (!result && !mathstk.Empty()) {
      cursor = mathstk.pop();
      cursor->Adjust();
      result = true;
      if (selection) SelClear();
   } else  
     if (result && cursor->IsActive()) {
	if (cursor->IsScript()) {
	   cursor->Prev();
	   if (!cursor->IsScript())
	     cursor->Next();
	   cursor->Adjust(); 
	   return true;
	}
	if (!selection) {
	    MathParInset *p = cursor->GetActiveInset();
	    mathstk.push(&cursor);
	    cursor->SetData(p);
	    cursor->GoLast();
	}
     } 
   return result;  
}

// Leave the inset
bool LyxMathCursor::Pop()
{
   if (!mathstk.Empty()) {
      cursor = mathstk.pop();
      cursor->Next(); 
      return true;
   }
   return false;
}

// Go to the inset 
bool LyxMathCursor::Push()
{ 
   if (cursor->IsActive()) {
      MathParInset *p = cursor->GetActiveInset();
      mathstk.push(&cursor);
      cursor->SetData(p);
      return true;
   }
   return false;
}  

bool LyxMathCursor::Right(bool sel)
{  
   if (macro_mode) {
      MacroModeClose();
      return true;
   } 
   if (sel && !selection) SelStart();
   if (!sel && selection) SelClear();
   bool result = false;
 
   if (cursor->IsActive()) {
      if (cursor->IsScript()) {
	 cursor->Next();
	 // A script may be followed by another script
	 if (cursor->IsScript()) 
	   cursor->Next();
	 return true;
      }
      if (!selection) { 
	  MathParInset *p = cursor->GetActiveInset();
	  mathstk.push(&cursor);
	  cursor->SetData(p);
	  result = true;
      } else
	 cursor->Next();
   } else
     if (!cursor->Next() && !mathstk.Empty()) {
	cursor = mathstk.pop();
	cursor->Next();
	cursor->Adjust();       
	result = true;
	if (selection) SelClear();
     }
   return result;
}


void LyxMathCursor::SetPos(int x, int y)
{
   if (macro_mode) MacroModeClose();  
   mathstk.Reset();
   mathstk.push(&cursor);
   par->SetFocus(x, y);
   cursor->SetData(par);
   while (cursor->GetX()<x) {
      if (cursor->IsActive()) {
	 MathParInset *p = cursor->GetActiveInset();
	 if (p->Inside(x, y)) {
	    p->SetFocus(x, y);
	    mathstk.push(&cursor);
	    cursor->SetData(p);
	    continue;
	 }
      }
      if (!cursor->Next() && !Pop()) 
	break;
   }
}
   

void LyxMathCursor::Home()
{
   if (macro_mode) MacroModeClose();
   mathstk.Reset();
   mathstk.push(&cursor);
   cursor->GoBegin();
}

void LyxMathCursor::End()
{
   if (macro_mode) MacroModeClose();
   mathstk.Reset();
   mathstk.push(&cursor);
   cursor->GoLast();
}

void LyxMathCursor::Insert(byte c, LyxMathTextCodes t)
{  
   if (selection) SelDel();
   if (t==LM_TC_CR) {
      MathParInset *p= cursor->p;
      if (p==par && p->GetType()<LM_OT_MPAR && p->GetType()>LM_OT_MIN) {
	 MathMatrixInset* mt = new MathMatrixInset(3);
	 mt->SetAlign(' ', "rcl");
	 mt->SetStyle(LM_ST_DISPLAY);
	 mt->SetType((p->GetType()==LM_OT_PARN) ? LM_OT_MPARN: LM_OT_MPAR);
	 mt->SetData(p->GetData());
	 p->SetData(NULL);
	 delete p;
	 par = mt;
	 cursor->SetData(par);
	 p = mt;
      }      
      if (p &&  (p->GetType()<=LM_OT_MATRIX && p->GetType()>=LM_OT_MPAR)) {
	 ((MathMatrixInset *)p)->AddRow();
	 cursor->SetData(p);
      }
   } else
   if (t==LM_TC_TAB) {
      LyxMathInset *p = cursor->p;
      if (p &&  (p->GetType()<=LM_OT_MATRIX && p->GetType()>=LM_OT_MPAR)) {
	 int ct = ((MathMatrixInset *)p)->GetTabPos();
	 while (ct==((MathMatrixInset *)p)->GetTabPos() && cursor->Next());
      }
   } else {
      if (macro_mode) {
	 if (MathIsAlphaFont(t) || t==LM_TC_MIN) {
	    MacroModeInsert(c);
	    return;
	 } else {
	    MacroModeClose();
	 }
      } 
      cursor->Insert(c, t);
   }
}

void LyxMathCursor::Insert(LyxMathInset* p, int t)
{
   if (macro_mode) MacroModeClose();
   if (selection) {
      if (MathIsActive(t)) {
	 SelCut();
	 ((MathParInset*)p)->SetData(selarray);
      } else
	SelDel();
   }
         
   if (mathstk.i<MAX_STACK_ITEMS-1) {
      cursor->Insert(p, t);
      if (MathIsActive(t)) {
	 cursor->Prev();
	 Push();
      }
     
   } else
     fprintf(stderr, "Math error: Full stack.\n");
}

void LyxMathCursor::Delete() 
{   
   if (macro_mode) return;
   if (selection) {
      SelDel();
      return;
   }
   if (cursor->Empty() && !mathstk.Empty()) {
      cursor = mathstk.pop();
   } 
   if (cursor->GetChar()!=LM_TC_TAB)
     cursor->Delete();
}

void LyxMathCursor::DelLine()
{  
   if (macro_mode) MacroModeClose();
   if (selection) {
      SelDel();
      return;
   }
   MathParInset *p= cursor->p;
   if (p &&  (p->GetType()<=LM_OT_MATRIX && p->GetType()>=LM_OT_MPAR)) {
      if (!((MathMatrixInset *)p)->DelRow() && !mathstk.Empty()) {
	 cursor = mathstk.pop();
	 cursor->Delete();
      } else  
	cursor->SetData(p);
   }
}

bool LyxMathCursor::Up()
{
   bool result = false;
   
   if (macro_mode) MacroModeClose();
   if (selection) SelClear();
    
   MathParInset *p;

   if (cursor->IsScript()) {
      char cd = cursor->GetChar();
      if (MathIsUp(cd)) {
         Push();
         return true;
      } else {
      	 // A subscript may be followed by a superscript
	 cursor->Next();
         if (MathIsUp(cursor->GetChar())) {
            Push();
            return true;
         } else
           cursor->Prev();
      }
   }    
     
   p = cursor->p;   
   if (p) {
      switch (p->GetType()) {
       case LM_OT_SCRIPT:
	 {
	    LyxMathXIter *cx = mathstk.Item(1);
	    if (cx->GetChar()==LM_TC_DOWN) {
	       cursor = mathstk.pop();
	       cursor->Next();
	       result = true;
	    }
	    break;
	 }
       case LM_OT_FRAC:
	 result = ((MathFracInset *)p)->Up();
	 cursor->SetData(p);
	 break;
       case LM_OT_MPAR:
       case LM_OT_MPARN:
       case LM_OT_MATRIX:	    
	 int ct = p->GetTabPos();
	 if ((result=((MathMatrixInset *)p)->Up())) {
	    cursor->SetData(p);
	    while (ct>p->GetTabPos() && cursor->Next());
	 }
	 break;
      }
      if (!result && !mathstk.Empty()) {
         cursor = mathstk.pop();
         return Up();
      }     
   }
   return result;
}
      
bool LyxMathCursor::Down()
{
   bool result = false;
   
   if (macro_mode) MacroModeClose();
   if (selection) SelClear();

   MathParInset *p;

   if (cursor->IsScript()) {
      char cd = cursor->GetChar(); 
      if (MathIsDown(cd)) {
	 Push();
	 return true;
      } else {
	 // A superscript may be followed by a subscript
	 cursor->Next();
	 if (MathIsDown(cursor->GetChar())) {
	    Push();
	    return true;
	 } else
	   cursor->Prev();
      }
   }
   
   p= cursor->p;   
   if (p) {
      switch (p->GetType()) {
       case LM_OT_SCRIPT:
	 {
	    LyxMathXIter *cx = mathstk.Item(1);
	    if (cx->GetChar()==LM_TC_UP) {
	       cursor = mathstk.pop();
	       cursor->Next();
	       result = true;
	    }
	    break;
	 }
       case LM_OT_FRAC:
	 result = ((MathFracInset *)p)->Down();
	 cursor->SetData(p);
	 break;
       case LM_OT_MPAR:
       case LM_OT_MPARN:
       case LM_OT_MATRIX:
	 int ct = ((MathMatrixInset *)p)->GetTabPos();
//	 fprintf(stderr, "DW[%d]", ct);
	 result = ((MathMatrixInset *)p)->Down();
	 cursor->SetData(p);
	 while (ct>((MathMatrixInset *)p)->GetTabPos()) cursor->Next();
//	 fprintf(stderr, "DW[%d %d]", ct,(MathMatrixInset *)p->GetTabPos());
	 break;
      }
      if (!result && !mathstk.Empty()) {
	 cursor = mathstk.pop();
	 return Down();
      }    
   }
   return result;
}

bool LyxMathCursor::Limits()
{
   if (cursor->IsInset()) {
      LyxMathInset *p = cursor->GetInset();
      bool ol = p->GetLimits();
      p->SetLimits(!ol);
      return (ol!=p->GetLimits());
   }
   return false;
}

void LyxMathCursor::SetSize(short size)
{
    MathParInset *p = cursor->p;
    p->UserSetSize(size);
    cursor->SetData(p);
}

void LyxMathCursor::Interpret(const char *s)
{
   LyxMathInset *p = NULL;
   latexkeys *l = NULL;   
   LyxMathTextCodes tcode = LM_TC_INSET;
   
   if (s[0]=='^' || s[0]=='_') {
      p = new MathParInset(LM_ST_SCRIPT, "", LM_OT_SCRIPT);
      Insert (p, (s[0]=='_') ? LM_TC_DOWN: LM_TC_UP); 
      return;
   } else   
   if (s[0]=='!' || s[0]==','  || s[0]==':' || s[0]==';') {
      int sp = ((s[0]==',') ? 1:((s[0]==':') ? 2:((s[0]==';') ? 3: 0))); 
      p = new MathSpaceInset(sp);
      Insert(p);
      return;
   } else  
     l = in_word_set (s, strlen(s));
   
   if (!l) {
      fprintf(stderr, "Math warning: unrecognized command '%s'\n", s);
      p = new MathFuncInset(strnew(s));
   } else {
      switch (l->token) {
       case LM_TK_BIGSYM:
	 {
	    p = new MathBigopInset(l->name, l->id);
	    break;
	 }
       case LM_TK_SYM:
       {	 	     
	  if (l->id<255) {
	     Insert((byte)l->id, MathIsBOPS(l->id) ? 
		                 LM_TC_BOPS: LM_TC_SYMB); 	    
	  } else {
	     p = new MathFuncInset(l->name);
	  }
	  break;
       }  
       case LM_TK_FRAC: 
       {	 
	  p = new MathFracInset; 
	  //((MathFracInset*)p)->SetData(new LyxArrayBase, new LyxArrayBase);
	  tcode = LM_TC_ACTIVE_INSET;
	  break;
       }
       case LM_TK_SQRT: 
       {	 
	  p = new MathSqrtInset; 
	  tcode = LM_TC_ACTIVE_INSET;
	  break;
       }
       case LM_TK_WIDE: 
       {	 
	  p = new MathDecorationInset(l->id); 
	  tcode = LM_TC_ACTIVE_INSET;
	  break;
       } 
       case  LM_TK_FUNCLIM:
       {
	  p = new MathFuncInset(l->name, LM_OT_FUNCLIM);
	  break;
       }
       case LM_TK_SPACE:
       {
	  p = new MathSpaceInset(l->id);
	  break;
       }	   
       case LM_TK_DOTS: {
          p = new MathDotsInset(l->name, l->id);
          break;
       } 
       default:
       {
	  p = new MathFuncInset(l->name);
	  break;
       }
      }
   }
   if (p) Insert(p, tcode);     
}


void LyxMathCursor::MacroModeOpen()
{
   if (!macro_mode)  {
      macroln = 0;
      macrobf[0] = '\0';
      imacro = new MathFuncInset(&macrobf[0], LM_OT_UNDEF);
      Insert (imacro);
      macro_mode = true;
   } else
     fprintf(stderr, "Mathed Warning: Already in macro mode\n");
}

void LyxMathCursor::MacroModeClose()
{
   if (macro_mode)  {
      macro_mode = false;
      latexkeys *l = in_word_set(macrobf, macroln);
      if (macroln>0 && (!l || (l && IsMacro(l->token, l->id)))) {
	 imacro->SetName(strnew(macrobf));
      } else {
         Left();
	 imacro->SetName(NULL);
         Delete();
      } 
      if (l && !IsMacro(l->token, l->id)) {
	 Interpret(macrobf);
      } 
      imacro = NULL;
   } else
     fprintf(stderr, "Mathed Warning: we are not in macro mode\n");
}

void LyxMathCursor::MacroModeBack()
{
   if (macro_mode) {
     if (macroln>0) {
	macrobf[--macroln] = '\0';
	imacro->Metrics();
     } else 
	MacroModeClose();
   } else
     fprintf(stderr, "Mathed Warning: we are not in macro mode\n");
}

void LyxMathCursor::MacroModeInsert(char c)
{
   if (macro_mode) {
      macrobf[macroln+1] = macrobf[macroln];
      macrobf[macroln++] = c;
      imacro->Metrics();
   } else
     fprintf(stderr, "Mathed Warning: we are not in macro mode\n");
}

void LyxMathCursor::SelCopy(void)
{
    if (selection) {
	int p1, p2;
	p1 = (cursor->pos < selpos) ? cursor->pos: selpos;
	p2 = (cursor->pos > selpos) ? cursor->pos: selpos;
	selarray = cursor->Copy(p1, p2);
	cursor->Adjust();
	selection = false;
    }
}

void LyxMathCursor::SelCut(void)
{   
    if (selection) {
	int p1, p2;

	p1 = (cursor->pos < selpos) ? cursor->pos: selpos;
	p2 = (cursor->pos > selpos) ? cursor->pos: selpos;
	if (p1==p2) return;
             
//	fprintf(stderr, "Cutting sel %d %d  ", p1, p2);  fflush(stderr);
	selarray = cursor->Copy(p1, p2);
	cursor->Clean(p1, p2);
	cursor->Adjust();
	selection = false;
    }
//    fprintf(stderr, "out Cutting ");  fflush(stderr);
}

void LyxMathCursor::SelDel(void)
{
//    fprintf(stderr, "Deleting sel ");
    if (selection) {    
	int p1, p2;
     	p1 = (cursor->pos < selpos) ? cursor->pos: selpos;
	p2 = (cursor->pos > selpos) ? cursor->pos: selpos;
	if (p1==p2) return;
     	cursor->Clean(p1, p2);
	cursor->Adjust();
	selection = false;
    }
}

void LyxMathCursor::SelPaste(void)
{
//    fprintf(stderr, "paste %p %d ", selarray, cursor->pos);
    if (selection) SelDel();
    if (selarray) {
	int nc = (cursor->p) ? cursor->p->GetColumns()-1: 0;
	cursor->Merge(selarray, nc);
	cursor->Adjust();
    }
}

void LyxMathCursor::SelStart()
{
//   fprintf(stderr, "Starting sel ");
//   cursel = *cursor; 
   selpos = cursor->pos;
   selection = true;
}

void LyxMathCursor::SelClear()
{   
//   fprintf(stderr, "Clearing sel ");
   selection = false;
}


void LyxMathCursor::SelGetArea(int& x1, int& y1, int& w, int& h)
{   
    x1 = y1 = w = h = 0; 
    if (!selection)
      return;

    // single row selection
    int a, d, p1, p2; 
    
    p1 = (cursor->pos < selpos) ? cursor->pos: selpos;
    p2 = (cursor->pos > selpos) ? cursor->pos: selpos;
    if (p1==p2) return;
    
    LyxMathXIter selcursor(cursor->p);
    selcursor.pos = p1;
    selcursor.Adjust();
    selcursor.GetPos(x1, y1);
    selcursor.IMetrics(p2, w, a, d);
    h = a + d;
    y1 -= a;
//    fprintf(stderr, "SA[%d %d %d %d %d %d]", x1, y1, w, h, p1, p2);
}


// This function is not recursive, as MathPar::Metrics is
void LyxMathXIter::IMetrics(int pos2, int& width, int& ascent, int& descent)
{  
    byte cx, cxp=0, *s;
    int x1, ls;
    int asc=0, des=0;
    bool limits = false;
  
    descent = ascent = width = 0;
    if (!array) return;
    if (array->Empty()) return;
//    if  (pos2 > array->last) return;
    x1 = x; 
    while (pos<pos2) {
	cx = GetChar();
	if (cx >= ' ') {
	    mathed_char_height(FCode(), size, cx, asc, des);
	    if (asc > ascent) ascent = asc;
	    if (des > descent) descent = des;
	    limits = false;
	} else
	if (MathIsInset(cx)) {
	    LyxMathInset *pp = GetInset();
	    int sz = pp->GetStyle();
	    
	    if (cx==LM_TC_UP) {
		if (!asc && p) {
		    int xx, yy;
		    p->GetXY(xx, yy);
		    ((MathParInset*)pp)->GetXY(xx, asc);
		    asc = yy - asc;
		}
		asc += ((limits) ? pp->Height()+4: pp->Ascent());
	    } else
	      if (cx==LM_TC_DOWN) {
		  if (!des && p) {
		      int xx, yy;
		      p->GetXY(xx, yy);
		      ((MathParInset*)pp)->GetXY(xx, des);
		      if (des-pp->Height()<yy && !asc)
			asc = yy - (des-pp->Height());
		      des -= yy;
		  }
		  des += ((limits) ? pp->Height()+4: pp->Height()-pp->Ascent()/2);
	      } else {
		  asc = pp->Ascent();
		  des = pp->Descent();
	      }
	    if (asc > ascent) ascent = asc;
	    if (des > descent) descent = des;
	    if (cx!=LM_TC_UP && cx!=LM_TC_DOWN)
	      limits = pp->GetLimits();
	} else 
	if (cx==LM_TC_TAB) {
	    limits = false;                   
	}      
	else {
	    fprintf(stderr, "Mathed Sel-Error: Unrecognized code[%d]\n", cx);
	    break;
	}       
	if (pos<pos2)  Next();
	cxp = cx;
   }
    width = x - x1;
}
