/* 
 *  File:        math_draw.C
 *  Purpose:     Interaction and drawing for mathed
 *  Author:      Alejandro Aguilar Sierra <asierra@servidor.unam.mx> 
 *  Created:     January 1996
 *  Description: Math drawing and 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_cursor.h"
#include "math_parser.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&);
   
GC canvasGC=0, mathGC=0, latexGC=0, cursorGC=0, mathFrameGC;

void
MathSpaceInset::Draw(long unsigned int pm, int x, int y)
{ 
   XPoint p[4] = {{++x, y-3}, {x, y}, {x+width-2, y}, {x+width-2, y-3}};
   XDrawLines(fl_display,pm,(space) ? latexGC: mathGC,p,4,CoordModeOrigin);
}

void 
MathParInset::Draw(long unsigned pm, int x, int y)
{
   byte cx, cxp=0;
   int xp=0, ls;
   int asc, des;
   bool limits = false;
              
   xo = x;  yo = y; 
   asc = des = 0;
   if (!array || array->Empty()) {
      mathed_set_font(LM_TC_VAR, 1);
      XDrawRectangle(fl_display,pm,mathGC,x,y-8, 4, 8); 
      return;
   }  
   LyxMathXIter data(this);
   data.Reset();
   while (data.OK()) {
      data.GetPos(x, ls);
      cx = data.GetChar();
      if (cx >= ' ') {
	 byte *s = data.GetString(ls);
	 mathed_set_font(data.FCode(), size);
	 GC gc = (data.FCode()==LM_TC_TEX) ? latexGC: mathGC;
	 XDrawString(fl_display, pm, gc, x, y, (char*)s, ls);
	 mathed_char_height(data.FCode(), size, s[ls-1], asc, des);
	 limits = false;
      } else {
	 if (cx==0) break;
	 if (MathIsInset(cx)) {
	    int yy = y;
	    LyxMathInset *p = data.GetInset();
	    if (cx==LM_TC_UP) {
	       if (limits) {
		  x -= (xp>p->Width()) ? p->Width()+(xp-p->Width())/2: xp;  
		  yy -= (asc + p->Descent()+4);
	       } else
		 yy -= asc;
	    } else
	    if (cx==LM_TC_DOWN) {
	       if (limits) {
		  x -= (xp>p->Width()) ? p->Width()+(xp-p->Width())/2: xp;
		  yy += des + p->Ascent() + 2;
	       } else
		 yy += des + p->Ascent()/2;
	    } else {
	       asc = p->Ascent();
	       des = p->Descent();
	    }
	    p->Draw(pm, x, yy);
	    if (cx!=LM_TC_UP && cx!=LM_TC_DOWN) {
	       limits = p->GetLimits();
	       if (limits) xp = p->Width();
	    }
	    data.Next();
	 } else 
	   if (cx==LM_TC_TAB) {
	      if ((cxp==cx || data.IsFirst()) && objtype==LM_OT_MATRIX)
		XDrawRectangle(fl_display,pm,mathGC,x,y-8, 4, 8);
	      data.Next();
	      limits = false;
	   }
	 else {	 
	    fprintf(stderr, "GLyxMath Error: Unrecognized code[%d]\n", cx);
	    break;
	 }
      }
      cxp = cx;
//      data.GetPos(x, ls);
   }
   if (cxp==LM_TC_TAB && objtype==LM_OT_MATRIX) {
      data.GetPos(x, ls);
      XDrawRectangle(fl_display,pm,mathGC,x,y-8, 4, 8);
   }
}

void 
MathParInset::Metrics()
{
   byte cx, cxp=0, *s;
   int ls;
   int asc, des;
   int tb = 0;
   bool limits = false;
   
   ascent = mathed_char_height(LM_TC_VAR, size, 'I', asc, des); 
   width = ascent/2;
   descent = 0;
   if (!array) return;
   if (array->Empty()) return;
   
   ascent = 0;
   LyxMathXIter data(this);
   data.Reset();
   while (data.OK()) {
      cx = data.GetChar();      
      if (cx >= ' ') {
	 s = data.GetString(ls);
	 mathed_string_height(data.FCode(), size, s, ls, asc, des);
	 if (asc > ascent) ascent = asc;
	 if (des > descent) descent = des;
	 limits = false;
      } else
      if (MathIsInset(cx)) {
	 LyxMathInset *p = data.GetInset();
	 int sz = p->GetStyle();
	 if (sz!=size && !(cx==LM_TC_UP || cx==LM_TC_DOWN))
	   p->SetStyle(size);
	 else 
	   if ((cx==LM_TC_UP || cx==LM_TC_DOWN) && size<LM_ST_SCRIPTSCRIPT) {
	      sz = (size<LM_ST_SCRIPT) ? LM_ST_SCRIPT: LM_ST_SCRIPTSCRIPT;
	      p->SetStyle(sz);   
      	   }
	 if (MathIsActive(cx)) 
	   p->Metrics();
	 if (cx==LM_TC_UP) {
	    asc += ((limits) ? p->Height()+4: p->Ascent());
	 } else
	 if (cx==LM_TC_DOWN) {
	    des += ((limits) ? p->Height()+4: p->Height()-p->Ascent()/2);
	 } else {
	    asc = p->Ascent();
	    des = p->Descent();
	 }
	 if (asc > ascent) ascent = asc;
	 if (des > descent) descent = des;
	 if (cx!=LM_TC_UP && cx!=LM_TC_DOWN)
	    limits = p->GetLimits();
	 data.Next();
      } else 
	if (cx==LM_TC_TAB) {
	   int x, y;
	   data.GetIncPos(x, y);
	   if (data.IsFirst() || cxp==LM_TC_TAB) {
	      if (ascent<8) ascent = 8;
	      SetTab(0);
	      tb = 1;
	   } else {
	      SetTab(x-tb);
	      tb = x;
	   }
	   limits = false;                   
	   data.Next();
	}      
      else {
	 fprintf(stderr, "Mathed Error: Unrecognized code[%d]\n", cx);
	 break;
      }       
      cxp = cx;
   }
   data.GetIncPos(width, ls);
   if (tb>0) {
      if (cxp==LM_TC_TAB) {
	 if (ascent<8) ascent = 8;
	 SetTab(0);	 
      } else
	SetTab(width-tb);
   }
}


void
MathSqrtInset::Draw(long unsigned int pm, int x, int y)
{ 
   MathParInset::Draw(pm, x+hmax+2, y); 
   int h=ascent, d=descent, h2=Height()/2, w2 = (Height()>4*hmax)?hmax:hmax/2; 
   XPoint p[4];
   p[0].x = x+width-1, p[0].y = y-h;
   p[1].x = x+hmax,    p[1].y = y-h;
   p[2].x = x+w2,      p[2].y = y+d;
   p[3].x = x,         p[3].y = y+d-h2;
   XDrawLines(fl_display, pm, mathGC,p, 4, CoordModeOrigin);
}

void
MathSqrtInset::Metrics()
{
   MathParInset::Metrics();
   ascent += 4;
   descent += 2;
   int a, b;
   hmax = mathed_char_height(LM_TC_VAR, size, 'I',a, b);
   if (hmax<10) hmax = 10;
   width += hmax + 4;
}

void
MathFracInset::Draw(long unsigned int pm, int x, int y)
{ 
   bool upx = upper; 
   upper = true;
   MathParInset::Draw(pm, x+(width-w0)/2, y - des0);
   den->Draw(pm, x+(width-w1)/2, y + den->Ascent() + 2 - dh);
   XDrawLine(fl_display, pm, mathGC, x+2, y-dh, x+width-4, y - dh);
   upper = upx;
}

void
MathFracInset::Metrics()
{
   if (!dh)
     dh = mathed_char_width(LM_TC_CONST, size, 'I')/2;
   
   bool upx = upper;
   upper = true;
   MathParInset::Metrics();
   w0 = width;
   int as = Height() + 2 + dh;
   des0 = Descent() + 2 + dh;
   den->Metrics();  
   w1 = den->Width();   
   width = ((w0 > w1) ? w0: w1) + 12;
   ascent = as; 
   descent = den->Height()+ 2 - dh;
   upper = upx;
}


void
MathBigopInset::Draw(long unsigned int pm, int x, int y)
{
   int ls;
   char c, *s;
   short t;
   
   if (sym<256 || sym==LM_oint) {
      ls = 1;
      c = (sym==LM_oint) ? LM_int: sym;
      s = &c;
      t = LM_TC_BSYM;
   } else {
      s = name;
      ls = strlen(name);
      t = LM_TC_TEXTRM;
   }
   mathed_set_font(t, size);
   if (sym==LM_oint) {
      XDrawArc(fl_display, pm, mathGC, x,y-5*width/4,width,width,0,23040);
      x++;
   }
   XDrawString(fl_display, pm, mathGC, x, y, s, ls); 
}

void
MathBigopInset::Metrics()
{   
   int ls;
   char c, *s;
   short t;

   if (sym<256 || sym==LM_oint) {
      ls = 1;
      c = (sym==LM_oint) ? LM_int: sym;
      s = &c;
      t = LM_TC_BSYM;
   } else {
      s = name;
      ls = strlen(name);
      t = LM_TC_TEXTRM;
   }
   mathed_set_font(t, size);
   mathed_string_height(t, size, (unsigned char*)s, ls, ascent, descent);
   width = mathed_string_width(t, size, (unsigned char*)s, ls);
   if (sym==LM_oint) width += 2;
}

