]> git.lyx.org Git - features.git/blobdiff - src/mathed/MathMacroTemplate.cpp
* Lazy MathData to avoid unneeded interpretation of macro definitions
[features.git] / src / mathed / MathMacroTemplate.cpp
index 773bac1e4e1bbd199a8534eaab14eca998f06b3e..77cf8350dd87f347225acd6818d769e9bef42b68 100644 (file)
@@ -1,5 +1,5 @@
 /**
- * \file math_macrotemplate.C
+ * \file MathMacroTemplate.cpp
  * This file is part of LyX, the document processor.
  * Licence details can be found in the file COPYING.
  *
 
 #include <config.h>
 
+#include "MathMacroTemplate.h"
+
 #include "DocIterator.h"
 #include "InsetMathBrace.h"
 #include "InsetMathChar.h"
 #include "InsetMathSqrt.h"
 #include "MathMacro.h"
 #include "MathMacroArgument.h"
-#include "MathMacroTemplate.h"
 #include "MathStream.h"
 #include "MathParser.h"
 #include "MathSupport.h"
 #include "Buffer.h"
 #include "Color.h"
 #include "Cursor.h"
-#include "debug.h"
+#include "support/debug.h"
 #include "DispatchResult.h"
 #include "FuncRequest.h"
 #include "FuncStatus.h"
-#include "gettext.h"
+#include "support/gettext.h"
 #include "Lexer.h"
 #include "Undo.h"
 
 #include "frontends/Painter.h"
 
 #include "support/convert.h"
+#include "support/docstream.h"
 #include "support/lstrings.h"
 
-#include "debug.h"
-
-#include <boost/assert.hpp>
-#include <boost/bind.hpp>
-#include <boost/function.hpp>
+#include "support/debug.h"
 
 #include <sstream>
 
+using namespace std;
 
 namespace lyx {
 
 using support::bformat;
 
-using std::ostream;
-using std::endl;
-
 
 class InsetMathWrapper : public InsetMath {
 public:
@@ -94,24 +90,26 @@ void InsetMathWrapper::draw(PainterInfo & pi, int x, int y) const
 
 
 MathMacroTemplate::MathMacroTemplate()
-       : InsetMathNest(3), numargs_(0), optionals_(0), type_(from_ascii("newcommand"))
+       : InsetMathNest(3), numargs_(0), optionals_(0), 
+         type_(MacroTypeNewcommand)
 {
        initMath();
 }
 
 
-MathMacroTemplate::MathMacroTemplate(docstring const & name, int numargs, int optionals,
-                                                                                                                                                docstring const & type, 
-                                                                                                                                                std::vector<MathData> const & optionalValues, 
-                                                                                                                                                MathData const & def, MathData const & display)
-: InsetMathNest(optionals + 3), numargs_(numargs), 
-       optionals_(optionals), optionalValues_(optionalValues), type_(type)
+MathMacroTemplate::MathMacroTemplate(docstring const & name, int numargs,
+       int optionals, MacroType type, 
+       vector<MathData> const & optionalValues, 
+       MathData const & def, MathData const & display)
+       : InsetMathNest(optionals + 3), numargs_(numargs), 
+         optionals_(optionals), optionalValues_(optionalValues),
+         type_(type)
 {
        initMath();
 
        if (numargs_ > 9)
                lyxerr << "MathMacroTemplate::MathMacroTemplate: wrong # of arguments: "
-                       << numargs_ << std::endl;
+                       << numargs_ << endl;
        
        asArray(name, cell(0));
        optionalValues_.resize(9);
@@ -123,7 +121,8 @@ MathMacroTemplate::MathMacroTemplate(docstring const & name, int numargs, int op
 
 
 MathMacroTemplate::MathMacroTemplate(docstring const & str)
-       : InsetMathNest(3), numargs_(0)
+       : InsetMathNest(3), numargs_(0), optionals_(0),
+       type_(from_ascii("newcommand"))
 {
        initMath();
 
@@ -131,6 +130,10 @@ MathMacroTemplate::MathMacroTemplate(docstring const & str)
        mathed_parse_cell(ar, str);
        if (ar.size() != 1 || !ar[0]->asMacroTemplate()) {
                lyxerr << "Cannot read macro from '" << ar << "'" << endl;
+               asArray(from_ascii("invalidmacro"), cell(0));
+               // FIXME: The macro template does not make sense after this.
+               // The whole parsing should not be in a constructor which
+               // has no chance to report failure.
                return;
        }
        operator=( *(ar[0]->asMacroTemplate()) );
@@ -149,6 +152,12 @@ docstring MathMacroTemplate::name() const
 }
 
 
+void MathMacroTemplate::updateToContext(MacroContext const & mc) const
+{
+       redefinition_ = mc.get(name()) != 0;
+}
+
+
 void MathMacroTemplate::metrics(MetricsInfo & mi, Dimension & dim) const
 {
        FontSetChanger dummy1(mi.base, from_ascii("mathnormal"));
@@ -156,13 +165,11 @@ void MathMacroTemplate::metrics(MetricsInfo & mi, Dimension & dim) const
 
        // valid macro?
        MacroData const * macro = 0;
-       if (validName() && mi.macrocontext.has(name())) {
-               macro = &mi.macrocontext.get(name());
-               if (type_ == from_ascii("newcommand") || type_ == from_ascii("renewcommand")) {
-                       // use the MacroData::redefinition_ information instead of MacroContext::has
-                       // because the macro is known here already anyway to detect recursive definitions
-                       type_ = macro->redefinition() ? from_ascii("renewcommand") : from_ascii("newcommand"); 
-               }
+       if (validName()) {
+               macro = mi.macrocontext.get(name());
+
+               // updateToContext() - avoids another lookup
+               redefinition_ = macro != 0;
        }
 
        // create label "{#1}{#2}:="
@@ -205,21 +212,21 @@ void MathMacroTemplate::metrics(MetricsInfo & mi, Dimension & dim) const
                defdim.width() + 16 + dspdim.width() + 2;       
 
        dim.asc = dim0.ascent();
-       dim.asc = std::max(dim.asc, labeldim.ascent());
-       dim.asc = std::max(dim.asc, defdim.ascent());
-       dim.asc = std::max(dim.asc, dspdim.ascent());
+       dim.asc = max(dim.asc, labeldim.ascent());
+       dim.asc = max(dim.asc, defdim.ascent());
+       dim.asc = max(dim.asc, dspdim.ascent());
 
        dim.des = dim0.descent();
-       dim.des = std::max(dim.des, labeldim.descent());
-       dim.des = std::max(dim.des, defdim.descent());
-       dim.des = std::max(dim.des, dspdim.descent());
+       dim.des = max(dim.des, labeldim.descent());
+       dim.des = max(dim.des, defdim.descent());
+       dim.des = max(dim.des, dspdim.descent());
 
        // make the name cell vertically centered, and 5 pixel lines margin
        int real_asc = dim.asc - dim0.ascent() / 2;
        int real_des = dim.des + dim0.ascent() / 2;
-       dim.asc = std::max(real_asc, real_des) + dim0.ascent() / 2 + 5;
-       dim.des = std::max(real_asc, real_des) - dim0.ascent() / 2 + 5;
-       
+       dim.asc = max(real_asc, real_des) + dim0.ascent() / 2 + 5;
+       dim.des = max(real_asc, real_des) - dim0.ascent() / 2 + 5;
+
        setDimCache(mi, dim);
 }
 
@@ -481,7 +488,9 @@ void MathMacroTemplate::makeOptional(Cursor & cur) {
 void MathMacroTemplate::makeNonOptional(Cursor & cur) {
        if (numargs_ > 0 && optionals_ > 0) {
                --optionals_;
-               optionalValues_[optionals_ - 1] = cell(optIdx(optionals_));
+               
+               // store default value for later if the use changes his mind
+               optionalValues_[optionals_] = cell(optIdx(optionals_));
                cells_.erase(cells_.begin() + optIdx(optionals_));
 
                // fix cursor
@@ -508,7 +517,7 @@ void MathMacroTemplate::makeNonOptional(Cursor & cur) {
 
 void MathMacroTemplate::doDispatch(Cursor & cur, FuncRequest & cmd)
 {
-       std::string const arg = to_utf8(cmd.argument());
+       string const arg = to_utf8(cmd.argument());
        switch (cmd.action) {
 
        case LFUN_MATH_MACRO_ADD_PARAM: 
@@ -565,9 +574,10 @@ void MathMacroTemplate::doDispatch(Cursor & cur, FuncRequest & cmd)
                break;
 
        case LFUN_MATH_MACRO_REMOVE_OPTIONAL_PARAM:
-               if (optionals_ > 0)
+               if (optionals_ > 0) {
+                       cur.recordUndoFullDocument();
                        removeParameter(cur, optionals_ - 1);
-               break;
+               break;
 
        case LFUN_MATH_MACRO_ADD_GREEDY_OPTIONAL_PARAM:
                if (numargs_ == optionals_) {
@@ -584,17 +594,18 @@ void MathMacroTemplate::doDispatch(Cursor & cur, FuncRequest & cmd)
 }
 
 
-bool MathMacroTemplate::getStatus(Cursor & cur, FuncRequest const & cmd,
+bool MathMacroTemplate::getStatus(Cursor & /*cur*/, FuncRequest const & cmd,
        FuncStatus & flag) const
 {
        bool ret = true;
-       std::string const arg = to_utf8(cmd.argument());
+       string const arg = to_utf8(cmd.argument());
        switch (cmd.action) {
                case LFUN_MATH_MACRO_ADD_PARAM: {
                        int num = numargs_ + 1;
                        if (arg.size() != 0)
                                num = convert<int>(arg);
-                       bool on = (num >= optionals_ && numargs_ < 9 && num <= numargs_ + 1);
+                       bool on = (num >= optionals_ 
+                                  && numargs_ < 9 && num <= numargs_ + 1);
                        flag.enabled(on);
                        break;
                }
@@ -612,11 +623,14 @@ bool MathMacroTemplate::getStatus(Cursor & cur, FuncRequest const & cmd,
                }
 
                case LFUN_MATH_MACRO_MAKE_OPTIONAL:
-                       flag.enabled(numargs_ > 0 && optionals_ < numargs_ && type_ != from_ascii("def"));
+                       flag.enabled(numargs_ > 0 
+                                    && optionals_ < numargs_ 
+                                    && type_ != MacroTypeDef);
                        break;
 
                case LFUN_MATH_MACRO_MAKE_NONOPTIONAL:
-                       flag.enabled(optionals_ > 0 && type_ != from_ascii("def"));
+                       flag.enabled(optionals_ > 0 
+                                    && type_ != MacroTypeDef);
                        break;
 
                case LFUN_MATH_MACRO_ADD_OPTIONAL_PARAM:
@@ -628,7 +642,8 @@ bool MathMacroTemplate::getStatus(Cursor & cur, FuncRequest const & cmd,
                        break;
 
                case LFUN_MATH_MACRO_ADD_GREEDY_OPTIONAL_PARAM:
-                       flag.enabled(numargs_ == 0 && type_ != from_ascii("def"));
+                       flag.enabled(numargs_ == 0 
+                                    && type_ != MacroTypeDef);
                        break;
 
                default:
@@ -652,7 +667,7 @@ void MathMacroTemplate::read(Buffer const &, Lexer & lex)
 }
 
 
-void MathMacroTemplate::write(Buffer const &, std::ostream & os) const
+void MathMacroTemplate::write(Buffer const &, ostream & os) const
 {
        odocstringstream oss;
        WriteStream wi(oss, false, false);
@@ -664,13 +679,23 @@ void MathMacroTemplate::write(Buffer const &, std::ostream & os) const
 
 void MathMacroTemplate::write(WriteStream & os) const
 {
-       if (type_ == "def") {
+       write(os, false);
+}
+
+
+void MathMacroTemplate::write(WriteStream & os, bool overwriteRedefinition) const
+{
+       if (type_ == MacroTypeDef) {
                os << "\\def\\" << name().c_str();
                for (int i = 1; i <= numargs_; ++i)
                        os << '#' << i;
        } else {
                // newcommand or renewcommand
-               os << "\\" << type_.c_str() << "{\\" << name().c_str() << '}';
+               if (redefinition_ && !overwriteRedefinition)
+                       os << "\\renewcommand";
+               else
+                       os << "\\newcommand";
+               os << "{\\" << name().c_str() << '}';
                if (numargs_ > 0)
                        os << '[' << numargs_ << ']';
                
@@ -750,14 +775,61 @@ bool MathMacroTemplate::validMacro() const
 }
 
 
-MacroData MathMacroTemplate::asMacroData() const
+bool MathMacroTemplate::fixNameAndCheckIfValid()
+{
+       // check all the characters/insets in the name cell
+       size_t i = 0;
+       MathData & data = cell(0);
+       while (i < data.size()) {
+               InsetMathChar const * cinset = data[i]->asCharInset();
+               if (cinset) {
+                       // valid character in [a-zA-Z]?
+                       char_type c = cinset->getChar();
+                       if ((c >= 'a' && c <= 'z')
+                           || (c >= 'A' && c <= 'Z')) {
+                               ++i;
+                               continue;
+                       }
+               }
+               
+               // throw cell away
+               data.erase(i);
+       }
+
+       // now it should be valid if anything in the name survived
+       return data.size() > 0;
+}
+
+
+void MathMacroTemplate::getDefaults(vector<docstring> & defaults) const
 {
-       std::vector<docstring> defaults(numargs_);
+       defaults.resize(numargs_);
        for (int i = 0; i < optionals_; ++i)
-               defaults[i] = asString(cell(optIdx(i)));
-       return MacroData(asString(cell(defIdx())), defaults,
-               numargs_, optionals_, asString(cell(displayIdx())), std::string());
+               defaults[i] = asString(cell(optIdx(i)));        
+}
+
+
+docstring MathMacroTemplate::definition() const
+{
+       return asString(cell(defIdx()));
 }
 
 
+docstring MathMacroTemplate::displayDefinition() const
+{
+       return asString(cell(displayIdx()));
+}
+
+
+size_t MathMacroTemplate::numArgs() const
+{
+       return numargs_;
+}
+
+
+size_t MathMacroTemplate::numOptionals() const
+{
+       return optionals_;
+}
+
 } // namespace lyx