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