/*
 *  File:        formula.h
 *  Purpose:     Implementation of formula inset
 *  Author:      Alejandro Aguilar Sierra <asierra@servidor.unam.mx> 
 *  Created:     January 1996
 *  Description: Allows the edition of math paragraphs inside Lyx. 
 *
 *  Copyright: (c) 1996, Alejandro Aguilar Sierra
 *
 *  Version: 0.4, 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 <stdlib.h>

#include "formula.h"
#include "keybind.h"
#include "xdefinitions.h"
#include "math_cursor.h"
#include "math_parser.h"
#include "file.h"
#include "buffer.h"
#include "lyx_cb.h"
#include "minibuffer.h"

extern void UpdateInset(Inset* inset, bool mark_dirty = true);
extern void LockedInsetStoreUndo(int);

extern void ToggleLockedInsetCursor(long, long, int, int);
extern void FitLockedInsetCursor(long, long, int, int);
extern int LockInset(UpdatableInset*);
extern int UnlockInset(UpdatableInset*);

extern GC canvasGC, mathGC, latexGC, cursorGC, mathFrameGC;
extern char *mathed_label;
extern GC LyXGetLatexGC();
extern GC LyXGetMathFrameGC();

extern int mono_video;

short greek_kb_flag = 0;

//   variables, constants, symbol, bold, cal, tt, text
LyXFont Math_Fonts[] = {
    { LYX_ROMAN_FAMILY, LYX_MEDIUM_SERIES, LYX_ITALIC_SHAPE,},
//    { LYX_ROMAN_FAMILY, LYX_MEDIUM_SERIES, LYX_UP_SHAPE,},
    { LYX_SYMBOL_FAMILY, LYX_MEDIUM_SERIES, LYX_UP_SHAPE,},
    { LYX_SYMBOL_FAMILY, LYX_MEDIUM_SERIES, LYX_ITALIC_SHAPE,},
    { LYX_ROMAN_FAMILY, LYX_BOLD_SERIES, LYX_UP_SHAPE,},
    { LYX_SANS_FAMILY, LYX_MEDIUM_SERIES, LYX_ITALIC_SHAPE,},
    { LYX_TYPEWRITER_FAMILY, LYX_MEDIUM_SERIES, LYX_UP_SHAPE,},
    { LYX_ROMAN_FAMILY, LYX_MEDIUM_SERIES, LYX_UP_SHAPE,},
};

static int lfont_size = LYX_SIZE_NORMAL;


// local global 
static LyxMathCursor* mathcursor = NULL; 

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

inline bool IsDigit(char c)
{
   return ('0' <= c && c <='9');
}

inline bool IsMacro(short token, int id)
{
   return (token!=LM_TK_FRAC && token!=LM_TK_SQRT &&
	  !((token==LM_TK_SYM || token==LM_TC_BSYM) && id<255));
}

LyXFont WhichFont(short type, int size)
{
   LyXFont f;

   switch (size) {
    case LM_ST_DISPLAY:     
      if (type==LM_TC_BSYM) {
	 size = (lfont_size<LYX_SIZE_GIANT-1) ? lfont_size+2: LYX_SIZE_GIANT;
	 break;
      }
    case LM_ST_TEXT:
      size = lfont_size;
      break;
    case LM_ST_SCRIPT:
      size = (lfont_size>0) ? lfont_size-1: 0;
      break;
    case LM_ST_SCRIPTSCRIPT:
      size = (lfont_size>1) ? lfont_size-2: 0;
      break;
    default:
      fprintf(stderr, "Mathed Error: wrong font size: %d\n", size);
      size = lfont_size;
      break;
   }
   
   switch (type) {
    case LM_TC_SYMB:	     
      f = Math_Fonts[2];
      break;
    case LM_TC_BSYM:	     
      f = Math_Fonts[2];
      break;
    case LM_TC_VAR:
      f = Math_Fonts[0];
      break;
    case LM_TC_BF:
      f = Math_Fonts[3];
      break;
    case LM_TC_CAL:
      f = Math_Fonts[4];
      break;
    case LM_TC_TT:
      f = Math_Fonts[5];
      break;
//    case LM_TC_TEX: Math_Fonts[2]; break;
    case LM_TC_TEXTRM:
    case LM_TC_RM:    
      f = Math_Fonts[6];
      break;
    default:
      f = Math_Fonts[1];
      break;
   }
//   if (type!=LM_TC_TEXTRM) 
   f.latex = LYX_FORMULA_LATEX;
   f.size = size;
   return f;
}


void mathed_set_font(short type, int size)
{
   if (!canvasGC) {
      cursorGC = LyXGetThinOnOffLineGC();    
      canvasGC = LyXGetLightedGC(); 
      latexGC =  LyXGetLatexGC();
      mathFrameGC = LyXGetMathFrameGC();
   }
   LyXFont f = WhichFont(type, size); 
   if (type==LM_TC_TEX) {
      f.latex = LYX_LATEX;
      latexGC = LyXGetGC(f);
   } else
     mathGC = LyXGetGC(f);
}

int mathed_char_width(short type, int size, byte c)
{
   int t=0;
   t = LyXTextWidth(WhichFont(type, size), (char*)&c, 1);
   return t;
}

int mathed_string_width(short type, int size, byte* s, int ls)
{
   int t = 0;
   t = LyXTextWidth(WhichFont(type, size), (char*)s, ls);
   return t;
}

int mathed_string_height(short type, int size, byte* s, int ls, int& asc, int& des)
{
   LyXFont font = WhichFont(type, size);
   asc = des = 0;
   for (int i=0; i<ls; i++) {
      if (LyXDescent(font, s[i]) > des)
	des = LyXDescent(font, s[i]);
      if (LyXAscent(font, s[i]) > asc)
	asc = LyXAscent(font, s[i]);
   }
   return asc+des;
}

int mathed_char_height(short type, int size, byte c, int& asc, int& des)
{
   LyXFont font = WhichFont(type, size);
   asc = des = 0;
   des = LyXDescent(font, c);
   asc = LyXAscent(font, c);
   return asc+des;
}

InsetFormula::InsetFormula(bool display)
{
   par = new MathParInset;
//   mathcursor = NULL;
   disp_flag = display;
   label = NULL;
   if (disp_flag) {
      par->SetType(LM_OT_PAR);
      par->SetStyle(LM_ST_DISPLAY);
   }
}

InsetFormula::InsetFormula(MathParInset *p)
{
   par = (p->GetType()>=LM_OT_MPAR) ? 
         new MathMatrixInset((MathMatrixInset*)p): 
         new MathParInset(p);
//   mathcursor = NULL;
   
   disp_flag = (par->GetType()>0);
   label = NULL;
}

InsetFormula::~InsetFormula()
{
   delete par;
   if (label) delete label;
}

Inset* InsetFormula::Clone(void)
{
   InsetFormula* f = new InsetFormula(par);
   if (label) f->label = StringCopy(label);   
   return (Inset*)f;
}

void InsetFormula::Write(FILE *file)
{
   fprintf(file, "Formula ");
   Latex(file, 0);
}

int InsetFormula::Latex(FILE *file, char fragile)
{
  int ret = 0;
   mathed_write(par, file, &ret, fragile, label);
   return ret;
}

void InsetFormula::Read(FILE *file)
{
   mathed_parser_file(file);   
   
   // Silly hack to read labels. 
   mathed_label = NULL;
   mathed_parse(0, NULL, &par);
   par->Metrics();
   disp_flag = (par->GetType()>0);
   
   if (mathed_label) {
      label = mathed_label;
      mathed_label = NULL;
   }
   
#ifdef DEBUG
   Write(stdout);
   fflush(stdout);
#endif
}

int InsetFormula::Ascent(LyXFont)
{
   return par->Ascent() + ((disp_flag) ? 8: 1);
}

int InsetFormula::Descent(LyXFont)
{
   return par->Descent() + ((disp_flag) ? 8: 1);
}

int InsetFormula::Width(LyXFont)
{
   
   return par->Width()+2;
}

void InsetFormula::Draw(LyXFont f, unsigned long pm, int baseline, float &x)
{
   lfont_size = f.size;
   mathed_set_font(LM_TC_TEXTRM, LM_ST_TEXT); // otherwise a segfault could occur
								// in some XDrawRectangles (i.e. matrix) (Matthias)   
   if (mathcursor && mathcursor->GetPar()==par)   
       mathcursor->Draw(pm, (int)x, baseline);
   else {
       par->Draw(pm, (int)x, baseline);
   }
   x += (float)Width(f);
 
   if (par->GetType()==LM_OT_PARN || par->GetType()==LM_OT_MPARN) {
       char s[80];
       LyXFont  font = WhichFont(LM_TC_BF, par->size);
       font.latex = LYX_NO_LATEX;
      
       if (par->GetType()==LM_OT_PARN) {
	   if (label)
	     sprintf(s, "(%s)", label);
	   else
	     sprintf(s, "(#)");
	   XDrawString(fl_display, pm, LyXGetGC(font), (int)x+20, baseline, 
		       s, strlen(s));
       } else 
       if (par->GetType()==LM_OT_MPARN) {
	   MathMatrixInset *mt = (MathMatrixInset*)par;
	   int x1, y, ir = mt->GetRowIdx();
	   mt->Top();
	   do {
	       mt->GetXY(x1, y);
	       if (mt->IsNumbered()) {
		   if (mt->GetLabel())
		     sprintf(s, "(%s)", mt->GetLabel());
		   else
		     sprintf(s, "(#)");
		   XDrawString(fl_display, pm, LyXGetGC(font), (int)x+20, y,
			       s, strlen(s));
	       }
	   } while (mt->Down());
	   // Return to the original position
	   mt->Top();
	   while (ir--) mt->Down();
       }
   }
   cursor_visible = false;
}

void InsetFormula::Edit(int x, int y)
{
   mathcursor = new LyxMathCursor(par);
   LockInset(this);
   UpdateInset(this, false);
   x += par->xo; 
   y += par->yo; 
   mathcursor->SetPos(x, y);
}
				       
void InsetFormula::InsetUnlock()
{
   if (mathcursor)
     delete mathcursor;
   mathcursor = NULL;
   UpdateInset(this, false);
}

// Now a symbol can be inserted only if the inset is locked
void InsetFormula::InsertSymbol(const char* s)
{ 
   if (!s || !mathcursor) return;
   
//   if (!par->array)
//     par->array = new LyxArrayBase;
   mathcursor->Interpret(s);
   UpdateLocal();
}
   
void InsetFormula::GetCursorPos(int& x, int& y)
{
    mathcursor->GetPos(x, y);
    x -= par->xo; 
    y -= par->yo;
}

void InsetFormula::ToggleInsetCursor()
{
  if (!mathcursor)
    return;
  int x, y, asc, desc;
  mathcursor->GetPos(x, y);
//  x -= par->xo; 
  y -= par->yo; 
  asc = LyXMaxAscent(WhichFont(LM_TC_TEXTRM, LM_ST_TEXT));
  desc = LyXMaxDescent(WhichFont(LM_TC_TEXTRM, LM_ST_TEXT));
  
  ToggleLockedInsetCursor(x, y, asc, desc);
  cursor_visible = !cursor_visible;
}

void InsetFormula::ShowInsetCursor(){
  if (!cursor_visible){
    int x, y, asc, desc;
    if (mathcursor){
      mathcursor->GetPos(x, y);
      //  x -= par->xo; 
      y -= par->yo; 
      asc = LyXMaxAscent(WhichFont(LM_TC_TEXTRM, LM_ST_TEXT));
      desc = LyXMaxDescent(WhichFont(LM_TC_TEXTRM, LM_ST_TEXT));
      FitLockedInsetCursor(x, y, asc, desc);
    }
    ToggleInsetCursor();
  }
}

void InsetFormula::HideInsetCursor(){
  if (cursor_visible)
    ToggleInsetCursor();
}

void InsetFormula::ToggleInsetSelection()
{
    if (!mathcursor)
      return;
    
    int x, y, w, h;
    
    mathcursor->SelGetArea(x, y, w, h);
    //  x -= par->xo; 
    //  y -= par->yo;
    bufferlist.current()->screen->DrawManualSelection(x, y, w, h);
}

void InsetFormula::SetDisplay(bool dspf)
{
   if (dspf!=disp_flag) {
      if (dspf) {
	 par->SetType(LM_OT_PAR);
	 par->SetStyle(LM_ST_DISPLAY);
      } else {
	 if (par->GetType()>=LM_OT_MPAR) { 
	    MathParInset *p = new MathParInset(par);
	    delete par;
	    par = p;
	    if (mathcursor) 
	       mathcursor->SetPar(par); 
	 }
	 par->SetType(LM_OT_MIN);
	 par->SetStyle(LM_ST_TEXT);
	 if (label && par->GetType()!=LM_OT_MPARN) {
	    delete[] label;
	    label = NULL;
	 }
      }
      disp_flag = dspf;
   }   
}
   


int InsetFormula::GetNumberOfLabels() 
{
   // This is dirty, I know. I'll clean it at 0.11
   if (par->GetType()==LM_OT_MPARN) {
       MathMatrixInset *mt = (MathMatrixInset*)par;
       int nl=0, ir = mt->GetRowIdx();
       mt->Top();
       do { 
	   if (mt->GetLabel()) nl++;
       } while (mt->Down());
       // Return to the original position
       mt->Top();
       while (ir--) mt->Down();
       fprintf(stderr, "NL%d ", nl);
       return nl;
   } else
   if (label)
       return 1;
   else
       return 0;
}

const char* InsetFormula::GetLabel(int i)
{
   // This is dirty, I know. I'll clean it at 0.11
   if (par->GetType()==LM_OT_MPARN) {
       MathMatrixInset *mt = (MathMatrixInset*)par;
       int nl=0, ir = mt->GetRowIdx();
       mt->Top();
       do { 
	   if (mt->GetLabel()) {
		   label = StringCopy(mt->GetLabel()); // added StringCopy to avoid: contravariance violation
	       if (nl==i) break;
	       nl++;
	   }
       } while (mt->Down());
       // Return to the original position
       mt->Top();
       while (ir--) mt->Down();
   }
   fprintf(stderr, "LS[%s] ", label); 
   return label;
}

void InsetFormula::UpdateLocal()
{
   par->Metrics();  // To inform lyx kernel the exact size 
                  // (there were problems with arrays).
   UpdateInset(this);
}
   


void InsetFormula::InsetButtonRelease(int x, int y, int /*button*/)
{
   HideInsetCursor();
   x += par->xo;
   y += par->yo;
   mathcursor->SetPos(x, y);
   ShowInsetCursor();
}

void InsetFormula::InsetButtonPress(int /*x*/, int /*y*/, int /*button*/)
{

}

void InsetFormula::InsetKeyPress(XKeyEvent *)
{
   fprintf(stderr, "Used InsetFormula::InsetKeyPress.\n");
}

// Special Mathed functions
bool InsetFormula::SetNumber(bool numbf)
{
   if (disp_flag) {
      short type = par->GetType();
      bool oldf = (type==LM_OT_PARN || type==LM_OT_MPARN);
      if (numbf && !oldf) type++;
      if (!numbf && oldf) type--;
      par->SetType(type);
      return oldf;
   } else
     return false;
}

bool InsetFormula::LocalDispatch(int action, char *arg)
{
//   extern char *dispatch_result;
   static LyxMathTextCodes varcode = LM_TC_MIN;       
   static short accent = 0;
   bool was_macro = mathcursor->InMacroMode();
   bool sel=false;
   bool space_on = false;
   static MathSpaceInset* sp=NULL;

   HideInsetCursor();
   if (mathcursor->Selection()) ToggleInsetSelection();
   switch (action) {
       
    // --- Cursor Movements ---------------------------------------------
    case LFUN_RIGHTSEL: sel = true;
    case LFUN_RIGHT:
      {
	 mathcursor->Right(sel);
	 break;
      }
    case LFUN_LEFTSEL: sel = true;     
    case LFUN_LEFT:
      {
	 mathcursor->Left(sel);
	 break;
      }
    case LFUN_UP:
      mathcursor->Up();
      break;
    case LFUN_DOWN:
      mathcursor->Down();
      break;
    case LFUN_HOME:
      mathcursor->Home();
      break;
    case LFUN_END:
      mathcursor->End();
      break;
    case LFUN_DELETE_LINE_FORWARD:
      LockedInsetStoreUndo(LYX_UNDO_INSERT);
      mathcursor->DelLine();
      UpdateLocal();
      break;
    case LFUN_BREAKLINE:
      LockedInsetStoreUndo(LYX_UNDO_INSERT);
      mathcursor->Insert(' ', LM_TC_CR);
      par = mathcursor->GetPar();
      UpdateLocal();     
      break;
    case LFUN_TAB:
      LockedInsetStoreUndo(LYX_UNDO_INSERT);
      mathcursor->Insert(' ', LM_TC_TAB);
      //UpdateInset(this);
      break;     
    case LFUN_BACKSPACE:
      if (!mathcursor->Left()) break;
      
    case LFUN_DELETE:
      LockedInsetStoreUndo(LYX_UNDO_INSERT);
      mathcursor->Delete();       
      UpdateInset(this);
      break;    
    case LFUN_GETXY:
//      sprintf(dispatch_buffer, "%d %d",);
//      dispatch_result = dispatch_buffer;
      break;
    case LFUN_SETXY:
      {
	 int x, y, x1, y1;
         sscanf(arg, "%d %d", &x, &y);
	 par->GetXY(x1, y1);
	 mathcursor->SetPos(x1+x, y1+y);
      }
      break;

      /* cursor selection ---------------------------- */

    case LFUN_PASTE: mathcursor->SelPaste(); UpdateLocal(); break;
    case LFUN_CUT: mathcursor->SelCut(); UpdateLocal(); break;
    case LFUN_COPY: mathcursor->SelCopy(); break;      
    case LFUN_UPSEL:
    case LFUN_DOWNSEL:
    case LFUN_HOMESEL:
    case LFUN_ENDSEL:
    case LFUN_WORDRIGHTSEL:
    case LFUN_WORDLEFTSEL:
      break;
      
    // --- accented characters ------------------------------

    case LFUN_UMLAUT: accent = LM_ddot; break;
    case LFUN_CIRCUMFLEX: accent = LM_hat; break;
    case LFUN_GRAVE: accent = LM_grave; break;
    case LFUN_ACUTE: accent = LM_acute; break;
    case LFUN_TILDE: accent = LM_tilde; break;
    case LFUN_MACRON: accent = LM_bar; break;
    case LFUN_DOT: accent = LM_dot; break;
    case LFUN_CARON: accent = LM_check; break;
    case LFUN_BREVE: accent = LM_breve; break;
    case LFUN_VECTOR: accent = LM_vec; break; 
      
    // Greek mode     
    case LFUN_GREEK:
    {
       if (!greek_kb_flag) {
	  greek_kb_flag = 1;
	  minibuffer.Set("Math greek mode on");
       } else
	 greek_kb_flag = 0;
       break;
    }  
      
    // Greek keyboard      
    case LFUN_GREEK_TOGGLE:
    {
       greek_kb_flag = (greek_kb_flag) ? 0: 2;
       if (greek_kb_flag)
	 minibuffer.Set("Math greek keyboard on");
       else
	 minibuffer.Set("Math greek keyboard off");
       break;
    }  
   
      //  Math fonts 
    case LFUN_BOLD:  varcode = LM_TC_BF; break;
    case LFUN_SANS:  varcode = LM_TC_SF; break;
    case LFUN_EMPH:  varcode = LM_TC_CAL; break;
    case LFUN_ROMAN: varcode = LM_TC_RM; break;
    case LFUN_CODE: varcode = LM_TC_TT; break;   
    case LFUN_DEFAULT:  varcode = LM_TC_VAR; break;
    case LFUN_TEX: 
    {
       varcode = LM_TC_TEX;
       minibuffer.Set("Macro mode"); 
       break;
    }

    case LFUN_MATH_NUMBER:
    {
      LockedInsetStoreUndo(LYX_UNDO_INSERT);
       if (disp_flag) {
	  short type = par->GetType();
	  bool oldf = (type==LM_OT_PARN || type==LM_OT_MPARN);
	  if (oldf) {
	     type--;
	     if (label) {
		delete label;
		label = NULL;
	     }
	     minibuffer.Set("No number");  
	  } else {
	     type++;
             minibuffer.Set("Number");
	  }
	  par->SetType(type);
	  UpdateLocal();
       }
       break;
    }
    
    case LFUN_MATH_NONUMBER:
    { 
	if (par->GetType()==LM_OT_MPARN) {
	   MathMatrixInset *mt = (MathMatrixInset*)par;
	   mt->SetNumbered(!mt->IsNumbered());
	   UpdateLocal();
	}
	break;
    }
       
    case LFUN_MATH_LIMITS:
    {
      LockedInsetStoreUndo(LYX_UNDO_INSERT);
       if (mathcursor->Limits())
	 UpdateLocal();
    }
 
    case LFUN_MATH_SIZE:
       if (arg) {
	   latexkeys *l = in_word_set (arg, strlen(arg));
	   int sz = (l) ? l->id: -1;
	   fprintf(stderr, "sz %d[%s] ", sz, arg);
	   mathcursor->SetSize(sz);
	   UpdateLocal();
	   break;
       }
       
    case LFUN_INSERT_MATH:
    {
      LockedInsetStoreUndo(LYX_UNDO_INSERT);
       InsertSymbol(arg);
       break;
    }
    
    case LFUN_INSERT_MATRIX:
    { 
      LockedInsetStoreUndo(LYX_UNDO_INSERT);
       int m, n;
       char s[80];
       sscanf(arg, "%d %d %s", &m, &n, s);
       MathMatrixInset *p = new MathMatrixInset(m, n);      
       if (mathcursor && p) {
	  if ((int)strlen(s)>m)
	    p->SetAlign(s[0], &s[1]);
	  mathcursor->Insert(p, LM_TC_ACTIVE_INSET);
	  UpdateLocal();     
       }
       break;
    }
      
    case LFUN_MATH_DELIM:
    {  
      LockedInsetStoreUndo(LYX_UNDO_INSERT);
       char lf[40], rg[40];
       int ilf = '.', irg = '.';
       latexkeys *l;
       
       if (!arg) break;
       sscanf(arg, "%s %s", lf, rg);
//       fprintf(stderr, "DL[%s|%s]", lf, rg);
       if (IsDigit(lf[0])) 
	 ilf = atoi(lf);
       else 
	 if (lf[1]) {
	    l = in_word_set(lf, strlen(lf));
	    ilf = l->id;
	 } else
	   ilf = lf[0];
       
       if (IsDigit(rg[0]))
         irg = atoi(rg);
       else 
	 if (rg[1]) {
	    l = in_word_set(rg, strlen(rg));
	    irg = l->id;
	 } else
	   irg = rg[0];
       
       MathDelimInset* p = new MathDelimInset(ilf, irg);
  //     p->SetData(new LyxArrayBase); 
       mathcursor->Insert(p, LM_TC_ACTIVE_INSET);
       UpdateLocal();                             
       break;
    }

    case LFUN_PROTECTEDSPACE:
    {
      LockedInsetStoreUndo(LYX_UNDO_INSERT);
       sp = new MathSpaceInset(1); 
       mathcursor->Insert(sp);
       space_on = true;
       UpdateLocal();
       break;
    }
      
    case LFUN_INSERT_LABEL:
    {
      LockedInsetStoreUndo(LYX_UNDO_INSERT);
       if (par->GetType()<LM_OT_PAR) break;
       char *lb = (arg) ? StringCopy(arg):
	          StringCopy(fl_show_input("Enter new label to insert:",""));
       if (lb && lb[0]> ' ') {
	  SetNumber(true);
	  if (par->GetType()==LM_OT_MPARN) {
	      MathMatrixInset *mt = (MathMatrixInset*)par;
	      mt->SetLabel(lb);
	  } else {
	      if (label) delete label;
	      label = lb;
	  }
	  UpdateLocal();
       } else 
	 label = NULL;
       break;
    }
    
    case LFUN_MATH_DISPLAY:
      LockedInsetStoreUndo(LYX_UNDO_INSERT);
      SetDisplay(!disp_flag);
      UpdateLocal();
      break;
      
    // Invalid actions under math mode
    case LFUN_MATH_MODE: // varcode = LM_TC_TEXT; break; 
    case LFUN_UNDO:
      minibuffer.Set("Invalid action in math mode!");
      break;
      
    default:
      if (action==-1 && arg)  {
	 char c = arg[0];
	 LockedInsetStoreUndo(LYX_UNDO_INSERT);
	 
	 if (c==' ' && accent==LM_hat) {
	     c = '^';
	     arg[0] = c;
	     accent = 0;
	 }
	 if (c==0) {      // Dead key, do nothing 
	     //fprintf(stderr, "deadkey");
	     break;
	 } 
	 if ('A' <= c  && c<='Z' || 'a' <= c  && c<='z') {
	    if (varcode==LM_TC_TEX) {	      
	       mathcursor->MacroModeOpen();
	       varcode = LM_TC_MIN;
	    } else	    
	    if (!varcode) {
	       short f = mathcursor->GetFCode();
	       varcode = MathIsAlphaFont(f) ? (LyxMathTextCodes)f:LM_TC_VAR;
	    }
	    if (accent) {
	       mathcursor->Insert(new MathAccentInset(c, 
				                      (greek_kb_flag) ? 
						      LM_TC_SYMB: varcode, 
						      accent));
	       accent = 0;
	    } else
	      mathcursor->Insert(c, (greek_kb_flag) ? LM_TC_SYMB: varcode);
	    varcode = LM_TC_MIN;
	    if (greek_kb_flag<2) greek_kb_flag = 0;
	 } else 
	   if (strchr("!,:;{}", c) && (varcode==LM_TC_TEX||was_macro)) {
	      mathcursor->Insert(c, LM_TC_TEX);
	      varcode = LM_TC_MIN;
	   } else
	   if (('0'<=c && c<='9') || strchr(";:!|[]().,{}", c)) 
	      mathcursor->Insert(c, LM_TC_CONST);
	 else
	   if (strchr("+/-*<>=", c))
	   mathcursor->Insert(c, LM_TC_BOP);
	 else   
	   if (c=='_' || c=='^') {
	      mathcursor->Interpret (arg);
	   } else
	   if (c==' ') {
	      if (was_macro)
		mathcursor->MacroModeClose();
	      else
	      if (sp) {
		 int isp = (sp->GetSpace()<5) ? sp->GetSpace()+1: 0;
		 sp->SetSpace(isp);
		 space_on = true;
	      } else {
		  mathcursor->Pop();
	      }
	   } else
	   if (c=='\'') {
	      mathcursor->Insert (c, LM_TC_VAR);
	   } else
	   if (c=='\\') {
	      minibuffer.Set("Macro mode"); 
	      varcode = LM_TC_TEX;
	   } 
	 UpdateLocal();
      } else {
	// fprintf(stderr, "Closed by action %d\n", action);
	 UnlockInset(this);
	 return false;
      }
   }
   if (was_macro!=mathcursor->InMacroMode()&&action>=0&&action!=LFUN_BACKSPACE)
     UpdateLocal();
   if (sp && !space_on) sp = NULL;
   if (mathcursor->Selection()) ToggleInsetSelection();
   ShowInsetCursor();
   return true;
}


void
MathFuncInset::Draw(long unsigned int pm, int x, int y)
{ 
  if (name && name[0]>' ') {
     LyXFont  font = WhichFont(LM_TC_TEXTRM, size);
     font.latex = LYX_LATEX;
     if (mono_video) {
	 int a=LyXAscent(font, 'I'), d=LyXDescent(font, 'g');
	 XFillRectangle (fl_display, pm, LyXGetCopyGC(), x, y-a,
			 LyXTextWidth(font, name, strlen(name)), a+d);  
     }
     XDrawString(fl_display, pm, LyXGetGC(font), x, y, name, strlen(name));
  }
}


void MathFuncInset::Metrics() 
{
   ln = (name) ? strlen(name): 0;
   LyXFont  font = WhichFont(LM_TC_TEXTRM, size);
   font.latex = LYX_LATEX;
   width = LyXTextWidth(font, name, ln);
   mathed_string_height(LM_TC_TEXTRM, size, (byte*)name, strlen(name), ascent, descent);
}

// If a mathinset exist at cursor pos, just lock it.
// Otherwise create a new one and lock it.
// This function should not be here, but... 
bool OpenMathInset()
{
   if (!bufferlist.current()->screen)
     return false;
  
   extern bool selection_possible;

   LyXCursor cursor = bufferlist.current()->text->cursor;
   if (cursor.pos < cursor.par->Last() 
       && cursor.par->GetChar(cursor.pos) == LYX_META_INSET
       && cursor.par->GetInset(cursor.pos)
       && cursor.par->GetInset(cursor.pos)->Editable()) {

      Inset* tmpinset = cursor.par->GetInset(cursor.pos);
      if (tmpinset->LyxCode()==LYX_MATH_CODE) {
	 selection_possible = false;
	 bufferlist.current()->text->SetCursorParUndo();
	 tmpinset->Edit(0, 0);
	 return true;
      } 
   } else {
      bufferlist.open_new_inset(new InsetFormula);
      return true;
   }

   return false;
}

