]> git.lyx.org Git - lyx.git/blob - src/mathed/math_macro.C
the freespacing patch from Kayvan, draw the math empty delim with onoffdash, asure...
[lyx.git] / src / mathed / math_macro.C
1 // -*- C++ -*-
2 /*
3  *  File:        math_macro.C
4  *  Purpose:     Implementation of macro class for mathed 
5  *  Author:      Alejandro Aguilar Sierra <asierra@servidor.unam.mx> 
6  *  Created:     November 1996
7  *  Description: WYSIWYG math macros
8  *
9  *  Dependencies: Mathed
10  *
11  *  Copyright: 1996, 1997 Alejandro Aguilar Sierra
12  *
13  *  Version: 0.2, Mathed & Lyx project.
14  *
15  *  This code is under the GNU General Public Licence version 2 or later.
16  */
17
18 #include <config.h>
19 #include FORMS_H_LOCATION
20
21 #ifdef __GNUG__
22 #pragma implementation "math_macro.h"
23 #pragma implementation "math_defs.h"
24 #endif
25
26 #include "LString.h"
27 #include "math_macro.h"
28 #include "math_iter.h"
29 #include "math_inset.h"
30 #include "support/lstrings.h"
31 #include "debug.h"
32
33
34 ostream & operator<<(ostream & o, MathedTextCodes mtc)
35 {
36         return o << int(mtc);
37 }
38
39 enum MathedMacroFlag {
40     MMF_Env= 1,
41     MMF_Exp= 2,
42     MMF_Edit= 4
43 };
44
45 ostream & operator<<(ostream & o, MathedMacroFlag mmf)
46 {
47         return o << int(mmf);
48 }
49
50 extern int mathed_string_width(short type, int style, byte const* s, int ls);
51 extern int mathed_string_height(short, int, byte const*, int, int&, int&);
52
53
54 MathMacro::MathMacro(MathMacroTemplate* t): 
55     MathParInset(LM_ST_TEXT, "", LM_OT_MACRO), tmplate(t)
56 {
57     nargs = tmplate->getNoArgs();
58     tcode = tmplate->getTCode();
59     args = new MacroArgumentBase[nargs];
60     for (int i = 0; i < nargs; ++i) {
61 //      if (tmplate->getMacroPar(i)->Permit(LMPF_ALLOW_CR))
62 //        args[i].row = new MathedRowSt(tmplate->getMacroPar(i)->GetColumns());
63 //      else 
64           args[i].row = 0;
65 /*      int k = tmplate->getMacroPar(i)->GetColumns();
66         if (k>0) {
67             args[i].array = new LyxArrayBase;
68             for (int j= 0; j<k-1; ++j) args[i].array->Insert(j, LM_TC_TAB);
69         }*/
70     }
71     idx = 0;
72     SetName(tmplate->GetName());
73 }
74
75
76 MathMacro::MathMacro(MathMacro * m): 
77     MathParInset(LM_ST_TEXT, m->GetName(), LM_OT_MACRO)
78 {
79     tmplate = m->tmplate;
80     nargs = tmplate->getNoArgs();
81     tcode = tmplate->getTCode();
82     args = new MacroArgumentBase[nargs];
83     idx = 0;
84     SetName(tmplate->GetName());
85     for (int i = 0; i < tmplate->nargs; ++i) {
86         m->setArgumentIdx(i);
87         MathedIter it(m->GetData());
88         args[i].row = m->args[i].row;
89         args[i].array = it.Copy();
90     }
91 }
92
93 MathMacro::~MathMacro()
94 {
95     for (idx = 0; idx < nargs; ++idx) {
96         MathedIter it(args[idx].array);
97         it. Clear();
98         delete args[idx].row;
99     }
100     delete[] args;
101 }
102
103
104 MathedInset * MathMacro::Clone()
105 {
106     return new MathMacro(this);
107 }
108
109
110 void MathMacro::Metrics()
111 {
112     if (nargs > 0)
113       tmplate->update(this);
114     tmplate->Metrics();
115     width = tmplate->Width();
116     ascent = tmplate->Ascent();
117     descent = tmplate->Descent();
118 }
119
120
121 void MathMacro::draw(Painter & pain, int x, int y)
122 {
123     xo = x;  yo = y;
124     Metrics();
125     tmplate->update(this);
126     tmplate->SetStyle(size);
127     tmplate->draw(pain, x, y);
128     for (int i = 0; i < nargs; ++i)
129       tmplate->GetMacroXY(i, args[i].x, args[i].y);
130 }
131
132
133 int MathMacro::GetColumns()
134 {
135     return tmplate->getMacroPar(idx)->GetColumns();
136 }
137
138
139 void MathMacro::GetXY(int & x, int & y) const
140 {
141     x = args[idx].x;  y = args[idx].y;
142 }
143
144
145 bool MathMacro::Permit(short f)
146 {
147     return (nargs > 0) ?
148             tmplate->getMacroPar(idx)->Permit(f) : MathParInset::Permit(f);
149 }
150
151
152 void MathMacro::SetFocus(int x, int y)
153 {
154     tmplate->update(this);
155     tmplate->SetMacroFocus(idx, x, y);
156 }
157
158
159 void MathMacro::Write(ostream & os)
160 {
161 #ifdef USE_OSTREAM_ONLY
162     if (tmplate->flags & MMF_Exp) {
163             lyxerr[Debug::MATHED] << "Expand " << tmplate->flags
164                                   << ' ' << MMF_Exp << endl; 
165         tmplate->update(this);
166         tmplate->Write(os);
167     } else {
168         if (tmplate->flags & MMF_Env) {
169                 os << "\\begin{"
170                    << name
171                    << "} ";
172         } else {
173                 os << '\\' << name;
174         }
175 //      if (options) { 
176 //        file += '[';
177 //        file += options;
178 //        file += ']';
179 //      }
180         
181         if (!(tmplate->flags & MMF_Env) && nargs > 0) 
182                 os << '{';
183         
184         for (int i = 0; i < nargs; ++i) {
185             array = args[i].array;
186             MathParInset::Write(os);
187             if (i < nargs - 1)  
188                     os << "}{";
189         }   
190         if (tmplate->flags & MMF_Env) {
191                 os << "\\end{"
192                    << name
193                    << '}';
194         } else {
195             if (nargs > 0) 
196                     os << '}';
197             else
198                     os << ' ';
199         }
200     }
201 #else
202    string output;
203    MathMacro::Write(output);
204    os << output;
205 #endif
206 }
207
208
209 #ifndef USE_OSTREAM_ONLY
210 void MathMacro::Write(string &file)
211 {
212     if (tmplate->flags & MMF_Exp) {
213             lyxerr[Debug::MATHED] << "Expand " << tmplate->flags
214                                   << ' ' << MMF_Exp << endl; 
215         tmplate->update(this);
216         tmplate->Write(file);
217     } else {
218         if (tmplate->flags & MMF_Env) {
219           file += "\\begin{";
220           file += name;
221           file += "} ";
222         } else {
223           file += '\\';
224           file += name;
225         }
226 //      if (options) { 
227 //        file += '[';
228 //        file += options;
229 //        file += ']';
230 //      }
231         
232         if (!(tmplate->flags & MMF_Env) && nargs > 0) 
233           file += '{';
234         
235         for (int i = 0; i < nargs; ++i) {
236             array = args[i].array;
237             MathParInset::Write(file);
238             if (i < nargs - 1)  
239               file += "}{";
240         }   
241         if (tmplate->flags & MMF_Env) {
242             file += "\\end{";
243             file += name;
244             file += '}';
245         } else {
246             if (nargs > 0) 
247                 file += '}';
248             else
249                 file += ' ';
250         }
251     }
252 }
253 #endif
254
255
256 /*---------------  Macro argument -----------------------------------*/
257
258 MathMacroArgument::MathMacroArgument(int n)
259 {
260     number = n;
261     expnd_mode = false;
262     SetType(LM_OT_MACRO_ARG);
263 }
264
265
266 void MathMacroArgument::draw(Painter & pain, int x, int baseline)
267 {
268     if (expnd_mode) {
269         MathParInset::draw(pain, x, baseline);
270     } else {
271 #ifdef HAVE_SSTREAM
272             ostringstream ost;
273             ost << '#' << number;
274             drawStr(pain, LM_TC_TEX, size, x, baseline, ost.str().c_str(), 2);
275 #else
276             char s[3];
277             ostrstream ost(s, 3);
278             ost << '#' << number << '\0';
279             drawStr(pain, LM_TC_TEX, size, x, baseline,
280                     reinterpret_cast<unsigned char*>(ost.str()), 2);
281 #endif
282     }
283 }
284
285
286 void MathMacroArgument::Metrics()
287 {
288     if (expnd_mode) {
289         MathParInset::Metrics();
290     } else {
291 #ifdef HAVE_SSTREAM
292             ostringstream ost;
293             ost << '#' << number;
294             width = mathed_string_width(LM_TC_TEX, size, ost.str().c_str(), 2);
295             mathed_string_height(LM_TC_TEX, size,
296                                  ost.str().c_str(), 2, ascent, descent);
297 #else
298         char s[3];
299         ostrstream ost(s, 3);
300         ost << '#' << number << '\0';
301         width = mathed_string_width(LM_TC_TEX, size,
302                                     reinterpret_cast<unsigned char*>
303                                     (ost.str()), 2);
304         mathed_string_height(LM_TC_TEX, size,
305                              reinterpret_cast<unsigned char*>(ost.str()),
306                              2, ascent, descent);
307 #endif
308     }
309 }
310
311
312 void MathMacroArgument::Write(ostream & os)
313 {
314 #ifdef USE_OSTREAM_ONLY
315     if (expnd_mode) {
316         MathParInset::Write(os);
317     } else {
318             os << '#' << number << ' ';
319     }
320 #else
321    string output;
322    MathMacroArgument::Write(output);
323    os << output;
324 #endif
325 }
326
327
328 #ifndef USE_OSTREAM_ONLY
329 void MathMacroArgument::Write(string & file)
330 {
331     if (expnd_mode) {
332         MathParInset::Write(file);
333     } else {
334         file += '#';
335         file += tostr(number);
336         file += ' ';
337     }
338 }
339 #endif
340
341
342 /* --------------------- MathMacroTemplate ---------------------------*/
343
344 MathMacroTemplate::MathMacroTemplate(char const * nm, int na, int flg):
345     MathParInset(LM_ST_TEXT, nm, LM_OT_MACRO), 
346     flags(flg), nargs(na)
347 {
348     if (nargs > 0) {
349         tcode = LM_TC_ACTIVE_INSET;
350         args = new MathMacroArgument[nargs];
351         for (int i = 0; i < nargs; ++i) {
352             args[i].setNumber(i + 1);
353         }
354     } else 
355       tcode = LM_TC_INSET;
356 }
357
358
359 MathMacroTemplate::~MathMacroTemplate()
360 {
361     // prevent to delete already deleted objects
362     for (int i = 0; i < nargs; ++i) {
363         args[i].SetData(0);
364     }
365     delete[] args;
366 }
367
368
369 void MathMacroTemplate::setEditMode(bool ed)
370 {
371     if (ed) {
372         flags |= MMF_Edit;
373         for (int i = 0; i < nargs; ++i) {
374             args[i].setExpand(false);
375         }
376     } else {
377         flags &= ~MMF_Edit;
378         for (int i = 0; i < nargs; ++i) {
379             args[i].setExpand(true);
380         }
381     }
382 }
383
384
385 void MathMacroTemplate::draw(Painter & pain, int x, int y)
386 {
387     int x2, y2;
388     bool expnd = (nargs > 0) ? args[0].getExpand(): false;
389     if (flags & MMF_Edit) {
390         for (int i = 0; i < nargs; ++i) {
391             args[i].setExpand(false);
392         }
393       x2 = x; y2 = y;
394     } else {
395         for (int i = 0; i < nargs; ++i) {
396             args[i].setExpand(true);
397         }
398       x2 = xo; y2 = yo;
399     }
400     MathParInset::draw(pain, x, y);
401     xo = x2; yo = y2;
402     
403     for (int i = 0; i < nargs; ++i) {
404         args[i].setExpand(expnd);
405     }
406 }
407
408
409 void MathMacroTemplate::Metrics()
410 {
411     bool expnd = (nargs>0) ? args[0].getExpand(): false;
412     
413     if (flags & MMF_Edit) {
414         for (int i = 0; i < nargs; ++i) {
415             args[i].setExpand(false);
416         }
417     } else {
418         for (int i = 0; i < nargs; ++i) {
419             args[i].setExpand(true);
420         }
421     }
422     MathParInset::Metrics();
423     
424     for (int i = 0; i < nargs; ++i) {
425         args[i].setExpand(expnd);
426     }
427 }
428
429
430 void MathMacroTemplate::update(MathMacro * macro)
431 {
432     int idx = (macro) ? macro->getArgumentIdx(): 0;
433     for (int i = 0; i < nargs; ++i) {
434         if (macro) {
435             macro->setArgumentIdx(i);
436             args[i].SetData(macro->GetData());
437             MathedRowSt *row = macro->getRowSt();
438             args[i].setRowSt(row);
439         }
440     }   
441     if (macro)
442       macro->setArgumentIdx(idx);
443 }
444     
445
446 void MathMacroTemplate::WriteDef(ostream & os)
447 {
448         os << "\n\\newcommand{\\" << name << "}";
449       
450     if (nargs > 0 ) 
451             os << "[" << nargs << "]";
452     
453     os << "{";
454     
455     for (int i = 0; i < nargs; ++i) {
456         args[i].setExpand(false);
457     }    
458     Write(os); 
459     os << "}\n";
460 }
461
462
463 #ifndef USE_OSTREAM_ONLY
464 void MathMacroTemplate::WriteDef(string & file)
465 {
466     file += "\n\\newcommand{\\";
467     file += name;
468     file += '}';
469       
470     if (nargs > 0 ) {
471       file += '[';
472       file += tostr(nargs);
473       file += ']';
474     }
475     
476     file += '{';
477     
478     for (int i = 0; i < nargs; ++i) {
479         args[i].setExpand(false);
480     }    
481     Write(file); 
482     file += "}\n";
483 }
484 #endif
485
486
487 void MathMacroTemplate::setArgument(LyxArrayBase * a, int i)
488 {
489     args[i].SetData(a);
490 }
491
492
493 void MathMacroTemplate::GetMacroXY(int i, int & x, int & y) const
494 {
495     args[i].GetXY(x, y);
496 }
497
498
499 MathParInset * MathMacroTemplate::getMacroPar(int i) const
500 {
501     return (i >= 0 && i < nargs) ? static_cast<MathParInset*>(&args[i]) : 0;
502 }
503
504
505 void MathMacroTemplate::SetMacroFocus(int &idx, int x, int y)
506 {
507     for (int i = 0; i < nargs; ++i) {
508         if (args[i].Inside(x, y)) {
509             idx = i;
510             break;
511         }
512     }
513 }
514
515
516 /* -------------------------- MathMacroTable -----------------------*/
517
518 MathMacroTable::MathMacroTable(int n) : max_macros(n)
519 {
520     macro_table = new MathMacroTemplateP[max_macros];
521     num_macros = 0;
522 }
523
524
525 MathMacroTable::~MathMacroTable()
526 {
527     delete[] macro_table;
528 }
529
530
531 // The search is currently linear but will be binary or hash, later.
532 MathMacroTemplate * MathMacroTable::getTemplate(char const * name) const
533 {
534     for (int i = 0; i < num_macros; ++i) {
535       if (strcmp(name, macro_table[i]->GetName()) == 0) 
536         return macro_table[i];
537     }
538     
539     return 0;
540 }
541
542 void MathMacroTable::addTemplate(MathMacroTemplate * m)
543 {
544     if (num_macros < max_macros)
545       macro_table[num_macros++] = m;
546     else
547             lyxerr << "Error (MathMacroTable::addTemplate): "
548                     "Macro table exhausted!" << endl;
549 }
550
551
552 // All this stuff aparently leaks because it's created here and is not 
553 // deleted never, but it have to live all the LyX sesion. OK, would not
554 // so hard to do it in the MacroTable destructor, but this doesn't harm
555 // seriously, so don't bother me with purify results here.   ;-)
556
557 void MathMacroTable::builtinMacros()
558 {
559     MathedIter iter;
560     MathParInset * inset;// *arg;
561     LyxArrayBase * array2;
562     
563     built = true;
564     
565     lyxerr[Debug::MATHED] << "Building macros" << endl;
566     
567     // This macro doesn't have arguments
568     MathMacroTemplate * m = new MathMacroTemplate("notin");  // this leaks
569     addTemplate(m);
570     LyxArrayBase * array = new LyxArrayBase; // this leaks
571     iter.SetData(array);
572     iter.Insert(new MathAccentInset(LM_in, LM_TC_BOPS, LM_not)); // this leaks
573     m->SetData(array);
574     
575     // These two are only while we are still with LyX 2.x
576     m = new MathMacroTemplate("emptyset"); // this leaks
577     addTemplate(m);
578     array = new LyxArrayBase; // this leaks
579     iter.SetData(array);
580     iter.Insert(new MathAccentInset('O', LM_TC_RM, LM_not)); // this leaks
581     m->SetData(array);
582     
583     m = new MathMacroTemplate("perp"); // this leaks
584     addTemplate(m);
585     array = new LyxArrayBase; // this leaks
586     iter.SetData(array);
587     iter.Insert(LM_bot, LM_TC_BOP);
588     m->SetData(array);
589
590     // binom has two arguments
591     m = new MathMacroTemplate("binom", 2);
592     addTemplate(m);
593     array = new LyxArrayBase; 
594     m->SetData(array);
595     iter.SetData(array);
596     inset = new MathDelimInset('(', ')');
597     iter.Insert(inset, LM_TC_ACTIVE_INSET);
598     array = new LyxArrayBase; 
599     iter.SetData(array);
600     MathFracInset *frac = new MathFracInset(LM_OT_ATOP);
601     iter.Insert(frac, LM_TC_ACTIVE_INSET);
602     inset->SetData(array);
603     array = new LyxArrayBase;
604     array2 = new LyxArrayBase;  
605     iter.SetData(array);
606     iter.Insert(m->getMacroPar(0));
607     iter.SetData(array2);
608     iter.Insert(m->getMacroPar(1));
609     frac->SetData(array, array2);
610
611 /*
612     // Cases has 1 argument
613     m = new MathMacroTemplate("cases", 1, MMF_Env); // this leaks
614     addTemplate(m);
615     array = new LyxArrayBase; // this leaks
616     iter.SetData(array);
617     arg = new MathMatrixInset(2, 1); // this leaks
618
619     m->setArgument(arg);
620     arg->SetAlign('c', "ll");
621     iter.Insert(arg, LM_TC_ACTIVE_INSET);
622     inset = new MathDelimInset('{', '.'); // this leaks
623     inset->SetData(array);
624     array = new LyxArrayBase; // this leaks
625     iter.SetData(array);
626     iter.Insert(inset, LM_TC_ACTIVE_INSET);
627     m->SetData(array);
628   
629
630     // the environment substack has 1 argument
631     m = new MathMacroTemplate("substack", 1, MMF_Env); // this leaks
632     addTemplate(m);     
633     arg = new MathMatrixInset(1, 1); // this leaks
634     m->setArgument(arg);
635     arg->SetType(LM_OT_MACRO);
636     array = new LyxArrayBase; // this leaks
637     iter.SetData(array);
638     iter.Insert(arg, LM_TC_ACTIVE_INSET);
639     m->SetData(array);*/
640 }
641
642
643 MathMacroTable MathMacroTable::mathMTable(255);
644 bool MathMacroTable::built = false;