]> git.lyx.org Git - lyx.git/blob - src/mathed/MathMacro.cpp
* Inset:
[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         dim_ = dim;
146 }
147
148
149 void MathMacro::draw(PainterInfo & pi, int x, int y) const
150 {
151         if (!MacroTable::globalMacros().has(name())) {
152                 // FIXME UNICODE
153                 drawStrRed(pi, x, y, "Unknown: " + name());
154         } else {
155                 MacroData const & macro = MacroTable::globalMacros().get(name());
156
157                 // warm up cache
158                 for (size_t i = 0; i < nargs(); ++i)
159                         cell(i).setXY(*pi.base.bv, x, y);
160
161                 if (macro.locked()) {
162                         // FIXME UNICODE
163                         drawStrRed(pi, x, y, "Self reference: " + name());
164                 } else if (editing_) {
165                         Font font = pi.base.font;
166                         augmentFont(font, from_ascii("lyxtex"));
167                         int h = y - dim_.ascent() + 2 + tmpl_.ascent();
168                         pi.pain.text(x + 3, h, name(), font);
169                         int const w = mathed_string_width(font, name());
170                         tmpl_.draw(pi, x + w + 12, h);
171                         h += tmpl_.descent();
172                         Dimension ldim;
173                         docstring t = from_ascii("#1: ");
174                         mathed_string_dim(font, t, ldim);
175                         for (idx_type i = 0; i < nargs(); ++i) {
176                                 MathData const & c = cell(i);
177                                 h += max(c.ascent(), ldim.asc) + 5;
178                                 c.draw(pi, x + ldim.wid, h);
179                                 char_type str[] = { '#', '1', ':', '\0' };
180                                 str[1] += static_cast<char_type>(i);
181                                 pi.pain.text(x + 3, h, str, font);
182                                 h += max(c.descent(), ldim.des) + 5;
183                         }
184                 } else {
185                         macro.lock();
186                         expanded_.draw(pi, x, y);
187                         macro.unlock();
188                 }
189
190                 // edit mode changed?
191                 if (editing_ != editing(pi.base.bv) || macroBackup_ != macro)
192                         pi.base.bv->cursor().updateFlags(Update::Force);
193         }
194 }
195
196
197 void MathMacro::drawSelection(PainterInfo & pi, int x, int y) const
198 {
199         // We may have 0 arguments, but InsetMathNest requires at least one.
200         if (nargs() > 0)
201                 InsetMathNest::drawSelection(pi, x, y);
202 }
203
204
205 void MathMacro::validate(LaTeXFeatures & features) const
206 {
207         string const require = MacroTable::globalMacros().get(name()).requires();
208         if (!require.empty())
209                 features.require(require);
210
211         if (name() == "binom" || name() == "mathcircumflex")
212                 features.require(to_utf8(name()));
213 }
214
215
216 Inset * MathMacro::editXY(Cursor & cur, int x, int y)
217 {
218         // We may have 0 arguments, but InsetMathNest requires at least one.
219         if (nargs() > 0) {
220                 // Prevent crash due to cold coordcache
221                 // FIXME: This is only a workaround, the call of
222                 // InsetMathNest::editXY is correct. The correct fix would
223                 // ensure that the coordcache of the arguments is valid.
224                 if (!editing(&cur.bv())) {
225                         edit(cur, true);
226                         return this;
227                 }
228                 return InsetMathNest::editXY(cur, x, y);
229         }
230         return this;
231 }
232
233
234 bool MathMacro::idxFirst(Cursor & cur) const
235 {
236         cur.updateFlags(Update::Force);
237         return InsetMathNest::idxFirst(cur);
238 }
239
240
241 bool MathMacro::idxLast(Cursor & cur) const
242 {
243         cur.updateFlags(Update::Force);
244         return InsetMathNest::idxLast(cur);
245 }
246
247
248 bool MathMacro::idxUpDown(Cursor & cur, bool up) const
249 {
250         if (up) {
251                 if (cur.idx() == 0)
252                         return false;
253                 --cur.idx();
254         } else {
255                 if (cur.idx() + 1 >= nargs())
256                         return false;
257                 ++cur.idx();
258         }
259         cur.pos() = cell(cur.idx()).x2pos(cur.x_target());
260         return true;
261 }
262
263
264 bool MathMacro::notifyCursorLeaves(Cursor & cur)
265 {
266         cur.updateFlags(Update::Force);
267         return InsetMathNest::notifyCursorLeaves(cur);
268 }
269
270
271 void MathMacro::maple(MapleStream & os) const
272 {
273         updateExpansion();
274         lyx::maple(expanded_, os);
275 }
276
277
278 void MathMacro::mathmlize(MathStream & os) const
279 {
280         updateExpansion();
281         lyx::mathmlize(expanded_, os);
282 }
283
284
285 void MathMacro::octave(OctaveStream & os) const
286 {
287         updateExpansion();
288         lyx::octave(expanded_, os);
289 }
290
291
292 void MathMacro::updateExpansion() const
293 {
294         MacroData const & macro = MacroTable::globalMacros().get(name());
295
296         // create MathMacroArgumentValue object pointing to the cells of the macro
297         std::vector<MathData> values(nargs());
298         for (size_t i = 0; i != nargs(); ++i)
299                                 values[i].insert(0, MathAtom(new MathMacroArgumentValue(*this, i)));
300         macro.expand(values, expanded_);
301         asArray(macro.def(), tmpl_);
302         macroBackup_ = macro;
303 }
304
305
306 void MathMacro::infoize(odocstream & os) const
307 {
308         os << "Macro: " << name();
309 }
310
311
312 void MathMacro::infoize2(odocstream & os) const
313 {
314         os << "Macro: " << name();
315
316 }
317
318
319 } // namespace lyx