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