X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Fmathed%2Fformula.C;h=a6c0b602018d1942208792fcdc2a17c6e4868572;hb=46880e2b9b49632c56bab2377ce9a3c826cf8d1d;hp=0b14506d4f15aef9b4d028ea98b6e9986089f6e4;hpb=8c3c804a4de7d0ceeac2b0b258e2494f5109b471;p=lyx.git diff --git a/src/mathed/formula.C b/src/mathed/formula.C index 0b14506d4f..a6c0b60201 100644 --- a/src/mathed/formula.C +++ b/src/mathed/formula.C @@ -30,19 +30,24 @@ #include "BufferView.h" #include "gettext.h" #include "debug.h" -#include "frontends/Alert.h" #include "support/LOstream.h" #include "support/LAssert.h" #include "support/lyxlib.h" -#include "support/syscall.h" -#include "support/lstrings.h" -#include "support/filetools.h" // LibFileSearch -#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_hullinset.h" #include "math_support.h" #include "math_mathmlstream.h" +#include "textpainter.h" + +#include +#include +#include using std::ostream; using std::ifstream; @@ -53,268 +58,92 @@ using std::vector; using std::getline; -namespace { - - string captureOutput(string const & cmd, string const & data) - { - string outfile = lyx::tempName(string(), "mathextern"); - string full = "echo '" + data + "' | (" + cmd + ") > " + outfile; - lyxerr << "calling: " << full << "\n"; - Systemcalls dummy(Systemcalls::System, full, 0); - string out = GetFileContents(outfile); - lyx::unlink(outfile); - lyxerr << "result: '" << out << "'\n"; - return out; - } - - - MathArray pipeThroughMaple(string const & extra, MathArray const & ar) - { - string header = "readlib(latex):\n"; - - // remove the \\it for variable names - //"#`latex/csname_font` := `\\it `:" - header += - "`latex/csname_font` := ``:\n"; - - // export matrices in (...) instead of [...] - header += - "`latex/latex/matrix` := " - "subs(`[`=`(`, `]`=`)`," - "eval(`latex/latex/matrix`)):\n"; - - // replace \\cdots with proper '*' - header += - "`latex/latex/*` := " - "subs(`\\,`=`\\cdot `," - "eval(`latex/latex/*`)):\n"; - - // remove spurious \\noalign{\\medskip} in matrix output - header += - "`latex/latex/matrix`:= " - "subs(`\\\\\\\\\\\\noalign{\\\\medskip}` = `\\\\\\\\`," - "eval(`latex/latex/matrix`)):\n"; - - //"#`latex/latex/symbol` " - // " := subs((\\'_\\' = \\'`\\_`\\',eval(`latex/latex/symbol`)): "; - - string trailer = "quit;"; - ostringstream os; - MapleStream ms(os); - ms << ar; - string expr = os.str().c_str(); - - for (int i = 0; i < 100; ++i) { // at most 100 attempts - // try to fix missing '*' the hard way by using mint - // - // ... > echo "1A;" | mint -i 1 -S -s -q - // on line 1: 1A; - // ^ syntax error - - // Probably missing an operator such as * p - // - lyxerr << "checking expr: '" << expr << "'\n"; - string out = captureOutput("mint -i 1 -S -s -q -q", expr + ";"); - if (out.empty()) - break; // expression syntax is ok - istringstream is(out.c_str()); - string line; - getline(is, line); - if (line.find("on line") != 0) - break; // error message not identified - getline(is, line); - string::size_type pos = line.find('^'); - if (pos == string::npos || pos < 15) - break; // caret position not found - pos -= 15; // skip the "on line ..." part - if (expr[pos] == '*' || (pos > 0 && expr[pos - 1] == '*')) - break; // two '*' in a row are definitely bad - expr.insert(pos, "*"); - } - - string full = "latex(" + extra + '(' + expr + "));"; - string out = captureOutput("maple -q", header + full + trailer); - - // change \_ into _ - - // - MathArray res; - mathed_parse_cell(res, out); - return res; - } - - - MathArray pipeThroughOctave(string const &, MathArray const & ar) - { - ostringstream os; - OctaveStream vs(os); - vs << ar; - string expr = os.str().c_str(); - string out; - - for (int i = 0; i < 100; ++i) { // at most 100 attempts - // - // try to fix missing '*' the hard way - // parse error: - // >>> ([[1 2 3 ];[2 3 1 ];[3 1 2 ]])([[1 2 3 ];[2 3 1 ];[3 1 2 ]]) - // ^ - // - lyxerr << "checking expr: '" << expr << "'\n"; - out = captureOutput("octave -q 2>&1", expr); - lyxerr << "checking out: '" << out << "'\n"; - - // leave loop if expression syntax is probably ok - if (out.find("parse error:") == string::npos) - break; - - // search line with single caret - istringstream is(out.c_str()); - string line; - while (is) { - getline(is, line); - lyxerr << "skipping line: '" << line << "'\n"; - if (line.find(">>> ") != string::npos) - break; - } - - // found line with error, next line is the one with caret - getline(is, line); - string::size_type pos = line.find('^'); - lyxerr << "caret line: '" << line << "'\n"; - lyxerr << "found caret at pos: '" << pos << "'\n"; - if (pos == string::npos || pos < 4) - break; // caret position not found - pos -= 4; // skip the ">>> " part - if (expr[pos] == '*') - break; // two '*' in a row are definitely bad - expr.insert(pos, "*"); - } - - if (out.size() < 6) - return MathArray(); - - // remove 'ans = ' - out = out.substr(6); - - // parse output as matrix or single number - MathAtom at(new MathArrayInset(out)); - MathArrayInset const * mat = at.nucleus()->asArrayInset(); - MathArray res; - if (mat->ncols() == 1 && mat->nrows() == 1) - res.push_back(mat->cell(0)); - else { - res.push_back(MathAtom(new MathDelimInset("(", ")"))); - res.back()->cell(0).push_back(at); - } - return res; - } - - - MathArray pipeThroughExtern(string const & lang, string const & extra, - MathArray const & ar) - { - if (lang == "octave") - return pipeThroughOctave(extra, ar); - - if (lang == "maple") - return pipeThroughMaple(extra, ar); - - // create normalized expression - ostringstream os; - NormalStream ns(os); - os << "[" << extra << ' '; - ns << ar; - os << "]"; - string data = os.str().c_str(); - - // search external script - string file = LibFileSearch("mathed", "extern_" + lang); - if (file.empty()) { - lyxerr << "converter to '" << lang << "' not found\n"; - return MathArray(); - } - - // run external sript - string out = captureOutput(file, data); - MathArray res; - mathed_parse_cell(res, out); - return res; - } - -} - InsetFormula::InsetFormula() - : par_(MathAtom(new MathHullInset)) + : par_(MathAtom(new MathHullInset)), loader_(0) {} -InsetFormula::InsetFormula(MathInsetTypes t) - : par_(MathAtom(new MathHullInset(t))) -{} - - -InsetFormula::InsetFormula(string const & s) +InsetFormula::InsetFormula(BufferView * bv) + : par_(MathAtom(new MathHullInset)), loader_(0) { - if (s.size()) { - bool res = mathed_parse_normal(par_, s); + view_ = bv; +} - if (!res) - res = mathed_parse_normal(par_, "$" + s + "$"); - if (!res) { - lyxerr << "cannot interpret '" << s << "' as math\n"; - par_ = MathAtom(new MathHullInset(LM_OT_SIMPLE)); - } - } - metrics(); +InsetFormula::InsetFormula(string const & data) + : par_(MathAtom(new MathHullInset)), loader_(0) +{ + 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(Buffer const * buf, ostream & os) const +void InsetFormula::write(Buffer const *, ostream & os) const { os << "Formula "; - latex(buf, os, false, false); + WriteStream wi(os, false, false); + par_->write(wi); } -int InsetFormula::latex(Buffer const * buf, ostream & os, bool fragil, bool) - const +int InsetFormula::latex(Buffer const *, ostream & os, bool fragile, bool) const { - WriteStream wi(buf, os, fragil); + WriteStream wi(os, fragile, true); par_->write(wi); - return wi.line_; + return wi.line(); } -int InsetFormula::ascii(Buffer const * buf, ostream & os, int) const + +int InsetFormula::ascii(Buffer const *, ostream & os, int) const { - WriteStream wi(buf, os, false); +#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_; + return wi.line(); +#endif } int InsetFormula::linuxdoc(Buffer const * buf, ostream & os) const { - return docbook(buf, os); + return docbook(buf, os, false); } -int InsetFormula::docbook(Buffer const * buf, ostream & os) const +int InsetFormula::docbook(Buffer const * buf, ostream & os, bool) const { MathMLStream ms(os); - ms << MTag("equation") << MTag("alt"); - int res = ascii(buf, ms.os_, 0); - ms << ETag("alt") << MTag("math"); - ms << par_.nucleus(); - ms << ETag("math") << ETag("equation"); - return ms.line_ + res; + 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; } @@ -322,10 +151,11 @@ void InsetFormula::read(Buffer const *, LyXLex & lex) { mathed_parse_normal(par_, lex); metrics(); + updatePreview(); } -//std::ostream & operator<<(std::ostream & os, LyXCursor const & c) +//ostream & operator<<(ostream & os, LyXCursor const & c) //{ // os << '[' << c.x() << ' ' << c.y() << ' ' << c.pos() << ']'; // return os; @@ -335,25 +165,36 @@ void InsetFormula::read(Buffer const *, LyXLex & lex) void InsetFormula::draw(BufferView * bv, LyXFont const & font, int y, float & xx, bool) const { - int x = int(xx); + 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; - Painter & pain = bv->painter(); + MathPainterInfo pi(bv->painter()); - metrics(bv, font); - int w = par_->width(); - int h = par_->height(); - int a = par_->ascent(); - - if (lcolor.getX11Name(LColor::mathbg)!=lcolor.getX11Name(LColor::background)) - pain.fillRectangle(x, y - a, w, h, LColor::mathbg); + 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); + } - if (mathcursor && mathcursor->formula() == this) { - mathcursor->drawSelection(pain); - pain.rectangle(x, y - a, w, h, LColor::mathframe); + par_->draw(pi, x, y); } - par_->draw(pain, x, y); - xx += par_->width(); + xx += w; xo_ = x; yo_ = y; @@ -363,7 +204,7 @@ void InsetFormula::draw(BufferView * bv, LyXFont const & font, vector const InsetFormula::getLabelList() const { - return mat()->getLabelList(); + return hull()->getLabelList(); } @@ -375,7 +216,7 @@ InsetFormula::localDispatch(BufferView * bv, kb_action action, switch (action) { - case LFUN_BREAKLINE: + case LFUN_BREAKLINE: bv->lockedInsetStoreUndo(Undo::INSERT); mathcursor->breakLine(); mathcursor->normalize(); @@ -387,9 +228,9 @@ InsetFormula::localDispatch(BufferView * bv, kb_action action, //lyxerr << "toggling all numbers\n"; if (display()) { bv->lockedInsetStoreUndo(Undo::INSERT); - bool old = mat()->numberedType(); + bool old = hull()->numberedType(); for (MathInset::row_type row = 0; row < par_->nrows(); ++row) - mat()->numbered(row, !old); + hull()->numbered(row, !old); bv->owner()->message(old ? _("No number") : _("Number")); updateLocal(bv, true); } @@ -401,10 +242,10 @@ InsetFormula::localDispatch(BufferView * bv, kb_action action, //lyxerr << "toggling line number\n"; if (display()) { bv->lockedInsetStoreUndo(Undo::INSERT); - MathCursor::row_type row = mathcursor->row(); - bool old = mat()->numbered(row); + MathCursor::row_type row = mathcursor->hullRow(); + bool old = hull()->numbered(row); bv->owner()->message(old ? _("No number") : _("Number")); - mat()->numbered(row, !old); + hull()->numbered(row, !old); updateLocal(bv, true); } break; @@ -414,8 +255,8 @@ InsetFormula::localDispatch(BufferView * bv, kb_action action, { bv->lockedInsetStoreUndo(Undo::INSERT); - MathCursor::row_type row = mathcursor->row(); - string old_label = mat()->label(row); + MathCursor::row_type row = mathcursor->hullRow(); + string old_label = hull()->label(row); string new_label = arg; if (new_label.empty()) { @@ -424,8 +265,6 @@ InsetFormula::localDispatch(BufferView * bv, kb_action action, pair const res = old_label.empty() ? Alert::askForText(_("Enter new label to insert:"), default_label) : Alert::askForText(_("Enter label:"), old_label); - - lyxerr << "res: " << res.first << " - '" << res.second << "'\n"; if (!res.first) break; new_label = frontStrip(strip(res.second)); @@ -436,13 +275,15 @@ InsetFormula::localDispatch(BufferView * bv, kb_action action, if (!new_label.empty()) { lyxerr << "setting label to '" << new_label << "'\n"; - mat()->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(); - mat()->label(row, new_label); + hull()->label(row, new_label); updateLocal(bv, true); break; @@ -454,7 +295,7 @@ InsetFormula::localDispatch(BufferView * bv, kb_action action, int x; int y; mathcursor->getPos(x, y); - mat()->mutate(arg); + mutate(arg); mathcursor->setPos(x, y); mathcursor->normalize(); updateLocal(bv, true); @@ -464,7 +305,8 @@ InsetFormula::localDispatch(BufferView * bv, kb_action action, case LFUN_MATH_EXTERN: { bv->lockedInsetStoreUndo(Undo::EDIT); - handleExtern(arg); + if (mathcursor) + mathcursor->handleExtern(arg); // re-compute inset dimension metrics(bv); updateLocal(bv, true); @@ -476,98 +318,48 @@ InsetFormula::localDispatch(BufferView * bv, kb_action action, int x = 0; int y = 0; mathcursor->getPos(x, y); - if (mat()->getType() == LM_OT_SIMPLE) - mat()->mutate(LM_OT_EQUATION); + if (hullType() == "simple") + mutate("equation"); else - mat()->mutate(LM_OT_SIMPLE); + mutate("simple"); mathcursor->setPos(x, y); mathcursor->normalize(); updateLocal(bv, true); break; } - + case LFUN_PASTESELECTION: { string const clip = bv->getClipboard(); - if (!clip.empty()) + if (!clip.empty()) mathed_parse_normal(par_, clip); break; } - case LFUN_MATH_COLUMN_INSERT: - { - if (mat()->getType() == LM_OT_ALIGN) - mat()->mutate(LM_OT_ALIGNAT); - mat()->addCol(mat()->ncols()); - mathcursor->normalize(); - updateLocal(bv, true); - break; - } - default: result = InsetFormulaBase::localDispatch(bv, action, arg); } - return result; -} + //updatePreview(); - -bool needEqnArray(string const & extra) -{ - return false; - return extra == "dsolve"; -} - - -void InsetFormula::handleExtern(const string & arg) -{ - // where are we? - if (!mathcursor) - return; - - string lang; - string extra; - istringstream iss(arg.c_str()); - iss >> lang >> extra; - if (extra.empty()) - extra = "noextra"; - - bool selected = mathcursor->selection(); - - MathArray ar; - if (needEqnArray(extra)) { - mathcursor->last(); - mathcursor->readLine(ar); - mathcursor->breakLine(); - } else if (selected) { - mathcursor->selGet(ar); - //lyxerr << "use selection: " << ar << "\n"; - } else { - mathcursor->last(); - mathcursor->stripFromLastEqualSign(); - ar = mathcursor->cursor().cell(); - mathcursor->insert(MathAtom(new MathCharInset('=', LM_TC_VAR))); - //lyxerr << "use whole cell: " << ar << "\n"; - } - - mathcursor->insert(pipeThroughExtern(lang, extra, ar)); + return result; } bool InsetFormula::display() const { - return mat()->getType() != LM_OT_SIMPLE; + return hullType() != "simple" && hullType() != "none"; } -MathHullInset const * InsetFormula::mat() const +MathHullInset const * InsetFormula::hull() const { lyx::Assert(par_->asHullInset()); return par_->asHullInset(); } -MathHullInset * InsetFormula::mat() +MathHullInset * InsetFormula::hull() { lyx::Assert(par_->asHullInset()); return par_->asHullInset(); @@ -588,32 +380,129 @@ void InsetFormula::validate(LaTeXFeatures & features) const bool InsetFormula::insetAllowed(Inset::Code code) const { - return + return (code == Inset::LABEL_CODE && display()) - || code == Inset::ERT_CODE; + || code == Inset::REF_CODE + || code == Inset::ERT_CODE; } int InsetFormula::ascent(BufferView *, LyXFont const &) const { - return par_->ascent() + 1; + 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() + 1; + 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 par_->width(); + return canPreview() ? loader_->image()->getWidth() : par_->width(); } -MathInsetTypes InsetFormula::getType() const +string InsetFormula::hullType() const { - return mat()->getType(); + 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; +} + + +void InsetFormula::statusChanged() +{ + 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)); +} +