X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Fmathed%2Fformula.C;h=a6c0b602018d1942208792fcdc2a17c6e4868572;hb=46880e2b9b49632c56bab2377ce9a3c826cf8d1d;hp=00c040a9db6e891e48c42dcceb275c763d22192b;hpb=f0dad9cf92372434ec81d6bd2a8d5345ad9ebe01;p=lyx.git diff --git a/src/mathed/formula.C b/src/mathed/formula.C index 00c040a9db..a6c0b60201 100644 --- a/src/mathed/formula.C +++ b/src/mathed/formula.C @@ -18,149 +18,193 @@ #endif #include -#include #include "formula.h" #include "commandtags.h" #include "math_cursor.h" #include "math_parser.h" +#include "math_charinset.h" +#include "math_arrayinset.h" +#include "math_deliminset.h" #include "lyx_main.h" #include "BufferView.h" #include "gettext.h" #include "debug.h" -#include "lyx_gui_misc.h" #include "support/LOstream.h" +#include "support/LAssert.h" #include "support/lyxlib.h" -#include "support/syscall.h" -#include "LyXView.h" -#include "Painter.h" +#include "support/systemcall.h" +#include "support/filetools.h" +#include "frontends/Alert.h" +#include "frontends/LyXView.h" +#include "frontends/Painter.h" +#include "graphics/GraphicsImage.h" #include "lyxrc.h" -#include "math_matrixinset.h" -#include "mathed/support.h" +#include "math_hullinset.h" +#include "math_support.h" +#include "math_mathmlstream.h" +#include "textpainter.h" + +#include +#include +#include -using std::ostringstream; using std::ostream; using std::ifstream; using std::istream; using std::pair; using std::endl; using std::vector; +using std::getline; -extern char const * latex_mathenv[]; -extern MathCursor * mathcursor; - - -// quite a hack i know. Should be done with return values... -int number_of_newlines = 0; InsetFormula::InsetFormula() - : InsetFormulaBase(new MathMatrixInset) + : par_(MathAtom(new MathHullInset)), loader_(0) {} -InsetFormula::InsetFormula(MathInsetTypes t) - : InsetFormulaBase(new MathMatrixInset(t)) -{} +InsetFormula::InsetFormula(BufferView * bv) + : par_(MathAtom(new MathHullInset)), loader_(0) +{ + view_ = bv; +} -InsetFormula::InsetFormula(string const & s) - : InsetFormulaBase(0) +InsetFormula::InsetFormula(string const & data) + : par_(MathAtom(new MathHullInset)), loader_(0) { - istringstream is(s.c_str()); - par(mathed_parse(is)); - Metrics(); + if (!data.size()) + return; + if (!mathed_parse_normal(par_, data)) + lyxerr << "cannot interpret '" << data << "' as math\n"; } + Inset * InsetFormula::clone(Buffer const &, bool) const { return new InsetFormula(*this); } -void InsetFormula::write(ostream & os) const +void InsetFormula::write(Buffer const *, ostream & os) const { os << "Formula "; - latex(os, false, false); + WriteStream wi(os, false, false); + par_->write(wi); } -int InsetFormula::latex(ostream & os, bool fragile, bool) const +int InsetFormula::latex(Buffer const *, ostream & os, bool fragile, bool) const { - par()->Write(os, fragile); - return 1; + WriteStream wi(os, fragile, true); + par_->write(wi); + return wi.line(); } -int InsetFormula::ascii(ostream & os, int) const + +int InsetFormula::ascii(Buffer const *, ostream & os, int) const { - par()->Write(os, false); - return 1; +#if 0 + TextMetricsInfo mi; + par()->metricsT(mi); + TextPainter tpain(par()->width(), par()->height()); + par()->drawT(tpain, 0, par()->ascent()); + tpain.show(os); + // reset metrics cache to "real" values + metrics(); + return tpain.textheight(); +#else + WriteStream wi(os, false, true); + par_->write(wi); + return wi.line(); +#endif } -int InsetFormula::linuxdoc(ostream & os) const +int InsetFormula::linuxdoc(Buffer const * buf, ostream & os) const { - return ascii(os, 0); + return docbook(buf, os, false); } -int InsetFormula::docBook(ostream & os) const +int InsetFormula::docbook(Buffer const * buf, ostream & os, bool) const { - return ascii(os, 0); + MathMLStream ms(os); + ms << MTag("equation"); + ms << MTag("alt"); + ms << "<[CDATA["; + int res = ascii(buf, ms.os(), 0); + ms << "]]>"; + ms << ETag("alt"); + ms << MTag("math"); + ms << par_.nucleus(); + ms << ETag("math"); + ms << ETag("equation"); + return ms.line() + res; } -void InsetFormula::read(LyXLex & lex) +void InsetFormula::read(Buffer const *, LyXLex & lex) { - par(mathed_parse(lex)); - Metrics(); + mathed_parse_normal(par_, lex); + metrics(); + updatePreview(); } -void InsetFormula::draw(BufferView * bv, LyXFont const &, +//ostream & operator<<(ostream & os, LyXCursor const & c) +//{ +// os << '[' << c.x() << ' ' << c.y() << ' ' << c.pos() << ']'; +// return os; +//} + + +void InsetFormula::draw(BufferView * bv, LyXFont const & font, int y, float & xx, bool) const { - int x = int(xx) - 1; - y -= 2; - MathInset::workwidth = bv->workWidth(); - Painter & pain = bv->painter(); - - Metrics(); - int w = par()->width(); - int h = par()->height(); - int a = par()->ascent(); - pain.fillRectangle(int(x), y - a, w, h, LColor::mathbg); - - if (mathcursor) { - if (mathcursor->formula() == this) { - if (mathcursor->Selection()) { - int xp[10]; - int yp[10]; - int n; - mathcursor->SelGetArea(xp, yp, n); - pain.fillPolygon(xp, yp, n, LColor::selection); - } - pain.rectangle(int(x), y - a, w, h, LColor::mathframe); + int const x = int(xx); + int const w = width(bv, font); + int const d = descent(bv, font); + int const a = ascent(bv, font); + int const h = a + d; + + MathPainterInfo pi(bv->painter()); + + if (canPreview()) { + pi.pain.image(x + 1, y - a + 1, w - 2, h - 2, *(loader_->image())); + } else { + //pi.base.style = display() ? LM_ST_DISPLAY : LM_ST_TEXT; + pi.base.style = LM_ST_TEXT; + pi.base.font = font; + pi.base.font.setColor(LColor::math); + if (lcolor.getX11Name(LColor::mathbg) + != lcolor.getX11Name(LColor::background)) + pi.pain.fillRectangle(x, y - a, w, h, LColor::mathbg); + + if (mathcursor && + const_cast(mathcursor->formula()) == this) + { + mathcursor->drawSelection(pi); + pi.pain.rectangle(x, y - a, w, h, LColor::mathframe); } + + par_->draw(pi, x, y); } - par()->draw(pain, int(x), y); - xx += par()->width(); + xx += w; + xo_ = x; + yo_ = y; setCursorVisible(false); } -void InsetFormula::Metrics() const -{ - const_cast(par_)->Metrics(display() ? LM_ST_DISPLAY : LM_ST_TEXT); -} - vector const InsetFormula::getLabelList() const { - return par()->getLabelList(); + return hull()->getLabelList(); } @@ -172,10 +216,11 @@ InsetFormula::localDispatch(BufferView * bv, kb_action action, switch (action) { - case LFUN_BREAKLINE: + case LFUN_BREAKLINE: bv->lockedInsetStoreUndo(Undo::INSERT); mathcursor->breakLine(); - updateLocal(bv); + mathcursor->normalize(); + updateLocal(bv, true); break; case LFUN_MATH_NUMBER: @@ -183,11 +228,11 @@ InsetFormula::localDispatch(BufferView * bv, kb_action action, //lyxerr << "toggling all numbers\n"; if (display()) { bv->lockedInsetStoreUndo(Undo::INSERT); - bool old = par()->numberedType(); - for (int row = 0; row < par()->nrows(); ++row) - par()->numbered(row, !old); + bool old = hull()->numberedType(); + for (MathInset::row_type row = 0; row < par_->nrows(); ++row) + hull()->numbered(row, !old); bv->owner()->message(old ? _("No number") : _("Number")); - updateLocal(bv); + updateLocal(bv, true); } break; } @@ -197,11 +242,11 @@ InsetFormula::localDispatch(BufferView * bv, kb_action action, //lyxerr << "toggling line number\n"; if (display()) { bv->lockedInsetStoreUndo(Undo::INSERT); - int row = mathcursor->row(); - bool old = par()->numbered(row); + MathCursor::row_type row = mathcursor->hullRow(); + bool old = hull()->numbered(row); bv->owner()->message(old ? _("No number") : _("Number")); - par()->numbered(row, !old); - updateLocal(bv); + hull()->numbered(row, !old); + updateLocal(bv, true); } break; } @@ -210,18 +255,16 @@ InsetFormula::localDispatch(BufferView * bv, kb_action action, { bv->lockedInsetStoreUndo(Undo::INSERT); - int row = mathcursor->row(); - string old_label = par()->label(row); + MathCursor::row_type row = mathcursor->hullRow(); + string old_label = hull()->label(row); string new_label = arg; if (new_label.empty()) { string const default_label = (lyxrc.label_init_length >= 0) ? "eq:" : ""; pair const res = old_label.empty() - ? askForText(_("Enter new label to insert:"), default_label) - : askForText(_("Enter label:"), old_label); - - lyxerr << "res: " << res.first << " - '" << res.second << "'\n"; + ? Alert::askForText(_("Enter new label to insert:"), default_label) + : Alert::askForText(_("Enter label:"), old_label); if (!res.first) break; new_label = frontStrip(strip(res.second)); @@ -232,51 +275,64 @@ InsetFormula::localDispatch(BufferView * bv, kb_action action, if (!new_label.empty()) { lyxerr << "setting label to '" << new_label << "'\n"; - par()->numbered(row, true); + hull()->numbered(row, true); } +#warning FIXME: please check you really mean repaint() ... is it needed, +#warning and if so, should it be update() instead ? if (!new_label.empty() && bv->ChangeRefsIfUnique(old_label, new_label)) - bv->redraw(); + bv->repaint(); - par()->label(row, new_label); + hull()->label(row, new_label); - updateLocal(bv); + updateLocal(bv, true); break; } - case LFUN_MATH_EXTERN: - bv->lockedInsetStoreUndo(Undo::EDIT); - handleExtern(arg, bv); - updateLocal(bv); - break; - case LFUN_MATH_MUTATE: { bv->lockedInsetStoreUndo(Undo::EDIT); int x; int y; - mathcursor->GetPos(x, y); - par()->mutate(arg); - mathcursor->SetPos(x, y); + mathcursor->getPos(x, y); + mutate(arg); + mathcursor->setPos(x, y); mathcursor->normalize(); - updateLocal(bv); + updateLocal(bv, true); + break; + } + + case LFUN_MATH_EXTERN: + { + bv->lockedInsetStoreUndo(Undo::EDIT); + if (mathcursor) + mathcursor->handleExtern(arg); + // re-compute inset dimension + metrics(bv); + updateLocal(bv, true); break; } case LFUN_MATH_DISPLAY: { - int x; - int y; - mathcursor->GetPos(x, y); - if (par()->GetType() == LM_OT_SIMPLE) { - par()->mutate(LM_OT_EQUATION); - par()->numbered(0, false); - } + int x = 0; + int y = 0; + mathcursor->getPos(x, y); + if (hullType() == "simple") + mutate("equation"); else - par()->mutate(LM_OT_SIMPLE); - mathcursor->SetPos(x, y); + mutate("simple"); + mathcursor->setPos(x, y); mathcursor->normalize(); - updateLocal(bv); + updateLocal(bv, true); + break; + } + + case LFUN_PASTESELECTION: + { + string const clip = bv->getClipboard(); + if (!clip.empty()) + mathed_parse_normal(par_, clip); break; } @@ -284,41 +340,29 @@ InsetFormula::localDispatch(BufferView * bv, kb_action action, result = InsetFormulaBase::localDispatch(bv, action, arg); } + //updatePreview(); + return result; } -void InsetFormula::handleExtern(const string & arg, BufferView *) -{ - //string outfile = lyx::tempName("maple.out"); - string outfile = "/tmp/lyx2" + arg + ".out"; - ostringstream os; - par()->WriteNormal(os); - string code = os.str().c_str(); - string script = "lyx2" + arg + " '" + code + "' " + outfile; - lyxerr << "calling: " << script << endl; - Systemcalls cmd(Systemcalls::System, script, 0); - - ifstream is(outfile.c_str()); - par(mathed_parse(is)); - Metrics(); -} - bool InsetFormula::display() const { - return par_->GetType() != LM_OT_SIMPLE; + return hullType() != "simple" && hullType() != "none"; } -MathMatrixInset * InsetFormula::par() const +MathHullInset const * InsetFormula::hull() const { - return static_cast(par_); + lyx::Assert(par_->asHullInset()); + return par_->asHullInset(); } -void InsetFormula::par(MathInset * p) -{ - delete par_; - par_ = p ? p : new MathMatrixInset; + +MathHullInset * InsetFormula::hull() +{ + lyx::Assert(par_->asHullInset()); + return par_->asHullInset(); } @@ -330,23 +374,135 @@ Inset::Code InsetFormula::lyxCode() const void InsetFormula::validate(LaTeXFeatures & features) const { - par()->Validate(features); + par_->validate(features); +} + + +bool InsetFormula::insetAllowed(Inset::Code code) const +{ + return + (code == Inset::LABEL_CODE && display()) + || code == Inset::REF_CODE + || code == Inset::ERT_CODE; } int InsetFormula::ascent(BufferView *, LyXFont const &) const { - return par()->ascent(); + const int a = par_->ascent(); + if (!canPreview()) + return a + 1; + return a + 1 - (par_->height() - loader_->image()->getHeight()) / 2; } int InsetFormula::descent(BufferView *, LyXFont const &) const { - return par()->descent(); + const int d = par_->descent(); + if (!canPreview()) + return d + 1; + return d + 1 - (par_->height() - loader_->image()->getHeight()) / 2; +} + + +int InsetFormula::width(BufferView * bv, LyXFont const & font) const +{ + metrics(bv, font); + return canPreview() ? loader_->image()->getWidth() : par_->width(); +} + + +string InsetFormula::hullType() const +{ + return hull() ? hull()->getType() : "none"; +} + + +void InsetFormula::mutate(string const & type ) +{ + if (hull()) + hull()->mutate(type); +} + + +// +// preview stuff +// + +bool InsetFormula::canPreview() const +{ + return lyxrc.preview && loader_ && !par_->asNestInset()->editing() + && loader_->status() == grfx::Ready; } -int InsetFormula::width(BufferView *, LyXFont const &) const +void InsetFormula::statusChanged() { - return par()->width(); + lyxerr << "### InsetFormula::statusChanged called!, status: " + << loader_->status() << "\n"; + if (loader_->status() == grfx::Ready) + view()->updateInset(this, false); + else if (loader_->status() == grfx::WaitingToLoad) + loader_->startLoading(); } + + +void InsetFormula::updatePreview() +{ + // nothing to be done if no preview requested + lyxerr << "### updatePreview() called\n"; + if (!lyxrc.preview) + return; + + // get LaTeX + ostringstream ls; + WriteStream wi(ls, false, false); + par_->write(wi); + string const data = ls.str(); + + // the preview cache, maps contents to image loaders + typedef std::map > cache_type; + static cache_type theCache; + static int theCounter = 0; + + // set our loader corresponding to our current data + cache_type::const_iterator it = theCache.find(data); + + // is this old data? + if (it != theCache.end()) { + // we have already a loader, connect to it anyway + //lyxerr << "### updatePreview(), old loader: " << loader_ << "\n"; + loader_ = it->second.get(); + loader_->statusChanged.connect + (boost::bind(&InsetFormula::statusChanged, this)); + return; + } + + // construct new file name + static string const dir = OnlyPath(lyx::tempName()); + ostringstream os; + os << dir << theCounter++ << ".lyxpreview"; + string file = os.str(); + + // the real work starts + //lyxerr << "### updatePreview(), new file " << file << "\n"; + std::ofstream of(file.c_str()); + of << "\\batchmode" + << "\\documentclass{article}" + << "\\usepackage{amssymb}" + << "\\thispagestyle{empty}" + << "\\pdfoutput=0" + << "\\begin{document}" + << data + << "\\end{document}\n"; + of.close(); + + // now we are done, start actual loading we will get called back via + // InsetFormula::statusChanged() if this is finished + theCache[data].reset(new grfx::Loader(file)); + //lyxerr << "### updatePreview(), new loader: " << loader_ << "\n"; + loader_ = theCache.find(data)->second.get(); + loader_->startLoading(); + loader_->statusChanged.connect(boost::bind(&InsetFormula::statusChanged, this)); +} +