]> git.lyx.org Git - lyx.git/blob - src/mathed/math_symbols.C
Fix cursor bug with math greek mode.
[lyx.git] / src / mathed / math_symbols.C
1 /*
2  *  File:        math_symbols.C
3  *  Purpose:     User interface to math symbols
4  *  Author:      Alejandro Aguilar Sierra <asierra@servidor.unam.mx>
5  *  Created:     November 1995
6  *  Version:     0.8 28/03/96
7  *  Description: Provides a GUI to introduce mathematical
8  *               symbols in lyx.
9  *
10  *  Dependencies: Xlib, XForms, Lyx
11  *
12  *  Copyright: 1995, 1996, Alejandro Aguilar Sierra 
13  *
14  *   You are free to use and modify it under the terms of
15  *   the GNU General Public Licence version 2 or later.
16  */
17
18 #include <config.h>
19 #include XPM_H_LOCATION
20
21 #ifdef __GNUG__
22 #pragma implementation "math_panel.h"
23 #endif
24
25 #include <algorithm>
26
27 #include "lyx_main.h"
28 #include "buffer.h"
29 #include "BufferView.h"
30 #include "minibuffer.h"
31 #include "lyxrc.h"
32 #include "LyXView.h"
33 #include "support/lstrings.h"
34 #include "debug.h"
35 #include "lyxfunc.h"
36 #include "lyxtext.h"
37
38 #include "formula.h"
39
40 #include "math_panel.h"                 
41 #include "math_parser.h"
42
43 using std::max;
44 using std::endl;
45 using std::ostream;
46
47 extern int greek_kb_flag;
48
49 extern BufferView * current_view;
50
51 /* Bitmaps */
52 #include "greek.xbm"
53 #include "arrows.xbm"
54 #include "brel.xbm"
55 #include "bop.xbm"
56 #include "misc.xbm"           
57 #include "varsz.xbm"           
58 #include "dots.xbm"
59
60 /* Latex code for those bitmaps */
61 static char const * latex_greek[] =  {
62    "Gamma", "Delta", "Theta", "Lambda", "Xi", "Pi",
63    "Sigma", "Upsilon", "Phi", "Psi", "Omega",
64    "alpha", "beta", "gamma", "delta", "epsilon", "varepsilon", "zeta",
65    "eta", "theta", "vartheta", "iota", "kappa", "lambda", "mu",
66    "nu", "xi", "pi", "varpi", "rho", "sigma", "varsigma",
67    "tau", "upsilon", "phi", "varphi", "chi", "psi", "omega", ""
68 };
69
70 static char const * latex_brel[] = {
71   "leq", "geq", "equiv", "models", 
72   "prec", "succ", "sim", "perp", 
73   "preceq", "succeq", "simeq", "mid", 
74   "ll", "gg", "asymp", "parallel", 
75   "subset", "supset", "approx", "smile", 
76   "subseteq", "supseteq", "cong", "frown", 
77   "sqsubseteq", "sqsupseteq", "doteq", "neq", 
78   "in", "ni", "propto", "notin", 
79   "vdash", "dashv", "bowtie", ""
80 };
81
82 static char const * latex_arrow[] = {
83   "downarrow", "leftarrow", "Downarrow", "Leftarrow", 
84   "hookleftarrow", "rightarrow", "uparrow", "Rightarrow", "Uparrow",
85   "hookrightarrow", "updownarrow", "Leftrightarrow", "leftharpoonup", 
86   "rightharpoonup", "rightleftharpoons", "leftrightarrow", "Updownarrow", 
87   "leftharpoondown", "rightharpoondown", "mapsto",
88   "Longleftarrow", "Longrightarrow", "Longleftrightarrow", 
89   "longleftrightarrow", "longleftarrow", "longrightarrow", "longmapsto",
90   "nwarrow", "nearrow", "swarrow", "searrow",  "",
91 };
92
93 char const * latex_varsz[] = {
94 "sum", "int", "oint", 
95 "prod", "coprod", "bigsqcup", 
96 "bigotimes", "bigodot", "bigoplus", 
97 "bigcap", "bigcup", "biguplus", 
98 "bigvee", "bigwedge", ""
99 };
100
101 static char const * latex_bop[] = {   
102   "pm", "cap", "diamond", "oplus", 
103   "mp", "cup", "bigtriangleup", "ominus", 
104   "times", "uplus", "bigtriangledown", "otimes", 
105   "div", "sqcap", "triangleright", "oslash", 
106   "cdot", "sqcup", "triangleleft", "odot", 
107   "star", "vee", "amalg", "bigcirc", 
108   "setminus", "wedge", "dagger", "circ", 
109   "bullet", "wr", "ddagger", ""
110 };
111
112 static char const * latex_misc[] = {
113   "nabla", "partial", "infty", "prime", "ell", 
114   "emptyset", "exists", "forall", "imath",  "jmath",
115   "Re", "Im", "aleph", "wp", "hbar", 
116   "angle", "top", "bot", "Vert", "neg", 
117   "flat", "natural", "sharp", "surd", "triangle", 
118   "diamondsuit", "heartsuit", "clubsuit", "spadesuit", "" 
119 };
120
121 static char const * latex_dots[] = { 
122    "ldots", "cdots", "vdots", "ddots"
123 };
124
125 extern char const ** mathed_get_pixmap_from_icon(int d);
126 extern "C" void math_cb(FL_OBJECT*, long);
127 static char const ** pixmapFromBitmapData(char const *, int, int);
128 void math_insert_symbol(char const * s);
129 bool math_insert_greek(char c);
130
131 BitmapMenu * BitmapMenu::active = 0;
132
133 BitmapMenu::BitmapMenu(int n,  FL_OBJECT * bt, BitmapMenu * prevx): nb(n)
134 {
135    w = h = 0;
136    form = 0;
137    i = 0;
138    ww = 2 * FL_abs(FL_BOUND_WIDTH);
139    x = y = ww;
140    y += 8;
141    bitmap = new FL_OBJECTP[nb];
142    button = bt;
143    button->u_vdata = this;
144    prev = prevx;
145    next = 0;
146    if (prev)
147      prev->next = this;
148 }
149
150
151 BitmapMenu::~BitmapMenu()
152 {
153         delete next;
154         if (form->visible) Hide();
155         fl_free_form(form);  
156         delete[] bitmap;
157 }
158
159
160 void BitmapMenu::Hide()
161 {
162    fl_hide_form(form);
163    fl_set_button(button, 0);
164    active = 0;
165 }
166
167
168 void BitmapMenu::Show()
169 {
170    if (active)
171      active->Hide();
172    active = this;
173    //   int x = button->form->x + button->x, y = button->form->y + button->y;
174    //   fl_set_form_position(form, x, y + button->h);
175    fl_set_button(button, 1);
176    fl_show_form(form, FL_PLACE_MOUSE, FL_NOBORDER, "");
177 }
178
179 FL_OBJECT *
180 BitmapMenu::AddBitmap(int id, int nx, int ny, int bw, int bh,
181                       unsigned char const * data, Bool vert)
182 {
183    if (i >= nb)
184      return 0;
185    int wx = bw+ww/2, wy = bh+ww/2;
186    wx += (wx % nx);
187    wy += (wy % ny); 
188    FL_OBJECT * obj = fl_create_bmtable(1, x, y, wx, wy, "");   
189    fl_set_object_callback(obj, math_cb, id);
190    fl_set_object_lcol(obj, FL_BLUE);    
191    fl_set_object_boxtype(obj, FL_UP_BOX);   
192    fl_set_bmtable_data(obj, nx, ny, bw, bh, data);
193    if (vert) { 
194       y += wy + 8;
195       h = max(y, h);
196       w = max(x + wx + ww, w);
197    } else  {
198       x += wx + 8;
199       w = max(x, w);
200       h = max(y + wy + ww, h);
201    }
202    bitmap[i++] = obj;
203    return obj;
204 }
205
206 void BitmapMenu::Create()
207 {
208    if (i < nb)  {
209            lyxerr << "Error: Bitmaps not created!" << endl;
210       return;
211    }
212    form = fl_bgn_form(FL_UP_BOX, w, h);   
213    for (i = 0; i < nb; ++i) {
214       fl_add_object(form, bitmap[i]);
215       bitmap[i]->u_vdata = this;
216    }
217    fl_end_form();
218    fl_register_raw_callback(form, KeyPressMask, C_peek_event);
219 }
220
221 int BitmapMenu::GetIndex(FL_OBJECT* ob)
222 {
223    if (active == this) {
224       int k = 0;
225       for (i = 0; i < nb; ++i) {
226          if (bitmap[i] == ob) 
227            return k+fl_get_bmtable(ob);
228          k += fl_get_bmtable_maxitems(bitmap[i]);
229       }
230    }
231    return -1;
232 }
233
234 int peek_event(FL_FORM * /*form*/, void *xev)
235 {
236    if (BitmapMenu::active == 0)
237      return 0;
238   
239    if (static_cast<XEvent *>(xev)->type == ButtonPress)
240    {
241          BitmapMenu::active->Hide();
242          return 1;
243    }
244    if (static_cast<XEvent *>(xev)->type == KeyPress)
245    {
246       char c[5];
247       KeySym keysym;
248       XLookupString(&static_cast<XEvent *>(xev)->xkey, &c[0], 5, &keysym, 0);
249       if (keysym == XK_Left) 
250         BitmapMenu::active->Prev(); else
251       if (keysym == XK_Right) 
252         BitmapMenu::active->Next(); 
253       else 
254         BitmapMenu::active->Hide();
255       return 1;
256    }
257    return 0;  
258 }
259
260 // This is just a wrapper.
261 extern "C" int C_peek_event(FL_FORM *form, void *ptr) {
262   return peek_event(form, ptr);
263 }
264
265
266 extern "C" void math_cb(FL_OBJECT* ob, long data)
267 {
268    BitmapMenu * menu = static_cast<BitmapMenu*>(ob->u_vdata);
269    int i = menu->GetIndex(ob);   
270    char const *s = 0;
271
272 //   lyxerr << "data[" << data << "]";
273    if (i<0) return;
274    switch (data)  {
275     case MM_GREEK: 
276       s = latex_greek[i]; 
277       break;
278     case MM_ARROW: 
279       s = latex_arrow[i]; 
280       break;  
281     case MM_BRELATS: 
282       s = latex_brel[i]; 
283       break;  
284     case MM_BOP: 
285       s = latex_bop[i]; 
286       break;  
287     case MM_VARSIZE: 
288       s = latex_varsz[i];  
289       break;
290     case MM_MISC: 
291       s = latex_misc[i];  
292       break;
293     case MM_DOTS: 
294 //      lyxerr << "dots[" << latex_dots[i] << " " << i << "]";
295       s = latex_dots[i-29];  
296       break;
297    }
298   
299    if (s)  {
300       if (current_view->available() && lyxrc.display_shortcuts) {
301           current_view->owner()->getMiniBuffer()->Set("Inserting symbol ", s);
302       }
303       current_view->owner()->getLyXFunc()->Dispatch(LFUN_INSERT_MATH, s);
304    }      
305    if (menu)  
306      menu->Hide(); 
307 }
308
309 char const ** get_pixmap_from_symbol(char const * arg, int wx, int hx)
310 {
311    char const ** data = 0;                  
312    latexkeys * l = in_word_set (arg, strlen(arg));
313    if (!l) 
314     return 0;
315     
316    switch (l->token) {
317     case LM_TK_FRAC:
318       data = mathed_get_pixmap_from_icon(MM_FRAC);
319       break;
320     case LM_TK_SQRT: 
321       data = mathed_get_pixmap_from_icon(MM_SQRT);
322       break;
323     case LM_TK_BIGSYM:
324     case LM_TK_SYM:
325        // I have to use directly the bitmap data since the
326        // bitmap tables are not yet created when this
327        // function is called.
328        data = pixmapFromBitmapData(arg, wx, hx);
329        break;
330    }
331    
332    return data;
333 }
334
335 bool math_insert_greek(BufferView * bv, char c)
336 {
337    if (bv->available() &&
338        (('A' <= c && c <= 'Z') ||
339         ('a'<= c && c<= 'z')))   {
340       string tmp;
341       tmp = c;
342       if (!bv->theLockingInset() || bv->theLockingInset()->IsTextInset()) {
343          int greek_kb_flag_save = greek_kb_flag;
344          InsetFormula * new_inset = new InsetFormula();
345          bv->beforeChange();
346          if (!bv->insertInset(new_inset)) {
347              delete new_inset;
348              return false;
349          }
350 //       Update(1);//BUG
351          new_inset->Edit(bv, 0, 0, 0);
352          new_inset->LocalDispatch(bv, LFUN_SELFINSERT, tmp);
353          if (greek_kb_flag_save < 2) {
354                  bv->unlockInset(new_inset); // bv->theLockingInset());
355                  bv->text->CursorRight(bv, true);
356          }
357       } else
358          if (bv->theLockingInset()->LyxCode() == Inset::MATH_CODE ||
359              bv->theLockingInset()->LyxCode() == Inset::MATHMACRO_CODE)
360                 static_cast<InsetFormula*>(bv->theLockingInset())->LocalDispatch(bv, LFUN_SELFINSERT, tmp);
361          else
362                 lyxerr << "Math error: attempt to write on a wrong "
363                         "class of inset." << endl;
364       return true;
365    }
366    return false;
367 }
368
369
370 void math_insert_symbol(BufferView * bv, string const & s)
371 {
372    if (bv->available())   {
373       if (!bv->theLockingInset() || bv->theLockingInset()->IsTextInset()) {
374          InsetFormula * new_inset = new InsetFormula();
375          bv->beforeChange();
376          if (!bv->insertInset(new_inset)) {
377              delete new_inset;
378              return;
379          }
380 //       Update(1);//BUG
381          new_inset->Edit(bv, 0, 0, 0);
382          new_inset->InsertSymbol(bv, s);
383       } else
384         if (bv->theLockingInset()->LyxCode() == Inset::MATH_CODE ||
385             bv->theLockingInset()->LyxCode() == Inset::MATHMACRO_CODE)
386                 static_cast<InsetFormula*>(bv->theLockingInset())->InsertSymbol(bv, s);
387         else 
388                 lyxerr << "Math error: attempt to write on a wrong "
389                         "class of inset." << endl;
390    }
391 }
392
393
394 BitmapMenu * sym_menu= 0;
395
396 void  create_symbol_menues(FD_panel * symb_form)
397 {
398    FL_OBJECT * obj; 
399    BitmapMenu * menu;
400    
401    sym_menu = menu = new BitmapMenu(2, symb_form->greek);
402    obj = menu->AddBitmap(MM_GREEK, 6, 2, Greek_width, Greek_height, 
403                            Greek_bits);         
404    fl_set_bmtable_maxitems(obj, 11);
405    obj = menu->AddBitmap(MM_GREEK, 7, 4, greek_width, greek_height,
406                            greek_bits); 
407    menu->Create();
408    
409    menu = new BitmapMenu(1, symb_form->boperator, menu);
410    obj = menu->AddBitmap(MM_BOP, 4, 8, bop_width, bop_height,
411                          bop_bits);      
412    fl_set_bmtable_maxitems(obj, 31);
413    menu->Create();   
414       
415    menu = new BitmapMenu(1, symb_form->brelats, menu);
416    obj = menu->AddBitmap(MM_BRELATS, 4, 9, brel_width, brel_height, 
417                          brel_bits);        
418    fl_set_bmtable_maxitems(obj, 35);       
419    menu->Create();
420    
421    menu = new BitmapMenu(3, symb_form->arrow, menu);
422    obj = menu->AddBitmap(MM_ARROW, 5, 4, arrow_width, arrow_height,
423                          arrow_bits);
424    obj = menu->AddBitmap(MM_ARROW, 2, 4, larrow_width, larrow_height, 
425                          larrow_bits, False);          
426    fl_set_bmtable_maxitems(obj, 7);
427    obj = menu->AddBitmap(MM_ARROW, 2, 2, darrow_width, darrow_height, 
428                          darrow_bits);
429    menu->Create(); 
430      
431    menu = new BitmapMenu(1, symb_form->varsize, menu);
432     obj = menu->AddBitmap(MM_VARSIZE, 3, 5, varsz_width, varsz_height, 
433                          varsz_bits);         
434    fl_set_bmtable_maxitems(obj, 14);
435    menu->Create();
436       
437    menu = new BitmapMenu(2, symb_form->misc, menu);
438      obj = menu->AddBitmap(MM_MISC, 5, 6, misc_width, misc_height,
439                            misc_bits);         
440      fl_set_bmtable_maxitems(obj, 29);
441      obj = menu->AddBitmap(MM_DOTS, 4, 1, dots_width, dots_height, 
442                            dots_bits);         
443    menu->Create();
444 }
445
446 static
447 char const ** pixmapFromBitmapData(char const * s, int wx, int hx)
448 {
449     int i;
450     char const ** data = 0;
451     
452     int id = -1;
453     
454     for (i = 0; i < 6; ++i) {
455         char const ** latex_str = 0;
456         switch (i) {
457          case 0: latex_str = latex_greek; break;
458          case 1: latex_str = latex_bop; break;
459          case 2: latex_str = latex_brel; break;
460          case 3: latex_str = latex_arrow; break;
461          case 4: latex_str = latex_varsz; break;
462          case 5: latex_str = latex_misc; break;
463         }
464         
465         for (int k = 0; latex_str[k][0] > ' '; ++k) {
466             if (strcmp(latex_str[k], s) == 0) {
467                 id = k;
468                 break;
469             }
470         }
471         if (id >= 0) break;
472     }
473     if (i < 6 && id >= 0) {
474         unsigned char const * bdata = 0;
475         int w = 0, h = 0, dw = 0, dh = 0;
476
477         lyxerr[Debug::MATHED] << "Imando " << i << ", " << id << endl;
478         switch (i) {
479          case 0: 
480             if (id<= 10) {
481                 w = Greek_width;
482                 h = Greek_height;
483                 bdata = Greek_bits;
484                 dw = 6;  dh = 2;
485             } else {
486                 w = greek_width;
487                 h = greek_height;
488                 bdata = greek_bits;
489                 dw = 7;  dh = 4;
490                 id -= 11;
491             }
492             break;
493          case 1:
494             w = bop_width;
495             h = bop_height;
496             bdata = bop_bits;
497             dw = 4;  dh = 8;
498             break;
499          case 2:
500             w = brel_width;
501             h = brel_height;
502             bdata = brel_bits;
503             dw = 4;  dh = 9;
504             break;
505          case 3:
506             if (id<20) {                
507                 w = arrow_width;
508                 h = arrow_height;
509                 bdata = arrow_bits;
510                 dw = 5;  dh = 4;
511             } else if (id>28) {         
512                 w = darrow_width;
513                 h = darrow_height;
514                 bdata = darrow_bits;
515                 dw = 2;  dh = 2;
516                 id -= 29;
517             } else {            
518                 w = larrow_width;
519                 h = larrow_height;
520                 bdata = larrow_bits;
521                 dw = 2;  dh = 4;
522                 id -= 20;
523             }
524             break;
525          case 4:
526             w = varsz_width;
527             h = varsz_height;
528             bdata = varsz_bits;
529             dw = 3;  dh = 5;
530             break;
531          case 5:
532             w = misc_width;
533             h = misc_height;
534             bdata = misc_bits;
535             dw = 5;  dh = 6;
536             break;
537         }
538         int ww = w/dw, hh = h/dh, x, y;
539    
540         XImage * xima = XCreateImage(fl_get_display(), 0, 1, XYBitmap, 0, 
541                                     const_cast<char*>(reinterpret_cast<char const *>(bdata)), w, h, 8, 0);
542         xima->byte_order = LSBFirst;
543         xima->bitmap_bit_order = LSBFirst;
544         x = (id % dw)*ww;
545         y = (id/dw)*hh;
546         if (ww > wx) ww = wx;
547         if (hh > hx) hh = hx;
548         XImage * sbima = XSubImage(xima, x, y, ww, hh);
549         XpmCreateDataFromImage(fl_get_display(), const_cast<char***>(&data), sbima, sbima, 0);
550         
551         // Dirty hack to get blue symbols quickly
552         char * sx = const_cast<char*>(strstr(data[2], "FFFFFFFF"));
553         if (sx) {
554             for (int k = 0; k < 8; ++k) sx[k] = '0';
555         }
556
557 //      XDestroyImage(xima);
558     }
559
560     return data;
561 }
562