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