]> git.lyx.org Git - lyx.git/blob - src/mathed/math_macro.C
269e8c31882deda3a88ba8342e80e1c18e8fcd38
[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     if (tmplate->flags & MMF_Exp) {
162             lyxerr[Debug::MATHED] << "Expand " << tmplate->flags
163                                   << ' ' << MMF_Exp << endl; 
164         tmplate->update(this);
165         tmplate->Write(os);
166     } else {
167         if (tmplate->flags & MMF_Env) {
168                 os << "\\begin{"
169                    << name
170                    << "} ";
171         } else {
172                 os << '\\' << name;
173         }
174 //      if (options) { 
175 //        file += '[';
176 //        file += options;
177 //        file += ']';
178 //      }
179         
180         if (!(tmplate->flags & MMF_Env) && nargs > 0) 
181                 os << '{';
182         
183         for (int i = 0; i < nargs; ++i) {
184             array = args[i].array;
185             MathParInset::Write(os);
186             if (i < nargs - 1)  
187                     os << "}{";
188         }   
189         if (tmplate->flags & MMF_Env) {
190                 os << "\\end{"
191                    << name
192                    << '}';
193         } else {
194             if (nargs > 0) 
195                     os << '}';
196             else
197                     os << ' ';
198         }
199     }
200 }
201
202
203 /*---------------  Macro argument -----------------------------------*/
204
205 MathMacroArgument::MathMacroArgument(int n)
206 {
207     number = n;
208     expnd_mode = false;
209     SetType(LM_OT_MACRO_ARG);
210 }
211
212
213 void MathMacroArgument::draw(Painter & pain, int x, int baseline)
214 {
215     if (expnd_mode) {
216         MathParInset::draw(pain, x, baseline);
217     } else {
218 #ifdef HAVE_SSTREAM
219             ostringstream ost;
220             ost << '#' << number;
221             drawStr(pain, LM_TC_TEX, size, x, baseline, 
222                     reinterpret_cast<byte const *>(ost.str().c_str()), 2);
223 #else
224             char s[3];
225             ostrstream ost(s, 3);
226             ost << '#' << number << '\0';
227             drawStr(pain, LM_TC_TEX, size, x, baseline,
228                     reinterpret_cast<byte *>(ost.str()), 2);
229 #endif
230     }
231 }
232
233
234 void MathMacroArgument::Metrics()
235 {
236     if (expnd_mode) {
237         MathParInset::Metrics();
238     } else {
239 #ifdef HAVE_SSTREAM
240             ostringstream ost;
241             ost << '#' << number;
242             width = mathed_string_width(LM_TC_TEX, size, 
243                                         reinterpret_cast<byte const *>(ost.str().c_str()), 2);
244             mathed_string_height(LM_TC_TEX, size,
245                                  reinterpret_cast<byte const *>(ost.str().c_str()), 
246                                  2, ascent, descent);
247 #else
248         char s[3];
249         ostrstream ost(s, 3);
250         ost << '#' << number << '\0';
251         width = mathed_string_width(LM_TC_TEX, size,
252                                     reinterpret_cast<byte *>
253                                     (ost.str()), 2);
254         mathed_string_height(LM_TC_TEX, size,
255                              reinterpret_cast<byte *>(ost.str()),
256                              2, ascent, descent);
257 #endif
258     }
259 }
260
261
262 void MathMacroArgument::Write(ostream & os)
263 {
264     if (expnd_mode) {
265         MathParInset::Write(os);
266     } else {
267             os << '#' << number << ' ';
268     }
269 }
270
271
272 /* --------------------- MathMacroTemplate ---------------------------*/
273
274 MathMacroTemplate::MathMacroTemplate(char const * nm, int na, int flg):
275     MathParInset(LM_ST_TEXT, nm, LM_OT_MACRO), 
276     flags(flg), nargs(na)
277 {
278     if (nargs > 0) {
279         tcode = LM_TC_ACTIVE_INSET;
280         args = new MathMacroArgument[nargs];
281         for (int i = 0; i < nargs; ++i) {
282             args[i].setNumber(i + 1);
283         }
284     } else 
285       tcode = LM_TC_INSET;
286 }
287
288
289 MathMacroTemplate::~MathMacroTemplate()
290 {
291     // prevent to delete already deleted objects
292     for (int i = 0; i < nargs; ++i) {
293         args[i].SetData(0);
294     }
295     delete[] args;
296 }
297
298
299 void MathMacroTemplate::setEditMode(bool ed)
300 {
301     if (ed) {
302         flags |= MMF_Edit;
303         for (int i = 0; i < nargs; ++i) {
304             args[i].setExpand(false);
305         }
306     } else {
307         flags &= ~MMF_Edit;
308         for (int i = 0; i < nargs; ++i) {
309             args[i].setExpand(true);
310         }
311     }
312 }
313
314
315 void MathMacroTemplate::draw(Painter & pain, int x, int y)
316 {
317     int x2, y2;
318     bool expnd = (nargs > 0) ? args[0].getExpand(): false;
319     if (flags & MMF_Edit) {
320         for (int i = 0; i < nargs; ++i) {
321             args[i].setExpand(false);
322         }
323       x2 = x; y2 = y;
324     } else {
325         for (int i = 0; i < nargs; ++i) {
326             args[i].setExpand(true);
327         }
328       x2 = xo; y2 = yo;
329     }
330     MathParInset::draw(pain, x, y);
331     xo = x2; yo = y2;
332     
333     for (int i = 0; i < nargs; ++i) {
334         args[i].setExpand(expnd);
335     }
336 }
337
338
339 void MathMacroTemplate::Metrics()
340 {
341     bool expnd = (nargs>0) ? args[0].getExpand(): false;
342     
343     if (flags & MMF_Edit) {
344         for (int i = 0; i < nargs; ++i) {
345             args[i].setExpand(false);
346         }
347     } else {
348         for (int i = 0; i < nargs; ++i) {
349             args[i].setExpand(true);
350         }
351     }
352     MathParInset::Metrics();
353     
354     for (int i = 0; i < nargs; ++i) {
355         args[i].setExpand(expnd);
356     }
357 }
358
359
360 void MathMacroTemplate::update(MathMacro * macro)
361 {
362     int idx = (macro) ? macro->getArgumentIdx(): 0;
363     for (int i = 0; i < nargs; ++i) {
364         if (macro) {
365             macro->setArgumentIdx(i);
366             args[i].SetData(macro->GetData());
367             MathedRowSt *row = macro->getRowSt();
368             args[i].setRowSt(row);
369         }
370     }   
371     if (macro)
372       macro->setArgumentIdx(idx);
373 }
374     
375
376 void MathMacroTemplate::WriteDef(ostream & os)
377 {
378         os << "\n\\newcommand{\\" << name << "}";
379       
380     if (nargs > 0 ) 
381             os << "[" << nargs << "]";
382     
383     os << "{";
384     
385     for (int i = 0; i < nargs; ++i) {
386         args[i].setExpand(false);
387     }    
388     Write(os); 
389     os << "}\n";
390 }
391
392
393 void MathMacroTemplate::setArgument(LyxArrayBase * a, int i)
394 {
395     args[i].SetData(a);
396 }
397
398
399 void MathMacroTemplate::GetMacroXY(int i, int & x, int & y) const
400 {
401     args[i].GetXY(x, y);
402 }
403
404
405 MathParInset * MathMacroTemplate::getMacroPar(int i) const
406 {
407     return (i >= 0 && i < nargs) ? static_cast<MathParInset*>(&args[i]) : 0;
408 }
409
410
411 void MathMacroTemplate::SetMacroFocus(int &idx, int x, int y)
412 {
413     for (int i = 0; i < nargs; ++i) {
414         if (args[i].Inside(x, y)) {
415             idx = i;
416             break;
417         }
418     }
419 }
420
421
422 /* -------------------------- MathMacroTable -----------------------*/
423
424 MathMacroTable::MathMacroTable(int n) : max_macros(n)
425 {
426     macro_table = new MathMacroTemplateP[max_macros];
427     num_macros = 0;
428 }
429
430
431 MathMacroTable::~MathMacroTable()
432 {
433     delete[] macro_table;
434 }
435
436
437 // The search is currently linear but will be binary or hash, later.
438 MathMacroTemplate * MathMacroTable::getTemplate(char const * name) const
439 {
440     for (int i = 0; i < num_macros; ++i) {
441       if (strcmp(name, macro_table[i]->GetName()) == 0) 
442         return macro_table[i];
443     }
444     
445     return 0;
446 }
447
448 void MathMacroTable::addTemplate(MathMacroTemplate * m)
449 {
450     if (num_macros < max_macros)
451       macro_table[num_macros++] = m;
452     else
453             lyxerr << "Error (MathMacroTable::addTemplate): "
454                     "Macro table exhausted!" << endl;
455 }
456
457
458 // All this stuff aparently leaks because it's created here and is not 
459 // deleted never, but it have to live all the LyX sesion. OK, would not
460 // so hard to do it in the MacroTable destructor, but this doesn't harm
461 // seriously, so don't bother me with purify results here.   ;-)
462
463 void MathMacroTable::builtinMacros()
464 {
465     MathedIter iter;
466     MathParInset * inset;// *arg;
467     LyxArrayBase * array2;
468     
469     built = true;
470     
471     lyxerr[Debug::MATHED] << "Building macros" << endl;
472     
473     // This macro doesn't have arguments
474     MathMacroTemplate * m = new MathMacroTemplate("notin");  // this leaks
475     addTemplate(m);
476     LyxArrayBase * array = new LyxArrayBase; // this leaks
477     iter.SetData(array);
478     iter.Insert(new MathAccentInset(LM_in, LM_TC_BOPS, LM_not)); // this leaks
479     m->SetData(array);
480     
481     // These two are only while we are still with LyX 2.x
482     m = new MathMacroTemplate("emptyset"); // this leaks
483     addTemplate(m);
484     array = new LyxArrayBase; // this leaks
485     iter.SetData(array);
486     iter.Insert(new MathAccentInset('O', LM_TC_RM, LM_not)); // this leaks
487     m->SetData(array);
488     
489     m = new MathMacroTemplate("perp"); // this leaks
490     addTemplate(m);
491     array = new LyxArrayBase; // this leaks
492     iter.SetData(array);
493     iter.Insert(LM_bot, LM_TC_BOP);
494     m->SetData(array);
495
496     // binom has two arguments
497     m = new MathMacroTemplate("binom", 2);
498     addTemplate(m);
499     array = new LyxArrayBase; 
500     m->SetData(array);
501     iter.SetData(array);
502     inset = new MathDelimInset('(', ')');
503     iter.Insert(inset, LM_TC_ACTIVE_INSET);
504     array = new LyxArrayBase; 
505     iter.SetData(array);
506     MathFracInset *frac = new MathFracInset(LM_OT_ATOP);
507     iter.Insert(frac, LM_TC_ACTIVE_INSET);
508     inset->SetData(array);
509     array = new LyxArrayBase;
510     array2 = new LyxArrayBase;  
511     iter.SetData(array);
512     iter.Insert(m->getMacroPar(0));
513     iter.SetData(array2);
514     iter.Insert(m->getMacroPar(1));
515     frac->SetData(array, array2);
516
517 /*
518     // Cases has 1 argument
519     m = new MathMacroTemplate("cases", 1, MMF_Env); // this leaks
520     addTemplate(m);
521     array = new LyxArrayBase; // this leaks
522     iter.SetData(array);
523     arg = new MathMatrixInset(2, 1); // this leaks
524
525     m->setArgument(arg);
526     arg->SetAlign('c', "ll");
527     iter.Insert(arg, LM_TC_ACTIVE_INSET);
528     inset = new MathDelimInset('{', '.'); // this leaks
529     inset->SetData(array);
530     array = new LyxArrayBase; // this leaks
531     iter.SetData(array);
532     iter.Insert(inset, LM_TC_ACTIVE_INSET);
533     m->SetData(array);
534   
535
536     // the environment substack has 1 argument
537     m = new MathMacroTemplate("substack", 1, MMF_Env); // this leaks
538     addTemplate(m);     
539     arg = new MathMatrixInset(1, 1); // this leaks
540     m->setArgument(arg);
541     arg->SetType(LM_OT_MACRO);
542     array = new LyxArrayBase; // this leaks
543     iter.SetData(array);
544     iter.Insert(arg, LM_TC_ACTIVE_INSET);
545     m->SetData(array);*/
546 }
547
548
549 MathMacroTable MathMacroTable::mathMTable(255);
550 bool MathMacroTable::built = false;