]> git.lyx.org Git - lyx.git/blob - src/mathed/formula.C
fix compilation
[lyx.git] / src / mathed / formula.C
1 /*
2 *  File:        formula.C
3 *  Purpose:     Implementation of formula inset
4 *  Author:      Alejandro Aguilar Sierra <asierra@servidor.unam.mx>
5 *  Created:     January 1996
6 *  Description: Allows the edition of math paragraphs inside Lyx.
7 *
8 *  Copyright: 1996-1998 Alejandro Aguilar Sierra
9 *
10 *  Version: 0.4, Lyx project.
11 *
12 *   You are free to use and modify this code under the terms of
13 *   the GNU General Public Licence version 2 or later.
14 */
15
16 #ifdef __GNUG__
17 #pragma implementation
18 #endif
19
20 #include <config.h>
21
22 #include "formula.h"
23 #include "commandtags.h"
24 #include "math_cursor.h"
25 #include "math_parser.h"
26 #include "math_charinset.h"
27 #include "math_arrayinset.h"
28 #include "math_deliminset.h"
29 #include "lyx_main.h"
30 #include "BufferView.h"
31 #include "gettext.h"
32 #include "debug.h"
33 #include "support/LOstream.h"
34 #include "support/LAssert.h"
35 #include "support/lyxlib.h"
36 #include "support/systemcall.h"
37 #include "support/filetools.h"
38 #include "frontends/Alert.h"
39 #include "frontends/LyXView.h"
40 #include "frontends/Painter.h"
41 #include "graphics/GraphicsImage.h"
42 #include "lyxrc.h"
43 #include "math_hullinset.h"
44 #include "math_support.h"
45 #include "math_mathmlstream.h"
46 #include "textpainter.h"
47
48 #include <fstream>
49 #include <boost/bind.hpp>
50 #include <boost/utility.hpp>
51
52 using std::ostream;
53 using std::ifstream;
54 using std::istream;
55 using std::pair;
56 using std::endl;
57 using std::vector;
58 using std::getline;
59
60
61
62 InsetFormula::InsetFormula()
63         : par_(MathAtom(new MathHullInset)), loader_(0)
64 {}
65
66
67 InsetFormula::InsetFormula(MathInsetTypes t)
68         : par_(MathAtom(new MathHullInset(t))), loader_(0)
69 {}
70
71
72 InsetFormula::InsetFormula(string const & s)
73 {
74         if (s.size()) {
75                 bool res = mathed_parse_normal(par_, s);
76
77                 if (!res)
78                         res = mathed_parse_normal(par_, "$" + s + "$");
79
80                 if (!res) {
81                         lyxerr << "cannot interpret '" << s << "' as math\n";
82                         par_ = MathAtom(new MathHullInset(LM_OT_SIMPLE));
83                 }
84         }
85         metrics();
86         updatePreview();
87 }
88
89
90 Inset * InsetFormula::clone(Buffer const &, bool) const
91 {
92         return new InsetFormula(*this);
93 }
94
95
96 void InsetFormula::write(Buffer const *, ostream & os) const
97 {
98         os << "Formula ";
99         WriteStream wi(os, false, false);
100         par_->write(wi);
101 }
102
103
104 int InsetFormula::latex(Buffer const *, ostream & os, bool fragile, bool) const
105 {
106         WriteStream wi(os, fragile, true);
107         par_->write(wi);
108         return wi.line();
109 }
110
111
112 int InsetFormula::ascii(Buffer const *, ostream & os, int) const
113 {
114 #if 0
115         TextMetricsInfo mi;
116         par()->metricsT(mi);
117         TextPainter tpain(par()->width(), par()->height());
118         par()->drawT(tpain, 0, par()->ascent());
119         tpain.show(os);
120         // reset metrics cache to "real" values
121         metrics();
122         return tpain.textheight();
123 #else
124         WriteStream wi(os, false, true);
125         par_->write(wi);
126         return wi.line();
127 #endif
128 }
129
130
131 int InsetFormula::linuxdoc(Buffer const * buf, ostream & os) const
132 {
133         return docbook(buf, os, false);
134 }
135
136
137 int InsetFormula::docbook(Buffer const * buf, ostream & os, bool) const
138 {
139         MathMLStream ms(os);
140         ms << MTag("equation");
141         ms <<   MTag("alt");
142         ms <<    "<[CDATA[";
143         int res = ascii(buf, ms.os(), 0);
144         ms <<    "]]>";
145         ms <<   ETag("alt");
146         ms <<   MTag("math");
147         ms <<    par_.nucleus();
148         ms <<   ETag("math");
149         ms << ETag("equation");
150         return ms.line() + res;
151 }
152
153
154 void InsetFormula::read(Buffer const *, LyXLex & lex)
155 {
156         mathed_parse_normal(par_, lex);
157         metrics();
158         updatePreview();
159 }
160
161
162 //ostream & operator<<(ostream & os, LyXCursor const & c)
163 //{
164 //      os << '[' << c.x() << ' ' << c.y() << ' ' << c.pos() << ']';
165 //      return os;
166 //}
167
168
169 void InsetFormula::draw(BufferView * bv, LyXFont const & font,
170                         int y, float & xx, bool) const
171 {
172         int const x = int(xx);
173         int const w = width(bv, font);
174         int const d = descent(bv, font);
175         int const a = ascent(bv, font);
176         int const h = a + d;
177
178         MathPainterInfo pi(bv->painter());
179
180         if (canPreview()) {
181                 pi.pain.image(x + 1, y - a + 1, w - 2, h - 2, *(loader_->image()));
182         } else {
183                 pi.base.style = display() ? LM_ST_DISPLAY : LM_ST_TEXT;
184                 pi.base.font  = font;
185                 pi.base.font.setColor(LColor::math);
186                 if (lcolor.getX11Name(LColor::mathbg)
187                             != lcolor.getX11Name(LColor::background))
188                         pi.pain.fillRectangle(x, y - a, w, h, LColor::mathbg);
189
190                 if (mathcursor &&
191                                 const_cast<InsetFormulaBase const *>(mathcursor->formula()) == this)
192                 {
193                         mathcursor->drawSelection(pi);
194                         pi.pain.rectangle(x, y - a, w, h, LColor::mathframe);
195                 }
196
197                 par_->draw(pi, x, y);
198         }
199
200         xx += w;
201         xo_ = x;
202         yo_ = y;
203
204         setCursorVisible(false);
205 }
206
207
208 vector<string> const InsetFormula::getLabelList() const
209 {
210         return hull()->getLabelList();
211 }
212
213
214 UpdatableInset::RESULT
215 InsetFormula::localDispatch(BufferView * bv, kb_action action,
216          string const & arg)
217 {
218         RESULT result = DISPATCHED;
219
220         switch (action) {
221
222                 case LFUN_BREAKLINE:
223                         bv->lockedInsetStoreUndo(Undo::INSERT);
224                         mathcursor->breakLine();
225                         mathcursor->normalize();
226                         updateLocal(bv, true);
227                         break;
228
229                 case LFUN_MATH_NUMBER:
230                 {
231                         //lyxerr << "toggling all numbers\n";
232                         if (display()) {
233                                 bv->lockedInsetStoreUndo(Undo::INSERT);
234                                 bool old = hull()->numberedType();
235                                 for (MathInset::row_type row = 0; row < par_->nrows(); ++row)
236                                         hull()->numbered(row, !old);
237                                 bv->owner()->message(old ? _("No number") : _("Number"));
238                                 updateLocal(bv, true);
239                         }
240                         break;
241                 }
242
243                 case LFUN_MATH_NONUMBER:
244                 {
245                         //lyxerr << "toggling line number\n";
246                         if (display()) {
247                                 bv->lockedInsetStoreUndo(Undo::INSERT);
248                                 MathCursor::row_type row = mathcursor->hullRow();
249                                 bool old = hull()->numbered(row);
250                                 bv->owner()->message(old ? _("No number") : _("Number"));
251                                 hull()->numbered(row, !old);
252                                 updateLocal(bv, true);
253                         }
254                         break;
255                 }
256
257                 case LFUN_INSERT_LABEL:
258                 {
259                         bv->lockedInsetStoreUndo(Undo::INSERT);
260
261                         MathCursor::row_type row = mathcursor->hullRow();
262                         string old_label = hull()->label(row);
263                         string new_label = arg;
264
265                         if (new_label.empty()) {
266                                 string const default_label =
267                                         (lyxrc.label_init_length >= 0) ? "eq:" : "";
268                                 pair<bool, string> const res = old_label.empty()
269                                         ? Alert::askForText(_("Enter new label to insert:"), default_label)
270                                         : Alert::askForText(_("Enter label:"), old_label);
271                                 if (!res.first)
272                                         break;
273                                 new_label = frontStrip(strip(res.second));
274                         }
275
276                         //if (new_label == old_label)
277                         //      break;  // Nothing to do
278
279                         if (!new_label.empty()) {
280                                 lyxerr << "setting label to '" << new_label << "'\n";
281                                 hull()->numbered(row, true);
282                         }
283
284 #warning FIXME: please check you really mean repaint() ... is it needed,
285 #warning and if so, should it be update() instead ? 
286                         if (!new_label.empty() && bv->ChangeRefsIfUnique(old_label, new_label))
287                                 bv->repaint();
288
289                         hull()->label(row, new_label);
290
291                         updateLocal(bv, true);
292                         break;
293                 }
294
295                 case LFUN_MATH_MUTATE:
296                 {
297                         bv->lockedInsetStoreUndo(Undo::EDIT);
298                         int x;
299                         int y;
300                         mathcursor->getPos(x, y);
301                         hull()->mutate(arg);
302                         mathcursor->setPos(x, y);
303                         mathcursor->normalize();
304                         updateLocal(bv, true);
305                         break;
306                 }
307
308                 case LFUN_MATH_EXTERN:
309                 {
310                         bv->lockedInsetStoreUndo(Undo::EDIT);
311                         if (mathcursor)
312                                 mathcursor->handleExtern(arg);
313                         // re-compute inset dimension
314                         metrics(bv);
315                         updateLocal(bv, true);
316                         break;
317                 }
318
319                 case LFUN_MATH_DISPLAY:
320                 {
321                         int x = 0;
322                         int y = 0;
323                         mathcursor->getPos(x, y);
324                         if (getType() == LM_OT_SIMPLE)
325                                 hull()->mutate(LM_OT_EQUATION);
326                         else
327                                 hull()->mutate(LM_OT_SIMPLE);
328                         mathcursor->setPos(x, y);
329                         mathcursor->normalize();
330                         updateLocal(bv, true);
331                         break;
332                 }
333
334                 case LFUN_PASTESELECTION:
335                 {
336                         string const clip = bv->getClipboard();
337                 if (!clip.empty())
338                                 mathed_parse_normal(par_, clip);
339                         break;
340                 }
341
342                 default:
343                         result = InsetFormulaBase::localDispatch(bv, action, arg);
344         }
345
346         //updatePreview();
347
348         return result;
349 }
350
351
352 bool InsetFormula::display() const
353 {
354         return getType() != LM_OT_SIMPLE;
355 }
356
357
358 MathHullInset const * InsetFormula::hull() const
359 {
360         lyx::Assert(par_->asHullInset());
361         return par_->asHullInset();
362 }
363
364
365 MathHullInset * InsetFormula::hull()
366 {
367         lyx::Assert(par_->asHullInset());
368         return par_->asHullInset();
369 }
370
371
372 Inset::Code InsetFormula::lyxCode() const
373 {
374         return Inset::MATH_CODE;
375 }
376
377
378 void InsetFormula::validate(LaTeXFeatures & features) const
379 {
380         par_->validate(features);
381 }
382
383
384 bool InsetFormula::insetAllowed(Inset::Code code) const
385 {
386         return
387                 (code == Inset::LABEL_CODE && display())
388                 || code == Inset::REF_CODE      
389                 || code == Inset::ERT_CODE;
390 }
391
392
393 int InsetFormula::ascent(BufferView *, LyXFont const &) const
394 {
395         const int a = par_->ascent();
396         if (!canPreview())
397                 return a + 1;
398         return a + 1 - (par_->height() - loader_->image()->getHeight()) / 2;
399 }
400
401
402 int InsetFormula::descent(BufferView *, LyXFont const &) const
403 {
404         const int d = par_->descent();
405         if (!canPreview())
406                 return d + 1;
407         return d + 1 - (par_->height() - loader_->image()->getHeight()) / 2;
408 }
409
410
411 int InsetFormula::width(BufferView * bv, LyXFont const & font) const
412 {
413         metrics(bv, font);
414         return canPreview() ? loader_->image()->getWidth() : par_->width();
415 }
416
417
418 MathInsetTypes InsetFormula::getType() const
419 {
420         return hull()->getType();
421 }
422
423
424 //
425 // preview stuff
426 //
427
428 bool InsetFormula::canPreview() const
429 {
430         return lyxrc.preview && loader_ && !par_->asNestInset()->editing()
431                 && loader_->status() == grfx::Ready;
432 }
433
434
435 void InsetFormula::statusChanged()
436 {
437         lyxerr << "### InsetFormula::statusChanged called!, status: "
438                 << loader_->status() << "\n";
439         if (loader_->status() == grfx::Ready) 
440                 view()->updateInset(this, false);
441         else if (loader_->status() == grfx::WaitingToLoad)
442                 loader_->startLoading();
443 }
444
445
446 void InsetFormula::updatePreview()
447 {
448         // nothing to be done if no preview requested
449         lyxerr << "### updatePreview() called\n";
450         if (!lyxrc.preview)
451                 return;
452
453         // get LaTeX 
454         ostringstream ls;
455         WriteStream wi(ls, false, false);
456         par_->write(wi);
457         string const data = ls.str();
458
459         // the preview cache, maps contents to image loaders
460         typedef std::map<string, boost::shared_ptr<grfx::Loader> > cache_type;
461         static cache_type theCache;
462         static int theCounter = 0;
463
464         // set our loader corresponding to our current data
465         cache_type::const_iterator it = theCache.find(data);
466
467         // is this old data?
468         if (it != theCache.end()) {
469                 // we have already a loader, connect to it anyway
470                 //lyxerr << "### updatePreview(), old loader: " << loader_ << "\n";
471                 loader_ = it->second.get();
472                 loader_->statusChanged.connect
473                         (boost::bind(&InsetFormula::statusChanged, this));
474                 return;
475         }
476
477         // construct new file name
478         static string const dir = OnlyPath(lyx::tempName());
479         ostringstream os;
480         os << dir << theCounter++ << ".lyxpreview";
481         string file = os.str();
482
483         // the real work starts
484         //lyxerr << "### updatePreview(), new file " << file << "\n";
485         std::ofstream of(file.c_str());
486         of << "\\batchmode"
487                  << "\\documentclass{article}"
488                  << "\\usepackage{amssymb}"
489                  << "\\thispagestyle{empty}"
490                  << "\\pdfoutput=0"
491                  << "\\begin{document}"
492                  << data
493                  << "\\end{document}\n";
494         of.close();
495
496         // now we are done, start actual loading we will get called back via
497         // InsetFormula::statusChanged() if this is finished
498         theCache[data].reset(new grfx::Loader(file));
499         //lyxerr << "### updatePreview(), new loader: " << loader_ << "\n";
500         loader_ = theCache.find(data)->second.get();
501         loader_->startLoading();
502         loader_->statusChanged.connect(boost::bind(&InsetFormula::statusChanged, this));
503 }
504