/*
 *  File:        math_simbols.C
 *  Purpose:     User interface to math symbols
 *  Author:      Alejandro Aguilar Sierra <asierra@servidor.unam.mx>
 *  Created:     November 1995
 *  Version:     0.8 28/03/96
 *  Description: Provides a GUI to introduce mathematical
 *               symbols in lyx.
 *
 *  Dependencies: Xlib, XForms, Lyx
 *
 *  Copyright: (c) 1995, 1996, Alejandro Aguilar Sierra 
 *
 *   You are free to use and modify it under the terms of
 *   the GNU General Public Licence version 2 or later.
 */

#include "config.h"

#ifdef XPM_H_WITHOUT_X11
# include <xpm.h>
#else
# include <X11/xpm.h>
#endif
#include <stdlib.h>

#include "file.h"
#include "buffer.h"
#include "xdefinitions.h"
#include "formula.h"

#include "math_panel.h"                 
#include "math_parser.h"

extern void SmallUpdate(signed char);
extern void BeforeChange();
extern void Update(signed char);
extern int UnlockInset(UpdatableInset*);
extern short greek_kb_flag;

/* Bitmaps */
#include "greek.xbm"
#include "arrows.xbm"
#include "brel.xbm"
#include "bop.xbm"
#include "misc.xbm"           
#include "varsz.xbm"           
#include "dots.xbm"

/* Latex code for those bitmaps */
static char *latex_greek[] =  {
   "Gamma", "Delta", "Theta", "Lambda", "Xi", "Pi",
   "Sigma", "Upsilon", "Phi", "Psi", "Omega",
   "alpha", "beta", "gamma", "delta", "epsilon", "varepsilon", "zeta",
   "eta", "theta", "vartheta", "iota", "kappa", "lambda", "mu",
   "nu", "xi", "pi", "varpi", "rho", "sigma", "varsigma",
   "tau", "upsilon", "phi", "varphi", "chi", "psi", "omega"
};

static char *latex_brel[] = {
  "leq", "geq", "equiv", "models", 
  "prec", "succ", "sim", "perp", 
  "preceq", "succeq", "simeq", "mid", 
  "ll", "gg", "asymp", "parallel", 
  "subset", "supset", "approx", "smile", 
  "subseteq", "supseteq", "cong", "frown", 
  "sqsubseteq", "sqsupseteq", "doteq", "neq", 
  "in", "ni", "propto", "notin", 
  "vdash", "dashv", "bowtie"
};

static char* latex_arrow[] = {
  "downarrow", "leftarrow", "Downarrow", "Leftarrow", 
  "hookleftarrow", "rightarrow", "uparrow", "Rightarrow", "Uparrow",
  "hookrightarrow","updownarrow", "Leftrightarrow", "leftharpoonup", 
  "rightharpoonup", "rightleftharpoons", "leftrightarrow", "Updownarrow", 
  "leftharpoondown", "rightharpoondown", "mapsto",
  "Longleftarrow", "Longrightarrow", "Longleftrightarrow", 
  "longleftrightarrow", "longleftarrow", "longrightarrow", "longmapsto",
  "nwarrow", "nearrow", "swarrow", "searrow",  
};

char *latex_varsz[] = {
"sum", "int", "oint", 
"prod", "coprod", "bigsqcup", 
"bigotimes", "bigodot", "bigoplus", 
"bigcap", "bigcup", "biguplus", 
"bigvee", "bigwedge"
};

static char* latex_bop[] = {   
  "pm", "cap", "diamond", "oplus", 
  "mp", "cup", "bigtriangleup", "ominus", 
  "times", "uplus", "bigtriangledown", "otimes", 
  "div", "sqcap", "triangleright", "oslash", 
  "cdot", "sqcup", "triangleleft", "odot", 
  "star", "vee", "amalg", "bigcirc", 
  "setminus", "wedge", "dagger", "circ", 
  "bullet", "wr", "ddagger"
};

static char* latex_misc[] = {
  "nabla", "partial", "infty", "prime", "ell", 
  "emptyset", "exists", "forall", "i",  "j",
  "Re", "Im", "aleph", "wp", "hbar", 
  "angle", "top", "bot", "Vert", "neg", 
  "flat", "natural", "sharp", "surd", "triangle", 
  "diamondsuit", "heartsuit", "clubsuit", "spadesuit" 
};

static char* latex_delim[] = { 
  "lceil", "rceil", 
  "lfloor", "rfloor", 
  "langle", "rangle",  
  "Vert", "backslash",
};

static char* latex_dots[] = { 
   "ldots", "cdots", "vdots", "ddots"
};

static signed char latin2greek[] =  {
  0, 1, 25, 3, 4, 23, 2, 7, 10, 24, 11, 12, 13, 14, -1, 16, 8, 18,
  19, 21, 22, 17, 27, 15, 26, 6
};
 
static signed char Latin2Greek[] =  {
 -1, -1, -1, 1, -1, 8, 0, -1, -1, -1, -1, 3, -1, -1, -1,
 5, 2, -1, 6, -1, 7, -1, 10, 4, 9, -1
}; 

extern char** mathed_get_pixmap_from_icon(int d);
static void math_cb(FL_OBJECT*, long);
static FD_panel* symb_form=0;
void math_insert_symbol(const char* s);
Bool math_insert_greek(const char c);

BitmapMenu *BitmapMenu::active = NULL;

BitmapMenu::BitmapMenu(int n,  FL_OBJECT* bt, BitmapMenu* prevx): nb(n)  {
   w = h = 0;
   form = 0;
   i = 0;
   ww = 2*FL_abs(FL_BOUND_WIDTH);
   x = y = ww;
   y += 8;
   bitmap = new FL_OBJECTP[nb];
   button = bt;
   button->u_vdata = this;
   prev = prevx;
   next = NULL;
   if (prev)
     prev->next = this;
}

BitmapMenu::~BitmapMenu() {
 if (next) delete next;
 fl_free_form(form);  
 delete[] bitmap;
}

void BitmapMenu::Hide()  {
   fl_hide_form(form);
   fl_set_button(button, 0);
   active = NULL;
}

void BitmapMenu::Show()  {
   if (active)
     active->Hide();
   active = this;
   int x = button->form->x + button->x, y = button->form->y + button->y;
   fl_set_form_position(form, x, y + button->h);
   fl_set_button(button, 1);
   fl_show_form(form, FL_PLACE_POSITION, FL_NOBORDER, "");
}

FL_OBJECT*
BitmapMenu::AddBitmap(int id, int nx, int ny, int bw, int bh, char* data, Bool vert)
{
   if (i>=nb)
     return NULL;
   int wx=bw+ww/2, wy=bh+ww/2;
   wx += (wx % nx);
   wy += (wy % ny); 
   FL_OBJECT *obj = fl_create_bmtable(1, x, y, wx, wy, "");   
   fl_set_object_callback(obj, math_cb, id);
   fl_set_object_lcol(obj, FL_BLUE);    
   fl_set_object_boxtype(obj, FL_UP_BOX);   
   fl_set_bmtable_data(obj, nx, ny, bw, bh, data);
   if (vert) { 
      y += wy + 8;
      h = FL_max(y, h);
      w = FL_max(x + wx + ww, w);
   } else  {
      x += wx + 8;
      w = FL_max(x, w);
      h = FL_max(y + wy + ww, h);
   }
   bitmap[i++] = obj;
   return obj;
}

void BitmapMenu::Create()
{
   if (i<nb)  {
      fprintf(stderr, "Error: Bitmaps not created!");
      return;
   }
   form = fl_bgn_form(FL_UP_BOX, w, h);   
   for (i=0; i<nb; i++) {
      fl_add_object(form, bitmap[i]);
      bitmap[i]->u_vdata = this;
   }
   fl_end_form();
   fl_register_raw_callback(form, KeyPressMask, peek_event);
}

int BitmapMenu::GetIndex(FL_OBJECT* ob)
{
   if (active==this) {
      int k = 0;
      for (i=0; i<nb; i++) {
	 if (bitmap[i]==ob) 
	   return k+fl_get_bmtable(ob);
	 k += fl_get_bmtable_maxitems(bitmap[i]);
      }
   }
   return -1;
}

int peek_event(FL_FORM * /*form*/, void *xev)
{
   if (BitmapMenu::active==NULL)
     return 0;
  
   if(((XEvent *)xev)->type == ButtonPress)
   {
	 BitmapMenu::active->Hide();
	 return 1;
   }
   if(((XEvent *)xev)->type == KeyPress)
   {
      char c[5];
      KeySym keysym;
      XLookupString(&((XEvent *)xev)->xkey, &c[0], 5, &keysym, NULL);
      if (keysym==XK_Left) 
	BitmapMenu::active->Prev(); else
      if (keysym==XK_Right) 
	BitmapMenu::active->Next(); 
      else 
	BitmapMenu::active->Hide();
      return 1;
   }
   return 0;  
}

static void math_cb(FL_OBJECT* ob, long data)
{
   BitmapMenu* menu = (BitmapMenu*)ob->u_vdata;
   int i = menu->GetIndex(ob);   
   char *s = 0;

//   fprintf(stderr, "data[%d]", data);     
   if (i<0) return;
   switch (data)  {
    case MM_GREEK: 
      s = latex_greek[i]; 
      break;
    case MM_ARROW: 
      s = latex_arrow[i]; 
      break;  
    case MM_BRELATS: 
      s = latex_brel[i]; 
      break;  
    case MM_BOP: 
      s = latex_bop[i]; 
      break;  
    case MM_VARSIZE: 
      s = latex_varsz[i];  
      break;
    case MM_MISC: 
      s = latex_misc[i];  
      break;
    case MM_DOTS: 
//      fprintf(stderr, "dots[%s %d]", latex_dots[i], i);
      s = latex_dots[i-29];  
      break;
   }
  
   if (s)  {
#ifdef MATHSYMDEMO      
      fprintf(stderr, "%s", s);  fflush(stderr);
#else
      math_insert_symbol(s);
#endif
   }      
   if (menu)  
     menu->Hide(); 
}

#ifndef MATHSYMDEMO      

char** get_pixmap_from_symbol(const char *arg, int wx, int hx)
{
   char** data=NULL;   		    
   latexkeys *l = in_word_set (arg, strlen(arg));
   if (!l) 
    return NULL;
    
   switch (l->token) {
    case LM_TK_FRAC:
      data = mathed_get_pixmap_from_icon(MM_FRAC);
      break;
    case LM_TK_SQRT: 
      data = mathed_get_pixmap_from_icon(MM_SQRT);
      break;
    case LM_TK_BIGSYM:
    case LM_TK_SYM:	  
      int w = greek_width/7, h = greek_height/4;
   
      XImage *xima = XCreateImage(fl_display, NULL, 1, XYBitmap, 0, 
			       greek_bits, greek_width, greek_height, 8, 0);
   
      if (!xima)
	return NULL;
      xima->byte_order = LSBFirst;
      xima->bitmap_bit_order = LSBFirst;
      xima->width = (w<wx-4) ? w: wx-4;
      xima->height = (h<hx-4) ? h: hx-4;
      XpmCreateDataFromImage(fl_display, &data, xima, NULL, NULL);
      break;
   }
   
   return data;
}

Bool math_insert_greek(const char c)
{
   int i;
   char *s=NULL;
   
   if ('A'<=c && c<='Z') {
      if ((i=Latin2Greek[c - 'A'])>=0)
	s = latex_greek[i];
   }   
   if ('a'<=c && c<='z') {
      if ((i=latin2greek[c - 'a'])>=0)
	s = latex_greek[i+11];
   }
   if (s) {
      math_insert_symbol(s);
      if (greek_kb_flag<2) {
	 greek_kb_flag = 0;
	 UnlockInset(bufferlist.current()->the_locking_inset);
      }
      return True;
   } else
     return False; 
}

void math_insert_symbol(const char* s)
{
   if (bufferlist.current()->text)   {
      if (!bufferlist.current()->the_locking_inset) {
	 InsetFormula* new_inset = new InsetFormula();
	 BeforeChange();
	 bufferlist.current()->text->InsertInset(new_inset);
	 Update(1);
	 new_inset->Edit(0,0);
	 new_inset->InsertSymbol(s);
      } else
	if (bufferlist.current()->the_locking_inset->LyxCode()==LYX_MATH_CODE)
	  ((InsetFormula*)bufferlist.current()->the_locking_inset)->InsertSymbol(s);
        else 
	  fprintf(stderr, "Math error: attempt to write on a wrong class of inset.\n");
   }
}
#endif

static BitmapMenu* sym_menu=0;

void  create_symbol_menues(void)
{
   FL_OBJECT* obj; 
   BitmapMenu* menu;
   
   sym_menu = menu = new BitmapMenu(2, symb_form->greek);
   obj = menu->AddBitmap(MM_GREEK, 6, 2, Greek_width, Greek_height, 
			   Greek_bits);         
   fl_set_bmtable_maxitems(obj, 11);
   obj = menu->AddBitmap(MM_GREEK, 7, 4, greek_width, greek_height,
			   greek_bits); 
   menu->Create();
   
   menu = new BitmapMenu(1, symb_form->boperator, menu);
   obj = menu->AddBitmap(MM_BOP, 4, 8, bop_width, bop_height,
                         bop_bits);      
   fl_set_bmtable_maxitems(obj, 31);
   menu->Create();   
      
   menu = new BitmapMenu(1, symb_form->brelats, menu);
   obj = menu->AddBitmap(MM_BRELATS, 4, 9, brel_width, brel_height, 
			 brel_bits);        
   fl_set_bmtable_maxitems(obj, 35);       
   menu->Create();
   
   menu = new BitmapMenu(3, symb_form->arrow, menu);
   obj = menu->AddBitmap(MM_ARROW, 5, 4, arrow_width, arrow_height,
			 arrow_bits);
   obj = menu->AddBitmap(MM_ARROW, 2, 4, larrow_width, larrow_height, 
			 larrow_bits, False);          
   fl_set_bmtable_maxitems(obj, 7);
   obj = menu->AddBitmap(MM_ARROW, 2, 2, darrow_width, darrow_height, 
			 darrow_bits);
   menu->Create(); 
     
   menu = new BitmapMenu(1, symb_form->varsize, menu);
    obj = menu->AddBitmap(MM_VARSIZE, 3, 5, varsz_width, varsz_height, 
			 varsz_bits);         
   fl_set_bmtable_maxitems(obj, 14);
   menu->Create();
      
   menu = new BitmapMenu(2, symb_form->misc, menu);
     obj = menu->AddBitmap(MM_MISC, 5, 6, misc_width, misc_height,
			   misc_bits);         
     fl_set_bmtable_maxitems(obj, 29);
     obj = menu->AddBitmap(MM_DOTS, 4, 1, dots_width, dots_height, 
			   dots_bits);         
   menu->Create();
}

void free_symbols_form()
{
   if (symb_form) {
      fl_hide_form(symb_form->panel);
      fl_free_form(symb_form->panel);
      delete sym_menu;
      delete symb_form;
      symb_form = NULL;  
   }
}

int AtClose_symbols_form(FL_FORM*, void */*d*/)
{
  free_symbols_form();
  return FL_IGNORE;
}
/*
void button_cb(FL_OBJECT *ob, long data)
{
   BitmapMenu *menu = 0;
   switch (data)  {
    case MM_GREEK:
    case MM_VARSIZE:
    case MM_BRELATS:
    case MM_ARROW:
    case MM_BOP:
    case MM_MISC: 
      menu = (BitmapMenu *)ob->u_vdata;
      menu->Show();  
      break;
    case 100:
      free_symbols_form();
      break;
   }
}
*/
void show_symbols_form(void)
{
   if (!symb_form) {
      symb_form = create_math_panel();
      fl_register_raw_callback(symb_form->panel, 
			       ButtonPressMask|KeyPressMask, peek_event);
      create_symbol_menues();	
#ifndef MATHSYMDEMO
      fl_set_form_atclose(symb_form->panel, AtClose_symbols_form, NULL);
#endif 
   }
   if (symb_form->panel->visible) {
	   // XRaiseWindow(fl_display, symb_form->panel->window);
	   fl_raise_form(symb_form->panel);
   } else
     fl_show_form(symb_form->panel, FL_PLACE_SIZE, FL_FULLBORDER, "Math Panel");
}

#ifdef MATHSYMDEMO
int main(int argc, char *argv[])
{
   fl_initialize(argv[0], "Bitmap Table Demo", 0, 0, &argc, argv);	    
   show_symbols_form();                 
   fl_do_forms();
   free_symbols_form();
}     
#endif

