]> git.lyx.org Git - lyx.git/blob - src/mathed/MathMacro.cpp
8711cc6999291ab14efea97ec98697438d88e278
[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  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12 #include <config.h>
13
14 #include "MathMacro.h"
15 #include "MathSupport.h"
16 #include "MathExtern.h"
17 #include "MathStream.h"
18
19 #include "Buffer.h"
20 #include "Cursor.h"
21 #include "debug.h"
22 #include "BufferView.h"
23 #include "LaTeXFeatures.h"
24 #include "frontends/Painter.h"
25
26
27 namespace lyx {
28
29 using std::string;
30 using std::max;
31
32
33 /// This class is the value of a macro argument, technically
34 /// a wrapper of the cells of MathMacro.
35 class MathMacroArgumentValue : public InsetMath {
36 public:
37         ///
38         MathMacroArgumentValue(MathMacro const & mathMacro, size_t idx)
39                 : mathMacro_(mathMacro), idx_(idx) {}
40         ///
41         void metrics(MetricsInfo & mi, Dimension & dim) const;
42         ///
43         void draw(PainterInfo &, int x, int y) const;
44         ///
45         int kerning() const { return mathMacro_.cell(idx_).kerning(); }
46
47 private:
48         Inset * clone() const;
49         MathMacro const & mathMacro_;
50         size_t idx_;
51 };
52
53
54 Inset * MathMacroArgumentValue::clone() const
55 {
56         return new MathMacroArgumentValue(*this);
57 }
58
59
60 void MathMacroArgumentValue::metrics(MetricsInfo & mi, Dimension & dim) const
61 {
62         // unlock outer macro in arguments, and lock it again later
63         MacroData const & macro = MacroTable::globalMacros().get(mathMacro_.name());
64         macro.unlock();
65         mathMacro_.cell(idx_).metrics(mi, dim);
66         macro.lock();
67 }
68
69
70 void MathMacroArgumentValue::draw(PainterInfo & pi, int x, int y) const
71 {
72         // unlock outer macro in arguments, and lock it again later
73         MacroData const & macro = MacroTable::globalMacros().get(mathMacro_.name());
74         macro.unlock();
75         mathMacro_.cell(idx_).draw(pi, x, y);
76         macro.lock();
77 }
78
79
80 MathMacro::MathMacro(docstring const & name, int numargs)
81         : InsetMathNest(numargs), name_(name), editing_(false)
82 {}
83
84
85 Inset * MathMacro::clone() const
86 {
87         MathMacro * x = new MathMacro(*this);
88         x->expanded_ = MathData();
89         x->macroBackup_ = MacroData();
90         return x;
91 }
92
93
94 docstring MathMacro::name() const
95 {
96         return name_;
97 }
98
99
100 void MathMacro::cursorPos(BufferView const & bv,
101                 CursorSlice const & sl, bool boundary, int & x, int & y) const
102 {
103         // We may have 0 arguments, but InsetMathNest requires at least one.
104         if (nargs() > 0)
105                 InsetMathNest::cursorPos(bv, sl, boundary, x, y);
106 }
107
108
109 void MathMacro::metrics(MetricsInfo & mi, Dimension & dim) const
110 {
111         kerning_ = 0;
112         if (!MacroTable::globalMacros().has(name())) {
113                 mathed_string_dim(mi.base.font, "Unknown: " + name(), dim);
114         } else {
115                 MacroData const & macro = MacroTable::globalMacros().get(name());
116
117                 if (macroBackup_ != macro)
118                         updateExpansion();
119
120                 if (macro.locked()) {
121                         mathed_string_dim(mi.base.font, "Self reference: " + name(), dim);
122                 } else if (editing(mi.base.bv)) {
123                         Font font = mi.base.font;
124                         augmentFont(font, from_ascii("lyxtex"));
125                         tmpl_.metrics(mi, dim);
126                         // FIXME UNICODE
127                         dim.wid += mathed_string_width(font, name()) + 10;
128                         // FIXME UNICODE
129                         int ww = mathed_string_width(font, from_ascii("#1: "));
130                         for (idx_type i = 0; i < nargs(); ++i) {
131                                 MathData const & c = cell(i);
132                                 c.metrics(mi);
133                                 dim.wid  = max(dim.wid, c.width() + ww);
134                                 dim.des += c.height() + 10;
135                         }
136                         editing_ = true;
137                 } else {
138                         macro.lock();
139                         expanded_.metrics(mi, dim);
140                         macro.unlock();
141                         kerning_ = expanded_.kerning();
142                         editing_ = false;
143                 }
144         }
145         // Cache the inset dimension. 
146         setDimCache(mi, dim);
147 }
148
149
150 void MathMacro::draw(PainterInfo & pi, int x, int y) const
151 {
152         if (!MacroTable::globalMacros().has(name())) {
153                 // FIXME UNICODE
154                 drawStrRed(pi, x, y, "Unknown: " + name());
155         } else {
156                 MacroData const & macro = MacroTable::globalMacros().get(name());
157
158                 // warm up cache
159                 for (size_t i = 0; i < nargs(); ++i)
160                         cell(i).setXY(*pi.base.bv, x, y);
161
162                 if (macro.locked()) {
163                         // FIXME UNICODE
164                         drawStrRed(pi, x, y, "Self reference: " + name());
165                 } else if (editing_) {
166                         Font font = pi.base.font;
167                         augmentFont(font, from_ascii("lyxtex"));
168                         Dimension const dim = dimension(*pi.base.bv);
169                         int h = y - dim.ascent() + 2 + tmpl_.ascent();
170                         pi.pain.text(x + 3, h, name(), font);
171                         int const w = mathed_string_width(font, name());
172                         tmpl_.draw(pi, x + w + 12, h);
173                         h += tmpl_.descent();
174                         Dimension ldim;
175                         docstring t = from_ascii("#1: ");
176                         mathed_string_dim(font, t, ldim);
177                         for (idx_type i = 0; i < nargs(); ++i) {
178                                 MathData const & c = cell(i);
179                                 h += max(c.ascent(), ldim.asc) + 5;
180                                 c.draw(pi, x + ldim.wid, h);
181                                 char_type str[] = { '#', '1', ':', '\0' };
182                                 str[1] += static_cast<char_type>(i);
183                                 pi.pain.text(x + 3, h, str, font);
184                                 h += max(c.descent(), ldim.des) + 5;
185                         }
186                 } else {
187                         macro.lock();
188                         expanded_.draw(pi, x, y);
189                         macro.unlock();
190                 }
191
192                 // edit mode changed?
193                 if (editing_ != editing(pi.base.bv) || macroBackup_ != macro)
194                         pi.base.bv->cursor().updateFlags(Update::Force);
195         }
196 }
197
198
199 void MathMacro::drawSelection(PainterInfo & pi, int x, int y) const
200 {
201         // We may have 0 arguments, but InsetMathNest requires at least one.
202         if (nargs() > 0)
203                 InsetMathNest::drawSelection(pi, x, y);
204 }
205
206
207 void MathMacro::validate(LaTeXFeatures & features) const
208 {
209         string const require = MacroTable::globalMacros().get(name()).requires();
210         if (!require.empty())
211                 features.require(require);
212
213         if (name() == "binom" || name() == "mathcircumflex")
214                 features.require(to_utf8(name()));
215 }
216
217
218 Inset * MathMacro::editXY(Cursor & cur, int x, int y)
219 {
220         // We may have 0 arguments, but InsetMathNest requires at least one.
221         if (nargs() > 0) {
222                 // Prevent crash due to cold coordcache
223                 // FIXME: This is only a workaround, the call of
224                 // InsetMathNest::editXY is correct. The correct fix would
225                 // ensure that the coordcache of the arguments is valid.
226                 if (!editing(&cur.bv())) {
227                         edit(cur, true);
228                         return this;
229                 }
230                 return InsetMathNest::editXY(cur, x, y);
231         }
232         return this;
233 }
234
235
236 bool MathMacro::idxFirst(Cursor & cur) const
237 {
238         cur.updateFlags(Update::Force);
239         return InsetMathNest::idxFirst(cur);
240 }
241
242
243 bool MathMacro::idxLast(Cursor & cur) const
244 {
245         cur.updateFlags(Update::Force);
246         return InsetMathNest::idxLast(cur);
247 }
248
249
250 bool MathMacro::idxUpDown(Cursor & cur, bool up) const
251 {
252         if (up) {
253                 if (cur.idx() == 0)
254                         return false;
255                 --cur.idx();
256         } else {
257                 if (cur.idx() + 1 >= nargs())
258                         return false;
259                 ++cur.idx();
260         }
261         cur.pos() = cell(cur.idx()).x2pos(cur.x_target());
262         return true;
263 }
264
265
266 bool MathMacro::notifyCursorLeaves(Cursor & cur)
267 {
268         cur.updateFlags(Update::Force);
269         return InsetMathNest::notifyCursorLeaves(cur);
270 }
271
272
273 void MathMacro::maple(MapleStream & os) const
274 {
275         updateExpansion();
276         lyx::maple(expanded_, os);
277 }
278
279
280 void MathMacro::mathmlize(MathStream & os) const
281 {
282         updateExpansion();
283         lyx::mathmlize(expanded_, os);
284 }
285
286
287 void MathMacro::octave(OctaveStream & os) const
288 {
289         updateExpansion();
290         lyx::octave(expanded_, os);
291 }
292
293
294 void MathMacro::updateExpansion() const
295 {
296         MacroData const & macro = MacroTable::globalMacros().get(name());
297
298         // create MathMacroArgumentValue object pointing to the cells of the macro
299         std::vector<MathData> values(nargs());
300         for (size_t i = 0; i != nargs(); ++i)
301                                 values[i].insert(0, MathAtom(new MathMacroArgumentValue(*this, i)));
302         macro.expand(values, expanded_);
303         asArray(macro.def(), tmpl_);
304         macroBackup_ = macro;
305 }
306
307
308 void MathMacro::infoize(odocstream & os) const
309 {
310         os << "Macro: " << name();
311 }
312
313
314 void MathMacro::infoize2(odocstream & os) const
315 {
316         os << "Macro: " << name();
317
318 }
319
320
321 } // namespace lyx