]> git.lyx.org Git - lyx.git/blob - src/mathed/MathMacro.cpp
getting rid of superfluous std:: statements.
[lyx.git] / src / mathed / MathMacro.cpp
1 /**
2  * \file MathMacro.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Alejandro Aguilar Sierra
7  * \author André Pönitz
8  * \author Stefan Schimanski
9  *
10  * Full author contact details are available in file CREDITS.
11  */
12
13 #include <config.h>
14
15 #include "MathMacro.h"
16 #include "MathSupport.h"
17 #include "MathExtern.h"
18 #include "MathStream.h"
19
20 #include "Buffer.h"
21 #include "BufferView.h"
22 #include "Cursor.h"
23 #include "support/debug.h"
24 #include "LaTeXFeatures.h"
25 #include "FuncStatus.h"
26 #include "FuncRequest.h"
27 #include "Undo.h"
28
29 #include "frontends/Painter.h"
30
31 #include <ostream>
32 #include <vector>
33
34 using namespace std;
35
36 namespace lyx {
37
38
39 /// A proxy for the macro values
40 class ArgumentProxy : public InsetMath {
41 public:
42         ///
43         ArgumentProxy(MathMacro & mathMacro, size_t idx) 
44                 : mathMacro_(mathMacro), idx_(idx) {}
45         ///
46         ArgumentProxy(MathMacro & mathMacro, size_t idx, docstring const & def) 
47                 : mathMacro_(mathMacro), idx_(idx) 
48         {
49                         asArray(def, def_);
50         }
51         ///
52         void metrics(MetricsInfo & mi, Dimension & dim) const {
53                 mathMacro_.macro()->unlock();
54                 mathMacro_.cell(idx_).metrics(mi, dim);
55                 if (!mathMacro_.editing() && !def_.empty())
56                         def_.metrics(mi, dim);
57                 mathMacro_.macro()->lock();
58         }
59         ///
60         void draw(PainterInfo & pi, int x, int y) const {
61                 if (mathMacro_.editing()) {
62                         // The only way a ArgumentProxy can appear is in a cell of the 
63                         // MathMacro. Moreover the cells are only drawn in the DISPLAY_FOLDED 
64                         // mode and then, in the case of "editing_ == true" the monochrome 
65                         // mode is entered by the MathMacro before calling the cells' draw
66                         // method. Then eventually this code is reached and the proxy leaves
67                         // monochrome mode temporarely. Hence, if it is not in monochrome 
68                         // here (and the assert triggers in pain.leaveMonochromeMode()) 
69                         // it's a bug.
70                         pi.pain.leaveMonochromeMode();
71                         mathMacro_.cell(idx_).draw(pi, x, y);
72                         pi.pain.enterMonochromeMode(Color_mathbg, Color_mathmacroblend);
73                 } else {
74                         if (def_.empty())
75                                 mathMacro_.cell(idx_).draw(pi, x, y);
76                         else {
77                                 mathMacro_.cell(idx_).setXY(*pi.base.bv, x, y);
78                                 def_.draw(pi, x, y);
79                         }
80                 }
81         }
82         ///
83         size_t idx() const { return idx_; }
84         ///
85         int kerning() const { return mathMacro_.cell(idx_).kerning(); }
86
87 private:
88         ///
89         Inset * clone() const 
90         {
91                 return new ArgumentProxy(*this);
92         }
93         ///
94         MathMacro & mathMacro_;
95         ///
96         size_t idx_;
97         ///
98         MathData def_;
99 };
100
101
102 MathMacro::MathMacro(docstring const & name)
103         : InsetMathNest(0), name_(name), displayMode_(DISPLAY_INIT),
104                 attachedArgsNum_(0), previousCurIdx_(-1), 
105                 optionals_(0), nextFoldMode_(true),
106                 macro_(0), editing_(false), needsUpdate_(false)
107 {}
108
109
110 Inset * MathMacro::clone() const
111 {
112         MathMacro * copy = new MathMacro(*this);
113         copy->needsUpdate_ = true;
114         copy->expanded_.cell(0).clear();
115         return copy;
116 }
117
118
119 docstring MathMacro::name() const
120 {
121         if (displayMode_ == DISPLAY_UNFOLDED)
122                 return asString(cell(0));
123         else
124                 return name_;
125 }
126
127
128 void MathMacro::cursorPos(BufferView const & bv,
129                 CursorSlice const & sl, bool boundary, int & x, int & y) const
130 {
131         // We may have 0 arguments, but InsetMathNest requires at least one.
132         if (nargs() > 0)
133                 InsetMathNest::cursorPos(bv, sl, boundary, x, y);
134 }
135
136
137 int MathMacro::cursorIdx(Cursor const & cur) const {
138         for (size_t i = 0; i != cur.depth(); ++i)
139                         if (&cur[i].inset() == this)
140                                 return cur[i].idx();
141         return -1;
142 }
143
144
145 bool MathMacro::editMode(Cursor const & cur) const {
146         // find this in cursor trace
147         for (size_t i = 0; i != cur.depth(); ++i)
148                 if (&cur[i].inset() == this) {
149                         // look if there is no other macro in edit mode above
150                         ++i;
151                         for (; i != cur.depth(); ++i) {
152                                 MathMacro const * macro = dynamic_cast<MathMacro const *>(&cur[i].inset());
153                                 if (macro && macro->displayMode() == DISPLAY_NORMAL)
154                                         return false;
155                         }
156
157                         // ok, none found, I am the highest one
158                         return true;
159                 }
160
161         return false;
162 }
163
164
165 void MathMacro::metrics(MetricsInfo & mi, Dimension & dim) const
166 {
167         // calculate new metrics according to display mode
168         if (displayMode_ == DISPLAY_INIT || displayMode_ == DISPLAY_INTERACTIVE_INIT) {
169                 mathed_string_dim(mi.base.font, from_ascii("\\") + name(), dim);
170         } else if (displayMode_ == DISPLAY_UNFOLDED) {
171                 cell(0).metrics(mi, dim);
172                 Dimension bsdim;
173                 mathed_string_dim(mi.base.font, from_ascii("\\"), bsdim);
174                 dim.wid += bsdim.width() + 1;
175                 dim.asc = max(bsdim.ascent(), dim.ascent());
176                 dim.des = max(bsdim.descent(), dim.descent());
177                 metricsMarkers(dim);
178         } else {
179                 BOOST_ASSERT(macro_ != 0);
180
181                 // calculate metric finally
182                 macro_->lock();
183                 expanded_.cell(0).metrics(mi, dim);
184                 macro_->unlock();
185
186                 // calculate dimension with label while editing
187                 if (editing_) {
188                         FontInfo font = mi.base.font;
189                         augmentFont(font, from_ascii("lyxtex"));
190                         Dimension namedim;
191                         mathed_string_dim(font, name(), namedim);
192 #if 0
193                         dim.wid += 2 + namedim.wid + 2 + 2;
194                         dim.asc = max(dim.asc, namedim.asc) + 2;
195                         dim.des = max(dim.des, namedim.des) + 2;
196 #endif
197                         dim.wid = max(1 + namedim.wid + 1, 2 + dim.wid + 2);
198                         dim.asc += 1 + namedim.height() + 1;
199                         dim.des += 2;
200                 }
201         }
202
203         // Cache the inset dimension. 
204         setDimCache(mi, dim);
205 }
206
207
208 int MathMacro::kerning() const {
209         if (displayMode_ == DISPLAY_NORMAL && !editing_)
210                 return expanded_.kerning();
211         else
212                 return 0;
213 }
214
215
216 void MathMacro::updateMacro(MetricsInfo & mi) 
217 {
218         if (validName() && mi.macrocontext.has(name())) {
219                 macro_ = &mi.macrocontext.get(name());
220                 if (macroBackup_ != *macro_) {
221                         macroBackup_ = *macro_;
222                         needsUpdate_ = true;
223                 }
224         } else {
225                 macro_ = 0;
226         }
227 }
228
229
230 void MathMacro::updateRepresentation(MetricsInfo & mi) 
231 {
232         // index of child where the cursor is (or -1 if none is edited)
233         int curIdx = cursorIdx(mi.base.bv->cursor());
234         previousCurIdx_ = curIdx;
235
236         // known macro?
237         if (macro_) {
238                 requires_ = macro_->requires();
239
240                 if (displayMode_ == DISPLAY_NORMAL) {
241                         // set edit mode to draw box around if needed
242                         bool prevEditing = editing_; 
243                         editing_ = editMode(mi.base.bv->cursor());
244
245                         // editMode changed and we have to switch default value and hole of optional?
246                         if (optionals_ > 0 && nargs() > 0 && 
247                                         prevEditing != editing_)
248                                 needsUpdate_ = true;
249
250                         // macro changed?
251                         if (needsUpdate_) {
252                                 needsUpdate_ = false;
253
254                                 // get default values of macro
255                                 vector<docstring> const & defaults = macro_->defaults();
256
257                                 // create MathMacroArgumentValue objects pointing to the cells of the macro
258                                 vector<MathData> values(nargs());
259                                 for (size_t i = 0; i < nargs(); ++i) {
260                                         if (!cell(i).empty() || i >= defaults.size() || 
261                                                         defaults[i].empty() || curIdx == (int)i)
262                                                 values[i].insert(0, MathAtom(new ArgumentProxy(*this, i)));
263                                         else
264                                                 values[i].insert(0, MathAtom(new ArgumentProxy(*this, i, defaults[i])));
265                                 }
266
267                                 // expanding macro with the values
268                                 macro_->expand(values, expanded_.cell(0));
269                         }
270                 }
271         }
272 }
273
274
275 void MathMacro::draw(PainterInfo & pi, int x, int y) const
276 {
277         Dimension const dim = dimension(*pi.base.bv);
278
279         setPosCache(pi, x, y);
280         int expx = x;
281         int expy = y;
282
283         if (displayMode_ == DISPLAY_INIT || displayMode_ == DISPLAY_INTERACTIVE_INIT) {         
284                 PainterInfo pi2(pi.base.bv, pi.pain);
285                 pi2.base.font.setColor(macro_ ? Color_latex : Color_error);
286                 //pi2.base.style = LM_ST_TEXT;
287                 pi2.pain.text(x, y, from_ascii("\\") + name(), pi2.base.font);
288         } else if (displayMode_ == DISPLAY_UNFOLDED) {
289                 PainterInfo pi2(pi.base.bv, pi.pain);
290                 pi2.base.font.setColor(macro_ ? Color_latex : Color_error);
291                 //pi2.base.style = LM_ST_TEXT;
292                 pi2.pain.text(x, y, from_ascii("\\"), pi2.base.font);
293                 x += mathed_string_width(pi2.base.font, from_ascii("\\")) + 1;
294                 cell(0).draw(pi2, x, y);
295                 drawMarkers(pi2, expx, expy);
296         } else {
297                 // warm up cells
298                 for (size_t i = 0; i < nargs(); ++i)
299                         cell(i).setXY(*pi.base.bv, x, y);
300
301                 if (editing_) {
302                         // draw header and rectangle around
303                         FontInfo font = pi.base.font;
304                         augmentFont(font, from_ascii("lyxtex"));
305                         font.setSize(FONT_SIZE_TINY);
306                         font.setColor(Color_mathmacrolabel);
307                         Dimension namedim;
308                         mathed_string_dim(font, name(), namedim);
309 #if 0
310                         pi.pain.fillRectangle(x, y - dim.asc, 2 + namedim.width() + 2, dim.height(), Color_mathmacrobg);
311                         pi.pain.text(x + 2, y, name(), font);
312                         expx += 2 + namew + 2;
313 #endif
314                         pi.pain.fillRectangle(x, y - dim.asc, dim.wid, 1 + namedim.height() + 1, Color_mathmacrobg);
315                         pi.pain.text(x + 1, y - dim.asc + namedim.asc + 2, name(), font);
316                         expx += (dim.wid - expanded_.cell(0).dimension(*pi.base.bv).width()) / 2;
317
318                         pi.pain.enterMonochromeMode(Color_mathbg, Color_mathmacroblend);
319                         expanded_.cell(0).draw(pi, expx, expy);
320                         pi.pain.leaveMonochromeMode();
321                 } else
322                         expanded_.cell(0).draw(pi, expx, expy);
323
324                 // draw frame while editing
325                 if (editing_)
326                         pi.pain.rectangle(x, y - dim.asc, dim.wid, dim.height(), Color_mathmacroframe);
327         }
328
329         // another argument selected?
330         idx_type curIdx = cursorIdx(pi.base.bv->cursor());
331         if (previousCurIdx_ != curIdx || editing_ != editMode(pi.base.bv->cursor()))
332                 pi.base.bv->cursor().updateFlags(Update::Force);
333 }
334
335
336 void MathMacro::drawSelection(PainterInfo & pi, int x, int y) const
337 {
338         // We may have 0 arguments, but InsetMathNest requires at least one.
339         if (cells_.size() > 0)
340                 InsetMathNest::drawSelection(pi, x, y);
341 }
342
343
344 void MathMacro::setDisplayMode(MathMacro::DisplayMode mode)
345 {
346         if (displayMode_ != mode) {             
347                 // transfer name if changing from or to DISPLAY_UNFOLDED
348                 if (mode == DISPLAY_UNFOLDED) {
349                         cells_.resize(1);
350                         asArray(name_, cell(0));
351                 } else if (displayMode_ == DISPLAY_UNFOLDED) {
352                         name_ = asString(cell(0));
353                         cells_.resize(0);
354                 }
355
356                 displayMode_ = mode;
357                 needsUpdate_ = true;
358         }
359 }
360
361
362 MathMacro::DisplayMode MathMacro::computeDisplayMode(MetricsInfo const &) const
363 {
364         if (nextFoldMode_ == true && macro_ && !macro_->locked())
365                 return DISPLAY_NORMAL;
366         else
367                 return DISPLAY_UNFOLDED;
368 }
369
370
371 bool MathMacro::validName() const
372 {
373         docstring n = name();
374
375         // empty name?
376         if (n.size() == 0)
377                 return false;
378
379         // converting back and force doesn't swallow anything?
380         /*MathData ma;
381         asArray(n, ma);
382         if (asString(ma) != n)
383                 return false;*/
384
385         // valid characters?
386         for (size_t i = 0; i<n.size(); ++i) {
387                 if (!(n[i] >= 'a' && n[i] <= 'z') &&
388                                 !(n[i] >= 'A' && n[i] <= 'Z')) 
389                         return false;
390         }
391
392         return true;
393 }
394
395
396 void MathMacro::validate(LaTeXFeatures & features) const
397 {
398         if (!requires_.empty())
399                 features.require(requires_);
400
401         if (name() == "binom" || name() == "mathcircumflex")
402                 features.require(to_utf8(name()));
403 }
404
405
406 void MathMacro::edit(Cursor & cur, bool left)
407 {
408         cur.updateFlags(Update::Force);
409         InsetMathNest::edit(cur, left);
410 }
411
412
413 Inset * MathMacro::editXY(Cursor & cur, int x, int y)
414 {
415         // We may have 0 arguments, but InsetMathNest requires at least one.
416         if (nargs() > 0) {
417                 cur.updateFlags(Update::Force);
418                 return InsetMathNest::editXY(cur, x, y);                
419         } else
420                 return this;
421 }
422
423
424 void MathMacro::removeArgument(Inset::pos_type pos) {
425         if (displayMode_ == DISPLAY_NORMAL) {
426                 BOOST_ASSERT(size_t(pos) < cells_.size());
427                 cells_.erase(cells_.begin() + pos);
428                 if (size_t(pos) < attachedArgsNum_)
429                         --attachedArgsNum_;
430                 if (size_t(pos) < optionals_) {
431                         --optionals_;
432                 }
433
434                 needsUpdate_ = true;
435         }
436 }
437
438
439 void MathMacro::insertArgument(Inset::pos_type pos) {
440         if (displayMode_ == DISPLAY_NORMAL) {
441                 BOOST_ASSERT(size_t(pos) <= cells_.size());
442                 cells_.insert(cells_.begin() + pos, MathData());
443                 if (size_t(pos) < attachedArgsNum_)
444                         ++attachedArgsNum_;
445                 if (size_t(pos) < optionals_)
446                         ++optionals_;
447
448                 needsUpdate_ = true;
449         }
450 }
451
452
453 void MathMacro::detachArguments(vector<MathData> & args, bool strip)
454 {
455         BOOST_ASSERT(displayMode_ == DISPLAY_NORMAL);   
456         args = cells_;
457
458         // strip off empty cells, but not more than arity-attachedArgsNum_
459         if (strip) {
460                 size_t i;
461                 for (i = cells_.size(); i > attachedArgsNum_; --i)
462                         if (!cell(i - 1).empty()) break;
463                 args.resize(i);
464         }
465
466         attachedArgsNum_ = 0;
467         expanded_.cell(0) = MathData();
468         cells_.resize(0);
469
470         needsUpdate_ = true;
471 }
472
473
474 void MathMacro::attachArguments(vector<MathData> const & args, size_t arity, int optionals)
475 {
476         BOOST_ASSERT(displayMode_ == DISPLAY_NORMAL);
477         cells_ = args;
478         attachedArgsNum_ = args.size();
479         cells_.resize(arity);
480         expanded_.cell(0) = MathData();
481         optionals_ = optionals;
482
483         needsUpdate_ = true;
484 }
485
486
487 bool MathMacro::idxFirst(Cursor & cur) const 
488 {
489         cur.updateFlags(Update::Force);
490         return InsetMathNest::idxFirst(cur);
491 }
492
493
494 bool MathMacro::idxLast(Cursor & cur) const 
495 {
496         cur.updateFlags(Update::Force);
497         return InsetMathNest::idxLast(cur);
498 }
499
500
501 bool MathMacro::notifyCursorLeaves(Cursor & cur)
502 {
503         cur.updateFlags(Update::Force);
504         return InsetMathNest::notifyCursorLeaves(cur);
505 }
506
507
508 void MathMacro::fold(Cursor & cur)
509 {
510         if (!nextFoldMode_) {
511                 nextFoldMode_ = true;
512                 cur.updateFlags(Update::Force);
513         }
514 }
515
516
517 void MathMacro::unfold(Cursor & cur)
518 {
519         if (nextFoldMode_) {
520                 nextFoldMode_ = false;
521                 cur.updateFlags(Update::Force);
522         }
523 }
524
525
526 bool MathMacro::folded() const
527 {
528         return nextFoldMode_;
529 }
530
531
532 void MathMacro::write(WriteStream & os) const
533 {
534         if (displayMode_ == DISPLAY_NORMAL) {
535                 BOOST_ASSERT(macro_);
536
537                 os << "\\" << name();
538                 bool first = true;
539                 idx_type i = 0;
540
541                 // Use macroBackup_ instead of macro_ here, because
542                 // this is outside the metrics/draw calls, hence the macro_
543                 // variable can point to a MacroData which was freed already.
544                 vector<docstring> const & defaults = macroBackup_.defaults();
545
546                 // Optional argument
547                 if (os.latex()) {
548                         if (i < optionals_) {
549                                 // the first real optional, the others are non-optional in latex
550                                 if (!cell(i).empty()) {
551                                         first = false;
552                                         os << "[" << cell(0) << "]";
553                                 }
554
555                                 ++i;
556                         }
557                 } else {
558                         // In lyx mode print all in any case
559                         for (; i < cells_.size() && i < optionals_; ++i) {
560                                 first = false;
561                                 os << "[" << cell(i) << "]";
562                         }
563                 }
564
565                 for (; i < cells_.size(); ++i) {
566                         if (cell(i).empty() && i < optionals_) {
567                                 os << "{" << defaults[i] << "}";
568                         } else if (cell(i).size() == 1 && cell(i)[0].nucleus()->asCharInset()) {
569                                 if (first)
570                                         os << " ";
571                                 os << cell(i);
572                         }       else
573                                 os << "{" << cell(i) << "}";
574                         first = false;
575                 }
576                 if (first)
577                         os.pendingSpace(true);
578         } else {
579                 os << "\\" << name() << " ";
580                 os.pendingSpace(true);
581         }
582 }
583
584
585 void MathMacro::maple(MapleStream & os) const
586 {
587         lyx::maple(expanded_.cell(0), os);
588 }
589
590
591 void MathMacro::mathmlize(MathStream & os) const
592 {
593         lyx::mathmlize(expanded_.cell(0), os);
594 }
595
596
597 void MathMacro::octave(OctaveStream & os) const
598 {
599         lyx::octave(expanded_.cell(0), os);
600 }
601
602
603 void MathMacro::infoize(odocstream & os) const
604 {
605         os << "Macro: " << name();
606 }
607
608
609 void MathMacro::infoize2(odocstream & os) const
610 {
611         os << "Macro: " << name();
612
613 }
614
615
616 } // namespace lyx